STM32学习笔记(白话文理解版)—搞懂PWM输出

STM32学习笔记(白话文理解版)—搞懂PWM输出

一、前言:为啥用PWM输出

PWM输出就是输出源源不断的方波信号,方波的大小我们可以控制,以此来输出高低电平,来驱动控制各种外设,如LED灯,舵机等。

  • 这是我们一定会有疑问?我用普通的IO口不也能输出高低电平嘛,就像最开始的点灯程序,我们只要给一个高电平,延迟一会,在给低电平不也能达到相同的效果?

  • 这是因为,延时函数是要占用单片机本身CPU工作时间的,而使用脉宽调制(PWM)就不用占用单片机的CPU了,可以同时处理其他事务,极大的提高了系统的工作效率

二、什么是舵机

通过输入平均电压的大小来控制旋转角度的小设备

  • 如何向舵机输入大小不同的电压呢,这是便需要让单片机输出PWM波

在这里插入图片描述
在这里插入图片描述

三、脉冲宽度调制(PWM)

定义
通过改变一个周期内高低电平的时间比例从而达到平均电压变高变低的原理就是脉冲宽度调制

基本原理

  • 脉宽调制有一个完整的周期
  • 在周期当中既有高电平又有低电平
  • 高低电平的比例不断变化可以调节(高电平多点,低电平就少点)
  • 且这样的周期会不断循环下去

别名:脉宽调制、占空比
应用:利用微处理的数字输出来对模拟电路进行控制的一种技术(解释:单片机产生高低电平,目的是对模拟电路"eg:舵机"进行控制)
PWM

四、PWM的产生

PWM可由STM32中的定时器产生,包括1个高级定时器TIM1和3个普通定时器TIM2、TIM3、TIM4

  • 什么是通用定时器呢?
    • 定时器本质上是一个计数器
    • STM32共有4个定时器(TIM1、TIM2、TIM3、TIM4)
    • 计数器可以从0开始累加,也可以从一个设定数值递减
    • 每隔一个固定的时间计数器的值加1或减1
    • 固定的时间是主晶振频率的周期
    • 当加到(或减到)到头时,会产生一个溢出信号,并将计时值清0重新计时
  • 每个定时器都有:

    • 16位自动加载递加/递减计数器

    • 16位预分频器

    • 4个独立通道(TIMx_CH1~4)可用来

      ① 输入捕获
      ② 输出比较
      PWM 生成(边缘或中间对齐模式)
      ④ 单脉冲模式输出

  • 定时器如何产生脉宽调制(PWM)的呢?

    只要在程序中配置一下两个值就能让定时器自动产生固定频率的脉宽调制信号,从而控制舵机的角度

    • 利用定时器的溢出值ARR可产生PWM的完整周期
    • 利用定时器的变化值CCRx可以决定一个周期内高低电平比例

CNT代表计数值

五、配置PWM不得不面对的问题

(1)定时器的计数模式

定时器计数模式

  • 定时器根据计数模式,例如向上计数,计数器从0到达ARR自动加载值后,就会立即清零,从0开始重新计数到ARR,并且产生一个计数器溢出事件,其余同理
  • 在下文步骤四中配置定时器时,我们要根据需求配置相应的计数模式

(2)定时器的工作模式

模式
这里我们只用到了PWM1、PWM2模式,下面介绍两种模式的区别
由图解给出:
在这里插入图片描述
其中有效电平是我们自行设置的,在步骤五中根据TIM_OCInitStrue.TIM_OCPolarity配置高电平为有效电平,还是低电平是有效电平

六、定时器PWM程序控制输出PWM波

(1)实验功能

设置PB0引脚输出PWM波,实现呼吸灯的效果

(2)主函数部分

