铜河 发表于 2013-2-28 20:01:18

PS2接口键盘的ICC解码程序

/********************************
*                PS2接口键盘实验                        *
* 文件名称:main.c                                *
* 实验环境:阿贵的M16实验板                *
* 建立日期:2008年06月22日                *
* 版    本:V16.0.1                                *
* 作    者:tonghe                                *
* 修改日期:2008年06月22日                *
* 功    能:实现PS2键盘                        *
*                        (支持第2套扫描码)        *
* 芯    片:ATMEGA16                        *
* 时钟频率:7.3728MHZ                        *
* 编 译 器:ICCAVR6.31A                        *
********************************/
#include <iom16v.h>
#include <macros.h>
#include "delay.h"
#include "lcd1602_8.h"

#pragma data:code                                   //将译码表放在FLASH
const unsigned char unshifted[]=                        //shift键没按下译码表
        {               
        0x0e,'`',        0x15,'q',        0x16,'1',        0x1a,'z',
        0x1b,'s',        0x1c,'a',        0x1d,'w',        0x1e,'2',
        0x21,'c',        0x22,'x',        0x23,'d',        0x24,'e',
        0x25,'4',        0x26,'3',        0x29,' ',        0x2a,'v',
        0x2b,'f',        0x2c,'t',        0x2d,'r',        0x2e,'5',
        0x31,'n',        0x32,'b',        0x33,'h',        0x34,'g',
        0x35,'y',        0x36,'6',        0x39,',',        0x3a,'m',
        0x3b,'j',        0x3c,'u',        0x3d,'7',        0x3e,'8',
        0x41,',',        0x42,'k',        0x43,'i',        0x44,'o',
        0x45,'0',        0x46,'9',        0x49,'.',        0x4a,'/',
        0x4b,'l',        0x4c,';',        0x4d,'p',        0x4e,'-',
        0x52,'\'',        0x54,'[',        0x55,'=',        0x5b,']',
        0x5d,'\\',        0x61,'<',        0x69,'1',        0x6b,'4',
        0x6c,'7',        0x70,'0',        0x71,'.',        0x72,'2',
        0x73,'5',        0x74,'6',        0x75,'8',        0x79,'+',
        0x7a,'3',        0x7b,'-',        0x7c,'*',        0x7d,'9',
        0,0
        };

const unsigned char shifted[]=      //shift键按下译码表
        {
        0x0e,'~',  0x15,'Q',  0x16,'!',  0x1a,'Z',
        0x1b,'S',  0x1c,'A',  0x1d,'W',  0x1e,'@',
        0x21,'C',  0x22,'X',  0x23,'D',  0x24,'E',
        0x25,'$',  0x26,'#',  0x29,' ',  0x2a,'V',
        0x2b,'F',  0x2c,'T',  0x2d,'R',  0x2e,'%',
        0x31,'N',  0x32,'B',  0x33,'H',  0x34,'G',
        0x35,'Y',  0x36,'^',  0x39,'L',  0x3a,'M',
        0x3b,'J',  0x3c,'U',  0x3d,'&',  0x3e,'*',
        0x41,'<',  0x42,'K',  0x43,'I',  0x44,'O',
        0x45,')',  0x46,'(',  0x49,'>',  0x4a,'?',
        0x4b,'L',  0x4c,':',  0x4d,'P',  0x4e,'_',
        0x52,'"',  0x54,'{',  0x55,'+',  0x5b,'}',
        0x5d,'|',  0x61,'>',  0x69,'1',  0x6b,'4',
        0x6c,'7',  0x70,'0',  0x71,'.',  0x72,'2',
        0x73,'5',  0x74,'6',  0x75,'8',  0x79,'+',
        0x7a,'3',  0x7b,'-',  0x7c,'*',  0x7d,'9',
        0,0
        };
#pragma data:data                        //将以后的变量放在RAM

unsigned char edge, bitcount,ascii=' ';        //edge为0下降沿中断,为1上升沿中断
                                                                           //bitcount为位计数值
                                                                        //ascii为翻译后的ASCII码,初值为空格
                                                               
