一、前言
05 蓝桥杯单片机设计与开发_基础模块_AT24C02-CSDN博客,承接上文,本文将继续介绍剩余蓝桥杯涉及模块(PWM、串口通信、NE555以及超声波测距),为大家做出详细编程指导。
二、PWM
PWM 是脉宽调制(Pulse Width Modulation)的缩写。它是一种常用的调制技术,用于控制电子设备中的电源或信号。通过调整 PWM 信号的占空比,可以实现对输出信号的精确控制。
1、PWM原理剖析
本文将编写程序,实现:频率为 1000Hz
,占空比可通过按键调节。
将使用两个定时器,定时器 0 设置为 100us
,用来控制占空比;定时器 1 设置为 2ms
,用来检测按键及数码管显示等。
编程思路:定义单片机上P34
引脚为pwm
,如: sbit pwm = P3^4
,初始 pwm = 1
。假设要设置20%占空比,则可在定时器 0 中断服务函数中计数,若计数值超过 2 则设令 pwm = 0
;若计数超过10则pwm
继续置1。即可实现占空比为20%,频率为 1/(10*100us) = 1000Hz。
2、核心代码
// 定时器0中断服务函数
void timer0() interrupt 1
{
static tt = 0;
// pwm_plus可通过按键调节,pwm_plus = 2,则占空比20%;pwm_plus = 5,则占空比50%
if(tt >= pwm_plus)
{
pwm = 0;
door(0x80,0xff);
}
else // 初始状态为 1
{
pwm = 1;
door(0x80,0x00);
}
tt++;
// 奇数超过10复位,则周期为10*100us,频率为1000Hz。(可通过修改该值来改变PWM频率)
if(tt >= 10)
tt = 0;
}
3、整体代码
由于考虑初学者基础较为薄弱,因此将完整的代码贴出来,包含中断函数、矩阵键盘、数码管显示等,并且在关键处进行了注释,希望对大家有所帮助。
// 包含头文件
#include "STC15F2K60S2.h"
// 数据类型定义
#define u8 unsigned char
#define u16 unsigned int
// 8位数码管状态
u8 dspbuf[8] = {10,10,10,10,10,10,10,10};
u8 code tab[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xff,0xbf,0xc6};
u8 pwm_plus = 0,dspcom = 0;
bit key_flag = 0,led_flag = 0;
sbit pwm = P3^4;
// 定时器 0 初始化
void Timer0Init(void) //100微秒@12.000MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x50; //设置定时初值
TH0 = 0xFB; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1;
}
// 定时器 1 初始化
void Timer1Init(void) //2毫秒@12.000MHz
{
AUXR |= 0x40; //定时器时钟1T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0x40; //设置定时初值
TH1 = 0xA2; //设置定时初值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1;
EA = 1;
}
// 573锁存器封装成函数
void door(u8 choose,u8 input)
{
P2 = (P2 & 0x1f) | choose;
P0 = input;
P2 &= 0x1f;
}
// 关灯、蜂鸣器
void all_init()
{
door(0x80,0xff);
door(0xa0,0xaf);
}
// 数码管显示函数
void display()
{
door(0xe0,0xff); // 消隐
door(0xc0,0x01<<dspcom);
door(0xe0,tab[dspbuf[dspcom++]]);
if(dspcom >= 8)
dspcom = 0;
}
// 矩阵键盘,接KBD
u8 keypress = 0,keyvalue = 0xff,keyread = 0;
u8 Read_key(void) //矩阵键盘,接KBD
{
u8 key_m,cal;
P3 = 0xf0;P42=1;P44=1;
P36=P42;P37=P44; //变量替换
key_m = (P3 & 0xf0);
if(key_m != 0xf0)
keypress++;
else
keypress = 0;
if(keypress == 3)
{
keypress = 0;
keyread = 1;
switch(key_m)
{
case 0x70:cal = 0;break;
case 0xb0:cal = 1;break;
case 0xd0:cal = 2;break;
case 0xe0:cal = 3;break;
}
P3 = 0x0f;P42=0;P44=0;
P36=P42;P37=P44; //变量替换
key_m = (P3 & 0x0f);
switch(key_m)
{
case 0x0e:keyvalue = (4*cal+7);break;
case 0x0d:keyvalue = (4*cal+6);break;
case 0x0b:keyvalue = (4*cal+5);break;
case 0x07:keyvalue = (4*cal+4);break;
}
}
P3 = 0x0f;P42=0;P44=0;
P36=P42;P37=P44; //变量替换
key_m = (P3&0x0f);
if((keyread == 1) && (key_m == 0x0f))
{
keyread = 0;
return keyvalue;
}
return 0xff;
}
//````````````````````````````````主函数````````````````````````````````````
void main()
{
u8 key_re,mode = 0,led_put = 0;
// 初始化
all_init();
Timer0Init();
Timer1Init();
while(1)
{
// 实时显示当前设置的占空比
dspbuf[6] = pwm_plus/10;
dspbuf[7] = pwm_plus%10;
if(key_flag)
{
key_flag = 0;
key_re = Read_key();
if(key_re != 0xff)
{
switch(key_re)
{
// 按键调节占空比
case 12:pwm_plus+=2;
if(pwm_plus > 10)
pwm_plus = 0;
break;
}
}
}
}
}
// 定时器 0 中断服务函数
void timer0() interrupt 1
{
static tt = 0;
// pwm_plus可通过按键调节,pwm_plus = 2,则占空比20%;pwm_plus = 5,则占空比50%
if(tt >= pwm_plus)
{
pwm = 0;
door(0x80,0xff);
}
else // 初始状态为 1
{
pwm = 1;
door(0x80,0x00);
}
tt++;
// 奇数超过10复位,则周期为10*100us,频率为1000Hz。(可通过修改该值来改变PWM频率)
if(tt >= 10)
tt = 0;
}
// 定时器 1 中断服务函数
void timer1() interrupt 3
{
static u8 t_20ms = 0; //MAX = 500 ms
static int t_1s;
display();
t_20ms++;
t_1s++;
if(t_20ms >= 10) //每20ms扫描一次按键
{
t_20ms = 0;
key_flag = 1;
}
if(t_1s >= 500)
{
t_1s = 0;
led_flag = 1;
}
}
至此,本次 PWM 已介绍完毕。