2015-07-21 12:27 PM
Hi All,
I'm configuring my DMA to transfer 48 bytes of data, and am triggering the DMA interrupt both on TC and HT events. Currently if I disable the DMA after the first interrupt, so I would expect to see 24 bytes of data but in reality I see 25-26 bytes. If I disable the DMA after the TC only, I see anywhere from 2-5 extra bytes of data. The data is the correct data in the consecutive bytes of my source so it seems like the DMA isn't being disabled fast enough. Also, if I slow my TIM2 down, the problem disappears. Is there a better way to do this than what I have below:/* Button press to initiate DMA transfer */
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(USER_BUTTON_EXTI_LINE) == SET)
{
EXTI_ClearITPendingBit(USER_BUTTON_EXTI_LINE);
// Clear all flags for DMA
DMA_ClearFlag(DMA1_FLAG_TC2 | DMA1_FLAG_HT2 | DMA1_FLAG_GL2 | DMA1_FLAG_TE2);
DMA_ClearFlag(DMA1_FLAG_TC5 | DMA1_FLAG_HT5 | DMA1_FLAG_GL5 | DMA1_FLAG_TE5);
DMA_ClearFlag(DMA1_FLAG_HT7 | DMA1_FLAG_GL7 | DMA1_FLAG_TE7);
// Configure number of bytes to transfer
DMA_SetCurrDataCounter(DMA1_Channel2, ARRAYSIZE); // ARRAYSIZE = 48
DMA_SetCurrDataCounter(DMA1_Channel5, ARRAYSIZE);
DMA_SetCurrDataCounter(DMA1_Channel7, ARRAYSIZE);
// Enable DMA1 Channel2
DMA_Cmd(DMA1_Channel2, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);
DMA_Cmd(DMA1_Channel7, ENABLE);
// Enable TIM2 Update trigger for DMA
TIM_DMACmd(TIM2, TIM_DMA_Update, ENABLE);
TIM_DMACmd(TIM2, TIM_DMA_CC1, ENABLE);
TIM_DMACmd(TIM2, TIM_DMA_CC2, ENABLE);
/* TIM Interrupts enable */
//TIM_ITConfig(TIM2, TIM_IT_Update | TIM_IT_CC1 | TIM_IT_CC2, ENABLE);
//TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
/* Enable Timer 2 with counter preloaded */
TIM_SetCounter(TIM2, TIMER_PERIOD);
TIM_Cmd(TIM2, ENABLE);
}
}
/* DMA1 Channel7 Interrupt Handler */
void DMA1_Channel7_IRQHandler(void)
//Clear DMA1 Channel1 Half Transfer, Transfer Complete and Global interrupt pending bits
DMA_ClearITPendingBit(DMA1_IT_GL7 | DMA1_IT_TC7 | DMA1_IT_HT7);
/* TIM Interrupts Disable */
TIM_ITConfig(TIM2, TIM_IT_Update | TIM_IT_CC1 | TIM_IT_CC2, DISABLE);
/* Disable Timer 2 */
TIM_Cmd(TIM2, DISABLE);
// disable the DMA channels
DMA_Cmd(DMA1_Channel2, DISABLE);
DMA_Cmd(DMA1_Channel5, DISABLE);
DMA_Cmd(DMA1_Channel7, DISABLE);
TIM_DMACmd(TIM2, TIM_DMA_CC1, DISABLE);
TIM_DMACmd(TIM2, TIM_DMA_CC2, DISABLE);
TIM_DMACmd(TIM2, TIM_DMA_Update, DISABLE);
STM_EVAL_LEDToggle(LED2);
}
2015-07-21 12:44 PM
Ok, how fast are you triggering the DMA?
The HT will fire after the 24th byte is sent. It will be off sending the 25th, and beyond.The idea with the HT is you then replace bytes 0-23, whilst 24-47 transit.The DMA is continuous, and in the background. In circular mode is does not stop.Things my be fogged slightly if you enable the FIFO. The interrupt will also have some latency.2015-07-21 12:48 PM
Can you stop the timer?
Can you use a timer with a repetition count?Can you master/slave a pair of timers?Can you pad the DMA with NULL frames?2015-07-21 02:42 PM
Hi Clive1,
I've posted my DMA initialization code as well, and to answer some of your questions:void WS2812_DMA_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // Enable DMA1 clock
/* ----------------------- Channel 2 --------------------- */
DMA_DeInit(DMA1_Channel2); // Reset DMA1 channe1 to default values;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // M2M Disabled- Peripheral mode (requires timer trigger)
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Circular mode
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // High priority
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // Memory to Peripheral
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16-bit Register
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Always write to same register
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&GPIOC->ODR; // Output data for GPIO
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 16-bit array
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // Always writing same data
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&WS2812_IO_High; // All outputs high
DMA_InitStructure.DMA_BufferSize = ARRAYSIZE; // Size of source array
DMA_Init(DMA1_Channel2, &DMA_InitStructure); // Initialize DMA
/* ----------------------- Channel 5 --------------------- */
DMA_DeInit(DMA1_Channel5); // Reset DMA1 channe5 to default values;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // M2M Disabled- Peripheral mode (requires timer trigger)
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Circular mode
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // High priority
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // Memory to Peripheral
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16-bit Register
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Always write to same register
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&GPIOC->ODR; // Output data for GPIO
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 16-bit array
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Increment through array
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&dmaBuffer; // 16-bit Source data
DMA_InitStructure.DMA_BufferSize = ARRAYSIZE; // Size of source array x 2
DMA_Init(DMA1_Channel5, &DMA_InitStructure); // Initialize DMA
/* ----------------------- Channel 7 --------------------- */
DMA_DeInit(DMA1_Channel7); // Reset DMA1 channe1 to default values;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // M2M Disabled- Peripheral mode (requires timer trigger)
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // Circular mode
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; // High priority
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; // Memory to Peripheral
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16-bit Register
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // Always write to same register
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&GPIOC->ODR; // Output data for GPIO
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 16-bit array
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // Always writing same data
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&WS2812_IO_Low; // All outputs low
DMA_InitStructure.DMA_BufferSize = ARRAYSIZE; // Size of source array x 2
DMA_Init(DMA1_Channel7, &DMA_InitStructure); // Initialize DMA
//Enable DMA1 channel IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Enable DMA1 Channel Transfer Complete interrupt
DMA_ITConfig(DMA1_Channel7, DMA_IT_TC | DMA_IT_HT, ENABLE);
}