工厂灯光控制系统案例解析
- 题目
- 流程图
- 关键点复盘
- 参考代码(IO模式)
题目
流程图
关键点复盘
- 设备检测——移位
L1~L8在板子上是从左至右,但是在对P0口赋值时是16进制从高位(L8)—>低位(L0)
根据原理图,LED赋值0亮1灭
为了方便赋值,可以把板子转一下,让LED排序与赋值顺序相同
左移<< 右移>>控制移位补0
,使LED依次点亮、依次熄灭
unsigned char i 作为下文表格中的循环变量
赋值 L8 L7 L6 L5 L4 L3 L2 L1 备注 0xff 1 1 1 1 1 1 1 1 全灭 0xff << i for(i = 1;i <= 8;i++)
左移补0~ (0xff) 0 0 0 0 0 0 0 0 全亮 ~ (0xff) << i for(i = 1;i <= 8;i++)
左移补1
- 运行时间的逻辑处理——if语句
- if语句嵌套:秒——>分——>时
- 定义unsigned chat count = 0; 将50ms作为计数初值,20次为1s
- 判断 秒、分 是否到60,而不是59
- 按键控制LED时不影响数码管运行——按键消抖 检测松手(现场保护)
if(S5 == 0) //发现按键S5信号 { SMG_Display(); //刷新数码管 作为 去抖动处理 if(S5 == 0) //确认按键S5信号 { while(S5 == 0) //等待按键S5松开 { SMG_Display(); //刷新数码管使其不受按键影响 } XXXXXXXXXXX其他操作XXXXXXXXXXXX; } }
- 串口远程控制——switch-case-break语句
根据同信规约:
高位
——命令类型
A 或 B
低位
——执行函数
LED显示 或 读取时间if(command != 0x00) //接收到一个上位机命令 { switch(command & 0xf0) //将命令类型(高4位)取出 { case 0xa0: XXXXXXXXX其他操作XXXXXXXXXX; break; command = 0x00; //command清零,防止重复执行 case 0xb0: XXXXXXXXX其他操作XXXXXXXXXX; break; command = 0x00; } }
- LED控制语句解析——与、或
- 与或关系
0 | any = any 用作取数
1 | any = 1 用作置1
0 & any = 0 用作置0
1 & any = any 用作取数
为了使“本地控制”(高位)与“远程控制”(低位)互不干扰,引入状态变量stat_led = 0xff
- 本地控制
- S5控制L7(判断S5按下后)
Select_HC573(4);
stat_led = (stat_led | 0x40) & (~stat_led | 0xbf);
P0 = stat_led;
- 远程控制(判断为case 0xa0以后)
Select_HC573(4);
stat_led = (stat_led | 0x0f) & (~command | 0xf0);
P0 = stat_led;
- 读取时间——发送数据HEX16进制的BCD码格式(0xMM)
(hour / 10) << 4 | (hour % 10)
(min / 10) << 4 | (min % 10)
(sec / 10) << 4 | (sec % 10)
原数取整放高位(左移4位
),原数取余放低位
- 注释规范性
- 标明定义变量
- 定义辅助寄存器(使用串口时)
- 定义独立按键管脚(使用独立按键时)
- 定义状态机(LED状态变量)
- 定义数码管段码表(有无小数点)
- 表明定义函数
/* ==================== XXX函数 =======================
功能:XXX
参数:XXX
返回:XXX
设计:XXX(20XX年XX月XX日)
======================================================= */
参考代码(IO模式)
J13接2~3
J5接2~3
#include "reg52.h"
sfr AUXR = 0x8e; //定义辅助寄存器
sbit S5 = P3^2; //定义按键S5引脚
sbit S4 = P3^3; //定义按键S4引脚
unsigned char count = 0; //定义50ms定时中断累计变量
unsigned char sec = 0; //定义运行时间的“秒”变量
unsigned char min = 0; //定义运行时间的“分”变量
unsigned char hour = 0; //定义运行时间的“时”变量
unsigned char command = 0x00; //定义串口命令字接受变量
unsigned char stat_led = 0xff; //定义LED灯当前开关状态
//-----共阳数码管的段码编码表(无小数点)-----
code unsigned char SMG_NoDot[10] = {0xc0,0xf9,0xa4,0xb0,0x99,
0x92,0x82,0xf8,0x80,0x90};
/*====================锁存器选择函数=======================
功能:选择要打通的锁存器
参数:channel 输出通道
返回:空
设计:饥饿的半导体(2023年1月30日)
=======================================================*/
void Select_HC573(unsigned char channel)
{
switch(channel)
{
case 4: P2 = P2 & 0x1f | 0x80; break; //Y4输出0,选择LED控制
case 5: P2 = P2 & 0x1f | 0xa0; break; //Y5输出0,选择蜂鸣器和继电器控制
case 6: P2 = P2 & 0x1f | 0xc0; break; //Y6输出0,选择数码管位选
case 7: P2 = P2 & 0x1f | 0xe0; break; //Y7输出0,选择数码管段码
case 0: P2 = P2 & 0x1f | 0x00; break; //所有锁存器不选择
}
}
/*=================数码管专用延时函数==================
功能:数码管动态显示专用延时函数
参数:t 延时长度
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void SMG_Delay(unsigned char t)
{
while(t--);
}
/*===============单个数码管显示函数===================
功能:在指定的数码管位置上显示指定的内容
参数:pos——位选,即要点亮的数码管的显示位置
dat——段选,即要点亮的数码管的显示内容
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void SMG_Bit(unsigned char pos,unsigned char dat)
{
Select_HC573(7); //消影
P0 = 0xff;
Select_HC573(6); //位选(低位—>高位 即 数码管左—>数码管右)
P0 = 0x01 << pos;
Select_HC573(7); //段选(内容由“定义的数码管段码数组内容”决定)
P0 = dat;
}
/*================全部数码管显示函数===================
功能:控制所有数码管显示相同内容,放在数码管显示函数后用于消影
参数: dat 段选,数码管显示内容
返回:空
设计:饥饿的半导体(2023年1月30日)
====================================================*/
void SMG_All(unsigned char dat)
{
Select_HC573(6); //位选全部
P0 = 0xff;
Select_HC573(7); //段选
P0 = dat;
}
/*=================普通延时函数=======================
功能:逐个检测LED与数码管的工作状态
参数:t 延时长度
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void Delay(unsigned int t)
{
while(t--);
while(t--);
}
/*=================设备检测函数=====================
功能:逐个检测LED与数码管的工作状态
参数:无
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void Device_Detect()
{
unsigned char i; //循环变量
Select_HC573(4);
for(i = 1;i <= 8;i++)
{
P0 = 0xff << i; //逐个点亮LED
Delay(60000);
}
for(i = 1;i <= 8;i++)
{
P0 = ~(0xff << i); //逐个熄灭LED
Delay(60000);
}
Select_HC573(0); //关闭所有锁存器
Select_HC573(7); //数码管a~dp所有段点亮
P0 = 0x00;
for(i = 0;i <= 7;i++)
{
Select_HC573(6);
P0 = ~(0xfe << i); //逐个点亮数码管
Delay(60000);
}
for(i = 0;i <= 7;i++)
{
Select_HC573(6);
P0 = (0xfe << i); //逐个熄灭数码管
Delay(60000);
}
Select_HC573(0); //关闭所有锁存器
}
/*=================定时器T0初始化函数===================
功能:将定时器T0设置为16位自动重装模式(模式00),计数初值为50ms
参数:无
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void Init_Time0()
{
TMOD = 0x20; //注意!!!T0与T1的工作模式必须一起赋值
TH0 = (65535 - 50000) / 256; //计数初值为50ms
TL0 = (65535 - 50000) % 256;
ET0 = 1; //使能定时器T0
EA = 1; //使能总中断
TR0 = 1; //启动定时器T0
}
/*=================定时器T0中断服务函数=================
功能:进行系统运行时间的逻辑处理
参数:无
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void Service_Time0() interrupt 1
{
count++;
if(count == 20)
{
count = 0;
sec++;
if(sec == 60)
{
sec = 0;
min++;
if(min == 60)
{
min = 0;
hour++;
{
if(hour == 99)
hour = 0;
}
}
}
}
}
/*=================串口初始化函数===================
功能:将串口初始化为波特率发生器(模式01),波特率为9600,允许接收
参数:无
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void Init_Uart()
{
TMOD = 0x20; //注意!!!T0与T1的工作模式必须一起赋值
TH1 = 0xfd; //设置9600波特率的参数
TH1 = 0xfd;
SCON = 0x50; //8位UART模式,允许接收
AUXR = 0x00; //辅助寄存器设置
ES = 1; //使能串口中断
EA = 1; //使能总中断
TR1 = 1; //启动定时器T1
}
/*=================串口中断服务函数===================
功能:接收上位机的数据并保持在command变量中
参数:无
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void Service_Uart() interrupt 4
{
if(RI == 1)
{
command = SBUF; //将接收到的数据保存在command变量中
RI = 0; //将接收完成标志RI清0
}
}
/*=================串口发送单字节函数===================
功能:串口向上位机发送一个字节
参数:dat 要发送的字节
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void Sent_Byte(unsigned char dat)
{
SBUF = dat;
while(TI == 0);
TI = 0;
}
/*=================系统运行时间显示函数=================
功能:在数码管上显示系统运行的时间
参数:无
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void SMG_Display()
{
SMG_Bit(0,SMG_NoDot[hour / 10]); //时十位
SMG_Delay(500);
SMG_Bit(1,SMG_NoDot[hour % 10]); //时个位
SMG_Delay(500);
SMG_Bit(2,0xbf); //分隔符
SMG_Delay(500);
SMG_Bit(3,SMG_NoDot[min / 10]); //分十位
SMG_Delay(500);
SMG_Bit(4,SMG_NoDot[min % 10]); //分个位
SMG_Delay(500);
SMG_Bit(5,0xbf); //分隔符
SMG_Delay(500);
SMG_Bit(6,SMG_NoDot[sec / 10]); //秒十位
SMG_Delay(500);
SMG_Bit(7,SMG_NoDot[sec % 10]); //分个位
SMG_Delay(500);
SMG_All(0xff);
}
/*================独立按键控制本地灯光函数===============
功能:扫描S5和S4按键并执行本地灯光控制
参数:无
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void Key_LED()
{
if(S5 == 0) //发现按键S5信号
{
SMG_Display(); //去抖动处理
if(S5 == 0) //确认按键S5信号
{
while(S5 == 0) //等待按键S5松开
{
SMG_Display();
}
Select_HC573(4);
stat_led = (stat_led | 0x40) & (~stat_led | 0xbf);
P0 = stat_led; //执行现场灯光控制
Select_HC573(0);
}
}
if(S4 == 0) //发现按键S4信号
{
SMG_Display(); //去抖动处理
if(S4 == 0) //确认按键S4信号
{
while(S4 == 0) //等待按键S4松开
{
SMG_Display();
}
Select_HC573(4);
stat_led = (stat_led | 0x80) & (~stat_led | 0x7f);
P0 = stat_led; //执行现场灯光控制
Select_HC573(0);
}
}
}
/*==================串口远程控制LED函数=====================
功能:上位机命令解析与执行,接收上位机数据并保存在command变量中
参数:无
返回:空
设计:饥饿的半导体(2023年1月30日)
=========================================================*/
void Execute_Command()
{
if(command != 0x00) //接收到一个上位机命令
{
switch(command & 0xf0) //将命令类型(高4位)取出来
{
case 0xa0: //远程灯光控制命令
Select_HC573(4);
stat_led = (stat_led | 0x0f) & (~command | 0xf0);
P0 = stat_led;
Select_HC573(0);
command = 0x00;
break;
case 0xb0: //读取本地系统运行时间命令
Sent_Byte((hour / 10) << 4 | (hour % 10));
Sent_Byte((min / 10) << 4 | (min % 10));
Sent_Byte((sec / 10) << 4 | (sec % 10));
command = 0x00;
break;
}
}
}
/*==================系统初始化函数=====================
功能:将蜂鸣器与继电器灯无关设备关闭
参数:无
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void Init_System()
{
Select_HC573(5);
P0 = 0x00;
Select_HC573(4);
P0 = 0xff;
Select_HC573(0);
}
/*=====================主函数==========================
功能:工厂灯光控制系统的主函数
参数:无
返回:空
设计:饥饿的半导体(2023年1月30日)
=====================================================*/
void main()
{
Init_System();
Device_Detect();
Init_Time0();
Init_Uart();
while(1)
{
Execute_Command();
SMG_Display();
Key_LED();
}
}