一、原理图) v: G2 {% `! k
3 Q$ g; b) V) v2 A# C
* {# I5 ]7 Y5 M6 m3 V* G6 }
% L' o# A5 c( o( }4 q2 ]二、 CubeMX配置
1 h3 u" v% \, X! G% @ @- ?5 u( E. n5 \/ C7 F$ d
Step1.打开 STM32CubeMX,点击“New Project”,选择芯片型号,STM32F103VETx。
* H1 B0 C( B9 s$ [8 m0 m5 `' l: N( [0 [
1 }. n0 D! S1 D8 ?+ G% k) Z D
, R* _& L3 \' D2 E Step2.选择时钟源,并配置时钟树。选择Crystal/Ceramic Resonator,并配置系统时钟为72M。
( l+ |# a: k. ^! b$ V+ E( t+ ?. i/ `
$ ?% g# e' L; N
, B! X% {: B. }" B, p6 T
* K% n& H. b, i8 q# T
% q1 H: f" C6 T7 e$ _% s, DStep3.配置SYS,我们这里选择的是Serial Wire。(正常情况配置不配置不影响,debug可以使用。但是你不可以把这两个引脚用于其他复用功能,如果用于其他复用功能,debug就不起作用了。)
' B _: ]! X/ ~2 y+ u
& G- s L+ E& f: h0 f3 W
. x- r- ~& h0 C9 H7 g" `
* l ]2 L# Q$ v7 v. U" }( E& ^6 e Step4.串口配置(主要为了在串口调试助手显示测试的电压),因为没有用到中断和DMA所以我们就不过多讲解。
8 I: b. z0 E" I# F* U$ s0 e( t6 L7 u9 B2 B
. c! Q9 b2 n* |* ?( [. j
. ?) R- V7 _0 d( x+ m
/ L' h" l, \1 F' b' v. @: {2 O) K0 }3 `) a2 a, \1 x' @! b& t
Step5.ADC的配置
' n. w0 s2 A) s# b4 ~% K. p
0 d. H w8 ~$ I. B0 p# G5 o
$ p7 K, h+ j; d4 l4 _5 |# [! P6 @( s! B9 m
9 O" O' o% q% }- i( K7 f
" v( ]& ^0 y/ K& @7 |Step6.因为多个通道需要使用到DMA传输到内存,否则ADC的存放转换数据的寄存器会不断覆盖上一个转换的数据。DMA模式:循环模式,重复传输,而常规模式则是传输1次。地址自增,因为ADC外设本身就只有一个寄存器所以不存在地址自增的情况,而内存是需要地址自增来存放不同通道的转换结果,如果不自增,则会覆盖上一次的结果。数据宽度:根据传输数据决定,因为此次传输的是12位的数据,所以使用半字(2个字节)就可以了。
9 ^6 ~ L9 E: M* I2 ]/ D, ~
( ]; M9 P6 Z8 L8 t6 @5 _% O' `
/ |) D0 L/ K& T+ a, n6 b# t# I1 @- s
! v, m" A6 L, p( Q3 `
4 B% W1 P" g) n! H7 ]/ f K) w& r3 G& v( f" K& E9 n( v9 P3 P& C4 T5 p
2 d4 ?# s5 L! I- f: U4 X e2 g& G1 [
到这里关于ADC参数配置基本已经完成,只需要根据之前文章《STM32Cube HAL:GPIO输入/输出(一)》Step4-Step8,设置相关工程参数和生成代码。
2 r& u6 t- Z T( b8 Z& l0 F2 t' z, M- h8 N
三、添加功能代码! Z1 _( `" J* N5 _ h3 S, z
& X# G( O I$ @ ^) u7 x1、我们等会会向串口调试助手发送数据,进行实验结果的验证。 发送数据我们采用printf函数,所有需要重定向c库函数printf到串口。注意使用时需要在keil设置中勾选微库(use mircolib),同时需要添加头文件#include <stdio.h>。重定向代码如下(usart.c)
9 J& v( F, A* ?* w3 [
1 O j v0 v* Z- //重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数
1 d& T5 g) |, H2 E5 ` - int fputc(int ch, FILE *f)
. B0 g" Y' P, `1 B ` - {
! \: {, u- @3 N: x' R0 g - /* 发送一个字节数据到串口DEBUG_USART */5 i; g- Q" E% \4 w! }
- HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
9 q; j8 t. G9 ^8 J/ Y4 {* T - * x& p& p. S6 i" Z
- return (ch);1 d0 G0 c+ ^! s }, v0 I2 i1 P
- }
; M% y6 \& I2 \+ a% j/ @" W" {
' b8 Q( W/ Z) T5 H# A4 q3 J- //重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
4 f- L& Z/ T* s' N - int fgetc(FILE *f)! F( ]& Q( U, ]! ~: b# N$ f
- {
9 P5 P5 i9 Y' e6 e% K( s - int ch;
5 i$ c* c4 Z; y2 X: d. U - HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 1000);
" Q$ w, R$ L) s/ T3 c2 y T - return (ch);! s# R/ c, [, a' t4 H
- }+ u ?) Z6 [6 X8 c& ?; |$ D
复制代码 " c4 c9 g i2 {5 P3 p8 @
2、定义相关变量,以及使能相关ADC和处理代码(main.c)+ x% U1 R9 }0 H7 o8 U/ E
0 _3 N* ~1 |# B( ~, u' S
- //全局变量
6 z V7 b; `0 R U# q, V - uint16_t conv_value[6];//用于存放DMA传输的数据
3 S ?/ O$ S# r/ [- a2 [, g2 y - float voltage[6];//计算后的电压
复制代码- //使能相关外设
# |% @% k" G& N" V4 e - HAL_ADC_Start_DMA(&hadc1,(uint32_t*)conv_value,6);//开启ADC转换以及DMA传输,6是DMA传输的次<span style="background-color: rgb(255, 255, 255);">数</span>
复制代码- //在WHILE(1)中,输出电压值$ u4 s V0 ?0 m0 [, w) s0 ?
- while (1)
4 _7 S3 O3 W+ n( {9 k' f2 k3 a - {+ }6 I8 A$ x$ P5 B; D' x2 I
- voltage[0]=(float)conv_value[0]*3.3/4096;//通过转化的值计算电压值
* n/ F+ i& E- ^4 y/ W: J# D' L$ s - voltage[1]=(float)conv_value[1]*3.3/4096;
6 S3 U' C7 c# b4 ^# U; D' H - voltage[2]=(float)conv_value[2]*3.3/4096;! R+ [* P& F( l9 K7 }# b
- voltage[3]=(float)conv_value[3]*3.3/4096;4 D5 G$ {; \! |! ^* y! V! Y
- voltage[4]=(float)conv_value[4]*3.3/4096;9 q( C* f+ N+ z2 H
- voltage[5]=(float)conv_value[5]*3.3/4096;
* j: v0 B# y- `4 z( {$ ~2 V( D - $ u* l$ v/ g$ [" N! M1 f
- printf("\n\rCH0 voltage=%.2fV\n\r",voltage[0]);//输出电压值) J5 N* Y) d/ r" Y0 o& d( d( [( U
- printf("\n\rCH1 voltage=%.2fV\n\r",voltage[1]);: \4 O& o+ A6 B k8 k" j, X6 o; R
- printf("\n\rCH2 voltage=%.2fV\n\r",voltage[2]);
6 K1 G9 ?" V4 J - printf("\n\rCH3 voltage=%.2fV\n\r",voltage[3]);
5 O2 b& ^0 J' ]& j2 H7 l0 e - printf("\n\rCH4 voltage=%.2fV\n\r",voltage[4]);
1 Q0 A* O2 Q( k4 x- |7 }- k2 j: X( o - printf("\n\rCH5 voltage=%.2fV\n\r",voltage[5]);. n' v3 [; M0 |3 ~0 a7 r
-
+ p% x( ], i; T( A9 u: Y - HAL_Delay(1000);//延时1秒 ' I; j0 }; _, U4 T
- }
复制代码
' E7 c5 Y& w& A8 X5 K7 t) P, T, t' H4 E' a0 U
在实验中主要注意的是函数% h2 B8 W& T: J3 F! z) V
6 R/ v% ?/ G7 \
HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
- x; U2 F0 N# T# G7 v1 b, _; k7 [6 L& h. y& I
hadc:adc句柄
6 w' T, V! y' A _$ [( f
4 c5 \; m# \! w) U' k8 Y9 P- m. JpData:buffer缓冲区指针
; |% d: w" ?1 `' L3 W& t; U- K6 }& q; h
Length:传输的数目(以数据宽度为单位)2 i5 Y! D- \' ]$ R7 t p' {
( G F2 y: T. \7 |/ W$ B
例: 1:5个通道,每个通道传输1个,一共传输5个 。那么Length=5;* O5 [7 R( i: O/ {. e" L7 s
3 I. K$ f5 V: O: k 2:1个通道,每个通道传输5个,一共传输5个。那么Length=5;
) J- Q' C" C2 l0 S5 ~" K
" a1 Q V: k" b! O8 S 关于这个函数的理解,如果有错误的地方,欢迎指出。
6 @' Q+ a# |( X
& m* \- w! P6 t' i关于length的说法,网上有些文章是说这个Length是和传输的数据宽度有关的,如果传输的是 word的话,那么Length,有多少通道就设置多少;如果是half word,则Length,是通道数的2倍。(暂时没理解)
3 |( {% [- L8 w ? I
' R& ~8 o7 b9 @# c* ?; u! @8 v5 ^) k1 X }$ K7 U4 ^
|