//PS2中断屏蔽设置 //en:1,开启;0,关闭; void PS2_Set_Int(u8 en) { EXTI->PR=1<<11; //清除LINE11上的中断标志位 if(en)EXTI->IMR|=1<<11;//不屏蔽line11上的中断 else EXTI->IMR&=~(1<<11);//屏蔽line11上的中断 } //等待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初始化 void PS2_Init(void) { RCC->APB2ENR|=1<<4; //使能PORTC时钟 GPIOC->CRH&=0XFFFF00FF;//PC10,11设置成输入 GPIOC->CRH|=0X00008800;//PC10,11设置成输出 GPIOC->ODR|=3<<10; Ex_NVIC_Config(GPIO_C,11,FTIR);//将line11映射到PC.11,下降沿触发. MY_NVIC_Init(1,2,EXTI15_10_IRQChannel,2);//分配到第二组,抢占2,响应3 } 该部分为底层的PS/2协议驱动程序,采用中断接收PS/2设备产生的时钟信号,然后解析。 保存ps2.c文件,并加入到HARDWARE组下,然后打开ps2.h,在该文件里面输入如下代码: #ifndef __PS2_H #define __PS2_H #include "delay.h" #include "sys.h" #define PS2_SCL PCin(11) //PC11 #define PS2_SDA PCin(10) //PC10 //PS2输出 #define PS2_SCL_OUT PCout(11) //PC11 #define PS2_SDA_OUT PCout(10) //PC10 //设置PS2_SCL输入输出状态. #define PS2_SET_SCL_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=0X00008000;} #define PS2_SET_SCL_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=0X00003000;} //设置PS2_SDA输入输出状态. #define PS2_SET_SDA_IN() {GPIOC->CRH&=0XFFFFF0FF;GPIOC->CRH|=0X00000800;} #define PS2_SET_SDA_OUT() {GPIOC->CRH&=0XFFFFF0FF;GPIOC->CRH|=0X00000300;} #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 该部分代码定义了一个鼠标结构体,用于存放鼠标相关的数据,并对鼠标的相关命令进行了宏定义(部分被省略),保存此部分代码。最后,打开test.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); //显示值 } |