yarak_ma 发表于 2007-3-22 13:53:23

马老师,您的AVR-51学习板3*4键盘设计有小问题

那行线上的三个电阻阻值太大了,发出的低电平根本读不回来。不该用5K的电阻。我搞了很久才明白我的扫描程序为什么扫不到,原来都是电阻惹的祸!希望改进一下。

machao 发表于 2007-3-23 00:08:13

这快板使用了多年了,我读键盘没有发生过读不到低电平的现象。



如果你使用中发现读不到,请检查



1。读键盘的I/O口线是否定义成输入状态,且内部上拉有效。AVR的内部上拉电阻>50K。当行线输出为“0”时,按键按下后电平值小于 5/(55)* 5 = 0.45v,应该是低电平,绝对不是高电平,对CMOS器件,高电平为Vcc*0.7 = 5*0.7 = 3.5v。



2。行线输出“0”后,需要等待一个时钟周期后读列线。这样才能读到正确的值。



====================================================================



实际上3个串连电阻可以省掉的。但怕有的人设置错误,将列线设置为输出状态,而又输出“1”,这样有可能造成MCU I/O脚的损坏。(列线的“1”通过按键与行线的“0”断路!!)。多功能实验板是为新手设计的,加上电阻防止万一。

yarak_ma 发表于 2007-3-23 09:01:15

哦,可是我完全按您的读键盘程序,不更改电阻就读不到按键。换成300欧的电阻才行。程序如下,基本是抄您教材上的程序。是什么原因呢?我想了很久都想不明白。烦请老师指点。



/*****************************************************

This program was produced by the

CodeWizardAVR V1.24.8c Professional

Automatic Program Generator

© Copyright 1998-2006 Pavel Haiduc, HP InfoTech s.r.l.

http://www.hpinfotech.com



Project :

Version :

Date    : 2007-3-19

Author: F4CG                           

Company : F4CG                           

Comments:





Chip type         : ATmega16L

Program type      : Application

Clock frequency   : 11.059200 MHz

Memory model      : Small

External SRAM size: 0

Data Stack size   : 256

*****************************************************/



#include <mega16.h>

flash char led_7={0x3f,0x06,0x5b,0x4f,0x66,0x6d,

                      0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x40};

flash char position={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};

char dis_buff;

char key_stime_counter;

char posit;

bit key_stime_ok;

void display(void)

{

    PORTB=0xff;

    PORTA=led_7];

    PORTB=position;

    if(++posit>=8)posit=0;

}



// Timer 0 output compare interrupt service routine

interrupt void timer0_comp_isr(void)

{

// Place your code here

    display();

    if(++key_stime_counter>=10)

    {

      key_stime_counter=0;

      key_stime_ok=1;

    }

}



#define no_key255

#define k1_1    1

#define k1_2    2

#define k1_3    3

//#define k1_4    4

#define k2_1    4

#define k2_2    5

#define k2_3    6

//#define k2_4    4

#define k3_1    7

#define k3_2    8

#define k3_3    9

//#define k1_4    4

#define k4_1    10

#define k4_2    0

#define k4_3    11

//#define k4_4    4

#define key_mask 0b00000111



char key_state,key_value,key_line;

char read_keyboard(void)

