|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
第二十五节:专用集成芯片TM1639驱动8位数码管
(1)开场白:
在那么多种数码管的驱动方案中,我最喜欢使用专用集成芯片TM1639来驱动数码管。我认为这种方案是性价比最高的。既然是专用的集成芯片,那么我们可以把TM1639看成是一个独立的单片机,这个单片机也是利用动态扫描数码管的办法,只是厂家出厂时就已经把扫描驱动数码管的程序都写好了,并且烧录进了TM1639,他们预留3根IO口出来让外部的单片机跟TM1639通讯,我们编写程序时,只要按照厂家提供的通讯协议,往里面写入对应的指令和数据就可以间接静态驱动数码管。注意,我这里提到了“动态驱动”和“静态驱动”的概念,TM1639直接驱动数码管是属于动态的,但是我们编写程序跟TM1639通讯,这种间接驱动数码管是属于静态驱动的,因为我们的单片机不用定时扫描数码管。总之,我认为TM1639有4个好处:
(a)节省单片机IO口,3根IO口就可以控制8位数码管.
(b)CPU开销小。对于单片机来说,是属于静态驱动控制。
(c)外围电路简单,连串接的限流电阻和三极管也不用了,因为此芯片内部集成了8级灰度调节电路,我们 只要发送相应的指令,就可以调节数码管的亮度。
(d)这个芯片本身价格不算高。
(2)功能需求:
在8个数码管中,从左到右,依次显示“12345678”。
(3)硬件原理:
用单片机的3根IO口跟TM1639通讯。TM1639跟数码管的电路特别简单,不用串电阻,也不用加三极管,具体的电路与通讯协议请读者自己在网上下载,资料很容易获得,而且还是中文的。本程序是驱动8位的共阴数码管。
(4)源码适合的单片机: PIC18f4520,晶振为11.0592MHz。
(5)源代码讲解如下:
#include<pic18.h> //包含芯片相关头文件
//tm1639的IO宏定义
#define tm1639_stb_dr LATA3
#define tm1639_clk_dr LATA4
#define tm1639_dio_dr LATA5
//位地址,共8位地址,每位地址里面的1个字节的数据代表8个LED灯的亮或灭
#define const_dig_addr0 0xc0
#define const_dig_addr1 0xc2
#define const_dig_addr2 0xc4
#define const_dig_addr3 0xc6
#define const_dig_addr4 0xc8
#define const_dig_addr5 0xca
#define const_dig_addr6 0xcc
#define const_dig_addr7 0xce
//数码管或者LED灯的显示亮度级别
#define const_level_off 0x80 //级别最低,最暗
#define const_level_1 0x88
#define const_level_2 0x89
#define const_level_4 0x8a //本程序采取这个级别的亮度
#define const_level_10 0x8b
#define const_level_11 0x8c
#define const_level_12 0x8d
#define const_level_13 0x8e
#define const_level_14 0x8f //级别最高,最亮
#define const_auto_add_addr 0x40 //采用地址自动加一方式写显存
void write_byte_tm1639(unsigned char tm1639_byte); //写入一个字节数据进tm1639
void display_tm1639(unsigned char dig_addr,unsigned char dig_data,unsigned char dig_level); //一次显示8个LED灯或者一位数码管
//其中dig_addr是地址,dig_data是地址的数据,dig_level是亮度
void display_drive(); //数码管驱动程序,放在main函数的while(1)循环里
unsigned char number_left1=0; //左边第1位数码管显示的内容
unsigned char number_left2=0; //左边第2位数码管显示的内容
unsigned char number_left3=0; //左边第3位数码管显示的内容
unsigned char number_left4=0; //左边第4位数码管显示的内容
unsigned char number_left5=0; //左边第5位数码管显示的内容
unsigned char number_left6=0; //左边第6位数码管显示的内容
unsigned char number_left7=0; //左边第7位数码管显示的内容
unsigned char number_left8=0; //左边第8位数码管显示的内容
unsigned char led_update=0; //更新显示变量,等于1时将执行一次更新显示数码管的程序
unsigned char number_temp[8]; //即将更新显示内容的中间变量
const unsigned char number_table[]= //数码管的字模转换表
{
0x3F, //0
0x06, //1
0x5B, //2
0x4F, //3
0x66, //4
0x6D, //5
0x7D, //6
0x07, //7
0x7F, //8
0x6F, //9
};
//主程序
main()
{
ADCON0=0x00;
ADCON1=0x0f; //全部为数字信号
ADCON2=0xa1; //右对齐
RBPU=0; //上拉电阻
SSPEN=0;
TRISA3=0; //TM1639的3根驱动IO之一
TRISA4=0; //TM1639的3根驱动IO之一
TRISA5=0; //TM1639的3根驱动IO之一
number_left1=1; //左边第1位数码管显示"1"
number_left2=2; //左边第2位数码管显示"2"
number_left3=3; //左边第3位数码管显示"3"
number_left4=4; //左边第4位数码管显示"4"
number_left5=5; //左边第5位数码管显示"5"
number_left6=6; //左边第6位数码管显示"6"
number_left7=7; //左边第7位数码管显示"7"
number_left8=8; //左边第8位数码管显示"8"
led_update=1; //更新显示
while(1)
{
CLRWDT(); //喂看门狗,大家不用过度关注此行
display_drive(); //数码管驱动程序,放在main函数的while(1)循环里
}
}
void display_drive() //数码管驱动程序,放在main函数的while(1)循环里
{
if(led_update==1) //有数据更新
{
led_update=0; //标志及时清零,避免一直扫描
number_temp[0]=number_table[number_left1]; //载入即将显示的内容
number_temp[1]=number_table[number_left2]; //载入即将显示的内容
number_temp[2]=number_table[number_left3]; //载入即将显示的内容
number_temp[3]=number_table[number_left4]; //载入即将显示的内容
number_temp[4]=number_table[number_left5]; //载入即将显示的内容
number_temp[5]=number_table[number_left6]; //载入即将显示的内容
number_temp[6]=number_table[number_left7]; //载入即将显示的内容
number_temp[7]=number_table[number_left8]; //载入即将显示的内容
display_tm1639(const_dig_addr0,number_temp[0],const_level_4); //显示左边第1位
display_tm1639(const_dig_addr1,number_temp[1],const_level_4); //显示左边第2位
display_tm1639(const_dig_addr2,number_temp[2],const_level_4); //显示左边第3位
display_tm1639(const_dig_addr3,number_temp[3],const_level_4); //显示左边第4位
display_tm1639(const_dig_addr4,number_temp[4],const_level_4); //显示左边第5位
display_tm1639(const_dig_addr5,number_temp[5],const_level_4); //显示左边第6位
display_tm1639(const_dig_addr6,number_temp[6],const_level_4); //显示左边第7位
display_tm1639(const_dig_addr7,number_temp[7],const_level_4); //显示左边第8位
}
}
//写入一个字节数据进tm1639
void write_byte_tm1639(unsigned char tm1639_byte)
{
unsigned char tm1639_i;
tm1639_stb_dr=0; //stb为低电平,程序不依赖于之前端口的状态,避免出现“端口迷失”
for(tm1639_i=0;tm1639_i<8;tm1639_i++)
{
tm1639_clk_dr=0;
if((tm1639_byte & 0x01)!=0)
tm1639_dio_dr=1;
else
tm1639_dio_dr=0;
tm1639_clk_dr=1;
tm1639_byte=tm1639_byte>>1;
}
}
//一次显示8个LED灯或者一位数码管
void display_tm1639(unsigned char dig_addr,unsigned char dig_data,unsigned char dig_level)
{
tm1639_dio_dr=1;
tm1639_clk_dr=1;
tm1639_stb_dr=1;
write_byte_tm1639(const_auto_add_addr); //设置成自动加一方式
tm1639_stb_dr=1;
write_byte_tm1639(dig_addr); //设置显示的位地址
write_byte_tm1639(dig_data & 0x0f); //显示的低4位数据,共代表4个led灯
write_byte_tm1639(dig_data >> 4 & 0x0f); //显示的高4位数据,共代表4个led灯
tm1639_stb_dr=1;
write_byte_tm1639(dig_level); //设置显示的亮度
tm1639_stb_dr=1;
}
(6)小结:
前面花了几个章节来讲常用的数码管驱动方式,下一节将要讲一下操作数码管显示的基本程序框架。
(7)下集预告:
按键操作数码管菜单的基本程序框架。
(未完待续,下节更精彩,不要走开哦) |
|