(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数

一、天威TM1640介绍

      TM1640 是一种LED(发光二极管显示器)驱动控制专用电路,内部集成有MCU 数字接口、数据锁存器、LED 驱动等电路。主要应用于电子产品LED显示屏驱动。采用SOP28、SSOP28的封装形式。

•  采用CMOS 工艺 
•  显示模式(8 段×16 位)
•  辉度调节电路(占空比8 级可调) 
•  两线串行接口(SCLK,DIN) 
•  振荡方式:内置RC 振荡 
•  内置上电复位电路 
•  封装形式:SSOP28、SOP28

(1).管脚定义:

(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数_第1张图片

(2).管脚功能定义:

(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数_第2张图片

(3).电气参数

(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数_第3张图片

(4).微处理器的数据通过两线总线接口和TM1640 通信,在输入数据时当 CLK 是高电平时,DIN 上的信号必须保持不变;只有 CLK 上的时钟信号为低电平时 DIN 上的信号才能改变。 数据的输入总是低位在前, 高位在后传输。 数据输入的开始条件是 CLK 为高电平时, DIN 由高变低;结束条件是 CLK 为高时, DIN 由低电平变为高电平。
指令数据传输过程如下图:

(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数_第4张图片

根据上面时序图,可以写出如下写一字节驱动代码:

void TM1640_Write_Byte(uint8 TM1640_Dat)
{
 uint8 i;
 for(i=0;i<8;i++)
   {
       CLK = 0;
       if((TM1640_Dat & 0X01)==0X01)
           DIN = 1;
       else
           DIN = 0;
       TM1640_Dat = TM1640_Dat >>1;
       //_nop_();   //延时 500ns
       CLK = 1;
       //_nop_();   //延时 500ns
   }	

//*********************************
	 //写完一字节数据,必须要把DIN,SLK拉低,否则会导致固定地址模式不能显示“.”
	 DIN = 0;
	 CLK = 0;
//*********************************	 
}

(5).两种模式

         写 SRAM 数据地址自动加 1 模式:

(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数_第5张图片

        写 SRAM 数据固定地址模式:

(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数_第6张图片

/两种写数据模式(固定地址模式、地址自动+1模式)
typedef enum
{
   Addr_Fix_Mode = 0X44,  //固定地址模式  
   Addr_Auto_Mod = 0X40,  //地址自动增+1模式
}  Addr_Mode_t;

(6)显示控制命令

(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数_第7张图片

//亮度调节
typedef enum
{
   Brightness_level_0      =  0X80,  //亮度 0 熄灭  
   Brightness_level_1      =  0X88,  //亮度 1 
   Brightness_level_2      =  0X89,  //亮度 2 
   Brightness_level_3      =  0X8A,  //亮度 3 
   Brightness_level_4      =  0X8B,  //亮度 4 
   Brightness_level_5      =  0X8C,  //亮度 5 
   Brightness_level_6      =  0X8D,  //亮度 6 
   Brightness_level_7      =  0X8E,  //亮度 7 
   Brightness_level_8      =  0X8F,  //亮度 8   
}  Brightness_level_t;

(7)地址命令设置

(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数_第8张图片

//显示寄存器地址
typedef enum
{ 
	All_Place_Addr     =  16,     //TM1640 最大支持16位数码管
	Place_Addr_01      =  0XC0,  
	Place_Addr_02      =  0XC1, 
	Place_Addr_03      =  0XC2, 
	Place_Addr_04      =  0XC3, 
	Place_Addr_05      =  0XC4, 
	Place_Addr_06      =  0XC5, 
	Place_Addr_07      =  0XC6, 
	Place_Addr_08      =  0XC7, 
	Place_Addr_09      =  0XC8, 
	Place_Addr_10      =  0XC9, 
	Place_Addr_11      =  0XCA, 
	Place_Addr_12      =  0XCB, 
	Place_Addr_13      =  0XCC,
	Place_Addr_14      =  0XCD,
	Place_Addr_15      =  0XCE,
	Place_Addr_16      =  0XCF,
} Place_Addr_t;

上电时,地址默认设为C0H。显示数据与芯片管脚以及显示地址之间的对应关系如下表所示:

(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数_第9张图片

注意:芯片显示寄存器在上电瞬间其内部保存的值可能是随机不确定的,此时客户直接发送开屏命令, 将有可能出现显示乱码。所以我司建议客户对显示寄存器进行一次上电清零操作,即上电后向16位显存地址 (C0H-CDH)中全部写入数据0x00。

TM1640初始化代码如下 :

void TM1640_Init()
{
   uint8 i =0;
 
	//地址自动增加 1 模式
   TM1640_Star();
   TM1640_Write_Byte(Addr_Auto_Mod);
	 TM1640_End(); 

   //清除显示寄存器
	 TM1640_Star();
   TM1640_Write_Byte(Place_Addr_01); //设置首地址
   for(i=0;i

二、 习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数

习题要求:使用TM1640驱动数码管时钟(带设置、加、减、确认按键功能)

  • VCC:电源正,推荐3.3V或5V  ---------5V供电
  • CLK:同步串行通信时钟信号输入引脚  --------CLK=P2^1
  • DIN:同步串行通信数据输入引脚   --------- DIN=P2^0
  • GND:地

简易电路搭建如下图所示:

(51单片机)习题:TM1640驱动数码管并用任意矩阵键盘调节显示参数_第10张图片

完整代码如下 :

/*********************************************************************
程序名称:TM1640驱动数码管时钟(带设置、加、减、确认按键功能)
程序版本:V1.0
作    者: Attack BUG
编写时间:2023.09.01	 
硬件支持: TM1640、STC89C52RC、 16位矩阵键盘
接口说明:
修改日志:
*********************************************************************/
/*********************************************************************
                        头文件包含/SFR定义
*********************************************************************/
#include 
#include 
/*********************************************************************
                             宏定义
*********************************************************************/
typedef unsigned char uint8;         //8位有符号字符型
typedef unsigned int uint16;         //16位有符号整型
typedef unsigned long uint32;        //32位有符号长整型

#define SET 1    //设置键
#define ADD 2    //加
#define SUB 3    //减
#define OK  4    //确认键
/*********************************************************************
                        单片机IO口位定义
*********************************************************************/
sbit   DIN=P2^0; //定义端口 3个位声明
sbit   CLK=P2^1;
/*********************************************************************
                          函数原形声明
*********************************************************************/
uint8 code SMG[19]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x76,0x80,0x40}; //共阴数码管段码表
uint8 key_scan();
uint8 shi=12,fen=30,miao=0;
void delay(uint16 x_ms);
void TM1640_Init();
void TM1640_Star();
void TM1640_End();
void TM1640_Write_Byte(uint8 TM1640_Dat);
void TM1640_Display_time(uint8 address, uint8 shi, uint8 fen, uint8 miao);
void TM1640_address_moshi(uint8 address_mode);	
void TM1640_liangdu(uint8 liangdu);
void TM1640_Display_dian(uint8 address,uint8 set_mode);	
void TM1640_Display_liushui();
//两种写数据模式(固定地址模式、地址自动+1模式)
typedef enum
{
   Addr_Fix_Mode = 0X44,  //固定地址模式  
   Addr_Auto_Mod = 0X40,  //地址自动增+1模式
}  Addr_Mode_t;

//亮度调节
typedef enum
{
   Brightness_level_0      =  0X80,  //亮度 0 熄灭  
   Brightness_level_1      =  0X88,  //亮度 1 
   Brightness_level_2      =  0X89,  //亮度 2 
   Brightness_level_3      =  0X8A,  //亮度 3 
   Brightness_level_4      =  0X8B,  //亮度 4 
   Brightness_level_5      =  0X8C,  //亮度 5 
   Brightness_level_6      =  0X8D,  //亮度 6 
   Brightness_level_7      =  0X8E,  //亮度 7 
   Brightness_level_8      =  0X8F,  //亮度 8   
}  Brightness_level_t;

//显示寄存器地址
typedef enum
{ 
	All_Place_Addr     =  16,     //TM1640 最大支持16位数码管
	Place_Addr_01      =  0XC0,  
	Place_Addr_02      =  0XC1, 
	Place_Addr_03      =  0XC2, 
	Place_Addr_04      =  0XC3, 
	Place_Addr_05      =  0XC4, 
	Place_Addr_06      =  0XC5, 
	Place_Addr_07      =  0XC6, 
	Place_Addr_08      =  0XC7, 
	Place_Addr_09      =  0XC8, 
	Place_Addr_10      =  0XC9, 
	Place_Addr_11      =  0XCA, 
	Place_Addr_12      =  0XCB, 
	Place_Addr_13      =  0XCC,
	Place_Addr_14      =  0XCD,
	Place_Addr_15      =  0XCE,
	Place_Addr_16      =  0XCF,
} Place_Addr_t;
/*********************************************************************
*函数名称:main
*函数功能:主函数
*函数调用:
*参数列表:无
*返 回 值:无
*结    果:
*备    注:
*********************************************************************/
void main()
{
	bit smg_mode = 0;    //数码管模式   0:时钟模式     1:设置模式
  uint8 set_mode = 0;  //设置模式的3种情况,1:秒   2:分   3:时
	uint8 temp = 0;      //缓存矩阵键盘键值
	uint16 count = 0;    //秒精度调节参数
	TM1640_Init();       //初始化TM1640,必需进行初始化

while(1)
	{
   if(smg_mode==0)  // smg_mode = 0   默认上电时钟模式
		{	
	  	count++;  //秒精度调节参数
			TM1640_address_moshi(Addr_Auto_Mod);	 //选择地址模式  Addr_Auto_Mode:地址自动加1模式
      TM1640_Display_time(Place_Addr_01, shi, fen, miao);  //在第一个数码管位置显示:时、分、秒
	    //TM1640_Display_liushui(); //从第9位数码管(Place_Addr_09)开始显示 8个横杠“-“
			TM1640_liangdu(Brightness_level_5);    //亮度选择:5级亮度
			
			if(count==800)
			{
				count=0;
				miao++;
				if(miao==60)
				{
					miao=0;
					fen++;
					if(fen==60)
					{
						fen=0;
						shi++;
						if(shi==24)
						{
							shi=0;
						}
					}
				}
			}
	  }
		
   else  //smg_mode = 1    设置模式
		{
			//TM1640_Display_liushui(); //从第9位数码管(Place_Addr_09)开始显示 8个横杠“-“
			TM1640_Display_time(Place_Addr_01, shi, fen, miao);  //在第一个数码管位置显示:时、分、秒
		}
		
//****************************************************************************
		temp = key_scan(); //缓存矩阵键盘键值,key_scan()有四种结果:SET、ADD、SUB、OK
		switch(temp)
		{
			case SET:     //识别到设置键时(每按一次set_mode进行一次自加计数)
			{
				smg_mode = 1; //识别到设置键按下,进入设置模式
				set_mode++;   //按一次设置键,切换一次设置模式
				if(set_mode >= 4)
					set_mode = 1;
			}
			break;
			
			case OK: //识别确认键时,退出,进入正常走时
			{
				smg_mode = 0; //返回时钟模式
				set_mode = 0; //退出模式设置
			}
			break;
			
			default:
				switch(set_mode) //基于设置模式下的三种情况(时、分、秒调节)
				{
					case 1: //模式一:设置秒钟  set_mode = 1
						{
							TM1640_Display_dian(Place_Addr_01,set_mode); //set_mode=1,秒钟个位显示"."

							if(temp==ADD) //识别 '加' 键
							{
								miao++; //秒+
								if(miao==60)
									 miao = 0;
							}
							if(temp==SUB) //识别 '减' 键
							{
								miao--; //秒-
								if(miao==255)
									 miao = 59;
							}
						}
						break;
						
					case 2: //模式二:设置分钟  set_mode = 2
						{
							TM1640_Display_dian(Place_Addr_01,set_mode); //set_mode=2,分钟个位显示"."
							if(temp==ADD) //识别 '加' 键
							{
								fen++; //分+
								if(fen==60)
									fen = 0;
							}
							if(temp==SUB) //识别 '减' 键
							{
								fen--; //分-
								if(fen==255)
									fen = 59;
							}
						}
						break;
						
					case 3:  //模式三:设置时  set_mode = 3
						{
							TM1640_Display_dian(Place_Addr_01,set_mode); //set_mode=3,时个位显示"."
							if(temp==ADD) //识别 '加' 键
							{
								shi++; //时+
								if(shi==24)
									shi = 0;
							}
							if(temp==SUB) //识别 '减' 键
							{
								shi--; //时-
								if(shi==255)
									shi = 23;
							}
						}
						break;
				}
				break;		
		}
		
 }	
}

void TM1640_Init()
{
   uint8 i =0;
 
	//地址自动增加 1 模式
   TM1640_Star();
   TM1640_Write_Byte(Addr_Auto_Mod);
	 TM1640_End(); 

   //清除显示寄存器
	 TM1640_Star();
   TM1640_Write_Byte(Place_Addr_01); //设置首地址
   for(i=0;i>1;
       //_nop_();   //延时 500ns
       CLK = 1;
       //_nop_();   //延时 500ns
   }	

//*********************************
	 //写完一字节数据,必须要把DIN,SLK拉低,否则会导致固定地址模式不能显示“.”
	 DIN = 0;
	 CLK = 0;
//*********************************	 
}

void TM1640_Display_liushui()
{
	uint8 j;
	TM1640_Star();   //开始条件
  TM1640_Write_Byte(Place_Addr_09);    //在第9个数码管位置写入地址
	for(j=0;j<8;j++)
	TM1640_Write_Byte(SMG_liushui[j]);   //显示"-"
	TM1640_End();	  //结束条件
}


void TM1640_Display_time(uint8 address, uint8 shi, uint8 fen, uint8 miao)
{
	TM1640_Star();   //开始条件
  TM1640_Write_Byte(address);   //写入地址
  TM1640_Write_Byte(SMG[shi%100/10]);   //显示时的十位
	TM1640_Write_Byte(SMG[shi%10]);       //显示时的个位
	TM1640_Write_Byte(SMG[18]);           //显示"-"
	TM1640_Write_Byte(SMG[fen%100/10]);   //显示分的十位
	TM1640_Write_Byte(SMG[fen%10]);       //显示分的个位
	TM1640_Write_Byte(SMG[18]);           //显示"-"
	TM1640_Write_Byte(SMG[miao%100/10]);  //显示秒的十位
	TM1640_Write_Byte(SMG[miao%10]);      //显示秒的个位
	
	TM1640_End();	  //结束条件
}

void TM1640_Display_dian(uint8 address,uint8 set_mode)  //小数点显示函数
{
	TM1640_Star(); //开始条件
	TM1640_Write_Byte(address); //写入首地址
	//传输显示数据
		if(set_mode==3) //若为时钟,时钟个位显示“.”
		{
			TM1640_Write_Byte(SMG[shi%100/10]);  //显示时 十位
			TM1640_Write_Byte(SMG[shi%10]|0X80);  //显示时 个位,同时显示"."
			TM1640_Write_Byte(SMG[18]);  //显示"-"
			TM1640_Write_Byte(SMG[fen%100/10]);  //显示分 十位
			TM1640_Write_Byte(SMG[fen%10]);  //显示分 个位
			TM1640_Write_Byte(SMG[18]);  //显示"-"
			TM1640_Write_Byte(SMG[miao%100/10]);  //显示秒 十位
			TM1640_Write_Byte(SMG[miao%10]);  //显示秒 个位
			TM1640_End(); //结束条件
		}
		
		else if(set_mode==2) //若为分钟,分钟个位显示“.”
		{
			TM1640_Write_Byte(SMG[shi%100/10]);  //显示时 十位
			TM1640_Write_Byte(SMG[shi%10]);  //显示时 个位
			TM1640_Write_Byte(SMG[18]);  //显示"-"
			TM1640_Write_Byte(SMG[fen%100/10]);  //显示分 十位
			TM1640_Write_Byte(SMG[fen%10]|0X80);  //显示分 个位,同时显示"."
			TM1640_Write_Byte(SMG[18]);  //显示"-"
			TM1640_Write_Byte(SMG[miao%100/10]);  //显示秒 十位
			TM1640_Write_Byte(SMG[miao%10]);  //显示秒 个位
			TM1640_End(); //结束条件
		}
		
		else if(set_mode==1) //若为秒钟,秒钟个位显示“.”
		{
			TM1640_Write_Byte(SMG[shi%100/10]);  //显示时 十位
			TM1640_Write_Byte(SMG[shi%10]);  //显示时 个位
			TM1640_Write_Byte(SMG[18]);  //显示"-"
			TM1640_Write_Byte(SMG[fen%100/10]);  //显示分 十位
			TM1640_Write_Byte(SMG[fen%10]);  //显示分 个位
			TM1640_Write_Byte(SMG[18]);  //显示"-"
			TM1640_Write_Byte(SMG[miao%100/10]);  //显示秒 十位
			TM1640_Write_Byte(SMG[miao%10]|0X80);  //显示秒 个位,同时显示"."
			TM1640_End(); //结束条件
		}
		
		else //默认显示 shi-fen-miao
		{
			TM1640_Write_Byte(SMG[shi%100/10]);  //显示时 十位
			TM1640_Write_Byte(SMG[shi%10]);  //显示时 个位
			TM1640_Write_Byte(SMG[18]);  //显示"-"
			TM1640_Write_Byte(SMG[fen%100/10]);  //显示分 十位
			TM1640_Write_Byte(SMG[fen%10]);  //显示分 个位
			TM1640_Write_Byte(SMG[18]);  //显示"-"
			TM1640_Write_Byte(SMG[miao%100/10]);  //显示秒 十位
			TM1640_Write_Byte(SMG[miao%10]);  //显示秒 个位
			TM1640_End(); //结束条件
		}
}

void TM1640_address_moshi(uint8 address_mode)   //模式选择函数
{
	TM1640_Star(); //开始条件
	TM1640_Write_Byte(address_mode); //写数据模式 地址自动加1模式 or 固定地址模式
	TM1640_End(); //结束条件
}

void TM1640_liangdu(uint8 liangdu)   //亮度调节
{
	TM1640_Star(); //开始条件
	TM1640_Write_Byte(liangdu); //设置亮度级别
	TM1640_End(); //结束条件
}

uint8 key_scan()
{
	uint8 keynum = 0; //矩阵键盘键值 1~16
	uint8 temp = 0;  //按键按下时的P3数据缓存
/**************************************************************************/
	// 扫描矩阵键盘第一行  0XFE 1111 1110
	P3 = 0XFE; 
	if(P3 != 0XFE) 
	{
		delay(10); //按键消抖 5~10ms
		if(P3 != 0XFE)
		{
			temp = P3; //缓存P3总线的数据状态
			while(P3 != 0XFE)
			{				
				TM1640_Display_time(Place_Addr_01,shi,fen,miao); //在第1位数码管(Place_Addr_09)显示时间 xx-xx-xx
			}
			switch(temp)
			{
				case 0X7E: keynum = 1; break;
				case 0XBE: keynum = 2; break;
				case 0XDE: keynum = 3; break;
				case 0XEE: keynum = 4; break;
				  default: keynum = 0; break;
			}
		}
	}

/**************************************************************************/
	// 扫描矩阵键盘第二行  0XFD 1111 1101
	P3 = 0XFD; 
	if(P3 != 0XFD) 
	{
		delay(10); //按键消抖 5~10ms
		if(P3 != 0XFD)
		{
			temp = P3; //缓存P3总线的数据状态
			while(P3 != 0XFD)
			{
				TM1640_Display_time(Place_Addr_01,shi,fen,miao); //在第1位数码管(Place_Addr_09)显示时间 xx-xx-xx
			}
			switch(temp)
			{
				case 0X7D: keynum = 5; break;
				case 0XBD: keynum = 6; break;
				case 0XDD: keynum = 7; break;
				case 0XED: keynum = 8; break;
				  default: keynum = 0; break;
			}
		}
	}

/**************************************************************************/
	// 扫描矩阵键盘第三行  0XFB 1111 1011
	P3 = 0XFB; 
	if(P3 != 0XFB) 
	{
		delay(10); //按键消抖 5~10ms
		if(P3 != 0XFB)
		{
			temp = P3; //缓存P3总线的数据状态
			while(P3 != 0XFB)
			{
				TM1640_Display_time(Place_Addr_01,shi,fen,miao); //在第1位数码管(Place_Addr_09)显示时间 xx-xx-xx
			}
			switch(temp)
			{
				case 0X7B: keynum = 9; break;
				case 0XBB: keynum = 10; break;
				case 0XDB: keynum = 11; break;
				case 0XEB: keynum = 12; break;
				  default: keynum = 0; break;
			}
		}
	}
/**************************************************************************/
	// 扫描矩阵键盘第四行  0XF7 1111 0111
	P3 = 0XF7; 
	if(P3 != 0XF7) 
	{
		delay(10); //按键消抖 5~10ms
		if(P3 != 0XF7)
		{
			temp = P3; //缓存P3总线的数据状态
			while(P3 != 0XF7)
			{
				TM1640_Display_time(Place_Addr_01,shi,fen,miao); //在第1位数码管(Place_Addr_09)显示时间 xx-xx-xx
			}
			switch(temp)
			{
				case 0X77: keynum = 13; break;
				case 0XB7: keynum = 14; break;
				case 0XD7: keynum = 15; break;
				case 0XE7: keynum = 16; break;
				  default: keynum = 0; break;
			}
		}
	}
/**************************************************************************/	
	return keynum;
}


/********************************延时函数***********************************/
void delay(uint16 x_ms) //延迟  x_ms*1  毫秒
{
  uint16 i,j;
	for(i=0;i

最终视觉效果如下 :

TM1640驱动数码管+任务矩阵按键调节

你可能感兴趣的:(51单片机,嵌入式硬件,单片机)