2017-03-24 11:00 AM
STM32F437
I am using TIM4 channel 1 and would like to have the timer output handled by DMA instead of my timer ISR but my DMA attempt is not working. So I am fishing for timer/dma example code hopefully that uses DMA interrupts as well.
Thanks,
JH
2017-03-25 09:11 AM
Have you browsed through cube nucleo/discovery/HAL/LL examples related to Timer+DMA?
2017-03-25 11:03 AM
Thanks for posting. I have not looked there. If there are non-HAL examples I would be interested. Could you provide a URL?
Thanks,
jh
2017-03-25 11:50 AM
I guess it's related to download STM32 Cube package.
Otherwise, did a fun project where the datasheet of the STM32F437 was C coded... consider this as pseudo code to get clues on the DMA vs Timer. In this code, it's mostly for Output compare extract.
//====================== Capture Compare Initialization functions ================================ 8><8 =======u32 EnableTimerCC_Interrupt(u32 u, u32 n, FunctionalState Enable) { // n = [1..4]
Timer_t* Timer = (Timer_t*) u; if(n==0) while(1); // not allowed if(n>=TIMER_MAX_CC) while(1); // not available CCn if(Timer->CCR[n]==0) while(1); // not available// Clear any pending flags first
Timer->TIM->SR &= TimerCCFlags[n]; if(Enable) Timer->TIM->DIER |= TimerCCFlags[n]; else Timer->TIM->DIER &= ~TimerCCFlags[n]; return 0; }u32 SetTimerInputCC(u32 u, u32 n, IO_Pin_t* Pin, FunctionalState Enable) {
Timer_t* Timer = (Timer_t*) u; if(n==0) while(1); // not allowed if(n>=TIMER_MAX_CC) while(1); // not available CCn if(Timer->CCR[n]==0) while(1); // not available // Enable is not used yet.... TIM_ICInitTypeDef IC; TIM_ICStructInit(&IC); // For now... should be passed as parameter later on. Rising edge, no prescaler, no filtering IC.TIM_Channel = TimerCC_Channels[n]; IC.TIM_ICPolarity = TIM_ICPolarity_Rising; IC.TIM_ICSelection = TIM_ICSelection_DirectTI; IC.TIM_ICPrescaler = TIM_ICPSC_DIV1; IC.TIM_ICFilter = 0; TIM_ICInit(Timer->TIM, &IC);if(Pin) {
Timer->PinCC[n] = Pin; IO_PinClockEnable(Pin); IO_PinSetHigh(Pin); IO_PinSetInput(Pin); IO_PinEnablePullUpDown(Pin, ENABLE, DISABLE); IO_PinEnableHighDrive(Pin, ENABLE); IO_PinConfiguredAs(Pin, GetPinAF(Pin->Name,(u32)Timer->TIM)); }; // EnableTimerCC_Interrupt(u, n, Enable); return 0;}u32 HookTimerCC(u32 u, u32 n, u32 fn, u32 ct) {Timer_t* Timer = (Timer_t*) u;
if(n==0) while(1); // not allowed
if(n>=TIMER_MAX_CC) while(1); // not available CCn if(Timer->CCR[n]==0) while(1); // not available Timer->ctCC[n] = ct; Timer->fnCC[n] = fn; return 0;}void TIM_DMA_Activate(Timer_t* Timer) {
u32 n;
for(n=1;n<4;n++) { if(Timer->EdgesSize[n]<=1) continue; // This CC is hooked up to a DMA, let's enable them DMA_StreamChannelInfo_t* DSCI = Get_TimerDMA_InfoByPPP_n((u32)Timer->TIM, n, DMA_DIR_PeripheralToMemory | DMA_DIR_MemoryToPeripheral); DMA_Cmd(DSCI->Stream/*DMA1_Stream4*/, ENABLE); // DMA enable BookDMA_Stream(DSCI->Stream); TIM_DMACmd(Timer->TIM, TIM_DMA_n[n], ENABLE); // TIM2 Update DMA Request enable }; }// In this scheme, the timer is reset, wait for Ch2 rising edge, then starts one pulse mode
// For example, the output compares can generate pulses or edge which can trigger time shifted ADC, DAC, Analog Switch behaviours// Using external triggers is easier to probe, hence easier debug, faster move forward jobvoid EnableOneShotTimerCC_Triggered(Timer_t* Timer, u32 n) {TIM_DMA_Activate(Timer); // Will sweep through all DMA possible interaction
TIM_SelectOnePulseMode(Timer->TIM, TIM_OPMode_Single); // One Pulse Mode selectionif(n==1)
TIM_SelectInputTrigger(Timer->TIM, TIM_TS_TI1FP1); // Input Timer Trigged on CH1 else if(n==2) TIM_SelectInputTrigger(Timer->TIM, TIM_TS_TI2FP2); // Input Timer Trigged on CH2 else while(1);// Slave Mode selection: Trigger Mode (the trigger will set the Timer enable bit)
TIM_SelectSlaveMode(Timer->TIM, TIM_SlaveMode_Trigger); }static void TimerOutputCC_SetDMA(Timer_t* Timer, u32 n) {
DMA_InitTypeDef DMAI; u32 DataSize; if(Timer->Max > 0x0000FFFF) DataSize = DMA_PeripheralDataSize_Word; else DataSize = DMA_PeripheralDataSize_HalfWord; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE);DMA_StreamChannelInfo_t* DSCI = Get_TimerDMA_InfoByPPP_n((u32)Timer->TIM, n, DMA_DIR_PeripheralToMemory | DMA_DIR_MemoryToPeripheral);
DMA_DeInit(DSCI->Stream);//(DMA1_Stream4); DMAI.DMA_Channel = DSCI->Channel;//DMA_Channel_5; DMAI.DMA_PeripheralBaseAddr = (u32)Timer->CCR[n]; DMAI.DMA_Memory0BaseAddr = (u32)Timer->EdgesTableAdr[n]; DMAI.DMA_DIR = DMA_DIR_MemoryToPeripheral; DMAI.DMA_BufferSize = Timer->EdgesSize[n]; DMAI.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMAI.DMA_MemoryInc = DMA_MemoryInc_Enable; DMAI.DMA_PeripheralDataSize = DataSize; DMAI.DMA_MemoryDataSize = DataSize; DMAI.DMA_Mode = DMA_Mode_Circular; DMAI.DMA_Priority = DMA_Priority_High; DMAI.DMA_FIFOMode = DMA_FIFOMode_Disable; DMAI.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMAI.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMAI.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DSCI->Stream/*DMA1_Stream4*/, &DMAI);
}void SetTimerOutputCC_SingleEdge(Timer_t* Timer, u32 n, u32 Value_lsb) {
Timer->EdgesSize[n] = 0; // Non zero triggers the DMA mode for multiple pulses Timer->EdgesTableAdr[n] = 0;TIM_OCInitTypeDef OC;
TIM_OCStructInit(&OC); OC.TIM_OCMode = TIM_OCMode_PWM2; OC.TIM_OutputState = TIM_OutputState_Enable; OC.TIM_Pulse = Value_lsb; OC.TIM_OCPolarity = TIM_OCPolarity_Low; TIM_OCnInit(Timer->TIM, n, &OC);}void SetTimerOutputCC_MultiEdges(Timer_t* Timer, u32 n, u32 Adr, u32 Size) { Timer->EdgesSize[n] = Size; // Non zero triggers the DMA mode for multiple pulses Timer->EdgesTableAdr[n] = Adr;TIM_OCInitTypeDef OC;
TIM_OCStructInit(&OC); OC.TIM_OCMode = TIM_OCMode_Toggle; OC.TIM_OutputState = TIM_OutputState_Enable; OC.TIM_Pulse = 1; OC.TIM_OCPolarity = TIM_OCPolarity_Low; TIM_OCnInit(Timer->TIM, n, &OC);//CC1 only for now
TimerOutputCC_SetDMA(Timer, n); TIM_OCnPreloadConfig(Timer->TIM, n, TIM_OCPreload_Disable); // Enable preload feature * // no shadow registers please}void EnableFreeRunTimer(Timer_t* Timer) {
//u32 n; // Simnple timer mode if(Timer->EdgesSize[1]<2) { // DMA needed here TIM_Cmd(Timer->TIM, ENABLE); return; } //======================================================= // DMA transfer is required here (CC1 hard code for now TIM_DMA_Activate(Timer); TIM_Cmd(Timer->TIM, ENABLE); // let's run the Timer at last}