蓝桥杯单片机基础之PWM(Pulse width modulation)

您所在的位置:网站首页 蓝波蓝波 蓝桥杯单片机基础之PWM(Pulse width modulation)

蓝桥杯单片机基础之PWM(Pulse width modulation)

2024-03-12 09:20| 来源: 网络整理| 查看: 265

文章目录 概念代码思路pwm的思考总结测试代码周期为20us,占空比为10%呼吸灯8个亮度等级8个亮度等级流水灯 加群

概念

对于PWM的概念这里就不过多赘述,大家可以直接查看一些文章对于pwm的介绍,下面给一段我认为比较好的说法:

脉冲宽度调制(PWM)是一种对模拟信号电平进行数字编码的方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。PWM信号仍然是数字的,因为在给定的任何时刻,满幅值的直流供电要么完全有(ON),要么完全无(OFF)。电压或电流源是以一种通(ON)或断(OFF)的重复脉冲序列被加到模拟负载上去的。通的时候即是直流供电被加到负载上的时候,断的时候即是供电被断开的时候。只要带宽足够,任何模拟值都可以使用PWM进行编码。–参考百度百科

这是一个什么意思呢?

为了后续理解的方便,我先给出PWM的两个重要指标:

周期(频率)占空比.

先来看一幅图: 下图是一个周期信号,他的周期是3.70ms,其高电平所占时间为1.85ms 我们就说这个信号的周期为3.70ms,占空比( 高电平时间/总周期时间)为50%

在这里插入图片描述 在这里插入图片描述 如果高电平是5V,低电平为0V,那么这个信号,在一个周期有一半时间是5V,一半时间是0V,它的电压可以这样计算 v = ( 5 + 0 ) ∗ 50 % = 2.5 伏 v = (5 + 0)*50\% = 2.5伏 v=(5+0)∗50%=2.5伏

那么如果我们要产生4V的电压如何算呢? 4 = ( 5 + 0 ) ∗ m 4 = (5+0)*m 4=(5+0)∗m 可以解得 m = 80 % m= 80\% m=80%,也就是说高电平需要占整个周期得4/5。这样说来我们可以通过占空比来调节输出电压的大小,但是周期的大小又对pwm来说意味着什么呢?

我们先看如下实验,周期为20us,占空比为10%

在这里插入图片描述

然后周期为20us,占空比为90%

在这里插入图片描述

然后是周期为4ms,占空比为10% 请添加图片描述

可以看到周期太长,会导致闪烁,这个很好理解,如何你把周期设置为1s,那么不久是在1s开灯,1s关灯嘛,所以说周期必须设置要够小才能有效果,根据我的测试,周期为1ms以下效果就还是不错的

代码思路

了解原理后,我们如何实现pwm呢?因为pwm涉及到定时,因此我们可以利用定时器来实现! 如果我们设置一个1us的定时器中断,那么每过1us,就会进入中断,我把这个定时器1us叫做最低步进 设pwm参数为一个结构体,其定义如下 其中cnt为计数值,max表示计数的最大值,highCnt表示高电平所占计数值

typedef struct pwm { uint cnt; uint max; uint highCnt; }pwm_t; pwm_t pwm1 = {0, 20, 2};

在定时器中断这样写:

