1. 程式人生 > 實用技巧 >利用TM1650控制鍵盤操作

利用TM1650控制鍵盤操作

1. TM1650簡介

TM1650是深圳市天微電子股份有限公司系列數碼管驅動晶片的一個型號,在驅動數碼管的同時,可以控制鍵盤操作,資料手冊下載

  • 驅動8段4位共陰數碼管
  • 工作電壓3-5V
  • 驅動7×4=28個按鍵

2. 特點

  • 價格比較便宜:淘寶上大約0.2元/個左右,加上外圍電阻、電容,大概總共0.5元
  • 佔用I/O資源少:資料線(第3腳SDA)、時鐘線(第2腳SCL)各一條
  • 有按鍵後,第16腳DP會提供低電平,可用作微控制器的外部中斷,這樣可省去微控制器輪詢按鍵的動作(7段開屏時)

3. 原理圖

  • CN2連線鍵盤,適用於最多4×4的鍵盤
  • DIG1~DIG4分別連線鍵盤的4行
  • A~G分別連線鍵盤的7列,圖中只用到4列,因此E、F、G未連線
  • MCU使用的是STC8G1K08-20PIN的
    • sbit KEY_KEY_SCL = P1^7
    • sbit KEY_KEY_SDA = P1^6
    • sbit KEY_IRQ = P3^7

4. 根據時序圖寫出基本操作指令

4.1 START

 1 /********************************************************
 2 *說明:TM1650的START動作
 3 *備註:start之後,CLK和SDA均處於0狀態
 4 *TM1650時序圖:CLK時鐘週期最小為200ns,相當於頻率為:1s/200ns=5MHz,
 5 *手冊中描述了平均傳輸速率為4MHz,對應週期為1s/4Mhz=250ns
6 *同時,根據上位機和硬體介面的配置,平均傳輸速率會出現較大差異,建議值為100kHz(0.1Mhz)以下 7 *100kHz的頻率對應週期為:1s/100k=10us 8 *STC8g設定的主頻為22.1184MHz,週期為1s/22.1184MHz=45.2ns 9 *使用_nop_()實現延時已遠遠不能滿足要求,看網上的例子不少都是延時5us,這裡也採用一下 10 *********************************************************/ 11 void start(void) 12 { 13 KEY_SCL = 1; 14 KEY_SDA = 1
; 15 Delay1us(5); 16 KEY_SDA = 0; 17 Delay1us(5); 18 KEY_SCL = 0; 19 Delay1us(5); 20 }

4.2 ACT

 1 /********************************************************
 2 *說明:TM1650的ACT動作
 3 *備註:如果本次通訊正常,晶片在序列通訊的第8個時鐘下降沿後,
 4 *      TM1650主動把DAT拉低------因此DAT拉低是TM1650的硬體行為
 5 *      直到檢測到CLK來了上升沿,DAT釋放為輸入狀態(本晶片)
 6 *      act結束後,CLK和DAT線均處於低電平狀態
 7 *********************************************************/
 8 void act(void)
 9 {
10     unsigned char i;
11     i = 0;
12     while(KEY_SDA && (i < 100))
13     {
14         i++;
15     }
16     KEY_SCL = 1;
17     Delay1us(5);
18     KEY_SCL = 0;
19     Delay1us(5);
20 }

4.3 STOP

/********************************************************
*說明:TM1650的stop動作
*備註:stop後兩條線均處於高電平狀態
*********************************************************/
void stop(void)
{
    KEY_SCL=1;
    Delay1us(5);
    KEY_SDA=1;
    Delay1us(5);
}

4.4 WRITE

 1 /***************************************************
 2 *說明:操作TM1650的底層寫函式
 3 *引數:byte為寫入TM1650的位元組
 4 *備註:io.h中定義了控制TM1650晶片的兩個管腳
 5 *      sbit KEY_SCL = P1^7;
 6 *      sbit KEY_SDA = P1^6;
 7 *在wrt之前的start動作已經置CLK和SDA與低電平狀態
 8 ***************************************************/
 9 void wrt(unsigned char byte)
10 {
11     unsigned char i;
12     for(i = 8; i > 0; i--)
13     {
14         byte <<= 1; //高位在前
15         if(CY)
16         {
17             KEY_SDA = 1;
18         }
19         else
20         {
21             KEY_SDA = 0;            
22         }
23         Delay1us(5);
24         KEY_SCL = 1;
25         Delay1us(5);
26         KEY_SCL = 0;
27         Delay1us(5);
28     }
29     // 寫完8bit後CLK處於低電平狀態
30 }

