cancel
Showing results for 
Search instead for 
Did you mean: 

Want to trigger DMA from Timer

JustMe
Associate II
Posted on October 14, 2015 at 18:22

I am using the ST32f

I would like TIM4 to trigger the DMA which should move data from memory to SPI2. Tim4 count is changing but it does not trigger the DMA. Can somebody please point out where I am going wrong - this is my first stm32 program

TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);// Time Base configuration
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 0xffff;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
//setup the output compare
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStructure.TIM_Pulse = X_OFFSET;
TIM_OC1Init(TIM4, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Disable);//??
TIM_ARRPreloadConfig(TIM4, DISABLE); //?? 
/* Slave Mode selection: TIM4 */
TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Combined_ResetTrigger);
TIM_SelectInputTrigger(TIM4, TIM_TS_ITR2);
TIM_DMACmd(TIM4,TIM_DMA_CC1, ENABLE ); // Enable TIM1_CC1 DMA Requests

DMA_DeInit(DMA1_Channel1);//reset DMA1 channe1 to default values;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//channel will be used for memory to SPI transfer
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//setting normal mode (non circular)
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//medium priority
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//source and destination data size word=32bit
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//automatic memory increment enable.
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//Location assigned to peripheral register will be destination
DMA_InitStructure.DMA_BufferSize = COUNT_MAX;//chunk of data to be transfered 
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;//source and destination start addresses
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Buffer;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);//send values to DMA registers
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);// Enable DMA1 Channel Transfer Complete interrupt

#stm32f303 #tim-spi-dma
5 REPLIES 5
Posted on October 14, 2015 at 22:40

> Tim4 count is changing

And does TIMx_SR.CC1IF get set? Btw., you can force the CC1 event even without having the timer actually running, by writing into TIMx_EGR register. > but it does not trigger the DMA. How do you know that? Post the relevant DMA registers as read in debugger. JW


Posted on October 14, 2015 at 22:59

There seems to be a lot of unnecessary configuration that isn't explained, perhaps instead of doing the selective cut-n-paste, you can provide a complete example that actually compiles and a description of what it should do, and at what rate, if it were to work as expected.

At the moment you seem to be creating a dependency on TIM3 that's not explained, the prescaler value isn't provided and the timer is set in a maximal count mode.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
JustMe
Associate II
Posted on October 15, 2015 at 00:00

clive1 you are correct regarding TIM3.

Overview: I have two external inputs. The one is the clock for TIM3 which I am using as a counter. The other input resets TIM3. Compare reg1 on TIM 3 triggers an interrupt, the interrupt enables the slave mode of TIM4 which is being clocked by the system clock. When the count reaches the TIM4 compare 1 value I want it to trigger the DMA. In the debugger I can see that TIM4 is counting up. The DMA registers all stay unchanged and the interrupt at the end of the DMA output is not triggered. Sorry about the pastes but program is made up of multiple modules, but here are most of the settings Prescaler value: PrescalerValue = (uint16_t) (SystemCoreClock / 8000000) - 1; The system clock is 72Mhz The compare values are: TIM4

X_OFFSET

== 20 TIM3 START1 == 24 TIM3 END1 == 312 DMA output in debugger 0690X000006031qQAA.jpg

uint16_t PrescalerValue = 0;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);//GPIOB clock enable
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);//GPIOC clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //TIM3 clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //TIM4 clock enable 
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1 clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//SPI2 clock enable
//GPIO Setup - input
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 ; // we want to configure AF pins on port B
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // we want the pins to be alternate functions
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // this sets the GPIO modules clock speed
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; // this sets the pin type to push / pull (as opposed to open drain)
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; // this sets the pullup / pulldown resistors
GPIO_Init(GPIOB, &GPIO_InitStruct);
//GPIO Setup - input
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6; // we want to configure AF pins on port C
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // we want the pins to be alternate functions
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // this sets the GPIO modules clock speed
GPIO_InitStruct.GPIO_OType = GPIO_OType_OD; // this sets the pin type to opposed to open drain
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; // this sets the pullup / pulldown resistors
GPIO_Init(GPIOC, &GPIO_InitStruct);
//GPIO Setup - output
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15 ; // we want to configure AF pins on port B
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // we want the pins to be alternate functions
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // this sets the GPIO modules clock speed
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // this sets the pin type to push / pull (as opposed to open drain)
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // this sets the pullup / pulldown resistors
GPIO_Init(GPIOB, &GPIO_InitStruct);
//-----------------------------------------------------------------
/* Connect inputs to alternate function 2 - AF2 */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_2);//ch2 on Tim3 
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_2);//ch1 on Tim3
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_5); //MOSI on SPI2

// Enable the TIM3 gloabal Interrupt
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//Enable DMA1 channel IRQ Channel 
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure); 
void DMA1_Channel1_IRQHandler(void)
{
DMA_ClearITPendingBit( DMA1_IT_TC1);
DMA_InitStructure.DMA_MemoryBaseAddr += COL_COUNT_MAX;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);//send values to DMA registers
LineCnt++;
}

void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)//int caused by compare1
{
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
if(MustStart)
{
TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Combined_ResetTrigger);//enable TIM4 trigger mode
MustStart = 0;
TIM_OCInitStructure.TIM_Pulse = END1;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
LineCnt = 0;
GrafRow = 0;
}
else
{
TIM_SelectSlaveMode(TIM4, 0);//disable TIM4 trigger mode
MustStart = 1;
TIM_OCInitStructure.TIM_Pulse = START1;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
}
}
/*if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)//int caused by TIM3 update
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}*/
}

Here is the TIM3 setup:

TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);// Time Base configuration
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = 0xffff;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
//setup the output compare
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStructure.TIM_Pulse = START1;
TIM_OC1Init(TIM3, &TIM_OCInitStructure); 
TIM_TIxExternalClockConfig(TIM3, TIM_TIxExternalCLK1Source_TI2, TIM_ICPolarity_Rising, 0);//set xternal clk source to TI2 which comes from AF2 port B.5
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Combined_ResetTrigger);//this should cause a reset and update
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);//select TI1 as reset input - comes from ch1
TIM_ITConfig(TIM3, /*TIM_IT_Update | */TIM_IT_CC1, ENABLE);//this should trigger interrupt when TIM3 {gets reset or} when compare1 matches
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);//??
TIM_ARRPreloadConfig(TIM3, DISABLE); //??
//Select the Master Slave Mode
TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable);
TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);
TIM_Cmd(TIM3, ENABLE);

Posted on October 17, 2015 at 00:22

Honestly, while I asked you to post the DMA registers content, I expected this will trigger your curiosity and you will check this yourself:

> CCR1 == 0x1592

the .EN bit is not set, thus the DMA channel is not enabled.

I don't use any ''library'', but there must be some DMA_Enable() or similar function to enable the channel.

JW

JustMe
Associate II
Posted on October 17, 2015 at 15:53

Thanks to those who gave advice.

After a lot of trial and error I created a simple test program and got it working. I have posted it below for anyone else who wants to see how to do it. Note that I am only sending out of the SPI MOSI. I am not using the SPI clk in this example, so if you need to use SPI with clock then you might need to change the SPI settings. Please note that this is my first attempt at programming an ARM so there may be some major bugs here.

//AIM: Send data from a buffer to SPI 1 row at a time using DMA
//TIM4 Compare 1 is used to trigger the DMA which then sends data from a buffer to the SPI port. 
//Upon completion the DMA triggers an interrupt and sets up for the next row then waits for next trigger from the timer
#include ''stm32f30x_dma.h''
#include ''stm32f30x_gpio.h''
#include ''stm32f30x_rcc.h''
#include ''stm32f30x_spi.h''
#include ''stm32f30x_tim.h''
#include ''stm32f30x_misc.h''
#include ''stm32f30x_syscfg.h''
#define X_OFFSET 20
#define ROW 200
#define COL 32
uint8_t MemBuf[ROW][COL];
uint8_t RowCount = 0;
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
//-----------------------------------------------------------------
void Init(void)
{
uint16_t PrescalerValue = 0;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);//GPIOB clock enable
// RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);//GPIOC clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //TIM4 clock enable
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1 clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//SPI2 clock enable
//------------------
//GPIO Setup - output
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15 ; // we want to configure AF pins on port B
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // we want the pins to be alternate functions
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // this sets the GPIO modules clock speed
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // this sets the pin type to push / pull (as opposed to open drain)
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // this sets the pullup / pulldown resistors
GPIO_Init(GPIOB, &GPIO_InitStruct);
//------------------
//TIM4 setup
PrescalerValue = (uint16_t) (SystemCoreClock / 8000000) - 1;//Compute the prescaler value
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);// Time Base configuration
TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = X_OFFSET + 80;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
//setup the output compare
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;
TIM_OCInitStructure.TIM_Pulse = X_OFFSET;
TIM_OC1Init(TIM4, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);//??
TIM_ARRPreloadConfig(TIM4, ENABLE); //??
TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable);
TIM_SelectOutputTrigger(TIM4, TIM_TRGOSource_OC1Ref);
TIM_DMACmd(TIM4,TIM_DMA_CC1, ENABLE ); // Enable TIM1_CC1 DMA Requests
//------------------
//Interrupt setup
//Enable DMA1 channel IRQ Channel
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//------------------
//DMA1 setup
//Transfer Data to SPI2 from RAM buffer
//Trigger from TIM4 OC1
DMA_Cmd(DMA1_Channel1, DISABLE);
DMA_DeInit(DMA1_Channel1);//reset DMA1 channe1 to default values;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//channel will be used for memory to SPI transfer
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//setting normal mode (non circular)
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//medium priority
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//source and destination data size half word=16bit
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//automatic memory increment enable.
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//Location assigned to peripheral register will be destination
DMA_InitStructure.DMA_BufferSize = COL / 2;//amount of data to be transfered - divide by 2 because we using 16bit
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;//source and destination start addresses
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)MemBuf;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);//send values to DMA registers
DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);// Enable DMA1 Channel Transfer Complete interrupt
//------------------
//SPI setup
SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &SPI_InitStructure);
//------------------
//Connect output to alternate function - AF2
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_5); //MOSI on SPI2
//------------------
SPI_Cmd(SPI2, ENABLE);
TIM_Cmd(TIM4, ENABLE);
DMA_Cmd(DMA1_Channel1, ENABLE);
}
//-----------------------------------------------------------------
void DMA1_Channel1_IRQHandler(void)
{
if(DMA_GetITStatus(DMA1_IT_TC1))
{
DMA_ClearITPendingBit(DMA1_IT_TC1 | DMA1_IT_GL1);
DMA_Cmd(DMA1_Channel1, DISABLE);
RowCount++;
if(RowCount >= ROW)
{
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)MemBuf;//end of buufer reached - start at beginning
RowCount = 0;
}
else
{
DMA_InitStructure.DMA_MemoryBaseAddr += COL;//move to next row in buffer
}
DMA_Init(DMA1_Channel1, &DMA_InitStructure);//send values to DMA registers
DMA_Cmd(DMA1_Channel1, ENABLE);
}
else
{
DMA_ClearITPendingBit(DMA1_IT_GL1);
}
}
//-----------------------------------------------------------------
int main(void)
{
Init();
for (uint8_t var = 0; var < 20; ++var)
{
MemBuf[50][var] = var;//put some stuff in one row of the buffer
}
while(1)
{
//put your code here
}
}