51单片机按键数码管显示 时间片轮转+状态机

前段时间看到了分层思想、时间片轮转和状态机的相关文章正好近期手上有一些小项目,于是就进行了一些尝试。相关的概念就不在细说了,很多嵌入式的公众号文章都有介绍过,下面只叙述下我开发的经历帮助后来者少走弯路也是我个人的一个记录。
我在看到讲述相关思想的文章后在脑子里想着架构出一个相对比较完善的系统。
分层思想不用多说根据相应的软硬件结构进行分层即可,其中的好处会在代码复用时体现出来。
时间片轮转思想在我个人实践的过程中走了很多弯路,本人使用的硬件为清翔电子的最小系统板,在最开始进行开发时使用定时器的方式对固定延时变量(REF20MS等)进行相应的环形累加(if(REF20MS==0)REF20MS=NUM20MS)(NUM20MS为宏定义的定时器计数记录到对应时间所需要的值)。然后当相应的变量计数完成后对相应的标志位进行计数。
在我最初的构想中,希望能够完成一个多个需要相同的延时时间的任务对同一个标记位的检测即可完成延时的效果,但是在实际实现过程中遇到了很多问题。
1、如何判定任务1完成了20ms延时(仅作为举例),判定相应的延时时间的话需要对REF20MS的值进行记录当该变量再一次到达记录值时则说明完成20ms延时,这有一个问题就是定时器中断的时间一定时比单片机主函数中运行的时间要慢的于是当该变量进行记录时定时器未能及时对延时记录变量进行增减从而导致没有延时的现象,当然这种问题可以通过再增加一个记录次数的标记位来解决但是还有更好的解决方式我下面会讲。
2、如何多个任务同时标记并使用同一个延时变量,这个问题使用变量记录加标记位的方式可以去解决但是结构复杂较为繁琐。
在遇到上述问题后我改变了想法,使用一个u32位(u64更好但是51不支持u64)的变量(u32 TimeRef)作为统一的一个时间片(定时器125us中断一次,足够使用一周左右),对每个任务定义一个_DELAY变量(如按键:u32 KEY_DELAY) 在每次需要延时时KEY_DELAY=TimeRef+NUM_20MS;(NUM20MS为宏定义的定时器计数记录到对应时间所需要的值),在使用状态机的思想进行状态转移即可。能够较为简单的时间一些任务轮转的功能,整体代码理解起来也比较简单。整体单片机代码如下:

#include<reg52.h>
#include<intrins.h>
//尝试分层思想与时间片轮转

#define u8  unsigned char
#define u16 unsigned int
#define u32 unsigned long int
//按键引脚映射
sbit         KEY1=P3^5;
sbit         KEY2=P3^4;
sbit         KEY3=P3^3;
sbit         KEY4=P3^2;
sbit         LED1=P1^0;
u8                 KEY_DAT=0;//按键寄存器按键为1存储当前被按下的按键键值,0为无按键

