STM32, interface série I2C. STM32, interface série I2C Description de l'interface Stm32 i2c suite

Certaines personnes aiment les tartes, d’autres non.

L'interface i2c est largement répandue et utilisée. Dans stm32f4, il existe jusqu'à trois modules qui implémentent ce protocole.
Naturellement, avec plein soutien tout ça.

Travailler avec le module est, en général, le même que dans les autres contrôleurs : vous lui donnez des commandes, il les exécute et rapporte le résultat :
I> Nous sommes allés COMMENCER.
S> Ok, je l'ai envoyé.
Moi> Cool, envoie l'adresse maintenant. Comme ceci : 0xXX.
S> Ok, je l'ai envoyé. On m'a dit que ACK. Allons-nous en.
I> Toujours en vie, bien. Voici le numéro de registre : 0xYY, - c'est parti.
S> Envoyé, reçu ACK.
I> Maintenant, envoyez-lui les données, voici l'octet : 0xZZ.
S> Envoyé, il accepte plus : ACK.
Je le baise, pas encore. Ils se sont arrêtés.
S> D'accord.

Et tout est à peu près dans cet esprit.

DANS ce contrôleur Les broches i2c sont dispersées sur les ports comme ceci :
PB6 : I2C1_SCL
PB7 : I2C1_SDA

PB8 : I2C1_SCL
PB9 : I2C1_SDA

PB10 : I2C2_SCL
PB11 : I2C2_SDA

PA8 : I2C3_SCL
PC9 : I2C3_SDA
En général, il est pratique de consulter le brochage des périphériques à la page 59.

Étonnamment, pour travailler avec i2c, vous avez besoin de tous ses registres, heureusement il y en a peu :
I2C_CR1- des commandes au module pour l'envoi de commandes/états et la sélection des modes de fonctionnement ;
I2C_CR2- mise en place du DMA et indication de la fréquence de fonctionnement du module (2-42 MHz) ;
I2C_OAR1- réglage de l'adresse de l'appareil (pour esclave), de la taille de l'adresse (7 ou 10 bits) ;
I2C_OAR2- réglage de l'adresse de l'appareil (s'il y a deux adresses) ;
I2C_DR- registre de données ;
I2C_SR1- registre d'état du module ;
I2C_SR2- registre d'état (esclave, doit être lu si les drapeaux ADDR ou STOPF sont activés dans SR1) ;
I2C_CCR- régler la vitesse de l'interface ;
I2C_TRISE- mise en place des timings des fronts.

Cependant, la moitié d’entre eux sont du type « écrivez-le et oubliez-le ».

La carte STM32F4-Discovery dispose déjà d'un périphérique I2C avec lequel vous pouvez vous entraîner : CS43L22, DAC audio. Il est connecté aux broches PB6/PB9. L'essentiel est de ne pas oublier d'appliquer un niveau élevé sur la broche PD4 (~RESET se trouve là), sinon le DAC ne répondra pas.

La procédure de configuration est approximativement la suivante :
1 . Autoriser la synchronisation des ports et du module lui-même.
Nous avons besoin des broches PB6/PB9, nous devons donc définir le bit 1 (GPIOBEN) dans le registre RCC_AHB1ENR pour activer le port.
Et définissez le bit 21 (I2C1EN) dans le registre RCC_APB1ENR pour activer le module I2C. Pour les deuxième et troisième modules, les numéros de bits sont respectivement 22 et 23.
2 . Ensuite, les broches sont configurées : sortie Oped Drain (GPIO->OTYPER), mode de fonction alternatif (GPIO->MODER) et numéro de fonction alternatif (GPIO->AFR).
Si vous le souhaitez, vous pouvez configurer un pull-up (GPIO->PUPDR), s'il n'est pas sur la carte (et un pull-up vers l'alimentation des deux lignes est requis sous quelque forme que ce soit). Le numéro pour I2C est toujours le même : 4. C'est bien qu'il y ait un numéro distinct pour chaque type de périphérique.
3 . La fréquence d'horloge actuelle du périphérique Fpclk1 (exprimée en MHz) est indiquée dans le registre CR2. Si je comprends bien, cela est nécessaire pour calculer différents délais de protocole.
À propos, il devrait y en avoir au moins deux pour le mode normal et au moins quatre pour le mode rapide. Et si vous avez besoin d'une vitesse maximale de 400 kHz, il faut également la diviser par 10 (10, 20, 30, 40 MHz).
Fréquence d'horloge maximale autorisée : 42 MHz.
4 . La vitesse de l'interface est configurée dans le registre CCR et le mode est sélectionné (normal/rapide).
La signification est : Tsck = CCR * 2 * Tpckl1, c'est-à-dire la période SCK est proportionnelle au CCR (pour le mode rapide tout est un peu plus délicat, mais c'est décrit dans RM).
5 . Le temps de front montant maximum dans le registre TRISE est ajusté. Pour le mode standard, ce temps est de 1 µs. Dans le registre, vous devez inscrire le nombre de cycles de bus qui correspondent à ce temps, plus un :
si le cycle Tpclk1 dure 125 ns, alors écrivez (1000 ns / 125 ns) + 1 = 8 + 1 = 9.
6 . La génération de signaux d'interruption (erreur, état et données) est activée en option ;
7 . Le module s'allume : le flag PE dans le registre CR1 est mis à 1.

Ensuite, le module fonctionne comme il se doit. Il vous suffit d'implémenter le bon ordre des commandes et de vérifier les résultats. Par exemple, une entrée de registre :
1 . Vous devez d'abord envoyer START en définissant un indicateur du même nom dans le registre CR1. Si tout va bien, après un certain temps, le drapeau SB sera défini dans le registre SR1.
Je voudrais noter un point - s'il n'y a pas de pull-up sur la ligne (et qu'ils sont à 0), alors ce drapeau peut ne pas attendre du tout.
2 . Si le drapeau est reçu, nous envoyons l'adresse. Pour une adresse sur sept bits, on l'écrit simplement en DR exactement telle qu'elle sera sur la ligne (7 bits d'adresse + bit de direction). Pour dix bits, un algorithme plus complexe.
Si l'appareil répond à l'adresse par un ACK, alors l'indicateur ADDR apparaîtra dans le registre SR1. Dans le cas contraire, alors l'indicateur AF (Acknowledge Failure) apparaîtra.
Si ADDR apparaît, vous devez lire le registre SR2. Vous n’avez rien à regarder ici, la simple lecture séquentielle de SR1 et SR2 réinitialise cet indicateur. Et pendant que l'indicateur est activé, le SCL est maintenu bas par le maître, ce qui est utile si vous devez demander au périphérique distant d'attendre avant d'envoyer des données.
Si tout va bien, alors le module passera alors en mode de réception ou de transmission de données, en fonction du bit le moins significatif de l'adresse envoyée. Pour écrire, il doit être zéro, pour lire, il doit être un.
mais nous examinons le dossier, donc nous supposerons qu'il y avait un zéro là-bas.
3 . Ensuite, nous envoyons l'adresse du registre qui nous intéresse. De la même manière, en l'écrivant dans DR. Après la transmission, les indicateurs TXE (tampon de transmission vide) et BTF (transfert terminé) sont définis.
4 . Viennent ensuite les données qui peuvent être envoyées pendant que l'appareil répond par un ACK. Si la réponse est NACK, ces indicateurs ne seront pas définis.
5 . A la fin du transfert (ou en cas de condition inattendue), nous envoyons STOP : le flag du même nom est positionné dans le registre CR1.

A la lecture, tout est pareil. Change seulement après avoir écrit l'adresse du registre.
Au lieu d'écrire des données, START est renvoyé (redémarrage) et l'adresse est envoyée avec le bit le moins significatif activé (signe de lecture).
Le module attendra les données de l'appareil. Pour l'encourager à envoyer les octets suivants, vous devez définir l'indicateur ACK dans CR1 avant la réception (afin qu'après réception, le module envoie ce même ACK).
Lorsque vous en avez assez, retirez le drapeau, l'appareil verra NACK et deviendra silencieux. Après quoi, nous envoyons STOP de la manière habituelle et nous nous réjouissons des données reçues.

