利用AXI VDMA實現OV5640攝像頭采集
導讀:攝像頭采樣圖像數據后經過VDMA進入DDR,通過PS部分控制,經過三級緩存,將DDR中保持的圖形數據通過VDMA發送出去。在FPGA的接收端口產生VID OUT時序驅動HDMI顯示器顯示圖形。
一、 基礎知識點
1、OV5640和VDMA之間同步信號的配置,輸入端采樣視頻流協議中的tuser作為同步信號。
2、VDMA主要端口:
(1)、S_AXI_Lite:寄存器配置接口,用於軟件配置VDMA,並讀取狀態信息。
(2)、S_AXI_S2MM:視頻流入端,接收外來的視屏數據。
(3)、M_AXI_MM2S:AXI4全協議主端,從DDR中讀取數據給M_AXI_MM2S。
(4)、M_AXI_S2MM:AXI4全協議主端,從DDR中讀取的數據給M_AXI_S2MM
(5)、M_AXIS_MM2S:視頻流接口,向外發送數據。
3、PL中的DMA IP對於DDR和PL存儲資源來說是主機(Master),DMA對於PS來說是從機(Slave),PS通過AXI_Lite配置DMA的寄存器從原地址向目的地址轉移多少數據,DMA通過AHP讀寫DDR,通過AXI讀寫PL資源。
4、在ZYNQ芯片內部,PS和PL是共享DDR控制器的,PS訪問DDR只要操作DDR虛擬地址的應設計可,對於PL,要接入DDR,必須通過AXI_HP端口。
二、 框架分析
1、PS配置
(1)、processing_system7_0
PS模塊處理單元,選擇的是端口M_AXI_GP0,用於通過AXI-Lite總線配置外設,引出IIC總線,硬件中已經將其搭到IIC總線,同時引出兩個時鍾FCLK_CLK0(100Mhz)作為系統時鍾,FCLK_CLK1(25Mhz)作為OV640攝像頭驅動時鍾,同時引出FCLK_RESET_N復位信號,用於系統中斷時對系統進行復位。引出PS側的DDR管腳,由此可以通過DMA讀寫DDR,同時PL側DMA可以通過AXI-HP總線讀取DDR的數據。對於兩個AXI總線(S_AXI_HP0和M_AXI_GP0),兩個時鍾工作在相同的時鍾下,都連接到FCLK_CLK0(100Mhz)。
此外,PS側開啟中斷輸入,檢測中斷。
(2)VDMA
輸出部分:
VDMA數據接口可以分為讀寫通過,通過M_AXI_S2MM端口將數據流寫入DDR3。通過M_AXI_MM2S端口可以從DDR3讀取數據,並通過M_AXI_MM2S端口將DDR3中讀取出的數據以AXIS_Stream類型輸出。
VDMA與Zynq processor通過AXI_Interconnect進行連接。
輸入部分:
在VDMA的IP配合部分,通過ZYNQ processor的M_AXI_GP接口進行配置。
視頻數據輸入是OV5640的數據經過video to Stream核實現的數據輸入。對於這些AXI協議類型的總線,需要同步於ZYNQ自帶的GP時鍾,也就是ACLK,S00_ACLK,M00_ACLK,S01_ACLK都要用FCLK_CLK0時鍾。
(3)Processor System Reset
輸入信號:
系統復位處理器,slowest_sync_clk最小同步時鍾,復位信號與時鍾進行同步。復位輸入信號是ZYNQ processor的FCLK_RESET_N。
輸出信號
interconnect_aresetn:互聯復位信號,連接到各個AXI connect連接模塊的復位信號輸入。
peripheral_aresetn:外圍模塊復位信號,作為其他各個外設模塊的AXI總線復位信號輸入。
三、 代碼分析
1、 關於XiixPs_MasterSendPolled()函數
該函數在主模式下啟動輪循(Polled)模式發送,講數據發送到Fifo,並等待從機接收數據,如果設備無法送fifo中讀取數據,導致發送超時而失敗。
1.1 傳入參數:
s32 XIicPs_MasterSendPolled(XIicPs *InstancePtr, u8 *MsgPtr, s32 ByteCount, u16 SlaveAddr)
@ 指向XiicPs實例結構的指針
@指向發送緩存區的指針
@要發送的字節數
@從設備的地址
1.2 語句1
if (((InstancePtr->IsRepeatedStart) != 0) ||
((ByteCount > XIICPS_FIFO_DEPTH) != 0U)) {
XIicPs_WriteReg(BaseAddr, XIICPS_CR_OFFSET,
XIicPs_ReadReg(BaseAddr, (u32)XIICPS_CR_OFFSET) |
(u32)XIICPS_CR_HOLD_MASK);
}
作用:
判斷實例結構中時都設置為重復啟動或者要放的數據的長度大於FIFO的深度。
由於條件1、2不成立,所以不會執行將IIC控制寄存器的bit4置一,該位的作用是:
0:在接收、發送完畢后,允許主機終止發送
1:在沒有數據接收與發送時,保持時鍾線為低直到主機操作
1.2 語句2 建立主機模式
(void)XIicPs_SetupMaster(InstancePtr, SENDING_ROLE);
SENDING_ROLE=1;
1.3 將所有的錯誤中斷進行關聯
Intrs = (u32)XIICPS_IXR_ARB_LOST_MASK | (u32)XIICPS_IXR_TX_OVR_MASK | (u32)XIICPS_IXR_NACK_MASK;
1.4 啟用中斷前將所有中斷標志位清除
IntrStatusReg = XIicPs_ReadReg(BaseAddr, XIICPS_ISR_OFFSET);
XIicPs_WriteReg(BaseAddr, XIICPS_ISR_OFFSET, IntrStatusReg);
采用的方式為先讀取中斷標志寄存器,然后將讀出的數據再進行寫入,寄存器類型為wtc,即write true clear,由此清除標志位。
1.5 清空發送fifo
(void)TransmitFifoFill(InstancePtr);
然后寫入0V5640的地址,再將要發送的數據經過fifo發送出去。
1.6 等待發送完畢
while (XIicPs_BusIsBusy(InstancePtr));
查詢IIC狀態寄存器的發送標志位,0x04,改為為1的話表示發送完畢,結束該部分。
2、 對於VDMA的配置
(1)、Xil_Out32((VDMA_BASEADDR + 0x030), 0x108B);// enable circular mode
該地址對應的是S2MM VDMA控制寄存器,寫入0x108B,即0x1000_1000_1010。該寄存器用於控制VDMA S2MM,可以實現復位、使能鎖相同步、設定幀存切換模式、啟動VDMA讀寫通道等操作。
bit3:使能鎖相同步或者動態鎖相同步模式
0:關閉Genlocken和動態Genlock同步。
1:開啟Genlock或Genlock同步
該位僅在通道配置為鎖相同步從接口或者動態鎖相從接口時才起作用。配置成鎖相同步主接口時,該位為保留位,恆為0.
bit2:先將通道進行復位
0:正常操作
1:復位S2MM通道。
bit1:制定幀存為循環模式還是停留模式。
0:停留模式,顯示用緩存也將停留在指定的幀存
1:循環模式,循環切換顯示用緩存頁。
bit0:控制VDMA的運行和停止,開始任何VDMA操作前,該位必須置一。
0:停止
1:運行
(2)、設置幀存的起始地址
Xil_Out32((VDMA_BASEADDR + 0x0AC), VIDEO_BASEADDR0); // start address
Xil_Out32((VDMA_BASEADDR + 0x0B0), VIDEO_BASEADDR1); // start address
Xil_Out32((VDMA_BASEADDR + 0x0B4), VIDEO_BASEADDR2); // start address
S2MM_START_ADDRESS(1-16):Ach~E8h
以上幾位用於設置S2MM的幀存地址。有最多32個寄存器用於存放幀存的起始地址,其分布在兩個bank上:bank0和bank1,每個bank上有16個寄存器。
存儲地址如何配置:
本次例程中用到了三幀緩存,采用了VIDEO_BASEADDR0、VIDEO_BASEADDR1、VIDEO_BASEADDR2三個地址,三個基地址的分配取決於輸入和輸出的位寬,DDR3的地址是0x0000_0000,實際使用時最好偏移0x0100_00000的地址來存儲應用的數據,因為程序在運行過程中需要一些內存來運行,為了保證不沖突,對其加一個偏移量。設置的空間還要保證可以存儲一幀數據,例如使用1280*720*4=3,686,400,轉換成16進制為384,000,0x0100_0000+0x0138_4000,小於VIDEO_BASEADDR1的基地址0x0200_0000,因此分配是合理的。
(3)、設置幀延遲和跨度寄存器
Xil_Out32((VDMA_BASEADDR + 0x0A8), (H_STRIDE*3)); //h offset (H_STRIDE* 3) bytes
S2MM幀延遲和跨度寄存器,該寄存器有兩個作用,
bit24~bit28:
僅用於Genlock從模式,bit24~bit28指定從接口要比主接口延遲多少個幀。
bit15~bit0:
低16位指定水平方向的跨度,同樣以字節為單位。跨度指的是每兩行第一個像素之間間隔的數據個數,可參見VDMA幀存格式。
(4)、設置S2MM_HSIZE S2MM水平方向尺寸
該寄存器的低16bit用於指定每一行有多少個字節的數據要傳輸,例如顯示分辨率為640*480,每個像素4個字節(RGB+Alpha),其值應該設置為640*4
Xil_Out32((VDMA_BASEADDR + 0x0A4), (H_ACTIVE*3)); // h size (H_ACTIVE * 3) bytes
對於純24bit的RGB數據,每一行有1280x3個字節,所以向寄存器寫入行有效數x3。
(5)、設置S2MM_VSIZE垂直方向尺寸
Xil_Out32((VDMA_BASEADDR + 0x0A0), V_ACTIVE); // v size (V_ACTIVE)
該寄存器的低13bit用於指定總共有多少行。
3、 對於DDR3讀取數據的配置
3.1 MM2S VDMA控制寄存器配置
Xil_Out32((VDMA_BASEADDR + 0x000), 0x8B); // enable circular mode
0x8B: 0x1000_1010
bit3:使能鎖相同步或者動態鎖相同步模式。
0:關閉鎖相同步或者動態鎖相同。
1:開啟鎖相同步或者動態鎖相同。
bit2:復位控制
0:正常操作
1:復位MM2S通道
bit1:指定幀存為循環模式還是停留模式
0:停留模式,顯示用的緩存頁將停留在指定的幀存。
1:循環模式,循環切換顯示用緩存頁。
bit0:運行/停止控制,控制VDMA通道的停止和運行。
0:停止。
1:運行。
3.2 設置MM2S幀存起始地址
Xil_Out32((VDMA_BASEADDR + 0x05c), VIDEO_BASEADDR0); // start address
Xil_Out32((VDMA_BASEADDR + 0x060), VIDEO_BASEADDR1); // start address
Xil_Out32((VDMA_BASEADDR + 0x064), VIDEO_BASEADDR2); // start address
3.3 設置MM2S幀延遲和跨度寄存器
該寄存器僅適用於Genlock從模式,指定從接口要比主接口至少延遲多少個幀。低16位指定水平方向上的跨度,以字節為單位。
Xil_Out32((VDMA_BASEADDR + 0x058), (H_STRIDE*3)); // h offset (H_STRIDE * 3) bytes
3.4 MM2S水平方向顯示大小寄存器設置
低16bit指定每一行有多少字節的數據要進行傳輸。
Xil_Out32((VDMA_BASEADDR + 0x054), (H_ACTIVE*3)); // h size (H_ACTIVE * 3) bytes
3.5 MM2S垂直方向顯示大小寄存器配置
該寄存器的低13位指定總共有多少行要進行顯示
Xil_Out32((VDMA_BASEADDR + 0x050), V_ACTIVE); // v size (V_ACTIVE)