
Dans la première partie, j'ai essayé de dire aux amateurs d'électronique qui sont issus du pantalon Arduino comment et pourquoi ils devraient lire les fiches techniques et autres documents pour les microcontrôleurs. Le texte s'est avéré être volumineux, j'ai donc promis de montrer des exemples pratiques dans un article séparé. Eh bien, il se faisait appeler une cargaison ...
Aujourd'hui, je vais montrer comment utiliser des fiches techniques pour résoudre des tâches assez simples, mais nécessaires pour 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 de nouveau révélé énorme, donc pour plus de commodité, je fais le contenu:
Pilule bleue STM32: 16 LED avec pilote DM634
STM8: configuration de six broches PWM
STM8: 8 LED RGB sur trois broches, interrompt
Avertissement: je ne suis pas ingénieur, je ne prétends pas avoir une connaissance approfondie de l'électronique, l'article est destiné à des amoureux comme moi. En fait, en tant que public cible, je me considérais il y a deux ans. Si quelqu'un m'avait dit alors que ce n'était pas effrayant de lire des fiches techniques sur une puce inconnue, je n'aurais pas passé beaucoup de temps à chercher des morceaux de code sur Internet et à inventer des béquilles avec des ciseaux et un pansement.
Au centre de cet article se trouvent des fiches techniques, pas des projets, donc le code peut ne pas être trop peigné et souvent béquillé. 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 dans une plongée de loisir.
STM32
16 LED avec DM634 et SPI
Un petit projet utilisant la Blue Pill (STM32F103C8T6) et le pilote LED DM634. À l'aide de fiches techniques, nous traiterons du pilote, des ports IO STM et configurerons SPI.
DM634
Puce taïwanaise avec 16 sorties PWM 16 bits, peut être connectée en chaîne. Le plus jeune modèle 12 bits est connu pour le projet domestique Lightpack . À un moment donné, en choisissant entre le DM63x et le bien connu TLC5940, je me suis arrêté au DM pour plusieurs raisons: 1) TLC sur Aliexpress est définitivement faux, mais celui-ci ne l'est pas; 2) le DM dispose d'un PWM autonome avec son propre générateur de fréquence; 3) il pourrait être acheté à bon marché à Moscou, et ne pas attendre un colis avec Ali. Eh bien, bien sûr, c'était intéressant d'apprendre à gérer la puce vous-même, et de ne pas utiliser une bibliothèque prête à l'emploi. Les puces sont maintenant principalement présentées dans le package SSOP24, elles sont faciles à souder à l'adaptateur.
Étant donné que le fabricant est taïwanais, la fiche technique de la puce est rédigée en anglais chinois, ce qui signifie que ce sera amusant. Tout d'abord, regardez la connexion des broches ( Pin Connection ) pour comprendre à quel pied se connecter et la description des broches ( Pin Description ). 16 conclusions:

Sources de courant continu entrant (drain ouvert)
Évier / Sortie à drain ouvert - drain; source de courant entrant; sortie active connectée à la masse - les LED sont connectées au pilote par des cathodes. Électriquement, ce n'est bien sûr pas un «drain ouvert», mais dans les fiches techniques, cette désignation pour les sorties en mode drain est courante.

Résistances externes entre REXT et GND pour régler 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 page 9 de la fiche technique. Dans le DM634, cette résistance peut également être contrôlée par programmation en définissant la luminosité globale ; Je n'entrerai pas dans les détails dans cet article, je viens de mettre une résistance de 2,2 à 3 kOhm ici.
Pour comprendre comment contrĂ´ler la puce, consultez la description de l'interface de l'appareil:

Oui, le voici, l'anglais chinois dans toute sa splendeur. La traduction est problématique, vous pouvez la comprendre si vous le souhaitez, mais il existe un autre moyen - de voir comment la connexion est décrite dans la fiche technique du TLC5940 fonctionnellement fermé:

... Trois broches seulement sont nécessaires pour entrer des données dans l'appareil. Le bord d'attaque du signal SCLK déplace les données de la broche SIN vers le registre interne. Une fois toutes les données téléchargées, un court signal XLAT élevé capture les données série dans des registres internes. Registres internes - Robinets-vannes déclenchés par XLAT. Toutes les données sont transmises dans le bit le plus significatif vers l'avant.
Loquet - un loquet / loquet / pince.
Front montant - bord d'attaque de l'impulsion
MSB d'abord - le bit le plus significatif (le plus Ă gauche) vers l'avant.
pour synchroniser les données - transmettre les données séquentiellement (au niveau du bit).
Le mot verrou se trouve souvent dans la documentation des puces et est traduit de différentes manières, je vais donc me permettre de comprendre
petit programme éducatifLe pilote LED est essentiellement un registre à décalage. «Shift» dans le nom est un mouvement de données au niveau du bit à l'intérieur de l'appareil: chaque nouveau bit poussé vers l'intérieur pousse la chaîne entière devant lui. Étant donné que personne ne veut observer le 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 verrou - c'est une sorte de salle d'attente où les bits sont disposés dans la séquence souhaitée. Lorsque tout est prêt, l'obturateur s'ouvre et les bits sont envoyés au travail, remplaçant le lot précédent. Le mot verrou dans la documentation des microcircuits implique presque toujours un tel amortisseur, quelle que soit la combinaison utilisée.
Ainsi, le transfert de données vers le DM634 est le suivant: réglez l'entrée DAI sur le bit de poids fort de la LED distante, tirez le DCK de haut en bas; réglez l'entrée DAI sur le bit suivant, tirez DCK; et ainsi de suite, jusqu'à ce que tous les bits soient transmis ( cadencés ), après quoi nous tirons le LAT. Cela peut être fait manuellement ( bit-bang ), mais il est préférable d'utiliser l'interface SPI affinée spécialement pour cela, car elle est présentée sur notre STM32 en deux exemplaires.
Blue Tablet STM32F103
Introduction: les contrôleurs STM32 sont beaucoup plus compliqués qu'Atmega328 qu'ils ne peuvent effrayer. Dans le même temps, pour des raisons d'économie d'énergie, presque tous les périphériques sont désactivés au démarrage et la fréquence d'horloge est de 8 MHz depuis une source interne. Heureusement, les programmeurs de la STM ont écrit du code qui a amené la puce aux 72 MHz «calculés», et les auteurs de tous les IDE que je connaissais l'ont inclus dans la procédure d'initialisation, nous n'avons donc pas besoin de synchroniser (mais vous pouvez, si vous le voulez vraiment ). Mais vous devez allumer les périphériques.
Documentation: La populaire puce STM32F103C8T6 est installée sur la Blue Pill, il y a deux documents utiles:
Dans une fiche technique, nous pouvons être intéressés par:
- Brochage - brochage des puces - au cas où nous déciderions de fabriquer nous-mêmes des planches;
- Carte mémoire - carte mémoire pour une puce spécifique. Dans le manuel de référence, il y a une carte pour toute la ligne, elle répertorie les registres qui ne sont pas sur les nôtres.
- Définitions des broches du tableau - répertoriant les fonctions principales et alternatives des broches; pour la «pilule bleue» sur Internet, vous pouvez trouver des images plus pratiques avec une liste de broches et leurs fonctions. Par conséquent, recherchez immédiatement le brochage Blue Pill sur Google et gardez cette image à portée de main:

NB: dans l'image provenant d'Internet, une erreur a été remarquée dans les commentaires, pour laquelle merci. L'image a été remplacée, mais c'est une leçon - il est préférable de vérifier les informations provenant de non-fiches techniques.
Nous supprimons la fiche technique, ouvrons le Manuel de référence, maintenant nous ne l'utilisons que.
Procédure: nous traitons les entrées / sorties standard, configurons SPI, allumons les périphériques souhaités.
Entrée-sortie
Les E / S Atmega328 sont extrĂŞmement simples, c'est pourquoi l'abondance d'options STM32 peut prĂŞter Ă confusion. Maintenant, nous n'avons besoin que de conclusions, mais mĂŞme il y a quatre options:

sortie drain ouvert, sortie push-pull, sortie push-pull alternative, drain ouvert alternative
" Push-pull " ( push-pull ) - la conclusion habituelle d'Arduina, la broche peut être soit HAUTE soit BASSE. Mais avec le "drain ouvert" il y a des difficultés , bien qu'en fait tout soit simple ici:


