2.3. Analog input (ADC)


1. Introduction

 

Analog to Digital Converters (ADC) are involved in several embedded projects. From the capture of UI potentiometers to digitalization of sensor data, the use of ADC is manifest in number of cases. STM32 may have one or more ADCs. An ADC is a complex hardware, that takes room on silicon and that requires power to operate. Therefore, few ADCs are usually embedded in a MCU. In order to achieve conversions on several channels (pins), a single ADC is sequentially connected to multiples input pins.
ADCs peripherals can operate on a single channel or on multiples channels sequentially. You can perform a single conversion on demand (or a single sequence), or let ADC work continuously.
In this tutorial, you will setup ADC to work continuously on a single channel. If you understand what’s done here, you will be able to adapt the configuration to your needs.

 

2. Pin mapping

 

Not any MCU pin can be elected for ADC conversion. You need to refer to the datasheet to discover which pins are connected to ADC input mux.

Note that ADC connection is not considered as an Alternate Function, as we saw for USART pins. ST calls this an Additional Function instead. It is reported in the pin definitions tables of the datasheet.

For example, PA0 and PA1 are connected to ADC input 0 and 1 respectively:

image011.png
 

In the STM32F072RB device, there is only one ADC, but the input mux features 16 inputs (0 to 15). There is no reason to prefer a channel among others, unless you have pin restrictions. In the lab, let us arbitrarily choose the ADC channel 11. According to the table below, channel 11 is connected to pin PC1.

image012.png

Ultimately, you have the make sure that PC1 is not involved in some board-level function (such as LED, button, USB…). The best source of information is the board schematics.
Here, we can see that PC1 is totally free for our purpose.

image013.png
 

3. ADC configuration and test

 

The code below sets the ADC for a single continuous conversion on channel 11. As usual, the first step is to configure associated GPIO in the correct mode (analog here).
Next, ADC clock is enabled, followed by ADC registers settings. Refer to the reference manual to grasp a complete understanding of what is done there.

Add the following code to existing bsp.c file in the my_project project:

/*
 * ADC_Init()
 * Initialize ADC for a single channel conversion
 * on channel 11 -> pin PC1
 */

void BSP_ADC_Init()
{
	// Enable GPIOC clock
	RCC->AHBENR |= RCC_AHBENR_GPIOCEN;

	// Configure pin PC1 as analog
	GPIOC->MODER &= ~GPIO_MODER_MODER1_Msk;
	GPIOC->MODER |= (0x03 <<GPIO_MODER_MODER1_Pos);

	// Enable ADC clock
	RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

	// Reset ADC configuration
	ADC1->CR 	= 0x00000000;
	ADC1->CFGR1  = 0x00000000;
	ADC1->CFGR2  = 0x00000000;
	ADC1->CHSELR = 0x00000000;

	// Enable continuous conversion mode
	ADC1->CFGR1 |= ADC_CFGR1_CONT;

	// 12-bit resolution
	ADC1->CFGR1 |= (0x00 <<ADC_CFGR1_RES_Pos);

	// Select PCLK/2 as ADC clock
	ADC1->CFGR2 |= (0x01 <<ADC_CFGR2_CKMODE_Pos);

	// Set sampling time to 28.5 ADC clock cycles
	ADC1->SMPR = 0x03;

	// Select channel 11
	ADC1->CHSELR |= ADC_CHSELR_CHSEL11;

	// Enable ADC
	ADC1->CR |= ADC_CR_ADEN;

	// Start conversion
	ADC1->CR |= ADC_CR_ADSTART;
}

 

Add the prototype declaration to bsp.h:

/*
 * ADC functions
 */

void BSP_ADC_Init		(void);

 

Now it is time to test this:

// Main program

void main()
{
	uint32_t	i;

	// Configure System Clock
	SystemClock_Config();

	// Initialize Debug Console
	BSP_Console_Init();
	my_printf("Console ready!\r\n");

	// Initialize and start ADC on PC1
	BSP_ADC_Init();
	my_printf("ADC ready!\r\n");

	// Main loop
	while(1)
	{
		// Wait here until ADC EOC
		while ((ADC1->ISR & ADC_ISR_EOC) != ADC_ISR_EOC);

		// Report result to console
		my_printf("ADC value = %d\r\n", ADC1->DR);

		// Wait about 200ms
		for (i=0; i<500000; i++);
	}
}

