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

基于STM32F1的CAN通信之ADC

[复制链接]
攻城狮Melo 发布时间:2023-10-23 19:04
一、什么是ADC( G3 j* Z+ W$ f- R* a
ADC(Analogto-Digital Converter)模拟数字转换器,是将模拟信号转换成数字信号的一种外设。比如某一个电阻两端的是一个模拟信号,单片机无法直接采集,此时需要ADC先将短租两端的电压这个模拟信号转化成数字信号,单片机才能够进行处理。
; v6 c2 N9 o% Y$ X* ~

$ {4 m- _0 V% `% E二、ADC的用途
; \" G# d5 V& a  pADC具有将模拟信号转换成数字信号的能力,比如将模拟的电压转换成数字信号,单片机进行处理。可以用作温度监测或者电流监测等方面,用途极广。) I) s6 J0 ]1 k$ e" Q

; `, S: w, V  M; o+ ~三、STM32F103ZET6的ADC
% K' c9 Z3 }& F2 ~$ |# M* I# j2 E6 L9 o根据中文参考手册介绍,STM32F103ZET6单片机有3个12位ADC,共有18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。+ _( V; B6 J- W
: }, `! Q/ X, A& I1 M

3 u6 M; ]& E2 U. L) e- j- x3.1 ADC通道对应引脚
5 d  k  O* D/ }8 o0 ]+ S: f. _" ^STM32F103ZET6的ADC各通道对应IO如下" z% k* w  v1 n0 `6 u
! L8 K4 x4 U; C4 i' B5 U
2LJFBR6J$G1TR[YF5Q9MQOF.png
; T3 w9 H. z& @6 f3 l   1 K  J# {( q, Q# K' f" ]$ o8 A
3.2ADC时钟9 c: F( U3 A0 G( U' E: v  g7 h  o+ M
ADC输入时钟ADC_CLK由APB2分频产生,最大值是14MHz。库函数提供了设置分频因子的函数/ G- i2 N& R7 u+ r
  1. void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)
复制代码

5 ]# r9 ]& f5 B可选的分频因子有  B& M. k% l" j3 G6 K
  1. #define RCC_PCLK2_Div2                   ((uint32_t)0x00000000)( \& c9 @- ~* \
  2. #define RCC_PCLK2_Div4                   ((uint32_t)0x00004000)0 P  x4 N) `5 G" B" N/ K
  3. #define RCC_PCLK2_Div6                   ((uint32_t)0x00008000)7 G# y% o2 ?# R& M/ A5 h9 C
  4. #define RCC_PCLK2_Div8                   ((uint32_t)0x0000C000)
复制代码

* o4 J/ X" n3 Z" a4 O/ CAPB2总线时钟为72MHz,而ADC的最大工作频率为14MHz,所以,分频因子一般设置为6,这样ADC的输入时钟频率为12MHz。
) o' u: p  ~/ P. q5 Z3 s
  A  S9 l1 r9 A- x& G  m. L
3.3 ADC工作模式
+ z- S# G% M  d根据中文参考手册介绍,STM32F1的ADC有三种工作模式
) e  b' j; k7 S9 ]• 单次转换模式 单次转换模式下,ADC只执行一次转换。该模式既可通过设置ADC_CR2寄存器的ADON位(只适用于规则通道)启动也可通过外部触发启动(适用于规则通道或注入通道),这时CONT位为0。$ R5 w, R$ l7 a6 {

; Y. D" G, S# f/ P5 V0 W• 连续转换模式 在连续转换模式中,当前面ADC转换一结束马上就启动另一次转换。此模式可通过外部触发启动或通过设置ADC_CR2寄存器上的ADON位启动,此时CONT位是1。  P, v. S" f+ Q9 I8 P) U. p' e
7 q+ b8 R% T$ y# ]
• 扫描模式: y: g# }, |$ b" ?7 ~' Q  S

