Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

В la première partie J'ai essayé de dire aux ingénieurs électroniciens amateurs qui ont grandi avec les pantalons Arduino comment et pourquoi ils devraient lire les fiches techniques et autres documentations sur les microcontrôleurs. Le texte s'est avéré volumineux, j'ai donc promis de montrer des exemples pratiques dans un article séparé. Eh bien, il s'appelait lui-même un champignon de lait...

Aujourd'hui, je vais vous montrer comment utiliser des fiches techniques pour résoudre des tâches assez simples, mais nécessaires à de nombreux projets, sur les contrôleurs STM32 (Blue Pill) et STM8. Tous les projets de démonstration sont dédiés à mes LED préférées, nous les allumerons en grande quantité, pour lesquelles nous devrons utiliser toutes sortes de périphériques intéressants.

Le texte s'est encore avéré énorme, donc pour plus de commodité, je crée le contenu :

STM32 Blue Pill : 16 LED avec driver DM634
STM8 : configuration de six broches PWM
STM8 : 8 LED RVB sur trois broches, interruptions

Disclaimer : je ne suis pas ingénieur, je ne prétends pas avoir des connaissances approfondies en électronique, l'article s'adresse aux amateurs comme moi. En fait, je me considérais il y a deux ans comme le public cible. Si quelqu'un m'avait dit alors que les fiches techniques d'une puce inconnue ne faisaient pas peur à lire, je n'aurais pas passé beaucoup de temps à chercher des morceaux de code sur Internet et à inventer des béquilles avec des ciseaux et du ruban adhésif.

Cet article se concentre sur les fiches techniques et non sur les projets. Le code peut donc ne pas être très soigné et souvent à l'étroit. Les projets eux-mêmes sont très simples, bien que adaptés à une première connaissance de la nouvelle puce.

J'espère que mon article aidera quelqu'un à un stade similaire d'immersion dans le hobby.

STM32

16 LED avec DM634 et SPI

Un petit projet utilisant Blue Pill (STM32F103C8T6) et un driver LED DM634. À l'aide de fiches techniques, nous déterminerons le pilote, les ports STM IO et configurerons SPI.

DM634

Puce taïwanaise avec 16 sorties PWM 16 bits, pouvant être connectée en chaînes. Le modèle 12 bits bas de gamme est connu d'un projet national Pack de lumière. À un moment donné, choisissant entre le DM63x et le bien connu TLC5940, j'ai choisi DM pour plusieurs raisons : 1) TLC sur Aliexpress est définitivement faux, mais celui-ci ne l'est pas ; 2) DM dispose d'un PWM autonome avec son propre générateur de fréquence ; 3) il pourrait être acheté à moindre coût à Moscou, plutôt que d'attendre un colis d'Ali. Et bien sûr, il était intéressant d’apprendre à contrôler la puce soi-même, plutôt que d’utiliser une bibliothèque toute faite. Les puces sont désormais principalement présentées dans le boîtier SSOP24 ; elles sont faciles à souder sur un adaptateur.

Puisque le fabricant est taïwanais, Fiche technique la puce est écrite en anglais chinois, ce qui signifie que ce sera amusant. Nous regardons d’abord le brochage (Connexion des broches) pour comprendre à quelle jambe connecter quoi, et une description des broches (Description de la broche). 16 broches :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Sources de puits CC (drain ouvert)

Lavabo / Sortie à drain ouvert - vidange; source de courant entrant ; la sortie est connectée à la terre à l'état actif - les LED sont connectées au driver par des cathodes. Électriquement, il ne s’agit bien sûr pas d’un « drain à ciel ouvert » (vidange à ciel ouvert), mais dans les fiches techniques, cette désignation pour les broches en mode drain est souvent trouvée.

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Résistances externes entre REXT et GND pour définir la valeur du courant de sortie

Une résistance de référence est installée entre la broche REXT et la masse, qui contrôle la résistance interne des sorties, voir le graphique à la page 9 de la fiche technique. Dans le DM634, cette résistance peut également être contrôlée par logiciel, en réglant la luminosité globale (luminosité globale); Je n'entrerai pas dans les détails dans cet article, je vais juste mettre ici une résistance de 2.2 à 3 kOhm.

Pour comprendre comment contrôler la puce, regardons la description de l'interface de l'appareil :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

Ouais, le voici, l'anglais chinois dans toute sa splendeur. Traduire cela est problématique, vous pouvez le comprendre si vous le souhaitez, mais il existe un autre moyen - regardez comment la connexion au TLC5940 fonctionnellement similaire est décrite dans la fiche technique :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
... Seules trois broches sont nécessaires pour saisir des données dans l'appareil. Le front montant du signal SCLK déplace les données de la broche SIN vers le registre interne. Une fois toutes les données chargées, un signal XLAT court et élevé verrouille les données transférées séquentiellement dans les registres internes. Les registres internes sont des portes déclenchées par le niveau du signal XLAT. Toutes les données sont transmises avec le bit de poids fort en premier.

Loquet – loquet/loquet/serrure.
Front montant – front montant de l'impulsion
MSB d'abord – bit de poids fort (le plus à gauche) vers l'avant.
pour synchroniser les données – transmettre les données de manière séquentielle (bit par bit).

Mot loquet se trouve souvent dans la documentation des puces et est traduit de diverses manières, donc par souci de compréhension je me permettrai

petit programme éducatifLe pilote de LED est essentiellement un registre à décalage. "Changement" (décalage) au nom - mouvement des données au niveau du bit à l'intérieur de l'appareil : chaque nouveau bit inséré à l'intérieur pousse toute la chaîne devant lui. Puisque personne ne veut observer un clignotement chaotique des LED pendant le décalage, le processus se déroule dans des registres tampons séparés des registres de travail par un amortisseur (loquet) est une sorte de salle d'attente où les bits sont disposés dans l'ordre souhaité. Lorsque tout est prêt, le volet s'ouvre et les bits se mettent au travail, remplaçant le lot précédent. Mot loquet dans la documentation des microcircuits, cela implique presque toujours un tel amortisseur, quelles que soient les combinaisons dans lesquelles il est utilisé.

Ainsi, le transfert de données vers le DM634 s'effectue comme ceci : réglez l'entrée DAI sur la valeur du bit le plus significatif de la LED distante, tirez DCK de haut en bas ; réglez l'entrée DAI sur la valeur du bit suivant, tirez DCK ; et ainsi de suite jusqu'à ce que tous les bits aient été transmis (chronométré), après quoi nous tirons LAT. Cela peut être fait manuellement (bit-bang), mais il vaut mieux utiliser une interface SPI spécialement adaptée pour cela, puisqu'elle est présentée sur notre STM32 en deux exemplaires.

