Chapter 3 AD Conversion
This chapter is divided into two parts. The first is the single channel conversion of AD, and the second is the multi-channel conversion of AD. First, convert the single channel.
The maximum conversion frequency of the AD built into STM32 is 14MHZ, with a total of 16 conversion channels.
ADC123_IN10 indicates that the PC0 pin can be used as the 10th channel of AD1, AD2, and AD3.
Next, we will take the example of configuring PC0 to be channel 10 of AD1.
3.1 First we should set PC0 as analog input:
#include "adc.h"
/*Why define ADC1_DR_Address as ((u32)0x40012400+0x4c)
, because the address of the register storing the AD conversion result is 0x4001244c*/
#define ADC1_DR_Address ((u32)0x40012400+0x4c)
/*Define variable ADC_ConvertedValue to store the converted data of AD1 channel 10*/
__IO uint16_t ADC_ConvertedValue;
static void ADC1_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable ADC1 and GPIOC clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1
RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
|
3.2 After setting the port, the next step is of course to initialize AD:
Here we need to add a knowledge point DMA. DMA is equivalent to a secretary of the CPU. Its role is to help the CPU reduce its burden. To be more specific, it helps the CPU to transfer data. We all know that after each AD conversion, the result of the conversion will be placed in a fixed register. In the past, if we want to assign the value in the register to a variable, we will use the assignment statement. If we do not use DMA, the assignment statement will be completed by the CPU. The CPU is already busy dealing with other things, and now it has to solve such a simple problem as the assignment statement. It will be painful. So we need DMA, the secretary, to help him solve this problem. Since DMA is just a secretary, it is relatively stupid. You can only complete the task well if you explain the task clearly. So how to give DMA a task? Smart people must have thought of it, of course, it is the "DMA_Init(DMA1_Channel1, &DMA_InitStructure)" function. Let's explain the task to DMA step by step.
/* Function name: ADC1_Mode_Config
* Description: Configure ADC1 to work in MDA mode
* Input: None
* Output: None
* Call: Internal call
*/
static void ADC1_Mode_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
/* Set the registers related to DMA to their initial values*/
DMA_DeInit(DMA1_Channel1);
/*Define the DMA peripheral base address. Here, ADC1_DR_Address is defined by the user, which is the register for storing conversion results. Its function is to tell DMA to fetch data from ADC1_DR_Address. */
DMA_InitStructure.DMA_PeripheralBaseAddr =ADC1_DR_Address;
/*Define the memory base address, that is, tell DMA to put the number taken from AD into ADC_ConvertedValue*/
DMA_InitStructure.DMA_MemoryBaseAddr =(u32)&ADC_ConvertedValue;
/*Define AD peripheral as the source of data transmission, that is, tell DMA to take data from AD and put it into memory, not vice versa*/
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
/*Specify the size of the DMA buffer of the DMA channel, that is, tell the DMA to open up several memory spaces. Since we only take the AD data of channel 10, we only need to open up one memory space*/
DMA_InitStructure.DMA_BufferSize = 1;
/*Set the register address to be fixed, that is, tell DMA to only fetch data from a fixed location*/
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
/*Set the memory address to be fixed, that is, each time DMA is used, only the data is moved to the fixed memory*/
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
/*Set the peripheral data width, that is, tell DMA the size of the number to be taken*/
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
/*Set the memory width*/
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
/*Set DMA to work in circular buffer mode, that is, tell DMA to keep moving data and not be lazy*/ DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
/*Set the software priority of the channel selected by DMA*/
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA channel1. The CPU has several DMA secretaries. Now only DMA1_Channel1 is used.
This secretary*/
DMA_Cmd(DMA1_Channel1, ENABLE);
/*Set ADC to work in independent mode*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
/*Specify that the AD conversion works in single-shot mode, that is, sampling one channel*/
ADC_InitStructure.ADC_ScanConvMode = DISABLE ;
/*Set AD conversion in continuous mode*/
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
/*Do not use external trigger conversion*/
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; /*The collected data is stored in the register in a right-aligned manner*/
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
/*Set the number of AD channels to be converted*/
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
/*Configure ADC clock to be 8-divided by PCLK2, i.e. 9MHz*/
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
/*Configure ADC1 channel 11 to 55.5 sampling cycles*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/*Reset calibration registers*/
ADC_ResetCalibration(ADC1);
/*Wait for calibration register reset to complete*/
while(ADC_GetResetCalibrationStatus(ADC1));
/* ADC calibration */
ADC_StartCalibration(ADC1);
/* Wait for calibration to complete */
while(ADC_GetCalibrationStatus(ADC1));
/* Since no external trigger is used, the ADC conversion is triggered by software*/
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
After configuring the above program, every time AD is converted, DMA will move the conversion result to the variable
ADC_ConvertedValue, instead of using assignment statements every time to get the AD converted value.
Part 2: AD Multi-channel Sampling
#include "adc.h"
#define ADC1_DR_Address ((u32)0x40012400+0x4c)
/*Define array variable ADC_ConvertedValue[2] to store the converted data of AD1 channels 10 and 11*/
__IO uint16_t ADC_ConvertedValue[2];
/*
* Function name: ADC1_GPIO_Config
* Description: Enable the clocks of ADC1 and DMA1, and set PC0 and PC1 as analog input
* Input: None
* Output: None
* Call: Internal call
*/
static void ADC1_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable DMA clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* Enable ADC1 and GPIOC clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
}
/* Function name: ADC1_Mode_Config
* Description: Configure ADC1 to work in MDA mode
* Input: None
* Output: None
* Call: Internal call
*/
static void ADC1_Mode_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
/* DMA channel1 configuration */
DMA_DeInit(DMA1_Channel1);
/*Define the DMA peripheral base address, which is the register that stores the conversion result*/
DMA_InitStructure.DMA_PeripheralBaseAddr =ADC1_DR_Address; /*Define memory base address*/
DMA_InitStructure.DMA_MemoryBaseAddr
=(u32)&ADC_ConvertedValue;
/*Define AD peripheral as the source of data transmission*/
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
/*Specify the size of the DMA buffer of the DMA channel, that is, how many memory spaces need to be opened. This experiment has two conversion channels, so two are opened*/
DMA_InitStructure.DMA_BufferSize = 2;
/*Set register address fixed*/
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; /*Set the memory address to increment, that is, each DMA transfers the value in the peripheral register to two memory spaces*/
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; /*Set peripheral data width*/
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
/*Set the memory width*/
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
/*Set DMA working recirculation buffer mode*/
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
/*Set the software priority of the channel selected by DMA*/
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
/* Enable DMA channel1 */
DMA_Cmd(DMA1_Channel1, ENABLE);
/*Set ADC to work in independent mode*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
/*Specify that the AD conversion works in scan mode, that is, sampling multiple channels*/ ADC_InitStructure.ADC_ScanConvMode = ENABLE;
/*Set AD conversion in continuous mode*/
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
/*Do not use external trigger conversion*/
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
/*The collected data is stored in the register in a right-aligned manner*/
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
/*Set the number of AD channels to be converted*/
ADC_InitStructure.ADC_NbrOfChannel = 2;
ADC_Init(ADC1, &ADC_InitStructure);
/*Configure ADC clock to be 8-divided by PCLK2, i.e. 9MHz*/
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
/*Configure the conversion order and sampling time of channels 10 and 11 of ADC1 to be 55.5 sampling cycles*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 2, ADC_SampleTime_55Cycles5);
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/*Reset calibration registers*/
ADC_ResetCalibration(ADC1);
/*Wait for calibration register reset to complete*/
while(ADC_GetResetCalibrationStatus(ADC1));
/* ADC calibration */
ADC_StartCalibration(ADC1);
/* Wait for calibration to complete */
while(ADC_GetCalibrationStatus(ADC1));
/* Since no external trigger is used, the ADC conversion is triggered by software*/
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
The differences between single-channel sampling and multi-channel sampling are
The second program is marked in red, pay attention to the comparison.
Summary: DMA is a selfless porter. If you want to put the value in the peripheral register into the memory, it originally needs the CPU to complete it. Now DMA helps the CPU to complete it, which liberates the CPU to a certain extent.
Previous article:How does the STM32 library read and write some pins of the same group of IO ports?
Next article:Detailed explanation of STM32 firmware library
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- Rambus Launches Industry's First HBM 4 Controller IP: What Are the Technical Details Behind It?
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- TI Award-winning Live Broadcast: Talking about "packaged antenna" smart millimeter wave sensors, even novices can handle industrial robots
- The voltage of the TPS73033 buck chip drops to 0.7V after the load is connected
- How to use MSP430 to implement PWM signal
- [Zero-knowledge ESP8266 tutorial] Quick start 29 How to use the nine-axis sensor module
- MicroPython adds self-power and power consumption parameters to USB
- [New Year's Festival Competition] + More than 200 girls walk
- Introduction to PLC, excerpted from "Control Engineer"
- Postpartum lamp - what kind of light do women need during confinement?
- Introduction to RF5 Architecture
- The relationship between standard, bandwidth, and baud rate/speed