ADC sampling with DMA/interrupts
Posted: Thu Aug 22, 2019 6:23 pm
Hello all, hope this is the correct place for this post.
We have been working to implement ADC sampling using the DMA and conversion callbacks for high performance peak-finding and have run into some problems on our pyboard D (STM32F723IEK), namely that the serial connection appears to be dropping during interrupt sampling.
To do this sampling, we added a new method to the ADC class in adc.c and modified the adcx_init_periph to enable the continuous conversion and DMA requests.
We started out by modifying the dma.c file to add a new dma_descr_t for the ADC and a DMA_InitTypeDef with the circular buffer mode enabled.
Here is the code we are using to initialise the DMA in adcx_init_periph:
//// dma.c
// adc struct is customised version of i2c/spi structs
const dma_descr_t dma_ADC = { DMA2_Stream0, DMA_CHANNEL_0, dma_id_8, &dma_init_struct_adc };
//// adc.c
STATIC void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution){
...
static DMA_HandleTypeDef DMA_Handle;
// init the DMA with custom descriptor for ADC behaviour
dma_init(&DMA_Handle, &dma_ADC, DMA_PERIPH_TO_MEMORY, adch);
// link the DMA, replaces __HAL_LINKDMA(data, xxx, *dma)
// where we have called data->xxx = dma as required in dma.c comments
adch->DMA_Handle = &DMA_Handle;
...
}
// new custom function
STATIC void adc_read_DMA(...){
...
HAL_ADC_Start_DMA(&self->handle, bufinfo.buf,nelems);
while(1){}
return mp_const_none;
}
// callback function
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *adch){
printf("Callback");
}
Unfortunately, we cannot seem to be able to get the HAL_ADC_ConvCpltCallback to trigger for our ADC input to pin X12, or if it is, then it is causing the board serial connection to drop.
This appears to be the way to initialise the DMA IRQHandler for our channel and stream in dma.c, however it looks like the streams used for ADC1 such as DMA2_Stream0 currently have a different use.
When we tried to overwrite this one the board kept resetting, presumably calling the SD card IRQ handler and not our convcpltcallback, is there a way to attach multiple handlers to one stream to make use of the channels?
We also tried using the unused DMA2_Stream7 for our ADC but still no interrupts were observed (although the serial connection remained intact). However there doesnt seem to be an adc function on this stream/channel combination so that would explain the non-result.
Therefore it seems impossible to use ADC1 for DMA reading without serious rewrites since DMA2 stream0 and stream4 are in use. It does seem possible however to use DMA2_Stream1 to implement the
DMA interrupts with ADC3 on DMA2_Stream1 due to the line in mpconfigboard_common.h :
#define MICROPY_HW_ENABLE_DCMI (0)
which implies that
[#if MICROPY_HW_ENABLE_DCMI]
const dma_descr_t dma_DCMI_0 = { DMA2_Stream1, DMA_CHANNEL_1, dma_id_9, &dma_init_struct_dcmi };
is ignored by the compiler, freeing up the stream for ADC DMA usage. The complication here is that we are required to rewrite the adc.c file to use ADC3 for this and we are uncertain
how to access the correct pin mappings, furthermore there could already be a handler associated with this stream and we are not sure what its name is or where to find it. We are also puzzled as to where
the callback functions are for the other DMA interrupts.
Additionally we have tried implementing the normal STM32 HAL ADC interrupts, which dont rely on the DMA IRQHandler to more apparent success.
For this, one is required to define the ADC_IRQHandler(), however it is not obvious where these IRQ Handlers should be placed, after some trial and error we used the following code:
////stm32_it.c
extern ADC_HandleTypeDef ADCHandle;
...
void ADC_IRQHandler(void){
HAL_ADC_IRQHandler(&ADCHandle);
}
and we put the ADC_IRQHandler in the header too.
Then we have configured the watchdog in adc.c like so:
////adc.c
adcx_init_periph(...){
...
adch->Init.ContinuousConvMode = ENABLE;
adch->Init.ExternalTrigConv = ADC_SOFTWARE_START;
...
}
adc_make_new(...){
HAL_NVIC_SetPriority(ADC_IRQn,0,0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
...
}
// CUSTOM FUNCTION again a copy of read timed
adc_read_IT(...){
...
HAL_ADC_Start_IT(&self->handle);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *adch){
HAL_NVIC_DisableIRQ(ADC_IRQn)
if(counter>20000000){
led_toggle(PYB_LED_RED);
}
else{
counter++;
}
HAL_NVIC_EnableIRQ(ADC_IRQn);
}
which turned the red LED on after a small delay, which proves that the interrupts are working. This particular callback was necessary because the board was becoming unresponsive when interrupts were enabled, so we couldn't monitor the serial port for print statements. Perhaps this is also what was happening in our DMA interrupts as well.
What are your thoughts on this, I need to do some more testing on the DMA interrupts tomorrow to see if they are triggering, however it seems our biggest problem is that the serial connection breaks down when the ADC interrupts are enabled. This is of course not ideal for any application; do you have any ideas on the causes of the serial connection
dropping and the board "malfunctioning" (according to windows) and does the approach to the DMA handling seem to be sound?
Thank you for your assistance.
-Ryan
We have been working to implement ADC sampling using the DMA and conversion callbacks for high performance peak-finding and have run into some problems on our pyboard D (STM32F723IEK), namely that the serial connection appears to be dropping during interrupt sampling.
To do this sampling, we added a new method to the ADC class in adc.c and modified the adcx_init_periph to enable the continuous conversion and DMA requests.
We started out by modifying the dma.c file to add a new dma_descr_t for the ADC and a DMA_InitTypeDef with the circular buffer mode enabled.
Here is the code we are using to initialise the DMA in adcx_init_periph:
//// dma.c
// adc struct is customised version of i2c/spi structs
const dma_descr_t dma_ADC = { DMA2_Stream0, DMA_CHANNEL_0, dma_id_8, &dma_init_struct_adc };
//// adc.c
STATIC void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution){
...
static DMA_HandleTypeDef DMA_Handle;
// init the DMA with custom descriptor for ADC behaviour
dma_init(&DMA_Handle, &dma_ADC, DMA_PERIPH_TO_MEMORY, adch);
// link the DMA, replaces __HAL_LINKDMA(data, xxx, *dma)
// where we have called data->xxx = dma as required in dma.c comments
adch->DMA_Handle = &DMA_Handle;
...
}
// new custom function
STATIC void adc_read_DMA(...){
...
HAL_ADC_Start_DMA(&self->handle, bufinfo.buf,nelems);
while(1){}
return mp_const_none;
}
// callback function
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *adch){
printf("Callback");
}
Unfortunately, we cannot seem to be able to get the HAL_ADC_ConvCpltCallback to trigger for our ADC input to pin X12, or if it is, then it is causing the board serial connection to drop.
This appears to be the way to initialise the DMA IRQHandler for our channel and stream in dma.c, however it looks like the streams used for ADC1 such as DMA2_Stream0 currently have a different use.
When we tried to overwrite this one the board kept resetting, presumably calling the SD card IRQ handler and not our convcpltcallback, is there a way to attach multiple handlers to one stream to make use of the channels?
We also tried using the unused DMA2_Stream7 for our ADC but still no interrupts were observed (although the serial connection remained intact). However there doesnt seem to be an adc function on this stream/channel combination so that would explain the non-result.
Therefore it seems impossible to use ADC1 for DMA reading without serious rewrites since DMA2 stream0 and stream4 are in use. It does seem possible however to use DMA2_Stream1 to implement the
DMA interrupts with ADC3 on DMA2_Stream1 due to the line in mpconfigboard_common.h :
#define MICROPY_HW_ENABLE_DCMI (0)
which implies that
[#if MICROPY_HW_ENABLE_DCMI]
const dma_descr_t dma_DCMI_0 = { DMA2_Stream1, DMA_CHANNEL_1, dma_id_9, &dma_init_struct_dcmi };
is ignored by the compiler, freeing up the stream for ADC DMA usage. The complication here is that we are required to rewrite the adc.c file to use ADC3 for this and we are uncertain
how to access the correct pin mappings, furthermore there could already be a handler associated with this stream and we are not sure what its name is or where to find it. We are also puzzled as to where
the callback functions are for the other DMA interrupts.
Additionally we have tried implementing the normal STM32 HAL ADC interrupts, which dont rely on the DMA IRQHandler to more apparent success.
For this, one is required to define the ADC_IRQHandler(), however it is not obvious where these IRQ Handlers should be placed, after some trial and error we used the following code:
////stm32_it.c
extern ADC_HandleTypeDef ADCHandle;
...
void ADC_IRQHandler(void){
HAL_ADC_IRQHandler(&ADCHandle);
}
and we put the ADC_IRQHandler in the header too.
Then we have configured the watchdog in adc.c like so:
////adc.c
adcx_init_periph(...){
...
adch->Init.ContinuousConvMode = ENABLE;
adch->Init.ExternalTrigConv = ADC_SOFTWARE_START;
...
}
adc_make_new(...){
HAL_NVIC_SetPriority(ADC_IRQn,0,0);
HAL_NVIC_EnableIRQ(ADC_IRQn);
...
}
// CUSTOM FUNCTION again a copy of read timed
adc_read_IT(...){
...
HAL_ADC_Start_IT(&self->handle);
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *adch){
HAL_NVIC_DisableIRQ(ADC_IRQn)
if(counter>20000000){
led_toggle(PYB_LED_RED);
}
else{
counter++;
}
HAL_NVIC_EnableIRQ(ADC_IRQn);
}
which turned the red LED on after a small delay, which proves that the interrupts are working. This particular callback was necessary because the board was becoming unresponsive when interrupts were enabled, so we couldn't monitor the serial port for print statements. Perhaps this is also what was happening in our DMA interrupts as well.
What are your thoughts on this, I need to do some more testing on the DMA interrupts tomorrow to see if they are triggering, however it seems our biggest problem is that the serial connection breaks down when the ADC interrupts are enabled. This is of course not ideal for any application; do you have any ideas on the causes of the serial connection
dropping and the board "malfunctioning" (according to windows) and does the approach to the DMA handling seem to be sound?
Thank you for your assistance.
-Ryan