你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32F1的CAN通信之ADC

[复制链接]
攻城狮Melo 发布时间:2023-10-23 19:04
一、什么是ADC. x$ f" S7 ^3 P* n2 b8 N# W
ADC(Analogto-Digital Converter)模拟数字转换器,是将模拟信号转换成数字信号的一种外设。比如某一个电阻两端的是一个模拟信号,单片机无法直接采集,此时需要ADC先将短租两端的电压这个模拟信号转化成数字信号,单片机才能够进行处理。
# {3 O  \' Q0 F& l0 X

: |2 t& F8 ^& a- J% d" r二、ADC的用途
6 r* h" V( k* V. y: JADC具有将模拟信号转换成数字信号的能力,比如将模拟的电压转换成数字信号,单片机进行处理。可以用作温度监测或者电流监测等方面,用途极广。; F# y, t6 ]: {- h3 c

6 R3 G* n- [; y三、STM32F103ZET6的ADC
8 v4 p) x( q% E' |+ |! S0 [根据中文参考手册介绍,STM32F103ZET6单片机有3个12位ADC,共有18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。. M  k/ D/ e! o: ^/ M8 G* q" x

8 y; E/ ?6 y2 l6 {) K) E/ ~5 ~: N, M! j

1 X' p- O% G4 L& }3.1 ADC通道对应引脚) k7 k6 ~# }. I( d4 B+ O) {( o
STM32F103ZET6的ADC各通道对应IO如下' n6 r. |! Z5 d6 [

$ z" s, A$ ~  M" ?) C1 R
2LJFBR6J$G1TR[YF5Q9MQOF.png
7 z2 t3 A8 o4 I6 K, j   
- D" R) U. e! g3.2ADC时钟
3 G! u: Q# b& E. x6 tADC输入时钟ADC_CLK由APB2分频产生,最大值是14MHz。库函数提供了设置分频因子的函数
8 z2 [( T# l4 F3 j
  1. void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)
复制代码

& O, A( m8 U4 K) H& b, u  j! W; G可选的分频因子有3 c7 r6 z) }* B, u) d
  1. #define RCC_PCLK2_Div2                   ((uint32_t)0x00000000)
    ! k- L6 z& Y5 y; F& @/ [7 }4 q
  2. #define RCC_PCLK2_Div4                   ((uint32_t)0x00004000). N8 Q. }3 t) I. b7 a+ e7 @
  3. #define RCC_PCLK2_Div6                   ((uint32_t)0x00008000)- d: z% v0 L2 G) C
  4. #define RCC_PCLK2_Div8                   ((uint32_t)0x0000C000)
复制代码

5 K: K4 `8 ^* x0 a  q* fAPB2总线时钟为72MHz,而ADC的最大工作频率为14MHz,所以,分频因子一般设置为6,这样ADC的输入时钟频率为12MHz。& c  _( V3 l$ E" p

, w( z4 h8 J# o4 o/ z3.3 ADC工作模式
/ V1 v$ ^6 |' ]- r3 X1 g' R根据中文参考手册介绍,STM32F1的ADC有三种工作模式- J- [# v8 t' o/ L
• 单次转换模式 单次转换模式下,ADC只执行一次转换。该模式既可通过设置ADC_CR2寄存器的ADON位(只适用于规则通道)启动也可通过外部触发启动(适用于规则通道或注入通道),这时CONT位为0。8 h4 M) u. f1 I% R
$ S; z* e0 T+ {& S
• 连续转换模式 在连续转换模式中,当前面ADC转换一结束马上就启动另一次转换。此模式可通过外部触发启动或通过设置ADC_CR2寄存器上的ADON位启动,此时CONT位是1。- r% d( ~2 t! `. C( e

2 ^% H3 z/ t7 s; d/ s: @• 扫描模式' W6 X, M/ w2 A( O9 j

8 d- ~  }2 d# ]5 a
, ~8 l2 C( {, J# b3 M
33.4 ADC转换时间- }# D$ N' q! i4 n) e
ADC的总转换时间与时钟频率有关,总转换时间 = 采样时间 + 12.5个周期。其中,采样时间最短为1.5个周期,也就是最短转换时间为14个时钟周期。使用软件触发时,可选择的采样时间如下
4 u6 j" [) R0 {) a
  1. #define ADC_SampleTime_1Cycles5                    ((uint8_t)0x00)5 C% x& v# ~$ K" U
  2. #define ADC_SampleTime_7Cycles5                    ((uint8_t)0x01). [8 d3 v) O' R8 Q1 ]4 q1 N5 g
  3. #define ADC_SampleTime_13Cycles5                   ((uint8_t)0x02)
    ) R. X- S* m4 [. f3 q6 W+ Y* q/ l
  4. #define ADC_SampleTime_28Cycles5                   ((uint8_t)0x03). @$ x" z. f1 R$ k( h7 \' z
  5. #define ADC_SampleTime_41Cycles5                   ((uint8_t)0x04)
    ' \6 Y$ E  F( Y+ c
  6. #define ADC_SampleTime_55Cycles5                   ((uint8_t)0x05), F: Q8 p$ m+ s& f
  7. #define ADC_SampleTime_71Cycles5                   ((uint8_t)0x06)- |5 t7 ?9 B' k
  8. #define ADC_SampleTime_239Cycles5                  ((uint8_t)0x07)
复制代码
  ]1 H3 S1 v1 a. X5 V! ?
