cancel
Showing results for 
Search instead for 
Did you mean: 

Trigger DMA From CCR Timer

jasonshaw
Associate II
Posted on March 09, 2014 at 20:46

Greetings all, I'm new here so I hope I'm doing this right.

STM32F0

I'm trying to trigger a DMA transfer from TIM2 with CCR1

Here's my code for the timer portion

TIM_TimeBaseInitTypeDef timerInitStructure;

timerInitStructure.TIM_Prescaler = 0;

timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;

timerInitStructure.TIM_Period = 60;

timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

timerInitStructure.TIM_RepetitionCounter = 0;

TIM_TimeBaseInit(TIM2, &timerInitStructure);

TIM_OCInitTypeDef outputChannelInit;

outputChannelInit.TIM_OCMode = TIM_OCMode_Timing;

outputChannelInit.TIM_OutputState = TIM_OutputState_Enable;

outputChannelInit.TIM_OCPolarity = TIM_OCPolarity_High;

outputChannelInit.TIM_Pulse = 41;

TIM_OC1Init(TIM2, &outputChannelInit);

TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);

TIM2->DIER |= (0x01 << 14) | (0x01 << 9);

TIM_Cmd(TIM2, ENABLE);

So what I was looking for was being about to trigger a DMA transfer when the count register reaches 41 on DMA1_Channel5.

I can verify that a transfer is happening but it looks like it's happening on Update.

Thanks!

-Jason

10 REPLIES 10
Posted on March 09, 2014 at 21:33

Looks to be the right DMA channel for TIM2_CH1, not sure if any additional mapping/config is required, not an F0 user.

The DMA is going to occur at the same periodicity as the timer, the timer isn't going slowly so it might be hard to perceive the difference between CC2 and Update, other than the race condition that altering CCR2 immediately might present.

What are you DMA'ing too? Can you present a complete/consise example?
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jasonshaw
Associate II
Posted on March 10, 2014 at 01:13

Hello Clive,
 First off I'd like to say thanks for taking the time to look at this I've been lurking around the boards for a little while and have learned a lot from your posts.
 So here's my line of thinking here. Wanted to jot this down in case there is a break down with my logic.
 48mhz
 48000000 / 60 = 800khz
 Which gives me 1.25us per period.
 OC1 is set to 30
 So I'm expecting to see a period of 1.25/2 ~ .625us on my scope but.. I'm seeing ~1.25us which would be consistent with the ODR register being written at Update.
 https://dl.dropboxusercontent.com/u/19198779/DSC02jpg
 Changing OC1 to any value has no effect on the output which is a bummer.
 But if I change the Period of the timer 30 I get the expected change (~.625us) .
 I want to be able to use two of the CCR to trigger dma interrupts otherwise I would just change the timer period and be done with it.

#include ''stm32f0xx.h''
#include ''stm32f0xx_dma.h''
#include <stdio.h>
/* ----- LED definitions --------------------------------------------------- */
#define DATA_OUT_PORT GPIOB
#define ARRAYSIZE 20
// ----------------------------------------------------------------------------
void
StartPulserTriggers();
void
InitOutputPins();
void
InitDMA();
uint16_t source[ARRAYSIZE];
int
main(
void
){
int
i;
for
(i=0; i<ARRAYSIZE;i++){
source[i] = i % 2 ? 1 : 0;
}
InitDMA();
InitOutputPins();
StartPulserTriggers();
while
(1){
}
}
#define DMA_CHANNEL DMA1_Channel5
void
InitDMA(){
DMA_Cmd(DMA_CHANNEL, DISABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA_CHANNEL);
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&GPIOB->ODR;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)source;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = ARRAYSIZE;
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(DMA_CHANNEL, &DMA_InitStructure);
/* Enable DMA1 Channel2 */
DMA_Cmd(DMA_CHANNEL, ENABLE);
}
void
InitOutputPins(){
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(DATA_OUT_PORT, &GPIO_InitStructure);
}
void
StartPulserTriggers(){
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef timerInitStructure;
timerInitStructure.TIM_Prescaler = 0;
timerInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
timerInitStructure.TIM_Period = 30;
timerInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
timerInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &timerInitStructure);
TIM_OCInitTypeDef outputChannelInit;
outputChannelInit.TIM_OCMode = TIM_OCMode_Timing;
outputChannelInit.TIM_OutputState = TIM_OutputState_Enable;
outputChannelInit.TIM_OCPolarity = TIM_OCPolarity_High;
outputChannelInit.TIM_Pulse = 30; 
// high pulse
TIM_OC1Init(TIM2, &outputChannelInit);
TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM2->DIER |= (0x01 << 14) | (0x01 << 9);
TIM_Cmd(TIM2, ENABLE);
TIM2->CCR1 = 14;
}

