ADC using DMA
This is the Third article in the series of how to use ADC in STM32 and this time we will be using DMA (Direct Memory Access). To view the other two methods, visit ADC using interrupt in STM32 and ADC in STM32 using HAL
The advantage of using DMA is that we can use multi channel conversion. The result of the conversion will be stored in the memory and at any instance during the code, we can access that memory and read ADC values within interrupt service routine.
In this Tutorial I will use two channels, One of them is Internal Temperature Sensor of the Cortex M4 and second is the IR sensor. I will be using STM32CubeMx, Keil IDE and HAL libraries as usual.
NOTE:- Temperature sensor is being used just to show different values for the two channels. I will make a separate tutorial on how to read the Internal Temperature sensor.
So let's get started first we need to open the cubeMx and select two channels from ADC
Go to ADC setting and make sure that the setting is as below
Now we have to add a DMA request. Go to DMA Setting Tab and follow the setting in the image below
Enable the ADC1 global interrupt in the NVIC setting tab
Generate the project and open it.
First we need to define a variable for buffer and another to read value from buffer.
As i am using two channels therefore adc_buf should store two bytes and so does adc_val.
Write a callback function to read the values from the buffer whenever the conversion is complete. This is same as writing a callback in interrupt method
Now whenever the conversion is complete, the values present in adc_buf will be saved in adc_val, which we can read at any instant in our program.
NOTE here that We set DMA in circular mode which means that the conversion will continue in circular mode, latest value overwriting the oldest one.
Inside main function we need to start ADC with DMA by writing
Here HAL_ADC_Start_DMA takes following arguments
ADC_HandleTypeDef* hadc ADC handler
uint32_t* pData The destination Buffer address
uint32_t Length The length of data to be transferred from ADC peripheral to memory.
To demonstrate the working here, I am going to write an infinite loop to read the values from buffer every 1 second. I am going to create another variable for debugging purpose, whose value will update every 1 sec.
NOTE:- To Download full code, check the end of the post.
Compile the code and start debugging.
Add display_val[0] (for channel 0) and display_val[1] (for internal temperature) to the watch list and run the debugger.
Initially the channel 0, which is connected to IR sensor, shows 4095 because there is no obstacle in front of it and internal temperature sensor shows 953. We still have to convert this value to Celsius scale. We will see that part in another tutorial.
This is how we use DMA to for two different channels without compromising the cpu time. The converted data will be stored in memory and we can access it at any instant during the entire program.
We can use DMA to do as much conversion as we want (obviously limited by the number of channels present in the microcontroller).
To download full code, visit
http://controllerstech.com/adc-using-dma-direct-memory-access/
CHECK OUT THE VIDEO BELOW
DMA or Direct Memory Access is a method that allows and input/output device to send or receive data directly to or from the main memory, bypassing the CPU to speedup memory operations.
The advantage of using DMA is that we can use multi channel conversion. The result of the conversion will be stored in the memory and at any instance during the code, we can access that memory and read ADC values within interrupt service routine.
In this Tutorial I will use two channels, One of them is Internal Temperature Sensor of the Cortex M4 and second is the IR sensor. I will be using STM32CubeMx, Keil IDE and HAL libraries as usual.
NOTE:- Temperature sensor is being used just to show different values for the two channels. I will make a separate tutorial on how to read the Internal Temperature sensor.
So let's get started first we need to open the cubeMx and select two channels from ADC
Channel 0 and Internal temperature sensor is selected |
Go to ADC setting and make sure that the setting is as below
Resolution is 12 bit, Scan conversion- enabled, Continuous conv - enabled, no of conv - 2 |
Now we have to add a DMA request. Go to DMA Setting Tab and follow the setting in the image below
Enable the ADC1 global interrupt in the NVIC setting tab
Generate the project and open it.
First we need to define a variable for buffer and another to read value from buffer.
uint32_t adc_buf[2], adc_val[2];
As i am using two channels therefore adc_buf should store two bytes and so does adc_val.
Write a callback function to read the values from the buffer whenever the conversion is complete. This is same as writing a callback in interrupt method
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
for (int i =0; i<2;i++)
{
adc_val[i] = adc_buf[i]; // store the values in adc_val from buffer
}
}
NOTE here that We set DMA in circular mode which means that the conversion will continue in circular mode, latest value overwriting the oldest one.
Inside main function we need to start ADC with DMA by writing
HAL_ADC_Start_DMA (&hadc1, (uint32_t *) adc_buf, 2);
Here HAL_ADC_Start_DMA takes following arguments
ADC_HandleTypeDef* hadc ADC handler
uint32_t* pData The destination Buffer address
uint32_t Length The length of data to be transferred from ADC peripheral to memory.
To demonstrate the working here, I am going to write an infinite loop to read the values from buffer every 1 second. I am going to create another variable for debugging purpose, whose value will update every 1 sec.
uint32_t display_value[2];
main ()
{
.......
..........
while (1)
{
display_value[0] = adc_val[0];
display_value[1] = adc_val[1];
HAL_Delay (1000);
}
}
NOTE:- To Download full code, check the end of the post.
Compile the code and start debugging.
Add display_val[0] (for channel 0) and display_val[1] (for internal temperature) to the watch list and run the debugger.
Initially the channel 0, which is connected to IR sensor, shows 4095 because there is no obstacle in front of it and internal temperature sensor shows 953. We still have to convert this value to Celsius scale. We will see that part in another tutorial.
display_val[0] = 4095, display_val[1] = 953 |
IR sensor value changes on bringing something near it but temperature remains same. As the main() function suggests, we are reading values every 1 sec, so the change is not instant but only after a delay of 1 sec.
display_val[0] = 472, display_val[1] = 953 |
We can use DMA to do as much conversion as we want (obviously limited by the number of channels present in the microcontroller).
To download full code, visit
http://controllerstech.com/adc-using-dma-direct-memory-access/
CHECK OUT THE VIDEO BELOW
ADC using DMA
Reviewed by Controllerstech
on
August 28, 2017
Rating:
thank you! you article helps me a lot!
ReplyDelete