ESP8266+STM32F407+OV7670实现图片传输 您所在的位置:网站首页 摄像头和网络连接方式 ESP8266+STM32F407+OV7670实现图片传输

ESP8266+STM32F407+OV7670实现图片传输

2024-06-03 00:56| 来源: 网络整理| 查看: 265

   声明:由于ESP8266与STM32之间采用串口进行通讯,导致传输速率较低,一到两秒才可以传输一帧图片,因此无法实现实时的图像显示。   本文虽然以串口通讯的方式进行数据传输,但是建议想要实现实时视频显示的朋友:想要提高无线传输的速率,必须放弃串口通讯的方式,采用如SPI或SDIO的通讯方式作为STM32单片机与ESP8266(或其他无线模块)进行通讯。 正文开始   1. 硬件结构

  本文使用到的硬件设备主要为STM32F407单片机、ESP8266无线WIFI模块、OV7670摄像头、LCD显示屏幕以及计算机。整体结构如下图所示:

在这里插入图片描述  上图中的LCD显示屏幕只是为了将OV7670采集到的图片在下位机进行显示,与图片传输没有什么关系,并且若增加LCD显示图像功能的话,会进一步影响图片传输的速率。因此不需要在下位机显示的朋友可以不用LCD显示屏幕,直接将图片数据通过ESP8266传输到上位机进行显示。

工作方式:

   此处对上图硬件结构中各个模块是如何工作的进行简单的介绍,首先OV7670摄像机模块在单片机的驱动下,采集图像,然后单片机通过串口将采集到的图像传输给ESP8266无线WIFI模块,该模块已经提前设置为无线透传模式(后文会介绍无线透传模式的设置方式),在透传模式下,ESP8266会将单片机串口发送来的所有数据,以无线的方式自动发送给与其连接好的上位机软件,上位机软件通过对接收到的图像数据进行处理,就能够显示出OV7670采集到的图片,本文使用到的上位机软件为C#语言编写,通过TCP/IP通讯方式与ESP8266进行通讯,并可以将传输到的数据处理成图片进行显示,后文会对上位机软件进行详细介绍。

  2. STM32驱动OV7670采集图片

  本文没有使用DCMI接口来驱动摄像机,而是使用IO口,实现OV7670图像数据的采集,而且其采集速率可以达到每秒24帧以上。以下为OV7670的IO口驱动代码:

/初始化GPIO u8 OV7670_Init(void) { u8 temp; u16 i=0; GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB\ |RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOE\ |RCC_AHB1Periph_GPIOG, ENABLE);//时钟使能 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;//D1 2 3 4 5 6 7数据线 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_Init(GPIOE, &GPIO_InitStructure); //IO口初始化 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //D0数据线 GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//VSYNC 中断输入线 GPIO_Init(GPIOA, &GPIO_InitStructure); /*output*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;//RRST及PCLK GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //WRST GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_15;//WEN及OE GPIO_Init(GPIOG, &GPIO_InitStructure); SCCB_Init(); //SCCB(与IIC类似)IO口初始化 if(SCCB_WR_Reg(0x12,0x80))return 1; //复位SCCB delay_ms(50); / //读取产品ID //读取产品型号 temp=SCCB_RD_Reg(0x0b); if(temp!=0x73)return 2; temp=SCCB_RD_Reg(0x0a); if(temp!=0x76)return 2; / //初始化序列 (该部分代码非常关键,也是整个底层驱动的核心。由厂家提供) for(i=0;i u32 j; u16 color; u8 re[640]={0}; u16 flag=0; if(ov_sta) { LCD_Scan_Dir(U2D_L2R); //LCD扫描方式 if(lcddev.id==0X1963)LCD_Set_Window((lcddev.width-240)/2,(lcddev.height-320)/2,240,320);//½«ÏÔʾÇøÓòÉèÖõ½ÆÁÄ»ÖÐÑë else if(lcddev.id==0X5510||lcddev.id==0X5310)LCD_Set_Window((lcddev.width-320)/2,(lcddev.height-240)/2,320,240);//½«ÏÔʾÇøÓòÉèÖõ½ÆÁÄ»ÖÐÑë LCD_WriteRAM_Prepare(); OV7670_RRST=0; //开始复位读指针 OV7670_RCK_L; OV7670_RCK_H; OV7670_RCK_L; OV7670_RRST=1; //复位读指针结束 OV7670_RCK_H; for(j=0;j Send_data_3(re); //使用串口3将数据传输给ESP8266,每次发送640个字节。 flag=0; } } ov_sta=0; //清除帧中断标志。 ov_frame++; LCD_Display_Dir(0); } } //LCD相关模式参数 const u8*LMODE_TBL[5]={"Auto","Sunny","Cloudy","Office","Home"}; const u8*EFFECTS_TBL[7]={"Normal","Negative","B&W","Redish","Greenish","Bluish","Antique"}; //7ÖÖÌØЧ int main() { VO7670_flag=0; SysTick_Init(168); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //ÖжÏÓÅÏȼ¶·Ö×é ·Ö2×é LED_Init(); KEY_Init(); USART3_Init(115200); USART1_Init(115200); LCD_Init(); //LCD初始化函数 POINT_COLOR=RED; //画笔颜色设置 LCD_ShowString(30, 70, 200, 16, 16, "OV7670 Test"); while(OV7670_Init()) { LCD_ShowString(30, 230, 200, 16, 16, "OV7670 Error!"); delay_ms(200); LCD_Fill(30,230,239,246,WHITE); delay_ms(200); } LCD_ShowString(30, 230, 200, 16, 16, "OV7670 OK!"); delay_ms(1500); OV7670_Light_Mode(0); OV7670_Color_Saturation(2); OV7670_Brightness(2); OV7670_Contrast(2); OV7670_Special_Effects(0); TIM4_Init(20000,7199); EXTI8_Init(); OV7670_Window_Set(12,176,240,320); OV7670_CS=0; LCD_Clear(BLACK); while(1) { if(VO7670_flag==1) //该参数是有上位机控制。可以去掉 { camera_refresh(); //更新显示图像或无线传输图像数据 VO7670_flag=0; } }   5. 上位机软件

   上位机主要用于与ESP8266进行无线连接,并将ESP8266发送来的图片数据重新编码为图片并进行显示,本文使用C#语言编写上位机软件,通过TCP/IP协议与ESP8266进行通讯,软件界面如下图所示。 在这里插入图片描述   软件界面中间黑色区域为图像显示区域,下方白色TextBox为数据发送区域,左侧TextBox为数据接收显示区域(程序设置为图片数据不显示)。由于下位机单片机采集到的图片数据格式为RGB565,因此上位机内部需要将接收到的RGB565格式的图像数据转化为RGB24,才可以正常将接收到的图片数据显示为图片。

   该上位机主要功能包括TCO/IP协议以及图像数据解码,具体代码如下所示:

private void Start_Click(object sender, EventArgs e) { panel1.BackColor = System.Drawing.Color.LightSkyBlue; panel4.BackColor = System.Drawing.Color.LightSkyBlue; panel3.BackColor = System.Drawing.Color.LightSteelBlue; //1.创建服务器端用于监听的SOCKET Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //2.绑定IP和端口 IPAddress ip = IPAddress.Parse(textBox_IP.Text); IPEndPoint ipandport = new IPEndPoint(ip, int.Parse(textBox_Port.Text)); serverSocket.Bind(ipandport); //3.开启监听 serverSocket.Listen(10); //使用线程池技术,服务器端接受客户端的连接 ThreadPool.QueueUserWorkItem(new WaitCallback(AcceptClientConnect), serverSocket); } /// /// 接受客户端的连接 /// /// public void AcceptClientConnect(object socket) { //传递过来的服务器端SOCKET Socket serverSocket = socket as Socket; this.AppendToMessage("服务器开始接受客户端的连接请求。"); //4.服务器端开始接受客户端的连接 //此处服务器端应该一直循环,用来一直接收可能的客户端。 while (true) { //创建代理Socket,用来与客户端进行通讯。 Socket proxsocket = serverSocket.Accept(); //将代理socker放到窗体级别的集合中 proxSocketList.Add(proxsocket); AppendToMessage(string.Format("客户端:{0}已经连接上。", proxsocket.RemoteEndPoint.ToString())); //ThreadPool.QueueUserWorkItem(new WaitCallback(ReceiveClientMessage),proxsocket); Thread MyThread = new Thread(new ParameterizedThreadStart(ReceiveClientMessage)); MyThread.IsBackground = true; MyThread.Start(proxsocket); Thread.Sleep(100); } } /// /// 接收客户端发送的消息 /// /// public void ReceiveClientMessage(object socket) { int flag= 0; //服务器端与客户端之间用于通讯的socket Socket proxSocket = socket as Socket; //开辟用来存储客户端发送来的数据的控件 byte[] dataClient = new byte[640*240]; proxSocket.ReceiveTimeout = 100 * 2; //设置接收数据时的阻塞时间为15秒钟 //接收客户端数据 while (true) //总共有240列数据,每列数据为640个字节,两个字节组成一个像素点。 { try { proxSocket.Receive(dataClient, 0, dataClient.Length, SocketFlags.None); } catch (SocketException e) { if (e.ErrorCode == 10060) { hang = 0; lie = 0; flag = 0; continue; } else { //客户端非正常退出。 proxSocketList.Remove(proxSocket); AppendToMessage(string.Format("客户端:{0}非正常退出。", proxSocket.RemoteEndPoint.ToString())); return; //线程终结 } } //绘制一列数据 if(flag hang = 0; lie = 0; flag = 0; } } } /// /// 根据接收的数组绘制一副图像 /// 注意:byte[] Data数据中的数据不一定为一副图像的完整数据 /// 若数据不完整,下次调用时将自动完成绘制 /// /// void Paint_bmp(byte[] Data) { int i = 0; //foreach (byte color in Data) for(i=0;i isheight = false; heightdate = Data[i]; } else { isheight = true; //若为低8位,则转化颜色,并写入bmp Color c = RGB565ToRGB24(heightdate, Data[i]); Write_Color(c); } } } /// /// 将一个16位的RGB565格式颜色转化成RGB24格式颜色,并返回Color类 /// /// /// /// Color RGB565ToRGB24(int RGB565_H, int RGB565_L) { int RGB565_MASK_RED = 0xF800; int RGB565_MASK_GREEN = 0x07E0; int RGB565_MASK_BLUE = 0x001F; int RGB565; int R, G, B; RGB565_H 11; G = (RGB565 & RGB565_MASK_GREEN) >> 5; B = (RGB565 & RGB565_MASK_BLUE); R return; } //使用委托在子线程操作主线程界面。 this.Invoke((EventHandler)(delegate { bmp.SetPixel(hang, lie, c); })); if (lie lie = 0; this.BeginInvoke((EventHandler)(delegate { pictureBox1.Image = bmp; })); if (hang hang = 0; } } }

   最终的显示结果如下图所示: 在这里插入图片描述    上图完成了最终的图像数据无线传输,并在上位机中完成了图像的解码,其中图像显示区域右侧黑色区域是因为图像显示区域设置的比真实图片的分辨率略大。后续可以进行改进。此外,由于单片机每发送一次数据,除原本的数据之后,貌似好有多余的几个字节的数据,导致图片显示出现些许差错,后续也可以进行改进。

上位机源代码:https://download.csdn.net/download/qq_37855507/16496001


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有