7、FIFO与鼠标控制

获取按键编码
修改int.c中的inthandle21

#define PORT_KEYDAT 0x0060   //从编号为0x0060设备输入的8位信息是按键编码

/* 来自 PS/2 键盘的中断 */
 void inthandler21(int *esp)   //将接收到的按键编码显示在画面上
{
    struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
    unsigned char data, s[4];

    //将“0x60+IRQ号码”输出给OCW2,用来通知PIC已经发生了“IRQ号码”中断,然后PIC继续监视“IRQ号码”中断是否发生
    io_out8(PIC0_OCW2, 0x61);   
    data = io_in8(PORT_KEYDAT);

    sprintf(s, "%02X", data);
    boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
    putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);

    return;
 }

按下键后可以显示按键编码
这里写图片描述

加快中断
先将按键编码接收下来,保存在变量里,然后由HariMain偶尔去查看变量
int.c节选

struct KEYBUF 
{
    unsigned char data,     //存储按键编码
    flag;           //flag用于表示缓冲区是否为空,0为空
};


#define PORT_KEYDAT 0x0060  //从编号为0x0060设备输入的8位信息是按键编码

struct KEYBUF keybuf;

void inthandler21(int *esp)
{
    unsigned char data;
    io_out8(PIC0_OCW2, 0x61);   /* 通知PIC IRQ1已结受理完毕 */
    data = io_in8(PORT_KEYDAT);
    if (keybuf.flag == 0) 
    {
        keybuf.data = data;
        keybuf.flag = 1;
    }
    return;
}

bootpack.c中的HariMain节选

for (;;) 
    {
        io_cli();   //屏蔽中断
        if (keybuf.flag == 0)  //没有键按下
        {
            io_stihlt();   //恢复中断,CPU休眠
        } 
        else 
        {
            i = keybuf.data;  //保存按键编码
            keybuf.flag = 0;  //将键码清空
            io_sti();     //开中断,因为键码已经保存完毕,所以再开放中断也没关系
            sprintf(s, "%02X", i);    
            boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
            putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);  //显示键码
        }
    }

缺陷:
当按下右键Ctrl时,一次产生两个字节键码值,因为键盘内部电路一次只能发送一个字节,所以一次按键会产生2次中断,本程序第二次产生的键码值被丢弃了。

制作FIFO缓冲区
问题出现在之前所创建的缓冲区只能存储一个字节,现在做一个能存储多个字节的缓冲区。
int.c节选

struct KEYBUF 
{
    unsigned char data[32],     //存储按键编码
    next;           //next的起点为0,接着是1,依次类推,一共32个存储位置
};

void inthandler21(int *esp)
{
    unsigned char data;
    io_out8(PIC0_OCW2, 0x61);   /* 通知PIC IRQ1已结受理完毕 */
    data = io_in8(PORT_KEYDAT);
    if (keybuf.next < 32) 
    {
        keybuf.data[keybuf.next] = data;
        keybuf.next ++;
    }
    return;
}

bootpack.c节选

for (;;) 
    {
        io_cli();   //屏蔽中断
        if (keybuf.next == 0)  //没有键按下
        {
            io_stihlt();   //恢复中断,CPU休眠
        } 
        else 
        {
            i = keybuf.data[0];  //保存按键编码
            keybuf.next--;  //将键码清空
            for(j=0; j<keybuf.next; j++)
                keybuf.data[j] = keybuf.data[j+1];
            io_sti();     //开中断,因为键码已经保存完毕,所以再开放中断也没关系
            sprintf(s, "%02X", i);    
            boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
            putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);  //显示键码
        }
    }

改善FIFO缓冲区
去掉数据移送操作
int.c节选

struct KEYBUF 
{
    unsigned char data[32];     //存储按键编码
    int next_r, next_w, len;    //len指缓冲区能记录多少个字节的数据
};


void inthandler21(int *esp)
{
    unsigned char data;
    io_out8(PIC0_OCW2, 0x61);   /* 通知PIC IRQ1已结受理完毕 */
    data = io_in8(PORT_KEYDAT);
    if (keybuf.len < 32) 
    {
        keybuf.data[keybuf.next_w] = data; 
        keybuf.len++;
        keybuf.next_w++;
        if (keybuf.next_w == 32)  //如果到了32后,再次从头开始写入
            keybuf.next_w = 0;
    }
    return;
}

bootpack.c节选

