Here is a bit of a look at how to use the ADC on the STM32 for simple applications. The ADC peripheral on the STM32 processor is a flexible but complex beast. The peripheral driver library should make it relatively easy to use. After all, there is no need to remember the names of all those pesky registers and bitfields. There are anything up to 18 multiplexed channels that can be converted singly, continually, scanned or discontinuously. The results can be transferred to memory through DMA and some devices have two or more ADCs for simultaneous conversions. With all those options, simple reading of an ADC port is a little tricky…

The first thing to note is that, on the 64 pin packages, Vref is not connected to a package pin but is connected internally to Vdda the analogue supply voltage. If you have one of the larger, 100 pin devices, Vref is tied to a pin.

My test setup is the ETT STM32 Stamp which has an 8MHz external crystal and runs the PLL to give a system clock of 72MHz.

The clock for the ADC is provided by a prescaler fed from PCLK2, the APB2 clock. By default, in the peripheral library, this is the same speed as the system clock so, on my board, that is 72MHz. According to the datasheet, the ADC clock should be in the range 600kHz to 14MHz so I will need to set a prescaler of 6 to bring the ADC clock within that range from the 72MHz system clock. the function that sets this is part of the RCC peripheral code (RCC_ADCCLKConfig()) rather than the ADC driver. The available divisors are 2, 4, 6 and 8 so that should present no problems. Running the ADC overspeed is possible but I would expect accuracy to suffer. Since the fastest conversion takes 12.5+1.5 = 14 cycles, I would expect to get about 857,000 conversions per second on my board. The maximum rate within the given limits is 1 million samples per second.

The master ADC (ADC1) has two additional channels. These are connected to the internal temperature sensor (ADC1_IN16) and the internal reference voltage (ADC1_IN17). the temperature sensor is said to be reliable in terms of the slope of its response but with an offset than can vary my as much as 40 degrees from chip to chip. For that reason, it is said to be better for measuring temperature change than absolute temperature. The internal voltage reference is not used by the ADC but can be used as a comparator for zero crossing detection. It may also be possible to use it to calibrate external readings or the Vref+ pin.

Once configured, the ADC can be turned on by setting the ADON bit in CR2. After a stabilisation period, the peripheral is ready to begin conversions. From the datasheet for the medium density devices, the stabilisation period will not exceed 1us.

The ADC can self-calibrate to reduce errors due to internal capacitor variations. the reference manual suggests that the ADC be self calibrated once after power up. Before starting the self calibration, the ADC must have been in the power-down state (ADON=0) for at least two clock cycles. the end of calibration is indicated by the CAL flag going low.

The result of a conversion may be optionally aligned left or right in the 16 bit result register. Only 12 bits of the result are significant. In regular mode, the other bits are filled with zeros.

Here is the configuration code I have used:

void ADC_Configuration(void)
{
  ADC_InitTypeDef  ADC_InitStructure;
  /* PCLK2 is the APB2 clock */
  /* ADCCLK = PCLK2/6 = 72/6 = 12MHz*/
  RCC_ADCCLKConfig(RCC_PCLK2_Div6);

  /* Enable ADC1 clock so that we can talk to it */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
  /* Put everything back to power-on defaults */
  ADC_DeInit(ADC1);

  /* ADC1 Configuration ------------------------------------------------------*/
  /* ADC1 and ADC2 operate independently */
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  /* Disable the scan conversion so we do one at a time */
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  /* Don't do contimuous conversions - do them on demand */
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  /* Start conversin by software, not an external trigger */
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  /* Conversions are 12 bit - put them in the lower 12 bits of the result */
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  /* Say how many channels would be used by the sequencer */
  ADC_InitStructure.ADC_NbrOfChannel = 1;

  /* Now do the setup */
  ADC_Init(ADC1, &ADC_InitStructure);
  /* Enable ADC1 */
  ADC_Cmd(ADC1, ENABLE);

  /* Enable ADC1 reset calibaration register */
  ADC_ResetCalibration(ADC1);
  /* Check the end of ADC1 reset calibration register */
  while(ADC_GetResetCalibrationStatus(ADC1));
  /* Start ADC1 calibaration */
  ADC_StartCalibration(ADC1);
  /* Check the end of ADC1 calibration */
  while(ADC_GetCalibrationStatus(ADC1));
}

Single conversion mode is started in software by writing a 1 to the ADON bit in ADC_CR2 with the CONT bit set to zero. Note that the ADON bit will already be set to one when the ADC is powered up. Reading the ADON bit serves to tell the program that the ADC is powered up. To prevent false triggering, a conversion will not start if any bit other than ADON is changed at the same time as ADON.

Conversions take 12.5 clock cycles but the time the ADC spend sampling the input can be varied on a channel-by-channel basis from 1.5 to 239.5 cycles. This value is set in the ADC_SMPR1 and ADC_SMPR2 registers.

The end of conversion is signalled by the setting of the EOC flag, an interrupt is optionally generated and the result is placed in the ADC_DR register. The EOC flag should be reset by software before another conversion is started. Reading the result from ADC_DR clears the EOC flag automatically.

Here is the function that reads a single ADC channel:


u16 readADC1(u8 channel)
{
  ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_1Cycles5);
  // Start the conversion
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);
  // Wait until conversion completion
  while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
  // Get the conversion value
  return ADC_GetConversionValue(ADC1);
}

This function includes a call to configure the channel and sets the sample time to its minimum. It takes 3.5us to run on my board. That could be substantially reduced by using the more sophisticated conversion modes available and by using better code. The Channel config call alone takes 1.3us. Where high speed is essential, there is DMA and automatic sequencing available. However, if all you want to do is read a few ADC channels, the speed is pretty good. All 16 channels could be read in under 60us. Note that the sampling time may need adjusting according to the nature of the signal source. A good. low, impedance source will need a shorter sample time.

When I need the more clever methods of reading the ADC, I will look into them. In the meantime, this will do me very nicely.

