马老师,您的AVR-51学习板3*4键盘设计有小问题
那行线上的三个电阻阻值太大了,发出的低电平根本读不回来。不该用5K的电阻。我搞了很久才明白我的扫描程序为什么扫不到,原来都是电阻惹的祸!希望改进一下。 这快板使用了多年了,我读键盘没有发生过读不到低电平的现象。如果你使用中发现读不到,请检查
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”断路!!)。多功能实验板是为新手设计的,加上电阻防止万一。 哦,可是我完全按您的读键盘程序,不更改电阻就读不到按键。换成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)
{
//µÚÒ»ÐÐ
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--; //
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;
}
}
}
};
} PORTD=0;
PORTD=0;
if(PIND.0==0)
return 3;
else
return 0;
===============================
PORTD = 0!!已经把上拉去掉了!! 马老师,不是说先设置为上拉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编辑过 我将第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; // 最右显示新按下键的键值
}
}
};
} 如果不是程序的问题,那么还有一个可能是否你的系统时钟频率非常高,使得(由于5k电阻的充放电时间)你读I/O时电平没有稳定。解决的办法是,送键扫描语句执行3次。
不过,我在AVR-51板上已经试了11.0592M,原来的程序没有问题,键盘读的非常好。 谢谢马老师!马老师真的是师之模范。我一个小问题纠缠这么久了你都不烦。呵呵!我键扫描语句执行了四次,就可以了。最后基本确定了原因:
我的板子上同时用了led数码管,蜂鸣器,8*8点阵led,晶体用了11.0592M,而我的电源只能输出400mA,可能是驱动力不够了。换了1000mA电源也可以解决问题。看来我的硬件水平太低了。需要好好提高。 学习了
页:
[1]