Configuration de la sortie / lorsque le port est affecté à la sortie: / le tampon de sortie est activé: / - mode drain ouvert: "0" active N-MOS dans le registre de sortie, "1" laisse le port en mode Hi-Z dans le registre de sortie (P-MOS n'est pas activé ) / - mode «pull-push»: «0» dans le registre de sortie active N-MOS, «1» dans le registre de sortie active P-MOS.
Toute la différence entre le drain ouvert et le push-pull est que dans la première broche, il ne peut pas accepter l'état HIGH: lors de l'écriture d'une unité dans le registre de sortie, il passe en haute impédance , Hi -Z ). Lors de l'enregistrement du zéro, la broche dans les deux modes se comporte de la même façon, à la fois logiquement et électriquement.
En mode de sortie normal, la broche traduit simplement le contenu du registre de sortie. Dans l '"alternative", elle est contrôlée par la périphérie correspondante (voir 9.1.4):

Si le bit de port est configuré comme sortie de fonction alternative, le registre de sortie est désactivé et la broche est connectée au signal de sortie périphérique
Une fonctionnalité alternative pour chaque broche est décrite dans la fiche technique des définitions de broche et figure sur l'image téléchargée. Lorsqu'on lui a demandé quoi faire si la broche a plusieurs fonctions alternatives, la réponse donne une note de bas de page dans la fiche technique:

Si plusieurs blocs périphériques utilisent la même broche, afin d'éviter un conflit entre des fonctions alternatives, un seul bloc périphérique doit être utilisé en même temps, la commutation utilisant le bit d'activation d'horloge périphérique (dans le registre RCC correspondant).
Enfin, les broches en mode sortie ont également une vitesse d'horloge. Ceci est une autre fonction d'économie d'énergie, dans notre cas, nous la réglons simplement au maximum et l'oublions.
Donc: nous utilisons SPI, donc deux broches (avec des données et un signal d'horloge) devraient être une «fonction push-push alternative», et une autre (LAT) devrait être «pull-push normale». Mais avant de les attribuer, nous traiterons avec SPI.
SPI
Un autre petit programme éducatifSPI ou Serial Peripherial Interface (interface périphérique série) - une interface simple et très efficace pour la communication MK avec d'autres MK et le monde extérieur en général. Le principe de son fonctionnement a déjà été décrit ci-dessus, en ce qui concerne le driver LED chinois (dans le manuel de référence, voir section 25). SPI peut fonctionner en modes maître («maître») et esclave («esclave»). SPI dispose de quatre canaux de base, dont tous ne sont pas nécessairement impliqués:
- MOSI, Master Output / Slave Input: cette broche en mode maître envoie, mais en mode esclave, elle reçoit des données;
- MISO, Master Input / Slave Output: au contraire, dans le maître accepte, dans l'esclave - donne;
- SCK, Serial Clock: définit la fréquence du transfert de données dans le maître ou reçoit un signal d'horloge dans l'esclave. Bat essentiellement les bits;
- SS, Slave Select: via ce canal, l'esclave apprend qu'il en veut quelque chose. Sur STM32, il est appelé NSS, où N = négatif, c'est-à -dire le contrôleur devient un esclave s'il y a de la terre dans ce canal. Se combine bien avec le mode Open Drain Output, mais c'est une autre histoire.
Comme tout le reste, le SPI du STM32 est riche en fonctionnalités, ce qui le rend quelque peu difficile à comprendre. Par exemple, cela peut fonctionner non seulement avec le SPI, mais aussi avec l'interface I2S, et dans la documentation leurs descriptions sont mélangées, vous devez couper l'excédent en temps opportun. Notre tâche est extrêmement simple: il vous suffit d'envoyer des données en utilisant uniquement MOSI et SCK. Nous allons à la section 25.3.4 (communication semi-duplex), où nous trouvons 1 horloge et 1 fil de données unidirectionnel (1 signal d'horloge et 1 flux de données unidirectionnel):

Dans ce mode, l'application utilise SPI en mode émission uniquement ou en 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 une broche d'entrée-sortie régulière. Dans ce cas, il suffit que l'application ignore le tampon Rx (si vous le lisez, il n'y aura pas de données transmises).
Eh bien, la broche MISO est libre de nous, connectons-y le signal LAT. Nous traiterons de Slave Select, qui peut être contrôlé par programmation sur le STM32, ce qui est extrêmement pratique. Nous lisons le paragraphe du même nom dans la section 25.3.1 de la description générale SPI:

