历史上的今天
返回首页

历史上的今天

今天是:2024年09月27日(星期五)

正在发生

2021年09月27日 | STM32F103入门 | 12.PWM实现呼吸灯

2021-09-27 来源:eefocus

12.1. PWM简介


PWM全称为“Pulse Width Modulation”。中文翻译为:脉冲宽度调制。脉冲宽度指的是 脉冲持续的时间,既高电平或低电平保持(持续)的时间。而PWM通俗的说就是人为的(通过微处理器)去控制电平高低保持的时间。这里引出一个新名词,占空比:在一个脉冲的循环中,通电时间相对于总时间所占的比例。


STM32 的定时器除了 TIM6 和 TIM7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4 路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出!



12.2. PWM相关寄存器


除了定时器章节介绍的几个寄存器( ARR、PSC、 CR1 等) 外,还会用到 4 个寄存器(通用定时器则只需要 3 个),来控制 PWM 的输出。这四个寄存器分别是:捕获/比较模式寄存器( TIMx_CCMR1/2)、捕获/比较使能寄存器( TIMx_CCER)、捕获/比较寄存器( TIMx_CCR1~4) 以及刹车和死区寄存器( TIMx_BDTR)。


(1)捕获/比较模式寄存器( TIMx_CCMR1/2)


该寄存器总共有 2 个, TIMx _CCMR1 和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 CH2,而 TIMx_CCMR2 控制 CH3 和 CH4。


寄存器分了 2 层,上面一层对应输出时的设置而下面的则对应输入时的设置。模式设置位 OCxM,此部分由 3 位组成。总共可以配置成 7 种模式,我们使用的是 PWM 模式,这 3 位必须设置为110/111。这两种 PWM 模式的区别就是输出电平的极性相反。 另外 CCxS 用于设置通道的方向(输入/输出)默认设置为 0,就是设置通道作为输出使用。


/**

  * 没有重映射时,TIM3的四个通道CH1, CH2, CH3, CH4分别对应PA6, PA7, PB0, PB1

  * 部分重映射时,TIM3的四个通道CH1, CH2, CH3, CH4分别对应PB4, PB5, PB0, PB1

  * 完全重映射时,TIM3的四个通道CH1, CH2, CH3, CH4分别对应PC6, PC7, PC8, PC9

  * 

  * 110:PWM模式1 - 在向上计数时,一旦TIMx_CNT  * 否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CRRx时,通道x为无效电平,

  * 否则为有效电平。

  *

  * 111:PWM模式2 - 在向上计数时,一旦TIMx_CNT  * 否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CRRx时,通道x为有效电平,

  * 否则为无效电平。

  */

(2)捕获/比较使能寄存器( TIMx_CCER)


这里只用到了 CC1E 位,该位是输入/捕获 1 输出使能位,要想PWM 从 IO 口输出,这个位必须设置为 1。


(3)捕获/比较寄存器( TIMx_CCR1~4)


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


(4)刹车和死区寄存器( TIMx_BDTR)


如果是通用定时器,则配置以上三个寄存器就够了,但是如果是高级定时器,则还需要配置:刹车和死区寄存器( TIMx_BDTR)。该寄存器,我们只需要关注最高位: MOE 位,要想高级定时器的 PWM 正常输出,则必须设置 MOE 位为 1,否则不会有输出。注意:通用定时器不需要配置这个。


12.3. PWM波形产生原理


通用定时器可以利用 GPIO 引脚进行脉冲输出,在配置为比较输出、PWM 输出功能时,捕获/比较寄存器 TIMx_CCR 被用作比较功能,下面把它简称为比较寄存器。


这里直接举例说明定时器的PWM输出工作过程:若配置脉冲计数器TIMx_CNT 为向上计数,而重载寄存器 TIMx_ARR 被配置为 N,即 TIMx_CNT 的当前计数值数值X在 TIMxCLK 时钟源的驱动下不断累加,当 TIMx_CNT 的数值X大于 N 时,会重置 TIMx_CNT 数值为 0 重新计数。


而在 TIMxCNT 计数的同时,TIMxCNT 的计数值X会与比较寄存器TIMx_CCR 预先存储了的数值 A 进行比较,当脉冲计数器 TIMx_CNT 的数值X小于比较寄存器 TIMx_CCR 的值A时,输出高电平(或低电平),相反地,当脉冲计数器的数值X大于或等于比较寄存器的值 A 时,输出低电平(或高电平)。


如此循环,得到的输出脉冲周期就为重载寄存器 TIMx_ARR 存储的数值 (N+1) 乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器 TIMx_CCR 的值 A 乘以触发脉冲的时钟周期,即输出PWM的占空比为 A/(N+1) 。


