最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

7、利用LabVIEW/C/Python庫函數(shù)節(jié)點直接調(diào)用Linux RT動態(tài)鏈接庫so文件里面的PCIe函數(shù)

2021-06-16 11:42 作者:神電測控  | 我要投稿

?? ?7.1、利用sudo cp拷貝指令將上位機下發(fā)給樹莓派的so文件拷貝到指定路徑下!

?????? ?在后續(xù)第8節(jié)里面可以看到,當(dāng)我們將Linux RT下的LabVIEW VI編譯生成rtexe可執(zhí)行程序后,LabVIEW默認(rèn)會在樹莓派的home根目錄下創(chuàng)建一個lvuer文件夾,這個文件夾里面存放的都是LabVIEW RT相關(guān)的文件。

?????? ?因此,為了統(tǒng)一起見,我們把上面編譯出來的so動態(tài)鏈接庫也拷貝到這個路徑下,這樣LabVIEW在自動搜索相關(guān)驅(qū)動文件時,一瞬間就能找到并快速進行鏈接。具體的路徑是/home/lvuser/natinst/bin;為了減少樹莓派里面的路徑深度,我們將前面的so文件復(fù)制一份,然后重新命名為:xillybus-LV-DLW30.so,如圖9-111所示。

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

圖9-111:將編譯出來的so文件復(fù)制一份重命名,縮短名稱

?? ? ? ? 接著利用VNC軟件將本地電腦上的so文件傳輸?shù)綐漭勺烂嫔?,如圖9-112所示。此時,樹莓派桌面上就有了一個名為“xillybus-LV-DLW30.so”的驅(qū)動文件,如圖9-113所示。

圖9-112:將重命名后的so文件利用VNC拷貝到樹莓派桌面上
圖9-113:傳輸?shù)綐漭勺烂嫔系膕o文件

? ? ? ? 最后,我們需要利用帶管理員權(quán)限的cp指令,將剛剛傳輸?shù)綐漭勺烂嫔系膕o文件復(fù)制到NI LabVIEW Linux RT創(chuàng)建的bin文件夾(/home/lvuser/natinst/bin)里面,因為直接鼠標(biāo)拖拽不行。用戶可以在樹莓派內(nèi)置的LXterminal終端里面輸入帶管理員權(quán)限的復(fù)制指令:sudo cp /home/pi/Desktop/xillybus-LV-DLW30.so ??/home/lvuser/natinst/bin

按下回車之后,沒有報錯,如圖9-114所示。然后打開/home/lvuser/natinst/bin這個目錄,可以看到,我們剛剛利用cp指令拷貝進去的so動態(tài)鏈接庫驅(qū)動文件,如圖9-115所示。

圖9-114:利用帶管理員權(quán)限的cp指令將樹莓派桌面上的so文件拷貝到指定路徑下
圖9-115:拷貝到LabVIEW Linux RT文件夾里面(與startup.rtexe同一個目錄)


?? ?7.2、樹莓派Linux RT端PCIe DMA FIFO驅(qū)動程序講解(LabVIEW/C/Python/QT調(diào)用so文件,神電提供lvlib庫)

?????? ?當(dāng)帶PCIe的FPGA硬件被樹莓派里面的Linux系統(tǒng)識別成功后,我們就可以在樹莓派里面編寫一個Linux RT端的應(yīng)用程序來與之通信,進行數(shù)據(jù)交互了。為了方便廣大用戶的使用,我們將8上8下共計16個通道的中間層DMA高速傳輸封裝成了Linux系統(tǒng)下的so動態(tài)鏈接庫,這樣對于使用不同編程語言(LabVIEW\C\C++\C#\Python)開發(fā)RT端實時應(yīng)用程序的用戶來說,直接調(diào)用我們封裝好的so驅(qū)動就可以了。這個so文件前面我們已經(jīng)拷貝到樹莓派里面了,如圖9-116所示。

圖9-116:我們給用戶編譯好的8上8下共計16個通道的Linux系統(tǒng)下的so動態(tài)鏈接庫

? ? ? ? 下面重點給用戶講解一下我們針對LabVIEW Linux RT系統(tǒng)封裝好的一個lvlib庫文件,對于熟悉LabVIEW編程的工程師來說,直接將lvlib里面的VI拖拽到自己的RT程序里面就可以完成RT端跟下位機FPGA之間的PCIe通信了,一目了然。圖9-117顯示的是我們封裝好的樹莓派Linux系統(tǒng)下的LabVIEW My FPGA PCIe Toolkit軟件工具包項目瀏覽器截圖。

圖9-117:封裝出來的FPGA PCIe DMA樹莓派Linux系統(tǒng)下的LabVIEW項目庫

? ? ? ? 里面主要包含兩大塊,分別是Linux RT端的FPGA PCIe范例程序(Example-PC虛擬文件夾)和中間層的lvlib庫文件。其中Linux RT端的范例程序在后續(xù)的樹莓派PCIe基礎(chǔ)和高級實驗里面再給大家做詳細(xì)的講解;本節(jié)先著重介紹一下我們封裝的Linux系統(tǒng)下的 lvlib庫函數(shù)(VI)。

?????? ?首先,在參照了NI FPGA 板卡的DMA傳輸工作流程之后,我們特地有針對性的封裝,將一個上行(FPGA-->Host)DMA傳輸過程分成7個步驟,分別對應(yīng)7個子VI,下行則不需要DMA機制:

l? PCIe DMA通道初始化并開辟指定深度的緩沖區(qū)(PC_FIFO_DMA_Poly_Init_DLW30.vi)

l? PCIe DMA通道啟動傳輸(PC_FIFO_DMA_Poly_Start_DLW30.vi)

l? PCIe DMA通道緩沖區(qū)存在的字節(jié)數(shù)數(shù)量(PC_FIFO_Poly_Buffer_Length_DLW30.vi)

l? PCIe DMA通道緩沖區(qū)讀取指定長度的數(shù)據(jù)(PC_FIFO_DMA_Poly_Read_DLW30.vi)

l? PCIe DMA通道停止傳輸(PC_FIFO_DMA_Poly_Stop_DLW30.vi)

l? PCIe DMA通道等待退出(PC_FIFO_DMA_Poly_Exit_Wait_DLW30.vi)

l? PCIe DMA通道銷毀(PC_FIFO_DMA_Poly_Destroy_DLW30.vi)

?????? ?注意:所有調(diào)用so文件的子VI都要設(shè)置成“任意線程”,切不可設(shè)置成“用戶線程”,如圖9-118所示。否則一旦so里面的函數(shù)出現(xiàn)讀寫阻塞之后,會導(dǎo)致LabVIEW界面無響應(yīng),卡死狀態(tài),更沒有辦法進行探針調(diào)試,切記!

圖9-118:LabVIEW調(diào)用底層有阻塞函數(shù)的so函數(shù)時,一定要設(shè)置成“任意線程運行”

? ? ? ? 下面我們對這7個多態(tài)子VI分別進行講解,之所以設(shè)計成多態(tài)VI,是為了方便用戶編程的時候,可以直接選擇切換通道。

?????? ?1)PC_FIFO_DMA_Poly_Init_DLW30.vi

