Accélération MicroPython

MicroPython est une implémentation du langage de programmation Python pour microcontrôleurs qui permet au public de ce langage en utilisant la syntaxe et les principes de programmation familiers de fonctionner avec de petits appareils informatiques.

Dans mon travail, j'utilise MicroPython pour prototyper, tester rapidement des idées et créer de petits stands. Grâce à REPL et à la syntaxe simple, MicroPython est également idéal pour les projets de bricolage et pour l'enseignement de la programmation.

En ce qui concerne l'interaction des ordinateurs avec le monde réel, je suis toujours intéressé par la vitesse de leur interaction. Dans certains cas, l'utilisation de la technologie des microprocesseurs, par exemple dans le domaine de l'Internet des objets, la vitesse de réaction de l'appareil n'est pas si importante. Il n'y a pas beaucoup de différence lorsque la sirène d'alarme se met en marche: 10 microsecondes après détection de mouvement ou 10 millisecondes.

Mais sous certains aspects, la vitesse et le temps de réaction sont importants et la question se pose de l'opportunité d'utiliser MicroPython. J'ai donc fait une petite recherche, qui s'est inspirée de la vidéo du discours du créateur de MicroPython Damien George. Je me demandais à quelle vitesse un programme écrit en Micropython réagirait à l'impact des entrées.

Le dispositif expérimental sera le microcontrôleur ESP8266, sur la carte NodeMcu avec la version MicroPython esp8266-2018511-v1.9.4 à bord.



Je vais appuyer sur le bouton et enregistrer sur l'oscilloscope la différence de temps entre la pression et l'apparition de 3,3 V sur l'autre jambe du microprocesseur. Chaque mesure est effectuée 15 fois, la moyenne est prise (illustrée dans les graphiques) et l'écart type est calculé (barre noire dans les graphiques).

Numéro de test 1.


Si vous résolvez ce problème "de front", alors le programme semble assez 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) 

Une forme d'onde typique avec un tel programme ressemble à ceci:



Ici et sur d'autres formes d'onde, le signal «bleu» est la broche avec le bouton, la broche de réponse «verte». Avec 15 répétitions, l'image suivante est obtenue:



En moyenne, le temps de réaction est d'environ 310 microsecondes, le maximum - 356 μs, pas très rapide, mais pour certaines applications, il est tout à fait acceptable.

Test numéro 2


Vous pouvez accélérer le code standard «prêt à l'emploi» grâce à la gestion des interruptions.

 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) 

Et l'image est la suivante:




et le temps de réponse maximum est de 306 μs.

L'utilisation d'interruptions donne une augmentation de la vitesse d'environ 20%, mais donne en même temps une dispersion assez importante du temps de réponse.

Numéro d'essai 3


Si les vitesses obtenues ne sont pas suffisantes, l'étape suivante consiste à utiliser la construction @ micropython.native, qui permet de convertir le code Python en code machine natif. Mais il y a quelques limites .

Option de code:

 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() 

Modèle de réponse typique sur la forme d'onde:



Par rapport à la méthode précédente, l'accélération est presque doublée:



Le temps de réponse le plus long est de 128 μs.

Numéro d'essai 4


L'étape suivante dans la recherche d'un MicroPython «rapide» consiste à utiliser la construction @ micropython.viper et à accéder directement aux registres du microprocesseur (les adresses des registres peuvent être trouvées ici .

 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() 

Et en conséquence, la réponse s'est sensiblement accélérée:



Le temps de réponse est très faible et ne peut être comparé à d'autres méthodes (maximum 820 ns):





Si cela ne suffit pas, vous pouvez utiliser des insertions d'assembleur via le décorateur @ micropython.asm_thumb. Avec cette méthode, python ne reste pas particulièrement (et les avantages de haut niveau de Python sont perdus), et si des vitesses plus élevées sont nécessaires, il est préférable d'utiliser d'autres matériels, par exemple FPGA (où Python peut également être utile, voir ici et ici ).

UART


S'il est nécessaire de transmettre de nombreuses informations après un événement, vous pouvez utiliser l'interface série UART.

Prenons par exemple deux options d'implémentation.

La première consiste à gérer les interruptions:

 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) 

Et la forme d'onde de réponse:



Le temps de réponse maximum est de 248 μs.
Et le deuxième test via 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() 

Et la forme d'onde dans le deuxième test:



Le temps de réponse maximum avec ce code est de 71 μs.
Le temps de réaction moyen dans deux tests:



L'accélération de la réaction est obtenue grâce à une détection plus rapide de l'effet d'entrée dans le deuxième test.

Conclusion


MicroPython vous permet d'utiliser des choses qui sont caractéristiques des langages de haut niveau (POO, gestion des exceptions, comprahansions liste et dict, etc.) lors de la programmation de microcontrôleurs et, si nécessaire, d'accélérer considérablement le code Python "classique".

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


All Articles