OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 19015|回复: 19

【ALIENTEK 战舰STM32开发板例程系列连载+教学】第十四章 PWM输出实验

[复制链接]

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
发表于 2013-1-17 22:33:18 | 显示全部楼层 |阅读模式

第十四章 PWM输出实验

上一章,我们介绍了STM32的通用定时器TIM3,用该定时器的中断来控制DS1的闪烁,这一章,我们将向大家介绍如何使用STM32TIM3来产生PWM输出。在本章中,我们将使用TIM3的通道2,把通道2重映射到PB5,产生PWM来控制DS0的亮度。本章分为如下几个部分:

14.1 PWM简介

14.2 硬件设计

14.3 软件设计

14.4 下载验证

14.1 PWM简介

脉冲宽度调制(PWM),是英文Pulse Width Modulation的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。

STM32的定时器除了TIM67。其他的定时器都可以用来产生PWM输出。其中高级定时器TIM1TIM8可以同时产生多达7路的PWM输出。而通用定时器也能同时产生多达4路的PWM输出,这样,STM32最多可以同时产生30PWM输出!这里我们仅使用TIM3CH2产生一路PWM输出。如果要产生多路输出,大家可以根据我们的代码稍作修改即可。

要使STM32的通用定时器TIMx产生PWM输出,除了上一章介绍的寄存器外,我们还会用到3个寄存器,来控制PWM的。这三个寄存器分别是:捕获/比较模式寄存器(TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)、捕获/比较寄存器(TIMx_CCR1~4)。接下来我们简单介绍一下这三个寄存器。

首先是捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器总共有2个,TIMx _CCMR1TIMx _CCMR2TIMx_CCMR1控制CH12,而TIMx_CCMR1控制CH34。该寄存器的各位描述如图14.1.1所示:


14.1.1 TIMx_CCMR1寄存器各位描述

该寄存器的有些位在不同模式下,功能不一样,所以在图14.1.1中,我们把寄存器分了2层,上面一层对应输出而下面的则对应输入。关于该寄存器的详细说明,请参考《STM32参考手册》第288页,14.4.7一节。这里我们需要说明的是模式设置位OCxM,此部分由3位组成。总共可以配置成7种模式,我们使用的是PWM模式,所以这3位必须设置为110/111。这两种PWM模式的区别就是输出电平的极性相反。

接下来,我们介绍捕获/比较使能寄存器(TIMx_CCER),该寄存器控制着各个输入输出通道的开关。该寄存器的各位描述如图14.1.2所示:


14.1.2 TIMx_ CCER寄存器各位描述

该寄存器比较简单,我们这里只用到了CC2E位,该位是输入/捕获2 输出使能位,要想PWMIO口输出,这个位必须设置为1,所以我们需要设置该位为1。该寄存器更详细的介绍了,请参考《STM32参考手册》第292页,14.4.9这一节。

最后,我们介绍一下捕获/比较寄存器(TIMx_CCR1~4),该寄存器总共有4个,对应4个输通道CH1~4。因为这4个寄存器都差不多,我们仅以TIMx_CCR1为例介绍,该寄存器的各位描述如图14.1.3所示:


14.1.3 寄存器TIMx_ CCR1各位描述

在输出模式下,该寄存器的值与CNT的值比较,根据比较结果产生相应动作。利用这点,我们通过修改这个寄存器的值,就可以控制PWM的输出脉宽了。本章,我们使用的是TIM3的通道2,所以我们需要修改TIM3_CCR2以实现脉宽控制DS0的亮度。

我们要使用TIM3CH2输出PWM来控制DS0的亮度,但是TIM3_CH2默认是接在PA7上面的,而我们的DS0接在PB5上面,如果普通MCU,可能就只能用飞线把PA7飞到PB5上来实现了,不过,我们用的是STM32,它比较高级,可以通过重映射功能,把TIM3_CH2映射到PB5上。

STM32的重映射控制是由复用重映射和调试IO配置寄存器(AFIO_MAPR)控制的,该寄存器的各位描述如图14.1.4所示:


14.1.4 寄存器AFIO_MAPR各位描述

我们这里用到的是TIM3的重映射,从上图可以看出,TIM3_REMAP是由[11:10]2个位控制的。TIM3_REMAP[1:0]重映射控制表如表14.1.1所示:


14.1.1 TIM3_REMAP重映射控制表

默认条件下,TIM3_REMAP[1:0]00,是没有重映射的,所以TIM3_CH1~TIM3_CH4分别是接在PA6PA7PB0PB1上的,而我们想让TIM3_CH2映射到PB5上,则需要设置TIM3_REMAP[1:0]=10,即部分重映射,这里需要注意,此时TIM3_CH1也被映射到PB4上了。

至此,我们把本章要用的几个相关寄存器都介绍完了,本章要实现通过重映射TIM3_CH2PB5上,由TIM3_CH2输出PWM来控制DS0的亮度。下面我们介绍配置步骤:

1)开启TIM3时钟,配置PB5为复用输出。

要使用TIM3,我们必须先开启TIM3的时钟(通过APB1ENR设置),这点相信大家看了这么多代码,应该明白了。这里我们还要配置PB5为复用输出,这是因为TIM3_CH2通道将重映射到PB5上,此时,PB5属于复用功能输出。

2)设置TIM3_CH2重映射到PB5上。

因为TIM3_CH2默认是接在PA7上的,所以我们需要设置TIM3_REMAP为部分重映射(通过AFIO_MAPR配置),让TIM3_CH2重映射到PB5上面。

3)设置TIM3ARRPSC

在开启了TIM3的时钟之后,我们要设置ARRPSC两个寄存器的值来控制输出PWM的周期。当PWM周期太慢(低于50Hz)的时候,我们就会明显感觉到闪烁了。因此,PWM周期在这里不宜设置的太小。

4)设置TIM3_CH2PWM模式。

接下来,我们要设置TIM3_CH2PWM模式(默认是冻结的),因为我们的DS0是低电平亮,而我们希望当CCR2的值小的时候,DS0就暗,CCR2值大的时候,DS0就亮,所以我们要通过配置TIM3_CCMR1的相关位来控制TIM3_CH2的模式。

5)使能TIM3CH2输出,使能TIM3

在完成以上设置了之后,我们需要开启TIM3的通道2输出以及TIM3。前者通过TIM3_CCER1来设置,是单个通道的开关,而后者则通过TIM3_CR1来设置,是整个TIM3的总开关。只有设置了这两个寄存器,这样我们才能在TIM3的通道2上看到PWM波输出。

6)修改TIM3_CCR2来控制占空比。

最后,在经过以上设置之后,PWM其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改TIM3_CCR2则可以控制CH2的输出占空比。继而控制DS0的亮度。

通过以上6个步骤,我们就可以控制TIM3CH2输出PWM波了。这里特别提醒一下大家,高级定时器虽然和通用定时器类似,但是高级定时器要想输出PWM,必须还要设置一个MOE(TIMx_BDTR的第15),以使能主输出,否则不会输出PWM!!

14.2 硬件设计

本实验用到的硬件资源有:

1)  指示灯DS0

2)  定时器TIM3

这两个前面都有介绍,但是我们这里用到了TIM3的部分重映射功能,把TIM3_CH2直接映射到了PB5上,而通过前面的学习,我们知道PB5DS0是直接连接的,所以电路上并没有任何变化。

14.3 软件设计

本章,我们依旧是在前一章的基础上修改代码,先打开之前的工程,然后我们在上一章的基础上,在timer.c里面加入如下代码:

//TIM3 PWM部分初始化

//PWM输出初始化

//arr:自动重装值

//psc:时钟预分频数

void TIM3_PWM_Init(u16 arr,u16 psc)

