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

基于STM32F1的CAN通信之ADC

[复制链接]
攻城狮Melo 发布时间:2023-10-23 19:04
一、什么是ADC( u% o2 k9 J3 d5 ^$ N2 _/ R
ADC(Analogto-Digital Converter)模拟数字转换器,是将模拟信号转换成数字信号的一种外设。比如某一个电阻两端的是一个模拟信号,单片机无法直接采集,此时需要ADC先将短租两端的电压这个模拟信号转化成数字信号,单片机才能够进行处理。+ G: y" `7 |( U, G, X

9 k. @, Z7 i$ r" D1 {* p3 p, R二、ADC的用途; [# T' E- [& C
ADC具有将模拟信号转换成数字信号的能力,比如将模拟的电压转换成数字信号,单片机进行处理。可以用作温度监测或者电流监测等方面,用途极广。6 s" B4 H) i' R' x3 X% i
5 t; S. P( j$ N1 H
三、STM32F103ZET6的ADC& c8 R1 @& L6 F4 N, s
根据中文参考手册介绍,STM32F103ZET6单片机有3个12位ADC,共有18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。
$ f- t& U. A, g3 ?2 \7 E" i8 K/ m# e! n( q; `: E# f3 b

. [4 S, c* B0 w3 o' w! l3.1 ADC通道对应引脚+ N  r" l4 C: D
STM32F103ZET6的ADC各通道对应IO如下
% M9 h8 }! m5 d) ], x/ R0 C- h3 Y
5 Z4 I/ d+ a7 G& L6 T
2LJFBR6J$G1TR[YF5Q9MQOF.png
; A) ^* ]  y. P2 }- ]7 H   " d- V6 _& t+ }* X& r
3.2ADC时钟
2 q# M: Q/ l( m* L9 g$ i# d) UADC输入时钟ADC_CLK由APB2分频产生,最大值是14MHz。库函数提供了设置分频因子的函数
2 U" W# X1 \/ |0 G: W
  1. void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)
复制代码

5 b$ ~' {5 y6 P9 C2 \% |可选的分频因子有% f1 v, X; j/ |* Q4 D
  1. #define RCC_PCLK2_Div2                   ((uint32_t)0x00000000)  u; e1 h0 D% `0 J3 F
  2. #define RCC_PCLK2_Div4                   ((uint32_t)0x00004000)
    4 y. B- d* e6 e2 d5 a) Q
  3. #define RCC_PCLK2_Div6                   ((uint32_t)0x00008000)6 Y9 c  U) ?9 }! f1 K' u" z* @
  4. #define RCC_PCLK2_Div8                   ((uint32_t)0x0000C000)
复制代码

- i8 b4 Y/ N, w3 V+ Y( b  E# bAPB2总线时钟为72MHz,而ADC的最大工作频率为14MHz,所以,分频因子一般设置为6,这样ADC的输入时钟频率为12MHz。7 X8 ?2 w- Q5 C3 x- S

+ S% l) ^$ U, G2 L( K) f3.3 ADC工作模式# G% R# V* Q' o. }" h" n* L
根据中文参考手册介绍,STM32F1的ADC有三种工作模式+ H3 l- ?. B* S* X
• 单次转换模式 单次转换模式下,ADC只执行一次转换。该模式既可通过设置ADC_CR2寄存器的ADON位(只适用于规则通道)启动也可通过外部触发启动(适用于规则通道或注入通道),这时CONT位为0。) r6 t' K" p( Z( R

% U0 \0 C2 L# ?5 T% c& p• 连续转换模式 在连续转换模式中,当前面ADC转换一结束马上就启动另一次转换。此模式可通过外部触发启动或通过设置ADC_CR2寄存器上的ADON位启动,此时CONT位是1。& t3 C+ U0 l) ]. {* D

. \0 ]7 G+ w% I% R9 B. g• 扫描模式
" a, b9 X, }2 G' N; i( K. T. A( i6 ]6 I1 i( Y! Y( d