Voici la même chose sous forme de code :
// Initialiser le module void i2c_Init(void) ( uint32_t Clock = 16000000UL; // Fréquence d'horloge du module (system_stm32f4xx.c n'est pas utilisé) uint32_t Speed ​​​​= 100000UL; // 100 kHz // Activer la synchronisation du port GPIOB RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; // Configurer les broches PB6, PB9 // Ouvrir le drain! GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_9; // Le pull-up est externe, il ne peut donc pas être configuré ici! // si nécessaire, voir le registre GPIOB->PUPDR // Numéro de la fonction GPIOB alternative ->AFR &= ~(0x0FUL<< (6 * 4)); // 6 очистим GPIOB->AFR |= (0x04UL<< (6 * 4)); // В 6 запишем 4 GPIOB->AFR &= ~(0x0FUL<< ((9 - 8) * 4)); // 9 очистим GPIOB->AFR |= (0x04UL<< ((9 - 8) * 4)); // В 9 запишем 4 // Режим: альтернативная функция GPIOB->MODER &= ~((0x03UL<< (6 * 2)) | (0x03UL << (9 * 2))); // 6, 9 очистим GPIOB->MODER |= ((0x02UL<< (6 * 2)) | (0x02UL << (9 * 2))); // В 6, 9 запишем 2 // Включить тактирование модуля I2C1 RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // À ce stade, I2C doit être désactivé // Tout réinitialiser (SWRST == 1, réinitialiser) I2C1->CR1 = I2C_CR1_SWRST; // PE == 0, c'est l'essentiel I2C1->CR1 = 0; // Nous supposons que nous fonctionnons à partir de RC (16 MHz) // Il n'y a pas de prescalers dans le système d'horloge (tous 1) // À l'amiable, nous devrions calculer tout cela à partir de // la fréquence d'horloge réelle du module I2C1->CR2 = Horloge / 1000000UL ; // 16 MHz // Ajuster la fréquence ( // Tclk = (1 / Fperiph); // Thigh = Tclk * CCR; // Tlow = Thigh; // Fi2c = 1 / CCR * 2; // CCR = Fperiph / ( Fi2c * 2); uint16_t Valeur = (uint16_t)(Horloge / (Vitesse * 2)); // Valeur minimale : 4 si(Valeur< 4) Value = 4; I2C1->CCR = Valeur ; ) // Fixer le temps de montée limite // En mode standard, ce temps est de 1000 ns // On ajoute simplement un à la fréquence exprimée en MHz (voir RM p. 604). I2C1->TRISE = (Horloge / 1000000UL) + 1 ; // Activer le module I2C1->CR1 |= (I2C_CR1_PE); // Maintenant vous pouvez faire quelque chose ) // Envoyer un octet bool i2c_SendByte(uint8_t Adresse, uint8_t Registre, uint8_t Données) ( if(!i2c_SendStart()) return false; // Adresse de la puce if(!i2c_SendAddress(Address)) return i2c_SendStop (); // Adresse d'enregistrement if(!i2c_SendData(Register)) return i2c_SendStop(); // Données if(!i2c_SendData(Data)) return i2c_SendStop(); // Stop! i2c_SendStop(); return true; ) // Recevoir l'octet bool i2c_ReceiveByte(uint8_t Address, uint8_t Register, uint8_t * Data) ( if(!i2c_SendStart()) return false; // Adresse de la puce if(!i2c_SendAddress(Address)) return i2c_SendStop(); // Enregistrer l'adresse if(! i2c_SendData(Register)) return i2c_SendStop(); // Redémarrer if(!i2c_SendStart()) return false; // Adresse de la puce (lecture) if(!i2c_SendAddress(Address | 1)) return i2c_SendStop(); // Recevoir un octet if(!i2c_ReceiveData(Data)) return i2c_SendStop(); // Stop! i2c_SendStop(); return true; ) Utilisation : ( uint8_t ID = 0; i2c_Init(); // Nous supposons que PD4 est défini à un niveau élevé et le DAC fonctionne (cela doit être fait d'une manière ou d'une autre) // Envoyez un octet à l'appareil avec l'adresse 0x94, pour enregistrer 0x00 avec la valeur 0x00. i2c_SendByte(0x94, 0x00, 0x00); // Reçoit un octet de l'appareil avec l'adresse 0x94 du registre 0x01 (ID) dans le tampon variable i2c_ReceiveByte(0x94, 0x01, &ID) ; )
Bien sûr, vous ne pouvez pas faire cela sauf dans un exemple de formation. L'attente pour que l'action se termine est trop longue pour un contrôleur aussi rapide.

(Guide du développeur pour les microcontrôleurs de la famille HCS08)

Pour contrôler le module I2C, 6 registres de fonctions spéciales sont utilisés :

  • IICC - premier registre de contrôle du module I2C ;
  • IICC2 - deuxième registre de contrôle du module I2C ;
  • IICS - Registre d'état du module I2C ;
  • IICF - Registre de débit en bauds du module I2C ;
  • IICA - Registre d'adresses du module I2C ;
  • IICD est le registre de données du module I2C.

Le MCU de la série QE contient 2 modules I2C et, par conséquent, deux registres de contrôle de chaque type. Par exemple, le premier registre d'état est IIC1S et le deuxième registre d'état est IIC2S.

11.2.8.1. Registre de contrôle IICC

Pour série MK AC. AW, Dx, EL, GB, GT, JM, LC, QE. Q.G. SG, SH, SL
Registre Mode J7 D6 J5 D4 D3 D2 D1 D0
IICC En lisant IICEN IICIE MST Émission TXAK 0 0 0
Enregistrer RSTA
Réinitialiser 0 0 0 0 0 0 0 0
Description des bits :
Nom du bit Description Symbole en langage C
IICEN Bit d'activation du module I2C :
0 — Le contrôleur I2C est désactivé ;
1 - Le contrôleur I2C est activé.
BIICEN
IICIE Bit d'activation d'interruption du module depuis I2C :
0 - Les interruptions de requête I2C sont désactivées ;
1 - Les interruptions de requête I2C sont activées.
bHCIE
MST Bit de sélection du mode de fonctionnement du contrôleur I2C :
0 - Le contrôleur I2C fonctionne en mode esclave ;
1 - Le contrôleur I2C fonctionne en mode Maître.
Lorsque ce bit passe de 0 à 1, un état Start est généré. A l’inverse, lorsqu’un bit passe de 1 à 0, une condition Stop est générée.
bMST
Émission Bit de sélection du sens de transmission sur la ligne de données SDA :
0 — la ligne fonctionne pour la saisie ;
1 - la ligne fonctionne pour la sortie.
bTX
TXAK Bit d'acquittement en mode réception :
0 - un bit d'accusé de réception est généré après réception d'un octet ;
1 - un bit d'octet non acquitté est généré.
Ce bit contrôle la génération d'un bit d'acquittement après réception d'un octet de données, qu'il s'agisse d'un esclave ou d'un maître.
bTXAK
RSTA Si le module I2C fonctionne en mode maître, alors l'écriture d'un 1 sur ce bit provoque la régénération de l'état Start - "Restart". BRSTA