Contrôle de programme NSS (SSM = 1) / Des informations sur la sélection de l'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, nous recherchons l'adresse de base dans la fiche technique - dans la section 3.3 Carte mémoire:

Eh bien, nous commençons:
#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))
Nous ouvrons la section 25.3.3 avec le dicton «Configuration de SPI en mode maître»:

1. Réglez la vitesse d'horloge série avec les bits BR [2: 0] dans le registre SPI_CR1.
Les registres sont collectés dans la section du manuel de référence du même nom. CR1 a un décalage d'adresse ( décalage d'adresse ) de 0x00, par défaut, tous les bits sont réinitialisés ( valeur de réinitialisation 0x0000):

Les bits BR définissent le diviseur de fréquence d'horloge du contrôleur, déterminant ainsi la fréquence à laquelle le SPI fonctionnera. Nous aurons une fréquence de STM32 de 72 MHz, le pilote LED, selon sa fiche technique, fonctionne avec une fréquence allant jusqu'à 25 MHz, il est donc nécessaire de 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;
2. Définissez les bits CPOL et CPHA pour déterminer la relation entre le transfert de données et la synchronisation de l'interface série (voir schéma page 240)
Étant donné que nous lisons ici les fiches techniques et que nous ne considérons pas les circuits, étudions mieux la description textuelle des bits CPOL et CPHA à la page 704 (SPI General Description):

