2016-10-09 02:07 PM
Hi,
We have a design using an STM32F030 and we have three analog channels attached to the ADC. We are currently not using a timer to start the conversions(doing it manually.) The problem is that I initially set up a DMA interrupt but it never fired. I then set up a ADC interrupt on the end of sequence. The ADC interrupt fires after all three channels have been converted but we do not see the DMA interrupt fire. The data is DMA'ed into the DMA buffer, just no interrupt. What have I mis-configured? Thanks, John C. int main(void) { /* GPIOA-C Periph clock enable */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); /* Enable SYSCFG clock */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); /* DMA1 clock enable */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE); // Configure the PortA(PA) analog input pins GPIO_InitStructure.GPIO_Pin = CADCELL | LINE | CURRENT; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, &GPIO_InitStructure); ADC_InitStruct.ADC_Resolution = ADC_Resolution_10b; ADC_InitStruct.ADC_ScanDirection = ADC_ScanDirection_Upward; /* Initialize ADC */ ADC_Init(ADC1, &ADC_InitStruct); /* Enable ADC */ ADC_GetCalibrationFactor(ADC1); ADC_ITConfig(ADC1 ,ADC_IT_EOSEQ, ENABLE); NVIC_EnableIRQ(ADC1_IRQn); /* (7) */ NVIC_SetPriority(ADC1_IRQn,0); /* (8) */ ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); //Wait for ADC to be ready while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY)); //Set ADC conversion channel sample time ADC_ChannelConfig(ADC1, ADC_Channel_0, ADC_SampleTime_28_5Cycles); ADC_ChannelConfig(ADC1, ADC_Channel_2, ADC_SampleTime_28_5Cycles); ADC_ChannelConfig(ADC1, ADC_Channel_4, ADC_SampleTime_28_5Cycles); DMA_InitTypeDef DMA_InitStructure; /* DMA1 Channel1 Config */ DMA_DeInit(DMA1_Channel1); //DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)dmaBuff; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize = 3; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); /* DMA1 Channel1 enable */ DMA_Cmd(DMA1_Channel1, ENABLE); NVIC_EnableIRQ(DMA1_Channel1_IRQn); NVIC_SetPriority(DMA1_Channel1_IRQn,0); while (1) { // Start conversion on ADC1 ADC_StartOfConversion(ADC1); //Wait for DMA to complete while(!DMA_GetFlagStatus(DMA1_FLAG_TC1)); //Clear DMA flag DMA_ClearFlag(DMA1_FLAG_TC1); } } void ADC1_COMP_IRQHandler(void) { if (ADC1->ISR & ADC_ISR_EOSEQ) /* Check the end of sequence bit */ { dmaHead++; dmaHead &= (MAX_DMA_XFERS-1); DMA1_Channel1->CMAR = (uint32_t)&dmaBuff[dmaHead][0]; ADC1->ISR |= ADC_ISR_EOSEQ; } if ((ADC1->ISR & ADC_ISR_OVR) != 0) /* Check OVR has triggered the IT */ { GPIOC->BSRR = (1<<8); /* Switch on orange led to report a resume of the conversion */ GPIOC->BSRR = (1<<(9+16)); /* Switch off green led to report it is due to overrun */ ADC1->ISR |= ADC_ISR_OVR; /* Clear the pending bit */ ADC1->CR |= ADC_CR_ADSTP; /* Stop the sequence conversion */ } else { //error |= ERROR_UNEXPECTED_ADC_IT; /* Report unexpected ADC interrupt occurrence */ } } void DMA1_Channel1_IRQHandler(void) { if ((DMA1->ISR & DMA_ISR_TCIF1) != 0) /* Test if transfer completed on DMA channel 1 */ { } }2016-10-09 06:27 PM
Hi John,
Missing this (from the F0 std peripheral lib) ? ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular); Glenn2016-10-10 07:42 AM
Hey Glenn,
I added your line before I enabled ADC1. I still do not see and DMA interrupts. Did I place the call in the wrong location? Thanks, John C. /* Enable ADC */ ADC_GetCalibrationFactor(ADC1); ADC_ITConfig(ADC1 ,ADC_IT_EOSEQ, ENABLE); NVIC_EnableIRQ(ADC1_IRQn); /* (7) */ NVIC_SetPriority(ADC1_IRQn,0); /* (8) */ ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular); <<------------- ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE);2016-10-10 08:56 AM
The code as presented in uncompilable.
I'd avoid setting the DMA address in the IRQ Handler.Review the DMA registers to understand if there is an error condition, or if it is just not advancing. The IRQ will not fire unless transfers are occurring, walk back the issue until you understand why.2016-10-10 09:32 AM
Hey Clive,
The DMA transfers (all three) complete correctly and continue to run fine. I only modified the DMA address in the ADC interrupt because I could not get the DMA complete interrupt to fire. As far as the code being uncompilable it is because this code is being run on our own board and not a demo/discovery. I have included all setup and running code for the ADC. Any suggestions on what I could look for in the DMA register set? Thanks, John C.2016-10-10 09:58 AM
Start by decomposing all the DMA registers related to the channel. Not using F0 myself, so unless the code is easy to rack-up and test, it won't be.
I don't see the TC interrupt enabled anywhere. I've done this previously with this/* Enable DMA1 Channel1 Half Transfer and Transfer Complete interrupt */
DMA_ITConfig(DMA1_Channel1, DMA_IT_HT, ENABLE);
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);
...
void DMA1_Channel1_IRQHandler(void) // Called at 4 Hz, LED Toggles at 2 Hz
{
/* Test on DMA1 Channel1 Transfer Complete interrupt */
if (DMA_GetITStatus(DMA1_IT_HT1))
{
/* Clear DMA1 Channel1 Half Transfer interrupt pending bits */
DMA_ClearITPendingBit(DMA1_IT_HT1);
GPIOC->BSRR = 0x100; /* Set PC8 */
GPIOC->BRR = 0x200;/* Reset PC9 */
}
/* Test on DMA1 Channel1 Transfer Complete interrupt */
if (DMA_GetITStatus(DMA1_IT_TC1))
{
/* Clear DMA1 Channel1 Transfer Complete interrupt pending bits */
DMA_ClearITPendingBit(DMA1_IT_TC1);
GPIOC->BSRR = 0x200; /* Set PC9 */
GPIOC->BRR = 0x100;/* Reset PC8 */
}
}
Example 4 and 5 channel examples I've built/evaluated...
/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/STM32F0%20ADC-DMA%20touble&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=3066
2016-10-10 10:33 AM
Hey Clive,
The DMA_ITConfig() call was the answer. All my code is based on whatever standard peripheral library examples I can find. There is always a shortage of example code that fits my exact needs, so I end up piecing together several examples. Here is my final DMA config that is working: DMA_InitTypeDef DMA_InitStructure; /* DMA1 Channel1 Config */ DMA_DeInit(DMA1_Channel1); //DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR); DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)dmaBuff; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //DMA_InitStructure.DMA_BufferSize = sizeof(dmaBuff); DMA_InitStructure.DMA_BufferSize = 3; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE); /* DMA1 Channel1 enable */ DMA_Cmd(DMA1_Channel1, ENABLE); NVIC_EnableIRQ(DMA1_Channel1_IRQn); NVIC_SetPriority(DMA1_Channel1_IRQn,0); // And the interrupt code void DMA1_Channel1_IRQHandler(void) { if ((DMA1->ISR & DMA_ISR_TCIF1) != 0) /* Test if transfer completed on DMA channel 1 */ { dmaHead++; dmaHead &= (MAX_DMA_XFERS-1); DMA1_Channel1->CMAR = (uint32_t)&dmaBuff[dmaHead][0]; // Clear the interrupt pending flag DMA_ClearITPendingBit(DMA1_IT_TC1); } } What is the difference between DMA_ClearITPendingBit(DMA1_IT_TC1) and DMA_ClearFlag(DMA1_FLAG_TC1) ? One clears all relative DMA channel interrupts? Thanks, John C.2016-10-10 11:40 AM
What is the difference between DMA_ClearITPendingBit(DMA1_IT_TC1) and DMA_ClearFlag(DMA1_FLAG_TC1) ? One clears all relative DMA channel interrupts?
In the general STM32 model there is sometimes a bit reflecting the peripheral state, and another reflecting the interrupt state (latched, gated, whatever), the defines may also have a more complex encoding, not necessarily a bit vector. Here there is potential overlap, from a coding perspective I'd use the IT variants when dealing with interrupts, and FLAG for polling status.