IMA4 2019/2020 EC2
Sommaire
Objectif
Vous allez travailler sur une carte de développement ATXMEGAC3-XPLD. L'idée est d'implanter micro-python sur cette carte.
Vous aurez donc à écrire un programme pour le micro-contrôleur ATXmega384C3 avec la LUFA.
Cherchez l'embryon de portage de micro-python sur AVR, récupérez le répertoire avr8-dummy et intégrez-le à une version récente de micro-python. Fusionnez ce logiciel avec l'exemple VirtualSerial de la LUFA pour avoir une console USB/série avec un interpréteur micro-python sur l'ATXMEGAC3-XPLD.
Matériel reçu
Cahier des charges
Durant cette épreuve, je vais écrire un programme pour le micro-contrôleur ATXmega384C3 avec la LUFA. L'objectif est d'implanter micro-python sur l'ATXMEGAC3-XPLD. Afin de réaliser cet objectif, il faudra procéder en plusieurs étapes :
- Comprendre et prendre en main la démo Virtual Serial de la bibliothèque LUFA
- Récupérer le répertoire avr8-dummy et l'intégrer à une version récente de micropython. Une fois ceci réalisé, comprendre et compiler une première fois le main.c du répertoire avr8-dummy
- Comprendre les deux makefiles utilisés
- Réaliser la fusion de la démo Virtual Serial de la Lufa avec micropython
Travail réalisé
Micro-python n'implante pas le port série pour les atxmega. Il faut donc le faire avec l'exemple VirtualSerial de la Lufa.
Virtual Serial
Le premier objectif de cette épreuve complémentaire a été de se familiariser avec la bibliothèque Lufa et plus particulièrement avec la demo Virtual Serial.
Le téléchargement de la bibliothèque Lufa s'effectue de la façon suivante :
git clone https://github.com/abcminiuser/lufa
Tout d'abord il a fallu modifier le fichier Descriptor.c avec les valeurs d'ID Vendor et d'ID Product de ma carte.
Ces valeurs sont accessible en utilisant la commande suivante ( en s'étant assurer au préalable d'avoir mis la carte en dfu-mode en appuyant sur le Bouton 0 et en branchant la carte au même moment ) :
dmesg -s 1024
La première chose à faire a été de modifier le Makefile :
Les 3 premières lignes correspondent au micro-contrôleur sur la carte. Puis il a fallu changer la fréquence. Ce changement de fréquence a été très important pour résoudre certain problème, lors du lancement du .hex sur l'atmega384c3, lorsque j'utilisais des fréquences trop faible. Puis j'ai créé un point upload dans le makefile afin de lancer les 3 commandes suivantes :
dfu-programmer atxmega384c3 erase dfu-programmer atxmega384c3 flash VirtualSerial.hex dfu-programmer atxmega384c3 reset
De manière automatique en utilisant la commande suivante :
make upload
Ensuite, une fois la carte débranché et rebranché, il faut vérifier la présence d'un nouveau port série ACM0 grâce à la commande
ls -l /dev/tty*
Puis j'ai créé une petite fonction Bonton() afin de tester la carte dans minicom
Une fois la fonction créé et le make upload lancé, j'ai lancé minicom afin de voir le résultat
Lors de l'appui d'un bouton, nous pouvons voir l'affichage "Bouton n" en fonction du bouton appuyé
Avr8-dummy
Tout d'abord, il a fallu télécharger les répertoires micropython et avr8-dummy
git clone https://github.com/micropython/micropython git clone https://github.com/slavaza/micropython-avr8/tree/master/ports/avr8-dummy
Puis il a fallu déplacer le répertoire avr8-dummy dans ports à l'intérieur du répertoire micropython
La fonction main dans le répertoire avr8-dummy permet de générer un REPL avec un prompt en micropython
Comme précisé précédemment, Micro-python n'implante pas le port série pour les atxmega. C'est pourquoi nous allons utiliser la demo VirtualSerial de la bibliothèque Lufa utilisé juste avant, afin d'implanter le port série pour l'atxmega.
Fusion de la demo Virtual Serial avec micropython
Afin de réaliser la fusion, il a fallu dans un premier temps pouvoir lancer le makefile de la demo Virtual Serial à partir du makefile de micropython. Le makefile de micropython étant donc le makefile principal que l'on lancera par la suite.
Pour ce faire, on ajoute cela au makefile de micropython :
LUFA_PROJECT = ./LUFA/SerialUSB $(LUFA_PROJECT): $(MAKE) -C $(LUFA_PROJECT)
Puis on met à l'intérieur du makefile de VirtualSerial :
first: $(TARGET).a mv $(TARGET).a lib$(TARGET).a
Ce makefile va donc compiler les fichiers .c et stocker à l'intérieur d'une librairie .a tous les .o créé.
Une fois cela effectué, il a fallu regrouper les fonctions principales à nos souhaits (VirtualSerial + Micropython) dans le main.c
Pour ce faire, je me suis concentré dans un premier temps sur le fonctionnement de virtualSerial dans le main.c de la fusion.
void LUFA_USB_Initialization(void) { /* Start the PLL to multiply the 2MHz RC oscillator to 32MHz and switch the CPU core to run from it */ XMEGACLK_StartPLL(CLOCK_SRC_INT_RC2MHZ, 2000000, F_CPU); XMEGACLK_SetCPUClockSource(CLOCK_SRC_PLL); /* Start the 32MHz internal RC oscillator and start the DFLL to increase it to 48MHz using the USB SOF as a reference */ XMEGACLK_StartInternalOscillator(CLOCK_SRC_INT_RC32MHZ); XMEGACLK_StartDFLL(CLOCK_SRC_INT_RC32MHZ, DFLL_REF_INT_USBSOF, F_USB); PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm; /* Hardware Initialization */ USB_Init(); GlobalInterruptEnable(); }
void LUFA_USB_Handling(void){ CDC_Device_USBTask(&VirtualSerial_CDC_Interface); USB_USBTask(); }
Ces deux fonctions sont définis dans le fichier VirtualSerial.c compilé par le makefile de VirtualSerial. Puis j'ai inclu ces fonctions dans le fichier VirtualSerialPublic.h, permettant de récupérer et d'utiliser ces fonctions dans le main.c.
On ajoute donc la ligne suivante afin d'utiliser ces fonctions dans le main.c
#include "LUFA/SerialUSB/VirtualSerialPublic.h"
Puis la fonction main sera donc :
int main(){ LUFA_USB_Initialization(); for(;;){ LUFA_USB_Handling(); } }
On obtient donc une création du port série ACM0 comme précédemment en lançant le makefile :
cd micropython/ports/atxmega-cdc/ make clean make all
Une fois cela réalisé, on implémente la fonction main des fonctions pour générer micropython
static char *stack_top; static char heap[2048]; int main(int argc, char **argv) { usb_vcp_init0(); int stack_dummy; stack_top = (char*)&stack_dummy; #if MICROPY_ENABLE_GC gc_init(heap, heap + sizeof(heap)); #endif mp_init(); #if MICROPY_ENABLE_COMPILER #if MICROPY_REPL_EVENT_DRIVEN pyexec_event_repl_init(); int c=-1; for (;;) { usb_vcp_handling(); c=mp_hal_stdin_rx_chr(); if(c>0){ pyexec_event_repl_process_char(c); } } #else pyexec_friendly_repl(); #endif //do_str("print('hello world!', list(x+1 for x in range(10)), end='eol\\n')", MP_PARSE_SINGLE_INPUT); //do_str("for i in range(10):\r\n print(i)", MP_PARSE_FILE_INPUT); #else pyexec_frozen_module("frozentest.py"); #endif mp_deinit(); }
Les fonctions de la LUFA (implémenté grâce au #include "LUFA/SerialUSB/VirtualSerialPublic.h") ont été regroupé dans un fichier usb.c pour rendre le main plus propre.
Une des premières erreurs auquel j'ai été confronté, a été la réception d'un caractère du port série. Utilisant de base la fonction suivante :
uint8_t LUFA_USB_GetByte(void){ while(CDC_Device_BytesReceived(&VirtualSerial_CDC_Interface)<=0); return CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface); }
Cependant le processeur passait beaucoup de temps dans celle-ci empéchant par conséquent le processeur à gérer les événements USB donc la création du port série ACM0. La solution a été de retirer le while() tournant à l'infini en attendant un caractère.
Une fois cela résolu, un deuxième problème a été la réception du caractère et l'affichage de celui-ci sur le port série. En utilisant minicom sur le port ACM0, on se rend bien compte que les fonctions micropython déjà créé dans le répertoire avr8-dummy initialement installé sont bien utilisé car on se rend bien compte de la compréhension par le port série de certaine commande utilisé (configuré dans le fichier /micropython/lib/utils/pyexec.c). On a donc logiquement un REPL micropython cependant les fonctions de réception et d'affichage de caractère ne sont pas bonnes