ov9650 VS2005应用程序设计

shoushui2009 2010-05-20 07:43:59
TQ2440板子,ov9650摄像头,用VS2005下的VC++编写应用程序,如何把OV9650 拍到的图像保存为BMP格式的文件?需要用到那些函数?如何得到数据缓冲区的地址?下面是部分驱动程序代码:
1、CIS_IoControl()中的一部分:
// for MPEG4
case CAM_IOCTL_GET_LATEST_FRAME:
RETAILMSG(MSG_EN_1,(TEXT("CAMERA:CAM_IOCTL_GET_LATEST_FRAME\r\n")));
if (frame_count < 2) {
RETAILMSG(1,(TEXT("CAMERA:CAM_IOCTL_GET_LATEST_FRAME - frame not available!!!\r\n")));
return FALSE;}
Tick_GET_FRAME_PREV = Tick_GET_FRAME_CUR;
Tick_GET_FRAME_CUR = GetTickCount();
Copy_Cam_Image(pBufOut, QCIF_XSIZE, QCIF_YSIZE, PORT_A);
break;

case CAM_IOCTL_SAMSUNG_CAM: // ID=0x520
RETAILMSG(MSG_EN_1,(_T("CAM_IOCTL_SAMSUNG_CAM\r\n")));
Samsung_camcoder(pBufOut);
break;

。。。。

2、Copy_Cam_Image(pBufOut, QCIF_XSIZE, QCIF_YSIZE, PORT_A)函数 一部分
{ 。。。
pImage = pBufOut;
if (image_size == 1) // QCIF
Y_size = QCIF_XSIZE*QCIF_YSIZE;
else if (image_size == 2) // CIF
Y_size = CIF_XSIZE*CIF_YSIZE;

C_size = Y_size/4;
P_size = Y_size + C_size*2;
。。。
buffer_y += VIRTUAL_ADDR_OFFSET;
buffer_cb += VIRTUAL_ADDR_OFFSET;
buffer_cr += VIRTUAL_ADDR_OFFSET;
。。。
RETAILMSG(MSG_EN_1,(_T("pBufOut 0 offset = 0x%x\r\n"), pImage));
memcpy(pImage, &yuvinfo, sizeof(YUVINFO));
pImage += sizeof(YUVINFO);

RETAILMSG(MSG_EN_1,(_T("pBufOut Y = 0x%x\r\n"), pImage));
memcpy(pImage, buffer_y, Y_size);
pImage += (Y_size);

RETAILMSG(MSG_EN_1,(_T("pBufOut cb = 0x%x\r\n"), pImage));
memcpy(pImage, buffer_cb, C_size);
pImage += C_size;
RETAILMSG(MSG_EN_1,(_T("pBufOut cr = 0x%x\r\n"), pImage));
memcpy(pImage, buffer_cr, C_size);
。。。
}

请问从驱动程序缓冲区的数据是不是可以通过 deviceIOcontrol 调用CAM_IOCTL_GET_LATEST_FRAME来读取并进一步保存为bmp格式图片?
...全文
946 30 打赏 收藏 转发到动态 举报
写回复
用AI写文章
30 条回复
切换为时间正序
请发表友善的回复…
发表回复
cs302dn 2012-03-11
  • 打赏
  • 举报
回复
再看一遍
wanglei5695312 2010-06-16
  • 打赏
  • 举报
回复
rgb_address的地址s2440CAM->CICOYSA1=CoFrameBuffer是由下面的函数得到的
csyu85 2010-06-03
  • 打赏
  • 举报
回复
我正在研究C#的方式, 目前已获得一个超过 0x80000000的内存地址,即是内核内存地址,正在想办法Copy出来到应用程序空间。有做过的兄弟请加QQ4827132聊聊。
shoushui2009 2010-06-01
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 gooogleman 的回复:]

楼主的方法改的不错。值得借鉴。学习了,很巧妙。
[/Quote]

你的帖子对我帮助很大,多谢gooogleman!
FrankBIBI 2010-05-31
  • 打赏
  • 举报
回复
不错~学习!赞一个!
gooogleman 2010-05-31
  • 打赏
  • 举报
