Library 06- AD Converter on STM32F4

In this tutorial I will talk about ADC (Analog to Digital Converter). AD converts analog voltage to digital number, that can be used in microcontrollers.

STM32F4xx MCUs have up to 3 ADCs of which every has 19 channels.

  • 16 external channels, connected to IO pins
  • 3 internal channels
    • Vbat
      • Voltage on battery pin for RTC
    • Temp sensor
      • unusable for measure temp,only for measure difference in temperature because it can fail up to 45°C
    • Vref
      • Voltage reference for ADC

Below is table with and channels connected to physical pins.

Library

Features

  • Measure analog value with pooling mode
  • Use all possible ADC’s with all possible pins
  • Measure Vbat pin for RTC battery

Dependencies

  • CMSIS
    • STM32F4xx
    • STM32F4xx RCC
    • STM32F4xx GPIO
    • STM32F4xx ADC
  • TM
    • TM GPIO
    • defines.h
Channel ADC1 ADC2 ADC3
APB 2 2 2
ADC Channel 0 PA0 PA0 PA0
ADC Channel 1 PA1 PA1 PA1
ADC Channel 2 PA2 PA2 PA2
ADC Channel 3 PA3 PA3 PA3
ADC Channel 4 PA4 PA4 PF6
ADC Channel 5 PA5 PA5 PF7
ADC Channel 6 PA6 PA6 PF8
ADC Channel 7 PA7 PA7 PF9
ADC Channel 8 PB0 PB0 PF10
ADC Channel 9 PB1 PB1 PF3
ADC Channel 10 PC0 PC0 PC0
ADC Channel 11 PC1 PC1 PC1
ADC Channel 12 PC2 PC2 PC2
ADC Channel 13 PC3 PC3 PC3
ADC Channel 14 PC4 PC4 PF4
ADC Channel 15 PC5 PC5 PF5

My library ADCx:

  • Resolution 12bits
  • Prescaler set to 4: APB2 clock / 4
  • Single mode,
  • Independent mode,
  • 8 clock cycles between 2 reads

Functions and enumerations

Example 1

Example 2

  • Measure voltage on Vbat pin
  • Result is in mV

Projects are available on Github, download library below.

Icon
TM STM32F4 ADC Library

ADC library to read analog voltage

tilz0R

Owner of this site. Also electronic enthusiasts, web developer, 3D printer fan, handball player and more. Big fan of STM32F4 devices. In anticipation of the new Discovery board for STM32F7 lines.

You may also like...

Read before commenting!

Before you make a new comment, make sure you agree with things listed below:

  • - Read post to make sure if it is already posted what you are asking for,
  • - Make sure you have the latest version of libraries used in your project,
  • - Make a clean and grammatically correct written message,
  • - Report as many details as possible, including what have you done so far,
  • - Do NOT post any code here. Use Pastebin,
  • - Do NOT post any error codes here. Use Pastebin,
  • - Specify STM32Fxxx family and used Discovery/EVAL/Nucleo or custom made board,
  • - Make sure your clock is set correct for PLL,
  • - If you are using my HAL drivers, please check this post how to start.
