搜索
bottom↓
回复: 24

原创:4*4键盘(反转法)

[复制链接]

出0入0汤圆

发表于 2006-5-8 16:33:40 | 显示全部楼层 |阅读模式


点击此处下载armok01116669.rar
头像被屏蔽

出0入0汤圆

发表于 2006-5-8 18:26:27 | 显示全部楼层
//this program  reading the value of the key on the keyboard,is designed by TanXianbo,

//all rights is reserved,if you have any problem ,please send email txbhj@163.com to me.

/*列接PD0~3*/

/*行接PD4~5*/

#include <iom16v.h>

#include <macros.h>

#define K1_1 0

#define K1_2 1

#define K1_3 2

#define K1_4 3

#define K2_1 4  

#define K2_2 5

#define K2_3 6

#define K2_4 7

#define K3_1 8

#define K3_2 9

#define K3_3 10

#define K3_4 11

#define K4_1 12

#define K4_2 13

#define K4_3 14

#define K4_4 15

// 1 2  3 +

// 4 5  6 -

// 7 8  9 *

// 0 ac = /

unsigned char dis_flag=0,disled[]={0x3f,0x06,0x5b,0x4f,

                                                                      0x66,0x6d,0x7d,0x07,

                                                                   0x7f,0x6f,0X77,0X7C,

                                                                   0X39,0X5E,0X79,0X71};

unsigned int array[]={0,0,0,0};

int key_value;



void delay10ms()

{

unsigned int i;

for(i=0;i<19940/2;i++)

asm("nop");

}



void timer0_init(void)

{

TCCR0 = 0x00; //stop

TCNT0 = 0x64; //set count

OCR0  = 0x9C;  //set compare

TCCR0 = 0x03; //start timer

}



#pragma interrupt_handler timer0_ovf_isr:10

void timer0_ovf_isr(void)

{

TCNT0 = 0x64; //reload counter value

display();

}



unsigned char read_keyboard(void)

{

unsigned int  key_value;

unsigned char key_return,key,keyl=0,keyh=0;

keyh=PIND;

PORTD=~PORTD;

DDRD=~DDRD;

asm("nop");

keyl=PIND;

key=keyl|keyh;

switch(key)

                   {

                           case 0b11101110:key_return=K1_1;

                                                         break;

                        case 0b11101101:key_return=K1_2;

                                                         break;

                        case 0b11101011:key_return=K1_3;

                                                         break;

                        case 0b11100111:key_return=K1_4;

                                                        break;

                        case 0b11011110:key_return=K2_1;

                                                         break;

                        case 0b11011101:key_return=K2_2;

                                                         break;

                        case 0b11011011:key_return=K2_3;

                                                         break;

                        case 0b11010111:key_return=K2_4;

                                                         break;

                        case 0b10111110:key_return=K3_1;

                                                               break;

                        case 0b10111101:key_return=K3_2;

                                                       break;

                        case 0b10111011:key_return=K3_3;

                                                         break;

                        case 0b10110111:key_return=K3_4;

                                                         break;

                        case 0b01111110:key_return=K4_1;

                                                         break;

                        case 0b01111101:key_return=K4_2;

                                                         break;

                        case 0b01111011:key_return=K4_3;

                                                         break;

                        case 0b01110111:key_return=K4_4;

                                                         break;

                        default:

                                        break;

                        }

PORTD=0b11110000;

DDRD= 0b00001111;

asm("nop");

while(PIND!=0B11110000);

return key_return;                                             

}



//call this routine to initialize all peripherals

void init_devices(void)

{

//stop errant interrupts until set up

CLI(); //disable all interrupts

timer0_init();



MCUCR = 0x00;

GICR  = 0x00;

TIMSK = 0x01; //timer interrupt sources

SEI(); //re-enable interrupts

//all peripherals are now initialized

}





void display(void)

{PORTA|=0XFF;

PORTB=disled[key_value];

PORTA&=~BIT(0);

  }







void main(void)

{

PORTB=0X00;

DDRB=0XFF;

PORTA=0Xff;

DDRA=0X0f;

init_devices();

while(1)

                {display();

                 PORTD=0b11110000;

                 DDRD= 0b00001111;

                 asm("nop");

                 while(PIND!=0B11110000)

                         {

                         delay10ms();

                         if(PIND!=0B11110000)                                       

                               key_value=read_keyboard();

                                }

               

        }

}

出0入0汤圆

发表于 2006-5-8 21:16:50 | 显示全部楼层
奉献精神值得嘉奖,但是这是一个合格的程序吗?

    我看了一眼就不愿意再看这个程序,一个没有合理注释的程序看起来太眼花。



    提的一个小建议,可能不太中听。请原谅。