回复
楼主的方法改的不错。值得借鉴。学习了,很巧妙。
shoushui2009 2010-05-31
  • 打赏
  • 举报
回复
今天,结贴!
shoushui2009 2010-05-31
  • 打赏
  • 举报
回复
我来解答:在wince5.0下,用EVC编写应用程序的时候可以调用SetKMode 函数从用户模式进入内核模式,进而对内核进行操作(如从内核某地址开始的内存空间读取数据并保存到在应用程序开辟的内存空间中)。所以可以通过上面11楼的函数保存摄像头预览到的图像。然而在wince6.0下,不支持在VS2005编写的应用程序中调用该函数直接进入内核传递数据,如何解决呢?
首先说一下wince5.0下从内核读取数据的两种方式:一种是通过DeviceIoControl函数获得内核缓冲区数据的地址,然后在应用程序中进入内核模式从该地址传递数据,例如:
BYTE* DDBdata=new BYTE[width*height*2];
ret=DeviceIoControl(m_hled,CAM_IOCTL_SAMSUNG_CAM_PR,NULL,NULL,(PBYTE)&image,NULL,NULL,NULL);
if(!ret)AfxMessageBox(_T("读取地址失败!"));
SetKMode(TRUE);
memcpy(DDBdata,(void *)image.rgb_address,width*height*2);
SetKMode(FALSE);
另一种是设置通过DeviceIoControl的参数,通过控制代码调用对应的驱动程序中的函数把内核中的

数据传递到应用程序开辟的空间中。例如:
BOOL ret;
BYTE* DDBdata=new BYTE[width*height*2];
BYTE* DIBdata;
ret=DeviceIoControl(hCam,IOCTL_CAM_COPY,NULL,NULL,DDBdata,width*height*2,NULL,NULL);
if(!ret)AfxMessageBox(_T("传递数据失败!"));


