STM32如何实现可调频率、占空比的PWM波形

发布者:心若澄明最新更新时间:2018-10-06 来源: eefocus关键字:STM32  可调频率  占空比  PWM波形 手机看文章 扫描二维码
随时随地手机看文章

使用两个TIM定时器:
一个输出可调频率、占空比的PWM,
一个对输出PWM脉冲计数(计时)。
1.门控方式能实现,但需要复杂的配置和计算,不推荐。

2.脉冲计数是比较实际,也是比较简单的方式;

对输出PWM脉冲计数(计时)方法有多种:

1.IO中断计数,或同步定时中断计数:用另外一个定时器,按照相同频率中断计数(类似IO中断);

2.由PWM频率和脉冲个数,计算输出全部所需的时间,使用定时中断,关闭输出PWM;

3.利用定时器外部脉冲触发(外部时钟模式2功能),计数个数为所需脉冲个数(10个脉冲),则关闭输出PWM;

STM32定时器
利用阻塞延时,控制IO高低变化输出PWM这种方式就行啦,也很简单。其实,这种方法的弊端很大。
1.输出的PWM可能存在误差;
2.对整个系统的实时性可能有影响;
所以不建议使用该方法。
几种实现方法
 使用两个定时器配合输出可调频率、占空比的PWM波形,且可指定输出脉冲个数的方法和原理其实不难。
输出PWM的方法就是使用TIM定时器自带有的PWM模式即可完成。主要难点在于还要控制指定输出脉冲的个数。
对于如何控制输出指定脉冲个数,下面大概说下三种方法:

1.脉冲中断计数法
IO中断,或者定时器同步(脉冲)中断。

定时器同步(脉冲)中断简单的说,就是利用定时器同时产生一个相同频率(或者说波形)的中断信号,在中断里面对其累计,累加个数为指定输出波形个数则关闭PWM波形的输出,同时关闭中断计数。

比如:我输出10个波形,10次中断(每次+1)之后,关闭输出。

它的原理,大致如下图:

STM32如何实现可调频率、占空比的PWM波形,且可指定输出脉冲个数?


此方法建议在输出高频PWM时不要使用,频繁中断对系统实时性也是有一定影响。 建议低于1KHz的PWM才使用此方法。
2.定时中断法
基于上面第一种,不适合高频PWM脉冲中断。经过思考,我们是否可以将多次中断的时间累加,只响应一次中断。
原理就是把定时的时间设定为单个脉冲的n倍(n个脉冲),只使用一次中断。

它的原理,大致如下图:

STM32如何实现可调频率、占空比的PWM波形,且可指定输出脉冲个数?


看图片中的提示,建议这个地方使用一个32位的定时器,这个值可能很大。
3.脉冲触发法
此方法可以避免上面两种方法中不足的地方, 相对上面两对实用性更强。电路上面,需要将PWM输出的波形,连接到另一个定时器的ETR引脚。

它的原理没什么特殊的,就是和我们常用的定时更新中断类似,只是输入信号改成PWM脉冲波形(默认为内部时钟CK_INT  如:36M)。

STM32如何实现可调频率、占空比的PWM波形,且可指定输出脉冲个数?


外部时钟源模式2实现方法

上面说过,使用PWM作为另一个定时器的输入时钟,即可达到对PWM计数的功能。

STM32如何实现可调频率、占空比的PWM波形,且可指定输出脉冲个数?


请参看手册中TIM定时器时钟选择章节。

