TM1640 数码管驱动代码(简化版)

/*TM1640的SRAM写数据格式-----------
 B7 B6 0 0 - - - -
  0  1 0 0 - - - -  寻址模式(1 Byte)
  1  1 0 0 - - - -  显存地址(1 Byte)
  data 1~N          数据传输(最多16 Byte) 固定地址时为一地址一数据
  1  0 0 0 - - - -  显示控制(1 Byte)
 指令类型:
            B7 B6 - - - - - -
 寻址模式:  0  1  
 显示控制:  1  0  
 显存地址:  1  1  
---------------------------------*/
#define DATA_MODE_AUTO   0x40  //地址自动加1/普通模式
#define DATA_MODE_FIXED  0x44  //固定地址
#define DATA_MODE_TEST   0x48  //测试模式
/*寻址模式-------------------------
 自动地址+1: 01000000            0x40
 固定地址  : 01000100            0x44
 普通模式  : 01000000            0x40
 测试模式  : 01001000 (内部使用) 0x48
---------------------------------*/

#define DIS_EN     0x8A  //显示开
#define DIS_DIS    0x80  //显示关
/*显示控制-------------------------
           B7 B6 - - B3 B2 B1 B0 (脉冲宽度)
 显示开关: 1  0  0 0 0  -  -  -  显示关  0x80
           1  0  0 0 1  -  -  -  显示开  0x88
 消光数量: 1  0  0 0 1  0  0  0    1/16  0x88
 (即亮度)  1  0  0 0 1  0  0  1    2/16  0x89
           1  0  0 0 1  0  1  0    4/16  0x8A
           1  0  0 0 1  0  1  1   10/16  0x8B
           1  0  0 0 1  1  0  0   11/16  0x8C
           1  0  0 0 1  1  0  1   12/16  0x8D
           1  0  0 0 1  1  1  0   13/16  0x8E
           1  0  0 0 1  1  1  1   14/16  0x8F
---------------------------------*/

/*显存地址-------------------------
           B7 B6 - - B3 B2 B1 B0 (显示单元)
     0xC0  1  1  0 0 0  0  0  0  GRID1
     0xC1  1  1  0 0 0  0  0  1  GRID2
     0xC2  1  1  0 0 0  0  1  0  GRID3
     0xC3  1  1  0 0 0  0  1  1  GRID4
     0xC4  1  1  0 0 0  1  0  0  GRID5
     0xC5  1  1  0 0 0  1  0  1  GRID6
     0xC6  1  1  0 0 0  1  1  0  GRID7
     0xC7  1  1  0 0 0  1  1  1  GRID8
     0xC8  1  1  0 0 1  0  0  0  GRID9
     0xC9  1  1  0 0 1  0  0  1  GRID10
     0xCA  1  1  0 0 1  0  1  0  GRID11
     0xCB  1  1  0 0 1  0  1  1  GRID12
     0xCC  1  1  0 0 1  1  0  0  GRID13
     0xCD  1  1  0 0 1  1  0  1  GRID14
     0xCE  1  1  0 0 1  1  1  0  GRID15
     0xCF  1  1  0 0 1  1  1  1  GRID16
---------------------------------*/

#define FIRST_ADDR  0xC0  //起始地址

/*======================================================================
 函数/功能主题描述/端口及常量、变量定义
======================================================================*/
// 端口定义
sbit DisDIN   =   P3^2;
sbit DisCLK   =   P3^3;
/*---------------------------------
 DIN : 显示端口-数据端
 DisCLK: 显示端口-时钟端
---------------------------------*/

u8 DisBuf[16];     // 显示缓存
/*---------------------------------
 各显示单元描述

…
---------------------------------*/

u8 code SegCode[]={
    //0x3F, 0x06, 0x5B, 0x4F, 0x66,  //  0~ 4: 0 1 2 3 4
    //0x6D, 0x7D, 0x07, 0x7F, 0x6F,  //  5~ 9: 5 6 7 8 9
    //0x77, 0x7C, 0x39, 0x5E, 0x79,  // 10~14: A b C d E
    //0x71, 0x76, 0x38, 0x50, 0x54,  // 15~19: F H L r n
    //0x1C, 0x73, 0x5C, 0x40, 0x00   // 20~24: u P o - null(熄灭)
};

端口驱动函数:

/**=====================================================================
 驱动功能函数
 SCLK: ──┐                   SCLK:  ┌──
         └─                        ─┘
  DIN: ─┐    //开始           DIN:   ┌─  //结束
        └──                        ──┘
          7   6   5   4   3   2   1   0
 传输:   ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─
       ──┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘
        ┌───┐───┌───┐───┌───┐───┌───┐───┐ ┌
       ─┘───└───┘───└───┘───└───┘───└───┘─┘  
          7   6   5   4   3   2   1   0
=====================================================================**/
//开始-----------------------------
void start(void){  //首次开始前(初始化期间)先触发1次结束, 以使SCLK=1, DIN=1
  DisDIN = 0;      //DIN由H→L, 开始
  DisCLK = 0;      //置SCLK为L, 为发送DIN位作准备
}
//发送数据-------------------------
void Send_DisDat(u8 dx){
  u8 i;
  for(i=0; i<8; i++){          //开始时已置SCLK=0
    DisDIN = (bit)(dx & 0x01); //发送位, 低位先传
    DisCLK = 1;                //位发送结束-DIN信号保持
    dx   >>= 1;
    DisCLK = 0;                //置SCLK=0, 为发送下一位/结束准备
  }
  //DisDIN = 0;               //该条语句与结束函数中的首条重复, 故可省掉
}
//结束-----------------------------
void stop(void){
  DisDIN = 0;
  DisCLK = 1;                  //置SCLK为H-结束准备
  DisDIN = 1;                  //DIN由L→H, 结束, 为下一次开始准备
}

