树莓派NETMF打造视频监控智能

树莓派是最近比较火热的开源硬件,其设备只有信用卡大小,运行着Linux系统,专为学生编程教育而设计。我十多年的技术路线基本以学习微软的技术为主,中间也曾试图学习过linux,但是相对陡峭的学习曲线,只好让我放弃了。最近几年深入研究嵌入式系统,自然绕不过去linux学习这个坎。幸好有了树莓派,一是让人容易滋生学习的兴趣;二是全球范围内网友技术交流,便于问题的定位和解决;所以在学习的过程中,慢慢地解开了linux的神秘面纱,使得有机会一探linux设计架构之美。以前用.NETMicroFramework系统做过一些智能车控制,但是功能相对简单。这次有了树莓派的加入,增加了SonyPS2遥控器、视频监控和机械手,便变得很有意思了。下图是设备连接的示意图:

从上图来看,功能还是相对比较复杂的,需要9路PWM,其中7路来自凌霄评估板(.NETMicroFramework开发板)3路控制机械手,余下的四路PWM和8个GPIO分别驱动四个马达;另外2路PWM来自树莓派,用来驱动摄像头云台(两自由度,可以水平和垂直旋转);树莓派引出一个GPIO,用来控制LED闪烁,摄像头选取的是配套摄像头;由于SonyPS2接收器和凌霄评估板连接,所以还需要把一些按键信息通过串口发给树莓派,由树莓派驱动摄像头云台。下图是组装好的设备图片:

为了让大家有一个直观的印象,先看一段演示:

由于需要介绍的内容相对较多,所以我们分四篇来讲解视频监控智能车的制作,分别为《遥控篇》:主要讲解SonyPS2遥控器信号接收处理;《控制篇(.NETMF)》:主要讲解用.NETMF如何驱动小车和控制机械手;《控制篇(树莓派)》:主要讲解如何用树莓派驱动GPIO、PWM和串口通信;《视频篇》:主要讲解视频服务的搭建,远程视频观看及自启动程序的配置。本篇先介绍SonyPS2遥控器信号获取。

遥控器说明

SonyPS2游戏机手柄有两个摇杆,14个功能键(不包含模式键),非常适合我们控制复杂的系统,比如控制机械手、摄像头云台、小车行进及速度快慢。目前网上购买一个这样的游戏手柄大概40元左右的样子,性价比还是非常高的。

设备接线

购买游戏手柄的时候已经包含了一个接收头了。有些店家还额外提供两种转接头,一种是SPI接口的,一种是串口的。SPI接口的其实就是进行了一个电平转换(3V3=5V),没有进行什么特别的处理。串口的转接头是中间加了一个AVR单片,可以主动把采集的按键信息,通过串口(TTL电平)发送出去。使用相对简单,但是功能上有问题,一是程序似乎有bug,在操作PSB_PINK和PSB_BLUE按键的时候,其返值和其它按键不同(PSB_PINK仅抬起发键值,PSB_BLUE按下和抬起都发键值,其它键都是按下发键值)。另外摇杆的键值是必须按下L2或R2时,才发送对应摇杆的X/Y值,此外多个按键如果同时按下,是无法区分的。所以我们选用SPI接口的(其实我们也可以直接把手柄接收头和我们的凌霄系统进行连接,只是增加转接板便于接线)。

凌霄评估板包含一个USB、一个TF卡槽和一路RS接口,另外直接引出31个PIN(两个标准.NETGadgeteer接口和一个子板接口)。提供2路SPI、1路I2C、5个串口、16路PWM、12路AD、2路DA、若干GPIO(Pin脚会有复用)。四个马达,两个驱动器供分别需要4个GPIO和2路PWM,为了便于连接,我们分别通过.NETGadgeteer接口提供,所以遥控手柄接收器我们连接在子板接口上。

接线如下:

?Mainboard.SubPort.Pin2(5V)--电源(4.vcc,如果是直接连,则连接Pin13V3)?Mainboard.SubPort.Pin12(PA5)--att(6.ATT选取)?Mainboard.SubPort.Pin10(PA7)--cmd(2.命令)?Mainboard.SubPort.Pin8(PC7)--dat(1.资料)?Mainboard.SubPort.Pin14(PB3)--clk(7.时钟)?Mainboard.SubPort.Pin3(GND)--地(GND)

中间的四个GPIO可以任意,只要在程序中指定就可以。

用户驱动开发

虽然接口类似SPI,但是实际用SPI接口去通信,设置各种模式(A/B/C/D四种模式),通信都不正常(返回0xFF等系列值)。所以我们采用用户驱动,用C++进行开发。用户驱动我已经写过几篇文章了,请网友自行参考《.NETMicroFramework之MDKC++二次开发》。我们直接从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操作代码:

inlinevoidPS2X::CMD_SET(void){

*_cmd_lport_set

=_cmd_mask;

}

inlinevoidPS2X::CMD_CLR(void){

*_cmd_lport_clr

=_cmd_mask;

}

inlinevoidPS2X::ATT_SET(void){

*_att_lport_set

=_att_mask;

}

inlinevoidPS2X::ATT_CLR(void){

*_att_lport_clr

=_att_mask;

}

inlineboolPS2X::DAT_CHK(void){

return(*_dat_lport_dat_mask)?true:false;

}

改为:

//Onpic32,usetheset/clrregisterstomakethematomic...

inlinevoidPS2X::CLK_SET(void){

MF-CPU_GPIO_SetPinState(SPI_CLK_Pin,TRUE);

}

inlinevoidPS2X::CLK_CLR(void){

MF-CPU_GPIO_SetPinState(SPI_CLK_Pin,FALSE);

}

inlinevoidPS2X::CMD_SET(void){

MF-CPU_GPIO_SetPinState(SPI_MO_Pin,TRUE);

}

inlinevoidPS2X::CMD_CLR(void){

MF-CPU_GPIO_SetPinState(SPI_MO_Pin,FALSE);

}

inlinevoidPS2X::ATT_SET(void){

MF-CPU_GPIO_SetPinState(SPI_CS_Pin,TRUE);

}

inlinevoidPS2X::ATT_CLR(void){

MF-CPU_GPIO_SetPinState(SPI_CS_Pin,FALSE);

}

inlineboolPS2X::DAT_CHK(void){

returnMF-CPU_GPIO_GetPinState(SPI_MI_Pin);

}

定义几个宏:

#definedelayMicrosecondsMF-HAL_Time_Sleep_MicroSeconds_InterruptEnabled#definemillis()(MF-HAL_Time_CurrentTime()/)#definedelay(x)MF-HAL_Time_Sleep_MicroSeconds_InterruptEnabled(*x)

由于没有map函数,需要我们自己实现:

intmap(INT32x,intin_min,intin_max,intout_min,intout_max)

{

return(x-in_min)*(out_max-out_min)/(in_max-in_min)+out_min;

}

另外就是重定义一些变量类型了,这里就不详述了。▌用户驱动接口的编程我们通过GeneralStream_Open2_UserDriver接口传递一个32位整型数,传入四个GPIO的值。

intGeneralStream_Open2_UserDriver(intconfig)

{

//必须第一个执行

InitUserDriver();

//获取系统函数的指针

MF=(IGeneralStream_Function*)config;

//配置IO

att=(UINT8)(MF-iParamxFF);

cmd=(UINT8)(MF-iParamxFF);

dat=(UINT8)(MF-iParamxFF);

clk=(UINT8)(MF-iParamxFF);

……

}

这里我们用到一个.NETMicroFrameworkPAL底层特有的一个功能函数:HAL_COMPLETION。可以定时去执行一个函数,类似一种多线程机制(可以定义多个)。我们定义一个20ms执行的扫描函数,用来扫描键值:

hcHander=MF-HAL_COMPLETION_Initialize(ScanKey,NULL);MF-HAL_COMPLETION_EnqueueDelta(hcHander,);//20ms执行一次