{

   

   char key_return=no_key,i;

   switch(key_state)

   {

       case 0:            

         key_line=0b00001000;

         for(i=1;i<4;i++)

         {

               PORTD=~key_line;//0b11110111

               PORTD=~key_line;//

               key_value=key_mask&PIND;

               if(key_value==key_mask)//

                   key_line<<=1;

               else                     //

               {

                   key_state++;

                   break;            

               }

         }

         break;                  

       case 1:                     //

         

         PORTD=~key_line;//0b11110111

         PORTD=~key_line;//

         if(key_value==(key_mask&PIND))//

         {

               

                switch(key_line|key_value)

                {

               //&micro;&Uacute;&Ograve;&raquo;&ETH;&ETH;

                  case 0b00001110:

                         key_return=k1_1;

                         break;

                  case 0b00001101:

                         key_return=k1_2;

                         break;

                  case 0b00001011:

                         key_return=k1_3;

                         break;

               //&micro;&Uacute;&para;&thorn;&ETH;&ETH;      

                  case 0b00010110:

                         key_return=k2_1;

                         break;

                  case 0b00010101:

                         key_return=k2_2;

                         break;

                  case 0b00010011:

                         key_return=k2_3;

                         break;

                   //&micro;&Uacute;&Egrave;&yacute;&ETH;&ETH;      

                  case 0b00100110:

                         key_return=k3_1;

                         break;

                  case 0b00100101:

                         key_return=k3_2;

                         break;

                  case 0b00100011:

                         key_return=k3_3;

                         break;

                  //&micro;&Uacute;&Euml;&Auml;&ETH;&ETH;

                  case 0b01000110:

                         key_return=k4_1;

                         break;

                  case 0b01000101:

                         key_return=k4_2;

                         break;

                  case 0b01000011:

                         key_return=k4_3;

                         break;

               }

               key_state++;

         }

         else

               key_state--;   //

         break;

       case 2:

         PORTD=0b00000111;

         PORTD=0b00000111;

         if((key_mask&PIND)==key_mask)//

               key_state=0;

         break;

   }      

         

   return key_return;

}

//*/

//用这个读也是读不到任何按键。

/*char read_keyboard(void)

{

   PORTD=0;

   PORTD=0;

   if(PIND.0==0)

      return 3;

   else

      return 0;

}

*/

// Declare your global variables here



void main(void)

{

char i,key_temp;

// Declare your local variables here



// Input/Output Ports initialization

// Port A initialization

// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out

// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0

PORTA=0x00;

DDRA=0xFF;



// Port B initialization

// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out

// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0

PORTB=0xff;

DDRB=0xFF;



// Port C initialization

// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=In Func1=In Func0=In

// State7=0 State6=0 State5=0 State4=0 State3=0 State2=P State1=P State0=P

PORTC=0xF7;

DDRC=0xF8;



// Port D initialization

// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=In Func1=In Func0=In

// State7=1 State6=1 State5=1 State4=1 State3=1 State2=P State1=P State0=P

PORTD=0B11111111;

DDRD=0B11111000;//0xF8;



// Timer/Counter 0 initialization

// Clock source: System Clock

// Clock value: 172.800 kHz

// Mode: CTC top=OCR0

// OC0 output: Disconnected

TCCR0=0x0B;

TCNT0=0xAD;

OCR0=0xAD;



// Timer/Counter 1 initialization

// Clock source: System Clock

// Clock value: Timer 1 Stopped

// Mode: Normal top=FFFFh

// OC1A output: Discon.

// OC1B output: Discon.

// Noise Canceler: Off

// Input Capture on Falling Edge

// Timer 1 Overflow Interrupt: Off

// Input Capture Interrupt: Off

// Compare A Match Interrupt: Off

// Compare B Match Interrupt: Off

TCCR1A=0x00;

TCCR1B=0x00;

TCNT1H=0x00;

TCNT1L=0x00;

ICR1H=0x00;

ICR1L=0x00;

OCR1AH=0x00;

OCR1AL=0x00;

OCR1BH=0x00;

OCR1BL=0x00;



// Timer/Counter 2 initialization

// Clock source: System Clock

// Clock value: Timer 2 Stopped

// Mode: Normal top=FFh

// OC2 output: Disconnected

ASSR=0x00;

TCCR2=0x00;

TCNT2=0x00;

OCR2=0x00;



// External Interrupt(s) initialization

// INT0: Off

// INT1: Off

// INT2: Off

MCUCR=0x00;

MCUCSR=0x00;



// Timer(s)/Counter(s) Interrupt(s) initialization

TIMSK=0x02;



// Analog Comparator initialization

// Analog Comparator: Off

// Analog Comparator Input Capture by Timer/Counter 1: Off

ACSR=0x80;

SFIOR=0x00;

for(i=0;i<8;i++)

{

    dis_buff=12;

}



// Global enable interrupts

#asm("sei")



while (1)

      {

      // Place your code here

       if(key_stime_ok)

       {

         key_stime_ok=0;

         key_temp=read_keyboard();

         if(key_temp != 0xff)

         {

                for(i=0;i<8;i++)

                {

                  //dis_buff=dis_buff;

                  dis_buff=key_temp;

                }

         }

       }

      };

}

