2013-08-06 09:47 PM
Our requirement is to configure the DMA so that it transfers data from the GPIO lines (8 bit data lines) to the RAM buffer upon receiving a trigger signal on the timer capture input channel.
We had received an example code from ST for the F1 series controllers and the code is attached. The code works fine with the F1 discovery board. Now we are trying to do the same thing on the F4 board. We have implemented the same configurations for F4 board and tried with F4 discovery board but the DMA data transfer doesn�t happen.The code for F4 is also attached.
The code file is attached. Please note that we have used standard peripheral library functions from ST. Can you please go through our configurations and point out errors? Or send the useful example which can fulfill our requirements.
2013-08-07 08:49 AM
The code doesn't look unreasonable as I skimmed over it. Are you sure you're testing with the right pins, and that they don't conflict with other things connected to your board. Why if GPIOC[0..7] is read are you setting it to be an output?
Is this a commercial project?2013-08-07 09:08 AM
I did not go through the code, but isn't the key difference in this regard between F1 and F4 in that in F4, GPIO is on AHB, and only DMA2 can perform memory-to-memory transfers?
JW2013-08-07 09:51 AM
Indeed, looks like you need DMA2, as DMA1 can only access peripherals on APB1
// STM32F4 DMA GPIO - sourcer32@gmail.com
// Per RM0090 DMA2, Channel0, Stream6 correlates to TIM1_CH1(PA8) source
// 2 KHz clock generated on TIM3_CH1 (PB4)
// Externally strap PA8 to PB4
// PC0 conflicts, adapt as required
#include ''stm32f4_discovery.h''
#define BufferSize 1000
uint8_t GPIO_DATA[BufferSize];
/*******************************************************************************
* Function Name : GPIOC Configuration
* Description : GPIO PORT configurations in such a way that so it status can be read
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void GPIOC_Configuration (void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); /* GPIOC clock enable */
// I'd prefer inputs, but going with original example...
GPIO_InitStructure.GPIO_Pin = 0xFF; // Pin0 .. Pin7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC, 0xFF); // Set High-Z
}
/*******************************************************************************
* Function Name : Timer1 configurations
* Description : Configure Timer1 in such a way that it can initiate data transfer using DMA on rising edge of clock signal received on port pin.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM1_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); /* TIM1 clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/* TIM1 channel 1 pin (PA8) configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1); // PB3 TIM1_CH1
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICFilter = 0x0;
TIM_ICInit(TIM1, &TIM_ICInitStructure);
TIM_CtrlPWMOutputs(TIM1, ENABLE); /* TIM Main Inputs/Output Enable */
TIM_Cmd(TIM1, ENABLE); /* TIM enable counter */
TIM_DMACmd(TIM1, TIM_DMA_CC1, ENABLE ); /* Enable TIM1_CC1 DMA Requests */
}
/*******************************************************************************
* Function Name : DMA2 configuration
* Description : Transfer Data from peripheral port (GPIOC) to RAM buffer
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void DMA2_Configuration(void)
{
/***************************************************************************************************************************************/
DMA_InitTypeDef DMA_InitStructure;
/* Enable the DMA clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
/* Configure the DMA Stream */
DMA_Cmd(DMA2_Stream6, DISABLE);
DMA_DeInit(DMA2_Stream6);
/* Set the parameters to be configured */
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&GPIOC->IDR; /* Read fron GPIO input data register */
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&GPIO_DATA[0]; /* Send the data to the RAM buffer */
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = BufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream6, &DMA_InitStructure);
/* Enable DMA Transfer Complete interrupt */
DMA_ITConfig(DMA2_Stream6, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA2_Stream6, ENABLE);
/***************************************************************************************************************************************/
}
/*******************************************************************************
* Function Name : TIM3_Configuration
* Description : Configure TIM3 to generate a PWM signal. Used for test purpose
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM3_Configuration (void)
{
#define TIM3_PERIOD (500-1) /* Timer 3 PWM period 2 KHz */
#define TIM3CH1_PULSE ((TIM3_PERIOD+1)/2) /* Timer 3 PWM pulse period */
#define TIM3_PRESCALER (84-1) /* Timer 3 prescaler 1 MHz */
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); /* TIM3 clock enable */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); /* GPIOB clock enable */
/* GPIOB Configuration: TIM3 channel 1 as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3); // PB4 TIM3_CH1
TIM_TimeBaseStructure.TIM_Period = TIM3_PERIOD;
TIM_TimeBaseStructure.TIM_Prescaler = TIM3_PRESCALER;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = TIM3CH1_PULSE;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_TimeBaseInit (TIM3, &TIM_TimeBaseStructure); /* Time base configuration */
TIM_OC1Init (TIM3, &TIM_OCInitStructure); /* PWM1 Mode configuration: Channel1 */
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable); /* Enable the timer preload */
TIM_ARRPreloadConfig(TIM3, ENABLE); /* Enable the time autoreload register */
TIM_Cmd (TIM3, ENABLE); /* Start the timer */
}
/**************************************************************************************/
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the DMA Stream IRQ Channel */
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**************************************************************************************/
void DMA2_Stream6_IRQHandler(void) // 2 Hz
{
/* Test on DMA Stream Transfer Complete interrupt */
if (DMA_GetITStatus(DMA2_Stream6, DMA_IT_TCIF6))
{
/* Clear DMA Stream Transfer Complete interrupt pending bit */
DMA_ClearITPendingBit(DMA2_Stream6, DMA_IT_TCIF6);
/* Toggle LED3 : End of Transfer */
STM_EVAL_LEDToggle(LED3);
// Add code here to process things
}
}
/**************************************************************************************/
/**
* @brief Main program
* @param None
* @retval None
*/
int main(void)
{
int i;
for(i=0;i<BufferSize; i++)
{
if((i % 2) == 0)
GPIO_DATA[i] = 0;
else
GPIO_DATA[i] = 0xFF;
}
GPIOC_Configuration();
NVIC_Configuration();
TIM1_Configuration();
TIM3_Configuration();
DMA2_Configuration();
STM_EVAL_LEDInit(LED3); /* Configure LEDs to monitor program status */
STM_EVAL_LEDOn(LED3); /* Turn LED3 on, 1 Hz means it working */
while(1); // Don't want to exit
}
Edit: Fixed a timer2 comment caught by waclawek.janto more accurately reflect the use of timer1
2014-02-12 03:36 AM
2014-02-12 09:56 AM
Do you have an example for something like this?
Sorry, I don't you'll have to experiment, there's no real flow control so it's likely to be ''bursty'' and discontinuous.You might want to look at how it's done for USB Camera's via DCMI for input applications.2014-04-09 08:21 AM
This is good info, thank you.
We are doing something similar. We want to run 4 SPI channels at the same time. I know that one of the variants of the part we are using (STM32F401xC) has 4 SPI ports built in but the physical size of that part is too big. We tried bit-banging the 4 channels, too slow.We are going to try to use the timers and DMA to read the four channels (16 bits each) in about 2 usec. I'm not sure it is possible but that's what we want to do.MikeI am responding to this post in part so that I can find it again. Is there a way to bookmark a post on the forums?2014-04-09 08:42 AM
There was another discussion about outputting via GPIO+DMA, and I built a version using the timer internally.
The forum is truly awful for finding things, or even looking for posts from a specific author. I use Google to find my own stuff, and try to mark/categorize them in a fashion that allows me to find them more easily. I also try to cross-link related posts so the Google stumbler can find other things. Someone else was trying to read multiple external ADC pins banked via GPIO, and the ability to use a clock pin, the trigger, or another modulated one, to clock the external device.2014-05-16 09:17 AM
This is an interesting application.
If i understand correctly: You supply 8-Bit of Data and a clock-signal to the processor. Upon rising edge of clock the DataByte is red from the GPIO-port and transfered to RAM via DMA. To achive the clock detection, you are using the Timer capture input, which in turn starts the DMA transfer. I wonder how fast that will go?2014-05-16 12:23 PM
I wonder how fast that will go?
It was more of a demonstration exercise, if I had to guess I'd suppose 21 MHz might be achievable on a 168 MHz F4 for GPIO Input w/External clock