Phase d'horloge et polarité
En utilisant les bits CPOL et CPHA du registre SPI_CR1, quatre options pour les relations de synchronisation peuvent être sélectionnées par programme. Le bit CPOL (polarité d'horloge) contrôle l'état de l'horloge lorsqu'aucune donnée n'est transmise. Ce bit contrôle les modes maître et esclave. Si le CPOL est réinitialisé, la broche SCK est faible en mode veille. Si le bit CPOL est défini, la broche SCK est à un niveau élevé en mode inactif.
Si le bit CPHA (phase d'horloge) est défini, le deuxième front du signal SCK agit comme une trappe du bit haut (décroissant si CPOL est réinitialisé, ou montant 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 front avant du signal SCK agit comme une trappe du bit haut (vers le bas si CPOL est défini, ou vers le haut si CPOL est effacé). Les données sont capturées par le premier changement du signal d'horloge.
Après avoir fumé cette connaissance, nous concluons que les deux bits doivent rester des zéros, car nous avons besoin que le signal SCK reste faible lorsqu'il n'est pas utilisé et que les données soient transmises le long du front d'attaque de l'impulsion (voir Rising Edge dans la fiche technique du DM634).
Soit dit en passant, nous avons ici rencontré pour la première fois une caractéristique du vocabulaire dans les fiches techniques ST: la phrase «réinitialiser le bit à zéro» est écrite pour réinitialiser un bit , et non pour effacer un bit , comme, par exemple, dans Atmega.
3. Définissez le bit DFF pour définir un format de bloc de données 8 bits ou 16 bits.
J'ai pris spécifiquement le DM634 16 bits, afin de ne pas déranger avec la transmission de données PWM 12 bits, comme le DM633. DFF est logique de mettre dans une unité:
#define DFF 0x0800 _SPI2_ (_SPI_CR1) |= DFF;
4. Configurez le bit LSBFIRST dans le registre SPI_CR1 pour déterminer le format de bloc
LSBFIRST, comme son nom l'indique, active le bit de poids faible. Mais le DM634 veut recevoir des données en commençant par le bit haut. Par conséquent, nous la laissons de côté.
5. En mode matériel, si une entrée de la broche NSS est requise, envoyez un signal haut à la broche NSS pendant toute la séquence de transfert d'octets. En mode programme NSS, définissez les bits SSM et SSI dans le registre SPI_CR1. Si la broche NSS doit fonctionner en 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;
6. Les bits MSTR et SPE doivent être définis (ils ne le sont que si un signal haut est appliqué au NSS)
En fait, avec ces bits, nous attribuons notre SPI en tant que maître et l'activons:
#define MSTR 0x0004 #define SPE 0x0040 _SPI2_ (_SPI_CR1) |= MSTR;
SPI est configuré, écrivons des fonctions qui envoient immédiatement des octets au pilote. Nous continuons à lire 25.3.3 "Configuration de SPI en mode maître":

Procédure 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 en mode parallèle (depuis le bus interne) pendant la transmission du premier bit, après quoi il est transmis en mode série à la broche MOSI, le premier ou le dernier bit en avant, selon le réglage du bit LSBFIRST dans le registre CPI_CR1. L'indicateur TXE est défini après le transfert des données du tampon Tx vers le registre à décalage , et une interruption est également créée si le bit TXEIE est défini dans le registre CPI_CR1.
J'ai souligné quelques mots dans la traduction pour attirer l'attention sur une caractéristique de la mise en œuvre de SPI dans les contrôleurs STM. Sur Atmega, l'indicateur TXE ( Tx Empty , Tx est vide et prêt à recevoir des données) n'est défini qu'après que tout l'octet s'est éteint . Et ici, ce drapeau est défini après que l'octet est poussé dans le registre à décalage interne. Puisqu'il y est poussé simultanément par tous les bits (en parallèle), puis que les données sont transmises séquentiellement, TXE est défini avant que l'octet ne soit complètement envoyé. Ceci est important car dans le cas de notre pilote LED, nous devons tirer la broche LAT après l'envoi de toutes les données, c'est-à -dire Seul le drapeau TXE ne nous suffira pas.
Et cela signifie que nous avons besoin d'un autre drapeau. Voyons dans 25.3.7 - «Indicateurs d'état»:

<...>

Drapeau BUSY
Le drapeau BSY est défini et réinitialisé par le matériel (l'écriture n'y affecte rien). L'indicateur BSY indique l'état de la couche de communication SPI.
Il est réinitialisé:
lorsque le transfert est terminé (sauf pour le mode maître, si le transfert est continu)
lorsque SPI est désactivé
lorsqu'une erreur de mode assistant se produit (MODF = 1)
Si la transmission n'est pas continue, le drapeau BSY est effacé entre chaque transmission de données.
D'accord, venez à portée de main. Nous découvrons où se trouve le tampon Tx. Pour ce faire, lisez le "SPI Data Register":

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 le second pour la lecture (tampon de réception). L'écriture dans le registre de données écrit dans le tampon Tx et la lecture dans le registre de données renvoie la valeur contenue dans le tampon Rx.
Eh bien, le registre d'état, où il y a des drapeaux TXE et BSY:

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;
Eh bien, puisque nous devons transmettre 16 octets deux fois, selon le 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);
Mais nous ne savons toujours pas comment tirer la broche LAT, nous allons donc revenir aux E / S.
Attribuer des broches
Dans STM32F1, les registres responsables de l'état des broches sont assez inhabituels. Il est clair qu'il y en a plus qu'Atmega, mais ils diffèrent également des autres puces STM. Section 9.1 Description générale de GPIO:

Chacun des ports d'entrée / sortie à 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 définition / réinitialisation 32 bits (GPIOx_BSRR), 16 bits un registre de réinitialisation (GPIOx_BRR) et un registre de bloc 32 bits (GPIOx_LCKR).
Insolites, mais plutôt gênants, les deux premiers registres sont là , car 16 broches de ports sont éparpillées au format «quatre bits par frère». C'est-à -dire les broches de zéro à septième se trouvent dans la CRL et les autres dans la CRH. En même temps, le reste des registres s'intègre avec succès dans les bits de toutes les broches du port - restant souvent à moitié «réservés».
Par souci de simplicité, commencez à la fin de la liste.
Nous n'avons pas besoin d'un registre de bloc.
Les registres de configuration et de réinitialisation sont assez drôles en ce qu'ils se dupliquent partiellement: vous pouvez tout écrire uniquement dans BSRR, où les 16 bits les plus élevés remettront la broche à zéro, et les plus bas - mis à 1, ou utiliser également BRR, les 16 bits inférieurs qui ne réinitialisent que la broche . J'aime la deuxième option. Ces registres sont importants en ce qu'ils fournissent un accès atomique aux broches:


Installation ou réinitialisation atomique
Vous n'avez pas besoin de désactiver les interruptions lors de la programmation de GPIOx_ODR au niveau des bits: vous pouvez modifier un ou plusieurs bits avec une opération d'écriture atomique APB2. Ceci est réalisé en écrivant «1» dans le registre de définition / réinitialisation (GPIOx_BSRR ou, pour la réinitialisation uniquement, dans GPIOx_BRR) le bit que vous souhaitez modifier. Les autres bits resteront inchangés.
Les registres de données ont des noms assez parlants - IDR = registre de direction d'entrée, registre d'entrée; ODR = registre de direction de sortie, registre de sortie. Dans le projet actuel, nous n'en aurons pas besoin.
Et enfin, les registres de contrôle. Puisque nous sommes intéressés par les broches du deuxième SPI, à savoir PB13, PB14 et PB15, nous examinons immédiatement CRH:

Et nous voyons qu'il faudra écrire quelque chose en bits du 20 au 31.
Nous avons déjà compris ce que nous voulons des broches, donc ici je peux me passer d'une capture d'écran, dites simplement que MODE définit la direction (entrée, si les deux bits sont définis sur 0) et la vitesse des broches (nous avons besoin de 50 MHz, c'est-à -dire les deux broche en "1"), et CNF définit le mode: normal "push-push" - 00, "alternative" - ​​10. Par défaut, comme nous le voyons ci-dessus, toutes les broches ont le troisième bit à partir du bas (CNF0), il les définit sur mode d' entrée flottant .
Puisque je prévois de faire autre chose avec cette puce, pour plus de simplicité, j'ai défini toutes les valeurs possibles de MODE et CNF 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 (l'adresse de base est 0x40010C00), code:
#define _PORTB_(mem_offset) (*(volatile uint32_t *)(0x40010C00 + (mem_offset))) #define _BRR 0x14 #define _BSRR 0x10 #define _CRL 0x00 #define _CRH 0x04
Et, en conséquence, vous pouvez écrire des définitions pour le LAT, ce qui va contracter les registres BRR et BSRR:
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14) #define LAT_low() _PORTB_(_BRR) = (1<<14)
(LAT_low juste par inertie, en quelque sorte ça a toujours été, laissez-vous rester)
Maintenant, tout est déjà génial, ne fonctionne tout simplement pas. Parce que c'est STM32, il économise de l'électricité, ce qui signifie que vous devez activer l'horloge pour les périphériques nécessaires.
Activer le timing
L'horloge est responsable de l'horloge, ils sont également horloge. Et nous pouvions déjà voir l'abréviation RCC. Nous le recherchons dans la documentation: c'est Reset et Clock Control.
Comme cela a été dit ci-dessus, heureusement, les gens de la STM ont fait la partie la plus difficile du thème du timing pour nous, merci beaucoup pour eux (je vais vous donner un lien vers le site Web de Di Halt pour montrer clairement à quel point cela est confus ). Nous avons juste besoin des registres chargés d'activer les horloges périphériques (registres d'activation d'horloge périphérique). Tout d'abord, trouvez l'adresse de base de RCC, elle est au tout début de la "Memory Card":

