网站首页 > 教程文章 正文
1)实验平台:正点原子STM32mini开发板
2)摘自《正点原子STM32 不完全手册(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子
第三十章 PS2 鼠标实验
PS/2 作为电脑的标准输入接口,用于鼠标键盘等设备。PS/2 只需要一个简单的接口(2 个
IO 口),就可以外扩鼠标、键盘等,是单片机理想的输入外扩方式。
ALIENTEK MiniSTM32 开发板也自带了一个 PS/2 接口,可以用来驱动标准的鼠标、键盘
等外设,也可以用来驱动一些 PS/2 接口的小键盘,条码扫描枪等。在本章中,我们将向大家介
绍,如何在 ALIENTEK MiniSTM32 开发板上,通过 PS/2 接口来驱动电脑鼠标。本章分为如下
几个部分:
30.1 PS/2 简介
30.2 硬件设计
30.3 软件设计
30.4 下载验证
30.1 PS/2 简介
PS/2 是电脑上常见的接口之一,用于鼠标、键盘等设备。一般情况下,PS/2 接口的鼠标为
绿色,键盘为紫色。
PS/2 接口是输入装置接口,而不是传输接口。所以 PS2 口根本没有传输速率的概念,只有
扫描速率。在 Windows 环境下,ps/2 鼠标的采样率默认为 60 次/秒,USB 鼠标的采样率为 120
次/秒。较高的采样率理论上可以提高鼠标的移动精度。
物理上的 PS/2 端口可有 2 种,一种是 5 脚的,一种是六脚的。下面给出这两种 PS/2 接口
的引脚定义图,如图 30.1.1 所示:
从图 30.1.1 可以看出,不管是 5 脚还是 6 脚的 PS/2 接头,都是有 4 根有用的线连接:时钟
线、数据线、电源线、地线。PS/2 设备的电源是 5V 的,而数据线和时钟线均是集电极开路的,
这两根信号线都需要接一个上拉电阻(开发板上使用的是 10K)。
PS/2 鼠标和键盘遵循一种双向同步串行协议,换句话说每次数据线上发送一位数据并且每
在时钟线上发一个脉冲就被读入。键盘/鼠标可以发送数据到主机,而主机也可以发送数据到设
备,但主机总是在总线上有优先权,它可以在任何时候抑制来自于键盘/鼠标的通讯,只要把时
钟拉低即可。
从设备到主机的数据在时钟信号的下降沿被主机读取,而从主机到设备的数据在时钟信号
的上升沿被设备读取。不论通信方向如何,时钟总是由设备产生的,最大的时钟频率为 33Khz,
大多数设备工作在 10~20Khz。
鼠标键盘,采用的是一种每帧包含 11/12 位的串行协议,这些位的含义如表 30.1.1 所示:
表 30.1.2 中校验位的含义是:如果数据位中包含偶数个 1,则校验位为 1;如果数据位中
包含奇数个 1,则校验位为 0。数据位中的 1 的个数加上校验位总为奇数(奇校验),用于数据
侦错。当主机发送数据给键盘/鼠标的时候,设备会发送一个握手信号来应答数据已经被收到了,
该位不会出现在设备到主机的通信中。
设备到主机的通信过程:
正常情况下数据线和时钟线都是高电平,当键盘/鼠标有数据要发送时,它先检测时钟线,
确认时钟线是高电平。如果不是,则是主机抑制了通信,设备必须缓冲任何要发送的数据,直
到重新获得总线的控制权(键盘有 16 字节的缓冲区而鼠标的缓冲区仅存储最后一个要发送的
数据包)。如果时钟线是高电平,设备就可以开始传送数据了。
设备到主机的数据在时钟线的下降沿被主机读入,如图 30.1.2 所示:
主机可以在设备发送数据的时候拉低时钟线来来放弃当前数据的传送。
主机到设备的通信过程:
主机到设备的通信与设备到主机的通信有点不同,因为 PS/2 的时钟总是由设备产生的,如
果主机要发送数据,则它必须首先把时钟线和数据线设置为请求发送状态。请求发送状态通过
如下过程实现:
1.拉低时钟线至少 100us 以抑制通信。
2.拉低数据线,以应用“请求发送”,然后释放时钟线。
设备在不超过 10ms 的时间内就会检测这个状态,当设备检测到这个状态后,它将开始产生
时钟信号,并且在设备提供的时钟脉冲驱动下输入八个数据位和一个停止位。主机仅当时钟线
为低的时候改变数据线,而数据在时钟脉冲的上升沿被锁存,这与发生在设备到主机通讯的过程中正好相反。主机到设备的通信时序图如图 30.1.3 所示:
以上简单介绍了 PS/2 协议的通信过程,更多的介绍请参考《PS/2 技术参考》一文。本章
我们要驱动一个 PS/2 鼠标,所以接下来简单介绍一下 PS/2 鼠标的相关信息。
标准的 PS/2 鼠标支持下面的输入:X(左右)位移、Y(上下)位移、左键、中键和右键。
但是我们目前用到鼠标大都还有滚轮,有的还有更多的按键,这就是所谓的 Intellimouse。它支
持 5 个鼠标按键和三个位移轴(左右、上下和滚轮)。
标准的鼠标有两个计数器保持位移的跟踪:X 位移计数器和 Y 位移计数器。可存放 9 位的 2 进制补码,并且每个计数器都有相关的溢出标志。它们的内容连同三个鼠标按钮的状态一起以三字节移动数据包的形式发送给主机,位移计数器表示从最后一次位移数据包被送往主机后所发生的位移量。
标准 PS/2 鼠标发送唯一和按键信息以 3 字节的数据包格式发给主机,三个数据包的意义如
图 30.1.4 所示:
位移计数器是一个 9 位 2 的补码整数,其最高位作为符号位出现在位移数据包的第一个字
节里。这些计数器在鼠标读取输入发现有位移时被更新。这些值是自从最后一次发送位移数据
包给主机后位移的累计量(即最后一次包发给主机后位移计数器被复位位移计数器可表示的值
的范围是-255 到+255)。如果超过了范围,相应的溢出位就会被置位,并在复位之前,计数器
不会再增减。
而所谓的 Intellimouse,因为多了 2 个按键和一个滚轮,所以 Intellimouse 的一个位移数据
包由 4 个字节组成,如图 30.1.5 所示:
Z0-Z3 是 2 的补码,用于表示从上次数据报告以来滚轮的位移量。有效范围从-8 到+7,第
四键如果按下,则 4th Btn 位被置位,如果没有按下,则 4th Btn 位为 0。第五键也与此类似。
鼠标的介绍我们就简单的介绍到这里,详细的说明请参考光盘《PS/2 技术参考》第三章
PS/2 鼠标接口(第 36 页)。
30.2 硬件设计
本章实验功能简介:开机的时候先检测是否有鼠标接入,如果没有/检测错误,则提示错误
代码。只有在检测到 PS/2 鼠标之后才开始后续操作,当检测到鼠标之后,就在 LCD 上显示鼠
标位移数据包的内容,并转换为坐标值,在 LCD 上显示,如果有按键按下,则会提示按下的是
哪个按键。同样我们也是用 LED0 来指示程序正在运行。
所要用到的硬件资源如下:
1) 指示灯 DS0
2) TFTLCD 模块
3) PS/2 鼠标
本章需要用到一个PS/2接口的鼠标,大家得自备一个。下面我来看一看开发板上的PS/2
接口与STM32的连接电路,如图30.2.1所示:
可以看到,PS/2 接口与 STM32 的连接仅仅 2 个 IO 口,其中 PS_CLK 连接在 PA15 上面,
而 PS_DAT 则连接在 PC5 上面,这两个口和 KEY1 和 KEY0 复用了,所以在按键使用的时候,
就不能使用 PS/2 设备了,这个在使用的时候大家要注意一下。
30.3 软件设计
打开上一章的工程,由于本章没有用到 SPI 接口、按键以及 NRF24L01 模块,所以,先去
掉 spi.c、key.c 和 24l01.c(此时 HARDWARE 组仅剩下:led.c 和 ILI93xx.c)。
然后,在 HARDWARE 文件夹下新建 PS2 和 MOUSE 两个文件夹。在 PS2 文件夹里面新建
ps2.c 和 ps2.h 两个文件。然后在 MOUSE 文件夹下新建 mouse.c 和 mouse.h 两个文件。并将这
个两个文件夹加入头文件包含路径。
打开 ps2.c,输入如下代码:
#include "ps2.h"
#include "usart.h"
//PS2_Status 当前状态标志
//[7]:接收到一次数据;[6]:校验错误;[5:4]:当前工作的模式;[3:0]:收到的数据长度;
u8 PS2_Status=CMDMODE; //默认为命令模式
u8 PS2_DATA_BUF[16]; //ps2 数据缓存区
//位计数器
u8 BIT_Count=0;
//中断 15~10 处理函数
//每 11 个 bit,为接收 1 个字节
//每接收完一个包(11 位)后,设备至少会等待 50ms 再发送下一个包
//只做了鼠标部分,键盘部分暂时未加入
//CHECK OK 2017/6/13
void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
//调用中断处理公用函数
}
//中断服务程序中需要做的事情
//在 HAL 库中所有的外部中断服务函数都会调用此函数
//GPIO_Pin:中断引脚号
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
static u8 tempdata=0;
static u8 parity=0;
if(GPIO_Pin==GPIO_PIN_15)
{
if(BIT_Count==0)
{
parity=0;
tempdata=0;
}
BIT_Count++;
if(BIT_Count>1&&BIT_Count<10)//这里获得数据
{
tempdata>>=1;
if(PS2_SDA)
{
tempdata|=0x80;
parity++;//记录 1 的个数
}
}else if(BIT_Count==10)//得到校验位
{
if(PS2_SDA)parity|=0x80;//校验位为 1
}
if(BIT_Count==11)//接收到 1 个字节的数据了
{
BIT_Count=parity&0x7f;//取得 1 的个数
if(((BIT_Count%2==0)&&(parity&0x80))||((BIT_Count%2==1)
&&(parity&0x80)==0))//奇偶校验 OK
{
//PS2_Status|=1<<7;//标记得到数据
BIT_Count=PS2_Status&0x0f;
PS2_DATA_BUF[BIT_Count]=tempdata;//保存数据
if(BIT_Count<15)PS2_Status++; //数据长度加 1
BIT_Count=PS2_Status&0x30; //得到模式
switch(BIT_Count)
{
case CMDMODE://命令模式下,每收到一个字节都会产生接收完成
PS2_Dis_Data_Report();//禁止数据传输
PS2_Status|=1<<7; //标记得到数据
break;
case KEYBOARD:
break;
case MOUSE:
if(MOUSE_ID==0)//标准鼠标,3 个字节
{
if((PS2_Status&0x0f)==3)
{
PS2_Status|=1<<7;//标记得到数据
PS2_Dis_Data_Report();//禁止数据传输
}
}else if(MOUSE_ID==3)//扩展鼠标,4 个字节
{
if((PS2_Status&0x0f)==4)
{
PS2_Status|=1<<7;//标记得到数据
PS2_Dis_Data_Report();//禁止数据传输
}
}
break;
}
}else
{
PS2_Status|=1<<6;//标记校验错误
PS2_Status&=0xf0;//清除接收数据计数器
}
BIT_Count=0;
}
}
}
//禁止数据传输
//把时钟线拉低,禁止数据传输
void PS2_Dis_Data_Report(void)
{
PS2_Set_Int(0); //关闭中断
PS2_SET_SCL_OUT();//设置 SCL 为输出
PS2_SCL_OUT=0; //抑制传输
}
//使能数据传输
//释放时钟线
void PS2_En_Data_Report(void)
{
PS2_SET_SCL_IN(); //设置 SCL 为输入
PS2_SET_SDA_IN(); //SDA IN
PS2_SCL_OUT=1; //上拉
PS2_SDA_OUT=1;
PS2_Set_Int(1); //开启中断
}
//PS2 中断屏蔽设置
//en:1,开启;0,关闭;
void PS2_Set_Int(u8 en)
{
EXTI->PR=1<<15; //清除 LINE15 上的中断标志位
if(en)EXTI->IMR|=1<<15;//不屏蔽 line15 上的中断
else EXTI->IMR&=~(1<<15);//屏蔽 line15 上的中断
}
//等待 PS2 时钟线 sta 状态改变
//sta:1,等待变为 1;0,等待变为 0;
//返回值:0,时钟线变成了 sta;1,超时溢出;
u8 Wait_PS2_Scl(u8 sta)
{
u16 t=0;
sta=!sta;
while(PS2_SCL==sta)
{
delay_us(1);
t++;
if(t>16000)return 1;//时间溢出 (设备会在 10ms 内检测这个状态)
}
return 0;//被拉低了
}
//在发送命令/数据之后,等待设备应带,该函数用来获取应答
//返回得到的值
//返回 0,且 PS2_Status.6=1,则产生了错误
u8 PS2_Get_Byte(void)
{
u16 t=0;
u8 temp=0;
while(1)//最大等待 55ms
{
t++;
delay_us(10);
if(PS2_Status&0x80)//得到了一次数据
{
temp=PS2_DATA_BUF[PS2_Status&0x0f-1];
PS2_Status&=0x70;//清除计数器,接收到数据标记
break;
}else if(t>5500||PS2_Status&0x40)break;//超时溢出/接收错误
}
PS2_En_Data_Report();//使能数据传输
return temp;
}
//发送一个命令到 PS2.
//返回值:0,无错误,其他,错误代码
u8 PS2_Send_Cmd(u8 cmd)
{
u8 i;
u8 high=0;//记录 1 的个数
PS2_Set_Int(0); //屏蔽中断
PS2_SET_SCL_OUT();//设置 SCL 为输出
PS2_SET_SDA_OUT();//SDA OUT
PS2_SCL_OUT=0;//拉低时钟线
delay_us(120);//保持至少 100us
PS2_SDA_OUT=0;//拉低数据线
delay_us(10);
PS2_SET_SCL_IN();//释放时钟线,这里 PS2 设备得到第一个位,开始位
PS2_SCL_OUT=1;
if(Wait_PS2_Scl(0)==0)//等待时钟拉低
{
for(i=0;i<8;i++)
{
if(cmd&0x01)
{
PS2_SDA_OUT=1;
high++;
}else PS2_SDA_OUT=0;
cmd>>=1;
//这些地方没有检测错误,因为这些地方不会产生死循环
Wait_PS2_Scl(1);//等待时钟拉高 发送 8 个位
Wait_PS2_Scl(0);//等待时钟拉低
}
if((high%2)==0)PS2_SDA_OUT=1;//发送校验位 10
else PS2_SDA_OUT=0;
Wait_PS2_Scl(1); //等待时钟拉高 10 位
Wait_PS2_Scl(0); //等待时钟拉低
PS2_SDA_OUT=1; //发送停止位 11
Wait_PS2_Scl(1);//等待时钟拉高 11 位
PS2_SET_SDA_IN();//SDA in
Wait_PS2_Scl(0);//等待时钟拉低
if(PS2_SDA==0)Wait_PS2_Scl(1);//等待时钟拉高 12 位
else
{
PS2_En_Data_Report();
return 1;//发送失败
}
}else
{
PS2_En_Data_Report();
return 2;//发送失败
}
PS2_En_Data_Report();
return 0; //发送成功
}
//PS2 初始化
//CHECK OK 2017/6/13
void PS2_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE();
//开启 GPIOA 时钟
__HAL_RCC_GPIOC_CLK_ENABLE();
//开启 GPIOC 时钟
GPIO_Initure.Pin=GPIO_PIN_15;
//PA15
GPIO_Initure.Mode=GPIO_MODE_IT_RISING; //上升沿
GPIO_Initure.Pull=GPIO_PULLUP;
//上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=GPIO_PIN_5;
//PC5
GPIO_Initure.Mode=GPIO_MODE_INPUT;
//输入
HAL_GPIO_Init(GPIOC,&GPIO_Initure);
HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,1); //抢占优先级为 2,子优先级为 1
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); //使能中断线 15
}
该部分为底层的 PS/2 协议驱动程序,采用中断接收 PS/2 设备产生的时钟信号,然后解析。
保存 ps2.c 文件,并加入到 HARDWARE 组下,然后打开 ps2.h,在该文件里面输入如下代
码:
#ifndef __PS2_H
#define __PS2_H
#include "delay.h"
#include "sys.h"
#define PS2_SCL PAin(15)
//PA15
#define PS2_SDA PCin(5)
//PC5
//PS2 输出
#define PS2_SCL_OUT PAout(15)
//PA15
#define PS2_SDA_OUT PCout(5)
//PC5
//设置 PS2_SCL 输入输出状态.
#define PS2_SET_SCL_IN() {GPIOA->CRH&=0X0FFFFFFF;GPIOA->CRH|=0X80000000;}
#define PS2_SET_SCL_OUT() {GPIOA->CRH&=0X0FFFFFFF;GPIOA->CRH|=0X30000000;}
//设置 PS2_SDA 输入输出状态.
#define PS2_SET_SDA_IN() {GPIOC->CRL&=0XFF0FFFFF;GPIOC->CRL|=0X00800000;}
#define PS2_SET_SDA_OUT() {GPIOC->CRL&=0XFF0FFFFF;GPIOC->CRL|=0X00300000;}
#define MOUSE 0X20 //鼠标模式
#define KEYBOARD 0X10 //键盘模式
#define CMDMODE 0X00 //发送命令
//PS2_Status 当前状态标志
//[5:4]:当前工作的模式;[7]:接收到一次数据
//[6]:校验错误;[3:0]:收到的数据长度;
extern u8 PS2_Status;
//定义为命令模式
extern u8 PS2_DATA_BUF[16]; //ps2 数据缓存区
extern u8 MOUSE_ID;
void PS2_Init(void);
u8 PS2_Send_Cmd(u8 cmd);
void PS2_Set_Int(u8 en);
u8 PS2_Get_Byte(void);
void PS2_En_Data_Report(void);
void PS2_Dis_Data_Report(void);
#endif
保存此部分代码,然后打开 mouse.c,输入如下代码:
#include "mouse.h"
#include "usart.h"
#include "lcd.h"
u8 MOUSE_ID;//用来标记鼠标 ID
PS2_Mouse MouseX;
//处理 MOUSE 的数据
void Mouse_Data_Pro(void)
{
MouseX.x_pos+=(signed char)PS2_DATA_BUF[1];
MouseX.y_pos+=(signed char)PS2_DATA_BUF[2];
MouseX.z_pos+=(signed char)PS2_DATA_BUF[3];
MouseX.bt_mask=PS2_DATA_BUF[0]&0X07;//取出掩码
}
//初始化鼠标
//返回:0,初始化成功
//其他:错误代码
//CHECK OK 2010/5/2
u8 Init_Mouse(void)
{
u8 t;
PS2_Init();
delay_ms(800);
//等待上电复位完成
PS2_Status=CMDMODE; //进入命令模式
t=PS2_Send_Cmd(PS_RESET); //复位鼠标
if(t!=0)return 1;
t=PS2_Get_Byte();
if(t!=0XFA)return 2;
t=0;
while((PS2_Status&0x80)==0)//等待复位完毕
{
t++; delay_ms(10);
if(t>50)return 3;
}
PS2_Get_Byte();//得到 0XAA
PS2_Get_Byte();//得到 ID 0X00
//进入滚轮模式的特殊初始化序列
PS2_Send_Cmd(SET_SAMPLE_RATE); //进入设置采样率
if(PS2_Get_Byte()!=0XFA)return 4;
//传输失败
PS2_Send_Cmd(0XC8);
//采样率 200
if(PS2_Get_Byte()!=0XFA)return 5;
//传输失败
PS2_Send_Cmd(SET_SAMPLE_RATE); //进入设置采样率
if(PS2_Get_Byte()!=0XFA)return 6;
//传输失败
PS2_Send_Cmd(0X64);
//采样率 100
if(PS2_Get_Byte()!=0XFA)return 7;
//传输失败
PS2_Send_Cmd(SET_SAMPLE_RATE); //进入设置采样率
if(PS2_Get_Byte()!=0XFA)return 8;
//传输失败
PS2_Send_Cmd(0X50);
//采样率 80
if(PS2_Get_Byte()!=0XFA)return 9;
//传输失败
//序列完成
PS2_Send_Cmd(GET_DEVICE_ID);
//读取 ID
if(PS2_Get_Byte()!=0XFA)return 10;
//传输失败
MOUSE_ID=PS2_Get_Byte();
//得到 MOUSE ID
PS2_Send_Cmd(SET_SAMPLE_RATE); //再次进入设置采样率
if(PS2_Get_Byte()!=0XFA)return 11;
//传输失败
PS2_Send_Cmd(0X0A);
//采样率 10
if(PS2_Get_Byte()!=0XFA)return 12;
//传输失败
PS2_Send_Cmd(GET_DEVICE_ID);
//读取 ID
if(PS2_Get_Byte()!=0XFA)return 13;
//传输失败
MOUSE_ID=PS2_Get_Byte();
//得到 MOUSE ID
PS2_Send_Cmd(SET_RESOLUTION); //设置分辨率
if(PS2_Get_Byte()!=0XFA)return 14;
//传输失败
PS2_Send_Cmd(0X03);
//8 点/mm
if(PS2_Get_Byte()!=0XFA)return 15;
//传输失败
PS2_Send_Cmd(SET_SCALING11);
//设置缩放比率为 1:1
if(PS2_Get_Byte()!=0XFA)return 16;
//传输失败
PS2_Send_Cmd(SET_SAMPLE_RATE); //设置采样率
if(PS2_Get_Byte()!=0XFA)return 17;
//传输失败
PS2_Send_Cmd(0X28);
//40
if(PS2_Get_Byte()!=0XFA)return 18;
//传输失败
PS2_Send_Cmd(EN_DATA_REPORT); //使能数据报告
if(PS2_Get_Byte()!=0XFA)return 19;
//传输失败
PS2_Status=MOUSE;
//进入鼠标模式
return 0;//无错误,初始化成功
}
该部分仅 2 个函数,Init_Mouse 用于初始化鼠标,让鼠标进入 Intellimouse 模式,里面的初
始化序列完全按照《PS/2 技术参考》里面介绍的来设计。另外一个函数就是将收到的数据简单
处理一下。保存 mouse.c,然后打开 mouse.h,输入如下内容:
#ifndef __MOUSE_H
#define __MOUSE_H
#include "ps2.h"
//HOST->DEVICE 的命令集
#define PS_RESET 0XFF //复位命令 回应 0XFA
……//省略部分指令
//#define RESEND
0XFE //再次发送
//鼠标结构体
typedef struct
{
short x_pos; //横坐标
short y_pos; //纵坐标
short z_pos; //滚轮坐标
u8 bt_mask; //按键标识,bit2 中间键;bit1,右键;bit0,左键
} PS2_Mouse;
extern PS2_Mouse MouseX;
extern u8 MOUSE_ID; //鼠标 ID,0X00,表示标准鼠标(3 字节);0X03 表示扩展鼠标(4 字节)
u8 Init_Mouse(void);
void Mouse_Data_Pro(void);
#endif
该部分代码定义了一个鼠标结构体,用于存放鼠标相关的数据,并对鼠标的相关命令进行
了宏定义(部分被省略),保存此部分代码。最后,打开 main.c 文件,修改代码如下:
//显示鼠标的坐标值
//x,y:在 LCD 上显示的坐标位置
//pos:坐标值
void Mouse_Show_Pos(u16 x,u16 y,short pos)
{
if(pos<0)
{
LCD_ShowChar(x,y,'-',16,0);
//显示负号
pos=-pos;
//转为正数
}else LCD_ShowChar(x,y,' ',16,0);
//去掉负号
LCD_ShowNum(x+8,y,pos,5,16);
//显示值
}
int main(void)
{
u8 t; u8 errcnt=0;
HAL_Init();
//初始化 HAL 库
Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M
delay_init(72);
//初始化延时函数
uart_init(115200);
//初始化串口
LED_Init();
//初始化 LED
LCD_Init();
//初始化 LCD
POINT_COLOR=RED;
//设置字体为红色
LCD_ShowString(60,50,200,16,16,"Mini STM32");
LCD_ShowString(60,70,200,16,16,"Mouse TEST");
LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(60,110,200,16,16,"2019/11/15");
while(Init_Mouse())
//检查鼠标是否在位.
{
LCD_ShowString(60,130,200,16,16,"Mouse Error");
delay_ms(400);
LCD_Fill(60,130,239,130+16,WHITE);
delay_ms(100);
}
LCD_ShowString(60,130,200,16,16,"Mouse OK");
LCD_ShowString(60,150,200,16,16,"Mouse ID:");
LCD_ShowNum(132,150,MOUSE_ID,3,16);//填充模式
POINT_COLOR=BLUE;
LCD_ShowString(30,170,200,16,16,"BUF[0]:");
LCD_ShowString(30,186,200,16,16,"BUF[1]:");
LCD_ShowString(30,202,200,16,16,"BUF[2]:");
if(MOUSE_ID==3)LCD_ShowString(30,218,200,16,16,"BUF[3]:");
LCD_ShowString(90+30,170,200,16,16,"X POS:");
LCD_ShowString(90+30,186,200,16,16,"Y POS:");
LCD_ShowString(90+30,202,200,16,16,"Z POS:");
if(MOUSE_ID==3)LCD_ShowString(90+30,218,200,16,16,"BUTTON:");
t=0;
while(1)
{
if(PS2_Status&0x80)//得到了一次数据
{
LCD_ShowNum(56+30,170,PS2_DATA_BUF[0],3,16);//填充模式
LCD_ShowNum(56+30,186,PS2_DATA_BUF[1],3,16);//填充模式
LCD_ShowNum(56+30,202,PS2_DATA_BUF[2],3,16);//填充模式
if(MOUSE_ID==3)LCD_ShowNum(56+30,218,PS2_DATA_BUF[3],3,16);
//填充模式
Mouse_Data_Pro();//处理数据
Mouse_Show_Pos(146+30,170,MouseX.x_pos);
//X 坐标
Mouse_Show_Pos(146+30,186,MouseX.y_pos);
//Y 坐标
if(MOUSE_ID==3)Mouse_Show_Pos(146+30,202,MouseX.z_pos); //滚轮位置
if(MouseX.bt_mask&0x01)LCD_ShowString(146+30,218,200,16,16,"LEFT");
else LCD_ShowString(146+30,218,200,16,16," ");
if(MouseX.bt_mask&0x02)LCD_ShowString(146+30,234,200,16,16,"RIGHT");
else LCD_ShowString(146+30,234,200,16,16," ");
if(MouseX.bt_mask&0x04)LCD_ShowString(146+30,250,200,16,16,"MIDDLE");
else LCD_ShowString(146+30,250,200,16,16," ");
PS2_Status=MOUSE;
PS2_En_Data_Report();//使能数据报告
}else if(PS2_Status&0x40)
{
errcnt++;
PS2_Status=MOUSE;
LCD_ShowNum(86+30,234,errcnt,3,16);//填充模式
}
t++;
delay_ms(1);
if(t==200)
{
t=0;
LED0=!LED0;
}
}
}
此部分,除了 main 函数,我们还编写了 Mouse_Show_Pos 函数,用于在指定位置显示鼠标坐标值,并支持负数显示,通过该函数,可以方便我们显示鼠标坐标数据。至此,PS/2 鼠标实验的软件设计部分就结束了。
30.4 下载验证
在代码编译成功之后,我们通过下载代码到 ALIENTEK MiniSTM32 开发板上,可以看到
LCD 显示如图 30.4.1 所示内容(假定 PS/2 鼠标已经接上,并且初始化成功):
移动鼠标,或者按动按键,就可以看到上面的数据不断变化,证明我们的鼠标已经成功被
驱动了,接下来我们就可以使用鼠标来控制 STM32 了。
猜你喜欢
- 2025-01-04 基于stm32的车辆减速灯项目——MPU6050或ADXL345
- 2025-01-04 基于STM32的自动跟踪小车
- 2025-01-04 Cortex-M3之串口与超级终端间发送与接收(STM32)
- 2025-01-04 485型风速和风向变送器数据包解析
- 2025-01-04 毕业设计|语音识别智能家居制作
- 2025-01-04 《STM32电子DIY》Lora模块SX1278通信
- 2025-01-04 FreeRTOS:Queue队列及队列API函数
- 2025-01-04 STM32调试蓝牙平衡小车及bug处理
- 2025-01-04 嵌入式软件开发常用的三种架构你知道吗?
- 2025-01-04 stm32使用MPU6050或ADXL345控制的车辆减速灯
- 最近发表
- 标签列表
-
- location.href (44)
- document.ready (36)
- git checkout -b (34)
- 跃点数 (35)
- 阿里云镜像地址 (33)
- qt qmessagebox (36)
- md5 sha1 (32)
- mybatis plus page (35)
- semaphore 使用详解 (32)
- update from 语句 (32)
- vue @scroll (38)
- 堆栈区别 (33)
- 在线子域名爆破 (32)
- 什么是容器 (33)
- sha1 md5 (33)
- navicat导出数据 (34)
- 阿里云acp考试 (33)
- 阿里云 nacos (34)
- redhat官网下载镜像 (36)
- srs服务器 (33)
- pico开发者 (33)
- https的端口号 (34)
- vscode更改主题 (35)
- 阿里云资源池 (34)
- os.path.join (33)