ligang 发表于 2014-1-29 13:56:52

数码管芯片TM1639的C程序

第二十五节:专用集成芯片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宏定义
#definetm1639_stb_dr   LATA3
#definetm1639_clk_dr   LATA4
#definetm1639_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;   //即将更新显示内容的中间变量

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=number_table;//载入即将显示的内容
      number_temp=number_table;//载入即将显示的内容
      number_temp=number_table;//载入即将显示的内容
      number_temp=number_table;//载入即将显示的内容
      number_temp=number_table;//载入即将显示的内容
      number_temp=number_table;//载入即将显示的内容
      number_temp=number_table;//载入即将显示的内容
      number_temp=number_table;//载入即将显示的内容

      display_tm1639(const_dig_addr0,number_temp,const_level_4); //显示左边第1位
      display_tm1639(const_dig_addr1,number_temp,const_level_4); //显示左边第2位
      display_tm1639(const_dig_addr2,number_temp,const_level_4); //显示左边第3位
      display_tm1639(const_dig_addr3,number_temp,const_level_4); //显示左边第4位
      display_tm1639(const_dig_addr4,number_temp,const_level_4); //显示左边第5位
      display_tm1639(const_dig_addr5,number_temp,const_level_4); //显示左边第6位
      display_tm1639(const_dig_addr6,number_temp,const_level_4); //显示左边第7位
      display_tm1639(const_dig_addr7,number_temp,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)下集预告:   
按键操作数码管菜单的基本程序框架。

(未完待续,下节更精彩,不要走开哦)

yao1318 发表于 2014-8-15 13:23:07

我们点数码管用LM1628多

jianhong_wu 发表于 2014-8-15 15:17:59

yao1318 发表于 2014-8-15 13:23
我们点数码管用LM1628多
涨知识了。下次用到数码管的,我也关注一下这款芯片LM1628 。

yao1318 发表于 2014-8-15 21:47:23

就不知哪 个便宜,我们全是用1628的,1639不怎见。
页: [1]
查看完整版本: 数码管芯片TM1639的C程序