厉害呀,受教了 |
由于还是新人 只有跟在高手后面慢慢做了! |
这是深夜逛论坛翻到宝了!!! |
楼主辛苦 回复的很仔细 |
6膜拜大佬 |
超级赞! 新手 目前写个 点亮一个LED都带写错的。。。。直接膜拜! |
高手啊,掌握了精髓,所以可以随心所欲了。 |
这真是大神啊 |
谢谢分享 |
高手呀,收藏一下 |
好东西,揽下试试,谢谢! |
学习。。。。。。。。。。。。。 |
![]() |
支持支持!! |
看看学习学习。。 |
楼主,我没看见按键啊? |
有套件卖吗?我要 |
由于还是新人 只有跟在高手后面慢慢做了! |
今天太晚了,下次再弄吧 |
const unsigned char Key_tab[]= //键码映射表 {// 0 1 2 3 4 5 6 7 8 9 22, 0, 2, 0, 0, 0, 0, 0, 4, 0, //0 0, 0, 0, 0, 0,18, 0, 0, 0, 0, //1X 0, 0, 0, 0, 0, 0, 3,14, 0, 0, //2X 20,10, 6, 0, 0, 0, 0, 0, 1,19, //3X 0, 5, 0, 0, 0,15, 0,11, 0, 0, //4X 0,17, 0, 0,13, 8, 0,21, 0, 9, //5X 16,12, 7, 0 //6X }; //=============== 检测按键 ================= void Key_scan() { unsigned char i; Key_count --; //扫描次序 Key_count &= 3; switch (Key_count) //按次序处理 { case 2: //第一轮扫描 if (PINA & 0x02) Key_state |= 0x01; //读入PINA.1,记录于Key_state.0 if (PINA & 0x04) Key_state |= 0x02; //读入PINA.2,记录于Key_state.1 DDRA &= ~(1<<0); //A口位0先改为输入 PORTA |= (1<<0); //A口位0上拉有效 DDRA &= ~(1<<2); //A口位2先改为输入 PORTA |= (1<<2); //A口位2上拉有效 PORTA &= ~(1<<1); //A口位1设定为0 DDRA |= (1<<1); //A口位1改为输出0 break; case 1: //每二轮扫描 if (PINA & 0x01) Key_state |= 0x04; //读入PINA.0,记录于Key_state.2 if (PINA & 0x04) Key_state |= 0x08; //读入PINA.2,记录于Key_state.3 PORTA |= (1<<1); DDRA &= ~(1<<1); PORTA &= ~(1<<2); DDRA |= (1<<2); DDRA &= ~(1<<0); PORTA |= (1<<0); break; case 0: //每三轮扫描 if (PINA & 0x02) Key_state |= 0x10; //读入PINA.1,记录于Key_state.4 if (PINA & 0x01) Key_state |= 0x20; //读入PINA.0,记录于Key_state.5 PORTA |= (1<<2); DDRA &= ~(1<<2); PORTA &= ~(1<<0); DDRA |= (1<<0); DDRA &= ~(1<<1); PORTA |= (1<<1); break; default: //每四轮扫描 if (!(PINA&0X01)) Key_state &= ~(1<<0); if (!(PINA&0X02)) Key_state &= ~(1<<1); if (!(PINA&0X04)) Key_state &= ~(1<<2); break; } //======更新显示缓冲区======= i = Key_tab[Key_state]; if (i == 0) { Disp_buf[2] = 0x11; //显示三横 Disp_buf[1] = 0x11; Disp_buf[0] = 0x11; } else { Disp_buf[2] = 0x0c; //字符"C" Disp_buf[1] = i / 10; //键码十位 Disp_buf[0] = i % 10; //键码个位 } //Key_state = 0; } Key_state什么时候清零才合适?要是4IO口的话,键码又如何扩展呢? |
你没细看程序,Key_state只用了6位,defalt那次的位用压缩方或与前面6位中的3位相与。如果真的用到9位,键码表占512字节就不好了。 至于dafalt后的break,在这里可用可不用,因为defalt后已经是switch的未端。 你可以把文件传到这里,或发至cowboy3@163.com |
四个IO扫3次,那Key_state就不止8位了,咋办呢? 另外,default语句是不是漏了break? 楼主,能否把QQ号给我?我直接把仿真文件给你,就能直观地看到错误了 |
h333 发表于 2013-7-10 00:43 51的四个IO能扫65个键(或可以再多6个,但这6个可靠性不太好). AVR的,2个IO就可以扫15键,4个IO可能超100了,你可以搜索一下h2feo4的贴子。 |
先改变IO状态,立刻回读IO,有时会出错,IO外部受分布参数影响会有延时,特别是AVR上拉电阻较大,如果立该回读可能出错。 这里看似先读再改变IO输出,其实读的时候,上一轮扫描中已经把IO改变,到现在已过了5ms,IO状态足够稳定了,这时读入就可靠。 读完了,再改变IO状态,其实是为下一次做好准备。 Key_state用组合生成,效果是一样,只是处理繁琐且占内存也多。你先前的程序IO切换过程不正确才可能导致没效果。 |
4个IO口,能扫多少键呢? ^_^ |
为什么 不是: case 2: //第一轮扫描 DDRA &= ~(1<<0); //A口位0先改为输入 PORTA |= (1<<0); //A口位0上拉有效 PORTA &= ~(1<<1); //A口位1设定为0 DDRA |= (1<<1); //A口位1改为输出0 //第一轮扫描 if (PINA & 0x02) Key_state |= 0x01; //读入PINA.1,记录于Key_state.0 if (PINA & 0x04) Key_state |= 0x02; //读入PINA.2,记录于Key_state.1 break; 先改变IO口状态,再做键值判断? 我那个 Key_state = (KB5<<5 + KB4<<4 + KB3<<3 + KB2<<2 + KB1<<1 + KB0);效果也应该是一样呀,只不过繁琐了一些 |
楼上还是没搞明白,举例说明一下 case 2: //第一轮扫描 if (PINA & 0x02) Key_state |= 0x01; //读入PINA.1,记录于Key_state.0 if (PINA & 0x04) Key_state |= 0x02; //读入PINA.2,记录于Key_state.1 DDRA &= ~(1<<0); //A口位0先改为输入 PORTA |= (1<<0); //A口位0上拉有效 PORTA &= (1<<1); //A口位1设定为0 DDRA |= (1<<1); //A口位1改为输出0 break; //以上端口变化顺序不要更改,否则可以出现短路 |
unsigned char KB0,KB1,KB2,KB3,KB4,KB5; //=============== 检测按键 ================= void Key_scan() { unsigned char i; Key_count --; //扫描次序 Key_count &= 3; switch (Key_count) //按次序处理 { case 2: //第一轮扫描 KB0 = PINA&0X02; KB1 = PINA&0X04; PORTA |= (1<<0); DDRA |= (1<<0); PORTA &= ~(1<<1); DDRA &= ~(1<<1); break; case 1: //每二轮扫描 KB2 = PINA&0X04; KB3 = PINA&0X01; PORTA |= (1<<1); DDRA |= (1<<1); PORTA &= ~(1<<2); DDRA &= ~(1<<2); break; case 0: //每三轮扫描 KB4 = PINA&0X01; KB5 = PINA&0X02; PORTA &= ~(1<<0); DDRA &= ~(1<<0); PORTA |= (1<<2); DDRA |= (1<<2); break; default: //每四轮扫描 if (!(PINA&0X01)) KB0 = 0; if (!(PINA&0X02)) KB2 = 0; if (!(PINA&0X04)) KB4 = 0; //======更新显示缓冲区======= Key_state = (KB0<<5 + KB1<<4 + KB2<<3 + KB3<<2 + KB4<<1 + KB5); i = Key_tab[Key_state]; if (i == 0) { Disp_buf[2] = 0x11; //显示三横 Disp_buf[1] = 0x11; Disp_buf[0] = 0x11; } else { Disp_buf[2] = 0x0c; //字符"C" Disp_buf[1] = i / 10; //键码十位 Disp_buf[0] = i % 10; //键码个位 } Key_state = 0; } } void main() { // TMOD = 0x10; //定时器1,16位模式 //TCON = 0xc0; //TR1=1;TF1=1; init_devices(); while(1) //主循环 { //Bus_drive(); //显示总线驱动 PORTB = LED_font[Disp_buf[2]]; PORTC = LED_font[Disp_buf[1]]; PORTD = LED_font[Disp_buf[0]]; Key_scan(); //检测按键 delay_ms(5); //延时5MS } } 怎么按,都是显示C22 |
h333 发表于 2013-7-10 00:21 首先,AVR的IO不是准双向口,在作为输入时,需要开上拉电阻,当切换到输出0时,需要改变PORTA和DDRA,你程序里没有更改DDRA,因而不能动作。 其次,没看到你对KB0~KB5的变量类型声明,51中声明为bit,AVR中的位操作比较麻烦,建议读入时直接更新Key_state,取消KB0~KB5。 至于显示的问题,我打算迟些开个新帖,因为和这主题关系不大。键码已从 i= Key_tab[Key_state] 获得,需要送显示可自行处理。 |
楼主,两点请求: 1、能否给我单总线数码管显示那块的电路图 2、我移植到AVR下,按键值不变,错在哪里呢? //=============== 检测按键 ================= void Key_scan() { unsigned char i; Key_count --; //扫描次序 Key_count &= 3; switch (Key_count) //按次序处理 { case 2: //第一轮扫描 KB0 = PINA&0X02; KB1 = PINA&0X04; PORTA |= (1<<0); PORTA &= ~(1<<1); break; case 1: //每二轮扫描 KB2 = PINA&0X04; KB3 = PINA&0X01; PORTA |= (1<<1); PORTA &= ~(1<<2); break; case 0: //每三轮扫描 KB4 = PINA&0X01; KB5 = PINA&0X02; PINA &= ~(1<<0); PORTA |= (1<<0); break; default: //每四轮扫描 if (!(PINA&0X01)) KB0 = 0; if (!(PINA&0X02)) KB2 = 0; if (!(PINA&0X04)) KB4 = 0; //======更新显示缓冲区======= Key_state = KB0<<5 + KB1<<4 + KB2<<3 + KB3<<2 + KB4<<1 + KB5; i = Key_tab[Key_state]; if (i == 0) { Disp_buf[2] = 0x11; //显示三横 Disp_buf[1] = 0x11; Disp_buf[0] = 0x11; } else { Disp_buf[2] = 0x0c; //字符"C" Disp_buf[1] = i / 10; //键码十位 Disp_buf[0] = i % 10; //键码个位 } Key_state = 0; } } 还有,你那个笔段代码是BCD码吗? |
对于这种方式的按键识别方法,很多朋友担心编程会很复杂,其实仔细分析后也很简单.比如上面例子,其本的思路是依次把三个IO拉低,然后记录另外两个IO的状态,最后三个IO都不下拉,再记录一次,就可得出的结果.对于按下不同的按键,就有不同的结果.如果只扫18键,那么最后一次扫描可以省掉,即扫描三次即可.实际应用时5MS的扫描间隔可以用定时中断来实现,这样就只占用很少的MCU时间. |
作为试验目的,没有接按键,只焊了个键盘框架,用镊子短路相应的节点来当按键。图中接二极管阵列的三根线是3个IO,单独的一根是地线。MCU发送串行数据给HC595驱动数码管作键码显示。 |
下次焊个四面体的4IO玩玩看 ----------------------------------------------------- 二极管数量 6条楞,每楞两个,12个 中间星型,4个 共16个 |