for (;;) 
    {
        io_cli();   //屏蔽中断
        if (keybuf.len == 0)  //没有键按下
        {
            io_stihlt();   //恢复中断,CPU休眠
        } 
        else 
        {
            i = keybuf.data[keybuf.next_r];  
            keybuf.len--;
            keybuf.next_r++;
            if (keybuf.next_r == 32) {   //如果到达32后,再次从头开始读
                keybuf.next_r = 0;
            }
            io_sti();     //开中断,因为键码已经保存完毕,所以再开放中断也没关系
            sprintf(s, "%02X", i);    
            boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
            putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);  //显示键码
        }
    }

整理FIFO缓冲区
将结构体改为:

struct FIFO8 
{
    unsigned char *buf;  //大小可变的缓冲区,buf为缓冲区的地址
    int next_w, next_r, size, free;   //size保存缓冲区的总字节数,free保存没有数据的字节
    int flags;          //flags记录缓冲区是否溢出,溢出为1
};

fifo.c

#define FLAGS_OVERRUN       0x0001

/* 初始化FIFO缓冲区 */
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
{
    fifo->size = size;
    fifo->buf = buf;
    fifo->free = size; /* 缓冲区的大小 */
    fifo->flags = 0;
    fifo->next_w = 0; /* 下一个数据写入位置 */
    fifo->next_r = 0; /* 下一个数据读出位置 */
    return;
}

/* 向FIFO缓冲区传送数据并保存 */
int fifo8_put(struct FIFO8 *fifo, unsigned char data)
{
    if (fifo->free == 0)  /* 空余没有了,溢出 */
    {
        fifo->flags |= FLAGS_OVERRUN;
        return -1;
    }
    fifo->buf[fifo->next_w] = data;
    fifo->next_w++;
    if (fifo->next_w == fifo->size) {
        fifo->next_w = 0;
    }
    fifo->free--;
    return 0;
}

/* 从FIFO取得一个数据 */
int fifo8_get(struct FIFO8 *fifo)
{
    int data;
    if (fifo->free == fifo->size)  //如果缓冲区为空,则返回-1
    {
        return -1;
    }
    data = fifo->buf[fifo->next_r];
    fifo->next_r++;
    if (fifo->next_r == fifo->size) {
        fifo->next_r = 0;
    }
    fifo->free++;
    return data;
}

/* 报告一下到底积攒了多少数据 */
int fifo8_status(struct FIFO8 *fifo)
{
    return fifo->size - fifo->free;
}

init.c节选

#define PORT_KEYDAT 0x0060  //从编号为0x0060设备输入的8位信息是按键编码

struct FIFO8 keyfifo;

void inthandler21(int *esp)
{
    unsigned char data;
    io_out8(PIC0_OCW2, 0x61);   /* 通知PIC IRQ1已结受理完毕 */
    data = io_in8(PORT_KEYDAT);
    fifo8_put(&keyfifo, data);  //第一个参数为内存地址
    return;
}

bootpack.c节选

char s[40], mcursor[256], keybuf[32];
fifo8_init(&keyfifo, 32, keybuf);
    for (;;) 
    {
        io_cli();
        if (fifo8_status(&keyfifo) == 0)  
        {
            io_stihlt();
        } 
        else 
        {
            i = fifo8_get(&keyfifo);
            io_sti();
            sprintf(s, "%02X", i);
            boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
            putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
        }
    }

激活鼠标控制器
bootpack.c节选

#define PORT_KEYDAT         0x0060
#define PORT_KEYSTA         0x0064
#define PORT_KEYCMD         0x0064
#define KEYSTA_SEND_NOTREADY        0x02
#define KEYCMD_WRITE_MODE       0x60
#define KBC_MODE            0x47  

//让键盘控制电路KBC(keyboard controller)做好准备,等待控制指令的到来
void wait_KBC_sendready(void)
{
    for (;;)  //一直进行查询
        //如果KBC可以接受CPU指令了,CPU从设备码0x0064处所读取的数据的倒数第二位应该是0
        if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) 
            break;

    return;
}

/* 初始化键盘控制电路。一边确认可否往键盘控制电路传送信息,一边发送模式设定指令 */
void init_keyboard(void)
{
    wait_KBC_sendready();
    io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);  //模式设定指令是0x60
    wait_KBC_sendready();
    io_out8(PORT_KEYDAT, KBC_MODE);   //鼠标模式的模式号码是0x47
    return;
}