?????? ?這個子VI可以用來完成對指定的FPGA PCIe通道進行初始化,同時還可以按照用戶輸入的數(shù)值向樹莓派申請開辟一個指定深度(長度或者大小,單位字節(jié))的緩沖區(qū),如圖9-119所示。圖中所示可以這樣理解:針對64位位寬的PCIe DMA ch0(通道0)向樹莓派Linux(主機)申請開辟一個512M字節(jié)的緩沖區(qū),緩沖區(qū)首地址或者說指針放在第3個DMA引用當(dāng)中,如果申請的緩沖區(qū)成功,那么對應(yīng)輸出的指示燈會點亮。

圖9-119:Linux RT主機端(樹莓派)的PCIe DMA通道初始化VI函數(shù)

? ? ? ??注意:初始化函數(shù)的緩沖區(qū)深度設(shè)置,跟后面的(PC_FIFO_DMA_Poly_Read_DLW30.vi)讀取長度之間需要滿足一定的關(guān)系,否則會出現(xiàn)讀取丟點的情況。原因是我們開辟的緩沖區(qū)(假設(shè)FIFO深度為M),實際上是一個異步的環(huán)形FIFO,F(xiàn)PGA端會把數(shù)據(jù)源源不斷的往這個FIFO里面寫,然后應(yīng)用層,比如LabVIEW會從這個FIFO里面取數(shù)據(jù),至于什么時候讀,讀多少?取決于這個FIFO里面現(xiàn)有的數(shù)據(jù)長度,一般的上位機讀取機制是:當(dāng)上位機判斷到這個FIFO已經(jīng)有了至少N個點時,我們就調(diào)用這個函數(shù)(PC_FIFO_DMA_Poly_Read_DLW30.vi)把N個點讀取出來,余下的數(shù)據(jù)還會留在FIFO里面,由于開辟的是環(huán)形FIFO,所以讀取位置會不斷的從開頭到末尾掃描,因此,在跨越邊界長度的時候,會出現(xiàn)讀取截斷或者丟點的現(xiàn)象。結(jié)論是M要能整除N才行。

?????? ?舉例說明:比如我們開辟了一個500MByte深度的緩沖區(qū),然后每次讀取2048個Byte點,實際上500M/2048無法整除,這樣就會導(dǎo)致在讀取最后一幀的時候,讀出來的數(shù)據(jù)有問題,為了避免這個問題,我們有兩種解決方法:如果讀取的長度事先確定了,那么我們在申請開辟緩沖區(qū)長度的時候,將讀取長度乘上一個整數(shù)就可以了,比如開辟2048×100000=204.8MByte;如果讀取長度是變化的,那么需要滿足整除這個條件,比如我們開辟的環(huán)形FIFO大小是500M,那么讀取長度可以選擇5、10、100、1000之類的,不能選擇1024或者3之類的。上面講解的機制和原理,用戶一定要記在心里!??!

?????? ?小心:在后續(xù)的PCIe+OV5640攝像頭例程里面,可以看到,720p RGB565格式下的每幀圖像字節(jié)是1280×720×2=1843200Byte,如果用戶想要每次讀取一幅圖像刷新顯示,那么在調(diào)用初始化函數(shù)(PC_FIFO_DMA_Poly_Init_DLW30.vi)開辟緩沖區(qū)長度的時候,一定要設(shè)置為1843200Byte的整數(shù)倍,比如184.32MByte或者368.64MByte。

?????? ?2)PC_FIFO_DMA_Poly_Start_DLW30.vi