This Post Has 96 Comments

  1. sf

    Thanks for all the info you are sharing about stm32.

    I just discovered this beast last week, I’m playing around with my first board…
    Being used to PIC, it’s an huge step and you are helping me a lot!

    Thanks,

    Simone

  2. Jiangong XU

    Since the end of conversion is signalled by the setting of the EOC flag, so ADC_FLAG_EOC must be eaqual to 1;why use this
    expression “while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);:”in my opinion that expression may be changed to “while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == SET);”my view may be wrong ,please correct me,thanks a lot.

    Jiangong XU

  3. peteh

    The flag is reset when the conversion is started and will become set at the end of the conversion so the code sits and waits for the flag to become set, indicating that we can read the result.

  4. Jiangong XU

    Thanks for peteh‘s help,I know the reasons,the code“while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);”is used to wait for the flag to become set,when the flag(the end of conversion)is set,the code ”return ADC_GetConversionValue(ADC1);”is executed 。when the flag(the end of conversion)is reset,the program is doing nothing,but waiting for
    the end of conversion。
    thanks for friend ’s help。

  5. Jayesh

    Thank you very much for giving such a nice information regarding ADC in STM32.I am working on that. For any further information regarding other peripheral in STM32 letme inform.

    Thank you very much.

  6. Jayesh

    I want to make PID controller using STM32. In this i set the set value by keypad and measure the actual value by ADC and give the corrected output on DAC. I need PID logic for the same. please help me.

    Thank you

  7. Manuel

    I would like to know how did you calculate the sample time of 3.5us, did you use timers to measure that value?. Please if you could help me doing that, i would be very grateful. I need to measure how much time does it takes to convert each channel.Thanks

  8. peteh

    I am afraid it was not calculated. I toggled an output pin before and after the function call and measured the time on an oscilloscope. The time taken is important to me because I have an application that needs to do 15 conversions in every 1ms control cycle.

  9. Manuel

    Hi, do you know how to use the end of conversion (EOC) interrupt?
    I need this to know where is the value of my last conversion. Thanks

  10. peteh

    So far, I have not used the ADC with interrupts – sorry.

  11. nader BOUKari

    can i use this programe ,?
    how i know the pin that iam getting the informationg from it ?
    think you for the programe i really like it ,its simple and efficacy

  12. Adam

    Hi, is there any reason why the function readADC1() starts with line: ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_1Cycles5); ? Wouldn’t it be better (faster) to have this line in the function ADC_Configuration()? I exptect there is a good reason why you do it like this but I can’t figure out why.

    BTW: Thanks a lot, I’ve been visiting this site for inspiration every now and then. I appreciate your work.

  13. Adam

    Hi, is there any reason why the function readADC1() starts with line: ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_1Cycles5); ? Wouldn’t it be better (faster) to have this line in the function ADC_Configuration()? I exptect there is a good reason why you do it like this but I can’t figure out why.

    BTW: Thanks a lot, I’ve been visiting this site for inspiration every now and then. I appreciate your work.

  14. peteh

    I probably copied the example from somewhere else to be honest. There seems no reason this should not go into the config section. Thanks for noticing.

    Unfortunately, I have been unable to spend much quality time with the STM32 of late. 

  15. jrive

    I’m confused as to the programability of what ST calls sampling time. Is this akin to track and hold? Why wouldn’t you always sample as fast as it can? Is this because depending on the source impedance, you may need to allow more time for the hold capacitor to charge up? lower source impedance, faster samping time possible?

    Thanks,
    Jorge

  16. Harjit Singh

    It is as you say – the sampling time needs to be varied to allow the sample/hold capacitor to charge up. They have an ADC app. note that walks through how to determine the sampling time.

  17. Johnson

    Would it be okay with you if I linked to this page from my website? Just asking since some people don’t allow linking to their sites if you don’t take their permission.

    Engineers who would like to have careers in management positions should seriously think about getting the PMP certification so that they can learn how to manage projects effectively. Getting the certification is a matter of passing the PMP exam which can be done with a bit of online PMP certification training.
  18. peteh

     Link away. Let me know the page you are linking from please.

  19. Llewellyn Remy

    Hi

    Im trying to get a very simple system up and running, just measure an analog voltage, then use the variable in some calculaiton and turn some LED’s on and of with the info. I tried to copy/paste the code, but the line “ADC_Init(ADC1, &ADC_InitStructure);” gives me errors, (amp undeclared and unexpected ; or something)

    I it possible to point me in the right direction? How would i use this to read a analog signal of PC4 and save it as a variable? I’m running a STM32Discover, and TrueSTUDIO lite
    Regards
    Llewellyn

  20. peteh

    Possibly the best way is to find one of the ADC examples that probably comes with Atollic. I have not used it so I cannot really say. You need to have al the correct header files included and the right variable defined.

    If you start with an existing ADC example and compare, then paste, you will stand a better chance.

     

  21. Llewellyn Remy

    Everything seems to be working(well, no errors or warnings. im just wondering, sorry if this is a silly question, but how would i call the function

    u16 readADC1(u8 channel)

    declare a int like
    u16 voltage1 = 0;

    the something like
    voltage1 = readADC();?

    Sorry if these are silly questions, before this ive only used the arduino, and that is substantially less comlpex than this 🙂

    tnx

  22. peteh

    All you should need to do is add the channel number to the function call. 

    u16 temperature;
    temperature = readADC(3);


  23. Llewellyn Remy

    Thanks. Since everywhere in the code i used ADC1, this would then be:

    u16 voltage;
    voltage= readADC(1); ?

    also, I cant see where the program sets the pin it reads from. would this part i set here set it to read at pin PC4?

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);

  24. Llewellyn Remy

    Im reading the voltage n with

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    // Wait until conversion completion
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
    // Get the conversion value
    voltage = ADC_GetConversionValue(ADC1);
    vleds = voltage/1024;
    displayleds(vleds);

    If the actual inpt is 3.2V, i read 3.0V, amd if i read actual 0V, it gives me 1V.

    Any suggestions?

  25. Anonymous

    Thank you for posting this info! Very helpful!

  26. Anonymous

    so just delete “amp;” and you’re good to go.

  27. Dennis

    Could you give me the code you wrote for your project?
    I would like to take a better look how you defined your functions etc. thats a bit mysterious to me for now.

    I’ll thank you very much for your effort to explain your project!
    It helps me alot!

    Greets,
    Dennis.

  28. peteh

    All there was beside what you see is a main function that read the ADC and put the answer in a variable. That was run with the debugger so that I could take one sample and examine the result.

  29. Tony

    Just got the code below going with the DMA, its really cool as the information from the DMA goes straight into the pre-defined variable “ADCConvertedValue[2]”. The code below just turns an LED on or off depending on the condition of the voltage read on channel 14. Note the comments in my code are just an easy way of me understanding (or trying to understand) what the micro is doing; I do not guarantee that what I say in my comments is right. The code however works 100% 🙂 The code is tailored to work on a ST Discovery board….

    /*
     *  Test code for the Discovery Board.  In this version we use the DMA to store the information
     *  of two converted channels (channel 14 and channel 15) into a pre-defined variable.  The method
     *  is continuous and the value of the ADC are continuously stored in the variable.
     */
    
    //==Includes==
    #include "stm32f10x.h"
    #include "STM32vldiscovery.h"
    
    
    //==Definitions==
    #define ADC1_DR_Address    ((uint32_t)0x4001244C)
    
    
    //==Global Variable declerations==
    uint16_t ADC_Val; //Stores the calculated ADC value
    double voltage1; //Used to store the actual voltage calculated by ADC for the 1st channel
    double voltage2; //Used to store the actual voltage calculated by ADC for the 2nd channel
    
    __IO uint16_t ADCConvertedValue[2]; //Array that is used to store the calculated DMA values for ADC1
    
    /*
     *  This function sets up the pins connected to the LED's as outputs; the blue LED
     *  is connected to pin 8 (Port C) and the green LED is connected to pin 9 (Port C).
     */
    void Configure_LED_Pins() {
    
       //==Configure the pins connected to the LED's to be outputs==
       //Blue LED is on pin 8 of Port C and Green LED is on pin 9 of Port C
    
       //Enable the clock for the port, by default this is off i.e. Enable GPIOC Clock
       RCC->APB2ENR |= RCC_APB2Periph_GPIOC; //APB2 indicates we dealing with the high speed bus
       //ENR means we want to enable the register
    
       //Specify the pins as either inputs or outputs
       GPIOC->CRL = 0x11111111; //This is definition for pins 0 - 7; each pin is configured with respect to CONTROL:MODE
       //All pins set to output mode (general purpose output push-pull)
       GPIOC->CRH = 0x44444433; //This is definition for pins 8 - 15; pin 8 and 9 set as output (50Mhz), general purpose output
    
       //Set all pins to 0V
       GPIOC->ODR = 0x0000; //The output port only uses the first 16 bits; the last 16 isn't used...
       //Here we are turning off all the pins...
    
    }//end Configure_LED_Pins
    
    /*
     *  Sets up Pin.C4 (Channel 14) and Pin.C5 (Channel 15) to be used as analog inputs.  The first channel
     *  of the DMA is also setup to be used with ADC1.  ADC1 is setup to continuously output data to the
     *  array "ADCConvertedValue"
     */
    void ADC_DMA_Configuration() {
       GPIO_InitTypeDef GPIO_InitStructure; //Variable used to setup the GPIO pins
       DMA_InitTypeDef DMA_InitStructure; //Variable used to setup the DMA
       ADC_InitTypeDef ADC_InitStructure; //Variable used to setup the ADC
    
       //==Configure the systems clocks for the ADC and DMA==
       //ADCCLK = PCLK2 / 4
       RCC_ADCCLKConfig(RCC_PCLK2_Div4); //Defines the ADC clock divider.  This clock is derived from the APB2 clock (PCLK2).  The
       //ADCs are clocked by the clock of the high speed domian (APB2) dibivied by 2/4/6/8.  The
       //frequency can never be bigger than 14MHz!!!!
    
       //--Enable DMA1 clock--
       RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
       //--Enable ADC1 and GPIOC--
       RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);
    
    
       //==Configure ADC pins (PC.04 -> Channel 14 and PC.05 -> Channel 15) as analog inputs==
       GPIO_StructInit(&GPIO_InitStructure); // Reset init structure, if not it can cause issues...
       GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
       GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
       GPIO_Init(GPIOC, &GPIO_InitStructure);
    
    
       //==Configure DMA1 - Channel1==
       DMA_DeInit(DMA1_Channel1); //Set DMA registers to default values
       DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address; //Address of peripheral the DMA must map to
       DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) & ADCConvertedValue; //Variable to which ADC values will be stored
       DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
       DMA_InitStructure.DMA_BufferSize = 2; //Buffer size (2 because we using two channels)
       DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
       DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
       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_M2M = DMA_M2M_Disable;
    
       DMA_Init(DMA1_Channel1, &DMA_InitStructure); //Initialise the DMA
       DMA_Cmd(DMA1_Channel1, ENABLE); //Enable the DMA1 - Channel1
    
    
       //==Configure ADC1 - Channel 14 and Channel 15==
       ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
       ADC_InitStructure.ADC_ScanConvMode = ENABLE;
       ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
       ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
       ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
       ADC_InitStructure.ADC_NbrOfChannel = 2; //We using two channels
    
       ADC_Init(ADC1, &ADC_InitStructure); //Initialise ADC1
    
       //Setup order in which the Channels are sampled....
       ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_55Cycles5);
       ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 2, ADC_SampleTime_55Cycles5);
    
       ADC_DMACmd(ADC1, ENABLE); //Enable ADC1 DMA
       ADC_Cmd(ADC1, ENABLE); //Enable ADC1
    
       //==Calibrate ADC1==
    
       //Enable ADC1 reset calibaration register
       ADC_ResetCalibration(ADC1);
       while (ADC_GetResetCalibrationStatus(ADC1)); //Check the end of ADC1 reset calibration register
    
       //Start ADC1 calibaration
       ADC_StartCalibration(ADC1);
       while (ADC_GetCalibrationStatus(ADC1)); //Check the end of ADC1 calibration
    
    }//end ADC_Configuration
    
    /*
     *  Simple program that reads our AD port, in this case pin 4 on port C.  The following program does the following:
     *  The converted voltage is stored in the global variable 'voltage'; the following things are done when the voltage value is
     *  obtained:
     *  0.000 < = V  No LEDs on
     *  0.825 <= V  Green LED on; Blue LED off
     *  1.650 <= V  Blue LED on; Green LED off
     *  2.475 <= V  Green LED on; Blue LED on
     *
     *  Since the variable 'voltage' is a global variable, its value can be checked when debugging the code....
     */
    void ADC_DMA_Test_Program() {
       /* Start ADC1 Software Conversion */
       ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    
       while (1) {
          //==Get the ADC value of channel 14==
          ADC_Val = ADCConvertedValue[0];
          voltage1 = (2.984 * ADC_Val) / 4096;
          //==Get the ADC value of channel 15==
          ADC_Val = ADCConvertedValue[1];
          voltage2 = (2.984 * ADC_Val) / 4096;
    
          if ((voltage1 >= 0) && (voltage1 < 0.825)) {
             ODR = 0b0000000000000000; //Turn off both LED's
          } else if ((voltage1 >= 0.825) && (voltage1 < 1.65)) {
             ODR = 0b0000001000000000; //Turn on the green LED
          } else if ((voltage1 >= 1.65) && (voltage1 < 2.475)) {
             ODR = 0b0000000100000000; //Turn on the blue LED
          } else {//voltage1 >= 2.475 && voltage
             ODR = 0b0000001100000000; //Turn on both LED's
          }
       }//end while(1)
    }//end ADC_Test_Program
    
    /*
     * Main program start
     */
    int main(void) {
       //**Micro clock settings**
       //Done by default from startup_stm32f10x_xx.s before coming to main!  You can edit SystemInit() in system_stm32f10x.c
       Configure_LED_Pins();
    
       ADC_DMA_Configuration();
       ADC_DMA_Test_Program();
    
    
       return 0;
    }//end main
    
  30. Pawan

    Hi,I need to ask that…..i have to read the adc values continuously….suppose i have to blink an led on 4 converted values……i am enable to do this because i have to reset it everytime for the new conversion…..i want it continuously without resetting it…..m doing it an continuous mode bt still nt getting it…..Hw cn i get it…??

  31. Dennis

    Hey,
    greak work! Thanks a lot for the nice information about the STM32 ADC. Your really helped me 😉

  32. Ali

    Thank you very much for your Tutorial. It is really a good start for ST32 Controllers!

  33. may

    Can u tell how did u toggled an output pin?
    Can you share the code?

  34. may

    Ok. thanks for help.

    I need to read ADc value for every 10micro sec.
    Is there any time or .. with which i can schedule it.

  35. may

    i would also like to know how did u give input to microcontroller..??

  36. Dennis Greguhn

    Thank you for this great tutorial about the ADC’s for a STM32!
    I’m a beginner with the STM32 processors and now I’m a little bit better 😉

    Thak you!
    Dennis

  37. D

    I’ve managed to get Tony’s code working (thank you very much!). Just wondering how you can configure this so the ADC value is passed out via a DAC?

    Cheers!

  38. Gene Osborne

    I am having trouble compiling the example on 32stmvldiscovery.
    it is giving me an error when it reaches ODR = 00000000000000000 cannot find a reference to the term ODR.
    if I remove the lines referencing ODR it appears to work in the debugger.

    Thanks in advance for any support
    Gene Osborne

  39. Peter Harrison

    In case the author of the comment does not notice, he seems just to be turning on some LEDs and has defined ODR elsewhere. Just use whatever code is appropriate for your platform for light ing up indicators or whatever.

  40. anonymous

    I tried to use the code as presented to digitise (using ADC1) the x and z axis output of an accelerometer. For some reason I am getting the same value for both channels. Has anyone noticed this when using the ACD1 to sample two different analogue signals one after the other?

  41. Peter Harrison

    Can you post a code fragment showing how you are doing the conversions?

  42. anonymous

    I am using the stm32F4 discovery with freertos. The code is as follows the functions to initiate and use the ADC are as follows, they are essentially the same as presented above

    void ADC_IO_Init(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin) {
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
      GPIO_Init(GPIOx, &GPIO_InitStructure);
    }
    
    void ADC_Enable(ADC_TypeDef* ADCx) {
      ADC_InitTypeDef ADC_InitStructure;
      ADC_CommonInitTypeDef ADC_CommonInitStructure;
    
      ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
      ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4;
      ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
      ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
      ADC_CommonInit(&ADC_CommonInitStructure);
    
      ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
      ADC_InitStructure.ADC_ScanConvMode = DISABLE;
      ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
      ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
      ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
      ADC_InitStructure.ADC_NbrOfConversion = 1;
    
      ADC_Init(ADCx, &ADC_InitStructure);
    
      /* Enable the specified ADC*/
      ADC_Cmd(ADCx, ENABLE);
    }
    
    u16 readADC(u8 channel, ADC_TypeDef* ADCx, uint8_t SampleTime) {
      ADC_RegularChannelConfig(ADCx, channel, 1, SampleTime);
      // Start the conversion
      ADC_SoftwareStartConv(ADCx);
      // Wait until conversion completion
      while (ADC_GetFlagStatus(ADCx, ADC_FLAG_EOC) == RESET);
      // Get the conversion value
      return (ADC_GetConversionValue(ADCx));
    }
    
    void ADCPeripheralInit() {
      ADC_Enable(ADC1); //Enable ADC1 to do the conversion
      ADC_IO_Init(GPIOA, GPIO_Pin_1); //PA1 connects to Accelerometer Z-axis
      ADC_IO_Init(GPIOA, GPIO_Pin_3); //PA2 connects to Accelerometer X-axis
    }
    
    The relevant tasks are listed below.
    int main(void) {
    
      HwInit();
    
      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); //enable clock for GPIOA
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //enable clock for ADC1
    
      ADCPeripheralInit(); //initialise the ADC peripheral
    
      xTaskCreate(tskSensor_Read, (signed char *) “Sensor”, configMINIMAL_STACK_SIZE,
        NULL, Sensor_Read_TASK_PRIORITY, &hSensor_Read);
    
      vTaskStartScheduler(); // This should never return.
    
      // Will only get here if there was insufficient memory to create
      // the idle task.
      for (;;);
    }
    
    portTASK_FUNCTION(tskSensor_Read, pvParameters) {
      portTickType xLastWakeTime;
      SWV_puts(“starting task tskSensor_Read\n”);
      xLastWakeTime = xTaskGetTickCount();
    
      uint16_t x_raw = 0;
      uint16_t z_raw = 0;
    
      for (;;) {
        z_raw = readADC(3, ADC1, ADC_SampleTime_56Cycles);
        x_raw = readADC(1, ADC1, ADC_SampleTime_56Cycles);
    
        SWV_puts(“\nx_raw is “);
        SWV_printnum(x_raw);
        SWV_puts(“\nz_raw is “);
        SWV_printnum(z_raw);
    
        vTaskDelayUntil(&xLastWakeTime, (4000 / portTICK_RATE_MS));
      }
    }
    

    If you are curious SWV_puts() is very similar to printf, it uses the the debugger to print text to the pc screen. I would like to add that the code is behaving very strangely. For example if i call “z_raw = readADC(3, ADC1, ADC_SampleTime_56Cycles);” twice then everything seems to work ok. Alternatively putting a 1 second delay between reading the first and second pins seems to fix things as well. I have no idea what is happening here.

  43. Peter Harrison

    This looks pretty much as I would expect. The actual code I am using for a sample on a STM32F4 is this:

    #define MAX_TIMEOUT 1000000
    int16_t ADCReadChannel (ADC_TypeDef * ADCx, int channel)
    {
      uint32_t timeOut;
      int16_t result;
      timeOut = 0;
      // select the ADC and the channel
      ADC_RegularChannelConfig (ADCx, channel, 1, ADC_SampleTime_15Cycles);
      // That may be an unacknowledged conversion so clear that
      ADC_ClearFlag (ADCx, ADC_FLAG_EOC);
      ADC_SoftwareStartConv (ADCx);
      while (ADC_GetFlagStatus (ADCx, ADC_FLAG_EOC) == RESET) {
        timeOut++;
        if (timeOut > MAX_TIMEOUT) {
          break;
        }
      };
      // Get the conversion value
      if (timeOut > MAX_TIMEOUT) {
        result = -1;
      } else {
        result = ADC_GetConversionValue (ADCx);
      }
      return result;
    };

    The only substantial difference being that I have cleared any pending conversion results before starting a new conversion.

    Otherwise, I would wonder about using the code inside a real time operating system. I have no idea what goes on inside FreeRTOS but that would be another place to look. What happens if you just perform the conversions outside of a task?

  44. anonymous

    OMG your the man!!!
    ADC_ClearFlag (ADCx, ADC_FLAG_EOC);
    seems to have done the trick.

    By the way would you mind if i link to this page from my blog?

  45. Peter Harrison

    By all means. Link away.

  46. Henry

    I dont like the STM Library-functions. To understand what is going on in the STM its better to read the reference and learn the registers and bits:

    This is an example how to initialize the ADC Word by Word as it is used to be with well known PICs, AVRs and so on:

    Unfortunately the stm32f10x.h is not fully implemented to controll all registers directly: to use DMA-Controll the DMA_TypeDef in the stm32f10x.h has to be repaired/added:

    typedef struct {
      __IO uint32_t ISR;
      __IO uint32_t IFCR;
      //self added, selbst hinzugefügt:
      __IO uint32_t CCR1;
      __IO uint32_t CMAR1;
      __IO uint32_t CPAR1;
      __IO uint32_t CNDTR1;
    } DMA_TypeDef;
    
    ADC1 Initialize - routine example by STM - Core - Adresses :
    
    __INLINE static void ADC1_Config_free(void) {
      //ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; 0B0000: Independent mode
      ADC1->CR1 = 0x00000000;
      //ADC_InitStructure.ADC_ScanConvMode = Disable;
      ADC1->CR1 |= (0 < < 8); // of course (0SQR3 = 0x00000008; //Kanal 8 als 1 Conversion
      ADC1->SMPR2 |= (5 < CR2 |= (0 < CR2 |= (1 < CR2 |= (1 < CR2)& (1 < 0);
      // Start ADC1 calibration
      //ADC_StartCalibration(ADC1);
      ADC1->CR2 |= (1 < CR2)& (1 < 0);
      //ADC_SoftwareStartConvCmd(ADC1, ENABLE);
      ADC1->CR2 |= (1 < CR2 |= (1 << 22); //SW-Start
    }
    
  47. Henry

    The comment editor is totaly crap . The code is deleted randomly between the characters. I give up.

  48. Peter Harrison

    It is.

    Generally, all you need to do is put tags around your code fragment as described in the note at the bottom of the editor window. Nonetheless, it is not much fun.

    However, I am interested in your contribution so, if you send me the listing that you want to see, I will edit it for you.

    I had a go at your original comment. Is that what you had in mind?

  49. Henry

    ok, last try:

    typedef struct { __IO uint32_t ISR; __IO uint32_t IFCR; //self added, selbst hinzugefügt: __IO uint32_t CCR1; __IO uint32_t CMAR1; __IO uint32_t CPAR1; __IO uint32_t CNDTR1; } DMA_TypeDef; //This is the ADC-init-function. i tried to translate the StdPeriph_Lib-functions to the STM32-register-calls: __INLINE static void ADC1_Config_free(void) { //ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; 0B0000: Independent mode ADC1->CR1 = 0x00000000 ; //ADC_InitStructure.ADC_ScanConvMode = Disable; ADC1->CR1 |= (0<CR2 |= (0<CR2 |= (1<CR2 |= (7<CR2 |= (0<CR1 |= (0<SQR1 = 0x00100000; ADC1->SQR3 = 0x00000008; //Kanal 8 als 1 Conversion ADC1->SMPR2 |= (5<CR2 |= (0<CR2 |= (1<CR2 |= (1<CR2)& (1<0); // Start ADC1 calibration //ADC_StartCalibration(ADC1); ADC1->CR2 |= (1<CR2)& (1<0); //ADC_SoftwareStartConvCmd(ADC1, ENABLE); ADC1->CR2 |= (1<CR2 |= (1<<22); //SW-Start }

  50. Henry

    still doesnt work even with code tags.

  51. radamta

    thanks you every body. i very happy when i reapd this topic. it help me understand about adc for Stm32.

  52. SAMEER

    for the first program what are the header files …i m biginner so pls help me…and i am not able to compile my program in kiel arm7

  53. Entropy

    I don’t care if the original post was 2009 and we are 2014 but i have got to say Thank you mate. i am pleased with your hard working and explanation you provided throughout this post. just out of curiosity would we really need to change much stuffs to get your simple ADC program to work on a newer processors such as stm32f407vg other than specifying some extraneous data regarding things like GPIO, Mode, PuPd and OType? Thanks

  54. Peter Harrison

    If you have a look back to a comment in July 2012, you should find an example for the STM32F4 processors. The main change is that you need to perform a common ADC configuration and then one specific for the ADC converter you are using.

  55. user'ssssss

    I tried to use the code as presented to digitise (using ADC1) the x and z axis output of an accelerometer. For some reason I am getting the same value for both channels. Has anyone noticed this when using the ACD1 to sample two different analogue signals one after the other?

  56. Peter Harrison

    I do this all the time.

    Make sure your sampling times are correct and double check the hardware.

  57. dirk

    I am using the stm32f303x
    These functions do not appear in the STM Peripheral Library pdf:
    ADC_ResetCalibration()
    ADC_GetResetCalibrationStatus()

  58. Peter Harrison

    I think they have been removed from the newer library version. You can leave them out as far as I know.

  59. Bui Pham Duc.

    Thanks for your useful post.

  60. Green Ye

    same as STM32F4 library, these functions are no longer exist. If you read the sample code for ADC you might figure out the difference between F103 and F3/F4

  61. Amna

    HI…I am using adc and then usart to send data to pc at timer interrupts.My code was working properly. checked it by sampling a signal from signal generator and displaying it on matlab GUI.But suddenly it stopped working.now it correctly samples DC signal but any varying signal is sampled by just continuous 0’s and 1’s.I checked everything independently. Timer and usart are working accurately. The problem is with ADC. can anyone help me to resolve this?

  62. Amna

    My code is

    
    #include "stm32f4xx_adc.h"
    #include "stm32f4xx_gpio.h"
    #include "stm32f4xx_rcc.h"
    #include
    #include
    #include
    ADC_CommonInitTypeDef ADC_CommonInitStructure;
    volatile unsigned int ConvertedValue = 0; //Converted value readed from ADC
    volatile unsigned int sample_data;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    
    //void INTTIM_Config(void);
    
    void INTTIM_Config (void)
    {
      NVIC_InitTypeDef NVIC_InitStructure;
      /* Enable the TIM2 gloabal Interrupt */
      NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_Init (&NVIC_InitStructure);
      /* TIM2 clock enable */
      RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM2, ENABLE);
      /* Time base configuration */
      RCC->CFGR |= 0X1400;
      TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 1 MHz down to 1 KHz (1 ms)
      TIM_TimeBaseStructure.TIM_Prescaler = 42 - 1; // 24 MHz Clock down to 1 MHz (adjust per your clock)
      TIM_TimeBaseStructure.TIM_ClockDivision = 0;
      TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
      TIM_TimeBaseInit (TIM2, &TIM_TimeBaseStructure);
      /* TIM IT enable */
      TIM_ITConfig (TIM2, TIM_IT_Update, ENABLE);
      /* TIM2 enable counter */
      TIM_Cmd (TIM2, ENABLE);
    }
    void init_USART1 (uint32_t baudrate)
    {
      GPIO_InitTypeDef GPIO_InitStruct; // this is for the GPIO pins used as TX and RX
      USART_InitTypeDef USART_InitStruct; // this is for the USART1 initilization
      NVIC_InitTypeDef NVIC_InitStructure; // this is used to configure the NVIC (nested vector interrupt controller)
      RCC_APB2PeriphClockCmd (RCC_APB2Periph_USART1, ENABLE);
      RCC->CFGR |= 0xE000;
      /* enable the peripheral clock for the pins used by
      * USART1, PB6 for TX and PB7 for RX
      */
      RCC_AHB1PeriphClockCmd (RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOA, ENABLE);
      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);
      GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; // Pins 6 (TX) and 7 (RX) are used
      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; // 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 (GPIOA, &GPIO_InitStruct);
      /* 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;	 // the baudrate is set to the value we passed into this init function
      USART_InitStruct.USART_WordLength = USART_WordLength_8b;// we want the data frame size to be 8 bits (standard)
      USART_InitStruct.USART_StopBits = USART_StopBits_1;	 // we want 1 stop bit (standard)
      USART_InitStruct.USART_Parity = USART_Parity_No;	 // we don't want a parity bit (standard)
      USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // we don't want flow control (standard)
      USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // we want to enable the transmitter and the receiver
      USART_Init (USART1, &USART_InitStruct);	 // again all the properties are passed to the USART_Init function which takes care of all the bit setting
      /* Here the USART1 receive interrupt is enabled
      * and the interrupt controller is configured
      * to jump to the USART1_IRQHandler() function
      * if the USART1 receive interrupt occurs
      */
      USART_ITConfig (USART1, USART_IT_RXNE, ENABLE); // enable the USART1 receive interrupt
      NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;	 // we want to configure the USART1 interrupts
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;// this sets the priority group of the USART1 interrupts
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;	 // this sets the subpriority inside the group
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;	 // the USART1 interrupts are globally enabled
      NVIC_Init (&NVIC_InitStructure);	 // the properties are passed to the NVIC_Init function which takes care of the low level stuff
    // finally this enables the complete USART1 peripheral
      USART_Cmd (USART1, ENABLE);
    }
    
    void USART_puts (USART_TypeDef* USARTx, volatile int s)
    {
    //while(*s){
    // wait until data register is empty
      while (! (USARTx->SR & 0x00000040));
      USART_SendData (USARTx, s);
    //*s++;
    //}
    }
    
    void TIM2_IRQHandler (void) //Timer Interupt for sending data
    {
      if (TIM_GetITStatus (TIM2, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit (TIM2, TIM_IT_Update);
        GPIO_ToggleBits (GPIOA, GPIO_Pin_0);
        sample_data = (uint8_t) (ConvertedValue >> 4); //convert 12 bit data to 8 bits
        USART_puts (USART1, sample_data);
      }
    }
    
    void adc_configure()
    {
      ADC_InitTypeDef ADC_init_structure; //Structure for adc confguration
      GPIO_InitTypeDef GPIO_initStructre; //Structure for analog input pin
    //Clock configuration
      RCC_APB2PeriphClockCmd (RCC_APB2Periph_ADC1, ENABLE); //The ADC1 is connected the APB2 peripheral bus thus we will use its clock source
      RCC_AHB1PeriphClockCmd (RCC_AHB1ENR_GPIOCEN, ENABLE); //Clock for the ADC port!! Do not forget about this one ;)
    //Analog pin configuration
      GPIO_initStructre.GPIO_Pin = GPIO_Pin_0;//The channel 10 is connected to PC0
      GPIO_initStructre.GPIO_Mode = GPIO_Mode_AN; //The PC0 pin is configured in analog mode
      GPIO_initStructre.GPIO_PuPd = GPIO_PuPd_NOPULL; //We don't need any pull up or pull down
      GPIO_Init (GPIOC, &GPIO_initStructre); //Affecting the port with the initialization structure configuration
    //ADC structure configuration
      ADC_DeInit();
      ADC_init_structure.ADC_DataAlign = ADC_DataAlign_Right;//data converted will be shifted to right
      ADC_init_structure.ADC_Resolution = ADC_Resolution_12b;//Input voltage is converted into a 12bit number giving a maximum value of 4096
      ADC_init_structure.ADC_ContinuousConvMode = ENABLE; //the conversion is continuous, the input data is converted more than once
      ADC_init_structure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;// conversion is synchronous with TIM1 and CC1 (actually I'm not sure about this one :/)
      ADC_init_structure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;//no trigger for conversion
      ADC_init_structure.ADC_NbrOfConversion = 1;//I think this one is clear :p
      ADC_init_structure.ADC_ScanConvMode = DISABLE;//The scan is configured in one channel
      ADC_Init (ADC1, &ADC_init_structure); //Initialize ADC with the previous configuration
    //Enable ADC conversion
      ADC_Cmd (ADC1, ENABLE);
    //Select the channel to be read from
      ADC_RegularChannelConfig (ADC1, ADC_Channel_10, 1, ADC_SampleTime_144Cycles);
    }
    int adc_convert()
    {
      ADC_SoftwareStartConv (ADC1); //Start the conversion
      while (!ADC_GetFlagStatus (ADC1, ADC_FLAG_EOC)); //Processing the conversion
      return ADC_GetConversionValue (ADC1); //Return the converted data
    }
    
    int main (void)
    {
      init_USART1 (19200);
      INTTIM_Config();
      adc_configure();
      while (1) {
        ConvertedValue = adc_convert();//Read the ADC converted value
      }
    }
    
    
  63. sonoi

    i want to use two ADCs , but i don know that. Please help me!

  64. sonoi

    I am trying to use DMA for ADC1 and use alots of chanel in ADC1. please help me! please give me projec hihi

  65. Peter Harrison

    You just need to repeat the setup code for the other ADC and make appropriate references to it in the other function calls.

  66. Peter Harrison

    Which processor?

  67. sonoi

    i am learning STM32F103RC. Could Peter Harrison help me to study that ? thank you hi

  68. replay1

    Good post. Right now I’m still learning about FreeRTOS and how to use it, so this example might be a bit too complex for me, but it’s still useful. Any idea how to start and what examples to use for learning FreeRTOS basics? I’m using STM32F0 Discovery board. Thanks!

  69. Peter Harrison

    I am afraid I know nothing about FreeRTOS.

  70. R1ddl3

    Hi, i’m working on a STM32f10x and i’d like to know if there is a way to change the resolution of its ADC from 12 bits to 8 bits.

  71. Peter Harrison

    It is a 12 bit converter. If you only want 8 bits of resolution you will have to either leave it right aligned and divide by 16 or set it to left align the result and only use the upper 8 bits.

  72. R1ddl3

    yes, but then i would be loosing some info, correct? I want to adjust its resolution to 8 bits instead of 12:P Do you know if there is any way of doing that? Thanks in advance.

  73. Peter Harrison

    It makes no difference. Eight bits is eight bits. You will have lower resolution than with ten or twelve bits.

  74. R1ddl3

    I understand. What if i wanted to go for 16 bits reso on the same microcontroller? Would there be a way of doing this?

  75. Green Ye

    alright, easy way here:
    use your 12 bit output multiply by 65536 then divide it by 4096 if you want a 16 bits output without changing any setting above. You won’t get any extra info anyway since the max is 12 bits.

  76. Rahul Jain

    I am trying to connect PT100 with stm32f105 but unable to read the ADC value. Please fix this problem. Here is the code…..
    #include “stm32f10x.h”
    #include
    #include “stm32f10x_flash.h”
    #include “stm32f10x_rcc.h”
    #include “stm32f10x_adc.h”
    #include “stm32f10x_gpio.h”
    #include “misc.h”
    #include
    #include
    #include
    #define TEMP_ADC_TABLE_SIZE 52
    #define LED_PORT GPIOC
    #define ADC1_DR_Address ((u32)0x4001244C)
    #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))

    /***************************************************************************//**
    * Global variables
    ******************************************************************************/
    int AD_value;
    static unsigned long ticks;
    unsigned char Clock1s;
    ADC_InitTypeDef ADC_InitStructure;
    vu16 ADC_ConvertedValue;
    ErrorStatus HSEStartUpStatus;

    /***************************************************************************//**
    * Declare function prototype
    ******************************************************************************/
    void Delay(__IO uint32_t nCount);
    void RCC_Configuration(void);
    void GPIO_Configuration(void);
    void NVIC_Configuration(void);
    void FLASH_Unlock(void);
    void ADC_TempSensorVrefintCmd(FunctionalState NewState);

    /***************************************************************************//**
    * @brief This example describes how to use the ADC to converts continuously
    * Analog signal to digital signal through channel1.
    *****
    *************************************************************************/

    int ADC_Exp(void)
    {
    /* System clocks configuration ———————————————*/
    RCC_Configuration();

    /* NVIC configuration ——————————————————*/
    NVIC_Configuration();

    /* GPIO configuration ——————————————————*/
    GPIO_Configuration();

    /* ADC1 configuration ——————————————————*/
    /* ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;

    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//converted by software rather than external trigger to start //
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    /* ADC1 regular channel14 configuration */
    /* ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5);
    // ADC_TempSensorVrefintCmd(ENABLE);
    /* Enable ADC1 */
    /* ADC_Cmd(ADC1, ENABLE);

    /* Enable ADC1 reset calibaration register */
    /* ADC_ResetCalibration(ADC1);
    /* Check the end of ADC1 reset calibration register */
    /* while (ADC_GetResetCalibrationStatus(ADC1));

    /* Start ADC1 calibaration */
    //ADC_StartCalibration(ADC1);
    /* Check the end of ADC1 calibration */
    // while (ADC_GetCalibrationStatus(ADC1));
    //ADC_SoftwareStartConvCmd(ADC1, ENABLE);

    // while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
    /* Start ADC1 Software Conversion */

    //debug_printf(“ADC value %d \n”,AD_value);
    //Return the converted data
    /*AD_value *= 3300;
    AD_value /= 0xfff; //Reading in mV
    AD_value /= (float)1000.0; //Reading in Volts
    AD_value -= (float)0.760; // Subtract the reference voltage at 25°C
    AD_value /= (float).0025; // Divide by slope 2.5mV
    AD_value += (float)25.0;*/ // Add the 25°C

    // if (ticks++ >= 9999)
    { /* Set Clock1s to 1 every 1 second */
    // ticks = 0;
    // Clock1s = 1;
    }

    /* Printf message with AD value to serial port every 1 second */
    /*if (Clock1s)
    {
    Clock1s = 0;
    // Delay(AD_value);
    // GPIO_WriteBit(LED_PORT, GPIO_Pin_8, Bit_SET);
    Delay(AD_value);
    // GPIO_WriteBit(LED_PORT, GPIO_Pin_8, Bit_RESET);
    }*/

    }

    /***************************************************************************//**
    * @brief Configures the different system clocks.
    ******************************************************************************/
    void RCC_Configuration(void)
    {
    /* RCC system reset(for debug purpose) */
    RCC_DeInit();

    /* Enable HSE */
    RCC_HSEConfig(RCC_HSE_ON);

    /* Wait till HSE is ready */
    HSEStartUpStatus = RCC_WaitForHSEStartUp();

    if (HSEStartUpStatus == SUCCESS)
    {
    /* HCLK = SYSCLK */
    RCC_HCLKConfig(RCC_SYSCLK_Div1);

    /* PCLK2 = HCLK */
    RCC_PCLK2Config(RCC_HCLK_Div1);

    /* PCLK1 = HCLK/2 */
    RCC_PCLK1Config(RCC_HCLK_Div2);

    /* ADCCLK = PCLK2/4 */
    RCC_ADCCLKConfig(RCC_PCLK2_Div4);

    /* Flash 2 wait state */
    FLASH_SetLatency(FLASH_Latency_2);
    /* Enable Prefetch Buffer */
    FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

    /* PLLCLK = 8MHz * 9 = 56 MHz */
    RCC_PLLConfig(0x00010000, RCC_PLLMul_9);

    /* Enable PLL */
    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready */
    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
    {
    }

    /* Select PLL as system clock source */
    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source */
    while (RCC_GetSYSCLKSource() != 0x08)
    {
    }
    }

    /* Enable ADC1 and GPIOC clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC
    | RCC_APB2Periph_GPIOC, ENABLE);
    }

    /***************************************************************************//**
    * @brief Configures the different GPIO ports.
    ******************************************************************************/
    void GPIO_Configuration(void)
    {
    GPIO_InitTypeDef GPIO_InitStructure;

    /* Configure PA.01 (ADC Channel1) as analog input ————————-*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    }

    /***************************************************************************//**
    * @brief Configures Vector Table base location.
    ******************************************************************************/
    void NVIC_Configuration(void)
    {
    #ifdef VECT_TAB_RAM
    /* Set the Vector Table base location at 0x20000000 */
    NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
    #else /* VECT_TAB_FLASH */
    /* Set the Vector Table base location at 0x08000000 */
    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
    #endif
    }

    /***************************************************************************//**
    * @brief Inserts a delay time.
    * @param nCount: specifies the delay time length.
    ******************************************************************************/
    void Delay(__IO uint32_t nCount)
    {
    uint32_t i = 0;
    for (; nCount != 0; i++)
    {
    if (i == 1000)
    {
    i = 0;
    nCount–;
    }
    }
    }

  77. Rahul Jain

    And main function…

    int main(int argc, char *argv[])
    {
    int8_t temp = 0;
    uint8_t x = 0;
    uint16_t u = 0;
    uint8_t return_code = 0;
    uint8_t i;
    ADC_Exp();

    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;

    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//converted by software rather than external trigger to start //
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    /* ADC1 regular channel14 configuration */
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5);
    // ADC_TempSensorVrefintCmd(ENABLE);
    /* Enable ADC1 */
    ADC_Cmd(ADC1, ENABLE);

    /* Enable ADC1 reset calibaration register */
    ADC_ResetCalibration(ADC1);
    /* Check the end of ADC1 reset calibration register */
    while (ADC_GetResetCalibrationStatus(ADC1));

    /* Start ADC1 calibaration */
    ADC_StartCalibration(ADC1);
    /* Check the end of ADC1 calibration */

    while (1)
    {

    }
    return 0;

    // debug_printf(“ADC value %d \n”,AD_value);
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
    AD_value = ADC_GetConversionValue(ADC1);
    debug_printf(“%d\n”,AD_value)
    }

  78. Jim Trezzo

    Peter,

    thanks for your example, it helped me with developing a faster data acquisition system for my project – underwater acoustic location system for an ROV (www.openrov.com). My signal is 40KHz and I want to sample at >100K SPS to process the signal.

    See postings – http://community.openrov.com/forum/topics/acoustic-location-system?commentId=6365107%3AComment%3A106814

    http://community.openrov.com/profiles/blogs/acoustic-modems-location-and-pingers

    OpenROV is an opensource project with an active community of builders and explorers. If you or anyone in your community is interested in getting involved, please do – there are plenty of interesting projects to have fun with.

    Regards,

    Jim Trezzo
    jim@openrov.com

  79. Paul

    Thanks for your example!

  80. Anon

    I’m trying to view the analog voltage as digital using ADC in stm32l152.
    I’ve done the configuration but don’t exactly know what’s the problem with the code.
    I’m basically viewing the voltage through USART in docklight. But it can’t read the value from ADC1->DR.
    Please help.
    P.s Pardon my negligence, I ain’t much familiar to it.
    Here’s my code

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

    #include “main.h”
    #include “stm32l1xx_adc.h”
    #include “stm32l1xx_gpio.h”
    #include “stm32l1xx_rcc.h”
    #include “stm32l1xx_adc.h”
    #include “stm32l1xx_usart.h”
    #define BOR_MODIFY
    #define BOR_LEVEL OB_BOR_OFF /*!< BOR is disabled at power down, the reset is asserted when the VDD power supply reachs the PDR(Power Down Reset) threshold (1.5V) */

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

    static TSL_tTick_ms_T last_tick_tsl; /* Hold the last tsl time value */
    extern unsigned char Bias_Current; /* Bias Current stored in E²Prom used for ICC mesurement precision */
    extern uint8_t t_bar[2]; /* LCD bar graph: used for displaying active function */
    extern bool self_test; /* Auto_test activation flag: set by interrupt handler if user button is pressed for a few seconds */
    extern bool Idd_WakeUP; /* */
    extern volatile bool KeyPressed; /* */
    extern bool UserButton; /* Set by interrupt handler to indicate that user button is pressed */
    uint8_t state_machine; /* Machine status used by main() wich indicats the active function, set by user button in interrupt handler */
    uint16_t Int_CurrentSTBY; /* */

    uint8_t message[29] = " ** 32L152CDISCOVERY **";

    /*******************************************************************************/
    /**
    * @brief main entry point.
    * @par Parameters None
    * @retval void None
    * @par Required preconditions: None
    */

    void String1(char* st);
    void adc(void);
    void Init_GPIOs (void);

    uint16_t ADC1_READ(uint16_t channel);

    //read adc value
    uint16_t getvalue;

    int main(void)
    {

    Init_GPIOs();
    adc();

    while(1)

    {
    ADC1_READ(11);

    //getvalue=ADC_GetConversionValue(ADC1);
    USART_SendData(USART1,ADC1_READ(11));
    Delay(200);
    }
    }

    /**
    * To initialize the ADC
    */

    void adc()
    {

    // Structure to init ADC
    ADC_InitTypeDef ADC_InitStruct;

    /* Enable ADC1 clock so that we can talk to it */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOCEN, ENABLE);

    //ADC structure configuration
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStruct.ADC_Resolution=ADC_Resolution_12b;
    ADC_ContinuousModeCmd(ADC1,ENABLE);
    ADC_InitStruct.ADC_ScanConvMode=DISABLE;
    ADC_InitStruct.ADC_ContinuousConvMode=ENABLE;
    ADC_InitStruct.ADC_ExternalTrigConvEdge= ADC_ExternalTrigConvEdge_None;
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStruct.ADC_NbrOfConversion=1;

    ADC_Init(ADC1,&ADC_InitStruct);
    //ADC_TempSensorVrefintCmd(ENABLE); // for temp measurement
    ADC_RegularChannelConfig(ADC1,ADC_Channel_11,1,ADC_SampleTime_4Cycles);

    ADC_Cmd(ADC1, ENABLE);

    }

    //TO Read ADC_value
    uint16_t ADC1_READ(uint16_t channel)
    {
    ADC_RegularChannelConfig(ADC1,channel,1,ADC_SampleTime_4Cycles);
    // Start the conversion
    ADC_SoftwareStartConv(ADC1);
    // Wait until conversion completion
    while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
    // Get the conversion value
    return ADC_GetConversionValue(ADC1);
    }

    void Init_GPIOs (void)
    {

    // Structure to init GPIO
    GPIO_InitTypeDef GPIO_InitStructure;

    // Structure to init USART
    USART_InitTypeDef USART_InitStruct;

    // clock USART
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    // clock PORTs
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA,ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC,ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 , GPIO_AF_USART1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_40MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);

    //Analog pin configuration
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;//The channel 11 is connected to PC1
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //The PA0 pin is configured in analog mode
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //We don't need any pull up or pull down
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_40MHz;
    GPIO_Init (GPIOC, &GPIO_InitStructure); //Affecting the port with the initialization structure configuration

    USART_InitStruct.USART_BaudRate=9600;
    USART_InitStruct.USART_WordLength=USART_WordLength_8b;
    USART_InitStruct.USART_StopBits=USART_StopBits_1;
    USART_InitStruct.USART_Parity=USART_Parity_No ;
    USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART1,&USART_InitStruct);
    USART_Cmd(USART1, ENABLE);

    }

    //adc_value
    void String1(char* st)
    {

    while(*st != '\0')
    {
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)== RESET){}
    USART_SendData(USART1,*st++);
    }
    }

    /**
    * @brief Inserts a delay time.
    * @param nTime: specifies the delay time length, in 1 ms.
    * @retval None
    */
    void Delay(uint32_t nTime)
    {
    while (TSL_tim_CheckDelay_ms((TSL_tTick_ms_T) nTime, &last_tick_tsl) != TSL_STATUS_OK);
    }

  81. Jim Bob

    I see a comment from this year. You refer to a “reference manual.” I’ve not found a full programming reference for the STM32x on the ‘net. Is that what you’re referring to? Or just for the ADC chip?

    Dennis aka Jim Bob

  82. Jim Bob

    Wow, I thought I’d scoured the ST site. That is certainly where I expected to find this stuff. Thanks!

  83. Tosif

    Hi

    i recently started using stm32.
    i was just trying to make a program to catch signals from a water flow censor and measure the per minute value of the water flow through stm32f0 discovery board.
    i was hoping if i can get some help

    regards

  84. sudhir acharya

    I just purchased STM32F10C8T6 I dont no how to use this ..what and all required to do ADC project Please help me(compiler,debugging tool)

  85. Luis

    Hello, I’m working in a project already in production, but while doing an analysis I discovered that by the way the registers are configured the ADC clock is set to 36 MHz, I know this is contradictory according to the max 14 MHz from the reference manual.

    Does anybody know what happens in this case? I have two assumptions, either ADC bus is forced to be at 14 MHz, or it runs at 36 MHz but reliability is not ensured.

  86. Raghu

    Hi
    How can I connect ADC with GPIOr

  87. Vaishnavi V

    Hi in your post, you have mentioned that “According to the datasheet, the ADC clock should be in the range 600kHz to 14MHz”. I wouId like to know to which controller it pertains to, or common to all stm32 uc. I am working on stm32f303VC, checked the datasheet, https://www.st.com/resource/en/datasheet/stm32f303vc.pdf. In page no 105, it is mentioned as max ADC clock is 72 MHz. I set Asynchronous clock PLL clock div_1 for ADC, i.e. 72 Mhz. But the total converition is not as expected. Its seems that clock is working lesser than that. Please mention about this ADC Clock range,

  88. Peter Harrison

    I wrote for the STM32F103. I am sorry, I do not know about the other versions.

  89. Vaishnavi V

    Thank you, I am using ADC in time triggered mode. I want to check the total conversion time of ADC. Timer update event is used as trigger. To see the conversion time, I am enabling a GPIO pin to high in Update interrupt,and the same GPIO pin is made low in EOC interrupt. DMA is used for transferring data from data register to memory. Is this fine to check the total conversion time…..

  90. sathguru

    How to read multiple ADC channels in STM32F302R8 using SPL?
    Hello,

    I want to read multiple ADC channels, using STM32F302R8 MCU my IDE is System workbench. I am searching for relevant examples with DMA but not able to find. Any help in this regard would be appreciated.

  91. sathguru

    Hello ​ ,

    I have validated your code & single ADC channel is working completely fine. But when I move for Multiple ADC channel ,I’m unable to read the buffer memory. Can you please guide me through it and feel free to pin point if I’m wrong at some point.Thanks in advance.
    code:

    /** ****************************************************************************** * @file DMA/DMA_ADCToTIM3Transfer/main.c * @author MCD Application Team * @version V1.1.2 * @date 14-August-2015 * @brief Main program body ****************************************************************************** * @attention * * © COPYRIGHT 2015 STMicroelectronics * * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License"); * You may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.st.com/software_license_agreement_liberty_v2 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /* Includes ------------------------------------------------------------------*/ #include "stm32f30x.h" /** @addtogroup STM32F30x_StdPeriph_Examples * @{ */ /** @addtogroup DMA_ADCToTIM3Transfer * @{ */ /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ //#define ADC1_DR_ADDRESS 0x50000040 #define TIM3_CCR3_ADDRESS 0x4000043C /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ __IO uint16_t calibration_value = 0; __IO uint32_t TimingDelay = 0; uint32_t *adc_var = 0; __IO uint32_t ADC1_DR_ADDRESS[2]; /* Private function prototypes -----------------------------------------------*/ static void ADC_Config(void); static void DMA_Config(void); static void TIM3_Config(void); void Delay(__IO uint32_t nTime); /* Private functions ---------------------------------------------------------*/ /** * @brief Main program. * @param None * @retval None */ int main(void) { /*!< At this stage the microcontroller clock setting is already configured, this is done through SystemInit() function which is called from startup file (startup_stm32f30x.s) before to branch to application main. To reconfigure the default setting of SystemInit() function, refer to system_stm32f30x.c file */ double val = 0, val1 = 0; adc_var = ADC1_DR_ADDRESS; /* DMA1 channel1 configuration */ DMA_Config(); /* TIM3 channel3 configuration */ TIM3_Config(); /* ADC channel7 configuration */ ADC_Config(); while (1) { /* ADC1 DMA Enable */ ADC_DMACmd(ADC1, ENABLE); ADC_DMAConfig(ADC1, ADC_DMAMode_Circular); /* Start ADC1 Software Conversion */ ADC_StartConversion(ADC1); val = ADC1_DR_ADDRESS[0]; val1 = ADC1_DR_ADDRESS[1]; } } /** * @brief Configures DMA1 channel1 to transfer data from * ADC1_DR_ADDRESS to TIM3_CCR3_ADDRESS * @param None * @retval None */ static void DMA_Config(void) { DMA_InitTypeDef DMA_InitStructure; /* Enable DMA1 clock */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel1); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)TIM3_CCR3_ADDRESS; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)& ADC1_DR_ADDRESS; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 2; 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_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, & DMA_InitStructure); /* Enable DMA1 Channel1 */ DMA_Cmd(DMA1_Channel1, ENABLE); } /** * @brief Configures the ADC1 channel7 in continuous mode. * @param None * @retval None */ static void ADC_Config(void) { // ADC_StructInit(&GPIO_InitStructure); GPIO_InitTypeDef GPIO_InitStructure; ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; uint32_t counter = 0; /* Enable ADC clock */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); /* Configure the ADC clock */ RCC_ADCCLKConfig(RCC_ADC12PLLCLK_Div2); /* Enable GPIO clock */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_ADC12, ENABLE); /* Configure ADCx Channel 11 as analog input */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, & GPIO_InitStructure); ADC_StructInit(& ADC_InitStructure); /* Calibration procedure */ ADC_VoltageRegulatorCmd(ADC1, ENABLE); /* Calibration procedure */ ADC_VoltageRegulatorCmd(ADC1, ENABLE); /* Insert delay equal to 10 µs */ Delay(10); ADC_SelectCalibrationMode(ADC1, ADC_CalibrationMode_Single); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1) != RESET) ; calibration_value = ADC_GetCalibrationValue(ADC1); /* Configure the ADC1 in continuous mode */ ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; ADC_CommonInitStructure.ADC_Clock = ADC_Clock_AsynClkMode; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; ADC_CommonInitStructure.ADC_DMAMode = ADC_DMAMode_OneShot; ADC_CommonInitStructure.ADC_TwoSamplingDelay = 0; ADC_CommonInit(ADC1, & ADC_CommonInitStructure); ADC_InitStructure.ADC_ContinuousConvMode = ADC_ContinuousConvMode_Enable; ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ExternalTrigConvEvent = ADC_ExternalTrigConvEvent_0; ADC_InitStructure.ADC_ExternalTrigEventEdge = ADC_ExternalTrigEventEdge_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_OverrunMode = ADC_OverrunMode_Disable; ADC_InitStructure.ADC_AutoInjMode = ADC_AutoInjec_Disable; ADC_InitStructure.ADC_NbrOfRegChannel = 2; ADC_Init(ADC1, & ADC_InitStructure); /* ADC1 regular channel7 configuration */ ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_181Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_181Cycles5); /* Enable ADC1 */ ADC_Cmd(ADC1, ENABLE); /* wait for ADRDY */ while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_RDY)) ; /* ADC1 DMA Enable */ ADC_DMACmd(ADC1, ENABLE); ADC_DMAConfig(ADC1, ADC_DMAMode_Circular); /* Start ADC1 Software Conversion */ ADC_StartConversion(ADC1); } /** * @brief Configures the TIM3 channel3 in PWM mode * @param None * @retval None */ static void TIM3_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; /* Enable GPIOB clock */ RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); /* GPIOB Configuration: PB0(TIM3 CH3) as alternate function push-pull */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_Init(GPIOB, & GPIO_InitStructure); GPIO_PinAFConfig(GPIOB, GPIO_PinSource0, GPIO_AF_2); /* Enable TIM3 clock */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); /* Time Base configuration */ TIM_TimeBaseStructInit(& TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = 0xFF0; TIM_TimeBaseStructure.TIM_Prescaler = 0x0; TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, & TIM_TimeBaseStructure); /* Channel3 Configuration in PWM mode */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0xF0; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC3Init(TIM3, & TIM_OCInitStructure); /* Enable TIM3 */ TIM_Cmd(TIM3, ENABLE); } /** * @brief Inserts a delay time. * @param nTime: specifies the delay time length, in milliseconds. * @retval None */ void Delay(__IO uint32_t nTime) { TimingDelay = nTime; while (TimingDelay-- != 0) ; }

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.