说起PWM波,做过智能小车的人肯定都很清楚了,其实他就是一种脉宽调制。在智能小车上,我们一般用PWM波来控制小车的速度,通过控制方波中高低电平的比例,来达到控制小车转速的目的。而32的芯片他提供了专门的PWM波输出通道,我们只需要进行相应的配置就可以调用32的PWM波的输出,今天我们就用定时器3的通道2产生PWM波来控制LED灯的亮度,以实现呼吸灯的效果。

我先来讲一下32的PWM波输出的大概工作原理。首先我们还是需要用定时器,(其实用的是计数器,因为定时器的本质就是计数器)假如说我们让计数器从0开始计数,一直计到100,然后我们在0到100中间再设定一个数,假如说我们设定为30,那么当计数器中的计数值小于30的时候,PWM输出的引脚会输出一个电平(具体是高电平还是低电平,是可以设置的),然后如果计数器的值大于了30,那么PWM波的输出引脚会输出和刚才相反的电平,然后计数到100之后,再返回到0,重新开始下一轮计数,然后我们通过控制中间这个数值(就是刚才那个例子中的30),我们就可以控制PWM波的占空比了,然后通过控制计数的最大值(就是刚才那个例子中100)就可以控制PWM波输出的周期了。这就是32中PWM波输出的基本原理。

今天我们还是用到了LED和定时器,按照老套路还是先建两个空白的文件,分别命名为time.c和time.h,然后根据前两篇博客的步骤给添加到工程中去,具体步骤在这我就不再赘述了。这次我们把所有的初始化函数都放在time.c文件里面,也就是说不用再建led.c和led.h文件了。
首先我们对定时器3进行基本的配置,代码如下

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//注意结构体的声明必须在函数的开头
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period=arr;
TIM_TimeBaseStructure.TIM_Prescaler=psc;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);

这些代码和上篇博客中的一样,我就不再多讲,不懂的可以去翻看上篇博客(利用定时器中断控制LED)。

然后我们来配置定时器3通道2的PWM输出的工作方式。代码如下

    //配置TIM3 Channel2 PWMTIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2;TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;TIM_OC2Init(TIM3,&TIM_OCInitStructure);//注意此处的函数名字以及函数的入口参数

大家看,这还是结构体,32里面基本所有的配置都是使用的结构体。我们首先来看这个结构体中的第一个成员变量 TIM_OCMode,这个变量是设置PWM的工作模式,关于PWM的工作模式有好几种,如下

#define TIM_OCMode_Timing                  ((uint16_t)0x0000)
#define TIM_OCMode_Active                  ((uint16_t)0x0010)
#define TIM_OCMode_Inactive                ((uint16_t)0x0020)
#define TIM_OCMode_Toggle                  ((uint16_t)0x0030)
#define TIM_OCMode_PWM1                    ((uint16_t)0x0060)
#define TIM_OCMode_PWM2                    ((uint16_t)0x0070)

我们这里选择模式2,其他的模式我也不清楚,我知道模式2和模式1的区别就是,他俩的有效电平是相反的,关于有效电平的概念我一会就会提到。

关于PWM输出
PWM Mode 1:
向上计数:TIMx_CNT<TIMx_CCR1 active ;TIMx_CNT>=TIMx_CCR1 inactive
向下计数:TIMx_CNT>TIMx_CCR1 inactive ;TIMx_CNT<=TIMx_CCR1 active
PWM Mode 2:
向上计数:TIMx_CNT<TIMx_CCR1 inactive ;TIMx_CNT>=TIMx_CCR1 active
向下计数:TIMx_CNT>TIMx_CCR1 active ;TIMx_CNT<=TIMx_CCR1 inactive

这里我们选择模式2,那么代码就是

TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2;

