在調試2440 相機接口的時候碰到問題最多的就是這個CamInit()函數,下面貼上代碼,分析之.
/* Description of Parameters
CoDstWidth: Destination Width of Codec Path
CoDstHeight: Destination Height of Codec Path
PrDstWidth: Destination Width of Preview Path
PrDstHeight: Destination Height of Preview Path
WinHorOffset: Size of Window Offset for Horizontal Direction
WinVerOffset: Size of Window Offset for Vertical Direction
CoFrameBuffer: Start Address for Codec DMA
PrFrameBuffer: Start Address for Previe DMA
*/
void CamInit(U32 CoDstWidth, U32 CoDstHeight, U32 PrDstWidth, U32 PrDstHeight,
U32 WinHorOffset, U32 WinVerOffset, U32 CoFrameBuffer, U32 PrFrameBuffer)
{
U32 WinOfsEn;
U32 divisor, multiplier;
U32 MainBurstSizeY, RemainedBurstSizeY, MainBurstSizeC, RemainedBurstSizeC, MainBurstSizeRGB, RemainedBurstSizeRGB;
U32 H_Shift, V_Shift, PreHorRatio, PreVerRatio, MainHorRatio, MainVerRatio;
U32 SrcWidth, SrcHeight;
U32 ScaleUp_H_Co, ScaleUp_V_Co, ScaleUp_H_Pr, ScaleUp_V_Pr;
if(CAM_CODEC_OUTPUT) // 註釋01
divisor=2; //CCIR-422
else
divisor=4; //CCIR-420
//constant for calculating preview dma address
if(CAM_PVIEW_OUTPUT) // 註釋02
multiplier=4; // RGB24 ,則每個像素用4字節
else
multiplier=2; // RGB16 ,則每個像素用2字節
if(WinHorOffset==0 && WinVerOffset==0) //註釋03 檢查是否需要縮放
WinOfsEn=0;
else
WinOfsEn=1;
SrcWidth=CAM_SRC_HSIZE-WinHorOffset*2;
SrcHeight=CAM_SRC_VSIZE-WinVerOffset*2;
// 註釋 04 scale up ---按比例增加,相應提高
if(SrcWidth>=CoDstWidth) ScaleUp_H_Co=0; //down 偏移後的原始圖像寬度還比目標圖像寬度大,那麼不需要放大,需要縮小.
else ScaleUp_H_Co=1; //up
if(SrcHeight>=CoDstHeight) ScaleUp_V_Co=0; // 偏移後的原始圖像高度比目標圖像高度大,那麼不需要放大,需要縮小.
else ScaleUp_V_Co=1;
if(SrcWidth>=PrDstWidth) ScaleUp_H_Pr=0; //down
else ScaleUp_H_Pr=1; //up
if(SrcHeight>=PrDstHeight) ScaleUp_V_Pr=0; // edited 040225
else ScaleUp_V_Pr=1;
// 註釋05
////////////////// common control setting
rCIGCTRL |= (1<<26)|(0<<27); // inverse PCLK, test pattern
//--- capbily
//rCIGCTRL |= (0<<26)|(0<<27); // don't inverse PCLK, test pattern
//---
rCIWDOFST = (1<<30)|(0xf<<12); // clear overflow 清除編碼和預覽的fifo
rCIWDOFST = 0;
rCIWDOFST=(WinOfsEn<<31)|(WinHorOffset<<16)|(WinVerOffset); // 設置窗口偏移並設置允許偏移
rCISRCFMT=(CAM_ITU601<<31)|(0<<30)|(0<<29)|(CAM_SRC_HSIZE<<16)|(CAM_ORDER_YCBYCR<<14)|(CAM_SRC_VSIZE);
// 設置源水平像素和垂直像素
//--- capbily
//rCISRCFMT=(CAM_ITU601<<31)|(1<<30)|(0<<29)|(CAM_SRC_HSIZE<<16)|(CAM_ORDER_YCBYCR<<14)|(CAM_SRC_VSIZE);
//---
// 註釋06
////////////////// codec port setting
if (CAM_CODEC_4PP)
{
rCICOYSA1=CoFrameBuffer;
rCICOYSA2=rCICOYSA1+CoDstWidth*CoDstHeight+2*CoDstWidth*CoDstHeight/divisor;
rCICOYSA3=rCICOYSA2+CoDstWidth*CoDstHeight+2*CoDstWidth*CoDstHeight/divisor;
rCICOYSA4=rCICOYSA3+CoDstWidth*CoDstHeight+2*CoDstWidth*CoDstHeight/divisor;
rCICOCBSA1=rCICOYSA1+CoDstWidth*CoDstHeight;
rCICOCBSA2=rCICOYSA2+CoDstWidth*CoDstHeight;
rCICOCBSA3=rCICOYSA3+CoDstWidth*CoDstHeight;
rCICOCBSA4=rCICOYSA4+CoDstWidth*CoDstHeight;
rCICOCRSA1=rCICOCBSA1+CoDstWidth*CoDstHeight/divisor;
rCICOCRSA2=rCICOCBSA2+CoDstWidth*CoDstHeight/divisor;
rCICOCRSA3=rCICOCBSA3+CoDstWidth*CoDstHeight/divisor;
rCICOCRSA4=rCICOCBSA4+CoDstWidth*CoDstHeight/divisor;
}
else
{
rCICOYSA1=CoFrameBuffer;
rCICOYSA2=rCICOYSA1;
rCICOYSA3=rCICOYSA1;
rCICOYSA4=rCICOYSA1;
rCICOCBSA1=rCICOYSA1+CoDstWidth*CoDstHeight;
rCICOCBSA2=rCICOCBSA1;
rCICOCBSA3=rCICOCBSA1;
rCICOCBSA4=rCICOCBSA1;
rCICOCRSA1=rCICOCBSA1+CoDstWidth*CoDstHeight/divisor;
rCICOCRSA2=rCICOCRSA1;
rCICOCRSA3=rCICOCRSA1;
rCICOCRSA4=rCICOCRSA1;
}
rCICOTRGFMT=(CAM_CODEC_IN_422<<31)|(CAM_CODEC_OUTPUT<<30)|(CoDstWidth<<16)|(CAM_FLIP_180<<14)|(CoDstHeight);
// 註釋07
CalculateBurstSize(CoDstWidth, &MainBurstSizeY, &RemainedBurstSizeY);
CalculateBurstSize(CoDstWidth/2, &MainBurstSizeC, &RemainedBurstSizeC);
rCICOCTRL=(MainBurstSizeY<<19)|(RemainedBurstSizeY<<14)|(MainBurstSizeC<<9)|(RemainedBurstSizeC<<4);
// 註釋08
CalculatePrescalerRatioShift(SrcWidth, CoDstWidth, &PreHorRatio, &H_Shift);
CalculatePrescalerRatioShift(SrcHeight, CoDstHeight, &PreVerRatio, &V_Shift);
MainHorRatio=(SrcWidth<<8)/(CoDstWidth<<H_Shift);
MainVerRatio=(SrcHeight<<8)/(CoDstHeight<<V_Shift);
// 註釋09
rCICOSCPRERATIO=((10-H_Shift-V_Shift)<<28)|(PreHorRatio<<16)|(PreVerRatio);
rCICOSCPREDST=((SrcWidth/PreHorRatio)<<16)|(SrcHeight/PreVerRatio);
rCICOSCCTRL=(CAM_SCALER_BYPASS_OFF<<31)|(ScaleUp_H_Co<<30)|(ScaleUp_V_Co<<29)|(MainHorRatio<<16)|(MainVerRatio);
rCICOTAREA=CoDstWidth*CoDstHeight;
///////////////// preview port setting
if (CAM_PVIEW_4PP) // codec view mode 預覽通道
{
rCIPRCLRSA1=PrFrameBuffer;
rCIPRCLRSA2=rCIPRCLRSA1+PrDstWidth*PrDstHeight*multiplier;
rCIPRCLRSA3=rCIPRCLRSA2+PrDstWidth*PrDstHeight*multiplier;
rCIPRCLRSA4=rCIPRCLRSA3+PrDstWidth*PrDstHeight*multiplier;
}
else // direct preview mode
{
rCIPRCLRSA1 = (U32)LCD_BUFFER_CAM;
rCIPRCLRSA2 = (U32)LCD_BUFFER_CAM;
rCIPRCLRSA3 = (U32)LCD_BUFFER_CAM;
rCIPRCLRSA4 = (U32)LCD_BUFFER_CAM;
}
rCIPRTRGFMT=(PrDstWidth<<16)|(CAM_FLIP_180<<14)|(PrDstHeight); // 設置預覽水平像素和垂直像素,圖像旋轉180度
if (CAM_PVIEW_OUTPUT==CAM_RGB24B)
CalculateBurstSize(PrDstWidth*2, &MainBurstSizeRGB, &RemainedBurstSizeRGB);
else // RGB16B
CalculateBurstSize(PrDstWidth*2, &MainBurstSizeRGB, &RemainedBurstSizeRGB);
rCIPRCTRL=(MainBurstSizeRGB<<19)|(RemainedBurstSizeRGB<<14);
CalculatePrescalerRatioShift(SrcWidth, PrDstWidth, &PreHorRatio, &H_Shift);
CalculatePrescalerRatioShift(SrcHeight, PrDstHeight, &PreVerRatio, &V_Shift);
MainHorRatio=(SrcWidth<<8)/(PrDstWidth<<H_Shift);
MainVerRatio=(SrcHeight<<8)/(PrDstHeight<<V_Shift);
rCIPRSCPRERATIO=((10-H_Shift-V_Shift)<<28)|(PreHorRatio<<16)|(PreVerRatio);
rCIPRSCPREDST=((SrcWidth/PreHorRatio)<<16)|(SrcHeight/PreVerRatio);
rCIPRSCCTRL=(1<<31)|(CAM_PVIEW_OUTPUT<<30)|(ScaleUp_H_Pr<<29)|(ScaleUp_V_Pr<<28)|(MainHorRatio<<16)|(MainVerRatio);
rCIPRTAREA= PrDstWidth*PrDstHeight;
}
我們來分析這段函數代碼
01) 根據宏CAM_CODEC_OUTPUT的定義來決定除數 divisor 的大小.
我們來看下CAM_CCIR422
在定義中是如何定義的:
#define CAM_CODEC_OUTPUT CAM_CCIR422
在這裏定義宏
CAM_CCIR422定義爲 CAM_CCIR422 表示視頻編碼通道輸出爲 4:2:2格式,此時divisor = 2;
如果宏CAM_CCIR422定義爲 CAM_CCIR420 表示視頻編碼通道輸出爲 4:2:0格式.此時divisor = 4;
02) 根據宏CAM_PVIEW_OUTPUT 的定義來決定乘數 multiplier 的大小.
我們來看下CAM_PVIEW_OUTPUT 在定義中是如何定義的:
#define CAM_PVIEW_OUTPUT CAM_RGB16B
在這裏定義宏
CAM_PVIEW_OUTPUT定義爲 CAM_RGB16B 表示視頻預覽通道輸出爲每個像素佔16位格式(佔2個字節),此時multiplier= 2;
如果宏CAM_PVIEW_OUTPUT定義爲 CAM_RGB24B 表示視頻預覽通道輸出爲真彩,此時每個像素佔4個字節,此時multiplier= 4;
03)根據設置,檢查是否需要進行圖像縮放
2440相機接口的縮放其實很簡單,
a)2440規定目標屏幕大小 TargetHsize_xx 必須小於等於原屏幕大小 SourceHsize.
b)在這樣的情況下,相機接口圖像的放大,只能是將原輸入圖像中需要放大的區域裁剪出來,"粘貼"到目標屏幕上,實現放大.
如果目標屏幕和原屏幕一樣大,那麼這種方式下圖像是放大不了的.
SrcWidth=CAM_SRC_HSIZE-WinHorOffset*2;
SrcHeight=CAM_SRC_VSIZE-WinVerOffset*2;
這兩行代碼就是計算被裁剪後區域的水平大小和垂直大小.
04)scale up 是個英文單詞,意思是按比例增加,相應提高.
if(SrcWidth>=CoDstWidth) ScaleUp_H_Co=0;
else ScaleUp_H_Co=1; //up
意思是水平方向偏移後的圖像寬度還比目標圖像寬度大,那麼不需要放大,直接將裁剪後區域水平方向"粘貼"到目標屏幕就可.
下面幾行鍼對視頻編碼通道和預覽通道分別判斷垂直方向是否需要放大.
05)
// 反轉PCLK時鐘
rCIGCTRL |= (1<<26)|(0<<27);
// 清除編碼和預覽的fifo
rCIWDOFST = (1<<30)|(0xf<<12);
rCIWDOFST = 0;
rCIWDOFST=(WinOfsEn<<31)|(WinHorOffset<<16)|(WinVerOffset); // 設置窗口偏移並設置允許偏移
// 設置源水平像素和垂直像素
rCISRCFMT=(CAM_ITU601<<31)|(0<<30)|(0<<29)|(CAM_SRC_HSIZE<<16)|(CAM_ORDER_YCBYCR<<14)|(CAM_SRC_VSIZE);
在上面這段代碼中
涉及到寄存器 rCIWDOFST,這個單詞r表示寄存器,CI表示camera interface,WD表示window,OFST表示offset.
rCIWDOFST 的第31位,表示是否允許放大,26到16位填寫窗口的水平偏移量,10到0位填寫窗口的垂直偏移量.
rCISRCFMT 是源幀格式寄存器,需要填寫相機的水平像素和垂直像素數目.
06) 這段代碼根據宏CAM_CODEC_4PP 的定義來設置幀內存地址
我們來看看在程序中的定義
#define CAM_CODEC_4PP (0) // 0:all equal, 1:4 pingpong
表示在幀存儲中不使用乒乓方式
幀存儲使用乒乓方式的好處是什麼呢?
答:在內存開闢1塊緩衝區Buf,當2440從圖像傳感器採集來數據後放入這個緩衝區Buf,同時DMA從這個緩衝區Buf將數據拷貝到LCD屏幕,
因爲DMA操作是不經過CPU控制的,這樣就產生了一個問題,可能從圖像傳感器來的數據放入緩衝區Buf的時候,恰好DMA也在操作Buf,這就會引發對數據訪問的同步與互斥問題,爲了解決這個問題,就設置了4塊內存區域,這樣就解決了問題.
根據 圖23-6. 乒乓存儲器體系我們看到
rCICOYSA1是編碼通道Y1分量的內存地址
rCICOYSA2是編碼通道Y2分量的內存地址
rCICOYSA3是編碼通道Y3分量的內存地址
rCICOYSA4是編碼通道Y4分量的內存地址
rCICOCBSA1 是編碼通道CB1分量的內存地址
rCICOCBSA2 是編碼通道CB2分量的內存地址
rCICOCBSA3 是編碼通道CB3分量的內存地址
rCICOCBSA4 是編碼通道CB4分量的內存地址
rCICOCRSA1 是編碼通道CR1分量的內存地址
rCICOCRSA2 是編碼通道CR2分量的內存地址
rCICOCRSA3 是編碼通道CR3分量的內存地址
rCICOCRSA4 是編碼通道CR4分量的內存地址
如果圖像傳感器按照 4:2 : 2方式採樣,那麼每4個像素中有4個Y分量,2個CB分量,2個CR分量,每個分量爲1字節;
第1幀採樣完成得到
CoDstWidth*CoDstHeight 個Y1, 放入rCICOYSA1[CoDstWidth*CoDstHeight]
CoDstWidth*CoDstHeight/2 個CB1,放入rCICOCBSA1[CoDstWidth*CoDstHeight/2]
CoDstWidth*CoDstHeight/2 個CR1,放入rCICOCRSA1[CoDstWidth*CoDstHeight/2]
第2幀採樣完成得到
CoDstWidth*CoDstHeight 個Y2, 放入rCICOYSA2 [CoDstWidth*CoDstHeight]
CoDstWidth*CoDstHeight/2 個CB2,放入rCICOCBSA2[CoDstWidth*CoDstHeight/2]
CoDstWidth*CoDstHeight/2 個CR2,放入rCICOCRSA2[CoDstWidth*CoDstHeight/2]
第3幀採樣完成得到
CoDstWidth*CoDstHeight 個Y3, 放入rCICOYSA3 [CoDstWidth*CoDstHeight]
CoDstWidth*CoDstHeight 個CB3, 放入rCICOCBSA3[CoDstWidth*CoDstHeight/2]
CoDstWidth*CoDstHeight 個CR3, 放入rCICOCRSA3[CoDstWidth*CoDstHeight/2]
第4幀採樣完成得到
CoDstWidth*CoDstHeight 個Y4, 放入rCICOYSA4 [CoDstWidth*CoDstHeight]
CoDstWidth*CoDstHeight/2 個CB4,放入rCICOCBSA4[CoDstWidth*CoDstHeight/2]
CoDstWidth*CoDstHeight/2 個CR4,放入rCICOCRSA4[CoDstWidth*CoDstHeight/2]
後面第5次採樣跟第一次採樣一樣,
第6次採樣跟第2次一樣.........................不斷循環採樣.
下面我們看這段代碼是如何對這些寄存器的地址進行分配分配的,前面註釋中說了如果是4:2:2採樣方式,則divisor爲2.
rCICOYSA1=CoFrameBuffer;
rCICOYSA2=rCICOYSA1+CoDstWidth*CoDstHeight+2*CoDstWidth*CoDstHeight/divisor;
rCICOYSA3=rCICOYSA2+CoDstWidth*CoDstHeight+2*CoDstWidth*CoDstHeight/divisor;
rCICOYSA4=rCICOYSA3+CoDstWidth*CoDstHeight+2*CoDstWidth*CoDstHeight/divisor;
rCICOCBSA1=rCICOYSA1+CoDstWidth*CoDstHeight;
rCICOCBSA2=rCICOYSA2+CoDstWidth*CoDstHeight;
rCICOCBSA3=rCICOYSA3+CoDstWidth*CoDstHeight;
rCICOCBSA4=rCICOYSA4+CoDstWidth*CoDstHeight;
rCICOCRSA1=rCICOCBSA1+CoDstWidth*CoDstHeight/divisor;
rCICOCRSA2=rCICOCBSA2+CoDstWidth*CoDstHeight/divisor;
rCICOCRSA3=rCICOCBSA3+CoDstWidth*CoDstHeight/divisor;
rCICOCRSA4=rCICOCBSA4+CoDstWidth*CoDstHeight/divisor;
如果rCICOYSA1的地址是CoFrameBuffer,那麼rCICOYSA2就是在rCICOYSA1的基礎上加上CoDstWidth*CoDstHeight個Y1的長度,再加上
CoDstWidth*CoDstHeight/2個CB1分量的長度,再加上CoDstWidth*CoDstHeight/2個CR1分量的長度.
rCICOCBSA1 就是在rCICOYSA1的基礎上加上CoDstWidth*CoDstHeight個Y1的長度.
rCICOCRSA1就是在rCICOCBSA1的基礎上加上CoDstWidth*CoDstHeight/2個CB分量的長度;
同理其它寄存器的分配就很清楚了.
07)計算突發長度,這塊手冊上說,所有突發長度必須爲2、4、8 或16 中的一個,代碼好理解,但是爲何這樣做暫時不清楚.
08) 計算預縮放比率
void CalculatePrescalerRatioShift(U32 SrcSize, U32 DstSize, U32 *ratio,U32 *shift)
{
if(SrcSize>=64*DstSize) {
Uart_Printf("ERROR: out of the prescaler range: SrcSize/DstSize = %d(< 64)/n",SrcSize/DstSize);
while(1);
}
else if(SrcSize>=32*DstSize) {
*ratio=32;
*shift=5;
}
else if(SrcSize>=16*DstSize) {
*ratio=16;
*shift=4;
}
else if(SrcSize>=8*DstSize) {
*ratio=8;
*shift=3;
}
else if(SrcSize>=4*DstSize) {
*ratio=4;
*shift=2;
}
else if(SrcSize>=2*DstSize) {
*ratio=2;
*shift=1;
}
else {
*ratio=1;
*shift=0;
}
}
根據SrcSize / DstSize 的不同值,設定不同的預縮放倍率,代碼很簡單.
09)註釋09,似乎從手冊上找不到解釋,有能給解釋下.
後面的代碼都是針對視頻預覽通道的,與視頻編碼通道一樣,不再詳細講述.