3.5 ADC校准
7 t4 P$ f* H# V9 U8 t$ m使能ADC后,需要对ADC进行校准。使用库函数开发时,提供了ADC校准的函数
7 E# s, D% e/ p+ ], {) ]# `: ]0 V2 h
  1. ADC_ResetCalibration(ADC1);//重置指定的ADC的校准寄存器$ M% i5 R( L4 Y* |  K
  2.     while(ADC_GetResetCalibrationStatus(ADC1));//获取ADC重置校准寄存器的状态
    " [  E. O& b, A% f) v5 F; d4 I
  3.     ) w& j# J% Z. B: E/ s9 n
  4.     ADC_StartCalibration(ADC1);//开始指定ADC的校准状态
    : \1 A, s3 a" t
  5.     while(ADC_GetCalibrationStatus(ADC1));//获取指定ADC的校准程序
复制代码

5 `0 O+ q: O; P, S3.6 ADC转换结果与实际电压的换算
3 [! j% Q+ t, n, C9 q: n
获取到的AD转换结果并不是实际电压,如果想要得到实际电压,需要经过换算。上面介绍了,STM32的ADC为12位,也就是AD值取值范围为0~4095。采集电压范围为0到3.3V。AD值与实际电压之间存在比例关系。
. h( p2 ]" Z0 g: f

! E2 z6 i: Q) V# Z7 y实际电压 = (AD值 / 4095) * 3.3。单位为伏特(V)
( t# B" R3 t; V; s! f1 Q) W
, y) J' o$ K7 r) c) n; m

" A6 P7 i; X/ n% ^) j( J四、ADC配置步骤
" O1 x& I$ K( ]• 使能GPIO时钟和ADC时钟,设置引脚为模拟输入
$ m# Z* d' P. w1 G$ X  ~( c• 设置ADC的分频因子
$ v! S/ W  M& r, X, {$ m3 f! h• 初始化ADC参数,包括ADC工作模式,规则序列等
6 R& d& u( J1 L9 r• 使能ADC并校准, H+ K3 x4 i) L1 f) r5 r
• 触发AD转换,读取AD转换值
8 i+ m! h7 D3 O8 n' N+ s. v4 ?1 W1 T5 ]9 w& H( _

+ ^2 l4 @9 Q5 V, D" V( `五、ADC配置程序+ V5 ~8 h( A' e6 g* f) Q: }
55.1 ADC初始化程序
9 y8 W  M1 M/ h# \* m; U! a
这里以配置ADC1的通道1为例,给出ADC的配置例程,分频因子设置为6,单次转换模式,软件触发。
0 @) q2 _$ F* h$ n  \
  1. /*  K  O0 Z8 D2 f. Z7 |2 S% f; f" X( a
  2. *==============================================================================1 |4 o9 D; v" X
  3. *函数名称:ADC1_Init
    " Q) E' F- e7 b7 o7 X
  4. *函数功能:初始化ADCx
    $ I. t" [' u" D9 z" m
  5. *输入参数:无
    $ [: F0 X$ q; L# |  G: R0 v+ ^
  6. *返回值:无0 ?7 I8 P; H0 L
  7. *备  注:无
    1 b% ~4 R' M' t. w/ t4 B" ?3 b
  8. *==============================================================================2 d( \; D6 o1 `6 y$ M% w) o
  9. */8 d( x& o0 A  m
  10. void ADC1_Init(void)
    . z! v8 V  t4 n' q3 _
  11. {# h1 K/ ^6 V2 r
  12.     // 结构体定义
    , [* `' x. z/ [* g2 q+ R/ j
  13.     GPIO_InitTypeDef GPIO_InitStructure;. R% M; B, Y. R
  14.     ADC_InitTypeDef ADC_InitStructure;
    ; l' w0 h6 A3 y( v6 M1 H
  15.    
    % g5 D3 i  D/ _9 P1 w% S# }
  16.     // 开启时钟
    2 ]1 L1 b3 J4 r' O
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);
    / p5 U) H% l( P2 @
  18.     1 s6 H2 H0 A# b+ e* d
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    . d; _8 m6 ?( z
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);- v; @' q9 ]& M7 X: z6 z
  21.    
    9 l& p) J$ R9 ]6 q6 S
  22.     // GPIO配置
    ! ~# Q1 F7 P9 r9 s
  23.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道11 Q5 A8 h7 ]% [- W' A. S9 @
  24.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入! r, {" R% ?5 R" w3 l9 d. x5 ]3 g( C! v
  25.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    6 E  N. H! Y& k1 l6 k
  26.     GPIO_Init(GPIOA,&GPIO_InitStructure);" Y; m3 w- B# B# K. {4 e# {  v+ |7 \
  27.    
    ; {8 V4 l4 A$ ^$ F7 r: W  b
  28.     // ADC参数配置. T# \6 @6 x, `" {1 q
  29.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式
    ( \- l2 t+ F4 Q# n8 |+ F8 r
  30.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式 9 p3 R/ z6 N, K/ A& C
  31.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换) j& v( m0 J- S; B+ B% @* u
  32.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   // 禁止触发检测,使用软件触发* W. Q& ]5 V8 j& p8 ~) ?3 K' ]) J
  33.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐 . e9 m( H4 U  x& Y# H
  34.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1
    7 t/ x' i0 Y* k4 V( z( y
  35.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化3 b; e9 {, Q- _, t6 E
  36.    
    9 [5 T  k& }! o/ A4 r
  37.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器/ `+ r1 y* Y9 C" F  z* x( R
  38.    
    , F( v4 b+ u7 e8 m6 @
  39.     // ADC校准2 H% A  X! ^* c9 h! |' S, j0 Y! w
  40.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器
    4 h. @( D, _! I! ?2 l
  41.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态' w: B  f3 `4 x# P1 A
  42.    
    9 O# R8 m: l9 P& [% P: g
  43.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态4 Z+ h: L* f. v$ {' H5 y2 d4 w
  44.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序9 r0 X* q2 V/ h
  45. ( m* s- F% U& X0 n
  46.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能
    ; D( I2 ]4 y6 Z, h/ k+ ^. M
  47. }
复制代码

- b8 ~; h: V2 ]% s# x5 E# q5.2 软件触发AD转换
* F5 S3 g" e' J+ }& j5 r" v6 |5 C库函数开发,配置为软件触发时,可以通过下面的函数触发AD转换
8 A! V9 P! ^! }. P7 S
  1. void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState)
复制代码

" t/ B# [/ {) L5 H4 ?8 d4 B5.3 读取AD转换结果
( ?( C- Q2 v$ _库函数提供了用于读取AD转换结果的函数6 o. K3 A' e0 s7 Y
  1. uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx)
复制代码
4 F9 p! }& E! C$ |& D( q: v
这里给出另一个函数,用于软件触发AD转换并读取转换结果; }& [* l( W8 S( Z" L
  1. /*( o: M! Q3 e+ |$ a6 D- V# \
  2. *==============================================================================
    : ~$ `# r  t0 K& p
  3. *函数名称:Get_ADC_Value2 e! n% y# Q8 E+ d: c5 M, Z) b
  4. *函数功能:读取某一规则通道AD值
    + t( `* C6 n' w; k& m& X1 t
  5. *输入参数:ch:规则通道ADC_Channel_x;times:读取次数
    2 E& e) V/ K3 A+ j0 {
  6. *返回值:无
    1 l( T  J  ~" K3 a5 B0 U- X2 Q
  7. *备  注:该函数配置好后,返回的结果是N次后的平均值
    2 n  F! c9 O: N
  8. *==============================================================================
    : G' w; b4 `8 y+ V8 g5 H1 M. `
  9. */; F0 C. |5 C2 ]7 e
  10. u16 Get_ADC_Value(u8 ch,u8 times)
    % z+ J1 @5 G: c2 {! p
  11. {
    ; m" j8 e* A6 F  }) p
  12.     u32 temp_val = 0;: y) t5 M: ]2 K
  13.     u8 t;
    5 O' h% M1 X$ t  g7 g
  14.     // 设置指定ADC的规则组通道,一个序列,采样时间0 ^0 R( i/ a# F# Y) M3 x
  15.     // ADC1,ADC通道,239.5个周期,提高采样时间可以提高精确度6 c6 J9 q  F- w1 ~
  16.     ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);      
    8 B9 g) n& g5 k  e
  17.     # \( X4 g; C6 S9 V) w, `  Y
  18.     for(t=0;t<times;t++)5 K, a- q% \3 N! F2 |
  19.     {
    $ X9 M. m5 `( x2 ]1 ^  t0 M# `$ A
  20.         ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能指定的ADC1的软件转换启动功能 9 N, l# c' b, m# i
  21.         while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));   // 等待转换结束
    3 f2 @- U# ?6 y
  22.         temp_val+=ADC_GetConversionValue(ADC1);
    * |+ U& s% c/ e* `4 ], D* H% N; Y
  23.         delay_ms(5);) K; N) U# o$ u- @6 Q9 D
  24.     }
    ' f( r6 _/ R9 R2 y4 B& ]+ }
  25.     return temp_val/times;
    $ f2 g4 n0 w0 q
  26. }
复制代码

7 h( r* M+ z  k( j9 I  t) o六、实战项目
3 r! b8 G8 B4 ~用ADC1的通道1采集某电阻两端电压(由于普中核心板没有可供采集的电阻,可以直接将采集引脚接到3.3V查看一下结果),将结果通过串口打印到电脑。其中关于串口的配置就不再做介绍,给出ADC的配置和main函数。7 h, S, k) i. T! N9 C8 P* D

; I! A. L3 c) E% S! L  B) k6.1 ADC初始化& {: I* F: H; L+ N. m7 i
  1. /*8 |- J. Q! d' S# e& Z
  2. *==============================================================================' q. r% x: z' e8 K
  3. *函数名称:ADC1_Init
    4 w% _3 ~9 j: v. W3 i. H
  4. *函数功能:初始化ADCx/ h- N. l7 U# h0 r" S7 \
  5. *输入参数:无
    : t; ^' K' p0 n5 q0 P) \
  6. *返回值:无5 }) H! {1 ^# h6 A% S# G1 S3 l
  7. *备  注:无
    ) B* T9 u8 v) ]$ w8 L( d
  8. *==============================================================================6 w# B7 {( Z  B' M0 R, k0 K0 N
  9. */
    ! _6 u" v" f, U; }2 b4 r
  10. void ADC1_Init(void)
    ( X( M1 I. U8 W3 D
  11. {
    6 n0 O! m) X( s- j% g( e
  12.     // 结构体定义
    ) A0 t5 K1 e% P/ C0 k
  13.     GPIO_InitTypeDef GPIO_InitStructure;$ m* g9 s; X1 Z
  14.     ADC_InitTypeDef ADC_InitStructure;
    7 X+ X. d6 q/ H  \
  15.     ' w2 }5 V  z: L1 N' P* K7 q
  16.     // 开启时钟* S- U& m. h) ]6 g" e! ?
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);3 }! S4 d. V& i% ]
  18.     3 x3 j0 |, ]9 m2 ^- u  D0 x
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M! x. V' v" w8 K, Q3 M/ d
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    7 [* b; [% |( V" z0 H( d* K
  21.    
    9 u) C9 M9 Z' V+ S) I
  22.     // GPIO配置- ^6 [% s' U2 z9 i0 S" |
  23.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道1
    1 d) F! R, L$ P" s
  24.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入
    8 s- p9 g8 ^; R1 z5 P  f2 b* d3 w
  25.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;4 U# M, y  Y2 {3 n
  26.     GPIO_Init(GPIOA,&GPIO_InitStructure);) w& C4 n; \4 y. a
  27.     " S" T( g$ ?3 n( S; f8 L- f8 H
  28.     // ADC参数配置
    & A7 ?- Z) A( ~4 @; q' M" r
  29.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式
    " Z# p" @# Q3 Z0 y: _# G
  30.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式 $ S, s. T0 z1 A
  31.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换. p, b3 N* ?# \5 ^
  32.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   // 禁止触发检测,使用软件触发
    ( }! Y4 L8 O  F4 Z% g- f* k
  33.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐
    $ u; w0 M! w3 g8 H0 l* B3 y
  34.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1
    ( b0 Y5 F5 T7 U; p
  35.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化! b- n: M! I0 D1 X! R
  36.    
    ) r' I( f, c2 f: \' s" I6 @3 P7 X
  37.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器" L9 s/ ^' U2 g- s, O7 d* m8 _
  38.    
    4 V* N' E$ s: M" {
  39.     // ADC校准
    ) D6 v! E5 k! O7 u$ e0 @+ V
  40.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器* I3 D- a% y# @) v% V, ~
  41.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态
    " K2 d: g+ T$ q$ Q! x7 ]. ]) o
  42.     4 V. v$ E; V; F2 |% u+ U7 Z, l
  43.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态
    , Y9 z  P* ^$ F- C  w  G& w
  44.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序7 l* G+ ?& @( S  b2 v* t- _. N
  45. - g6 s2 i8 ^* r0 M+ w/ p
  46.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能
    $ ?; t6 r2 C) ?3 t- V; }$ y
  47. }
复制代码

( v8 }; p& f; D' |1 P
  h( c5 c- h  p$ y1 ^6.2 main函数
* L% i0 d" Q8 {7 d/ x+ |
  1. u16 gAdcAdValue = 0;   // 存储AD值
    7 T. ~: F. T3 d. ?5 Z
  2. float gAdcVol = 0;   // 实际电压值
    ) C" b6 {5 R) o# K6 l# F' s

  3. 4 C7 ?9 v- [" ^, ~, F& T" k
  4. int main(void)
    0 Z5 c+ h( q' W/ E) N
  5. {
    * o9 [/ g. C% ~6 w  @3 g
  6.     Med_Mcu_Iint();   // 系统初始化% _# Q6 U- p: w& `5 I4 Z
  7.    
    7 v' K+ `# R2 u8 Z1 c# d3 m8 z
  8.     while(1)
    " ]$ Y4 X' ~( ]- X, N
  9.   {% |9 h, W7 D4 ]/ \4 A5 g& a: o
  10.         gAdcAdValue = Get_ADC_Value (ADC_Channel_1,10);   // 获取转换结果, z/ J1 N' ~/ m9 M
  11.         gAdcVol = (gAdcAdValue / 0xFFF) * 3.3;   // 计算实际电压( T1 W) u6 [" z/ ^
  12.         printf ("Vol=%.1f V\r\n",gAdcVol);   // 串口打印结果
    + Z' O' o" `- \& t6 _
  13.         & H8 V' _! V1 ?* O5 g5 t6 t
  14.         delay_ms (500);   // 防止打印过快2 ]. v7 x8 V7 _# `. |" V! J
  15.     }0 d, I8 U/ u8 G/ L: Z9 o
  16. }
复制代码

# U/ c/ d$ V* ?4 ^/ }2 _/ b0 H* I( |8 y. _
七、拓展% ?! d& z9 L0 C7 ^- J+ z
7.1 定时器触发ADC采集
2 m) W! _; P& I- O
根据中文参考手册介绍,ADC可以通过定时器触发AD转换(只有PWM的上升沿可以触发AD转换)。触发方式有以下几种
! g" N  \: q$ h4 ^* a3 a7 `• TIM1_CH1 :定时器 1 的通道 1 的 PWM 触发+ \0 Y4 ^' z+ M& k4 |$ t
• TIM1_CH2 : 定时器 2 的通道 2 的 PWM 触发
8 g( g1 H8 I! z" K+ u* Q( K& m• TIM1_CH3: 定时器 1 的通道 3 的 PWM 触发
+ A5 g& T0 q2 y% h• TIM2_CH2 : 定时器 2 的通道 2 的 PWM 触发7 ?7 m. i8 o) x; y5 \. V, T- J
• TIM3_TRGO: 定时器 3 触发% E  S! @/ {( K- R5 k) K
• TIM4_CH4 : 定时器 4 的通道 4 的 PWM 触发
1 Z2 ^" e/ P; _! `3 p! t* T7 n
: l. H( i. u. c( k# c* u

; ^* o/ v4 |! ]
微信图片_20231023190424.png

; ?" k5 z5 [0 V' a# g0 p
ADC外部触发方式
( \# [! s' _4 I  ^* I* I

/ g9 q+ `( D1 [2 n/ I# P8 O; K, f& J6 j: P. B
这里以TIM4的通道4触发ADC采集为例,给出程序配置。9 c# S+ k1 C) Z5 d4 t7 |

0 s) w( u8 D# y# z3 \首先是定时器PWM的配置,不对引脚进行重映射。& f7 v% t$ b* D) ~9 r
  1. /*1 x$ p0 F9 f+ V6 j' z
  2. *==============================================================================- Z: c. o  i) A7 D* F( N, b
  3. *函数名称:TIM4_CH4_PWM_Init; E! ~2 E2 w* w" @+ O* @2 w; ?# O
  4. *函数功能:初始化定时器4的PWM通道4
    ' l) A8 T$ B. \8 X- t  y8 B
  5. *输入参数:per:自动重装载值;psc:预分频系数
    $ F" v1 c2 g! C9 w$ S
  6. *返回值:无4 H& U5 O+ V5 A" |
  7. *备  注:无
    , V6 a6 W! b( z0 f# p+ r0 c& h* E
  8. *==============================================================================* S+ E: S' j; V0 w
  9. *// [- i% O% V" Y
  10. void TIM4_CH4_PWM_Init (u16 per,u16 psc)  N. z& z# u9 N- D5 T0 k
  11. {
    ( Y# H3 \" I" J6 P* e% ~2 A
  12.     // 结构体定义
    % f1 f: i1 k+ R3 _: [2 Y
  13.     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    ' ]' D) z2 f  E8 l8 f' o  i7 r
  14.     TIM_OCInitTypeDef TIM_OCInitStructure;
    + |* y0 `9 r- g' G. J7 x
  15.     GPIO_InitTypeDef GPIO_InitStructure;! P; o0 u. F3 i$ c
  16.     " a* |' M4 p' b  F  p
  17.     // 开启时钟7 O, l+ G  A& O
  18.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);+ ^3 h% n, D) q* E
  19.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);) M. ?! n# i1 j# ~9 T& @3 ^) U
  20.     + c; E3 L+ }6 u+ R+ F
  21.     // 初始化GPIO. k4 O9 `+ W. Q. l: S. ]* L$ @  D
  22.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    . u* v7 i( p4 l0 ~9 q
  23.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;- t" n* F7 O: x9 a+ e$ s
  24.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   // 复用推挽输出
    6 g+ R; o, I7 ^5 w
  25.     GPIO_Init(GPIOB,&GPIO_InitStructure);% I2 \3 _4 z, _# h
  26.     5 l! y2 C0 M! m4 C4 F
  27.     // 初始化定时器参数
    3 P3 E' G5 ^9 C- a
  28.     TIM_TimeBaseInitStructure.TIM_Period = per;   // 自动装载值
    2 h. r) @: y' t1 H+ b
  29.     TIM_TimeBaseInitStructure.TIM_Prescaler = psc;   // 分频系数" V$ E+ S5 Y# V: _" \
  30.     TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    ( M3 M1 s7 R& [7 T
  31.     TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;   // 设置向上计数模式- z  l' a2 z  U9 s
  32.     TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
    . R. }% Y0 i: V+ a. j8 B
  33.     3 Y6 D5 b, X' D4 ~7 D6 E
  34.     // 初始化PWM参数
    0 ^/ K; h& i5 S. ]
  35.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   // 比较输出模式5 A( M0 v" K6 V& A, _8 S3 \' T
  36.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;   // 输出极性
    0 I0 x  Q: w0 u" X7 \' B8 x
  37.     TIM_OCInitStructure.TIM_Pulse = 500;   // 脉冲宽度0 ]6 @6 h9 z' H
  38.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   // 输出使能
    ) y& i/ X! W: o
  39.     TIM_OC4Init(TIM4,&TIM_OCInitStructure);   // 输出比较通道2初始化* z1 B  G! V8 T& x$ |
  40.    
    ; Z. Z; p% G+ p$ H$ \
  41.     TIM_OC4PreloadConfig(TIM4,TIM_OCPreload_Enable);   // 使能TIMx在 CCR2 上的预装载寄存器/ @0 E) W9 m$ @6 o
  42.     TIM_ARRPreloadConfig(TIM4,ENABLE);   // 使能预装载寄存器( i8 A! v" i$ G9 ^, L5 U' u8 }; g
  43.    
    3 e( ]" k7 N5 B& h
  44.     TIM_Cmd(TIM4,ENABLE);   // 使能定时器1 E1 y4 J9 c' o) m
  45. }
