Linux設備驅動之LCD顯示攝像圖像之一框架與準備工作

大體框架:
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
6cp 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
  1. 用新內核、新文件系統啓動開發板
    啓動開發板至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
然後 發現錯誤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章