出0入0汤圆

 楼主| 发表于 2006-5-8 22:22:09 | 显示全部楼层
收到楼上的建议,谢谢你对我提的建议!以后我会注意这点的,我把程序加了注释,和编程的思路一起打上了,不足之处敬请指之。

编程思想:第一步,将PD4~PD7编程为行输入线,PD0~PD3编程为列输出线,并使PD7~PD0的状态为11110000,若有按键按下,则对应的行线为0,此时读取D口的状态,假设是第1行的第列的键被按下,则读取D口的值为11100000,即确定了行的位置,然后转入第二步。

第二步,将第一步中的方向反转过来,即将PD4~PD7编程为行输出线,PD0~PD3编程为列输入线,并使PD7~PD0的状态为00001111,接着再读取D口的状态,读取值为00001110,将两次读取的值相或得11101110,即得到了第1行第1列的键的键值。这种方法的优点是不需要多次扫描,只要经过两个步骤即可获得键值。

#include <iom16v.h>

#include <macros.h>

#define K1_1 0

#define K1_2 1

#define K1_3 2

#define K1_4 3

#define K2_1 4   

#define K2_2 5

#define K2_3 6

#define K2_4 7

#define K3_1 8

#define K3_2 9

#define K3_3 10

#define K3_4 11

#define K4_1 12

#define K4_2 13

#define K4_3 14

#define K4_4 15

unsigned char disled[]={0x3f,0x06,0x5b,0x4f,

                        0x66,0x6d,0x7d,0x07,

                        0x7f,0x6f,0X77,0X7C,

                        0X39,0X5E,0X79,0X71};

int key_value;



void delay10ms() //10MS延时,用于按键消抖

{

unsigned int i;

for(i=0;i<19940/2;i++)

asm("nop");

}

  

void timer0_init(void) //定时0初始化

{

TCCR0 = 0x00; //stop

TCNT0 = 0x64; //set count

OCR0  = 0x9C;  //set compare

TCCR0 = 0x03; //start timer clock/64

}



#pragma interrupt_handler timer0_ovf_isr:10

void timer0_ovf_isr(void)     //定时调用显示函数

{

TCNT0 = 0x64; //reload counter value

display();

}



unsigned char read_keyboard(void) //读键值函数

{



unsigned char key_return,key,keyl=0,keyh=0;

keyh=PIND;  //读取行值

PORTD=~PORTD; //将PORTD的状态取反

DDRD=~DDRD;  //将行列的输入输出模式调换

asm("nop");  //此语句一定要,datasheet上有说明

keyl=PIND;   //读取列值

key=keyl|keyh; //读取该键的键值 

switch(key)

         {

            case 0b11101110:key_return=K1_1;

                      break;

         case 0b11101101:key_return=K1_2;

                      break;

         case 0b11101011:key_return=K1_3;

                      break;

         case 0b11100111:key_return=K1_4;

                     break;

         case 0b11011110:key_return=K2_1;

                      break;

         case 0b11011101:key_return=K2_2;

                      break;

         case 0b11011011:key_return=K2_3;

                      break;

         case 0b11010111:key_return=K2_4;

                      break;

         case 0b10111110:key_return=K3_1;

                            break;

         case 0b10111101:key_return=K3_2;

                         break;

         case 0b10111011:key_return=K3_3;

                      break;

         case 0b10110111:key_return=K3_4;

                      break;

         case 0b01111110:key_return=K4_1;

                      break;

         case 0b01111101:key_return=K4_2;

                      break;

         case 0b01111011:key_return=K4_3;

                      break;

         case 0b01110111:key_return=K4_4;

                      break;

         default:

               break;

         }

PORTD=0b11110000; //将PORTD,DDRD的状态设置为初始状态

DDRD= 0b00001111;

asm("nop");

while(PIND!=0B11110000); //等待按键释放;

return key_return;      //返回键值               

}



//call this routine to initialize all peripherals

void init_devices(void)

{

//stop errant interrupts until set up

CLI(); //disable all interrupts

timer0_init();



MCUCR = 0x00;

GICR  = 0x00;

TIMSK = 0x01; //timer interrupt sources

SEI(); //re-enable interrupts

//all peripherals are now initialized

}





void display(void) //显示函数

{PORTA|=0XFF;

PORTB=disled[key_value];

PORTA&=~BIT(0);

  }

  





void main(void)

