stm32 视频小车

先直接上视频:http://www.tudou.com/programs/view/u-qxiyVeJAk/

 

 

总体设计

 2.1系统思路

 小车分为智能小车端以及客户端,小车端包括包括stm32单片机,电机,H桥集成电路,舵机,小车器件,ov767O摄像头,mr09wifi模块。小车端采用stm32主控芯片,LwIP嵌入式网络协议栈为网络传输手段,接受客户端发送的控制信息,实现PC上位机控制智能小车,通过PC机发送命令并经由wifi传输给智能小车,改变stm32单片机定时器模块输出PWM信号的占空比,从而控制电机以及舵机,完成Qt端命令的动作比如小车的运动和摄像头的旋转。使用windows+qt为客户端编写平台,客户端可以向智能车端发送控制信息,也可以从智能车端接受实时图像信息在完成处理后加以显示。由于Qt是跨平台的特点,因此较为容易的可以将客户端移植到其他平台上。

  摄像头ov767O是绑定在舵机上的,舵机与ov767O之间无杜邦线连接,而控制摄像头的旋转是通过控制舵机的旋转而带动摄像头的旋转,摄像头与stm32单片机直接相连,使得stm32ov767O的寄存器的配置等。

 

2.2 小车总体框图

stm32 视频小车_第1张图片

为表达简洁清晰,各个外设模块的驱动均没有在框图中表示。

(1)电源:负责对stm32输出5V电压,使得小车端正常工作。

(2)摄像头ov767O:负责采集视频,在所购买的stm32开发板中,含有兼容ov767O的摄像头接口,并且ov767O自带FIFO,当摄像头采集一帧图像后,自动将图像保存到FIFO中,使得用户实现程序简单。摄像头被舵机带动旋转。

(3)L298n:采用L298n直流电机驱动芯片。控制电机转向。性能稳定可靠。

(4)电机:通过stm32的定时器4输出的四路PWM信号连接到电机,从而控制小车的的运动与停止。PWM信号的占空比与电机转速有关

(5)舵机:通过stm32的定时器3输出的两路PWM信号连接到舵机,从而控制舵机的旋转。

(6)wifi模块:采用mr09芯片,该wifi模块能自己构成一个小型局域网,需要手动配置IP, 连接到该局域网后可以实现通过wifi通信,使得小车和Qt端相互之间传送信息

(7)Qt端:从图像接受与发送的角度来讲,Qt端属于服务器端,小车端属于客户端。从控制小车的命令发送与执行来讲,Qt属于客户端,小车属于服务器端。

因此,小车的功能有:运动与停止、视频传输以及无线通信功能。

 

选择硬件与调试环境

3.1 主控芯片选择

 主控芯片满足:第一,能够实现小车的功能需要,比如,定时器要足够、GPI0口够用。第二,大众化、经济适用功耗低。第三,短时间能掌握。第四,适合快速开发。

 选择stm32芯片能完全满足以上几个要求。因为stm32具有稳健的官方库,一方面可以加快开发速度,而且官方提供的库函数都是经过很多次测验的,稳健可靠。同时,库函数是开源的,非常适合学习之用,对C语言的能力也能有提高,而且库函数中会涉及到移位等操作,可以熟悉嵌入式编程。另一方面,不直接操作寄存器,虽然源头还是在寄存器上,但是直接调用接口就行。再者,stm32的主频达到72MHZ,这是其他如51单片机所不能及的,同时stm32作为主流单片机,与ARM9等相比,是非常适合入门的。ARM9由于难度高,学习周期开销过大不适合初学者[3]

  智能小车上采用野火stm32 ISO-MINI stm32f103VET6开发板,第一,容量够用,具有64KB SRAM,512KB FLASH,第二,板载资源丰富,有摄像头接口,SDIO,多定时器等,能完全满足基于wifi的视频传输智能小车的需求。第三,在外设与CPU之间有跳帽,不存在信号复用干扰的情况,即是零复用,零干扰。第四,配套学习资料丰富,能快速入门,能够随时答疑而且答疑渠道多

stm32实物图如下:

stm32 视频小车_第2张图片 

        图3-1  stm32实物图

3.2 外设选择