为了更好的比较两种方式的差异,我把驱动程序中的相关函数和代码也复制过来:
CIS_IoControl()函数中定义的一些控制代码的功能:
case CAM_IOCTL_SAMSUNG_CAM_PR: // ID=0x522
RETAILMSG(MSG_EN_1,(_T("CAM_IOCTL_SAMSUNG_CAM_PR\r\n")));
Samsung_camcoder_pr(pBufOut);
break;
。。。。。。
case IOCTL_CAM_COPY :
RETAILMSG(MSG_EN_1,(TEXT("CAMERA:IOCTL_CAM_COPY(dwLenIn:%x)\r\n"), dwLenIn));
Copy_Cam_Preview_Image(pBufOut,dwLenOut);
break;
相关函数:
void Samsung_camcoder_pr(U8 *pBufOut)
{
U8 *pImage;
PINGPONG_PR prinfo;
pImage = pBufOut;
if ( rgb_flag )
{
prinfo.flag = 1;
prinfo.rgb_address = rgb_address;
memcpy(pImage, &prinfo, sizeof(PINGPONG_PR));
rgb_flag = 0;
}
//显然,Samsung_camcoder_pr这个函数主要是把rgb_address中保存的地址传递给pBufOut(当然前面还有一个rgb_flag标志)
void Copy_Cam_Preview_Image(U8 * pBufOut,DWORD dwLenOut)
{

int temp;
U8 *buffer_rgb;
U8 *pImage;
temp = (s2440CAM->CIPRSTATUS>>26)&3;
temp = (temp + 2) % 4;
RETAILMSG(MSG_EN_1,(_T("Copy_preview_index = %d, size %d\r\n"), temp, image_size));

switch (temp)
{
case 0:
buffer_rgb = (U8*)s2440CAM->CIPRCLRSA1;
break;
case 1:
buffer_rgb = (U8*)s2440CAM->CIPRCLRSA2;
break;
case 2:
buffer_rgb = (U8*)s2440CAM->CIPRCLRSA3;
break;
case 3:
buffer_rgb = (U8*)s2440CAM->CIPRCLRSA4;
break;
default :
buffer_rgb = (U8*)s2440CAM->CIPRCLRSA1;
break;
}
RETAILMSG(MSG_EN_1,(_T("buffer_rgb[PHY] = 0x%x\r\n"), buffer_rgb));
buffer_rgb += VIRTUAL_ADDR_OFFSET;
#if 1
RETAILMSG(MSG_EN_1,(_T("buffer_rgb = 0x%x\r\n"), buffer_rgb));
#endif
pImage=pBufOut;
memcpy(pImage,buffer_rgb,dwLenOut);
RETAILMSG(MSG_EN_1,(_T("Data Bytes = 0x%x\r\n"), dwLenOut));
}//这个函数是我参考原驱动中的void Copy_Cam_Image(U8 *pBufOut, U32 size_x, U32 size_y, U8port)函数改写后添加到驱动中的。可以看出,函数完成的功能是把rgb_address指针指向的地址开始的dwLenOut个字节传递给pBufOut指针指向的空间中。说明:temp = (temp + 2) % 4;这个语句的意义我也不太懂,望高手帮忙解答。
比较一下前后两个DeviceIoControl函数的参数,
ret=DeviceIoControl(m_hled,CAM_IOCTL_SAMSUNG_CAM_PR,NULL,NULL,(PBYTE)&image,NULL,NULL,NULL);
ret=DeviceIoControl(hCam,IOCTL_CAM_COPY,NULL,NULL,DDBdata,width*height*2,NULL,NULL);
第一个只是传递地址,第二个是直接传递数据。wince6.0下用VS2005编写应用程序时无法调用SetKMode进入内核模式,因此无法采用第一种方式,先得到地址,再获取数据;于是只能采用第二种方式,直接在应用程序中通过DeviceIoControl函数调用控制代码,在驱动函数中完成数据传递,前提是得有相关函数,例如上面我在驱动中添加的void Copy_Cam_Preview_Image(U8 * pBufOut,DWORD dwLenOut)函数。此外,在原驱动中也有类似的函数,即void Copy_Cam_Image(U8 *pBufOut, U32 size_x, U32 size_y, U8 port)函数。

说明:毕业设计基于嵌入式,以前没接触过,研究OV9650的驱动程序,VC++也没学过。很长时间了,不知道的函数一点一点的查资料,慢慢的看懂驱动,保存图片用了近半个月,走了不少弯路,现在看看,其实工作量很小,但是网上却找不到wince6.0下的保存图片的解决办法。所以把自己这些天来的一点收获与大家分享,这只是我个人目前的理解,不一定准确,如有误请大家指出,共同学习。
http://blog.csdn.net/shoushui2009/archive/2010/05/31/5637487.aspx
gooogleman 2010-05-25
  • 打赏
  • 举报
回复
楼主还是换回 wince5.0 吧。6.0 要我也没有保存过,5.0 就可以。
shoushui2009 2010-05-23
  • 打赏
  • 举报
回复

void Samsung_camcoder_pr(U8 *pBufOut)
{
U8 *pImage;
PINGPONG_PR prinfo;

pImage = pBufOut;

if ( rgb_flag )
{
prinfo.flag = 1;
prinfo.rgb_address = rgb_address;
memcpy(pImage, &prinfo, sizeof(PINGPONG_PR));
rgb_flag = 0;
}
}
上面的驱动程序函数可以获取到内核缓冲区数据地址,(11楼函数)然后通过DeviceIoControl,得到这个地址,再在应用程序中使用SetKMode()函数访问内核中的数据并用memcpy()函数把这些数据复制到应用程序开辟的空间中在保存为bmp图片.然而 ce6.0不再支持在应用程序中使用SetKMode()函数访问内核空间,所以要做一些修改,我有一点思路,但由于水平有限,不知道可不可行:

总体思路:还是利用DeviceIoControl,但传递的不是地址,直接获取内核缓冲区的数据:
修改一下应用程序:
BYTE* DDBdata=new BYTE[width*height*2];
BYTE* DIBdata;
ret=DeviceIoControl(m_hled,CAM_IOCTL_SAMSUNG_CAM_PR,NULL,NULL,DDBdata,width*height*2,NULL,NULL);
我想直接把内核缓冲区中width*height*2字节的数据传到DDBdata开辟的空间中,这样应用程序中就可以不必使用SetKMode()函数。
修改一下驱动函数:

void Samsung_camcoder_pr(U8 *pBufOut)
{
if ( rgb_flag )
{
SetKMode(TRUE);
memcpy(pBufOut, rgb_address, sizeof(240*180*2));
rgb_flag = 0;
SetKMode(FALSE);
}
}

这只是我的一个想法,不知道可不可行,语法上是不是有错误?

shoushui2009 2010-05-23
  • 打赏
  • 举报
回复
void Samsung_camcoder_pr(U8 *pBufOut)
{
U8 *pImage;
PINGPONG_PR prinfo;

pImage = pBufOut;

if ( rgb_flag )
{
prinfo.flag = 1;
prinfo.rgb_address = rgb_address;
memcpy(pImage, &prinfo, sizeof(PINGPONG_PR));
rgb_flag = 0;
}
}
上面的驱动程序函数可以获取到内核缓冲区数据地址,然后通过(11楼函数)DeviceIoControl,得到这个地址,再在应用程序中使用SetKMode()函数访问内核中的数据并用memcpy()函数把这些数据复制到应用程序开辟的空间中在保存为bmp图片.然而 ce6.0不再支持在应用程序中使用SetKMode()函数访问内核空间,所以要做一些修改,我有一点思路,但由于水平有限,不知道可不可行:

总体思路:还是利用DeviceIoControl,但传递的不是地址,直接获取内核缓冲区的数据:
修改一下应用程序:
BYTE* DDBdata=new BYTE[240*180*2];
BYTE* DIBdata;
ret=DeviceIoControl(m_hled,CAM_IOCTL_SAMSUNG_CAM_PR,NULL,NULL,DDBdata,240*180*2,NULL,NULL);
我想直接把内核缓冲区中240*180*2字节的数据传到DDBdata开辟的空间中,这样应用程序中就可以不必使用SetKMode()函数。


修改一下驱动函数:
void Samsung_camcoder_pr(U8 *pBufOut)
{
SetKMode(TRUE);
if ( rgb_flag )
{
memcpy(pBufOut, (void *)rgb_address, sizeof(240*180*2));
rgb_flag = 0;

}
SetKMode(FALSE);
}

这只是我的一个想法,不知道可不可行?
gooogleman 2010-05-22
  • 打赏
  • 举报
回复
想办法在驱动解决。关于这个问题看看sunrain_hjb的博客,有讲。
shoushui2009 2010-05-22
  • 打赏
  • 举报
回复
MSDN关于这个函数有如下内容:
This function switches between kernel and user modes.
This function is no longer supported as of CE 6.0.
Syntax
BOOL SetKMode( BOOL fMode);

Parameters
fMode
[in] Setting to TRUE enters kernel mode. Setting to FALSE enters user mode.
Return Value
Returns the mode prior to the current call to SetKMode. TRUE if the previous mode was kernel mode; FALSE if the previous mode was user mode.
Remarks
Only the calling thread is affected by SetKMode.

To allow nested operation, set fMode to the value returned from an initial call to SetKMode to return to the original state.

Requirements
Header pkfuncs.h
Library coredll.lib
Windows Embedded CE Windows CE 2.10 and later
在6.0里不支持SetKMode()函数,是不是?又该如何修改?


dgw6698555 2010-05-22
  • 打赏
  • 举报
回复
学习了
shoushui2009 2010-05-22
  • 打赏
  • 举报
回复
好的,多谢gooogleman!
gooogleman 2010-05-22
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 shoushui2009 的回复:]
应该包含那个头文件?这是.cpp文件里的:
#include "stdafx.h"
#include "OV9650.h"
#include "OV9650Dlg.h"
#include "winioctl.h"
#include <stdlib.h>
请明示,呵呵
[/Quote]