11.2.8.2. Registre de statut IICS

Pour les séries MK AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL
Registre Mode J7 D6 J5 D4 D3 D2 D1 D0
IICS En lisant TCF IAAS OCCUPÉ ARBL 0 SRW IICIF RXAK
Enregistrer
Réinitialiser 0 0 0 0 0 0 0 0
Description des bits :
Nom du bit Description Symbole en langage C
TCF Bit de fin d'échange. Défini une fois l’échange d’un octet terminé :
0 — échange non terminé ;
1 - échange terminé.
L'indicateur TCF est remis à 0 lorsque le registre de données IICD est lu (en mode réception) ou lorsque le registre de données IICD est écrit (en mode transmission).
BTCF
IAAS Indicateur d'adresse d'esclave. Définir si l'appareil fonctionne en mode esclave et que l'adresse transmise dans le message maître est égale à l'adresse esclave, qui est stockée dans le registre d'adresses IICA.
L'indicateur est effacé lors de l'écriture dans le registre IICC.
IAAS
OCCUPÉ Indicateur de ligne occupée. Cet indicateur est défini si le module I2C a reconnu le bit de départ sur la ligne. Le drapeau est effacé lorsque le module détecte un bit d'arrêt sur la ligne.
0 — Le bus I2C est gratuit ;
1 - Le bus I2C est occupé.
bOCCUPÉ
ARBL Indicateur de perte suite à l'arbitrage :
0 - aucune violation dans le fonctionnement du bus I2C ;
1 – il y a une perte d’arbitrage. Le module I2C doit attendre un moment puis recommencer l'opération de transfert.
barbL
SRW Bit de direction de transmission esclave. Ce bit indique l'état du bit R/W dans le champ d'adresse :
0 - l'esclave accepte. Le chef transmet à l'esclave ;
1 - l'esclave transmet. Le chef reçoit de l'esclave.
bSRW
IICIF Flag des demandes d'interruption non servies pour le module I2C. Mis à 1 si l'un des flags est activé : TCF, IAAS ou ARBL.
0 - aucune interruption non traitée ;
1 - il y a des interruptions non desservies.
Le drapeau est réinitialisé en y écrivant 1.
BIICIF
RXAK Bit d'acquittement maître :
0 : l'esclave a confirmé la réception des données ;
1 — l'esclave n'a pas accusé réception de données.
Ce bit reflète l'état du champ ASK sur le chronogramme de l'échange.
BRXAK

11.2.8.3. Registre d'adresses de l'IICA

Registre Mode J7 D6 J5 D4 D3 D2 D1 D0
IICA En lisant ADDR
Enregistrer
Réinitialiser 0 0 0 0 0 0 0 0

Ce registre stocke l'adresse esclave 7 bits attribuée par le développeur. cet appareil lors du développement du système. Cette adresse est automatiquement comparée au code d'adresse que l'esclave a reçu dans le champ d'adresse du bus I2C. Si les adresses correspondent, le bit IAAS dans le registre d'état IICS est activé.

11.2.8.4. Registre de débit en bauds IICF

Pour les séries MK AC, AW, Dx, EL,GB, GT, JM, LC, QE, QG, SG, SH, SL
Registre Mode J7 D6 J5 D4 D3 D2 D1 D0
IICF En lisant MULT ICR
Enregistrer
Réinitialiser 0 0 0 0 0 0 0 0
Description des bits :

Valeurs des coefficients SCL_DIV et SDA_HV

ISR SCL_DIV SDA_HV ISR SCL_DIV SDA_HV
0x00 20 7 0x20 160 17
0x01 22 7 0x21 192 17
0x02 24 8 0x22 224 33
0x03 26 8 0x23 256 33
0x04 28 9 0x24 288 49
0x05 30 9 0x25 320 49
0x06 34 10 0x26 384 65
0x07 40 10 0x27 480 65
0x08 28 7 0x28 320 33
0x09 32 7 0x29 384 33
0x0A 36 9 0x2A 448 65
0x0B 40 9 0x2B 512 65
0x0C 44 11 0x2C 576 97
0x0D 48 11 0x2D 640 97
0x0E 56 13 0x2E 768 129
0x0F 68 13 0x2F 960 129
0x10 48 9 0x30 640 65
0x11 56 9 0x31 768 65
0x12 64 13 0x32 896 129
0x13 72 13 0x33 1024 129
0x14 80 17 0x34 1152 193
0x15 88 17 0x35 1280 193
0x16 104 21 0x36 1536 257
0x17 128 21 0x37 1920 257
0x18 80 9 0x38 1280 129
0x19 96 9 0x39 1536 129
0x1A 112 17 0x3A 1792 257
0x1B 128 17 0x3B 2048 257
0x1C 144 25 0x3C 2304 385
0x1D 160 25 0x3D 2560 385
0x1E 192 33 0x3E 3072 513
0x1F 240 33 0x3F 3840 513

Ce registre stocke deux champs de bits qui déterminent les paramètres de vitesse et de synchronisation de l'échange I2C. La fréquence des signaux de synchronisation est déterminée par la formule :

Le temps d'établissement des données SDA_hold_time sur le bus I2C est l'intervalle de temps entre le moment où le signal SCL est mis à 0 et les données sur la ligne SDA changent. Attribué par le paramètre SDA_HV (SDA_Hold_Value) du tableau du facteur ICR du registre de débit en bauds :

.

11.2.8.5. Registre de données IICD

Pour les séries MK AC, AW, Dx, EL,GB, GT, JM, LC, QE, QG, SG, SH, SL
Registre Mode J7 D6 J5 D4 D3 D2 D1 D0
IICD En lisant DONNÉES I2C
Enregistrer
Réinitialiser 0 0 0 0 0 0 0 0

Si le module I2C fonctionne en mode maître, alors une opération d'écriture dans ce registre initie la communication I2C (mais seulement si le bit de sens de communication dans le registre de contrôle IICC est correctement défini, c'est-à-dire TX = 1). Le premier octet après l'état Start, que le programme écrit dans le registre de données, est interprété par les esclaves comme l'adresse de l'appareil. Par conséquent, le programme doit former correctement le contenu du premier octet. L'opération de lecture du registre renvoie le dernier octet reçu via I2C. L'opération de lecture du registre déclenche également le début de la réception de l'octet suivant, mais uniquement si le bit de direction de communication dans le registre de contrôle IICC est correctement défini, c'est-à-dire à TX = 0 ! Avec TX = 1, une opération de lecture de registre n'entraînera pas la réception d'un nouvel octet via I2C depuis l'esclave.

Si le module I2C fonctionne en mode esclave, alors les données écrites dans ce registre seront transférées vers la ligne SDA du bus I2C lorsque l'appareil maître effectuera un cycle de réception depuis cet esclave. L'opération de lecture du registre renvoie le dernier octet reçu du maître.

11.2.8.6. Registre de contrôle IICC2