#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))
Et puis, soit cliquez sur le lien oĂą essayer de trouver quelque chose dans la plaque, soit, bien mieux, parcourez les descriptions des registres d'inclusion dans les sections sur les registres d'activation . OĂą nous trouvons RCC_APB1ENR et RCC_APB2ENR:


Et en eux, respectivement, des bits, y compris l'horloge SPI2, IOPB (I / O Port B) et des fonctions alternatives (AFIO).
#define _APB2ENR 0x18 #define _APB1ENR 0x1C #define IOPBEN 0x0008 #define SPI2EN 0x4000 #define AFIOEN 0x0001
Le code final peut être trouvé ici .
S'il y a une opportunité et un désir de tester, alors nous connectons DM634 comme ceci: DAI à PB15, DCK à PB13, LAT à PB14. Nous alimentons le driver à partir de 5 volts, n'oubliez pas de combiner la terre.

STM8 PWM
PWM sur STM8
Lorsque je planifiais cet article, par exemple, j'ai décidé d'essayer d'apprendre certaines fonctionnalités d'une puce inconnue avec juste une fiche technique, afin de ne pas avoir un cordonnier sans bottes. STM8 convenait parfaitement à 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 en l'absence de ces mêmes solutions.
La puce a également une fiche technique et un manuel de référence RM0016 , dans les premières adresses de brochage et d'enregistrement, dans la seconde - tout le reste. STM8 est programmé en C dans le laid IDE ST Visual Develop .
Horloge et E / S
Par défaut, le STM8 fonctionne à une fréquence de 2 MHz, cela doit être corrigé immédiatement.

