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.
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
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
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.
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。
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.
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
I cannot help with this at the moment. You would be better trying the ST forums:
http://www.st.com/mcu/forums.html
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
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.
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
So far, I have not used the ADC with interrupts – sorry.
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
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.
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.
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.
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
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.
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.
Link away. Let me know the page you are linking from please.
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
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.
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
All you should need to do is add the channel number to the function call.
u16 temperature;
temperature = readADC(3);
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);
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?
Thank you for posting this info! Very helpful!
so just delete “amp;” and you’re good to go.
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.
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.
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….
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…??
Hey,
greak work! Thanks a lot for the nice information about the STM32 ADC. Your really helped me 😉
Pingback: Usando ADC da placa SMT32Discovery, mode sem pegadinhas « Jedizone
Thank you very much for your Tutorial. It is really a good start for ST32 Controllers!
Can u tell how did u toggled an output pin?
Can you share the code?
If you have a look here:
https://micromouseonline.com/2009/12/14/crossworks-blinky-project-1
or at Tony’s comment a little further up (Oct ’11), you should find enough to get you going.
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.
i would also like to know how did u give input to microcontroller..??
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
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!
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
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.
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?
Can you post a code fragment showing how you are doing the conversions?
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
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.
This looks pretty much as I would expect. The actual code I am using for a sample on a STM32F4 is this:
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?
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?
By all means. Link away.
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:
The comment editor is totaly crap . The code is deleted randomly between the characters. I give up.
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?
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 }
still doesnt work even with
code
tags.thanks you every body. i very happy when i reapd this topic. it help me understand about adc for Stm32.
Pingback: Работа с АЦП в STM32 | MyLinks
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
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
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.
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?
I do this all the time.
Make sure your sampling times are correct and double check the hardware.
I am using the stm32f303x
These functions do not appear in the STM Peripheral Library pdf:
ADC_ResetCalibration()
ADC_GetResetCalibrationStatus()
I think they have been removed from the newer library version. You can leave them out as far as I know.
Thanks for your useful post.
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
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?
My code is
i want to use two ADCs , but i don know that. Please help me!
I am trying to use DMA for ADC1 and use alots of chanel in ADC1. please help me! please give me projec hihi
You just need to repeat the setup code for the other ADC and make appropriate references to it in the other function calls.
Which processor?
i am learning STM32F103RC. Could Peter Harrison help me to study that ? thank you hi
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!
I am afraid I know nothing about FreeRTOS.
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.
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.
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.
It makes no difference. Eight bits is eight bits. You will have lower resolution than with ten or twelve bits.
I understand. What if i wanted to go for 16 bits reso on the same microcontroller? Would there be a way of doing this?
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.
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–;
}
}
}
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)
}
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
Thanks for your example!
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);
}
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
These are readily available on the ST site. For example:
http://www.st.com/content/st_com/en/support/resources/resource-selector.html?querycriteria=productId=SS1031$$resourceCategory=technical_literature$$resourceType=reference_manual
Wow, I thought I’d scoured the ST site. That is certainly where I expected to find this stuff. Thanks!
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
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)
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.
Hi
How can I connect ADC with GPIOr
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,
I wrote for the STM32F103. I am sorry, I do not know about the other versions.
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…..
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.
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) ; }