/*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();
}