ADC | STM32F103C8T6 - ARM Cortex M3

 ADC in STM32F103C8T6 is 12-bit and a successive approximation analog-to-digital converter.

It has up to 18 multiplexed channels allowing it measure signals from sixteen external and two internal sources. The result of the ADC is stored in a left-aligned or right-aligned 16-bit data register.

ADC converter supports several conversion modes:

  1. Single mode, which converts only one channel, in single-shot or continuous mode.           
  2. Scan mode, which converts a complete set of pre-defined programmed input channels, in single-shot or continuous mode.                                                                                       
  3. Discontinuous mode, converts only a single channel at each trigger signal from the list of pre-defined programmed input channels.
In each mode, there are regular and injected channels modes
  1. In regular mode, ADC read channels sequentially in a loop and convert them regularly.    
  2. In injected mode conversion is triggered by an external event or by software. An injected conversion has higher priority in comparison to a regular conversion and  thus interrupts the regular conversions. 



Temperature sensor:

The temperature sensor is connected to channel ADCx_IN16 and the internal reference voltage VREFINT is connected to ADCx_IN17.

The recommended sampling time for the temperature sensor is 17.1 µs.

The TSVREFE bit must be set to enable both internal channels: ADCx_IN16 (temperature sensor) and ADCx_IN17 (VREFINT) conversion.

Let's use ADC 1 to convert the analog value to digital from internal temperature sensor.

Program:

/* ADC in STM32
- Single mode, regular channel
- Right alignment
*/

#include "stm32f10x.h"
void RCC_config(void);
void ADC1_2_IRQHandler(void);
void delay(uint16_t);

static uint16_t data;

void delay(uint16_t d)
{
  for(; d; d --);
}

void ADC1_2_IRQHandler()
{
  if(ADC1 -> SR & ADC_SR_EOC)
  {
    data = (uint16_t)ADC1 -> DR;         // Read the data (16-bit), this will also clear the flag
    GPIOC -> BSRR = GPIO_BSRR_BR13;     // PC13 LED - ON
    // Turn off ADC if required using ADCON bit
  }
}

void RCC_config() // RCC clock configuration
{
  RCC -> CR |= RCC_CR_HSEON;                               // HSE ON
  RCC -> CFGR |= (RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL4);    // Setting PLL = HSE * 4
  RCC -> CFGR |= RCC_CFGR_SW_1;                            // SysClk = PLLCLK
  RCC -> CR |= RCC_CR_PLLON;                                              
  // Turn ON PLL after above PLL configurations, making SysClk = HSE * 4 = 8 * 4 = 32 MHz
}

int main()
{
  RCC_config();
 
  /* PC13 LED configuration */
  RCC -> APB2ENR |= RCC_APB2ENR_IOPCEN;                  // Enable Port-C Clock
  GPIOC -> CRH |= GPIO_CRH_MODE13_1 | GPIO_CRH_CNF13_0;  // PortC_Pin 13 as output @2MHZ with open-drain
  GPIOC -> BSRR = GPIO_BSRR_BS13;                       // Initialize PC13 LED - OFF
 
  /* ADC configuration */
  RCC -> CFGR |= RCC_CFGR_ADCPRE_DIV8;                   // ADC Prescaler: SysClk / 8 = 32 / 8 = 4 MHz
  RCC -> APB2ENR |= RCC_APB2ENR_ADC1EN;                  // Enable ADC 1 clock
  /*ADC1 -> SQR1 = 0000 -> 1 conversion */
  ADC1 -> SQR3 |= ADC_SQR3_SQ1_4;                        // Seq 1 : channel 16 (bit 4)
  ADC1 -> SMPR1 |= ADC_SMPR1_SMP16_0 | ADC_SMPR1_SMP16_2;// Sample time = 55.5 cycle
  ADC1 -> CR1 |= ADC_CR1_EOCIE;                          // EOC interrupt enable
  NVIC_EnableIRQ(ADC1_2_IRQn);                           // Enable interrupt in NVIC
  ADC1 -> CR2 |= ADC_CR2_TSVREFE;                        // Enable Temperature sensor
 
  ADC1 -> CR2 |= ADC_CR2_ADON;                           // Turn ON ADC
  delay(32);                                             // approximate 1 us delay for 32 MHz
  ADC1 -> CR2 |= ADC_CR2_ADON;                           // Turn ON ADC second time
  /* CONT = 0 **, ALIGN = 0 */
  ADC1 -> CR2 |= ADC_CR2_CAL;                            // Calibrate ADC before start
  ADC1 -> CR2 |= ADC_CR2_RSTCAL;                         // Indicates start of calibration
  while(ADC1 -> CR2 & ADC_CR2_RSTCAL);                   // Wait till calibration is done
 
  //ADC1 -> CR2 |= ADC_CR2_SWSTART;                        // Start the regular conversion
 
  while(1)
  {
    // Do nothing
  }
  return 0;
}

 For more details on RCC  configuration, see previous post.

