ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

单片机学习(三)开发板动态数码管的控制

2021-08-04 11:05:58  阅读:227  来源: 互联网

标签:u8 单片机 time 开发板 数码管 second num Time


目录

分析动态数码管控制的原理

可以看出来,P0寄存器的每一个bit分别与输入a~dp联系起来,当对应的引脚为高电平时,对应的LED则点亮。

但是我们也可以发现每个8位数码管都是由输入a~dp进行控制的,那岂不是我们输入一个值,每个LED都呈现相同的内容了?但其实除了P0寄存器之外,P2的第2-4位作为了动态数码管的使能位,例如当这三位为1,1,1时,此时编号为0的寄存器处于使能状态,它可以输出发光的内容,而其他寄存器处于禁用状态。

由此可见,我们在同一个时刻最多只能有1个八位数码管可以发光,其他的7个都是熄灭的,那如何使我们能看到它们同时发光呢?其实很简单,只要使能位切换得够快,我们肉眼就会发现变化的几个数码管都是发光的了。

编码实现效果

数码管计数器

在这里插入图片描述
首先我们需要先将0~F这16个字符使用数码管表示出来,例如0这个字符,输入a,b,c,d,e,f应该为1(即高电平),则我们输入给P0的值应该是0b0011 1111,即0x3f,这样再设置P2[2..4]=0b111(设置使能位,使第0个数码管可以工作),这样我们就可以在第0个数码管看到字符0了,其他以此类推,就可以得到这16个字符对应的二进制编码。

为了方便使用,我们使用一个数组将这16个字符的编码存储起来:

u8 code smgduan[16]={
    0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71            
};

然后我们可以实现计数器,即使一个数码管从0到F不断地变化,核心代码如下:

int main() {
    u8 i;

    P0 = smgduan[2];
    while (1)
    {
        for(i=0;i<16;i++) {
            P0 = smgduan[i];
            defaultDeley();
        }
    }
}

因为P2[2..4]默认为0b111,即默认是第一个数码管是可以显示的,所以我们会看到第一个数码管由0变化到F。

运行效果:

数码管流水灯

之前已经实现了LED流水灯,这次希望可以实现数码管的流水灯。要实现它,我们需要完成数码管索引到使能位的映射,即当我指定一个编号的数码管使能时,它就能工作。

我们先看一下映射关系:
这里0号数码管使能时,P2[2..4]=0b111;当1号数码管使能时,P2[2..4]=0b110...

可以发现这里有个明显的对应关系,设数码管编号为index,则只需令P2[2..4]=0b111-index,即可使对应的数码管工作了,但是如何使结果赋值到P2[2..4]这三位中呢?

此时我们可以使用“与置零,或置一”的方法进行部分赋值,例如下面的一个例子:

有变量toSet == 0b1010 0011此时我们希望使它的低4位变成1010,我们可以

  1. toSet = toSet & 0b1111 0000,此时toSet的值为0b1010 0000,即我们将它的低4位设置为了0
  2. toSet = toSet | 0b0000 1010,此时toSet的值为0b1010 1010,完成了要求

即我们对某几位赋值时可以先用与运算符清零,再使用或运算符赋值完成操作,而这里需要修改P[2...4]三位,我们先使用0x1110 0011(即~(0x7<<2))使用与运算符进行清零,再将(0b111-index)<<2使用或运算符进行赋值即可,代码如下:

void enableIndexLED(u8 index) {
    P2 = P2 & ~(0x07 << 2);
    P2 = P2 | (0x7 - index)<<2;
}

如此一来,我们的流水灯就可以轻松实现,核心代码如下(这里流水灯的数字为‘2’):

int main() {
    u8 i = 0;

    P0 = smgduan[2];
    while (1)
    {
        for(i = 0;i<8;i++) {
            enableIndexLED(i);
            defaultDeley();
        }
    }
}

运行效果:

显示0~65535的数字

首先我们将各个数码管需要显示的数字使用一个数组进行存储:u8 array[8];,这个数组的元素的值被初始化为notDisplay:

#define notDisplay 255
int main() {
	u8 array[8];
	u8 n= 8;
	for(i=0;i<n;i++) {
	    array[i] = notDisplay;
	}
}

然后我们编写一个函数,接收一个变量num,然后将它分解后填入到array数组中的位置中:

void updateArray(u16 num, u8* array, u8 n) {
    u8 i = n-1;
    if (num == 0) {
        array[i] = 0;
        return;
    }
    while (num)
    {
        array[i] = num %10;
        num /=10;
        i--;
    }
}

例如数字65535会使数组变为:

array=[notDisplay,notDisplay,notDisplay,6,5,5,3,5]

然后我们将数组的每个非notDisplay的元素映射到数码管即可。

显示数字函数:

// 第index个数码管显示num数字
void displayOneNum(u8 index, u8 num) {
    enableIndexLED(index);
    if (num == notDisplay) {
        P0 = 0x00;
    }else {
        P0 = smgduan[num];
    }
}

void display(u16 num, u8* array, u8 n) {
    u8 i;
    updateArray(num, array, n);
    for(i=0;i<n;i++) {
        displayOneNum(i, array[i]);
        deley(100);
        P0=0x00;//消隐
    }
}

主函数:

int main() {
    u8 i = 0;
    u8 array[8];
    u8 n = 8;
    u16 cnt = 1;

    for(i=0;i<n;i++) {
        array[i] = notDisplay;
    }
    while (1) {
        display(12345,array,n);
    }
}

这样我们就可以在数码管中看到“65535”的数字字符串了,运行结果:

计数器升级版

这次的计数器是可以从0计数到65535的

displayForAWhile()函数如下所示:

void displayForAWhile(u16 num, u8* array, u8 n) {
    u8 times = 125;
    while (times--)
    {
        display(num,array,n);
    }
}

当输入一个数字时,这个函数中会重复显示这个数字125次,因为1s大概是deley(100000)消耗的时间,这里渲染一次一个数字的时间大概是deley(100*8)=deley(800),然后渲染125次的时间约为deley(100000),即这个函数会显示num约一秒钟,所以我们可以用来做个简单的计数器,主函数代码如下:

int main() {
    u8 i = 0;
    u8 array[8];
    u8 n = 8;
    u16 cnt = 1;
    
    while (1)
    {
        for(i=0;i<n;i++) {
            array[i] = notDisplay;
        }
        for(cnt = 0; cnt <= 65535; cnt++) {
            displayForAWhile(cnt, array, n);
        }
    }
}

这样它就会从0一直变化到65535了。

运行结果(这是加快后的运行结果):

模拟时钟

tips: 在设计的过程中其实使用到了面向对象的思想,那些Time_xxx的函数其实在观念上是Time类的成员函数或静态函数,这样设计可以更加有条理。

首先先设计存储时间的结构体:

typedef struct {
    u8 hour, minute, second;
} Time;

时间递增函数,每调用一次秒数加一,然后再处理进位:

void Time_increace(Time *time) {
    time->second++;
    if (time->second==60)
    {
        time->second = 0;
        time->minute++;
        if (time->minute==60)
        {
            time->minute = 0;
            time->hour++;
            if (time->hour == 24)
            {
                time->hour = 0;
            }
        }
    }
}

数字分解函数:

void Time_split(u8 num, u8* first, u8* second) {
    *second = num%10;
    num/=10;
    *first = num%10;
}

即功能是输入一个数字,这里是时或秒或分,把它分解成两个数并存储到first和second指针指向的空间。

数据展示函数:

static void Time_displayData(u8 num, u8 startIndex) {
    u8 first, second;
    Time_split(num, &first, &second);
    displayOneNum(startIndex, first);
    deley(100);
    P0=0x00;//消隐

    displayOneNum(startIndex+1,second);
    deley(100);
    P0=0x00;//消隐
}

输入一个数字,为时或秒或分,先将这个数字分解成两个数字,然后分别展示到第startIndex个数码管和第startIndex+1个数码管上。

中间分隔符展示函数:

void Time_displaySplitor() {
    displayOneChar(2,0x40);
    deley(100);
    P0=0x00;//消隐
    displayOneChar(5,0x40);
    deley(100);
    P0=0x00;//消隐
}

总的时间展示函数:

void Time_show(Time* time) {
    Time_displayData(time->hour, 0);
    Time_displayData(time->minute, 3);
    Time_displayData(time->second, 6);

    Time_displaySplitor();
}

展示一个时间一秒钟的函数:(即展示一个时间100次,大概1s的时间)

Time_showForASecond(Time* time) {
    u8 times = 100;
    while (times--) {
        Time_show(time);
    }
}

主函数:

int main() {
    Time time;
    time.hour = time.minute = time.second = 0;
    
    while (1)
    {
        Time_showForASecond(&time);
        Time_increace(&time);
    }
}

这样我们就实现了计时的功能。

运行效果(这里使用time.hour = 23; time.minute = 59; time.second = 50; 进行测试。):
在这里插入图片描述

标签:u8,单片机,time,开发板,数码管,second,num,Time
来源: https://www.cnblogs.com/CodeReaper/p/15097884.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有