OpenEdv-开源电子网

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

【Alientek STM32 实验8】--PWM输出实验

[复制链接]

98

主题

408

帖子

3

精华

金牌会员

Rank: 6Rank: 6

积分
1280
金钱
1280
注册时间
2010-12-14
在线时间
0 小时
发表于 2010-12-14 23:08:34 | 显示全部楼层 |阅读模式

3.8 PWM输出实验

 

上一节,我们介绍了STM32的通用定时器TIM3,用该定时器的中断来控制DS1的闪烁,这一节,我们将向大家介绍如何使用STM32TIM3来产生PWM输出。本节分为如下几个部分:

3.8.1 PWM简介

3.8.2 硬件设计

3.8.3 软件设计

3.8.4 下载与测试

3.8.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。该寄存器的各位描述如下:

              3.8.1.1寄存器TIMx_CCMR1各位描述

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

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

                   3.8.1.2寄存器TIMx_ CCER各位描述

该寄存器比较简单,我们这里不多说了,有不明白的地方,请参考《STM32参考手册》第251页,13.4.9这一节。

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

                              3.8.1.3寄存器TIMx_ CCR1各位描述

在输出模式下,该寄存器的值与CNT的值比较,根据比较结果产生相应动作。利用这点,我们通过修改这个寄存器的值,就可以控制PWM的输出脉宽了。

至此,我们把这一节要用的几个TIMx相关寄存器都介绍完了,解析来我们就说说这一节要实现的功能。我们要利用TIM3CH2(对应MiniSTM32开发板的PA7)输出PWM来控制DS0的亮度。所以我们在软件上要做的就是控制TIM3_CH2PWM输出。接下来我们将介绍通过哪几个步骤,就可以达到这个目的:

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

要使用TIM3,我们必须先开启TIM3的时钟(通过APB1ENR设置),这点相信大家看了这么多代码,应该明白了。这里我们还要配置PA7为复用输出,这是因为TIM3_CH2通道是以IO复用的形式连接到PA7上的,这里我们要使用复用输出功能。

2)设置TIM3ARRPSC

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

3)设置TIM3_CH2PWM模式。

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

4)使能TIM3CH2输出,使能TIM3

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

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

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

通过以上5个步骤,我们就可以控制TIM3CH2输出PWM波了。

 

3.8.2 硬件设计

    

该部分,因为我们DS0是连接在PA8上的,而我们的PWM输出是在PA7,所以,硬件上应该把PA7PA8通过跳线帽短接起来,然后配置PA8为浮空输入(IO口复位后的状态),以免干扰PA7的信号。因此,这一节的电路,除了在PA7PA8之间放一个跳线帽,其他的都不需要改动。

 3.8.2.1硬件连接图

            将上图中的PA7PA8用跳线帽短接,图中绿色圈出部分。

 

3.8.3 软件设计

 

这里,我们在之前的定时器中断实验的基础上修改,先打开之前的工程,然后我们在timer.c里面加入如下代码:

//PWM输出初始化

//arr:自动重装值

//psc:时钟预分频数

void PWM_Init(u16 arru16 psc)

{                                                                           

     //此部分需手动修改IO口设置

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

     GPIOA->CRH&=0XFFFFFFF0;//PA8输出

     GPIOA->CRH|=0X00000004;//浮空输入

     GPIOA->CRL&=0X0FFFFFFF;//PA7输出

     GPIOA->CRL|=0XB0000000;//复用功能输出        

     GPIOA->ODR|=1<<7;//PA7上拉           

 

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

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

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

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

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

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

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

} 

此部分代码包含了上面介绍的PWM输出设置的前4个步骤。这里我们关于TIM3的设置就不再说了,要说的是里面对于PA7PA8的设置,此函数刚开始就设置了PA8为浮空输入,这是因为我们把PA7PA8端接起来了,而在LED_Init函数里面有把PA8设置成推挽输出,因为这里我们不需要用PA8的输出,而是使用PA7的复用输出,所以必须禁止PA8,否则就会干扰PA7的输出,甚至出现IO口自短路!而PA7设置成复用输出,则比较好理解了,因为我们使用的是IO口的第二功能。

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

