目录
一,熟悉矩阵led小灯
1,点亮矩阵的一只led
2,点亮矩阵的一排led
3,点亮矩阵的全部led
static 关键字
unsigned 关键字
4,点阵的静态显示
2)心形矩阵显示代码
3)效果
二,课后练习题
1、用点阵做一个9到0的倒计时牌显示。
1)效果
2)代码
2、尝试实现流水灯、数码管和点阵的同时显示。
1)效果
2)代码
资料见本文所在的专栏:
一,熟悉矩阵led小灯
1,点亮矩阵的一只led
1)基础
2)代码
#include<reg52.h>
sbit enled=P1^4; // 138译码器使能
sbit addr3=P1^3;
sbit addr2=P1^2;
sbit addr1=P1^1;
sbit addr0=P1^0;
sbit led=P0^0; // 这里只点亮led点阵的左上角第一个led小灯
void main(){
enled=0; // 控制点阵led 的 U4(138)使能,需要e2低电平
addr3=0; // 控制点阵led 的 U4(138)使能,需要e1低电平
addr2=0; // y0输出低电平,使Q10晶体管开关打开,使led灯能够点亮
addr1=0;
addr0=0;
led=0; // 控制一只小灯点亮
while(1); // 长亮
}
3)效果
2,点亮矩阵的一排led
1)基础
2)代码
#include<reg52.h>
sbit enled=P1^4; // 138译码器使能
sbit addr3=P1^3;
sbit addr2=P1^2;
sbit addr1=P1^1;
sbit addr0=P1^0;
void main(){
enled=0; // 控制点阵led的 U4(138)使能,需要e2低电平
addr3=0; // 控制点阵led的 U4(138)使能,需要e1低电平
addr2=0; // y0输出低电平,使Q10晶体管开关打开,使led灯能够点亮
addr1=0;
addr0=0; // 选择了第1行的led
P0=0x00; // 控制列上的led,如果写成0x01,则左边第一列不亮,剩下其它列的led亮
while(1); // 长亮
}
3)效果
3,点亮矩阵的全部led
1)代码
下面用到了定时器及中断器(在单片机中带有break关键字的switch语句和中断器是绝配,天造地设的一对)一旦中断器检测到break关键字,就会停下来。在动态显示中,不管是点阵还是数码管点亮的持续时间通常为1ms左右,然后切换到下一个数码管。这意味着在1ms的时间内,数码管应该能够完成从关闭到完全点亮,再到关闭的完整过程。就会有“鬼影”,即会有视觉残留,我们会看到小灯有明显的亮暗波动,所以定时器和中断器的使用就很有必要。
#include<reg52.h>
sbit enled=P1^4; // 138译码器使能
sbit addr3=P1^3;
sbit addr2=P1^2;
sbit addr1=P1^1;
sbit addr0=P1^0;
void main(){
enled=0; // 控制点阵led 的 U4(138)使能,需要e2低电平
addr3=0; // 控制点阵led 的 U4(138)使能,需要e1低电平
EA=1; // 总使能中断打开
ET0=1; // 定时器T0使能中断打开
TMOD=0x01; // 定时器T0的模式为1
TH0=0xFC; // 定时1ms
TL0=0x67;
TR0=1; // 开启定时器T0
while(1); // 长亮
}
void InterrutpTimer0() interrupt 1{ // 中断服务函数
static unsigned char rowmilisec=0; // 用于存储亮起的行,且是每一毫秒亮一行
TMOD=0x01; // 只要有溢出造成的中断,就将T0初始值重新赋值
TH0=0xFC;
P0=0xFF; //关闭段
switch(rowmilisec){
case 0:addr2=0;addr1=0;addr0=0;P0=0x00;rowmilisec++;break;
case 1:addr2=0;addr1=0;addr0=1;P0=0x00;rowmilisec++;break;
case 2:addr2=0;addr1=1;addr0=0;P0=0x00;rowmilisec++;break;
case 3:addr2=0;addr1=1;addr0=1;P0=0x00;rowmilisec++;break;
case 4:addr2=1;addr1=0;addr0=0;P0=0x00;rowmilisec++;break;
case 5:addr2=1;addr1=0;addr0=1;P0=0x00;rowmilisec++;break;
case 6:addr2=1;addr1=1;addr0=0;P0=0x00;rowmilisec++;break;
case 7:addr2=1;addr1=1;addr0=1;P0=0x00;rowmilisec=0;break;
}
}
在51单片机中,总使能中断EA是Enable All Interrupt的缩写。具体来说,EA是中断允许寄存器(IE寄存器)中的一个位,用于控制是否允许CPU响应所有中断请求。
- 当EA=0时,CPU会屏蔽所有中断请求,即不会响应任何中断。
- 当EA=1时,CPU会开放所有中断请求,即会根据其他中断使能位(如ES、ET0、ET1、EX0、EX1等)的设置来响应相应的中断。
static
关键字
在函数内部声明一个变量为 static
时,该变量的生命周期会持续到程序执行完毕,而不是在函数返回时结束。此外,static
变量只会初始化一次,即当程序开始运行时。在后续的函数调用中,该变量会保持其上一次被修改后的值。
在中断服务函数 InterrutpTimer0
中,rowmilisec
被声明为 static
是为了确保它能在每次中断调用之间保持其值。这是一个定时器中断,它会在定时器溢出时定期被调用。由于 rowmilisec
用于跟踪已经过去的毫秒数(或行),所以需要它在每次中断之间保持其值,以便知道下一行应该是什么。
unsigned
关键字
unsigned
关键字指定了一个整数类型,它只能存储非负值。与 signed
类型(如 int
)相比,unsigned
类型没有符号位,因此它可以存储两倍于相同大小 signed
类型的正数。
在上面的代码中rowmilisec
被用来作为一个计数器,从 0 计数到 7,然后回到 0。由于这个值永远不会是负数,所以使用 unsigned char
是合适的。这不仅可以确保值始终是非负的,而且还可以节省一个位(符号位)
2)效果
4,点阵的静态显示
1)基础
经过点亮点阵的一只led,一排led,全部led灯相关操作,现在我们开始来让其静态的展示我们指定的图案。要绘制图案,就需要使用到《点阵液晶取模》程序。
对应软件及资源的网盘连接,见本文专栏。之后下载app里面的字模软件即可,如下👇
之后绘制想要的图案请按照电子书的步骤来:
这里绘制出来的是心形图像并使用软件取模得到P0口所需要的值,如下👇
2)心形矩阵显示代码
#include<reg52.h>
sbit enled=P1^4; // 138译码器使能
sbit addr3=P1^3;
sbit addr2=P1^2;
sbit addr1=P1^1;
sbit addr0=P1^0;
unsigned char code heart_matrix[]={ // 心型矩阵真值表
0xFF,0x99,0x00,0x00,0x00,0x81,0xC3,0xE7
};
void main(){
enled=0; // 控制点阵led 的 U4(138)使能,需要e2低电平
addr3=0; // 控制点阵led 的 U4(138)使能,需要e1低电平
EA=1; // 总使能中断打开
ET0=1; // 定时器T0使能中断打开
TMOD=0x01; // 定时器T0的模式为1
TH0=0xFC; // 定时1ms
TL0=0x67;
TR0=1; // 开启定时器T0
while(1); // 长亮
}
void InterrutpTimer0() interrupt 1{ // 中断服务函数
static unsigned char rowmilisec=0; // 用于存储亮起的行,且是每一毫秒亮一行
// 只要有溢出造成的中断,就将T0初始值重新赋值
TH0=0xFC;
TL0=0x67;
P0=0xFF; // 关闭列
switch(rowmilisec){
case 0:addr2=0;addr1=0;addr0=0;P0=heart_matrix[0];rowmilisec++;break;
case 1:addr2=0;addr1=0;addr0=1;P0=heart_matrix[1];rowmilisec++;break;
case 2:addr2=0;addr1=1;addr0=0;P0=heart_matrix[2];rowmilisec++;break;
case 3:addr2=0;addr1=1;addr0=1;P0=heart_matrix[3];rowmilisec++;break;
case 4:addr2=1;addr1=0;addr0=0;P0=heart_matrix[4];rowmilisec++;break;
case 5:addr2=1;addr1=0;addr0=1;P0=heart_matrix[5];rowmilisec++;break;
case 6:addr2=1;addr1=1;addr0=0;P0=heart_matrix[6];rowmilisec++;break;
case 7:addr2=1;addr1=1;addr0=1;P0=heart_matrix[7];rowmilisec=0;break;
}
}
3)效果
二,课后练习题
1、用点阵做一个9到0的倒计时牌显示。
1)效果
矩阵实现每隔1s倒计时(从9到0)
2)代码
#include<reg52.h>
sbit enled=P1^4; // 138译码器使能
sbit addr3=P1^3;
sbit addr2=P1^2;
sbit addr1=P1^1;
sbit addr0=P1^0;
unsigned char code matrix[10][8]={ // 数字矩阵真值表
{0xFF,0xC3,0xDB,0xDB,0xC3,0xDF,0xDF,0xFF},//9
{0xFF,0xC3,0xDB,0xC3,0xDB,0xDB,0xC3,0xFF},//8
{0xFF,0xC3,0xDF,0xDF,0xDF,0xDF,0xDF,0xFF}, //7
{0xFF,0xC3,0xFB,0xC3,0xDB,0xDB,0xC3,0xFF}, //6
{0xFF,0xC3,0xFB,0xC3,0xDF,0xDF,0xC3,0xFF}, //5
{0xFF,0xEB,0xEB,0xEB,0xC3,0xEF,0xEF,0xFF}, //4
{0xFF,0xC3,0xDF,0xC3,0xDF,0xDF,0xC3,0xFF}, //3
{0xFF,0xC3,0xDF,0xDF,0xC3,0xFB,0xC3,0xFF}, //2
{0xFF,0xDF,0xDF,0xDF,0xDF,0xDF,0xDF,0xFF}, // 1
{0xFF,0xC3,0xDB,0xDB,0xDB,0xDB,0xC3,0xFF} //0
};
void main(){
enled=0; // 控制点阵led 的 U4(138)使能,需要e2低电平
addr3=0; // 控制点阵led 的 U4(138)使能,需要e1低电平
EA=1; // 总使能中断打开
ET0=1; // 定时器T0使能中断打开
TMOD=0x01; // 定时器T0的模式为1
TH0=0xFC; // 定时1ms
TL0=0x67;
TR0=1; // 开启定时器T0
while(1);
}
void Timer0() interrupt 1{ // 中断服务函数
static unsigned char rowmilisec=0;
static unsigned char ind=0;
static unsigned int milisec=0; //int容纳1000以上的数值
// 只要有溢出造成的中断,就将T0初始值重新赋值
TH0=0xFC;
TL0=0x67;
P0=0xFF;
switch(rowmilisec){
case 0:addr2=0;addr1=0;addr0=0;P0=matrix[ind][0];rowmilisec++; break;
case 1:addr2=0;addr1=0;addr0=1;P0=matrix[ind][1];rowmilisec++;break;
case 2:addr2=0;addr1=1;addr0=0;P0=matrix[ind][2];rowmilisec++;break;
case 3:addr2=0;addr1=1;addr0=1;P0=matrix[ind][3];rowmilisec++; break;
case 4:addr2=1;addr1=0;addr0=0;P0=matrix[ind][4];rowmilisec++; break;
case 5:addr2=1;addr1=0;addr0=1;P0=matrix[ind][5];rowmilisec++; break;
case 6:addr2=1;addr1=1;addr0=0;P0=matrix[ind][6];rowmilisec++; break;
case 7:addr2=1;addr1=1;addr0=1;P0=matrix[ind][7];rowmilisec=0;break;
default:break;
}
milisec++;
if(milisec==1000){ //1s更新数字
ind++;
milisec=0;
if(ind ==10){ // 倒计时一轮之后继续倒计时
ind=0;
}
}
}
2、尝试实现流水灯、数码管和点阵的同时显示。
- 点阵:从9倒计时到0(间隔1s);
- 流水灯:从最左边往右1s移动一个,到头之后再从左开始;
- 数码管:使用最左边的一支数码管,让其从9倒计时到0(间隔1s);
1)效果
点阵和数码管同时倒计时led流水灯从左到右
2)代码
#include<reg52.h>
sbit addr3 = P1^3;
sbit enled = P1^4;
void setTimer0(unsigned long ms);//设置定时器的定时毫秒数
void scan();//点阵,led流水灯,数码管扫描函数
unsigned char T0H_att = 0; // 用来存储临时产生的定时器初始值
unsigned char T0L_att = 0;
unsigned char code zzb_matrix[10][8]={ // 数字矩阵真值表
{0xFF,0xC3,0xDB,0xDB,0xC3,0xDF,0xDF,0xFF},//9
{0xFF,0xC3,0xDB,0xC3,0xDB,0xDB,0xC3,0xFF},//8
{0xFF,0xC3,0xDF,0xDF,0xDF,0xDF,0xDF,0xFF}, //7
{0xFF,0xC3,0xFB,0xC3,0xDB,0xDB,0xC3,0xFF}, //6
{0xFF,0xC3,0xFB,0xC3,0xDF,0xDF,0xC3,0xFF}, //5
{0xFF,0xEB,0xEB,0xEB,0xC3,0xEF,0xEF,0xFF}, //4
{0xFF,0xC3,0xDF,0xC3,0xDF,0xDF,0xC3,0xFF}, //3
{0xFF,0xC3,0xDF,0xDF,0xC3,0xFB,0xC3,0xFF}, //2
{0xFF,0xDF,0xDF,0xDF,0xDF,0xDF,0xDF,0xFF}, // 1
{0xFF,0xC3,0xDB,0xDB,0xDB,0xDB,0xC3,0xFF} //0
};
unsigned char code zzb_smg[]={//数码管显示字符转换表0~9(P0值)
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90 //0~9
// , 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E // A~F
};
unsigned char code zzb_led[8] = { // 指定led亮起的P0值?数码管位
0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F
};
unsigned char off_smg[]={ // 关闭数码管的段,消隐
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
};
bit flag1s = 0;//1s定时标志,flag1s只存储一位,所以使用bit来声明定义
unsigned char interrupt_t0 = 0;//记录T0中断次数
static char row_matrix=-1; // 矩阵的行,设置成-1是为了让它在i=0时,显示9,而不是8。
static unsigned char ind_smg=0; // 数码管索引
void main()
{
// unsigned long sec_smg = 0; // 数码管秒数
enled = 0;
setTimer0(1); //定时器定时1ms
while(1)
{
if(flag1s == 1)
{
flag1s = 0;
//sec_smg++; // 记录数码管显示的秒数,因为这里只显示9~0,所以不用sec_smg这个变量
row_matrix++; //实现点阵的图片刷新(更新)
if(row_matrix>9){ // 点阵只有8行,之所以本来应该是大于8就让它置零,是因为我们一开始设置了row_matrix=-1
row_matrix=0;
}
/*off_smg[0] = zzb_smg[sec%10]; // 如果想要多位显示,可以把相应的代码块注释去掉
off_smg[1] = zzb_smg[sec/10%10];
off_smg[2] = zzb_smg[sec/100%10];
off_smg[3] = zzb_smg[sec/1000%10];
off_smg[4] = zzb_smg[sec/10000%10];*/
off_smg[5] = zzb_smg[9-ind_smg]; // ind_smg=0,数码管显示9-0=9
ind_smg++;
if(ind_smg>9){
ind_smg=0;
}
}
}
}
void scan()
{
static unsigned char i = 0;
P0 = 0xFF;
if(i>7)//扫描数码管与LED小灯
{
addr3 = 1; // led流水灯亮
P1 = (P1&0xF8)|(i-8);
if(i==14){
off_smg[6]=zzb_led[interrupt_t0];
}
P0 = off_smg[i-8];
}
else //i<7时扫描点阵
{
addr3 = 0; // 点阵对应的led打开
P1 = (P1&0xF8)|i;
if(row_matrix>=0){
P0 = zzb_matrix[row_matrix][i];
}
}
i++;
if(i>15) i=0; //
}
void setTimer0(unsigned long milisec)
{
unsigned long n;
n = 11059200/12;
n=(n*milisec)/1000;
n = 65536-n;
n += 12; //补偿中断函数引起的误差,编写《教材手把手教你学51单片机-C语言版》的作者经过多次尝试加12是最佳的
T0H_att = (unsigned char)(n>>8);
T0L_att = (unsigned char)n;
TMOD=0x01;
EA = 1;
ET0 = 1;
TH0 = T0H_att;
TL0 = T0L_att;
TR0 = 1;
}
void InterruptTimer0() interrupt 1
{
static unsigned long milisec = 0;
TH0 = T0H_att;
TL0 = T0L_att;
milisec++;
if(milisec>=1000)
{
milisec=0;
interrupt_t0++;
flag1s=1;
if(interrupt_t0>7)//interrupt_t0为LED数组索引
interrupt_t0=0;
}
scan();//点阵、数码管、LED扫描
}
有任何问题请在评论区留言或者是私信我,一天8h在线。