当前位置:   article > 正文

基于STM32、OV2640及ESP8266的无线图传_基于hal库stm32f407zgt6+ov2640+esp8266

基于hal库stm32f407zgt6+ov2640+esp8266

一、简介:

        本文利用STM32F407单片机、OV2640摄像机模块以及ESP8266 WIFI模块,并基于C#编写的TCP上位机服务,来实现图像的无线传输。

        本文受启发于博客:ESP8266+STM32F407+OV7670实现图片传输,在此感谢该文作者。与该文不同的是,本文采用的摄像机模块是0V2640,传输的数据是压缩之后的jpeg格式的图像数据,而不是像上文博主那样,将RGB565数据直接传输到上位机。此外,本文存在和上文博主同样的问题,即采用串口传输方式,数据传输速率过低,实际应用中,发现3秒左右才能够传出一帧320*240的图片。本文接下来对整个图传功能中使用到的模块一一进行详细介绍。

二、DCMI+OV2640摄像头

        在整个图传项目中,使用到的最核心的模块就是OV2640模块, 关于该模块的介绍以及详细的驱动,可以参考文章:STM32F4驱动OV2640摄像头,该文详细介绍了OV2640模块、DMCI接口及整个OV2640的驱动。此处进行简要介绍:

        STM32F4驱动OV2640采用的是DCMI接口及DMA直接存储器访问对于DMA的相关介绍可以参考文章:STM32:DMA。整个驱动流程是:STM32单片机通过DCMI接口,获取OV2640摄像头采集到的图像数据,并通过提前配置好的DMA数据流,将数据传输到LCD或内部数组。如下图所示,是通过DMA配置,将DCMI采集到的图像数据传输到LCD显示屏的效果:

        关于LCD显示屏的介绍及详细驱动,可以参考博文:  STM32: LCD显示。这样,首先通过OV2640及LCD屏幕,将摄像头模块采集到的数据显示在屏幕上。

         该部分的源码: OV2640驱动,虽然我们将OV2640采集到的图像成功显示在了LCD屏幕之上,但是,我们最终的目的是将图像数据利用ESP8266模块通过WIFI传输到上位机或网页中,因此,我们还需要ESP8266模块。

三、ESP8266

         ESP8266是比较常见的WIFI模块,该模块有三种不同的工作模式,即softAP 模式station 模式softAP + station 共存模式。(SoftAP:即无线接入点,是一个无线网络的中心节点,通常使用的无线路由器就是一个无线接入点;Station:即无线终端,是一个无线网络的终端端。)

        本文我们将ESP8266作为客户端,并将其传输模式设置为透传,让其连接位于电脑的TCP上位机服务器,然后将OV2640采集的图像数据通过单片机传输给ESP8266模块,并采用WIFI发送给上位机。ESP8266的配置过程如下:

  • AT+RESTORE                                                      #恢复出厂设置
  • AT+CWMODE=1                                                  #设置ESP8266工作模式为STA
  • AT+RST                                                                #复位
  • AT+CWJAP="路由器账号","密码"                         #连接路由器
  • AT+CIPMODE=1                                                  #设置透传模式
  • AT+CIPSTART="TCP","192.168.6.117",8266      #连接TCP服务器(上位机)
  • AT+CIPSEND                                                       #开启透传

        此处要注意,本文的ESP8266是通过AT指令进行配置的,因此要保证ESP8266已经烧录了AT固件库,一般网上买的ESP8266模块默认会烧录AT固件库,如若没有,可以自行进行烧录。

四、工作原理

        基本的模块介绍已经完成,通过模块介绍,我们也可以发现,无线图传模块的工作原理是:首先我们利用STM32和OV2640模块,采集图像数据,然后,我们配置好ESP8266,让其连接上位机服务器,然后,我们通过串口,将STM32采集到的图像数据传输给ESP8266,由于ESP8266配置的是透传模式,因此其会将STM32通过串口发送过来的数据原封不动的通过WIFI发送给上位机,上位机在将这些图像数据解析为图像显示出来就可以了,如下图所示:

         上文中,我们介绍OV2640模块时,将图像显示在LCD屏幕上,但是,我们真正的目的,是将数据通过ESP8266模块传输给上位机。其实这两者本质上是一样的,一个是将数据通过DMA传输给LCD屏幕,而另外一个则是将数据传输给串口(因为ESP8266和STM32是通过串口连接的)