3.2.1 摄像头选择

 摄像头选择ov767O,它能够提供 VGA器的所有功能,它可以整帧子采样等方式输出各种分率 8/10 位图像数据,支持的多个数据格式,包括 GRB4:2:2,RGB565/555/444以及YUV RGB。体积小,工作电压低,能进行VGA摄像头影处理,可以对图像进行多种处理处理。 

    ov767O是彩色摄像头,选择输出格式为RGB565,RGB是三彩色的基色红、绿、蓝,如果采用5:6:5的关系,则一幅图像需要150K字节,大大减少了开销。在每个像素中,RGB分布如下:第一字节的高5位Y[7...3]对应R[4...0],Y[2...0]对应G[5...3],Y[7...5]对应G[2...0],Y[4...0]对应B[4...0][4]

     ov767O自带FIFO,型号为AL422B,其容量高达384K字节[5]。 自带FIFO给编程人员带来极大便利,在初始化结束后,先把摄像头场中断信号送入单片机,再次捕捉到场中断时,说明一幅图已经存入了FIFO中,此时要关闭场中断当摄像头采集了一幅图像后,它会把图像放到自带的FIFO中。而FIFO连接GPI0,一方面通过GPI0口接收读写命令,另一方面通过GPI0口把接收到的图像发出去。也就是说,只需要从GPI0口读出数据即可。

stm32 视频小车_第3张图片 

         图3-2 摄像头实物图

 

 

3.2.2舵机的选择

 舵机能够用于那些需要角度变化的环境,由于摄像头需要旋转,因此将摄像头搭载到舵机上,完成摄像头旋转。常见舵机有2.5g、4.4g、7g、9g等,使用9g,因为g代表重量克,越重就表示着体积扭力越大。同时,舵机反应速度是一个很重要的参数,实验中为0.12秒/60度,效果较好。控制舵机信号的占空比与旋转角度呈线性变化。可旋转角度为0-180。舵机一边转动一边反馈,然后根据所在位置调节方向角度,直到达到目的角度,随后停止[6]

舵机具体工作情况是接受一个脉宽,输出轴就对应到那个脉宽的角度,此时外界转矩的改变不会影响到角度的改变,只有改变脉宽才可以更新角度。判断方向与大小是由于舵机内部有比较器,它会将外部脉宽和基准脉宽比较,产生转动信号,达到角度。

如图,舵机带动摄像头旋转。

stm32 视频小车_第4张图片 

        3-3  舵机实物图

 

3.2.3 电机驱动选择

 

    对电机的控制可以继电器、数字电位器、H桥电路

    继电器控制对电动机的开关,来调整转速,电路简单但是响应时间长,不可靠。

    数字电位器可对电动机分压改变转速,但是其昂贵而且所用电动机的电阻小,电流大,分压会导致效率低,实现困难。

    H桥电路为专门的电机驱动,H桥作为电机驱动和电机是分不开的,它能精确调整转速,效率高,响应快

    因此选择H桥电路控制,具体的采用L298n,因为它能输出电流大,功率强,驱动能力强[7]。输入端能够直接连线到与stm32的GPI0口,能够很方便的受stm32控制。L298n还具有过电流保护功能,能够一定程度上防止电机被烧坏,安全性比较高。

 

      图3-4  H桥电路实物图

 

3.3 wifi模块选择

小车是可以随意移动的,因此利用无线传输比有线传输更加方便。在物理层与MAC层,以太网与wifi具有较大不同。而MAC层之上包括LLC、网络层、传输层、应用层基本相同的,具有封装的特点。

    而能够实现无线传输的有多种方式,比如蓝牙、wifi等。从速度、能传距离、功耗等综合考虑,采用wifi方式。蓝牙协议较复杂、功耗高、成本高、不能穿墙。有效的范围在10米左右。使用蓝牙效果不太理想。而wifi可随时接入、移动性强,不过功耗比蓝牙大,但是由于不像WSN对功耗要求严格,可以接受功耗的损失。

采用的时mr09wifi模块,不仅与stm32完全兼容,而且是既可以动态分配IP地址,也可以静态分配IP地址。从实现难易的角度来说,由于只有一个或者几个PC连接到该wifi,因此采用静态分配的方法,简单实用。wifi模块与PC端直接处于一个网段,不需要路由。采用轻量级LwIP协议栈。

 

 

      3-5  mr09实物图

3.4调试环境