#include "stm32f10x.h"
#include "sys.h"
#include "led.h"
#include "nvic.h"
#include "delay.h"
#include "pwm.h"
int main(void)
{
	u16 ledB0pwmval=0;    
	u8 dir=1;	//标志系数
	delay_init();//延时函数初始化
	TIM3_PWM_Init(100,359); 
	//设置自动重装载值ARR,和预分频系数
	while(1) 
	{
		delay_ms(10);	 //不加delay()变化太快 肉眼看不出来
		if(dir)ledB0pwmval++; //占空比不断增加,小灯逐渐变亮
		else ledB0pwmval--;	 //占空比不断减小,小灯逐渐变暗
			
 		if(ledB0pwmval>100)dir=0;//当超过100时,标志系数变为0
		if(ledB0pwmval==0)dir=1;//当得零时 标志系数变1   					 
		TIM_SetCompare3(TIM3,ledB0pwmval);//不断改变	ledB0pwmval的值,以此来改变占空比
		//第二个参数CCR的值,设置这个值改变输出高电平的时间
		//第二个参数除以TIM3_PWM_Init()的第一个参数等于占空比
	};
}

(3)PWM初始化部分

PWM初始化步骤:
注:并不标准,纯个人主观理解,但一定能够帮助更好的理解配置PWM

  1. 使能定时器3和相关IO口的时钟,使能AFIO时钟(如需配置重映射功能)
  2. 初始化IO口为复用功能模式
  3. 根据需求,查找数据手册,确定哪个引脚为PWM的引脚,如需重映射,配置重映射
  4. 初始化定时器:ARR设置自动重装在值,PSC预分频系数
  5. 初始化定时器的通道几:(也就是初始化比较参数CCRX)
  6. 使能预装载计数器(使能第五步的配置)
  7. 使能定时器(使能步骤四的配置)
  8. 不断改变比较值CCRx,达到改变占空比的效果

过程中遇到疑问的理解:

  • 牢记配置PWM定时器需要两个参数:一个是ARR确定周期一个是CCR确定占空比
  • 初始化4配置自动重装载值ARR和PSC——确认一个周期的时间
  • 初始化5配置比较值CCRx,确定高电平和低电平分贝占一个周期的时间
  • 不仅得初始化还得使能:所以使能4:TIM_Cmd | 使能5:TIM_OC3PreloadConfig
  • 为什么要初始化IO口,因为PWM波从IO口输出
  • 配置STM32不仅得初始化各个参数,还得使能寄存器
#include "pwm.h"

void TIM3_PWM_Init(u16 arr,u16 psc){  //TIM3 PWM初始化 arr重装载值 psc预分频系数
    GPIO_InitTypeDef     GPIO_InitStrue;
    TIM_OCInitTypeDef     TIM_OCInitStrue;
    TIM_TimeBaseInitTypeDef     TIM_TimeBaseInitStrue;
    
// 1. 使能定时器3和相关IO口的时钟,使能AFIO时钟(如需配置重映射功能)
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能TIM3和相关GPIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能GPIOB时钟(LED在PB0引脚)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO时钟(定时器3通道3需要重映射到BP5引脚)
    
// 2. 初始化IO口为复用功能模式
    GPIO_InitStrue.GPIO_Pin=GPIO_Pin_0;     // TIM_CH3
    GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP;    // 复用推挽
    GPIO_InitStrue.GPIO_Speed=GPIO_Speed_50MHz;    //设置最大输出速度
    GPIO_Init(GPIOB,&GPIO_InitStrue);    //根据配置,GPIO端口初始化设置
    
//3. 根据需求,查找数据手册,确定PB0引脚为PWM的引脚,为TIM3定时器得端口3输出,切不需要重映射

//4. 初始化定时器:ARR设置自动重装在值,PSC预分频系数
    TIM_TimeBaseInitStrue.TIM_Period=arr;    //设置自动重装载值   //输出PWM波形的频率=定时器的输入频率/TIM_TImeBaseStructure.TIM_Period,eg:200 000Hz/100=2000Hz,即0.5ms一个周期
    TIM_TimeBaseInitStrue.TIM_Prescaler=psc;        //预分频系数	//不分频时APB1时钟为72MHZ 输入频率=APB1时钟/(预分频系数+1)=72 000 000Hz/360=200 000Hz =200K 
    TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up;   //计数模式:计数器向上溢出
    TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1;       //时钟的分频因子,起到了一点点的延时作用,一般设为TIM_CKD_DIV1
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStrue);        //TIM3初始化设置(设置PWM的周期)
    