Pour les séries MK AC, Dx, EL, JM, QE, SG, SH, SL
Registre Mode J7 D6 J5 D4 D3 D2 D1 D0
IICC En lisant GCAEN ADEXT 0 0 0 AD10 AD9 AD8
Enregistrer
Réinitialiser 0 0 0 0 0 0 0 0

Premiers pas avec le compilateur STM32 et mikroC pour l'architecture ARM - Partie 4 - Connexion LCD basée sur I2C, pcf8574 et HD4478

Je voudrais consacrer le prochain article au travail avec l'interface i2c commune, qui est souvent utilisée dans divers microcircuits connectés à un microcontrôleur.

I2C est un bus qui fonctionne sur deux connexions physiques (en plus du fil commun). On en écrit beaucoup sur Internet ; il existe de bons articles sur Wikipédia. De plus, l'algorithme de fonctionnement du bus est décrit très clairement. En bref, le bus est un bus synchrone bifilaire. Jusqu'à 127 appareils peuvent être sur le bus en même temps (l'adresse de l'appareil est de 7 bits, nous y reviendrons plus tard). Vous trouverez ci-dessous un schéma typique de connexion d'appareils au bus i2c, avec le MK comme appareil maître.


Pour i2c, tous les appareils (maîtres et esclaves) utilisent des sorties à drain ouvert. En termes simples, ils peuvent UNIQUEMENT attirer le pneu vers le SOL. Le niveau de bus élevé est assuré par des résistances pull-up. La valeur de ces résistances est généralement choisie entre 4,7 et 10 kOhm. i2c est assez sensible aux lignes physiques reliant les appareils, donc si une connexion avec une grande capacité est utilisée (par exemple, un long câble fin ou blindé), l'influence de cette capacité peut « brouiller » les bords du signal et interférer avec fonctionnement normal pneus. Plus la résistance de rappel est petite, moins cette capacité a d'influence sur les caractéristiques des fronts du signal, mais plus la CHARGE sur les transistors de sortie des interfaces i2c est GRANDE. La valeur de ces résistances est choisie pour chaque mise en œuvre spécifique, mais elles ne doivent pas être inférieures à 2,2 kOhms, sinon vous pouvez simplement graver les transistors de sortie dans les appareils fonctionnant avec le bus.