Comment will be deleted on breaking these rules without notification!
  • phivu

    Hi Tilen, I transfer my code using AD link DMA. I used ADC 1 channel 0 and ADC1 channel 1 with DMA channel 0 stream4. ADC is actiaved by TIM3. It can show value already but I donot know why it usually shows in int value although i set up it in float value already. Please help me to solve and tell me what i am wrong.

    /* Includes ——————————————————————*/

    #include “main.h”

    #include “stm32f4xx.h”

    #include “string.h”

    #include “stdio.h”

    /* Private typedef ———————————————————–*/

    GPIO_InitTypeDef GPIOHandle;

    USART_InitTypeDef USARTHandle;

    ADC_InitTypeDef ADCHandle;

    ADC_CommonInitTypeDef ADC_Common;

    DMA_InitTypeDef DMAHandle;

    /* Private define ————————————————————*/

    #define NS 10 // Number of samples to get from ADC

    //====================================================================================

    // Functions used in this module

    //====================================================================================

    static void ADC_SoftwareConv(void);

    static void TIM3_Config(void);

    static void ADC_Config(void);

    static void USART2_Config(void);

    static void TM_Delay_Init(void);

    static void TM_DelayMillis(uint32_t millis);

    static void TM_DelayMicros(uint32_t micros);

    /* Private macro ————————————————————*/

    /* Private variables ———————————————————*/

    // timer counter

    uint32_t multiplier;

    // acceleration sensor

    float gx,gz;

    volatile uint16_t ADCValue[2]={0};

    /* We need to implement own __FILE struct */

    /* FILE struct is used from __FILE */

    struct __FILE {

    int dummy;

    };

    /* You need this if you want use printf */

    /* Struct FILE is implemented in stdio.h */

    FILE __stdout;

    int fputc(int ch, FILE *f) {

    /* Do your stuff here */

    /* Send your custom byte */

    /* Send byte to USART */

    USART_SendData(USART2, (uint8_t) ch);

    /* Loop until the end of transmission */

    while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET)

    {}

    /* If everything is OK, you have to return character written */

    return ch;

    /* If character is not correct, you can return EOF (-1) to stop writing */

    //return -1;

    }

    /* Private function prototypes ———————————————–*/

    /* Private functions ———————————————————*/

    /**

    * @brief Main program

    * @param None

    * @retval None

    */

    int main(void)

    {

    USART2_Config();

    TM_Delay_Init();

    ADC_Config();

    TIM3_Config();

    printf(“rn***Starting program***”);

    //Configure KXP84 for Quick Start Impl. (AN008):

    while (1)

    {

    ADC_SoftwareConv();

    printf(“rn Value gx: “);printf(” %f “,gx);

    printf(“rn Value gz: “);printf(” %f “,gz);

    }

    }

    /**

    * @brief Congig USART2

    * @param None

    * @retval None

    */

    void USART2_Config()

    {

    /* USARTx configuration ——————————————————*/

    /* USARTx configured as follows:

    – BaudRate = 9600 baud

    – Word Length = 8 Bits

    – Two Stop Bit

    – Odd parity

    – Hardware flow control disabled (RTS and CTS signals)

    – Receive and transmit enabled

    */

    /* Enable GPIOA clock */

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    /* Set alternating function to USART 2, PA2: TX; PA3: RX */

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2);

    /* Set pin settings, alternate function */

    GPIOHandle.GPIO_Mode = GPIO_Mode_AF;

    GPIOHandle.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;

    GPIOHandle.GPIO_Speed = GPIO_Speed_50MHz;

    GPIOHandle.GPIO_PuPd = GPIO_PuPd_UP;

    GPIOHandle.GPIO_OType = GPIO_OType_PP;

    /* Initialize pins for USART2 */

    GPIO_Init(GPIOA, &GPIOHandle);

    /* Enable UART2 clock */

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    /* USARTx configuration ——————————————————*/

    USARTHandle.USART_BaudRate = 115200;

    USARTHandle.USART_WordLength = USART_WordLength_8b;

    USARTHandle.USART_StopBits = USART_StopBits_1;

    USARTHandle.USART_Parity = USART_Parity_No;

    USARTHandle.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    USARTHandle.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART2, &USARTHandle);

    /* Enable USART */

    USART_Cmd(USART2, ENABLE);

    }

    /**

    * @brief Congig ADC

    * @param None

    * @retval None

    */

    static void ADC_Config(void)

    {

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    //==Configure ADC pins (PA.0 -> Channel 0 and PA.1 -> Channel 1) as analog inputs==

    GPIO_StructInit(&GPIOHandle); // Reset init structure, if not it can cause issues…

    GPIOHandle.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_1;

    GPIOHandle.GPIO_Mode = GPIO_Mode_AIN;

    GPIOHandle.GPIO_PuPd = GPIO_PuPd_NOPULL ;

    GPIO_Init(GPIOA, &GPIOHandle);

    /* DMA2 Stream0 channel0 configuration **************************************/

    DMAHandle.DMA_Channel = DMA_Channel_0;

    DMAHandle.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC1->DR));

    DMAHandle.DMA_Memory0BaseAddr = (uint32_t)&ADCValue;

    DMAHandle.DMA_DIR = DMA_DIR_PeripheralToMemory;

    DMAHandle.DMA_BufferSize = 2; //Buffer size (2 because we using two channels)

    DMAHandle.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

    DMAHandle.DMA_MemoryInc = DMA_MemoryInc_Enable;

    DMAHandle.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;

    DMAHandle.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;

    DMAHandle.DMA_Mode = DMA_Mode_Circular;

    DMAHandle.DMA_Priority = DMA_Priority_High;

    DMAHandle.DMA_FIFOMode = DMA_FIFOMode_Enable;

    DMAHandle.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;

    DMAHandle.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;

    DMAHandle.DMA_MemoryBurst = DMA_MemoryBurst_Single;

    DMAHandle.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

    DMA_Init(DMA2_Stream4, &DMAHandle);

    DMA_Cmd(DMA2_Stream4, ENABLE);

    ADC_Common.ADC_Mode = ADC_Mode_Independent;

    ADC_Common.ADC_Prescaler = ADC_Prescaler_Div2;

    ADC_Common.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;

    ADC_Common.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;

    ADC_CommonInit(&ADC_Common);

    ADCHandle.ADC_Resolution = ADC_Resolution_12b;

    ADCHandle.ADC_ScanConvMode = ENABLE;

    ADCHandle.ADC_ContinuousConvMode = ENABLE; // ENABLE for max ADC sampling frequency

    ADCHandle.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;

    // ADCHandle.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T3_TRGO;

    ADCHandle.ADC_DataAlign = ADC_DataAlign_Right;

    ADCHandle.ADC_NbrOfConversion = 2;

    ADC_Init(ADC1, &ADCHandle);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_3Cycles);

    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_3Cycles);

    ADC_DMACmd(ADC1, ENABLE);

    ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);

    ADC_Cmd(ADC1, ENABLE);

    ADC_SoftwareStartConv(ADC1);

    }

    //====================================================================================

    // Configuring TIM3 to trigger at 2kHz which is the ADC sampling rate

    //====================================================================================

    void TIM3_Config(void)

    {

    TIM_TimeBaseInitTypeDef TIM3_TimeBase;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

    TIM_TimeBaseStructInit(&TIM3_TimeBase);

    TIM3_TimeBase.TIM_Period = (uint16_t)49; // Trigger = CK_CNT/(49+1) = 2kHz

    TIM3_TimeBase.TIM_Prescaler = 420; // CK_CNT = 42MHz/420 = 100kHz

    TIM3_TimeBase.TIM_ClockDivision = 0;

    TIM3_TimeBase.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM3, &TIM3_TimeBase);

    TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);

    TIM_Cmd(TIM3, ENABLE);

    }

    //====================================================================================

    // Description: Averaging samples from ADC, calculating temperature in Celsius

    //====================================================================================

    void ADC_SoftwareConv(void)

    {

    gx= ((ADCValue[0] – 2048) /819);

    gz= ((ADCValue[1] – 2048) /819);

    printf(“rn Value analogx: “);printf(“%d”,ADCValue[0]);

    printf(“rn Value analogz: “);printf(“%d”,ADCValue[1]);

    TM_DelayMicros(100);

    }

    /**

    * @brief Congig delay ms and us

    * @param None

    * @retval None

    */

    void TM_Delay_Init(void) {

    RCC_ClocksTypeDef RCC_Clocks;

    /* Get system clocks */

    RCC_GetClocksFreq(&RCC_Clocks);

    /* While loop takes 4 cycles */

    /* For 1 us delay, we need to divide with 4M */

    multiplier = RCC_Clocks.HCLK_Frequency / 4000000;

    }

    void TM_DelayMicros(uint32_t micros) {

    /* Multiply micros with multipler */

    /* Substract 10 */

    micros = micros * multiplier – 10;

    /* 4 cycles for one loop */

    while (micros–);

    }

    void TM_DelayMillis(uint32_t millis) {

    /* Multiply millis with multipler */

    /* Substract 10 */

    millis = 1000 * millis * multiplier – 10;

    /* 4 cycles for one loop */

    while (millis–);

    }

    • Next time copy big file to pastebin.
      Emm i dont find conversion from adc result to float.

      • phivu

        That is the gx and gz parameters. but I donot know why when i use printf with them, it shows me the result in int value.

        Beside of that, TIM_Period = 49 ,therefore, it will trigger 42 Mhz/ 420= 100 Khz / (49+1) = 2KHZ for activating adc right?

        • Try to remove #include string line.

          I told you, that if you set prescaler to 420, like you did, this is actually real prescaler 421, so timer’s clock is 42MHz / 421 = 99760Hz
          Set parameter to 419 and you will get correct speed.

  • phivu

    Here is my new code. Unfortunately, it shows me wrong and same value between two channels. Please help me to fix by my code here. Thanks.

    http://www.mediafire.com/download/jautywxt89uxo38/ADC+and+DMA.rar

    • In this project I see at least 3 main.c files.
      Please make clean project without unused shit.

  • Callum Lacey

    hello, Firstly thankyou for this website it is so so so helpful !

    i have a question im trying to edit the digital value after the ADC so i declare at the start uint16_t X; once i call TM_ADC_READ this works i display it on the display but then if i put X = ADC1->DR it says X was referenced but never used this also occurs if i put getconversionvalue then try X= ADC1->DR

    is there something blindingly simple that im missing ? any help would be greatly appreciated

    • Hi, function getconversionvalue already returns you ADC->DR value.
      No need to manually call this.
      If you assign X to some value and then you do nothing with this value, then maybe compiler assume that variable is useless and show you warning.

  • bobby

    Is there any example for ADC DMA processing if we want to get high speed ADC sample rate?

  • Vinícius Jean Ferreira

    Hi, bro! I’m very new in microcontrollers and I don’t understand why you have used USART in this example. I want to get a voltage signal in an ADC and then put in to a float variable to show in the monitor. I can use this example if I modify it? Thanks 🙂

    • HI,
      USART is the peripheral, which is used to transmit AD conversion result to computer, so user can see it.
      On the other side, you need USB->UART converter to receive data.

      This example is then for you. You don’t need to modify it at all 🙂

      • Vinícius Jean Ferreira

        Ah, I think I used this with an ARM Cortex-M3 from Texas Instruments, but USART makes the conversion appear in the monitor of the PC, right? I need to put the result on the LCD of the kit, so, I think I just need the ADC library, right?

        • Ok, but this libraries are for STM32F4xx not for texas instruments.

          • Vinícius Jean Ferreira

            Can you check my code? The ADC is given me the wrong tension. I don’t know why.

            PS.: I have used your code as a base.

            /* Include core modules */

            #include “stm32f4xx.h”

            /* Include my libraries here */

            #include “defines.h”

            #include “tm_stm32f4_ili9341_ltdc.h”

            #include “tm_stm32f4_fonts.h”

            #include “tm_stm32f4_delay.h”

            #include “tm_stm32f4_disco.h”

            #include “tm_stm32f4_adc.h”

            #include

            #include

            int main(void) {

            float tensao = 0;

            char texto[200] = “Leitura de tensao:”;

            char texto2[200];

            /* Initialize system */

            SystemInit();

            /* Initialize delay */

            TM_DELAY_Init();

            /* Initialize ILI9341 with LTDC */

            /* By default layer 1 is used */

            TM_ILI9341_Init();

            /* Initialize ADC1 on channel 0, this is pin PA0 */

            TM_ADC_Init(ADC1, ADC_Channel_0);

            /* Rotate LCD for 90 degrees */

            TM_ILI9341_Rotate(TM_ILI9341_Orientation_Landscape_2);

            /* Go to layer 1 */

            TM_ILI9341_SetLayer1();

            /* Fill screen on layer 1 */

            TM_ILI9341_Fill(ILI9341_COLOR_YELLOW);

            /* Show text */

            while (1)

            {

            tensao = TM_ADC_Read(ADC1, ADC_Channel_0);

            sprintf(texto2, “Tensao = %f V”, tensao);

            TM_ILI9341_Puts(10, 30, texto, &TM_Font_11x18, ILI9341_COLOR_BLACK, ILI9341_COLOR_YELLOW);

            TM_ILI9341_Puts(10, 60, texto2, &TM_Font_11x18, ILI9341_COLOR_BLACK, ILI9341_COLOR_YELLOW);

            Delayms(1000);

            }

            }

          • Vinícius Jean Ferreira

            Ops, where I write “tension”, read “voltage”.

          • Yes, your problem is that you have to convert result to volts.
            Adc is 12bit,meaning that max number is 4095 and min is 0. Is you put 3.3V signal to pin, you will read 4095.

            To get volts, do floating calculation: (float)adc_result / (float)4095 * (float)3.3.

            Next time use pastebin.

          • Vinícius Jean Ferreira

            Very Thanks 😀
            But, one less question:
            If I want to change the scale of the ADC, for example, to use a scale from 0 to 1V, I can configure this?

          • VREF+ pin on STM is for this use.

  • Syrer

    Hello!
    As Vinícius Jean Ferreira
    asked. I am interfacing an analog sensor which gives me output from 0.2 to 4.7 V. thats pretty higher value than the 3V3. The good thing is that most of the ADC-Channels are 5V tolerant. But I am not pretty sure how to configure the ADC to scale REF from 0-3V3 to 0-5, if it possible for sure.
    any idea?

    thanx

    • As far I know, ADC can’t go up to 5V.
      You can do resistor divider to “split” it down your signal.

      • Syrer

        I see.. Well practically, the output voltage wont exceed 2V. Maybe i can just ignore values higher than 3V3 cuz they wont occur in my application 😀
        Thanx for the great and competent answers

  • Mengu Ucok

    Some AD channels in STM32F429 discovery board are broken, acting weirdly not showing anything (reads 1-10) around 0-2.5V. ADC reading increase very rapidly after 2.5V but didn’t reach to maximum(4095) stay around 3000. I have seen this behaviour in PA2 (ADC channel 2). What can be the reason? I never applied anything over than 3V3 to inputs.

    • Yeah, on this board is connected a lot of stuff to pins.
      You should check board schematics to see what could be a reason 🙂

      • Mengu Ucok

        Thank you 🙂

  • bao.lam

    Hi pro,
    I try to read ADC from PA1 in single mode, but values always 0 (for Vin<VDD) or 1 (0xfff) (for Vin=VDD).
    Have you ever met that issuse before?
    I already set GPIO for PA1 in ANALOG mode.
    Any suggestion?
    sorry for my poor English, it's not my first tongue! Thanks!

  • Guest

    Hi pro,

    I try to read ADC from PA1 in single mode, but values always 0 (for Vin<VDD) or 1 (0xfff) (for Vin=VDD)

    Have you ever met that issuse before?

    I already set GPIO for PA1 in ANALOG mode.

    Any suggestion?

    sorry for my poor English, it's not my first tongue! Thanks!

  • carlos

    I have made an example of using the adc with ewin, I took the example PDF ewin and have placed a marker

    /**
    * Keil project example for emWin
    *
    * Before you start, select your target, on the right of the “Load” button
    *
    * @author Tilen Majerle
    * @email tilen@majerle.eu
    * @website http://stm32f4-discovery.com
    * @ide Keil uVision 5
    * @packs STM32F4xx Keil packs version 2.2.0 or greater required
    * @stdperiph STM32F4xx Standard peripheral drivers version 1.4.0 or greater required
    */
    /* Include core modules */
    #include “stm32f4xx.h”
    /* Include my libraries here */
    #include “defines.h”
    #include “tm_stm32f4_delay.h”
    #include “tm_stm32f4_disco.h”
    #include “tm_stm32f4_emwin.h”
    #include
    #include “tm_stm32f4_delay.h”
    #include “tm_stm32f4_adc.h”

    /* GUI modules */
    /*******************************************************************/
    #include “CALENDAR.h”
    #include “CHECKBOX.h”
    #include “CHOOSECOLOR.h”
    #include “CHOOSEFILE.h”
    #include “DIALOG.h”
    #include “DROPDOWN.h”
    #include “EDIT.h”
    #include “FRAMEWIN.h”
    #include “Global.h”
    #include “GRAPH.h”
    #include “GUI.h”
    #include “GUI_ARRAY.h”
    #include “GUI_Debug.h”
    #include “GUI_FontIntern.h”
    #include “GUI_HOOK.h”
    #include “GUI_SetOrientation.h”
    #include “GUI_Type.h”
    #include “GUI_Version.h”
    #include “GUI_VNC.h”
    #include “IMAGE.h”
    #include “LISTBOX.h”
    #include “LISTVIEW.h”
    #include “LISTWHEEL.h”
    #include “MENU.h”
    #include “MESSAGEBOX.h”
    #include “MULTIEDIT.h”
    #include “MULTIPAGE.h”
    #include “PROGBAR.h”
    #include “RADIO.h”
    #include “SCROLLBAR.h”
    #include “SLIDER.h”
    #include “SPINBOX.h”
    #include “TEXT.h”
    #include “TREEVIEW.h”
    #include “WIDGET.h”
    #include “WM.h”
    #include “WM_GUI.h”
    #include “WM_Intern.h”
    /*******************************************************************/

    #define ARM_MATH_CM4
    #include “arm_math.h”
    /*******************************************************************/
    /*******************************************************************/

    void DrawArcScale(void);
    void Drawlinea(int i);

    /*****************************************************************/
    /*******************************************************************/

    int main(void) {

    char ac[4] ,leidostring[4];
    int x0 = 160;
    int y0 = 180;
    int i,resta;
    int leido,leido_paso ;

    /* Initialize system */
    SystemInit();

    /* Initialize delay functions */
    TM_DELAY_Init();

    /* Initialize ADC1 on channel 0, this is pin PA0 */
    TM_ADC_Init(ADC1, ADC_Channel_0);

    /* Initialize LEDs */
    TM_DISCO_LedInit();

    /* Initialize emWin */
    if (TM_EMWIN_Init() != TM_EMWIN_Result_Ok) {
    /* Initialization error */
    while (1) {
    /* Toggle RED led */
    TM_DISCO_LedToggle(LED_RED);

    /* Delay */
    Delayms(100);
    }
    }

    /*******************************************************************/
    TM_EMWIN_Rotate(TM_EMWIN_Rotate_90);
    DrawArcScale();
    GUI_SetFont(&GUI_Font32_1);

    while (1)
    {

    GUI_DrawCircle(160, 200, 5);
    leido = TM_ADC_Read(ADC1, ADC_Channel_0);
    sprintf(leidostring, “%d”, leido);
    GUI_SetColor( GUI_BLUE );
    GUI_DispStringHCenterAt(leidostring,160,130);

    if (leido_paso != leido)
    {

    sprintf(leidostring, “%d”, leido_paso);
    GUI_SetColor( GUI_BLUE );
    GUI_DispStringHCenterAt(leidostring,160,130);

    GUI_SetColor( GUI_BLUE );
    Drawlinea(leido );

    if (leido_paso > leido)
    {
    resta = leido_paso – leido ;

    for (i=0; i<= resta; i++)
    {
    GUI_SetColor(GUI_RED );
    Drawlinea(leido_paso -i);
    sprintf(leidostring, "%d", leido_paso – i);
    GUI_DispStringHCenterAt(leidostring,160,130);

    Delayms(50);

    GUI_SetColor( GUI_BLUE );
    sprintf(leidostring, "%d", leido_paso – i);
    GUI_DispStringHCenterAt(leidostring,160,130);
    Drawlinea(leido_paso – i);
    }
    }

    if (leido_paso < leido)

    {
    resta = leido – leido_paso ;

    for (i=0; i<= resta; i++)
    {

    GUI_SetColor(GUI_RED );
    Drawlinea(leido_paso + i);
    sprintf(leidostring, "%d", leido_paso + i);
    GUI_DispStringHCenterAt(leidostring,160,130);

    Delayms(50);

    GUI_SetColor( GUI_BLUE );
    sprintf(leidostring, "%d", leido_paso + i);
    GUI_DispStringHCenterAt(leidostring,160,130);
    Drawlinea(leido_paso + i);
    }
    }

    }

    GUI_SetColor( GUI_RED );
    Drawlinea(leido);

    sprintf(leidostring, "%d", leido);
    GUI_DispStringHCenterAt(leidostring,160,130);

    leido_paso = leido;
    Delayms(400);

    } /* fin while */

    } /* fin main */

    /*******************************************************************/
    /*******************************************************************/
    void DrawArcScale(void) {
    int x0 = 160;
    int y0 = 180;
    int i;
    char ac[4];

    GUI_SetBkColor(GUI_BLUE);
    GUI_Clear();
    GUI_SetPenSize( 5 );
    GUI_SetTextMode(GUI_TM_TRANS);
    GUI_SetFont(&GUI_FontComic18B_ASCII);
    GUI_SetColor( GUI_BLACK );
    GUI_DrawArc( x0,y0,150, 150,-30, 210 );
    GUI_Delay(1000);

    for (i=0; i<= 23; i++)
    {
    float a = (-30+i*10)*3.1415926/180;
    int x = -141*cos(a)+x0;
    int y = -141*sin(a)+y0;
    if (i%2 == 0)
    GUI_SetPenSize( 5 );
    else
    GUI_SetPenSize( 4 );
    GUI_DrawPoint(x,y);
    if (i%1 == 0)
    {
    x = -123*cos(a)+x0;
    y = -130*sin(a)+y0;
    sprintf(ac, "%d", i);
    GUI_SetTextAlign(GUI_TA_VCENTER);
    GUI_DispStringHCenterAt(ac,x,y);
    GUI_DrawCircle(160, 200, 5);
    }
    }
    }

    void Drawlinea(int i) {
    int x0 = 160;
    int y0 = 180;

    float a = (-30+i*10)*3.1415926/180;
    int x = -110*cos(a)+x0;
    int y = -110*sin(a)+y0;
    GUI_DrawLine(160, 200, x, y);
    }
    /*******************************************************************/

  • Ednucatio Matters

    Hi, Thanks for this lib…
    My Question is:

    I’ve used 3 ADC Channels namely PA0,PA1 and PA2.. of STM32F429I Discovery board.
    When I apply sine wave to the PA0 pin, it’s allright with it.
    But when I apply sine wave to PA1 and PA2, the amplitude of wave decreases.
    (I observe this facts by connecting PA0,PA1 and PA2 to Digital Storage Oscilloscope)

    What could be the reason?
    Then I tried with PC0,PC1 and PC2..
    For PC1 it’s working good but not for PC0 and PC2..

    What could be the problem?
    Thanks 🙂

    • The problem is probably with input impedance.
      Try with external pul-down resistor 😀

      • Ednucatio Matters

        It didn’t work! Tried connecting 15K,100K,470K, 1MOhm….

  • mdk

    How much maximum voltage can measure by ADC in stm32f4 without voltage divider? 3.3V?

    • Hello,

      in bottom image is print screen from datasheet.
      There is also input range value.

  • Lilangani

    Hai all, Thank you very much for sharing your knowledge. I am new to STM32F4. I want to monitor a sine wave using this. Is it possible. The magnitude of this wave varying from 50V to 60V, and frequency=50Hz . I hope to use a signal conditioning circuit to attenuate this voltage into STM32F4 chip measurable range.

    • You will need voltage divider and you will need to make offset to your voltage.
      Then you will be able to measure it.

  • hosweetim

    hi!, may I know how can I configure the ADC sampling rate? i would like to sample the ADC at 1kHz?

    • You need timer for 1kHz trigger, then interrupt/DMA for data transfer to our memory.
      This library does not support that.

      • samir revelo

        Hello Tilen,

        I´m working with your ADC Library but I´m wondering what kind of AD Conversion are you doing with the function “TM_ADC_Read”. I have seen that each time you call this function you are configuring the sample time of the desired ADC and starting again a conversion with “HAL_ADC_Start”. Why are you doing that again and again? Doesn´t it suffice to configure and call in “TM_ADC_Read” the function “HAL_ADC_Start” just once?

        What happens after calling once “TM_ADC_Read” ? Does the ADC continue sampling or does it stops until this function is called again? What would happen if I call for example “HAL_ADC_GetValue” after calling JUST ONCE function “TM_ADC_Read”. Would the ADC continue giving me a value or not?

        I was trying to configure the two ADC, i.e. ADC1 and ADC2, by myself to function with one and the same timer interrupt but it didn´t function. That´s because I´m trying to use your library now since it is easy to configure the ADC and its different channels.

        Thanks in advance.

        Alexis

  • Hai Anh Le

    hi! I’m trying to convert the signal from lm35 temperatre sensor but somehow the result isn’t correct, it’s just change from 0 oC -> more than 100oC
    Here is how i config the adc and calculate the temperature http://pastebin.com/GE1NaW0x

    • I think that your conversion from ADC result to degrees is not correct.
      How you read ADC values?

  • samit

    Hello,
    I am not getting what is the role of USART. I am reading the analog value from any sensor and displaying it on the LCD… Is it happening so?

    • I don’t understand your point here.

      • samit

        In AD converter on STM32F4, what is actually happening in example 1 can you explain it briefly? What is the role of using USART initialization?

        • It sends data to computer.
          If you have USB->UART converter you can see converted data on your terminal.

          • samit

            ok. thank you 🙂

          • samit

            In the given example 1, what value are we sending to computer?

  • vedro

    Hello Tilen,
    great job on the whole stm32 f4 area!

    A question regarding the ADC library… It it possible to sample with it at the highest speed that the uC allows it (2,4 msps), with your library? I mean this on one PIN (Channel). Possibly to start the conversion on a external interrupt and stop it after 300ms?

    • Hi,

      You need DMA for your speed.
      You can start dma on irq and after 300ms, disable dma.
      To count 300ms,you can use timer which will do irq for you where you stop dma for adc.

      • vedro

        Hello Tilen,

        ok, can you please give me a bit more advice on that matter… The whole DMA, starting dma on irq,..its a new filed for me, pointing to a good explanation (tutorial) on the web would be appreciated…
        Your library does not support DMA, correct?

  • Pingback: STM32F429 Discovery ADC Uygulaması – roboturka.com |()

  • marie

    Hey Tiljen, I have a probably quite stupid question, but I am pretty new to microcontrollers and electronics. I have a potentiometer that I want to connect to my STM32F4Discovery and read through ADC. What I understand is that the pins are internally connected to VDD, so at first I thought that I had to connect the not-pin-side of my potentiometer to GND. However, everywhere I read about Vref, so I do not really understand which is the point and sense of this. How do I need to connect my potentiometer at the end? It would be great if you could help me!
    Thanks a lot for your tutorials by the way, they helped me a lot during the past weeks!

    • Hi,

      one side to VCC (3V), second side to GND, middle to input pin.

      • marie

        Thanks a lot, it´s working!

  • Shagas Heizenberg

    Works straight out of the box

  • Name

    HI

    I would like to sample a fast signal. The values are between 0,5V and 2,5 V. The frequency of the signal is max 500 Hz. Do I have to change anything on this library to measure this(prescaler, clock cycles between read, sampling time…)? Or can I measure this signal with the setttings of this library?

    • I suggest you implement ADC with DMA and timer interrupt to do your job.
      Buy anyway, you can easily use this library for that purpose. Without any problem.

  • Kyaw Kyaw Aung

    Hi TM.
    I’m trying to use your ADC library with DMA. If my peripheral pin is PA4 (ADC1, channel 4), what should my peripheral base address for DMA_InitStructure be? I’m trying to use DMA2_Stream4 to store my 16bit ADC values to memory. Thanks a million.

  • Farhan Musthafa

    How may I configure the TIM for specific PINS. For instance, if I want to configure ADC for the pin PA6 and PA7. Which TIM has these PINS for ADC, Could you please help in that?

    • There is no pin and timer relation. You need a timerto trigger adc to start measure. Which pins will be used are set using adc channel selected.

      Then here is dma to transfer result to memory.

  • Kyaw Kyaw Aung

    Hi, TM. Referring the picture attached, my STM32F4 supplies 3V to a microphone+op-amp circuit (with a virtual ground different from STM’s ground), how do I make sure my ADC pin is receiving correct values? Do I need to make use of Vref?

  • FELIX

    Hi Tilen,

    After a couple of weeks, back to see my STM32F4. I like to program for STM32 device because, I could find lot of similar works on internet easily. By the way, your all library is very useful. Thanks for that and appreciate your hard work to develop and share with people.

    So, this time I am playing with ADC and learning it. I have a simple question, as I see from the example you used the command sprintf (…); . Could you please explain me why it is much necessary to use here?

    Usually, sprintf in C does print the string value out to the output. but, I could not clear what it does with MCU (STM32)? If we use this command, what is the process done by MCU?

    Thanks for your response.

    • Hi Fahrad,

      Thisis definetelly not true. You are talkingabout printf, not sprintf. Sprintf stores datainto memory, does not output to stream directly.

      • FELIX

        Thanks.

        Now I have the errors on the ADC initialization line. I hope you can help me fix this. Please refer the attached image.

        Thanks.

  • FELIX

    OKAY, NOW THE ERROR is FIXED. After downloading the ADC code to MCU. The ADC value is crap. it keeps on running even no Analog voltage on pin. Is there something wrong this library with my board??

    • I doubt lib is a problem. I would say lib works ok. At least till today it always worked ok.

      • FELIX

        I tested with the file “stm32fxx_adc.h” and “stm32fxx_adc.c” and written my own configuration code without using your library, the ADC value was worked fine. Though, I feel more comfortable to use your library as I am using them for other peripherals. Only the ADC I face problem that as I mentioned above the value is running from 0 to some values and some pins stays at 3.3V.

        Also, I tested with fixed analog voltage it still the same the value is varying in the loop.

  • Joshua Simamora

    Hay Tillen just to make sure, Does the adc port on stm32f407 have maximum voltage to 3.3v? And if i have a 5 v sensor it won’t work very well?? thanks before

    • True.

      • Joshua Simamora

        Thanks Tilen for your help. Keep up the good works.

  • FELIX

    HI,

    As far as successfully running the ADC. But now I am having an issue which is related to KEIL debugger and watch window. I beleive If I post here anyone can help me. Actually, I wanted to read the real time data recorded from ADC and read from the variable that I store the adc value. but the KEIL debugger “not in scope” or “cannot evaluate” sometime.

    and then I checked simply assigned some integer value in the variable and tried to view the local variable value. I still have the same issue. Could anyone please help to solve this problem? (Attached image)…Thank you

    • You have 2 options here:
      1. You have optimization set to “Default”, which is not “MODE 0” and variables are removed to be able to be seen on debugger if they are not global.
      2. You are trying to watch local variable when you are not in local scope of this variable.

      • FELIX

        what do you mean by “variables are removed” ?

        in my case,

        while()
        {
        data = Get_Adc_Average(ADC1,ADC_Channel_5,20);
        }

        so the variable data records the value at adc pin PA5 (channel 5)

        If I want to see the value through debugger, then I trace the variable “data” .. if I remove it, How cud I trace the ADC at PA5?

        • Debugger removes variable to be view-able in debug mode.

  • Skandinaff

    Hi Tilen,

    First of all, great work you’ve pulled here, your guides are awesome.

    I’m sorry if the following is extremely dumb, but I’ve just started to work with stm32.
    Now, I have an issue with using ADC library. I am trying to get ADC to sample each 2mS, that is 500Hz signal, and so if I read the ADC in interrupt routine the whole thing just doesn’t work.
    What is the proper way how to set up ADC with interrupts?

    Best regards,
    AS

    • They best way to do this would be to have timer to trigger ADC sampling and then 2 options:
      1. interrupt when ADC is done or
      2. use DMA to transfer from ADC to memory and later read memory when DMA finishes with transactions.

      • Skandinaff

        Okay, so if I go with a first option, how do I get an interrupt when ADC is done? And if I will, how can I control time intervals that ADC is sampling?
        Cheers

        • Enable ADC interrupt flag in ADC peripheral and in NVIC (check ADC example in HAL package).

          Use timer to control ADC samples.

          • Skandinaff

            Oh, okay, seems reasonable. I started my current project, based on your FFT example, and as I understand, there isn’t HAL libraries included. I am trying to add them to the project, but each time I rebuild all target files, I get a million of errors..

          • Sure you can. Fft in general is arm specific, not stm.

          • Skandinaff

            Oh, okay, but I am getting errors of “incompatible redefinition of macro … (declared at line … STANDRARD_PERIPHERAL_DRIVERS)”

            So, can I use both Std Periph drivers and HAL drivers?

          • You cam use fft with both but you cant use bith at the same time.

  • Entropy

    Hi again Tilen, i am also getting started with STM32F4 to be specific the 407VG one, one thing i have been seeing in few forums and codes from all over however, is that i don’t seem to get how the peripherals get connected to the GPIO, i mean for example for peripherals like SPI or USARTs it’s straightforwards to me as for example “GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1);” specificies that a pin6 of a typical GPIO port in this example being port B, will be used for this USART, however i don’t see this in ADC peripheral, how do we remap the pins to the ADC? thanks

    • Entropy

      being said that unless any pin that is configured as “AN” for analog in automatically becomes the ADC one then, am i correct? thanks

    • You don’t have remapping for ADC pins, because they are specific for each ADCx and each ADC Channel.
      All you have to do is to identify pin for your ADC and channel and set that pin as analog. That’s all.
      Pins are described in datasheet for specific MCU.

      • Entropy

        Thanks for the explanation

      • Entropy

        One last question regarding ADC on this particular topic, the both the RM0090 Reference manual and the data sheet don’t seem to comment further on the ADC channels unless i have to dig deeper, doing some googling i came across the ADC1 channels which are as follow:

        Channel — Port

        ——- —–

        ADC1_IN0 PA0

        ADC1_IN1 PA1

        ADC1_IN2 PA2

        ADC1_IN3 PA3

        ADC1_IN4 PA4

        ADC1_IN5 PA5

        ADC1_IN6 PA6

        ADC1_IN7 PA7

        ADC1_IN8 PB0

        ADC1_IN9 PB1

        ADC1_IN10 PC0

        ADC1_IN11 PC1

        ADC1_IN12 PC2

        ADC1_IN13 PC3

        ADC1_IN14 PC4

        ADC1_IN15 PC5

        Could you provide a page in the reference manual or anywhere else just to get an insight and more about the channels particularly for rest of ADC2 and ADC3? Thanks again.

        Regards

  • Skandinaff

    Hi Tilen!

    I got a weird glitch using the ADC library. If I initialize both ADC1 on channel 3 and ADC3 on channel 11, when I get to the point where I TM_ADC_Read(ADC3, ADC_Channel_11); I get stuck in a loop

    while (ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC) == RESET);

    so I never get to the point of

    return ADC_GetConversionValue(ADCx);

    Any ideas why it could be so? With other combinations, like ADC1 Chn 3 and ADC3 Chn 6 everything works.

    Best regards,
    Alex

  • James

    Hi Tilen, the following is my ADC configuration function:
    void ADC_Configuration(){
    ADC_InitTypeDef ADC_InitStructure;

    ADC_StructInit(&ADC_InitStructure);

    ADC_DeInit();
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_6b;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //continuous conversion
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;
    ADC_InitStructure.ADC_NbrOfConversion = 1;
    ADC_InitStructure.ADC_ScanConvMode =DISABLE; // 1=scan more that one channel in group
    ADC_Init(ADC1,&ADC_InitStructure);

    ADC_RegularChannelConfig(ADC1,ADC_Channel_13,1,ADC_SampleTime_480Cycles);

    ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);

    ADC_DMACmd(ADC1, ENABLE); //Enable ADC1 DMA

    ADC_Cmd(ADC1, ENABLE); // Enable ADC1

    ADC_SoftwareStartConv(ADC1); // Start ADC1 conversion
    }

    if I change the ADC_Sample_Time to anything less than 480 cycles, the ADC does not work. But it does work on 480. Any reason for this?

  • jerry

    Tilen, I am using your DAC code to generate signals using DMA. I now need to read those signals back at the end of each wave using analog sensing hardware. Basically I generate a signal and need to read the results back point by point during the generation. Can you give me some hints as to how to accomplish this? I guess I won’t be able to use DMA on the generation if I have to read the points immediately after, correct? The code running on an 8051 generates a point and then reads it back in a timer interrupt to keep the signal frequency constant.

    Thank you