C 用户驱动开发 虽然接口类似SPI,但是实际用SPI接口去通信,设置各种模式(A/B/C/D四种模式),通信都不正常(返回0xFF等系列值)。所以我们采用用户驱动,用C++进行开发。 用户驱动我已经写过几篇文章了,请网友自行参考《.NET Micro Framework之MDK C++二次开发》。 我们直接从Arduino相关驱动进行修改移植,包含两个文件:ps2x_lib.h和ps2x_lib.cpp。 我们需要修改和GPIO操作、时钟操作相关的部分。 在config_gamepad函数中我们添加GPIO初始化相关代码 MF->CPU_GPIO_EnableOutputPin(att,FALSE); MF->CPU_GPIO_EnableOutputPin(cmd,FALSE); MF->CPU_GPIO_EnableInputPin(dat,FALSE,NULL,GPIO_INT_NONE,RESISTOR_PULLUP); MF->CPU_GPIO_EnableOutputPin(clk,FALSE); 原GPIO操作代码: inline void PS2X::CMD_SET(void) { *_cmd_lport_set |= _cmd_mask; } inline void PS2X::CMD_CLR(void) { *_cmd_lport_clr |= _cmd_mask; } inline void PS2X::ATT_SET(void) { *_att_lport_set |= _att_mask; } inline void PS2X::ATT_CLR(void) { *_att_lport_clr |= _att_mask; } inline bool PS2X::DAT_CHK(void) { return (*_dat_lport & _dat_mask)? true : false; } 改为: // On pic32, use the set/clr registers to make them atomic... inline void PS2X::CLK_SET(void) { MF->CPU_GPIO_SetPinState(SPI_CLK_Pin,TRUE); } inline void PS2X::CLK_CLR(void) { MF->CPU_GPIO_SetPinState(SPI_CLK_Pin,FALSE); } inline void PS2X::CMD_SET(void) { MF->CPU_GPIO_SetPinState(SPI_MO_Pin,TRUE); } inline void PS2X::CMD_CLR(void) { MF->CPU_GPIO_SetPinState(SPI_MO_Pin,FALSE); } inline void PS2X::ATT_SET(void) { MF->CPU_GPIO_SetPinState(SPI_CS_Pin,TRUE); } inline void PS2X::ATT_CLR(void) { MF->CPU_GPIO_SetPinState(SPI_CS_Pin,FALSE); } inline bool PS2X::DAT_CHK(void) { return MF->CPU_GPIO_GetPinState(SPI_MI_Pin); } 定义几个宏: #define delayMicroseconds MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled #define millis() (MF->HAL_Time_CurrentTime()/1000) #define delay(x) MF->HAL_Time_Sleep_MicroSeconds_InterruptEnabled(1000*x) 由于没有map函数,需要我们自己实现: int map(INT32 x, int in_min, int in_max, int out_min, int out_max) { return(x -in_min)*(out_max -out_min)/(in_max -in_min)+out_min; } 另外就是重定义一些变量类型了,这里就不详述了。
下面说一下用户驱动接口的编程: 我们通过GeneralStream_Open2_UserDriver接口传递一个32位整型数,传入四个GPIO的值。 int GeneralStream_Open2_UserDriver(int config) { //必须第一个执行 InitUserDriver(); //获取系统函数的指针 MF = (IGeneralStream_Function*)config; //配置IO att = (UINT8)(MF->iParam1>>24 & 0xFF); cmd= (UINT8)(MF->iParam1>>16 & 0xFF); dat= (UINT8)(MF->iParam1>>8 & 0xFF); clk = (UINT8)(MF->iParam1>>0 & 0xFF); …… } 这里我们用到一个.NET Micro Framework PAL底层特有的一个功能函数:HAL_COMPLETION。可以定时去执行一个函数,类似一种多线程机制(可以定义多个)。 我们定义一个20ms执行的扫描函数,用来扫描键值: hcHander = MF->HAL_COMPLETION_Initialize(ScanKey,NULL); MF->HAL_COMPLETION_EnqueueDelta(hcHander,20000); //20ms执行一次 完整的扫描函数代码如下: void ScanKey(void *arg) { if(error == 1 || type == 2) { InitPS2(); MF->HAL_COMPLETION_EnqueueDelta(hcHander,1000000); //1s执行一次 if(error == 1 || type == 2)return; } //读状态 ps2x.read_gamepad(false, vibrate); UINT8 button = 0; for(int i=0;i<16;i++) { if(ps2x.NewButtonState(Buttons[i])) { button = ps2x.Button(Buttons[i]); //MF->debug_printf("%s:%d\r\n",ButtonNames[i],button); //MF->lcd_printf("%s:%d \r\n",ButtonNames[i],button); if(button) ButtonState |= 1<<i; else ButtonState &= ~(1<<i); //触发事件 MF->Notice_GenerateEvent(UserDriver_Hander,(byte)i<<16 | button ); } } UINT8 lx=ps2x.Analog(PSS_LX); UINT8 ly=ps2x.Analog(PSS_LY); UINT8 rx=ps2x.Analog(PSS_RX); UINT8 ry=ps2x.Analog(PSS_RY); ButtonAnalog = lx<<24 | ly<<16 | rx<<8 | ry; if(frist!=1) { if(lx!=olx || ly!=oly) { //MF->lcd_printf("lx:%d ly:%d \r\n",lx,ly); //触发事件 MF->Notice_GenerateEvent(UserDriver_Hander,(byte)16<<16 | lx<<8 | ly ); } if(rx!=orx || ry!=ory) { //MF->lcd_printf("rx:%d ry:%d \r\n",rx,ry); //触发事件 MF->Notice_GenerateEvent(UserDriver_Hander,(byte)17<<16 | rx<<8 | ry ); } } olx=lx;oly=ly;orx=rx;ory=ry;frist=0; MF->HAL_COMPLETION_EnqueueDelta(hcHander,20000); //20ms执行一次 } 为了便于同时获取键值和摇杆值,我们还封装了一个接口,代码如下: int GeneralStream_IOControl2_UserDriver(int code,int parameter) { //获取当前按键状态 if(code == 0) return ButtonState; else if(code == 1) return ButtonAnalog; return -1; } 以上代码编译成bin文件,通过YFAccessFlash直接部署到设备中即可。
下面我们介绍一下,用户C#代码 我们先做一个简单的封装: public PS2(Cpu.Pin clk,Cpu.Pin cmd,Cpu.Pin att,Cpu.Pin dat ) { gs = new GeneralStream(); if (gs.Open("UserDriver", (int)((int)clk << 24 | (int)cmd << 16 | (int)att << 8 | (int)dat)) <= 0) { throw new Exception("Open UserDriver failed!"); } gs.Notice += new GeneralStreamEventHandler(gs_Notice); } void gs_Notice(uint hander, uint data, DateTime timestamp) { //Debug.Print(hander.ToString() + " - " + data.ToString()); if (hander == 1) { Key key = (Key)(data >> 16 & 0xFF); int state = 0,x=0,y=0; if (key == Key.LRocker || key == Key.RRocker) { x = (int)(data >> 8 & 0xFF); y = (int)(data & 0xFF); } else { state = (int)(data & 0xFF); } if (Click != null) Click(this, new ButtonArgs(key, state, x, y)); } } public class Program { public static void Main() { PS2 ps2 = new PS2(Mainboard.SubPort.Pin12, Mainboard.SubPort.Pin10, Mainboard.SubPort.Pin8, Mainboard.SubPort.Pin14 ); ps2.Click += new PS2.ClickHandle(ps2_Click); Thread.Sleep(Timeout.Infinite); } static void ps2_Click(object sender, PS2.ButtonArgs e) { Debug.Print(e.ToString()); } }
D用户应用程序功能测试 接上设备,把以上的程序运行,操作游戏机手柄,我们就可以看到按键信息了。 文章导航: 2、【树莓派+.NET MF打造视频监控智能车】控制篇(.NET MF) 3、【树莓派+.NET MF打造视频监控智能车】控制篇(树莓派) 4、【树莓派+.NET MF打造视频监控智能车】视频篇 小结: 1、 有了用户驱动C/C++二次开发接口,很容易移植相关C/C++代码。 2、 .NET Micro Framework的封装性能,让用户程序仅关注业务逻辑即可,显得非常的简单易用。 3、 VS2010/VS2012可以在线调试.NET Micro Framework(加断点、单步执行等等),便于问题诊断和调试。 本文地址:http://www.cnblogs.com/yefanqiu/p/3533454.html |