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