cancel
Showing results for 
Search instead for 
Did you mean: 

Hello, I have been working with a STM32F4 where I use two channels of TIM8 to trigger data transfers from two GPIO ports to memory using two DMA2 streams. I would like to have these DMA streams write 16-bit data interleaved to 32-bit memory addresses.

dennis2399
Associate II

I have gotten the 16-bit data transfers individually working where each DMA stream would write to memory in half-world fashion. For this application it would make more sense to write 16-bit data from GPIO port I to the lower half of a 32-bit word and the data from GPIO port J to the higher half of that same 32-bit word.

My idea to implement this was to set up the DMA streams to have the peripheral data size to half-words and the memory size to worlds and have the memory base address aligned to respectively the address of a common data buffer and the address of the data buffer + 2.

The results however do not make sense to me. For testing purposes, using only 1 DMA stream the 16-bit data is written subsequently in the memory, not with a 16-bit gap of zeros between the individual DMA transfers.

When changing the size of the peripheral data size to words and testing with only 1 DMA stream the results is 16-bit data, 16-bit zeros, 16-bit data, ... When testing this with the other DMA stream the result is the same which I guess is due to data alignment being either half-word or word. (As specified in chapter 9.3.7 in the STM32F4 reference manual).

For reference I included the DMA setup code, the data buffers pointers are respectively pointing to the same address with p_data_buffer_2 pointing to the next half-word:

void Config_TIM8_CH2 (void){
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_7;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)p_data_buffer_1;
DMA_InitStructure.DMA_BufferSize = ACQ_TRANSFER_SIZE_HW;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(GPIOJ->IDR);
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
 
DMA_Init(DMA2_Stream3, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream3, DISABLE);
}
 
void Config_TIM8_CH3 (void){
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_7;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)p_data_buffer_2;
DMA_InitStructure.DMA_BufferSize = ACQ_TRANSFER_SIZE_HW;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(GPIOI->IDR);
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
 
DMA_Init(DMA2_Stream4, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream4, DISABLE);
}

Does anyone happen to know how I could achieve this using the DMA settings or is this there another way to do increment the DMA memory pointer with an extra offset?

2 REPLIES 2

Try to have a look at PINCOS bit in DMA control register.

It is a relatively obscure feature, you'll need to experiment with it.

Forget about using SPL, first.

You'll need to "turn around" the direction, too, having the GPIO at "memory"side, the memory buffer at "peripheral" side. Thus, peripheral side increments, memory doesn't. Both sides halfword. Use direct mode (no FIFO). Timing will be affected but that can be compensated at the timer, later, of it proves to be viable.

I'm not sure if you can use word unaligned address on the peripheral side with PINCOS (that's needed for the "assembly" to work. First pre-fill the buffer with recognizable pattern, then run the DMA set to word-aligned address to see the effect of PINCOS. If that works, try the unaligned address.

Report back with findings.

Plan B is to use 3 DMAs - two halfword GPIO-to-adjacent-halfwords-in-memory-nonincrementing, third, interleaved in time, picking the assembled word and storing it into final buffer.

JW

dennis2399
Associate II

The way I got it working now is using 3 DMA streams. Two DMA streams write their respective half-word GPIO data to either the lower or higher nibble of a 32-bit word variable triggered by TIM8. The third DMA stream writes the data from the 32-bit word to the data buffer in 32-bit word transfers triggered by a third channel of the TIM8 peripheral. I was quite amazed how good this actually works.