IMA4 2019/2020 EC2

De Wiki d'activités IMA

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

ATXMEGAC3-XPLD

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.

Descriptor

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
ID Vendor et ID Product

La deuxième chose à faire a été de modifier le Makefile :

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

Fonction Bouton

Une fois la fonction créé et le make upload lancé, j'ai lancé minicom afin de voir le résultat

Minicom

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

Puis en lançant le fichier.hex sur l'atxmega :

cd build/
avr-objcopy -O ihex firmware.elf firmware.hex
dfu-programmer atxmega384c3 erase
dfu-programmer atxmega384c3 flash VirtualSerial.hex
dfu-programmer atxmega384c3 reset

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();
   for (;;) {
       usb_vcp_handling();    
       int c=mp_hal_stdin_rx_chr();
       if(c>0){
           if (pyexec_event_repl_process_char(c)) {
               break;
           }
       }
   }
   #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();
   return 0;
}

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 erreurs auquelles 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. On a donc :

uint16_t LUFA_USB_GetByte(void){
     if(CDC_Device_BytesReceived(&VirtualSerial_CDC_Interface)>0){
         return CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);}
     else{return -1;}
}

Une fois ce problème résolu, j'ai pu réaliser un test du REPL micropython sur minicom.

minicom ACM0
REPL micropython

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 il persiste un problème au niveau du résultat de la fonction. On peut voir que la commande print et la ligne avec une simple addition sont bien reçu, cependant il n'y a pas d'affichage du résultat de celle-ci.

Quelques réglages ont été nécessaire au niveau du fichier mpconfigport.h afin de résoudre ce problème

modification mpconfigport.h

On obtient donc un REPL Micropython avec un retour des différentes fonctions rentrées.

REPL micropython

Cependant il persiste un problème sans solutions pour l'instant. Il arrive que lors de l'utilisation d'une commande, le résultat ne s'affiche pas (voir photo ci-dessus) et il faut par conséquent réutiliser la commande.

Résultat Final

On obtient donc un REPL micropython sur le port série créé grâce à la demo virtual Serial de la LUFA fusionné au code micropython.

Documents

La taille du fichier micropython complet étant trop grand, je vous joins seulement le fichier créé pour notre atxmega dans le répertoire ports de micropython.

Fichier:Atxmega-cdc.zip