核心传输代码如下:

  1. //处理JPEG数据
  2. //当采集完一帧JPEG数据后,调用此函数,切换JPEG BUF.开始下一帧采集.
  3. void jpeg_data_process(void)
  4. {
  5. if(ov2640_mode)//只有在JPEG格式下,才需要做处理.
  6. {
  7. if(jpeg_data_ok==0) //jpeg数据还未采集完?
  8. {
  9. DMA_Cmd(DMA2_Stream1, DISABLE);//停止当前传输
  10. while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}// 等待DMA2_Stream1可配置
  11. jpeg_data_len=jpeg_buf_size-DMA_GetCurrDataCounter(DMA2_Stream1);// 得到此次数据传输的长度
  12. jpeg_data_ok = 1; // 数据已经采集完成,等待被处理
  13. }
  14. if(jpeg_data_ok==2) //上一次的jpeg数据已经被处理了
  15. {
  16. DMA2_Stream1->NDTR=jpeg_buf_size;
  17. DMA_SetCurrDataCounter(DMA2_Stream1,jpeg_buf_size);//传输长度为jpeg_buf_size*4字节
  18. DMA_Cmd(DMA2_Stream1, ENABLE); //重新传输
  19. jpeg_data_ok=0; //标记数据未采集
  20. }
  21. }
  22. }
  23. //jpeg模式
  24. void jpeg_test(void)
  25. {
  26. u32 i,jpgstart,jpglen;
  27. u8 headok=0;
  28. u8 *p;
  29. u8 effect=0,saturation=2,contrast=2;
  30. u8 size=3;
  31. u8 msgbuf[15]; //消息缓存区
  32. //uart4初始化
  33. LTE_uart3_init(115200);
  34. LCD_Clear(WHITE);
  35. POINT_COLOR=RED;
  36. LCD_DisplayString(30,60,24,"ALIENTEK STM32F4");
  37. LCD_DisplayString(30,90,24,"OV2640 JPEG Mode");
  38. OV2640_JPEG_Mode(); // JPEG模式
  39. My_DCMI_Init(); //DCMI配置
  40. DCMI_DMA_Init((u32)&jpeg_buf,jpeg_buf_size,DMA_MemoryDataSize_Word,DMA_MemoryInc_Enable);//DCMI配置 输出到数组
  41. OV2640_OutSize_Set(jpeg_img_size_tbl[size][0], jpeg_img_size_tbl[size][1]);
  42. DCMI_Start(); //启动传输
  43. delay_ms(500);
  44. while(1)
  45. {
  46. if(jpeg_data_ok==1) // 已经采集完成一帧图像,开始处理数据
  47. {
  48. p=(u8*)jpeg_buf;
  49. LCD_DisplayString(30,150,24,"Sending JPEG data...");
  50. jpglen=0; //设置jpg文件大小为0
  51. headok=0; //清除jpg头标记
  52. for(i=0; i<jpeg_data_len*4; i++)
  53. {
  54. //查找OXFF,OXD8和0XFF,0XD9,获取jpg文件大小
  55. if((p[i]==0XFF)&&(p[i+1]==0XD8)){
  56. jpgstart=i;
  57. headok=1; //标记找到jpg头(FF D8)
  58. }
  59. if((p[i]==0XFF)&&(p[i+1]==0XD9)&&headok)//找到头以后,再找FF D9
  60. {
  61. jpglen=i-jpgstart+2;
  62. break;
  63. }
  64. }
  65. if(jpglen) // 正常的jpeg数据
  66. {
  67. p+=jpgstart;
  68. for(i=0;i<jpglen;i++) //发送整个jpg文件
  69. {
  70. while(USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET); //循环发送,直到发送完毕
  71. USART_SendData(USART3, p[i]);
  72. }
  73. }
  74. jpeg_data_ok = 2; //标记jpeg数据处理完了,可以让DMA去采集下一帧了.
  75. delay_ms(2000);
  76. }
  77. }
  78. }

        上述核心代码其实是两个函数,函数jpeg_data_process()在帧中断中被调用,也就是说,每次采集完一帧图像,该函数都会被调用。在该函数中,主要是修改标志jpeg_data_ok 为1,以便另外一个函数jpeg_test()可以对这一帧数据进行处理,所谓的处理在此处其实就是将数据通过串口发送给ESP8266ESP8266模块会将数据发送给上位机,并由上位机解析然后显示。

五、TCP服务上位机

        ESP8266在拿到STM32通过串口传输的图像数据之后,会将其发送给上位机,那么ESP8266和上位机之间是如何通讯的呢?是通过TCP/IP协议

        本文使用C#语言,基于TCP/IP协议,写了一个简单的上位机服务,该服务接受ESP8266的连接,并将其发送过来的数据编码为图像进行显示。由于软件写的很简单,因此很多功能并没有进行扩充实现,如其只支持一个设备的图传,后续可以进行升级改进。软件界面如下所示:

        该软件的实现思路非常简单,因此其功能也比较粗狂,bug较多。

        软件整体包含两条主要的线程以及一个数据缓冲容器(生产者消费者模式)。在服务器启动之后,软件开启监听服务,监听ESP8266的连接请求,并开启一个消费线程,用于从缓冲容器中获取图像数据(此时应该没有数据),在服务与ESP8266建立连接之后,就开始接收来自ESP8266的图像数据,此时,创建一个数据生产线程,将服务器接收到的数据存放到缓冲容器中,这样,这两个线程一个专门负责将接收到的数据放到缓冲容器中,一个专门负责从缓冲容器中获取数据并将其解码为图像在界面显示。