#define KEYCMD_SENDTO_MOUSE     0xd4
#define MOUSECMD_ENABLE         0xf4

/* 激活鼠标 */
void enable_mouse(void)
{
    wait_KBC_sendready();
    io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE); //如果往键盘控制电路发送指令0xd4,下一个数据会自动发送给鼠标
    wait_KBC_sendready();
    io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);  
    return; /* 顺利的话,键盘控制器会返送回ACK(0xfa) */
}

然后在主函数中调用init_keyboard();和enable_mouse();
make run后产生了鼠标中断
这里写图片描述

从鼠标接收数据
int.c节选

struct FIFO8 mousefifo;

/* 来自PS/2鼠标的中断  */
void inthandler2c(int *esp)
{
    unsigned char data;
    io_out8(PIC1_OCW2, 0x64);   /* 通知PIC1 IRQ-12的受理已经完成 */
    io_out8(PIC0_OCW2, 0x62);   /* 通知PIC0 IRQ-02的受理已经完成 */
    data = io_in8(PORT_KEYDAT);
    fifo8_put(&mousefifo, data);
    return;
}

bootpack.c节选

char s[40], mcursor[256], keybuf[32], mousebuf[128];

fifo8_init(&keyfifo, 32, keybuf); 
fifo8_init(&mousefifo, 128, mousebuf);

for (;;) 
    {
        io_cli();
        if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) 
            io_stihlt();
        else 
        {
            if (fifo8_status(&keyfifo) != 0) 
            {
                i = fifo8_get(&keyfifo);
                io_sti();
                sprintf(s, "%02X", i);
                boxfill8(binfo->vram, binfo->scrnx, COL8_008484,  0, 16, 15, 31);
                putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
            } 
            else if (fifo8_status(&mousefifo) != 0) 
            {
                i = fifo8_get(&mousefifo);
                io_sti();
                sprintf(s, "%02X", i);
                boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 47, 31);
                putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
            }
        }
    }

这里写图片描述

总结一下从鼠标或者键盘获取数据并显示:
1、初始化鼠标控制电路。鼠标控制电路包含在键盘控制电路中,如果键盘控制电路初始完成,鼠标控制器的激活也就完成了。循环检查键盘电路是否准备好,准备好后发送模式设定指令。
2、发送激活鼠标指令。归根到底还是要想键盘控制器发送指令。这一步的函数与上一步很像,不同点在于写入数据不同,如果往键盘控制电路写入0xd4,下一个数据就会自动发送给鼠标。
3、在中断处理函数中编写中断处理,如要将显示的内容存入缓冲区。
4、在主函数显示中断处理,如将缓冲区中的内容显示处来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、设计按键FIFO的优点   要介绍实现按键FIFO的优点,首先要了解FIFO的一些基本概念。FIFO即First In First Out,是一种先进先出的数据缓存方式,例如在超市购物之后我们会提着满满的购物车来到收银台排在结账队伍的最后等待付款,先排队的客户先付款离开,后面排队的只有等待前面付款离开才能进行付款。说白了FIFO就是这样一种先进先出机制,先存入的数据在读取时最先被读取到。   设计按键FIFO注意有三个方面的优点(来自于安富莱电子Eric2013大佬总结):   1、可以有效记录按键事件的发生,特别是系统要实现记录按键按下、松开、长按时,使用FIFO来实现是一种不错的选择方式。   2、系统是阻塞的,这样系统在检测到按键按下的情况,由于机械按键抖动的原因不需要在这里等待一段时间,然后在确定按键是否正常按下。   3、按键FIFO程序在系统定时器中定时检测按键状态,确认按键按下后将状态写入FIFO中,不一定在主程序中一直做检测,这样可以有效降低系统资源的消耗。 2、按键的硬件设计   按键的原理图如上图所示,对于KEY0~KEY2这三个按键,一端接地,另一端连接stm32的GPIO端口。当按键按下时相应的IO口被拉低,如果把GPIO口配置为输入模式,此时读取相应的IO口电平,就可以检测到按键是否被按下。对于KEY_UP按键则是与前面三个按键相反,IO口配置为输入模式时,读取到高电平时表示按键按下。因为机械固有的物理特性,按键按下内部弹簧片在瞬间接触的时候会有力学的回弹,造成2-8毫秒内信号不稳定,所以在设计检测机械按键是否按下的程序时,应考虑到按键消抖问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值