然后这个结构体的第二个成员就是设置输出极性,也就是有效电平。假如说我们设置输出极性(有效电平)是高电平,对于模式2来说,当计数器的值小于你设定的阈值(上面那个例子中的30)的时候,PWM波对应的输出引脚就会输出无效电平,也就是低电平,当计数器的值大于你设定的阈值的时候,PWM波就会输出有效电平,也就是高电平;而对于模式1来说,则正好相反,当计数器的值小于你设定的阈值(上面那个例子中的30)的时候,PWM波对应的输出引脚就会输出有效电平,也就是高电平,当计数器的值大于你设定的阈值的时候,PWM波就会输出无效电平,也就是低电平;
这里我们选择输出极性(有效电平)为高电平,代码如下

TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;

然后这个结构体的第三个成员就是选择输出状态是使能或者不使能,我们选择使能,代码如下

TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;

到这就把PWM波的工作方式配置完了,然后就是把这些成员利用下面这个函数给导入到相应的寄存器中,代码如下

TIM_OC2Init(TIM3,&TIM_OCInitStructure);//注意此处的函数名字以及函数的入口参数

在这我们需要注意一下这个函数的名字TIM_OC2Init,它里面的2就代表着配置的是定时器的第二个通道,然后第一个参数就代表着配置的是定时器3,第二个参数就是刚才配置的结构体,总的来说就是配置了定时器3的第二个通道,如果要用到定时器3的其他通道,则把这个函数名字中的2改成对应的通道数就行。
定时器配置完了,接下来我们就要开始配置这次所用到的GPIO口了,代码如下

GPIO_InitTypeDef GPIO_InitStructure;//注意结构体声明必须放在函数的开头
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);

程序的第一行同样是定义一个结构体,然后第二行是开启GPIOB的时钟,然后第三行是开启复用功能的时钟,关于复用功能我简单说一下。我们今天想实现的是用定时器3的通道2来控制LED的亮度,在我使用的开发板上,今天用到的LED是接在PB5上,然而定时器3的通道2所产生的的PWM波却不是通过PB5引脚来输出的,所以说我们就需要开启32的复用功能,我么可以把定时器3的通道2 的PWM波输出给映射到PB5引脚上,这就可以实现让PWM波从PB5引脚上输出了,(重映射的引脚不是随意的,只能映射到固定的引脚)关于复用功能具体的大家可以百度查询。
然后程序的第四行就是配置GPIO口的工作模式,我们这里选择复用推挽输出模式。然后程序的第五、六、七行就不多说了。

这些都配置完之后,我们要再调用一个函数,如下

    //把TIM3部分重映射到PB5GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);

这个函数就是实现了把定时器3给重映射到了PB5引脚上。

然后我们用到了定时器3,所以就需要开启定时器3的时钟,代码如下

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

我们今天没有用到定时器中断,所以就不用再使能定时器中断的时钟了。
到此为止,我们time.c文件的程序都已经写好了,完整代码如下

#include "time.h"
void time3_PWM_init(u16 arr,u16 psc)
{//配置TIM3TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//注意结构体的声明必须在函数的开头TIM_OCInitTypeDef TIM_OCInitStructure;GPIO_InitTypeDef GPIO_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseStructure.TIM_Period=arr;TIM_TimeBaseStructure.TIM_Prescaler=psc;TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);//配置TIM3 Channel2 PWMTIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2;TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;TIM_OC2Init(TIM3,&TIM_OCInitStructure);//注意此处的函数名字以及函数的入口参数//配置GPIO口,并且设置成复用功能RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);//把TIM3部分重映射到PB5GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);//使能TIM3时钟TIM_Cmd(TIM3,ENABLE);
}

接下来开始写time.h文件,还是只需要把time.c文件中所定义的函数给声明一下就行,代码如下

#ifndef __TIME_H
#define __TIME_H#include "sys.h"void time3_PWM_init(u16 arr,u16 psc);#endif

然后我们就开始写main.c文件中的程序了。先把全部代码贴出来