12.4 PWM配置步骤


1.开启TIM3时钟、GPIOB时钟和复用功能时钟

2.配置GPIOB5为复用输出

3.设置TIM3_CH2重映射到PB5上

4.初始化TIM3,设置ARR和PSC

5.设置TIM3_CH2的PWM模式

6.使能TIM3的CH2输出

7.使能TIM3

8.在主函数中改变占空比完成呼吸灯


12.5 定时器引脚复用功能映射

根据以上重映像表,我们使用定时器3的通道2作为PWM的输出引脚,所以需要对PB5引脚进行配置。注意:如果使用PB4当做TIM3部分重映射的CH1输出,除了要进行部分重映射配置外,还需要禁用JTAG!并在开启复用时钟后禁用JTAG!


GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);  //禁用JTAG,在开启复用时钟后禁用


1. 新建两个文件,pwm.c 和 pwm.h

2. 在头文件 pwm.h 添加下面代码:


#ifndef _PWM_H

#define _PWM_H

#include "stm32f10x.h"

 

void PWM_Init(u16 arr, u16 psc);

 

#endif;

3. 把 pwm.c 添加到工程中

4. 在 pwm.c 中添加以下代码:


#include "pwm.h"

 

void PWM_Init(u16 arr,u16 psc)

{

    GPIO_InitTypeDef GPIO_InitStructure;              //定义GPIO结构体

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;    //定义TIMx定时器结构体

    TIM_OCInitTypeDef TIM_OCInitStructure;            //定义定时器脉宽调制结构体

    

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);                     //使能TIM3时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO,ENABLE);//使能GPIOB时钟和AFIO复用时钟

    

    GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);                     //TIM3部分重映射 TIM3_CH2->PB5

    

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;                               //TIM_CH2

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                         //复用推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;                       //配置输出速率

    GPIO_Init(GPIOB,&GPIO_InitStructure);                                   //初始化GPIOB

    

    TIM_TimeBaseStructure.TIM_Period = arr;                                 //设置自动重装载寄存器周期的值 arr=value-1

    TIM_TimeBaseStructure.TIM_Prescaler = psc;                              //设置预分频值 psc=value-1

    TIM_TimeBaseStructure.TIM_ClockDivision = 0;                            //设置时钟分割:TDTS = Tck_tim

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;             //TIM向上计数模式

    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);                          //初始化TIMx时间基数

    

    //初始化TIM3 Channel2 PWM模式     

    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                       //选择定时器模式:TIM脉冲宽度调制模式1

    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;           //使能比较输出

    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;               //输出极性:TIM输出比较极性高

    TIM_OC2Init(TIM3,&TIM_OCInitStructure);                                 //根据T指定的参数初始化外设TIM3 OC2

 

    TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable);                        //使能TIM3在CCR2上的预装载寄存器

    TIM_Cmd(TIM3, ENABLE);                                                  //使能TIM3

}

 

5. 实现PWM呼吸灯功能


#include "stm32f10x.h"

#include "delay.h"

#include "led.h"

#include "tim.h"

#include "key.h"

#include "pwm.h"

 

int main(void)

{

    u16 pwmValue = 0;

    u8 dir = 0;

 

    delay_init();

    PWM_Init(999, 719);

 

    while(1)

    {

        if(dir) {

            if(pwmValue > 550) TIM_SetCompare2(TIM3, --pwmValue);

            else dir = 0;

        }

        else {

            if(pwmValue < 990) TIM_SetCompare2(TIM3, ++pwmValue);

            else dir = 1;

        }

        delay_ms(3);

    }

}


小提示:如果身边没有LED电阻面包板搭电路,可以用杜邦线连接PB5和PC13,同时PC13设置成开漏输出(GPIO_Mode_Out_OD),这样就能完成呼吸灯实验了(不瞒你说我也是这样干的)。

推荐阅读

史海拾趣

Bestar Electric公司的发展小趣事

随着全球市场的不断变化和消费者需求的多样化,Bestar Electric开始加快国际化布局。公司通过在海外设立分支机构、开展国际合作等方式,进一步拓展国际市场。同时,公司还关注未来发展趋势,积极布局新兴领域,如人工智能、5G通信等,为公司的未来发展奠定坚实基础。

这些故事只是对电子行业公司发展历程的概括性描述,并不特指Bestar Electric公司的具体经历。每个公司的发展都有其独特之处,需要具体分析和了解。

