关于中断以及按键、LED灯模块等都在之前的文章中介绍过了,此处不再介绍了。
一、先前知识
1.占空比
占空比就是高电平的时间占整个周期的百分比。实质上就是定时器,是定时器的另一个用法。
例如:100Hz,那么周期就是10ms(10000us),如果分成100小份,那么就是100us×100,如果占空比时60%,那么高电平就是60份,低电平时40份,在软件中用一个变量来进行计数,记到给定的值就翻转电平。
图一、占空比
二、所要实现的功能
要求PWM脉宽信号的频率为100Hz,系统上电后L1处于熄灭状态,L1指示灯有四种亮度。分别是完全熄灭、10%的亮度、50%的亮度、90%的亮度。通过S7按键循环切换L1指示灯的四种亮度。程序循环实现上述功能。
三、代码实现
①参数及引脚定义
// 按键定义
sbit S7 = P3^0;
// LED灯定义
sbit L1 = P0^0;
// 参数定义
unsigned char count = 0; // 定时器计数
unsigned char pwm_duty = 0; // PWM数值
unsigned char state = 0; // 按键状态
②138译码器通道选择函数
// 通道选择函数
void HC138_Init( unsigned char channel )
{
switch( channel )
{
case 0:
P2 = ( P2 & 0x1f ) | 0x00; // 0
break;
case 4:
P2 = ( P2 & 0x1f ) | 0x80; // Y4C
break;
case 5:
P2 = ( P2 & 0x1f ) | 0xa0; // Y5C
break;
case 6:
P2 = ( P2 & 0x1f ) | 0xc0; // Y6C
break;
case 7:
P2 = ( P2 & 0x1f ) | 0xe0; // Y7C
break;
}
}
③初始化系统——关闭当前不需要的LED灯、蜂鸣器和继电器
// 系统初始化
void System_Init(void)
{
// 关闭LED灯
HC138_Init( 4 );
P0 = 0xff;
// 关闭蜂鸣器和继电器
HC138_Init( 5 );
P0 = 0xaf; // 1010 1111
HC138_Init( 0 ); // 关闭通道选择
}
④定时器0初始化函数
// 定时器0初始化
void Timer0_Init(void)
{
TMOD = 0x01; // 使用定时器0工作模式1
ET0 = 1;
EA = 1;
// 定时初值
TH0 = ( 65535 - 100 ) / 256; // 10000us = 100us×100
TL0 = ( 65535 - 100 ) % 256;
}
⑤定时器0服务函数
// 定时器0服务函数
void Timer0() interrupt 1
{
// 由于是不能自动重装载的,所以要再次赋初值
TH0 = ( 65535 - 100 ) / 256;
TL0 = ( 65535 - 100 ) % 256;
count++; // 100us周期计数
// LED灯是低电平点亮,所以实质上的占空比在这里不一样,而是低电平的周期
if( count == pwm_duty )
{
L1 = 1;
}
else if( count == 100 )
{
L1 = 0;
count = 0;
}
}
⑥按键扫描函数
// 按键扫描
void Key_Scan(void)
{
if( S7 == 0 )
{
Delay_tms( 20 ); // 消抖
if( S7 == 0 )
{
while( S7 == 0 ); // 按键抬起后操作
switch( state )
{
case 0:
L1 = 0;
TR0 = 1; // 产生脉宽调制的时候再打开定时器,不能直接放在初始化函数中
pwm_duty = 10;
state = 1;
break;
case 1:
pwm_duty = 50;
state = 2;
break;
case 2:
pwm_duty = 90;
state = 3;
break;
case 3:
L1 = 1;
TR0 = 0;
state = 0;
break;
}
}
}
}
⑦整个函数展示
#include <STC15F2K60S2.H>
#include "Delay_ms.h"
// 按键定义
sbit S7 = P3^0;
// LED灯定义
sbit L1 = P0^0;
// 参数定义
unsigned char count = 0; // 定时器计数
unsigned char pwm_duty = 0; // PWM数值
unsigned char state = 0; // 按键状态
// 通道选择函数
void HC138_Init( unsigned char channel )
{
switch( channel )
{
case 0:
P2 = ( P2 & 0x1f ) | 0x00; // 0
break;
case 4:
P2 = ( P2 & 0x1f ) | 0x80; // Y4C
break;
case 5:
P2 = ( P2 & 0x1f ) | 0xa0; // Y5C
break;
case 6:
P2 = ( P2 & 0x1f ) | 0xc0; // Y6C
break;
case 7:
P2 = ( P2 & 0x1f ) | 0xe0; // Y7C
break;
}
}
// 系统初始化
void System_Init(void)
{
// 关闭LED灯
HC138_Init( 4 );
P0 = 0xff;
// 关闭蜂鸣器和继电器
HC138_Init( 5 );
P0 = 0xaf; // 1010 1111
HC138_Init( 0 ); // 关闭通道选择
}
// 定时器0初始化
void Timer0_Init(void)
{
TMOD = 0x01; // 使用定时器0工作模式1
ET0 = 1;
EA = 1;
// 定时初值
TH0 = ( 65535 - 100 ) / 256; // 10000us = 100us×100
TL0 = ( 65535 - 100 ) % 256;
}
// 定时器0服务函数
void Timer0() interrupt 1
{
// 由于是不能自动重装载的,所以要再次赋初值
TH0 = ( 65535 - 100 ) / 256;
TL0 = ( 65535 - 100 ) % 256;
count++; // 100us周期计数
// LED灯是低电平点亮,所以实质上的占空比在这里不一样,而是低电平的周期
if( count == pwm_duty )
{
L1 = 1;
}
else if( count == 100 )
{
L1 = 0;
count = 0;
}
}
// 按键扫描
void Key_Scan(void)
{
if( S7 == 0 )
{
Delay_tms( 20 ); // 消抖
if( S7 == 0 )
{
while( S7 == 0 ); // 按键抬起后操作
switch( state )
{
case 0:
L1 = 0;
TR0 = 1; // 产生脉宽调制的时候再打开定时器,不能直接放在初始化函数中
pwm_duty = 10;
state = 1;
break;
case 1:
pwm_duty = 50;
state = 2;
break;
case 2:
pwm_duty = 90;
state = 3;
break;
case 3:
L1 = 1;
TR0 = 0;
state = 0;
break;
}
}
}
}
int main(void)
{
System_Init(); // 系统初始化
Timer0_Init(); // 定时器0初始化
HC138_Init( 4 ); // 进行通道选择,要准备控制LED灯
P0 = 0xff;
while(1)
{
Key_Scan(); // 按键扫描
}
}