要学会查找啊,看MSN'SetKMode'在哪个里,就知道了。
shoushui2009 2010-05-22
  • 打赏
  • 举报
回复
应该包含那个头文件?这是.cpp文件里的:
#include "stdafx.h"
#include "OV9650.h"
#include "OV9650Dlg.h"
#include "winioctl.h"
#include <stdlib.h>
请明示,呵呵
gooogleman 2010-05-22
  • 打赏
  • 举报
回复
可以的。是你头文件问题。嘿嘿。
shoushui2009 2010-05-22
  • 打赏
  • 举报
回复
void CCameratestDlg::OnButton3()
{
// TODO: Add your control notification handler code here
PINGPONG_PR image;
WORD width=GetSystemMetrics(SM_CXSCREEN);
WORD height=GetSystemMetrics(SM_CYSCREEN);
BOOL ret;
BYTE* DDBdata=new BYTE[width*height*2];
BYTE* DIBdata;
ret=DeviceIoControl(m_hled,CAM_IOCTL_SAMSUNG_CAM_PR,NULL,NULL,(PBYTE)&image,NULL,NULL,NULL);
if(!ret)
AfxMessageBox(_T("读取图片失败!"));
else
{
SetKMode(TRUE);
memcpy(DDBdata,(void *)image.rgb_address,width*height*2);
SetKMode(FALSE);
CBitmap bitmap;
HBITMAP dstBmp;
bitmap.CreateBitmap(width,height,1,16,DDBdata);
HDC hdcSrc = CreateCompatibleDC(NULL);
HDC hdcDst = CreateCompatibleDC(NULL);
BITMAPINFOHEADER bih = {0};//位图信息头
bih.biBitCount = 16;//每个像素字节大小
bih.biCompression = BI_RGB;
bih.biHeight = height;//高度
bih.biPlanes = 1;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biSizeImage = 0;// width*height*2;//图像数据大小
bih.biWidth = width;//宽度
BITMAPFILEHEADER bfh = {0};//位图文件头
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);//到位图数据的偏移量
bfh.bfSize = bfh.bfOffBits + width*height*2;//文件总的大小
bfh.bfType = (WORD)0x4d42;
BITMAPINFO bi={0};
bi.bmiHeader=bih;
dstBmp=CreateDIBSection(hdcDst, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void **)&DIBdata, NULL, 0);
SelectObject(hdcDst, dstBmp);
SelectObject(hdcSrc, bitmap);
BitBlt(hdcDst, 0, 0, width, height, hdcSrc, 0, 0, SRCCOPY);
CFile file(_T("image.bmp"),CFile::modeCreate|CFile::modeReadWrite);
file.Write(&bfh,sizeof(bfh));
file.Write(&bih,sizeof(bih));
file.Write(DIBdata,width*height*2);
file.Close();
}
delete []DDBdata;
}

