enum GPIO_PIN_SETTINGS { I_ANALOG, // 00:00 Analog mode : Input mode O10_GP_PP, // 00:01 General purpose output push-pull : Output mode, max speed 10 MHz O02_GP_PP, // 00:10 General purpose output push-pull : Output mode, max speed 02 MHz O50_GP_PP, // 00:11 General purpose output push-pull : Output mode, max speed 50 MHz I_FLOATING, // 01:00 Floating input : Input mode (reset state) O10_GP_OD, // 01:01 General purpose output open-drain : Output mode, max speed 10 MHz O02_GP_OD, // 01:10 General purpose output open-drain : Output mode, max speed 02 MHz O50_GP_OD, // 01:11 General purpose output open-drain : Output mode, max speed 50 MHz I_PU_PD, // 10:00 Input with pull-up / pull-down : Input mode O10_AF_PP, // 10:01 Alternate function output push-pull : Output mode, max speed 10 MHz O02_AF_PP, // 10:10 Alternate function output push-pull : Output mode, max speed 02 MHz O50_AF_PP, // 10:11 Alternate function output push-pull : Output mode, max speed 50 MHz RESERVED, // 11:00 Reserved : Input mode O10_AF_OD, // 11:01 Alternate function output push-pull : Output mode, max speed 10 MHz O02_AF_OD, // 11:10 Alternate function output push-pull : Output mode, max speed 02 MHz O50_AF_OD, // 11:11 Alternate function output push-pull : Output mode, max speed 50 MHz };On peut maintenant configurer la PIN 2 du GPIO A en Output à 50 MHz et Push-Pull:
GPIOA->CRL |= O50_GP_PP << (2 * 4); // PA2 Output, 50 MHz, general purpose, push-pullCe qui n'est pas une bonne idée car une bonne pratique consiste d'abord à remettre à zéro puis de modifier des bits de configuration:
#define GPIO_PIN_RESET_Msk 0xf GPIOA->CRL &= ~(GPIO_PIN_RESET_Msk << (2 * 4)); // Reset 4 bits [8:11] GPIOA->CRL |= O50_GP_PP << (2 * 4); // PA2 Output, 50 MHz, general purpose, push-pullL'astuce ici consiste à utiliser le numéro de la PIN pour calculer le décalage de bit (rappel: les opérations binaires sont un pré-requis).
#define GPIO_PIN_RESET_Msk 0xf #define GPIO_HIGH_OFFSET 32 void gpio_pin_init(GPIO_TypeDef *gpio, uint8_t pin, enum GPIO_PIN_SETTINGS setting) { pin *= 4; if (pin < GPIO_HIGH_OFFSET) { gpio->CRL &= ~(GPIO_PIN_RESET_Msk << pin); gpio->CRL |= setting << pin; } else { pin -= GPIO_HIGH_OFFSET; gpio->CRH &= ~(GPIO_PIN_RESET_Msk << pin); gpio->CRH |= setting << pin; } }Note pour moi-même: je sais bien que mixer des paramètres de 8 et 32 bits ne se fera pas dans la joie et la bonne humeur mais il est un peu tôt pour commencer à se prendre la tête avec l'assembleur, le prefetch et les optimisations d'alignement.
$ cat config.h #pragma once #define INIT_SECONDS 3 #define SWITCH_GPIO GPIOA #define SWITCH_PIN 0 #define RED_GPIO GPIOA #define RED_PIN 1 #define RED_SECONDS 8 #define ORANGE_GPIO GPIOA #define ORANGE_PIN 2 #define ORANGE_SECONDS 4 #define GREEN_GPIO GPIOA #define GREEN_PIN 3 #define GREEN_SECONDS 8 #define APB2_EN RCC_APB2ENR_IOPAENLe programme principal:
1 #include "stm32f1xx.h" 2 #include "gpio.h" 3 #include "systick.h" 4 5 #include "config.h" 6 7 enum STATES { INIT = 0, RED, ORANGE, GREEN }; 8 9 unsigned int state, systick_irqs; 10 11 int 12 main (void) { 13 SystemCoreClockUpdate(); // Update globale variable SystemCoreClock 14 RCC->APB2ENR |= APB2_EN; // Enable GPIOs 15 16 gpio_pin_init(SWITCH_GPIO, SWITCH_PIN, O02_GP_PP); 17 SWITCH_GPIO->BRR = 1 << SWITCH_PIN; // PIN low SWITCH off 18 19 gpio_pin_init(RED_GPIO, RED_PIN, O02_GP_PP); 20 gpio_pin_init(ORANGE_GPIO, ORANGE_PIN, O02_GP_PP); 21 gpio_pin_init(GREEN_GPIO, GREEN_PIN, O02_GP_PP); 22 23 // PINs high LEDs off 24 RED_GPIO->BSRR = 1 << RED_PIN; 25 ORANGE_GPIO->BSRR = 1 << ORANGE_PIN; 26 GREEN_GPIO->BSRR = 1 << GREEN_PIN; 27 28 SWITCH_GPIO->BSRR = 1 << SWITCH_PIN; // PIN high SWITCH on 29 30 state = INIT; 31 systick_irqs = 0; 32 systick_delay_irq(SystemCoreClock); // 1 second @8 MHz 33 34 while (1) { 35 __WFI(); 36 if (state == RED && systick_irqs == RED_SECONDS) { 37 systick_irqs = 0; 38 state = GREEN; 39 RED_GPIO->BSRR = 1 << RED_PIN; // PIN high RED off 40 GREEN_GPIO->BRR = 1 << GREEN_PIN; // PIN low GREEN on 41 } 42 else if (state == GREEN && systick_irqs == GREEN_SECONDS) { 43 systick_irqs = 0; 44 state = ORANGE; 45 GREEN_GPIO->BSRR = 1 << GREEN_PIN; // PIN high GREEN off 46 ORANGE_GPIO->BRR = 1 << ORANGE_PIN; // PIN low ORANGE on 47 } 48 else if (state == ORANGE && systick_irqs == ORANGE_SECONDS) { 49 systick_irqs = 0; 50 state = RED; 51 ORANGE_GPIO->BSRR = 1 << ORANGE_PIN; // PIN high ORANGE off 52 RED_GPIO->BRR = 1 << RED_PIN; // PIN low RED on 53 } 54 else if (state == INIT && systick_irqs == INIT_SECONDS) { 55 systick_irqs = 0; 56 state = RED; 57 RED_GPIO->BRR = 1 << RED_PIN; // PIN low RED on 58 } 59 } 60 61 return 0; 62 } 63 64 void 65 SysTick_Handler(void) { 66 systick_irqs++; 67 }
La série de if est "triée", c'est pourquoi l'état "INIT" est testé en dernier (il n'est valable qu'au démarrage de la carte) et celui d'"ORANGE" après ceux de "GREEN" et "RED", bien plus fréquents. Il serait dommage de s'arréter (au feu rouge, ah ah ah ...) en si bon chemin et ajoutons un bouton.
Le code complet: https://gitlab.com/dsx/blue-pill/-/tree/master/stm32f103c8t6/00_gpios