Aceleração MicroPython

O MicroPython é uma implementação da linguagem de programação Python para microcontroladores que permite ao público dessa linguagem usar os princípios familiares de sintaxe e programação para trabalhar com pequenos dispositivos de computação.

No meu trabalho, uso o MicroPython para prototipar, testar rapidamente idéias e criar pequenos estandes. Graças ao REPL e sua sintaxe simples, o MicroPython também é ótimo para projetos de bricolage e para ensinar programação.

Quando se trata da interação dos computadores com o mundo real, estou sempre interessado na velocidade da interação deles. Em alguns casos, o uso da tecnologia de microprocessador, por exemplo, no campo da Internet, a velocidade de reação do dispositivo não é tão importante. Não há muita diferença quando a sirene de alarme é ativada: 10 microssegundos após a detecção de movimento ou 10 milissegundos.

Mas, em alguns aspectos, a velocidade e o tempo de reação são importantes e surge a questão de saber se o MicroPython deve ser usado. Então fiz uma pequena pesquisa, inspirada no vídeo do discurso do criador do MicroPython Damien George. Eu me perguntava com que rapidez um programa escrito em Micropython responderia ao impacto da entrada.

O dispositivo experimental será o microcontrolador ESP8266, na placa NodeMcu com a versão MicroPython esp8266-2018511-v1.9.4 na placa.



Pressionarei o botão e registrarei no osciloscópio a diferença horária entre a pressão e a aparência de 3,3 V na outra perna do microprocessador. Cada medição é feita 15 vezes, a média é obtida (ilustrada nos gráficos) e o desvio padrão é calculado (barra preta nos gráficos).

Teste número 1.


Se você resolver esse problema "de frente", o programa parecerá bastante trivial:

import machine import time o = machine.Pin(5, machine.Pin.OUT) #D1 out i = machine.Pin(4, machine.Pin.IN) #D2 in while 1: if i.value(): o.value(1) time.sleep(0.1) o.value(0) 

Uma forma de onda típica com esse programa se parece com isso:



Aqui e em outras formas de onda, o sinal "azul" é o pino com o botão, o pino de resposta "verde". Com 15 repetições, a seguinte imagem é obtida:



Em média, o tempo de reação é de cerca de 310 microssegundos, o máximo é de 356 μs, não muito rápido, mas para algumas aplicações é bastante aceitável.

Teste número 2


Você pode acelerar o código padrão "pronto para uso" através do tratamento de interrupções.

 import machine import time o = machine.Pin(5, machine.Pin.OUT) #D1 out i = machine.Pin(4, machine.Pin.IN) #D2 in def f(_): o.value(1) time.sleep(0.1) o.value(0) i.irq(trigger=machine.Pin.IRQ_RISING, handler=f) 

E a imagem é a seguinte:




e o tempo máximo de resposta é 306 μs.

O uso de interrupções gera um aumento na velocidade de cerca de 20%, mas ao mesmo tempo gera uma propagação bastante grande no tempo de resposta.

Teste número 3


Se as velocidades obtidas não forem suficientes, o próximo passo é usar a construção @ micropython.native, que possibilita a conversão do código Python em código de máquina nativo. Mas existem algumas limitações .

Opção de código:

 import machine import time o = machine.Pin(5, machine.Pin.OUT) #D1 out i = machine.Pin(4, machine.Pin.IN) #D2 in @micropython.native def f(): while 1: if i.value(): o.value(1) time.sleep(0.1) o.value(0) f() 

Padrão de resposta típico na forma de onda:



Comparado com o método anterior, a aceleração quase dobrou:



O tempo de resposta mais longo é de 128 μs.

Teste número 4


O próximo passo na busca por um MicroPython "rápido" é usar a construção @ micropython.viper e acessar os registros do microprocessador diretamente (os endereços dos registros podem ser encontrados aqui .

 import time @micropython.viper def f(): O = ptr32(0x60000300) #  GPIO ESP8266 while 1: s = ((O[6] & 0x10) >> 4) #    4  if s: O[1] = 0x20 # 5  time.sleep(0.1) O[2] = 0x20 # 5  f() 

E, como resultado, a resposta acelerou visivelmente:



O tempo de resposta é muito pequeno e não pode ser comparado com outros métodos (máximo de 820 ns):





Se isso não for suficiente, você poderá usar inserções do assembler através do decorador @ micropython.asm_thumb. Com este método, o python não permanece particularmente (e as vantagens de alto nível do Python são perdidas) e, se forem necessárias velocidades mais altas, é melhor usar outro hardware, por exemplo, FPGA (onde o FPGA (onde o Python também pode ser útil, veja aqui e aqui )).

UART


Se houver necessidade de transmitir muitas informações após algum evento, você poderá usar a interface UART serial.

Tomemos, por exemplo, duas opções de implementação.

O primeiro é através do tratamento de interrupções:

 import machine i = machine.Pin(4, machine.Pin.IN) #D2 in ua = machine.UART(1) ua.init(1000000) def f(_): ua.write(b'\x01') i.irq(trigger=machine.Pin.IRQ_RISING, handler=f) 

E a forma de onda da resposta:



O tempo máximo de resposta é de 248 μs.
E o segundo teste através do viper:

 import machine import time i = machine.Pin(4, machine.Pin.IN) #D2 in ua = machine.UART(1) ua.init(1000000) @micropython.viper def f(): O = ptr32(0x60000300) while 1: if ((O[6] & 0x10) >> 4): ua.write(b'\x01') time.sleep(0.1) f() 

E a forma de onda no segundo teste:



O tempo máximo de resposta com este código é de 71 μs.
O tempo médio de reação em dois testes:



A aceleração da reação é alcançada devido à detecção mais rápida do efeito de entrada no segundo teste.

Conclusão


O MicroPython permite que você use itens característicos de linguagens de alto nível (OOP, manipulação de exceções, compras de lista e ditado, etc.) ao programar microcontroladores e, se necessário, acelere significativamente o código Python "clássico".

Source: https://habr.com/ru/post/pt448702/


All Articles