Horloge HSI (vitesse interne)
L'horloge HSI est obtenue à partir d'un oscillateur RC interne de 16 MHz avec un diviseur programmable (1 à 8). Il est placé dans le registre du diviseur d'horloge (CLK_CKDIVR).
Remarque: au début, l'oscillateur HSI RC avec diviseur 8 est sélectionné comme source d'horloge principale.
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 démarrer PWM et connecter les LED, nous regardons le brochage:

La puce est petite, de nombreuses fonctions sont suspendues sur les mêmes broches. Le fait que, entre crochets, soit une «fonctionnalité alternative», elle est commutée par des « octets d'options » - quelque chose comme les fusibles d'Atmega. Vous pouvez modifier leurs valeurs par programme, mais 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échargements avec Visual Develop) qui peut modifier ces octets. Le brochage montre que les conclusions de CH1 et CH2 du premier temporisateur sont cachées entre crochets; il est nécessaire de mettre les bits AFR1 et AFR0 dans STVP, et le second transfèrera également la sortie de CH1 du deuxième temporisateur de PD4 à PC5.
Ainsi, 6 broches contrĂ´leront les LED: PC6, PC7 et PC3 pour le premier temporisateur, PC5, PD3 et PA3 pour le second.
La configuration des broches d'E / S elles-mĂŞmes sur le STM8 est plus simple et plus logique que sur le STM32:
- familier avec le registre de direction des données Atmega: 1 = sortie;
- le premier registre de commande CR1 à la sortie définit 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 à la 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
Configuration PWM
Définissons d'abord les termes:
- PWM Frequency - fréquence avec laquelle le chronomètre tourne;
- Auto-rechargement, AR - valeur de chargement automatique à laquelle la minuterie comptera (période d'impulsion);
- Événement de mise à jour, UEV - un événement qui se produit lorsque le temporisateur a compté pour AR;
- Cycle d'utilisation PWM - Cycle d'utilisation PWM, souvent appelé "cycle d'utilisation";
- Capture / Compare Value - valeur pour la capture / comparaison, après avoir compté jusqu'à ce que le temporisateur fasse quelque chose (dans le cas de PWM, il inverse le signal de sortie);
- Preload Value - valeur préchargée. La valeur de comparaison ne peut pas changer pendant que la minuterie tourne, sinon le cycle PWM s'arrêtera. Par conséquent, les nouvelles valeurs transmises sont placées dans le tampon et retirées de là lorsque le temporisateur atteint la fin du comptage et est réinitialisé;
- Modes alignés sur les bords et alignés sur le centre - alignement sur la bordure et au centre, le même que le PWM rapide Atmelovskie et le PWM à correction de phase .
- OCiREF, Output Compare Reference Signal - le signal de sortie de référence, en fait, qui en mode PWM est sur la broche correspondante.
Comme il ressort déjà du brochage, deux temporisateurs ont les capacités PWM - le premier et le second. Les deux sont en 16 bits, le premier a beaucoup de fonctionnalités supplémentaires (en particulier, il peut compter à la fois vers le haut et vers le bas). Nous avons besoin des deux pour travailler de la même manière, j'ai donc décidé de commencer par la seconde évidemment la plus pauvre, afin de ne pas utiliser accidentellement quelque chose qui n'y est pas. Un problème est que la description de la fonctionnalité PWM de tous les temporisateurs dans le manuel de référence se trouve dans le chapitre sur le premier temporisateur (17.5.7 Mode PWM), vous devez donc aller et venir tout au long du document.
PWM sur STM8 a un avantage important sur PWM Atmega:

PWM avec alignement des bords
Configuration du compte de bas en haut
Le décompte 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 OCiREF PWM est maintenu haut tandis que TIM1_CNT <TIM1_CCRi. Sinon, cela prend un faible niveau. Si la valeur de comparaison dans TIM1_CCRi est supérieure à la valeur de démarrage (registre TIM1_ARR), le signal OCiREF est maintenu à 1. Si la valeur de comparaison est 0, OCiREF est maintenu à zéro. ...
Le temporisateur STM8 pendant l' événement de mise à jour vérifie d'abord la valeur de comparaison , puis ne donne qu'un signal de référence. La minuterie d'Atmega mélange d'abord, puis compare, à la suite de quoi, lorsque la compare value == 0
la sortie entraîne une aiguille qui doit être combattue 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
), nous considérons de bas en haut, l'alignement le long de la frontière. Étant donné que les ampoules sont connectées à la puce par des cathodes, le PWM doit sortir 0 (la LED est allumée) avant de comparer la valeur et 1 après.
Nous avons déjà lu quelques informations sur le mode PWM , nous trouvons donc le registre souhaité du deuxième temporisateur en cherchant dans le manuel de référence cette phrase (18.6.8 - TIMx_CCMR1):

