1 void 2 adc_regular_add(ADC_TypeDef *adc, uint8_t channel, enum ADC_CYCLES cycles, uint8_t idx) { 3 volatile uint32_t *sqr; 4 uint8_t sqr_pos; 5 6 adc_init_channel(adc, channel, cycles); 7 #define BITS_PER_SEQUENCE 5 8 #define RESET_5BITS 0x1F 9 if (idx < 7) { 10 sqr = &adc->SQR3; 11 sqr_pos = idx * BITS_PER_SEQUENCE; 12 } 13 else if (idx < 13) { 14 sqr = &adc->SQR2; 15 sqr_pos = (idx - 7) * BITS_PER_SEQUENCE; 16 } 17 else { 18 sqr = &adc->SQR1; 19 sqr_pos = (idx - 13) * BITS_PER_SEQUENCE; 20 } 21 *sqr &= ~(RESET_5BITS << sqr_pos); 22 *sqr |= channel << sqr_pos; 23 }La séquence commence par le registre SQR3 et se termine par le SQR1. L'index passé en paramètre (idx) permet de calculer la position où placer le numéro de canal dans le registre. Les lignes 9 à 20 calculent le registre et la position, la ligne 22 place la valeur du canal au bon endroit.
... uint16_t adc_values[] = { SENSORS_CHANNELS }; // channel number as fake value uint8_t i, current_sensor; GPIO_TypeDef *sensors_led_gpio[] = { RED_GPIO, ORANGE_GPIO, GREEN_GPIO }; uint8_t sensors_led_pin[] = { RED_PIN, ORANGE_PIN, GREEN_PIN }; int main (void) { int8_t sensors_channels[] = { SENSORS_CHANNELS, -1 }; ...On trouve ici le tableau servant à stocker les conversions (adc_values), 2 variables pour naviguer dans les tableaux et les 2 tableaux d'accès aux LEDs. Petite astuce: j'utilise la liste de canaux pour initialiser le tableau des valeurs afin qu'il ait la bonne taille (mais des valeurs "fantaisistes"). Le tableau sensors_channels liste les canaux, le -1 est une autre astuce pour terminer le parcours du tableau (et éviter d'utiliser sizeof).
... adc_init(SENSORS_ADC, SENSORS_CR2); adc_init_calibration(SENSORS_ADC); for (i = 0; sensors_channels[i] != -1; i++) { adc_regular_add(SENSORS_ADC, sensors_channels[i], ADC_239_5, i); adc_values[i] = ADC_MAX_VALUE / 2; // reset initial values } SENSORS_ADC->SQR1 |= (i - 1) << ADC_SQR1_L_Pos; // sequence length (3 < i) SENSORS_ADC->CR1 |= ADC_CR1_SCAN; // scan mode SENSORS_ADC->CR2 |= ADC_CR2_DMA | ADC_CR2_CONT; // enable DMA & CONTinous mode ...Initialisation de l'ADC, ajout des capteurs et configuration de l'ADC (longueur de la séquence, mode scan, activation du DMA et conversion en continu).
void SysTick_Handler(void) { ONBOARD_LED_GPIO->ODR ^= 1 << ONBOARD_LED_PIN; // toggle state SysTick->LOAD = SYSTICK_LOAD_MIN + (adc_values[current_sensor] * SYSTICK_LOAD_STEP); }C'est bien parce que le gestion d'interruption SysTick calcule la prochaine valeur à charger qu'il faut veiller à ce qu'elle ne soit pas trop petite au risque de subir une tempête d'interruption.
void BUTTON_HANDLER(void) { EXTI->PR |= BUTTON_EXTI_PR; // Reset irq sensors_led_gpio[current_sensor]->BSRR = 1 << sensors_led_pin[current_sensor]; // PIN high, LED off current_sensor++; if (current_sensor == i) { current_sensor = 0; } sensors_led_gpio[current_sensor]->BRR = 1 << sensors_led_pin[current_sensor]; // PIN low, LED on }Le gestionnaire d'interruption du bouton acquitte l'interruption, éteint la LED courante, passe à la LED suivante et l'allume.
Le code complet: https://gitlab.com/dsx/blue-pill/-/tree/master/stm32f103c8t6/04_adc_sensors