Les expériences et les conseils ATXMEGA

Système analogique

    Pour les détecteurs d'occupation, le courant de leur section respective circule dans une résistance et la tension est limitée par deux diodes Schottky tête-bêche. La chute de tension résultante dans la zone de -0,4 V à +0,4 V est utilisée pour la mesure.
    L'ATXMEGA propose un Convertisseur Analogique-Numérique, qui peut aussi mesurer les tensions «négatives». Voir le test ci-après:
  • Réglages:
      2 MHz fréquence d'échantillonnage
      Mesure simple, mode signé, résolution 12 bits
      Référence Bandgap interne avec 1V, Gain 1
      puce alimentée en 3V3 régulateur linéaire
      tension mesurée fournie par une alimentation de laboratoire (längsgeregeltem) via 1k
      Contrôler la tension avec DVM 4-1/2 chiffres.
  • Résultats:
      Bruit: environ 4 chiffres, soit 2mV.
      Plage de mesure: 0 .. 1V, puis le transducteur va proprement en saturation avec le code 2047;
      DC offset environ 3 mV, la non-linéarité environ 3 mV.
     Tensions négatives: plage de mesure linéaire: 0 .. -340mV, puis, le convertisseur n'est plus linéaire et sature à environ -400 mV

Optimisations spéciales

Minuteur (Timer)

    A première vue tous les compteurs ATXMEGA sont identiques, tous peuvent compter sur: 8 bits, 16 bits, tant pour les registres de comparaison que pour ceux de capture etc..., En outre, un certain nombre de choses sont installés, ce qui a causé toute un émoi à ce jour:
  • Quand un mode de génération de forme d'onde est sélectionnée, vous pouvez toujours accéder aux broches - et utilisable soit comme un pont en H, soit en PWM croissant ou décroissant, (par exemple pour la découpe de DCC BiDi).
  • Il ya un registre supplémentaire pour déclencher des événements de minuterie. Vous pouvez redémarrer le chronomètre, provoquant une mise à jour (par exemple multiple chargement parallèle du "Compare register") via ce registre.

XMEGA et DCC

Décodage DCC

    Le XMEGA dispose de 8 lignes d'événements internes, vous pouvez librement définir qui envoie et qui reçoit. Ceci peut être utilisé pour décoder DCC élégamment:
  • L'entrée DCC est programmée de telle sorte que XMEGA répondra à un front (montant ou descendant). Cet événement est défini à la ligne d'événement.
  • Un minuteur libre est programmé, la sortie est comparée à la période de 87μs (DCC = Period_1 * 0,75 = programmé). Dans le registre de correspondance événements actions, 'restart' est ajouté à l'événement ci-dessus.
    Ainsi le minuteur est réinitialisé au changement de front du signal de DCC, et exactement après 87 µs il y aura un déclenchement sur compare.
  • Maintenant, on peut ajouter une routine d'interruption et et lire le port ou déclencher de nouveau un Event qui provoque la lecture par le DMA et stocke en mémoire.
  • Les événements permettent un timing très précis et l'exécution des opérations sans interaction CPU.

