输出频率可调的方波(占空比不可调,默认为50%)
使用定时器来产生PWM波大家应该都很熟悉,但是如何使用定时器产生频率可调的PWM波呢?这就需要使用定时器的输出比较翻转模式TIM_OCMode_Toggle
先大概介绍一些这个模式是如何工作的:
输出比较翻转,顾名思义,就是程序运行的过程中,会一直拿计数器的当前计数值和比较寄存器中的值进行比较,如果当前计数值和比较寄存器中的值相等了,那么就会产生输入/捕获中断,然后定时器会自动地翻转当前的电平输出状态,然后再往比较寄存器中加上一个定值(也就是程序中的CCR1_ValCCR2_Val),让计数器中的值继续去追赶更新后的比较寄存器中的值,以使得定时器还是相隔相同的时间去产生下一次输入捕获中断。
先来看下定时器以及PWM波输出配置程序:

void PWMOut_Init_Adjust_Fre(void)//输出两路频率可调的PWM波
{TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_OCInitTypeDef  TIM_OCInitStructure;NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);/* GPIOA and GPIOB clock enable */RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);/* Time base configuration */TIM_TimeBaseStructure.TIM_Period = 65535;//必须是这个数值65535TIM_TimeBaseStructure.TIM_Prescaler = 71;//72分频,计数频率为1MHzTIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);/* Output Compare Toggle Mode configuration: Channel1 */TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;//输出比较翻转模式TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = CCR1_Val;//设置通道1的比较值(跳变值),也就是比较寄存器的初始值TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;TIM_OC1Init(TIM3, &TIM_OCInitStructure);TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable); //使能或者失能TIMx在CCR1上的预装载寄存器/* Output Compare Toggle Mode configuration: Channel2 */TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = CCR2_Val;//设置通道2的比较值(跳变值),也就是比较寄存器的初始值TIM_OC2Init(TIM3, &TIM_OCInitStructure);TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);TIM_Cmd(TIM3,ENABLE);TIM_ITConfig(TIM3,TIM_IT_CC1 | TIM_IT_CC2,ENABLE);
}

上面这个配置程序完成的工作就是配置定时器得到基本参数,包括计数周期、计数计数频率以及PWM工作的模式等等。
下面再来看一下中断服务函数我们都干了啥:

void TIM3_IRQHandler(void)
{if(TIM_GetITStatus(TIM3,TIM_IT_CC1) != RESET)//TIM_IT_CC1为捕获中断{TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);capture = TIM_GetCapture1(TIM3);//获取当前计数器中的值TIM_SetCompare1(TIM3,capture + CCR1_Val);//每次都让比较寄存器再加上一个翻转周期,以保证每次都是相同的翻转周期,TIM_SetCompare1()函数的作用就是把参数传递给比较寄存器当中}if(TIM_GetITStatus(TIM3,TIM_IT_CC2) != RESET){TIM_ClearITPendingBit(TIM3,TIM_IT_CC2);capture = TIM_GetCapture2(TIM3);TIM_SetCompare2(TIM3,capture + CCR2_Val);}
}