void port_init(void)
        {
        PORTA = 0x00;
        DDRA  = 0x00;
        PORTB = 0x00;
        DDRB  = 0xFF;
        PORTC = 0x00;
        DDRC  = 0xFF;
        PORTD = 0x44;
        DDRD  = 0x00;
        }

/************************************
*                初始化PS2接口函数                        *
************************************/
void Init_kb(void)
        {           
        MCUCR = 2;                                //设置INT0为下降沿触发中断
        edge = 0;                                //0为下降沿中断标志,1为上升沿中断标志
        bitcount = 11;                        //每次11位数据,一个起始位(0),8个数据位,
                                                        //一个奇偶校验位,一个停止位(1)

        GICR  = 0x40;
        SEI();                                        //打开全局中断
        }

/************************************
*                        扫描码翻译函数                        *
* 入口参数: 需要翻译的扫描码                *
************************************/
void Decode(unsigned char scancode)
        {
        static unsigned char up=0,shift=0;//up为通、断码标志,shift为shift键按下标志
        unsigned char i;
        if (!up)                                                //已接收的11位数据是通码(up为0)
                {
                switch (scancode)                        //开始翻译扫描码
                        {
                        case 0xF0:                                //键盘释放标志(随后的一个字节是断码)
                                up=1;                                //设置up为断码标志
                                break;
                        case 0x12:                                //左shift键按下
                                shift=1;                        //设置shift为按下标志
                                break;
                        case 0x59:                                //右shift键按下
                                shift=1;                        //设置shift为按下标志
                                break;   
                        default:        
                                if(!shift)                        //如果shift键没有按下
                                        {                                //查找unshifted表,表中左列是扫描码,右列是对应的ASCII码
                                        for(i=0;unshifted!=scancode&&unshifted;i++);
                                        if(unshifted==scancode)
                                                {
                                                ascii=unshifted;
                                                }
                                        }
                                else                                //如果shift键按下
                                        {                                //查找shifted表
                                        for(i=0;shifted!=scancode&&shifted;i++);
                                        if(shifted==scancode)
                                                {
                                                ascii=shifted;
                                                }
                                        }   
                                break;
                        }
                }
        else                                                        //已接收的11位数据是断码(up为1)
                {
                up = 0;                                                //将断码标志复位
                switch (scancode)                        //检测shift键释放
                        {
                        case 0x12 :                                //左shift键
                                shift = 0;
                                break;
                        case 0x59 :                                //右shift键
                                shift = 0;
                                break;
                        default:
                                break;
                        }
                }
        }

/************************************
*                        外部中断0函数                        *
* 功    能: 数据时钟输入端,上升沿        *
*                        中断读入11位数据,下降        *
*                        沿调用解码程序                        *
************************************/
#pragma interrupt_handler Int0:2
void Int0(void)
        {
        static unsigned char data;                // 声明局部静态变量来保存扫描码
        if (!edge)                                                // 如果是下降沿触发中断
                {
                if(bitcount < 11 && bitcount > 2)//3到10位是数据,起始位,校验位和停止位忽略
                        {
                        data = (data >> 1);                //右移保存数据
                        if(PIND&(1<<PD6))
                                {
                                data|=0x80;                        //存储一个'1'
                                }
                        }
                MCUCR=3;                                        //设置INT0为上升沿触发中断
                edge=1;                                                //设置上升沿中断标志
                }
        else                                                        //如果是上升沿触发中断
                {
                MCUCR=2;                                        //设置INT0为下降沿触发中断
                edge=0;                                                //设置下降沿中断标志
                if(--bitcount==0)                        //如果11位全部接收完毕
                        {
                        Decode(data);                        //将扫描码翻译成ASCII码   
                        bitcount = 11;                        //重新设为11位数据
                        }
                }
        }