#include "sys.h"
#include "delay.h"
#include "time.h"int main(void)
{u16 led0pwm=0;u8 dir=1;delay_init();time3_PWM_init(899,0);while(1){delay_ms(10);if(dir)led0pwm++;else if(!dir)led0pwm--;if(led0pwm>=300)dir=0;else if(led0pwm==0)dir=1;TIM_SetCompare2(TIM3,led0pwm);}
}

在main.c文件的刚开始我们还是先写需要调用的头文件,因为我们今天主函数里面用到了延时函数,所以要添加头文件”delay.h”,调用的头文件如下

#include "sys.h"
#include "delay.h"
#include "time.h"

然后我们在主函数中定义了两个变量:led0pwm和dir,其中led0pwm是当做PWM波输出的那个阈值(就是前面举的例子中的那个30)的,然后变量dir是用来控制led0pwm的增长方向,以便实现呼吸灯的效果。然后我们还用到了另外一个重要的函数

TIM_SetCompare2(TIM3,led0pwm);

这个函数有两个入口参数,第一个参数就是选择对哪个定时器进行操作,第二个参数就是设置PWM波输出的那个阈值,我们在主函数中每隔10ms对led0pwm改变一次数值,也就是PWM波的那个阈值一直的在变化(每隔10ms变化一次),而每一个阈值对应的LED的发光亮度都会不一样,对于我们今天设定的工作方式来说,阈值越大,LED亮度越大,阈值越小,LED亮度越小。(因为我们刚才配置的PWM波工作模式为模式2,然后输出有效电平是高电平,而对于本人使用的开发板来说,给PB5引脚高电平,LED熄灭,给PB5引脚低电平,LED点亮,所以说当计数器的值小于led0pwm的时候,PWM波输出为无效电平,也就是低电平,这个时候LED点亮,而当计数器的值大于led0pwm的时候,PWM波输出有效电平,也就是高电平,这个时候LED熄灭,所以说我们的led0pwm的值越小,PWM波输出低电平的时间就越短,LED的亮度就越小,led0pwm的值越大,PWM波输出低电平的时间就越长,LED的亮度就越大)
综上所述,一直改变led0pwm的值,就可以一直改变LED的亮度,LED就会实现从暗到亮,然后从亮到暗,然后再从暗到亮,这样一直循环下去,就实现了呼吸灯的效果。
至此,本次用定时器3输出PWM波控制LED灯的实验就结束了,把代码烧录到开发板中,就可以看到正点原子精英版的开发板上DS0的亮度一直的在变化。
注意主函数中的那一条延时语句 delay_ms(10) 不能省略,否则我们将看不到LED的亮度变化,即我们不能让led0pwm的值变化的太快。

STM32之PWM波相关推荐

  1. stm32+定时器PWM波+电机驱动+直流电机

    利用stm32单片机控制直流电机. 硬件部分:stm32f103c8t6.TB6612电机驱动模块.直流减速电机 首先搞明白原理.例如一个12v的直流电机,在其两端接上12v的电压,电机会满额转动,转 ...

  2. 编写stm32输出PWM波的代码

    首先,你需要先定义 STM32 的 GPIO 引脚,然后配置 PWM 频率,最后启动 PWM 输出.具体的代码如下:GPIO_InitTypeDef GPIO_InitStructure;TIM_Ti ...

  3. STM32:利用PWM波控制飞盈电调过程和注意事项

    STM32:利用PWM波控制电调过程和注意事项 在进行模型控制的过程中,如四旋翼无人机等,需要用到电机,这些电机需要通过电调来控制电机的转速.在电调模块中带有的说明书一般都是利用遥控器进行控制,有些情 ...

  4. STM32单片机一个定时器输出不同频率PWM波

      在使用STM32单片机输出PWM波形的时候,通常可以直接使用定时器提供的PWM模式.可以通过自动重装载寄存器(TIMx_ARR)来设置定时器的输出频率,然后通过捕获/ 比较寄存器 1(TIMx_C ...

  5. STM32单片机:定时器TIM输出PWM波

    学习32单片机过程中使用的工具:MDK Keil5 + 正点原子精英版(STM32F103ZET6)+ STM32CubeMX + HAL开发 一.PWM的产生原理 关于PWM的产生原理网上有很多教程 ...

  6. STM32一个定时器同时捕获4路PWM波

    问题的提出: 最近需要用航模遥控器控制遥控车,32单片机做主控,需要用到4个通道即需要捕获4路PWM波.如果用四个定时器来捕获四路PWM波,就太浪费资源了.由于STM32单片机的定时器资源有限,故设想 ...

  7. 使用STM32测量脉宽可变的PWM波的脉冲宽度

    最近受疫情影响导致我莫得办法出去玩,打游戏一不小心又给打通关了就只能找点东西玩玩了,所以就有了下面这篇文章......搞这个东西的时候遇见一些好玩的问题,我写在第6部分,希望能帮到看到这篇小文章的同志 ...

  8. STM32 cubeMX下配置STM32单片机使无源蜂鸣器输出PWM波并放音乐

    1.首先我们要了解定时器(timer) timer分为三种:高级定时器(tim1,tim8),基本定时器(tim6,tim7),通用定时器(tim2,tim3,tim4,tim5). 基本定时器:它有 ...

  9. STM32学习日记3---定时器TIM3重映射PWM波呼吸灯实验

    文章目录 1. 代码 关于PWM波的输出,我们需要用到定时器的4个通道(通用定时器和高级定时器有,基本定时器没有这4个通道)里面的PWM生成.然后查看数据手册,了解定时器的重映射引脚情况.所以配置时, ...

最新文章

  1. listview显示mysql数据_C#在listview控件中显示数据库数据
  2. 最小径集的算法_如何为数据集选择正确的聚类算法?
  3. Java面试题之有没有有顺序的Map实现类,如果有,他们是怎么实现有序的?
  4. 20级:班级日常分享,一天一瞬间
  5. OpenCV学习笔记:矩阵的掩码操作
  6. python图像处理模糊_Python+OpenCV图像处理之模糊操作
  7. Ngnix笔记proxy_set_header设置X-Real-IP(Java获取客户端IP地址)
  8. 微课|中学生可以这样学Python(例11.2):tkinter猜数游戏(2)
  9. C# 调用C/C++动态链接库,结构体中的char*类型
  10. python中的sorted是什么意思_Python中sorted()排序与字母大小写的问题
  11. 06. Django基础:GET请求和POST请求
  12. 20141124-1
  13. 解决在 WP8/ WP8.1 项目中 引用 C++ 组件时出现的 System.TypeLoadException 错误
  14. 定时重启php,linux系统定时重启
  15. (2019.12.21已解决)pdf有密码如何打印
  16. simulink积分器报错
  17. 使用 Jib 生成 Java Docker 镜像
  18. Gamecenter 测试失败的解决方案
  19. APP内打电话的小功能
  20. 云计算运维:运维人员常用到的11款服务器监控工具

热门文章

  1. Chart控件X轴显示不全的解决方法
  2. Linux_异常_01_CentOS7无法ping 百度
  3. 【方案分享】2021快手品牌号专项营销方案.pdf(附下载链接)
  4. leetcode力扣338. 比特位计数
  5. 腾讯专家详解Angel平台实操技巧,助你比赛一马当先!
  6. 冯仕堃:预训练模型哪家强?百度知识增强大模型探索实践!
  7. Airbnb搜索:深度学习排序算法如何进化?
  8. Leetcode每日一题:649.dota2-senate(Dota2参议院)
  9. Leetcode每日一题:514.freedom-trail(自由之路)
  10. Stanford CS230深度学习(三)调参、正则化和优化算法