{                                               

       RCC->APB1ENR|=1<<1;            //TIM3时钟使能   

       RCC->APB2ENR|=1<<3;         //使能PORTB时钟      

       GPIOB->CRL&=0XFF0FFFFF;   //PB5输出

       GPIOB->CRL|=0X00B00000;      //复用功能输出       

       RCC->APB2ENR|=1<<0;         //开启辅助时钟       

       AFIO->MAPR&=0XFFFFF3FF;  //清除MAPR[11:10]

       AFIO->MAPR|=1<<11;           //部分重映像,TIM3_CH2->B5

       TIM3->ARR=arr;                        //设定计数器自动重装值

       TIM3->SC=psc;                        //预分频器不分频 

       TIM3->CCMR1|=7<<12;            //CH2 PWM2模式       

       TIM3->CCMR1|=1<<11;             //CH2预装载使能   

       TIM3->CCER|=1<<4;               //OC2 输出使能      

       TIM3->CR1=0x0080;                //ARPE使能

       TIM3->CR1|=0x01;                  //使能定时器3                                                        

此部分代码包含了上面介绍的PWM输出设置的前5个步骤。这里我们关于TIM3的设置就不再说了,这里提醒下:在配置AFIO相关寄存器的时候,必须先开启辅助功能时钟。

接着我们修改timer.h如下:

#ifndef __TIMER_H

#define __TIMER_H

#include "sys.h"

//通过改变TIM3->CCR2的值来改变占空比,从而控制LED0的亮度

#define LED0_PWM_VAL TIM3->CCR2

void TIM3_Int_Init(u16 arr,u16 psc);

void TIM3_PWM_Init(u16 arr,u16 psc);

#endif

这里头文件与上一章的不同是加入了TIM3_PWM_Init的声明以及宏定义了TIM3通道2的输入/捕获寄存器。通过这个宏定义,我们可以在其他文件里面修改LED0_PWM_VAL的值,就可以达到控制LED0的亮度的目的了。也就是实现了前面介绍的最后一个步骤。

接下来,我们修改主程序里面的main函数如下:

int main(void)

{                                                             

       u16 led0pwmval=0;   

       u8 dir=1;

       Stm32_Clock_Init(9);    //系统时钟设置

       uart_init(72,9600);      //串口初始化为9600

       delay_init(72);                  //延时初始化

       LED_Init();                 //初始化与LED连接的硬件接口

       BEEP_Init();                //初始化蜂鸣器端口

       KEY_Init();           //初始化与按键连接的硬件接口

      TIM3_PWM_Init(899,0);      //不分频。PWM频率=72000/(899+1)=80Khz

     while(1)

       {

             delay_ms(10);

              if(dir)led0pwmval++;

              else led0pwmval--;

             if(led0pwmval>300)dir=0;

              if(led0pwmval==0)dir=1;                                        

              LED0_PWM_VAL=led0pwmval;    

       }

}

这里,我们从死循环函数可以看出,我们控制LED0_PWM_VAL的值从0变到300,然后又从300变到0,如此循环,因此DS0的亮度也会跟着从暗变到亮,然后又从亮变到暗。至于这里的值,我们为什么取300,是因为PWM的输出占空比达到这个值的时候,我们的LED亮度变化就不大了(虽然最大值可以设置到899),因此设计过大的值在这里是没必要的。至此,我们的软件设计就完成了。

14.4 下载验证

在完成软件设计之后,将我们将编译好的文件下载到战舰STM32开发板上,观看其运行结果是否与我们编写的一致。如果没有错误,我们将看DS0不停的由暗变到亮,然后又从亮变到暗。每个过程持续时间大概为3秒钟左右。

实际运行结果如下图14.4.1所示:


14.4.1 PWM控制DS0亮度

 

实验9 PWM输出实验.rar

34.05 KB, 下载次数: 1535

《STM32开发指南》第十四章 PWM输出实验.rar

626.83 KB, 下载次数: 1630

我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

13

帖子

0

精华

新手上路

积分
33
金钱
33
注册时间
2014-8-14
在线时间
0 小时
发表于 2014-8-30 22:51:01 | 显示全部楼层
好。。。。。。。。。。。。。。。。。。。
回复 支持 反对

使用道具 举报

11

主题

61

帖子

0

精华

初级会员

Rank: 2

积分
134
金钱
134
注册时间
2014-8-18
在线时间
2 小时
发表于 2014-10-22 10:28:03 | 显示全部楼层
原子哥,     TIM3->CCER|=1<<4;               //OC2 输出使能  
这句是不是应该改成    TIM3->CCER|=3<<4;               //OC2 输出使能       
才对?
回复 支持 反对

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
 楼主| 发表于 2014-10-22 23:48:25 | 显示全部楼层
回复【3楼】小郭:
---------------------------------
为什么呢?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

11

主题

61

帖子

0

精华

初级会员

Rank: 2

积分
134
金钱
134
注册时间
2014-8-18
在线时间
2 小时
发表于 2014-10-23 13:04:27 | 显示全部楼层
回复【4楼】正点原子:
---------------------------------
TIM3->CCMR1|=7<<12;   //这里将OC1M置为111,即CNT<CCR1时,通道1为无效电平;
TIM3->CCER|=1<<4;    //然后是这里,按原子哥你的代码,是将CC1E位打开(开启OC2信号输出),而CC1P为0,即OC2高电平有效;

我按照你的代码运行时,总是在SYSTICK中死循环,后来查手册后,将TIM3->CCER|=1<<4;改为TIM3->CCER|=3<<4;就没问题;不知道是不是我的板子的问题,不过就算是其他的板子应该也不会有这种问题,因为GPIOB5是低电平驱动的; 所以我就想问下你;
回复 支持 反对

使用道具 举报

11

主题

61

帖子

0

精华

初级会员

Rank: 2

积分
134
金钱
134
注册时间
2014-8-18
在线时间
2 小时
发表于 2014-10-23 15:54:19 | 显示全部楼层
回复【4楼】正点原子:
---------------------------------
老大,我理解错误了;常亮是因为暗的时间太短,看不出变化;一开始对定时器的理解错了,导致后面的理解也错了;不过还是有个问题想问下,当我设置CNT<CCR2时,低电平为有效电平;在CNT>=CCR2时,为无效电平,这个无效电平是不是高电平?
回复 支持 反对

使用道具 举报

2

主题

9

帖子

0

精华

新手上路

积分
37
金钱
37
注册时间
2014-9-20
在线时间
0 小时
发表于 2014-10-23 19:53:47 | 显示全部楼层

原子哥,这个延时函数的作用是什么,为什么我修改延时时间实验现象会不一样呢?

(延时1ms亮暗快速变化,延时100ms亮了之后不会变暗)

回复 支持 反对

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
 楼主| 发表于 2014-10-23 22:53:14 | 显示全部楼层
回复【7楼】失心:
---------------------------------
就是让人看见效果。
1ms变快正常,100ms亮了不暗是不可能的,只是时间慢了而已,10倍。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

2

主题

9

帖子

0

精华

新手上路

积分
37
金钱
37
注册时间
2014-9-20
在线时间
0 小时
发表于 2014-10-24 09:07:17 | 显示全部楼层
回复【8楼】正点原子:
---------------------------------
我懂了,之前理解错了,谢谢原子哥。
回复 支持 反对

使用道具 举报

15

主题

181

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
389
金钱
389
注册时间
2014-11-8
在线时间
40 小时
发表于 2015-3-4 13:06:00 | 显示全部楼层
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源 
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx的中断待处理位:TIM 中断源 
LED1=!LED1;
}
}