Pilule bleue STM32F103

Introduction : les contrôleurs STM32 sont bien plus complexes que l'Atmega328 qu'ils ne paraissent effrayants. De plus, pour des raisons d'économie d'énergie, presque tous les périphériques sont éteints au démarrage, et la fréquence d'horloge est à 8 MHz de la source interne. Heureusement, les programmeurs STM ont écrit du code qui amène la puce jusqu'à 72 MHz « calculés », et les auteurs de tous les IDE que je connais l'ont inclus dans la procédure d'initialisation, nous n'avons donc pas besoin de synchroniser (mais tu peux si tu veux vraiment). Mais il faudra allumer les périphériques.

Documentation : Blue Pill est équipé de la populaire puce STM32F103C8T6, il existe deux documents utiles pour cela :

Dans la fiche technique, nous pourrions être intéressés par :

  • Brochages – brochages des puces – au cas où nous déciderions de fabriquer les cartes nous-mêmes ;
  • Carte mémoire – carte mémoire pour une puce spécifique. Le manuel de référence contient une carte pour toute la ligne et mentionne des registres que le nôtre n'a pas.
  • Tableau de définitions des broches – répertoriant les fonctions principales et alternatives des broches ; pour la « pilule bleue », vous pouvez trouver des images plus pratiques sur Internet avec une liste de broches et leurs fonctions. Par conséquent, nous recherchons immédiatement le brochage de Blue Pill sur Google et gardons cette image à portée de main :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
NB : il y a eu une erreur dans la photo provenant d'Internet, notée dans les commentaires, merci pour cela. L'image a été remplacée, mais c'est une leçon - il est préférable de vérifier les informations et non les fiches techniques.

Nous supprimons la fiche technique, ouvrons le manuel de référence et désormais nous l'utilisons uniquement.
Procédure : nous nous occupons des entrées/sorties standards, configurons SPI, allumons les périphériques nécessaires.

Entrée sortie

Sur l'Atmega328, les E/S sont implémentées de manière extrêmement simple, c'est pourquoi l'abondance des options STM32 peut prêter à confusion. Maintenant, nous n’avons besoin que de conclusions, mais même celles-ci ont quatre options :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
drain ouvert, push-pull, push-pull alternatif, drain ouvert alternatif

"Tirer-pousser" (pousser tirer) est la sortie habituelle de l'Arduino, la broche peut prendre la valeur HIGH ou LOW. Mais avec le « drain ouvert », il y a la complexité, même si en fait tout est simple ici :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Configuration de sortie / lorsque le port est affecté à la sortie : / tampon de sortie activé : / – mode drain ouvert : « 0 » dans le registre de sortie active N-MOS, « 1 » dans le registre de sortie laisse le port en mode Hi-Z ( P-MOS n'est pas activé ) / – mode push-pull : « 0 » dans le registre de sortie active N-MOS, « 1 » dans le registre de sortie active P-MOS.

Toute la différence entre un drain ouvert (vidange à ciel ouvert) du « push-pull » (pousser tirer) est que la première broche ne peut pas accepter l'état HAUT : lors de l'écriture dans le registre de sortie, elle passe en mode haute résistance (impédance élevée, Salut-Z). Lors de l'écriture du zéro, la broche se comporte de la même manière dans les deux modes, à la fois logiquement et électriquement.

En mode de sortie normal, la broche diffuse simplement le contenu du registre de sortie. Dans "l'alternative", il est piloté par les périphériques correspondants (voir 9.1.4) :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Si un bit de port est configuré comme broche de fonction alternative, le registre de broches est désactivé et la broche est connectée à la broche périphérique.

Les fonctionnalités alternatives de chaque broche sont décrites dans Définitions des broches La fiche technique se trouve sur l'image téléchargée. A la question de savoir que faire si une broche a plusieurs fonctions alternatives, la réponse est donnée par une note de bas de page dans la fiche technique :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Si plusieurs périphériques utilisent la même broche, pour éviter tout conflit entre des fonctions alternatives, un seul périphérique doit être utilisé à la fois, basculé à l'aide du bit d'activation de l'horloge périphérique (dans le registre RCC approprié).

Enfin, les broches en mode sortie ont également une vitesse d'horloge. Il s’agit d’une autre fonctionnalité d’économie d’énergie ; dans notre cas, nous la réglons simplement au maximum et l’oublions.

Donc : nous utilisons SPI, ce qui signifie que deux broches (avec des données et un signal d'horloge) doivent être une "fonction push-pull alternative", et une autre (LAT) doit être une "fonction push-pull régulière". Mais avant de les attribuer, parlons de SPI.

SPI

Un autre petit programme éducatif

SPI ou Serial Peripheral Interface (interface périphérique série) est une interface simple et très efficace pour connecter un MK avec d'autres MK et avec le monde extérieur en général. Le principe de son fonctionnement a déjà été décrit ci-dessus, à propos du driver LED chinois (dans le manuel de référence, voir section 25). SPI peut fonctionner en mode maître (« maître ») et esclave (« esclave »). SPI dispose de quatre canaux de base, dont tous ne peuvent pas être utilisés :

  • MOSI, Master Output / Slave Input : cette broche transmet les données en mode maître, et reçoit les données en mode esclave ;
  • MISO, Master Input / Slave Output : au contraire, il reçoit dans le maître, et transmet dans l'esclave ;
  • SCK, Serial Clock : définit la fréquence de transmission des données dans le maître ou reçoit un signal d'horloge dans l'esclave. Essentiellement, frapper des rythmes ;
  • SS, Slave Select : grâce à ce canal, l'esclave sait qu'on attend de lui quelque chose. Sur STM32, cela s'appelle NSS, où N = négatif, c'est-à-dire le contrôleur devient esclave s'il y a de la terre dans ce canal. Il se combine bien avec le mode Open Drain Output, mais c'est une autre histoire.

Comme tout le reste, SPI sur STM32 est riche en fonctionnalités, ce qui le rend quelque peu difficile à comprendre. Par exemple, cela peut fonctionner non seulement avec SPI, mais aussi avec une interface I2S, et dans la documentation leurs descriptions sont mélangées, il est nécessaire de couper l'excédent en temps opportun. Notre tâche est extrêmement simple : il nous suffit d'envoyer des données en utilisant uniquement MOSI et SCK. On passe à la section 25.3.4 (communication semi-duplex, communication semi-duplex), où l'on trouve 1 horloge et 1 fil de données unidirectionnel (1 signal d'horloge et 1 flux de données unidirectionnel) :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Dans ce mode, l'application utilise SPI en mode transmission uniquement ou en mode réception uniquement. / Le mode de transmission uniquement est similaire au mode duplex : les données sont transmises sur la broche de transmission (MOSI en mode maître ou MISO en mode esclave) et la broche de réception (MISO ou MOSI respectivement) peut être utilisée comme broche d'E/S normale. . Dans ce cas, l'application n'a qu'à ignorer le tampon Rx (s'il est lu, aucune donnée n'y sera transférée).