?????? ?這個多態(tài)子VI的作用是用來控制Linux RT主機端的PCIe DMA FIFO通道是否接收下位機FPGA發(fā)送的數(shù)據(jù),相當(dāng)于啟動了DMA傳輸,一旦調(diào)用了這個VI,那么只要下位機FPGA有數(shù)據(jù)在發(fā)送,通過Linux任務(wù)管理器就能看到我們前面申請開辟的緩沖區(qū)里面就會有源源不斷的數(shù)據(jù)進來,如果下位機FPGA不發(fā)送上行數(shù)據(jù),那么這個DMA通道也會一直處于等待接收狀態(tài),相當(dāng)于線程阻塞了。這個子VI不需要輸入?yún)?shù),如圖9-120所示。通過顯示控件“DMA啟動傳輸成功?”指示燈狀態(tài),可以判斷PCIe DMA通道是否成功開啟傳輸模式,由于我們支持8個DMA通道同時并行傳輸數(shù)據(jù),所以前面我們初始化了哪些DMA通道,這里我們就需要利用“PC_FIFO_DMA_Poly_Start_DLW30.vi”函數(shù)來開啟這些通道的數(shù)據(jù)傳輸使能。

圖9-120:Linux RT主機端(樹莓派)的PCIe DMA啟動通道數(shù)據(jù)傳輸VI函數(shù)

? ? ? ? 3)PC_FIFO_Poly_Buffer_Length_DLW30.vi

?????? ?這個子VI的功能跟我們平時常用的一個VISA屬性節(jié)點“串口緩沖區(qū)字節(jié)數(shù)”功能類似,就是用戶可以通過輪詢的方式,查看當(dāng)前Linux RT主機端(樹莓派)的DMA通道對應(yīng)的緩沖區(qū)里面存在多少字節(jié)的數(shù)據(jù),比如,當(dāng)字節(jié)數(shù)量大于10KBytes的時候,我們再調(diào)用讀取VI將這些數(shù)據(jù)從FIFO緩沖區(qū)里面取走,這樣可以提高讀取效率,同時也能降低讀取頻率,釋放CPU給其他線程使用。通過調(diào)用這個子VI可以實時觀察到緩沖區(qū)里面的數(shù)據(jù)增加或者減少,對于不定長傳輸也會起到一定的作用。這個子VI返回的長度單位是Byte,通常會跟FIFO Read函數(shù)一起使用。

圖9-121:Linux RT主機端(樹莓派)的PCIe DMA緩沖區(qū)字節(jié)數(shù)量查詢函數(shù)

? ? ? ? 4)PC_FIFO_DMA_Poly_Read_DLW30.vi

?????? ?當(dāng)我們通過前面的“PC_FIFO_Poly_Buffer_Length_DLW30.vi”函數(shù)查詢到樹莓派Linux緩沖區(qū)里面已經(jīng)有了我們需要的數(shù)據(jù)時,接下來就可以利用這個“PC_FIFO_DMA_Poly_Read_DLW30.vi”函數(shù)從FIFO里面把數(shù)據(jù)讀取出來,給到我們的應(yīng)用程序進行處理、顯示或者流盤保存等操作了。如圖9-122所示,這個VI需要用戶給定讀取數(shù)據(jù)的長度值,單位是Byte字節(jié),指定讀取的長度不能超過緩沖區(qū)已有的數(shù)據(jù)長度;其次,從緩沖區(qū)里面讀取出來的數(shù)據(jù)都是最原始的字節(jié)數(shù)組或者說是字符串,用戶可以根據(jù)實際情況,利用數(shù)值選板里面的“強制類型轉(zhuǎn)換”函數(shù)將原始的字節(jié)數(shù)組轉(zhuǎn)成需要的數(shù)據(jù)類型,比如下位機FPGA發(fā)送的是有符號32位類型的波形數(shù)據(jù),那么我們可以參考圖9-122所示的那樣,將真實的數(shù)據(jù)轉(zhuǎn)換出來,如果用戶在界面上給定讀取的是數(shù)據(jù)是波形點數(shù),那么Size_Read應(yīng)該×4比較好,這樣轉(zhuǎn)換后的點數(shù)就跟實際的點數(shù)對應(yīng)上了。

圖9-122:從Linux RT主機端(樹莓派)的PCIe DMA通道FIFO緩沖區(qū)里面讀取指定長度的字節(jié)數(shù)據(jù)

? ? ? ??注意:初始化函數(shù)的緩沖區(qū)深度設(shè)置,跟后面的(PC_FIFO_DMA_Poly_Read_DLW30.vi)讀取長度之間需要滿足一定的關(guān)系,否則會出現(xiàn)讀取丟點的情況。原因是我們開辟的緩沖區(qū)(假設(shè)FIFO深度為M),實際上是一個異步的環(huán)形FIFO,F(xiàn)PGA端會把數(shù)據(jù)源源不斷的往這個FIFO里面寫,然后應(yīng)用層,比如LabVIEW會從這個FIFO里面取數(shù)據(jù),至于什么時候讀,讀多少?取決于這個FIFO里面現(xiàn)有的數(shù)據(jù)長度,一般的上位機讀取機制是:當(dāng)上位機判斷到這個FIFO已經(jīng)有了至少N個點時,我們就調(diào)用這個函數(shù)(PC_FIFO_DMA_Poly_Read_DLW30.vi)把N個點讀取出來,余下的數(shù)據(jù)還會留在FIFO里面,由于開辟的是環(huán)形FIFO,所以讀取位置會不斷的從開頭到末尾掃描,因此,在跨越邊界長度的時候,會出現(xiàn)讀取截斷或者丟點的現(xiàn)象。結(jié)論是M要能整除N才行。

