3
\$\begingroup\$

I'm having some trouble getting my memory to GPIO DMA circular settings right. The initialization code pasted below has some issues.

I'm trying moving data from a uint16_t array to the GPIO->ODR register. I have the source and destination both configured as halfword since they're both 16-bit registers. My array is 48 bytes long, and I want to transfer 96 bytes (the 48 byte array 2 times). So I want the DMA to shift out the 48 bytes, then go back to the start of the array, and do it again with the same array.

However I'm not clear on how to tell the DMA after how many bytes to circle back. If I set the buffersize to 96 it just keeps reading 48 bytes past my array.

void WS2812_Basic_DMA_Init2(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);      // Enable DMA1 clock

    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_HalfWord;         // 16-bit array
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                     // Increment through array
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&source16;                 // 16-bit Source data

    DMA_InitStructure.DMA_BufferSize = ARRAYSIZE;           // Size of source array x 2

    DMA_Init(DMA1_Channel2, &DMA_InitStructure);            // Initialize DMA

    //Enable DMA1 channel IRQ Channel */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_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_Channel2, DMA_IT_TC, ENABLE);
}
\$\endgroup\$
3
\$\begingroup\$

The DMA was set up correctly but I was disabling it on the TC event. If I don't disable it, it will keep moving the specified number of bytes indefinitely.

The buffer size is what tells the DMA how many bytes to transfer before wrapping around, and it will continue to loop through those addresses until explicitly disabled.

\$\endgroup\$
  • 1
    \$\begingroup\$ Don't forget to accept your answer if that's solved it, otherwise they keep getting bumped as unanswered questions every month. \$\endgroup\$ – PeterJ Aug 18 '15 at 8:24
2
\$\begingroup\$

My array is 48 bytes long, and I want to transfer 96 bytes (the 48 byte array 2 times).

If I set the buffersize to 96 it just keeps reading 48 bytes past my array.

That's because the "number of data" register (DMA1_Channel2->CNTDR) should be the number of transfers in each pass (24), not the number of bytes in each pass (24 * sizeof (uint16_t)) or the number across all passes. The DMA would normally stop after this many transfers, but since you have circular mode enabled, it'll start another pass each time it finishes.

The wrapper functions give a false impression that you can correctly use these peripherals without reading the reference manual, but the details do still matter.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.