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

本文主要是介绍STM32如何实现可调频率、占空比的PWM波形,且可指定输出脉冲个数?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

640?wx_fmt=jpeg

读者朋友“*imYan*”问:

pwm实现频率可调和占空比可调后怎么来实现输出10个脉冲呢?我这边看有门控或者单脉冲加重复计数,黄老师平时用的什么方法?

我的回答:

使用两个TIM定时器:一个输出可调频率、占空比的PWM,一个对输出PWM脉冲计数(计时)。

1.门控方式能实现,但需要复杂的配置和计算,不推荐。

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

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

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

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

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

·

·

·

STM32定时器

STM32的TIM定时器少则五六个,多则二十个。 可能许多初学者觉得:那么多定时器用的完吗? 那么多不是浪费吗?  

这么说吧,STM32的定时器功能非常强大,之所以有那么多定时器,原因在于使用定时器的地方有许多,本文要讲的这个例子只是很基础的一个例子。

当然,可能很多人想问:利用阻塞延时,控制IO高低变化输出PWM这种方式就行啦,也很简单。其实,这种方法的弊端很大。

1.输出的PWM可能存在误差;

2.对整个系统的实时性可能有影响;

·

·

·

所以不建议使用该方法。

几种实现方法

 使用两个定时器配合输出可调频率、占空比的PWM波形,且可指定输出脉冲个数的方法和原理其实不难。

输出PWM的方法就是使用TIM定时器自带有的PWM模式即可完成。主要难点在于还要控制指定输出脉冲的个数。

对于如何控制输出指定脉冲个数,下面大概说下三种方法:

1.脉冲中断计数法

IO中断,或者定时器同步(脉冲)中断。

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

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

它的原理,大致如下图:

640?wx_fmt=png

此方法建议在输出高频PWM时不要使用,频繁中断对系统实时性也是有一定影响。 建议低于1KHz的PWM才使用此方法。

2.定时中断法

基于上面第一种,不适合高频PWM脉冲中断。经过思考,我们是否可以将多次中断的时间累加,只响应一次中断。

原理就是把定时的时间设定为单个脉冲的n倍(n个脉冲),只使用一次中断。

它的原理,大致如下图:

640?wx_fmt=png

看图片中的提示,建议这个地方使用一个32位的定时器,这个值可能很大。


3.脉冲触发法

此方法可以避免上面两种方法中不足的地方, 相对上面两对实用性更强。电路上面,需要将PWM输出的波形,连接到另一个定时器的ETR引脚。

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

640?wx_fmt=png


下面章节我就以该方法(第3种方法,PWM波形作为定时器的输入时钟的方式,用代码给大家讲述一下。

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

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

640?wx_fmt=png

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

1.输出PWM配置

/************************************************
函数名称 : PWM_TIM_Configuration
功    能 : PWM输出定时器配置
参    数 : 无
返 回 值 : 无
作    者 : strongerHuang
*************************************************/
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);/* PWM模式配置 */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.选择外部时钟,定时中断配置

/************************************************
函数名称 : CNT_TIM_Configuration
功    能 : 计时定时器配置
参    数 : 无
返 回 值 : 无
作    者 : strongerHuang
*************************************************/
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配置 */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函数接口

/************************************************
函数名称 : PWM_Output
功    能 : 输出PWM
参    数 : Frequency --- 频率Dutycycle --- 占空比(12代表占空比为12%)NumPulse  --- 脉冲个数
返 回 值 : 无
作    者 : strongerHuang
*************************************************/
void PWM_Output(uint32_t Frequency, uint32_t Dutycycle, uint32_t NumPulse)
{uint32_t pwm_period;uint32_t pwm_pulse;/* 输出PWM */pwm_period = PWM_CK_CNT/Frequency - 1;                             //计算出计数周期(决定输出的频率)pwm_pulse  = (pwm_period + 1)*Dutycycle / 100;                     //计算出脉宽值(决定PWM占空比)TIM_Cmd(PWM_TIMx, DISABLE);                                        //失能TIMTIM_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输出了。中途不用软件参数,输出结束时自动响应定时中断,关闭定时器。

中断接口函数

/************************************************
函数名称 : CNT_TIM_IRQHandler
功    能 : 计时中断
参    数 : 无
返 回 值 : 无
作    者 : strongerHuang
*************************************************/
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);                  //关闭计数}
}

实际效果和代码

为方便大家,提供了一个简单裸机程序:

int main(void)
{System_Initializes();  while(1){LED_TOGGLE();                 //LED变化Delay(5);                     //延时(约240ms)PWM_Output(1000, 20, 10);     //1KHz, 20%占空比, 10个脉冲}
}

main函数中实现效果:间隔240ms(软件延时不精确)输出10个PWM波形

640?wx_fmt=png

波形具体情况:输出1KHz, 20%占空比, 10个脉冲精确的PWM波形

640?wx_fmt=png

下载地址(STM32F401为例工程,STM32其他芯片类似):

链接:https://pan.baidu.com/s/10GPPxCky8SZmU9S9pleqJg 

密码:4jf3

推荐阅读:

1.关于STM32的计数和延时

2.STM32F4_TIM输出PWM波形(可调频率、占空比)

3.STM32F4_TIM输入波形捕获(脉冲频率)

最后

  1. 置顶公众号,不怕找不到我;

  2. 记得给我点赞哦!

  3. 微信搜索“strongerHuang” 或者扫描下面二维码、关注,在我的底部菜单查看更多精彩内容!

640?wx_fmt=jpeg

长按识别二维码 关注


640

赞赏是对作者的认可与支持!

这篇关于STM32如何实现可调频率、占空比的PWM波形,且可指定输出脉冲个数?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/304151

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

spoj705( 求不相同的子串个数)

题意:求串s的不同子串的个数 解题思路:任何子串都是某个后缀的前缀,对n个后缀排序,求某个后缀的前缀的个数,减去height[i](第i个后缀与第i-1 个后缀有相同的height[i]个前缀)。 代码如下: #include<iostream>#include<algorithm>#include<stdio.h>#include<math.h>#include<cstrin

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

XTU 1233 n个硬币连续m个正面个数(dp)

题面: Coins Problem Description: Duoxida buys a bottle of MaiDong from a vending machine and the machine give her n coins back. She places them in a line randomly showing head face or tail face o

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、