Super, la broche MISO est libre, connectons-y le signal LAT. Regardons Slave Select, qui sur le STM32 peut être contrôlé par programme, ce qui est extrêmement pratique. On lit le paragraphe du même nom dans la section 25.3.1 Description générale du SPI :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Contrôle logiciel NSS (SSM = 1) / Les informations de sélection d'esclave sont contenues dans le bit SSI du registre SPI_CR1. La broche NSS externe reste libre pour d'autres besoins d'application.

Il est temps d'écrire dans les registres. J'ai décidé d'utiliser SPI2, recherchez son adresse de base dans la fiche technique - dans la section 3.3 Carte mémoire :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

Eh bien, commençons :

#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))

Ouvrez la section 25.3.3 avec le titre explicite « Configuration de SPI en mode maître » :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

1. Définissez la fréquence d'horloge série avec les bits BR[2:0] dans le registre SPI_CR1.

Les registres sont rassemblés dans la section du manuel de référence du même nom. Changement d'adresse (Décalage d'adresse) pour CR1 – 0x00, par défaut tous les bits sont effacés (Réinitialiser la valeur 0x0000) :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

Les bits BR définissent le diviseur d'horloge du contrôleur, déterminant ainsi la fréquence à laquelle le SPI fonctionnera. Notre fréquence STM32 sera de 72 MHz, le driver LED, selon sa fiche technique, fonctionne avec une fréquence allant jusqu'à 25 MHz, nous devons donc diviser par quatre (BR[2:0] = 001).

#define _SPI_CR1 0x00

#define BR_0        0x0008
#define BR_1        0x0010
#define BR_2        0x0020

_SPI2_ (_SPI_CR1) |= BR_0;// pclk/4

2. Définissez les bits CPOL et CPHA pour définir la relation entre le transfert de données et la synchronisation de l'horloge série (voir schéma à la page 240).