, ]; w. A: z1 I/ C5 t! z
' s& z/ A! |3 q2 u' k3 Y
33.4 ADC转换时间
: T+ ~% T  u/ eADC的总转换时间与时钟频率有关,总转换时间 = 采样时间 + 12.5个周期。其中,采样时间最短为1.5个周期,也就是最短转换时间为14个时钟周期。使用软件触发时,可选择的采样时间如下2 Z+ N* E. v8 u
  1. #define ADC_SampleTime_1Cycles5                    ((uint8_t)0x00)
    ( U* f, J% k4 q6 W4 g; p$ F* I
  2. #define ADC_SampleTime_7Cycles5                    ((uint8_t)0x01)
    9 A2 m* q( `2 R& a8 A
  3. #define ADC_SampleTime_13Cycles5                   ((uint8_t)0x02)$ b5 r$ W, ?6 z' f
  4. #define ADC_SampleTime_28Cycles5                   ((uint8_t)0x03)
    8 G) @8 A2 e, `: \4 g
  5. #define ADC_SampleTime_41Cycles5                   ((uint8_t)0x04)
    6 h  A5 s9 N# v, [9 v" P
  6. #define ADC_SampleTime_55Cycles5                   ((uint8_t)0x05)
    & U# q/ L9 F+ m7 P
  7. #define ADC_SampleTime_71Cycles5                   ((uint8_t)0x06)
    ; I& b: [, V/ Q: X
  8. #define ADC_SampleTime_239Cycles5                  ((uint8_t)0x07)
复制代码

- N7 c, u9 Q( {/ }3.5 ADC校准
' l0 b- c( w4 l+ S: ]% G: K使能ADC后,需要对ADC进行校准。使用库函数开发时,提供了ADC校准的函数5 k) s5 u8 g4 V9 G# l" J
  1. ADC_ResetCalibration(ADC1);//重置指定的ADC的校准寄存器6 \9 @$ S" S6 |6 u; A: b
  2.     while(ADC_GetResetCalibrationStatus(ADC1));//获取ADC重置校准寄存器的状态  J# }' J- Z) j9 r! X! j  T( n
  3.     % o% F3 l0 O3 E
  4.     ADC_StartCalibration(ADC1);//开始指定ADC的校准状态" ]0 b1 \3 r7 M3 \
  5.     while(ADC_GetCalibrationStatus(ADC1));//获取指定ADC的校准程序
复制代码
) i# |7 \- }/ q: k/ T
3.6 ADC转换结果与实际电压的换算

. E" |4 c# Q4 a  H/ A* `获取到的AD转换结果并不是实际电压,如果想要得到实际电压,需要经过换算。上面介绍了,STM32的ADC为12位,也就是AD值取值范围为0~4095。采集电压范围为0到3.3V。AD值与实际电压之间存在比例关系。4 y. b  x. ^) {( V) h$ n

( j( v) l) A4 |! ^; H. c实际电压 = (AD值 / 4095) * 3.3。单位为伏特(V). r2 f* P$ M& }9 E
8 F) i( n/ Q1 }) _; @7 d
' r" y: y% W, R! b# v( ?
四、ADC配置步骤6 Y& k* ?0 B9 l: I" k2 ?, a
• 使能GPIO时钟和ADC时钟,设置引脚为模拟输入
$ C9 F5 @" I- s8 [) @• 设置ADC的分频因子, U# y5 H+ X; z% }
• 初始化ADC参数,包括ADC工作模式,规则序列等9 f9 K( ~6 T0 n% L2 w
• 使能ADC并校准
8 f- \; H. @+ Q; r/ v• 触发AD转换,读取AD转换值
; p% _: E' c7 v8 y9 ]
; \" O5 P# T7 l5 ^

/ {2 J' K4 Q- O五、ADC配置程序7 t6 Q9 q, c( _/ t
55.1 ADC初始化程序
9 c3 \3 ?# N/ Y3 a' K* t) ]
这里以配置ADC1的通道1为例,给出ADC的配置例程,分频因子设置为6,单次转换模式,软件触发。( W" o% }& J4 T1 ~4 h! c- n; }: V
  1. /*2 V) V4 k  l& X7 L
  2. *==============================================================================2 L5 i" v8 T1 ^& J. b3 y! P- g1 a0 R
  3. *函数名称:ADC1_Init
    % }- |$ D3 j$ b; j) \2 L1 r
  4. *函数功能:初始化ADCx2 u( I' H7 h( b( O4 p+ \7 P3 g6 C; H
  5. *输入参数:无
    6 ^2 w; ?; u5 C* b0 F
  6. *返回值:无
    ) u1 r# Q% l; r  s7 f  A
  7. *备  注:无
    & J+ O3 I6 k$ V
  8. *==============================================================================4 _: }: ]2 l( q( T
  9. */
    6 B9 r4 q/ K' K" Q
  10. void ADC1_Init(void)
    3 Z3 ?5 f8 ?) x8 \
  11. {
    0 U. A4 |  |; `6 n) S# W
  12.     // 结构体定义
    7 R' q( @8 \7 m) k
  13.     GPIO_InitTypeDef GPIO_InitStructure;
    9 K6 A8 e' g' P$ @3 V
  14.     ADC_InitTypeDef ADC_InitStructure;
    0 a, k9 m. G- O- h  \
  15.     / g( p3 S5 [- Q
  16.     // 开启时钟! D! d. L9 q" c- t+ ^9 K
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);
    5 J- K7 b- x# A/ z
  18.     . `7 \3 ], S# O0 Y
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    & ]8 A1 Q. S- s# {4 R6 O5 B6 e) S
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);# d% y8 ~% E% b1 O
  21.    
    # |7 U% U1 u. Y4 T% [) Z1 p: M6 X
  22.     // GPIO配置
    8 k8 f9 }7 h- @
  23.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道18 L- p- a" j$ g" F# B$ b, e
  24.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入. R) M. q, N4 v! k9 M3 w
  25.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    # E% Z2 w3 m' w1 S4 f6 b6 |
  26.     GPIO_Init(GPIOA,&GPIO_InitStructure);
    . s" H) N1 G2 D0 S  r
  27.    
    ! j" q/ x1 Q# o2 s
  28.     // ADC参数配置
    & C: j# J% b. a8 o$ B$ A* R4 h
  29.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式
    " l! B2 j" w6 Y% d+ h. a
  30.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式 ; c+ p$ i4 |  e% D
  31.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换) m& G7 s6 L! O& s5 Q0 t. T, |, U
  32.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   // 禁止触发检测,使用软件触发
    % l' J# z. I/ c- a* N4 }
  33.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐
      G) M4 V( h4 h; N' g$ q
  34.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1
    ) ^( k, @3 ?- L% U
  35.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化
    0 t0 d$ y$ g# w5 N" A# G. g
  36.    
    5 u: @3 g9 a0 y! v$ s2 t
  37.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器+ r6 k( M. ~9 d/ X! v) F
  38.     & O% M' H1 Y6 L4 O& h
  39.     // ADC校准6 e  _* L0 w# h! p$ r- R/ _
  40.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器9 f8 i0 f- U  a4 t
  41.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态9 V. w( j8 a8 S$ ^
  42.    
    0 v* I+ }) ]4 h/ E
  43.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态& M8 H3 t$ x7 T! U+ J
  44.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序& @" i% R* u. s
  45. 2 z: B, y- x: L( _3 g3 B# M
  46.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能
    : k; _: Y1 k1 E+ V! d
  47. }
复制代码

3 Z& G1 h8 w. C6 K1 L5.2 软件触发AD转换$ ^* Z2 \2 Q6 G" ]/ Q2 j/ @  p
库函数开发,配置为软件触发时,可以通过下面的函数触发AD转换* Z4 x! d5 t" D
  1. void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState)
复制代码
( K; P$ t& \% [% `7 y* P/ B6 O
5.3 读取AD转换结果- E. \6 F! J' x& V: k4 V7 X8 j2 Z
库函数提供了用于读取AD转换结果的函数* z4 ^6 b: q9 t
  1. uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx)
复制代码
; ^' ^5 _" f3 X. R: ^0 n3 V
这里给出另一个函数,用于软件触发AD转换并读取转换结果+ |2 h, `# v- {. K: A# |: {
  1. /*
    2 L* E& h6 ]2 O4 a' W. I/ N
  2. *==============================================================================% u2 @0 \  r) ^) h$ [4 ~# I
  3. *函数名称:Get_ADC_Value. N5 p4 L! v5 E' y6 m: J" m9 M; x
  4. *函数功能:读取某一规则通道AD值( |7 V& }( m- x6 B, x" x" J4 E
  5. *输入参数:ch:规则通道ADC_Channel_x;times:读取次数/ N1 D, q4 i& z. R. f
  6. *返回值:无2 w1 b/ g  s$ C  S3 _, ~7 [
  7. *备  注:该函数配置好后,返回的结果是N次后的平均值/ a( ]+ p" @) U
  8. *==============================================================================
    6 x1 O. t/ }  S' p/ d. I- W
  9. */1 \; i+ X  m5 u& c! ?+ c" T
  10. u16 Get_ADC_Value(u8 ch,u8 times). |5 a6 H5 [. i6 l
  11. {
    3 j$ ?3 X* r' A. K" e
  12.     u32 temp_val = 0;
    ! Y0 m3 B# ]$ [/ Z( P9 l6 m* |
  13.     u8 t;
    % B: x5 c2 b' f0 O1 H0 j
  14.     // 设置指定ADC的规则组通道,一个序列,采样时间- g, x3 H5 U0 I4 \7 u9 o
  15.     // ADC1,ADC通道,239.5个周期,提高采样时间可以提高精确度
    $ T3 G5 w1 H- q" T
  16.     ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);       ) u# Y& k, Y' t
  17.    
    $ `8 \8 n9 p/ D8 N4 o+ k0 C4 w
  18.     for(t=0;t<times;t++)* X- X" u$ S8 _
  19.     {
    2 q" D" D! l4 Z
  20.         ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能指定的ADC1的软件转换启动功能
    4 K8 u- [2 y! g0 ]5 I
  21.         while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));   // 等待转换结束
    6 _: E, C7 _' z- T% I3 o) E
  22.         temp_val+=ADC_GetConversionValue(ADC1);
    - f& A4 p# E) f2 k6 U
  23.         delay_ms(5);
    9 |0 [3 t/ d* [; W
  24.     }" p$ B1 P+ j* a( A& Q
  25.     return temp_val/times;
    : Q/ a! }9 c3 ^5 Q: W
  26. }
复制代码
. Z7 C$ J; d2 ?/ e
六、实战项目# y. T- P' m% P% y" g2 k
用ADC1的通道1采集某电阻两端电压(由于普中核心板没有可供采集的电阻,可以直接将采集引脚接到3.3V查看一下结果),将结果通过串口打印到电脑。其中关于串口的配置就不再做介绍,给出ADC的配置和main函数。( l  i2 }. }" j& O
% h% H' c* a: m9 m
6.1 ADC初始化
6 e$ K, i5 \# `" g3 n
  1. /*
    6 C$ H( ^7 P* ^3 W8 M6 L0 l
  2. *==============================================================================9 x- ?$ S# R- N3 ?, ^
  3. *函数名称:ADC1_Init
    # R3 Q$ {" V5 ]
  4. *函数功能:初始化ADCx9 Z) W8 E3 i0 p$ i# X
  5. *输入参数:无
    , d) Z2 W$ v2 h9 `/ G
  6. *返回值:无/ f& |$ D+ V4 {. c
  7. *备  注:无' f1 h' W0 P8 y/ {
  8. *==============================================================================
    $ w& ^6 {- I0 D% {; l& m
  9. */2 v% w+ u5 b0 y- a- L. e$ U! f
  10. void ADC1_Init(void)3 G- Y* v" x4 ?  t# \: b" _0 {( ^" U
  11. {
    ' T+ V" o3 T9 O; a
  12.     // 结构体定义
      e  W3 R. k- E' X, \7 y  D
  13.     GPIO_InitTypeDef GPIO_InitStructure;7 r. o# S) G& X) G
  14.     ADC_InitTypeDef ADC_InitStructure;
    , ?+ Y! w5 Q  {! }8 b7 A3 ~
  15.    
    , T$ o6 p0 X1 E
  16.     // 开启时钟$ A; W! L7 b$ T- R5 f
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);
    5 {+ ^/ K4 d9 R4 N+ ]: H' }% v. U0 W; w
  18.    
    0 V' J" W$ C* r
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M: c7 Y7 L& }7 }* M- w& F: H; j5 Z. A8 e
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    ) z, e, {: [/ Z
  21.     , |/ h! g# F) b7 X
  22.     // GPIO配置
    ( r% v. }& h/ m# ?- f+ H% j
  23.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道1
    " D; b' a; O, }1 }
  24.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入" r- V) |: |' ^+ T$ }4 [. g* m
  25.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    - {5 ~, ]0 ]7 n. r& l7 c3 _. g# [
  26.     GPIO_Init(GPIOA,&GPIO_InitStructure);
    - h. A* D8 h6 U2 _, E
  27.    
    4 F4 l( I0 r; n5 r
  28.     // ADC参数配置* C. O: O) H* @* e$ ^( T
  29.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式* k8 F; B/ D, S* E$ Z% p& F9 J" I" Y
  30.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式
    ' Y' u) I' v, c+ p$ V+ S. C; k
  31.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换
    ; z" h* F. A! O$ O; r
  32.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   // 禁止触发检测,使用软件触发
    , H) t/ {8 q% M- b7 |0 X6 `
  33.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐
    " N3 K, O8 w/ {- R! W
  34.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1
    2 A0 A4 a5 K) G& z
  35.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化  V/ ~4 G0 [; @0 G8 F' r4 B* h
  36.     $ K& J3 b: l/ S5 G4 t8 _' [
  37.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器% W9 }$ R6 S8 M3 ~- `. v
  38.    
    7 y1 J* L- K1 t/ B" f
  39.     // ADC校准
    - I+ f  z: i# h& f# Y+ w. M7 B
  40.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器1 L9 C, d; R4 V1 H
  41.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态$ }9 ?; @7 l5 N2 Y
  42.    
    ; ~2 j* X: ]$ w' K
  43.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态
    4 x. l% L. X- w( k  |* c2 z" {( @( u
  44.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序6 x9 m1 M  Q$ ^: q8 O6 v
  45. 6 w& j% L1 [  e/ t! E; A8 J
  46.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能
    5 Y1 Q- U3 R; }4 g: r- K
  47. }