u8 SEG7[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

//时间片为125us
#define NUM_1MS                 8
#define NUM_2MS                 16
#define NUM_5MS                        40
#define NUM_20MS                 160
#define NUM_100MS         800
#define NUM_200MS         1600
#define NUM_500MS         4000
#define NUM_1000MS         8000

//时间片轮转变量
u32 TimeRef=0;




//任务延时变量
//按键任务
u32 KEY_DELAY                                =0;
u8  KEY_STATE                                =0;
u8         KEY_DATx                                =0;
//显示任务
u32 SEG_DELAY                =0;
u8  SEG_STATE                =0;//0位标记是否第一次进入
u8         SEG_NUM[4]        ={0,0,0,0};
u8         SEG_I                          =0;

/函数声明/
//读按键键值
u8 ReadKeyDat(void);
//读按键寄存器
u8 ReadKeyReg(void);
//中断初始化函数
void IsrInit(void);
//数码管显示控制函数
void SegDisplay(u16 num);

void main(void)
{
        u16 num=0;
        u8 key=0;
        IsrInit();
        while(1)
        {               
                SegDisplay(num);
                key = ReadKeyDat();
                if(key==1)
                        num++;
                if(key==2)
                        num--;
//                if(key==3)
//                        LED1=0;
//                if(key==4)
//                        LED1=1 ;
        }
        
}
//数码管显示控制函数
void SegDisplay(u16 num)
{
        u8 i=0;
        if(SEG_STATE==0)//分割字符状态
        {
                SEG_I=0;
                for(i=0;i<4;i++)
                {
                        SEG_NUM[SEG_I++]=num%10;
                        num=num/10;
                }
                SEG_STATE=1;
               
        }
                          
        if(SEG_STATE==1)//显示状态
        {
                P0=SEG7[SEG_NUM[4-SEG_I]];//[];
                P2=~(0x01<<(SEG_I-1));
                SEG_I--;
                SEG_DELAY=TimeRef+NUM_5MS;
                SEG_STATE=2;
        }

        if(SEG_STATE==2)//延时等待状态
        {
                if(TimeRef >= SEG_DELAY)
                {
                        //判断是否为最后一次延时
                        if(SEG_I==0)
                                SEG_STATE=0;
                        else
                                SEG_STATE=1;
                }
        }
        
}



//读按键键值
u8 ReadKeyDat(void)
{
        ReadKeyReg();
        if(KEY_STATE == 0)//第一次读取状态
        {
                if(KEY_DAT!=0)//读取到按键
                {
                        KEY_DATx=KEY_DAT;
                        KEY_DELAY=TimeRef+NUM_100MS;
                        KEY_STATE=1;
                }
        }
        
        if(KEY_STATE == 1)//延时状态
        {
                if(TimeRef >= KEY_DELAY)
                {
                        KEY_STATE=2;
                }
        }
        if(KEY_STATE == 2)//第二次读取状态
        {
                if(KEY_DATx == KEY_DAT)//读取到按键
                {
                        KEY_STATE=0;
                        return KEY_DATx;
                }
                KEY_STATE=0;
        }
        return 0;
}

//读按键寄存器
u8 ReadKeyReg(void)
{
        if(KEY1==0)
        {
                KEY_DAT=1;
                return 1;
        }
        if(KEY2==0)
        {
                KEY_DAT=2;
                return 1;
        }
        if(KEY3==0)
        {
                KEY_DAT=3;
                return 1;
        }
        if(KEY4==0)
        {
                KEY_DAT=4;
                return 1;
        }
        KEY_DAT=0;
        return 0;
}

//中断初始化函数
void IsrInit(void)
{
        EA=1;
        
        //使能定时器中断并开启定时器
//        ET0=1;
//        TR0=1;
        //ET1=1;
        //使能两个外部中断并将其设定为跳沿触发
//        EX0=1;
//        IT0=1;
//        EX1=1;
//        IT1=1;
        //定时器0,方式2,计数器
        TMOD|=0x02;
        TH0=0x8d;//125us 中断一次
        TL0=0x8d;
        ET0=1;
        TR0=1;
//        //使用定时器1,方式2,计数器
//        TMOD|=0x20;
//        TH1=0x8d;//125us 中断一次
//        TL1=0x8d;
}
//定时器0,提供基础时钟125us中断1次
//统一时间完成标志位可能没有意义//
int Tim0Isr() interrupt 1 using 1
{
        TimeRef++;        
//        P0=SEG7[TimeRef/100%10];//[];
//        P2=0xfe;
}

KEIL工程下载:http://www.51hei.com/bbs/dpj-214415-1.html

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
51单片机八位数码管同时显示需要使用到多路数码管显示驱动芯片,如常用的TM1638、MAX7219等。以下是使用TM1638驱动芯片的示例代码: ```c #include <reg52.h> #include <intrins.h> #define uchar unsigned char #define uint unsigned int sbit TM1638_DIO = P1^0; // 定义TM1638数据线引脚 sbit TM1638_CLK = P1^1; // 定义TM1638时钟线引脚 sbit TM1638_STB = P1^2; // 定义TM1638片选线引脚 uchar code DIGITS[] = { // 定义数字0-9、A-F的编码 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71 }; void delay(uint ms) // 延时函数 { uint i, j; for(i=0; i<ms; i++) for(j=0; j<125; j++); } void TM1638_SendByte(uchar data) // 发送一个字节数据 { uchar i; for(i=0; i<8; i++) { TM1638_DIO = (data & 0x01); data >>= 1; TM1638_CLK = 1; _nop_(); _nop_(); TM1638_CLK = 0; } } void TM1638_SendCommand(uchar command) // 发送命令 { TM1638_STB = 0; TM1638_SendByte(command); TM1638_STB = 1; } void TM1638_Init() // 初始化TM1638 { TM1638_SendCommand(0x8f); // 打开显示,设置亮度为最高 TM1638_SendCommand(0x44); // 设置地址自动加1 TM1638_STB = 0; TM1638_SendByte(0xc0); // 设置起始地址为0 for(uchar i=0; i<16; i++) TM1638_SendByte(0x00); // 清空显示缓存 TM1638_STB = 1; } void TM1638_Display(uchar* digits) // 显示函数 { TM1638_STB = 0; TM1638_SendByte(0xc0); // 设置起始地址为0 for(uchar i=0; i<8; i++) { TM1638_SendByte(digits[i]); // 发送显示数据 TM1638_SendByte(DIGITS[10+i]); // 发送小数点数据(没有小数点可省略) } TM1638_STB = 1; } void main() { uchar digits[8] = { // 要显示的数字 1, 2, 3, 4, 5, 6, 7, 8 }; TM1638_Init(); // 初始化TM1638 while(1) { TM1638_Display(digits); // 显示数字 delay(100); // 延时100ms } } ``` 在上述代码中,通过定义TM1638_DIO、TM1638_CLK、TM1638_STB三个引脚来控制TM1638驱动芯片的数据、时钟和片选信号。在TM1638_Init()函数中,设置了TM1638的工作模式、亮度和起始地址,并清空了显示缓存。在TM1638_Display()函数中,通过循环发送8位数字数据和小数点数据来实现八位数码管的显示。在主函数中,循环调用TM1638_Display()函数并延时100ms来更新显示内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值