cancel
Showing results for 
Search instead for 
Did you mean: 

Extra Data Transferred by DMA

daniel2
Associate II
Posted on July 21, 2015 at 21:27

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);
}

3 REPLIES 3
Posted on July 21, 2015 at 21:44

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.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on July 21, 2015 at 21:48

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?
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
daniel2
Associate II
Posted on July 21, 2015 at 23:42

Hi Clive1,

I've posted my DMA initialization code as well, and to answer some of your questions:
  1. The 3 channels of TIM2 are all running at 800MHz, but are offset. So the 3 channels of the DMA are triggering at 800MHz, but they all drive a single GPIO port so the effective speed on the port is 2.4MHz
  2. I can stop the timer, and do, but this is also done in the DMA interrupt. I can try implementing a counter that counts to the same number the DMA counter is loaded with to see if that is faster.
  3. I'm not sure what you mean by master/slaveing a pair of timers... can you explain this?
  4. Padding the data will NULL frames may not wont work because it is being sent to the shift register of a string of LEDs so a few extra frames may get shifted. I can try this out though and see how the devices react.

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);
}