51单片机实验05 -点阵

目录

一,熟悉矩阵led小灯

1,点亮矩阵的一只led

2,点亮矩阵的一排led

3,点亮矩阵的全部led

[static 关键字](#static 关键字)

[unsigned 关键字](#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单片机中,总使能中断EAEnable 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在线。

相关推荐
智者知已应修善业2 小时前
【51单片机用数码管显示流水灯的种类是按钮控制数码管加一和流水灯】2022-6-14
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机
智商偏低8 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen9 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森11 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白11 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D12 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术15 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt15 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘16 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang16 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c