| //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);        //显示值                          }                                       |