看到板子上一大块暗着的像时钟一样的数码管吗?今天让我们给他点起。
一、原理图讲解
如图是数码管模块的原理图,可以看到,最下面的数码管有左右两边端口。随便挑一个,不难发现,左边的控制一个’8’中哪一横亮(A’->a),右边则控制哪个’8’亮(COM1->DP1)。
那么就有问题了,数码管一次只能亮一个’8’,怎么做到同时显示数字?只要让每个’8’以极短的时间显示就行。
想要显示数码管,这就是关键:每2ms切换一根数码管显示。
二、要求提出
- 每2ms切换数码管显示
- 数码管的显示不能干扰正常循环代码,否则可能导致阻塞。
三、代码编写
前情提要:这块数码管是共阳极,即低电平点亮。(CA,Common Anode 阳极共同连接,阴极相互独立。与之相反的是CC,Common Cathode)
1.让我们先用while函数点亮一根数码管。
比如先点亮原理图左边数码管的左边第二个‘8’的bc,使其显示“1”(COM2-c)
基本思路:
1.将P0改为指向COM2(P0= 0000 0010),_74HC138函数选中Y6
2.将P0改为指向bc,即~0000 0110(0xF9),_74HC138函数选中Y7
于是就有了下面代码
(待填充)
2.数组存储
每次都写0xF9等十六进制太麻烦了,这里我们引入数组,做到输入一个数,就能代替一个十六进制。
数组的语法如下:
需要注意的是,在大多数的编程语言,0是作为数组的开端的。所以如果我希望取0xF8,尽管它在第8个,但我得取数组的第七位。
当我需要取数组内数时,只需写nixie_table[n]即可,n是0-9的正整数。
当需要修改数组内数字时,只需写nixie_table[n]=x即可,n是0-9的正整数,x是任意0-255的数(在任何进制下,x范围随定义的数组输出数字类型决定)
广告:
不想一个个推?打开“SEG_TABLE_对应断码.pdf”文件,找到你的专属十六进制断码!(笑)
PS:部分考纲要求的显示还是要自己推的,但减了不少麻烦。
3.定时器
想要2ms运行一次代码还不阻塞main循环,我们就需要引入一个定时器。
如果我们初始化一段2ms的定时器,并为定时器赋予一串代码,软件就会这样跑:(黑为main,红为定时器。实际上红段会小很多,此处仅是为了理解。)
可以看到,即使只有一个核心,单片机也会在规定时间运行要执行的代码,并且不阻塞源代码的持续。
在ISC中,我们可以直接生成定时器代码,具体步骤如下。
在软件界面打开“定时器计算器”,调整以下参数:
1.时钟频率:12.0000Hz(考纲要求)
2.定时长度:1毫秒(便于计量计算)
3.选中“使能定时器中断”
点击"生成C代码"并复制你会得到这串代码,相关注释已经放在旁边
void Timer0_Isr(void) interrupt 1 { //这里就是红线部分 } void Timer0_Init(void) //1毫秒@12.000MHz { AUXR |= 0x80; //定时器时钟1T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0x20; //设置定时初始值 TH0 = 0xD1; //设置定时初始值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; //使能定时器0中断EA = 1; //使计时器开启(这段要自己写,否则开不了) }将它直接放到main.c里面,于是就有
//main.c #include <STC15F2K60S2.H> #include "_74HC138.h" #include "Led.h" void Timer0_Init(void) //计时器 { AUXR |= 0x80; TMOD &= 0xF0; TL0 = 0x20; TH0 = 0xD1; TF0 = 0; TR0 = 1; ET0 = 1; EA = 1; } void Timer0_Isr(void) interrupt 1 { //红线部分 } void main() { //刚上电的运行 while(1) { //初始化后的总循环(蓝线部分) } }好,定时器到这里就结束了,后面我们将用计时器直接使灯循环亮起。
4.开始编写
请读者建立一个意识,数码管的循环显示与更改需要解耦,占据更小的运行时间,同时还使代码更易编写。
没看懂上面的话?翻译一下,就是我们可以将用数组保存数码管需要显示的数字,
并创建一个全新的,使数码管按照数组点亮的函数循环2ms运行。如图,右边扫描函数一直运行,一直使数码管亮起,左边while函数只需修改数组。 如此,我们就需要创建三个函数:
让单个数码管‘8’亮起的函数,让数码管一直循环亮的扫描函数,与修改数组的函数。
//nixie,c // #include <STC15F2K60S2.H> #include "_74HC138.h" #include "nixie.h"code1unsigned char nixie_table[11] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0xFF}; //0 1 2 3 4 5 6 7 8 9 Null unsigned char nixie_display[9] ={10,10,10,10,10,10,10,10,10}; //让单个数码管‘8’亮起的函数 void nixie(unsigned char Location, display) // { P0 = 0xFF; //对P0改为0xFF,为所有数码管置零做准备,避免不必要的显示 _74HC138_init(7); //选中Y7,更改数码管显示为全暗 P0 = 0x01 << (Location-1); //使用P0预选中相对应'8',为点亮做准备 _74HC138_init(6); //选中Y6,结合P0选中相对应的'8' P0 = display; //指明对应数码管的显示 _74HC138_init(7); //选中Y7,更改数码管的显示 } //让数码管一直循环亮的扫描函数 void nixie_loop() {static2unsigned char n = 1; //声明一个char,作为每次刷新的位置记录 nixie(n,nixie_table[nixie_display[n]]); //每次char刷新后,执行一个'8'的点亮 n++; //每次数码管点亮后,自动将位置后移一格 if(n >= 9){n = 1;} //当n超过8了,自动回到1 } //修改数组的函数 void set_nixie_display(unsigned char Location, display){ nixie_display[Location] = display; //将显示数组调整成需要的数 }那么这个时候,就可以使用定时器让数码管点亮了,此处我直接把代码贴出来并做出注释。
//main.c #include <STC15F2K60S2.H> #include "_74HC138.h" #include "nixie.h" void Timer0_Init(void) { AUXR &= 0x7F; TMOD &= 0xF0; TL0 = 0x18; TH0 = 0xFC; TF0 = 0; TR0 = 1; ET0 = 1; EA = 1; //这一段别忘了加 } void Timer0_Isr(void) interrupt 1 { static unsigned char count1 = 0; //从软件开始的一刻,设定一个1ms自动加1的数,方便计时。 count1++; if(count1 >= 2) //如果2ms了 { nixie_loop(); //执行一次循环,让所有'8'亮一下 count1 = 0; //置零,到了下一次2ms可以循环 } } void main() { _74HC138_init_all(); //初始化所有74HC138下的硬件 Timer0_Init(); //初始化并开启定时器 while(1) //软件主循环 { set_nixie_display(1,2); //设置第一个灯管亮“2”这个数字 } }此处省略关于头文件的书写与74HC138选中的函数,有需要可以查看上一章节,也可以直接下载文档并导入。
文档链接:https://hkpanel.sakurarun.vip/s/G5zirAY7sBE5E?operateNode=local
那么到现在,我们就完成了数码管灯的点亮,是时候留点思考题了!!!
在所有灯(电源灯除外)熄灭的前提下,编写一个20s的计时器,当最后3s时,LED灯4号亮起,0s后,一直保持0s(不要求数字在哪里显示)【可能用到的计算:{'/'除得商,12/10=1},{'%'除得余,12%10=2}】//main.c #include <STC15F2K60S2.H> #include "_74HC138.h" #include "nixie.h" #include "LED.h"void Timer0_Init(void)
{
AUXR &= 0x7F;
TMOD &= 0xF0;
TL0 = 0x18;
TH0 = 0xFC;
TF0 = 0;
TR0 = 1;
ET0 = 1;
EA = 1; //这里记得补充。
}static char time = 21;
//此处使用21,因为count2运行时先减1再显示
void Timer0_Isr(void) interrupt 1
{
static unsigned char count1 = 0;
static unsigned int count2 = 0;
//设置一个count2,方便以秒计时
count1++;
count2++;
if(count1 >= 2)
{
nixie_loop();
count1 = 0;
}
if(count2 >= 1000){ //如果1s之后
if(time>0){ //且20s倒计时还没到0s
time–; //先减1s
set_nixie_display(1,time/10); //再将十位调到时间与10整除得到的商
set_nixie_display(2,time%10); //再将个位跳到时间与10整除得到的余
if(time<=3) //如果时间小于或等于3s
{LED(4);} //把4号LED亮起来
count2 = 0;} //并置零count2,等待下一个1s}
}void main()
{
_74HC138_init_all();
Timer0_Init(); //这里记得初始化函数,不要让定时器开不了。
while(1)
{}
}