some tips

闪烁可以使用取反的方法


LedInfo = ~LedInfo; // 闪烁3次

依次点亮,或依次熄灭可以使用 左移或右移

LedInfo = LedInfo << 1; // 依次点亮

当顺序实现不同情况时,使用状态机FSM,有利理解及代码实现。

case 0: // 默认状态为3次闪烁
        LedInfo = ~LedInfo;
        Led(LedInfo);
        if (LedCounter++ == 5) // 计到5时,正好是3次亮灭重复出现
        {
            LedStatus = 1;  // 切换到下一个状态机
            LedCounter = 0; // 计数归0
            LedInfo = 0xff; // 设置下个状态的LED状态
        }
        break;
    case 1:                     // 依次点亮情况
        LedInfo = LedInfo << 1; // 左移
        Led(LedInfo);           // 点亮
        if (++LedCounter == 8)  // 一共8位LED,所以计数8
        {
            LedStatus = 2;  // 切换状态机
            LedCounter = 0; // 计数归0
            BoolRelay = 1;  // 依次点亮LED后,就是操作继电器,所以设定继电器状态启用
        }
        break;
    case 2:             // 等待状态,等待继电器 吸合 再断开后,进行依次熄灭LED的中间过渡
        LedInfo = 0x00; // 因为依次熄灭使用的是取反操作,所以初始状态设为0x00。
        if (!BoolRelay) // 继电器关闭,进入LED下一状态
            LedStatus = 3;
        break;

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141139315

数码管的显示,建议使用状态机FSM。另外,数码管间隔显示时间1ms为宜。

void DynamicSeg()
{
    switch (segStatus)
    {
    case 0: // 2,第1个显示
        PortEnabled = 0x01;
        OneSegDisplay(ShowInfos[segStatus], PortEnabled);
        segStatus = 1;
        break;
    case 1:                 // 0,第2个显示
        PortEnabled = 0x02; // 换成 PortEnabled = PortEnabled << 1会出现重影,未解决。
        OneSegDisplay(ShowInfos[segStatus], PortEnabled);
        segStatus = 2;
        break;
    case 2: // 1,第3个显示,
        PortEnabled = 0x04;
        OneSegDisplay(ShowInfos[segStatus], PortEnabled);
        segStatus = 3;
        break;
    case 3: // 8,第4个显示
        PortEnabled = 0x8;
        OneSegDisplay(ShowInfos[segStatus], PortEnabled);
        segStatus = 4;
        break;
    case 4: // -,第5个显示
        PortEnabled = 0x10;
        OneSegDisplay(ShowInfos[segStatus], PortEnabled);
        segStatus = 5;
        break;
    case 5: // -,第6个显示
        PortEnabled = 0x20;
        OneSegDisplay(ShowInfos[segStatus], PortEnabled);
        segStatus = 6;
        break;
    case 6: // 月份十位
        PortEnabled = 0x40;
        OneSegDisplay(ShowMonth / 10, PortEnabled); // 十位固定显示0
        segStatus = 7;
        break;
    case 7: // 月份个位
        PortEnabled = 0x80;
        OneSegDisplay(ShowMonth % 10, PortEnabled); // 个位显示
        segStatus = 8;
        break;
    case 8:
        if (BoolHalfOneSecond) // 如果达到设定的增长间隔时间,这里用第2个定时器Timer1也可以实现。
        {
            ShowMonth++;
            BoolHalfOneSecond = 0;
        }
 
        if (ShowMonth == 13) // 如果1-12已经循环显示完,重新循环
        {
            ShowMonth = 1;
        }
        segStatus = 0; // 回到状态0
        break;
 
    default:
        segStatus = 0;
        PortEnabled = 0x01;
        ShowMonth = 1;
        break;
    }
}

————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141168052

加Delay1ms()的:

switch (arrayStatus)
    {
    case 0:
        min10Seg1(stopWatchTimer.min10);
        Delay1ms();
        arrayStatus = 1;
        break;
    case 1:
        min1Seg2(stopWatchTimer.min1);
        Delay1ms();
        arrayStatus = 2;
        break;
    case 2:
        dashSeg3();
        Delay1ms();
        arrayStatus = 3;
        break;
    case 3:
        sec10Seg4(stopWatchTimer.sec10);
        Delay1ms();
        arrayStatus = 4;
        break;
    case 4:
        sec1Seg5(stopWatchTimer.sec1);
        Delay1ms();
        arrayStatus = 5;
        break;
    case 5:
        dashSeg6();
        Delay1ms();
        arrayStatus = 6;
        break;
    case 6:
        ms10Seg7(stopWatchTimer.ms10);
        Delay1ms();
        arrayStatus = 7;
        break;
    case 7:
        ms1Seg8(stopWatchTimer.ms1);
        Delay1ms();
        arrayStatus = 0;
        break;

————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141267009

需要锁定某个状态时,比如长按不松手,建议使用状态机FSM,加一个下降沿变量锁定。

case 1:
        if ((k7 == 0) || (k6 == 0)) // 如果S6、S7按下,产生 下降沿,
        {
            keyStatus = 2;
            Falling = 1;
        }
        if (((k5 == 0) || (k4 == 0)) && (selectEnableS6 || selectEnableS7)) // 如果S5、S4按下,同时是在S7或S6已经按下的情况,产生 下降沿,
        {
            keyStatus = 3;
            Falling = 1;
        }
        selectEnableS6 = 1; // 初始化S6启用状态
        selectEnableS7 = 1; // 初始化S7启用状态
 
        break;
 
    case 2:
        if ((k7 == 0) && selectEnableS7) // S7按下低电平,同时满足启用状态(用于对应题目中"S6、S7不可响应"操作的要求)
        {
            selectedValue = 7; // 根据按键值对应返回值
 
            if (Falling) // 第1次按下时,即下降沿时
            {
                ledEnableS7 = ~ledEnableS7; // 用于控制点亮LED的标记,长按不翻转
            }
        }
        else if ((k6 == 0) && selectEnableS6) // 同S7
        {
            selectedValue = 6;
            if (Falling)
            {
                ledEnableS6 = ~ledEnableS6;
            }
        }
        if ((k7 == 0) || (k6 == 0)) // 按键不松开,停在当前状态,长按状态,在测试时,如果不区别长按短按,LED灯会闪烁,因为程序中有翻转情况
        {
            keyStatus = 2; // 停留在当前状态
            Falling = 0;   // 没有下降沿,
        }
        else
        {
            keyStatus = 3;
            Falling = 1;
        }
 
        break;
 
    case 3:
        if (k5 == 0) // 同S7、S6
        {
            controlValue = 5;
            if (Falling)
            {
                ledEnableS5 = ~ledEnableS5;
            }
        }
        else if (k4 == 0) // 同S7、S6
        {
            controlValue = 4;
            if (Falling)
            {
                ledEnableS4 = ~ledEnableS4;
            }
        }
        if ((k5 == 0) || (k4 == 0)) // 按键不松开,停在当前状态
        {
            keyStatus = 3;
            Falling = 0;
        }
        else
            keyStatus = 4;
        break;

————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141193720
if ((s4 == 0) | (s5 == 0)) // 如果长按锁定在当前状态
        {
            falling = 0;
            keyStatus = 1;
        }
        else
            keyStatus = 2;
        break;

————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141267009

矩阵键盘,可以使用行列组成一个8位变量来进行检测状态,

uint8_t GetPortState()
{
    uint8_t portState;
    portState = P3 & 0x3F;                              // 0x3F: 0011 1111
    (col2) ? (portState |= 0x40) : (portState &= 0xbf); // 0x40: 0100 0000,0xbf:1011 1111
    (col1) ? (portState |= 0x80) : (portState &= 0x7f); // 0x80: 1000 0000, 0x7f: 0111 1111
    return portState;
}
v
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141217673

也可以使用单独列、行来实现检测。

// 矩阵键盘 行
sbit row1 = P3 ^ 0;
sbit row2 = P3 ^ 1;
sbit row3 = P3 ^ 2;
sbit row4 = P3 ^ 3;
// 矩阵键盘 列
sbit col1 = P4 ^ 4;
sbit col2 = P4 ^ 2;
sbit col3 = P3 ^ 5;
sbit col4 = P3 ^ 4;
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141218118

为了对应矩阵的数值,可以自定义一下二维数组,注意,数组大小要定义。

// 从左到右,从上到下,依次显示0~F,定义一个二维数组,注意数组大小一定要写,不然会报销
code uint8_t ArrayNum[4][4] = {
    {0, 1, 2, 3},
    {4, 5, 6, 7},
    {8, 9, 10, 11},
    {12, 13, 14, 15}};

————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141217673

按键消抖,就是定时器,或者Delay1ms()函数。

if (counterEnable) // 达到计数间隔,即消抖
    {
        KeyScan();
        if (keyValue == 1) // S7对应L1,
            ledInfo = ~1;
        else if (keyValue == 2) // S6对应L2
            ledInfo = ~2;
        else if (keyValue == 3) // S5对应L3
            ledInfo = ~4;
        else if (keyValue == 4) // S4对应L4
            ledInfo = ~8;
        else if (keyValue == 255) // 抬起按键
            ledInfo = ~0;
        LED(ledInfo);
        counterEnable = 0;
    }
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141176358

外部中断,主要是看原理图中找到的复用按键,S5/S4、P32、P33

void INT0_ISR() interrupt 0
{
    if (KeyS5 == 0)
    {
        interruptEnalbe = 1;
        LightLed8(); // 这个可以写到外面,比如增加Led8Proc()函数,将LightLe8与OffLed8作为子函数调用。
    }
    else
        interruptEnalbe = 0;
}

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141230458

某个LED灯,或某端口某位不需要改变,同一端口另一位需要变动时,可以直接对端口使用与或运算。注意使用条件,端口数据不是实时变动。

case 0:
        LED(P0 & L1);      // 点亮L1
        if (EnableHalfSec) // 0.5s延时
        {
            StatusHalfSec = 1;
            EnableHalfSec = 0;
        }
        break;
    case 1:
        LED(P0 | 0x01);    // 熄灭L1
        if (EnableHalfSec) // 0.5秒延时
        {
            StatusHalfSec = 0;
            EnableHalfSec = 0;
        }
        break;

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141231326

端口数据实时变动的例子:

因为程序前面实时操作数码管显示时间,所以P0口数据不停变动。

所以在LED灯这里,只能定义一个变量LedInfo,保存上个传到P0口操作LED灯的数值,而不会受到数码管P0口数据的影响。

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);
    }

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141304759

在串口数据这儿也是,增加一个临时变量tmpInfo2,保存前一状态。

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的亮灯状态不受影响。
    }
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/mouze2000/article/details/141304759

每个模块的单独练习正常了,不代表综合运用会没问题。

工厂灯光控制系统这个项目中,数码管,LED灯,串口,每个单独运用没问题,但放在一起,因为P0口是复用的,所以状态信息在不停变,需要建立一个变量对应每个模块来保存当前模块的上一个状态。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

zfdc

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

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

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

打赏作者

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

抵扣说明:

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

余额充值