machao 发表于 2007-3-23 10:19:58

PORTD=0;

   PORTD=0;

   if(PIND.0==0)

      return 3;

   else

      return 0;

===============================

PORTD = 0!!已经把上拉去掉了!!

yarak_ma 发表于 2007-3-24 10:09:51

马老师,不是说先设置为上拉PORTD=0B11111111; 再设置为输入,DDRD=0B11111000; 之后更改PORTD寄存器的值就不会改变上拉了吗?(即设置为输入口后,PORTD寄存器已经控制不到上拉电阻了。)

我的程序改为:

    PORTD&=0B00000111;

   PORTD=0B00000111;

   if(PIND.0==0)

      return 3;

   else

      return 0;





一样的结果。怪了,原理上如您所诉,我也看懂了,5K应该没有任何问题的呀,您的板子是完全正确的。但我的必须把5k电阻换掉才行。我自己慢慢琢磨吧,实在不好意思浪费您宝贵的时间了。谢谢您不厌其烦的指点。
-----此内容被yarak_ma于2007-03-24,10:12:34编辑过

machao 发表于 2007-3-24 12:13:19

我将第9章的例子简易手机键盘的程序又试了一次,肯定可以的,我的板上也是串了3个5.1k电阻,电路图与9章图一样,不同的是3个上拉电阻没用,使用内部上拉。



程序如下:

#include <mega16.h>

flash char led_7={0x3F,0x06,0x5B,0x4F,0x66,0x6D,        // 字型码

0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x40};

                                                                          // 后3位为 "A","b","-"       

flash char position={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};



char        dis_buff;                                // 显示缓冲区,存放要显示的8个字符的段码值

char        key_stime_counter;

char        posit;

bit        key_stime_ok;



void display(void)                                        // 8位LED数码管动态扫描函数

{

                PORTC = 0xff;

                PORTA = led_7];

                PORTC = position;

                if (++posit >=8 ) posit = 0;

}



// Timer 0 比较匹配中断服务,2ms定时

interrupt void timer0_comp_isr(void)

{

                display();                                                // 调用LED扫描显示

                if (++key_stime_counter >=5)

                {

                        key_stime_counter = 0;

                        key_stime_ok = 1;                                // 10ms到

                }       

}

#define No_key         255

#define K1_1        1

#define K1_2        2

#define K1_3        3

#define K2_1        4

#define K2_2        5

#define K2_3        6

#define K3_1        7

#define K3_2        8

#define K3_3        9

#define K4_1        10

#define K4_2        0

#define K4_3        11

#define Key_mask        0b00000111





char read_keyboard()