jasonshaw
Associate II
Posted on March 10, 2014 at 01:27

Added some wrong info here at first.

Posted on March 10, 2014 at 03:16

I'm not familiar with OC TIMING mode. In the PWM modes the Pulse relates to the DUTY of a cycle on a specific channel. The Period controls the frequency for all channels, and the TIM only has ONE counting element (CNT), the compare registers are latches (CCRx), with comparators.

If you wanted multiple pulses with the same frequency, but shifted phase, then TOGGLE mode works well. ie 3 Phases, shifted 120 degrees

One can generate multiple frequencies from a single timer by setting the period to maximal, and then have the CCRx advance at every event, and toggle the pin. As this will also generate a lot of interrupts, it's best used for slower frequencies (say below 10 KHz)
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on March 10, 2014 at 03:21

Timing Mode = Frozen, per RM0091

000: Frozen - The comparison between the output compare register TIMx_CCR1 and the

 

counter TIMx_CNT has no effect on the outputs (this mode is used to generate a timing

 

base).
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jasonshaw
Associate II
Posted on March 10, 2014 at 05:37

From what I've read in the datasheet that just means it doesn't change the pin state. I'm interested in a timing base so I believe this is what I want to do.

From rm0091 as well I'm going off of this 17.3.8:

When a match is found between the capture/compare register and the counter, the output compare function:

- Assigns the corresponding output pin to a programmable value defined by the output compare mode (OCxM bits in the TIMx_CCMRx register) and the output polarity (CCxP bit in the TIMx_CCER register). The output pin can keep its level (OCXM=000), be set active (OCxM=001), be set inactive (OCxM=010) or can toggle (OCxM=011) on match.

- Sets a flag in the interrupt status register (CCxIF bit in the TIMx_SR register).

- Generates an interrupt if the corresponding interrupt mask is set (CCXIE bit in the TIMx_DIER register).

- Sends a DMA request if the corresponding enable bit is set (CCxDE bit in the TIMx_DIER register, CCDS bit in the TIMx_CR2 register for the DMA request selection).

I'm most interested in the last.

I verified DIER has bit 14 set to trigger DMA request and that bit 9 is set to trigger DMA request on CC1 match.

also that TIM2->CR2 CCDS bit has been cleared to enable DMA request on CCx event occurs.

Seems like no matter what I do though, I only get a DMA request on update.

I'm probably missing something here, I'm just not seeing it.
Posted on March 10, 2014 at 06:59

Ok, so are you observing the phase relationship between the data you're posting to GPIOB wrt the TIM2_CH1, or a proxy for it (say TIM2_CH2 in PWM mode to a pin), or simply that the periodicity of the output on GPIOB is that of the underlying timer?

No, bit 14 is the TRGO selection (TIM_SelectOutputTrigger - CR2), you should just need to set bit 9 (TIM_DMA_CC1)

or TIM_DMACmd(TIM2, TIM_DMA_CC1, ENABLE);

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jasonshaw
Associate II
Posted on March 10, 2014 at 19:32

Ahh you are correct on bit 14 read that one wrong.

Regardless, What's I'm expecting to see on my my output port is a waveform that changes state during the CC1 event. So in theory what I would like to see is the DMA transfer triggered when CC1 happens. In reality what I'm seeing is the change happen at the full 60 count not the 14.

Posted on March 10, 2014 at 22:26

Ok, but HOW are you making this determination? CCR1 will not alter the frequency, it would allow you to modulate phase.

You could generate frequency but advancing the CCR1 value at each interrupt, and setting to a maximal period for the counter.

ie TIMx->CCR1 = Next; Next += 1000;
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..