Puisque nous lisons ici une fiche technique et ne regardons pas de schémas, examinons de plus près la description textuelle des bits CPOL et CPHA à la page 704 (Description générale SPI) :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Phase et polarité de l'horloge
À l'aide des bits CPOL et CPHA du registre SPI_CR1, vous pouvez sélectionner par programme quatre relations de synchronisation. Le bit CPOL (polarité d'horloge) contrôle l'état du signal d'horloge lorsqu'aucune donnée n'est transmise. Ce bit contrôle les modes maître et esclave. Si CPOL est réinitialisé, la broche SCK est faible en mode repos. Si le bit CPOL est défini, la broche SCK est haute en mode repos.
Lorsque le bit CPHA (phase d'horloge) est défini, le stroboscope de piège de bit haut est le deuxième front du signal SCK (descendant si CPOL est clair, augmentant si CPOL est défini). Les données sont capturées par le deuxième changement du signal d'horloge. Si le bit CPHA est effacé, le stroboscope de piège de bit haut est le front montant du signal SCK (front descendant si CPOL est activé, front montant si CPOL est effacé). Les données sont capturées au premier changement du signal d'horloge.

Après avoir absorbé ces connaissances, nous arrivons à la conclusion que les deux bits doivent rester des zéros, car Nous voulons que le signal SCK reste faible lorsqu'il n'est pas utilisé et que les données soient transmises sur le front montant de l'impulsion (voir Fig. Front montant dans la fiche technique DM634).

À propos, nous avons rencontré ici pour la première fois une caractéristique du vocabulaire des fiches techniques ST : dans celles-ci, l'expression « remettre le bit à zéro » est écrite pour réinitialiser un peuEt ce n'est pas pour clarifier un peu, comme, par exemple, Atmega.

3. Définissez le bit DFF pour déterminer si le bloc de données est au format 8 bits ou 16 bits.

J'ai spécifiquement pris un DM16 634 bits pour ne pas m'embêter avec la transmission de données PWM 12 bits, comme le DM633. Il est logique de définir DFF sur un :

#define DFF         0x0800

_SPI2_ (_SPI_CR1) |= DFF; // 16-bit mode

4. Configurez le bit LSBFIRST dans le registre SPI_CR1 pour déterminer le format de bloc

LSBFIRST, comme son nom l'indique, configure la transmission avec le bit le moins significatif en premier. Mais le DM634 veut recevoir des données à partir du bit le plus significatif. Par conséquent, nous le laissons réinitialisé.

5. En mode matériel, si une entrée de la broche NSS est requise, appliquez un signal haut à la broche NSS pendant toute la séquence de transfert d'octets. En mode logiciel NSS, définissez les bits SSM et SSI dans le registre SPI_CR1. Si la broche NSS doit être utilisée comme sortie, seul le bit SSOE doit être défini.

Installez SSM et SSI pour oublier le mode matériel NSS :

#define SSI         0x0100
#define SSM         0x0200

_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high

6. Les bits MSTR et SPE doivent être définis (ils restent définis uniquement si le signal NSS est haut)

En fait, avec ces bits, nous désignons notre SPI comme maître et l'allumons :

#define MSTR        0x0004
#define SPE         0x0040

_SPI2_ (_SPI_CR1) |= MSTR; //SPI master
//когда все готово, включаем SPI
_SPI2_ (_SPI_CR1) |= SPE;

SPI est configuré, écrivons immédiatement les fonctions qui envoient des octets au pilote. Continuer la lecture de 25.3.3 « Configuration de SPI en mode maître » :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Ordre de transfert de données
La transmission commence lorsqu'un octet est écrit dans le tampon Tx.
L'octet de données est chargé dans le registre à décalage à parallèle mode (depuis le bus interne) lors de la transmission du premier bit, après quoi il est transmis à séquentiel Mode broche MOSI, premier ou dernier bit en avant en fonction du réglage du bit LSBFIRST dans le registre CPI_CR1. Le drapeau TXE est défini après la transmission des données du tampon Tx au registre à décalage, et génère également une interruption si le bit TXEIE dans le registre CPI_CR1 est défini.

J'ai souligné quelques mots dans la traduction pour attirer l'attention sur une fonctionnalité de l'implémentation SPI dans les contrôleurs STM. Sur Atmega le drapeau TXE (Émission vide, Tx est vide et prêt à recevoir des données) n'est défini qu'après que l'octet entier a été envoyé out. Et ici, cet indicateur est défini après que l'octet a été inséré dans le registre à décalage interne. Puisqu'il y est poussé avec tous les bits en même temps (en parallèle), puis que les données sont transférées séquentiellement, TXE est défini avant que l'octet ne soit complètement envoyé. C'est important parce que dans le cas de notre driver LED, nous devons retirer la broche LAT après l'envoi tous des données, c'est-à-dire Le pavillon TXE seul ne nous suffira pas.

Cela signifie que nous avons besoin d'un autre drapeau. Regardons 25.3.7 - « Drapeaux d'état » :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
<…>
Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Drapeau OCCUPÉ
L'indicateur BSY est défini et effacé par le matériel (l'écriture dessus n'a aucun effet). Le drapeau BSY indique l'état de la couche de communication SPI.
Il réinitialise :
lorsque le transfert est terminé (sauf en mode maître si le transfert est continu)
lorsque SPI est désactivé
lorsqu'une erreur de mode maître se produit (MODF=1)
Si le transfert n'est pas continu, le flag BSY est effacé entre chaque transfert de données

D'accord, cela sera utile. Voyons où se trouve le tampon Tx. Pour ce faire, lisez « SPI Data Register » :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Bits 15:0 DR[15:0] Registre de données
Données reçues ou données à transmettre.
Le registre de données est divisé en deux tampons : un pour l'écriture (tampon de transmission) et un pour la lecture (tampon de réception). L'écriture dans le registre de données écrit dans le tampon Tx et la lecture à partir du registre de données renverra la valeur contenue dans le tampon Rx.

Eh bien, et le registre d'état, où se trouvent les drapeaux TXE et BSY :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

Nous écrivons:

#define _SPI_DR  0x0C
#define _SPI_SR  0x08

#define BSY         0x0080
#define TXE         0x0002

void dm_shift16(uint16_t value)
{
    _SPI2_(_SPI_DR) = value; //send 2 bytes
    while (!(_SPI2_(_SPI_SR) & TXE)); //wait until they're sent
}

Eh bien, puisque nous devons transmettre 16 fois deux octets, en fonction du nombre de sorties du pilote LED, quelque chose comme ceci :

void sendLEDdata()
{
    LAT_low();
    uint8_t k = 16;
    do
    {   k--;
        dm_shift16(leds[k]);
    } while (k);

    while (_SPI2_(_SPI_SR) & BSY); // finish transmission

    LAT_pulse();
}

Mais nous ne savons pas encore comment retirer la broche LAT, nous reviendrons donc aux E/S.

Attribution de broches

Dans le STM32F1, les registres responsables de l'état des broches sont assez inhabituels. Il est clair qu'il y en a plus qu'Atmega, mais elles sont également différentes des autres puces STM. Section 9.1 Description générale du GPIO :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Chacun des ports d'E/S à usage général (GPIO) possède deux registres de configuration 32 bits (GPIOx_CRL et GPIOx_CRH), deux registres de données 32 bits (GPIOx_IDR et GPIOx_ODR), un registre de configuration/réinitialisation 32 bits (GPIOx_BSRR), un registre de réinitialisation 16 bits (GPIOx_BRR) et un registre 32 bits. registre de blocage de bits (GPIOx_LCKR).

Les deux premiers registres sont inhabituels, et également assez gênants, car les 16 broches du port sont réparties sur eux selon un format « quatre bits par frère ». Ceux. les broches zéro à sept sont dans CRL et les autres sont dans CRH. Dans le même temps, les registres restants contiennent avec succès les bits de toutes les broches du port - restant souvent à moitié « réservés ».

Pour simplifier, commençons par la fin de la liste.

Nous n'avons pas besoin d'un registre de blocage.

Les registres set et reset sont assez amusants dans la mesure où ils se dupliquent partiellement : vous pouvez tout écrire uniquement dans BSRR, où les 16 bits supérieurs remettront la broche à zéro, et les bits inférieurs seront définis sur 1, ou vous pouvez également utilisez BRR, dont les 16 bits inférieurs ne réinitialisent que la broche. J'aime la deuxième option. Ces registres sont importants car ils fournissent un accès atomique aux broches :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Ensemble ou réinitialisation atomique
Il n'est pas nécessaire de désactiver les interruptions lors de la programmation de GPIOx_ODR au niveau des bits : un ou plusieurs bits peuvent être modifiés avec une seule opération d'écriture atomique APB2. Ceci est réalisé en écrivant un "1" dans le registre de mise à XNUMX/réinitialisation (GPIOx_BSRR ou, pour la réinitialisation uniquement, GPIOx_BRR) du bit qui doit être modifié. Les autres bits resteront inchangés.

Les registres de données ont des noms assez explicites - IDR = Entrée Registre de direction, registre d'entrée ; ODR = Sortie Registre de direction, registre de sortie. Nous n'en aurons pas besoin dans le projet actuel.

Et enfin, les registres de contrôle. Puisque nous nous intéressons aux deuxièmes broches SPI, à savoir PB13, PB14 et PB15, nous regardons immédiatement CRH :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

Et nous voyons que nous devrons écrire quelque chose en bits de 20 à 31.

Nous avons déjà compris ci-dessus ce que nous attendons des broches, donc ici je me passerai de capture d'écran, je dirai simplement que MODE spécifie la direction (entrée si les deux bits sont mis à 0) et la vitesse des broches (nous avons besoin de 50 MHz, c'est-à-dire les deux broches sur « 1 »), et CNF définit le mode : « push-pull » régulier – 00, « alternatif » – 10. Par défaut, comme nous le voyons ci-dessus, toutes les broches ont le troisième bit à partir du bas (CNF0), ça les met en mode entrée flottante.

Puisque je prévois de faire autre chose avec cette puce, par souci de simplicité, j'ai défini toutes les valeurs MODE et CNF possibles pour les registres de contrôle inférieur et supérieur.