?????? ?舉例說明:比如我們開辟了一個500MByte深度的緩沖區(qū),然后每次讀取2048個Byte點,實際上500M/2048無法整除,這樣就會導(dǎo)致在讀取最后一幀的時候,讀出來的數(shù)據(jù)有問題,為了避免這個問題,我們有兩種解決方法:如果讀取的長度事先確定了,那么我們在申請開辟緩沖區(qū)長度的時候,將讀取長度乘上一個整數(shù)就可以了,比如開辟2048×100000=204.8MByte;如果讀取長度是變化的,那么需要滿足整除這個條件,比如我們開辟的環(huán)形FIFO大小是500M,那么讀取長度可以選擇5、10、100、1000之類的,不能選擇1024或者3之類的。上面講解的機制和原理,用戶一定要記在心里?。?!

?????? ?小心:在后續(xù)的PCIe+OV5640攝像頭例程里面,可以看到,720p RGB565格式下的每幀圖像字節(jié)是1280×720×2=1843200Byte,如果用戶想要每次讀取一幅圖像刷新顯示,那么在調(diào)用初始化函數(shù)(PC_FIFO_DMA_Poly_Init_DLW30.vi)開辟緩沖區(qū)長度的時候,一定要設(shè)置為1843200Byte的整數(shù)倍,比如184.32MByte或者368.64MByte。

?????? ?5)PC_FIFO_DMA_Poly_Stop_DLW30.vi

?????? ?當(dāng)我們不需要PCIe DMA通道或者退出Linux RT主機端(樹莓派)應(yīng)用程序之前,用戶可以調(diào)用這里的“PC_FIFO_DMA_Poly_Stop_DLW30.vi”函數(shù)來關(guān)停Linux RT主機端(樹莓派)的DMA通道的數(shù)據(jù)接收功能,此時,即使下位機FPGA還在發(fā)送數(shù)據(jù),Linux RT主機端的FIFO緩沖區(qū)也不會接收任何數(shù)據(jù),處于關(guān)閉狀態(tài)。這個VI跟前面的第2個函數(shù)(PC_FIFO_DMA_Poly_Start_DLW30.vi)其實是一對功能互反的VI。用戶調(diào)用的時候,只需要選擇想要關(guān)閉的DMA通道就可以了,如圖9-123所示。

圖9-123:關(guān)閉Linux RT主機端(樹莓派)的PCIe DMA通道的數(shù)據(jù)傳輸

? ? ? ? 6)PC_FIFO_DMA_Poly_Exit_Wait_DLW30.vi

?????? ?雖然前面我們把Linux RT主機端(樹莓派)的DMA緩沖區(qū)接收功能關(guān)閉了,但是中間層的驅(qū)動里面,還有一些人為開辟的線程沒有關(guān)掉,我們特地給用戶封裝了一個等待線程退出函數(shù)“PC_FIFO_DMA_Poly_Exit_Wait_DLW30.vi”,通過這個函數(shù)可以確保中間層的so里面所有線程都退出了,防止出現(xiàn)線程阻塞卡死等狀態(tài)。這個VI的調(diào)用非常簡單,用戶只需要選擇想要退出的DMA通道就可以了,如圖9-124所示。

圖9-124:等待Linux RT主機端(樹莓派)的中間層的so里面的PCIe DMA通道退出

? ? ? ? 7)PC_FIFO_DMA_Poly_Destroy_DLW30.vi

?????? ?當(dāng)DLL里面的DMA Read線程退出之后,最后我們還需要調(diào)用一下這里的“PC_FIFO_DMA_Poly_Destroy_DLW30.vi”函數(shù)將so里面申請開辟的FIFO緩沖區(qū)句柄、讀取和狀態(tài)輪詢線程的引用以及對應(yīng)的句柄全部銷毀掉,防止出現(xiàn)內(nèi)存泄露導(dǎo)致Linux系統(tǒng)崩潰。這個VI跟前面的第一個函數(shù)“PC_FIFO_DMA_Poly_Init_DLW30.vi”功能是反的,前面那個函數(shù)在so里面負(fù)責(zé)申請開辟資源,創(chuàng)建線程等等,這里的VI則負(fù)責(zé)將其全部銷毀,否則一旦發(fā)生內(nèi)存泄露,后果不堪設(shè)想。這個VI的用法也很簡單,用戶只需要選擇想要銷毀的DMA通道資源就可以了,如圖9-125所示。

圖9-125:銷毀Linux RT的中間層so里面開辟的FIFO緩沖區(qū)指針、引用句柄和線程句柄

? ? ? ??注意:最后這3個VI,一般是在退出Linux RT應(yīng)用程序之前,連在一起使用的,如圖9-126所示。但是千萬需要注意的是,在執(zhí)行DMA停止、退出、銷毀這3個VI的時候,下位機FPGA里面的PCIe DMA Write不能停發(fā)數(shù)據(jù),也就是說,下位機FPGA要一直往PCIe DMA CLIP對應(yīng)的通道里面寫數(shù)據(jù),否則會造成中間層so里面的DMA緩沖區(qū)Read讀取線程處于-1,永不超時狀態(tài),也就是線程阻塞了,Linux RT主機端(樹莓派)應(yīng)用程序會陷入卡死狀態(tài)。因此,我們建議用戶,等到so里面的PCIe DMA FIFO和讀取線程銷毀之后,Linux RT主機端(樹莓派)再發(fā)送“停止寫入指令”給下位機FPGA,最后再關(guān)閉下行的PCIe通道資源。

圖9-126:退出Linux RT主機端(樹莓派)應(yīng)用程序之前,依次調(diào)用FIFO停止、等待線程退出、銷毀FIFO和引用句柄

? ? ? ? 介紹完上行的PCIe DMA Read這7個函數(shù)之后,下面來看看我們給大家封裝的3個下行(PC-->FPGA)Write子VI,如圖9-127所示。這3個函數(shù)就可以實現(xiàn)將Linux RT主機端(樹莓派)的數(shù)據(jù)或者指令或者參數(shù)直接通過PCIe總線下發(fā)傳輸寫到FPGA芯片里面。

圖9-127:lvlib庫里面的3個下行PCIe通道寫函數(shù)

l? FPGA_FIFO_Write_Pipe_Init_DLW30_Linux.vi

l? FPGA_FIFO_Write_Pipe_Send_DLW30_Linux.vi

l? FPGA_FIFO_Write_Pipe_Close_DLW30_Linux.vi

?????? ?下面我們逐一給大家講解一下這3個完成PCIe下行通信的VI含義和用法。

?????? ?1)FPGA_FIFO_Write_Pipe_Init_DLW30_Linux.vi

?????? ?這個VI可以打開用戶指定的PCIe下行(Host-->FPGA)通道并完成初始化工作,如圖9-128所示。因為我們給用戶封裝了8個下行寫通道(當(dāng)然了,還有前面介紹的8個上行讀通道),因此,調(diào)用這個VI的時候,需要根據(jù)實際情況選擇一個合適的下行通道作為Linux RT主機端(樹莓派)發(fā)送數(shù)據(jù)給FPGA的Pipe管道,比如,我們發(fā)送的是一些指令形式的數(shù)據(jù),或者數(shù)據(jù)量非常小的一些參數(shù)等等,那么可以選擇低速的8位位寬的Channel4~7作為載體;如果我們需要快速下發(fā)一些諸如任意信號發(fā)生器的波形數(shù)據(jù),那么可以選擇吞吐率高的64位或者32位位寬的Channel0或者Channel1作為下行傳輸通道。如果下行寫通道初始化成功,這個VI會返回一個唯一的句柄“fd_write”,后面所有跟寫通道相關(guān)的函數(shù)VI都需要借助這個引用指針“fd_write”。

圖9-128:PCIe下行寫通道初始化VI

? ? ? ? 2)FPGA_FIFO_Write_Pipe_Send_DLW30_Linux.vi

?????? ?當(dāng)下行的寫通道成功打開之后,我們就可以利用這里的“FPGA_FIFO_Write_Pipe_Send_DLW30_Linux.vi”函數(shù)將Linux RT主機端(樹莓派)的數(shù)據(jù)下發(fā)給FPGA了,如圖9-129所示。需要注意的地方是,這個通道寫VI的數(shù)據(jù)輸入類型是字節(jié)數(shù)組,所以,如果外部的波形信號或者參數(shù)或者指令等數(shù)據(jù)類型不是字節(jié),那么我們需要通過數(shù)值選板里面的“強制類型轉(zhuǎn)換”VI先轉(zhuǎn)一下,圖9-129中顯示的是把Linux RT主機端(樹莓派)一個浮點型的正弦信號先通過I64轉(zhuǎn)成有符號64位整形數(shù)組,然后通過“強制類型轉(zhuǎn)換”變成U8字節(jié)數(shù)組給到我們的“FPGA_FIFO_Write_Pipe_Send_DLW30_Linux.vi”函數(shù),寫到FPGA芯片里面去。

圖9-129:PCIe下行通道數(shù)據(jù)發(fā)送VI(Host-->FPGA)

? ? ? ? 3)FPGA_FIFO_Write_Pipe_Close_DLW30_Linux.vi

?????? ?當(dāng)我們需要退出Linux RT主機端(樹莓派)應(yīng)用程序之前,需要利用這里的“FPGA_FIFO_Write_Pipe_Close_DLW30_Linux.vi”函數(shù)來把先前打開的PCIe寫通道引用句柄指針關(guān)掉,如圖9-130所示。防止出現(xiàn)內(nèi)存泄露,更重要的是,如果不執(zhí)行關(guān)閉的話,下次再打開這個通道的時候,會提示這個通道被占用了,所以務(wù)必要檢查一下之前開啟的通道句柄是否銷毀了。

圖9-130:關(guān)閉PCIe下行寫通道引用句柄,釋放通道占用

? ? ? ??總結(jié):用戶在熟悉和掌握了上面介紹的這10個Linux系統(tǒng)下的子函數(shù)(VI)含義和用法之后,就可以大刀闊斧的開始編寫Linux RT主機端(樹莓派)的PCIe應(yīng)用程序了。關(guān)于這部分內(nèi)容,我們會在后面的第9和第10節(jié)實驗部分,做詳細(xì)的講解。

?? ?7.3、樹莓派Linux RT端PCIe Memory驅(qū)動程序講解(C/LabVIEW調(diào)用so文件,神電提供lvlib庫,可以變相實現(xiàn)通過PCIe讀寫FPGA前面板控件等狀態(tài)信息)

?????? ?當(dāng)帶PCIe的FPGA硬件被系統(tǒng)識別成功后,我們就可以編寫一個Linux RT主機端(樹莓派)的應(yīng)用程序來與之通信,比如用來監(jiān)控下位機FPGA前面板上的控件值或者下發(fā)控制指令給FPGA了。為了方便廣大用戶的使用,我們將2上2下共計4個通道的中間層Memory讀寫通道也封裝到前面給用戶介紹過的那個Linux系統(tǒng)下的so動態(tài)鏈接庫里面了,這樣對于使用不同編程語言(LabVIEW\C\C++\C#\Python)開發(fā)Linux RT主機端(樹莓派)應(yīng)用程序的用戶來說,直接調(diào)用我們封裝好的so驅(qū)動就可以了。這個so位于本書配套的云盤里面,如圖9-131所示。

圖9-131:我們給用戶封裝好的Linux系統(tǒng)下包含8對DMA FIFO和2對Memory通道的so動態(tài)鏈接庫文件

? ? ? ? 下面再回顧一下前面我們針對LabVIEW軟件封裝好的一個lvlib庫文件,對于熟悉LabVIEW編程的工程師來說,直接將lvlib里面的VI拖拽到自己的程序里面就可以完成樹莓派跟下位機FPGA之間的PCIe通信了,一目了然。圖9-132顯示的是我們封裝好的Linux RT主機端(樹莓派)的LabVIEW My FPGA PCIe Toolkit軟件工具包項目瀏覽器截圖。

圖9-132:封裝出來的FPGA PCIe DMA和Memory樹莓派主機端的LabVIEW項目庫

? ? ? ? 里面主要包含兩大塊,分別是PCIe DMA FIFO相關(guān)的多態(tài)VI和Memory通道讀寫相關(guān)的多態(tài)VI;DMA FIFO相關(guān)函數(shù)我們在前面7.2節(jié)已經(jīng)做了詳細(xì)介紹,本節(jié)著重介紹一下我們封裝的lvlib庫里面的Memory相關(guān)函數(shù)(VI),這些函數(shù)位于這個虛擬文件夾“PC_Memory_RW_2Chs_Func”里面。

?????? ?為了提高Memory讀寫的效率,我們特地有針對性的封裝,將Memory上行讀數(shù)據(jù)(FPGA-->Host)和下行寫數(shù)據(jù)(Host-->FPGA)的傳輸過程分成3個步驟,分別對應(yīng)6個子VI,也就是打開、讀寫、關(guān)閉,如圖9-133所示。

圖9-133:lvlib庫里面的6個PCIe Memory通道讀寫函數(shù)

l? PCIe Memory讀通道初始化(FPGA_Memory_Read_Poly_Init_DLW30.vi)

l? PCIe Memory讀通道數(shù)據(jù)讀?。‵PGA_Memory_Read_Poly_Receive_DLW30.vi)

l? PCIe DMA讀通道關(guān)閉(FPGA_Memory_Read_Poly_Close_DLW30.vi)

?

l? PCIe Memory寫通道初始化(FPGA_Memory_Write_Poly_Init_DLW30.vi)

l? PCIe Memory寫通道數(shù)據(jù)下發(fā)(FPGA_Memory_Write_Poly_Send_DLW30.vi)

l? PCIe DMA寫通道關(guān)閉(FPGA_Memory_Write_Poly_Close_DLW30.vi)

?????? ?再次強調(diào):所有調(diào)用so的子VI都要設(shè)置成“任意線程”,切不可設(shè)置成“用戶線程”,如圖9-134所示。否則一旦DLL里面的函數(shù)出現(xiàn)讀寫阻塞之后,會導(dǎo)致LabVIEW界面無響應(yīng),卡死狀態(tài),更沒有辦法進行探針調(diào)試,切記!

圖9-134:LabVIEW調(diào)用底層有阻塞函數(shù)的so時,一定要設(shè)置成“任意線程運行”

? ? ? ? 下面我們對這6個多態(tài)子VI分別進行講解,之所以設(shè)計成多態(tài)VI,是為了方便用戶編程的時候,可以直接選擇切換通道。下面我們逐一給大家講解一下這6個PCIe Memory通道讀寫的VI含義和用法。

?????? ?1)FPGA_Memory_Read_Poly_Init_DLW30.vi

?????? ?這個子VI可以用來完成對指定的FPGA PCIe Memory讀通道進行初始化,本質(zhì)上就是打開Xillybus里面的Memory通道文件名,如果Memory讀通道初始化成功,該VI會返回一個唯一的句柄“fd_read”,后面所有跟讀通道相關(guān)的函數(shù)VI都需要借助這個引用指針“fd_read”,如圖9-135所示。用戶可以根據(jù)實際情況,在左側(cè)的下拉列表“Pipe_Read_Name”里面選擇16位或者32位位寬的Memory通道作為讀取通道,其中,后綴mem0對應(yīng)16位位寬,mem1對應(yīng)32位位寬,也就是每次操作的數(shù)據(jù)是雙字節(jié)還是4字節(jié)。

圖9-135:PCIe Memory讀通道初始化VI

? ? ? ? 2)FPGA_Memory_Read_Poly_Receive_DLW30.vi

?????? ?當(dāng)上行的Memory讀取通道打開成功之后,我們就可以利用這里的“FPGA_Memory_Read_Poly_Receive_DLW30.vi”函數(shù)按照指定的地址把下位機FPGA里面的數(shù)組或者Memory元素讀取出來了,如圖9-136所示。該VI除了錯誤簇外,還有4個連接端口,最上面的左右兩側(cè)分別是Memory讀通道打開后的引用句柄,另外就是地址輸入控件“address_read_U16”和數(shù)據(jù)輸出控件“data_read_U16”。就是用戶可以將FPGA里面的數(shù)組或者Memory指定位置(地址)處的元素讀取到Linux RT主機端(樹莓派)來。

圖9-136:PCIe Memory讀通道讀取下位機FPGA Memory函數(shù)

? ? ? ? 這個VI本質(zhì)上調(diào)用的是一個so函數(shù),里面實際上包含了兩個系統(tǒng)函數(shù),分別是lseek文件位置偏移函數(shù)和read文件內(nèi)容讀取函數(shù)。如果用戶不會LabVIEW的話,也可以使用其他語言,比如Python、C++、C#等直接調(diào)用系統(tǒng)里面的lseek和read函數(shù)就可以實現(xiàn)圖9-136這個VI一樣的功能和效果,因此我們開發(fā)的FPGA PCIe CLIP不限制操作系統(tǒng),無論是Windows還是Linux都支持。

?????? ?下面我們雙擊打開這個VI,看看程序框圖里面有沒有什么需要注意的地方,如圖9-137所示。

圖9-137:PCIe Memory讀通道讀取VI程序框圖

? ? ? ? 為了讓用戶在執(zhí)行讀取操作的時候,地址是連續(xù)的,也就是地址0對應(yīng)的U16或者U32數(shù)組的第1個元素,地址1對應(yīng)第2個元素,地址2對應(yīng)第3個元素,以此類推,而不需要考慮什么雙字節(jié)或者4字節(jié)的倍數(shù)整除問題,為此,我們特地在這個VI里面加入了一個轉(zhuǎn)換公式,如圖9-138~9-139所示,其中,16位位寬的Memory地址,我們下發(fā)之前給FPGA的地址做了偏移(如果是Linux系統(tǒng),直接將外面給進來的地址×2;如果是Windows系統(tǒng),需要×2+1;對于32位位寬的Memory地址做了偏移(如果是Linux系統(tǒng),直接將外面給進來的地址×4;如果是Windows系統(tǒng),需要×4+3千萬要注意:如果地址賦值不正確,會導(dǎo)致LabVIEW應(yīng)用程序死機!

圖9-138:16位的Memory地址偏移算法(Linux系統(tǒng)下的地址偏移)
圖9-139:32位的Memory地址偏移算法(Linux系統(tǒng)下的地址偏移)

? ? ? ? 最后需要注意的一點是:PCIe Memory跟FIFO一樣,對超過8位位寬的數(shù)據(jù),存在大小端格式轉(zhuǎn)換問題,為了減少FPGA資源的消耗,鑒于Memory讀寫速度很慢,我們特地將大小端格式轉(zhuǎn)換代碼放在了Linux RT主機端(樹莓派),也就是本節(jié)介紹的Memory通道讀寫VI里面,如圖9-140所示。將讀出來的字節(jié)數(shù)組進行翻轉(zhuǎn),然后再利用“強制類型轉(zhuǎn)換”函數(shù)變成U16或者U32就可以了。

圖9-140:將Memory大小端格式轉(zhuǎn)換放在Linux RT主機端(樹莓派)VI里面處理,下位機FPGA則不需要

? ? ? ? 3)FPGA_Memory_Read_Poly_Close_DLW30.vi

?????? ?當(dāng)我們需要退出上位機應(yīng)用程序之前,需要利用這里的“FPGA_Memory_Read_Poly_Close_DLW30.vi”函數(shù)來把先前打開的PCIe Memory讀通道引用句柄指針關(guān)掉,如圖9-141所示。防止出現(xiàn)內(nèi)存泄露,更重要的是,如果不執(zhí)行關(guān)閉的話,下次再打開這個通道的時候,會提示這個通道被占用了,所以務(wù)必要檢查一下之前開啟的通道句柄是否銷毀了。另外,為了保證Memory通道引用被強制關(guān)閉,我們在輸入錯誤簇之前加入一個“清除錯誤簇”函數(shù),防止上游的錯誤導(dǎo)致該VI不執(zhí)行關(guān)閉功能。

圖9-141:關(guān)閉PCIe Memory上行讀通道引用句柄,釋放通道占用

? ? ? ? 4)FPGA_Memory_Write_Poly_Init_DLW30.vi

?????? ?這個子VI可以用來完成對指定的FPGA PCIe Memory寫通道進行初始化,本質(zhì)上就是打開Xillybus里面的Memory通道文件名,如果Memory寫通道初始化成功,該VI會返回一個唯一的句柄“fd_write”,后面所有跟寫通道相關(guān)的函數(shù)VI都需要借助這個引用指針“fd_write”,如圖9-142所示。用戶可以根據(jù)實際情況,在左側(cè)的下拉列表“Pipe_Write_Name_Linux”里面選擇16位或者32位位寬的Memory通道作為寫入通道,其中,后綴mem0對應(yīng)16位位寬,mem1對應(yīng)32位位寬,也就是每次操作的數(shù)據(jù)是雙字節(jié)還是4字節(jié)。

圖9-142:PCIe Memory寫通道初始化VI

? ? ? ? 5)FPGA_Memory_Write_Poly_Send_DLW30.vi

?????? ?當(dāng)下行的Memory寫取通道打開成功之后,我們就可以利用這里的“FPGA_Memory_Write_Poly_Send_DLW30.vi”函數(shù)將Linux RT主機端(樹莓派)的地址和數(shù)據(jù)下發(fā)給下位機FPGA,按照索引值(地址)將數(shù)組或者Memory里面的元素覆蓋更新,如圖9-143所示。該VI除了錯誤簇外,還有4個連接端口,最上面的左右兩側(cè)分別是Memory寫通道打開后的引用句柄,另外就是地址輸入控件“address_write_U16”和數(shù)據(jù)輸入控件“data_write_U16”。就是用戶可以將Linux RT主機端(樹莓派)的數(shù)據(jù)按照給定的地址對FPGA里面的數(shù)組或者Memory指定位置(地址)處的元素進行覆蓋更新。

