|
概述 本章STM32CUBEMX配置STM32F103的ADC在DMA模式扫描多个通道,通过串口进行打印。
. J, a' }+ B' z生成例程
% _1 ?) I' K. [4 U7 J+ f 使用STM32CUBEMX生成例程,这里使用NUCLEO-F103RB开发板。 - h E: F/ \ F' X
4 t* `1 J3 X& D" J1 I
查看原理图,PA2和PA3设置为开发板的串口。
" z% k {+ v% B5 J
5 D4 D2 V; y/ x4 \7 K 配置串口。 6 z. P: C$ w4 N! j
4 A: B) G4 I) {4 f 开启中断。
6 {( V8 X* M4 L0 J, Z" b5 t- C1 f9 H, o* P( L
查看原理图,Arduino的接口A0-A5都是AD口。 3 |! O, F0 O/ n: r A9 ^
$ G7 r, {. t3 q. ?( l% A$ r
ADC通道配置
; p1 s- k5 |3 Z6 @9 i. r- Q7 F5 \# O+ w! x" L+ a& c
& t) k+ L9 K3 e
ADC1配置。
6 j! x3 N6 l, P7 M& u/ p2 p" i; x, D8 R9 u1 N7 u
● ADCs_Common_Settings: ○ Mode: Independent mod 独立 ADC 模式,当使用一个 ADC 时是独立模式,使用两个 ADC 时是双模式,在双模式下还有很多细分模式可选,具体配置 ADC_CR1 UALMOD 位。 ● ADC_Settings: ○ Data Alignment: Right alignment 转换结果数据右对齐,一般我们选择右对齐模式。 Left alignment 转换结果数据左对齐。 ○ Scan Conversion Mode: Disabled 禁止扫描模式。如果是单通道 AD 转换使用 DISABLE。 Enabled 开启扫描模式。如果是多通道 AD 转换使用 ENABLE。 ○ Continuous Conversion Mode: Disabled 单次转换。转换一次后停止需要手动控制才重新启动转换。 Enabled 自动连续转换。 ○ DiscontinuousConvMode: Disabled 禁止间断模式。这个在需要考虑功耗问题的产品中很有必要,也就是在某个事件触发下,开启转换。 Enabled 开启间断模式。 ● ADC_Regular_ConversionMode: Enable Regular Conversions 是否使能规则转换。 Number Of Conversion ADC转换通道数目,有几个写几个就行。 External Trigger Conversion Source 外部触发选择。这个有多个选择,一般采用软件触发方式。 ● Rank: Channel ADC转换通道 Sampling Time 采样周期选择,采样周期越短,ADC 转换数据输出周期就越短但数据精度也越低,采样周期越长,ADC 转换数据输出周期就越长同时数据精度越高。 ● ADC_Injected_ConversionMode: Enable Injected Conversions 是否使能注入转换。注入通道只有在规则通道存在时才会出现。 ● WatchDog: Enable Analog WatchDog Mode 是否使能模拟看门狗中断。当被 ADC 转换的模拟电压低于低阈值或者高于高阈值时,就会产生中断。 DMA开启。
1 j+ z5 R0 K' E
' s& w: [+ J3 }2 B7 U/ Z3 w3 i 生成独立的文件。
* I* N! ~' D P9 ]2 t. q' A' C2 t$ z, i
STM32CUBEIDE配置9 C2 F/ U3 R0 J$ b" h- [
, c4 D8 T: v4 P( W5 @
0 {7 V# [* M* ?/ y8 E 若需要打印浮点型,需要勾选下面的选项。
1 g( Y( J- y, ]$ G1 I' z
$ x, J$ K) V$ q1 }0 m0 n1 D, j3 e" `! k
串口重定向
" e5 O1 F1 ~8 l! r0 _8 M: Z% F 在main.c中,添加头文件,若不添加会出现 identifier "FILE" is undefined报错。 - /* USER CODE BEGIN Includes */# z( r5 `# a% a/ z9 g
- #include "stdio.h"
: `7 y8 `) p' V3 w; l/ Y+ z9 I - /* USER CODE END Includes */
复制代码 ) @$ S' u. g8 e7 a+ U% Z
函数声明和串口重定向: - /* USER CODE BEGIN PFP */
5 P& q) r0 A4 P& I* f - #ifdef __GNUC__ //串口重定向
) X% R) _" S! s) P& M - #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)8 m7 `) J; n5 N8 ]
- #else) c8 n8 r% E& k4 E! j: Z5 h0 U
- #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
+ M% W |, Y1 l5 M - #endif
0 j3 J6 n, H4 }9 e - PUTCHAR_PROTOTYPE- O- X* W& o* u
- {6 z; R8 i7 I. F5 _" F# ?" N+ ]* n
- HAL_UART_Transmit(&huart2 , (uint8_t *)&ch, 1, 0xFFFF);/ v+ h7 r/ c' c/ `1 m9 |9 L
- return ch;
; J( T0 } K0 t& R' E - }
8 r4 X9 H5 W+ Q8 B+ i - /* USER CODE END PFP */
复制代码 ! m2 `$ Y% k1 p
] X4 T7 k/ Z# Y: P7 |5 ^代码5 e6 P& q$ r- y1 ^2 v, q8 {/ ?+ c2 W/ ]& P6 i
2 o! D$ \# G+ w ~# m 定义变量,存放采集到的数据。 - /* USER CODE BEGIN 0 */
# a5 t( @( _3 P - uint8_t i;% S+ r7 d3 W, F, b: z
- uint16_t adc1Buf[3];//ADC1数组
4 t4 [2 [; `4 L1 X! p, S: i4 p8 m - uint16_t adc2Buf[3];//ADC2数组# J2 L2 H4 R" E B2 d s) ^
- - n2 K! N0 j; [" C7 l% u3 F: t
- /* USER CODE END 0 */
复制代码 / X3 D0 h; Q. R7 f
使能ADC传输。 - /* USER CODE BEGIN 2 */
" G4 s+ t; q- k1 L1 L1 T8 E - HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1_Value,30); //使用DMA传输6 E$ `+ h1 e4 c- s4 n
- /* USER CODE END 2 */
复制代码 & x1 I+ h% ~+ L: g7 P- J
主循环。 - /* USER CODE BEGIN WHILE */
: v: M# c. G3 @5 o) b - while (1)
) t! I% ?& _$ i( w. p0 X$ y- N - {
& y8 Z* H$ [$ m - /* USER CODE END WHILE */- @# b) |6 e) h- V* x$ f
- ; y2 M5 g$ P0 P" Z8 e6 v
- /* USER CODE BEGIN 3 */: p0 V- N" V' N( W# k0 f3 J& l
- if(ADC1_Flag==1)& g: E& i8 o V# s" C
- {- y) S$ y3 a9 i/ h
- ADC1_Flag=0;
' \( _9 d1 W8 |/ k9 A - ADC1_1=0;
- Q* u' Z1 |. D$ U - ADC1_2=0;. m7 y8 \2 ~; s: Q: T, V/ G9 L! a# ^
- ADC1_3=0;
' A. q9 O& J' t4 ~) }% F - for(i=0;i<30;)
5 g! e( \% y+ U& L# n& b - {
; e2 k) `, f; J9 i7 T# \ - ADC1_1+=ADC1_Value[i++];
- n2 O! O p+ N* v. ?6 P - ADC1_2+=ADC1_Value[i++];
5 \. N# L0 L( J+ g - ADC1_3+=ADC1_Value[i++];
# `! x9 E3 w$ Q) F7 W7 k# {3 `) g - }2 n7 k, Q1 d* s, s% P+ m" ~
- printf("\n");
, x% _5 g* y K4 f/ s - printf("adc1_IN0(PA0)=%4.0d,ADC_IN0=%1.4f\r\n",ADC1_1/10,ADC1_1/10*3.3f/4096);# l8 w' Z8 z* d
- printf("adc1_IN1(PA1)=%4.0d,ADC_IN1=%1.4f\r\n",ADC1_2/10,ADC1_2/10*3.3f/4096);' d* p1 A/ f% X f$ V
- printf("adc1_IN4(PA4)=%4.0d,ADC_IN2=%1.4f\r\n",ADC1_3/10,ADC1_3/10*3.3f/4096);
6 o/ g( f0 d& m9 t$ W% E - HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC1_Value,30); //使用DMA传输
& s. `; A& T! t. r! }% f$ B - }" B3 x! q# U, i$ @4 Y2 ?+ p, v
- HAL_Delay(1000);
2 [4 K3 x( C- c6 Y - }
+ ?7 k# |$ Y7 v; _) y3 M - /* USER CODE END 3 */
复制代码 : g/ v, S5 {6 K! f. R. B
ADC回调函数。 DMA传输的时候如果读取内存片段,会有仲裁器的问题,加了一句关闭DMA的语句HAL_ADC_Stop_DMA(&hadc1);
2 \' C% V1 j& V- /* USER CODE BEGIN 4 */6 r6 w6 O. e6 {' W4 r
- void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)/ S$ U- m, Y& e2 d
- {5 y9 v- T" f* c$ P0 S1 V& _
- if(hadc->Instance == ADC1){; _" X+ }. ~' _( R3 f$ \) v& G
- ADC1_Flag=1;" x- R/ ^) ^, J% [3 t$ `) s2 z$ ^
- /*
" |% V( I6 d& }5 I- K - * DMA传输的时候如果读取内存片段,会有仲裁器的问题,加了一句关闭DMA的语. H( ^- H; C4 W" H8 q$ o5 K
- 7 `) `8 J+ m" y( P- u
- */( `5 e+ J3 S; a/ ]& V9 `
- HAL_ADC_Stop_DMA(&hadc1);& {" w3 Z1 P1 T, |+ ?6 g% |
- }
9 D! l$ A g5 u% ?. W T& C - }3 N: _3 X9 \7 W- D2 W& E
- /* USER CODE END 4 */
复制代码 8 K+ k# p1 }7 ?5 Y
测试结果8 X3 Q1 ^5 }) H3 a: v
输入固定电压进行测试。
( ?6 r4 `0 D6 B1 l7 X1 v . L. d1 u+ t+ R+ E* k" u9 u
d6 S; c' s2 p8 j- d9 T9 P5 U
$ G0 e9 d7 H2 d/ h) j" A Normal下测试结果如下。 ( S% z* u% c" p! _ ]
7 x1 e+ S# i, ?0 o0 r; ?4 N
若不试用关闭DMA的语句HAL_ADC_Stop_DMA(&hadc1); 会造成数据错乱。
9 T) ?+ Q2 t: _6 D2 h
4 \1 x+ l/ w& D- } Circular可以下可以一直进行采集,不需要HAL_ADC_Stop_DMA(&hadc1)都可。 u' U3 }, Y: ~ I% ^9 J5 f9 G, |+ ^
' I8 f; x! T6 G W0 Q* V
; r$ s1 i3 O, T |