# 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](../assets/cd00171190-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf)) : 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 : ![ODR](../assets/odr.png) Pour sélectionner le mode, cela se passe dans le registre CR L/H : ![CRLH](../assets/crl.PNG) 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 : ![CRLH](../assets/table20_portbit.PNG) 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 : ```c 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](../assets/cd00171190-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf) page 197, la second e est l'ISER qui permet la réecriture des fonction d'IRQ trouvable dans le [Manuel de programmation PM0056](../assets/New_Programming_Manual_CortexM3.pdf) page 119. ```c 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** : ![ADC CR2](../assets/adc_cr2.png) 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 : ```c 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](../assets/cd00171190-stm32f101xx-stm32f102xx-stm32f103xx-stm32f105xx-and-stm32f107xx-advanced-armbased-32bit-mcus-stmicroelectronics.pdf). 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 ![ODR](../assets/ccer.png) 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](../assets/STM32F103RB-3.pdf) 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** : ![ADCPRE](../assets/adc_cfgr.png) Le minimum pouvant être mis en place est donc "10" avec PCLK2 divisé par 6, ce qui donne 12 MHz ```c 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. ```c 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*. ```c 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. ```c 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 : ```c 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 : ![ODR](../assets/usart_cr1.png) 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) : ![ODR](../assets/usart_sr.png) - 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](../assets/STM32F103RB-3.pdf) 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