2015-07-09 01:17 PM
Hi All,
I'm trying to drive some WS2812 LEDs which require a (400ns high + 800ns low) or (800ns high + 400ns low) signal to indicate a 1 or a 0.So esentially I'm trying to take an array of data and output it directly to a GPIO at about 2.5MHz. I've never used the DMA before but from what I understand it should be able to ouput data straight from memory to a GPIO.How should I try to accomplish this using the DMA, or are there any better ways of outputting data on a GPIO at a consistent clock?2015-07-09 05:41 PM
I've demonstrated TIM driven DMA to/from GPIO in several STM32 families. Suggest you review those. Refer to the Reference Manual to understand the association between TIM triggers and DMA channels.
For 2.5 MHz, you're going to have to run the processor at some integer multiple of that frequency for the TIM dividers to function.2015-07-10 08:45 AM
Hi Clive,
That sounds like it would be useful to me- do you have these demonstrations available somewhere?From further reading it seems like the best thing for me to do would be to do DMA transfers from RAM to the GPIO ODR. If I want to move 8 bits from RAM into the ODR for outputs PinA0 - PinA7, and use circular mode with a double buffer how should I configure the data sizes?I was thinking the memoryDataSize should be HalfWord while PeripheralDataSize would be byte, but in some examples I've seen the opposite.2015-07-10 09:25 AM
do you have these demonstrations available somewhere?
I post everything here, use the site search, or Google. I'd probably look at doing a 32-bit write to GPIOx->BSRR I'd look to update pattern buffers in a ping-pong fashion using DMA HT/TC interrupts.[DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Data%20transfer%20from%20GPIO%20port%20to%20RAM%20buffer%20using%20DMA%20upon%20receiving%20a%20trigger%20signal%20on%20the%20timer%20capture%20input%20channel&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&TopicsView=https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/AllItems.aspx¤tviews=4111]https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=%2fpublic%2fSTe2ecommunities%2fmcu%2fLists%2fcortex_mx_stm32%2fData%20transfer%20from%20GPIO%20port%20to%20RAM%20buffer%20using%20DMA%20upon%20receiving%20a%20trigger%20signal%20on%20the%20timer%20capture%20input%20channel&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&TopicsView=https%3A%2F%2Fmy.st.com%2Fpublic%2FSTe2ecommunities%2Fmcu%2FLists%2Fcortex_mx_stm32%2FAllItems.aspx¤tviews=41112015-07-13 04:31 PM
I'm having some trouble getting these settings right. The code pasted below, does work, but I'm trying to get circular buffering working, and I believe I shouldn't be using M2M mode. However, if I disable M2M mode the whole things breaks, and I'm not sure how to setup the circular buffer.
1. Should I be doing this with M2M disabled or enable? 2. If disabled, then why is it not working if I disable M2M 3. My data packets are 24 bits each. Do I need to create a uint32_t array[2] to ping pong in or should I create a uint32_t array[48] and load the data 24 bits at a time? Either way how do I configure the MemoryDataSize, PeripheralDataSize, and bufferSize variables to have the DMA circle through my array?/* Test on DMA1 Channel 2 */
void WS2812_DMA2_Init(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_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = ARRAYSIZE;
/* Memory Parameters */
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&source;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
/* Peripheral Parameters */
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&GPIOC->BSRR;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_Init(DMA1_Channel2, &DMA_InitStructure);
//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);
}
2015-07-15 07:16 AM
I discovered that I needed to use the timer interrupts with DMA_M2M disabled. That answers 1. & 2. but I'm still wondering about how to setup the circular buffer.
I'm moving data from a uint16_t 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 halfWords long (2 x 24 halfWord packets), and I want to transfer 192 halfWords (8 packets or twice through my 48 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 halfWords past my array.uint16_t source16[ARRAYSIZE] = {};
for (i=2; i<ARRAYSIZE/2;i++){
if (i%2 == 0){
source16[i] = i/2;
}
else{
source16[i]= LED_OFF_ODR;
}
}
WS2812_GPIO_Init();
WS2812_DMA_Init();
WS2812_Timer_Init();
void EXTI15_10_IRQHandler(void)
{
if (EXTI_GetITStatus(USER_BUTTON_EXTI_LINE) == SET)
{
// Clear all flags for DMA
DMA_ClearFlag(DMA1_FLAG_TC2 | DMA1_FLAG_HT2 | DMA1_FLAG_GL2 | DMA1_FLAG_TE2);
// Configure number of bytes to transfer
DMA_SetCurrDataCounter(DMA1_Channel2, ARRAYSIZE*4);
// Enable DMA1 Channel2
DMA_Cmd(DMA1_Channel2, ENABLE);
// Enable TIM2 Update trigger for DMA
TIM_DMACmd(TIM2, TIM_DMA_Update, ENABLE);
/* Enable Timer 2 */
TIM_Cmd(TIM2, ENABLE);
EXTI_ClearITPendingBit(USER_BUTTON_EXTI_LINE);
}
}
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 * 4; // Size of source array x 4
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);
}
2015-07-15 08:09 AM
You'd want to initialize your pattern buffer for TWO sets of data, and configure BOTH the HT and TC interrupts.
The HT (Half Transfer) occurs after the FIRST set of data is dispatched, and you replace it with the THIRD set of data while the DMA is sending the SECOND set, after which you'll get the TC (Transfer Complete), and the circular DMA wraps around and starts over.You will clearly need to generate new pattern data faster than the DMA is consuming it for this to work.2015-07-15 10:15 AM
I have done all this as far as I know. I'm not replacing the data on the HT yet, but I would like to see the DMA wrap around with just the two sets of data. However I don't understand how the DMA knows when to wrap. Like I said, if I set the buffer size to ARRAYSIZE * 2, the DMA does not currently wrap around but rather just keeps reading past my array.
What's the mechanism for telling the DMA how many transfers to do before wrapping around?2015-07-15 12:22 PM
The DMA doesn't understand bytes, it measures transfers as a count on elements, of size you specify
HT occurs at DMA_InitStructure.DMA_BufferSize / 2 elementsTC occurs at DMA_InitStructure.DMA_BufferSizeIf DMA_InitStructure.DMA_BufferSize = 16, the array is a[0] .. a[15], if circular repeats as a[0] .. a[15], triggering HT at each a[7] and TC at each a[15]