samedi 9 décembre 2017

Busy Bee partie 3, PCA suite

Je poursuis mes expériences avec le PCA Programmable Counter Array du EFM8BB10F8G-A-SOIC6 débuté dans la partie 2 de cette série. J'en suis rendu au mode high speed output.

high speed output

D'emblée je dois dire que la documentation fournie par Silicon Labs laisse à désirer. Voici tout ce qu'a à dire le manuel de référence à propos de ce mode de fonctionnement.
À partir de cette information insuffisante j'ai eu de la difficulté à faire fonctionner ce mode malgré mes recherches sur le site de Silicon labs qui ne m'ont pas permis de trouver un exemple d'application spécifique à ce MCU mais seulement la note applicative AN107 qui concerne les MCU C8051Fxxxx. Après de nombreux essais et erreurs j'ai réussi à faire fonctionner le mode high speed output pour faire du PWM 16 bits. Pour cette expérience j'ai utilisé le même montage que pour la partie 1. Dans cette expérience l'intensité de la LED varie selon une fonction triangulaire sur une période de 2 secondes.

Comme on le voie sur le diagramme bloc ci-dessous ce mode utilise un compteur 16 bits constitué de PCA0H:PCA0L et d'un registre de comparaison de 16 bits constitué de PCA0CPHn:PCA0CPLn. Lorsque le compteur atteint la valeur de comparaison la sortie CEXn est inversée. Le compteur PCA0 marche en boucle de 0 à 65535. Donc la période est fixée par la fréquence Fpca qui alimente le compteur PCA0. Si on configure Fsys à la fréquence maximale de 24,5Mhz et qu'on alimente le compteur PCA0 directement à cette fréquence,la fréquence du cycle PWM sera de 24,5e6/65536=373,8Hertz.

Pour faire du PWM 16 bits avec ce mode il faut activer l'interruption sur débordement du Compteur PCA0 et celle du comparateur et la l'intérieur de la routine d'interruption modifier la valeur de comparaison dans PCA0CP. On va garder le même projet que dans la partie 2 mais en retournant dans le configurateur et on va modifier la configuration pour obtenir les valeurs suivantes.

  • clock à la fréquence maximale pour obtenir un Fsys de 24,5Mhz.
  • PCA0 alimenté par Fsys ce qui va nous donner une fréquence PWM de 24,5e6/65536=373,8Hertz. On va activé l'interruption sur débordement du compteur PCA0
  • PCA0, Channel 0 configuré pour le high speed output avec activation de l'interruption sur le comparateur. La sortie est aussi inversée car la broche est configurée en open drain avec la cathode de la LED sur la broche P1.3. Si la sortie n'est pas inversée l'intensitée sera inversement proportionnelle à la valeur de PCA0CP0 et non proportionnelle mais ça va fonctionner quand même.
  • .

On ferme le configurateur pour mettre à jours les fichiers et on va ajouter quelques lignes de codes.

  • Dans le fichier InitDevice.h on va ajouter les constantes suivantes. La valeur de PWM_MIN a été déterminée par essaie et erreur et dépend de la durée de l'interruption. Plus l'interruption met de temps à s'exécuter plus cette valeur est grande.
  • Dans le fichier Interrupts.c on va ajouter du code dans la routine d'interruption générée par le configurateur.
  • Finalement on va ajouter le code pour contrôler l'intensité de la LED dans la fonction main() du fichier pca_experiments_main.c
    On met les instructions à l'intérieur de la boucle while parce que à chaque interruption le MCU sort du mode idle et que c'est à ce moment qu'on modifie l'intensité avant de remettre le MCU en idle.

Fonctionnement

Commençons par la fonction main(). le code tourne indéfiniment dans une boucle while(1){}. La première instruction de la boucle suspend l'exécution de code en mettant le MCU en mode idle. Lorsqu'une interruption se produit le MCU sort du mode idle et lorsque l'interruption se termine le CPU exécute le code qui suis l'instruction PCON0|=1. Ce code débute par un verrou lock. lock est ce qu'on appelle en informatique une variable mutex (i.e. mutually exclusive). Sont rôle est d'empêcher la routine d'interruption d'accéder la variable pwm_val lorsque la routine main() est en train de la modifier. Comme il s'agit d'une variable 16 bits il faut plus d'une instruction machine pour modifier cette variable. Imaginez qu'une interruption se produit entre le moment où la fonction main() a modifié l'octet faible de pwm_val et celui où elle modifie l'octet fort de pwm_val. Si l'interruption fait une lecture de pwm_val la valeur lue sera erronée. Donc avant de lire pwm_val l'interruption vérifie la valeur de la variable lock et ne lie la variable que si lock est à zéro. Ainsi on est certain que la valeur lue est bonne. Donc la variable lock est mise à 1 avant de débuter le bloc d'instructions qui va modifier la valeur de pwm_val qui contrôle l'intensité de la LED. Elle est remise à zéro lorsque la modification est complétée.

À chaque interruption la fonction main() modifie la valeur de pwm_val en ajoutant ou soustrayant la valeur step qui contrôle la vitesse du cycle fade in, fade out. Plus step est grand plus la LED va varier d'intensité rapidement. Dans ce démo le cycle est régler sur 2 secondes. La fonction main() s'assure que la valeur de pwm_val demeure à l'intérieur des limites valides.