4.5 READ

 1 /***************************************************
 2 *說明:操作TM1650的底層讀函式
 3 *返回:讀取按鍵的TM1650程式碼
 4 *備註:io.h中定義了控制TM1650晶片的兩個管腳
 5 *      sbit KEY_SCL = P1^7;
 6 *      sbit KEY_SDA = P1^6;
 7 *指令順序:start-command(4f)-ack-讀key-ack-stop
 8 ***************************************************/
 9 unsigned char read(void)
10 {
11     unsigned char i;
12     unsigned char coder = 0;
13     start();
14     wrt(COMMAND_READ_KEY_DATA); //讀按鍵命令
15     act();
16     for(i = 8; i > 0; i--)
17     {
18         KEY_SCL = 1;
19         Delay1us(5);
20         coder <<= 1;
21         if(KEY_SDA)
22         {
23             coder++;
24         }
25         KEY_SCL = 0;
26         Delay1us(5);
27     }
28     act();
29     stop();
30     return coder;
31 }

4.6 使用的一些巨集

 1 //命令巨集定義區---------------------------開始*/
 2 #define COMMAND_SET_PARAMETER  0x48 //設定系統引數命令
 3 #define COMMAND_READ_KEY_DATA  0x49 //讀取按鍵資料命令
 4 //命令巨集定義區---------------------------結束*/
 5 
 6 //引數巨集定義區---------------------------開始*/
 7 #define PARAMETER_BRIGHTNESS_NORMAL 0x00
 8 #define PARAMETER_BRIGHTNESS_ONE    0x10
 9 #define PARAMETER_BRIGHTNESS_TWO    0x20
10 #define PARAMETER_BRIGHTNESS_THREE  0x30
11 #define PARAMETER_BRIGHTNESS_FOUR   0x40
12 #define PARAMETER_BRIGHTNESS_FIVE   0x60
13 #define PARAMETER_BRIGHTNESS_SIX    0x60
14 #define PARAMETER_BRIGHTNESS_SEVEN  0x70
15 
16 #define PARAMETER_SEGMENT_MODE_EIGHT 0x00 //8段輸出(預設)
17 #define PARAMETER_SEGMENT_MODE_SEVEN 0x08 //7段輸出,用於鍵盤讀出時,16腳DP/KP為鍵盤掃描標識輸出
18                                           //  7段模式且開屏時(48H+09H):
19                                           //    在沒有按鍵按下時DP/KP腳輸出高電平
20                                           //    在有按鍵按下時,DP/KP腳會輸出低電平
21                                           //    當下一次按鍵資料被讀取後(或關屏)DP/KP腳輸出高電平
22                                           //    KP可以用作是否有按鍵的指示,連線MCU的外部中斷腳
23 #define PARAMETER_WORK_MODE_RUNNING  0x00 //正常工作模式
24 #define PARAMETER_WORK_MODE_STANDBY  0x04 //待機工作模式
25 
26 #define PARAMETER_DISPLAY_MODE_OFF 0x00 //關閉屏顯示
27 #define PARAMETER_DISPLAY_MODE_ON  0x01 //開啟屏顯示
28                                         //  當傳送開屏命令且為正常工作模式時,DIG1-DIG4開始進行掃描
29                                         //  所以,讀鍵盤時也需要開屏
30 //引數巨集定義區---------------------------結束*/

5 TM1650的初始化

 1 /***************************************************
 2 *說明:初始化連線鍵盤晶片的管腳
 3 *備註:io.h中定義了控制TM1650晶片的三個管腳
 4 *      sbit KEY_KEY_SCL = P1^7;
 5 *      sbit KEY_KEY_SDA = P1^6;
 6 *      sbit KEY_IRQ = P3^7; 在7段模式下,在沒有按鍵按下時DP/KP腳輸出高電平,
 7 *                           在有按鍵按下時, DP/KP腳會輸出低電平,
 8 *                           當下一次按鍵資料被讀取後(或關屏) DP/KP腳輸出高電平
 9 *     (讀按鍵時資料從TM1650輸出到MCU,此時與TM1650的DAT相連的IO口必須設定為輸入模式且釋放匯流排)
10 *      將這兩個管腳設定成準雙向模式(00)
11 *操作順序:start-command1(系統命令)-act-command2(系統引數設定)-act-stop
12 ***************************************************/
13 void TM1650_Init(void)
14 {
15     // 留出上電初始化時間
16     Delay1ms(60);
17     // 設定兩線管腳工作模式,準雙向(00)
18     P1M1 &= ~(0x03<<6);
19     P1M0 &= ~(0x03<<6);
20     // 讀鍵盤功能初始化,7段、正常模式、開屏
21     start();
22     wrt(COMMAND_SET_PARAMETER);
23     act();
24     wrt(PARAMETER_SEGMENT_MODE_SEVEN | PARAMETER_WORK_MODE_RUNNING | PARAMETER_DISPLAY_MODE_ON);
25     act();
26     stop();
27     //KEY_IRQ P3.7初始化為INT3
28     INTCLKO |= 1<<5; // 允許INT3(僅支援下降沿中斷)
29     EA = 1;
30     // TM1650第16腳DP按鍵觸發訊號引腳初始化,高阻模式(預設值)
31     P3M1 |= 0x01 << 7;    // 1
32     P3M0 &= ~(0x01 << 7); // 0
33 }