+ c3 i3 v5 u  {33.4 ADC转换时间8 t+ o+ s: [: d
ADC的总转换时间与时钟频率有关,总转换时间 = 采样时间 + 12.5个周期。其中,采样时间最短为1.5个周期,也就是最短转换时间为14个时钟周期。使用软件触发时,可选择的采样时间如下: A7 k4 |( l6 [- l- r% ^" H
  1. #define ADC_SampleTime_1Cycles5                    ((uint8_t)0x00)! w' R& H9 |+ [- S
  2. #define ADC_SampleTime_7Cycles5                    ((uint8_t)0x01)8 `2 g' O8 D# t
  3. #define ADC_SampleTime_13Cycles5                   ((uint8_t)0x02)5 _! O. b2 q+ h2 k, B  [! P; a
  4. #define ADC_SampleTime_28Cycles5                   ((uint8_t)0x03)( H0 l' G, j% r$ c7 n
  5. #define ADC_SampleTime_41Cycles5                   ((uint8_t)0x04)
    2 E: S) M' L3 {2 W
  6. #define ADC_SampleTime_55Cycles5                   ((uint8_t)0x05)
    - l* R. R; ^! g' J% x: G; R9 B
  7. #define ADC_SampleTime_71Cycles5                   ((uint8_t)0x06)
    & _) [, H0 q8 l9 l; z6 h
  8. #define ADC_SampleTime_239Cycles5                  ((uint8_t)0x07)
复制代码

0 A% H' n! {" E$ j3.5 ADC校准6 x  _7 }7 H8 @
使能ADC后,需要对ADC进行校准。使用库函数开发时,提供了ADC校准的函数; Q0 \  L, d9 K9 a6 d# e
  1. ADC_ResetCalibration(ADC1);//重置指定的ADC的校准寄存器
    2 z! q. ]1 A4 Z: [
  2.     while(ADC_GetResetCalibrationStatus(ADC1));//获取ADC重置校准寄存器的状态
    % X. c/ W+ `/ m" Y8 ?+ e
  3.    
    3 _6 [/ a* l1 |1 A7 N
  4.     ADC_StartCalibration(ADC1);//开始指定ADC的校准状态
    7 ~! w% N' b% {) {8 E# l
  5.     while(ADC_GetCalibrationStatus(ADC1));//获取指定ADC的校准程序
复制代码

' q, I% }9 h3 O/ ]$ d3.6 ADC转换结果与实际电压的换算
: L/ [1 X. Z4 q4 G" X
获取到的AD转换结果并不是实际电压,如果想要得到实际电压,需要经过换算。上面介绍了,STM32的ADC为12位,也就是AD值取值范围为0~4095。采集电压范围为0到3.3V。AD值与实际电压之间存在比例关系。
0 J) ?+ u: M2 x# i  z- ]& n

7 }* L* ]* z$ q3 r& k6 q) y实际电压 = (AD值 / 4095) * 3.3。单位为伏特(V)7 _1 t8 B5 |& ~; V7 j; W+ D
) p; k* d! H/ Q# ^/ H" w

8 e. L0 H, H; I四、ADC配置步骤; d& x/ K' Z* l5 x6 Q+ q/ E/ y* o
• 使能GPIO时钟和ADC时钟,设置引脚为模拟输入
/ O- A8 e  |0 T, E. l% N• 设置ADC的分频因子; M( s* G0 M1 N! `" @  U3 k
• 初始化ADC参数,包括ADC工作模式,规则序列等1 e5 a; y! K2 l: h+ R1 i; K
• 使能ADC并校准
8 N( Z+ K, J' |7 y* A' L6 b2 e9 _• 触发AD转换,读取AD转换值
) @4 v! {2 H% m
% _4 |  D7 Y. I. a$ ~

) _9 K; Q4 @: }% z& N/ C0 i: E% o五、ADC配置程序
9 k' a- M! k3 Q3 n9 r6 l8 J55.1 ADC初始化程序

9 N0 f3 I3 O' E8 S' E" u0 E这里以配置ADC1的通道1为例,给出ADC的配置例程,分频因子设置为6,单次转换模式,软件触发。
2 W3 ?! h, i) e' n
  1. /*
    6 {" g& _' ]; X/ U
  2. *==============================================================================" l+ g; y) \/ h8 I% y; [
  3. *函数名称:ADC1_Init! @. m( Q7 X' V3 b5 b& J8 O
  4. *函数功能:初始化ADCx
    1 w1 z7 C9 [9 J( j& }
  5. *输入参数:无' b( f$ m, B: T9 E" z
  6. *返回值:无
    4 ?: U5 q7 o) U* b+ `- h0 X( y5 A
  7. *备  注:无+ L8 O0 U0 g- w, F$ g2 P
  8. *==============================================================================
    ' {, Z( e. f# S4 b6 ]) P
  9. */' B! x, h( ^. O% u- Q
  10. void ADC1_Init(void)) `) [: I$ ]0 a  k9 {" D
  11. {
    ! X! `9 G/ z1 h% \
  12.     // 结构体定义
    $ I: p( _2 v( y
  13.     GPIO_InitTypeDef GPIO_InitStructure;
    ( F2 J: b4 V/ q9 w2 L
  14.     ADC_InitTypeDef ADC_InitStructure;
    # M2 \' G9 D3 i) F8 k
  15.    
    : h9 K5 v7 m4 E, R& f& G0 z
  16.     // 开启时钟
    9 L# Q7 L/ K( Z- w$ ?9 f" z) n/ ^
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);
    + ~% j/ X$ s8 e, r4 F
  18.     ) @5 ]+ ^2 Y# t& M' f
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    / ^/ e* k5 d/ _/ B
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);1 l- s: S$ S6 b6 u
  21.     % e; E! l* i0 T. D6 q
  22.     // GPIO配置* q1 _8 F! R, g& C5 M
  23.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道1
    0 P: X$ G- Z6 N8 ?( j+ E4 j
  24.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入
    + O4 k7 Z% i( u9 k) ]( g
  25.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;& u1 t7 H2 b" i1 s" e2 d
  26.     GPIO_Init(GPIOA,&GPIO_InitStructure);7 }4 {! e5 Y( A/ d% ?3 v3 Z
  27.     2 X; o1 X$ W% Z, d; M) `
  28.     // ADC参数配置
    / J* s+ P1 ]: M3 E5 T5 n4 s0 F
  29.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式
    $ {9 ~3 C" L- K9 a7 {
  30.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式
    5 g2 j% b! d# Z0 a: ^
  31.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换
    0 z6 M- A6 l; t" u- d( Y$ W4 U
  32.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   // 禁止触发检测,使用软件触发
    7 q1 c4 J* H* m' [; D
  33.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐
    3 S3 }2 r5 }$ J" A3 j
  34.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1 / p2 ]# _0 @4 _4 e* p
  35.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化; u' ~9 c: L5 G, Y+ l4 s+ ^' x
  36.     $ A0 ?/ A/ x2 `
  37.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器  C, h0 w+ q4 M( [* h$ g7 I( t
  38.     9 Y; l' f/ o* c' T# m
  39.     // ADC校准
    3 _( A1 s& @. r6 d9 H' Y
  40.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器3 \- C& s: I- f& i2 y( t
  41.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态
    + J+ E9 U. S9 t
  42.     & @) G' K! D! ~
  43.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态8 H) s2 B7 J) v' s- [3 p5 k- b
  44.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序
    * h' p* b! f; J3 u! u/ V6 \; H

  45. 7 c0 `, _3 T% ?
  46.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能! E, m- u1 ?8 {
  47. }
复制代码
# e& K/ N/ V2 }, P1 H* S( J: l4 P3 _; g
5.2 软件触发AD转换
0 x0 R. A7 \1 {2 D' x8 W$ A7 Q, `5 u库函数开发,配置为软件触发时,可以通过下面的函数触发AD转换
( u: w5 n5 W- ~
  1. void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState)
复制代码
4 V% u" T# z8 {  w3 u0 T8 a, \7 L2 d2 V
5.3 读取AD转换结果; ?4 t3 K" n% F+ y
库函数提供了用于读取AD转换结果的函数9 K# r- H$ a% ^* Y6 l/ d9 ]
  1. uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx)
复制代码
) `; b5 _& y& y: R0 `; m) ~
这里给出另一个函数,用于软件触发AD转换并读取转换结果0 s4 l$ a0 X. r0 {/ E. k6 t
  1. /*0 D8 Q8 K+ P6 a
  2. *==============================================================================; o6 x+ b7 ]1 z5 W1 F3 `8 p
  3. *函数名称:Get_ADC_Value) v; L6 W/ _& L/ T
  4. *函数功能:读取某一规则通道AD值* N- E* ?: Z7 z
  5. *输入参数:ch:规则通道ADC_Channel_x;times:读取次数; z& v& E. y+ q
  6. *返回值:无
    % k) H) C! @. A! O; w  P
  7. *备  注:该函数配置好后,返回的结果是N次后的平均值: I" ]9 M' [! c4 I& d+ y
  8. *==============================================================================& `4 U$ {* W* }2 \; i
  9. */- W8 m7 V  B1 W0 N9 _* Z- z& B
  10. u16 Get_ADC_Value(u8 ch,u8 times)
    : {) D/ v* K$ u" R0 h
  11. {: U' \% O. E& J
  12.     u32 temp_val = 0;
    6 B! A9 E8 U7 c
  13.     u8 t;
      U7 m) j; n/ R% X- [' _; \3 ~
  14.     // 设置指定ADC的规则组通道,一个序列,采样时间
    $ n1 E0 q/ _  A9 b3 E6 Z6 L
  15.     // ADC1,ADC通道,239.5个周期,提高采样时间可以提高精确度
    0 v1 Q. j& h. J
  16.     ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);      
    8 q% C  L8 o' r7 c5 M! {
  17.    
    8 _* v! \3 `" I: ~
  18.     for(t=0;t<times;t++)
    ! U$ }" Y# [# K& S
  19.     {
    7 @9 Y4 W- _( I
  20.         ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能指定的ADC1的软件转换启动功能
    2 i0 X8 a# I2 z3 _; ]
  21.         while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));   // 等待转换结束- G+ x/ l5 e9 D0 V7 H" j! L* w. {
  22.         temp_val+=ADC_GetConversionValue(ADC1);
    ( ], R3 }( v+ W7 ^' \
  23.         delay_ms(5);7 `  m' M4 C" w/ A
  24.     }
    . e- E* J; k# ]( ]
  25.     return temp_val/times;! _3 U  ~7 I3 n3 r' u+ x
  26. }