复制代码

6 Q# V' T8 t2 \- B2 l. UADC配置程序如下,触发源选择TIM4的CH4,使能外部触发。& ?/ R, m, K/ [- Z9 m
  1. /*
    2 C1 @# |3 \% M+ b( V7 O7 O8 l
  2. *==============================================================================; H( K, P5 V; A! i! q2 G4 i
  3. *函数名称:ADC1_Init. z  q. U6 K, Q0 _4 I; A
  4. *函数功能:初始化ADCx2 J- z0 C% e, X: t2 R: o
  5. *输入参数:无
    0 E  f; i! U+ E
  6. *返回值:无9 a5 @- ]; U' N- M
  7. *备  注:无
    9 \) ]1 E4 i" |4 x1 ^2 c; v
  8. *==============================================================================4 @2 S$ V! O9 v8 E# V7 R8 X
  9. */4 e3 s: \' v, a$ f
  10. void ADC1_Init(void)! S! K2 x. i' K) Q" E! M
  11. {
    # t2 F1 T# {7 q: j1 A  G
  12.     // 结构体定义
    ( s3 c7 p" M/ K8 C
  13.     GPIO_InitTypeDef GPIO_InitStructure;
    5 Z8 c6 O7 s/ z. g
  14.     ADC_InitTypeDef ADC_InitStructure;8 g# R+ C, Z, ?4 }1 \4 g8 _
  15.    
    ' ^* [" R- _2 a: @# T
  16.     // 开启时钟
    4 M, O/ o2 R1 a' ~
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);
    4 n( ~) d; w9 E+ I
  18.    
    3 ^9 v+ N5 D; r: b. T+ d
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    + _* Q- |1 x& R* M
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    ! m: L0 x8 E" M
  21.     // 规则通道配置- o" u  G0 i2 R" }2 I& f  a, \' b
  22.     ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);3 e, e! `6 T( r7 x# L
  23.     9 H4 ]6 u9 R2 ~' ]! a$ {1 C4 |$ \
  24.     // GPIO配置
    - P/ o2 e( u; C
  25.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道14 e6 d6 ^& J" p6 j# ^! |
  26.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入3 S; O' I$ s0 p! l% V
  27.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    + T3 E) y+ d. e$ O9 j! S# O
  28.     GPIO_Init(GPIOA,&GPIO_InitStructure);
    ! I. N! T; o8 S" b) @
  29.     0 n- i! C/ s. v/ z
  30.     // ADC参数配置
    - A$ ^0 I$ M  F$ m4 F; a+ ^4 E
  31.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式3 `8 c, l8 K& F- d+ p1 C  Q
  32.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式 . b8 k% l9 A4 G* f7 Q) ]
  33.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换
    . W' A7 ?3 A) n7 c% ]: D( A7 R0 Q+ |
  34.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;   // TIM2通道2触发1 h% D8 W1 _0 n% r9 I- A
  35.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐 , `+ X# ~- a9 J
  36.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1
    ; O( M) p! W5 T9 @8 G
  37.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化+ z' {) S3 G0 P' w7 \0 g$ U# z3 f- X
  38.    
    8 s) x! {8 V( O/ [5 v6 ?
  39.     // 使能外部触发
    + J% ]% |% f- d% A8 p
  40.     ADC_ExternalTrigConvCmd(ADC1, ENABLE);: G* U9 K* B" D$ z3 q# j8 O% C, T5 l
  41.     " y/ G+ j5 ~8 w( l
  42.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器6 H% W; L' Y: _/ L
  43.     5 z# ]3 r, l4 _$ y8 }8 d$ h, t
  44.     // ADC校准
    . X! q& o$ h* Q& P5 r4 N5 N
  45.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器
    1 S- b+ U1 p! {
  46.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态8 I+ H2 j$ k4 z- R( a5 k/ e2 K) P
  47.     ' B5 y+ @$ O7 X8 U
  48.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态
    4 {7 x7 K3 |0 S
  49.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序
    * }5 t1 z, u$ E, ~- P
  50. . b7 ?, A2 q4 _; t! y  c, t7 z( g( L& A
  51.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能
    4 ~4 q, |* m& W$ F
  52. }
复制代码

, F" Y  A& W" d8 S% }. @& I% pmain函数如下8 b3 B+ X( g. w2 j
  1. u16 gAdcAdValue = 0;   // 存储AD值
    . O. q) M9 g2 {% y2 N( p1 C( q. M
  2. float gAdcVol = 0;   // 实际电压值6 `+ E& u' p  e- Y7 x0 ^
  3. ) i# u8 G3 T& d+ u
  4. int main(void)
    - x! T9 U( ~8 q( y9 h, [- R( Q
  5. {9 j) P7 a% X) E
  6.     Med_Mcu_Iint();   // 系统初始化: a. s4 k. v/ a# z& ~
  7.    
    & ]5 }4 n* \6 l0 g, f
  8.     while(1)
    . u9 S; Z3 e! ~8 A9 Q0 m+ O' z0 M
  9.   {
    * B0 F: q0 I9 k5 O, a
  10.         gAdcAdValue = ADC_GetConversionValue (ADC1);   // 获取转换结果* j# }# ^8 K7 }) S1 Y( ?. }4 U
  11.         gAdcVol = (gAdcAdValue / 0xFFF) * 3.3;   // 计算实际电压) G5 `$ a1 j! s4 [/ Z
  12.         printf ("Vol=%.1f V\r\n",gAdcVol);   // 串口打印结果/ t1 }( S5 |* x& @% a: O
  13.         : v; m+ M9 E* q/ D( G* \
  14.         delay_ms (500);   // 防止打印过快
    - d  ~4 [, y9 [/ Q2 m9 W' }
  15.     }, D. L3 y6 k+ u- F- U
  16. }
复制代码
0 z; T. f3 J, S! d+ b
初始化定PWM时,程序为
6 H" @9 \1 f. ^& y* Z8 u+ v
  1. TIM4_CH4_PWM_Init(1000,71);   // 初始化TIM4的通道4
复制代码
; \& N4 i0 _" |. {( l
分频系数为71 + 1,自动重装载值为1000,也就是1KHz的方波,也就是触发AD转换的频率为1KHz,与占空比无关。. c- e& b& O* H

, w, v0 d4 _' i# {5 ?

- S, R0 Q, U" d' v, b7.2 ADC采集交流信号3 a  m' l3 x! H. f3 W
ADC能够采集的电压范围是0~3.3V,也就是说负电压无法采集。比如,需要采集下图中的一个交流信号
" @; D& P* ?7 p) Q9 ~

7 d) ~: ?' F: N  D* K
微信图片_20231023190420.png
& |2 q! C, r2 }
交流信号图

$ V0 ^9 b  n' x8 h
# j: s* c+ n9 n: I6 T+ |
# c) G  ~0 N3 G1 Z
其位于0以下的部分是无法采集的。因此,在利用STM32采集交流信号时,在交流信号输入ADC引脚前,给交流信号增加一个直流偏置,将交流信号的最低点抬升到0以上,之后再输入ADC引脚。2 ], g* B) ]* X* `" I. I
) K" C$ x2 m4 e# v
+ q3 b) j: ]2 H# Z
7.3 计算交流信号有效值
+ V) s$ ^9 i9 A, e$ F, ?+ F+ v4 l! fADC可以用于电流监测,实时监测主线路中的电流。当然,硬件方面需要搭配电流互感线圈,通过采集互感线圈两端的电压,来监测主线路电流。由于一般都是交流信号,所以需要计算有效值。) |7 ~+ L) m( V- q( {
7 Y% }& h3 N: t* w) F
根据我们所学的知识,计算交流信号有效值常用两种方法。一种是峰峰值除以根号2,另一种是计算均方根得到有效值。通常我们采用计算均方根的方法来计算有效值。因为如果用峰峰值除以根号2去计算有效值,峰峰值很容易不准确。如果在某一个时刻,由于环境干扰或者硬件问题,导致突然出现了一个很大的值,会导致计算结果与实际偏差较大。关于为什么计算均方根可以得到交流信号的有效值,这里就不做介绍了,只给出部分程序设计。由于博主目前身边没有合适的设备验证,因此仅供参考。
7 c$ A* J. H4 n% }2 [, ~4 h. W# F& p3 w/ z. H
假设需要计算一个50Hz交流信号的的有效值,在其输入到ADC采集引脚之前,增加一个稳定的1.65V的偏置。ADC的采样频率为1KHz,也就是一个正弦波的周期可以采集20个点。假设采集到的AD值存储到一个数组中,计算有效值的程序设计如下
4 z$ F/ U/ a% R% p2 B0 s' Q, R
  1. int gAdcAdValue[20];   // 存储采样结果AD值的数组0 n4 T+ ^+ x+ ]/ j& a$ \
  2. int gAdcValidValue = 0;   // 有效值) i9 y) ?  p8 p" k$ P" F
  3. 6 {6 r. T, y5 `: n
  4. void Med_Adc_ValidValueCal (void)
    ! o  K1 d5 U, X! a
  5. {& n6 c: f7 E8 u* ^3 r
  6.     int tempVar = 0;   // 循环变量+ T, d- `& i  R$ l" Y# h, L; r3 s
  7.     int squarSum = 0;   // 平方和/ ^, _8 B0 `, I8 }
  8.     3 T; p2 }4 ]" ]
  9.     // 求平方和% u* b4 ^* P- ?9 g. {- I4 [
  10.     for (tempVar = 0;tempVar < 20;tempVar ++)
    1 L, F$ \$ G5 l$ A. s8 w
  11.     {/ B" f/ f$ I; f$ f0 Q
  12.         // 减去直流偏置
    + W8 M5 J' N; C9 f% T6 X* x8 B
  13.         gAdcAdValue[tempVar] = gAdcAdValue[tempVar] - 2048;( s$ T. J6 h& x5 I7 V+ t* d4 C
  14.         // 计算平方和
    ; v0 H( Y3 w, y2 h2 E
  15.         squarSum = squarSum + gAdcAdValue[tempVar] * gAdcAdValue[tempVar];. \0 w9 u! `/ w8 T- T9 D4 m9 U' f
  16.     }% ?# g  z9 U, ?9 l" C3 u# Q% X
  17.     " e& ?9 k$ Q3 ?. R9 ?% B1 y* S
  18.     // 求平均
    . e6 f9 F9 g- J/ A  I
  19.     squarSum = squarSum / 20;
    $ J1 [, N# w2 M, L
  20.     9 {. p4 \: t6 M) \; W4 F
  21.     // 开根号得到均方根(有效值)
    ; a% D' I  N7 |# V7 |
  22.     gAdcValidValue = sqrt (squarSum);
    9 J4 x. I* }4 m$ e3 ~1 P
  23. }
复制代码

, _% x3 ?  v8 H$ N* y) r在进行程序设计时需要注意不要超出数据类型范围。在实际应用时肯定会存在误差,这里也简单介绍一下误差消除方法。目前用到的有两种方法,第一种是分段矫正,在不同的区间内,误差满足线性关系时可以使用。另一种是按比例矫正,这种方法常用于误差随着测量值的增大而增大的情况。在计算出有效值后,减去或者加上一定比例的计算值来做矫正。* Z; p% C  Q2 h+ n2 S0 _% K3 {, O
0 e9 U2 X9 N) b7 A

( `5 e% [) s; p  {7 g5 Q
) }9 M% ]& ^: {$ T4 w; @
转载自: 二土电子% I' M% [- [) m! z# v
如有侵权请联系删除
0 H0 R% {% q% X3 f0 B
4 t, i. F0 [8 l' g
赞 收藏 评论0 发布时间:2023-10-23 19:04

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版