一、概述二、51单片机,TM1638芯片+DS1302驱动共阴数码管时钟,最后包括按键检测程序(是分开的。
)TM1638是带键盘扫描接口的LED(发光二极管显示器)驱动控制专用电路,内部集成有MCU 数字接口、数据锁存器、LED 高压驱动、键盘扫描等电路。
主要应用于冰箱、空调、家庭影院等产品的高段位显示屏驱动。
二、特性说明•采用功率CMOS 工艺•显示模式 10 段×8 位•键扫描(8×3bit)•辉度调节电路(占空比8 级可调)•串行接口(CLK,STB,DIO)•振荡方式:RC 振荡(450KHz+5%)•内置上电复位电路•采用SOP28封装三、、管脚定义:电路图数码管:程序代码:/*************** TM1638.H头文件*******************/#ifndef _TM1638_H#define _TM1638_H//引脚定义sbit DIO=P1^0;sbit CLK=P1^1;sbit STB=P1^2;//写一个8Bit数据void TM1638_Write(unsigned char DATA) //写数据函数{unsigned char i;for(i=0;i<8;i++){CLK=0;if(DATA&0X01)DIO=1;elseDIO=0;DATA>>=1;CLK=1;}}/*unsigned char TM1638_Read(void) //读数据函数{unsigned char i;unsigned char temp=0;DIO=1; //设置为输入for(i=0;i<8;i++){temp>>=1;CLK=0;if(DIO)temp|=0x80;CLK=1;}return temp;} */void Write_COM(unsigned char cmd) //发送命令字{STB=0;TM1638_Write(cmd); //写命令STB=1;}void Write_DATA(unsigned char add,unsigned char DA TA) //指定地址写入数据{Write_COM(0x44);STB=0;TM1638_Write(0xc0|add); //地址高位为1:11** ****,写地址TM1638_Write(DA TA);STB=1;}void init_TM1638(void){unsigned char i;Write_COM(0x8a);//亮度Write_COM(0x40); //写数据命令,写数据到显示寄存器STB=0;TM1638_Write(0xc0); //写地址命令for(i=0;i<16;i++)TM1638_Write(0x00);STB=1;}#endif/***************************DS1302.H头文件***************************/#ifndef DS1302_H#define DS1302_H#define uchar unsigned char#define uint unsigned int#include"DELAY.h"sbit acc0=ACC^0; //移位时的第0位sbit acc7=ACC^7; //移位时用的第7位uchar second,minute,hour,day,month,year,week,count=0;uchar ReadValue,num,time;sbit SCLK=P2^0;sbit DATA=P2^1;sbit RST=P2^2;void Write1302(uchar dat){uchar i;SCLK=0; //拉低SCLK,为脉冲上升沿写入数据做好准备delay1(2); //稍微等待,使硬件做好准备for(i=0;i<8;i++) //连续写8个二进制位数据{DATA=dat&0x01; //取出dat的第0位数据写入1302delay(2); //稍微等待,使硬件做好准备SCLK=1; //上升沿写入数据delay1(2); //稍微等待,使硬件做好准备SCLK=0; //重新拉低SCLK,形成脉冲dat>>=1; //将dat的各数据位右移1位,准备写入下一个数据位}}void WriteSet1302(uchar Cmd,uchar dat){RST=0; //禁止数据传递SCLK=0; //确保写数居前SCLK被拉低RST=1; //启动数据传输delay1(2); //稍微等待,使硬件做好准备Write1302(Cmd); //写入命令字Write1302(dat); //写数据SCLK=1; //将时钟电平置于已知状态RST=0; //禁止数据传递}uchar Read1302(void){uchar i,dat;delay(2); //稍微等待,使硬件做好准备for(i=0;i<8;i++) //连续读8个二进制位数据{dat>>=1; //将dat的各数据位右移1位,因为先读出的是字节的最低位if(DATA==1) //如果读出的数据是1dat|=0x80; //将1取出,写在dat的最高位SCLK=1; //将SCLK置于高电平,为下降沿读出delay1(2); //稍微等待SCLK=0; //拉低SCLK,形成脉冲下降沿delay1(2); //稍微等待}return dat; //将读出的数据返回}uchar ReadSet1302(uchar Cmd){uchar dat;RST=0; //拉低RSTSCLK=0; //确保写数居前SCLK被拉低RST=1; //启动数据传输Write1302(Cmd); //写入命令字dat=Read1302(); //读出数据SCLK=1; //将时钟电平置于已知状态RST=0; //禁止数据传递return dat; //将读出的数据返回}void Init_DS1302(void){WriteSet1302(0x8E,0x00); //根据写状态寄存器命令字,写入不保护指令WriteSet1302(0x80,((0/10)<<4|(0%10))); //根据写秒寄存器命令字,写入秒的初始值WriteSet1302(0x82,((41/10)<<4|(41%10))); //根据写分寄存器命令字,写入分的初始值WriteSet1302(0x84,((15/10)<<4|(15%10))); //根据写小时寄存器命令字,写入小时的初始值WriteSet1302(0x86,((29/10)<<4|(24%10))); //根据写日寄存器命令字,写入日的初始值WriteSet1302(0x88,((2/10)<<4|(2%10))); //根据写月寄存器命令字,写入月的初始值WriteSet1302(0x8c,((10/10)<<4|(11%10))); //nianWriteSet1302(0x8a,((0/10)<<4|(0%10)));WriteSet1302(0x8E,0x80); //根据写状态寄存器命令字,写入保护指令}#endif/***************显示头文件*******************************/#include"TM1638.h"uchar data DisBuffer[8]={0,0,0,0,0,0,0,0}; /*显示缓存区*/ //各个数码管显示的值uchar code tab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71 ,0x40,0xef};void display1638(uchar hour,uchar minute,uchar second){uchar i;DisBuffer[0]=hour/10;DisBuffer[1]=hour%10;DisBuffer[3]=minute/10;DisBuffer[4]=minute%10;DisBuffer[6]=second/10;DisBuffer[7]=second%10;DisBuffer[2]=16;DisBuffer[5]=16;for(i=0;i<8;i++){Write_DATA(i*2,tab[DisBuffer[i]]);}}void DisplayHour(newval){DisBuffer[0]=newval/10;DisBuffer[1]=newval%10;for(i=0;i<2;i++){Write_DATA(i*2,tab[DisBuffer[i]]);}}#endif/**********************************************************///下面是处理程序头文件,调试功能的实现,但是本人接调时的来源是//是鼠标芯片,所以这里都写上,各位可以根据自己是按键还是其他的,做修改/***********************************************/#ifndef CHULI_H#define CHULI_H#define uchar unsigned char#define uint unsigned int#include"DISPLAY.h"#include"DS1302.h"void read_date(void){ReadValue = ReadSet1302(0x81);second=((ReadValue&0x70)>>4)*10 + (ReadV alue&0x0F);ReadValue = ReadSet1302(0x83);minute=((ReadValue&0x70)>>4)*10 + (ReadV alue&0x0F);ReadValue = ReadSet1302(0x85);hour=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = ReadSet1302(0x87);day=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = ReadSet1302(0x89);month=((ReadValue&0x70)>>4)*10 + (ReadV alue&0x0F);ReadValue = ReadSet1302(0x8d);year=((ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue=ReadSet1302(0x8b); //读星期week=ReadValue&0x07;display1638(hour,minute,second) ;}void turn_val(char newval,uchar flag,uchar newaddr,uchar s1num){newval=ReadSet1302(newaddr); //读取当前时间newval=((newval&0x70)>>4)*10+(newval&0x0f); //将bcd码转换成十进制if(flag) //判断是加一还是减一{newval++;switch(s1num){ case 1: if(newval>99) newval=0;DisplayYear(newval);break;case 2: if(newval>12) newval=1;DisplayMonth(newval);break;case 3: if(newval>31) newval=1;DisplayDay(newval);break;case 4: if(newval>6) newval=0;DisplayWeek(newval);break;case 5: if(newval>23) newval=0;DisplayHour(newval);break;case 6: if(newval>59) newval=0;DisplayMinute(newval);break;case 7: if(newval>59) newval=0;DisplaySecond(newval);break;default:break;}}else{newval--;switch(s1num){ case 1: if(newval==0) newval=99;DisplayYear(newval);break;case 2: if(newval==0) newval=12;DisplayMonth(newval);break;case 3: if(newval==0) newval=31;DisplayDay(newval);break;case 4: if(newval<0) newval=6;DisplayWeek(newval);break;case 5: if(newval<0) newval=23;DisplayHour(newval);break;case 6: if(newval<0) newval=59;DisplayMinute(newval);break;case 7: if(newval<0) newval=59;DisplaySecond(newval);break;default:break;}}WriteSet1302((newaddr-1),((newval/10)<<4)|(newval%10)); //将新数据写入寄存器}void write_flsh(uchar com,uchar dat){ Write_DATA(com,tab[16]);delay(100);Write_DATA(com,tab[dat]);}void sp2_mouse(void){uchar miao,z,flag=0;rd=0;miao=ReadSet1302(0x81);second=miao;WriteSet1302(0x80,miao|0x80);while(1){ led=0;////////////////////////////////////////* if((move_x<4)&&(flag==0)){write_flash(0x02,1) ;if((mouse_data[0]&0x01)&&(flag==1)){while(mouse_data[0]&0x01);while(1){ write_flash(0x02,1) ;z=move_x;if(move_x>z){ delayms(10);turn_val(year,1,0x8d,1);}if(z>move_x){delayms(10);turn_val(year,0,0x8d,1);}if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);flag=1;move_x=3;z=move_x;break;}}}}///////////////////////////////////////////////////////if((move_x>=4)&&(move_x<6)&&(flag==0)){write_com(0x87);if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);while(1){ z=move_x;write_com(0x87);if(move_x>z){ delayms(10);turn_val(month,1,0x89,2);}if(z>move_x){delayms(10);turn_val(month,0,0x89,2);}if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);flag=1;move_x=5;z=move_x;break;}}}}/////////////////////////////////////////////////////////if((move_x>=6)&&(move_x<8)&&(flag==0)){write_com(0x8a);if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);while(1){z=move_x;write_com(0x8a);if(move_x>z){ delayms(10);turn_val(day,1,0x87,3);}if(z>move_x){ delayms(10);turn_val(day,0,0x87,3);}if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);flag=1;move_x=7;z=move_x;break;}}}}///////////////////////////////////////////////////////////if((move_x>=8)&&(move_x<10)&&(flag==0)){write_com(0x8e);if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);while(1){z=move_x;write_com(0x8e);if(move_x>z){ delayms(10);turn_val(week,1,0x8b,4);}if(z>move_x){delayms(10);turn_val(week,0,0x8b,4);}if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);flag=1;move_x=9;z=move_x;break;}}}} */////////////////////////////////////////////////////////////if((move_x>=10)&&(move_x<12)&&(flag==0)){write_flash(0x02,1) ;if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);while(1){z=move_x;write_flash(0x02,1) ;if(move_x>z){ delayms(10);turn_val(hour,1,0x85,5);}if(z>move_x){ delayms(10);turn_val(hour,0,0x85,5);}if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);flag=1;move_x=11;z=move_x;break;}}}}//////////////////////////////////////////////////////////////if((move_x>=12)&&(move_x<14)&&(flag==0)){write_com(0xc4) ;if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);while(1){z=move_x;write_com(0xc4) ;if(move_x>z){ delayms(10);turn_val(minute,1,0x83,6); //写入分寄存器}if(z>move_x){delayms(10);turn_val(minute,0,0x83,6); //写入分寄存器}if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);flag=1;move_x=13;z=move_x;break;}}}}////////////////////////////////////////////////////////////////if((move_x>=14)&&(flag==0)){write_com(0xc7);if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);while(1){z=move_x;write_com(0xc7);if(move_x>z){ delayms(10);turn_val(second,1,0x81,7);}if(z>move_x){delayms(10);turn_val(second,0,0x81,7);}if(mouse_data[0]&0x01){while(mouse_data[0]&0x01);flag=1;move_x=15;z=move_x;break;}}}}///////////////////////////////////////////////////////////////if((mouse_data[0]&0x01)&&(z!=move_x)){flag=0;while(mouse_data[0]&0x01);}//////////////////////////////////////////////////////////////if((mouse_data[0]&0x01)&&(flag==1)&&(z==move_x)){flag=0;delayms(300);miao=ReadSet1302(0x81);second=miao;WriteSet1302(0x80,second&0x7f);write_com(0x0c) ;WriteSet1302(0x8E,0x80); //根据写状态寄存器命令字,写入不保护指令break;}}}#endif/*********鼠标芯片*****/#ifndef PS2_H#define PS2_H#include"DELAY.h"sbit mouse_SDA=P3^4;//数据线P3_5 计数器0输入端口sbit mouse_CLK=P3^3;//时钟线P3_3 外部中断1输入端口sbit led=P3^6;bit pp=0;bit ACK=0;uchar bdata mouse_byte; //接收字节bdata-->可寻址的片内RAMsbit mouse_byte_bit0=mouse_byte^0;//mouse_byte第0位sbit mouse_byte_bit1=mouse_byte^1;//mouse_byte第1位sbit mouse_byte_bit2=mouse_byte^2;//mouse_byte第2位sbit mouse_byte_bit3=mouse_byte^3;//mouse_byte第3位sbit mouse_byte_bit4=mouse_byte^4;//mouse_byte第4位sbit mouse_byte_bit5=mouse_byte^5;//mouse_byte第5位sbit mouse_byte_bit6=mouse_byte^6;//mouse_byte第6位sbit mouse_byte_bit7=mouse_byte^7;//mouse_byte第7位uchar mouse_buffer[11];//接收位数据缓冲区uchar mouse_buffer_bit=0;//mouse_buffer[mouse_buffer_bit]uchar mouse_data[4];//接收鼠标数据缓冲区,分别存放:功能信息字节,x位移量,y位移量uchar mouse_data_bit=0;//mouse_data[mouse_data_bit]uchar move_x;//存放横坐标uint move_y;//存放纵坐标uchar move_z;extern uchar move_x;/*********************************************************************** 发送数据************************************************************************/void Init_ter(void){EA=1; //开放中断EX1=1;//允许外部中断1PX1=1;//设置中断优先级设外部中断1为最高优先级别}void host_to_mouse(uchar cmd){uchar i;EX1=0;mouse_CLK=0;delay100;delay100;ACC=cmd;pp=~P; //获得奇偶校验位mouse_SDA=0;mouse_CLK=1;for(i=0;i<8;i++){while(mouse_CLK==1);mouse_SDA=cmd&0x01;cmd>>=1;while(mouse_CLK==0);}while(mouse_CLK==1);mouse_SDA=pp; //发送奇偶校验位while(mouse_CLK==0);while(mouse_CLK==1);mouse_SDA=1;while(mouse_CLK==0);while(mouse_CLK==1);ACK=mouse_SDA; //接收应答位while(mouse_CLK==0);EX1=1;}void Init_mouse(void){host_to_mouse(0xf4);delayms(50);Init_ter();delayms(50);host_to_mouse(0xf3);delayms(50);host_to_mouse(0xc8);delayms(50);host_to_mouse(0xf3);delayms(50);host_to_mouse(0x64);delayms(50);host_to_mouse(0xf3);delayms(50);host_to_mouse(0x50);delayms(50);host_to_mouse(0xf2);delayms(200);mouse_data[0]=0;mouse_data[1]=0;mouse_data[2]=0;mouse_data[3]=0;move_x=10;move_y=0;move_z=0;}/*********************************************奇校检**********************************************/uchar Checkout(void){ACC=mouse_byte;if(~P==mouse_buffer[9])return 1;elsereturn 0;}/********************************************************* 数据分析及处理**********************************************************/ void data_analyse(void){//将收到的11位信号中截取8位数据放进mouse_bytemouse_byte_bit0=mouse_buffer[1];mouse_byte_bit1=mouse_buffer[2];mouse_byte_bit2=mouse_buffer[3];mouse_byte_bit3=mouse_buffer[4];mouse_byte_bit4=mouse_buffer[5];mouse_byte_bit5=mouse_buffer[6];mouse_byte_bit6=mouse_buffer[7];mouse_byte_bit7=mouse_buffer[8];if(Checkout())//如果校验正确{if(mouse_data_bit<4)mouse_data[mouse_data_bit++]=mouse_byte;if(mouse_data_bit==4){mouse_data_bit=0;if(mouse_data[0]&0x10)//如果"X sign bit"为1,表示鼠标向右左移{move_x-=(256-mouse_data[1]);//x坐标减if(move_x<=1)move_x=17;}else{move_x+=mouse_data[1];//x坐标加if(move_x>=17)move_x=2;}if(mouse_data[0]&0x20){move_y-=(256-mouse_data[2]);//y坐标减}else{move_y+=mouse_data[2];//y坐标加}if(mouse_data[3]&0x08){move_z-=(16-(mouse_data[3]&0x0f));}else{mouse_data[3]=mouse_data[3]&0x0f;move_z+=mouse_data[3]; //Z坐标加}}}}/**************************************************外部中断1***************************************************/void ReceiveData(void) interrupt 2{ led=0;if(mouse_buffer_bit<=10){while(mouse_CLK==0);//等待设备拉高时钟线mouse_buffer[mouse_buffer_bit++]=mouse_SDA;//接收数据}if(mouse_buffer_bit==10){data_analyse();//数据分析及处理mouse_buffer_bit=0;}}#endif////////////////////////////////主函数部分//////////////////////////////////////////////////// #include<reg51.h>#include"TM1638.h"#include"DS1302.h"#include"DELAY.h"#include"CHULI.h"#include"DISPLAY.h"void main(){delayms(500);init_TM1638();delayms(50);Init_DS1302(); //将1302初始化delay(500);while(1){read_date();delayms(50);}}/**********************************************/再来一个按键测试的程序:这个是另外的,与上面的无关;#ifndef _TM1638_H#define _TM1638_H#include <REGX51.H>#define DATA_COMMAND 0X40#define DISP_COMMAND 0x80#define ADDR_COMMAND 0XC0//引脚定义sbit DIO=P1^0;sbit CLK=P1^1;sbit STB=P1^2;unsigned char code tab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71 ,0x40};void TM1638_Write(unsigned char DATA) //写数据函数{unsigned char i;for(i=0;i<8;i++){CLK=0;if(DATA&0X01)DIO=1;elseDIO=0;DATA>>=1;CLK=1;}}unsigned char TM1638_Read(void) //读数据函数{unsigned char i;unsigned char temp=0;DIO=1; //设置为输入for(i=0;i<8;i++){temp>>=1;CLK=0;if(DIO)temp|=0x80;CLK=1;}return temp;}void Write_COM(unsigned char cmd) //发送命令字{STB=0;TM1638_Write(cmd);STB=1;}unsigned char Read_key(void){unsigned char c[4],i,key_value=0;STB=0;TM1638_Write(0x42); //读取键盘值for(i=0;i<4;i++)c[i]=TM1638_Read();STB=1; //4个字节数据合成一个字节for(i=0;i<4;i++)key_value|=c[i];for(i=0;i<4;i++)if(c[i]==key_value)break;return (i*8+key_value);}void Write_DATA(unsigned char add,unsigned char DA TA) //指定地址写入数据{Write_COM(0x44);STB=0;TM1638_Write(0xc0|add);TM1638_Write(DA TA);STB=1;}/*void Write_oneLED(unsigned char num,unsigned char flag) //单独控制一个LED函数,num 为需要控制的led序号,flag为0时熄灭,不为0时点亮{if(flag)Write_DATA(2*num+1,1);elseWrite_DATA(2*num+1,0);}void Write_allLED(unsigned char LED_flag) //控制全部LED函数,LED_flag表示各个LED状态{unsigned char i;for(i=0;i<8;i++){if(LED_flag&(1<<i))Write_DATA(2*i+1,3);elseWrite_DATA(2*i+1,0);}} */void init_TM1638(void){unsigned char i;Write_COM(0x8a);//亮度Write_COM(0x40);STB=0;TM1638_Write(0xc0);for(i=0;i<16;i++)TM1638_Write(0x00);STB=1;}#endif#include <at89x51.H>#include <tm1638.h>#include "555.h"unsigned char num[8]; //各个数码管显示的值unsigned char x=0xff;sbit key=P3^4;int main(void){init_TM1638();system_init();display(x);delay_ms(200);display1638();while(1){display1638();// detectkey();x=Read_key();display(~x);}}void display1638(){ uchar i;if(sec_flag|i){num[0]=hour/10;num[1]=hour%10;num[3]=minute/10;num[4]=minute%10;num[6]=second/10;num[7]=second%10;num[2]=16;num[5]=16;for(i=0;i<8;i++){Write_DATA(i*2,tab[num[i]]);}}}#ifndef _555_H#define _555_H#include<at89x51.h>#define uchar unsigned charvoid delay_ms(uchar);void display();void detectkey();void display1638();uchar code tap[10]={0xC0,/*0*/0xF9,/*1*/0xA4,/*2*/0xB0,/*3*/0x99,/*4*/0x92,/*5*/0x82,/*6*/0xF8,/*7*/0x80,/*8*/0x90,/*9*/};sbit key_hour=P3^5;sbit key_min=P3^6;uchar hour=12;uchar minute=0;uchar second=0;uchar flag=0;uchar sec_flag=0;/************************* 初始化程序**************************/ void system_init(){ TMOD=0x01;EA=1;ET0=1;EX0=1;IT0=1;TR0=1;TH0=(65536-50000)/256;TL0=(65536-50000)%256;}/************************* 定时器中断0中断处理程序**************************/ void int1_ISR() interrupt 1 {TH0=(65536-50000)/256;TL0=(65536-50000)%256;flag++;if(flag==20){sec_flag=1;second++;flag=0;}if(second==60){second=0;minute++;}if(minute==60){minute=0;hour++;hour%=24;}}/************************* 按键程序**************************/ void detectkey(){delay_ms(10);if(key_hour==0){hour++;hour=hour%24;while(!key_hour)display();}if(key_min==0){minute++;minute=minute%60;while(!key_min)display();}if(P3_7==0){hour=12;minute=0;second=0;while(!P3_5)display();}/*************************显示程序**************************/void display(uchar i){P0=i;}/*************************延时处理程序**************************/void delay_ms(uchar no){uchar i,j;for(i=0;i<no;i++){for(j=0;j<164;j++);for(j=0;j<164;j++);}}#endif说明:按键值送P0接的LED显示。