In this tutorial, I will share how to use ADC on STM32F4 Discovery to read analog voltage. ADC is stands for Analog to Digital Converter. Microcontrollers are digital component, so they only understand discrete/digital signals. Therefore if you want to read analog voltage that can be from various sensors, you need an ADC. STM32F407 has 3 ADC that can work independently. Every ADC have 18 channels. 16 channels are external and connected to GPIO pin. 2 channels are internal that connected to internal temperature sensor and ADC voltage reference.
When you use ADC, you can choose which ADC and its channel from this table:
Once you have select which ADC and its pin that you want to use, you can start to coding. In this example, I will use ADC1 on channel 9 (PB1). I will connect trimpot to PB1 as a source for analog input voltage.
This is code for setup ADC. I made this code on a function called ADC_Config().
void ADC_Config(void) { // Enable clock for ADC1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // Init GPIOB for ADC input GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOB, &GPIO_InitStruct); // Init ADC1 ADC_InitTypeDef ADC_InitStruct; ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_ExternalTrigConv = DISABLE; ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStruct.ADC_NbrOfConversion = 1; ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b; ADC_InitStruct.ADC_ScanConvMode = DISABLE; ADC_Init(ADC1, &ADC_InitStruct); ADC_Cmd(ADC1, ENABLE); // Select input channel for ADC1 // ADC1 channel 9 is on PB1 ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 1, ADC_SampleTime_84Cycles); }This is the function for read ADC value:
uint16_t ADC_Read(void) { // Start ADC conversion ADC_SoftwareStartConv(ADC1); // Wait until conversion is finish while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); return ADC_GetConversionValue(ADC1); }
This is the full code of this tutorial. In the main program, ADC1 reads the value of trimpot that connected to PB1 every 1s then send it to PC via USART1. Here I used three library that I made from previous tutorial for setting clock to 168MHz, delay using SysTick timer, and USART.
#include "stm32f4xx.h" #include "stm32f4xx_rcc.h" #include "stm32f4xx_gpio.h" #include "stm32f4xx_adc.h" #include "clock.h" #include "delay.h" // USART library (USART1 on PB6 and PB7) #include "usart.h" #include <stdio.h> // For ADC value uint16_t adcValue = 0; // For ADC value in string char sAdcValue[5]; void ADC_Config(void) { // Enable clock for ADC1 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // Init GPIOB for ADC input GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOB, &GPIO_InitStruct); // Init ADC1 ADC_InitTypeDef ADC_InitStruct; ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_ExternalTrigConv = DISABLE; ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStruct.ADC_NbrOfConversion = 1; ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b; ADC_InitStruct.ADC_ScanConvMode = DISABLE; ADC_Init(ADC1, &ADC_InitStruct); ADC_Cmd(ADC1, ENABLE); // Select input channel for ADC1 // ADC1 channel 9 is on PB1 ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 1, ADC_SampleTime_84Cycles); } uint16_t ADC_Read(void) { // Start ADC conversion ADC_SoftwareStartConv(ADC1); // Wait until conversion is finish while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); return ADC_GetConversionValue(ADC1); } int main(void) { // Set clock to 168MHz // Don't forget to add defined symbol "HSE_VALUE=8000000" // in compile configuration CLOCK_SetClockTo168MHz(); // Init delay function DELAY_Init(); // Init USART // Here I'm using USART1 (PB6 Tx, PB7 Rx) USART_Config(); // ADC config ADC_Config(); while (1) { // Read ADC value adcValue = ADC_Read(); // Convert to string sprintf(sAdcValue, "%i\n", adcValue); // Send to PC via USART1 USART_PutString(sAdcValue); DELAY_Ms(1000); } }
I read lot of articles and really like this article. This information is definitely useful for everyone in daily life. Fantastic job.
ReplyDeleteOET Coaching in Melbourne
OET Training in Melbourne
OET Coaching for Dentists in Melbourne
OET Coaching for Nurses in Melbourne
Thank you so much for reading my blog.
Deletethx for your blog, after building the following error appears:
ReplyDeletecollect2.exe: error: ld returned 1 exit status
I added many components:
C library
Retarget printf
semihosting
M4 CMSIS Core
CMSIS boot
RCC
GPIO
Flash
ADC
USART
Moreover I checked the toolchain path and its Ok (I correctly do tutorial2 :) thx for it)
Any help please
I already test this code and I get this message:
ReplyDeleteGCC HOME: C:\Program Files (x86)\GNU Tools ARM Embedded\4.9 2015q3\bin
compile:
[mkdir] Skipping E:\CooCox & STM32F4 Discovery\ADC\adc\Debug\bin because it already exists.
[mkdir] Skipping E:\CooCox & STM32F4 Discovery\ADC\adc\Debug\obj because it already exists.
[cc] Error reading history.xml: org.xml.sax.SAXParseException: The entity name must immediately follow the '&' in the entity reference.
[cc] 200
[cc] 10 total files to be compiled.
[cc] arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -Wall -ffunction-sections -g -O0 -c -DSTM32F407VG -DSTM32F4XX -DUSE_STDPERIPH_DRIVER -D__ASSEMBLY__ "-IE:\CooCox & STM32F4 Discovery\ADC\cmsis_lib" "-IE:\CooCox & STM32F4 Discovery\ADC" "-IE:\CooCox & STM32F4 Discovery\ADC\cmsis_boot" "-IE:\CooCox & STM32F4 Discovery" "-IE:\CooCox & STM32F4 Discovery\ADC\cmsis_lib\include" -IE:\ "-IE:\CooCox & STM32F4 Discovery\ADC\cmsis" '"E:\CooCox & STM32F4 Discovery\ADC\delay.c"' '"E:\CooCox & STM32F4 Discovery\ADC\cmsis_lib\source\stm32f4xx_usart.c"' '"E:\CooCox & STM32F4 Discovery\ADC\cmsis_boot\startup\startup_stm32f4xx.c"' '"E:\CooCox & STM32F4 Discovery\ADC\main.c"' '"E:\CooCox & STM32F4 Discovery\ADC\cmsis_lib\source\stm32f4xx_rcc.c"' '"E:\CooCox & STM32F4 Discovery\ADC\cmsis_lib\source\stm32f4xx_adc.c"' '"E:\CooCox & STM32F4 Discovery\ADC\cmsis_lib\source\stm32f4xx_gpio.c"' '"E:\CooCox & STM32F4 Discovery\ADC\cmsis_lib\source\stm32f4xx_flash.c"' '"E:\CooCox & STM32F4 Discovery\ADC\cmsis_boot\system_stm32f4xx.c"' '"E:\CooCox & STM32F4 Discovery\ADC\clock.c"'
[cc] Starting link
[cc] arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb -g -nostartfiles -Wl,-Map=ADC.map -O0 -Wl,--gc-sections -LC:\CooCox\CoIDE\configuration\ProgramData\ADC -Wl,-TC:\CooCox\CoIDE\configuration\ProgramData\ADC/arm-gcc-link.ld -g -o ADC.elf ..\obj\delay.o ..\obj\stm32f4xx_usart.o ..\obj\startup_stm32f4xx.o ..\obj\main.o ..\obj\stm32f4xx_rcc.o ..\obj\stm32f4xx_adc.o ..\obj\stm32f4xx_gpio.o ..\obj\stm32f4xx_flash.o ..\obj\system_stm32f4xx.o ..\obj\clock.o
[cc] c:/program files (x86)/gnu tools arm embedded/4.9 2015q3/bin/../lib/gcc/arm-none-eabi/4.9.3/../../../../arm-none-eabi/lib/armv7e-m\libg.a(lib_a-sbrkr.o): In function `_sbrk_r':
[cc] sbrkr.c:(.text._sbrk_r+0xc): undefined reference to `_sbrk'
[cc] collect2.exe: error: ld returned 1 exit status
BUILD FAILED
Total time: 2 seconds
I correctly add libraries clock, delay, and usart then the program is compiled with success :) but when I debug I get:
ReplyDeleteGDB Hardware Debugger suspended
thread[1] suspended signal 'SIGINT' received. descrption:interrupt
4 default_Handler() startup_stm32xx.c:414 0x08000520
3 () 0xfffffff9
2 _svfprintf_r() 0x08001ddc
1 0x6078af00
Any help is welcome
This comment has been removed by the author.
ReplyDeleteHello,
ReplyDeleteHow to improve the program code to properly sample your input signal, eg. Sinusoidal frequency fm = 10kHz?
How to set the sampling frequency fs = 300kHz?
Then we have 30 samples per period Tm = 1 / fm.
Please reply!
Thank you very much and regards
Wojtek Polish
It is only missing enabling GPIO B Peripheral.
ReplyDeleteAdd the code below after line 19.
//Enable the clock on GPIO B
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);