利用TM1650控制键盘操作

文章地址:https://www.cnblogs.com/jqdy/p/13865153.html

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的键盘(将E、F、G都引出后可适用于4×7键盘)
  • DIG1~DIG4分别连接键盘的4行
  • A~G分别连接键盘的7列,图中只用到4列,因此E、F、G未连线
  • MCU使用的是STC8G1K08-20PIN的
    • sbit KEY_SCL = P1^7
    • sbit 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. 按键的读取

     通过外部中断得到有按键被按下的消息后,就可读取按键值了。

 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 '0';
19         break;
20         case 0x17:
21             return '#';
22         break;
23         case 0x07:
24             return '*';
25         break;
26         case 0x0e:
27             return '8';
28         break;
29         case 0x16:
30             return '9';
31         break;
32         case 0x06:
33             return '7';
34         break;
35         case 0x0d:
36             return '5';
37         break;
38         case 0x15:
39             return '6';
40         break;
41         case 0x05:
42             return '4';
43         break;
44         case 0x0c:
45             return '2'; 
46         break;
47         case 0x14:
48             return '3';
49         break;
50         case 0x04:
51             return '1'; 
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 }
原文地址:https://www.cnblogs.com/jqdy/p/13865153.html