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

【零基础学习STM32】第八讲:定时器PWM实验——呼吸灯

[复制链接]
kkhkbb 发布时间:2018-3-14 09:48
本帖最后由 kkhkbb 于 2018-3-14 13:35 编辑 - h+ ^$ o4 ~2 u2 y; `
; e% N+ j! q$ O4 i8 `
一、 概述
1、 PWM简介
      PWM即脉冲宽度调制,是一种模拟控制方式,其根据相应载荷的变化来调制晶体管基极或MOS管栅极的偏置,来实现晶体管或MOS管导通时间的改变,从而实现开关稳压电源输出的改变。这种方式能使电源的输出电压在工作条件变化时保持恒定,是利用微处理器的数字信号对模拟电路进行控制的一种非常有效的技术。
      PWM的主要应用:在STM32中如调节屏幕亮度、音调等应用都可以通过PWM控制占空比进行实现。下面我们会为大家举例介绍如何利用PWM控制LED的亮暗,以实现呼吸灯的效果。
2、PWM的工作原理
      脉宽调制基本原理:其控制方式就是对电路开关器件的通断进行控制,使输出端得到一系列幅值相等的脉冲,用这些脉冲来代替正弦波或所需要的波形。也就是在输出波形的半个周期中产生多个脉冲,使各脉冲的等值电压为正弦波形,所获得的输出平滑且低次谐波少。按一定的规则对各脉冲的宽度进行调制,即可改变电路输出电压的大小,也可改变输出频率。
    (1)脉宽调制原理
      脉宽调制调制模式可以生产一个由TIMx_ARR寄存器确定频率,由TIMx_CCRx确定占空比的信号。如下图所示:
微信图片_20180314093059.png
                          图8_0脉宽调制原理示意图
       图中可知0到t2为一个周期时间,而占空比由CCRx值决定。一个周期时间可以由以下公式计算:
微信图片_20180314093512.png
        其中:ARR为重新装载值;PSC为预分频系数;TCLK为定时器时钟频率。如本次实验中TCLk为84M,选择ARR为499,PSC为83,其一个周期为0.5ms。
   (2)脉宽调制原理
          PWM主要工作过程如下:
微信图片_20180314093103.png
8 _* N1 Z& x. p" c7 }" R! M" s
                             图8_1 PWM工作过程示意图
    PWM工作步骤解析:        
    a. CCR1捕获比较值寄存器设置比较值,将其与当前值寄存器的值比较,要说明的是修改TIM_CCMR1寄存器的OC1M[2:0]位可控制 PWM模式,方法如下:
  • 110:PWM模式1——向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则有效电平(OC1REF=1)。
  • 111:PWM模式2——在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电平。$ T4 P1 v# Q5 `4 h/ {+ v
   b. 经过输出控制器后,得到OC1ref电平还要经过一个选择,其由TIMx_CCRE寄存器的CC1P位控制:输入/捕获1输出极性。0:高 电平有效。1:低电平有效
   c. 选择完成后经过输出电路来输出,输出电路由TIM_xCCRE寄存器的CC1E位控制。
       控制方式为0:关闭  1:打开。

二、 实验原理

       STM32的定时器有PWM功能,iCore3的三个LED都连接在定时器的输出接口上,可以通过定时器的PWM输出控制LED的亮度,从而实现呼吸灯的功能。硬件连接示意图如下图所示:
微信图片_20180314093107.png
                        图8_2  驱动示意图

三、源代码