圖9-143:PCIe Memory寫通道下位機FPGA Memory寫入更新函數(shù)

? ? ? ? 這個VI本質(zhì)上調(diào)用的是一個so函數(shù),里面實際上包含了兩個系統(tǒng)函數(shù),分別是lseek文件位置偏移函數(shù)和write文件內(nèi)容寫入函數(shù)。如果用戶不會LabVIEW的話,也可以使用其他語言,比如Python、C++、C#等直接調(diào)用系統(tǒng)里面的lseek和write函數(shù)就可以實現(xiàn)圖9-143這個VI一樣的功能和效果,因此我們開發(fā)的FPGA PCIe CLIP不限制操作系統(tǒng),無論是Windows還是Linux都支持。

?????? ?下面我們雙擊打開這個VI,看看程序框圖里面有沒有什么需要注意的地方,如圖9-144所示。

圖9-144:PCIe Memory寫通道寫入VI程序框圖

? ? ? ? 為了讓用戶在執(zhí)行寫入操作的時候,地址是連續(xù)的,也就是地址0對應(yīng)的U16或者U32數(shù)組的第1個元素,地址1對應(yīng)第2個元素,地址2對應(yīng)第3個元素,以此類推,而不需要考慮什么雙字節(jié)或者4字節(jié)的倍數(shù)整除問題,為此,我們特地在這個VI里面加入了一個轉(zhuǎn)換公式,如圖9-145~9-146所示,其中,16位位寬的Memory地址,我們下發(fā)之前給FPGA的地址做了偏移(如果是Linux系統(tǒng),直接將外面給進來的地址×2;如果是Windows系統(tǒng),需要×2+1;對于32位位寬的Memory地址做了偏移(如果是Linux系統(tǒng),直接將外面給進來的地址×4;如果是Windows系統(tǒng),需要×4+3千萬要注意:如果地址賦值不正確,會導(dǎo)致LabVIEW應(yīng)用程序死機!

圖9-145:16位的Memory地址偏移算法(Linux系統(tǒng)下的地址偏移)
圖9-146:32位的Memory地址偏移算法(Linux系統(tǒng)下的地址偏移)

? ? ? ? 最后需要注意的一點是:PCIe Memory跟FIFO一樣,對超過8位位寬的數(shù)據(jù),存在大小端格式轉(zhuǎn)換問題,為了減少FPGA資源的消耗,鑒于Memory讀寫速度很慢,我們特地將大小端格式轉(zhuǎn)換代碼放在了Linux RT主機端(樹莓派),也就是本節(jié)介紹的Memory通道讀寫VI里面,如圖9-147所示。先利用“強制類型轉(zhuǎn)換”函數(shù)將U16或者U32轉(zhuǎn)換成字節(jié)數(shù)組,再利用“反轉(zhuǎn)一維數(shù)組”函數(shù)進行反轉(zhuǎn)就可以了。

圖9-147:將Memory大小端格式轉(zhuǎn)換放在Linux RT主機端(樹莓派)VI里面處理,下位機FPGA則不需要

? ? ? ? 6)FPGA_Memory_Write_Poly_Close_DLW30.vi

?????? ?當(dāng)我們需要退出上位機應(yīng)用程序之前,需要利用這里的“FPGA_Memory_Write_Poly_Close_DLW30.vi”函數(shù)來把先前打開的PCIe Memory寫通道引用句柄指針關(guān)掉,如圖9-148所示。防止出現(xiàn)內(nèi)存泄露,更重要的是,如果不執(zhí)行關(guān)閉的話,下次再打開這個通道的時候,會提示這個通道被占用了,所以務(wù)必要檢查一下之前開啟的通道句柄是否銷毀了。另外,為了保證Memory通道引用被強制關(guān)閉,可以在輸入錯誤簇前面加一個“清除錯誤簇”函數(shù),防止上游的錯誤導(dǎo)致該VI不執(zhí)行關(guān)閉功能。

圖9-148:關(guān)閉PCIe Memory下行寫通道引用句柄,釋放通道占用

? ? ? ??總結(jié):用戶在熟悉和掌握了上面介紹的這6個PCIe Memory子函數(shù)(VI)的含義和用法之后,就可以大刀闊斧的開始編寫Linux RT主機端(樹莓派)上的PCIe Memory應(yīng)用程序了。關(guān)于這部分內(nèi)容,我們會在后續(xù)的實驗68里面,做詳細(xì)的講解。

7、利用LabVIEW/C/Python庫函數(shù)節(jié)點直接調(diào)用Linux RT動態(tài)鏈接庫so文件里面的PCIe函數(shù)的評論 (共 條)

分享到微博請遵守國家法律
荃湾区| 成都市| 兴业县| 呼伦贝尔市| 道真| 肃南| 岳阳县| 上饶市| 文昌市| 金溪县| 乌海市| 万山特区| 石城县| 呼图壁县| 阳信县| 双江| 鄂州市| 繁峙县| 青川县| 平舆县| 汽车| 咸丰县| 桃园市| 郯城县| 沙湾县| 平邑县| 兰考县| 铜川市| 辽中县| 增城市| 临洮县| 敖汉旗| 顺义区| 吉安县| 双流县| 洛宁县| 海南省| 泗水县| 儋州市| 齐齐哈尔市| 中牟县|