AnsweredAssumed Answered

Parallel data transmission using GPIO and DMA STM32F051xx

Question asked by bradaas on Aug 13, 2016
Latest reply on Aug 14, 2016 by bradaas
Hi all! I'm trying to create a parallel synchronous data transmission using DMA and GPIO on a STM32F051 MCU. I've been following the AN4666 document that ST released, but is set up for STM32F4 MCUs. I also don't use HAL drivers, unlike the example code ST has for that document.

I'm having issues with getting the DMA to output data to the pins. I see only 0 on the pins. I should see all 1s as I set the data buffer to 0xFFFF for all location in the buffer. I'm guessing I don't have DMA set up properly or have it tied correctly up to TIM1. TIM1 is currently outputting a 20KHz PWM signal as expected.

001.#include "base_lib.h"
002. 
003./* Private typedef -----------------------------------------------------------*/
004.GPIO_InitTypeDef        GPIO_InitStructure;
005.TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
006.TIM_OCInitTypeDef  TIM_OCInitStructure;
007.NVIC_InitTypeDef NVIC_InitStructure;
008.DMA_InitTypeDef DMA_InitStructure;
009. 
010.static uint16_t asrc_buffer[FULL_FRAME_SIZE];
011. 
012.uint32_t counter=0;
013. 
014. 
015./* Private function prototypes -----------------------------------------------*/
016.static void Data_GPIO_Config(void);
017.static void Timer_GPIO_Config(void);
018.static void ConfigureTIMxAsPWM_withDMA(void);
019.//static void SetupTimer(void);
020./* Private functions ---------------------------------------------------------*/
021. 
022./**
023.  * @brief  Main program
024.  * @param  None
025.  * @retval None
026.  */
027.int main(void)
028.{
029. 
030.  //Create data to be sent
031.  for (counter = 0; counter < FULL_FRAME_SIZE - 1 ; counter++)
032.  {
033.    asrc_buffer[counter] &= 0xFFFF;
034.  }
035. 
036. 
037.  /* Configure the Data GPIO in output mode  */
038.  Data_GPIO_Config();
039. 
040.  //Set up timer GPIO
041.  Timer_GPIO_Config();
042. 
043.  /* Start TIM PWM  with DMA1 channel 5 */
044. ConfigureTIMxAsPWM_withDMA();
045. 
046.  while (1){}
047. 
048.}
049. 
050./**
051.  * @brief  Configure the Data GPIO in output mode
052.  *
053.  * @note   None
054.  * @param  None
055.  * @retval None
056.  */
057. void Data_GPIO_Config(void)
058.{
059. 
060.    RCC->AHBENR |=RCC_AHBENR_GPIOCEN; // Enable GPIO port C
061. 
062.    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
063.        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
064.        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
065.        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
066.        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
067.        GPIO_Init(GPIOC, &GPIO_InitStructure);
068.}
069. 
070./**
071.  * @brief  Configure the Timer GPIO
072.  *
073.  * @note   None
074.  * @param  None
075.  * @retval None
076.  */
077. void Timer_GPIO_Config(void)
078.{
079. 
080.    RCC->AHBENR |=RCC_AHBENR_GPIOAEN; // Enable GPIO port A
081. 
082.        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
083.        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
084.        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
085.        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
086.        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
087. 
088.        //place before GPIO_Init to avoid pin being connected
089.        GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_2);
090. 
091.        GPIO_Init(GPIOA, &GPIO_InitStructure);
092.}
093. 
094.void ConfigureTIMxAsPWM_withDMA(void)
095.{
096. 
097.        /* TIM1 clock enable */
098.  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 , ENABLE);
099.  /* DMA1 clock enable */
100.  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
101. 
102.  uint16_t Period;
103. 
104.    Period = 1000000 / 20000; // 20 KHz for 1MHz prescaled
105. 
106. 
107.  /* Time Base configuration */
108.  TIM_TimeBaseStructure.TIM_Prescaler = 48-1;
109.  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
110.  TIM_TimeBaseStructure.TIM_Period = Period - 1;
111.  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
112.  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
113. 
114.  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
115. 
116.  // Channel 2
117.  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
118.   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
119.  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
120.  TIM_OCInitStructure.TIM_Pulse = Period/2 ; //50%
121.  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
122. 
123.  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
124.  TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
125. 
126.TIM_OC2Init(TIM1, &TIM_OCInitStructure);
127. 
128. 
129.//reset DMA1 channel5 to default values
130.DMA_DeInit(DMA1_Channel5);
131. 
132.  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&asrc_buffer; //Data source, aka our data buffer
133.  DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST; // Memory to peripheral
134.  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Always write to same register
135.  DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&(GPIOC->ODR); //Where we want to output the data
136.  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //Increment through data buffer
137.  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16-bit register
138.  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 16-bit array
139.  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //not circular
140.  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
141. 
142.  DMA_Init(DMA1_Channel5, &DMA_InitStructure);
143. 
144.// Enable DMA1 Channel5 Transfer Complete interrupt
145.DMA_ITConfig(DMA1_Channel5,DMA_IT_TC,ENABLE);
146. 
147. 
148.//Enable DMA1 channel IRQ Channel5 */
149.NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_5_IRQn; //DMA1_Channel4 and Channel5 interrupts
150.NVIC_InitStructure.NVIC_IRQChannelPriority=0;
151.NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
152.NVIC_Init(&NVIC_InitStructure);
153. 
154.//Enable the TIM1 compare capture 1 dma requests
155.TIM_DMACmd(TIM1,TIM_DMA_CC1,ENABLE);
156. 
157.  /* TIM1 Main Output Enable */
158.    TIM_CtrlPWMOutputs(TIM1, ENABLE);
159. 
160.  // turning on TIM1 and PWM outputs
161.  TIM_Cmd(TIM1, ENABLE);
162. 
163.}
164. 
165.void DMA1_Channel5_IRQHandler(void)
166.{
167.  //stop the clock cycles. TODO: use bare metal register instead
168.  TIM_Cmd(TIM1,DISABLE);
169. 
170.  if(DMA_GetITStatus(DMA1_IT_TC5))
171.  {
172. 
173.   //Clear DMA1 Channel5 Half Transfer, Transfer Complete and Global interrupt pending bits
174.    DMA_ClearITPendingBit(DMA1_IT_GL5);
175.  }
176.}

Outcomes