一、整体架构

STM32使用DCMI驱动OV2640,DMA通道获取图像数据,然后通过以太网将数据发送至PC端,PC端通过socket接收数据,并用BufferedImage将其显示。(PC端使用的java)

二、具体实现

1、STM32驱动OV2640

stm32f4系列自带数字摄像头接口(DCMI),所以驱动OV2640会比较方便。下面是关于DCMI的配置:

GPIO_InitTypeDef  GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;/**时钟初始化**/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI,ENABLE);/**所需IO口的初始化**/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_DCMI); //PA4,AF13  DCMI_HSYNC
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_DCMI); //PA6,AF13  DCMI_PCLK
GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_DCMI); //PB7,AF13  DCMI_VSYNC
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_DCMI); //PC6,AF13  DCMI_D0
GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_DCMI); //PC7,AF13  DCMI_D1
GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_DCMI); //PC8,AF13  DCMI_D2
GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_DCMI); //PC9,AF13  DCMI_D3
GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_DCMI);//PC11,AF13 DCMI_D4
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_DCMI); //PB6,AF13  DCMI_D5
GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_DCMI); //PE5,AF13  DCMI_D6
GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_DCMI); //PE6,AF13  DCMI_D7/**DCMI配置**/
DCMI_DeInit();
DCMI_InitStructure.DCMI_CaptureMode=DCMI_CaptureMode_Continuous;   //连续模式
DCMI_InitStructure.DCMI_CaptureRate=DCMI_CaptureRate_All_Frame;    //全帧捕获
DCMI_InitStructure.DCMI_ExtendedDataMode= DCMI_ExtendedDataMode_8b;    //8位数据格式
DCMI_InitStructure.DCMI_HSPolarity= DCMI_HSPolarity_Low;    //HSYNS低电平有效
DCMI_InitStructure.DCMI_PCKPolarity= DCMI_PCKPolarity_Rising;    //PLCK上升沿有效
DCMI_InitStructure.DCMI_SynchroMode= DCMI_SynchroMode_Hardware;    //硬件同步HSYNC,VSYNC
DCMI_InitStructure.DCMI_VSPolarity=DCMI_VSPolarity_Low;    //VSYNC低电平有效
DCMI_Init(&DCMI_InitStructure);
DCMI_ITConfig(DCMI_IT_FRAME,ENABLE);
DCMI_Cmd(ENABLE);/**DCMI中断配置**/
NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

2、视频数据传输

DCMI得到数据后,通过DMA通道运送至内存,并通过以太网发送。这里DMA采用双缓存区的模式,即DMA往buf1里面写数据的时候,CPU发送buf2的数据,然后DMA往buf2写数据,CPU发送buf1,这样能增加数据的发送效率并且防止数据丢失。

DMA相关配置:

DCMI_DMA_Init((u32)jpeg_buf0,(u32)jpeg_buf1,jpeg_dma_bufsize,DMA_MemoryDataSize_Word,DMA_MemoryInc_Enable);void DCMI_DMA_Init(u32 DMA_Memory0BaseAddr,u32 DMA_Memory1BaseAddr,u16 DMA_BufferSize,u32 DMA_MemoryDataSize,u32 DMA_MemoryInc)
{DMA_InitTypeDef  DMA_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);DMA_DeInit(DMA2_Stream1);while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}/**配置DMA**/DMA_InitStructure.DMA_Channel = DMA_Channel_1;DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR;    //外设地址为DCMI->DRDMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr;    //内存数组buf1地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;    //外设到存储器模式DMA_InitStructure.DMA_BufferSize = DMA_BufferSize;    //数据传输量DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;    //外设数据长度 32位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;    //循环模式DMA_InitStructure.DMA_Priority = DMA_Priority_High;DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;      DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;DMA_Init(DMA2_Stream1, &DMA_InitStructure);/**双缓存区模式**/if(DMA_Memory1BaseAddr){DMA_DoubleBufferModeCmd(DMA2_Stream1,ENABLE);DMA_MemoryTargetConfig(DMA2_Stream1,DMA_Memory1BaseAddr,DMA_Memory_1);   //内存数组buf2地址DMA_ITConfig(DMA2_Stream1,DMA_IT_TC,ENABLE);NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure);}
}

DMA中断函数:

/**
jpeg_dma_bufsize :5x1024
jpeg_buf0,jpeg_buf1:u32指针变量,申请内存空间 jpeg_dma_bufsize * 4
jpeg_data_buf0,jpeg_data_buf1: u8指针变量,申请内存空间 jpeg_dma_bufsize * 4
**/void DMA2_Stream1_IRQHandler(void)
{if(DMA_GetFlagStatus(DMA2_Stream1,DMA_FLAG_TCIF1)==SET)    //传输完成标志{DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF1);    //清除中断标志位jpeg_dcmi_rx_callback();}
}  void jpeg_dcmi_rx_callback(void)
{if(DMA2_Stream1->CR&(1<<19))  //buf0已经写好{ if(Dma_Recv_Buf0_Flag)  //判断jpeg_data_buf0是否已存储数据{}else  //如果jpeg_data_buf0没有存储视频数据,则把buf0的数据拷贝至jpeg_data_buf0,并将Dma_Recv_Buf0_Flag置位,通知CPU jpeg_data_buf0数组有数据需要发送{memcpy(jpeg_data_buf0, jpeg_buf0, jpeg_dma_bufsize*4);Dma_Recv_Buf0_Flag = 1;}}else  //否则就是buf1已经写好{if(Dma_Recv_Buf1_Flag)  //判断jpeg_data_bu1里面是否已存储数据{}else  //如果jpeg_data_buf1没有存储视频数据,则把buf1的数据拷贝至jpeg_data_buf1,并将Dma_Recv_Buf1_Flag置位,通知CPU jpeg_data_buf1数组有数据需要发送{memcpy(jpeg_data_buf1, jpeg_buf1, jpeg_dma_bufsize*4);Dma_Recv_Buf1_Flag = 1;}}
}

CPU发送数据:

void tcp_send_msg(uint8_t *str, uint16_t strlen)
{while(data_send_over_flag != 0);  //每次发送数据后,需要等待PC回复,将data_send_over_flag 清零,才可以再次发送TCP_Client_Send_Data(pcb,str,strlen);data_send_over_flag = 1;networkspeed = networkspeed  + strlen;
}while(1)
{if(Dma_Recv_Buf0_Flag)  //Dma_Recv_Buf0_Flag标志置位,说明jpeg_data_buf0有需要发送的数据{x = 0;  total_buf_len = jpeg_dma_bufsize*4 ;  //需要发送的数据量while(total_buf_len > 0){if(total_buf_len > ONE_FRAME_SIZE){tcp_send_msg(&jpeg_data_buf0[x*ONE_FRAME_SIZE], ONE_FRAME_SIZE);  //TCP发送total_buf_len = total_buf_len - ONE_FRAME_SIZE;x++;}else{tcp_send_msg(&jpeg_data_buf0[ONE_FRAME_SIZE*x], total_buf_len);break;  //发送完毕,跳出while循环}}Dma_Recv_Buf0_Flag = 0;  //Dma_Recv_Buf0_Flag清零,当DMA有数据来时,jpeg_data_buf0可存储新的数据}if(Dma_Recv_Buf1_Flag)  //同上,处理jpeg_data_buf1的数据{x = 0;  total_buf_len = jpeg_dma_bufsize*4 ;  //需要发送的数据量while(total_buf_len > 0){if(total_buf_len > ONE_FRAME_SIZE){tcp_send_msg(&jpeg_data_buf1[x*ONE_FRAME_SIZE], ONE_FRAME_SIZE);  //TCP发送total_buf_len = total_buf_len - ONE_FRAME_SIZE;x++;}else{tcp_send_msg(&jpeg_data_buf1[ONE_FRAME_SIZE*x], total_buf_len);break;  //发送完毕,跳出while循环}}Dma_Recv_Buf1_Flag = 0;  //Dma_Recv_Buf1_Flag清零,当DMA有数据来时,jpeg_data_buf1可存储新的数据}
}

3、PC端接收及显示 

PC端通过socket接收stm32发送的数据。在接收时,现将收到的数据输入至一个字节数组缓冲区(ByteArrayOutputStream),当识别到jpg图像结束标志符(0xFF,0xD9)时,将字节数组缓冲区的数据全部拷贝至新创建的一个字节数组tempbuffer中,然后再将新创建的字节数组tempbuffer转换为输入流InputStream,通过ImageIO.read(InputStream),将值赋值给BufferedImage,实现字节数据更新至jpg图片内存中,调用repaint()方法实时显示stm32发送的图片。

/**建立窗体,创建socket server**/
public class PngServer{public static void CreatServer(){ServerSocket serverSocket;Socket socket;ImagePanel dsplayImagePanel = new ImagePanel();JFrame frame = new JFrame();  //创建一个窗口用来显示图片frame.setTitle("JavaFrame");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setSize(800, 600);frame.add(dsplayImagePanel);frame.setVisible(true);try {serverSocket =new ServerSocket(2041);  //监听2041while(true){socket = serverSocket.accept();System.out.println(socket.getRemoteSocketAddress());clientProcess newClient = new clientProcess(socket, dsplayImagePanel);socket.close();}}catch(IOException e) {System.out.println(e);}}public static void main(String[] args) {System.out.println("PngServer:");new MyThread().start();CreatServer();}
}/**刷新图片并显示**/
class ImagePanel extends JPanel{private BufferedImage image;public ImagePanel() {  //构造函数,打开一张图并显示try {image = ImageIO.read(new File("Image/Temp.png"));} catch (IOException ex) {// handle exception...}}/**更新图片的方法**/public void exchangeImage(InputStream in) {try {image = ImageIO.read(in);  //更新内存中图片数据repaint();  //重新显示} catch (IOException ex) {// handle exception...}}/**显示图片**/@Overridepublic void paintComponent(Graphics g) {g.drawImage(image, 0, 0, null); }
}/**socket接收数据并处理**/
class clientProcess{Socket clientSocket;ImagePanel clientImagePanel;InputStream clientIs;OutputStream clientOs;static int frame = 0;static int netWorkSpeed = 0;/**构造函数,保存连接的socket对象信息,以及初始化一些变量**/clientProcess(Socket socket, ImagePanel ImagePanel){clientSocket = socket;clientImagePanel = ImagePanel;frame = 0;recvProcess();}/**数据接收并处理**/void recvProcess(){int count = 0;int i = 0;byte[] buffer = new byte[2048];int getPicFlag = 0;ByteArrayOutputStream StreamArray = new ByteArrayOutputStream();try {clientIs = clientSocket.getInputStream();clientOs = clientSocket.getOutputStream();while(true){count=clientIs.read(buffer);if(count == -1){System.out.println("Client Disconnect.........");break;}else{if(getPicFlag == 1){for(i = 0; i < count -1; i++){if((buffer[i]==-1)&&(buffer[i+1]==-39)) //-1,-39为十六进制的0xFF,0xD9,为jpg图像数据的结束标志{StreamArray.write(buffer,0,i+2);byte[] tempbuffer =  StreamArray.toByteArray();InputStream ImageStream = byteTostream(tempbuffer);clientImagePanel.exchangeImage(ImageStream); StreamArray.close();ImageStream.close(); StreamArray = new ByteArrayOutputStream();frame++;    //计算fpsgetPicFlag = 0;}}if(getPicFlag == 1){StreamArray.write(buffer,0,count);}}if(getPicFlag == 0){for(i = 0; i < count -1; i++){if((buffer[i] == -1) && (buffer[i+1] == -40))  //-1,-40为十六进制的0xFF,0xD8,为jpg图像数据的起始标志{getPicFlag = 1;StreamArray.write(buffer,i,count - i);}}}buffer[0] = 'O';buffer[1] = 'K';clientOs.write(buffer, 0, 2);  //回复stm32,开始发下一个数据包netWorkSpeed = netWorkSpeed + count/1024;  //计算接收速率的}}} catch (IOException e) {// TODO Auto-generated catch blockSystem.out.println(e);e.printStackTrace();try {clientIs.close();clientOs.close();clientSocket.close();} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}}}public static int getFrame(){return frame;}public static void clearFrame(){frame = 0;}public static int getNetWorkSpeed(){return netWorkSpeed;}public static void clearNetWorkSpeed(){netWorkSpeed = 0;}public static final InputStream byteTostream(byte[] buf) {  return new ByteArrayInputStream(buf);  }
}/**新建一个线程定时计算网速以及帧率**/
class MyThread extends Thread{public void run(){while(true){try {sleep(1000);System.out.println("Frame  " + clientProcess.getFrame() + "fps/s" + "  NetWork:" + clientProcess.getNetWorkSpeed() + " Kb/s");clientProcess.clearFrame();clientProcess.clearNetWorkSpeed();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}

三、总结

1、在摄像头采集帧率:15 fps/s,  采集尺寸:800*600的情况下,不丢失数据不失帧。

源工程及代码下载链接:https://pan.baidu.com/s/15t_0FZ79znMOoFgWp8-P4g  提取码:t5q0

STM32F407传输OV2640视频数据并在PC端显示相关推荐

  1. 网页显示不全(运行时网页pc端显示完全,手机端显示不全)

    故障情况:   网页css编写无误,项目运行时,网页在pc端显示完全,但是当在手机端的屏幕宽度小于1080时,会出现右上角变成白色,可以交互,但不能正确显示 解决方法:  在该页面的 <head ...

