STM32 ADC example (based on DMA)

Publisher:创新火花Latest update time:2018-05-14 Source: eefocusKeywords:STM32 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

ADC Introduction:

    ADC (Analog-to-Digital Converter). That is, converting analog signals into digital signals for processing. When storing or transmitting, analog-to-digital converters are almost indispensable.

   The ADC peripherals integrated on the STM32 chip are very powerful. The STM32F103VET6 development board I use has an enhanced CPU. It has 18 channels and can measure 16 external and 2 internal signal sources. The A/D conversion of each channel can be performed in single, continuous, scan or intermittent mode, and the ADC result can be stored in a 16-bit data register in left-aligned or right-aligned mode.



ADC working process analysis:

   We analyze the ADC regular channel conversion process. As shown in the figure above, all devices are developed around the analog-to-digital converter in the middle. Its left end VREF+, VREF- and other ADC reference voltages, ADCx_IN0 ~ ADCx_IN15 are the ADC input signal channels, that is, some GPIO pins. The input signal is sent to the ADC device through these channels. The ADC device needs to receive a trigger signal to start the conversion, such as EXTI external trigger, timer trigger, or software trigger. After the ADC component receives the trigger signal, it samples the signal of the input channel under the drive of the ADCCLK clock and performs analog-to-digital conversion, where ADCCLK comes from the ADC prescaler.

    The converted value of the ADC component is saved in a 16-bit regular channel data register (or injected channel data register). We can read it into the memory (variable) through CPU instructions or DMA. After the analog-to-digital conversion, we can trigger a DMA request or trigger an ADC conversion end event. If an analog watchdog is configured and the collected voltage is greater than the threshold, the watchdog interrupt will be triggered.

   In fact, for ADC sampling, software programming is mainly about ADC configuration. Of course, I am based on DMA mode, so DMA configuration is also the key! Let's look at the code without further ado!

Main function: main.c

#include "printf.h"  

#include "adc.h"  

#include "stm32f10x.h"  

  

extern __IO uint16_t ADC_ConvertedValue;  

float ADC_ConvertedValueLocal;  

void Delay(__IO uint32_t nCount)  

{  

   for(;nCount !=0;nCount--);  

}  

int main(void)  

{    

  printf_init();      

  adc_init();  

  printf("******This is a ADC test******\n");  

          

  while(1)  

    {  

        ADC_ConvertedValueLocal =(float) ADC_ConvertedValue/4096*3.3;  

        printf("The current AD value =0x%04X\n",ADC_ConvertedValue);  

    printf("The current AD value =%f V\n",ADC_ConvertedValueLocal);  

          

    Delay(0xffffee);  

    }  

  return 0;   

}  

Note that ADC_ConvertedValueLocal saves the voltage value calculated from the conversion value. The calculation formula is: actual voltage value = ADC conversion value x LSB. Here, since the reference voltage connected to VREF+ of my board is 3.3V, LSB = 3.3/4096, and the accuracy of STM32's ADC is 12 bits.

ADC and DMA configuration: adc.c


#include "adc.h"  

volatile uint16_t ADC_ConvertedValue;  

void adc_init()  

