前面帖子中介绍了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开发板上测试通过。