注:以上三段函数省掉了不必要的语句,但须在初始化阶段完成端口电平的就绪状态(也即是结束数据传输后的状态)。

实际运行的数据信号波形(黄色为SCLK,青色为DIN):

提示:根据TM1640的规格书说明——CLK为高电平时,DIN高电平->低电平即为开始;CLK为高电平时,DIN低电平->高电平即为结束。换句话说就是,TM1640在下降沿捕捉开始信号,上升沿捕捉结束信号,且开始/结束信号的捕捉区间为CLK处于高电平时。 CLK处于低电平时为 数据信号的捕捉区间,且捕捉发生在CLK的上升沿。

为何要分为三段函数,而不是将它们合为一个函数?

因为在发送位数据的过程中有单字节和多字节数据 的区分,当然可以使用2层循环及使用指针传送单字节数据或数组地址来解决,比如下面的代码。但是,使用2层循环会涉及循环变量及必要的逻辑判断,且会消耗过多的执行时间,单单循环内末语句结束到退出循环——循环结束,就得10来个机器周期的时间,还不如使用函数调用(2~4个机器周期即完成)。

提示:对于底层操作代码,每一个多余的指令都会对直接影响上层操作代码的执行效率——若底层代码包含1条冗余代码,上层代码调用时可能会执行n多次该冗余代码。 简单来说,“一个很小的基数乘以一个很大的倍数”的问题对于底层 代码来说应需重视。

//数据紧凑发送---------------------
void sendDisDat(u8 *p){
  u8 i, j, dat, len;
  len = sizeof(p);
  //---------------------------开始
  DisDIN = 0;      //DIN由H→L, 开始
  DisCLK = 0;      //置SCLK为L, 为发送DIN位作准备
  //-----------------------发送数据
  for(i=0; i<len; i++){       //开始时已置SCLK=0
    if(len==1){dat = p;}else{dat = p[i];}  //单字节时指针值即为数据值,多字节时指针值为数组地址
    for(j=0; j<8; j++){
      DisDIN   = (bit)(dat & 0x01); //发送位
      DisCLK   = 1;                 //位发送结束-DIN信号保持
      dat >>= 1;
      DisCLK   = 0;                 //置SCLK=0, 为发送下一位/结束准备
    }
  }
  //---------------------------结束
  DisDIN = 0;
  DisCLK = 1;                  //置SCLK为H-结束准备
  DisDIN = 1;                  //DIN由H→L, 结束, 为下一次开始准备
}

 

 数据据操作函数:

/**=====================================================================
 函数名称: Update_DisDat
 输    入: addr-显存地址, dx-8位地址数据
 功能描述: 更新dx指定的地址的数据
 说    明: 
=====================================================================**/
void Update_DisDat(u8 addr, u8 dx){
  start(); Send_DisDat(DATA_MODE_FIXED); stop(); //固定地址
  start();
    Send_DisDat(addr);                           //指定地址
    Send_DisDat(DisBuf[dx]);                     //数据传输-1字符
  stop();
}

/**=====================================================================
 函数名称: UpdateAllDisDat
 输    入: 无
 功能描述: 更新所有显示数据
 说    明: 
=====================================================================**/
void UpdateAllDisDat(void){
  u8 i;
  //start(); Send_DisDat(DIS_DIS);        stop(); //显示关
  start(); Send_DisDat(DATA_MODE_AUTO); stop(); //自动地址
  start(); Send_DisDat(FIRST_ADDR);             //起始地址
  for(i=0; i<15; i++){                          //数据传输开始-15字符
    Send_DisDat(DisBuf[i]);
  }
  stop();                                       //数据传输结束
  //start(); Send_DisDat(DIS_EN);         stop(); //显示开
}

/**=====================================================================
 函数名称: DisBufFresh
 输    入:  dat[]-计数器
 功能描述: 错误时对应的编号闪烁
 说    明: 
=====================================================================**/
void DisBufFresh(u8 *dat){
  //显示缓冲更新代码
}

初始化函数:

/**=====================================================================
 函数名称: Dis_Init()
 输    入: 无
 功能描述: 显示缓冲及驱动初始化
 说    明: 
=====================================================================**/
void DisBufInit(void){
  u8 i;
  stop();                               //触发1次结束, 以使SCLK=1, DIN=1
  start(); Send_DisDat(DIS_EN); stop(); //显示开
  for(i=0; i<16; i++){
    DisBuf[i]  = SegCode[24];           //全部熄灭
  }
  UpdateAllDisDat();
}

 

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乘羽扬风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值