J-link带有支持ARM一款仿真器。它支持很多集成开发软件,支持的很多ARM内核芯片调试[8]。无论是速度、使用便捷度、电压兼容情况、使用广泛度来讲,J-link都是一款实用高效的仿真器。

 

3.5小车图像

stm32 视频小车_第5张图片 

        3-6  小车正面图

      当硬件选购好了以后,根据电路图加各个部件连接起来,就构成了智能小车的支架,这个时候小车是不会有任何反应的,因此,只有这些器件是远远不够的,因此,还需要软件设计,只有像芯片中烧入代码,小车才能接受控制并且给出相应的反应。将软件与硬件相结合,才能完成智能小车的制作。

 

软件设计

4.1电机控制程序设计

    stm32有8个16位定时器,采用第2、3、4、5定时器为通用的定时器,第6、7定时器只具有基本功能,第1、8个定时器有高级功能。通用计数器主要用在测量输入脉冲的频率、脉冲宽与输出PWM脉冲的场合等情况下采用定时器TIM4输出具有一定占空比的PWM信号。

 配置脉冲计数器为向上计数,而重载寄存器被配置为re_load,当脉冲计数器的数值real_pls_count>re_load时,会重置real_pls_count,并开始新一轮重新计数。在real_pls_count值变化的同时,会real_pls_count与比较寄存器预先存储了的数值compare_count进行比较,当real_pls_count<compare_count,输出高电平,比较结果相反输出低电平。 

 电机的控制由4路PWM信号完成完成,使用TIM4定时器是为了让TIM4输出4路PWM信号,2路为一组,控制电机的转向。TIM4产生的PWM信号要通过GPI0口输出,因此先对GPI0口进行配置。然后配置TIM4本身的参数,需要配置TIM4的周期、频率、计数方式、电平跳变值、初始化输出通道,最后使能TIM4.使得TIM4能够输出4路PWM信号。将PD12,PD13,PD14,PD15引脚复用推挽输出PWM信号,反应速度为50MHZ,初始时,电平跳变值均为0,定时器从0计数到999,采用向上计数方式,不预分频仍未72MHZ,配置为PWM模式1,采用TIM4的1、2、3、4通道输出PWM信号,当定时器计数到达某个通道设置的电平跳变值时,那个通道输出的电平发生变化,由O到1或者由1到O.当计数值小于通道电平跳变值时,输出高电平。

    当完成对GPI0D口初始化、定时器基本参数配置的时,还需要使能定时器,使得它开始工作,输出暂时占空比不变的PWM信号一旦Qt端操作,按下控制小车的任意键,占空比会改变。

当用户通过Qt端对小车发送命令时,比如前进、后退、左转、后传、停止。stm32通过wifi接受到命令,并且判断命令要求动作,进而执行相关函数,改变不同通道的电平跳变值,使得输出到电机的PWM信号占空比改变,从而实现对电机运动情况的改变,以停止命令为例,将所有的通道的电平跳变值变为999即可

 

 

 

 

4.2 摄像头的程序设计

首先得明白ov767O的内部结构图

 

stm32 视频小车_第6张图片

 

如上所示,ov767O通过SCCB(摄像头控制总线)连接ov767O内部寄存器,必须通过对内部寄存器的赋值摄像头的配置。该SCCB工作在2线串行模式。SI0_C表示时钟信号,SI0_D表示数据信号,由于stm32没有SCCB模块的接口,只能通过GPI0口模拟控制总线的时序。因此需要首先配置GPI0口,设置PC6、PC7模拟SCCB时序

 再如上图表示,当摄像头采集了一幅图像后,它会把图像放到自带的FIF0中。而FIF0连接GPI0,通过GPI0口把接收到的图像发出去。因此,需要配置GPI0设置。其中PB8-PB15输出数据。PC5读时钟,PA2读复位,PB5写复位,PD3写使能。

    完成了对GPI0和SCCB的配置后,检查ID是否正确,ov767O的寄存器发送配置参数,初始化0V767O。

 由于通过VSYNC时序来判断图像是否存入FIFO,对于VSYNC的检测中使用了中断[9]。因此还需要对VSYNC进行中断相关配置。初始化相应的GPI0口,并为它配置EXTI中断。 主要包括三方面,配置GPI0的A口的地第0引脚即PA0,下降沿触发,将中断挂载到EXTI_LineO线.还需要进行中断配置,如优先级等。最后将VSYNC信号初始化为低电平。