110: Premier mode PWM - lors du comptage de bas en haut, le premier canal est actif lorsque TIMx_CNT <TIMx_CCR1. Sinon, le premier canal est inactif. [plus loin dans le document copier-coller erroné du temporisateur 1]
111: Deuxième mode PWM - lors du comptage de bas en haut, le premier canal est inactif tandis que TIMx_CNT <TIMx_CCR1. Sinon, le premier canal est actif.
Comme les LED sont connectées aux cathodes MK, le deuxième mode nous convient (le premier aussi, mais nous ne le savons pas encore).

Bit 3 OC1PE: Activer la précharge de la sortie 1
0: registre de précharge à TIMx_CCR1 désactivé. Vous pouvez écrire à TIMx_CCR1 à tout moment. La nouvelle valeur fonctionne immédiatement.
1: Le registre de préchargement à TIMx_CCR1 est activé. Les opérations de lecture / écriture accèdent au registre de précharge. 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écharge doivent être activés. ( TIMx_CR1 OPM).
, , , :
#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 , :
#define TIM2_ARRH *(volatile uint8_t *)0x00530F #define TIM2_ARRL *(volatile uint8_t *)0x005310 TIM2_ARRH = 0; TIM2_ARRL = 255;
-, , . , , 256. TIM2_PSCR :
#define TIM2_PSCR *(volatile uint8_t *)0x00530E TIM2_PSCR = 8;
. Capture/Compare Enable : , . , , .. PWM Mode 1. :
#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;
, , TIMx_CR1:

#define TIM2_CR1 *(volatile uint8_t *)0x005300 TIM2_CR1 |= 1;
AnalogWrite(), . Capture/Compare registers , : 8 TIM2_CCRxL TIM2_CCRxH. 8- , :
#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; }
, , 100% ( 255 ). , , .
, .
( , «» , ). . , .. , 16- Prescaler High Low . … . ?
1, , . 17.7.30 Break register (TIM1_BKR) , :

#define TIM1_BKR *(volatile uint8_t *)0x00526D TIM1_BKR = (1<<7);
, .

STM8 Multiplex
STM8
- , RGB- . – LED-, , - , , ( persistence of vision , ). - - .
:
.. , , «» . . , , , UEV RGB-.
LED , «», . :
uint8_t colors[8][3];
, , .
uint8_t cnt;
, , CD74HC238. – , <<
. ( 0, 1 2) X, ( 1<<X
). . , – , , . , .
CD74HC238 , . P-MOSFET, , .. 20 , absolute maximum ratings . CD74HC238 :

H = , L = , X –
E2 E1 , E3, A0, A1 A3 PD5, PC3, PC4 PC5 STM8. , , push-pull .
, , :
-, Update Event (UEV), , LED. Update Interrupt Enable

#define TIM2_IER *(volatile uint8_t *)0x005303 //enable interrupt TIM2_IER = 1;
, ghosting – . - , , UEV, , LED - . (0 = , 255 = ) . C'est-à -dire , UEV .
:
//set polarity TIM2_CCER1 |= (CC1P | CC2P); TIM2_CCER2 |= CC3P;
r, g b 255 .
, - . - , .
ST Visual Develop, main.c
stm8_interrupt_vector.c
, . NonHandledInterrupt
. .
, :

13 TIM2 /
14 TIM2 /
LED UEV, â„–13.
, -, stm8_interrupt_vector.c
, â„–13 (IRQ13) :
{0x82, TIM2_Overflow}, /* irq13 */
-, main.h
:
#ifndef __MAIN_H #define __MAIN_H @far @interrupt void TIM2_Overflow (void); #endif
, , 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; }
. rim
– Programming Manual :
//enable interrupts _asm("rim");
– sim
– . «», .
– .

- , , . , .