📄 pwm.c
字号:
/*------------------------------------------------------------------------------------*-
文 件 名:PWM.c
功 能:左右两路PWM输出,各自输出周期可调,可显示,脉冲宽度可调,可显示。
调试结果完全符合要求
适用器件:51系列,这里使用P89V51RB2,用STC89C54仿真
作 者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
#include "main.h"
#include "port.h"
static tWord RightPWM;
static tWord LeftPWM;
static tWord LPwmCnt;
static tWord RPwmCnt;
static tByte PwmWidth;
static tByte PwmWidt_R;
static tByte PwmWidt_L;
static tByte PwmDisp;
//显示缓冲区
tByte xdata disnum[8]={0};
//共阳数码管笔段码 0 1 2 3 4 5 6 7 8 9 L R P -
tByte code dispcode[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90, 0xc7,0x88,0x8c,0xbf};
static bit KeyScanTask;
static bit ShapTask;
static bit ShapCheck;
static bit KeyProcessTask;
static bit DispTask;
/*------------------------------------------------------------------------------------*-
函数原形:tByte read165(void)
函数功能:从74LS165中读数据
输入参数:无
输出参数:无
注 意:移位问题,详细请看程序注释,先关中断,读完数据再开
作 者:冯耿超
完成日期:2007-07-10
-*------------------------------------------------------------------------------------*/
tByte Read_165()
{
tByte dat_buf,cnt;
//数据锁存
_165_sft=0;
dat_buf=0;
cnt=8;
//关中断
EA = 0;
//开始移位
_165_sft=1;
do
{
_165_clk=0;
//这里要先移位,否则会出现最低位被补0而最高位被移出的现象
dat_buf<<=1;
if(_165_sda)
{
dat_buf|=0x01;
}
_165_clk=1;
}while(--cnt);
//开中断
EA = 1;
//返回键值
return dat_buf;
}
void Key_Scan()
{
if(Read_165()!=0xff)
//检测满足,使能去抖动
ShapCheck = 1;
}
/*------------------------------------------------------------------------------------*-
*函数原形:Key_Board_Process()
*函数功能:键盘功能处理函数
*输入参数:无
*输出参数:无
*修改情况:修改按键功能,使得对应显示时所以对应的按键才起作用,语句请看程序中带*号的语句
*修改日期:200707-20
*作 者:冯耿超
*完成日期:2007-07-12
-*------------------------------------------------------------------------------------*/
void Key_Board_Process()
{
if(Read_165()!=0xff)
{
switch(Read_165())
{
//左边PWM加
case 0xfe: if(PwmDisp ==0) //*
{
LeftPWM++;
if(LeftPWM>999)
LeftPWM = 999;
}
break;
//左边PWM减
case 0xfd: if(PwmDisp ==0) //*
{
if(LeftPWM!=0)
LeftPWM--;
else
LeftPWM = 1;
}
break;
//右边PWM加
case 0xfb: if(PwmDisp ==1) //*
{
RightPWM++;
if(RightPWM>999)
RightPWM = 999;
}
break;
//右边PWM减
case 0xf7: if(PwmDisp ==1) //*
{
if(RightPWM!=0)
RightPWM--;
else
RightPWM = 1;
}
break;
//脉冲宽度加
case 0xdf: if(PwmDisp ==2) //*
{
PwmWidth++;
if(PwmWidth>250)
PwmWidth = 250;
}
break;
//脉冲宽度减
case 0xbf: if(PwmDisp ==2)
{
if(PwmWidth!=0)
PwmWidth--;
else
PwmWidth = 0;
}
break;
//选择显示值
case 0x7f: PwmDisp++;
if(PwmDisp==5)PwmDisp = 0;
break;
default: break;
}
}
}
/*------------------------------------------------------------------------------------*-
函数原形:void _138_drive_bit(tByte xdata led_bit)
函数功能:led位驱动,用74LS138作为位选,位选参数为led_bit
输入参数:无
输出参数:无
作 者:冯耿超
完成日期:2007-06-13
-*------------------------------------------------------------------------------------*/
void _138_drive_bit(tByte xdata led_bit)
{
switch(led_bit)
{
case 1:_138_a = 0;_138_b = 0;_138_c = 0; break;
case 2:_138_a = 1;_138_b = 0;_138_c = 0; break;
case 3:_138_a = 0;_138_b = 1;_138_c = 0; break;
case 4:_138_a = 1;_138_b = 1;_138_c = 0; break;
case 5:_138_a = 0;_138_b = 0;_138_c = 1; break;
case 6:_138_a = 1;_138_b = 0;_138_c = 1; break;
case 7:_138_a = 0;_138_b = 1;_138_c = 1; break;
case 8:_138_a = 1;_138_b = 1;_138_c = 1; break;
default:break;
}
}
/*------------------------------------------------------------------------------------*-
函数原形:void _164_send_data(tByte xdata led_code)
函数功能:将共阳数码管笔段通过74LS164送出
输入参数:无
输出参数:无
注 意:74LS164为串入并出,时钟先为上升沿触发,先关中断,送完数据再开
作 者:冯耿超
完成日期:2007-06-13
-*------------------------------------------------------------------------------------*/
void _164_send_data(tByte xdata led_code)
{
tByte data i;
EA = 0;
for(i=0;i<8;i++)
{
_164_sck = 0;
_164_sda = (led_code&0x80);
_164_sck = 1;
led_code<<=1;
}
EA = 1;
_164_sck = 0;
}
/*------------------------------------------------------------------------------------*-
函数原形:void display_led(tByte xdata led,tByte xdata num)
函数功能:把显示笔段num送到指定的LED上,指定位为led
输入参数:无
输出参数:无
注 意:LED动态显示按照关显示——>送笔段——>开显示的步骤,防止LED串红
作 者:冯耿超
完成日期:2007-06-13
-*------------------------------------------------------------------------------------*/
void display_led(tByte xdata led,tByte xdata num)
{
//关LED显示
_138_oe = 1;
//送显示笔段到指定的LED上
switch(led)
{
case 1:_138_drive_bit(led);
_164_send_data(num);break;
case 2:_138_drive_bit(led);
_164_send_data(num);break;
case 3:_138_drive_bit(led);
_164_send_data(num);break;
case 4:_138_drive_bit(led);
_164_send_data(num);break;
case 5:_138_drive_bit(led);
_164_send_data(num);break;
case 6:_138_drive_bit(led);
_164_send_data(num);break;
case 7:_138_drive_bit(led);
_164_send_data(num);break;
case 8:_138_drive_bit(led);
_164_send_data(num);break;
default:break;
}
//开LED显示
_138_oe = 0;
}
/*------------------------------------------------------------------------------------*-
函数原形:void display()
函数功能:在LED上从右到左显示按键按下所对应的值
输入参数:无
输出参数:无
注 意:参数K的取值
作 者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
void display()
{
static tByte cnt;
switch (PwmDisp)
{
//显示左边PWM值
case 0:
disnum[0] = LeftPWM%10;
disnum[1] = LeftPWM/10%10;
disnum[2] = LeftPWM/100%10;
disnum[3] = LeftPWM/1000%10;
//显示“L”
disnum[4] = 13;
disnum[5] = 13;
disnum[6] = 13;
disnum[7] = 10;
break;
//显示右边PWM值
case 1:
disnum[0] = RightPWM%10;
disnum[1] = RightPWM/10%10;
disnum[2] = RightPWM/100%10;
disnum[3] = RightPWM/1000%10;
//显示“R”
disnum[4] = 13;
disnum[5] = 13;
disnum[6] = 13;
disnum[7] = 11;
break;
//显示脉冲高电平的宽度
case 2:
disnum[0] = PwmWidth%10;
disnum[1] = PwmWidth/10%10;
disnum[2] = PwmWidth/100%10;
disnum[3] = PwmWidth/1000%10;
//显示“P”
disnum[4] = 13;
disnum[5] = 13;
disnum[6] = 13;
disnum[7] = 12;
break;
//右边脉冲计数
case 3:
disnum[0] = RPwmCnt%10;
disnum[1] = RPwmCnt/10%10;
disnum[2] = RPwmCnt/100%10;
disnum[3] = RPwmCnt/1000%10;
disnum[4] = 13;
disnum[5] = 13;
disnum[6] = 12;
disnum[7] = 11;
break;
//左边脉冲计数
case 4:
disnum[0] = LPwmCnt%10;
disnum[1] = LPwmCnt/10%10;
disnum[2] = LPwmCnt/100%10;
disnum[3] = LPwmCnt/1000%10;
disnum[4] = 13;
disnum[5] = 13;
disnum[6] = 12;
disnum[7] = 10;
break;
default:break;
}
//LED显示器显示
if(++cnt==8)cnt=0;
switch(cnt)
{
case 0:display_led(cnt+1,dispcode[disnum[7]]);break;
case 1:display_led(cnt+1,dispcode[disnum[6]]);break;
case 2:display_led(cnt+1,dispcode[disnum[5]]);break;
case 3:display_led(cnt+1,dispcode[disnum[4]]);break;
case 4:display_led(cnt+1,dispcode[disnum[3]]);break;
case 5:display_led(cnt+1,dispcode[disnum[2]]);break;
case 6:display_led(cnt+1,dispcode[disnum[1]]);break;
case 7:display_led(cnt+1,dispcode[disnum[0]]);break;
default:break;
}
}
/*------------------------------------------------------------------------------------*-
函数原形:void T0_Init()
函数功能:T0初始化,工作方式1,16位定时计数器,定时5ms作为系统时标
输入参数:无
输出参数:无
作 者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
void T0_Init()
{
TMOD &=0xf0;
TMOD |=0x01;
//5ms中断初值
TH0 = 0xec;
TL0 = 0x78;
ET0 = 1;
TR0 = 1;
EA = 1;
}
/*------------------------------------------------------------------------------------*-
函数原形:void Task_Init()
函数功能:系统任务初始化
输入参数:无
输出参数:无
作 者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
void Task_Init()
{
//左右脉冲周期初始化
RightPWM = 100;
LeftPWM = 100;
LPwmCnt = 0;
RPwmCnt = 0;
//用于调节高电平脉冲的宽度,
//它必须小于脉冲周期,即PWM的中断周期
PwmWidth = 30;
//左右边高电平脉冲宽度
PwmWidt_R = 0;
PwmWidt_L = 0;
//默认显示脉冲宽度值
PwmDisp = 2;
//键盘扫描任务初始化
KeyScanTask = 0;
//去抖动初始化
ShapCheck = 0;
//键盘处理任务初始化
KeyProcessTask = 0;
//LED显示任务初始化
DispTask = 0;
}
/*------------------------------------------------------------------------------------*-
函数原形:系统主函数
函数功能:由PwmWidth可以计算高电平的宽度为5ms*20=100ms
低电平的宽度为RightPWM或是LeftPWM的初值乘以5ms减去100ms
输入参数:无
输出参数:无
作 者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
void main()
{
//系统初始化
T0_Init();
Task_Init();
while(1)
{
//执行LED显示任务
if(DispTask)
{
DispTask = 0;
display();
}
//保持高电平时间为PwmWidt_R*5ms
if(PwmWidt_R>0)
{
Right = 1;
}
else
{
Right = 0;
}
//保持高电平时间为PwmWidt_L*5ms
if(PwmWidt_L>0)
{
Left = 1;
}
else
{
Left = 0;
}
//键盘扫描任务
if(KeyScanTask)
{
KeyScanTask = 0;
Key_Scan();
}
//键盘处理任务
if(KeyProcessTask)
{
KeyProcessTask = 0;
Key_Board_Process();
}
}
}
/*------------------------------------------------------------------------------------*-
函数原形:void T0_ISR()
函数功能:T0中断服务子程序
输入参数:无
输出参数:无
作 者:冯耿超
完成日期:2007-07-13
-*------------------------------------------------------------------------------------*/
void T0_ISR() interrupt 1 using 1
{
//右边PWM宽度
static tByte RightPwmCnt = 0;
//左边PWM宽度
static tByte LeftPwmCnt = 0;
//键盘扫描
static tByte KeyCnt = 0;
//键盘去抖动
static tByte ShapCnt = 0;
//重装初值
TH0 = 0xec;
TL0 = 0x78;
//添加显示任务
DispTask = 1;
//添加右边PWM输出
if(++RightPwmCnt==RightPWM)
{
RightPwmCnt = 0;
PwmWidt_R = PwmWidth;
Right = 1;
RPwmCnt++;
}
//添加左边PWM输出
if(++LeftPwmCnt==LeftPWM)
{
LeftPwmCnt = 0;
PwmWidt_L = PwmWidth;
Left = 1;
LPwmCnt++;
}
//150ms也就是30个时标执行一次键盘扫描
if(++KeyCnt==30)
{
KeyCnt = 0;
KeyScanTask = 1;
}
//是否允许去抖动开始
if(ShapCheck)
{
//去抖动时间为10ms
if(++ShapCnt==2)
{
//去抖动关闭
ShapCheck = 0;
//去抖动时长复位
ShapCnt = 0;
//添加键盘处理任务
KeyProcessTask = 1;
}
}
//左右两边PWM宽度调节
//注意这里的“--”符号位置
if(PwmWidt_R--==0)PwmWidt_R = 0;
if(PwmWidt_L--==0)PwmWidt_L = 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -