前面帖子中介绍了TM1638的工作特点,本帖就以一个以TM1638驱动的数码管模块为例,编程驱动显示和键盘扫描处理。
这个数码管显示、键盘扫描模块的电路图如下:
使用的是共阴数码管3461BS。
本帖只处理显示部分的代码,根据电路图和TM1638的工作特性,每次要显示一组8位数据的时候,需要把8个数码管相同位置的字段数据组织起来传给TM1638的显示寄存器。以下是处理程序:
/** 驱动TM1638的数码管模块 数码管 - 共阴数码管 3461BS 数码管笔段(4位数码管) TM1638 Pin -------------------------------------- a GR1 24 b GR2 23 c GR3 22 d GR4 21 e GR5 20 f GR6 19 g GR7 17 dp GR8 16 第一组数码管D1 g1(数码管1) SG1 5 g2(数码管2) SG2 6 g3(数码管3) SG3 7 g4(数码管4) SG4 8 第二组数码管D2 g1(数码管1) SG5 9 g2(数码管2) SG6 10 g3(数码管3) SG7 11 g4(数码管4) SG8 12 */ #define STB 16 #define CLK 5 #define DIO 4 // 数码管显示的值 unsigned char tab[]={ 0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F, // 0-9, 索引:0-9 0x77,0x7C,0x39,0x5E,0x79,0x71, // A, b, C, d, E,F 索引:10-15 0xbf,0x86,0xDB,0xCF,0xE6,0xED,0xFD,0x87,0xFF,0xeF, // 0. - 9. , 索引:16-25 0xF7,0xFC,0xB9,0xDE,0xF9,0xF1, // A., b., C., d., E.,F. 索引:26-31 0x00,0x40,0x08, 0x37, 0x7C, 0x73, 0x80 // 空白,-,_, N, o, P, 点 索引:32-38 }; // 写数据函数 void TM1638_Write(unsigned char DATA) { unsigned char i; // 发送字节,每个字节8位 for(i=0;i<8;i++) { // 准备上升沿脉冲 digitalWrite(CLK, LOW); // 根据输出位,使DIO为高电平或者低电平 // 输出按照先低后高的顺序 if(DATA&0X01) digitalWrite(DIO, HIGH); else digitalWrite(DIO, LOW); // 移位到下一个Bit位 DATA>>=1; // 产生上升沿 digitalWrite(CLK, HIGH); } } // 读数据函数 unsigned char TM1638_Read(void) { unsigned char i; unsigned char temp=0; // 为了正常输入,拉高输入口 digitalWrite(DIO, HIGH); //设置为输入 pinMode(DIO, INPUT); for(i=0;i<8;i++) { temp>>=1; digitalWrite(CLK, LOW); if(digitalRead(DIO)>0) temp|=0x80; digitalWrite(CLK, HIGH); } return temp; } // 发送命令字 void Write_CMD(unsigned char cmd) { // 使DIO转换为输出模式 pinMode(DIO, OUTPUT); // 选通模块,允许读写 digitalWrite(STB, LOW); // 发送指令 TM1638_Write(cmd); // 关闭选通 digitalWrite(STB, HIGH); } // 读取按键 unsigned char Read_key(void) { unsigned char c[4],i,key_value=0; // 选通模块,允许读写 digitalWrite(STB, LOW); // 输出指令:0x42 - TM1638_Write(0x42); // 延迟100ms delay(100); // 为了正常输入,拉高输入口 digitalWrite(DIO, HIGH); pinMode(DIO, INPUT); // 取得输入数据 for(i=0;i<4;i++) c[i]=TM1638_Read(); digitalWrite(STB, HIGH); // 判断键值 if(c[0]==0x04) key_value=1; if(c[0]==0x40) key_value=2; if(c[1]==0x04) key_value=3; if(c[1]==0x40) key_value=4; if(c[2]==0x04) key_value=5; if(c[2]==0x40) key_value=6; if(c[3]==0x04) key_value=7; if(c[3]==0x40) key_value=8; if(c[0]==0x02) key_value=9; if(c[0]==0x20) key_value=10; if(c[1]==0x02) key_value=11; if(c[1]==0x20) key_value=12; if(c[2]==0x02) key_value=13; if(c[2]==0x20) key_value=14; if(c[3]==0x02) key_value=15; if(c[3]==0x20) key_value=16; // 延迟100ms delay(100); // 返回键值 return (key_value); } // 显示处理 void LedDisplay(unsigned char Num7,unsigned char Num6,unsigned char Num5,unsigned char Num4, unsigned char Num3,unsigned char Num2,unsigned char Num1,unsigned char Num0) { unsigned char seg_a,seg_b,seg_c,seg_d,seg_e,seg_f,seg_g,seg_dp; // 转换为笔段数据 seg_a=(tab[Num0]&0x01)+((tab[Num1]&0x01)<<1)+((tab[Num2]&0x01)<<2)+((tab[Num3]&0x01)<<3)+((tab[Num4]&0x01)<<4) +((tab[Num5]&0x01)<<5)+((tab[Num6]&0x01)<<6)+((tab[Num7]&0x01)<<7); seg_b=((tab[Num0]&0x02)>>1)+((tab[Num1]&0x02))+((tab[Num2]&0x02)<<1)+((tab[Num3]&0x02)<<2)+((tab[Num4]&0x02)<<3) +((tab[Num5]&0x02)<<4)+((tab[Num6]&0x02)<<5)+((tab[Num7]&0x02)<<6); seg_c=((tab[Num0]&0x04)>>2)+((tab[Num1]&0x04)>>1)+((tab[Num2]&0x04))+((tab[Num3]&0x04)<<1)+((tab[Num4]&0x04)<<2) +((tab[Num5]&0x04)<<3)+((tab[Num6]&0x04)<<4)+((tab[Num7]&0x04)<<5); seg_d=((tab[Num0]&0x08)>>3)+((tab[Num1]&0x08)>>2)+((tab[Num2]&0x08)>>1)+((tab[Num3]&0x08))+((tab[Num4]&0x08)<<1) +((tab[Num5]&0x08)<<2)+((tab[Num6]&0x08)<<3)+((tab[Num7]&0x08)<<4); seg_e=((tab[Num0]&0x10)>>4)+((tab[Num1]&0x10)>>3)+((tab[Num2]&0x10)>>2)+((tab[Num3]&0x10)>>1)+((tab[Num4]&0x10)) +((tab[Num5]&0x10)<<1)+((tab[Num6]&0x10)<<2)+((tab[Num7]&0x10)<<3); seg_f=((tab[Num0]&0x20)>>5)+((tab[Num1]&0x20)>>4)+((tab[Num2]&0x20)>>3)+((tab[Num3]&0x20)>>2)+((tab[Num4]&0x20)>>1) +((tab[Num5]&0x20))+((tab[Num6]&0x20)<<1)+((tab[Num7]&0x20)<<2); seg_g=((tab[Num0]&0x40)>>6)+((tab[Num1]&0x40)>>5)+((tab[Num2]&0x40)>>4)+((tab[Num3]&0x40)>>3)+((tab[Num4]&0x40)>>2) +((tab[Num5]&0x40)>>1)+((tab[Num6]&0x40))+((tab[Num7]&0x40)<<1); seg_dp=((tab[Num0]&0x80)>>7)+((tab[Num1]&0x80)>>6)+((tab[Num2]&0x80)>>5)+((tab[Num3]&0x80)>>4)+((tab[Num4]&0x80)>>3) +((tab[Num5]&0x80)>>2)+((tab[Num6]&0x80)>>1)+((tab[Num7]&0x80)); // 开显示,设置脉冲宽度为4/16 Write_CMD(0x8A); // 写数据到显示寄存器的指令 Write_CMD(0x40); // 选通模块,允许读写 digitalWrite(STB, LOW); // 写地址命令:0xC0 - 从显示地址0x00开始 TM1638_Write(0xC0); // 输出组合后的笔段数据(00H~0FH,共计16字节单元) // 见《三、显示寄存器地址》中数据设置的说明 TM1638_Write(seg_a); TM1638_Write(0x80); TM1638_Write(seg_b); TM1638_Write(0x00); TM1638_Write(seg_c); TM1638_Write(0x00); TM1638_Write(seg_d); TM1638_Write(0x00); TM1638_Write(seg_e); TM1638_Write(0x00); TM1638_Write(seg_f); TM1638_Write(0x00); TM1638_Write(seg_g); TM1638_Write(0x00); TM1638_Write(seg_dp); TM1638_Write(0x00); digitalWrite(STB, HIGH); delay(100); } // 显示字符串 // 获取字符对应的索引值,根据索引值,执行显示 void LedDispString(unsigned char *str) { unsigned char ch=0, idx=0, i=0; unsigned char dispIndex[8]={32,32,32,32,32,32,32,32}; for (i=0; ;i++) { ch=*(str+i); if (ch=='\0') { break; } if (i==0 && ch=='.') { //dispIndex[i]=38; dispIndex[0]=16; idx++; continue; } switch(ch) { case '0': dispIndex[idx]=0;idx++;break; case '1': dispIndex[idx]=1;idx++;break; case '2': dispIndex[idx]=2;idx++;break; case '3': dispIndex[idx]=3;idx++;break; case '4': dispIndex[idx]=4;idx++;break; case '5': dispIndex[idx]=5;idx++;break; case '6': dispIndex[idx]=6;idx++;break; case '7': dispIndex[idx]=7;idx++;break; case '8': dispIndex[idx]=8;idx++;break; case '9': dispIndex[idx]=9;idx++;break; case 'A': case 'a': dispIndex[idx]=10;idx++;break; case 'B': case 'b': dispIndex[idx]=11;idx++;break; case 'C': case 'c': dispIndex[idx]=12;idx++;break; case 'D': case 'd': dispIndex[idx]=13;idx++;break; case 'E': case 'e': dispIndex[idx]=14;idx++;break; case 'F': case 'f': dispIndex[idx]=15;idx++;break; case '.': dispIndex[idx-1]=dispIndex[idx-1]+16;break; case ' ': dispIndex[idx]=32;idx++;break; case '-': dispIndex[idx]=33;idx++;break; case '_': dispIndex[idx]=34;idx++;break; case 'N': case 'n': dispIndex[idx]=35;idx++;break; case 'O': case 'o': dispIndex[i]=36;break; case 'P': case 'p': dispIndex[i]=37;break; } if (idx>=8) { break; } } LedDisplay(dispIndex[0], dispIndex[1], dispIndex[2], dispIndex[3], dispIndex[4], dispIndex[5], dispIndex[6], dispIndex[7]); } void setup() { unsigned char i = 0; unsigned char idx[8]={32,32,32,32,32,32,32,32}; // 初始化接口为输出模式 pinMode(STB,OUTPUT); pinMode(CLK,OUTPUT); pinMode(DIO, OUTPUT); Serial.begin(115200); //LedDisplay(7,6,5,4,3,2,1,0); //LedDispString((unsigned char *)"3.1415926"); // 0从第一位到第八位移位显示 for (i=0; i < 8; i++) { idx[i] = 0; LedDisplay(idx[0], idx[1], idx[2], idx[3], idx[4], idx[5], idx[6], idx[7]); idx[i]=32; delay(500); } } long cnt=0; void loop() { char str[16]; // 转换为8位长的字符串 sprintf(str, "%8d", cnt); // 数码管显示 LedDispString((unsigned char *)str); // 自动加1 if (cnt>=99999999) { cnt=0; } else { cnt++; } delay(10); }
以上程序在ESP8266开发板上测试通过。