复制代码

) Q1 l. h/ u/ V5 z2 \9 Z; a4 u& F' Y2 k
6.2 main函数
2 T" E! f5 q# V, i1 t
  1. u16 gAdcAdValue = 0;   // 存储AD值
    6 \2 }& H' Y; X( Q& w+ G: j" A
  2. float gAdcVol = 0;   // 实际电压值
    * J$ K' k6 t4 g1 g5 A% S( A7 v
  3. " g+ x' `7 |" d" b$ q, \
  4. int main(void)+ _8 L, q. T( v9 n" A
  5. {
    * s+ W* _' W& _7 l! L9 w1 [9 W
  6.     Med_Mcu_Iint();   // 系统初始化
    ' P0 j4 `& v/ r# ]3 S1 X
  7.     ' \: }. Z4 @" a2 O7 u  O# u
  8.     while(1)
    * l& F. e7 H3 g( h$ N
  9.   {
    % Z4 Y5 h& i7 r, O/ U7 a+ N
  10.         gAdcAdValue = Get_ADC_Value (ADC_Channel_1,10);   // 获取转换结果
    $ w; w$ x+ C7 E/ N
  11.         gAdcVol = (gAdcAdValue / 0xFFF) * 3.3;   // 计算实际电压
    8 }2 p; o' a( I' I
  12.         printf ("Vol=%.1f V\r\n",gAdcVol);   // 串口打印结果3 R, E: }4 {  i& v$ a
  13.         8 y/ i0 N6 k# B+ B7 n# N: ~8 x
  14.         delay_ms (500);   // 防止打印过快5 N+ ^& M7 E( k" p/ [- n
  15.     }
    2 d/ P+ i% a8 i% q( l, h8 r6 S* J
  16. }
复制代码
% M' j0 ]- \& a8 R+ n
: @7 U3 ?6 N! v6 w5 ~
七、拓展; N8 E. d, ]6 Y5 O$ y6 X3 i
7.1 定时器触发ADC采集
- x; j% F# \3 K( ?8 l
根据中文参考手册介绍,ADC可以通过定时器触发AD转换(只有PWM的上升沿可以触发AD转换)。触发方式有以下几种
; p$ ]" ]; W! f• TIM1_CH1 :定时器 1 的通道 1 的 PWM 触发* E) n9 f2 l2 X" X- @
• TIM1_CH2 : 定时器 2 的通道 2 的 PWM 触发
/ N$ u/ T( A. a• TIM1_CH3: 定时器 1 的通道 3 的 PWM 触发+ Z; w, Q+ e; D/ v3 Q% R
• TIM2_CH2 : 定时器 2 的通道 2 的 PWM 触发
" o" G9 f! l3 E  G• TIM3_TRGO: 定时器 3 触发" \9 a7 W( Q' h# w$ t
• TIM4_CH4 : 定时器 4 的通道 4 的 PWM 触发
3 Z% }& R& H/ |+ C
+ G( ^: g" f. ]3 W- P4 C0 s
1 Z6 C! r/ ~. x. W4 }1 c. X& a6 e5 H5 m
微信图片_20231023190424.png
# G! N9 ]7 q+ p  {
ADC外部触发方式
5 c5 k6 q9 {. M7 y

) ]; r9 s$ t4 B5 Q* h. k
7 M# N$ G& p8 T# Z( |
这里以TIM4的通道4触发ADC采集为例,给出程序配置。
/ @; z; u$ F8 G& e
3 M" p, s) v) P9 n0 t  r, _2 u首先是定时器PWM的配置,不对引脚进行重映射。
/ V8 k" W  j+ O5 s6 R1 ~8 M
  1. /*
    5 z8 c# S. m* {( _& J
  2. *==============================================================================
    + N: C' m* j9 p; n
  3. *函数名称:TIM4_CH4_PWM_Init2 w# B6 B% _; Q1 c& ~
  4. *函数功能:初始化定时器4的PWM通道4/ @3 {$ C) x3 \% Y" j4 |# \
  5. *输入参数:per:自动重装载值;psc:预分频系数
    5 b& X" {6 `+ x8 V; P
  6. *返回值:无
    ! L" T  I5 h# ~6 P( ^$ G. x! C9 s( L
  7. *备  注:无
    . b$ W- T" E3 e6 S4 t! T' n9 u; m
  8. *==============================================================================
    * n( N1 }" A7 n# |8 a$ d  @& Z  X
  9. */
    % g! n1 @) h' [9 N2 g1 ^
  10. void TIM4_CH4_PWM_Init (u16 per,u16 psc)7 Y3 F7 D/ q$ I
  11. {
    $ t# e% W* _, o) F
  12.     // 结构体定义; h  `' P0 p$ j2 y3 F" `
  13.     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
      |( a) d2 s. O# E3 O4 u
  14.     TIM_OCInitTypeDef TIM_OCInitStructure;
    3 G$ a3 u; \$ Y0 s+ G
  15.     GPIO_InitTypeDef GPIO_InitStructure;3 k! D' G5 J( O8 ~" X( V3 P% A2 M7 a
  16.     ! b; |9 T, `. c4 z5 |  W! s
  17.     // 开启时钟4 ~* k2 e" w& p. f; R
  18.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    * t4 E, \' b0 Z; n1 ^# Q' O
  19.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
    $ N: P5 @4 }3 p. u/ ]6 ?
  20.    
    ( ^7 a) T1 Z  }4 O
  21.     // 初始化GPIO: ~+ U2 K8 F# _5 r$ {" n1 ]8 \7 K
  22.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;& L5 m' ?8 j& j! y& Z5 x, [& B1 v
  23.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;# V& A  |2 V0 a! N' Z
  24.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   // 复用推挽输出% {3 j) V3 o0 f1 j; A2 e
  25.     GPIO_Init(GPIOB,&GPIO_InitStructure);
    ) n) M& J* J. O  K
  26.    
    ) w3 E" g" p, u8 c
  27.     // 初始化定时器参数
    : i( w% e+ V. V8 J4 v, l5 i
  28.     TIM_TimeBaseInitStructure.TIM_Period = per;   // 自动装载值, s: N. C8 x5 o4 E2 ]
  29.     TIM_TimeBaseInitStructure.TIM_Prescaler = psc;   // 分频系数' }% C( ^. W6 N; T" c+ C
  30.     TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;. V5 h- \( E% ~
  31.     TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;   // 设置向上计数模式
    $ K5 }$ ~8 B5 c  O* F! ]2 X  H
  32.     TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
    - O$ E* ?5 M5 I. p$ ^0 L, y' h. m
  33.    
    ) @! l- Q) v4 ?  t' b, F
  34.     // 初始化PWM参数
    ) r# _! H5 z% N4 Q7 \/ V
  35.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   // 比较输出模式
    9 z8 {5 Z( a( K  l% K3 D% m' g
  36.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;   // 输出极性$ B: @8 h; }2 V* [' A3 t( f
  37.     TIM_OCInitStructure.TIM_Pulse = 500;   // 脉冲宽度
    + f1 M6 t/ {: U2 [% e9 G  E
  38.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   // 输出使能2 b) w1 X! K  f' {# ~
  39.     TIM_OC4Init(TIM4,&TIM_OCInitStructure);   // 输出比较通道2初始化
    , t! O: f* ^1 f1 y* m
  40.     + f0 C7 Z0 g' G( E, w, t3 C
  41.     TIM_OC4PreloadConfig(TIM4,TIM_OCPreload_Enable);   // 使能TIMx在 CCR2 上的预装载寄存器
    + P$ E* l" B5 M: R5 g; }2 ~
  42.     TIM_ARRPreloadConfig(TIM4,ENABLE);   // 使能预装载寄存器' w; i: h/ @8 ~
  43.    
    . H3 |9 @: S4 K: z& ^/ m& G! c
  44.     TIM_Cmd(TIM4,ENABLE);   // 使能定时器2 t; i: t( j8 ^* f- l% L. p4 v( v
  45. }
复制代码
( Q6 M  p# `6 X& Q9 u& @
ADC配置程序如下,触发源选择TIM4的CH4,使能外部触发。
+ v* V" ?) K) x$ B( {$ R
  1. /*
    # K+ B( d1 B5 z- o' D9 ]
  2. *==============================================================================1 n, p6 G2 a; R
  3. *函数名称:ADC1_Init. {: D  N$ i# J- c
  4. *函数功能:初始化ADCx
    . V( T4 u% g/ q$ s  ~" v
  5. *输入参数:无1 r. {! F/ J; @4 Y, X; m+ x+ j
  6. *返回值:无# z/ ~' P* a3 J
  7. *备  注:无
    ! I5 R$ ]" G8 C! |- O7 N
  8. *==============================================================================
    3 y9 q  y6 ]" r* D9 f! O- H
  9. */
    ! ~4 Q: p9 P4 l* P2 h% \& [
  10. void ADC1_Init(void)
    % y* R# ^$ T! Q5 n5 S
  11. {
    + V& v: o+ ]: H7 I! x' U5 i
  12.     // 结构体定义! W9 m% ?% [3 l
  13.     GPIO_InitTypeDef GPIO_InitStructure;: F7 b/ P( J4 v0 T! ?
  14.     ADC_InitTypeDef ADC_InitStructure;
    * J4 y2 P& |8 g4 f$ _
  15.     ! k) V) @0 j$ K- A. G0 d- W
  16.     // 开启时钟0 i, a6 H+ b6 Q% W( W) ?/ Y: ]
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);+ @, G/ t% O/ _8 i" ^6 ^0 y/ p
  18.     / o" W* o% S& p( c! g. @$ \8 u
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M" Q& k' h2 P; u1 g6 u- _( f5 P. }! a; q
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    9 m# j8 C  m3 |3 o& c
  21.     // 规则通道配置
    3 ~/ ^% j, J  O' T! B4 ?  X
  22.     ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);7 y: K& `; N8 N7 S9 ^7 ?6 ?+ T
  23.     % k+ x/ x" _% h: S6 u* ]
  24.     // GPIO配置) R3 [) s6 `/ `( R  K3 L1 A4 J
  25.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道1* y  i/ b1 ~% h' G, F
  26.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入
    : V" X9 ~6 W4 [- X( Z( F
  27.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;6 V5 F9 Q& e; R4 Y  S) R
  28.     GPIO_Init(GPIOA,&GPIO_InitStructure);
    9 ^9 r% |" P1 e
  29.    
    + J2 s/ O6 I/ }) T' k
  30.     // ADC参数配置
    # t6 ^3 O) {* e
  31.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式
    - u' e8 l0 ^% M. Q
  32.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式
    ! ?- \( L% U+ [
  33.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换9 z; c& v" j, b8 E( C, H4 a- A
  34.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;   // TIM2通道2触发' V- l7 _! J) d- o* B- q  d
  35.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐 : H& k! ]3 u% S9 d: Y+ x) Y
  36.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1
    " n( U( g4 A& u5 f
  37.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化; o. k: ~% @% T
  38.       K* C- e& G7 q# e1 A
  39.     // 使能外部触发' M+ ~6 z7 |* c/ W" @) p2 B
  40.     ADC_ExternalTrigConvCmd(ADC1, ENABLE);
    : T7 h) [/ P. i, L. _0 G# ]
  41.    
      N  b( E7 O4 r0 _  ~
  42.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器
    ( x8 N( J8 V3 I6 Z6 _
  43.    
    ' V) R6 f1 W1 ?1 p3 s/ Q
  44.     // ADC校准
    4 M, S$ _: ~8 N; s6 ~
  45.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器
    , ^& T8 L, Q. a' d0 T2 m0 a/ I( S
  46.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态& V. K! Z* W" R. S& g/ M5 g
  47.       r6 j# Q; r0 S7 H2 p( Z% ]
  48.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态  Q- {) n5 a  n& M
  49.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序9 U$ ]2 e) b( F' P5 G, p3 ?' r

  50.   n: P) l6 w  k  ^$ P% x
  51.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能7 F2 k& b% a4 B7 {
  52. }
复制代码

; O% H2 O' c: E( T2 lmain函数如下
0 _, _3 X1 o8 o
  1. u16 gAdcAdValue = 0;   // 存储AD值
    6 M1 ^% t% O. ]. `8 i
  2. float gAdcVol = 0;   // 实际电压值; ^2 v# F* j* n/ e0 ?

  3.   @6 D$ o8 I' q& q
  4. int main(void)5 w9 Z: Q) L" Y, U0 @6 p( n
  5. {5 A- v( P+ k3 D# Z; @% ^
  6.     Med_Mcu_Iint();   // 系统初始化
    + Y5 s6 `& R3 S1 g" D1 k
  7.     ( f# d6 ]) ~; n% Z
  8.     while(1)
    6 {) E4 Y; t3 q  }4 R
  9.   {
    0 c7 `4 P9 D5 g1 e; ]: R
  10.         gAdcAdValue = ADC_GetConversionValue (ADC1);   // 获取转换结果
    8 `% W6 b2 j8 u* \0 g9 v
  11.         gAdcVol = (gAdcAdValue / 0xFFF) * 3.3;   // 计算实际电压
    9 J' G) l! w' ]# {3 l2 N5 s8 \
  12.         printf ("Vol=%.1f V\r\n",gAdcVol);   // 串口打印结果+ O6 V- F7 y, R6 ?$ j
  13.         " P2 Y: l' g% }- d2 e8 l
  14.         delay_ms (500);   // 防止打印过快
    . g% f6 t' h. L7 v: z! ?( m, z
  15.     }
    3 B2 g" E: \  |5 \* a
  16. }
复制代码

9 G: F, Z1 N" ?/ Y/ i. ]5 K初始化定PWM时,程序为
6 u9 ?5 O' F" H6 B% E
  1. TIM4_CH4_PWM_Init(1000,71);   // 初始化TIM4的通道4
复制代码

; e- \: k* Y  W6 [分频系数为71 + 1,自动重装载值为1000,也就是1KHz的方波,也就是触发AD转换的频率为1KHz,与占空比无关。
9 a6 b! \, {/ u$ e# w3 a0 r" L/ |% l6 O4 L: }* J

" F6 g5 D2 m. x4 c7.2 ADC采集交流信号
" ?) F, ?* h7 `* @' v7 y* _/ V0 V" sADC能够采集的电压范围是0~3.3V,也就是说负电压无法采集。比如,需要采集下图中的一个交流信号% B1 X; ^' U- c; L# C: J/ @

1 V6 |% U  {( a
微信图片_20231023190420.png

. Q% a$ R9 x2 E2 ?/ Q
交流信号图

- T- y/ b9 b3 V6 x
- ]& i/ e3 L! M: X# R. f3 I2 ]
& I: l# |& a3 L9 k% D
其位于0以下的部分是无法采集的。因此,在利用STM32采集交流信号时,在交流信号输入ADC引脚前,给交流信号增加一个直流偏置,将交流信号的最低点抬升到0以上,之后再输入ADC引脚。& M1 Y1 Q/ K4 H: V/ N: h4 I
& I( q. `; f, V; z

. A2 |; s- p- N3 v7.3 计算交流信号有效值: s$ r: J7 [  T" F( J. t
ADC可以用于电流监测,实时监测主线路中的电流。当然,硬件方面需要搭配电流互感线圈,通过采集互感线圈两端的电压,来监测主线路电流。由于一般都是交流信号,所以需要计算有效值。
) U& P* X& h! Z' k: R1 M5 y+ |

& ~' S3 n* Y% z& A4 F4 [( J+ r1 p9 ?根据我们所学的知识,计算交流信号有效值常用两种方法。一种是峰峰值除以根号2,另一种是计算均方根得到有效值。通常我们采用计算均方根的方法来计算有效值。因为如果用峰峰值除以根号2去计算有效值,峰峰值很容易不准确。如果在某一个时刻,由于环境干扰或者硬件问题,导致突然出现了一个很大的值,会导致计算结果与实际偏差较大。关于为什么计算均方根可以得到交流信号的有效值,这里就不做介绍了,只给出部分程序设计。由于博主目前身边没有合适的设备验证,因此仅供参考。
/ F9 v- O# R3 J+ G1 T1 K! f; Y3 E, v1 q$ F- |
假设需要计算一个50Hz交流信号的的有效值,在其输入到ADC采集引脚之前,增加一个稳定的1.65V的偏置。ADC的采样频率为1KHz,也就是一个正弦波的周期可以采集20个点。假设采集到的AD值存储到一个数组中,计算有效值的程序设计如下
; K& n* q6 K6 ]3 b7 d
  1. int gAdcAdValue[20];   // 存储采样结果AD值的数组5 R/ L, E9 h+ u8 ?5 r; J
  2. int gAdcValidValue = 0;   // 有效值
    , Z# \$ v& u1 h2 G0 ~; A+ |! C0 T
  3. . v! X7 N- u- @6 s' ~
  4. void Med_Adc_ValidValueCal (void)- c" W* a9 \0 J" t: Y
  5. {
    ' G. w7 j1 O) ^( `) s! {
  6.     int tempVar = 0;   // 循环变量5 I4 |* o& \9 a5 b
  7.     int squarSum = 0;   // 平方和
    , Q$ A9 T0 n# V$ y8 V  x5 g8 C+ z
  8.     " _8 L# v5 v# B! z, y
  9.     // 求平方和
    4 [0 F0 i) G1 i: }  \
  10.     for (tempVar = 0;tempVar < 20;tempVar ++). n0 a- g% \7 f, Q& H
  11.     {+ y# e/ c8 b( P5 g" C
  12.         // 减去直流偏置8 d0 x0 x! T' w. L& _; A, a
  13.         gAdcAdValue[tempVar] = gAdcAdValue[tempVar] - 2048;, I  s3 ]$ s4 \0 [
  14.         // 计算平方和
    ; r0 k8 {7 a; `7 X* a
  15.         squarSum = squarSum + gAdcAdValue[tempVar] * gAdcAdValue[tempVar];5 I8 [. O1 w: m6 p! ?, l9 J  s
  16.     }1 x4 b$ b; l: F6 `  b1 p
  17.    
    : o8 f; @# u$ `
  18.     // 求平均2 [! }  C& U  t7 x$ R+ h+ e! X4 ^
  19.     squarSum = squarSum / 20;2 @: Q5 J6 `) o" X, j: A2 B
  20.    
    * ~* b6 K4 Q1 ?! L! E* n
  21.     // 开根号得到均方根(有效值)( q3 V0 _4 R% ]# G
  22.     gAdcValidValue = sqrt (squarSum);# a& `% |/ F/ d8 n
  23. }
复制代码

, Y2 V7 Z0 G* a  ^8 ]1 ?0 H5 Q: X$ s在进行程序设计时需要注意不要超出数据类型范围。在实际应用时肯定会存在误差,这里也简单介绍一下误差消除方法。目前用到的有两种方法,第一种是分段矫正,在不同的区间内,误差满足线性关系时可以使用。另一种是按比例矫正,这种方法常用于误差随着测量值的增大而增大的情况。在计算出有效值后,减去或者加上一定比例的计算值来做矫正。7 q3 l2 m5 J- R8 k0 m, n8 {
. m' R/ V9 w, g7 q
% k' s) q% k1 ?4 p0 z
7 K5 B- Z% L6 Y5 d/ b
转载自: 二土电子
# C" R. a! i3 m, w( W如有侵权请联系删除/ c, u5 `, F3 \3 a
+ N2 P2 c6 K  v
赞 收藏 评论0 发布时间:2023-10-23 19:04

举报

0个回答

所属标签

相似技术帖

官网相关资源

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