Eh bien, quelque chose comme ça

#define CNF0_0 0x00000004
#define CNF0_1 0x00000008
#define CNF1_0 0x00000040
#define CNF1_1 0x00000080
#define CNF2_0 0x00000400
#define CNF2_1 0x00000800
#define CNF3_0 0x00004000
#define CNF3_1 0x00008000
#define CNF4_0 0x00040000
#define CNF4_1 0x00080000
#define CNF5_0 0x00400000
#define CNF5_1 0x00800000
#define CNF6_0 0x04000000
#define CNF6_1 0x08000000
#define CNF7_0 0x40000000
#define CNF7_1 0x80000000
#define CNF8_0 0x00000004
#define CNF8_1 0x00000008
#define CNF9_0 0x00000040
#define CNF9_1 0x00000080
#define CNF10_0 0x00000400
#define CNF10_1 0x00000800
#define CNF11_0 0x00004000
#define CNF11_1 0x00008000
#define CNF12_0 0x00040000
#define CNF12_1 0x00080000
#define CNF13_0 0x00400000
#define CNF13_1 0x00800000
#define CNF14_0 0x04000000
#define CNF14_1 0x08000000
#define CNF15_0 0x40000000
#define CNF15_1 0x80000000

#define MODE0_0 0x00000001
#define MODE0_1 0x00000002
#define MODE1_0 0x00000010
#define MODE1_1 0x00000020
#define MODE2_0 0x00000100
#define MODE2_1 0x00000200
#define MODE3_0 0x00001000
#define MODE3_1 0x00002000
#define MODE4_0 0x00010000
#define MODE4_1 0x00020000
#define MODE5_0 0x00100000
#define MODE5_1 0x00200000
#define MODE6_0 0x01000000
#define MODE6_1 0x02000000
#define MODE7_0 0x10000000
#define MODE7_1 0x20000000
#define MODE8_0 0x00000001
#define MODE8_1 0x00000002
#define MODE9_0 0x00000010
#define MODE9_1 0x00000020
#define MODE10_0 0x00000100
#define MODE10_1 0x00000200
#define MODE11_0 0x00001000
#define MODE11_1 0x00002000
#define MODE12_0 0x00010000
#define MODE12_1 0x00020000
#define MODE13_0 0x00100000
#define MODE13_1 0x00200000
#define MODE14_0 0x01000000
#define MODE14_1 0x02000000
#define MODE15_0 0x10000000
#define MODE15_1 0x20000000

Nos broches sont situées sur le port B (adresse de base – 0x40010C00), code :

#define _PORTB_(mem_offset) (*(volatile uint32_t *)(0x40010C00 + (mem_offset)))

#define _BRR  0x14
#define _BSRR 0x10
#define _CRL  0x00
#define _CRH  0x04

//используем стандартный SPI2: MOSI на B15, CLK на B13
//LAT пусть будет на неиспользуемом MISO – B14

//очищаем дефолтный бит, он нам точно не нужен
_PORTB_ (_CRH) &= ~(CNF15_0 | CNF14_0 | CNF13_0 | CNF12_0);

//альтернативные функции для MOSI и SCK
_PORTB_ (_CRH) |= CNF15_1 | CNF13_1;

//50 МГц, MODE = 11
_PORTB_ (_CRH) |= MODE15_1 | MODE15_0 | MODE14_1 | MODE14_0 | MODE13_1 | MODE13_0;

Et, en conséquence, vous pouvez écrire des définitions pour LAT, qui seront modifiées par les registres BRR et BSRR :