Le bus se compose de deux lignes : SDA (ligne de données) et SCL (signal d'horloge). Synchronise le périphérique maître du bus, généralement notre MK. Lorsque SCL est élevé, les informations sont lues à partir du bus de données. L'état SDA ne peut être modifié que lorsque le signal d'horloge est faible.. Lorsque SCL est élevé, le signal sur SDA change lors de la génération de signaux COMMENCER (lorsque SCL est élevé, le signal sur SDA passe de haut en bas) et ARRÊT - lorsque le niveau SCL est haut, le signal sur SDA passe de bas à haut).

Séparément, il faut dire que dans i2c, l'adresse est spécifiée sous la forme d'un nombre de 7 bits. 8 - le bit le moins significatif indique la direction du transfert de données 0 - signifie que l'esclave transmettra des données, 1 - recevra.. En bref, l'algorithme pour travailler avec i2c est le suivant :

  • Haut niveau sur SDA et SCL- le bus est gratuit, vous pouvez commencer à travailler
  • Ascenseurs principaux SCLà 1, et change d'état S.D.A. de 1 à 0 - l'attire vers le sol - un signal se forme COMMENCER
  • Le maître transmet une adresse esclave de 7 bits avec un bit de direction (données sur S.D.A. sont exposés lorsque SCL tiré au sol, et lu par l'esclave lorsqu'il est relâché). Si l'esclave n'a pas le temps de « saisir » le bit précédent, il attire SCL au sol, indiquant clairement au maître que l'état du bus de données n'a pas besoin d'être modifié : "Je suis toujours en train de lire le précédent." Après que le capitaine a largué le pneu, il vérifie l'esclave l'a-t-elle laissée partir ?.
  • Après avoir transmis 8 bits de l'adresse, le maître génère le 9ème cycle d'horloge et libère le bus de données. Si l'esclave entendait son adresse et l'acceptait, alors il va appuyer S.D.A. au sol. C'est ainsi que le signal est formé DEMANDER- accepté, tout va bien. Si l’esclave ne comprend rien, ou s’il n’est tout simplement pas là, alors il n’y aura personne pour appuyer sur le pneu. le maître attendra un temps mort et comprendra qu'il n'a pas été compris.
  • Après avoir transmis l'adresse, si nous avons fixé la direction du maître à l'esclave(8 bits de l'adresse sont égaux à 1), puis le maître transmet les données à l'esclave en n'oubliant pas de vérifier la présence de DEMANDER de l'esclave, en attendant que le périphérique esclave traite les informations entrantes.
  • Lorsque le maître reçoit des données de l'esclave, le maître génère lui-même un signal DEMANDER après avoir reçu chaque octet, et l'esclave contrôle sa présence. Le maître ne peut pas spécifiquement envoyer DEMANDER avant d'envoyer la commande ARRÊT, indiquant généralement clairement à l'esclave qu'il n'est pas nécessaire de fournir d'autres données.
  • Si après l'envoi des données par le maître (mode écriture), il est nécessaire de lire les données de l'esclave, puis le maître génère à nouveau le signal COMMENCER , en envoyant l'adresse de l'esclave avec un indicateur de lecture. (si avant la commande COMMENCER n'a pas été transféré ARRÊT puis une équipe se forme REDÉMARRAGE). Ceci est utilisé pour changer la direction de la communication maître-esclave. Par exemple, nous transmettons l'adresse du registre à l'esclave, puis lisons les données.)
  • À la fin du travail avec l'esclave, le maître génère un signal ARRÊT- à un niveau haut du signal d'horloge, il forme une transition du bus de données de 0 à 1.
Le STM 32 dispose d'émetteurs-récepteurs de bus i2c implémentés matériellement. Il peut y avoir 2 ou 3 modules de ce type dans un MK. Pour les configurer, des registres spéciaux sont utilisés, décrits dans la référence du MK utilisé.

Dans MicroC, avant d'utiliser i2c (ainsi que tout périphérique), il doit être correctement initialisé. Pour ce faire, nous utilisons la fonction suivante (Initialisation en maître) :

I2Cn_Init_Advanced (long non signé : I2C_ClockSpeed, const Module_Struct *module) ;

  • n- numéro du module utilisé, par exemple I2C1 ou I2C2.
  • I2C_ClockSpeed- vitesse du bus, 100 000 (100 kbs, mode standard) ou 400 000 (400 kbs, mode rapide). Le second est 4 fois plus rapide, mais tous les appareils ne le prennent pas en charge
  • *module- pointeur vers un module périphérique par exemple &_GPIO_MODULE_I2C1_PB67, n'oublions pas ici que Assistant de codage (ctrl-espace ) aide beaucoup.
Tout d'abord, vérifions si le bus est libre ; il existe une fonction pour cela I2Cn_Is_Idle(); renvoyant 1 si le bus est gratuit, et 0 s'il y a un échange dessus.

I2Cn_Start();
n- numéro du module i2c utilisé de notre microcontrôleur. La fonction retournera 0 s'il y a une erreur sur le bus et 1 si tout va bien.

Afin de transférer des données vers l'esclave, nous utilisons la fonction :

I2Cn_Write (adresse_esclave de caractère non signé, caractère non signé *buf, nombre long non signé, mode END_mode long non signé);

  • n- numéro du module utilisé
  • adresse_esclave- Adresse esclave 7 bits.
  • *buf- un pointeur vers nos données - un octet ou un tableau d'octets.
  • compter- le nombre d'octets de données transférés.
  • END_mode- que faire après le transfert des données vers l'esclave, END_MODE_STOP - transmettre un signal ARRÊT, ou END_MODE_RESTART envoyer à nouveau COMMENCER, générant un signal REDÉMARRAGE et en faisant clairement comprendre au département que la session avec lui n'est pas terminée et que les données vont maintenant être lues sur lui.
Pour lire les données de l'esclave, utilisez la fonction :

I2Cn_Read (char slave_address, char *ptrdata, nombre long non signé, mode END_mode long non signé);

  • n- numéro du module utilisé
  • adresse_esclave- Adresse esclave 7 bits.
  • *buf- un pointeur vers une variable ou un tableau dans lequel nous recevons des données, tapez char ou short int
  • compter- nombre d'octets de données reçus.
  • END_mode- que faire après avoir reçu des données de l'esclave - END_MODE_STOP - transmettre un signal ARRÊT, ou END_MODE_RESTART envoyer un signal REDÉMARRAGE.
Essayons de connecter quelque chose à notre MK. Pour commencer : le très répandu microcircuit PCF8574(A), qui est un extenseur de ports d'entrée/sortie contrôlé via le bus i2c. Cette puce ne contient qu'un seul registre interne, qui est son port d'E/S physique. Autrement dit, si vous lui transmettez un octet, ses conclusions seront immédiatement exposées. Si vous en comptez un octet (Transmettre COMMENCER adresse avec indicateur de lecture, signal RESTER, lire les données et enfin générer un signal ARRÊT) alors il reflétera les états logiques sur ses sorties. Connectons notre microcircuit conformément à la fiche technique :


L'adresse du microcircuit est formée à partir de l'état des broches A0, A1, A2. Pour microcircuit PCF8574 l'adresse sera : 0100A0A1A2. (Par exemple, nous avons A0, A1, A2 à un niveau élevé, donc l'adresse de notre microcircuit sera 0b0100 111 = 0x27). Pour PCF8574A - 0111A0A1A2, qui avec notre schéma de connexion donnera l'adresse 0b0111 111 = 0x3F. Si, disons, A2 est connecté à la terre, alors l'adresse de PCF8574A volonté 0x3B. Au total, 16 microcircuits peuvent être montés simultanément sur un bus i2c, 8 PCF8574A et PCF8574 chacun.

Essayons de transférer quelque chose, initialisons le bus i2c et transférons quelque chose sur notre PCF8574.

#define PCF8574A_ADDR 0x3F //Adresse de notre PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // Générer le signal START I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); // Transférer 1 octet de données et générer le STOP signal) char PCF8574A_reg ; // la variable que l'on écrit dans PCF8574 void main() ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // Démarre I2C delay_ms(25); // Attends un peu PCF8574A_reg.b0 = 0; // allume la première LED PCF8574A_reg.b1 = 1; // éteint la deuxième LED pendant que (1) ( delay_ms(500); PCF8574A_reg.b0 = ~PCF8574A_reg.b0; PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // inverse l'état des LED I2C_PCF8574_WriteReg (PCF8574A_reg) ; // transférer les données vers notre PCF8574 ) )
Nous compilons et exécutons notre programme et voyons que nos LED clignotent alternativement.
J'ai connecté la cathode des LED à notre PCF8574 pour une raison. Le fait est que lorsqu'un 0 logique est fourni à la sortie, le microcircuit tire honnêtement sa sortie vers la terre, mais lorsqu'un 1 logique est appliqué, il le connecte à l'alimentation + via une source de courant de 100 μA. Autrement dit, vous ne pouvez pas obtenir un 1 logique « honnête » à la sortie. Et vous ne pouvez pas allumer une LED avec 100 µA. Cela a été fait afin de configurer la sortie PCF8574 sur l'entrée sans registres supplémentaires. Nous écrivons simplement dans le registre de sortie 1 (en définissant essentiellement les états des broches sur Vdd) et pouvons simplement le court-circuiter à la masse. La source de courant ne permettra pas à l’étage de sortie de notre extenseur d’E/S de « s’épuiser ». Si la jambe est tirée vers le sol, alors le potentiel de terre est dessus et le 0 logique est lu. Si la jambe est tirée vers +, alors le 1 logique est lu. D'un côté, c'est simple, mais de l'autre, vous devez toujours vous en souvenir lorsque vous travaillez avec ces microcircuits.


Essayons de lire l'état des broches de notre puce d'extension.

#define PCF8574A_ADDR 0x3F //Adresse de notre PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // Générer le signal START I2C1_Write(PCF8574A_ADDR, &wData, 1, END_MODE_STOP); // Transférer 1 octet de données et générer le STOP signal ) void I2C_PCF8574_ReadReg (unsigned char rData) ( I2C1_Start(); // Générer le signal START I2C1_Read(PCF8574A_ADDR, &rData, 1, END_MODE_STOP); // Lire 1 octet de données et générer le signal STOP) char PCF8574A_reg; //variable que nous écrivons dans PCF8574 char PCF8574A_out; // la variable que nous lisons et PCF8574 char lad_state; //notre LED est allumée ou éteinte void main() ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // Démarrez I2C delay_ms(25); // Attendez un peu PCF8574A_reg.b0 = 0; // allumez la première LED PCF8574A_reg.b1 = 1; / / éteint la deuxième LED PCF8574A_reg.b6 = 1; // Tirez les broches 6 et 7 pour alimenter. PCF8574A_reg.b7 = 1; while (1) ( delay_ms(100); I2C_PCF8574_WriteReg (PCF8574A_reg); // écriture de données vers PCF8574 I2C_PCF8574_ReadReg (PCF85 74A_out ); // lecture depuis PCF8574 si (~PCF8574A_out.b6) PCF8574A_reg.b0 = ~PCF8574A_reg.b0; // Si 1 bouton est enfoncé (le 6ème bit de l'octet lu depuis PCF8574 est 0, alors allumer/éteindre notre LED) if (~PCF8574A_out .b7) PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // similaire pour 2 boutons et 2 LED ) )
Maintenant, en appuyant sur les boutons, nous allumons ou éteignons notre LED. Le microcircuit a une autre sortie INT. Une impulsion y est générée à chaque fois que l'état des broches de notre extenseur d'E/S change. En le connectant à l'entrée d'interruption externe de notre MK (je vous expliquerai comment configurer les interruptions externes et comment les utiliser dans l'un des articles suivants).

Utilisons notre extenseur de port pour connecter un affichage de caractères via celui-ci. Il en existe un grand nombre, mais presque tous sont construits sur la base d'une puce de contrôleur. HD44780 et ses clones. Par exemple, j'ai utilisé un écran LCD2004.


La fiche technique de celui-ci et du contrôleur HD44780 peut être facilement trouvée sur Internet. Connectons notre écran au PCF8574, et le sien, respectivement, à notre STM32.

HD44780 utilise une interface fermée parallèle. Les données sont transmises par 8 (en un cycle d'horloge) ou 4 (en 2 cycles d'horloge) impulsions de porte à la sortie E. (lecture par le contrôleur d'affichage sur front descendant, passage de 1 à 0). Conclusion R.S. indique si nous envoyons des données à notre écran ( RS = 1) (les caractères qu'il doit afficher sont en réalité des codes ASCII) ou la commande ( RS = 0). RW indique le sens du transfert, de l'écriture ou de la lecture des données. Habituellement, nous écrivons des données sur l'écran, donc ( LW=0). La résistance R6 contrôle le contraste de l'affichage. Vous ne pouvez pas simplement connecter l’entrée de réglage du contraste à la masse ou à l’alimentation, sinon vous ne verrez rien.. VT1 est utilisé pour allumer et éteindre le rétroéclairage de l'écran selon les commandes MK. MicroC dispose d'une bibliothèque pour travailler avec de tels écrans via une interface parallèle, mais il est généralement coûteux de dépenser 8 pieds sur un écran, j'utilise donc presque toujours le PCF8574 pour travailler avec de tels écrans. (Si quelqu'un est intéressé, j'écrirai un article sur l'utilisation d'écrans basés sur HD44780 intégrés à MicroC via une interface parallèle.) Le protocole d'échange n'est pas particulièrement compliqué (nous utiliserons 4 lignes de données et transférerons les informations en 2 cycles d'horloge), cela est clairement montré par le chronogramme suivant :


Avant de transférer des données vers notre écran, celui-ci doit être initialisé en passant des commandes de service. (décrits dans la fiche technique, nous présentons ici uniquement les plus utilisés)

  • 0x28- communication avec l'indicateur via 4 lignes
  • 0x0C- activer la sortie d'image, désactiver l'affichage du curseur
  • 0x0E- activer la sortie d'image, activer l'affichage du curseur
  • 0x01- effacer l'indicateur
  • 0x08- désactiver la sortie d'image
  • 0x06- une fois le symbole affiché, le curseur se déplace d'1 emplacement familier
Comme nous devrons travailler assez souvent avec cet indicateur, nous allons créer une bibliothèque de plug-ins "i2c_lcd.h" . A cet effet dans Chef de projet Fichiers d'en-tête et choisissez Ajouter un nouveau fichier . Créons notre fichier d'en-tête.

#define PCF8574A_ADDR 0x3F //Adresse de notre PCF8574 #define DB4 b4 // Correspondance entre les pins du PCF8574 et l'indicateur #define DB5 b5 #define DB6 b6 #define DB7 b7 #define EN b3 #define RW b2 #define RS b1 #define BL b0 // contrôle du rétroéclairage #define displenth 20 // nombre de caractères dans notre ligne d'affichage static unsigned char BL_status; // variable stockant l'état du rétroéclairage (on/off) void lcd_I2C_Init(void); // Affichage et fonction d'initialisation PCF8574 void lcd_I2C_txt(char *pnt); // Affiche une ligne de texte, le paramètre est un pointeur vers cette ligne void lcd_I2C_int(int pnt); // Affiche la valeur d'une variable entière, le paramètre est la valeur de sortie void lcd_I2C_Goto(unsigned short row, unsigned short col); // déplace le curseur à la position spécifiée, paramètres row - line (de 1 à 2 ou 4 selon l'affichage) et col - (de 1 à displenth)) void lcd_I2C_cls(); // Efface l'écran void lcd_I2C_backlight (état int court non signé) ; // Active (lors de la transmission de 1 et désactive - lors de la transmission de 0 le rétroéclairage de l'écran)
Décrivons maintenant nos fonctions, revenons à Chef de projet clic droit sur le dossier Sources et choisissez Ajouter un nouveau fichier . Créer un fichier "i2c_lcd.с" .

#include "i2c_lcd.h" //inclut notre fichier d'en-tête char lcd_reg; //enregistrement pour le stockage temporaire des données envoyées au PCF8574 void I2C_PCF8574_WriteReg(unsigned char wData) //fonction d'envoi de données via i2c à la puce PCF8574 ( I2C1_Start(); I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); ) void LCD_COMMAND ( char com) / /fonction d'envoi d'une commande à notre écran ( lcd_reg = 0; //écrit 0 dans le registre temporaire lcd_reg.BL = BL_status.b0; //définit la broche de rétroéclairage en fonction de la valeur de la variable stockant le état du rétroéclairage lcd_reg.DB4 = com.b4; // définit les 4 bits les plus significatifs de notre commande sur le bus de données de l'indicateur lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; lcd_reg.EN = 1; //définit la sortie stroboscopique sur 1 I2C_PCF8574_WriteReg ( lcd_reg); //écrit dans le registre PCF8574, envoyant effectivement des données à l'indicateur delay_us (300); //attend le délai d'attente lcd_reg.EN = 0; / /remettre l'impulsion stroboscopique à 0, l'indicateur lit les données I2C_PCF8574_WriteReg (lcd_reg); delay_us (300) ; lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.DB4 = com.b0; // idem pour les 4 moins bits significatifs lcd_reg.DB5 = com.b1 ; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; lcd_reg.EN = 1; I2C_PCF8574_WriteReg(lcd_reg); delay_us(300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg(lcd_reg); delay_us(300); ) void LCD_CHAR (unsigned char com) //envoi de données (code de caractère ASCII) à l'indicateur ( lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; //envoi d'un caractère est différent de l'envoi de commandes en définissant le bit RS sur 1 lcd_reg.DB4 = com.b4; //définit les 4 bits les plus significatifs aux entrées lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; I2C_PCF8574_WriteReg (lcd_reg) ; delay_us (300); lcd_reg.EN = 0; // remet l'impulsion stroboscopique à 0, l'indicateur lit les données I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); lcd_reg = 0; lcd_reg .BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; lcd_reg.DB4 = com.b0; //définit les 4 bits de poids faible aux entrées lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; I2C_PCF8574_WriteReg ( lcd_reg); delay_us (300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); ) void lcd_I2C_Init(void) ( I2C1_Init_Advanced(400000, &_ GPIO_MODULE_I2C1_PB67 ); //initialiser notre module I2c pour MK delay_ms(200) ; lcd_Command(0x28); // Affichage en 4 bits par mode horloge delay_ms (5); lcd_Command(0x08); //Désactiver la sortie des données sur l'affichage delay_ms (5); lcd_Command(0x01); //Effacer l'affichage delay_ms (5); lcd_Command(0x06); //Activer le déplacement automatique du curseur après l'affichage du symbole delay_ms (5); lcd_Command(0x0C); //Activer l'affichage des informations sans afficher le curseur delay_ms (25); ) void lcd_I2C_txt(char *pnt) //Affiche une chaîne de caractères à l'écran ( unsigned short int i; //variable d'index de tableau de caractères temporaire char tmp_str; //tableau temporaire de caractères, longueur 1 supérieure à la longueur de l'affichage ligne, puisque la ligne doit se terminer сiv avec un caractère NULL ASCII 0x00 strncpy(tmp_str, pnt, displenth); // ne copie pas plus que les caractères displenth de la chaîne d'origine dans notre chaîne temporaire pour (i=0; i Connectons maintenant la bibliothèque nouvellement créée au fichier avec notre fonction principale :

#include "i2c_lcd.h" //inclut notre fichier d'en-tête non signé int i; //compteur variable temporaire void main() ( lcd_I2C_Init(); //initialiser l'affichage lcd_I2C_backlight (1); //allumer le rétroéclairage lcd_I2C_txt ("Bonjour habrahabr"); //afficher la ligne while (1) ( delay_ms( 1000) ; lcd_I2C_Goto (2,1); //aller au caractère 1 de la ligne 2 lcd_i2c_int (i); //afficher la valeur i++; //incrémenter le compteur ) )

Si tout est assemblé correctement, alors nous devrions voir du texte sur l'indicateur et un compteur s'incrémentant toutes les secondes. En général, rien de compliqué :)

Dans le prochain article, nous continuerons à comprendre le protocole i2c et les appareils qui fonctionnent avec lui. Envisageons de travailler avec la mémoire EEPROM 24XX et l'accéléromètre/gyroscope MPU6050.

Aujourd'hui, il y a un nouvel invité sur notre table d'opération, il s'agit d'un produit Microchip, l'extenseur de port MCP23008-E. Cette chose est destinée (comme son nom l'indique) à augmenter le nombre de jambes d'E/S du microcontrôleur si elles deviennent soudainement insuffisantes. Bien sûr, si nous avons besoin de jambes et de sorties, nous pouvons les prendre sans nous en soucier. Si vous avez besoin de jambes d'entrée, il existe une solution basée sur une logique stricte. Si nous avons besoin à la fois d'entrées et de sorties ainsi que d'un pull-up contrôlé pour les entrées, alors un extenseur de port est peut-être la solution la plus normale. Quant au prix de l'appareil, il est très modeste – environ un dollar. Dans cet article, je vais essayer de décrire en détail comment contrôler ce microcircuit à l'aide du microcontrôleur AVR.

Tout d'abord, un peu sur les caractéristiques :

  • 8 broches de port indépendantes
  • Interface de communication avec le monde extérieur - I2C (fréquence jusqu'à 1,7 MHz)
  • Pull-up personnalisable pour les entrées
  • Peut secouer la jambe lorsque l'état de certaines entrées change
  • Trois entrées pour régler l'adresse du microcircuit (vous pouvez accrocher 8 appareils sur un bus)
  • Tension de fonctionnement de 1,8 à 5,5 volts
  • Faible consommation de courant
  • Large choix de boîtiers (PDIP/SOIC/SSOP/QFN)

Je préfère utiliser l'interface I2C, elle m'attire avec un petit nombre de câblage :-) cependant, si vous avez besoin d'une vitesse très rapide (jusqu'à 10 MHz), alors vous devez utiliser l'interface SPI présente dans le MCP23S08. La différence entre MCP23S08 et MCP23008, si je comprends bien, réside uniquement dans l'interface et dans le nombre de pattes pour définir l'adresse de la puce. Qui aime quelque chose de plus court ? Le brochage du mikruhi est dans la fiche technique, il n'est en aucun cas intéressant et ne sera pas abordé ici. Par conséquent, passons immédiatement à la façon de commencer à travailler avec cet appareil. Tout travail se résume à l'écriture et à la lecture de certaines données des registres du microcircuit. À ma grande joie, il n’y avait pas beaucoup de registres – seulement onze. L'écriture et la lecture de données à partir de registres sont très simples, postez-en. C'est vrai qu'on ne parle pas de registres, mais le principe est le même. Il ne reste plus qu'à déterminer dans quels registres lire quoi et dans quels registres écrire quoi. Bien entendu, la fiche technique nous y aidera. De la fiche technique, nous apprenons que le microcircuit possède les registres suivants :

Registre IODIR
Spécifie la direction dans laquelle les données circulent. Si le bit correspondant à une certaine jambe est mis à un, alors la jambe est une entrée. Si remis à zéro, quittez. Bref, ce registre est analogue au DDRx en AVR (seulement en AVR 1 est une sortie et 0 est une entrée).

Registre IPOL
Si un bit de registre est activé, l'inversion d'entrée est activée pour la branche correspondante. Cela signifie que si vous appliquez un journal sur la jambe. zéro alors du registre GPIO est considéré comme un et vice versa. L'utilité de cette fonctionnalité est très discutable.

Registre GPINTEN
Chaque bit de ce registre correspond à une broche de port spécifique. Si le bit est défini, la broche du port correspondant configurée comme entrée peut provoquer une interruption. Si le bit est réinitialisé, peu importe ce que vous faites avec le tronçon de port, il n'y aura pas d'interruption. Les conditions d'interruption sont définies par les deux registres suivants.

Registre DEFVAL
La valeur actuelle des broches configurées pour l'entrée est constamment comparée à ce registre. Si soudainement la valeur actuelle commence à différer de celle contenue dans ce registre, une interruption se produit. En termes simples, si le bit est activé, l'interruption se produira lorsque le niveau passera de haut à bas sur la branche correspondante. Si le bit est effacé, l'interruption se produit sur un front montant.

Registre INTCON
Chaque bit de ce registre correspond à une broche de port spécifique. Si le bit est à zéro, alors tout changement du niveau logique vers le niveau opposé provoque une interruption. Si le bit est activé, l'apparition de l'interruption est influencée par le registre DEFVAL (sinon, elle est complètement ignorée).

Registre IOCON
Il s'agit du registre des paramètres. Il se compose de quatre bits :
SEQOP- Les bits contrôlent l'augmentation automatique de l'adresse. S'il est installé, l'augmentation automatique est désactivée, sinon elle est activée. Si nous le désactivons, nous pouvons lire la valeur du même registre très rapidement car nous n'avons pas besoin de transmettre son adresse à chaque fois. Si vous devez lire rapidement les 11 registres à tour de rôle, l'augmentation automatique doit être activée. Chaque fois qu'un octet est lu dans le registre, l'adresse elle-même augmentera de un et n'aura pas besoin d'être transmise.
DISSLW- qui sait ce que c'est. Peu importe comment je le tourne, tout fonctionne toujours. Je serais heureux si quelqu'un explique.
HAEN— Bit de réglage de la sortie INT. S'il est défini, la broche est configurée comme un drain ouvert, si le bit est réinitialisé, alors le niveau actif sur la jambe INT détermine le bit INTPOL
INTPOL— détermine le niveau actif sur la branche INT. S'il est défini, le niveau actif est un, sinon zéro.

Il en reste encore un peu HAEN mais il n'est pas utilisé dans cette puce (active/désactive les broches d'adressage matériel dans MCP23S08)