#ifndef __TIMER_H

#define __TIMER_H

#include "sys.h"

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

#define LED0_PWM_VAL TIM3->CCR2

void Timerx_Init(u16 arru16 psc);

void PWM_Init(u16 arru16 psc);

#endif

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

接下来,我们修改main函数如下:

int main(void)

{              

     u16 led0pwmval=0;

     u8 dir=1;          

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

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

     uart_init(729600);  //串口初始化

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

     PWM_Init(9000);       //不分频。PWM频率=72000/900=8Khz

     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亮度变化就不大了(虽然最大值可以设置到900),因此设计过大的值在这里是没必要的。至此,我们的软件设计就完成了。

 

3.8.4 下载与测试

    

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

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

                              3.8.4.1 PWM控制DS0亮度

ALIENTEK MINISTM32 实验8 PWM输出实验.rar

909.92 KB, 下载次数: 2948

PWM实验.pdf

437.71 KB, 下载次数: 2600

希望openedv能给大家提供一个友好的技术交流平台!
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

21

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2013-2-18
在线时间
0 小时
发表于 2013-2-28 15:39:24 | 显示全部楼层
回复【楼主位】张洋:
---------------------------------
首先是捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器总共有2个,TIMx _CCMR1和TIMx _CCMR2。TIMx_CCMR1控制CH1和2,而TIMx_CCMR1控制CH3和4。该寄存器的各位描述如下:
这一句,中是2个TIMX_CCMR1, 我想说后面的那一个是不是TIMx_CCMR2啊?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2013-2-28 15:52:47 | 显示全部楼层
是的,真仔细,呵呵。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

21

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2013-2-18
在线时间
0 小时
发表于 2013-2-28 16:04:30 | 显示全部楼层
回复【3楼】正点原子:
---------------------------------
 TIM3->CR1=0x8000;   //ARPE使能
弱弱的问一句,这个寄存器不是只有【9:0】位是有效的吗,[15:10]是保留的,这个0X8000;怎么使能ARPE 啊?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2013-2-28 16:41:39 | 显示全部楼层
也有问题,应该是0X0080。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
21
金钱
21
注册时间
2013-8-11
在线时间
0 小时
发表于 2013-8-11 16:31:43 | 显示全部楼层
RCC->APB1ENR|=1<<1;       //TIM3时钟使能    

     GPIOA->CRH&=0XFFFFFFF0;//PA8输出

     GPIOA->CRH|=0X00000004;//浮空输入 

     GPIOA->CRL&=0X0FFFFFFF;//PA7输出

     GPIOA->CRL|=0XB0000000;//复用功能输出        

     GPIOA->ODR|=1<<7;//PA7上拉            

弱弱的问下,怎么没有使能A组端口呢?即RCC->APB2ENR|=1<<2//使能PORTA口时钟呢?我看串口实验都有使能,想问下一般什么时候要使能呢?谢谢啦。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2013-8-11 19:37:55 | 显示全部楼层
回复【6楼】Ljhslsf:
---------------------------------
在其他地方使能了的话,这里就不用再使能了.
就好像一个开关,你开10次和开1次,是一样的效果.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

5

帖子

0

精华

新手上路

积分
39
金钱
39
注册时间
2014-3-12
在线时间
1 小时
发表于 2014-3-12 12:29:42 | 显示全部楼层
弱弱的问一句斑竹,为什么不直接用PA8 TIM1 CH1作为驱动DS0呢! 还要通过PA7 TIM3 CH2来做,我板子上设计的是PA8作为驱动PWM,可是就不成功。
回复 支持 反对

使用道具 举报

0

主题

5

帖子

0

精华

新手上路

积分
39
金钱
39
注册时间
2014-3-12
在线时间
1 小时
发表于 2014-3-12 12:31:15 | 显示全部楼层
回复【8楼】76985639@qq.com:

弱弱的问一句斑竹,为什么不直接用PA8 TIM1 CH1作为驱动DS0呢! 还要通过PA7 TIM3 CH2来做,我板子上设计的是PA8作为驱动pwm,可是就不成功。

---------------------------------
难道 A8不能作为 WM
回复 支持 反对

使用道具 举报

1

主题

7

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2014-5-14
在线时间
0 小时
发表于 2014-5-14 15:48:34 | 显示全部楼层
回复【9楼】76985639@qq.com:
不知道你问题解决了没,我是初学者,把TIM3的CH2改为CH1、CH3都不行,你问题解决的话能不能把现在的程序分享下,谢了
回复 支持 反对

使用道具 举报

6

主题

37

帖子

0

精华

初级会员

Rank: 2

积分
81
金钱
81
注册时间
2013-9-15
在线时间
0 小时
发表于 2014-6-5 11:50:19 | 显示全部楼层
回复【7楼】正点原子:
---------------------------------
原子哥,最近在深入学习定时器这里,你的手册里怎么有些地方PWM初始化的时候arr-1,有的时候却直接是arr,
比如
PWM_Init(900,0);    //不分频。PWM 频率=72000/900=8Khz
TIM3_PWM_Init(899,72-1);    //不分频。PWM 频率=72000/(899+1)=80Khz
虽然这个1可能影响不大,但是这样不严谨不是吗,正确的应该是哪个呢
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2014-6-6 00:38:50 | 显示全部楼层
arr-1才准确。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

7

主题

31

帖子

0

精华

初级会员

Rank: 2

积分
79
金钱
79
注册时间
2014-7-12
在线时间
0 小时
发表于 2014-8-19 21:50:14 | 显示全部楼层
回复【12楼】正点原子:
---------------------------------
原子哥,请问CCR2里的值是如何改变占空比的?
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2014-8-19 23:00:52 | 显示全部楼层
回复【13楼】beiker:
---------------------------------
这个你理解PWM原理,就知道了.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

7

主题

31

帖子

0

精华

初级会员

Rank: 2

积分
79
金钱
79
注册时间
2014-7-12
在线时间
0 小时
发表于 2014-8-20 15:18:43 | 显示全部楼层
回复【14楼】正点原子:
---------------------------------
好的,我在好好看看!
回复 支持 反对

使用道具 举报

17

主题

168

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
260
金钱
260
注册时间
2014-4-12
在线时间
0 小时
发表于 2014-10-14 21:34:09 | 显示全部楼层
除了那两个例程,其他的定时器啊都得自己设置
规格严格,功夫到家
回复 支持 反对

使用道具 举报

17

主题

168

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
260
金钱
260
注册时间
2014-4-12
在线时间
0 小时
发表于 2014-10-14 21:34:44 | 显示全部楼层
对照着技术文档,练功夫的时候到了
规格严格,功夫到家
回复 支持 反对

使用道具 举报

6

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
171
金钱
171
注册时间
2013-8-15
在线时间
37 小时
发表于 2014-11-20 22:39:52 | 显示全部楼层
回复【14楼】正点原子:
---------------------------------
原子哥,PWM实验为什么不直接用PA8 TIM1 CH1作为驱动DS0呢?我自己改成TIM1 CH1就是不行,不解啊
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2014-11-20 23:09:49 | 显示全部楼层
回复【18楼】86king:
---------------------------------
用TIM1_CH1,还需要使能MOE位哦,你使能了么?
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

6

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
171
金钱
171
注册时间
2013-8-15
在线时间
37 小时
发表于 2014-11-20 23:22:44 | 显示全部楼层
回复【19楼】正点原子:
---------------------------------
原子哥,有使能的,就改了一下初始化代码,但是DS0就是一直常亮,查了好几遍没发现哪里出问题了。不明白例程为什么不直接用TIM1_CH1?麻烦帮忙看看初始化代码,谢谢了
void WM_Init(u16 arr,u16 psc)
{  

GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_OCInitTypeDef  TIM_OCInitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA  | RCC_APB2Periph_AFIO, ENABLE);  
                                                                         

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = 0; 
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; 
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

 
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; 
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; 
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; 
TIM_OC2Init(TIM1, &TIM_OCInitStructure);  
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);  


TIM_CtrlPWMOutputs(TIM1,ENABLE); 
TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable); 
TIM_ARRPreloadConfig(TIM1, ENABLE); 
TIM_Cmd(TIM1, ENABLE); 
 

}
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2025-6-28 21:50

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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