1.输出PWM配置
void PWM_TIM_Configuration(void) {  GPIO_InitTypeDef        GPIO_InitStructure;  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  TIM_OCInitTypeDef       TIM_OCInitStructure;

   RCC_APB1PeriphClockCmd(PWM_TIM_CLK, ENABLE);  RCC_AHB1PeriphClockCmd(PWM_TIM_GPIO_CLK, ENABLE);  GPIO_InitStructure.GPIO_Pin = PWM_TIM_PIN;  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  GPIO_Init(PWM_TIM_GPIO_PORT, &GPIO_InitStructure);

   GPIO_PinAFConfig(PWM_TIM_GPIO_PORT, PWM_TIM_SOURCE, PWM_TIM_AF);
 
   TIM_TimeBaseStructure.TIM_Prescaler = PWM_PRESCALER;               //预分频值  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;        //向上计数  TIM_TimeBaseStructure.TIM_Period = 0xFFFF;                         //定时周期  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;            //分频因子  TIM_TimeBaseInit(PWM_TIMx, &TIM_TimeBaseStructure);

   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                  //PWM1模式  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;      //使能输出  TIM_OCInitStructure.TIM_Pulse = 0xFFFF;                            //脉宽值  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;          //输出极性  PWM_TIM_OCxInit(PWM_TIMx, &TIM_OCInitStructure);  TIM_Cmd(PWM_TIMx, DISABLE); }
初始化频率和占空比填充的值是最大值,即TIM_Period = 0xFFFF;TIM_Pulse = 0xFFFF; 实际没有使能定时器(输出的配置见下面函数接口)

2.选择外部时钟,定时中断配置
void CNT_TIM_Configuration(void) {  GPIO_InitTypeDef        GPIO_InitStructure;  TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;  NVIC_InitTypeDef        NVIC_InitStructure;

   RCC_APB1PeriphClockCmd(CNT_TIM_CLK, ENABLE);  RCC_AHB1PeriphClockCmd(CNT_TIM_GPIO_CLK, ENABLE);  GPIO_InitStructure.GPIO_Pin = CNT_TIM_PIN;  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  GPIO_Init(CNT_TIM_GPIO_PORT, &GPIO_InitStructure);
 
   GPIO_PinAFConfig(CNT_TIM_GPIO_PORT, CNT_TIM_SOURCE, CNT_TIM_AF);

   NVIC_InitStructure.NVIC_IRQChannel = CNT_TIM_IRQn;  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = CNT_TIM_Priority;  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  NVIC_Init(&NVIC_InitStructure);

   TIM_ETRClockMode2Config(CNT_TIMx, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0);

   TIM_TimeBaseStructure.TIM_Prescaler = 0;                           //预分频值  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;        //向上计数  TIM_TimeBaseStructure.TIM_Period = 0xFFFF;                         //定时周期  TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;            //分频因子  TIM_TimeBaseInit(CNT_TIMx, &TIM_TimeBaseStructure);  TIM_ClearFlag(CNT_TIMx, TIM_FLAG_Update);  TIM_ITConfig(CNT_TIMx, TIM_IT_Update, ENABLE);                     //使能"更新"中断  TIM_Cmd(CNT_TIMx, DISABLE); }

和常规的不同点在于: 使用外部时钟源

TIM_ETRClockMode2Config(CNT_TIMx, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0);

注意检测(捕获)极性TIM_ExtTRGPolarity_Inverted,一般PWM都是高电平为脉冲波形,下降沿才算一个波形的计数。
3.输出PWM函数接口

void PWM_Output(uint32_t Frequency, uint32_t Dutycycle, uint32_t NumPulse) {  uint32_t pwm_period;  uint32_t pwm_pulse;

   pwm_period = PWM_CK_CNT/Frequency - 1;                             //计算出计数周期(决定输出的频率)  pwm_pulse  = (pwm_period + 1)*Dutycycle / 100;                     //计算出脉宽值(决定PWM占空比)  TIM_Cmd(PWM_TIMx, DISABLE);                                        //失能TIM  TIM_SetCounter(PWM_TIMx, 0);                                       //计数清零  TIM_SetAutoreload(PWM_TIMx, pwm_period);                          //更改频率  PWM_TIM_SetComparex(PWM_TIMx, pwm_pulse);                          //更改占空比  TIM_Cmd(PWM_TIMx, ENABLE);                                         //使能TIM    TIM_Cmd(CNT_TIMx, DISABLE);  TIM_SetCounter(CNT_TIMx, 0);  TIM_SetAutoreload(CNT_TIMx, NumPulse-1);                          //设置中断更新数  TIM_ClearFlag(CNT_TIMx, TIM_FLAG_Update);  TIM_Cmd(CNT_TIMx, ENABLE); }

void PWM_Output(uint32_t Frequency, uint32_t Dutycycle, uint32_t NumPulse);

我们只需要调用该函数接口就可以实现指定个数PWM输出了。中途不用软件参数,输出结束时自动响应定时中断,关闭定时器。

中断接口函数

void CNT_TIM_IRQHandler(void) {
 if(TIM_GetITStatus(CNT_TIMx, TIM_IT_Update) != RESET)  {    TIM_ClearITPendingBit(CNT_TIMx, TIM_IT_Update);    TIM_Cmd(PWM_TIMx, DISABLE);                  //关闭PWM输出    TIM_Cmd(CNT_TIMx, DISABLE);                  //关闭计数  } }


关键字:STM32  可调频率  占空比  PWM波形 引用地址:STM32如何实现可调频率、占空比的PWM波形

上一篇:STM32F407ADC多通道+定时器触发+DMA模式设置
下一篇:关于STM32进入低功耗模式后无法烧写程序的解决办法

推荐阅读最新更新时间:2024-03-16 16:15

STM32电机方波】记录3——TIM1时基初始化配置
定时器分类 : STM32F1x 系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器。 基本定时器 TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。 通用定时器 TIM2/3/4/5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。 高级定时器 TIM1/8是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。 高级定时器TIM1的库函数: 定时器TIM1时基结构体缺省值: { TI
[单片机]
【<font color='red'>STM32</font>电机方波】记录3——TIM1时基初始化配置
结合STM32单片来讲讲结构体的运用
摘要:听说还有好多学单片机的小伙伴不会用结构体?指针和结构体是学单片机必须要掌握的,如果你C语言掌握的不牢,单片机根本学不到精髓,只能完成一些低级的项目。看得懂结构体并且能够灵活运用结构体才能说你入门了单片机。本篇将以最通俗的方式结合STM32单片来讲讲结构体的运用。解决你学完C语言、考过了计算机二级还是看不懂单片机结构体的苦恼。宝藏文章,记得点赞转发收藏。 大家知道指针和结构体是单片机的难点,所以就去学习C语言,找视频看书。 这里面每一个视频的播放量都非常高。对于单纯的学习C语言,这里讲的很清楚。看完你不禁在下面评论一句:哇!讲的真的太清楚了吧!但是等你真正的学单片机的时候,你会发现我不是学过C语言吗?计算机二级我也过了
[单片机]
STM32应用简章之外部中断
①初始化IO口为输入。 GPIO_Init(); ② 开启IO口复用时钟。 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); ③设置IO口与中断线的映射关系。 void GPIO_EXTILineConfig(); ④初始化线上中断,设置触发条件等。 EXTI_Init(); ⑤配置中断分组(NVIC),并使能中断。 NVIC_Init(); ⑥ 编写中断服务函数。 EXTIx_IRQHandler(); ⑦清除中断标志位 EXTI_ClearITPendingBit();
[单片机]
STM32复习笔记(六)看门狗
一、为什么要看门狗? 在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,由单片机控制的系统无法继续工作,会造成整个系统的陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的模块或者芯片,俗称“看门狗”(watchdog) 。 二、看门狗解决的问题是什么? 在启动正常运行的时候,系统不能复位。 在系统跑飞(程序异常执行)的情况,系统复位,程序重新执行。 三、独立看门狗概述: 1、STM32内置两个看门狗,提供了更高的安全性,时间的精确性和使用 的灵活性。两个看门狗
[单片机]
<font color='red'>STM32</font>复习笔记(六)看门狗
STM32开发 -- 启动流程
启动模式讲完了,我们知道是主闪存存储器启动的(主闪存存储器就是芯片内部的flash)。主闪存存储器被映射到启动空间(0x0000 0000),但仍然能够在它原有的地址(0x0800 0000)访问它。 接下来,再看一下它的启动流程是怎样的。 话说启动流程,我都讲过很多个了。 参看:S5PV210开发 – 启动流程 参看:DM368开发 – Bootloader 开发(转毕设) 一、回顾 首先你得知道主闪存存储器、系统存储器、内置SRAM代表什么?它们映射哪个区域。 我主要以STM32F105RC为例来讲一下。 参看:STM32F105RC 介绍 主闪存存储器:芯片内部的flash,即256K字节;从用户闪存启动,这是正
[单片机]
<font color='red'>STM32</font>开发 -- 启动流程
如何实现stm32单片机每次接上电源就进行一次重启动?
如何实现stm32单片机每次接上电源就进行一次重启动? 在嵌入式系统中,单片机的重启有时是十分必要的,它可以解决一些系统启动过程中的问题、恢复系统的正常运行、使系统更加稳定等。 然而,有时候我们需要每次接上电源就进行一次重启,也就是实现系统的自动重启。本文将给出一个思路,来实现stm32单片机每次接上电源就进行一次重启动。 一、背景知识 在嵌入式系统中,有两类重启,分别是软重启和硬重启。 1.1 软重启 软重启是指通过程序控制单片机重新开始执行,实现系统的重启。在stm32单片机中,一般采用如下代码进行软重启: ```c NVIC_SystemReset(); //调用此函数进行软重启 ``` 1.2 硬重启 硬重启是指通过硬件控
[单片机]
STM32中 warning: #61-D: integer operation result is out of range
由于编译器默认signed int即32位有符号整数类型,而1 31实际为0x80000000, 这样就有可能改写了符号位(最高位) 依此类推,(2 30),(3 29)...等都会出现编译警告问题. 解决办法为: ((unsigned int)1 31),((unsigned int)2 30),...
[单片机]
如何有效地使用串口通讯接收数据
引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? 同学A:数据来了就会进入串口中断,在中断中读取数据就行了! 中断就是打断程序正常运行,怎么能保证高效呢?经常把主程序打断,主程序还要不要运行了? 同学B:串口可以配置成用DMA的方式接收数据,等接收完毕就可以去读取了! 这个同学是对的,我们可以使用DMA去接收数据,不过DMA需要定长才能产生接收中断,如何接收不定长的数据呢? DMA简介 题外话:其实,上面的问题是很有必要思考一下的,不断思考,才能进步。 什么是DMA DMA :全称Direct Memory Access,即直接存储器访问
[单片机]
如何有效地使用串口通讯接收数据
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多精选电路图
换一换 更多 相关热搜器件
更多每日新闻
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved