一、簡介:
????????本文利用STM32F407單片機(jī)、OV2640攝像機(jī)模塊以及ESP8266 WIFI模塊,并基于C#編寫的TCP上位機(jī)服務(wù),來實現(xiàn)圖像的無線傳輸。
????????本文受啟發(fā)于博客:ESP8266+STM32F407+OV7670實現(xiàn)圖片傳輸,在此感謝該文作者。與該文不同的是,本文采用的攝像機(jī)模塊是0V2640,傳輸?shù)臄?shù)據(jù)是壓縮之后的jpeg格式的圖像數(shù)據(jù),而不是像上文博主那樣,將RGB565數(shù)據(jù)直接傳輸?shù)缴衔粰C(jī)。此外,本文存在和上文博主同樣的問題,即采用串口傳輸方式,數(shù)據(jù)傳輸速率過低,實際應(yīng)用中,發(fā)現(xiàn)3秒左右才能夠傳出一幀320*240的圖片。本文接下來對整個圖傳功能中使用到的模塊一一進(jìn)行詳細(xì)介紹。
二、DCMI+OV2640攝像頭
? ? ? ? 在整個圖傳項目中,使用到的最核心的模塊就是OV2640模塊,?關(guān)于該模塊的介紹以及詳細(xì)的驅(qū)動,可以參考文章:STM32F4驅(qū)動OV2640攝像頭,該文詳細(xì)介紹了OV2640模塊、DMCI接口及整個OV2640的驅(qū)動。此處進(jìn)行簡要介紹:
? ? ? ? STM32F4驅(qū)動OV2640采用的是DCMI接口及DMA直接存儲器訪問,對于DMA的相關(guān)介紹可以參考文章:STM32:DMA。整個驅(qū)動流程是:STM32單片機(jī)通過DCMI接口,獲取OV2640攝像頭采集到的圖像數(shù)據(jù),并通過提前配置好的DMA數(shù)據(jù)流,將數(shù)據(jù)傳輸?shù)絃CD或內(nèi)部數(shù)組。如下圖所示,是通過DMA配置,將DCMI采集到的圖像數(shù)據(jù)傳輸?shù)絃CD顯示屏的效果:
? ? ? ? 關(guān)于LCD顯示屏的介紹及詳細(xì)驅(qū)動,可以參考博文:??STM32: LCD顯示。這樣,首先通過OV2640及LCD屏幕,將攝像頭模塊采集到的數(shù)據(jù)顯示在屏幕上。
?????????該部分的源碼:?OV2640驅(qū)動,雖然我們將OV2640采集到的圖像成功顯示在了LCD屏幕之上,但是,我們最終的目的是將圖像數(shù)據(jù)利用ESP8266模塊通過WIFI傳輸?shù)缴衔粰C(jī)或網(wǎng)頁中,因此,我們還需要ESP8266模塊。
三、ESP8266
?????????ESP8266是比較常見的WIFI模塊,該模塊有三種不同的工作模式,即softAP 模式,station 模式,softAP + station 共存模式。(SoftAP:即無線接入點,是一個無線網(wǎng)絡(luò)的中心節(jié)點,通常使用的無線路由器就是一個無線接入點;Station:即無線終端,是一個無線網(wǎng)絡(luò)的終端端。)
????????本文我們將ESP8266作為客戶端,并將其傳輸模式設(shè)置為透傳,讓其連接位于電腦的TCP上位機(jī)服務(wù)器,然后將OV2640采集的圖像數(shù)據(jù)通過單片機(jī)傳輸給ESP8266模塊,并采用WIFI發(fā)送給上位機(jī)。ESP8266的配置過程如下:
- AT+RESTORE? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #恢復(fù)出廠設(shè)置
- AT+CWMODE=1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #設(shè)置ESP8266工作模式為STA
- AT+RST? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #復(fù)位
- AT+CWJAP="路由器賬號","密碼"? ? ? ? ? ? ? ? ? ? ? ? ?#連接路由器
- AT+CIPMODE=1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #設(shè)置透傳模式
- AT+CIPSTART="TCP","192.168.6.117",8266 ? ? ?#連接TCP服務(wù)器(上位機(jī))
- AT+CIPSEND? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#開啟透傳
? ? ? ? 此處要注意,本文的ESP8266是通過AT指令進(jìn)行配置的,因此要保證ESP8266已經(jīng)燒錄了AT固件庫,一般網(wǎng)上買的ESP8266模塊默認(rèn)會燒錄AT固件庫,如若沒有,可以自行進(jìn)行燒錄。
四、工作原理
? ? ? ? 基本的模塊介紹已經(jīng)完成,通過模塊介紹,我們也可以發(fā)現(xiàn),無線圖傳模塊的工作原理是:首先我們利用STM32和OV2640模塊,采集圖像數(shù)據(jù),然后,我們配置好ESP8266,讓其連接上位機(jī)服務(wù)器,然后,我們通過串口,將STM32采集到的圖像數(shù)據(jù)傳輸給ESP8266,由于ESP8266配置的是透傳模式,因此其會將STM32通過串口發(fā)送過來的數(shù)據(jù)原封不動的通過WIFI發(fā)送給上位機(jī),上位機(jī)在將這些圖像數(shù)據(jù)解析為圖像顯示出來就可以了,如下圖所示:
? ? ? ? ?上文中,我們介紹OV2640模塊時,將圖像顯示在LCD屏幕上,但是,我們真正的目的,是將數(shù)據(jù)通過ESP8266模塊傳輸給上位機(jī)。其實這兩者本質(zhì)上是一樣的,一個是將數(shù)據(jù)通過DMA傳輸給LCD屏幕,而另外一個則是將數(shù)據(jù)傳輸給串口(因為ESP8266和STM32是通過串口連接的)。
核心傳輸代碼如下:
//處理JPEG數(shù)據(jù)
//當(dāng)采集完一幀JPEG數(shù)據(jù)后,調(diào)用此函數(shù),切換JPEG BUF.開始下一幀采集.
void jpeg_data_process(void)
{
if(ov2640_mode)//只有在JPEG格式下,才需要做處理.
{
if(jpeg_data_ok==0) //jpeg數(shù)據(jù)還未采集完?
{
DMA_Cmd(DMA2_Stream1, DISABLE);//停止當(dāng)前傳輸
while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}// 等待DMA2_Stream1可配置
jpeg_data_len=jpeg_buf_size-DMA_GetCurrDataCounter(DMA2_Stream1);// 得到此次數(shù)據(jù)傳輸?shù)拈L度
jpeg_data_ok = 1; // 數(shù)據(jù)已經(jīng)采集完成,等待被處理
}
if(jpeg_data_ok==2) //上一次的jpeg數(shù)據(jù)已經(jīng)被處理了
{
DMA2_Stream1->NDTR=jpeg_buf_size;
DMA_SetCurrDataCounter(DMA2_Stream1,jpeg_buf_size);//傳輸長度為jpeg_buf_size*4字節(jié)
DMA_Cmd(DMA2_Stream1, ENABLE); //重新傳輸
jpeg_data_ok=0; //標(biāo)記數(shù)據(jù)未采集
}
}
}
//jpeg模式
void jpeg_test(void)
{
u32 i,jpgstart,jpglen;
u8 headok=0;
u8 *p;
u8 effect=0,saturation=2,contrast=2;
u8 size=3;
u8 msgbuf[15]; //消息緩存區(qū)
//uart4初始化
LTE_uart3_init(115200);
LCD_Clear(WHITE);
POINT_COLOR=RED;
LCD_DisplayString(30,60,24,"ALIENTEK STM32F4");
LCD_DisplayString(30,90,24,"OV2640 JPEG Mode");
OV2640_JPEG_Mode(); // JPEG模式
My_DCMI_Init(); //DCMI配置
DCMI_DMA_Init((u32)&jpeg_buf,jpeg_buf_size,DMA_MemoryDataSize_Word,DMA_MemoryInc_Enable);//DCMI配置 輸出到數(shù)組
OV2640_OutSize_Set(jpeg_img_size_tbl[size][0], jpeg_img_size_tbl[size][1]);
DCMI_Start(); //啟動傳輸
delay_ms(500);
while(1)
{
if(jpeg_data_ok==1) // 已經(jīng)采集完成一幀圖像,開始處理數(shù)據(jù)
{
p=(u8*)jpeg_buf;
LCD_DisplayString(30,150,24,"Sending JPEG data...");
jpglen=0; //設(shè)置jpg文件大小為0
headok=0; //清除jpg頭標(biāo)記
for(i=0; i<jpeg_data_len*4; i++)
{
//查找OXFF,OXD8和0XFF,0XD9,獲取jpg文件大小
if((p[i]==0XFF)&&(p[i+1]==0XD8)){
jpgstart=i;
headok=1; //標(biāo)記找到j(luò)pg頭(FF D8)
}
if((p[i]==0XFF)&&(p[i+1]==0XD9)&&headok)//找到頭以后,再找FF D9
{
jpglen=i-jpgstart+2;
break;
}
}
if(jpglen) // 正常的jpeg數(shù)據(jù)
{
p+=jpgstart;
for(i=0;i<jpglen;i++) //發(fā)送整個jpg文件
{
while(USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET); //循環(huán)發(fā)送,直到發(fā)送完畢
USART_SendData(USART3, p[i]);
}
}
jpeg_data_ok = 2; //標(biāo)記jpeg數(shù)據(jù)處理完了,可以讓DMA去采集下一幀了.
delay_ms(2000);
}
}
}
? ? ? ? 上述核心代碼其實是兩個函數(shù),函數(shù)jpeg_data_process()在幀中斷中被調(diào)用,也就是說,每次采集完一幀圖像,該函數(shù)都會被調(diào)用。在該函數(shù)中,主要是修改標(biāo)志jpeg_data_ok 為1,以便另外一個函數(shù)jpeg_test()可以對這一幀數(shù)據(jù)進(jìn)行處理,所謂的處理在此處其實就是將數(shù)據(jù)通過串口發(fā)送給ESP8266,ESP8266模塊會將數(shù)據(jù)發(fā)送給上位機(jī),并由上位機(jī)解析然后顯示。
五、TCP服務(wù)上位機(jī)
? ? ? ? ESP8266在拿到STM32通過串口傳輸?shù)膱D像數(shù)據(jù)之后,會將其發(fā)送給上位機(jī),那么ESP8266和上位機(jī)之間是如何通訊的呢?是通過TCP/IP協(xié)議。
? ? ? ? 本文使用C#語言,基于TCP/IP協(xié)議,寫了一個簡單的上位機(jī)服務(wù),該服務(wù)接受ESP8266的連接,并將其發(fā)送過來的數(shù)據(jù)編碼為圖像進(jìn)行顯示。由于軟件寫的很簡單,因此很多功能并沒有進(jìn)行擴(kuò)充實現(xiàn),如其只支持一個設(shè)備的圖傳,后續(xù)可以進(jìn)行升級改進(jìn)。軟件界面如下所示:
? ? ? ? 該軟件的實現(xiàn)思路非常簡單,因此其功能也比較粗狂,bug較多。
? ? ? ? 軟件整體包含兩條主要的線程以及一個數(shù)據(jù)緩沖容器(生產(chǎn)者消費者模式)。在服務(wù)器啟動之后,軟件開啟監(jiān)聽服務(wù),監(jiān)聽ESP8266的連接請求,并開啟一個消費線程,用于從緩沖容器中獲取圖像數(shù)據(jù)(此時應(yīng)該沒有數(shù)據(jù)),在服務(wù)與ESP8266建立連接之后,就開始接收來自ESP8266的圖像數(shù)據(jù),此時,創(chuàng)建一個數(shù)據(jù)生產(chǎn)線程,將服務(wù)器接收到的數(shù)據(jù)存放到緩沖容器中,這樣,這兩個線程一個專門負(fù)責(zé)將接收到的數(shù)據(jù)放到緩沖容器中,一個專門負(fù)責(zé)從緩沖容器中獲取數(shù)據(jù)并將其解碼為圖像在界面顯示。
上位機(jī)核心代碼:
/// <summary>
/// 接收客戶端發(fā)送的圖像數(shù)據(jù),并放到緩沖容器中。
/// </summary>
/// <param name="proxsocket"></param>
public void ReceiveClientMessage(object socket)
{
int imageCount = 0;
//服務(wù)器端與客戶端之間用于通訊的socket
Socket proxSocket = socket as Socket;
//開辟用來存儲客戶端發(fā)送來的數(shù)據(jù)的控件
byte[] dataClient = new byte[640 * 240];
proxSocket.ReceiveTimeout = 1000 * 300; // 設(shè)置接收數(shù)據(jù)時的阻塞時間為15秒鐘
//接收客戶端數(shù)據(jù)
while (serverStart)
{
StringBuilder imageData = new StringBuilder();
try
{
while (imageCount < 5)
{
int countRev = proxSocket.Receive(dataClient, 0, dataClient.Length, SocketFlags.None);
string revDate = StringUtils.ToHexStrFromByte(dataClient, countRev);
if (revDate.Length>0)
{
imageData.Append(revDate);
imageCount++;
AppendToMessage(revDate);
}
}
jpegQueue.Add(imageData.ToString());
imageCount = 0;
}
catch (SocketException e)
{
if (e.ErrorCode == 10060)
{
continue;
}
else
{
//客戶端非正常退出。
AppendToMessage(string.Format("客戶端:{0}非正常退出。", proxSocket.RemoteEndPoint.ToString()));
return; //線程終結(jié)
}
}
}
}
/// <summary>
/// 從容器中獲取圖形數(shù)據(jù),并解析為圖像顯示
/// </summary>
private void createImg()
{
string imgData = "";
while (serverStart)
{
string imgListData = jpegQueue.Take();
imgData = imgData + imgListData;
while (imgData.IndexOf("FF D8")!=-1)
{
int startIndex = imgData.IndexOf("FF D8");
int endIndex = imgData.IndexOf("FF D9");
if (endIndex != -1)
{
string jpeg = imgData.Substring(startIndex,endIndex+5-startIndex);
imgData = imgData.Substring(endIndex+5);
Bitmap imageBitmap = StringUtils.GetJpegImage(jpeg.Replace(" ", ""));
if (imageBitmap != null)
{
Image img = Image.FromHbitmap(imageBitmap.GetHbitmap());
if (this.JpgImage.InvokeRequired)
{
JpgImage.Invoke(new Action<Image>(s =>
{
JpgImage.Image = s;
}), img);
}
else
{
this.JpgImage.Image = img;
}
}
else
{
continue;
}
}
else
{
break;
}
}
}
}
? ? ? ? 圖像數(shù)據(jù)的解析也非常簡單,根據(jù)JPEG圖像的特點,其開頭為FFD8,結(jié)尾為FFD9,我們通過查找這兩個標(biāo)志位,其中間的數(shù)據(jù)就是整幀的圖像數(shù)據(jù),我們將其這些數(shù)據(jù)轉(zhuǎn)化位Bitmap位圖進(jìn)行顯示就可以了。顯示效果如下圖:
? ? ? ? ?上圖中可以看出,OV2640采集到的圖像數(shù)據(jù),最終經(jīng)過ESP8266發(fā)送到了上位機(jī),并成功顯示出來,只是受限于串口速率,大概3秒才會傳輸完一幀數(shù)據(jù),在實際使用時,也可以發(fā)現(xiàn),上位機(jī)數(shù)據(jù)緩沖容器時常都是空的,數(shù)據(jù)生產(chǎn)線程受限于串口速率,導(dǎo)致數(shù)據(jù)的生產(chǎn)遠(yuǎn)遠(yuǎn)小于數(shù)據(jù)的消耗,表現(xiàn)出來就是幾秒才會刷新一幀數(shù)據(jù)。
六、結(jié)束:
? ? ? ? 本文主要基于STM32、OV2640以及ESP8266完成圖像的網(wǎng)絡(luò)傳輸,本文受啟發(fā)并參考了博文:ESP8266+STM32F407+OV7670實現(xiàn)圖片傳輸,在此對該文作者表示深深的感謝。
? ? ? ? 本文下位機(jī)圖像數(shù)據(jù)采集以及上位機(jī)圖像數(shù)據(jù)解析源碼如下:
無線圖傳下位機(jī)源碼:https://download.csdn.net/download/sssxlxwbwz/85251144
無線圖傳上位機(jī)源碼:?https://download.csdn.net/download/sssxlxwbwz/85251105
????????文章來源地址http://www.zghlxwxcb.cn/news/detail-801644.html文章來源:http://www.zghlxwxcb.cn/news/detail-801644.html
????????
到了這里,關(guān)于基于STM32、OV2640及ESP8266的無線圖傳的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!