文章地址: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 }