首先我们要明白这个中断是如何产生的,if(TIM_GetITStatus(TIM3,TIM_IT_CC1) != RESET)代表的就是捕获/比较中断,也就是意味着计数器中的值和当前比较寄存器中的值相等的时候,就会产生这样一个捕获/比较中断。产生中断之后,我们首先要清零中断标志位TIM_ClearITPendingBit(TIM3,TIM_IT_CC1);
然后用一个全局变量capture去获取当前计数器当中的值,然后下一步就是重要的一步了,下一步就是更新比较寄存器中的值,那么到底更新成多少,才能保证再隔相同的时间产生下一次捕获/比较中断呢?因为计数值是一直向上计数的,所以为了使下一次计数值还能和比较寄存器中的值相等,那肯定比较寄存器的值也要增加,为了使每次产生捕获/比较中断都是相同的时间间隔,所以我们就需要把当前计数器中的值加上CCR1_Val重新赋值给比较寄存器,也就是每次都得让更新后的比较寄存器中的值比当前计数器中的值大上一个变量CCR1_Val的值,也就是这一句代码TIM_SetCompare1(TIM3,capture + CCR1_Val);
如果还不理解,我再举一个简单的小栗子:
假如现在计数器的值为0,比较寄存器中的值为600(即CCR1_Val初始化为600),此时对应的PWM输出引脚输出低电平,然后计数器开始向上计数,0,1,2,3,…,597,598,599,600,当计数器一直加到600的时候,也就是当计数器中的值与比较寄存器中的值相等的时候,就会产生一个捕获/比较中断,在中断函数里面,我们要做的就是先获取当前计数器中的值,也就是capture = TIM_GetCapture1(TIM3);,即当前的capture =600。然后,在把新的值capture + CCR1_Val,也就是600+600重新赋值给比较寄存器中,也就是TIM_SetCompare1(TIM3,capture + CCR1_Val),赋值完成之后,此时计数器中的值还是600,而比较寄存器中的值更新为600+600=1200。此时定时器会自动翻转对应的PWM输出引脚的电平状态,即翻转为高电平。然后退出中断服务函数,计数器计数开始往上计数,600,601,601,…,1198,1199,1200,当计数器加到1200的时候,计数器的值又和比较寄存器中的值相等了,即又会产生一次捕获/比较中断,又进入到了中断服务函数,还是重复刚才的动作,首先清零中断标志位,然后获取当前计数器中的值capture = TIM_GetCapture1(TIM3);,即当前的capture =1200。然后再把当前新的值capture + CCR1_Val,也就是1200+600重新赋值给比较寄存器中,也就是TIM_SetCompare1(TIM3,capture + CCR1_Val),赋值完成之后,此时计数器中的值还是1200,而比较寄存器中的值更新为1200+600=1800。此时定时器仍然会自动翻转对应的PWM输出引脚的电平状态,即翻转为低电平,然后退出中断服务函数,计数器计数向上计数,然后等待着下一次计数器中的值与比较寄存器中的值1800相等时,又会产生一次捕获/比较中断,在中断里面还是重复刚才的几个动作,即清零中断标志位、获取当前计数值器中的值以及更新比较寄存器中的值,就这样,一直循环下去,就可以产生占空比为50%的方波。
注意在上面的过程中,变化的是计数器中的值和比较寄存器中的值,不变的是变量CCR1_Val,在上面的例子中CCR1_Val一直等于600,也就是这个CCR1_Val控制着PWM波的频率。假如说计数频率为1MHz,也就是1us计一个数,那么PWM波输出的周期就是CCR1_Val*2us,因为占空比为50%,所以一个PWM波的周期内高低电平的时间均为CCR1_Val。假如说CCR1_Val=1000,那么PWM波周期就是1000*2us=2ms,也就是500Hz。然后我们通过调节这个CCR1_Val的值,就可以实时的改变PWM波输出的频率了。

再来补充一下,如果比较寄存器的值加的过程超过了65535怎么办:
结果就是,超过了也没事,反正计数器和比较寄存器中的值都是最大到65535。假如说现在比较寄存器和计数器相等了,并且都等于65530,然后比较寄存器又加上了一个数100,那么他现在的值就是94(超过65535之后,又从0开始),那么计数器也是从65530开始一个一个往上加的,它的计数过程也是65530,65531,…65535,0,1…94,也就是说超过了65535之后,他俩由于是同样的计数规则,所以说并不影响每次产生中断的时间间隔。

下篇博客介绍频率可调、占空比可调的PWM波输出
参考博客:
1、输出频率可调、占空比可调的PWM波
https://blog.csdn.net/qq_36554582/article/details/88225484
2、蓝桥杯嵌入式比赛备赛手册
https://blog.csdn.net/Zach_z/article/details/80548423