Registre GPPU
Contrôle l’extraction d’entrée. Si le bit est activé, un pull-up vers l'alimentation via une résistance de 100 kOhm apparaîtra sur la broche correspondante.

Registre INTF
Registre des drapeaux d'interruption. Si le bit est activé, cela signifie que le tronçon de port correspondant a provoqué une interruption. Bien entendu, les interruptions pour les segments requis doivent être activées dans le registre GPINTEN

Registre INTCAP
Lorsqu'une interruption se produit, l'intégralité du port est lue dans ce registre. S'il y a d'autres interruptions après cela, le contenu de ce registre ne sera pas écrasé par la nouvelle valeur tant que nous ne l'aurons pas lu ou GPIO.

Registre GPIO
Lors de la lecture des données du registre, nous lisons les niveaux logiques sur les broches du port. Lors de l'enregistrement des données, nous définissons des niveaux logiques. Lors de l'écriture dans ce registre, les mêmes données sont automatiquement écrites dans le registre OLAT

Registre OLAT
En écrivant des données dans ce registre, nous sortons les données vers le port. Si vous lisez des données à partir de là, vous lirez ce qui a été écrit, et non ce qui se trouve réellement aux entrées du port. Pour lire les entrées, nous utilisons uniquement GPIO.

La description des registres, à mon avis, est tout à fait normale et ne doit épater personne. Maintenant, juste pour le plaisir, écrivons une petite démo qui interrogera 4 boutons connectés à l'extenseur de port, et en fonction de leur état, allumera ou éteindra 4 LED correspondantes connectées au même extenseur. L'exemple sera le plus simple possible, sans aucune interruption. Nous lisons simplement en permanence l'état des boutons et affichons cet état sur les LED. Le schéma de notre appareil ressemblera à ceci :