复制代码
1 t3 @4 Z  O3 s' K# |# X' j
六、实战项目' o( t$ k+ C% {: G! _" G0 J
用ADC1的通道1采集某电阻两端电压(由于普中核心板没有可供采集的电阻,可以直接将采集引脚接到3.3V查看一下结果),将结果通过串口打印到电脑。其中关于串口的配置就不再做介绍,给出ADC的配置和main函数。
+ @4 g& n$ L, d
5 ^' S6 ^1 |7 n) F
6.1 ADC初始化
/ U/ \$ L% x9 b4 s. J4 P( [
  1. /*
    : J2 k. k# S# l6 k7 a) p
  2. *==============================================================================( [& H' q0 K5 C& @  r
  3. *函数名称:ADC1_Init9 Z9 k. Q6 Y; g- E! P- h
  4. *函数功能:初始化ADCx; S+ R0 d5 R" }. n) z9 a: g* d
  5. *输入参数:无
    0 T, L: n% z% k
  6. *返回值:无
    # Q5 c# {7 L! Q. Y
  7. *备  注:无2 F5 F2 l, J( `) T! ~
  8. *==============================================================================% d. V( g$ b( d6 ]! Q* E& ?
  9. */
    4 C+ s/ v9 j- `# v2 Q
  10. void ADC1_Init(void): s7 Y# l0 S1 {5 p
  11. {! q3 o8 P& e7 k8 d4 i, Z& _
  12.     // 结构体定义. O1 ~  [( }. Y5 f# h# }! d
  13.     GPIO_InitTypeDef GPIO_InitStructure;$ R5 d- n' _/ l& N8 U: j
  14.     ADC_InitTypeDef ADC_InitStructure;
    ; P; A% z3 w* a* f& N. }
  15.    
    ' m7 Z1 {- B& V& s
  16.     // 开启时钟
    / ]- Q) i: b( `3 ~
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);) G/ k4 o  R$ o
  18.    
    3 F- z! f5 M" D2 p( @+ r" O
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M9 _2 g/ y, c  L/ Y) X* c( y
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);  X( k- E. a; l
  21.     - n1 M  T$ {, {5 h
  22.     // GPIO配置6 f! g6 j/ |) \5 L  W1 b0 c# T
  23.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道1
    , J" \. o0 J, s3 f8 _% W4 o4 a
  24.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入- u9 y  o5 i  f& g* v0 |
  25.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    + i+ f. }7 t9 Z" J. j1 f+ t
  26.     GPIO_Init(GPIOA,&GPIO_InitStructure);( H$ j8 t! \* c" a3 x
  27.    
    , j' R( H$ W' \& y
  28.     // ADC参数配置/ g7 |" g( ]  @% w: q
  29.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式( u  [# _7 g' d5 X7 g
  30.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式
    4 r$ G0 Z( ~# y4 i
  31.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换
    ! x$ X4 P, h) g2 u
  32.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   // 禁止触发检测,使用软件触发4 j& C& J* X; b8 J
  33.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐
    ' [5 f, l. X; n
  34.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1 2 N; U4 y8 ?3 R# I7 L( I0 Q- [
  35.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化1 ~! k+ p5 S2 T5 [0 p
  36.    
    " ]2 \5 u9 V" Z1 {% a
  37.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器
    9 j( v! A8 F4 p: Q( j
  38.    
    ) W& g) f* O$ U% d& c$ H
  39.     // ADC校准0 v7 }# e/ t5 C
  40.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器# q  b) @9 O0 q
  41.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态
    2 T, N/ u1 f0 C* [$ A0 q2 W
  42.     ) c! x) i6 a* Q4 a. ?. _
  43.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态: [) K+ }+ x# _4 q& T9 {3 g  r
  44.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序
    + z& j/ B* I( E  L& t
  45. ) l) j3 b9 a8 M1 T8 D8 J, E
  46.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能
    % D4 r+ c4 Q+ e3 f$ N: L9 L
  47. }
复制代码
4 s. P2 Q) [7 v9 Y+ ]9 V$ C- s( ~
, a/ h; D9 ~6 r1 K
6.2 main函数
9 j. O3 h! s* B& M+ A
  1. u16 gAdcAdValue = 0;   // 存储AD值
    % O6 F5 e# B$ P0 v& _( ^. t7 N4 x
  2. float gAdcVol = 0;   // 实际电压值1 B* [# U! q6 y
  3.   Q& _4 e9 x; \  C4 O
  4. int main(void)" [) r! X, R4 i7 l# E
  5. {
    2 o9 `0 X. M3 ]+ g' O) o9 c
  6.     Med_Mcu_Iint();   // 系统初始化) j: ?8 \8 D; N
  7.     0 y7 H; v  ~& ]
  8.     while(1)" L7 m" F5 E' u- P, f1 |- x
  9.   {
    : {3 E' `* O7 z8 U
  10.         gAdcAdValue = Get_ADC_Value (ADC_Channel_1,10);   // 获取转换结果
    0 L2 D" t1 ~! |2 T
  11.         gAdcVol = (gAdcAdValue / 0xFFF) * 3.3;   // 计算实际电压* A( i( m5 X" p/ g6 t) ?3 _
  12.         printf ("Vol=%.1f V\r\n",gAdcVol);   // 串口打印结果
    / @1 |: N- A5 @" n7 b' ^& @
  13.         
    . o# j+ M$ t& W% b/ r" g
  14.         delay_ms (500);   // 防止打印过快
    9 S# \/ e% k  g. P
  15.     }
    : N* O- j" _4 [' }4 x; @" I0 w8 g: t
  16. }
复制代码
7 l8 ]: h8 F* p/ G0 I; ?" K

3 r6 w* S5 ^! B4 t七、拓展) [& F( \  f7 q* d6 T2 s" N* f/ ?
7.1 定时器触发ADC采集

9 Z. I$ W) c; t# M根据中文参考手册介绍,ADC可以通过定时器触发AD转换(只有PWM的上升沿可以触发AD转换)。触发方式有以下几种% D' J1 G# o9 u/ \' e
• TIM1_CH1 :定时器 1 的通道 1 的 PWM 触发! ?' u" b3 Y+ a3 \% n* \8 r
• TIM1_CH2 : 定时器 2 的通道 2 的 PWM 触发( a$ J! W$ ]/ s9 a& P
• TIM1_CH3: 定时器 1 的通道 3 的 PWM 触发
) L& [/ B4 ^/ S; {" B• TIM2_CH2 : 定时器 2 的通道 2 的 PWM 触发
) ^" C/ K/ ], E• TIM3_TRGO: 定时器 3 触发* w+ Y% U! G' v* M$ W( `& O& H
• TIM4_CH4 : 定时器 4 的通道 4 的 PWM 触发
& o" v0 O( \9 S8 ~9 N6 h3 h9 s2 t- y8 `" V, W

, V; i' m8 a4 W
微信图片_20231023190424.png

- h6 Q! t; S5 ?; f) g* `  A; R
ADC外部触发方式
* G) Q) l4 e$ v/ I  E
* N; y" ]* D% ?( I3 N7 z9 K

7 ~! H8 m  j: B& d
这里以TIM4的通道4触发ADC采集为例,给出程序配置。3 g! u/ ^! g: I6 Z+ i1 A

' U8 H3 V3 [. R8 P8 K/ s首先是定时器PWM的配置,不对引脚进行重映射。
: x8 n8 v. c* i0 @3 h
  1. /*
    ' J) s% z' M6 U$ i) U- w
  2. *==============================================================================
    ) t+ |& G7 u. b: [7 m" _
  3. *函数名称:TIM4_CH4_PWM_Init
    3 A; z" ^- m- y$ T& a. O
  4. *函数功能:初始化定时器4的PWM通道47 m& [* l$ \3 |: G4 x' n8 D
  5. *输入参数:per:自动重装载值;psc:预分频系数
    ( H. j$ H* ^0 P5 S
  6. *返回值:无
    . c: o" s' @/ W, q" l! d
  7. *备  注:无  k  c, p. q1 U" [) Q8 g
  8. *==============================================================================6 C; ^. K6 A  ]$ u4 C0 z1 @
  9. */. ^; |  m7 }1 P" \' V
  10. void TIM4_CH4_PWM_Init (u16 per,u16 psc)
    / R  t0 [5 Q2 G4 F0 x
  11. {
    2 b# o  h" p7 t
  12.     // 结构体定义
    * J- M3 B9 q9 E/ \$ X# ]) V
  13.     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    $ a* Y/ R8 n- x( [; X
  14.     TIM_OCInitTypeDef TIM_OCInitStructure;
    1 g  Z& }4 k$ W3 `  _( P* D5 a0 D
  15.     GPIO_InitTypeDef GPIO_InitStructure;$ i6 H, K0 C) O' @6 K: v
  16.    
    " w$ R# h; H- k7 H, _. V
  17.     // 开启时钟
    ; {6 Q/ S; a0 k! A" D& {7 N
  18.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);" ?( f2 k4 I) T. b; U
  19.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
    5 q5 `2 X+ S8 C/ l! J% K
  20.     7 u. v; X1 j) z; U1 o( b* A
  21.     // 初始化GPIO
    % `  D0 `5 c5 w6 M3 i
  22.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;: v4 r4 e" h" m* F6 M/ L
  23.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;3 q3 |7 g6 p: V- r0 a2 H
  24.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   // 复用推挽输出5 V* _$ }: D  W6 j; ]5 z
  25.     GPIO_Init(GPIOB,&GPIO_InitStructure);
    6 L' N* R4 H" z7 g% G8 j
  26.     ' `1 {7 W/ l% G' a7 F- q+ S( y
  27.     // 初始化定时器参数
    ) F+ j4 u# E: z# c  q8 _
  28.     TIM_TimeBaseInitStructure.TIM_Period = per;   // 自动装载值
    : W( w. U- l. f* r% p" g6 w
  29.     TIM_TimeBaseInitStructure.TIM_Prescaler = psc;   // 分频系数! w9 x* G8 D' j
  30.     TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;3 U* D) n% E1 _( E( {
  31.     TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;   // 设置向上计数模式
    ) v, _2 t2 h8 k; `1 v) k
  32.     TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure); 2 a1 M  T* c8 z4 }% P
  33.     3 `3 {, s* [' S$ P) }" _
  34.     // 初始化PWM参数
    9 ]/ d6 X. {$ [6 \) B& N- w9 {
  35.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   // 比较输出模式
    8 P4 R. ^6 D: k3 F
  36.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;   // 输出极性
      @0 H) e# i0 x; f& T$ j
  37.     TIM_OCInitStructure.TIM_Pulse = 500;   // 脉冲宽度
    8 q4 u  W/ ~9 ]2 Z# O
  38.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   // 输出使能) F" N5 M$ _7 {& W9 m
  39.     TIM_OC4Init(TIM4,&TIM_OCInitStructure);   // 输出比较通道2初始化
    ; q- v# n8 w( m0 @! z
  40.       W. p( [$ v6 F0 d8 p. I
  41.     TIM_OC4PreloadConfig(TIM4,TIM_OCPreload_Enable);   // 使能TIMx在 CCR2 上的预装载寄存器
    ! S. q, R& f- A8 D* U' A
  42.     TIM_ARRPreloadConfig(TIM4,ENABLE);   // 使能预装载寄存器
    1 ?2 ]  q% E0 |& Z7 F* e( R
  43.    
    - w3 ^9 [7 e' L" F6 |6 T
  44.     TIM_Cmd(TIM4,ENABLE);   // 使能定时器8 ^4 q& X& c2 H; h. u, t
  45. }
复制代码

# M7 D6 d9 F0 a# X+ z. kADC配置程序如下,触发源选择TIM4的CH4,使能外部触发。
* L# q$ C3 X. v% @
  1. /*; e" F- I' b" ?
  2. *==============================================================================
    % K/ y6 p2 }+ I6 V- O" T
  3. *函数名称:ADC1_Init
    ; Q- e- `# H7 ^* ?" b/ I
  4. *函数功能:初始化ADCx0 M' ^5 s3 B7 a5 x
  5. *输入参数:无
      {+ B6 F8 C  L
  6. *返回值:无3 ^+ H% c5 q# x" J8 Z3 c7 d
  7. *备  注:无
    5 e- t6 n* \' ?, e# ]9 q
  8. *==============================================================================% n* e$ U9 {7 R: c0 J3 g$ g
  9. */
    ) B, a1 K# P+ {  a
  10. void ADC1_Init(void)  `' U3 A$ i  W( k
  11. {
    ! L  |1 n( c" v' L- j, S* N
  12.     // 结构体定义, W) x6 v! a. J+ o! S
  13.     GPIO_InitTypeDef GPIO_InitStructure;
    2 M: R9 q9 A5 j9 W( \; D
  14.     ADC_InitTypeDef ADC_InitStructure;
    + e0 p: a# u) ]9 ]! S
  15.     - R! `% e* X6 w3 q! Q2 t; w
  16.     // 开启时钟
    6 B4 d$ I. e0 {2 R# Y$ b0 b
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);4 C9 U/ [3 F' n1 @7 m* O& Z
  18.     , Y: Y5 c  n+ @. C1 t8 f# }: w0 V
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M, c& H" M, e' u1 g* y
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);+ N. `9 l2 W& l/ ^6 Q* s+ G, ^
  21.     // 规则通道配置
    , O/ |7 @3 J5 L: u% }4 p! y9 G2 z
  22.     ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);0 @- X5 T5 Q- D9 ~/ I: i
  23.     7 E4 x( X; z% c  Z' [$ {
  24.     // GPIO配置3 u5 p! g6 ]& S! k' m" l* q
  25.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道1/ @5 o6 Z4 I: ]* x0 H7 d
  26.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入* F& n3 T0 |# q
  27.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    # K' z3 ~( W; j. z/ X
  28.     GPIO_Init(GPIOA,&GPIO_InitStructure);  z& m, ~. C5 @8 x1 J
  29.     # t. u, }6 L9 ]) L% T
  30.     // ADC参数配置( g6 y8 y  ]  v) i) t  t
  31.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式: I/ G+ B7 ]/ Q! O
  32.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式
    ( q1 P! c9 K, ~; r# L4 j
  33.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换  F) H; u1 t% e5 B
  34.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;   // TIM2通道2触发' `* z) H7 g3 T; T
  35.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐
    9 F4 i* z/ ~, n9 M# o* C
  36.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1 $ Z; Z5 w; ]! @' K6 {2 @* I
  37.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化+ j) e, j0 \: g4 X5 Z* F8 B  A) ?
  38.    
    & g* R' R& }, p( G5 o) h
  39.     // 使能外部触发5 d* I0 o2 |3 K$ c1 T3 I
  40.     ADC_ExternalTrigConvCmd(ADC1, ENABLE);
    ) i3 B: u( a4 z8 ~) V/ a& m
  41.     . L  V7 B* G! Z1 _4 c- c
  42.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器
    ) `! `' s) _) l* b5 S, [( o
  43.    
    ) g5 @2 V7 y8 N) O
  44.     // ADC校准
    7 _, E# C3 \* z. Z
  45.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器
    ; ^7 f0 w1 @! y
  46.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态  Z4 M4 r+ E( J  R6 ?) B
  47.    
    0 @3 x; ^' Q- ]+ W& {4 K# U
  48.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态8 c4 ?: `+ o1 F! p5 c( U
  49.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序- Z3 M% s' |' f* S
  50. 8 G/ E  Z5 S9 k, B/ z4 o0 C6 g
  51.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能
    / \6 N5 u! Q  I7 A6 J
  52. }
复制代码

  j7 H4 D9 n) y1 |0 {main函数如下9 s1 Z* v+ m; Y( @* N! r
  1. u16 gAdcAdValue = 0;   // 存储AD值8 A( w  a( E' b" E! J- U, L
  2. float gAdcVol = 0;   // 实际电压值0 X+ m/ g- q3 X2 A

  3. - }: `/ S4 N! B$ f' |
  4. int main(void)
    % n7 C. m! R* y$ L
  5. {
    # a3 y! ^% D5 d% n
  6.     Med_Mcu_Iint();   // 系统初始化
    8 w2 P: b6 [" r8 R9 [1 R
  7.     8 h; T+ c3 i6 O* Z' ^, [% _
  8.     while(1)/ A* \, ~* ]2 g9 z
  9.   {
    7 v4 }# Y0 I2 U4 x( ]( B8 [
  10.         gAdcAdValue = ADC_GetConversionValue (ADC1);   // 获取转换结果2 N7 D! I! b* J  h# R# T# |1 p
  11.         gAdcVol = (gAdcAdValue / 0xFFF) * 3.3;   // 计算实际电压
    " {/ \' _+ U& d6 V) ^+ {
  12.         printf ("Vol=%.1f V\r\n",gAdcVol);   // 串口打印结果: E" h" K' r1 p& M  m
  13.         
    % t- P( M3 m2 t6 l" M2 E  n& m  E
  14.         delay_ms (500);   // 防止打印过快5 o: Z# w# l) ~" O2 Y% p
  15.     }
    " y8 h+ O6 `3 q' v7 X" u& w
  16. }
