教程不断更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429
第56章 emWin6.x的摄像头OV7670动态图形显示
本章节为大家讲解emWin中实现摄像头图形的动态展示。
56.1 初学者重要提示
56.2 第1步,开辟存储设备
56.3 第2步,摄像头DMA传输完整中断发消息
56.4 第3步,窗口回调函数里面绘制图像并开启下次传输
56.5 实验例程说明(RTOS)
56.6 实验例程说明(裸机)
56.7 总结
56.1 初学者重要提示
- 本章配套了320*240和640*480两种分辨率图像显示案例。
- emWin中实现摄像头动态图像展示的关键是开辟一个存储设备,每次摄像头采集的一帧数据通道DMA传输到缓冲里面后,将其通过存储设备绘制到emWin里面。
56.2 第1步,开辟存储设备
我们这里以640*480分辨率为例进行说明,实现代码如下:
/* ********************************************************************************************************* * 函 数 名: MainTask * 功能说明: GUI主函数 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ void MainTask(void) { //省略未写 /* 创建存储设备,用于加载摄像头数据 */ hMem = GUI_MEMDEV_CreateEx(0, 0, 640, 480, GUI_MEMDEV_NOTRANS); uiDispMemAddr = (uint32_t)GUI_MEMDEV_GetDataPtr(hMem); /* 屏幕显示后点亮,有效防止瞬间高亮 */ GUI_Delay(200); LCD_SetBackLight(255); /* 初始摄像头 */ bsp_InitCamera(); CAM_Start(uiDispMemAddr); /* 临时加载个红色背景 */ GUI_MEMDEV_Select(hMem); GUI_SetBkColor(GUI_RED); GUI_Clear(); GUI_MEMDEV_Select(0); //省略未写 }
- 通过函数GUI_MEMDEV_CreateEx创建摄像头所需的缓冲器,我们这里是640*480分辨率,所以存储设备也创建这么大即可。
- 函数bsp_InitCamera初始化摄像头,然后函数CAM_Start先启动一次传输。
- 函数GUI_MEMDEV_Select实现一个临时的绘制,方便查看是否申请存储设备成功。
56.3 第2步,摄像头DMA传输完成中断发消息
一帧640*480图像传输完毕后,DMA中断实现代码如下:
/* ********************************************************************************************************* * 函 数 名: CAM_Stop * 功能说明: 停止DMA和DCMI * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ #include "MainTask.h" void CAM_Stop(void) { HAL_DCMI_Stop(&hdcmi); } void DMA1_Stream7_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_dcmi); } void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { /* 关闭摄像 */ CAM_Stop(); WM_SendMessageNoPara(hWinMainTask, MSG_CAMERA); g_tCam.CaptureOk = 1; /* 表示DMA传输结束 */ }
- 上面代码中红色代码是关键,这里是发送自定义消息给emWin的窗口回调函数。
56.4 第3步,窗口回调函数里面绘制图像并开启下次传输
代码实现如下:
/* ********************************************************************************************************* * 函 数 名: CAM_Stop * 功能说明: 停止DMA和DCMI * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ #include "MainTask.h" void CAM_Stop(void) { HAL_DCMI_Stop(&hdcmi); } void DMA1_Stream7_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_dcmi); } void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi) { /* 关闭摄像 */ CAM_Stop(); WM_SendMessageNoPara(hWinMainTask, MSG_CAMERA); g_tCam.CaptureOk = 1; /* 表示DMA传输结束 */ } /* ********************************************************************************************************* * 函 数 名: _cbDialog * 功能说明: 对话框回调函数 * 形 参: pMsg 回调参数 * 返 回 值: 无 ********************************************************************************************************* */ static void _cbDialog(WM_MESSAGE * pMsg) { static WM_HTIMER hTimerCAMERA; switch (pMsg->MsgId) { /* 接收到摄像头数据 */ case MSG_CAMERA: hTimerCAMERA = WM_CreateTimer(pMsg->hWin, ID_TimerCAMERA, 2, 0); break; case WM_TIMER: /* 删除定时器 */ WM_DeleteTimer(hTimerCAMERA); /* 选择操作窗口 */ WM_SelectWindow(hWinMainTask); /* Cache Clean和无效化 */ SCB_CleanInvalidateDCache(); /* 绘制到多缓冲里面 */ GUI_MULTIBUF_Begin(); //g_tCam.CaptureOk = 0; GUI_MEMDEV_WriteAt(hMem, 0, 0); GUI_MULTIBUF_End(); WM_SelectWindow(WM_HBKWIN); /* 开始下次绘制 */ CAM_Start1(uiDispMemAddr); break; default: WM_DefaultProc(pMsg); break; } }
- 窗口回调消息MSG_CAMERA的处理比较关键, 其实是可以直接在这个消息里处理,但是这个里面直接处理,三缓冲效果就无法正常执行了,也就是图形会有撕裂,所以我们这里简单开个单次定时器,在定时器消息里面执行图形绘制。
- 定时器消息里面有个三个知识点:
- WM_SelectWindow实现将图像显示到本窗口上。在WM_PAINT消息里面的话,就不需要调用此函数了,其它消息里面需要调用。
- 函数GUI_MULTIBUF_Begin和GUI_MULTIBUF_End实现三缓冲绘制,将其整体加载后绘制,防止撕裂。
- 函数GUI_MEMDEV_WriteAt实现图像实际绘制。
56.5 实验例程说明(RTOS)
配套例子:
V7-576_emWin6.x实验_摄像头动态采集展示(RTOS,320x240)
V7-578_emWin6.x实验_摄像头动态采集展示(RTOS,640x480)
实验目的:
- 学习emWin上动态展示摄像头采集数据。
- emWin功能的实现在MainTask.c文件里面。
实验内容:
1、K1按键按下,串口或者RTT打印任务执行情况(串口波特率115200,数据位8,奇偶校验位无,停止位1)。
2、(1) 凡是用到printf函数的全部通过函数App_Printf实现。
(2) App_Printf函数做了信号量的互斥操作,解决资源共享问题。
3、默认上电是通过串口打印信息,如果使用RTT打印信息:
MDK AC5,MDK AC6或IAR通过使能bsp.h文件中的宏定义为1即可
#define Enable_RTTViewer 1
4、各个任务实现的功能如下:
App Task Start 任务 :启动任务,这里用作BSP驱动包处理。
App Task MspPro任务 :消息处理,这里用作LED闪烁。
App Task UserIF 任务 :按键消息处理。
App Task COM 任务 :暂未使用。
App Task GUI 任务 :GUI任务。
μCOS-III任务调试信息(按K1按键,串口打印):
RTT 打印信息方式:
程序设计:
任务栈大小分配:
μCOS-III任务栈大小在app_cfg.h文件中配置:
#define APP_CFG_TASK_START_STK_SIZE 512u
#define APP_CFG_TASK_MsgPro_STK_SIZE 2048u
#define APP_CFG_TASK_COM_STK_SIZE 512u
#define APP_CFG_TASK_USER_IF_STK_SIZE 512u
#define APP_CFG_TASK_GUI_STK_SIZE 2048u
任务栈大小的单位是4字节,那么每个任务的栈大小如下:
App Task Start 任务 :2048字节。
App Task MspPro任务 :8192字节。
App Task UserIF 任务 :2048字节。
App Task COM 任务 :2048字节。
App Task GUI 任务 :8192字节。
系统栈大小分配:
μCOS-III的系统栈大小在os_cfg_app.h文件中配置:
#define OS_CFG_ISR_STK_SIZE 512u
系统栈大小的单位是4字节,那么这里就是配置系统栈大小为2KB
emWin动态内存配置:
GUIConf.c文件中的配置如下:
#define EX_SRAM 1/*1 used extern sram, 0 used internal sram */ #if EX_SRAM #define GUI_NUMBYTES (1024*1024*24) #else #define GUI_NUMBYTES (100*1024) #endif
通过宏定义来配置使用内部SRAM还是外部的SDRAM做为emWin的动态内存,当配置:
#define EX_SRAM 1 表示使用外部SDRAM作为emWin动态内存,大小24MB。
#define EX_SRAM 0 表示使用内部SRAM作为emWin动态内存,大小100KB。
默认情况下,本教程配套的所有emWin例子都是用外部SDRAM作为emWin动态内存。
emWin界面显示效果:
800*480分辨率界面效果。
56.6 实验例程说明(裸机)
配套例子:
V7-575_emWin6.x实验_摄像头动态采集展示(裸机,320x240)
V7-577_emWin6.x实验_摄像头动态采集展示(裸机,640x480)
实验目的:
- 学习emWin上动态展示摄像头采集数据。
- emWin功能的实现在MainTask.c文件里面。
emWin界面显示效果:
800*480分辨率界面效果。
emWin动态内存配置:
GUIConf.c文件中的配置如下:
#define EX_SRAM 1/*1 used extern sram, 0 used internal sram */ #if EX_SRAM #define GUI_NUMBYTES (1024*1024*24) #else #define GUI_NUMBYTES (100*1024) #endif
通过宏定义来配置使用内部SRAM还是外部的SDRAM做为emWin的动态内存,当配置:
#define EX_SRAM 1 表示使用外部SDRAM作为emWin动态内存,大小24MB。
#define EX_SRAM 0 表示使用内部SRAM作为emWin动态内存,大小100KB。
默认情况下,本教程配套的所有emWin例子都是用外部SDRAM作为emWin动态内存。
56.7 总结
本章节主要为大家讲解了摄像头动态图像在emWin中的显示方法,大家也可以尝试其它方式实现动态图像展示。