如何使用定时器产生两路频率可调的PWM波相关推荐

  1. 如何使用定时器产生两路频率可调、占空比可调的PWM波

    接上次的博客,上一篇博客介绍的是产生两路频率可调.占空比固定为50%的方波,但是更多情况下也需要调节占空比,这篇博客简单介绍下.其实和频率可调的输出基本上是一样的,只需要再加上一个Duty来控制CCR ...

  2. 产生频率或者占空比可调的PWM波【汇编语言】

    PWM在各个编程中都非常的重要,在学习32单片机,FPGA等中都有学习到,本次文章为介绍汇编语言编写频率或占空比可调的PWM波,从更加底层的方面介绍和去了解pwm波生成的过程,基于S3C2410X/S ...

  3. STM8L051F3P6TR 定时器2两路PWM输出+死区控制+端口重映射PC5PC6

    使用定时器2的通道1.2输出PWM CLK_PeripheralClockConfig(CLK_Peripheral_TIM2,ENABLE);//打开串口时钟源TIM2_DeInit();TIM2_ ...

  4. STM32用一个定时器输出多路不同频率及占空比的PWM(输出比较模式)

    我们使用STM32输出PWM时会使用定时器的PWM输出模式来进行生成,但是这样子生成PWM是有局限的,它只能生成四路频率相同的PWM,当你设定了TIMx_PSC(预分频寄存器)和TIMx_ARR(自动 ...

  5. 51--可调频率和占空比的PWM波

    可通过period_div和duty这两个输入信号控制PWM波占空比 module PWM(input clk,input rst_n,input [6:0]duty,input [12:0]peri ...

  6. 如何使用定时器捕获一路PWM波信号的频率和占空比

    本次实验将采用定时器2的通道2产生两路频率和占空比均可调的PWM信号,然后使用定时器3的通道1来捕获其中的一路PWM波的频率和占空比. 1.首先来看下产生PWM波的程序,也就是和上篇博客是一样的,只不 ...

  7. stm32f4 用一个定时器输出多个不同频率占空比PWM波(含代码)

    之前有写过怎么使用定时器生成PWM波,以及怎么修改频率与占空比,具体大家可以看下面这篇 stm32f4 生成PWM波_居安士的博客-CSDN博客_stm32产生pwm波 STM32每一个定时器都有4路 ...

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

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

  9. STM32F7同一定时器多路输出PWM波通道之间相互影响问题

    --------------------------2020/8/12更新-------------------------------- 这次用Cube直接生成PWM控制代码,然后再RT-Threa ...

最新文章

  1. Windows进程与线程学习笔记(五)—— 模拟线程切换
  2. javascript初级代码块
  3. Boost:bind绑定的回归测试
  4. [JavaWeb-HTML]HTML概念介绍和快速入门
  5. mysql还原txt表的字段结构,mysql 修改表结构(转)
  6. A-Graph Games_2019牛客暑期多校训练营(第三场)
  7. aba问题mysql_面试题总结:可能是全网最好的MySQL重要知识点
  8. 4008-基于邻接矩阵的新边的增加(C++,附思路)
  9. java异常错误的是,java 异常 错误处理
  10. 高效办公,从几行批处理命令开始 | 原力计划
  11. 汉威电子持续发力智慧城市产业链
  12. 引号 解析 逗号_笔试积累 | 军队文职公共科目真题解析18
  13. ABP理论学习之发布说明
  14. 扫地机器人半湿拖布_告别干湿不均,懂湿拖的自动扫地机器人最在行
  15. Angular:升级Angular 13到Angular 14
  16. 法国数学到底有多厉害?
  17. vxworks下的脚本script创建和使用(相当于windows下的autoexec.bat)
  18. 表格内容单/多行展示(一)- 单行/多行显示的方法
  19. POSIX标准是什么?
  20. wiki服务器网页地址,搭建个人wiki站点

热门文章

  1. mySAP标准培训教材全套列表
  2. 解决python2.7.9以下版本requests访问https的问题
  3. springboot mybatis常见异常及处理方法
  4. 【报告分享】2020年小红书内容生态报告.pdf(附下载链接)
  5. 【报告分享】人工智能2020:落地挑战与应对.pdf(附下载链接)
  6. 2015-2020年各类国际会议与期刊基于图像的三维对象重建论文综述(7)——Datasets
  7. python编译:setup.py添加.h头文件或者库的搜索路径
  8. qtitanribbon注册_Qt组件QtitanRibbon教程:如何运用office 2016类
  9. 建站+流量+运营,跨境电商的2021
  10. linux++php无法解析,怎么解决linux php无法解析的问题