Steps:    

1. Enable ADC clock in RCC register and use ADC prescaler to get desired ADC clock

SysClk = 32 MHz
ADC Prescaler = 8
ADC clock = 32 / 8 = 4 MHz

2. Enable Temperature sensor with TSVREFE bit in CR2 register

3. Since we are using in-built sensor, we are not configuring any ADC channel pins as analog



4. Single mode one-shot conversion: CONT bit in CR2 register is 0

- Conversion in regular channel 

- Data alignment: ALIGN bit in CR2 register is 0 - Right alignment

5. Assign the channels you want to convert in the regular sequence register and the number of conversion in L[3:0] in ADC_SQR1 register

6. Sampling Time: Configure RCC and ADC prescaler to get desired time    

ADC samples the input voltage for a number of ADC_CLK cycles which can be modified using the SMP[2:0] bits in the ADC_SMPR1 and ADC_SMPR2 registers. 

Each channel can be sampled with a different sample time. 

We are going to modify ADC_16 channel sampling time to which temperature sensor is connected.

The total conversion time is calculated as follows: 

Tconv = Sampling time + 12.5 cycles 

With an ADCCLK = 4 MHz and a sampling time of 55.5 cycles to get sampling time of 17.1 µs for temperature sensor

Tconv = 55.5 + 12.5 = 68 cycles 

68 * 1 / 4 MHz = 17 µs (approx.)

7. ADC on-off control:

The ADC can be powered-on by setting the ADON bit in the ADC_CR2 register. When the ADON bit is set for the first time, it wakes up the ADC from Power Down mode.

Conversion starts when ADON bit is set for a second time by software after ADC power-up time (tSTAB). The conversion can be stopped, and the ADC put in power down mode by resetting the ADON bit.

8. Lets calibrate the ADC before starting the conversion

Calibration is started by setting the CAL bit in the ADC_CR2 register. Once calibration is over, the CAL bit is reset by hardware and normal conversion can be performed. It is recommended to calibrate the ADC once at power-on.

Note: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) for at least two ADC clock cycles.

9. Start the regular conversion using SWSTART bit in CR2 register

10. Once the conversion of the selected channel is complete: 

 In a regular channel: 

The converted data is stored in the 16-bit ADC_DR register

– The EOC (End Of Conversion) flag is set

– and an interrupt is generated if the EOCIE is set. 

ADC 1 and 2 have same interrupt handler - global interrupt

Once we get the data, we could convert the digital value to temperature (in this case) or to any other sensor physical value

11. If more than one channel conversion is done, in order to avoid data loss, use DMA.

12. ADC watchdog can be used to sense if the measured voltage gets above or below high / low voltage threshold.

13. To Print output on a Serial Terminal, use UART. For UART in STM32, see this post

For DMA in STM32, see this post

For RTOS in STM32, see this post

Connecting Bluetooth with STM32 - post



Comments