我用的是VS2005+wince6.0,编译以后:
1>正在编译...
1>OV9650Dlg.cpp
1>.\OV9650Dlg.cpp(306) : error C2065: 'PINGPONG_PR' : undeclared identifier
1>.\OV9650Dlg.cpp(306) : error C2146: syntax error : missing ';' before identifier 'image'
1>.\OV9650Dlg.cpp(306) : error C2065: 'image' : undeclared identifier
1>.\OV9650Dlg.cpp(323) : error C3861: 'SetKMode': identifier not found
1>.\OV9650Dlg.cpp(324) : error C2228: left of '.rgb_address' must have class/struct/union
1> type is ''unknown-type''
1>.\OV9650Dlg.cpp(325) : error C3861: 'SetKMode': identifier not found
1>Generating Code...

这是怎么回事?在应用程序中不能调用SetKMode()吗?'PINGPONG_PR' 是驱动程序中定义的一个结构体,在应用程序中能直接使用吗?如果不能通过SetKMode()获取内存缓冲区数据,如何在应用程序中操作才能够实现?
shoushui2009 2010-05-22
  • 打赏
  • 举报
回复
可是我水平还不够啊,驱动程序就看了很长时间,渐渐地看懂一点。应用程序也是慢慢理解,现在就想把图片保存下来,看到了你的一个帖子,试了一下,看来还得改一下。拖得时间很长了,我想尽快完成,想听一下gooogleman的建议
加载更多回复(10)

19,498

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 嵌入开发(WinCE)
社区管理员
  • 嵌入开发(WinCE)社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