2020-05-05 06:35 AM
Hello.
I am trying to read data from a 12bit ADC AD9226 connected to port GPIOC, on
STM32F407VGT6 clocked at 168MHz. On port PA8 there is a 42MHz clock for the external ADC
and also the same TIM1_CH1 I triggers the DMA2 to transfer data from pheripheral to
memory. The DMA2 uses CH6, STREAM 3.
The sistem get's the data from the external ADC when a trigger signal is on GPIOA_Pin_9,
and after DMA2_Stream3_IRQHandler is called, we stop the DMA2 and set a flag so we can know that we have data, and then we display data on a 324x240 display.
The problem is that i barely reach to see corectly a 5MHz signal from a signal generator.
The DMA2 transfers the data at 42MHz?
Please take a look at my code to see if there are any errors. Thank you!
2020-05-05 06:35 AM
/*
* Keil uVision 5
* STM32F4xx Keil packs version 2.4.0
* STM32F4xx Standard peripheral drivers version 1.5.0
* 3.2" TFT LCD Display Module (HY32D)
*/
/* Include core modules */
#include "stm32f4xx.h"
#include "defines.h"
#include "glcd.h"
#define TOGGLE_GREEN_LED GPIOD->ODR ^= GPIO_Pin_12
#define TOGGLE_ORANGE_LED GPIOD->ODR ^= GPIO_Pin_13
// Used to clear the past signal from the display
uint16_t buf2[320];
// ADC buffer size
#define BufferSize 320 // * 100
// ADC buffer
uint16_t GPIO_DATA[BufferSize];
volatile uint8_t data_ready = 0;
/*******************************************************************************
* 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_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
uint16_t Period;
Period = 4; //(SystemCoreClock / 42000000);
// Clocks enable
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
// GPIOA Configuration: Channel 1 as alternate function push-pull
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_TIM1);
// TIM1 clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 , ENABLE);
// Time Base configuration
TIM_TimeBaseStructure.TIM_Prescaler = 0; // Dump 1X clock into timer
TIM_TimeBaseStructure.TIM_Period = Period - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
// Channel 1 Configuration in PWM mode
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = Period / 2; // 50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OC1Init(TIM1, &TIM_OCInitStructure);
// TIM1 Main Output Enable
TIM_CtrlPWMOutputs(TIM1, ENABLE);
// TIM1 counter enable
TIM_Cmd(TIM1, ENABLE);
TIM_DMACmd( TIM1, TIM_DMA_CC1, ENABLE);
}
/*******************************************************************************
* Function Name : DMA2 configuration
* Description : Transfer Data from peripheral port (GPIOA) to RAM buffer
* Input : None
* Output : None
* Return : None
* PA8 -> TIM1_CH1 - DMA2 STREAM 3, CH6 --- THIS IS USED
*******************************************************************************/
void DMA2_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// Enable the DMA Stream IRQ Channel
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// Enable the DMA clock
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
// Configure the DMA Stream
DMA_Cmd(DMA2_Stream3, DISABLE);
DMA_DeInit(DMA2_Stream3);
// Check if the DMA Stream is disabled before enabling it.
// Note that this step is useful when the same Stream is used multiple times:
// enabled, then disabled then reenabled... In this case, the DMA Stream disable
// will be effective only at the end of the ongoing data transfer and it will
// not be possible to reconfigure it before making sure that the Enable bit
// has been cleared by hardware. If the Stream is used only once, this step might
// be bypassed.
//while (DMA_GetCmdStatus(DMA2_Stream3) != DISABLE);
// Set the parameters to be configured
DMA_InitStructure.DMA_Channel = DMA_Channel_6;
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_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_BufferSize = BufferSize;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; // HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream3, &DMA_InitStructure);
// Enable DMA Transfer Complete interrupt
DMA_ITConfig(DMA2_Stream3, DMA_IT_TC, ENABLE);
DMA_Cmd(DMA2_Stream3, ENABLE);
}
void DMA2_Stream3_IRQHandler(void) {
// Test on DMA Stream Transfer Complete interrupt
if (DMA_GetITStatus(DMA2_Stream3, DMA_IT_TCIF3)) // != RESET)
{
// Clear DMA Stream Transfer Complete interrupt pending bit
DMA_ClearITPendingBit(DMA2_Stream3, DMA_IT_TCIF3);
DMA_Cmd(DMA2_Stream3, DISABLE);
//TIM_Cmd(TIM1, DISABLE);
data_ready = 1;
}
}
int main(void) {
double Vin;
uint16_t tmp;
uint16_t counter = 0x0;
uint16_t idx = 0x0;
uint32_t offset = 0x0;
GPIO_InitTypeDef GPIO_InitStructure;
/* Initialize system */
SystemInit();
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
/* STM32F4 Discovery: (STM32F407VG)
* - Onboard leds:
* - LED_GREEN on PD12
* - LED_ORANGE on PD13
* - Output type is push-pull
* - Mode is output
* - No pull resistor
* - Speed 100MHz
*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
// ADC pins
// D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11
// PC11 PC10 PC9 PC8 PC7 PC6 PC5 PC4 PC3 PC2 PC1 PC0
// PA8 is the CLK for the ADC (TIM1_CH2)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //GPIO_OType_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// Configure SYNC signal input pin
// Signal used to start the aquisition
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
LCD_Initializtion();
LCD_Clear(Black);
TIM1_Configuration();
DMA2_Configuration();
while (1)
{
// If SYNC is HIGH start DMA2 to get data
if (GPIOA->IDR & GPIO_Pin_9)
{
TOGGLE_ORANGE_LED;
// Start again to colect data
DMA_Cmd(DMA2_Stream3, ENABLE);
//TIM_Cmd(TIM1, ENABLE);
}
// If buffer is full then draw data to screen
if (data_ready == 1)
{
// Clear data ready flag
data_ready = 0;
// Erase last disaplayed signal
for (counter = 5; counter < 318; counter++)
{
LCD_DrawLine(240 - buf2[counter], counter, 240 - buf2[counter+1], counter+1, Black);
}
// Draw red line in middle screen
for (counter = 0; counter < 319; counter++)
{
LCD_SetPoint(120, 319 - counter , Red);
}
// Convert AD9266 values to voltage
for (counter = 0; counter < 319; counter++)
{
// ADC to voltage conversion
Vin = (2048.0 - GPIO_DATA[offset + counter]) / 409.6;
tmp = (uint16_t)(120.0 + (Vin*100.0)); // * 20 zoom to display 50mV signall
buf2[counter] = tmp;
}
// Draw the signal
for (counter = 5; counter < 318; counter++)
{
LCD_DrawLine(240 - buf2[counter], counter, 240 - buf2[counter+1], counter+1, Yellow);
}
// End of data to screen
TOGGLE_GREEN_LED;
}
}
}
2020-05-05 06:47 AM
The DMA can't handle transferring at 84MB/s.
Read the answers to this question:
https://community.st.com/s/question/0D50X00009XkYFc/dma-pheriperal-to-memory-max-speed
And the linked app note:
2020-05-05 07:05 AM
Hello.
I readed 3 times the PDF from above.
Why do you mean by 84MB/s ?
The timer that drives the DMA2 is at 42MB/s.
2020-05-05 07:09 AM
Mind your units.
Your timer that drives DMA2 is at 42 MHz. You're transferring a half-word each time (16 bits). So your desired throughput is 42 MHz * 16 bits = 84 MByte/s.
2020-05-05 07:27 AM
Ok. Now I get it right 42000000 * 16 / 8 = 84MB/s. Thank you for clarifying this.
Now a question arrise: If I will use an 8bit ADC it will give me 42MB/s.
Also what it means by data contention, I can understand this:
"Probably close to 21 MHz, but you'll get an overwhelming amount of data, and a lot of contention. I'd also expect jitter in the sample point/rate
Consider using the DCMI bus, admittedly not 16-bit wide, but could use 8-bit mode. Or the F(S)MC"
Thank yo for taking your time to answer my questions!
2020-05-08 04:10 AM
Someone please tell me why the STM32F429 DMA MEM2MEM can go up to 96MHz, I can't find anywere information about this.
Thank you!
2020-05-08 05:49 AM
Contention, the DMA and CPU both want to access the same memory unit at the same time, they can't, so they have to take turns and share the bandwidth.
2020-05-08 06:10 AM
Thank you!