12 KiB
Driver cheat sheet
GPIO
Ne pas oublier de toujours activer la clock pour le driver concerné. Dans le cas du GPIO il faut activer celui pour GPIO_X, le registre est dans RCC->APBXENR Dans notre cas chaque GPIO est dans APB2ENR (page 112 de Reference Manual) : Des variables sont disponibles comme RCC_APB2ENR_IOPAEN
Par la suite il faut choisir quelle pin activer dans le GPIO et en quel mode. Tout cela se passe dans le registre CR ainsi que ODR pour un GPIO Précis. ODR lui va permettre de sélectionner le pull-up/pull-down. Voici le registre :
Pour sélectionner le mode, cela se passe dans le registre CR L/H :
Attention pour les pins > 7 -> Il faut aller sur le registre high, donc CRH, qui suit la même logique que ce dernier, mais à la différence que celui-ci commence par le bit 8. Il suffit donc avec tous ces registres de suivre le tableau Figure 20 Sur les différents modes :
L'un des meilleurs moyens pour respecter ce dernier est donc de faire une énumération avec le configuration mode voulu. Comme ça plus besoin de chercher les bons bits. Pour les bits MODE, il suffit de mettre à 0 pour Input, et n'importe quelle autre variable pour Output. Ensuite il faut modifier en conséquence CNF[1:0], soit le tableau suivant :
Configuration Name | Bits sets |
---|---|
In_Floating | 0x04 |
In_PullDown | 0x08 |
In_PullUp | 0xF8 (normalement 0x08, mais ici différencié) |
In_Analog | 0x00 |
Out_PullUp | 0x01 |
Out_OD | 0x05 |
AltOut_Ppull | 0x09 |
AltOut_OD | 0x0d |
Tout ces defines doivent être décalés de 4 * PinNumber. |
Pour ensuite set ou reset ces pins il suffit d'utiliser les deux registres suivants :
- BSRR pour set -> 1 décalé de la pin
- BRR pour reset -> 1 décalé de la pin Pour observer ces dernières, le registre IDR est le plus intéressant :
char gpioRead = (GPIO->IDR & (0x1 << GPIO_Pin)) > 0;
Timer
Dans le cas du Timer il faut activer celui pour TIMXEN, le registre est dans RCC->APBXENR. Pour le timer 1 il se trouve dans APB2ENR, et pour les autres dans APB1ENR.
Il suffit ensuite de régler le PSC et l'ARR pour pouvoir obtenir l'horloge de notre choix sur un timer donné. Le PSC permet de diviser la clock par un certain nombre en respectant cette formule :
fCKPSC/PSC+1 = fCKCNT
Ensuite en fonction du Time-base unit différents modes sont possible pour ARR, le registre de comptage va s'arrêter de compter à partir de cette valeur ARR. Mais le signal renvoyé par la clock, ou même le flag du timer ne répondra pas de la même manière. Le mode par défaut est donc celui du change on reset. pour trouver la fréquence associée il nous suffit de faire :
fCKCNT/ARR+1 = CK_ARR
Au final les deux registres étant sur 16 bits, il est possible d'avoir une précision de la division à 32 bits/
Interruption
Les interruptions doivent être activées de manières différente pour chaque module, mais cela dit il reste quelques similaritées sur le module NVIC qui doit être correctement configuré pour un module donné. Il faut tout d'abord activer l'interruption du côté du module et ensuite modifier la table des vecteurs du NVIC. La première est celle des prioritées, trouvable dans le Manuel de Référence RM008 page 197, la second e est l'ISER qui permet la réecriture des fonction d'IRQ trouvable dans le Manuel de programmation PM0056 page 119.
int iserRegister = 0; //A voir par rapport au manue lde reference RM008 -> emplacement de la table
uint32_t IRQNumber = GetInterruptNum(); //XXX_IRQn
NVIC->IP[IRQNumber] |= (Prio << 0x4);
NVIC->ISER[iserRegister] |= (0x1<<(IRQNumber-(32*iserRegister)));
Enfin, une fonction nommée XXX_IRQHandler sera appelée à l'interruption, il suffiera de réecrire cette dernière pour qu'elle soit active. NB : Il est important de remettre le flag d'interruption à 0 pour relancer ce dernier.
Timer Interruption
Le registre pour l'activation de l'interruption est dans TimerX_DIER, sur le bit 0 nommé UIE (Update Interrupt Enable). Le flag d'interruption est UIF.
ADC Interruption
L'adc est plus complexe que les autres car il demande des pins pour être liés à une interruption, pour activer ce dernier nous allons observer un registre en particulier, ADC_CR2 :
Ici quelques bits vont être mis à 1 pour permettre l'interruption, soit :
- ADC_CR2_EXTTRIG : active un trigger externe
- ADC_CR2_EXTSEL : On choisi l'event externe pour l'allumage de l'adc, par défaut SWSTART On active ensuite l'interruption avec le bit EOCIE qui est présent dans le registre CR1. Le flag d'intteruption est EOC (End of Conversion) dans le registre SR.
Pour relancer une conversion ou init le premier, il faut mettre à 1 dans le registre CR2 le bit SWSTART comme ceci :
ADC->CR2 |= ADC_CR2_SWSTART;
NB : Il est important de faire tout ce qui est fait dans la partie ADC avant de faire les interruptions. En effet ADON sert toujours à l'initialisation mais ne servira pas pour relancer une conversion, il est remplacé par SW_START
UART Interruption
Le registre pour l'activation de l'interruption est dans USART_CR1, sur le bit 5 (RXNEIE pour la recepetion) et 7 (TXEIE pour la transmission). Le flag d'intteruption est RXNE ou TXE.
PWM
Pour activer le mode PWM il existe un registre pour chacun des timers permettant d'activer un mode pwm précis. Ce dernier est présent dans le registre CCMRx. Les bits qui nous intéresse sont OCXM. Il existe différents modes pouvant être trouvés page 414 du Reference Manual. Ici nous utiliseront le même car il est simple :
- 110 : PWM Mode 1, tant que TIMx_CNT < TIMx_CCRX le channel est actif. CCMR2 possède les channels 3 et 4, et CCMR1 1 et 2. Tous sont utilisable pour chaque Timer.
Le CCRX (Capture/Compare Register) est un registre de 16 bit qui va déterminer pour un mode de PWM donné le moment où le timer renverra 1 ou 0. Cette variable peut être directement liée à l'ARR : ce qui permet de régler l'alpha d'un PWM directement.
Et finalement pour activer ce registre le Timer possède un registre nommé CCER
Ce dernier permet d'activer avec CCXE le registre précedemment paramétré.
Une GPIO est ainsi associé à chaque PIN. Pour la retrouver il suffit de se rendre à la page 28 du Manuel F103RB et voir quelle pin est associée à quel channel de timer. Il suffit de mettre cette dernière en AltOutPpull.
ADC
Dans le cas de l'ADC il faut activer celui pour ADCX, le registre est dans RCC->APB2ENR.
L'une des difficultés que nous pouvons rencontrer avec l'ADC est la prédivision de la clock qui doit être réalisé pour qu'une conversion soit possible. En effet la clock de base de 72 MHz fonctionne bien trop rapidement par rapport au module ADC. Ce dernier fonctionne au maxmimum à 16 MHz. Pour régler ce dernier il faut alors toucher au RCC_CFGR, le registre de clock configuration. Les bits qui nous intéresses sont 15 et 14 : ADCPRE :
Le minimum pouvant être mis en place est donc "10" avec PCLK2 divisé par 6, ce qui donne 12 MHz
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
RCC->CFGR |= RCC_CFGR_ADCPRE_1;
Le second registre qui nous intéressera sera ADC_SMPRX. Ce dernier permet de modifier pour un channel donné X, le nombre de cycle que l'adc devra réalisé pour récupérer une donnée. Une explication liée au résistance peut être faite, mais ici nous nous sommes contentés de mettre sa valeur à 41.5 cycles, soit 100.
ADCStructPtr->ADC->SMPR2 |= (ADCStructPtr->cycle<<(ADCStructPtr->channel*3));
//ou pour channel > 10 :
ADCStructPtr->ADC->SMPR1 |= (ADCStructPtr->cycle<<((ADCStructPtr->channel-10)*3));
Le prochain registre est le ADC_SQR1 : Ce dernier va nous permettre de faire un certain nombre de conversions. Ici nous ne voulons qu'une seule donc la donnée dans les bits L (Bits 23-20) sera 0000.
ADCStructPtr->ADC->SQR1 &= ADC_SQR1_L;
Le registre ADC_SQR3 va par la suite nous permettre de sélectionner quel channel nous utilisons pour l'ADC. En effet après avoir changé le nombre de cycles, il faut faire comprendre au STM32 que nous voulons la donnée sur un channel précis. C'est donc aux bits SQ1(Bits 4-0) que nous mettrons un numéro de channel sur 5 bits. C'est le premier channel de conversion, il est possible dans régler d'autres par la suite, mais nous ne l'avons pas fait.
ADCStructPtr->ADC->SQR3 |= channel; //avec channel de 0 à 17
Finalement, pour initialiser ou faire une une conversion analogique numérique un biot précis dans le registre ADC_CR2 doit être mis à 1. Ce dernier est ADON et permet dans un premier temps d'initialiser l'ADC, et dans un second, de faire une conversion quelconque, il est donc préférable de l'utiliser en tant que MACRO :
ADCStructPtr->ADC->CR2 |= ADC_CR2_ADON;
//utilisation en macro :
#define MyADC_Start(ADC) (ADC->CR2 |= ADC_CR2_ADON)
UART
Dans le cas de l'UART il faut activer celui pour USARTXEN, le registre est dans RCC->APBXENR. Pour l'USART 1 il se trouve dans APB2ENR, et pour les autres dans APB1ENR.
Après cela il suffit de régler le registre BRR. C'est un registre de 16 bits qui permet de calculer le baud rate. Il existe une méthode de calcul avec le chiffre après la virgule mais il est ici inutile de l'utiliser. Il suffit de faire :
BRR=f_CLK/BaudRate
A noter que le f_CLK est différent en fonction de l'U(S)ART choisi. Ici nous avons retenu que :
UART Channel | f_CLK |
---|---|
1 | 72 MHz |
2 | 36 MHz |
3 | 36 MHz |
Ces données sont trouvables dans le schéma de câblage des UARTs
Par la suite il faut régler le mode de l'UART, pour cela nous allons nous concentrer sur deux registres :
Registre | Fonctions |
---|---|
CR1 | Enable, Taille, Parité, RxD/TxD Enable, Interruption |
CR2 | Bits de stop |
Voici le registre CR1 de l'UART et quels bits modifier pour chacune des fonctions voulues :
Les registres qui nous intéresse sont donc les suivants :
- UE : USART Enable, à activer dès le début
- M : Taille de la donnée, 0->8 bits, 1->9 bits
- PCE et PS : PCE active ou non la parité. Si PCE=1, alors PS détermine le type de parité : 0 pour paire, 1 pour impaire.
- TE et RE : Activation de la transmission et de la reception
- RXNEIE : Activation de l'interruption pour la reception Pour CR2 tout ce qui nous intéresse sont les bits STOP (12 et 13):
STOP [1:0] | Stop bit |
---|---|
00 | 1 |
01 | 0.5 |
10 | 1.5 |
11 | 2 |
Pour la reception ou la transmission des bits sont associés pour savoir si le uC a bien envoyé ou reçu une donnée depuis le TR/RR (TxD Register, RxD Register) vers le Data Register, Ils sont tous présents dans le SR (Status Register) :
- RXNE détermine qu'une donnée est reçue si ce bit est à 1. Après lecture de la donnée dans le DR(Data Register), il faut forcer ce dernier à 0 pour le reset
- TXE permet de savoir si le registre TR est vide (ce qui veut dire que la donnée a bien été envoyée).
- TC permet de savoir si le dernier bit de stop a bien été mis dans le TR. NB : Il est préférable d'utiliser TXE pour l'envoi de donnée. Ces trois bits fonctionnent aussi en interruption
Une GPIO est ainsi associé à chaque PIN. Pour la retrouver il suffit de se rendre à la page 28 du Manuel F103RB et voir quelle pin est associée à quel USART Tx/Rx. Par la suite il est conseillé de mettre les différentes pins dans ces modes :
- RXD : In Floating/In Pull up (pour un RaspberryPi par exemple)
- TXD : AltOut Push Pull