Electronic – STM32F3 Circular DMA

dmastm32

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

Best Answer

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.