{

PORTB=0X00; //I/O口初始化

DDRB=0XFF;

PORTA=0Xff;

DDRA=0X0f;

init_devices();

while(1)

       {

        PORTD=0b11110000;

        DDRD= 0b00001111;

       asm("nop");

       while(PIND!=0B11110000)

          {

          delay10ms();       //延时消抖

          if(PIND!=0B11110000)               

                key_value=read_keyboard();

            }

      

   }

}

-----此内容被txbhj于2006-05-08,22:23:31编辑过



-----此内容被txbhj于2006-05-08,22:25:12编辑过


-----此内容被txbhj于2006-05-08,22:26:48编辑过

出0入0汤圆

发表于 2006-5-9 08:39:19 | 显示全部楼层
这方法比较适合于8条线接到同个端口的情况。如果是仅做键盘扫描的话还说的过去。在主函数中调用delay延时消抖,太浪费时间了。

出0入0汤圆

发表于 2006-5-11 22:11:58 | 显示全部楼层
为什么这一段编译有问题

keyh=PIND;  //读取行值

PORTD=~PORTD; //将PORTD的状态取反

DDRD=~DDRD;  //将行列的输入输出模式调换

asm("nop");  //此语句一定要,datasheet上有说明

keyl=PIND;   //读取列值

key=keyl|keyh; //读取该键的键值 

出0入0汤圆

发表于 2006-5-12 00:55:29 | 显示全部楼层
这种键盘,一次只能按一个键,很不爽



看看我这个已经应用在遥控上的键盘函数吧,每一个键都独立使用不互相干扰,缺点是得用4*4个二极管



#define Scan_P0 DDRB=0x01

#define Scan_P1 DDRB=0x02

#define Scan_P2 DDRB=0x04

#define Scan_P3 DDRB=0x08

#define Scan_Over DDRB=0x00



Uint ScanKey(void)

{Uint keydata=0;

Scan_P3;

nop();nop();nop();nop();nop();nop();nop();nop();

nop();nop();nop();nop();nop();nop();nop();nop();

keydata|=((PINB>>4)&0x0e);

keydata<<=3;

Scan_P2;

nop();nop();nop();nop();nop();nop();nop();nop();

nop();nop();nop();nop();nop();nop();nop();nop();

keydata|=(PINB>>4);

keydata<<=4;

Scan_P1;

nop();nop();nop();nop();nop();nop();nop();nop();

nop();nop();nop();nop();nop();nop();nop();nop();

keydata|=(PINB>>4);

keydata<<=3;

Scan_P0;

nop();nop();nop();nop();nop();nop();nop();nop();

nop();nop();nop();nop();nop();nop();nop();nop();

keydata|=((PINB>>4)&0x02);

keydata|=((PINB>>6)&0x01);

keydata|=((PINB>>2)&0x04);

keydata|=(((Uint)(PINB&0x80))<<7);//按键位置重新调整

Scan_Over;

keydata|=0x8000;//只有15个开关

return(~keydata);

}



键盘采用排线连接,由于MCU工作速度太快,需要用上拉电阻(芯片内部不够用)和NOP减小串扰。

出0入0汤圆

发表于 2006-5-12 09:00:11 | 显示全部楼层
请问楼上的二极管怎么接啊?

出0入0汤圆

发表于 2006-5-12 10:08:06 | 显示全部楼层
这不能叫原创吧,至少在《AVR单片机C语言开发入门指导》里面有个个例子和这个原理是一样的,还是用中断触发的,比这个更进了一步。

出0入0汤圆

发表于 2006-5-12 10:32:31 | 显示全部楼层
二极管用4148,与每个开关串联,接通时通过二极管和开关把带有上拉的数据线拉低。

在4VF改装系统里有图纸。

出0入0汤圆

发表于 2006-5-12 15:58:29 | 显示全部楼层
LOW--<|--_---HIGH



谢谢 !

出0入0汤圆

发表于 2006-5-12 17:26:21 | 显示全部楼层
强烈支持

出0入0汤圆

发表于 2006-5-12 18:09:43 | 显示全部楼层
反转法可以同时查到2个键,但是同时按3个键就有可能丢掉1个键。

出0入0汤圆

发表于 2006-5-12 20:11:33 | 显示全部楼层
楼上的请把您的程序展示一下,好吗?

出0入0汤圆

发表于 2006-5-12 20:38:22 | 显示全部楼层

出0入0汤圆

发表于 2006-5-12 20:43:17 | 显示全部楼层
1、2、5 号  共3个键同时按下。

分析:

  当PD0~3为输出口输出0000,PD4~7为输入口内部上拉。PD4~5读到00

  当PD4~7为输出口输出0000,PD0~3为输入口内部上拉。PD0~1读到00

2、5 号  共2个键同时按下。