Génération du signal DCC

    Avec DCC chaque bit est codé par deux états de conduction (pour une impulsion positive, une impulsion négative), un 1 est dans ce cas représenté par 58μs haut + 58μs bas, un 0 est codé par 116μs haut puis 116μs bas.

    Il est possible d'utiliser différentes approches pour générer ce signal, celle présentée ci-après ne nécessite pas des interruptions au sens stricte et ne provoque qu'une très faible charge CPU.

    Un timer utilisé en mode fréquence: ici le compteur compte jusqu'à une valeur finale réglable et cette valeur atteinte il recommence. A chaque valeur finale le signal de sortie change de polarité. Si on écrit cette valeur finale à chaque tour de compteur, on peut produire n'importe quelles chaînes d'impulsion. On fera de préférence l'écriture avec le contrôleur DMA. Veuillez noter que le mode de fréquence est disponible uniquement sur le Compareregister A et sa sortie correspondante.

    Commençons avec la minuterie. Le pré-diviseur est fixé à 64, qui produit une trame temporaire de 2 µs à 32 MHz d'horloge. En outre, on positionne le minuteur sur le mode d'octet, seulement 8 bits sont nécessaires pour couvrir toutes les besoins, le DMA n'a alors besoin que d'un octet de rechargement.

          static void init_timer_d0(void)
            {
              TCD0.CNT = 0;
              TCD0.PER = 0;
              TCD0.CCA = TIME_DCC_HALF1;
              TCD0.CTRLE = (1 << TC0_BYTEM_bp);       // Byte mode: 1 run as 8-bit counter
              TCD0.CTRLA = ( TCD0.CTRLA & ~TC0_CLKSEL_gm ) | TC_CLKSEL_DIV64_gc;
              TCD0.CTRLB = (0 << TC0_CCDEN_bp)
                         | (0 << TC0_CCCEN_bp)
                         | (0 << TC0_CCBEN_bp)
                         | (1 << TC0_CCAEN_bp)        // enable A (dcc)
                         | TC_WGMODE_FRQ_gc;
              TCD0.INTCTRLA = ( TCD0.INTCTRLA & ~TC0_OVFINTLVL_gm ) | TC_OVFINTLVL_OFF_gc;
            }



    Ensuite, vous devez programmer le contrôleur DMA. Ici, les canaux 2 et 3 sont utilisées, et le DBUFMODE est activé: après la transmission du canal 2 le contrôleur de gestion DMA change automatiquement sur le canal 3 et vice versa. Un octet est transmis (nous savons que le minuteur fonctionne sur le mode octet courent), SRC est incrémenté, DEST reste toujours constant. Le rechargement des pointeurs SRC doit être fait par bloc (pour nous c'est un message DCC).

         static void init_dma(void)
            {
              DMA.CTRL = (1 << DMA_ENABLE_bp)
                       | (0 << DMA_RESET_bp)
                       | DMA_PRIMODE_CH0123_gc
                       | DMA_DBUFMODE_CH23_gc;
              DMA.CH2.REPCNT = 0;                        // run forever
    
              DMA.CH2.CTRLA = (0 << DMA_CH_ENABLE_bp)    //  Channel Enable
                            | (0 << DMA_CH_RESET_bp)   // Channel Software Reset
                            | (1 << DMA_CH_REPEAT_bp)   // Channel Repeat Mode bit position.
                            | (0 << DMA_CH_TRFREQ_bp)   // Channel Transfer Request bit position.
                            | (1 << DMA_CH_SINGLE_bp)   // Channel Single Shot Data Transfer bit position.
                            | DMA_CH_BURSTLEN_1BYTE_gc;
              DMA.CH2.CTRLB = DMA_CH_ERRINTLVL_OFF_gc
                            | DMA_CH_TRNINTLVL_LO_gc;  // transaction complete
    
              DMA.CH2.ADDRCTRL = DMA_CH_SRCRELOAD_BLOCK_gc  // Source address reload mode : _NONE, BLOCK, BURST, TRANSACTION
                               | DMA_CH_SRCDIR_INC_gc       // Source addressing mode: FIXED, INC, DEC
                               | DMA_CH_DESTRELOAD_NONE_gc  // Destination adress reload mode: NONE, BLOCK, BURST, TRANSACTION
                               | DMA_CH_DESTDIR_FIXED_gc;   // Destination adressing mode: FIXED, INC, DEC
    
              DMA.CH2.TRIGSRC = DMA_CH_TRIGSRC_TCD0_CCA_gc;   // Counter D, comp. A
    
              src.as_uint32 = dcc_buffer_0;
              DMA.CH2.SRCADDR0 = src.low8;
              DMA.CH2.SRCADDR1 = src.high8;
              DMA.CH2.SRCADDR2 = src.upper8;
    
              dest.as_uint32 = &TCD0.CCAL;
              DMA.CH2.DESTADDR0 = dest.low8;
              DMA.CH2.DESTADDR1 = dest.high8;
              DMA.CH2.DESTADDR2 = dest.upper8;
    
              DMA.CH3.REPCNT = 0;
    
              DMA.CH3.CTRLA = (0 << DMA_CH_ENABLE_bp)    //  Channel Enable
                            | (0 << DMA_CH_RESET_bp)     // Channel Software Reset
                            | (1 << DMA_CH_REPEAT_bp)   // Channel Repeat Mode bit position.
                            | (0 << DMA_CH_TRFREQ_bp)   // Channel Transfer Request bit position.
                            | (1 << DMA_CH_SINGLE_bp)   // Channel Single Shot Data Transfer bit position.
                            | DMA_CH_BURSTLEN_1BYTE_gc;
              DMA.CH3.CTRLB = DMA_CH_ERRINTLVL_OFF_gc
                            | DMA_CH_TRNINTLVL_LO_gc;  // transaction complete
    
              DMA.CH3.ADDRCTRL = DMA_CH_SRCRELOAD_BLOCK_gc  // Source address reload mode : _NONE, BLOCK, BURST, TRANSACTION
                               | DMA_CH_SRCDIR_INC_gc       // Source addressing mode: FIXED, INC, DEC
                               | DMA_CH_DESTRELOAD_NONE_gc  // Destination adress reload mode: NONE, BLOCK, BURST, TRANSACTION
                               | DMA_CH_DESTDIR_FIXED_gc;   // Destination adressing mode: FIXED, INC, DEC
    
              DMA.CH3.TRIGSRC = DMA_CH_TRIGSRC_TCD0_CCA_gc;   // Counter D, comp. A
    
              src.as_uint32 = dcc_buffer_1;
              DMA.CH3.SRCADDR0 = src.low8;
              DMA.CH3.SRCADDR1 = src.high8;
              DMA.CH3.SRCADDR2 = src.upper8;
    
              dest.as_uint32 = &TCD0.CCAL;
              DMA.CH3.DESTADDR0 = dest.low8;
              DMA.CH3.DESTADDR1 = dest.high8;
              DMA.CH3.DESTADDR2 = dest.upper8;
    
              dma_active_channel = 2;
    
              // and fire the DMA (we start with channel 2)
    
              DMA.CH2.CTRLA = (1 << DMA_CH_ENABLE_bp)     //  Channel Enable
                            | (0 << DMA_CH_RESET_bp)      // Channel Software Reset
                            | (1 << DMA_CH_REPEAT_bp)     // Channel Repeat Mode
                            | (0 << DMA_CH_TRFREQ_bp)     // Channel Transfer Request
                            | (1 << DMA_CH_SINGLE_bp)     // Channel Single Shot Data Transfer
                            | DMA_CH_BURSTLEN_1BYTE_gc;
            }


    Quand un cycle DMA est terminée, une interruption se produit. Dans cette interruption, le pointeur est avancé au message de DCC actif à un et également déclenche un nouveau calcul de la forme d'onde pour le prochain message DCC.

          ISR(DMA_CH2_vect)
            {
              DMA.INTFLAGS = DMA_CH2ERRIF_bm  | DMA_CH2TRNIF_bm;
    
              dma_active_channel = 3;
              actual_index++;
              actual_index &= 0x3;  // modulo 4
              isr_set_task_ready(TASK_DCC_GEN);
            }
    
          ISR(DMA_CH3_vect)
            {
              DMA.INTFLAGS = DMA_CH3ERRIF_bm  | DMA_CH3TRNIF_bm;
    
              dma_active_channel = 2;
              actual_index++;
              actual_index &= 0x3;  // modulo 4
              isr_set_task_ready(TASK_DCC_GEN);
            }



    La tâche partagée DMA-ISR (Interrupt Sub Routine) calcule ensuite le train d'impulsions pour le signal DCC suivant:

       #define WRITE_1 {*dcc_buffer++ = TIME_DCC_HALF1;*dcc_buffer++ = TIME_DCC_HALF1;}
       #define WRITE_0 {*dcc_buffer++ = TIME_DCC_HALF0;*dcc_buffer++ = TIME_DCC_HALF0;}
    
       static void prepare_dma_buffer(t_dcc_message* dcc_message)
         {
           uint16_t size; uint8_t* dcc_buffer;
           uint8_t i,prebits; uint8_t my_xor = 0; // dcc checksum
    
           if (dma_active_channel == 2) dcc_buffer = dcc_buffer_1;
           else dcc_buffer = dcc_buffer_0;
    
           // ----- now fill this buffer with timing data
    
           if (dcc_message->type == is_prog) prebits = MAX_PREAMBLE_BITS;
           else prebits = NORMAL_PREAMBLE_BITS;
    
           for (i=0; i<prebits; i++)  {  WRITE_1; }                // preamble
           WRITE_0;                                                    // dcc starts
           for (i=0; i<dcc_message->size; i++)
             {
               uint8_t current_byte = dcc_message->dcc[i];
               my_xor ^= current_byte;
               for (uint8_t mask=0x80; mask; mask >>= 1)  // msb first
                 {
                   if (current_byte & mask) { WRITE_1; }
                   else  { WRITE_0; }
                 }
               WRITE_0;                                                // byte delimiter
             }
           for (uint8_t mask=0x80; mask; mask >>= 1)
             {
               if (my_xor & mask) { WRITE_1; }
               else  { WRITE_0; }
             }
           WRITE_1;                                                    // packet end bit
    
           if (dma_active_channel == 2)                              // ---- load to isr / dma
             {
               size = dcc_buffer - dcc_buffer_1;
               DMA.CH3.TRFCNT = size;
             }
    
           else
             {
               size = dcc_buffer - dcc_buffer_0;
               DMA.CH2.TRFCNT = size;
             }
         }

     

    Dans cette approche la charge liée à l'interruption est de l'ordre de 10μs toutes les 6 ms, et comme calculer les valeurs de du message DCC prend environ 40μs (à 32MHz ATXMEGA128A1), ce générateur DCC entraîne une charge de moins de 1%.

Xmega et la Génération DMX

    DMX 512 est un protocole très simple pour contrôler les gradateurs.Il s'agit d'un RS485 / RS422 avec 250kBaud, 8 bits, pas de parité, deux bits d'arrêt (8N2). Il a la structure suivante:

  • Début de trame: il est envoyé une pause (c.-à-dire TX =niveau bas) pour la durée d'au moins 88us.
  • suivi une marque après la pause (c.-à-dire TX =niveau haut) pour une période d'au moins 8us.
  • Octet de début de la transmission de données normale est envoyé 0x00.
  • Boucle sur tous les octets DMX, où chaque octet est attribué à un seul canal: le premier octet - premier canal, le deuxième octet - deuxième canal, etc Les données ne sont pas ni codées ou ni garanties, transfert possible de max. 512 octets.
  • Ce n'est pas très excitant et peut être fait avec l'UART. Seule la génération des pauses au début de la trame ne peut pas être générée aisément avec l'UART. Pour cette génération vous déconnecter le port de l'UART, régler le niveau et attendre activement au moyen de delay_us () ou utiliser le truc suivant:

    Pour générer le "break" la vitesse de transmission est réglée plus lente à 57600 bauds et une donnée à 0xC0 est envoyée: avec 57600 un bit dure 17uS, la rupture résultante dure 121us: bit d'arrêt et 6 LSB.

    Sortie DMX
    Début: Régler UART 57600 bauds.
    définir UART.DATA avec 0xC0.
    Débloquer interruption TXC (TX-complet)
    ISR(TXC): Configuration UART à 250.000.
    Mettre le compteur = 0.
    définir UART.DATA avec0x00.
    Désactiver interruption TXC, libérer DRE-Interrupt (registre de données vide)
    ISR(DRE): définir UART.DATA avec dmx[compteur].
    incrémenter le compteur.
    Quand la fin est atteinte: Bloquer DRE-Interrupt.
    Avec cette méthode, l'appel àl'opération de démarrage émet les données complète DMX. Exemple de code pour générer le DMX512 ATXMEGA sur le côté du projet OneDMX . L'exemple de code pour produire le DMX512 avec l'Atxmega est sur la page OneDMX-Projekt.

Stockage de données en Flash avec ATXMEGA

    Le stockage permanent des données en flash est un peu délicat: seul le bootloader peut écrire dans la mémoire (l'instruction SPM est exécutée à partir de cette zone de boot), on a donc besoin du code correspondant dans le chargeur de démarrage. On doit démarrer ce code, , exécuter de là le processus d'écriture et revenir de nouveau.
    Il faut noter que la zone d'amorçage est au-delà de la portée des appels normaux ou de l'accès par pointeur: à la fois pour l'appel et l'accès aux données des dispositions sont à prendre. (programmer en conséquence RAMPZ, EIND). Par ailleurs, dans une installation typique, un chargeur de démarrage se trouve dans la zone d'amorçage , c'est à dire au cours de mise à jour firmware est exclu de cette zone, C.-à-d. dans la mise à jour du firmware, on exclut ce domaine, en ajoutant -R .boot aux HEXFLAGS du Linkers dans makefile.

    C.-à-d. on est nettement là dans le domaine du boot, et cependant on ne le peut pas en principe... J'ai résolu en sortant la fonction 'écriture Flash' du chargeur de démarrage ,(Section .BOOT du sp_driver.c) qui est lié à une adresse fixe (0x10Ef0), puis démarre aussi l'adresse de l'application . (Avec set EIND-bit).
    Pour que je ne saute pas dans le nirvana et laisse tomber le processeur, j'examine dès le départ du programme, si le code approprié se trouve sur la place auquel je veux sauter. S'il n'y a rien, les accès mémoire flash en écriture sont bloqués à partir de l'application.

    La prochaine chose à noter est que par EIND et RAMPZ un adressage normal du CCG reprend en suivant, ici rationnellement, des barrages d'interruption sont devant la procédure de mémoire et re-libération.