ADC conversion result is available in the Data Register (DR). It is a good idea to make sure that last conversion is done before reading this register. This is achieved by polling the EOC (End Of Conversion) flag of ADC1 peripheral.

 

Save all, build and run:

image017.png
 
  Commit name "ADC driver"
  Push onto Gitlab

 

Note that you could as well use the debugger to monitor ADC results, but in a less “real-time” fashion.
To further test the code, one should cook a little bit of hardware.

First, locate the PC1 pin on the Nucleo board header:

image020.png
 

PC1 corresponds to the A4 “Arduino” pin. Leaving PC1 floating may produce random conversion results. Connecting PC1 to GND should display 0 for the ADC value. Connecting PC1 to the +3.3V pin should display something close to 4095 (0x0FFF, the full range for a 12-bit value).

If these simple tests succeed, then it seems that everything is working fine.

To modulate the ADC input continuously, you can wire a potentiometer this way:

image022.png
 

4. Monitoring with STM Studio

 

4.1. STM Studio® Install

 

STM Studio® is a software that can display program variable in real-time. Of course, there are few limitations. Basically, you can monitor:

  • Statically allocated variables (i.e. global) only
  • At 1kHz maximum sample rate (1ms)

This is still an incredibly powerful way for finding out bugs or tuning computation loop in control applications (ex. tuning a PID controller)

 

First, you need to install STM Studio. You can get STM Studio from ST website :

Because we can only monitor global variables, let’s add a global variable to store ADC result:

// GLobal variables

uint16_t	ADC_result;

// Main program

void main()
{
	uint32_t	i;

	// Configure System Clock
	SystemClock_Config();

	// Initialize Debug Console
	BSP_Console_Init();
	my_printf("Console ready!\r\n");

	// Initialize and start ADC on PC1
	BSP_ADC_Init();
	my_printf("ADC ready!\r\n");

	// Main loop
	while(1)
	{
		// Wait here until ADC EOC
		while ((ADC1->ISR & ADC_ISR_EOC) != ADC_ISR_EOC);

		// Report result to console
		ADC_result = ADC1->DR;
		my_printf("ADC value = %d\r\n", ADC_result);

		// Wait about 200ms
		for (i=0; i<500000; i++);
	}
}

 
Build the project, verify that you have no errors or warning, and then flash your target board. Check in your terminal that the program is working well, just as before.

 

4.2. STM Studio Setup

 

Start STM Studio application from Windows start menu.

image028.png

 

Change the ST-Link protocol to SWD (Serial Wire Debug Interface)

image030.png

 

Go to the main menu File → Import variables or click image031.png

 

In the Import variables from executable windows, use the image032.png button on the right of the Excutable file field to browse for your .elf project executable file. You will find the .elf file in the following subdirectory from your project folder under the \Debug folder.

STM Studio then reports a list of all the variables that you can “spy”:

image035.png
 

Select ADC_result, and then click the Import button. Then Close this window.

 

Drag & drop the ADC_result variable from the Variable settings zone to the VarViewer 1 zone:

image039.png

 

Then set the Viewer range according to what you expect from the variable. 12-bit ADC delivers values from 0 to 4095.

image041.png
 

Now go to the menu Option → Acquisition Settings or clickimage042.png

image045.png
 

Set the Graphical refresh rate to 5ms, and uncheck the Log to file option. You may also change the Acquisition Rate accordingly. Click OK to close the window.

 

4.3. Start monitoring

 

Go to menu Run → Start or click the image047.png button.

If you get the window below, make sure that you terminate the OpenOCD session under Eclipse and retry. You cannot have both connected to the ST-Link at the same time.

image048.png
 

 
You can now monitor the fluctuations of the voltage applied on PC1 pin, just like with an oscilloscope. You can even make sure that STM Studio and the data you print in the console are the same (as it should be).

image049.png

 

You can also play with the various options you have to display data (Curve, Bar Graph, Table):

image051.png

 

You may also change the acquisition settings, you can zoom (in/out) curves, and even record data to a file for further analysis or reporting purposes (Excel, Matlab…).
Note that you cannot flash the code while STM Studio is running.

You don’t have to close STM Studio, just hit the stop image052.png button before flashing a new code and then restart monitoring process.

STM Studio is bond to the .elf file now, and will update variable addresses automatically if necessary.

 

5. Summary

In this tutorial, you have setup ADC for a single channel continuous conversion and make use of STM-Studio to real-time monitor the conversion result.