2012-10-26 12:46 AM
Is it possible to have more than one ADC interrupt source? Because I am trying to program ADC3 so that the interrupt can be caused both from an end of conversion (EOC) event and from the analogue watchdog (AWD) event. Is this possible?
2012-10-26 06:40 AM
Of course it's possible.
But apart from distinguishing the cause at the start of the interrupt handler, do not forget to clear the pending-flag. While the EOC flag is cleared implicitely by reading the ADC->DR register, the others are not. Semantically, having both the EOC and the AWD interrupt makes not too much sense, you could check the analog limit also when handling an EOC interrupt. However, dropping EOC for the AWD interrupt makes more sense IMHO. It would significantly reduce interrupt load.2012-11-02 02:02 AM
2012-11-02 02:22 AM
Thanks for your help. I have managed to achieve this. But yes, you're right, I can do without the AWD by configuring two pins to indicate when I reach my upper and the lower thresholds in the EOC event handler.
I would have skipped the EOC instead, because the AWD interrupt would do the additional work of comparing the ADC input to your threshold ... Currently, I'm having another problem. I am generating other pulses in the EOC so as to ... When you configure the ADC in continuous mode, the sampling frequency (for one channel) depends only on the ADC clock speed, the configured sampling time (ADC_SampleTime_xxxCycles), and the number of channel scanned. This is obviously not what you want. First step is to turn continuous mode off. For low sampling frequencies, you can trigger each sample individually by SW. For higher rates, a timer is the best solution.
/* TIM3 Configuration */
TIM_DeInit (TIM3);
TIM_TimeBaseStructInit (&TIM_TimeBaseStructure);
TIM_OCStructInit (&TIM_OCInitStructure);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = 0x4E1;
// try to get 125 us
TIM_TimeBaseStructure.TIM_Prescaler = 0x2;
// update rate of 625us (2.560 kHz)
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
// at a 16MHz timer clock
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
/* TIM3 TRGO selection */
TIM_SelectOutputTrigger (TIM3, TIM_TRGOSource_Update);
/* -------- ADC1 Configuration -------- */
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
/* Don't do continuous conversions - do them on demand */
/* trigger on Timer 3 instead */
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
/* 12 bit, right aligned */
ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward;
ADC_Init (ADC1, &ADC_InitStructure);
/* ADC Ch10 ... CH13 configuration */
ADC_ChannelConfig (ADC1, ADC_Channel_10, ADC_SampleTime_28_5Cycles);
ADC_ChannelConfig (ADC1, ADC_Channel_11, ADC_SampleTime_28_5Cycles);
ADC_ChannelConfig (ADC1, ADC_Channel_12, ADC_SampleTime_28_5Cycles);
ADC_ChannelConfig (ADC1, ADC_Channel_13, ADC_SampleTime_28_5Cycles);
/* configure one transfer after each conversion sequence, and enable */
#ifdef DMA_INT_ON_EOT
ADC_DMARequestModeConfig (ADC1, ADC_DMAMode_Circular);
ADC_DMACmd (ADC1, ENABLE);
#endif
/* Enable DMA1 Channel Transfer Complete interrupt */
#ifdef DMA_INT_ON_EOT
DMA_ITConfig (DMA1_Channel1, DMA_IT_TC, ENABLE);
#else
ADC_ITConfig (ADC1, ADC_IT_EOC, ENABLE);
/* ADC Interrupt enable */
// ADC_ITConfig (ADC1, ADC_IT_EOC | ADC_IT_EOSEQ, ENABLE); /* ADC Interrupt enable, alternatively... */
#endif
/* no explicit calibration required for F0 ADC, load calibration constants */
ADC_GetCalibrationFactor(ADC1);
ADC_Cmd (ADC1, ENABLE);
/* Enable ADC1 */
while
(!ADC_GetFlagStatus (ADC1, ADC_FLAG_ADEN));
/* Wait the ADCEN flag */
TIM_Cmd (TIM3, ENABLE);
/* TIM3 enable counter */
/* Software Start Conv - enables the selected hardware trigger */
ADC_StartOfConversion(ADC1);
This is example code for the STM32F0, which uses DMA additionally.
It contains the configuration to trigger the ADC from a timer, the rest is skipped. For available triggers, you might need to consult the reference manual.
2012-11-04 11:55 PM
Good morning!
Thanks a lot. I managed to get TIM1 triggering ADC3. As regards EOC and AWD, I need them both, because in the EOC I'm calculating a voltage level based on the ADC converted value and in the AWD I'm checking that the thresholds are not exceeded (although this can be done in the EOC event handler as already said). But for sure, in my case I need EOC event handler.Lately I realised that I have another problem, that was present before doing the last modification. The program is getting into the ADC IRQ handler, but its not getting in the EOC block. That is, when I'm checking if there was an EOC by using if (ADC_GetITStatus(ADC3, ADC_IT_EOC) != RESET) , it seems that the EOC status bit is never being set. In fact, the pulse I'm generating at the start of the ADC IRQ handler can be visualised on the oscilloscope, while the pulse I'm generating in the EOC block is not being displayed. By this I came to the conclusion that there is something wrong with the EOC status bit.Regards,Terence2012-11-05 01:27 AM
If you are in the ADC interrupt routine, there should be some flag set.
Can't you run it in the debugger, and check what flag is set upon entry ? On a F0 example, I used the following code to configure for an EOC interrupt:/* NVIC ADC interrupt setup */
NVIC_InitStructure.NVIC_IRQChannel = ADC1_COMP_IRQn;
// ADC1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init (&NVIC_InitStructure);
...
ADC_ITConfig (ADC1, ADC_IT_EOC, ENABLE);
/* ADC Interrupt enable, */
2012-11-05 11:00 PM
Good morning!
Actually I should have everything set right for the ADC. I also enabled the ADC interrupt in the NVIC (like you showed in your last reply). My ADC is using the DMA controller, and my supervisor told me that this might be the source of problem. He advised me to use a DMA interrupt instead of ADC interrupt. In fact, now I have created a DMA IRQ handler, and programmed it to occur at every Transfer complete. However, the DMA interrupt is not occuring at the ADC sampling and conversion frequency, but at another frequency.2012-11-05 11:43 PM
However, the DMA interrupt is not occuring at the ADC sampling and conversion frequency, but at another frequency.
Of course. It depends on how you configured your DMA. Cyclic mode makes most sense with the ADC. Usually, one configures an DMA TC interrupt (Transmission Complete), which is triggered after the configured number of items is transferred. And this number of items uses to match the number of channels. There is a section in the reference manual, dealing with ADC usage in DMA mode, which I recommend to digest
2012-11-07 01:31 AM
Good morning!
I've finally managed to get DMA interrupt working correctly. What is still unclear is how come without the following, T1_CC1 is still being used as the source of triggering for my ADC:TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update); //this one was used by you in the code you gave me last weekTIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_OC1Ref); //this one was the one I tried to useRegards,Terence2012-11-08 12:01 AM
I'm not sure what the problem is.
In reference to the source code posted, I used an example which I adapted to my requirements. I think I changed the timer unit and the counter/prescaler values for the timer. It is several weeks ago, but I believe it was from the F0 firmware examples...