第12章 常用单片机接口程序 1 静 态 显 示 2 动 态 显 示 3 独立按键 4 矩阵按键 数码管是常用的输出显示器件,按键是常用的信号输入器件。
常见数码管引脚排列如图所示
静态显示电路如下图:
① 数码管要显示某个数字或字符,首先根据单只数码管引脚图,确定需要点亮数 码管的哪几段,从而确定数码管8个引脚电平的高低。
例12.1 74HC595移位显示程序,上电后数码管固定显示123456。 #include "STC15W4K.H" | // | 注意宏定义后面没分号 | #include "intrins.h" | // | 程序中_nop_()函数需用 |
sbit | Dat=P3^2; | // | 定义串行数据输入端 | sbit | Clk=P3^3; | // | 定义时钟端 | sbit | CNT=P3^4; | // | 定义控制端 |
unsigned char DispBuf[6]; unsigned char code DispTab[]={0x03,0x9f,0x25,0x0d,0x19,0x41,0x1f,0x01,0x09, 0x11,0xc1,0x63,0x85,0x61,0x71}; // 定义定形码表 void SendData(unsigned char SDat) // 74HC595传送一个字节的数据 { // 固定代码,直接复制使用 } void Disp() // 显示位数在6位内不用修改 { unsigned char c=0,i=0; CNT=0; // 为产生脉冲上升沿作准备 for(i=0;i<6;i++) // 显示位数需用根据硬件修改 { c=DispBuf[ i ]; // 取出待显示字符 SendData(DispTab[c]); // 送出字形码数据 } CNT=1; // 产生脉冲上升沿,并行输出数据 }
void main() { unsigned long i=123456; // 123456 DispBuf[0]=i%10; // 个位 DispBuf[1]=i/10%10; // 十位 DispBuf[2]=i/100%10; // 百位 DispBuf[3]=i/1000%10; // 千位 DispBuf[4]=i/10000%10; // 万位 DispBuf[5]=i/100000%10; // 十万位 Disp(); for(;;) { ; // 程序停在这里 } } 2 动态显示 这里介绍的动态显示电路如下图所示,看起来有点像静态显示电路,但比静态 显示电路占用硬件要少,比单片机直接驱动数码管占用IO口也要少。 DAT 结合电路图,控制某一位数码管电源通断的位选码就简单多了,如下所示。 单片机内部数据位 595引脚 | D7 Q0 | D6 Q1 | D5 Q2 | D4 Q3 | D3 Q4 | D2 Q5 | D1 Q6 | D0 Q7 | 位码 | 十万位 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 0xDF | 万位 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 0xBF | 千位 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0x7F | 百位 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0xFB | 十位 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 0xFD | 个位 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0xFE |
例12.3 单片机上电后数码管显示123456 sbit | Dat=P4^2; | // | 定义串行数据输入端 | sbit | Clk=P4^4; | // | 定义时钟端 | sbit | CNT=P4^5; | // | 定义控制端 |
unsigned char code BitTab[]={0xFE,0xFD,0xFB,0x7F,0xBF,0xDF,}; // 位选 code unsigned char DispTab[] = {0x14,0x77,0x4c,0x45,0x27,0x85,0x84,0x57, 0x04,0x05,0x06,0xa4,0x9c,0x64,0x8c,0x8e}; // 字形码 unsigned char DispBuf[6]; // 6字节的显示缓冲区,DispBuf[0]是最低位; void SendData(unsigned char SendDat) // 74HC595传送一个字节的数据 { ;// 同例12.1 } void timer0_init() // 定时器初始化 { // 2ms定时中断设置 } void main() { timer0_init(); DispBuf[0]=6; DispBuf[1]=5; DispBuf[2]=4;DispBuf[3]=3; DispBuf[4]=2;DispBuf[5]=1; for(;;) { ; } }
void Timer0() interrupt 1 {
unsigned char tmp; //临时变量
static unsigned char Count=0; //显示程序通过它得知现正显示哪个数码管
// ***************** 重装定时常数 *************************** TH0=0xf1; TL0=0x99; // 定时时间为2ms/22.1184MHz // ******************点亮某位数码管************************** CNT=0; // 为产生脉冲上升沿作准备 SendData(BitTab[Count]); // 最先点亮最右边个位 // ******************输出待显数据************************** tmp=DispBuf[Count]; // 根据当前的计数值取显示缓冲待显示值 tmp=DispTab[tmp]; // 取字形码 SendData(tmp) ; CNT=1; // 产生脉冲上升沿,并行输出数据 // ******************************************** Count++; if(Count==6) Count=0; }
3 独立按键 电路如图所示,单片机引脚作为输入使用,软件首先将接有按键的IO口置1,当键 没有被按下时,单片机引脚上为高电平,而当键被按下后,引脚接地,单片机引 脚上为低电平,通过编程即可获知是否有键按下及按键的位置。 由于机械按键按下和松开瞬间都会产生抖动,为了不让一次按键动作过程中程序产生多 次响应引起混乱,就需要软件去抖动处理,它的思路是:在单片机获得某按键IO口为低 的信息后,不是立即认定该键被按下,而是延时一段时间,通常选择10mS,再次检测IO 口,如果仍为低,则说明该键的确被按下,这避开了按键的前沿抖动,而在检测到按键 释放后(该IO口为高),再延时10mS,消除释放时的后沿抖动,然后再对键值进行处 理,实际的程序中其实一般都是不需要后沿抖动处理的,在后沿抖动的过程中,程序可 能误判为键按下,在键按下后程序会执行前沿延时10mS,所以前沿的10mS延时也就同时 用作了后沿去抖动的10mS。主要程序代码如下: void main() { unsigned char KValue; | // | 存放键值 | port_mode(); | // | 所有IO口设为准双向弱上拉方式。 | for(;;) { | | | KValue=Key(); | // | 调用键盘程序并获得键值 | if(KValue) | // | 如果该值不等于0,表示有键按下 | { | | | KProce(KValue); | // | 根据键值执行不同的功能 |
} } } unsigned char Key() // 获取键值函数 { unsigned char KValue; // 存放键值 unsigned char tmp; // 临时变量 P3|=0x3c; // 0x3c=0011 1100,将P3口接键盘的中间四位置1 _nop_();_nop_(); KValue=P3; KValue|=0xc3; | // | // STC指令太快,加上更可靠 0xc3=1100 0011,将未接键的4位置1 |
if(KValue==0xff) // 中间4位均为1,无键按下 |
return(0); | | // 返回 | delay10ms(); KValue=P3; KValue|=0xc3; | // | // 延时10ms,去键抖 // 与下一行一起作最终返回键值。 0xc3=1100 0011,将未接键的4位置1,最终返回键值。 |
if(KValue==0xff) // 中间4位均为1,无键按下 |
return(0); // 返回,如尚未返回,说明一定有1或更多位被按下 for(;;) { tmp=P3; // 等待按键释放 if((tmp|0xc3)==0xff) break; } return(KValue); }
4 矩阵按键 电路如图所示,只要熟悉了独立按键,矩阵按键也同样的简单。
主要代码如下: sbit KeyOut1 = P2^7;sbit KeyOut2 = P2^6;sbit KeyOut3 = P2^5;sbit KeyOut4 = P2^4; sbit KeyIn1 = P2^3;sbit KeyIn2 = P2^2;sbit KeyIn3 = P2^1;sbit KeyIn4 = P2^0; unsigned char KeyScan() { unsigned char key=0xff; // 临时变量 KeyOut1 = 1; KeyOut2 = 1; KeyOut3 = 1; KeyOut4 = 0; // 扫描第1列 if(KeyIn1 == 0) { delay10ms(); if(KeyIn1 == 0) key=0x00; } if(KeyIn2 == 0) { delay10ms(); if(KeyIn2 == 0) key=0x04; } if(KeyIn3 == 0) { delay10ms(); if(KeyIn3 == 0) key=0x08; } if(KeyIn4 == 0) { delay10ms(); if(KeyIn4 == 0) key=0x0c; } KeyOut1 = 1; KeyOut2 = 1; KeyOut3 = 0; KeyOut4 = 1; // 扫描第2列 if(KeyIn1 == 0) { delay10ms(); if(KeyIn1 == 0) key=0x01; } …… } |