蓝桥杯,小蜜蜂,单元训练15:工厂灯光控制系统
/*
* @Description:
* @Author: fdzhang
* @Email: zfdc@qq.com
* @Date: 2024-08-17 23:23:51
* @LastEditTime: 2024-08-18 20:15:38
* @LastEditors: fdzhang
*/
#include "stc15f2k60s2.h"
#include "intrins.h"
// y4c
#define lightLED(x) \
{ \
P0 = x; \
P2 = P2 & 0x1f | 0x80; \
P2 &= 0x1f; \
}
// y5c
#define Buzz(x) \
{ \
P0 = x; \
P2 = P2 & 0x1f | 0xA0; \
P2 &= 0x1f; \
}
#define Relay(x) \
{ \
P0 = x; \
P2 = P2 & 0x1f | 0xA0; \
P2 &= 0x1f; \
}
// y6c
#define lightCOM(x) \
{ \
P0 = x; \
P2 = P2 & 0x1f | 0xc0; \
P2 &= 0x1f; \
}
// y7c
#define lightSEG(x) \
{ \
P0 = x; \
P2 = P2 & 0x1f | 0xE0; \
P2 &= 0x1f; \
}
sbit s5L7 = P3 ^ 2;
sbit s4L8 = P3 ^ 3;
#define L7 0xbf // 定义Led7亮灯信息
#define L8 0x7f // 定义Led8亮灯信息
#define locSeg1h10 0x01 // 定义数码管从左到右,小时十位
#define locSeg2h1 0x02 // 定义数码管从左到右,小时个位
#define locSeg3dash 0x04 // 定义数码管从左到右,符号-
#define locSeg4min10 0x08 // 定义数码管从左到右,分十位
#define locSeg5min1 0x10 // 定义数码管从左到右,分个位
#define locSeg6dash 0x20 // 定义数码管从左到右,符号 -
#define locSeg7sec10 0x40 // 定义数码管从左到右,秒十位
#define locSeg8sec1 0x80 // 定义数码管从左到右,秒个位
code unsigned char Seg_Table[] =
{
0xc0, // 0
0xf9, // 1
0xa4, // 2
0xb0, // 3
0x99, // 4
0x92, // 5
0x82, // 6
0xf8, // 7
0x80, // 8
0x90, // 9
0x88, // A,10
0x83, // b,11
0xc6, // C,12
0xa1, // d,13
0x86, // E,14
0x8e, // F,15
0x00, // 全部点亮,16,8.
0xbf // 符号-,17
};
typedef unsigned char uint8_t;
uint8_t ledInfo = 0xFF;
uint8_t segInfo; // 定义数码管信息用于自检函数中
uint8_t basicTimerCounter = 0;
uint8_t boolCounter1s = 0; // 达到1s计时单位
uint8_t cntSecs = 0; // 计数秒
uint8_t cntMins = 0; // 计数分
uint8_t cntHours = 0; // 计数小时
uint8_t recv_data = 0; // uart接收数据
uint8_t keyStatus;
uint8_t keyValue;
uint8_t s4LedOnOff = 0; // 按键s4 长按时不切换状态
uint8_t s5LedOnOff = 0;
uint8_t s4Falling = 0; // 按键s4的下降沿
uint8_t s5Falling = 0;
uint8_t timeStatus;
void Timer0_Init(void) // 5毫秒@12.000MHz
{
AUXR |= 0x80; // 定时器时钟1T模式
TMOD &= 0xF0; // 设置定时器模式
TL0 = 0xA0; // 设置定时初始值
TH0 = 0x15; // 设置定时初始值
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时
ET0 = 1; // 使能定时器0中断
EA = 1;
}
void UartInit(void) // 9600bps@12.000MHz
{
SCON = 0x50; // 8位数据,可变波特率
AUXR |= 0x40; // 定时器时钟1T模式
AUXR &= 0xFE; // 串口1选择定时器1为波特率发生器
TMOD &= 0x0F; // 设置定时器模式
TL1 = 0xC7; // 设置定时初始值
TH1 = 0xFE; // 设置定时初始值
ET1 = 0; // 禁止定时器中断
TR1 = 1; // 定时器1开始计时
ES = 1;
EA = 1;
}
void Delay1ms() //@12.000MHz 因为定时器主要用于计数小时、分、秒,所以其他延时采用这种方式
{
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j)
;
} while (--i);
}
void Delay500ms() //@12.000MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k)
;
} while (--j);
} while (--i);
}
void sysInit()
{
Relay(0x00);
Buzz(0x00);
lightLED(0xff);
lightCOM(0x00);
lightSEG(0xff);
}
void deviceCheck()
{
uint8_t i;
uint8_t chkledInfo = 0xff;
for (i = 0; i < 8; i++)
{
chkledInfo = chkledInfo << 1;
lightLED(chkledInfo);
Delay500ms();
}
chkledInfo = 0xff;
for (i = 0; i < 8; i++)
{
chkledInfo = chkledInfo << 1;
lightLED(~chkledInfo);
Delay500ms();
}
segInfo = 0xFF; // 数码管专用变量
for (i = 0; i < 8; i++)
{
segInfo = segInfo << 1;
lightCOM(~segInfo);
lightSEG(Seg_Table[16]);
Delay500ms();
}
segInfo = 0xFF;
for (i = 0; i < 8; i++)
{
segInfo = segInfo << 1;
lightCOM(segInfo);
lightSEG(Seg_Table[16]);
Delay500ms();
}
}
void showSg1h10(uint8_t h10) // 从左到右,第1位,小时十位
{
lightCOM(locSeg1h10);
lightSEG(Seg_Table[h10]);
}
void showSg2h1(uint8_t h1)
{
lightCOM(locSeg2h1);
lightSEG(Seg_Table[h1]);
}
void showSg3dash()
{
lightCOM(locSeg3dash);
lightSEG(Seg_Table[17]);
}
void showSg4min10(uint8_t min10)
{
lightCOM(locSeg4min10);
lightSEG(Seg_Table[min10]);
}
void showSg5min1(uint8_t min1)
{
lightCOM(locSeg5min1);
lightSEG(Seg_Table[min1]);
}
void showSg6dash()
{
lightCOM(locSeg6dash);
lightSEG(Seg_Table[17]);
}
void showSg7sec10(uint8_t sec10)
{
lightCOM(locSeg7sec10);
lightSEG(Seg_Table[sec10]);
}
void showSg8sec1(uint8_t sec1)
{
lightCOM(locSeg8sec1);
lightSEG(Seg_Table[sec1]);
}
void counterSeconds() // 计数小时、分、秒
{
if (boolCounter1s)
{
++cntSecs;
if (cntSecs == 60)
{
++cntMins;
cntSecs = 0;
if (cntMins == 60)
{
++cntHours;
cntMins = 0;
if (cntHours == 60)
cntHours = 0;
}
}
boolCounter1s = 0;
}
}
void sysTime() // 显示系统时间
{
counterSeconds();
switch (timeStatus) // 使用状态机依次轮流显示系统运行时间
{
case 0:
showSg1h10(cntHours / 10);
Delay1ms(); // 显示间隔1ms
timeStatus = 1;
break;
case 1:
showSg2h1(cntHours % 10);
Delay1ms();
timeStatus = 2;
break;
case 2:
showSg3dash();
Delay1ms();
timeStatus = 3;
break;
case 3:
showSg4min10(cntMins / 10);
Delay1ms();
timeStatus = 4;
break;
case 4:
showSg5min1(cntMins % 10);
Delay1ms();
timeStatus = 5;
break;
case 5:
showSg6dash();
Delay1ms();
timeStatus = 6;
break;
case 6:
showSg7sec10(cntSecs / 10);
Delay1ms();
timeStatus = 7;
break;
case 7:
showSg8sec1(cntSecs % 10);
Delay1ms();
timeStatus = 0;
break;
default:
timeStatus = 0;
break;
}
}
void keyScan() // L7、L8本地控制LED的键盘扫描,因为此口接外部中断 0、1,这种方式应该也可以。
{
switch (keyStatus)
{
case 0:
if (s5L7 == 0)
{
s5Falling = 1;
keyStatus = 1;
}
else if (s4L8 == 0)
{
s4Falling = 1;
keyStatus = 1;
}
break;
case 1:
if (s5L7 == 0 && s5Falling)
{
keyValue = 5;
s5LedOnOff = ~s5LedOnOff; // 切换一次状态,使得LED停留某一种状态不变
s5Falling = 0;
}
else if (s4L8 == 0 && s4Falling)
{
keyValue = 4;
s4LedOnOff = ~s4LedOnOff;
s4Falling = 0;
}
if ((s5L7 == 0) && !s5Falling)
{
keyStatus = 1; // 锁定状态机,使得长按时,LED控制信号不切换,保持LED灯不闪烁。
}
else if ((s4L8 == 0) && !s4Falling)
{
keyStatus = 1;
}
else
{
keyStatus = 2;
}
break;
case 2:
if ((s5L7 == 1) && (s4L8 == 1))
{
keyStatus = 0;
}
break;
default:
keyStatus = 0;
keyValue = 0;
break;
}
}
void keyProc()
{
keyScan();
Delay1ms();
if (keyValue == 5) // L7
{
if (s5LedOnOff)
{
ledInfo = ledInfo & L7;
// 注意,不能使用P0口直接运算,比如P0&L7是不行的,因为数码管在不停变动,所以P0的数字也是在不停变动。
}
else
ledInfo = ledInfo | 0x40;
lightLED(ledInfo);
}
else if (keyValue == 4) // L8
{
if (s4LedOnOff)
{
ledInfo &= L8;
}
else
ledInfo |= 0x80;
lightLED(ledInfo);
}
}
void uartSend(uint8_t datasend) // 串口发送
{
SBUF = datasend;
while (TI == 0)
;
TI = 0;
}
void uartValue() // L1-L4远程控制组,
{
uint8_t tmpInfo1, tmpInfo2;
tmpInfo2 = ledInfo; // 保存当前ledinfo状态,用于后续输入不同A0、A1指令,可以控制亮灭
if ((recv_data & 0xF0) == 0xA0) // 0xA?
{
tmpInfo1 = ~(recv_data & 0x0f); // 计算当前指令,比如A1,对应操作为1111 1110
ledInfo = ledInfo & tmpInfo1; // 与原来的led状态进行与运算,比如S5控制的L7已经亮灯,
// 再与tmpInfo1进行与运算,L1、L7同时亮灯
lightLED(ledInfo);
ledInfo = tmpInfo2; // 恢复操作前的led状态信息
// 这里其实是个讨巧的方法,相当于:输入A1时,每次进入本函数后,重新运算一下
// 主要目的就是保证原来的S5-L7、S4-L8的亮灯状态不受影响。
}
else if (recv_data == 0xB0) // 0xB?
{
uartSend((cntHours / 10 << 4) | (cntHours % 10));
uartSend((cntMins / 10 << 4) | (cntMins % 10));
uartSend((cntSecs / 10 << 4) | (cntSecs % 10));
}
}
void main()
{
Timer0_Init();
UartInit();
sysInit();
deviceCheck();
while (1)
{
sysTime();
keyProc();
uartValue();
}
}
void Timer0_Isr(void) interrupt 1
{
// 5毫秒@12.000MHz,1s,1000ms,计200次
if (basicTimerCounter++ == 200)
{
boolCounter1s = 1;
basicTimerCounter = 0;
}
}
void uart_isr(void) interrupt 4
{
if (RI == 1)
{
RI = 0;
recv_data = SBUF;
}
}