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

基于STM32F1的CAN通信之ADC

[复制链接]
攻城狮Melo 发布时间:2023-10-23 19:04
一、什么是ADC
" S$ Z+ X. a( ~, FADC(Analogto-Digital Converter)模拟数字转换器,是将模拟信号转换成数字信号的一种外设。比如某一个电阻两端的是一个模拟信号,单片机无法直接采集,此时需要ADC先将短租两端的电压这个模拟信号转化成数字信号,单片机才能够进行处理。
/ w1 g; i  E) G& q. u

' u! d& o7 Y* H% ?0 f二、ADC的用途
5 ~- c# L8 B$ A" |. }! RADC具有将模拟信号转换成数字信号的能力,比如将模拟的电压转换成数字信号,单片机进行处理。可以用作温度监测或者电流监测等方面,用途极广。; l1 c1 B# o7 G; [2 a
8 P# f7 ^* {. ^9 i
三、STM32F103ZET6的ADC
+ v! |: e6 e) _: }根据中文参考手册介绍,STM32F103ZET6单片机有3个12位ADC,共有18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。ADC的输入时钟不得超过14MHz,它是由PCLK2经分频产生。
$ t* W7 A7 g$ j/ q( T5 C$ o8 _$ e/ W4 s% C0 h6 Z

* @) U+ Y2 V1 D& _! P4 y3.1 ADC通道对应引脚* v5 O$ |- W$ ]2 C: Q% J
STM32F103ZET6的ADC各通道对应IO如下0 J. j! J5 V7 E) S" Z. @  A

; c7 |/ a* b/ b6 r0 _( q! F
2LJFBR6J$G1TR[YF5Q9MQOF.png 9 w0 G7 D9 |4 s4 r& v9 X3 q
   
% f3 K! p" u) J) r6 s3 T  D3 U3.2ADC时钟' p3 h* t$ h# N. Z& Y; @
ADC输入时钟ADC_CLK由APB2分频产生,最大值是14MHz。库函数提供了设置分频因子的函数/ h9 c1 s, L# Z) e( u1 W
  1. void RCC_ADCCLKConfig(uint32_t RCC_PCLK2)
复制代码

9 M( u/ z2 b% H5 ]/ t可选的分频因子有
0 }8 T# l, q1 p. K
  1. #define RCC_PCLK2_Div2                   ((uint32_t)0x00000000)
    , H6 u4 W) H! B4 i
  2. #define RCC_PCLK2_Div4                   ((uint32_t)0x00004000)
    ; f9 W% ?! J# d# K5 Y" A! @# b
  3. #define RCC_PCLK2_Div6                   ((uint32_t)0x00008000)8 h/ U5 ^/ _+ x/ k; _3 C8 @' C+ Y; T; ]
  4. #define RCC_PCLK2_Div8                   ((uint32_t)0x0000C000)
复制代码
4 \1 n# o) z# g+ ?  ~$ j
APB2总线时钟为72MHz,而ADC的最大工作频率为14MHz,所以,分频因子一般设置为6,这样ADC的输入时钟频率为12MHz。: z7 c1 y. ~6 d$ x) Z