  2. 朋友圈点赞,微信发红包,视频的播放,Pc端微信加群,app端微信加群的测试点

    测试点经典例题*** 朋友圈点赞点用例的测试点: 1.是否可以点赞 2. 是否可以取消点赞 3. 多次点赞会出现什么情况 4. 多人点赞时显示是否按照时间进行排序 5. 点赞会不会提示发圈人 6. 取 ...

  3. vue 移动端 div背景图片 pc端显示 移动端不显示

    最近碰到个需求,移动端首页表头,需要添加背景图片,写好之后在pc端正常显示没有问题,发布到测试服务器之后找不到图片,一开始以为是图片路径写的有问题,修改之后还是不行,废话不多说直接上代码. style ...

  4. websocket传输canvas图像数据给C++服务端opencv图像实现web在线实时图像处理

    为什么80%的码农都做不了架构师?>>>    前后端的耦合想了很久,上下课都在思考怎么做,然后终于憋出来了.这是之前搞的一个视觉计算的项目,boss叫对接到前端,于是就产生了这样一 ...

  5. 天刀手游pc端显示服务器维护,天刀手游电脑版无法更新网络修复工具

    天涯明月刀手游电脑版更新失败修复工具是一款可以解决天涯明月刀手游电脑版更新失败问题的工具,最近很多玩家反映自己的游戏更新后无法打开,大家可以利用这款天涯明月刀手游电脑版更新失败修复工具修复网络来解决这 ...

  6. 1、树莓派使用FFMPEG推流到PC端显示

    基本思想:最近思索做个产品,因此前期调研相关技术,准备开干,,呦呦呦 http://dranger.com/ffmpeg/tutorial01.html 1:虚拟机设置USB3.1 2:虚拟机设置摄像 ...

  7. 修改adb shell在pc端显示的名称

    点击打开链接 软件:android4.4,硬件:lc1860evb2 通常adb shell之后,输入busybox vi /system/build.prop 修改ro.product.device ...

  8. 手把手教你爬取PC端『某酷视频』个人历史播放数据,并可视化展示

    大家好,我是阿辰,今天手把手教你爬取PC端『某酷视频』个人历史播放数据,并可视化展示 上次有粉丝说,那个是ios手机,安卓手机现在需要root权限才可以安装证书,那么今天就不以手机为例,以电脑PC端为 ...

  9. 多路视频数据实时采集系统设计与实现

    多路视频数据实时采集系统设计与实现 常永亮   王霖萱  常馨蓉 摘要 面对越来越多的实时视频采集.播放的应用,如何能更加方便的操控视频采集,保证流畅的播放效果,成为近几年实时媒体流的一个重要研究方向 ...

最新文章

  1. 机器学习笔记:Adam
  2. 迭代Iterator的用法
  3. 为什么选择Nginx
  4. 你真的会用ABAP, Java和JavaScript里的constructor么?
  5. 【完整目录】每天5分钟用C#学习数据结构
  6. 一个Python爬虫案例让你看清Python2和3之间的区别
  7. 点对点借贷dApp Yield宣布流动性激励迁移至SushiSwap
  8. 学习分析技术【02】--社交网络分析
  9. Microsoft Visual C++ 14.0 is required. Get it with “Microsoft Visual C++ Build Tools”:报错解决
  10. 炼丹笔记三:数据增强
  11. 访问不了共享文件夹提示“网络错误“的解决方法
  12. Silverlight Blend动画设计系列五:故事板(StoryBoards)和动画(Animation)
  13. 致80后的北漂IT人:未来在哪儿?
  14. 用canvas写 看你有多色 游戏
  15. 用CSS美化你的HTML
  16. 期末总结---为本学期画上圆满的句号
  17. windows蓝屏代码含意全集
  18. Android拍照和相册+系统裁剪功能返回图片
  19. gensim bm25模型保存与加载
  20. color-scheme 属性根据操作系统配色方案调整页面样式

热门文章

  1. 郑州大学计算机新媒体专业介绍,专业介绍:网络与新媒体专业
  2. git prune 相关
  3. centos6.8经典实用大全、教程
  4. 计算机视觉中的双目立体视觉和体积度量
  5. win10清理_别人都说win10不需要装电脑管家,那电脑产生的垃圾该怎么清理呢
  6. Oracle增删改查
  7. 犹豫两年,我还是重回大厂996了:还是得先搞钱
  8. 抓虾的告别信RSS即将退出舞台
  9. BlumNet: Graph Component Detection for Object Skeleton Extraction阅读笔记
  10. python爬取微博博主历史博文存入Excel