{  

    GPIO_InitTypeDef GPIO_InitStructure;  

    ADC_InitTypeDef ADC_InitStructure;  

    DMA_InitTypeDef DMA_InitStructure;  

      

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);  

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_ADC1,ENABLE);      

  

    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;        

    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;  

    GPIO_Init(GPIOC,&GPIO_InitStructure);  

    DMA_DeInit(DMA1_Channel1);  

    DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//ADC地址  

    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue; //Memory address  

    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //Direction (from peripheral to memory)  

    DMA_InitStructure.DMA_BufferSize = 1; //Transfer content size  

    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //Peripheral address fixed  

    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable; //Memory address is fixed  

    DMA_InitStructure.DMA_PeripheralDataSize =   

    DMA_PeripheralDataSize_HalfWord; //Peripheral data unit  

    DMA_InitStructure.DMA_MemoryDataSize =   

    DMA_MemoryDataSize_HalfWord; //Memory data unit  

    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA mode: circular transmission  

    DMA_InitStructure.DMA_Priority = DMA_Priority_High; //Priority: High  

    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //Disable memory-to-memory transfer  

      

    DMA_Init(DMA1_Channel1, &DMA_InitStructure); //Configure DMA1's 4 channels  

    DMA_Cmd(DMA1_Channel1,ENABLE);  

   

    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //Independent ADC mode  

    ADC_InitStructure.ADC_ScanConvMode = DISABLE; //Disable scanning mode  

    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // Enable continuous conversion mode   

    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //Do not use external trigger conversion  

    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //Right-align the collected data  

    ADC_InitStructure.ADC_NbrOfChannel = 1; //Number of channels to be converted  

    ADC_Init(ADC1, &ADC_InitStructure);  

      

    RCC_ADCCLKConfig(RCC_PCLK2_Div8); //Configure ADC clock to be 8-divided by PCLK2, i.e. 9Hz  

    ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_55Cycles5); //Configure ADC1 channel 11 to 55.5 sampling cycles   

      

    ADC_DMACmd(ADC1,ENABLE);  

    ADC_Cmd(ADC1,ENABLE);  

  

    ADC_ResetCalibration(ADC1); //Reset calibration register  

    while(ADC_GetResetCalibrationStatus(ADC1)); //Wait for the calibration register to be reset  

  

    ADC_StartCalibration(ADC1); //ADC calibration  

    while(ADC_GetCalibrationStatus(ADC1)); //Wait for calibration to complete  

  

    ADC_SoftwareStartConvCmd(ADC1, ENABLE); //Since no external trigger is used, the ADC conversion is triggered by software  

}  

ADC configuration is relatively simple. After all, only a single channel is configured. Let's analyze it! Here I configure the GPIO pin PC1 used by channel 11 of ADC1 to analog input mode. When used as the input of ADC, analog input must be used. For ADC channels, each ADC channel corresponds to a GPIO pin port. After the GPIO pin is set to analog input mode, it can be used to input analog voltage. STM32F103VET6 has three ADCs, and these three ADCs share 16 external channels.

The overall configuration of DMA is: using channel 1 of DMA1, data is transferred from the data register of the ADC peripheral (ADC1_DR_Address) to the memory (ADC_ConvertedValue variable), the memory peripheral address is fixed, the size of each transfer is half word (16 bits), and the DMA circular transfer mode is used.


The peripheral address of DMA transfer, that is, the address of ADC1 is 0x40012400+0x4c. This address can be obtained by checking the STM32 datasheet, as shown in the figure;



Pay special attention to the ADC conversion time configuration. Since the higher the ADC clock frequency, the faster the conversion speed, does it mean that the ADC clock frequency should be set as high as possible? In fact, this is not the case. According to the ADC clock diagram, the ADC clock has an upper limit, which cannot exceed 14MHz, as shown in the figure:


Here, the input of the ADC pre-divider is the high-speed peripheral clock (PCLK2). The RCC_ADCCLKConfig() library function is used to set the division value of the ADC pre-divider. The common clock of PCLK2 is 72MHz, and ADCCLK must be less than 14MHz, so here ADCCLK is 6 times the frequency of PCLK2, that is, 12MHz. In my program, I just set it to 8 or 9MHz. If you want the ADC to run at the highest frequency of 14MHz, you can set PCLK2 to 56MHz, and then divide it by 4 to get ACCLK.

The conversion time of ADC is not only related to the ADC clock, but also to the sampling period. Each ADC channel can be set to a different sampling period. The calculation formula for the ADC sampling time of STM32 is:

  T = sampling period + 12.5 periods

The sampling period in the formula is the ADC_SampleTime configured in the function, and the 12.5 periods added later are fixed values, so the conversion time of ADC1 channel 11 is T=(55.5+12.5) x 1/9=7.56us.