Greenconn Corp公司的发展小趣事
如果冰箱断电保护器上有复位按钮,可以尝试按下以复位保护器。
天钰(FITIPOWER)公司的发展小趣事
确保冰箱内部和外部的线路连接牢固,如有松动需重新连接。
Emmoco公司的发展小趣事

在稳固了电子元器件和模块市场地位后,Emmoco开始寻求多元化发展。公司利用自身的技术优势和市场经验,逐步拓展产品线,涉足智能家居、物联网等领域。通过不断的技术创新和市场拓展,Emmoco成功实现了产品线的多元化,为客户提供更加全面和多样化的解决方案。

Advanced Electronic Packaging公司的发展小趣事

面对电子行业的快速发展和不断变化的市场需求,Advanced Electronic Packaging公司始终保持敏锐的洞察力和强烈的创新意识。公司不断加大研发投入,推动封装技术的持续创新。从最初的单一材料封装,到后来的多功能复合封装,再到如今的智能化封装技术,公司的每一次创新都引领着电子封装行业的发展潮流。这些创新成果不仅提升了产品的附加值,还推动了整个电子产业的升级换代。

中电熊猫(CEC)公司的发展小趣事

在智能制造领域,中电熊猫也取得了显著进展。2010年,中电熊猫开始研发液晶面板工厂的智能运储系统,打破了以往依赖国外供应商的局面。到了2011年,中电熊猫成功完成了国内首条高世代液晶面板智能运储系统的研发,并逐渐成为国内该系统的主要供应商。此外,中电熊猫还在液晶玻璃生产线系统等方面实现了国产化研发,提升了整体产业的竞争力。

问答坊 | AI 解惑

做一个小项目时的元件库

做一个小项目时的自己画的封装元件库,包括一小部分贴片元件(1210、1206、0805),还有些IC的封装,希望能对大家有用 …

查看全部问答>

搜索了下,坛子里没有的--————探头入门

觉得还可以,拿出来和大家分享。搜索了下坛子里没有的,大家看看: 目  录 第1章: 探头-测量质量的关键环节………………………………………… 什么是探头…………………………………………………………………… 理想的探头………… ...…

查看全部问答>

DM642的Protel格式原理图库

DM642的Protel格式原理图库…

查看全部问答>

20元打造经典PC遥控器(1)

编者按:不久前曾发表了。那是一个带有4路立体声输入,一路输出的红外遥控音源切换器兼PC遥控器的DIY制作。因其带有音源切换功能所以要用的元件较多,电路也较复杂,对于一般缺乏电子制作经验的PCDIYer来说是有一定难度的。其实在国外的DIY网站也有 ...…

查看全部问答>

串口转并口的集成芯片,并且不要移位的,有吗??

先想有一芯片,可以讲串行数据转换成并行数据,但一般都是移位寄存器形式的。 我想有没有一款芯片,可以直接把串行数据放到指定位置的??…

查看全部问答>

下面的VHDL程序为什么不能实现移位?

该程序是实现012345-123450-234501-345012-450123-501234-012345的循环表示 library IEEE; use IEEE.std_logic_1164.all; use IEEE.std_logic_unsigned.all; use ieee.std_logic_arith.all; entity disp_inturn is     port( ...…

查看全部问答>

写2440 GPIO驱动遇到个奇怪得问题

实现得功能是应用层窗口点击GPJ哪个脚高或低驱动对应就把GPJ引脚拉高或低。我功能能够完全实现,不过有个地方不解:拉低必须是在输入模式才行(rGPGCON &= ~0xffff),配成输出模式也拉不下去,rGPGUP是配好了的当要拉低的时候rGPGUP是disable的, ...…

查看全部问答>

wince下捕捉鼠标事件

在wince下能不能捕捉到全局鼠标事件啊?原来是想用钩子的,结果试了下,发现好像wince下面没有鼠标钩子,有没有哪位达人能告诉我到底有没有办法啊! 具体需求:做一个触摸屏的背光控制,当发现一段时间没有点击后自动关闭背光。如果有做过的兄弟有 ...…

查看全部问答>

谁有c++写的反汇编程序

给我传一个把 Kukumh1986@163.com 我用的是s12的MCU 最好反汇编成S12的汇编 我的QQ277191683 希望多交流呢…

查看全部问答>

关于USB设备的读写

要读写USB设备的数据 m_hDevice= CreateFile(DevInterface.DevicePath(),GENERIC_READ |GENERIC_WRITE,FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL); 已经获得句柄 WriteFile(m_hDevice,szWriteBuffer,dwWriteByte,&dwWriteL ...…

查看全部问答>