复制代码

4 g2 M! l. _7 s# y- [1 N: R% @6 L初始化定PWM时,程序为
; r% f( a6 h$ B! V
  1. TIM4_CH4_PWM_Init(1000,71);   // 初始化TIM4的通道4
复制代码

- P, v/ {+ u8 n& r# f# F分频系数为71 + 1,自动重装载值为1000,也就是1KHz的方波,也就是触发AD转换的频率为1KHz,与占空比无关。
) N# y  |* f% x. `) h' N6 \8 V# E- L2 ?$ i1 ?! V

, I# v( P& l' T7.2 ADC采集交流信号0 E+ ]; V5 {$ {. g0 Q
ADC能够采集的电压范围是0~3.3V,也就是说负电压无法采集。比如,需要采集下图中的一个交流信号& T' U- h6 J3 H( o' ^
, f  J& w. V- i" {/ N
微信图片_20231023190420.png

0 {  g4 z& i) b7 w0 R6 ^
交流信号图
9 v2 U0 ^3 V- T) g0 Z

5 K' m! J' x9 n) O
( k- p( `. n6 m
其位于0以下的部分是无法采集的。因此,在利用STM32采集交流信号时,在交流信号输入ADC引脚前,给交流信号增加一个直流偏置,将交流信号的最低点抬升到0以上,之后再输入ADC引脚。: h2 r4 i4 o9 n

  i! B8 v+ e- y/ |3 W( v
( K6 O. z0 q9 s1 n: v; B, K* ^
7.3 计算交流信号有效值$ D  J+ r* V; Y, j& C* {
ADC可以用于电流监测,实时监测主线路中的电流。当然,硬件方面需要搭配电流互感线圈,通过采集互感线圈两端的电压,来监测主线路电流。由于一般都是交流信号,所以需要计算有效值。# I' v2 }! @$ y8 q4 P

6 F0 e3 a6 z, t# [0 C根据我们所学的知识,计算交流信号有效值常用两种方法。一种是峰峰值除以根号2,另一种是计算均方根得到有效值。通常我们采用计算均方根的方法来计算有效值。因为如果用峰峰值除以根号2去计算有效值,峰峰值很容易不准确。如果在某一个时刻,由于环境干扰或者硬件问题,导致突然出现了一个很大的值,会导致计算结果与实际偏差较大。关于为什么计算均方根可以得到交流信号的有效值,这里就不做介绍了,只给出部分程序设计。由于博主目前身边没有合适的设备验证,因此仅供参考。
* k7 A: S6 f' J. q  b3 y1 _1 u
. e# u0 ]+ F) ]假设需要计算一个50Hz交流信号的的有效值,在其输入到ADC采集引脚之前,增加一个稳定的1.65V的偏置。ADC的采样频率为1KHz,也就是一个正弦波的周期可以采集20个点。假设采集到的AD值存储到一个数组中,计算有效值的程序设计如下1 p- L1 b8 G0 r+ K
  1. int gAdcAdValue[20];   // 存储采样结果AD值的数组
    % k7 V' C# F& s' i( u
  2. int gAdcValidValue = 0;   // 有效值2 ~- k7 ]8 ~; S2 D) ?! {; E4 w8 O
  3. , d5 O4 R# j1 f; q* H/ a
  4. void Med_Adc_ValidValueCal (void)& J0 u8 r) T0 P( K) u
  5. {4 E* U- B, o4 p, {" S. q
  6.     int tempVar = 0;   // 循环变量( T+ V" x& _  C  n  N
  7.     int squarSum = 0;   // 平方和
    3 s0 l, l4 k5 {- C0 K# H  G# o$ k2 @
  8.    
    - y2 ?7 _; D; ]
  9.     // 求平方和
    9 X% _5 _: k: m$ r
  10.     for (tempVar = 0;tempVar < 20;tempVar ++)
    % ^! ~$ h/ k) J; ^* ~9 a  E
  11.     {& ?* m3 \; e* N
  12.         // 减去直流偏置4 ?8 o2 d; t/ q' ]3 S
  13.         gAdcAdValue[tempVar] = gAdcAdValue[tempVar] - 2048;) k9 l1 o! S9 J1 ~# a
  14.         // 计算平方和
    1 g1 h% K. F3 x& T! n
  15.         squarSum = squarSum + gAdcAdValue[tempVar] * gAdcAdValue[tempVar];
    - [4 v7 p5 V4 L: R$ d" C
  16.     }" v2 Y6 p" G( K, U0 Q
  17.     5 |+ u6 {# T/ W' y" r7 }0 Q& ~
  18.     // 求平均* s% \6 E6 n2 y
  19.     squarSum = squarSum / 20;" e7 \% G/ ?. l! s2 ^1 Z* r
  20.    
    ) l4 y! N; X  ?6 j* T
  21.     // 开根号得到均方根(有效值)0 }# x) g4 o( t
  22.     gAdcValidValue = sqrt (squarSum);
    ( `+ ^7 b" l  c8 c# a0 |3 Y3 C, D
  23. }
复制代码

, G2 i: X; x) X. w2 z在进行程序设计时需要注意不要超出数据类型范围。在实际应用时肯定会存在误差,这里也简单介绍一下误差消除方法。目前用到的有两种方法,第一种是分段矫正,在不同的区间内,误差满足线性关系时可以使用。另一种是按比例矫正,这种方法常用于误差随着测量值的增大而增大的情况。在计算出有效值后,减去或者加上一定比例的计算值来做矫正。
6 w, x1 k! [* O9 r1 z% ?. }
" y' g% w% r: y; g, a4 f' p+ x
/ b6 L$ U2 S+ Y8 G) I7 C0 E; l

7 Y" R3 g5 c6 h5 O转载自: 二土电子
# p+ _2 o( i* \- P" r" f如有侵权请联系删除0 U3 d4 g0 I$ m; `: L8 i' G) i

5 G. z+ y3 ]0 ~, ~$ |# ^" G6 D
收藏 评论0 发布时间:2023-10-23 19:04

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版