La broche d'interruption n'a pas besoin d'être connectée, nous n'en avons pas besoin ici, mais le pull-up pour la jambe de réinitialisation est requis ! J'ai passé beaucoup de temps jusqu'à ce que je réalise que mon extenseur de port était périodiquement réinitialisé en raison d'un manque de serrage. Passons maintenant au code. Bien sûr, nous continuerons à écrire. Code simple :

Programme rashiritel ; const AdrR=%01000001 ; //Adresse de la puce avec le bit lu const AdrW=%01000000; //Adresse de la puce avec l'enregistrement de bits var r:byte; ///La procédure écrit les données de la variable Dat dans le registre à l'adresse Adr Procedure WriteReg(Dat,Adr:byte); Commencez TWI_Start(); TWI_Write(AdrW); TWI_Write(Adr); TWI_Write(Dat); TWI_Stop(); Fin; ///La fonction renvoie la valeur du registre à l'adresse Adr Function ReadReg(Adr:byte):byte; var a:octet; Commencez TWI_Start(); TWI_Write(AdrW); TWI_Write(Adr); TWI_Start(); TWI_Write(AdrR); a:=TWI_Read(0); TWI_Stop(); résultat :=a ; Fin; commencer TWI_INIT(200000); ///Initialisation d'i2c WriteReg(%00001111,0x00); //Les 4 bits les plus bas sont des entrées et les 4 bits restants sont des sorties WriteReg(%00001111,0x06); //Sur pull-up pour 4 entrées Tant que TRUE do Begin r:=ReadReg(0x09); //Lire l'état des entrées r:= NOT r; //Il faut inverser les bits, sinon les LED s'allumeront au relâchement du bouton r:= r shl 4; //Décalage de 4 bits vers la gauche... WriteReg(r,0x0A); //Affiche l'état de fin des boutons ; fin.