由于IP协议一次传输数据报大小收到限制,它完全无法一次传输完一帧图像,因此,必须将每帧图像分割为若干个数据包,分别由小车端传给Qt端,由于采用UDP协议,存在先来后到的情况,解决的办法是给每一个包一个数据编号,标示它是这一帧图像中的第几个包,就好比是一个座位号。当Qt端接受到这些包时,根据这个数据编号,将数据包放到该放的位置,也就相当于按照座位号指定的位置坐好。

 

这个协议如下:

                    表4-1 自定义协议

2字节协

议头

1字节图像内编号

1字节控制信息

2字节数据大小

数据

2字节协议尾

 

 

 核心代码:

 uint16_t i, j;

u8 Re,Gr,Bl;

   uint16_t Cameras_Data;

for(ilo = 0; ilo < 320*240/SIZE_RGB; ilo++)

{

      for(jlo = 0 ; jlo < SIZE_RGB ; jlo ++)

  {

 

                READ_FIFO_PIXEL(Cameras_Data);

  Re = ((Cameras_Data>>11)<<3) ;

  Gr= ((Cameras_Data&0x7ff)>>5) << 2;

  Bl= ((Cameras_Data) & 0x1f)<<3;

  mess[jlo] = ((77*Re + 151*Gr + 28*Bl)>>8) & 0xff;

 

      }

if(ilo == 0)

{

        mess[0] = 'ss';

          }

else if(ilo == 320*240/SIZE_RGB - 1)

{

mess[0] = 'ee';     

             }

else

{

mess[0] = 'oo';

         }

           mess[1] = ( ilo & 0x00ff );

udp_sendto(Clipcb,p_tx,&ipaddr,50000);

 

     

      上述程序解释:mess[0]=ee表示该数据片是一帧图像的末尾,mess[0]=ss表示该数据片是一帧图像的开始,mess[0]=oo表示该数据片是一帧图像的中间部分。jlo指示该数据片在那帧图像中的座位。SIZE_RGB是指每个数据片中所包含图像信息大小,能整除数据一帧图像大小。 为了减少发送的数据量,将图片调成了灰度,根据算法: mess[jlo] = ((77*Re + 151*Gr + 28*Bl)>>8) & 0xff。这是在一个像素点的数据中通过移位的到该像素点的三个基本色的数据。

 

          stm32 视频小车_第7张图片

 

 

4.3舵机程序设计

舵机的控制也是需要接收2路PWM信号,由定时器TIM3产生。使用TIM3定时器是为了让TIM3输出2路PWM信号,控制舵机的旋转。TIM3产生的2路PWM信号要通过GPI0口输出,因此先对GPI0口进行配置。然后配置TIM3本身的参数,比如配置TIM4的周期、频率、计数方式、电平跳变值、初始化输出通道,最后使能TIM3.将PB0、PB1引脚复用推挽输出PWM信号,反应速度为50MHZ,初始时,电平跳变值均为0,定时器从0计数到999,采用向上计数方式,不预分频仍未72MHZ,配置为PWM模式1,采用TIM3的3、4通道输出PWM信号,当定时器计数到达某个通道(比如一通道)的电平跳变值时,一通道的输出电平就马上发生变化,由O到1或者由1到O.当计数值小于通道电平跳变值时,输出高电平。

PWM信号占空比的改变:当初始化TIM3后,由于通道3、4的电平跳变值没变,其输出信号的占空比是保持不变的,当Qt端拖动滑动条,小车端接受到后改变通道3、4的电平跳变值,使得TIM3能够输出变化的占空比,从而完成舵机的旋转,Qt端变化时小车端的电平跳变值改变主要代码:

/*设置通道3的电平跳变,输出一个占空比PWM信号。

*/

void set_hmotor(int k)

{

    if(k != 4)

      TIM3->CCR3 = ( 4 - k ) * 25 + 25;

else

TIM3->CCR3 = 34;

}

/*设置通道4的电平跳变,输出另一个占空比PWM信号。

*/

void set_vmotor(int k)

{

      TIM3->CCR4 = ( 2 - k ) * 25 + 100;

}

 

 

 

4.4 Qt客户端程序设计

      采用windows+qt作为编写平台,Qt具有与十分强大的跨平台特点,能够支持包括主流操作系统在内的至少10种操作系统。同时,Qt封装机制好,具有独一无二的信号与槽机制,这使得各个元件的协同简单易行,而且具有大量的API函数和帮助文档[10]。采用Qt自带的轻量级编译工具Qt creator完成 Qt端的编写。

    Qt端大致分为几个部分,第一,界面。第二部分,就是从小车端接受图像并显示的部分。第三部分,根据用户对界面的控制向小车端发送相应的命令。

界面部分:界面部分要求操作简单。界面部分主要包括图像显示、小车前进、后退、左转、右转、停止命令按钮,摄像头旋转调整滑动条,图4-3 是Qt端界面,下面左半边是控制部分。上面显示视频,为灰度。

 

 

 

 

第二部分,就是从小车端接受图像并显示。这是,Qt端是作为服务器端的。先解决一帧图像传输到Qt端的情况,由于采用UDP协议,要解决存在的先来后到情况,Qt端需要根据自定义的协议对一个一个的数据片进行组装,不然可能造成图像显示混乱。代码如下:

/*将一帧图像里的不同数据片组装*/

void add_pix(unsigned char * plo,unsigned char * qlo)

{

    int jlo;

    unsigned short k = qlo[1];

    for(jlo = 0 ; jlo  < SIZE_RGB ; jlo ++)

    {

 

        plo[SIZE_RGB * 1 * k + 1 * i] = (qlo[2 + 1 * i] & 0xff) ;

 

    }

}

/*不停的接受图像并且灰度显示*/

 while(1)

    {

       

        while(1)

        {

       

         recv(ReccivingSocket, (char *)RecciveBuf, BufLength, 0);

            if(stute == 1)

            {

                if(RecciveBuf[0] == 'ss')

                {

                    stute = 2;

                    add_pix(p,RecciveBuf);

                }

            }

            else

            {

               if(RecciveBuf[0] == 'ss' || RecciveBuf[0] == 'oo')

                {

                

                    add_pix(p,RecciveBuf);

                }

                if(RecciveBuf[0] == 'ee' )

                {

                   add_pix(p,RecciveBuf);

                    stute = 1;

                    break;

                }

            }

     }

 

QImagc * imgScaled =new QImagc((uchar *)(p), 320, 240,320,    QImagc::Format_Indexed8,0,0);

             m_receiver->setPixmap(QPixmap::fromImage(*imgScaled,Qt::AutoColour));

         (*this->sem) = 0;

          QThread::msleep(20);

          (*this->sem) = 1;

       

    }

 

 

其中,jlo代表一帧图像中的第几包数据,当一包数据来到时,会按照‘座位号’把该包数据放在相应的位置。

 第三部分,根据用户对界面的控制向小车端发送相应的命令。这时,Qt端是作为客户端。这利用信号与槽机制同事件相结合来处理。当用户对界面操作时,按下A、W、Q、S、D按键或者拖动滑动条时,会将命令发送给小车,命令中含有表示命令类型的字符。小车端接受后解析,从而调用相应函数完成动作。当小车端接受到命令后,先进行解析,依次判断命令类型,这在代码中由switch分支语句完成。如果接受到的命令不属于这其中的任何一种,那么不进行任何操作。代码仅仅以按下W键为例,其他的处理相同。

 switch(e->key())

    {

       case Qt::Key_W:

           send(clientsocket,"w\n",strlen("q\n")+1,0);

           

       ......

       

       Default:;

}

 

 

4.5 无线通信程序设计

    数据的无线传输涉及到socket编程、LwIP协议栈移植等,由于命令的传输要求准确无误,因此采用的是TCP协议,图像传输要求快而及时,对于正确性要求不高,丢失数据也没关系。而且又是自构建的局域网,没有过多的主机接入,干扰少,因此采用UDP协议。 

小车端对LwIP协议栈初始化,协议栈初始化中主要包括初始化内存池、静态配置IP地址、网关等[11]。在LwIP协议栈中,有两个非常重要的结构体:pbuf和netif。前者建立与MAC层收发数据的结构体,实现了协议栈对数据的管理,后者保存网卡状态,实现对网卡的管理。

 

无线网络连接成功后,可以使用ping命令检查是否ping 通了。



来自为知笔记(Wiz)



你可能感兴趣的:(stm32 视频小车)