6. 鍵盤中斷處理

1 /**************************************************
2 *說明:按下某個按鍵時觸發INT3中斷,這是INT3的中斷處理程式
3 **************************************************/
4 void Int3_Irq(void) interrupt 11
5 {
6     g_FlagKeyPressed = TRUE;
7 }

g_FlagKeyPressed是一個全域性變數,main函式中只需要檢視該變數的值就知道是否有按鍵被按下了。

7. 按鍵的讀取

通過外部中斷得到有按鍵被按下的訊息後,就可讀取按鍵值了。下面程式中巨集ZERO代表0的ASCII碼0x30,其他類同。

 1 /***************************************************
 2 *說明:讀取按鍵的值
 3 *返回:根據自己的鍵盤佈局,將得到的值與所按鍵的ASCII碼進行對應
 4 ***************************************************/
 5 unsigned char TM1650_GetKeyAscii(void)
 6 {
 7     unsigned char key;
 8     do{
 9         key = read();
10     }while(key > 0x40); // 等待按鍵鬆開
11     init(); // 初始化TM1650
12     
13     //BeepBlink(); //聲光提示(其他功能,與操作TM1650無關)
14     
15     switch(key)
16     {
17         case 0x0f:
18             return ZERO;
19         break;
20         case 0x17:
21             return HASH;
22         break;
23         case 0x07:
24             return ASTERISK;
25         break;
26         case 0x0e:
27             return EIGHT;
28         break;
29         case 0x16:
30             return NINE;
31         break;
32         case 0x06:
33             return SEVEN;
34         break;
35         case 0x0d:
36             return FIVE;
37         break;
38         case 0x15:
39             return SIX;
40         break;
41         case 0x05:
42             return FOUR;
43         break;
44         case 0x0c:
45             return TWO; 
46         break;
47         case 0x14:
48             return THREE;
49         break;
50         case 0x04:
51             return ONE; 
52         break;
53         default:
54             return 0;
55     }
56 }

上段程式中,第11行init()非常重要,其作用是再次初始化TM1650。具體原因如下:

1)當某個按鍵被按下時,DP給出低電平,觸發外部中斷,進而可以讀取改鍵的鍵值。

2)此時DP會一直保持低電平狀態,不能再次觸發外部中斷。

3)執行init(),再次初始化TM1650,DP將恢復到高電平狀態,為下次按鍵做好準備。

 1 /***************************************************
 2 *說明:恢復第16腳DP的低電平狀態
 3 *備註:第一次按鍵後,DP會從高電平變成低電平狀態,只有再次初始化TM1650才能
 4 *      將DP恢復成高電平狀態,從而為第二次按鍵做好準備
 5 ***************************************************/
 6 void init()
 7 {
 8     start();
 9     wrt(COMMAND_SET_PARAMETER);
10     act();
11     wrt(PARAMETER_SEGMENT_MODE_SEVEN | PARAMETER_WORK_MODE_RUNNING | PARAMETER_DISPLAY_MODE_ON);
12     act();
13     stop();
14 }

8 main函式

去掉其他功能,簡化的main函式非常簡單。

 1 void main(void)
 2 {
 3     unsigned char key;
 4     TM1650_Init();
 5     while(1)
 6     {
 7         if(g_FlagKeyPressed == TRUE)
 8         {
 9             g_FlagKeyPressed = FALSE;
10             key = TM1650_GetKeyAscii();
11             // 處理key。。。。。。
12         }
13     }
14 }