An introduction to ‘Bare Metal’-programming the Arm-Cortex-M4(R) MCUs – Lesson 12: ADC (II): Multiple channels without DMA

0 Abstract

This (short) lesson will cover the advanced usage of the Analog-Digital-Converter (ADC) in the STM32F4 MCU. We will access multiple ADC input pins, here we are going to use PA4 and PA5 alternately as an example. No DMA is used in this code. This example is mainly for those who have read ADC values in 8-bit machines as it uses the same technique.

1 Setup sequence

This setup sequence is the same like in the unit covering the basic ADC usage. It starts, as usual, with the correct port initialisation:

/////////////////////////
//ADC1 Init //
/////////////////////////
//Port config and GPIOA power up
RCC->AHB1ENR |= (1 << 0);
GPIOA->MODER |= (3 << (4 << 1)); //Set PA4 to analog mode
GPIOA->MODER |= (3 << (5 << 1)); //Set PA5 to analog mode

followed by ADC configuration:

//ADC config sequence
RCC->APB2ENR |= (1 << 8); //Enable ADC1 clock (Bit8) 
ADC1->CR1 &= ~(1 << 8); //SCAN mode disabled (Bit8)
ADC1->CR1 &= ~(3 << 24); //12bit resolution (Bit24,25 0b00)
ADC1->SQR1 &= ~(0x0F << 20); //Set number of conversions projected (L[3:0] 0b0000)
ADC1->SQR3 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register
ADC1->SQR3 |= (4 << 0); //First conversion in regular sequence: PB4 as ADC1_4 (-> select ADC channel 4 as first (and only) conversion)
ADC1->CR2 &= ~(1 << 1); //Single conversion ADON=1
ADC1->CR2 &= ~(1 << 11); //Right alignment of data bits bit12....bit0
ADC1->SMPR2 |= (7 << 0); //Sampling rate 480 cycles. 16MHz bus clock for ADC. 1/16MHz = 62.5ns. 480*62.5ns=30us
ADC1->CR2 |= (1 << 0); //Switch on ADC1
//ADC ready for use

There is also one dummy conversion of the PA4 input but that is not necessary.

2 Reading multiple ADC related pins

Next we define a function that maps ADC1 to different channels:

int get_adc(int adc_channel)
{
    int adc_val = 0;

    switch(adc_channel)
    {
        case 1: ADC1->SQR3 &= ~(0x3FFFFFFF);
                ADC1->SQR3 |= (4 << 0); //Connect to ADC channel 4 (PA4)
                break;
        case 2: ADC1->SQR3 &= ~(0x3FFFFFFF);
                ADC1->SQR3 |= (5 << 0); //Connect to ADC channel 5 (PA5)
                break;
    }

    ADC1->CR2 |= (1 << 30);        //Start conversion using SWSTART bit 
    while(!(ADC1->SR & (1 << 1))); //Wait until conversion is complete
    adc_val = ADC1->DR;            //Read value from register

    return adc_val;

} 

Please note that the SQR3 register must be reset first before writing the new value to SQ1[4:0] bits! We write 0 into bits 29:0 by using the negation of ADC1->SQR3 &= ~(0x3FFFFFFF);