单元训练15:工厂灯光控制系统

蓝桥杯,小蜜蜂,单元训练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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zfdc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值