{       

static char key_state = 0, key_value, key_line;

char key_return = No_key,i;

        switch (key_state)

        {

                case 0:

                        key_line = 0b00001000;

                        for (i=1; i<=4; i++)                                        // 扫描键盘

                        {       

                                PORTD = ~key_line;                                        // 输出行线电平

                                PORTD = ~key_line;                                        // 必须送2次!!!

                             key_value = Key_mask & PIND;                // 读列电平

                                if (key_value == Key_mask)

                                        key_line <<= 1;                                // 没有按键,继续扫描

                                else

                                {

                                        key_state++;                                        // 有按键,停止扫描

                                        break;                                                // 转消抖确认状态

                                }

                        }

                        break;

                case 1:

                        if (key_value == (Key_mask & PIND))                // 再次读列电平,

                        {

                                switch (key_line | key_value)                // 与状态0的相同,确认按键

                                {                                                                // 键盘编码,返回编码值

                                        case 0b00001110:

                                                key_return = K1_1;

                                                break;

                                        case 0b00001101:

                                                key_return = K1_2;

                                                break;

                                        case 0b00001011:

                                                key_return = K1_3;

                                                break;

                                        case 0b00010110:

                                                key_return = K2_1;

                                                break;

                                        case 0b00010101:

                                                key_return = K2_2;

                                                break;

                                        case 0b00010011:

                                                key_return = K2_3;

                                                break;

                                        case 0b00100110:

                                                key_return = K3_1;

                                                break;

                                        case 0b00100101:

                                                key_return = K3_2;

                                                break;

                                        case 0b00100011:

                                                key_return = K3_3;

                                                break;

                                        case 0b01000110:

                                                key_return = K4_1;

                                                break;

                                        case 0b01000101:

                                                key_return = K4_2;

                                                break;

                                        case 0b01000011:

                                                key_return = K4_3;

                                                break;

                                }

                                key_state++;                                // 转入等待按键释放状态

                        }

                        else

                                key_state--;                                // 两次列电平不同返回状态0,(消抖处理)

                        break;                                               

                case 2:                                                        // 等待按键释放状态

                        PORTD = 0b00000111;                        // 行线全部输出低电平

                        PORTD = 0b00000111;                        // 重复送一次

                        if ( (Key_mask & PIND) == Key_mask)

                                key_state=0;                                // 列线全部为高电平返回状态0

                        break;

        }

        return key_return;

}



void main(void)

{

                char i, key_temp;

       

                PORTA = 0x00;                                                        // 显示控制I/O端口初始化

                DDRA = 0xFF;

                PORTC = 0xFF;

                DDRC = 0xFF;

                PORTD = 0xFF;                                                        // 键盘接口初始化

                DDRD = 0xF8;                                                        // PD2、PD1、PD0列线,输入方式,上拉有效

                // T/C0 初始化

                TCCR0=0x0B;                        // 内部时钟,64分频(4M/64=62.5KHz),CTC模式

                TCNT0=0x00;

                OCR0=0x7C;                        // OCR0 = 0x7C(124),(124+1)/62.5=2ms

                TIMSK=0x02;                        // 允许T/C0比较匹配中断



                for (i=0; i<8 ;i++)

                {dis_buff= 12;}                // LED初始显示8个"-"

                #asm("sei")                        // 开放全局中断



                while (1)

                {

                        if (key_stime_ok)                               

                        {

                                key_stime_ok = 0;                                // 10ms到

                                key_temp = read_keyboard();                // 调用键盘接口函数读键盘

                                if (key_temp != No_key)

                                {                                                        // 有按键按下

                                        for (i=0; i<7; i++)

                                        {dis_buff = dis_buff;}        // LED显示左移一位

                                        dis_buff = key_temp;                 // 最右显示新按下键的键值

                                }

                        }

                };

}

machao 发表于 2007-3-24 12:28:08

如果不是程序的问题,那么还有一个可能是否你的系统时钟频率非常高,使得(由于5k电阻的充放电时间)你读I/O时电平没有稳定。解决的办法是,送键扫描语句执行3次。





不过,我在AVR-51板上已经试了11.0592M,原来的程序没有问题,键盘读的非常好。

yarak_ma 发表于 2007-3-24 12:46:51

谢谢马老师!马老师真的是师之模范。我一个小问题纠缠这么久了你都不烦。呵呵!我键扫描语句执行了四次,就可以了。最后基本确定了原因:

    我的板子上同时用了led数码管,蜂鸣器,8*8点阵led,晶体用了11.0592M,而我的电源只能输出400mA,可能是驱动力不够了。换了1000mA电源也可以解决问题。看来我的硬件水平太低了。需要好好提高。

hyw0663 发表于 2011-7-12 11:14:08

学习了
页: [1]
查看完整版本: 马老师,您的AVR-51学习板3*4键盘设计有小问题