上位机核心代码:

  1. /// <summary>
  2. /// 接收客户端发送的图像数据,并放到缓冲容器中。
  3. /// </summary>
  4. /// <param name="proxsocket"></param>
  5. public void ReceiveClientMessage(object socket)
  6. {
  7. int imageCount = 0;
  8. //服务器端与客户端之间用于通讯的socket
  9. Socket proxSocket = socket as Socket;
  10. //开辟用来存储客户端发送来的数据的控件
  11. byte[] dataClient = new byte[640 * 240];
  12. proxSocket.ReceiveTimeout = 1000 * 300; // 设置接收数据时的阻塞时间为15秒钟
  13. //接收客户端数据
  14. while (serverStart)
  15. {
  16. StringBuilder imageData = new StringBuilder();
  17. try
  18. {
  19. while (imageCount < 5)
  20. {
  21. int countRev = proxSocket.Receive(dataClient, 0, dataClient.Length, SocketFlags.None);
  22. string revDate = StringUtils.ToHexStrFromByte(dataClient, countRev);
  23. if (revDate.Length>0)
  24. {
  25. imageData.Append(revDate);
  26. imageCount++;
  27. AppendToMessage(revDate);
  28. }
  29. }
  30. jpegQueue.Add(imageData.ToString());
  31. imageCount = 0;
  32. }
  33. catch (SocketException e)
  34. {
  35. if (e.ErrorCode == 10060)
  36. {
  37. continue;
  38. }
  39. else
  40. {
  41. //客户端非正常退出。
  42. AppendToMessage(string.Format("客户端:{0}非正常退出。", proxSocket.RemoteEndPoint.ToString()));
  43. return; //线程终结
  44. }
  45. }
  46. }
  47. }
  48. /// <summary>
  49. /// 从容器中获取图形数据,并解析为图像显示
  50. /// </summary>
  51. private void createImg()
  52. {
  53. string imgData = "";
  54. while (serverStart)
  55. {
  56. string imgListData = jpegQueue.Take();
  57. imgData = imgData + imgListData;
  58. while (imgData.IndexOf("FF D8")!=-1)
  59. {
  60. int startIndex = imgData.IndexOf("FF D8");
  61. int endIndex = imgData.IndexOf("FF D9");
  62. if (endIndex != -1)
  63. {
  64. string jpeg = imgData.Substring(startIndex,endIndex+5-startIndex);
  65. imgData = imgData.Substring(endIndex+5);
  66. Bitmap imageBitmap = StringUtils.GetJpegImage(jpeg.Replace(" ", ""));
  67. if (imageBitmap != null)
  68. {
  69. Image img = Image.FromHbitmap(imageBitmap.GetHbitmap());
  70. if (this.JpgImage.InvokeRequired)
  71. {
  72. JpgImage.Invoke(new Action<Image>(s =>
  73. {
  74. JpgImage.Image = s;
  75. }), img);
  76. }
  77. else
  78. {
  79. this.JpgImage.Image = img;
  80. }
  81. }
  82. else
  83. {
  84. continue;
  85. }
  86. }
  87. else
  88. {
  89. break;
  90. }
  91. }
  92. }
  93. }

        图像数据的解析也非常简单,根据JPEG图像的特点,其开头为FFD8,结尾为FFD9,我们通过查找这两个标志位,其中间的数据就是整帧的图像数据,我们将其这些数据转化位Bitmap位图进行显示就可以了。显示效果如下图:

         上图中可以看出,OV2640采集到的图像数据,最终经过ESP8266发送到了上位机,并成功显示出来,只是受限于串口速率,大概3秒才会传输完一帧数据,在实际使用时,也可以发现,上位机数据缓冲容器时常都是空的,数据生产线程受限于串口速率,导致数据的生产远远小于数据的消耗,表现出来就是几秒才会刷新一帧数据。

六、结束:

        本文主要基于STM32、OV2640以及ESP8266完成图像的网络传输,本文受启发并参考了博文:ESP8266+STM32F407+OV7670实现图片传输,在此对该文作者表示深深的感谢。

        本文下位机图像数据采集以及上位机图像数据解析源码如下:

无线图传下位机源码https://download.csdn.net/download/sssxlxwbwz/85251144

无线图传上位机源码: https://download.csdn.net/download/sssxlxwbwz/85251105

        

        

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/46248
推荐阅读
相关标签
  

闽ICP备14008679号