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

读者朋友“*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)之后,关闭输出。
它的原理,大致如下图:
此方法建议在输出高频pwm时不要使用,频繁中断对系统实时性也是有一定影响。建议低于1khz的pwm才使用此方法。
2.定时中断法
基于上面第一种,不适合高频pwm脉冲中断。经过思考,我们是否可以将多次中断的时间累加,只响应一次中断。
原理就是把定时的时间设定为单个脉冲的n倍(n个脉冲),只使用一次中断。
它的原理,大致如下图:
看图片中的提示,建议这个地方使用一个32位的定时器,这个值可能很大。
3.脉冲触发法
此方法可以避免上面两种方法中不足的地方, 相对上面两对实用性更强。电路上面,需要将pwm输出的波形,连接到另一个定时器的etr引脚。
它的原理没什么特殊的,就是和我们常用的定时更新中断类似,只是输入信号改成pwm脉冲波形(默认为内部时钟ck_int 如:36m)。
下面章节我就以该方法(第3种方法),pwm波形作为定时器的输入时钟的方式,用代码给大家讲述一下。
ⅲ外部时钟源模式2实现方法
上面说过,使用pwm作为另一个定时器的输入时钟,即可达到对pwm计数的功能。
请参看手册中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); //失能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输出了。中途不用软件参数,输出结束时自动响应定时中断,关闭定时器。
中断接口函数
/************************************************函数名称 : 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波形
波形具体情况:输出1khz, 20%占空比, 10个脉冲精确的pwm波形
下载地址(stm32f401为例工程,stm32其他芯片类似):
链接:https://pan.baidu.com/s/10gppxcky8szmu9s9pleqjg
密码:4jf3

华为Mate30Pro详细配置曝光 搭配8GB+512GB存储并内置4500mAh电池
传明年特斯拉D1芯片在台积电投片量将增至1万片
红米4高配与红米note4x不知道怎么选?细数两者的不同点
中芯国际在12nm工艺上获得新突破
如何解决电厂环境下的烟道腐蚀问题
STM32如何实现可调频率、 占空比的PWM波形,且可指定输出脉冲个数?
AI视觉分析技术在城和市农村电动自行车违法管理中的应用
灰尘传感器/粉尘传感器DSM501的特点及应用分析
风光互补监控供电系统—石油管道应用
Window 10操作系统的v1999和1903受追捧,占比已接近80%
汇总:这些年开拓海外的中国公司都吃了哪些亏
最麻烦的PLL杂散信号——整数边界杂散
圆柱式高能量锂亚电池ER10450
vivoZ6三款配色公布 将支持44W超快闪充
基于STR-6无线数据传输模块实现开放式数控系统的设计
多维科技推出TMR3016和TMR3017角度传感器芯片
PlanetSpark推出基于Xilinx的AI边缘计算盒子
TIDA-01579高效低波纹输出电源参考设计
小米计划将在印度推出Redmi 9 Power手机
什么是混合光纤/同轴电缆