cancel
Showing results for 
Search instead for 
Did you mean: 

ADC via dma and USART

arvydasruibys
Associate II
Posted on February 13, 2013 at 10:52

Hello, i am having trouble writing code on STM32F4Discovery. my goal is to get values from ADC in continuous mode and send them to USART/UART (with DMA or without)using interrupts. so i wrote a code made out of couple examples on stm std lib and internet. but it doesn't work and i don't know why. please give me some guidelines 🙂

Some code comments are not true because i changed things several times. ADC pin is PC2, using ADC3 peripheral, USART1 peripheral connected to PB6 ant PB7

/* Includes ------------------------------------------------------------------*/
#include ''stm32f4xx.h'' 
/* Private define ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
#define ADC3_DR_ADDRESS ((uint32_t)0x4001224C) 
uint8_t NbrOfDataToTransfer; 
__IO uint8_t TxCounter; 
__IO uint16_t ADC3ConvertedValue = 0; 
__IO uint32_t ADC3ConvertedVoltage = 0; 
void
ADC3_CH12_DMA_Config(
void
) 
{ 
ADC_InitTypeDef ADC_InitStructure; 
ADC_CommonInitTypeDef ADC_CommonInitStructure; 
DMA_InitTypeDef DMA_InitStructure; 
GPIO_InitTypeDef GPIO_InitStructure; 
/* Enable ADC3, DMA2 and GPIO clocks ****************************************/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC, ENABLE); 
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE); 
/* DMA2 Stream0 channel2 configuration **************************************/
DMA_InitStructure.DMA_Channel = DMA_Channel_2; 
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC3_DR_ADDRESS; 
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADC3ConvertedValue; 
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; 
DMA_InitStructure.DMA_BufferSize = 1; 
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; 
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 
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_Stream0, &DMA_InitStructure); 
DMA_Cmd(DMA2_Stream0, ENABLE); 
/* Configure ADC3 Channel7 pin as analog input ******************************/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; 
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; 
GPIO_Init(GPIOC, &GPIO_InitStructure); 
/* ADC Common Init **********************************************************/
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; 
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; 
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; 
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; 
ADC_CommonInit(&ADC_CommonInitStructure); 
/* ADC3 Init ****************************************************************/
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; 
ADC_InitStructure.ADC_ScanConvMode = DISABLE; 
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; 
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; 
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; 
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; 
ADC_InitStructure.ADC_NbrOfConversion = 1; 
ADC_Init(ADC3, &ADC_InitStructure); 
/* ADC3 regular channel7 configuration *************************************/
ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_3Cycles); 
/* Enable DMA request after last transfer (Single-ADC mode) */
ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE); 
/* Enable ADC3 DMA */
ADC_DMACmd(ADC3, ENABLE); 
/* Enable ADC3 */
ADC_Cmd(ADC3, ENABLE); 
} 
void
init_USART1(uint32_t baudrate) 
{ 
USART_InitTypeDef USART_InitStruct; 
GPIO_InitTypeDef GPIO_InitStruct; 
// this is for the GPIO pins used as TX and RX 
/* enable APB2 peripheral clock for USART1 
* note that only USART1 and USART6 are connected to APB2 
* the other USARTs are connected to APB1 
*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); 
/* enable the peripheral clock for the pins used by 
* USART1, PB6 for TX and PB7 for RX 
*/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 
/* This sequence sets up the TX and RX pins 
* so they work correctly with the USART1 peripheral 
*/
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; 
// Pins 6 (TX) and 7 (RX) are used 
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; 
// the pins are configured as alternate function so the USART peripheral has access to them 
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; 
// this defines the IO speed and has nothing to do with the baudrate! 
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; 
// this defines the output type as push pull mode (as opposed to open drain) 
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; 
// this activates the pullup resistors on the IO pins 
GPIO_Init(GPIOB, &GPIO_InitStruct); 
// now all the values are passed to the GPIO_Init() function which sets the GPIO registers 
/* The RX and TX pins are now connected to their AF 
* so that the USART1 can take over control of the 
* pins 
*/
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1); 
// 
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1); 
/* Now the USART_InitStruct is used to define the 
* properties of USART1 
*/
USART_InitStruct.USART_BaudRate = baudrate; 
USART_InitStruct.USART_WordLength = USART_WordLength_8b; 
USART_InitStruct.USART_StopBits = USART_StopBits_1; 
USART_InitStruct.USART_Parity = USART_Parity_No; 
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; 
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; 
USART_Init(USART1, &USART_InitStruct); 
/* Enable the EVAL_USART1 Transmit interrupt: this interrupt is generated when the 
EVAL_USART1 transmit data register is empty */
USART_ITConfig(USART1, USART_IT_TXE, ENABLE); 
USART_Cmd(USART1, ENABLE);
// finally this enables the complete USART1 peripheral 
} 
void
NVIC_Config(
void
) 
{ 
NVIC_InitTypeDef NVIC_InitStructure; 
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; 
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 
NVIC_Init(&NVIC_InitStructure); 
} 
int
main(
void
) 
{ 
init_USART1(115200); 
NVIC_Config(); 
ADC3_CH12_DMA_Config(); 
ADC_SoftwareStartConv(ADC3); 
while
(1) 
{ 
ADC3ConvertedVoltage = ADC3ConvertedValue *3300/0xFFF; 
USART_SendData(USART1, ADC3ConvertedVoltage); 
} 
} 
// this is the interrupt request handler (IRQ) for ALL USART1 interrupts 
void
USART1_IRQHandler(
void
) 
{ 
/* Wait until EVAL_USART1 send the TxBuffer */
while
(TxCounter < NbrOfDataToTransfer) 
{} 
/* The software must wait until TC=1. The TC flag remains cleared during all data 
transfers and it is set by hardware at the last frame’s end of transmission*/
while
(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) 
{} 
} 

#adc #dma #usart-interrupt #stm32
6 REPLIES 6
raptorhal2
Lead
Posted on February 13, 2013 at 15:14

I presume you have used a debugger to see if the ADC3ConvertedValue is correct. If so, the problem is in USART_SendData, which is not shown here.

Cheers, Hal

Posted on February 13, 2013 at 15:34

You enable an interrupt which you don't service. You need to disable the TXE interrupt if you have no data, but at the very least you must clear the interrupt to ever leave the interrupt state.

You need to wait for the USART1 to assert TXE before stuffing data out by USART_SendData().

You need to do something about the data, as USART_SendData() is only sending 8-bit binary, maybe you want to send the high/low bits of the sample, or send it in ASCII.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
arvydasruibys
Associate II
Posted on May 13, 2013 at 17:42

The original post was too long to process during our migration. Please click on the provided URL to read the original post. https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I6az&d=%2Fa%2F0X0000000brN%2FrFD6Kj811TtkROYF9K51N_vLxKIWodYXYiWGWbqqP0U&asPdf=false
Posted on May 13, 2013 at 19:19

I'm not sure this will achieve what you want, as you'd need to better pace the generation of samples, and dispatch those to the USART.

You don't call ADC_Configuration(), so it won't start.

The increment, or not, of the DMA relates to how it updates the source/destination addresses. Obviously the peripheral registers don't move, they have a new sample in each time they are read, thus you don't increment the register address.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
arvydasruibys
Associate II
Posted on May 13, 2013 at 19:49

thanks for info 🙂 so how i could implement DMA in my project? i think that using DMA makes sense especially with max speeds (Triple Interleaved mode at 7.2MS/s). i think without dma i wouldn't achieve those speeds. what should i do then?

Posted on May 13, 2013 at 21:26

At 7.2 MSps you'll definitely need DMA to service the ADC. You'll also need to buffer ALL the data you expect to sample, as the USART's running at about 11KBps

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..