1、主函数

  1. /*3 P6 N9 c$ H& d
  2. * Name                : main
    7 @; T7 a8 R' `8 ?& b" B
  3. * Description         : ---9 o& K: H# w% z
  4. * Author              : ysloveivy.  N5 b3 @; p3 E, w# Z3 M
  5. *
    8 e9 ~' \! ^: ?3 H5 Z$ Z# B) ?* ~1 Y
  6. * History
    : l  f2 ^) {1 s
  7. * --------------------# D  ]. n+ e- Y
  8. * Rev                 : 0.00- Z- |7 D, z8 O( ^
  9. * Date                : 11/21/2015
    + [6 M6 `9 `0 d/ }  O! K' x8 Q
  10. * , S/ U1 I# ^# P4 U
  11. * create.
    - z- `# E- Z" X% ~
  12. * --------------------# a; d: R* A3 m5 U" u5 O- O
  13. */+ E5 k* x' b0 l# h. ?' j
  14. int main(void), d/ x+ F, W1 g% D# o
  15. {
    ; p$ J% q; z! G3 |* [0 f# @# X
  16.         int i;9 A% B) {$ F3 x
  17.         int brightness = 99;1 _) i( G. X( ^6 l7 \9 t
  18.         pwm.initialize();1 S5 |3 z; c4 ?6 o0 ^4 z% k
  19.         float temp = 0.0;9 D# K6 L4 f, S$ ^
  20.         int data = 0;
    0 S  ^! Q0 I$ t" k
  21. & Q- x( c! V, T3 j3 @' J7 Y& l
  22.         //红色LED灯循环由亮变暗,由暗变亮
    $ f  d2 D. c" d# R2 @5 d7 H& [
  23.         while(1){
    * e  s- G# Q0 p3 Z% s6 `
  24.                 for(i = 0;i < 1000000;i++);
      f, E; q) G& D0 D% y
  25.                 brightness ++;
    , _$ Z! Y& f1 p6 ~' D) u
  26.                 if(brightness == 100){
    5 Z! y! `' n) D: H9 ^! d
  27.                         brightness = 0;& K5 {# E* e4 |5 [7 W
  28.                 }+ p& j$ P6 ]# i. S- ?
  29. ! h  C  k; F( b2 P
  30.                 temp = sin(PI / 99.0 * brightness) * 0.5 + 0.5;
    . c. {, d1 A' s$ f6 G* r
  31.                 temp *= 499;6 i' V* q0 a4 r/ l2 A
  32.                 data = (int)temp;6 x# z7 T8 y5 [1 K
  33. 0 m* u  }2 z* y; x
  34.                 pwm.set_compare(data);
    " l+ t+ R* @' _' ?' F
  35.         }' F5 w0 t; W2 Z) t+ v+ A4 c2 l  U
  36. }
复制代码
2.PWM初始化
   以下是TIM8初始化及PWM设置程序:
  1. /*
    / K# u0 E& U* a! ~8 Q- ?. M
  2. * Name               : initialize, e$ T( I9 X6 J# ^& {
  3. * Description        : ---
    ! f  `" Q' ^5 {' `9 H" q
  4. * Author             : ysloveivy.
    / Y4 Z6 _7 @* J3 {
  5. *
      g, m" F2 J; I! B
  6. * History
    5 U, E8 G; Q3 m- M  u* n
  7. * -------------------
    0 [+ K5 I: W  w( r! q0 L# T" @) x
  8. * Rev                : 0.00
    " q: l" f% q2 q4 l5 t
  9. * Date               : 11/21/20157 [; A1 b, A7 p" C& [' U- w/ ~
  10. * / h) x; X1 b' c0 a" C: E
  11. * create.
    " n; c+ C6 S5 s% ^( g0 i# p
  12. * -------------------" N- k* J8 q" q+ a/ o$ W- L- j
  13. */
    . @3 ]$ `3 [2 O1 N, ~
  14. static int initialize(void)
    0 c- J2 ]2 [* D) h
  15. {  7 J4 F$ P. G) t, A+ Z
  16.         GPIO_InitTypeDef           GPIO_InitStructure;- W# [( r5 d: B1 g, ^
  17.         TIM_TimeBaseInitTypeDef   TIM_TimeBaseStructure;7 o2 ~6 s- C4 Z& }
  18.         TIM_OCInitTypeDef          TIM_OCInitStructure;
    3 O, C3 C- y4 H3 r, M7 {, g

  19. , t& p( O0 E( z7 t8 F5 @' _
  20.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8,ENABLE);                       //开启TIM8定时器的时钟9 j  x9 B2 a$ ~3 w2 C; A
  21.         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOI, ENABLE);                     //开启GPIOI的时钟
    6 {, {+ p% Q; Z
  22.         GPIO_PinAFConfig(GPIOI,GPIO_PinSource5,GPIO_AF_TIM8);                     //PI5复用为TIM82 e" I1 `1 |+ W* n' R2 ^
  23.         //GPIO初始化设置
    * i4 s2 t# y& p+ w( p* F
  24.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;, n9 o9 ?6 O* m5 N1 i2 \# [. j0 \
  25.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                              //模式设为复用
    # S0 w: o$ J% [5 Q+ c; R
  26.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;- @6 D7 e2 u$ Q5 a
  27.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                            //复用推挽输出; N4 [* s' J* b0 c7 F
  28.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    * \% l' V4 C' U' c% W% |
  29.         GPIO_Init(GPIOI,&GPIO_InitStructure);
    & w0 m& Q- J8 B+ g1 [  r) m
  30.         //TIM8初始化
    3 g+ N* b6 y/ u6 i. W
  31.         TIM_TimeBaseStructure.TIM_Prescaler = 83;                                 //设置预分频值/ m, K  O& Q' j' v7 e: K
  32.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;               //向上计数模式$ P. _# |4 w# U7 O! |
  33.         TIM_TimeBaseStructure.TIM_Period = 499;                                   //自动重装载值$ E9 O. q; g" T; |. p
  34.         TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;   
    ' d2 N3 t( U( R# \, c3 Y- a
  35.        //设置时钟分割           ' e; ~4 f) L/ M  b+ J. ~
  36.         TIM_TimeBaseInit(TIM8,&TIM_TimeBaseStructure);
    4 k1 L) S7 L8 g: ?& D
  37.         //TIM8 CH1 LED_RED 9 r* ?) e0 h5 g- E
  38.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;                         //设置为PWM模式2
    , Y5 F* j$ `3 O- Z; r4 O
  39.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;             //比较输出使能0 }) c. x* I- w( P6 z' \( T, Z$ i
  40.         TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;                //互补输出使能/ g) S" t6 T  [$ z
  41.         TIM_OCInitStructure.TIM_Pulse = 0;                                        //设置待装入捕获比较寄存器的脉冲值0 G! x- I" F" P, i* J3 @/ w; z9 H
  42.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;                  //当定时器的值小于脉冲值时,输出低电平
    , G& @4 j: g8 o9 }
  43.         TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
    , t1 h( o3 x8 M2 ~3 p* Y- s. O7 u. {5 l# i
  44.         TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
    6 B4 C  I9 f( k8 l
  45.         TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
    2 {$ y/ ^4 i) N" @3 _. z
  46.         TIM_OC1Init(TIM8, &TIM_OCInitStructure);1 Q2 w3 b3 V  }5 w  E# I

  47. / l4 |$ f+ E' p# g' ]
  48.         TIM_OC1PreloadConfig(TIM8, TIM_OCPreload_Enable);                         //使能输出比较预装载1 V+ W' k, J  _$ @# v7 z7 W$ @

  49. * a% ]  u  a- I5 f
  50.         TIM_ARRPreloadConfig(TIM8,ENABLE);                                      //使能TIM8自动重装载的预装载寄存器允许位& s8 n/ u4 F! l6 x& }, k
  51.         TIM_Cmd(TIM8, ENABLE);                                                    //TIM8使能
    9 k4 x: s. S6 y; F3 R4 ]) U3 w7 O
  52.         TIM_CtrlPWMOutputs(TIM8, ENABLE);                                         //使能主输出; k; K2 }1 e# l) c

  53. 5 r, L. ?8 }. ]- c( Q4 z
  54.         return 0;4 _2 P: L  `, w7 ]" l* l* _
  55. }
复制代码
3.修改占空比函数
  1. static int set_compare(int temp)
    1 d2 P/ g" C8 h% y  Q# Q. G
  2. {
    2 |% j% M2 W. ^  E% \
  3.         TIM_SetCompare1(TIM8,temp);                                                      //修改占空比$ W+ \6 o; ^. D# f5 {3 M, G0 O
  4.         return 0;( v: p2 c3 v, _7 e' s$ d% p7 [
  5. }
复制代码
4.小知识
    在上面程序中有一段函数为:
    GPIO_PinAFConfig(GPIOI,GPIO_PinSource5,GPIO_AF_TIM8);
    它的功能是将PI5复用为TIM8_CH1,初学者可能会问了为什么PI5可以复用为TIM8_CH1,其他引角可不可以。这就要借助该芯片的数据手册了,芯片上众多引角他们往往带有自己特殊的使命,如以下从数据手册上的截屏:
微信图片_20180314094431.png
                  图8_3 芯片引脚功能示意图
      其在数据手册的Pinouts and pin description——STM32F40xxx pin and ball definitions目录下。前面六列为该芯片手册包含的芯片类型,接下来第七列是引脚的名称,第八列是引脚类型如可以输入输出的I/O口,第八列中有FT标识的引脚表示能承受5V电压,第九列是注释,第十列为可复用功能如PI5要复用为TIM8_CH1功能则可通过刚刚介绍的函数实现,第十一列为可重映射功能。
四、 实验现象:
      iCore3 双核心板红色LED灯亮度从亮到暗,然后从暗到亮。
五、 代码包下载链接
5 n- a& v# d4 Q+ V% D/ B7 V# j7 L
6 z( q; F* h1 T& m( Y; W$ c- H0 S

8 k, f9 X* D& U# Y/ j5 V
) T% ^. ^' d1 m! [! D6 y1 ]
收藏 1 评论5 发布时间:2018-3-14 09:48

举报

5个回答
loveu99 回答时间:2018-4-19 09:08:55
主函数里的正弦函数怎么理解(如下行)?' F' R7 u4 u% u4 O, H5 m3 J
temp = sin(PI / 99.0 * brightness) * 0.5 + 0.5;
GKoSon 回答时间:2018-4-23 18:56:21
loveu99 发表于 2018-4-19 09:08
+ A9 U4 Y" R8 q2 W0 o' m主函数里的正弦函数怎么理解(如下行)?
9 n2 \3 c+ ?' i2 Utemp = sin(PI / 99.0 * brightness) * 0.5 + 0.5; ...
# H, x; I6 \0 G
反过来看:  S% U/ w9 m+ j, F  e3 W6 G
  data = (int)temp;
" p+ D& ~7 [, E0 C这里是强转  因为后者是浮点型 他的好处是可以sin保留小数点后面
$ X- A8 ]! z' ltemp *= 499;
) f" y; a: R1 |; K/ {因为init是499,所以前面temp是0--1之间的小数。
" y; ?& ~$ ^% p$ g" q' w' j* i; R本来 sin(PI / 99.0 * brightness)就可以了
2 p0 o- D: i8 w- c  g: N5 m4 y2 h8 y6 d. x
temp = sin(PI / 99.0 * brightness) * 0.5 + 0.5;
6 M( r2 M  n. k9 |! T% D" S* E他的temp min是0.5 也就是灯不会全灭 max是1也就是最亮8 L" f$ o* L9 p  H+ K+ e( ]1 _
loveu99 回答时间:2018-4-26 14:38:10
与龙共舞 发表于 2018-4-23 18:56* H' f7 x; P9 ~
反过来看:
) O5 m- g- w% U5 Y2 M  data = (int)temp;1 F; K  y5 q- r; ~& D0 e6 |
这里是强转  因为后者是浮点型 他的好处是可以sin保留小数点后面

2 r5 p. l& g: d: V- I" l4 X  d' ~# xsin(PI / 99.0 * brightness) 4 W! ~; X+ w: l- `; |
这个正弦函数跟亮度是什么关系?
GKoSon 回答时间:2018-4-27 09:16:44
loveu99 发表于 2018-4-26 14:386 i1 O% K1 y/ B( w7 S) }
sin(PI / 99.0 * brightness)
1 L; [3 t$ v. Y4 l5 t/ e这个正弦函数跟亮度是什么关系?
! @  I( g( b: n5 Y0 x
pwm.set_compare(data);把这个数值放到ccnx吧 动态改变占空比 完成亮度时间切换 实现呼吸灯
zhuzhe 回答时间:2019-6-17 19:22:10
好好

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版