cancel
Showing results for 
Search instead for 
Did you mean: 

TIMer triggers ADC which outputs via DMA just fires its DMA-ISR once

Gahlen Feld
Associate II
Posted on July 14, 2017 at 15:41

Hello,

in my STM32F765 project I am using a timer (TIM6) which is set to a constant frequency. The update event of the timer triggers an ADC (ADC2&3) which is configured to start conversion of a regular group of channels on the TRGO of the timer.

DMA2Stream2_IrqHandler clears the TC&HT flags of both DMA2-Streams (2&0) but is called only once and then never again.

I have done any number of combinations for different ADC/DMA settings but it behaves always the same. I suspect the DMA controller but I did not got it working right. Can somebody give me a hint, please?

Thank you in  advance,

Gahlen

Here is my code for configuring the hardware:

void Timer6::Init(void) {

    hardware.rcc.EnableClock(APB1, RCC_APB1ENR_TIM6EN);

        hardware.timer.SetPrescaler   (TIM6, prescaler-1);

        hardware.timer.SetPeriod      (TIM6, period-1);

        hardware.timer.SetMasterMode  (TIM6, TIMER_MASTER_MODE_UPDATE); // TRGO on update event

}

void Timer6::Start(void) { // initiates the Adc conversion

    hardware.timer.Enable(TIM6);

}

void Adc::GeneralConfig(void) {

    EnableDigitalClock();

    SetAnalogClock(ADC_APB2_DIV4); // => 27MHz (1.8MS/s max.)

    SetResolution(ADC_RESOLUTION_12BIT);

    if ((adc_number == 2) || (adc_number == 3)) {

        SetExternalRegularTrigger(ADC_TRIGGER_RISING);

        SetExternalRegularTriggerSource(ADC_EXTERNAL_TRIGGER_REGULAR_TIM6_TRGO); // ADC1 is triggered manually in systemick ISR

    }

    SetAlignmentRight();

    ClearContinuousMode(); // look at application note AN3116 for selection of ADC modes

    ClearDiscontinuousMode();

    SetScanMode();         // in this mode the ADC converts all channels of the group one time until it is retriggered by timer6 for the next group of conversion

    //ClearScanMode();

    SetEocForSequence();

}

void Adc::GpioConfig(void) {

    GPIO_TypeDef* gpio;

    for (u16 i=0; i<number_of_channels; i++) {

        gpio = config->channel_sequence[i].gpio;

        if (gpio != (GPIO_TypeDef*)0) hardware.gpio.InitPin(gpio, (GpioPinNumberType)Position(config->channel_sequence[i].pin), GPIO_MODE_ANALOG);

    }

}

void Adc::DmaConfig(void) {

    DMA_TypeDef*   dma    = config->dma.hardware;

    DmaStreamsType stream = config->dma.stream;

    SetContinuousDMA(); // ADCn DMA is running continuously on every sampled value

    //SetSingleGroupDMA();

    hardware.rcc.EnableClock (AHB1, RCC_AHB1ENR_DMA2EN);

    hardware.dma.SetChannel  (dma, stream, config->dma.channel);

    hardware.dma.SetPriority (dma, stream, config->dma.priority);

    hardware.dma.SetMode     (dma, stream, config->dma.mode);

    hardware.dma.SetDirection(dma, stream, DMA_PERIPH2MEMORY);

    hardware.dma.SetIncrement(dma, stream, DMA_PERIPHERAL_INCREMENT_NONE, DMA_MEMORY_INCREMENT);

    hardware.dma.SetDataSize (dma, stream, DMA_PERIPHERAL_SIZE_16BIT, DMA_MEMORY_SIZE_16BIT);

    hardware.dma.EnableIrq   (dma, stream, DMA_IRQ_TRANSFER_COMPLETE);

    hardware.dma.SetLength   (dma, stream, number_of_channels);

    hardware.dma.SetAddresses(dma, stream, (u32)&adc->DR, (u32)dma_memory);

    hardware.dma.EnableStream(dma, stream);

    if (config->irq.priority != IRQ_PRIORITY_NONE) hardware.cortex_m.nvic.InitIrq(config->irq.line, config->irq.priority, ENABLE);

}

void Adc::ChannelsConfig(void) {

    u16 i;

    for (i=0; i<number_of_channels; i++) {

        AdcChannelType channel = config->channel_sequence[i].adc_input_channel;

        SetSampleTime(channel, config->channel_sequence[i].sampletime);

        SetRegularSequenceChannel((AdcSequenceType)i, channel);

        if ((adc_number == 1) && ((channel == ADC_CHANNEL17) || (channel == ADC_CHANNEL18))) EnableTemperatureAndBandgapMeasurement();

    }

    SetRegularSequenceLength(i);

}

void Adc::Start(void) {

    switch (adc_number) {

        case 2  :

        case 3  : hardware.timer6.Start();

            break;

        default :                          break;

    }

}

void Adc::Init(ADC_TypeDef* ADCx) {

...

    DisableDMA();

    Disable();

    GpioConfig();

    DmaConfig();

    GeneralConfig();

    ChannelsConfig();

    Enable();

    EnableDMA();

    

    Start();

}

void DMA2_Stream2_IRQHandler(void) {

    if (hardware.dma.GetFlagStatus(DMA2, DMA_STREAM_2, DMA_FLAG_TRANSFER_COMPLETE)) {

        ClearDma2Stream2TcFlag(); // this is mandatory

        ClearDma2Stream0TcFlag();

        ...

    }

}

#tim #dma #adc #stm32f765
1 ACCEPTED SOLUTION

Accepted Solutions
Posted on July 14, 2017 at 16:36

Check the DMA registers in debugger, confirm it is in circular mode, and no errors are pending. In normal mode you'd have to reconfigure at each TC. Consider a buffer with some actual depth, so the interrupts are incessant, and you double the buffer so HT/TC can be used to look at the inactive half.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

View solution in original post

2 REPLIES 2
Posted on July 14, 2017 at 16:36

Check the DMA registers in debugger, confirm it is in circular mode, and no errors are pending. In normal mode you'd have to reconfigure at each TC. Consider a buffer with some actual depth, so the interrupts are incessant, and you double the buffer so HT/TC can be used to look at the inactive half.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Gahlen Feld
Associate II
Posted on July 17, 2017 at 09:35

Hello Clive One,

thank you for your advise!

I checked the CIRC bit (it was set) & looked in the RM for that bit.

I also read the description of other bits in that register. Some of them are marked with 'These bits are protected and can be written only if EN is ‘0’.'.

Subsequently I know where my error is: I only had to move the 'DmaConfig();' two lines down and now everything works fine 🙂

You gave me the right hint for my problem eventually. Thank you!