大體框架:
1、攝像頭讀出數據存入VideoBuf
2、將VideoBuf轉換成RGB模式(LCD爲RGB模式),存入ConvertBuf
3、進行縮放,存入ZoomBuf
4、進行合併,存入FrameBuf
5、LCD讀取FrameBuf,進行顯示
面對對象編程:
1、VideoDevice
.iPixelFormat
.iWidth
.iHeight
2、VideoOpr
.InitDevice
.ExitDevice
.GetFrame
.PutFrame
.StartDevice
.StopDevice
我們如今只需要一個
所以我們構造:
V4l2VideoOpr
3、ConvertOpr
.isSurpport
.convert
.ConvertExit
我們需要轉換:YUV2RGB, MJPEG2RGB, RGB2RGB
所以我們要構造一個Convert_manager.c
然後寫三個Yuv2rgb.c mjpeg2rgb.c rgb2rgb.c 然後向Convert_manager.c進行結構體的註冊RegisterVideoConvert
4、DispOpr
.DeviceInit
.ShowPixel
.CleanScreen
.ShowPage
需要寫兩個函數,fb和VGA。一個在屏幕上顯示,一個在LCD上顯示
我們可以通過main.c便可以看出整個項目的主要流程:
這是在LCD上進行顯示的main函數
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <config.h>
#include <disp_manager.h>
#include <video_manager.h>
#include <convert_manager.h>
#include <render.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
/* video2lcd </dev/video0,1,...> */
int main(int argc, char **argv)
{
int iError;
T_VideoDevice tVideoDevice;
PT_VideoConvert ptVideoConvert;
int iPixelFormatOfVideo;
int iPixelFormatOfDisp;
PT_VideoBuf ptVideoBufCur;
T_VideoBuf tVideoBuf;
T_VideoBuf tConvertBuf;
T_VideoBuf tZoomBuf;
T_VideoBuf tFrameBuf;
int iLcdWidth;
int iLcdHeigt;
int iLcdBpp;
int iTopLeftX;
int iTopLeftY;
float k;
if (argc != 2)
{
printf("Usage:\n");
printf("%s </dev/video0,1,...>\n", argv[0]);
return -1;
}
/* 一系列的初始化 */
/* 註冊顯示設備 */
DisplayInit();
/* 可能可支持多個顯示設備: 選擇和初始化指定的顯示設備 */
SelectAndInitDefaultDispDev("fb");
GetDispResolution(&iLcdWidth, &iLcdHeigt, &iLcdBpp);
//Garmen:獲得顯存信息 ,將獲得的數據存入tFramBuf當中;
GetVideoBufForDisplay(&tFrameBuf);
iPixelFormatOfDisp = tFrameBuf.iPixelFormat;
VideoInit();
//Garmen:打開argv[1]這個設備,做某些初始化,看是否成功,成功的話,得到tVideoDevice這麼一個結構體 ,最重要的是tVideoDevice還含有PT_VideoOpr這個函數指針!
iError = VideoDeviceInit(argv[1], &tVideoDevice);
if (iError)
{
DBG_PRINTF("VideoDeviceInit for %s error!\n", argv[1]);
return -1;
}
//GARMEN:得到攝像設備的格式,是從上面Init得到的tVideoDevice直接直接取出它裏面的IPixelFormat
iPixelFormatOfVideo = tVideoDevice.ptOPr->GetFormat(&tVideoDevice);
VideoConvertInit();
//Garmen: iPixelFormatOfVideo是攝像設備的格式,iPixelFormatOfDisp是顯示設備的格式,成功返回一個VideoConvert結構體
ptVideoConvert = GetVideoConvertForFormats(iPixelFormatOfVideo, iPixelFormatOfDisp);
if (NULL == ptVideoConvert)
{
DBG_PRINTF("can not support this format convert\n");
return -1;
}
/* 啓動攝像頭設備 */
iError = tVideoDevice.ptOPr->StartDevice(&tVideoDevice);
if (iError)
{
DBG_PRINTF("StartDevice for %s error!\n", argv[1]);
return -1;
}
memset(&tVideoBuf, 0, sizeof(tVideoBuf));
memset(&tConvertBuf, 0, sizeof(tConvertBuf));
tConvertBuf.iPixelFormat = iPixelFormatOfDisp;
tConvertBuf.tPixelDatas.iBpp = iLcdBpp;
memset(&tZoomBuf, 0, sizeof(tZoomBuf));
while (1)
{
/* 讀入攝像頭數據 ,讀入數據存入tVideoBuf*/
iError = tVideoDevice.ptOPr->GetFrame(&tVideoDevice, &tVideoBuf);
if (iError)
{
DBG_PRINTF("GetFrame for %s error!\n", argv[1]);
return -1;
}
ptVideoBufCur = &tVideoBuf;
//Garmen:攝像設備的格式不等於顯示設備的格式才進行格式轉換,轉換得到的結構體保存到tConvertBuf
if (iPixelFormatOfVideo != iPixelFormatOfDisp)
{
/* 轉換爲RGB */
iError = ptVideoConvert->Convert(&tVideoBuf, &tConvertBuf);
DBG_PRINTF("Convert %s, ret = %d\n", ptVideoConvert->name, iError);
if (iError)
{
DBG_PRINTF("Convert for %s error!\n", argv[1]);
return -1;
}
//Garmen:如果轉換成功,保存爲ptVideoBufCur
ptVideoBufCur = &tConvertBuf;
}
/* 如果圖像分辨率大於LCD, 縮放 */
if (ptVideoBufCur->tPixelDatas.iWidth > iLcdWidth || ptVideoBufCur->tPixelDatas.iHeight > iLcdHeigt)
{
/* 確定縮放後的分辨率 */
/* 把圖片按比例縮放到VideoMem上, 居中顯示
* 1. 先算出縮放後的大小
*/
//Garmen:先算出比例K,然後等比例縮放!
k = (float)ptVideoBufCur->tPixelDatas.iHeight / ptVideoBufCur->tPixelDatas.iWidth;
//Garmen:然後先將Zoom的寬度設置爲LCD寬度,LCD的高度再乘上K,就得到等比例縮放的圖像
tZoomBuf.tPixelDatas.iWidth = iLcdWidth;
tZoomBuf.tPixelDatas.iHeight = iLcdWidth * k;
if ( tZoomBuf.tPixelDatas.iHeight > iLcdHeigt)
{
tZoomBuf.tPixelDatas.iWidth = iLcdHeigt / k;
tZoomBuf.tPixelDatas.iHeight = iLcdHeigt;
}
tZoomBuf.tPixelDatas.iBpp = iLcdBpp;
tZoomBuf.tPixelDatas.iLineBytes = tZoomBuf.tPixelDatas.iWidth * tZoomBuf.tPixelDatas.iBpp / 8;
tZoomBuf.tPixelDatas.iTotalBytes = tZoomBuf.tPixelDatas.iLineBytes * tZoomBuf.tPixelDatas.iHeight;
if (!tZoomBuf.tPixelDatas.aucPixelDatas)
{
tZoomBuf.tPixelDatas.aucPixelDatas = malloc(tZoomBuf.tPixelDatas.iTotalBytes);
}
//Garmen:進行近鄰取樣插值方法縮放圖片。得到縮放後的圖片的像素數據存在後面的參數
PicZoom(&ptVideoBufCur->tPixelDatas, &tZoomBuf.tPixelDatas);
ptVideoBufCur = &tZoomBuf;
}
/* 合併進framebuffer */
/* 接着算出居中顯示時左上角座標 */
iTopLeftX = (iLcdWidth - ptVideoBufCur->tPixelDatas.iWidth) / 2;
iTopLeftY = (iLcdHeigt - ptVideoBufCur->tPixelDatas.iHeight) / 2;
/*
* 函數名稱: PicMerge
* 功能描述: 把小圖片合併入大圖片裏
* 輸入參數: iX,iY - 小圖片合併入大圖片的某個區域, iX/iY確定這個區域的左上角座標
* ptSmallPic - 內含小圖片的象素數據
* ptBigPic - 內含大圖片的象素數據
*/
PicMerge(iTopLeftX, iTopLeftY, &ptVideoBufCur->tPixelDatas, &tFrameBuf.tPixelDatas);
FlushPixelDatasToDev(&tFrameBuf.tPixelDatas);
//Garmen:用完之後進行釋放
iError = tVideoDevice.ptOPr->PutFrame(&tVideoDevice, &tVideoBuf);
if (iError)
{
DBG_PRINTF("PutFrame for %s error!\n", argv[1]);
return -1;
}
/* 把framebuffer的數據刷到LCD上, 顯示 */
}
return 0;
}
編譯環境準備工作:
2.安裝工具鏈 :
sudo tar xjf arm-linux-gcc-4.3.2.tar.bz2 -C /
設置環境變量:
sudo vi /etc/environment :
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/arm/4.3.2/bin"
假如不想重啓,手工設置:
①: echo $PATH
②:
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/arm/4.3.2/bin
3.編譯內核:
1、解壓linux內核,打補丁
2、將中我們自己寫的lcd_4.3.c複製到linux-3.4.2/drivers/video
3、修改linux-3.4.2/drivers/video中的Makefile
#obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o
obj-$(CONFIG_FB_S3C2410) += lcd_4.3.o
4、將準備好的dm9dev9000c.c和dm9000.h複製到linux-3.4.2/drivers/net/ethernet/davicom
5、修改linux-3.4.2/drivers/net/ethernet/davicom/Makefile
#obj-$(CONFIG_DM9000) += dm9000.o
obj-$(CONFIG_DM9000) += dm9dev9000c.o
6、cp config_ok .config
7、make menuconfig
8、 <*> Multimedia support --->
<*> Video For Linux
[*] Video capture adapters (NEW) --->
[*] V4L USB devices (NEW) --->
<*> USB Video Class (UVC)
9、將之前修改後的uvc_video.c和uvc_driver.c 替換linux-3.4.2/drivers/media/video/uvc 中的文件;
10、make uImage
11、也可以使用我們製作好的補丁:
linux-3.4.2_camera_jz2440.patch
patch -p1 < ../linux-3.4.2_camera_jz2440.patch
cp config_ok .config
make uImage
cp arch/arm/boot/uImage /work/nfs_root/uImage_new
4、文件系統:
先將已經制作好的文件系統fs_mini_mdev_new.tar.bz2拷貝到/work/nfs_root
cd /work/nfs_root
sudo tar xjf fs_mini_mdev_new.tar.bz2
sudo chown book:book fs_mini_mdev_new
- 用新內核、新文件系統啓動開發板
啓動開發板至UBOOT
設置UBOOT的環境變量:
set ipaddr 192.168.1.104
set bootcmd 'nfs 32000000 192.168.1.101:/work/nfs_root/uImage_new; bootm 32000000'
set bootargs console=ttySAC0,115200 root=/dev/nfs nfsroot=192.168.1.101:/work/nfs_root/fs_mini_mdev_new ip=192.168.1.104
save
boot
6、開發板上測試:
make&&cp video2lcd /work/nfs_root/fs_mini_mdev_new
./video2lcd /dev/video0
發送段錯誤時候解決方法:
讓程序在開發板上直接運行,當它發生錯誤時,令它產生core dump文件
然後使用gdb根據core dump文件找到發生錯誤的地方
在ARM板上:
1. ulimit -c unlimited
2. 執行應用程序 : 程序出錯時會在當前目錄下生成名爲core的文件
ls /work/nfs_root/fs_mini_mdev_new/core
在PC上:
3. /bin/arm-linux-gdb ./test_debug /work/nfs_root/fs_mini_mdev_new/core
執行命令:backtrace
然後 發現錯誤。