void Timer0Handle() interrupt 1 { //如果cnt和highCnt相等,表示高电平的时间已经到了,因此这里要灭灯(也即高电平灭) if(pwm1.cnt == pwm1.highCnt) { SL(LED, 0xff); } //让cnt变量在0-max之间不断运行 if(pwm1.cnt++ 5, 10, 15, 20, 30, 40, 50, 105}; uchar ledValue = 0x00; void Timer0Handle() interrupt 1 { uchar i = 0; //让cnt变量在0-max之间不断运行 if(pwm_cnt++ if(pwm_cnt == pwm_value[i]) { //如果cnt等于对应灯的占空比了,那么这个灯就应该被关闭了 ledValue |= (1 //结束周期,注意:(如果要外部控制每个LED,那么这里就不是全部亮起了!) ledValue = maskValue; //如果cnt为0表示周期开始,那么周期开始的时候是亮灯(也即低电平亮) //因为都是占空比的不同,但是起始的时候都是高电平,不同在于低电平来到的时间 SL(LED, ledValue); pwm_cnt = 0; } //遍历8个灯的情况 for(i=0; i //如果cnt等于对应灯的占空比了,那么这个灯就应该被关闭了 ledValue |= (1 static uchar i; delay1s.ok = 0; if(i++ LED = 4, EXT, SEL, CODE}; typedef struct pwm { uint cnt; uint max; uint highCnt; uchar ok; }pwm_t; pwm_t pwm1 = {0, 20, 2, 0}; void SL(uchar _dev, uchar _data) { P0 = _data; SELE(_dev); } void Timer0Handle() interrupt 1 { if(pwm1.cnt == 0) { SL(LED, 0); } else if(pwm1.cnt == pwm1.highCnt) { SL(LED, 0xff); } if(pwm1.cnt++ AUXR |= 0x80; //定时器时钟1T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0xF4; //设置定时初值 TH0 = 0xFF; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; } void main() { SL(LED, 0x0); Timer0Init(); EA = 1; while(1) { } } 呼吸灯 #include "STC15F2K60S2.h" #define uchar unsigned char #define uint unsigned int #define SELE(x) P2 = P2 & 0x1f | x uint cnt; uint max; uint highCnt; }pwm_t; typedef struct Delay { uint max; uint cnt; uchar ok; }t_delay; pwm_t pwm1 = {0, 20, 2}; t_delay delay5000 = {5000, 0, 0}; void SL(uchar _dev, uchar _data) { P0 = _data; SELE(_dev); } void TimeRun(t_delay* _time) { if(_time->cnt++ max); else { _time->cnt = 0; _time->ok = 1; } } void Timer0Handle() interrupt 1 { //如果cnt为0表示周期开始,那么周期开始的时候是亮灯(也即低电平亮) if(pwm1.cnt == 0) { SL(LED, 0); } //如果cnt和highCnt相等,表示高电平的时间已经到了,因此这里要灭灯(也即高电平灭) else if(pwm1.cnt == pwm1.highCnt) { SL(LED, 0xff); } //让cnt变量在0-max之间不断运行 if(pwm1.cnt++ AUXR |= 0x80; //定时器时钟1T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0xF4; //设置定时初值 TH0 = 0xFF; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; } void huxi() { //每5ms让占空比加 if(delay5000.ok) { delay5000.ok = 0; pwm1.highCnt = (pwm1.highCnt + 1)%pwm1.max; } } void main() { SL(LED, 0x0); Timer0Init(); EA = 1; while(1) { huxi(); } } 8个亮度等级 #include "STC15F2K60S2.h" #define uchar unsigned char #define uint unsigned int #define SELE(x) P2 = P2 & 0x1f | x 5, 10, 15, 20, 30, 40, 50, 105}; uchar ledValue = 0x00; void SL(uchar _dev, uchar _data) { P0 = _data; SELE(_dev); } void Timer0Handle() interrupt 1 { //如果cnt为0表示周期开始,那么周期开始的时候是亮灯(也即低电平亮) //因为都是占空比的不同,但是起始的时候都是高电平,不同在于低电平来到的时间 uchar i = 0; if(pwm_cnt == 0) { SL(LED, 0); } //让cnt变量在0-max之间不断运行 if(pwm_cnt++ if(pwm_cnt == pwm_value[i]) { //如果cnt等于对应灯的占空比了,那么这个灯就应该被关闭了 ledValue |= (1 SL(LED, 0x0); Timer0Init(); EA = 1; while(1) { } } 8个亮度等级流水灯 #include "STC15F2K60S2.h" #define uchar unsigned char #define uint unsigned int #define SELE(x) P2 = P2 & 0x1f | x 3, 20, 30, 50, 60, 90, 100, 105}; uchar ledValue = 0x00; uchar maskValue = 0xff; typedef struct Delay { unsigned long max; unsigned long cnt; uchar ok; }t_delay; t_delay delay1s = {10000, 0, 0}; void SL(uchar _dev, uchar _data) { P0 = _data; SELE(_dev); } void TimeRun(t_delay* _time) { if(_time->cnt++ max); else { _time->cnt = 0; _time->ok = 1; } } void Timer0Handle() interrupt 1 { //如果cnt为0表示周期开始,那么周期开始的时候是亮灯(也即低电平亮) //因为都是占空比的不同,但是起始的时候都是高电平,不同在于低电平来到的时间 uchar i = 0; if(pwm_cnt == 0) { SL(LED, ledValue); } //让cnt变量在0-max之间不断运行 if(pwm_cnt++ if(pwm_cnt == pwm_value[i]) { //如果cnt等于对应灯的占空比了,那么这个灯就应该被关闭了 ledValue |= (1 static uchar i; delay1s.ok = 0; if(i++ AUXR |= 0x80; //定时器时钟1T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0xF4; //设置定时初值 TH0 = 0xFF; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; } void main() { SL(LED, 0x0); Timer0Init(); EA = 1; while(1) { liushui(); } } 加群

在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3