//5. 初始化定时器的通道几:(也就是初始化比较参数CCRX)   
    TIM_OCInitStrue.TIM_OCMode=TIM_OCMode_PWM1;   //定时器3怎么个功能?设置成哪个模式就哪个功能,设置为PWM1模式    // PWM模式1:CNT < CCR时输出有效电平
    TIM_OCInitStrue.TIM_OCPolarity=TIM_OCPolarity_High;// 设置极性-有效电平为:高电平   //上述两个配置,决定CCR得值,到底是数值高 高电平多,还是数值少 高电平多	
    TIM_OCInitStrue.TIM_OutputState=TIM_OutputState_Enable;// 输出使能
	TIM_OCInitStrue.TIM_Pulse =0;//最开始时占空比设置为0,可根据TIM_SetCompare3 函数改变
    TIM_OC3Init(TIM3,&TIM_OCInitStrue);        //TIM3的通道3 PWM 模式设置 		//定时器3有4个独立通道,用哪个,根据STM32中文参考手册查询,用这里用TIM3的3通道
    
//6. 使能预装载计数器(使能第五步的配置)
    TIM_OC3PreloadConfig(TIM3,TIM_OCPreload_Enable);        //使能预装载寄存器

//7. 使能定时器(使能步骤四的配置)
    TIM_Cmd(TIM3,ENABLE);     //使能TIM3
    
}

(4)实验结果

PWM输出

  • 4
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OllyICE v1.10 修改 OllyICE.exe 是在cao_cong汉化第二基础上修改的。 OLLYDBG.EXE 英文修改,修改的地方与OllyICE.exe一样 注:OllyICE.exe(cao_cong汉化)配制文件Ollydbg.ini完全英文化,因此中英文本的OllyDBG可以很好地共用一个配置文件。 OllyICE这个名称来自forgot的OLLYDBG修改本。 OllyICE.exe与OLLYDBG.EXE同时做了如下修改: 1.窗口、类名等常见修改; 2.格式化字符串的漏洞[OutPutDebugString]补丁; 3.参考dyk158的ODbyDYK v1.10 ,自动配置UDD、PLUGIN为绝对路径; 4.参考nbw的"OD复制BUG分析和修正"一文,修正从内存区复制数据时,有时无法将所有的数据都复制到剪贴板的bug。 5.参考ohuangkeo“不被OD分析原因之一和修补方法”,稍改进了OD识别PE格式能力(可能仍报是非PE文件,但己可调试了)。 6.修正OllyScript.dll插件bpwm命令内存读写都中断的问题。 7.jingulong的Loaddll.exe,可以方便让OllDbg中断在dll的入口。 8.感谢DarkBul告知SHIFT+F2条件窗口显示的bug及修复。 9.感谢dreaman修复Findlabel,Findname,Findnextname三个函数处理字符串会溢出的bug。 10.改善sprintf函数显示某些浮点数会崩溃的bug,这里的修复代码直接引用heXer的代码。 11.该修改,配合HideOD插件,可以很好地隐藏OD。 12.新增实用的快捷键功能: 说明:OllyICE是OLLYDBG的一个修改本,而OLLYDBG是一个新的动态追踪工具,它将IDA与SoftICE结合起来的思想,Ring 3级调试器,非常容易上手,己代替SoftICE成为当今最为流行的调试解密工具,同时它还支持插件扩展功能,是目前最强大的调试工具。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值