Le rôle de la routine d'interruption

Le compteur PCA0 compte en boucle de 0 à 65535. À chaque débordement de PCA0 ainsi que lorsque PCA0==PCA0CP0 l'interruption SI_INTERRUPT (PCA0_ISR, PCA0_IRQn) est déclenchée. S'il s'agit d'une interruption causée par le débordement du compteur PCA0, c'est la première moitié du cycle PWM. On vérifie si la variable pwm_val est disponible. Si c'est le cas on réinitialise la variable locale brightness avec la valeur de pwm_val. Ensuite on affecte la valeur de brightness au registre PCA0CP0 qui détermine l'intensité de la LED. Par contre si l'interruption est causée par PCA0==PCA0CP0,c'est la deuxième moitié du cycle PWM. Alors PCA0CP0 est mis à 0 pour compléter le cycle en inversant à nouveau la sortie P1.3 lorsque le compteur va revenir à zéro par débordement.

Notez que PWM_MIN ne peut être inférieur au délais de réponse de l'interruption car pendant ce délais le compteur PCA0 continue à avancé. Si on initialise PCA0CP avec une valeur trop petite le compteur aura déjà dépassé cette valeur lorsque PCACP0 sera initialisé et le comparateur ne déclenchera pas l'inversion de la sortie. Ce raisonnement vaut aussi pour la valeur PWM_MAX qui est le complément à 1 de PWM_MIN.

Générateur de fréquence avec diviseur 16 bits

Il est possible d'utiliser le mode high speed output pour générer des fréquences. On retourne dans le configurateur pour désactiver l'interruption sur le débordement du compteur PCA0.


On remplace le code dans la routine d'interruption par ceci

Le principe de fonctionnement est le même que pour le générateur de fréquence de la partie 2 sauf que la mise à jour de PCA0CP doit-être faite manuellement dans la routine d'interruption. Ce qui implique encore une fois une valeur minimum pour FRQ_DIV. Par essaie erreur j'obtiens 48 pour cette valeur minimum. Avec le compteur PCA0 alimenté à 24,5Mhz ça donne une fréquence maximale de 24,5Mhz/2/48=255Khz la valeur de fréquence minimale est de 24,5Mhz/2/65535=187Hertz. La granularité, c'est à dire la variation de fréquence entre 2 diviseurs adjacents, est inversement proportionnelle à FRQ_DIV.

Gestion de la consommation

Tous les MCU possèdent un mécanisme de gestion de la consommation électrique. Les EFM8BB1xxx ne font pas exceptions. Quelques explications sur les modes disponibles. Il y en a 4.

  1. Normal, dans ce mode le CPU et les périphériques activés fonctionnent normalement. C'est le maximum de consommation électrique. On peut réduire la consommation en réduisant la fréquence de l'horloge.
  2. Idle, dans ce mode le CPU est désactivé donc aucune instructions n'est exécutée. Par contre les périphériques continues à fonctionner comme ils ont été configurés. On économise donc l'énergie dépensée par le CPU. Ce mode est utilisé dans les applications pilotées par interruption. Sitôt qu'une interruption est déclenchée le CPU se remet en fonction pour exécuter le code de l'interruption. À la fin de l'interruption il ne retombe pas en mode idle il faut l'y remettre. L'exemple ci-haut est un exemple de ce mode de fonctionnement. La dépense d'énergie est principalement celle causée par les périphériques. Dans ce mode la mémoire RAM est conservée et les états du CPU sont conservés. A la sortie de l'interruption c'est l'instruction qui suis celle qui a mis le CPU en idle qui est exécutée.
  3. stop, arrête tous les signaux d'horloges. Donc le CPU et les périphériques nécessitant un signal d'horloge sont désactivés. Par contre les entrés/sorties demeure dans leur état. Donc si une LED branchée sur une broche est allumée elle demeure allumée. Les comparateurs analogiques continus de fonctionner et peuvent-être configurer pour déclencher un reset. La seule façon de sortir de ce mode est par un reset ou une remise sous tension. Notez que si la détection missing clock est activée elle va générer un reset après son délais normal et redémarrer le MCU. Pour garder le MCU en mode stop il faut donc que la détection missing clock soit désactivée. Lorsque le MCU sort de ce mode il exécute le code à partir de l'adresse 0x0000 comme s'il venait juste d'être mis sous tension.
  4. shutdown, semblable au mode stop mais en plus le régulateur de tension du CPU est désactivé. Consommation minimale assurée. Le CPU fonctionne à 1,8 volt et puisque le MCU doit-être alimenté avec une tension entre 2,2 et 3,6 volt il faut un régulation de tension interne fournissant 1,8 volt au CPU.
La figure suivante montre par code de couleur de qui est actif dans chaque mode.

Il n'y a que 2 registres pour gérer la consommation
  • PCON0, Contient les bits IDLE et STOP. Les 6 autres bits peuvent-être utilisés par les application comme indicateurs booléens et sont nommés GF0 à GF5.
  • REGCN0, Il n'y a qu'un seul bit d'utilisé dans ce registre STOPCF. Si ce bit est mis à 1 avant de mettre le bit STOP à 1 alors le MCU va passé en mode SHUTDOWN et seule la broche ~reset ou un cyclage de l'alimentation pourra le redémarrer.