Article directory
- ADC – Analog to Digital Conversion
-
- ADC functional block diagram
-
- Voltage input range
-
- How to measure voltage exceeding 0~3.3V?
- Input channel
- Channel conversion sequence
- trigger source
- conversion time
- data register
- interrupt
- voltage conversion
- ADC initialization structure
- Code example
-
- 1-Independent mode-Single channel-Interrupt reading
- 2-Independent mode-Single channel-DMA read
- 3-Independent mode-Multi-channel-DMA reading
- 4-Dual mode-Multi-channel-Rule synchronization
ADC – Analog to Digital Conversion
ADC: Analog to Digital, analog-to-digital converter
1-Three independent ADCs 1 / 2 / 3
2-The resolution is 12 bits
3-Each ADC has 18 channels, including 16 external channels
ADC functional block diagram
1-Voltage input range
2-Input channel
3-Conversion order
4-Trigger source
5-Conversion time
6-Data register
7-Interruption
Voltage input range
Input voltage: VREF- ≤ VIN ≤ VREF +
Pins that determine the input voltage: VREF-, VREF +, VDDA, VSSA
VSSA and VREF- are grounded, VREF + and VDDA are connected to 3V3, and the input voltage range of the ADC is: 0~3.3V.
How to measure the voltage exceeding 0~3.3V?
ADC can measure:-10V~10V
According to Kirchhoff’s law (KCL), the current flowing into a node is equal to the current flowing out
(Vint – Vout)/R2 + (3V3-Vout)/R1 = Vout / R3
Vout = (Vint + 10) /6
R1 = 100K, R2 = 300K, R3 = 150K, the effect will be better
Input channel
The 16 external channels are divided into regular channels and injection channels during conversion. There are up to 16 regular channels and 4 injection channels. So what is the difference between these two channels? When to use?
Regular Channel: As the name implies, the regular channel means very regular. This is the channel we usually use.
Injection channel: Injection can be understood as inserting or jumping in line. It is a restless channel. It is a method that is forcibly inserted into the conversion during regular channel conversion. This is very similar to interrupt programs, both of which are restless masters. Therefore, the injection channel will only appear when the regular channel exists.
Channel conversion sequence
Trigger source
1. Software trigger: ADC_CR2:ADON/SWST ART/JSWSTART
2. External event trigger: Internal timer/external IO
Selection: ADC_CR2:EXTSEL[2:0] and JEXTSEL[2:0]
Activation: ADC_CR2:EXTEN and JEXTEN
Conversion time
Conversion timeT: conv = sampling time + 12.5 cycles
ADC_CLK: ADC analog circuit clock, the maximum value is 14M, provided by PCLK2, it can also be divided by 2/4/6/8 (generally it can only be divided by 6), ADCPRE[1 of RCC_CFGR: 0] settings. PCLK2=72M.
Digital clock: RCC_APB2ENR, used to access registers
Sampling time: The ADC requires several ADC_CLK cycles to complete sampling of the input analog quantity. The number of sampling cycles can be set through the SMPx[2:0] bits in the ADC sampling time registers ADC_SMPR1 and ADC_SMPR2. ADC_SMPR2 It controls channels 0~9, and ADC_SMPR1 controls channels 10~17. Each channel can be sampled at a different time. The minimum sampling period is 1.5, that is, if we want to achieve the fastest sampling, we should set the sampling period to 31.5 periods. The period mentioned here is 1/ADC_CLK.
Minimum conversion time: Tconv = sampling time + 12.5 cycles
PCLK2 = 72M, ADC_CLK = 72/6 = 12M
Tconv = 1.5 + 12.5 = 14 cycles = 14/12us=1.17us
Data register
After everything is ready, the ADC converted data depends on the conversion group. The data of the rule group is placed in the ADC_DR register, and the data of the injection group is placed in JDRx.
1-16 bits are valid, used to store independent mode conversion completed data
2- ADC_CR2:ALIGN
3- Only one, multi-channel acquisition is best done using DMA
Bits 1-16 are valid, used to store injection channel conversion completion data
2- ADC_CR2:ALIGN
3- There are 4 such registers
Interruption
1-ADC_SR,ADC_CR1
2- ADC_HTR, ADC_LTR
Voltage conversion
How to calculate the analog quantity based on the amount of data
1-The voltage input range is: 0~3.3V
2-The resolution is 12 bits
3-The minimum accuracy is: 3.3/2^12
4-Suppose the digital quantity is X, then there is an analog quantity Y = (3.3 / 2^12)*X
ADC initialization structure
ADC_InitTypeDef
ADC_MODE: mode selection, ADC_CR1:DUALMOD
ADC_ScanConvMode: Scan mode, ADC_CR1:SCAN
ADC_ContinuousConvMode: continuous conversion mode, ADC_CR2:CON
ADC_ExternalTrigConv: External trigger conversion selection, ADC_CR2: EXTTRIG and EXTSEL[2:0]
ADC_MODE: mode selection, ADC_CR1:DUALMOD
ADC_ScanConvMode: Scan mode, ADC_CR1:SCAN
ADC_DataAlign: data alignment format, ADC_CR2:ALIGN
ADC_NbrOfChannel: Number of channels converted, configuration rule sequence register and injection sequence register
Several commonly used firmware library functions
ADC_Init(); 429
RCC_ADCCLKConfig(); 680
ADC_RegularChannelConfig(); 442
ADC_Cmd(); 431 //Single ADC mode use
ADC_SoftwareStartConvCmd(); 438 //Use in single ADC mode
ADC_ExternalTrigConvCmd(); 443 //Dual ADC mode use
ADC_DMACmd(); 432
Code example
Compass ADC interface
1-Independent mode-Single channel-Interrupt reading
1-Initialize the GPIO used by ADC
2-Initialize the ADC initialization structure
3-Configure the ADC clock, configure the conversion sequence and sampling time of the channel
4-Enable ADC conversion completion interrupt and configure interrupt priority
The interrupt source is searched in
stm32f10x.h
, and the interrupt service function is searched in the startup file
5-Enable ADC and prepare to start conversion
6-Calibrate ADC
7-The software triggers the ADC and actually starts the conversion
8-Write an interrupt service function to read ADC conversion data
The interrupt source is searched in
stm32f10x.h
, and the interrupt service function is searched in the startup file
9-Write the main function to print out the converted data
bsp_adc.h
#ifndef _BSP_ADC_H #define _BSP_ADC_H #include "stm32f10x.h" #define ADC_GPIO_CLK RCC_APB2Periph_GPIOC #define ADC_PORT GPIOC #define ADC_PIN GPIO_Pin_1 #define ADC_x ADC2 #define ADC_CHANNEL ADC_Channel_11 #define ADC_CLK RCC_APB2Periph_ADC2 #define ADC_IRQ ADC1_2_IRQn //Both ADC1 and ADC2 use this interrupt source and interrupt service function #define ADC_IRQHandler ADC1_2_IRQHandler void ADCx_Init(void); #endif /*_BSP_ADC_H*/
bsp_adc.c
#include "bsp_adc.h" static void ADCx_GPIO_Config(void) {<!-- --> GPIO_InitTypeDef GPIO_InitStructure; \t RCC_APB2PeriphClockCmd(ADC_GPIO_CLK,ENABLE); \t GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin = ADC_PIN; GPIO_Init(ADC_PORT, &GPIO_InitStructure); } static void ADCx_Mode_Config(void) {<!-- --> ADC_InitTypeDef ADC_InitStruct; RCC_APB2PeriphClockCmd(ADC_CLK ,ENABLE); \t ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // No external trigger conversion is required, just open the software ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // Only one ADC is used, which is independent mode ADC_InitStruct.ADC_NbrOfChannel = 1; ADC_InitStruct.ADC_ScanConvMode = DISABLE; //Disable scan mode, only required for multi-channel, not required for single channel ADC_Init(ADC_x, & amp;ADC_InitStruct); \t //Configure the ADC clock to be PCLK2 divided by 8, that is, 9MHz RCC_ADCCLKConfig(RCC_PCLK2_Div8); //Configure ADC channel conversion sequence and sampling time ADC_RegularChannelConfig(ADC_x,ADC_CHANNEL,1,ADC_SampleTime_55Cycles5); // An interrupt is generated when the ADC conversion is completed, and the conversion value is read in the interrupt service routine. ADC_ITConfig(ADC_x, ADC_IT_EOC, ENABLE); \t ADC_Cmd(ADC_x,ENABLE); \t //ADC starts calibration ADC_StartCalibration(ADC_x); \t //Wait for ADC calibration to complete while(ADC_GetCalibrationStatus(ADC_x)); \t // Since no external trigger is used, software is used to trigger the ADC conversion. ADC_SoftwareStartConvCmd(ADC_x, ENABLE); \t } static void ADC_NVIC_Config(void) {<!-- --> NVIC_InitTypeDef NVIC_InitStruct; \t //Priority grouping NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); \t //Configure interrupt priority NVIC_InitStruct.NVIC_IRQChannel = ADC_IRQ; NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; \t NVIC_Init( & amp;NVIC_InitStruct); } void ADCx_Init(void) {<!-- --> ADCx_GPIO_Config(); ADCx_Mode_Config(); ADC_NVIC_Config(); \t }
mian.c
#include "stm32f10x.h" #include "bsp_led.h" #include "bsp_usart.h" #include "./adc/bsp_adc.h" // ADC single channel acquisition, does not use DMA, generally only ADC2 is used in this way, because ADC2 cannot use DMA extern __IO uint16_t ADC_ConversionValue; //Local variables are used to save the converted voltage value float ADC_ConvertedValueLocal; //Software delay void Delay(__IO uint32_t nCount) {<!-- --> for(; nCount != 0; nCount--); } int main(void) {<!-- --> USART_Config(); ADCx_Init(); printf("\r\ ----This is an ADC single-channel interrupt reading experiment----\r\ "); while(1) {<!-- --> ADC_ConvertedValueLocal = (float) ADC_ConversionValue/4096*3.3; printf("\r\ The current AD value = 0x x \r\ ", ADC_ConversionValue); printf("\r\ The current AD value = %f V \r\ ", ADC_ConvertedValueLocal); Delay(0xffffee); } \t }
stm32f10x_it.c
__IO uint16_t ADC_ConversionValue; void ADC_IRQHandler(void) {<!-- --> if(ADC_GetITStatus(ADC_x, ADC_IT_EOC) == SET) {<!-- --> ADC_ConversionValue = ADC_GetConversionValue(ADC_x); } ADC_ClearITPendingBit(ADC_x, ADC_IT_EOC); }
2-Independent mode-Single channel-DMA reading
bsp_adc.c
#include "bsp_adc.h" __IO uint16_t ADC_ConversionValue; static void ADCx_GPIO_Config(void) {<!-- --> GPIO_InitTypeDef GPIO_InitStructure; \t RCC_APB2PeriphClockCmd(ADC_GPIO_CLK,ENABLE); \t GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin = ADC_PIN; GPIO_Init(ADC_PORT, &GPIO_InitStructure); } static void ADCx_Mode_Config(void) {<!-- --> DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStruct; // Turn on DMA clock RCC_AHBPeriphClockCmd(ADC_DMA_CLK, ENABLE); DMA_DeInit(ADC_DMA_CHANNEL); //Set DMA source address: ADC data register address*/ DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)( & amp;(ADC_x->DR)); // memory address DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) & amp;ADC_ConversionValue; // Direction: from peripheral to memory DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //Transfer size DMA_InitStructure.DMA_BufferSize = 1; // Peripheral address does not increase DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //The memory address does not increase DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // Peripheral data unit DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //Memory data unit DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // DMA mode, loop mode DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //Priority: high DMA_InitStructure.DMA_Priority = DMA_Priority_High; // Disable memory-to-memory transfers DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //Configure DMA channel DMA_Init(ADC_DMA_CHANNEL, & amp;DMA_InitStructure); // Enable DMA DMA_Cmd (ADC_DMA_CHANNEL,ENABLE); \t RCC_APB2PeriphClockCmd(ADC_CLK ,ENABLE); \t ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // No external trigger conversion is required, just open the software ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // Only one ADC is used, which is independent mode ADC_InitStruct.ADC_NbrOfChannel = 1; ADC_InitStruct.ADC_ScanConvMode = DISABLE; //Disable scan mode, only required for multi-channel, not required for single channel ADC_Init(ADC_x, & amp;ADC_InitStruct); \t //Configure the ADC clock to be PCLK2 divided by 8, that is, 9MHz RCC_ADCCLKConfig(RCC_PCLK2_Div8); //Configure ADC channel conversion sequence and sampling time ADC_RegularChannelConfig(ADC_x,ADC_CHANNEL,1,ADC_SampleTime_55Cycles5); //Generate ADC DMA request ADC_DMACmd(ADC_x, ENABLE); ADC_Cmd(ADC_x,ENABLE); \t //ADC starts calibration ADC_StartCalibration(ADC_x); \t //Wait for ADC calibration to complete while(ADC_GetCalibrationStatus(ADC_x)); \t // Since no external trigger is used, software is used to trigger the ADC conversion. ADC_SoftwareStartConvCmd(ADC_x, ENABLE); \t } void ADCx_Init(void) {<!-- --> ADCx_GPIO_Config(); ADCx_Mode_Config(); \t \t }
bsp_adc.h
#ifndef _BSP_ADC_H #define _BSP_ADC_H #include "stm32f10x.h" #define ADC_GPIO_CLK RCC_APB2Periph_GPIOC #define ADC_PORT GPIOC #define ADC_PIN GPIO_Pin_1 #define ADC_x ADC1 #define ADC_CHANNEL ADC_Channel_11 #define ADC_CLK RCC_APB2Periph_ADC1 #define ADC_DMA_CLK RCC_AHBPeriph_DMA1 #define ADC_DMA_CHANNEL DMA1_Channel1 void ADCx_Init(void); #endif /*_BSP_ADC_H*/
mian.c
#include "stm32f10x.h" #include "bsp_led.h" #include "bsp_usart.h" #include "./adc/bsp_adc.h" // ADC single channel acquisition, does not use DMA, generally only ADC2 is used in this way, because ADC2 cannot use DMA extern __IO uint16_t ADC_ConversionValue; //Local variables are used to save the converted voltage value float ADC_ConvertedValueLocal; //Software delay void Delay(__IO uint32_t nCount) {<!-- --> for(; nCount != 0; nCount--); } int main(void) {<!-- --> USART_Config(); ADCx_Init(); printf("\r\ ----This is an ADC independent mode-single channel-DMA reading experiment----\r\ "); while(1) {<!-- --> ADC_ConvertedValueLocal = (float) ADC_ConversionValue/4096*3.3; printf("\r\ The current AD value = 0x x \r\ ", ADC_ConversionValue); printf("\r\ The current AD value = %f V \r\ ", ADC_ConvertedValueLocal); Delay(0xffffee); } }
3-Independent mode-Multi-channel-DMA reading
bsp_adc.c
#include "bsp_adc.h" __IO uint16_t ADC_ConversionValue[NUMCHANNEL] = {<!-- -->0,0,0,0,0,0}; static void ADCx_GPIO_Config(void) {<!-- --> GPIO_InitTypeDef GPIO_InitStructure; \t RCC_APB2PeriphClockCmd(ADC_GPIO_CLK,ENABLE); \t GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin = ADC_PIN1; GPIO_InitStructure.GPIO_Pin = ADC_PIN2; GPIO_InitStructure.GPIO_Pin = ADC_PIN3; GPIO_InitStructure.GPIO_Pin = ADC_PIN4; GPIO_InitStructure.GPIO_Pin = ADC_PIN5; GPIO_InitStructure.GPIO_Pin = ADC_PIN6; GPIO_Init(ADC_PORT, &GPIO_InitStructure); } static void ADCx_Mode_Config(void) {<!-- --> DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStruct; // Turn on DMA clock RCC_AHBPeriphClockCmd(ADC_DMA_CLK, ENABLE); DMA_DeInit(ADC_DMA_CHANNEL); //Set DMA source address: ADC data register address*/ DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)( & amp;(ADC_x->DR)); // memory address DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConversionValue; // Direction: from peripheral to memory DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //Transfer size DMA_InitStructure.DMA_BufferSize = NUMCHANNEL; // Peripheral address does not increase DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //The memory address does not increase DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Peripheral data unit DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //Memory data unit DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // DMA mode, loop mode DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //Priority: high DMA_InitStructure.DMA_Priority = DMA_Priority_High; // Disable memory-to-memory transfers DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //Configure DMA channel DMA_Init(ADC_DMA_CHANNEL, & amp;DMA_InitStructure); // Enable DMA DMA_Cmd (ADC_DMA_CHANNEL,ENABLE); \t RCC_APB2PeriphClockCmd(ADC_CLK ,ENABLE); \t ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // No external trigger conversion is required, just open the software ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // Only one ADC is used, which is independent mode ADC_InitStruct.ADC_NbrOfChannel = NUMCHANNEL; ADC_InitStruct.ADC_ScanConvMode = ENABLE; //Enable scan mode, required for multi-channel, not required for single channel ADC_Init(ADC_x, & amp;ADC_InitStruct); \t //Configure the ADC clock to be PCLK2 divided by 8, that is, 9MHz RCC_ADCCLKConfig(RCC_PCLK2_Div8); //Configure ADC channel conversion sequence and sampling time ADC_RegularChannelConfig(ADC_x,ADC_CHANNEL1,1,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC_x,ADC_CHANNEL2,2,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC_x,ADC_CHANNEL3,3,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC_x,ADC_CHANNEL4,4,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC_x,ADC_CHANNEL5,5,ADC_SampleTime_55Cycles5); ADC_RegularChannelConfig(ADC_x,ADC_CHANNEL6,6,ADC_SampleTime_55Cycles5); //Generate ADC DMA request ADC_DMACmd(ADC_x, ENABLE); ADC_Cmd(ADC_x,ENABLE); \t //ADC starts calibration ADC_StartCalibration(ADC_x); \t //Wait for ADC calibration to complete while(ADC_GetCalibrationStatus(ADC_x)); \t // Since no external trigger is used, software is used to trigger the ADC conversion. ADC_SoftwareStartConvCmd(ADC_x, ENABLE); \t } void ADCx_Init(void) {<!-- --> ADCx_GPIO_Config(); ADCx_Mode_Config(); \t \t }
bsp_adc.h
#ifndef _BSP_ADC_H #define _BSP_ADC_H #include "stm32f10x.h" #define ADC_GPIO_CLK RCC_APB2Periph_GPIOC #define ADC_PORT GPIOC #define ADC_PIN1 GPIO_Pin_0 #define ADC_PIN2 GPIO_Pin_1 #define ADC_PIN3 GPIO_Pin_2 #define ADC_PIN4 GPIO_Pin_3 #define ADC_PIN5 GPIO_Pin_4 #define ADC_PIN6 GPIO_Pin_5 #define ADC_x ADC1 #define NUMCHANNEL 6 #define ADC_CHANNEL1 ADC_Channel_10 #define ADC_CHANNEL2 ADC_Channel_11 #define ADC_CHANNEL3 ADC_Channel_12 #define ADC_CHANNEL4 ADC_Channel_13 #define ADC_CHANNEL5 ADC_Channel_14 #define ADC_CHANNEL6 ADC_Channel_15 #define ADC_CLK RCC_APB2Periph_ADC1 #define ADC_DMA_CLK RCC_AHBPeriph_DMA1 #define ADC_DMA_CHANNEL DMA1_Channel1 void ADCx_Init(void); #endif /*_BSP_ADC_H*/
mian.c
#include "stm32f10x.h" #include "bsp_led.h" #include "bsp_usart.h" #include "./adc/bsp_adc.h" // ADC single channel acquisition, does not use DMA, generally only ADC2 is used in this way, because ADC2 cannot use DMA extern __IO uint16_t ADC_ConversionValue[NUMCHANNEL]; //Local variables are used to save the converted voltage value float ADC_ConvertedValueLocal[NUMCHANNEL]; //Software delay void Delay(__IO uint32_t nCount) {<!-- --> for(; nCount != 0; nCount--); } int main(void) {<!-- --> USART_Config(); ADCx_Init(); printf("\r\ ----This is an ADC independent mode-multi-channel-DMA reading experiment----\r\ "); while(1) {<!-- --> ADC_ConvertedValueLocal[0] = (float) ADC_ConversionValue[0]/4096*3.3; ADC_ConvertedValueLocal[1] = (float) ADC_ConversionValue[1]/4096*3.3; ADC_ConvertedValueLocal[2] = (float) ADC_ConversionValue[2]/4096*3.3; ADC_ConvertedValueLocal[3] = (float) ADC_ConversionValue[3]/4096*3.3; ADC_ConvertedValueLocal[4] = (float) ADC_ConversionValue[4]/4096*3.3; ADC_ConvertedValueLocal[5] = (float) ADC_ConversionValue[5]/4096*3.3; printf("\r\ The CH0 AD value = %f V \r\ ", ADC_ConvertedValueLocal[0]); printf("\r\ The CH1 AD value = %f V \r\ ", ADC_ConvertedValueLocal[1]); printf("\r\ The CH2 AD value = %f V \r\ ", ADC_ConvertedValueLocal[2]); printf("\r\ The CH3 AD value = %f V \r\ ", ADC_ConvertedValueLocal[3]); printf("\r\ The CH4 AD value = %f V \r\ ", ADC_ConvertedValueLocal[4]); printf("\r\ The CH5 AD value = %f V \r\ ", ADC_ConvertedValueLocal[5]); printf("\r\ \r\ "); Delay(0xffffee); } \t }
4-Dual mode-Multi-channel-Rule synchronization
bsp_adc.c
#include "bsp_adc.h" __IO uint32_t ADC_ConversionValue[NUMCHANNEL] = {<!-- -->0}; static void ADCx_GPIO_Config(void) {<!-- --> GPIO_InitTypeDef GPIO_InitStructure; \t RCC_APB2PeriphClockCmd(ADC_GPIO_CLK,ENABLE); \t GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin = ADC_1_PIN | ADC_2_PIN; GPIO_Init(ADC_PORT, &GPIO_InitStructure); } static void ADCx_Mode_Config(void) {<!-- --> DMA_InitTypeDef DMA_InitStructure; ADC_InitTypeDef ADC_InitStruct; // Turn on DMA clock RCC_AHBPeriphClockCmd(ADC_DMA_CLK, ENABLE); DMA_DeInit(ADC_DMA_CHANNEL); //Set DMA source address: ADC data register address*/ DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)( & amp;(ADC_1->DR)); // memory address DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConversionValue; // Direction: from peripheral to memory DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //Transfer size DMA_InitStructure.DMA_BufferSize = NUMCHANNEL; // Peripheral address does not increase DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //The memory address does not increase DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; // Peripheral data unit DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; //Memory data unit DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; // DMA mode, loop mode DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //Priority: high DMA_InitStructure.DMA_Priority = DMA_Priority_High; // Disable memory-to-memory transfers DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //Configure DMA channel DMA_Init(ADC_DMA_CHANNEL, & amp;DMA_InitStructure); // Enable DMA DMA_Cmd (ADC_DMA_CHANNEL,ENABLE); //ADC1 RCC_APB2PeriphClockCmd(ADC_1_CLK ,ENABLE); ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // No external trigger conversion is required, just open the software ADC_InitStruct.ADC_Mode = ADC_Mode_RegSimult; ADC_InitStruct.ADC_NbrOfChannel = NUMCHANNEL; ADC_InitStruct.ADC_ScanConvMode = DISABLE; //Enable scan mode, required for multi-channel, not required for single channel ADC_Init(ADC_1, & amp;ADC_InitStruct); //Configure the ADC clock to be PCLK2 divided by 8, that is, 9MHz RCC_ADCCLKConfig(RCC_PCLK2_Div8); //Configure ADC channel conversion sequence and sampling time ADC_RegularChannelConfig(ADC_1,ADC_1_CHANNEL,1,ADC_SampleTime_55Cycles5); //ADC2 RCC_APB2PeriphClockCmd(ADC_2_CLK ,ENABLE); ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // No external trigger conversion is required, just open the software ADC_InitStruct.ADC_Mode = ADC_Mode_RegSimult; ADC_InitStruct.ADC_NbrOfChannel = NUMCHANNEL; ADC_InitStruct.ADC_ScanConvMode = DISABLE; //Enable scan mode, required for multi-channel, not required for single channel ADC_Init(ADC_2, & amp;ADC_InitStruct); //Configure the ADC clock to be PCLK2 divided by 8, that is, 9MHz RCC_ADCCLKConfig(RCC_PCLK2_Div8); //Configure ADC channel conversion sequence and sampling time ADC_RegularChannelConfig(ADC_2,ADC_2_CHANNEL,1,ADC_SampleTime_55Cycles5); //Generate ADC DMA request ADC_DMACmd(ADC_1, ENABLE); ADC_Cmd(ADC_1,ENABLE); \t //ADC starts calibration ADC_StartCalibration(ADC_1); \t //Wait for ADC calibration to complete while(ADC_GetCalibrationStatus(ADC_1)); \t ADC_Cmd(ADC_2,ENABLE); \t //ADC starts calibration ADC_StartCalibration(ADC_2); \t //Wait for ADC calibration to complete while(ADC_GetCalibrationStatus(ADC_2)); //Enable ADC2 as external trigger ADC_ExternalTrigConvCmd(ADC_2,ENABLE); // Since no external trigger is used, software is used to trigger the ADC conversion. ADC_SoftwareStartConvCmd(ADC_1, ENABLE); \t } void ADCx_Init(void) {<!-- --> ADCx_GPIO_Config(); ADCx_Mode_Config(); \t \t }
bsp_adc.h
#ifndef _BSP_ADC_H #define _BSP_ADC_H #include "stm32f10x.h" /* In dual mode, the data converted by ADC1 and ADC2 are stored in the data register of ADC1 ADC1 is in the lower 16 bits, ADC2 is in the upper 16 bits In dual ADC mode, the first ADC must be ADC1, and the second ADC must be ADC2. */ #define ADC_GPIO_CLK RCC_APB2Periph_GPIOC #define ADC_PORT GPIOC #define ADC_1_PIN GPIO_Pin_1 #define ADC_2_PIN GPIO_Pin_4 #define ADC_1 ADC1 #define ADC_2 ADC2 #define NUMCHANNEL 1 #define ADC_1_CHANNEL ADC_Channel_11 #define ADC_2_CHANNEL ADC_Channel_14 #define ADC_1_CLK RCC_APB2Periph_ADC1 #define ADC_2_CLK RCC_APB2Periph_ADC2 #define ADC_DMA_CLK RCC_AHBPeriph_DMA1 #define ADC_DMA_CHANNEL DMA1_Channel1 void ADCx_Init(void); #endif /*_BSP_ADC_H*/
mian.c
#include "stm32f10x.h" #include "bsp_led.h" #include "bsp_usart.h" #include "./adc/bsp_adc.h" // ADC single channel acquisition, does not use DMA, generally only ADC2 is used in this way, because ADC2 cannot use DMA extern __IO uint32_t ADC_ConversionValue[NUMCHANNEL]; //Local variables are used to save the converted voltage value float ADC_ConvertedValueLocal[NUMCHANNEL*2] = {<!-- -->0,0}; //Software delay void Delay(__IO uint32_t nCount) {<!-- --> for(; nCount != 0; nCount--); } int main(void) {<!-- --> uint16_t temp0 = 0, temp1 = 0; USART_Config(); ADCx_Init(); printf("\r\ ----This is an ADC dual mode-multi-channel-rule synchronous reading experiment----\r\ "); while(1) {<!-- --> //Get the high 16 bits of the ADC1 data register, this is the conversion data of ADC2 temp0 = (ADC_ConversionValue[0] & amp; 0XFFFF0000) >>16; //Get the lower 16 bits of the ADC1 data register. This is the conversion data of ADC1. temp1 = (ADC_ConversionValue[0] & amp; 0X0000FFFF); ADC_ConvertedValueLocal[0] = (float) temp0/4096*3.3; ADC_ConvertedValueLocal[1] = (float) temp1/4096*3.3; printf("\r\ ADC1 AD Value = %f V \r\ ", ADC_ConvertedValueLocal[1]); printf("\r\ ADC2 AD Value = %f V \r\ ", ADC_ConvertedValueLocal[0]); printf("\r\ \r\ "); Delay(0xffffee); } \t }