请问原子哥这个中断函数执行LED1=!LED1;这句话有什么作用?各位大神明白的帮助解答一下
Good good study , day day up...
回复 支持 反对

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
 楼主| 发表于 2015-3-4 22:37:48 | 显示全部楼层
回复【10楼】Just Beat It:
---------------------------------
作用就是取反LED1
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

2

主题

15

帖子

0

精华

初级会员

Rank: 2

积分
108
金钱
108
注册时间
2013-5-30
在线时间
21 小时
发表于 2015-4-25 17:56:58 | 显示全部楼层
看了一下库函数版的代码,楼主使用
TIM_SetCompare2(TIM3,led0pwmval); 修改PWM的占空比,
 另外看了一下库函数有 TIM_SetAutoreload()可以修改PWM的周期,

问题是,同时调用这两个函数,即修改占空比和周期,实际测试,发现LED不会立即执行而是延时一段时间后才按照修改后的参数执行,
            单独只调用TIM_SetCompare2(TIM3,led0pwmval);感觉不到延迟就立即执行了。

翻阅了一把参考手册,貌似是影子寄存器导致设置ARR后木有立即更新的缘故,可问题是在pwm初始化的时候并没有使能ARPE位啊,不知为何会有延时?

最后调试中尝试让ARPE置位,TIM_ARRPreloadConfig(TIM3, ENABLE); 反而问题解决了,每次修改占空比和周期都可以立即更新,真个闹不懂是啥道理,原子老大有遇到过吗?