+ v( y' @& l, y$ g% V9 E8 }3.3 ADC工作模式1 W$ g' W  @) _9 g, l
根据中文参考手册介绍,STM32F1的ADC有三种工作模式
$ ^3 k" p& H6 F. {: X• 单次转换模式 单次转换模式下,ADC只执行一次转换。该模式既可通过设置ADC_CR2寄存器的ADON位(只适用于规则通道)启动也可通过外部触发启动(适用于规则通道或注入通道),这时CONT位为0。
- \4 z9 l; a* U+ Q; n
) ]3 b! T( v  m" p* W( Z9 l
• 连续转换模式 在连续转换模式中,当前面ADC转换一结束马上就启动另一次转换。此模式可通过外部触发启动或通过设置ADC_CR2寄存器上的ADON位启动,此时CONT位是1。
3 r3 Q  l5 a( [1 a) z' j+ F$ Z- r8 V) `' O3 u, l. f6 A( `
• 扫描模式$ x& m1 p5 n# ~8 F) Z8 T8 @0 S
; [# L9 k4 B- F5 t+ o; x; r

, u5 P' r' K0 b33.4 ADC转换时间
% C+ ]  `$ [- |ADC的总转换时间与时钟频率有关,总转换时间 = 采样时间 + 12.5个周期。其中,采样时间最短为1.5个周期,也就是最短转换时间为14个时钟周期。使用软件触发时,可选择的采样时间如下
% x" |. Y) G$ n5 r6 {* {5 }
  1. #define ADC_SampleTime_1Cycles5                    ((uint8_t)0x00)0 l! `3 F6 U. |9 A% \
  2. #define ADC_SampleTime_7Cycles5                    ((uint8_t)0x01)
    1 s- {/ \2 R& r2 N
  3. #define ADC_SampleTime_13Cycles5                   ((uint8_t)0x02)+ T' \# ~3 l  Q. c
  4. #define ADC_SampleTime_28Cycles5                   ((uint8_t)0x03)
    2 @+ g- R/ P; g% V3 G
  5. #define ADC_SampleTime_41Cycles5                   ((uint8_t)0x04), e3 u+ j' E* t6 }
  6. #define ADC_SampleTime_55Cycles5                   ((uint8_t)0x05)
    6 p" F5 V7 T* f; V  C
  7. #define ADC_SampleTime_71Cycles5                   ((uint8_t)0x06)
    , ~* U1 |$ r; O& H8 w* M/ c5 w. _
  8. #define ADC_SampleTime_239Cycles5                  ((uint8_t)0x07)
复制代码

4 c, F* l! Z+ C) z: F' [' r3.5 ADC校准
8 O' q- j- {+ m使能ADC后,需要对ADC进行校准。使用库函数开发时,提供了ADC校准的函数% u1 R2 |. ?* ?* z7 g% G. G) b! d
  1. ADC_ResetCalibration(ADC1);//重置指定的ADC的校准寄存器, j1 {+ W- N+ B3 s# v, M6 D
  2.     while(ADC_GetResetCalibrationStatus(ADC1));//获取ADC重置校准寄存器的状态0 g# _. O8 c7 `
  3.    
    . }  `, l1 v1 f
  4.     ADC_StartCalibration(ADC1);//开始指定ADC的校准状态
    # u: W- v) z" w2 u( Q: q' c
  5.     while(ADC_GetCalibrationStatus(ADC1));//获取指定ADC的校准程序
复制代码
2 x- @: j) q! m: c0 K( X& a
3.6 ADC转换结果与实际电压的换算

: N( g9 T7 b" Q2 g0 N获取到的AD转换结果并不是实际电压,如果想要得到实际电压,需要经过换算。上面介绍了,STM32的ADC为12位,也就是AD值取值范围为0~4095。采集电压范围为0到3.3V。AD值与实际电压之间存在比例关系。
) H/ O* t- G5 Z5 l4 Q6 x

  x) U' Y" R" e) o6 Q$ Y+ F实际电压 = (AD值 / 4095) * 3.3。单位为伏特(V)( Y" F0 z0 I( C

6 [8 _5 [6 r* b% q; `

2 Y% W# a, d9 z( i四、ADC配置步骤
- ~* o! E, M2 z1 U: J; ~; q; b5 Y• 使能GPIO时钟和ADC时钟,设置引脚为模拟输入
6 {' J6 V: P. p- x; d7 |8 B' L• 设置ADC的分频因子
! I) ]6 ~5 D1 Z  Y* ^8 q• 初始化ADC参数,包括ADC工作模式,规则序列等
7 Y; h$ z0 u4 C  ~! P% \9 Z4 P• 使能ADC并校准
4 h# ~. @1 y$ c1 `; F• 触发AD转换,读取AD转换值
6 K$ P. G0 {. b
+ y9 \8 F; e' M0 H
8 i/ A3 h6 X6 T% F( m; q
五、ADC配置程序5 |# u6 s. k! t) O
55.1 ADC初始化程序

5 |1 Z' M( [, v8 A# E0 {这里以配置ADC1的通道1为例,给出ADC的配置例程,分频因子设置为6,单次转换模式,软件触发。* Q2 O3 S) f. c1 ^
  1. /*; q4 C: P6 f3 z; _2 K) A
  2. *==============================================================================6 F/ t2 J" T9 U9 c
  3. *函数名称:ADC1_Init, G& P- ~5 p$ K) h5 m1 U) ]) J7 d
  4. *函数功能:初始化ADCx
    - Y2 E# |% P2 O$ [. }& \! F1 u
  5. *输入参数:无
    6 r2 y1 Y- q0 q) d5 g
  6. *返回值:无5 F8 Q( }. C% `1 A
  7. *备  注:无
    5 Y8 P7 s* H- Y- D
  8. *==============================================================================5 |7 u3 O0 G, p/ _) V, Y
  9. */2 U2 ?( {" ^. ~+ v4 e" z
  10. void ADC1_Init(void)
    % c+ ]+ I( L3 J- g: k
  11. {; h) N( y" i6 t8 N
  12.     // 结构体定义* m; Z4 D" G& W0 H& K: p: l$ I
  13.     GPIO_InitTypeDef GPIO_InitStructure;' m. m0 w( [9 ~) c, U( @0 q
  14.     ADC_InitTypeDef ADC_InitStructure;
    ) e! S# A, h- x+ K  e8 g: Q
  15.     + o0 x% m  A7 M2 f2 \) t
  16.     // 开启时钟
    2 Y( i3 f0 W: O+ o. v
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);
    - H5 Y' F" a% e. m- T+ R, F
  18.    
    % Z/ B% I1 j+ g# H2 d
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    7 Q$ u  [0 A6 R& R
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);8 r0 A' E3 M: {+ K7 E- l1 C7 w
  21.     ) E; _8 t+ L! {8 l* [( z) ~
  22.     // GPIO配置
    5 N, F1 M- w6 q, C9 ]
  23.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道1
    $ {; z: U1 n6 G9 \
  24.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入8 D, Y( l0 B2 r9 ?
  25.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;" v, \# |( S8 f* A! \, k3 f
  26.     GPIO_Init(GPIOA,&GPIO_InitStructure);7 Z& h8 ?* U. g9 f6 w8 a1 R
  27.    
    6 T2 D, V  W& ]! X
  28.     // ADC参数配置
    $ W( C" ~; k$ |0 d6 t0 X0 A9 q; Y
  29.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式
    9 W$ M8 M; C8 o! s. ]
  30.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式
    * o$ |' O# X# n6 }, u8 P( c7 ^
  31.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换
    / {3 K) y; d" |: D1 _% s4 i
  32.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   // 禁止触发检测,使用软件触发# Z+ W( Y+ n8 c! x' ?: A
  33.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐
    $ w! l: [" Y8 v0 q
  34.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1 , p- h  i# [. k" j& z/ i
  35.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化
    ( E5 u0 F4 H& T* {
  36.     $ r% ~8 B; J* I5 |" q& x
  37.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器
    7 ?% x+ j- a. t8 b* Q& B+ L
  38.    
    ) {5 D  Y& F7 z$ n
  39.     // ADC校准
    ' G+ a% Q/ i& h8 B9 v5 t1 _% e5 e, y
  40.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器
    2 ~. L, `$ ^5 n7 G
  41.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态3 z& [% `! N/ d
  42.     7 [! C0 K( T& o8 N/ @# B# N  Y
  43.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态. Q0 ]5 ~! n# D
  44.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序
    ( m% {- ~; x! b$ V6 G6 C, O4 W7 d
  45. 2 b# a; r+ b' s
  46.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能( a6 |9 o8 ?, R
  47. }
复制代码
1 ?* a4 }* Z$ ?  M6 s' U/ l
5.2 软件触发AD转换
9 |' a: H' u  [库函数开发,配置为软件触发时,可以通过下面的函数触发AD转换$ a% }' y$ l- I. X
  1. void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState)
复制代码

4 _( E3 X8 o) q5 C3 Z3 v3 ?5.3 读取AD转换结果) W' G& ^8 O; r! i0 w. D* i, R
库函数提供了用于读取AD转换结果的函数
$ h! o7 v3 L3 N% {
  1. uint16_t ADC_GetConversionValue(ADC_TypeDef* ADCx)
复制代码

( n6 i  W. n) ]# H0 _这里给出另一个函数,用于软件触发AD转换并读取转换结果
* y& S* |4 Y' }' f
  1. /*
    7 f! o: k. F  x; W
  2. *==============================================================================
    8 L' s3 J1 |4 m4 R: @& f
  3. *函数名称:Get_ADC_Value
    + ]0 J5 i) ~; |0 D* r
  4. *函数功能:读取某一规则通道AD值4 }1 P5 \4 F% t9 J" v- _
  5. *输入参数:ch:规则通道ADC_Channel_x;times:读取次数
      c4 J, [! H/ T6 P/ Y) x# ]0 o& R
  6. *返回值:无
    / f* Y5 K- x: \- t( w$ o
  7. *备  注:该函数配置好后,返回的结果是N次后的平均值+ B, _2 V, W* o. f/ t. h1 ?
  8. *==============================================================================6 `3 C1 }/ `3 X6 e. ]2 `! k; g4 z0 A
  9. */6 Q; v, V3 t% i' b
  10. u16 Get_ADC_Value(u8 ch,u8 times)
    : o: t5 D% ]* f' G# h
  11. {  v8 p& s7 f$ q+ d
  12.     u32 temp_val = 0;
    + u6 z  z: T* N' D0 D  I! r
  13.     u8 t;
    3 Y/ e0 b; ~) r2 e8 M; [
  14.     // 设置指定ADC的规则组通道,一个序列,采样时间
    ; A$ t; N4 q; @8 y
  15.     // ADC1,ADC通道,239.5个周期,提高采样时间可以提高精确度
    6 U7 b, W7 N& [, m8 v/ r& o" `
  16.     ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5);      
    ' [$ p* p  U3 P, w1 Y+ u$ C
  17.     1 x% L3 ]8 M: Q! H' W$ U
  18.     for(t=0;t<times;t++)
    * m& L7 k7 ?* t* \
  19.     {
    6 d* b2 `+ Y: J$ v, S6 V3 D
  20.         ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能指定的ADC1的软件转换启动功能
    6 _0 S0 O1 R4 w% G# S& z# p
  21.         while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));   // 等待转换结束
    ) n. |+ z, a+ X9 d( D" n* w
  22.         temp_val+=ADC_GetConversionValue(ADC1);/ p5 t  ]6 k! {" E# O
  23.         delay_ms(5);
      O: ~8 I( I) _0 y* z% T6 _
  24.     }
    9 n  a+ G# L" i, l
  25.     return temp_val/times;& C; @5 |* e' F. m) m. f# @
  26. }
复制代码

3 q8 |. x! U- I, B; W" G六、实战项目+ u, y+ F& i6 T* `  j: f; g
用ADC1的通道1采集某电阻两端电压(由于普中核心板没有可供采集的电阻,可以直接将采集引脚接到3.3V查看一下结果),将结果通过串口打印到电脑。其中关于串口的配置就不再做介绍,给出ADC的配置和main函数。
! B" v' p5 H* p+ ?2 v* m+ T0 s% B
5 [$ |2 x) [; f! ]7 }
6.1 ADC初始化
- T) G$ U( b/ C7 X# O; R( Q* @% p7 l8 [
  1. /*( @4 C! R8 U$ Y3 N$ U" N- Q* p
  2. *==============================================================================
    # c0 ^1 Y$ C: [) m& x1 S- @$ }; `: Q
  3. *函数名称:ADC1_Init
    + b7 f8 V! o" B% i! o& ~/ d- B7 n
  4. *函数功能:初始化ADCx
    4 K0 l1 s7 B" ?% {, |
  5. *输入参数:无
    6 V. ~2 W" `) v6 Q( Y2 S
  6. *返回值:无
    4 v. u) K  g; y) k1 O
  7. *备  注:无
    " Y3 H" p& Q0 g: M' o, Z) t8 u
  8. *==============================================================================
    6 X" W" S# I& [# ^! w+ [8 B9 ?
  9. */" e7 C7 Q+ e) c0 S1 D
  10. void ADC1_Init(void)' Z; V6 Z# K" F% B$ f& H4 f( F
  11. {% s8 W# g6 K8 `
  12.     // 结构体定义6 [0 z& m( \) r6 A
  13.     GPIO_InitTypeDef GPIO_InitStructure;
    % h4 \2 S0 H# }( y( S3 G
  14.     ADC_InitTypeDef ADC_InitStructure;. p6 L" x7 E; [& P2 [$ ?
  15.    
    * j5 V% I. t1 |( X
  16.     // 开启时钟
    2 C- r' ]- ]( r: O
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);
    * ^& {9 B1 W' V& y* f
  18.     ' n8 g0 U1 p( K4 A: F7 l  O
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M# ?. z; O: i8 C
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);  o6 o4 H8 i( `8 h' D7 c4 e/ l
  21.     6 e3 v; a* @  Y8 y
  22.     // GPIO配置3 l3 `6 X9 h: M$ v  B8 w+ _% |
  23.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道1
    . @  o0 N6 y% l. T! `5 J
  24.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入
    : X& m0 H6 ^3 g/ x! r& G: z
  25.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    ( w- U" Y/ I- ^) D  Z0 ?( L
  26.     GPIO_Init(GPIOA,&GPIO_InitStructure);
    / T+ A! L  O7 _* r8 E
  27.     3 c/ G. o7 {" U- ]
  28.     // ADC参数配置
    * e6 p3 `* \* }
  29.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式1 U6 D: h, ~. E& Q# c/ \* C( O' A
  30.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式
    # [; m" Z$ x+ p
  31.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换
    ! H, A  w0 B( C6 w* u$ S
  32.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;   // 禁止触发检测,使用软件触发6 x" G  f3 a2 Z9 l8 X. o/ H8 F) H
  33.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐
    ! d9 @: H( D6 J) Y1 C
  34.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1
    7 i3 i5 C; K  @
  35.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化/ {; o4 f) W) l/ i
  36.    
    + r$ _0 D7 G, h+ Y
  37.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器7 ~* a8 d4 H$ v% Y
  38.     # i: `$ X# @  C! z/ |7 @- l
  39.     // ADC校准
    + ]; X, C; V6 p% @% a
  40.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器
    : u4 K* h! s5 b* H
  41.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态
    # S, s  T; C! D# C; u* d
  42.    
    ! {/ P5 Q1 g  Q3 @* J4 F
  43.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态, q3 s7 v5 [# x6 H8 C* V; R
  44.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序
    * A9 B+ i# \( j  Y0 Z' i4 x
  45. , A; K) v5 A3 i$ L5 h9 }0 p
  46.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能
    1 M4 @8 p, a) Q$ J
  47. }
复制代码
- d4 u) n4 S: h7 o
& z% P' N0 l/ {; o7 R0 d  p0 _. s
6.2 main函数7 h) T8 J( q! v, p& U
  1. u16 gAdcAdValue = 0;   // 存储AD值- s; ^4 A; H" U$ F8 `$ U
  2. float gAdcVol = 0;   // 实际电压值2 n& S4 [% @) f# T& y

  3.   N2 ?# U) j7 y
  4. int main(void)5 U# q. |# q8 V* u& i7 A# k! T
  5. {
    4 O, f$ P8 C" h- _, c# ~, V
  6.     Med_Mcu_Iint();   // 系统初始化9 j  h* Q/ s) [; U: f& T) w: H" W, ]
  7.     ; g. Z4 \3 e& a3 o8 R( }+ ]
  8.     while(1)
    / b& d( s% y1 s4 K
  9.   {
    ( R/ r1 j7 p1 I/ v% w3 i" j
  10.         gAdcAdValue = Get_ADC_Value (ADC_Channel_1,10);   // 获取转换结果
    : k4 R: O! S5 X/ ?; U6 H# o; |
  11.         gAdcVol = (gAdcAdValue / 0xFFF) * 3.3;   // 计算实际电压
    . T9 p  B4 L9 s- v" r8 w- j" s: }6 T. |
  12.         printf ("Vol=%.1f V\r\n",gAdcVol);   // 串口打印结果
    9 H0 Y2 v  @, P4 y0 A
  13.         " @' w) x- D/ `# c* ^
  14.         delay_ms (500);   // 防止打印过快4 g. T0 P6 L, Y0 j9 H
  15.     }, @  K8 q9 ]( c) m- B
  16. }
复制代码

2 C8 r/ B+ M5 m' p$ F
! Y) j0 J% A: e/ n3 B七、拓展
, D& s+ E4 u  ~5 i. n" S- N7.1 定时器触发ADC采集
: s& H) f: l# Z' j
根据中文参考手册介绍,ADC可以通过定时器触发AD转换(只有PWM的上升沿可以触发AD转换)。触发方式有以下几种# g) k/ S% {8 ?2 n! C
• TIM1_CH1 :定时器 1 的通道 1 的 PWM 触发; R' Y) ~- R8 t& k5 _
• TIM1_CH2 : 定时器 2 的通道 2 的 PWM 触发
7 @" N; l! `0 v• TIM1_CH3: 定时器 1 的通道 3 的 PWM 触发
. s) x, y6 ?  _6 ^• TIM2_CH2 : 定时器 2 的通道 2 的 PWM 触发
& |3 J+ g9 }7 A1 _& R• TIM3_TRGO: 定时器 3 触发
& U  `! K' Y7 d+ v! {: \4 W& ]/ A• TIM4_CH4 : 定时器 4 的通道 4 的 PWM 触发
  ^: w! Y0 H+ g- ]
/ F' p9 S# ^" ~0 Y! n5 C* M
2 L; D: W5 h; _6 d! I3 `7 [2 G
微信图片_20231023190424.png
( @+ o( j; F1 L
ADC外部触发方式
+ D- b4 Y+ v/ [# W& i
. l* a9 w+ L- l& \/ q: k

7 W$ Z# ~# c( N2 a/ {" J7 H
这里以TIM4的通道4触发ADC采集为例,给出程序配置。  W8 ]9 y. ^, I( h9 K9 z5 a- _

& d# T' z7 m1 J+ s4 s- n! Y首先是定时器PWM的配置,不对引脚进行重映射。
9 J% G1 C% O) V- A+ v
  1. /*
    2 h: b' }7 Z0 z, ~
  2. *==============================================================================; g* x1 S+ J, `/ U  L. b( z& K
  3. *函数名称:TIM4_CH4_PWM_Init7 t) o* \1 f5 N6 n' l' d
  4. *函数功能:初始化定时器4的PWM通道4  _" f: Q+ }+ }0 I, p- R
  5. *输入参数:per:自动重装载值;psc:预分频系数! f* a! I/ e7 e. o: i* Y( j9 h% b
  6. *返回值:无9 v+ u* f8 ~8 S2 X+ X* h
  7. *备  注:无
    % H' P. ]( N$ Z( f
  8. *==============================================================================" q+ l  O' J5 J+ {9 `' x
  9. */* Y5 Q/ K/ e. M% P+ X/ R+ y
  10. void TIM4_CH4_PWM_Init (u16 per,u16 psc)
      _$ z4 w) ~8 R& X3 }- M
  11. {
    . r: T- v5 a- X/ c7 C8 c: N
  12.     // 结构体定义- C- T5 S" p( U" V
  13.     TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    . A  _( x$ ~9 G' D
  14.     TIM_OCInitTypeDef TIM_OCInitStructure;2 r* l5 z/ q% F7 Z% k' l) u- |3 @
  15.     GPIO_InitTypeDef GPIO_InitStructure;# W# J& w8 {; t
  16.     % @, G  x# u' |
  17.     // 开启时钟
    $ f6 b( t; I1 q/ i9 H' v
  18.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    8 m4 n0 _, g, S( }+ R) ^7 Y
  19.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);0 Y& A! w4 A! d0 l: i; G7 M2 \
  20.     2 L) h8 a6 j9 g0 m, {- \
  21.     // 初始化GPIO3 V7 N  g0 o6 X3 z4 |8 d; r9 I. w
  22.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;/ x0 e3 b! s  P
  23.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;$ e- n$ H0 ?8 Q1 @! k
  24.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   // 复用推挽输出* ^6 j8 u& t9 P: [1 D- d; y
  25.     GPIO_Init(GPIOB,&GPIO_InitStructure);
    / L3 I. j" z8 z$ ^3 y; `
  26.     $ Q; H6 v7 ^6 B
  27.     // 初始化定时器参数5 q' m: ]0 |& z2 H. n3 V4 C3 d
  28.     TIM_TimeBaseInitStructure.TIM_Period = per;   // 自动装载值3 G, U. u( o2 w# s2 p. m5 l
  29.     TIM_TimeBaseInitStructure.TIM_Prescaler = psc;   // 分频系数1 t$ _* R5 [+ ]* R3 z5 H9 }
  30.     TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;/ ?+ P0 F6 @/ w5 ?9 J
  31.     TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;   // 设置向上计数模式
    3 `; p8 z0 o1 v0 q6 v  O
  32.     TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure); * S- K8 x$ C1 f3 q+ X% U7 l
  33.     2 O+ F, p5 x. i: x! e# Y
  34.     // 初始化PWM参数. u) V$ G3 v1 Y2 m; F5 l
  35.     TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   // 比较输出模式, Y4 D4 j; B! e
  36.     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;   // 输出极性& r. \0 L" v5 \. _1 T+ _/ A
  37.     TIM_OCInitStructure.TIM_Pulse = 500;   // 脉冲宽度5 v, `  K8 {  I& s4 [
  38.     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   // 输出使能) J# q6 d  z; g( M4 B* [
  39.     TIM_OC4Init(TIM4,&TIM_OCInitStructure);   // 输出比较通道2初始化
    ) R% b4 v* V$ N( E' G
  40.    
    2 e# G) {& u2 b8 t
  41.     TIM_OC4PreloadConfig(TIM4,TIM_OCPreload_Enable);   // 使能TIMx在 CCR2 上的预装载寄存器
    8 \4 _, t  O/ b" x' K" z8 }& ^. R
  42.     TIM_ARRPreloadConfig(TIM4,ENABLE);   // 使能预装载寄存器1 I* K7 C  C. c: A$ d, G- e' V* a
  43.    
    ; P# d7 x7 Y7 l/ }
  44.     TIM_Cmd(TIM4,ENABLE);   // 使能定时器
    ( c  E/ y' Q; r- v- j
  45. }
复制代码
( ?, M9 f4 Z/ r8 a
ADC配置程序如下,触发源选择TIM4的CH4,使能外部触发。
* [0 u1 Z- }2 M9 G0 t7 L; v
  1. /*' q+ f$ ^+ v7 V5 `+ p7 {
  2. *==============================================================================
    3 B9 x$ g: ~! C7 ~
  3. *函数名称:ADC1_Init
    ; t$ T0 }4 \. S7 l- E6 P
  4. *函数功能:初始化ADCx" }( E1 t2 r( V* o
  5. *输入参数:无
    # d1 F, A0 A9 ]# t0 B. w
  6. *返回值:无* h# `" x0 E; r3 D
  7. *备  注:无
    # I$ U# u5 ~+ i0 h1 ]! {
  8. *==============================================================================- j5 E( I2 Q: p- S  A  X3 S
  9. */
    / Z  Z% |% l3 S
  10. void ADC1_Init(void)4 I7 L1 Z9 n/ p, W
  11. {
    ! v" ~1 j1 D' ^2 H& ]! Z2 }- `
  12.     // 结构体定义
    ; q' i7 k2 h9 {; G8 y
  13.     GPIO_InitTypeDef GPIO_InitStructure;4 l8 H( P2 w# O4 I
  14.     ADC_InitTypeDef ADC_InitStructure;
    ( N) ?, B8 }; o8 G
  15.     3 Q8 _! I! v4 M6 r+ i# i
  16.     // 开启时钟
    : }( g' n1 j! t/ P
  17.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1,ENABLE);
    / v5 r+ V' Q9 w* ~- ]
  18.    
    , a! m% T% ]2 ^/ Y( K
  19.     // 设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M& q/ [: i% I9 p* b0 N
  20.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);0 `3 Y8 O$ y9 c) a1 |! z
  21.     // 规则通道配置
      V. c' W+ P& Z# C8 j+ Y
  22.     ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);' a+ m% I: l. }7 ~: |' k
  23.    
    # Z% r7 w5 Q/ `( J6 [4 t, ^7 q
  24.     // GPIO配置
    4 y  t4 G! ]' K9 P- c5 u0 i; W
  25.     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;   //ADC1通道1. u6 Y) J5 H( X7 K/ U7 U8 Z# z: P
  26.     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;   // 模拟输入
      G" _' J% j+ C; p9 _9 L# E
  27.     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;' y8 ^1 I. q4 h3 n" Q  Q" `6 F
  28.     GPIO_Init(GPIOA,&GPIO_InitStructure);6 d# r2 w( K( D$ E
  29.     # V& M8 B8 \! S& d9 }5 E
  30.     // ADC参数配置
    ! g+ [. @1 z  w% T2 F' B
  31.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;   // 独立模式
    # s& m8 j1 B' q# o
  32.     ADC_InitStructure.ADC_ScanConvMode = DISABLE;   // 非扫描模式 2 X: v2 V' O) Q9 _% i
  33.     ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;   // 关闭连续转换- H# j( q* A8 h; n) ~
  34.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T4_CC4;   // TIM2通道2触发
    , G' g" J9 t& x  e2 c
  35.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;   // 右对齐
    " h2 |! A6 S3 ^. }( q
  36.     ADC_InitStructure.ADC_NbrOfChannel = 1;   // 1个转换在规则序列中 也就是只转换规则序列1 ) p1 v4 ]+ D5 i/ O; p' f' q
  37.     ADC_Init(ADC1, &ADC_InitStructure);   // ADC初始化6 e" l6 I7 V" f. S: ^* B5 u
  38.    
    : R7 [$ h" H5 A+ k
  39.     // 使能外部触发% o" ~- e- y$ L0 N9 {" K
  40.     ADC_ExternalTrigConvCmd(ADC1, ENABLE);
    + e# [) @, r. g+ @  i; q
  41.     9 D2 |% d+ {; S- z( b( ^3 u( B1 A
  42.     ADC_Cmd(ADC1, ENABLE);   // 开启AD转换器9 C- n. P3 o/ O5 a7 C# q' |
  43.    
    ) K9 b# e, G0 n* ^  _7 }
  44.     // ADC校准7 A) j5 O0 Y$ L
  45.     ADC_ResetCalibration(ADC1);   // 重置指定的ADC的校准寄存器
    3 }8 }2 F8 r6 w4 p
  46.     while(ADC_GetResetCalibrationStatus(ADC1));   // 获取ADC重置校准寄存器的状态$ n6 L8 t/ Y) L8 \* ?
  47.     ; C( |0 F; H, m" n; ?, s+ w
  48.     ADC_StartCalibration(ADC1);   // 开始指定ADC的校准状态
    9 g/ C' x# h2 D9 i( w# z
  49.     while(ADC_GetCalibrationStatus(ADC1));   // 获取指定ADC的校准程序
    8 c1 Y  Y% i; K( w6 r0 U5 |
  50. - b: w. b8 N1 v; j( ^$ \$ ^" N2 c4 q4 G
  51.     ADC_SoftwareStartConvCmd(ADC1, ENABLE);   // 使能或者失能指定的ADC的软件转换启动功能
      ^. c  Q8 B6 ^
  52. }
复制代码
8 `' f- p0 H. G: j; `2 B- K% U
main函数如下$ |- r3 H- Q" V  l' X, K
  1. u16 gAdcAdValue = 0;   // 存储AD值, E/ O4 U: I- p8 o8 \0 O
  2. float gAdcVol = 0;   // 实际电压值. \( B8 T7 J6 `- S: |2 @

  3. ' z3 D+ o5 |. g+ H/ ^* d, q
  4. int main(void)
    & X- W$ d5 R$ C  Z3 G) [
  5. {
    ; c" d4 m( [; b$ p  h
  6.     Med_Mcu_Iint();   // 系统初始化. Q  ]: Y3 P) ?8 Q" N+ ]+ }! g
  7.    
    : K( C- J3 q( _! D& T! H0 ~
  8.     while(1)
    8 [: u+ h( T! Q
  9.   {
    9 H6 z, X, R% c. V. x, ?- g( p
  10.         gAdcAdValue = ADC_GetConversionValue (ADC1);   // 获取转换结果
    3 V6 b1 j3 O, `4 t* L) J
  11.         gAdcVol = (gAdcAdValue / 0xFFF) * 3.3;   // 计算实际电压3 S( T. p! s5 B+ h' Z( r( T& i
  12.         printf ("Vol=%.1f V\r\n",gAdcVol);   // 串口打印结果8 v) N& P- y/ J! l( e
  13.         , k( R; x' \% m+ ]6 q/ h/ o" _
  14.         delay_ms (500);   // 防止打印过快% Q7 i* X/ s2 O. ~) n6 I0 r1 _
  15.     }
    3 ?& e/ J  d  `& ~+ t5 W! N3 D
  16. }
复制代码
, {( ?- p0 x4 \& n
初始化定PWM时,程序为6 l6 I- R: u, v! O5 K' a! a
  1. TIM4_CH4_PWM_Init(1000,71);   // 初始化TIM4的通道4
复制代码
) u4 V. @% I* ?" n6 p% O0 p1 F
分频系数为71 + 1,自动重装载值为1000,也就是1KHz的方波,也就是触发AD转换的频率为1KHz,与占空比无关。# \1 U; s7 u  _. ^/ a* s2 u+ d9 L. L

& _* p2 S: f# g2 m
( p7 \- c7 n' G4 L* G
7.2 ADC采集交流信号
1 n) T7 v6 H0 z/ e# G7 k+ z, x/ l. CADC能够采集的电压范围是0~3.3V,也就是说负电压无法采集。比如,需要采集下图中的一个交流信号
, z3 S/ w$ O: H& j. e; x

% ~& }& _6 l% Y: L2 z  ~
微信图片_20231023190420.png

; ?0 E4 H7 Q; P& `# G$ Y; ]
交流信号图

3 W+ |) V+ u1 L

9 N- E5 \" c" b* Y* Z
+ F0 `) r1 Q+ T( V1 B. B. m
其位于0以下的部分是无法采集的。因此,在利用STM32采集交流信号时,在交流信号输入ADC引脚前,给交流信号增加一个直流偏置,将交流信号的最低点抬升到0以上,之后再输入ADC引脚。4 g5 p# a' Q/ r) M& o; j
# h! [, g) k) u- n! n4 V9 j& f
0 @( l4 |4 W: C( B  p
7.3 计算交流信号有效值
" S1 x, ^0 y) |4 y' d+ ?ADC可以用于电流监测,实时监测主线路中的电流。当然,硬件方面需要搭配电流互感线圈,通过采集互感线圈两端的电压,来监测主线路电流。由于一般都是交流信号,所以需要计算有效值。. K6 a2 [, u( b% C
2 p, F, L" r  b, ]5 b
根据我们所学的知识,计算交流信号有效值常用两种方法。一种是峰峰值除以根号2,另一种是计算均方根得到有效值。通常我们采用计算均方根的方法来计算有效值。因为如果用峰峰值除以根号2去计算有效值,峰峰值很容易不准确。如果在某一个时刻,由于环境干扰或者硬件问题,导致突然出现了一个很大的值,会导致计算结果与实际偏差较大。关于为什么计算均方根可以得到交流信号的有效值,这里就不做介绍了,只给出部分程序设计。由于博主目前身边没有合适的设备验证,因此仅供参考。
* e6 u5 T" x4 T2 |: n; j
# Z' v# G0 l- t( V假设需要计算一个50Hz交流信号的的有效值,在其输入到ADC采集引脚之前,增加一个稳定的1.65V的偏置。ADC的采样频率为1KHz,也就是一个正弦波的周期可以采集20个点。假设采集到的AD值存储到一个数组中,计算有效值的程序设计如下
  @/ y% y7 E3 z; T" U% P, o
  1. int gAdcAdValue[20];   // 存储采样结果AD值的数组" g0 x4 r2 k/ {- U
  2. int gAdcValidValue = 0;   // 有效值9 A& W! }* F! i" G

  3. : v0 O& D" q6 A* A/ b
  4. void Med_Adc_ValidValueCal (void)
    1 W$ E+ R2 }! D* j9 X
  5. {
    9 V4 {+ P6 H0 F& p
  6.     int tempVar = 0;   // 循环变量6 I/ \: ?0 S: e5 _9 ]2 e0 T
  7.     int squarSum = 0;   // 平方和
    8 D( _" a9 z/ C/ B7 `: D. o
  8.    
    + }4 {+ X' R/ V3 t) ]
  9.     // 求平方和* W4 H; Y) f; Z7 M/ F9 r) ?/ }4 c
  10.     for (tempVar = 0;tempVar < 20;tempVar ++)- @5 ^) M' W8 z8 L) r+ y' d% F
  11.     {
    $ J9 m4 N; g0 n0 I* B
  12.         // 减去直流偏置$ @. u! u+ d* W- T& Z7 O; D8 @
  13.         gAdcAdValue[tempVar] = gAdcAdValue[tempVar] - 2048;
    - e! @) Z1 F4 R; k- R
  14.         // 计算平方和
    $ ~& x  m* n" ~
  15.         squarSum = squarSum + gAdcAdValue[tempVar] * gAdcAdValue[tempVar];6 G! }7 S$ f( p3 c2 t9 B! a
  16.     }. {3 {% t: W, n/ ]& ]
  17.    
    & y' ?* c0 f- N; i# q
  18.     // 求平均
    % ?3 Z; ^/ _3 d2 U! A
  19.     squarSum = squarSum / 20;
    : h! }/ F! ^! y$ @) B( D4 h% M
  20.     % `1 o  o/ q6 @0 t0 Q- m. H
  21.     // 开根号得到均方根(有效值)
    ; h% Y, l4 u8 e' k% E3 r7 n, c
  22.     gAdcValidValue = sqrt (squarSum);
    5 P6 ]/ P. v' O/ Z1 v
  23. }
复制代码
9 c  ^# Z8 w% j9 ~( I/ V
在进行程序设计时需要注意不要超出数据类型范围。在实际应用时肯定会存在误差,这里也简单介绍一下误差消除方法。目前用到的有两种方法,第一种是分段矫正,在不同的区间内,误差满足线性关系时可以使用。另一种是按比例矫正,这种方法常用于误差随着测量值的增大而增大的情况。在计算出有效值后,减去或者加上一定比例的计算值来做矫正。; ], }9 @) q7 g) V  J

  n  d7 X* K# r* G" I. ^! H$ ~. l/ X+ ?# H
# }8 C- J! k; `" H/ Q* m
转载自: 二土电子! J7 k. o) a9 O5 S/ ^. }
如有侵权请联系删除7 K( R' B+ e9 N7 ?% F

. Y+ n$ w% ^& |
赞 收藏 评论0 发布时间:2023-10-23 19:04

举报

0个回答

所属标签

相似技术帖

官网相关资源

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