/*** LAT pulse – high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)

#define LAT_low() _PORTB_(_BRR) = (1<<14)

(LAT_low juste par inertie, ça a toujours été comme ça, laisse ça rester)

Maintenant, tout va bien, mais ça ne marche pas. Comme il s'agit de STM32, ils économisent de l'électricité, ce qui signifie que vous devez activer la synchronisation des périphériques requis.

Activer le pointage

La montre, également connue sous le nom d'Horloge, est responsable du pointage. Et on pouvait déjà remarquer l'abréviation RCC. Nous le cherchons dans la documentation : il s'agit de Reset and Clock Control.

Comme cela a été dit plus haut, heureusement, la partie la plus difficile du sujet du pointage a été réalisée pour nous par des gens de la STM, ce pour quoi nous les remercions beaucoup (encore une fois je donne un lien vers Le site de Di Halt, pour bien montrer à quel point c'est déroutant). Nous n'avons besoin que de registres chargés d'activer la synchronisation des périphériques (Peripheral Clock Enable Registers). Tout d'abord, trouvons l'adresse de base du RCC, elle se trouve au tout début de la « Memory Map » :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))

Et puis soit cliquez sur le lien où vous essayez de trouver quelque chose dans la plaque, soit, bien mieux, parcourez les descriptions des registres d'activation dans les sections sur activer les registres. Où l'on trouvera RCC_APB1ENR et RCC_APB2ENR :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

Et ils contiennent donc des bits qui incluent la synchronisation de SPI2, IOPB (port d'E/S B) et des fonctions alternatives (AFIO).

#define _APB2ENR 0x18
#define _APB1ENR 0x1C

#define IOPBEN 0x0008
#define SPI2EN 0x4000
#define AFIOEN 0x0001

//включаем тактирование порта B и альт. функций
_RCC_(_APB2ENR) |= IOPBEN | AFIOEN;

//включаем  тактирование SPI2
_RCC_(_APB1ENR) |= SPI2EN;

Le code final peut être trouvé ici.

Si vous avez l'opportunité et l'envie de tester, alors connectez le DM634 comme ceci : DAI au PB15, DCK au PB13, LAT au PB14. On alimente le driver en 5 volts, n'oubliez pas de connecter les masses.

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

STM8 PWM

PWM sur STM8

Alors que je planifiais cet article, j'ai décidé, à titre d'exemple, d'essayer de maîtriser certaines fonctionnalités d'une puce inconnue en utilisant uniquement une fiche technique, afin de ne pas me retrouver avec un cordonnier sans bottes. STM8 était idéal pour ce rôle : d'une part, j'avais quelques cartes mères chinoises avec STM8S103, et d'autre part, il n'est pas très populaire, et donc la tentation de lire et de trouver une solution sur Internet repose sur le manque de ces mêmes solutions.

La puce a également Fiche technique и manuel de référence RM0016, dans le premier il y a les adresses de brochage et d'enregistrement, dans le second - tout le reste. STM8 est programmé en C dans un terrible IDE Développement visuel ST.

Synchronisation et E/S

Par défaut, STM8 fonctionne à une fréquence de 2 MHz, cela doit être corrigé immédiatement.

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Horloge HSI (interne haute vitesse)
Le signal d'horloge HSI est dérivé d'un oscillateur RC interne de 16 MHz avec un diviseur programmable (1 à 8). Il est défini dans le registre diviseur d'horloge (CLK_CKDIVR).
Remarque : au départ, un oscillateur HSI RC avec un diviseur de 8 est sélectionné comme source principale du signal d'horloge.

Nous trouvons l'adresse du registre dans la fiche technique, la description dans refman et voyons que le registre doit être effacé :

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Puisque nous allons exécuter du PWM et connecter les LED, regardons le brochage :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

La puce est petite, de nombreuses fonctions sont suspendues aux mêmes broches. Ce qui est entre crochets est une « fonctionnalité alternative », elle est commutée par des « octets d'option » (octets d'options) – quelque chose comme des fusibles Atmega. Vous pouvez modifier leurs valeurs par programme, mais ce n'est pas nécessaire, car La nouvelle fonctionnalité n'est activée qu'après un redémarrage. Il est plus facile d'utiliser ST Visual Programmer (téléchargé avec Visual Develop), qui peut modifier ces octets. Le brochage montre que les broches CH1 et CH2 du premier temporisateur sont cachées entre crochets ; il est nécessaire de définir les bits AFR1 et AFR0 dans STVP, et le second transférera également la sortie CH1 du deuxième temporisateur de PD4 vers PC5.

Ainsi, 6 broches contrôleront les LED : PC6, PC7 et PC3 pour le premier timer, PC5, PD3 et PA3 pour le second.

La configuration des broches d'E/S elles-mêmes sur STM8 est plus simple et plus logique que sur STM32 :

  • familier du registre de direction des données Atmega DDR (Registre de direction des données) : 1 = sortie ;
  • le premier registre de commande CR1, lors de sa sortie, règle le mode push-pull (1) ou drain ouvert (0) ; puisque je connecte les LED à la puce avec des cathodes, je laisse des zéros ici ;
  • le deuxième registre de contrôle CR2, en sortie, règle la vitesse d'horloge : 1 = 10 MHz

#define PA_DDR     *(volatile uint8_t *)0x005002
#define PA_CR2     *(volatile uint8_t *)0x005004
#define PD_DDR     *(volatile uint8_t *)0x005011
#define PD_CR2     *(volatile uint8_t *)0x005013
#define PC_DDR     *(volatile uint8_t *)0x00500C
#define PC_CR2     *(volatile uint8_t *)0x00500E

PA_DDR = (1<<3); //output
PA_CR2 |= (1<<3); //fast
PD_DDR = (1<<3); //output
PD_CR2 |= (1<<3); //fast
PC_DDR = ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //output
PC_CR2 |= ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //fast

Paramètre PWM

Tout d'abord, définissons les termes :

  • Fréquence PWM – fréquence à laquelle le chronomètre tourne ;
  • Rechargement automatique, AR – valeur autochargeable jusqu'à laquelle le temporisateur comptera (période d'impulsion) ;
  • Événement de mise à jour, UEV – un événement qui se produit lorsque le temporisateur a compté jusqu'à AR ;
  • Cycle de service PWM – Cycle de service PWM, souvent appelé « facteur de service » ;
  • Capturer/Comparer la valeur – valeur de capture/comparaison à laquelle le minuteur a compté fera quelque chose (dans le cas du PWM, il inverse le signal de sortie) ;
  • Valeur de précharge – valeur préchargée. Comparer la valeur ne peut pas changer pendant que la minuterie tourne, sinon le cycle PWM sera interrompu. Par conséquent, les nouvelles valeurs transmises sont placées dans un tampon et extraites lorsque le minuteur atteint la fin de son compte à rebours et est réinitialisé ;
  • Aligné sur les bords и Modes alignés au centre – alignement le long de la bordure et au centre, identique à celui d’Atmel PWM rapide и PWM à phase correcte.
  • OCiREF, signal de référence de comparaison de sortie – signal de sortie de référence, en fait, ce qui apparaît sur la broche correspondante en mode PWM.

Comme le montre déjà clairement le brochage, deux minuteries ont des capacités PWM – la première et la seconde. Les deux sont 16 bits, le premier a de nombreuses fonctionnalités supplémentaires (en particulier, il peut compter à la fois vers le haut et vers le bas). Nous avons besoin que les deux fonctionnent de manière égale, j'ai donc décidé de commencer par le deuxième, évidemment le plus pauvre, afin de ne pas utiliser accidentellement quelque chose qui n'est pas là. Un problème est que la description de la fonctionnalité PWM de toutes les minuteries dans le manuel de référence se trouve dans le chapitre sur la première minuterie (17.5.7 Mode PWM), vous devez donc aller et venir tout le temps dans le document.

Le PWM sur STM8 présente un avantage important par rapport au PWM sur Atmega :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
PWM aligné sur les limites
Configuration du compte de bas en haut
Le comptage ascendant est actif si le bit DIR du registre TIM_CR1 est effacé
Exemple
L'exemple utilise le premier mode PWM. Le signal de référence PWM OCiREF est maintenu haut tant que TIM1_CNT < TIM1_CCRi. Sinon il faut un niveau bas. Si la valeur de comparaison dans le registre TIM1_CCRi est supérieure à la valeur de chargement automatique (registre TIM1_ARR), le signal OCiREF est maintenu à 1. Si la valeur de comparaison est 0, OCiREF est maintenu à zéro....

Minuterie STM8 pendant événement de mise à jour vérifie d'abord comparer la valeur, et produit alors seulement un signal de référence. La minuterie d'Atmega se trompe d'abord, puis compare, ce qui donne compare value == 0 la sortie est une aiguille, qui doit être traitée d'une manière ou d'une autre (par exemple, en inversant la logique par programme).

Donc ce que nous voulons faire : PWM 8 bits (AR == 255), en comptant de bas en haut, alignement le long de la bordure. Étant donné que les ampoules sont connectées à la puce par des cathodes, le PWM devrait produire 0 (LED allumée) jusqu'à ce que comparer la valeur et 1 après.

Nous avons déjà entendu parler de certains Mode PWM, on trouve donc le registre recherché du deuxième temporisateur en recherchant dans le manuel de référence cette phrase (18.6.8 - TIMx_CCMR1) :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
110 : Premier mode PWM – lors du comptage de bas en haut, le premier canal est actif tant que TIMx_CNT < TIMx_CCR1. Sinon, le premier canal est inactif. [plus loin dans le document il y a un copier-coller erroné du timer 1] 111 : Deuxième mode PWM – lors du comptage de bas en haut, le premier canal est inactif tant que TIMx_CNT < TIMx_CCR1. Sinon, le premier canal est actif.

Les LED étant reliées au MK par des cathodes, le deuxième mode nous convient (le premier aussi, mais on ne le sait pas encore).

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Bit 3 OC1PE : Activer la précharge de la broche 1
0 : le registre de préchargement sur TIMx_CCR1 est désactivé. Vous pouvez écrire dans TIMx_CCR1 à tout moment. La nouvelle valeur fonctionne immédiatement.
1 : le registre de préchargement sur TIMx_CCR1 est activé. Les opérations de lecture/écriture accèdent au registre de préchargement. La valeur préchargée TIMx_CCR1 est chargée dans le registre fantôme lors de chaque événement de mise à jour.
*Remarque : pour que le mode PWM fonctionne correctement, les registres de préchargement doivent être activés. Ceci n'est pas nécessaire en mode signal unique (le bit OPM est défini dans le registre TIMx_CR1).

D'accord, allumons tout ce dont nous avons besoin pour les trois canaux du deuxième minuteur :

#define TIM2_CCMR1 *(volatile uint8_t *)0x005307
#define TIM2_CCMR2 *(volatile uint8_t *)0x005308
#define TIM2_CCMR3 *(volatile uint8_t *)0x005309

#define PWM_MODE2   0x70 //PWM mode 2, 0b01110000
#define OCxPE       0x08 //preload enable

TIM2_CCMR1 = (PWM_MODE2 | OCxPE);
TIM2_CCMR2 = (PWM_MODE2 | OCxPE);
TIM2_CCMR3 = (PWM_MODE2 | OCxPE);

AR se compose de deux registres de huit bits, tout est simple :

#define TIM2_ARRH  *(volatile uint8_t *)0x00530F
#define TIM2_ARRL  *(volatile uint8_t *)0x005310

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Le deuxième minuteur ne peut compter que de bas en haut, alignement le long de la bordure, rien ne doit être modifié. Fixons le diviseur de fréquence, par exemple, à 256. Pour le deuxième temporisateur, le diviseur est défini dans le registre TIM2_PSCR et est une puissance de deux :

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Il ne reste plus qu'à activer les conclusions et le deuxième minuteur lui-même. Le premier problème est résolu par les registres Capturer/Comparer Activer: il y a deux, trois canaux répartis de manière asymétrique. Ici, nous pouvons également apprendre qu'il est possible de changer la polarité du signal, c'est-à-dire en principe, il était possible d'utiliser le mode PWM 1. On écrit :

#define TIM2_CCER1 *(volatile uint8_t *)0x00530A
#define TIM2_CCER2 *(volatile uint8_t *)0x00530B

#define CC1E  (1<<0) // CCER1
#define CC2E  (1<<4) // CCER1
#define CC3E  (1<<0) // CCER2

TIM2_CCER1 = (CC1E | CC2E);
TIM2_CCER2 = CC3E;

Et enfin, on démarre le timer dans le registre TIMx_CR1 :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Écrivons un simple analogue d'AnalogWrite(), qui transférera les valeurs réelles au minuteur pour comparaison. Les registres sont nommés de manière prévisible Capturer/Comparer les registres, il y en a deux pour chaque canal : les 8 bits de poids faible dans TIM2_CCRxL et les 2 bits de poids fort dans TIM8_CCRxH. Puisque nous avons créé un PWM XNUMX bits, il suffit d'écrire uniquement les bits les moins significatifs :

#define TIM2_CCR1L *(volatile uint8_t *)0x005312
#define TIM2_CCR2L *(volatile uint8_t *)0x005314
#define TIM2_CCR3L *(volatile uint8_t *)0x005316

void setRGBled(uint8_t r, uint8_t g, uint8_t b)
{
    TIM2_CCR1L = r;
    TIM2_CCR2L = g;
    TIM2_CCR3L = b;
}

Le lecteur attentif remarquera que nous avons un PWM légèrement défectueux, incapable de produire un remplissage à 100 % (à une valeur maximale de 255, le signal est inversé pendant un cycle de temporisation). Pour les LED, cela n'a pas d'importance, et le lecteur attentif peut déjà deviner comment y remédier.

PWM sur la deuxième minuterie fonctionne, passons à la première.

Le premier temporisateur a exactement les mêmes bits dans les mêmes registres (c'est juste que les bits qui sont restés « réservés » dans le deuxième temporisateur sont activement utilisés dans le premier pour toutes sortes de choses avancées). Il suffit donc de retrouver les adresses des mêmes registres dans la fiche technique et de copier le code. Eh bien, changez la valeur du diviseur de fréquence, parce que... le premier temporisateur veut recevoir non pas une puissance de deux, mais une valeur exacte de 16 bits dans deux registres Préscaler élevé и Faible. Nous faisons tout et... le premier minuteur ne fonctionne pas. Quel est le problème?

Le problème ne peut être résolu qu'en parcourant toute la section sur les registres de contrôle du temporisateur 1, où nous recherchons celui que le deuxième temporisateur n'a pas. Il y aura 17.7.30 Registre de rupture (TIM1_BKR), où se trouve ce bit :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Activer la sortie principale

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

C'est tout sûr maintenant, le code ibid.

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

Multiplexage STM8

Multiplexage sur STM8

Le troisième mini-projet consiste à connecter huit LED RVB à la deuxième minuterie en mode PWM et à leur faire afficher différentes couleurs. Il est basé sur le concept de multiplexage des LED, c'est-à-dire que si vous allumez et éteignez les LED très, très rapidement, il nous semblera qu'elles sont constamment allumées (persistance de la vision, inertie de la perception visuelle). Je l'ai fait une fois quelque chose comme ça sur Arduino.

L'algorithme de travail ressemble à ceci :

  • connecté l'anode de la première LED RVB ;
  • l'allumé, envoyant les signaux nécessaires aux cathodes ;
  • attendu la fin du cycle PWM ;
  • connecté l'anode de la deuxième LED RVB ;
  • allume le...

Eh bien, etc. Bien entendu, pour un bon fonctionnement, il est nécessaire que l'anode soit connectée et que la LED soit « allumée » en même temps. Enfin, ou presque. Dans tous les cas, nous devons écrire un code qui affichera les valeurs dans trois canaux de la deuxième minuterie, les modifiera lorsque l'UEV sera atteint et modifiera en même temps la LED RVB actuellement active.

Étant donné que la commutation des LED est automatique, nous devons créer une « mémoire vidéo » à partir de laquelle le gestionnaire d'interruption recevra les données. Il s'agit d'un tableau simple :

uint8_t colors[8][3];

Afin de changer la couleur d'une LED spécifique, il suffira d'écrire les valeurs requises dans ce tableau. Et la variable sera responsable du numéro de LED active

uint8_t cnt;

Démultiplexage

Pour un multiplexage correct, nous avons besoin, curieusement, d'un démultiplexeur CD74HC238. Démultiplexeur - une puce qui implémente l'opérateur dans le matériel <<. Grâce à trois broches d'entrée (bits 0, 1 et 2), nous lui transmettons un nombre X de trois bits et, en réponse, il active le numéro de sortie (1<<X). Les entrées restantes de la puce sont utilisées pour mettre à l’échelle l’ensemble de la conception. Nous avons besoin de cette puce non seulement pour réduire le nombre de broches occupées du microcontrôleur, mais aussi pour des raisons de sécurité - afin de ne pas allumer accidentellement plus de LED que possible et de ne pas brûler le MK. La puce coûte un centime et doit toujours être conservée dans votre armoire à pharmacie.

Notre CD74HC238 se chargera d'alimenter en tension l'anode de la LED souhaitée. Dans un multiplex à part entière, il fournirait une tension à la colonne via un P-MOSFET, mais dans cette démo, c'est possible directement, car il consomme 20 mA, selon notes maximales absolues dans la fiche technique. Depuis fiche technique CD74HC238 nous avons besoin de brochages et de cette aide-mémoire :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
H = niveau de tension élevé, L = niveau de tension faible, X – peu importe

Nous connectons E2 et E1 à la masse, E3, A0, A1 et A3 aux broches PD5, PC3, PC4 et PC5 de STM8. Étant donné que le tableau ci-dessus contient à la fois des niveaux bas et élevés, nous configurons ces broches comme broches push-pull.

PWM

Le PWM sur le deuxième temporisateur est configuré de la même manière que dans l'histoire précédente, avec deux différences :

Tout d'abord, nous devons activer l'interruption sur Événement de mise à jour (UEV) qui appellera une fonction qui bascule la LED active. Cela se fait en changeant le bit Activation de l'interruption de mise à jour dans un registre avec un nom révélateur

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
Registre d'activation des interruptions

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

La deuxième différence est liée au phénomène de multiplexage, tel que ghosting – lueur parasite des diodes. Dans notre cas, cela peut apparaître dû au fait que la minuterie, ayant provoqué une interruption sur l'UEV, continue de fonctionner et que le gestionnaire d'interruption n'a pas le temps de commuter la LED avant que la minuterie ne commence à écrire quelque chose sur les broches. Pour lutter contre cela, vous devrez inverser la logique (0 = luminosité maximale, 255 = rien n'est allumé) et éviter les valeurs de rapport cyclique extrêmes. Ceux. assurez-vous qu'après UEV, les LED s'éteignent complètement pendant un cycle PWM.

Changement de polarité :

//set polarity 
    TIM2_CCER1 |= (CC1P | CC2P);
    TIM2_CCER2 |= CC3P;

Évitez de régler r, g et b sur 255 et n'oubliez pas de les inverser lorsque vous les utilisez.

Interruptions

L'essence d'une interruption est que dans certaines circonstances, la puce arrête d'exécuter le programme principal et appelle une fonction externe. Les interruptions se produisent en raison d'influences externes ou internes, y compris la minuterie.

Lorsque nous avons créé un projet pour la première fois dans ST Visual Develop, en plus de main.c nous avons reçu une fenêtre avec un fichier mystérieux stm8_interrupt_vector.c, automatiquement inclus dans le projet. Dans ce fichier, une fonction est assignée à chaque interruption NonHandledInterrupt. Nous devons lier notre fonction à l'interruption souhaitée.

La fiche technique contient un tableau de vecteurs d'interruption, où l'on trouve ceux dont nous avons besoin :

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8
13 Mise à jour/débordement TIM2
14 Capture/comparaison TIM2

Nous devons changer la LED à l'UEV, nous avons donc besoin de l'interruption n°13.

Ainsi, d'une part, dans le dossier stm8_interrupt_vector.c changez le nom par défaut de la fonction responsable de l'interruption n°13 (IRQ13) par le vôtre :

{0x82, TIM2_Overflow}, /* irq13 */