Publié le 26/10/2016

Dans l'article précédent, nous avons vu le fonctionnement du STM32 avec le bus I 2 C comme maître. Autrement dit, il était le leader et a interrogé le capteur. Faisons maintenant du STM32 un esclave et répondons aux demandes, c'est-à-dire qu'il fonctionne lui-même comme un capteur. Nous allons allouer 255 octets de mémoire pour les registres avec des adresses de 0 à 0xFF, et permettre au Maître de les écrire/lire. Et pour rendre l'exemple moins simple, faisons de notre STM32 un convertisseur analogique-numérique avec une interface I 2 C. L'ADC traitera 8 canaux. Le contrôleur donnera les résultats des transformations au Maître lors de la lecture des registres. Puisque le résultat de la conversion ADC est de 12 bits, nous avons besoin de 2 registres (2 octets) pour chaque canal ADC.

i2c_slave.h contient les paramètres :

I2CSLAVE_ADDR– adresse de notre appareil ;

ADC_ADDR_START– l'adresse de départ des registres responsables des résultats des conversions ADC.

Dans le fichier i2c_slave.c nous sommes plus intéressés par les fonctions get_i2c1_ram Et set_i2c1_ram. Fonction get_i2c1_ram est responsable de la lecture des données des registres. Il renvoie les données de l'adresse spécifiée, qui est donnée au maître. Dans notre cas, les données sont lues à partir du tableau i2c1_ram, mais si le maître demande des adresses de registre dans la plage allouée pour les résultats ADC, alors les données de conversion ADC sont envoyées.

get_i2c1_ram:

Uint8_t get_i2c1_ram(uint8_t adr) ( //Données ADC si ((ADC_ADDR_START<= adr) & (adr < ADC_ADDR_START + ADC_CHANNELS*2)) { return ADCBuffer; } else { // Other addresses return i2c1_ram; } }

Fonction set_i2c1_ram– écrit les données reçues du maître dans des registres avec l'adresse spécifiée. Dans notre cas, les données sont simplement écrites dans un tableau i2c1_ram. Mais c'est facultatif. Vous pouvez, par exemple, ajouter un chèque et, lorsqu'un certain numéro arrive à une certaine adresse, effectuer certaines actions. De cette façon, vous pouvez envoyer différentes commandes au microcontrôleur.

set_i2c1_ram:

Void set_i2c1_ram(uint8_t adr, uint8_t val) ( i2c1_ram = val; return; )

L'initialisation est assez simple :

Int main(void) ( SetSysClockTo72(); ADC_DMA_init(); I2C1_Slave_init(); while(1) ( ) )

Nous définissons d’abord la fréquence de fonctionnement maximale du contrôleur. Une vitesse maximale est requise lorsqu'il faut éviter tout retard sur le bus I 2 C. Ensuite, nous démarrons le fonctionnement de l'ADC en utilisant DMA. À PROPOS . À PROPOS . Et enfin, on initialise le bus I 2 C comme Esclave. Comme vous pouvez le constater, rien de compliqué.

Connectons maintenant notre module STM32 au Raspberry Pi. Connectons les potentiomètres aux canaux ADC. Et nous lirons les indicateurs ADC de notre contrôleur. N'oubliez pas que pour que le bus I 2 C fonctionne, vous devez installer des résistances de rappel sur chaque ligne du bus.

Dans la console Raspberry, vérifions si notre appareil est visible sur le bus I 2 C (à propos de ça) :

I2cdétect -y 1

Comme vous pouvez le voir, l'adresse de l'appareil 0x27, bien que nous ayons spécifié 0x4E. Lorsque vous avez le temps, réfléchissez à la raison pour laquelle cela s'est produit.

Pour lire dans les registres du périphérique I 2 C-Slave, exécutez la commande :

I2cget -y 1 0x27 0x00

Où:
0x27– l'adresse de l'appareil,
0x00– adresse d'enregistrement (0x00…0xFF).

Pour écrire dans les registres du périphérique I 2 C-Slave, exécutez la commande :

I2cset -y 1 0x27 0xA0 0xDD

De :
0x27– l'adresse de l'appareil,
0xA0– adresse d'enregistrement
0xDD-Données 8 bits (0x00…0xFF)

La commande précédente a écrit le numéro 0xDD dans le registre 0xA0(vous pouvez écrire dans les 16 premiers registres, mais cela ne sert à rien, mais ils sont réservés à l'ADC). Lisons maintenant :

I2cget -y 1 0x27 0xA0

Pour simplifier le processus de lecture des données du canal ADC, j'ai écrit un script :

#!/usr/bin/env python import smbus import time bus = smbus.SMBus(1) adresse = 0x27 while (1) : ADC = (); pour i dans la plage (0, 8) : LBS = bus.read_byte_data(adresse, 0x00+i*2) MBS = bus.read_byte_data(adresse, 0x00+i*2+1) ADC[i] = MBS*256 + LBS imprimer l'heure ADC.sleep (0.2)

Il interroge et affiche les résultats des 8 canaux ADC sur la console.

De la même manière, vous pouvez combiner plusieurs microcontrôleurs. L'un d'eux devrait être Master(), l'autre Slave.

Je te souhaite du succès!




Haut