(需要说明的一点是,我程序中木有采用循环设置PWM 占空比和周期的参数,而是通过串口,发送一次,就配置一次PWM的参数。)
回复 支持 反对

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
 楼主| 发表于 2015-4-25 21:37:18 | 显示全部楼层
回复【12楼】高阳:
---------------------------------
没遇到过哦。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

7

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
180
金钱
180
注册时间
2014-10-14
在线时间
22 小时
发表于 2015-7-6 20:59:55 | 显示全部楼层
为什么“DS0接在PB5上面,如果普通MCU,可能就只能用飞线把PA7飞到PB5上来实现了”,而stm32不行呢?
回复 支持 反对

使用道具 举报

7

主题

24

帖子

0

精华

初级会员

Rank: 2

积分
180
金钱
180
注册时间
2014-10-14
在线时间
22 小时
发表于 2015-7-7 10:50:41 | 显示全部楼层
回复【13楼】正点原子:
---------------------------------
为什么“DS0接在PB5上面,如果普通MCU,可能就只能用飞线把PA7飞到PB5上来实现了”,而stm32不行呢?
回复 支持 反对

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
 楼主| 发表于 2015-7-8 00:19:19 | 显示全部楼层
回复【15楼】Leejasing:
---------------------------------
不明白你的意思
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

6

帖子

0

精华

新手上路

积分
26
金钱
26
注册时间
2015-7-20
在线时间
0 小时
发表于 2015-7-20 15:34:27 | 显示全部楼层
问下原子哥,要获取多路PWM要怎么修改程序,新手,不知道要修改哪里,已经能够通过一个电位器控制一个灯的亮度,但是我想用多个电位器控制多的LED灯,看了程序还是无从下手啊
回复 支持 反对

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
 楼主| 发表于 2015-7-20 23:24:17 | 显示全部楼层
回复【17楼】李杰培:
---------------------------------
获取PWM???
PWM输入捕获,算么?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

6

帖子

0

精华

新手上路

积分
26
金钱
26
注册时间
2015-7-20
在线时间
0 小时
发表于 2015-7-21 08:57:54 | 显示全部楼层
回复【18楼】正点原子:
---------------------------------
是想要得到多路的PWM输出,频率固定,然后用多个电位器控制不同PWM的占空比,来调节不同LED灯的亮度
回复 支持 反对

使用道具 举报

2

主题

15

帖子

0

精华

新手上路

积分
43
金钱
43
注册时间
2015-3-12
在线时间
0 小时
发表于 2015-7-28 08:38:23 | 显示全部楼层
该寄存器比较简单,我们这里只用到了CC2E位,该位是输入/捕获2 输出使能位,要想PWM从IO口输出,这个位必须设置为1,所以我们需要设置该位为1。该寄存器更详细的介绍了,请参考《STM32参考手册》第292页,14.4.9这一节。
=======================================================
CC2E应该是捕获比较使能位吧...好像OC2E才是输入捕获使能...
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-5-17 12:49

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表