Deuxièmement, nous devrons créer un fichier main.h ce contenu:

#ifndef __MAIN_H
#define __MAIN_H

@far @interrupt void TIM2_Overflow (void);
#endif

Et enfin, écrivez cette fonction dans votre main.c:

@far @interrupt void TIM2_Overflow (void)
{
    PD_ODR &= ~(1<<5); // вырубаем демультиплексор
    PC_ODR = (cnt<<3); // записываем в демультиплексор новое значение
    PD_ODR |= (1<<5); // включаем демультиплексор

    TIM2_SR1 = 0; // сбрасываем флаг Update Interrupt Pending

    cnt++; 
    cnt &= 7; // двигаем счетчик LED

    TIM2_CCR1L = ~colors[cnt][0]; // передаем в буфер инвертированные значения
    TIM2_CCR2L = ~colors[cnt][1]; // для следующего цикла ШИМ
    TIM2_CCR3L = ~colors[cnt][2]; // 

    return;
}

Il ne reste plus qu'à activer les interruptions. Cela se fait à l'aide de la commande assembleur rim - tu devras le chercher dans Manuel de programmation:

//enable interrupts
_asm("rim");

Une autre commande assembleur est sim – désactive les interruptions. Ils doivent être désactivés pendant l'écriture de nouvelles valeurs dans la « mémoire vidéo », afin qu'une interruption provoquée au mauvais moment ne gâche pas le tableau.

Tout le code - sur GitHub.

Lire les fiches techniques 2 : SPI sur STM32 ; PWM, minuteries et interruptions sur STM8

Si au moins quelqu’un trouve cet article utile, alors je ne l’ai pas écrit en vain. Je serai heureux de recevoir des commentaires et des remarques, j'essaierai de répondre à tout.

Source: habr.com

Ajouter un commentaire