This article was originally written by Guan Haitao. The original author has all rights reserved. For more information, see the source.
As an industrial chip, ADC sampling is a very important peripheral. Stm32 integrates three 12-bit, 18-channel internal ADC, with a maximum speed of 1 microsecond. Combined with DMA, it can free up the CPU for better processing.
Other logic functions on the ADC interface include:
● Synchronous sampling and holding
● Cross sampling and holding
● Single Sampling
The simulated watchdog function allows you to precisely monitor one, multiple, or all selected channels. When the monitored signal exceeds the preset threshold, interruption occurs.
Events generated by the standard timer (timx) and advanced control timer (tim1 and tim8) can be triggered internally cascade to the start and injection of the ADC, respectively, the application can synchronize the ad ing with the clock.
12-bit ADC is a successive approximation analog digital converter. It has up to 18 channels and can measure 16 external and 2 internal signal sources.
The input clock of the ADC cannot exceed 14 MHz, which is produced by pclk2.
If the analog voltage to be converted by the ADC is lower than the low threshold value or higher than the high threshold value, the AWD simulated watchdog status is set.
For the relationship between ADC sampling and DMA, refer to the following description on the Internet:
What are the advantages of stm32?
In addition to the publicity link, analyze it in detail.
Stm32 clock is not fast, 72 MHz,
Nor can large-capacity Ram flash be expanded,
It is also not as powerful as DSP instruction sets.
What are its advantages?
--- You can quickly collect and process data.
Arm features convenience.
This fast collection, high-performance ADC is a good embodiment,
12-bit precision, the fastest conversion speed of 1 US, usually with more than 2 independent ADC controllers,
This means that,
Stm32 can quickly collect multiple analog numbers at the same time,
This feature is not available in general MCU.
The above high-performance ADCs are supported by relatively small instruction sets and some special algorithms,
It constitutes a powerful feature of stm32 in motor control.
Well, the question is, how can we make a simple ADC? Note that it is simple,
ADC is a complex problem involving hardware design, power quality, reference voltage, signal preprocessing, and so on.
We will only discuss how to complete the ADC once in the MCU.
Speaking of ADC, we need to introduce another important device DMA for the first time.
What is DMA.
This concept is rarely found in the era of 8-bit single-chip microcomputer.
When more external resources are available,
The main processor and peripherals of an mcu are used.
The main processor is of course the main part of executing our commands,
Peripherals are devices that use serial I2C ADC to implement specific functions.
Recall what is the most common task of our main processor in the 8-bit era?
Logical judgment? No. Just a few commands
Computing algorithm? No. Most of the time, algorithms are simple.
In fact, the main processor is a porter,
Receives and saves usart data.
Receives and saves the ADC data.
Store the data to be sent and store them in usart one by one.
............
To solve this conflict,
People think of a way to establish a channel between peripherals and memory,
With the master processor's permission,
Allow the peripherals and memory to read and write directly, thus releasing the master processor,
This is DMA.
For example:
A mcu is a company.
The boss is the main processor.
Employees are peripherals
Warehouse is memory
In the past, everything in the warehouse was managed by the boss.
If employees need to work with raw materials, they will report them to the boss one by one, and the boss will take them one by one in the warehouse.
What employees do is to give to the boss one by one and put the boss in the warehouse one by one.
The boss is very tired. Although the boss is Superman, he cannot stand more and more employees and orders.
Finally, the boss hired a warehouse keeper, Which is DMA.
He is responsible for warehouse receiving and warehouse picking,
You only need to show the warehouse picking and warehouse receiving plan to the boss
The boss said OK.
Subsequent warehouse receiving and warehouse picking Processes,
Employees only need to deal with the warehouse keeper.
-------- Gossip, Ma qishi often wants to enable DMA between the device and the device.
The metaphor is complete.
ADC is a high-speed device, as mentioned above.
In addition, the data collected by the ADC cannot be directly used. Even if you carefully design the peripheral circuit, unexpected data will always appear.
In general, it is to collect a batch of data and then process it. This process is software filtering.
DMA is suitable here. Enable high-speed acquisition of ADC, and fill the data in RAM with a certain number, such as 32 and 64 MCU.
----- One more sentence means that a single ADC is meaningless.
Next we will introduce how to use DMA for ADC operations.
The initialization function consists of two parts: DMA initialization and ADC initialization.
We have multiple administrators -- DMA
Of course, an administrator manages more than one DMA Operation. Therefore, DMA has multiple channels.
Program analysis is as follows:
The program is implemented based on stm32f103vet6 and library functions.
RCC section: (ignore system clock configuration)
// Start the DMA clock
Rcc_ahbperiphclockcmd (rcc_ahbperiph_dma1, enable );
// Start the adc1 clock
Rcc_apb2periphclockcmd (rcc_apb2periph_adc1, enable );
Gpio Section: (for the ADC pins, see the table above)
// Adc_ch10 --> pc0
Gpio_initstructure.gpio_pin = gpio_pin_0;
Gpio_initstructure.gpio_mode = gpio_mode_ain; // simulate Input
Gpio_init (gpioc, & gpio_initstructure );
// PC2
Gpio_initstructure.gpio_pin = gpio_pin_2;
Gpio_initstructure.gpio_mode = gpio_mode_ain;
Gpio_init (gpioc, & gpio_initstructure );
Adc1 configuration: (two external inputs, and an internal temperature sensor)
Void adc1_configuration (void)
{
Adc_inittypedef adc_initstructure;
Adc_initstructure.adc_mode = adc_mode_independent; // The conversion mode is independent, and there are various options such as crossover.
Adc_initstructure.adc_scanconvmode = Enable;
Adc_initstructure.adc_continuousconvmode = Enable; // enables continuous conversion.
Adc_initstructure.adc_externaltrigconv = adc_externaltrigconv_none;
Adc_initstructure.adc_dataalign = adc_dataalign_right;
Adc_initstructure.adc_nbrofchannel = 3; // you can specify the length of the conversion sequence as 3.
Adc_init (adc1, & adc_initstructure );
// ADC built-in Temperature Sensor enabling (do not turn it on if you want to use an in-chip temperature sensor)
Adc_tempsensorvrefintcmd (enable );
// Conventional Conversion sequence 1: Channel 10
Adc_regularchannelconfig (adc1, adc_channel_10, 1, adc_sampletime_239cycles5 );
// Conventional Conversion sequence 2: Channel 16 (internal temperature sensor), sampling time> 2.2us, (239 cycles)
Adc_regularchannelconfig (adc1, adc_channel_16, 2, adc_sampletime_239cycles5 );
Adc_regularchannelconfig (adc1, adc_channel_1, 3, adc_sampletime_239cycles5 );
// Input parameters: ADC peripherals, ADC channels, conversion sequence, sampling time
// Enable adc1
Adc_cmd (adc1, enable );
// Enable the DMA support of the ADC (to implement the DMA function, you also need to independently configure parameters such as the DMA channel)
Adc_dmacmd (adc1, enable );
// The following is the automatic calibration of the ADC, which needs to be performed once after startup to ensure accuracy.
// 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 automatic calibration completion ---------------
Adc_softwarestartconvcmd (adc1, enable); // ADC startup
}
DMA configuration: (no software filtering)
Void dma_configuration (void)
{
Dma_inittypedef dma_initstructure;
Dma_deinit (dma1_channel1 );
Dma_initstructure.dma_peripheralbaseaddr = adc1_dr_address; // DMA peripheral address, defined in the header
Dma_initstructure.dma_memorybaseaddr = (u32) & ad_value; // memory address
Dma_initstructure.dma_dir = dma_dir_peripheralsrc; // peripherals to memory mode
// Buffersize = 2 because the ADC conversion sequence has two channels
// Set the result of sequence 1 to ad_value [0], and result of Sequence 2 to ad_value [1].
Dma_initstructure.dma_buffersize = 3; // three values are converted at a time.
Dma_initstructure.dma_peripheralinc = dma_peripheralinc_disable; // after one request is received, the device address is not moved back.
Dma_initstructure.dma_memoryinc = dma_memoryinc_enable; // after receiving the request, the memory address is moved back.
Dma_initstructure.dma_peripheraldatasize = dma_peripheraldatasize_halfword; // transmit a half word each time
Dma_initstructure.dma_memorydatasize = dma_memorydatasize_halfword;
// The loop mode is enabled. When the buffer is full, the system automatically returns to the initial address to start transmission.
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 );
// After the configuration is complete, start the DMA Channel
Dma_cmd (dma1_channel1, enable );
}
This DMA routine is used for one-time ADC conversion. With the software filter, the following changes can be made:
Global declaration:
V2010ad_value [30] [3]; // ad sample value
V2010after_filter [3]; // After ad Filtering
DMA part: (with interrupt filtering)
Void dma_configuration (void)
{
Dma_inittypedef dma_initstructure;
Dma_deinit (dma1_channel1 );
Dma_initstructure.dma_peripheralbaseaddr = adc1_dr_address;
Dma_initstructure.dma_memorybaseaddr = (u32) & ad_value;
Dma_initstructure.dma_dir = dma_dir_peripheralsrc;
// Buffersize = 2 because the ADC conversion sequence has two channels
// Set the result of sequence 1 to ad_value [0], and result of Sequence 2 to ad_value [1].
Dma_initstructure.dma_buffersize = 90;
Dma_initstructure.dma_peripheralinc = dma_peripheralinc_disable;
Dma_initstructure.dma_memoryinc = dma_memoryinc_enable;
Dma_initstructure.dma_peripheraldatasize = dma_peripheraldatasize_halfword;
Dma_initstructure.dma_memorydatasize = dma_memorydatasize_halfword;
// The loop mode is enabled. When the buffer is full, the system automatically returns to the initial address to start transmission.
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 );
// After the configuration is complete, start the DMA Channel
Dma_cmd (dma1_channel1, enable );
Dma_itconfig (dma1_channel1, dma_it_tc, enable); // enable DMA transmission to complete interruption
}
Objective C:
Nvic_initstructure.nvic_irqchannel = dma?channel=irqchannel;
Nvic_initstructure.nvic_irqchannelpreemptionpriority = 1;
Nvic_initstructure.nvic_irqchannelsubpriority = 0;
Nvic_initstructure.nvic_irqchannelcmd = Enable;
Nvic_init (& nvic_initstructure); // enable the DMA interrupt
File stm32f10x_it.c:
Void dma1_channelpolicirqhandler (void)
{
If (dma_getitstatus (dma1_it_tc1 )! = Reset)
{
Filter ();
Dma_clearitpendingbit (dma1_it_tc1 );
}
}
Filtering part: (mean filtering)
# Define N 30
Void filter (void)
{
Int sum = 0;
U8 count, I;
For (I = 0; I <2; I ++)
{
For (COUNT = 0; count <n; count ++)
{
Sum + = ad_value [count] [I];
}
After_filter [I] = sum/N;
Sum = 0;
}
}
Sample Data and actual voltage/temperature conversion:
2010gettemp (2010advalue)
{
U32 vtemp_sensor;
S32 current_temp;
// After the ADC conversion ends, read the results in the adc_dr register. The formula for calculating the conversion temperature value is as follows:
// V25-vsense
// T (℃) = ------------ + 25
// Avg_slope
// V25: the output voltage of the temperature sensor at 25 ℃, typically 1.43 v.
// Vsense: the current output voltage of the temperature sensor. The Conversion Relationship between the current output voltage and the result adc_convertedvalue in the adc_dr register is:
// Adc_convertedvalue * VDD
// Vsense = --------------------------
// Vdd_convert_value (0 xfff)
// Avg_slope: The correlation parameter between the output voltage and temperature of the temperature sensor. The typical value is 4.3 mV/℃.
Vtemp_sensor = advalue * 330/4096;
Current_temp = (s32) (143-vtemp_sensor) * 10000/43 + 2500;
Return (S16) current_temp;
}
2010getvolt (2010advalue)
{
Return (330/4096) (advalue );
}
The idea of filtering is that the ADC normally samples three channels consecutively, which are carried by DMA and 90 data records are transferred at a time, namely, 1-2-3-2 cycles, each of which is 30 times in a channel, in ad_value [30] [3], 30 indicates 30 data entries per channel, and 3 indicates three channels. This process is automatically completed based on two-dimensional array storage. When the DMA process ends, the DMA interruption is triggered, and the 30 data averages are input into the filter function and saved to after_filter [3]. The entire process of filtering calculation requires the CPU to participate, and the sampling result values in the program are always up-to-date, trying to solve Program Complexity and CPU load. X = getvolt (after_filter [0]); to obtain the real-time voltage value.
Reference this article: stm32 manual, "Software Filtering Method of ADC data and its example program", "The following section describes the application of ADC of stm32" are all from the Internet
This article is reposted from Ghost → wind, blow off the stm32
ADC combined with DMA data sampling and Software Filtering