分析:

  当PD0~3为输出口输出0000,PD4~7为输入口内部上拉。PD4~5读到00

  当PD4~7为输出口输出0000,PD0~3为输入口内部上拉。PD0~1读到00



结果一样,就会将1号键丢掉。

出0入0汤圆

发表于 2007-3-11 21:32:26 | 显示全部楼层
回15楼,会丢掉一个建,但是无法区分同时按下的1,6还是2,5,还要再加一次扫描才能判断。

出0入42汤圆

发表于 2007-3-11 22:02:37 | 显示全部楼层
我是建立一个码表,读到键电平之后查表,这样更快些

#include <avr/io.h>

#include <util/delay.h>

#include <code.h>



#define colin   ((PINB&0Xf0)>>4)        //行输入引脚

#define colout  4

#define rowin    (PINB&0x0f)        //列输入引脚

#define rowout   0

#define KEYPORT  PORTB                        //键盘口

#define PORTDD   DDRB                        //键盘口的数据方向

#define set_half_byte(srf,bit)  (srf|=(0x0f<<bit))

#define clr_half_byte(srf,bit)  (srf&=~(0x0f<<bit))



#define uchar unsigned char

#define uint  unsigned int

#define bool  unsigned char

#define true  1

#define false 0



void key_init(void)

{

        PORTDD=0x0f;                        //行为输入,列为输出

        set_half_byte(KEYPORT,colout);        //行输入上拉

        clr_half_byte(KEYPORT,rowout);        //列输出低电平

}



void key_not(void)

{

        PORTDD=0xf0;                        // 行为输出,列为输入

        set_half_byte(KEYPORT,rowout);        //列输入上拉

        clr_half_byte(KEYPORT,colout);        //行输出低电平

}



void uart_init(void)                //UART初始化

{

   UBRRH=0;

   UBRRL=25;

   UCSRB=(1<<RXEN);

}



void putchar(uchar c)        //UART发送一个8位的数据

{

   while (!(UCSRA&(1<<UDRE)));

    UDR=c;

}



bool keytest(void)

{

  if (colin==0x0f)

  {

     return true;

  }

  else

  {

     return false;

  }

}



uchar keycode(uchar data)

{  

  uchar a=0;

   do

  {

    data=(data>>1);

        a++;

  }

  while (data!=0x00);

  a=a-1;

  return a;

}



uchar keyscan(void)

{

  uchar rdata=0x00;

  uchar cdata=0x00;

  uchar i=0x00;

  uchar j=0x00;

  uchar temp=0x00;

  cdata=((~colin)&0x0f);                //读入行电平

  key_not();                                        //行列电平取反

  _delay_ms(10);

  rdata=((~rowin)&0x0f);                //读入列电平

  i=keycode(cdata);

  j=keycode(rdata);

  key_init();

  temp=num[j];

  return temp;

}



int main(void)

{

  key_init();

  uart_init();

  while(1)

    {

         while(keytest());

         _delay_ms(100);

     putchar(keyscan());

         while(!keytest());

         _delay_ms(1000);

         _delay_ms(1000);

        }

}

---------------------------------code.h------------------------

const unsigned char num[4][4]=

{

  {'0','1','2','3'},

  {'4','5','6','7'},

  {'8','9','a','b'},

  {'c','d','e','f'}

};

出0入42汤圆

发表于 2007-3-11 22:05:01 | 显示全部楼层
我觉得在按键两端接一个104的瓷片电容就可以不用软件消抖了。个人意见而已

出0入0汤圆

发表于 2007-3-11 22:29:22 | 显示全部楼层
先顶了

出0入0汤圆

发表于 2007-5-7 21:39:00 | 显示全部楼层
请问6楼:

我用的键盘是IC卡公用电话的那种键盘,我的IO口都接了上拉,但是像你说的靠那几个二极管来拉低数据口无法实现啊,那键盘就一排线连到我的单片机上啊,这样子我如何得到各个按键独立工作的效果呢?谢谢赐教!

出0入0汤圆

发表于 2007-5-8 10:34:21 | 显示全部楼层
asm("nop")应该是起一个延时的作用,因为I/O口作为普通I/O口使用时,其状态方向由输出变为输入时,需要至少等待一个时钟周期才能正确地读到PINX的状态。

出0入0汤圆

发表于 2007-5-8 14:39:07 | 显示全部楼层
楼主,这个程序有问题吧!!!!!!

出0入0汤圆

发表于 2011-8-11 09:16:30 | 显示全部楼层
mark

出0入0汤圆

发表于 2011-8-11 09:36:01 | 显示全部楼层
mark
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-8 02:12

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表