Supplement: The ADC_ConvertedValue variable is defined in the adc.c file. Please note that this variable is modified by the keyword volatile. The role of volatile is to prevent the compiler from optimizing this variable. In this way, each time this variable is used, it must return to the memory of the corresponding variable to get the value. If volatile is not used for modification, the ADC_ConvertedValue variable may be directly taken from the CPU register when it is accessed (because the variable has been accessed before, that is, the value of ADC_ConvertedValue was taken from the memory and saved in a CPU register). The reason why the value is directly taken from the register instead of the memory is the result of the compiler optimizing the code (accessing the CPU register is much faster than accessing the memory). The CPU register here refers to the CPU general registers such as R0, R1, which are used for CPU calculations and temporary data storage, not the registers in the peripherals.

         Because the value of the variable ADC_ConvertedValue can be changed by the DMA controller at any time, it is modified with volatile to ensure that the real-time ADC conversion value is read each time.

adc.h:

#ifndef _adc_H  

#define _adc_H  

#include "stm32f10x.h"  

#include "stm32f10x_dma.h"  

#include "stm32f10x_adc.h"  

#define ADC1_DR_Address  ((uint32_t)0x4001244c);  

  

void adc_init(void);  

  

#endif 

Effect picture:


Since my development board does not have a sliding resistor, I connected the voltage input end to the 3V pin of the general IO port. As shown in the figure:


Keywords:STM32 Reference address:STM32 ADC example (based on DMA)

Previous article:STM32 Watchdog Summary
Next article:STM32F4 ADC1 analog watchdog [library function operation]

Recommended ReadingLatest update time:2024-11-16 17:53

STM32 USB Design--Hardware
The STM32 chip integrates USB peripherals, which greatly reduces the design burden of USB circuits. You only need to design the USB interface circuit to realize the USB communication design of the circuit board based on the STM32 chip. (This article will specifically describe the USB design based on the STM32F103RBT6 c
[Microcontroller]
STM32 USB Design--Hardware
STM32_DAC outputs triangular waveform
Today we will explain "STM32F103 DAC output triangular waveform", which is relatively basic, but it is still worth learning for friends who are using this function for the first time.   The software project provided and explained today is modified based on yesterday's software project "DDAC output triangle waveform".
[Microcontroller]
STM32_DAC outputs triangular waveform
Circular analog-to-digital converter circuit
Circular analog- to-digital converter circuit:
[Analog Electronics]
Circular analog-to-digital converter circuit
Analysis and arrangement of Zhilin STM32 program source code 01
1. Purpose 1. A few days ago, I wrote a program for keyboard scanning, character input and simple shell on the development board. The program was modified based on the previous project. The source code was organized in a mess and there were no good comments. In the past two days, I organized the code and added more de
[Microcontroller]
STM32 IAP upgrade has a small probability of running away
Use IAP technology to automatically upgrade the MCU. The official source code is generally modified according to needs. For example, because our products are everywhere, it is impossible to let construction workers press buttons one by one to trigger the upgrade, so the serial port command trigger upgrade is obviously
[Microcontroller]
STM32 learning notes: use of external interrupts
The status of interrupts in developing embedded systems is absolutely unquestionable. In the era of C51 microcontrollers, there were only 5 interrupts, including 2 external interrupts, 2 timer/counter interrupts and one serial port interrupt. However, in STM32, the number of interrupts has greatly increased, and the i
[Microcontroller]
STM32 CRC32 and corresponding software CRC32
Simple implementation of STM32 CRC32 usage Remember to enable the STM32 CRC clock before use //STM32 hardware CRC32 byte data calculation, move the data to the highest bit, and fill the low bit with FF   u32 stm32_crc32_byte(u8 *pBuff, u32 len)   {       u32 i;       u8 buff ;       u32 *p32 = (u32 *)buff;  
[Microcontroller]
【STM32】FSMC Application
【Time calculation】   From the formula: DATAST × HCLK = tWRLW, DATAST = 15/13.8 ns = 1.08, take 1.    Then, from the formula ((ADDSET + 1) + (DATAST + 1)) × HCLK = max(tCYC, tCYC(READ)), that is, ((ADDSET + 1) + (DATAST + 1)) × HCLK = 66, we can calculate ADDSET = 66/13.8 - 3 = 1.7, which is 2.     Finally
[Microcontroller]
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号