/********************************
*                        主函数名称                        *
********************************/
void main(void)
        {
        port_init();                                   //初始化
        delay_ms(100);
        lcd_init();                                 //初始化1602
        delay_ms(100);
        Init_kb();                                          //初始化PS2键盘接口
        locate(1,2);
        lcd("KEY_ASCII:");
       
        while(1)
                {
                locate(1,13);
                lcd_da(ascii);                        //显示翻译后的ASCII码
                }
        }

铜河 发表于 2013-2-28 20:02:27

LCD函数:
/****************************************
*        LCD1602操作函数,8位数据联接关系:        *
*        LCD1602:DB0-DB7        M16:PC0-PC7                        *
*        LCD1602:RS                M16:PB4                                *
*        LCD1602:E                M16:PB2                                *
*        LCD1602:R/W                M16:PB5                                *
*        作    者:tonghe                                        *
*        日          期:2008年08月25日                        *
****************************************/
#include <iom16v.h>
#include <macros.h>
#include "delay.h"

//LCD相关端口定义
#define RS1602 PB4                                        //RS接在PORTB4上
#define CLR_RS PORTB&=~(1<<RS1602)        //RS清0
#define SET_RS PORTB|=1<<RS1602                //RS置1

#define E1602 PB2                                        //E接在PORTB2上
#define CLR_E PORTB&=~(1<<E1602)        //E清0
#define SET_E PORTB|=1<<E1602                //E置1

#define RW1602 PB5                                        //RW接在PORTB5上
#define CLR_RW PORTB&=~(1<<RW1602)        //RS清0
#define SET_RW PORTB|=1<<RW1602                //RS置1

#define DATA_1602        PORTC

//简化宏定义
#define uchar unsigned char
#define uint unsigned int

/*************************************
*         LCD1602操作函数组                         *
*************************************/
//写数据到LCD的函数:入口data,需写入的显示数据
void lcd_da(uchar data)
        {
        SET_RS;                                            //RS置高,写数据
        delay_ms(1);
//        SET_E;
        DATA_1602 = data;                        //数据送端口
        SET_E;
        delay_ms(1);
        CLR_E;                                                //E下降沿写入数据
        delay_ms(1);                                //延时1MS
        }

//写指令到LCD的函数:入口data,需写入的控制指令
void lcd_comm(uchar data)
        {
        CLR_RS;                                                 //RS清0,写指令
        delay_ms(1);                                //延时1MS
//        SET_E;
        DATA_1602 = data;                        //指令送端口
        SET_E;
        delay_ms(1);
        CLR_E;                                                //E下降沿写入指令
        delay_ms(1);                                //延时1MS
        }
   
//LCD1602初始化函数
void lcd_init(void)
        {
        CLR_RW;
        delay_ms(5);
        lcd_comm(0x38);                         //写指令:8位数据、2行显示、5*8点阵
        delay_ms(5);
        lcd_comm(0x06);                         //写指令:自左向右显示
        delay_ms(5);
        lcd_comm(0x0C);                         //写指令:显示开
        delay_ms(5);
        lcd_comm(0x14);                         //写指令:移动
        delay_ms(5);
        lcd_comm(0x80);                         //写指令:第1行地址
        delay_ms(5);               
        lcd_comm(0x01);                         //写指令:CLS
        delay_ms(5);
        }

//清屏指令函数
void cls(void)
    {
        lcd_comm(0x01);
        delay_ms(5);
        }

//定位指令函数
void locate(uchar a,uchar b)
    {
        uchar x;
        if(a==1)
            x=0x80+b-1;
        if(a==2)
            x=0xC0+b-1;
        lcd_comm(x);
        delay_ms(1);
        }
   
/***********************************
*         显示一个字符串函数           *
* 入    口:待显示的字符串                   *
***********************************/
void lcd(char *s)
    {
        while(*s)
            {
                lcd_da(*s);
                s++;
                }
        }

铜河 发表于 2013-2-28 20:03:49

完整的工程项目文件:
页: [1]
查看完整版本: PS2接口键盘的ICC解码程序