完整的扫描函数代码如下:

voidScanKey(void*arg)

{

if(error==1

type==2)

{

InitPS2();

MF-HAL_COMPLETION_EnqueueDelta(hcHander,000);//1s执行一次

if(error==1

type==2)return;

}

//读状态

ps2x.read_gamepad(false,vibrate);

UINT8button=0;

for(inti=0;i16;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

=1i;

elseButtonState=~(1i);

//触发事件

MF-Notice_GenerateEvent(UserDriver_Hander,(byte)i16

button);

}

}

UINT8lx=ps2x.Analog(PSS_LX);

UINT8ly=ps2x.Analog(PSS_LY);

UINT8rx=ps2x.Analog(PSS_RX);

UINT8ry=ps2x.Analog(PSS_RY);

ButtonAnalog=lx24

ly16

rx8

ry;

if(frist!=1)

{

if(lx!=olx

ly!=oly)

{

//MF-lcd_printf("lx:%dly:%d\r\n",lx,ly);

//触发事件

MF-Notice_GenerateEvent(UserDriver_Hander,(byte)

lx8

ly);

}

if(rx!=orx

ry!=ory)

{

//MF-lcd_printf("rx:%dry:%d\r\n",rx,ry);

//触发事件

MF-Notice_GenerateEvent(UserDriver_Hander,(byte)

rx8

ry);

}

}

olx=lx;oly=ly;orx=rx;ory=ry;frist=0;

MF-HAL_COMPLETION_EnqueueDelta(hcHander,);//20ms执行一次

}

为了便于同时获取键值和摇杆值,我们还封装了一个接口,代码如下:

intGeneralStream_IOControl2_UserDriver(intcode,intparameter)

{

//获取当前按键状态

if(code==0)returnButtonState;

elseif(code==1)returnButtonAnalog;

return-1;

}

以上代码编译成bin文件,通过YFAccessFlash直接部署到设备中即可。

▌用户C#代码

我们先做一个简单的封装:

publicPS2(Cpu.Pinclk,Cpu.Pincmd,Cpu.Pinatt,Cpu.Pindat)

{

gs=newGeneralStream();

if(gs.Open("UserDriver",(int)((int)clk24

(int)cmd16

(int)att8

(int)dat))=0)

{

thrownewException("OpenUserDriverfailed!");

}

gs.Notice+=newGeneralStreamEventHandler(gs_Notice);

}

voidgs_Notice(uinthander,uintdata,DateTimetimestamp)

{

//Debug.Print(hander.ToString()+"-"+data.ToString());

if(hander==1)

{

Keykey=(Key)(dataxFF);

intstate=0,x=0,y=0;

if(key==Key.LRocker

key==Key.RRocker)

{

x=(int)(data80xFF);

y=(int)(data0xFF);

}

else

{

state=(int)(data0xFF);

}

if(Click!=null)Click(this,newButtonArgs(key,state,x,y));

}

}

publicclassProgram

{

publicstaticvoidMain()

{

PS2ps2=newPS2(Mainboard.SubPort.Pin12,Mainboard.SubPort.Pin10,Mainboard.SubPort.Pin8,Mainboard.SubPort.Pin14

);

ps2.Click+=newPS2.ClickHandle(ps2_Click);

Thread.Sleep(Timeout.Infinite);

}

staticvoidps2_Click(objectsender,PS2.ButtonArgse)

{

Debug.Print(e.ToString());

}

}

用户应用程序功能测试

接上设备,把以上的程序运行,操作游戏机手柄,我们就可以看到按键信息了。

小结

1、有了用户驱动C/C++二次开发接口,很容易移植相关C/C++代码。2、.NETMicroFramework的封装性能,让用户程序仅







































北京最好的白癜风医院是哪家
白癜风复发怎么治疗



转载请注明地址:http://www.fupingp.com/fzpc/3385.html
  • 上一篇文章:
  • 下一篇文章: 没有了
  • 热点文章

    • 没有热点文章

    推荐文章

    • 没有推荐文章