01概括 前文提供了ad7606的驅動程序,本文通過串口將8路adc采集的數據傳輸給上位機顯示。工程的總體框圖如下圖所示,ad7606_drive驅動模塊采集ad7606八路數據,八個數據處理模塊data_dispose把采集的八路數據轉換為電壓數據,并且轉換為BCD編碼。uart_byte模塊選擇對應通道的數據發送給串口發送模塊uart_tx,傳輸給上位機顯示。
圖1 工程框圖注意本工程ad7606驅動雖然是200Ksps采樣率一直在采集數據,但由于uart的傳輸速率過低,其實并不是采集的所有數據都會通過uart傳輸給上位機,本工程只能簡單采集穩定的電壓,做做電壓表可以,并不能用來采集波形數據。

圖2 添加波形文件對應結果如下所示,相關模塊的所有信號已經全部添加且分類。

圖3 波形界面之后重新開始仿真,點擊run -all即可,仿真文件運行到$stop位置自動停止仿真。 由于TestBench給ad7606八個通道賦值相同數據,因此最終采集的數據如下圖所示。
圖4 八個通道采集數據通道0的數據處理模塊如下圖所示,首先采集到的16位補碼數據為16’d4107,對應原碼為15’d4107,然后計算對應的模擬電壓((4107) / (2^15)) * 5V = 0.6266V = 626mv,最終將計算結果轉換為BCD碼,與下圖結果完全一致。

圖5 數據處理模塊仿真而uart_byte模塊把八個通道計算結果根據含義保存到對應存儲器中,同時根據計數器的值向下游模塊傳輸數據。下圖所示,每當上游模塊完成一次數據轉換,均會將存儲器的數據刷新。

圖6 存儲數據傳輸數據相關的計數器如下所示,uart_tx_rdy為高電平表示uart發送模塊處于空閑狀態。為了防止uart接收端反應不過來,這里會讓空閑狀態多持續一段時間,才開始發送下字節數據。使用計數器rdy_cnt來計數這段時間。
圖7 數據傳輸時序上圖中ch_cnt用來表示當前傳輸第幾個通道的數據,而byte_cnt用來計數當前發送該通道第幾字節數據。上圖中表示傳輸通道0第0字節數據為8’d65,對應字符A,然后發送第1字節數據8’d68對應D,之后傳輸通道對應數值,一般給用戶顯示都是從1開始的,因此8’d49表示通道1。上述三個數據顯示結果為AD1,后續仿真類似,不再贅述。最后就是uart發送模塊,如下圖所示,發送8’h41(與8’d65相等),先發送低位數據,起始位會占用一位,結果正確。

圖8 uart發送模塊仿真由于工程比較簡單,因此仿真這塊做得都比較簡潔,文末會提供工程,拿到后可以自行仿真。04上板實測 由于手里這塊板子的設計問題,導致最終沒辦法上板,具體問題如下所示。板子上有一個uart的公頭,如下所示。

圖9 板載uart接口對應原理圖如下所示,公頭的2腳接的是板子的uart發送引腳。

圖10 uart原理圖下圖是淘寶上該接口的線材接口信息,母頭線材2腳也是發送引腳?沒找到2腳用于接收的母頭線材,不知道設計這塊板子的工程師是如何想的,黑金自家也沒有賣這種線材。

圖11 淘寶線材在網上找了一張功能類似的截圖,上板后的結果應該與下圖一致。
圖12 串口調試助手顯示雖然不能使用串口顯示數據,但是也可以通過signal tap抓一下實測數據,如果時序沒有問題,依舊可以保證工程沒有問題。如下圖所示,ad7606的八個通道全部懸空,然后下載程序到板子中。
圖13 懸空ad7606八個通道輸入引腳使用signal tap抓取通道0的數據,如下所示,對應數據為16’d11857,轉換為模擬電壓為(16’d11857 / (2^15)) * 5V = 1.809V = 1809mv,與抓取的bcd碼轉換結果一致。

圖14 轉換時序由于uart每發送一個字節數據中間會間隔很長時間,為了看到依次發送的數據對不對,則需要抓取很多數據,signal tap采用如下設置,只抓取o_uart_tx_vld為高電平的數據,舍棄其余無用數據。

圖15 signal tap設置抓取數據如下所示,65對應的字符A,54對應的是通道6,由此可知第一幀數據為通道6的數據,第二幀為通道7的數據。

圖16 抓取發送的uart幀數據與下圖譯碼器部分代碼對比,可以驗證上圖的正確性。


圖17 對應譯碼最后修改signal tap抓取uart發送模塊時序,如下圖所示,發送數據為8’h2e,先發送低位數據。
圖18 抓取uart發送模塊時序相關時序驗證結束,整體時序是沒有問題的,如果換個正常的uart硬件接口可以直接使用。


驅動模塊直接查看前面即可,本文不再贅述。
02程序設計 2.1數據處理模塊
AD7606模塊輸入電壓范圍是[-5,+5],本模塊功能是將16位adc補碼數據轉換為電壓值,最終轉換為BCD碼,便于后續傳輸給上位機顯示。如下所示,首先把補碼數據轉換為15位原碼數據,且保存符號位。轉換為15位數據ch_data后,對應的模擬電壓值為[0,5V],符號位決定電壓值的正負。
always@(posedge clk or negedge rst_n)begin
r_din_vld <= {r_din_vld[0],din_vld};
end
//對應通道數據有效時,把補碼數據轉換為原碼且保留符號位;
always@(posedge clk or negedge rst_n)begin
if(~rst_n)begin
r_ch_data <=?'d0;
r_ch_sig <= 'd0;
end
elseif(din_vld)begin
r_ch_data <= din[15] ? (~din[14:0] +1) : din[14:0];
r_ch_sig <= din[15];
end
end
電壓值y,ad7606采集的15位數據x的轉換關系為y=x/(2^15)。因此通過下面的方式轉換,為了保留計算精度,電壓值ch_voltage為真實值的2^15*1000倍,單位mv。ch_voltage = ch_data * 1000的計算方式采用移位和加法計算。
//對應通道數據乘以5000,得到真實電壓的2^15倍,單位mv;
always@(posedge clk)begin
if(r_din_vld[0])begin//5/32768≈0.15mv,ADC采集數據乘以0.15得電壓,0.15*2^15*1000=5000
r_ch_voltage <= {r_ch_data,12'd0} + {r_ch_data,9'd0} + {r_ch_data,8'd0} + {r_ch_data,7'd0} + {r_ch_data,3'd0};
end
end
舍棄ch_voltage的低15位數據,等效除以2^15,電壓值r_voltage計算就是轉換結果,單位mv。
always@(posedge clk)begin
r_voltage<= r_din_vld[1] ? r_ch_voltage[27:15] : r_voltage;//除以2^15次方,得到mv電壓;
r_voltage_vld<= r_din_vld[1];
voltage_sig<= r_ch_sig;
end
最后通過hex2bcd模塊把轉換后的電壓值轉換為BCD碼,該模塊全部采用參數化設計,前面有一篇文章也講了具體實現原理,有興趣的可以點擊查看。
//--###############################################################################################
//--#
//--# File Name : data_dispose
//--# Designer : 數字站
//--# Tool : Quartus 2018.1
//--# Design Date : 2024.10.10
//--# Description :
//--# Version : 0.0
//--# Coding scheme : UTF-8(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module data_dispose (
input clk ,//系統時鐘信號;
input rst_n ,//系統復位信號,低電平有效;
input [15:0] din ,//輸入數據;
input din_vld ,//輸入數據有效指示信號,高電平有效;
output [15:0] voltage ,//輸出電壓,單位mv
output reg voltage_sig ='d0 ,//輸出電壓的正負,低電平表示正;
output voltage_vld //輸出數據有效指示信號,高電平有效;
);
reg [1:0] r_din_vld ='d0 ;
reg [14:0] r_ch_data ;//
reg [27:0] r_ch_voltage ='d0 ;
reg r_ch_sig ='d0 ;
reg [12:0] r_voltage ='d0 ;
reg r_voltage_vld='d0 ;
always@(posedge clk or negedge rst_n)begin
r_din_vld<={r_din_vld[0],din_vld};
end
//對應通道數據有效時,把補碼數據轉換為原碼且保留符號位;
always@(posedge clk or negedge rst_n)begin
if(~rst_n)begin
r_ch_data<='d0;
r_ch_sig<='d0;
end
elseif(din_vld)begin
r_ch_data<=din[15]?(~din[14:0]+1) : din[14:0];
r_ch_sig<=din[15];
end
end
//對應通道數據乘以5000,得到真實電壓的2^15倍,單位mv;
always@(posedge clk)begin
if(r_din_vld[0])begin//5/32768≈0.15mv,ADC采集數據乘以0.15得電壓,0.15*2^15*1000=5000
r_ch_voltage<={r_ch_data,12'd0}+{r_ch_data,9'd0}+{r_ch_data,8'd0}+{r_ch_data,7'd0}+{r_ch_data,3'd0};
end
end
//產生輸出數據;
always@(posedge clk)begin
r_voltage<=r_din_vld[1]?r_ch_voltage[27:15] : r_voltage;//除以2^15次方,得到mv電壓;
r_voltage_vld<=r_din_vld[1];
voltage_sig<=r_ch_sig;
end
//調用十六進制轉BCD模塊,將13位十六進制數據轉換成16位BCD碼;
hex2bcd #(
.IN_DATA_W (13 )//輸入數據位寬;
)
u_hex2bcd (
.clk ( clk ),//系統時鐘;
.rst_n ( rst_n ),//系統復位,低電平有效;
.din ( r_voltage ),//輸入二進制數據;
.din_vld ( r_voltage_vld ),//輸入數據有效指示信號,高電平有效;
.rdy ( ),//忙閑指示信號,該信號高電平時才能輸入有效數據;
.dout ( voltage ),//輸出8421BCD碼;
.dout_vld ( voltage_vld )//輸出數據有效指示信號,高電平有效;
);
endmodule
2.2
uart字節發送模塊
首先通過存儲器保存八路adc轉換后的bcd數據,保存的時候還要將數據加48轉換為ascii編碼,因為0對應的ascii碼對應數值為48。
always@(posedge clk)begin
if(i_voltage_vld[0])begin
r_voltage_sig[0] <= ?i_voltage_sig[0];//保存通道0的符號位;
r_voltage_g[0] <= i_voltage0[15:12] +8'd48;//將通道0的個位數據轉換為ASCII對應字符;
r_voltage_s[0] <= i_voltage0[11:8] +8'd48;//將通道0的十分位數據轉換為ASCII對應字符;
r_voltage_b[0] <= i_voltage0[7:4] +8'd48;//將通道0的百分位數據轉換為ASCII對應字符;
r_voltage_q[0] <= i_voltage0[3:0] +8'd48;//將通道0的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[1])begin
r_voltage_sig[1] <= ?i_voltage_sig[1];//保存通道1的符號位;
r_voltage_g[1] <= i_voltage1[15:12] +8'd48;//將通道1的個位數據轉換為ASCII對應字符;
r_voltage_s[1] <= i_voltage1[11:8] +8'd48;//將通道1的十分位數據轉換為ASCII對應字符;
r_voltage_b[1] <= i_voltage1[7:4] +8'd48;//將通道1的百分位數據轉換為ASCII對應字符;
r_voltage_q[1] <= i_voltage1[3:0] +8'd48;//將通道1的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[2])begin
r_voltage_sig[2] <= ?i_voltage_sig[2];//保存通道2的符號位;
r_voltage_g[2] <= i_voltage2[15:12] +8'd48;//將通道2的個位數據轉換為ASCII對應字符;
r_voltage_s[2] <= i_voltage2[11:8] +8'd48;//將通道2的十分位數據轉換為ASCII對應字符;
r_voltage_b[2] <= i_voltage2[7:4] +8'd48;//將通道2的百分位數據轉換為ASCII對應字符;
r_voltage_q[2] <= i_voltage2[3:0] +8'd48;//將通道2的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[3])begin
r_voltage_sig[3] <= ?i_voltage_sig[3];//保存通道3的符號位;
r_voltage_g[3] <= i_voltage3[15:12] +8'd48;//將通道3的個位數據轉換為ASCII對應字符;
r_voltage_s[3] <= i_voltage3[11:8] +8'd48;//將通道3的十分位數據轉換為ASCII對應字符;
r_voltage_b[3] <= i_voltage3[7:4] +8'd48;//將通道3的百分位數據轉換為ASCII對應字符;
r_voltage_q[3] <= i_voltage3[3:0] +8'd48;//將通道3的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[4])begin
r_voltage_sig[4] <= ?i_voltage_sig[4];//保存通道4的符號位;
r_voltage_g[4] <= i_voltage4[15:12] +8'd48;//將通道4的個位數據轉換為ASCII對應字符;
r_voltage_s[4] <= i_voltage4[11:8] +8'd48;//將通道4的十分位數據轉換為ASCII對應字符;
r_voltage_b[4] <= i_voltage4[7:4] +8'd48;//將通道4的百分位數據轉換為ASCII對應字符;
r_voltage_q[4] <= i_voltage4[3:0] +8'd48;//將通道4的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[5])begin
r_voltage_sig[5] <= ?i_voltage_sig[5];//保存通道5的符號位;
r_voltage_g[5] <= i_voltage5[15:12] +8'd48;//將通道5的個位數據轉換為ASCII對應字符;
r_voltage_s[5] <= i_voltage5[11:8] +8'd48;//將通道5的十分位數據轉換為ASCII對應字符;
r_voltage_b[5] <= i_voltage5[7:4] +8'd48;//將通道5的百分位數據轉換為ASCII對應字符;
r_voltage_q[5] <= i_voltage5[3:0] +8'd48;//將通道5的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[6])begin
r_voltage_sig[6] <= ?i_voltage_sig[6];//保存通道6的符號位;
r_voltage_g[6] <= i_voltage6[15:12] +8'd48;//將通道6的個位數據轉換為ASCII對應字符;
r_voltage_s[6] <= i_voltage6[11:8] +8'd48;//將通道6的十分位數據轉換為ASCII對應字符;
r_voltage_b[6] <= i_voltage6[7:4] +8'd48;//將通道6的百分位數據轉換為ASCII對應字符;
r_voltage_q[6] <= i_voltage6[3:0] +8'd48;//將通道6的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[7])begin
r_voltage_sig[7] <= ?i_voltage_sig[7];//保存通道7的符號位;
r_voltage_g[7] <= i_voltage7[15:12] +8'd48;//將通道7的個位數據轉換為ASCII對應字符;
r_voltage_s[7] <= i_voltage7[11:8] +8'd48;//將通道7的十分位數據轉換為ASCII對應字符;
r_voltage_b[7] <= i_voltage7[7:4] +8'd48;//將通道7的百分位數據轉換為ASCII對應字符;
r_voltage_q[7] <= i_voltage7[3:0] +8'd48;//將通道7的千分位數據轉換為ASCII對應字符;
end
end
上位機顯示的格式為:ADX:±3.426V,上位機顯示的每個通道的數據需要發送11字節數據,并且后續還要跟兩個空格或者回車,因此每個通道需要發送13字節數據。為了便于數據接收端識別數據,當串口發送一個字節數據后,需要暫停一段時間后在發送下字節數據。因此使用一個空閑計數器對空閑時間計數,停留發送2位數據才開始下字節數據傳輸。
always@(posedge clkornegedge rst_n)begin
if(~rst_n)begin//初始值為0;
r_rdy_cnt<='d0;
end
else if(i_uart_tx_rdy)begin
if(r_end_rdy_cnt)
r_rdy_cnt <= 'd0;
else
r_rdy_cnt<=r_rdy_cnt+'d1;
end
end
always@(posedge clk)begin
r_end_rdy_cnt <= i_uart_tx_rdy && (r_rdy_cnt == 2*BPS_CNT-2);
end
通過兩個計數器分別記錄發送當前通道的第幾個數據、發送的數據屬于第幾個通道。
always@(posedge clkornegedge rst_n)begin
if(~rst_n)begin//初始值為0;
r_byte_cnt<='d0;
end
else if(r_end_rdy_cnt)begin
if(r_byte_cnt == 12)
r_byte_cnt <= 'd0;
else
r_byte_cnt<=r_byte_cnt+'d1;
end
end
//一輪需要傳輸8個通道的數據到PC端,使用一個8進制計數器對傳輸數據的通道數計數;
//當一個通道數據傳輸結束時加1,計數器采用溢出清零;
always@(posedge clk or negedge rst_n)begin
if(~rst_n)begin//初始值為0;
r_ch_cnt <= 'd0;
end
elseif(r_end_rdy_cnt&&(r_byte_cnt==12))begin
r_ch_cnt<=r_ch_cnt+'d1;
end
end
通過譯碼器轉換數據,根據字節計數器的值發送對應的數據,有部分數據根據通道不同發送不同數據。比如通道7數據發送結束后,需要發送回車和換行字符,不需要發送空格字符。
always@(posedge clk)begin
if(r_end_rdy_cnt)begin
case(r_byte_cnt)
4'd0 : o_uart_txdata <= 8'd65;//發送字符A對應的ASCCI碼值;
4'd1 : o_uart_txdata <= 8'd68;//發送字符D對應的ASCCI碼值;
4'd2 : o_uart_txdata <= r_ch_cnt + 'd49;//發送通道r_ch_cnt對應的ASCCI碼值;
4'd3 : o_uart_txdata <= 8'd58;//發送字符:對應的ASCCI碼值;
//電壓的正負值對應的ASCII碼,r_voltage_sig為高電平表示對應通道電壓為負數。
4'd4 : o_uart_txdata <= r_voltage_sig[r_ch_cnt] ? 8'd45 :8'd43;
4'd5 : o_uart_txdata <= r_voltage_g[r_ch_cnt];//發送個位電壓對應的ASCCI碼值;
4'd6 : o_uart_txdata <= 8'd46;//發送字符.對應的ASCCI碼值;
4'd7 : o_uart_txdata <= r_voltage_s[r_ch_cnt];//發送十分位對應的ASCCI碼值;
4'd8 : o_uart_txdata <= r_voltage_b[r_ch_cnt];//發送百分位對應的ASCCI碼值;
4'd9 : o_uart_txdata <= r_voltage_q[r_ch_cnt];//發送千分位對應的ASCCI碼值;
4'd10 : o_uart_txdata <=?8'd86 ;//發送字符V對應的ASCCI碼值;
4'd11 : o_uart_txdata <= (&r_ch_cnt) ??8'd10 : 8'd32;//如果是發送最后一個通道的數據,則發送換行,否則發送空格;
4'd12 : o_uart_txdata <= (&r_ch_cnt) ? 8'd13 :8'd32;//如果是發送最后一個通道的數據,則發送回車,否則發送空格;
default : o_uart_txdata <= 8'hff;
endcase
end
end
always@(posedge clk)begin
o_uart_txdata_vld <= r_end_rdy_cnt;//生成并行數據有效指示信號;
end
該模塊參考代碼如下所示:
//--###############################################################################################
//--#
//--# File Name : uart_byte
//--# Designer : 數字站
//--# Tool : Quartus 2018.1
//--# Design Date : 2024.10.10
//--# Description :
//--# Version : 0.0
//--# Coding scheme : UTF-8(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module uart_byte#(
parameter FLCK = 50_000_000 ,//系統時鐘頻率,默認50MHZ;
parameter BPS = 9600 //串口波特率;
)(
input clk ,//系統時鐘信號;
input rst_n ,//系統復位信號,低電平有效;
input [15:0] i_voltage0 ,//輸入數據;
input [15:0] i_voltage1 ,//輸入數據;
input [15:0] i_voltage2 ,//輸入數據;
input [15:0] i_voltage3 ,//輸入數據;
input [15:0] i_voltage4 ,//輸入數據;
input [15:0] i_voltage5 ,//輸入數據;
input [15:0] i_voltage6 ,//輸入數據;
input [15:0] i_voltage7 ,//輸入數據;
input [7:0] i_voltage_sig ,//各通道數據的正負數據指示信號;
input [7:0] i_voltage_vld ,//輸入數據有效指示信號,高電平有效;
input i_uart_tx_rdy ,//uart發送模塊空閑指示信號;
output reg [7:0] o_uart_txdata ='d0 ,//需要uart發送的并行數據;
output reg o_uart_txdata_vld = 'd0 //需要的發送的并行數據有效指示信號;
);
localparam BPS_CNT = FLCK/BPS ;//波特率為9600bit/s,當波特率為115200bit/s時,DATA_115200==434;
localparam BPS_CNT_W = $clog2(2*BPS_CNT-1) ;//根據BPS_CNT調用函數自動計算計數器bps_cnt位寬;
reg [7:0] r_voltage_sig ='d0 ;
reg [BPS_CNT_W-1:0] r_rdy_cnt ;
reg r_end_rdy_cnt ;
reg [7 : 0] r_voltage_g [7 : 0] ;
reg [7 : 0] r_voltage_s [7 : 0] ;
reg [7 : 0] r_voltage_b [7 : 0] ;
reg [7 : 0] r_voltage_q [7 : 0] ;
reg [3 : 0] r_byte_cnt ;//
reg [2 : 0] r_ch_cnt ;
/********** 存儲數據 *************/
always@(posedge clk)begin
if(i_voltage_vld[0])begin
r_voltage_sig[0] <= ?i_voltage_sig[0];//保存通道0的符號位;
r_voltage_g[0] <= i_voltage0[15 : 12] + 8'd48;//將通道0的個位數據轉換為ASCII對應字符;
r_voltage_s[0] <= i_voltage0[11:8] +8'd48;//將通道0的十分位數據轉換為ASCII對應字符;
r_voltage_b[0] <= i_voltage0[7 : 4] + 8'd48;//將通道0的百分位數據轉換為ASCII對應字符;
r_voltage_q[0]<= i_voltage0[3:0] +8'd48;//將通道0的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[1])begin
r_voltage_sig[1] <= ?i_voltage_sig[1];//保存通道1的符號位;
r_voltage_g[1] <= i_voltage1[15 : 12] + 8'd48;//將通道1的個位數據轉換為ASCII對應字符;
r_voltage_s[1] <= i_voltage1[11:8] +8'd48;//將通道1的十分位數據轉換為ASCII對應字符;
r_voltage_b[1] <= i_voltage1[7 : 4] + 8'd48;//將通道1的百分位數據轉換為ASCII對應字符;
r_voltage_q[1]<= i_voltage1[3:0] +8'd48;//將通道1的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[2])begin
r_voltage_sig[2] <= ?i_voltage_sig[2];//保存通道2的符號位;
r_voltage_g[2] <= i_voltage2[15 : 12] + 8'd48;//將通道2的個位數據轉換為ASCII對應字符;
r_voltage_s[2] <= i_voltage2[11:8] +8'd48;//將通道2的十分位數據轉換為ASCII對應字符;
r_voltage_b[2] <= i_voltage2[7 : 4] + 8'd48;//將通道2的百分位數據轉換為ASCII對應字符;
r_voltage_q[2]<= i_voltage2[3:0] +8'd48;//將通道2的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[3])begin
r_voltage_sig[3] <= ?i_voltage_sig[3];//保存通道3的符號位;
r_voltage_g[3] <= i_voltage3[15 : 12] + 8'd48;//將通道3的個位數據轉換為ASCII對應字符;
r_voltage_s[3] <= i_voltage3[11:8] +8'd48;//將通道3的十分位數據轉換為ASCII對應字符;
r_voltage_b[3] <= i_voltage3[7 : 4] + 8'd48;//將通道3的百分位數據轉換為ASCII對應字符;
r_voltage_q[3]<= i_voltage3[3:0] +8'd48;//將通道3的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[4])begin
r_voltage_sig[4] <= ?i_voltage_sig[4];//保存通道4的符號位;
r_voltage_g[4] <= i_voltage4[15 : 12] + 8'd48;//將通道4的個位數據轉換為ASCII對應字符;
r_voltage_s[4] <= i_voltage4[11:8] +8'd48;//將通道4的十分位數據轉換為ASCII對應字符;
r_voltage_b[4] <= i_voltage4[7 : 4] + 8'd48;//將通道4的百分位數據轉換為ASCII對應字符;
r_voltage_q[4]<= i_voltage4[3:0] +8'd48;//將通道4的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[5])begin
r_voltage_sig[5] <= ?i_voltage_sig[5];//保存通道5的符號位;
r_voltage_g[5] <= i_voltage5[15 : 12] + 8'd48;//將通道5的個位數據轉換為ASCII對應字符;
r_voltage_s[5] <= i_voltage5[11:8] +8'd48;//將通道5的十分位數據轉換為ASCII對應字符;
r_voltage_b[5] <= i_voltage5[7 : 4] + 8'd48;//將通道5的百分位數據轉換為ASCII對應字符;
r_voltage_q[5]<= i_voltage5[3:0] +8'd48;//將通道5的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[6])begin
r_voltage_sig[6] <= ?i_voltage_sig[6];//保存通道6的符號位;
r_voltage_g[6] <= i_voltage6[15 : 12] + 8'd48;//將通道6的個位數據轉換為ASCII對應字符;
r_voltage_s[6] <= i_voltage6[11:8] +8'd48;//將通道6的十分位數據轉換為ASCII對應字符;
r_voltage_b[6] <= i_voltage6[7 : 4] + 8'd48;//將通道6的百分位數據轉換為ASCII對應字符;
r_voltage_q[6]<= i_voltage6[3:0] +8'd48;//將通道6的千分位數據轉換為ASCII對應字符;
end
end
always@(posedge clk)begin
if(i_voltage_vld[7])begin
r_voltage_sig[7] <= ?i_voltage_sig[7];//保存通道7的符號位;
r_voltage_g[7] <= i_voltage7[15 : 12] + 8'd48;//將通道7的個位數據轉換為ASCII對應字符;
r_voltage_s[7] <= i_voltage7[11:8] +8'd48;//將通道7的十分位數據轉換為ASCII對應字符;
r_voltage_b[7] <= i_voltage7[7 : 4] + 8'd48;//將通道7的百分位數據轉換為ASCII對應字符;
r_voltage_q[7]<= i_voltage7[3:0] +8'd48;//將通道7的千分位數據轉換為ASCII對應字符;
end
end
/********* 發送數據 ************/
//空閑計數器,發送一字節數據后,暫停一段時間在發送下字節數據;
always@(posedge clk or negedge rst_n)begin
if(~rst_n)begin//初始值為0;
r_rdy_cnt <= 'd0;
end
elseif(i_uart_tx_rdy)begin
if(r_end_rdy_cnt)
r_rdy_cnt <=?'d0;
else
r_rdy_cnt <= r_rdy_cnt + 'd1;
end
end
always@(posedge clk)begin
r_end_rdy_cnt <= i_uart_tx_rdy && (r_rdy_cnt ==?2*BPS_CNT-2);
end
//每個通道需要發送13字節串口數據到PC端,使用一個13進制計數器對發送數據計數;
//當下游模塊空閑時表示發送完成1字節數據,計數器加1.
always@(posedge clkornegedge rst_n)begin
if(~rst_n)begin//初始值為0;
r_byte_cnt <=?'d0;
end
else if(r_end_rdy_cnt)begin
if(r_byte_cnt == 12)
r_byte_cnt <= 'd0;
else
r_byte_cnt <= r_byte_cnt +?'d1;
end
end
//一輪需要傳輸8個通道的數據到PC端,使用一個8進制計數器對傳輸數據的通道數計數;
//當一個通道數據傳輸結束時加1,計數器采用溢出清零;
always@(posedge clk or negedge rst_n)begin
if(~rst_n)begin//初始值為0;
r_ch_cnt <= 'd0;
end
elseif(r_end_rdy_cnt && (r_byte_cnt ==12))begin
r_ch_cnt <= r_ch_cnt +?'d1;
end
end
//產生下游uart模塊需要發送的并行數據;
always@(posedge clk)begin
if(r_end_rdy_cnt)begin
case(r_byte_cnt)
4'd0 : o_uart_txdata <=?8'd65;//發送字符A對應的ASCCI碼值;
4'd1 : o_uart_txdata <=?8'd68;//發送字符D對應的ASCCI碼值;
4'd2 : o_uart_txdata <= r_ch_cnt +?'d49;//發送通道r_ch_cnt對應的ASCCI碼值;
4'd3 : o_uart_txdata <=?8'd58;//發送字符:對應的ASCCI碼值;
//電壓的正負值對應的ASCII碼,r_voltage_sig為高電平表示對應通道電壓為負數。
4'd4 : o_uart_txdata <= r_voltage_sig[r_ch_cnt] ??8'd45 : 8'd43;
4'd5 : o_uart_txdata <= r_voltage_g[r_ch_cnt];//發送個位電壓對應的ASCCI碼值;
4'd6 : o_uart_txdata <=?8'd46;//發送字符.對應的ASCCI碼值;
4'd7 : o_uart_txdata <= r_voltage_s[r_ch_cnt];//發送十分位對應的ASCCI碼值;
4'd8 : o_uart_txdata <= r_voltage_b[r_ch_cnt];//發送百分位對應的ASCCI碼值;
4'd9 : o_uart_txdata <= r_voltage_q[r_ch_cnt];//發送千分位對應的ASCCI碼值;
4'd10 : o_uart_txdata <= 8'd86 ;//發送字符V對應的ASCCI碼值;
4'd11 : o_uart_txdata <= (&r_ch_cnt) ? 8'd10:8'd32;//如果是發送最后一個通道的數據,則發送換行,否則發送空格;
4'd12 : o_uart_txdata <= (&r_ch_cnt) ??8'd13 : 8'd32;//如果是發送最后一個通道的數據,則發送回車,否則發送空格;
default : o_uart_txdata <=?8'hff;
endcase
end
end
always@(posedge clk)begin
o_uart_txdata_vld <= r_end_rdy_cnt;//生成并行數據有效指示信號;
end
endmodule
uart發送模塊依舊使用以前模塊,前文詳細講解過uart接收模塊全模式設計方式,本文就不再贅述,參考代碼如下所示:
//--###############################################################################################
//--#
//--# File Name : uart_tx
//--# Designer : 數字站
//--# Tool : Quartus 2018.1
//--# Design Date : 2024.10.10
//--# Description :
//--# Version : 0.0
//--# Coding scheme : UTF-8(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module uart_tx
parameter FLCK = 50_000_000 ,//系統時鐘頻率,默認50MHZ;
parameter BPS = 9600 ,//串口波特率;
parameter DATA_W = 8 ,//發送數據位數以及輸出數據位寬;
parameter START_W = 1 ,//1位起始位;
parameter CHECK_W = 2'b00 ,//校驗位,2'b00代表無校驗位,2'b01表示奇校驗,2'b10表示偶校驗,2'b11按無校驗處理。
parameter STOP_W = 2'b01 //停止位,2'b01表示1位停止位,2'b10表示2位停止位,2'b11表示1.5位停止位;
)(
input clk ,//系統工作時鐘50MHZ
input rst_n ,//系統復位信號,低電平有效
input [DATA_W-1:0] tx_data ,//數據輸入信號。
input tx_data_vld ,//數據有效指示信號,高電平有效。
output reg uart_tx ,//uart接口數據輸出信號。
output reg tx_rdy //模塊忙閑指示信號;
);
localparam BPS_CNT = FLCK/BPS ;//波特率為9600bit/s,當波特率為115200bit/s時,DATA_115200==434;
localparam BPS_CNT_W = $clog2(BPS_CNT-1);//根據BPS_CNT調用函數自動計算計數器bps_cnt位寬;
localparam DATA_CNT = START_W + DATA_W + (^CHECK_W) + ((STOP_W==2'b11) ?2: STOP_W);//計數器計數值;
localparam DATA_CNT_W= $clog2(DATA_CNT-1);//根據計數器cnt的值,利用函數自動計算此計數器的位寬;
reg flag ;
reg tx_rdy_ff0 ;//
reg [DATA_CNT-1:0] tx_data_tmp ;
reg [BPS_CNT_W-1:0] bps_cnt ;
reg [DATA_CNT_W-1:0]data_cnt ;
wire add_bps_cnt ;
wire end_bps_cnt ;
wire end_data_cnt ;
/*發送一位數據所需要的時間*/
always @(posedge clkornegedge rst_n)begin
if(!rst_n)begin
bps_cnt <= {{BPS_CNT_W}{1'b0}};
end
elseif(add_bps_cnt)begin
if(end_bps_cnt || end_data_cnt)
bps_cnt <= {{BPS_CNT_W}{1'b0}};
else
bps_cnt <= bps_cnt + {{{BPS_CNT_W-1}{1'b0}},1'b1};
end
end
assign add_bps_cnt = flag;
assign end_bps_cnt = add_bps_cnt && bps_cnt == BPS_CNT-1;
/*發送一組數據所用時間*/
always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin//
data_cnt <= {{DATA_CNT}{1'b0}};
end
elseif(end_data_cnt)begin
data_cnt <= {{DATA_CNT}{1'b0}};
end
elseif(end_bps_cnt)begin
data_cnt <= data_cnt + {{{DATA_CNT-1}{1'b0}},1'b1};
end
end
//根據停止位的不同生成不同的計數器結束條件;
generate
if(STOP_W ==2'b11)//1.5位停止位,因為CNT_NUM沒有包含起始位,所以要等計數器計數到CNT_NUM時清零;
assign end_data_cnt = data_cnt == DATA_CNT-1&& add_bps_cnt && bps_cnt == BPS_CNT/2-1;
else//停止位為1位或者2位;
assign end_data_cnt = end_bps_cnt && data_cnt == DATA_CNT-1;
endgenerate
//這個期間UART在發送數據;
always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin
flag <=?1'b0;
end
elseif(tx_data_vld)begin
flag <=?1'b1;
end
elseif(end_data_cnt)begin
flag <=?1'b0;
end
end
//UART模塊處于忙時期,收到上游模塊數據或者正在處理上游模塊所發數據;
always@(*)begin
if(tx_data_vld || flag)begin
tx_rdy =1'b0;
end
elsebegin
tx_rdy =1'b1;
end
end
always@(posedge clk)begin
tx_rdy_ff0 <= tx_rdy;
end
//將上游模塊所發并行數據轉化為串行數據;
generate
if(CHECK_W ==2'b01)begin//奇校驗;
always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin
tx_data_tmp <= {{DATA_CNT+1}{1'b1}};
end
elseif(tx_rdy_ff0 && tx_data_vld)begin
tx_data_tmp <= {{{STOP_W}{1'b1}},~(^tx_data),tx_data,{{START_W}{1'b0}}};
end
end
end
elseif(CHECK_W ==2'b10)begin//偶校驗
always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin
tx_data_tmp <= {{DATA_CNT+1}{1'b1}};
end
elseif(tx_rdy_ff0 && tx_data_vld)begin
tx_data_tmp <= {{{STOP_W}{1'b1}},(^tx_data),tx_data,{{START_W}{1'b0}}};
end
end
end
elsebegin//無校驗
always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin
tx_data_tmp <= {{DATA_CNT+1}{1'b1}};
end
elseif(tx_rdy_ff0 && tx_data_vld)begin
tx_data_tmp <= {{{STOP_W}{1'b1}},tx_data,{{START_W}{1'b0}}};
end
end
end
endgenerate
//將串行數據按9600波特率送出,先發低位;
always@(posedge clkornegedge rst_n)begin
if(rst_n==1'b0)begin
uart_tx <=?1'b1;
end
elseif(add_bps_cnt && bps_cnt==0)begin
uart_tx <= tx_data_tmp[data_cnt];
end
end
endmodule
2.3
頂層模塊
頂層模塊如下所示,單獨列出來是因為代碼使用for循環例化了8個通道的數據處理模塊。參考代碼如下所示:
//--###############################################################################################
//--#
//--# File Name : top
//--# Designer :
//--# Tool : Quartus 2018.1
//--# Design Date : 2024.10.10
//--# Description :
//--# Version : 0.0
//--# Coding scheme : UTF-8(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module top
parameter FLCK = 100_000_000 ,//系統時鐘頻率,默認50MHZ;
parameter BPS = 115200 ,//串口波特率;
parameter UART_DATA_W = 8 ,//發送數據位數以及輸出數據位寬;
parameter START_W = 1 ,//1位起始位;
parameter CHECK_W = 2'b00 ,//校驗位,2'b00代表無校驗位,2'b01表示奇校驗,2'b10表示偶校驗,2'b11按無校驗處理。
parameter STOP_W = 2'b01 //停止位,2'b01表示1位停止位,2'b10表示2位停止位,2'b11表示1.5位停止位;
)(
input clk ,//系統時鐘,50MHz;
input rst_n ,//系統復位,低電平有效;
input busy ,//轉換完成指示信號,下降沿有效;
input frstdata ,//指示采集到的第一個數據;
input [15:0] adc_din ,//AD7606所采集到的十六位數據信號;
input uart_rx ,
output cs ,//AD7606片選信號,讀數據時拉低;
output rd ,//AD7606讀使能信號,讀數據時拉低,下降沿時AD7606將數據發送到數據線上,上升沿時可以讀出數據;
output reset ,//AD7606復位信號,高電平有效,每次復位至少拉高50ns;
output [2:0] os ,//AD7606過采樣模式信號,默認不使用過采樣;
output convst ,//AD7606采樣啟動信號,無效時高電平,采樣計數器完成時拉低兩個時鐘;
output uart_tx //uart接口數據輸出信號。
);
wire clk_100m ;//鎖相環輸出時鐘;
wire [15:0] w_7606_data ;//AD7606通道采集的補碼數據;
wire [7:0] w_7606_data_vld ;//指示AD7606輸出的數據來自哪個數據通道;
wire [15:0] w_voltage [7:0] ;//輸出電壓,單位mv
wire [7:0] w_voltage_sig ;//輸出電壓的正負,低電平表示正;
wire [7:0] w_voltage_vld ;//輸出數據有效指示信號,高電平有效;
wire [UART_DATA_W-1:0] w_tx_data ;//數據輸入信號。
wire w_tx_data_vld ;//數據有效指示信號,高電平有效。
wire w_tx_rdy ;//模塊忙閑指示信號;
//例化鎖相環
pllu_pll(
.areset ( ~rst_n ),
.inclk0(clk ),
.c0 (clk_100m )
);
//例化ad7606驅動模塊
ad7606_drive
.FCLK ( FLCK ),//系統時鐘頻率,單位Hz,默認100MHz;
.SMAPLE (200_000 )//AD7606采樣頻率,單位Hz,默認200KHz;
)
u_ad7606_drive (
.clk ( clk_100m ),//系統時鐘,100MHz;
.rst_n ( rst_n ),//系統復位,低電平有效;
.busy ( busy ),//轉換完成指示信號,下降沿有效;
.frstdata ( frstdata ),//指示采集到的第一個數據;
.adc_din ( adc_din ),//AD7606所采集到的十六位數據信號;
.cs ( cs ),//AD7606片選信號,讀數據時拉低;
.rd ( rd ),//AD7606讀使能信號,讀數據時拉低,下降沿時AD7606將數據發送到數據線上,上升沿時可以讀出數據;
.reset ( reset ),//AD7606復位信號,高電平有效,每次復位至少拉高50ns;
.os ( os ),//AD7606過采樣模式信號,默認不使用過采樣;
.convst ( convst ),//AD7606采樣啟動信號,無效時高電平,采樣計數器完成時拉低兩個時鐘;
.data ( w_7606_data ),//AD7606采集到的數據.數據均為補碼;
.data_vld ( w_7606_data_vld )//指示AD7606輸出的數據來自哪個數據通道;
);
genvar i;
//使用for循環例化8個數據處理模塊,將ad7606采集的數據轉換為bcd碼的mv電壓;
generate
for(i=0; i<8; i=i+1)begin : DATA
data_disposeu_data_dispose(
.clk ( clk_100m ),//系統時鐘信號;
.rst_n (rst_n ),//系統復位信號,低電平有效;
.din (w_7606_data ),//輸入數據;
.din_vld (w_7606_data_vld[i]),//輸入數據有效指示信號,高電平有效;
.voltage (w_voltage[i] ),//輸出電壓,單位mv
.voltage_sig (w_voltage_sig[i] ),//輸出電壓的正負,低電平表示正;
.voltage_vld (w_voltage_vld[i] )//輸出數據有效指示信號,高電平有效;
);
end
endgenerate
//例化數據處理模塊
uart_byte
.FLCK ( FLCK ),//系統時鐘頻率,默認50MHZ;
.BPS ( BPS )//串口波特率;
)
u_uart_byte(
.clk ( clk_100m ),//系統時鐘信號;
.rst_n ( rst_n ),//系統復位信號,低電平有效;
.i_voltage0 ( w_voltage[0] ),//輸入數據;
.i_voltage1 ( w_voltage[1] ),//輸入數據;
.i_voltage2 ( w_voltage[2] ),//輸入數據;
.i_voltage3 ( w_voltage[3] ),//輸入數據;
.i_voltage4 ( w_voltage[4] ),//輸入數據;
.i_voltage5 ( w_voltage[5] ),//輸入數據;
.i_voltage6 ( w_voltage[6] ),//輸入數據;
.i_voltage7 ( w_voltage[7] ),//輸入數據;
.i_voltage_sig ( w_voltage_sig ),//各通道數據的正負數據指示信號;
.i_voltage_vld ( w_voltage_vld ),//輸入數據有效指示信號,高電平有效;
.i_uart_tx_rdy ( w_tx_rdy ),//uart發送模塊空閑指示信號;
.o_uart_txdata ( w_tx_data ),//需要uart發送的并行數據;
.o_uart_txdata_vld ( w_tx_data_vld )//需要的發送的并行數據有效指示信號;
);
//例化串口發送模塊;
uart_tx
.FLCK ( FLCK ),//系統時鐘頻率,默認50MHZ;
.BPS ( BPS ),//串口波特率;
.DATA_W ( UART_DATA_W ),//發送數據位數以及輸出數據位寬;
.START_W ( START_W ),//1位起始位;
.CHECK_W ( CHECK_W ),//校驗位,2'b00代表無校驗位,2'b01表示奇校驗,2'b10表示偶校驗,2'b11按無校驗處理。
.STOP_W ( STOP_W )//停止位,2'b01表示1位停止位,2'b10表示2位停止位,2'b11表示1.5位停止位;
)
u_uart_tx (
.clk ( clk_100m ),//系統工作時鐘50MHZ
.rst_n ( rst_n ),//系統復位信號,低電平有效
.tx_data ( w_tx_data ),//數據輸入信號。
.tx_data_vld( w_tx_data_vld ),//數據有效指示信號,高電平有效。
.uart_tx ( uart_tx ),//uart接口數據輸出信號。
.tx_rdy ( w_tx_rdy )//模塊忙閑指示信號;
);
endmodule
03工程仿真 仿真的數據來源依舊使用前文ad7606驅動模塊的測試數據,對應參考代碼如下所示。
`timescale1ns/1ns
//--###############################################################################################
//--#
//--# File Name : test
//--# Designer : 數字站
//--# Tool : Quartus 2018.1
//--# Design Date : 2024.11.3
//--# Description :
//--# Version : 0.0
//--# Coding scheme : GBK(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module test();
localparam CYCLE = 20 ;//系統時鐘周期,單位ns,默認10ns;
localparam RST_TIME= 10 ;//系統復位持續時間,默認10個系統時鐘周期;
reg clk = 'd1 ;//系統時鐘,100MHz;
reg rst_n = 'd1 ;//系統復位,高電平有效;
reg busy = 'd0 ;//轉換完成指示信號,下降沿有效;
reg frstdata = 'd0 ;//指示采集到的第一個數據;
reg [15:0] adc_din = 'd0 ;//AD7606所采集到的十六位數據信號;
wire cs ;//AD7606片選信號,讀數據時拉低;
wire rd ;//AD7606讀使能信號,讀數據時拉低,下降沿時AD7606將數據發送到數據線上,上升沿時可以讀出數據;
wire reset ;//AD7606復位信號,高電平有效,每次復位至少拉高50ns;
wire [2:0] os ;//AD7606過采樣模式信號,默認不使用過采樣;
wire convst ;//AD7606采樣啟動信號,無效時高電平,采樣計數器完成時拉低兩個時鐘;
wire uart_tx ;//uart接口數據輸出信號。
//待測試的模塊例化
top u_top (
.clk ( clk ),//系統時鐘,50MHz;
.rst_n ( rst_n ),//系統復位,低電平有效;
.busy ( busy ),//轉換完成指示信號,下降沿有效;
.frstdata ( frstdata ),//指示采集到的第一個數據;
.adc_din ( adc_din ),//AD7606所采集到的十六位數據信號;
.cs ( cs ),//AD7606片選信號,讀數據時拉低;
.rd ( rd ),//AD7606讀使能信號,讀數據時拉低,下降沿時AD7606將數據發送到數據線上,上升沿時可以讀出數據;
.reset ( reset ),//AD7606復位信號,高電平有效,每次復位至少拉高50ns;
.os ( os ),//AD7606過采樣模式信號,默認不使用過采樣;
.convst ( convst ),//AD7606采樣啟動信號,無效時高電平,采樣計數器完成時拉低兩個時鐘;
.uart_tx ( uart_tx )//uart接口數據輸出信號。
);
initial begin
forever #(CYCLE/2) clk = ~clk;//生成本地時鐘50M
end
//產生復位信號
localparamDATA_NUM=400;
reg [15:0] stimulus[1:DATA_NUM];
integerPattern;
initial begin
$readmemb("waveform_bit.txt",stimulus);//從外部TX文件(waveform_bit.txt)讀入數據作為測試激勵;
Pattern=1;
#1;
rst_n=1'b0;
#(CYCLE*RST_TIME);
rst_n=1'b1;
repeat(20)@(posedge clk);
repeat(DATA_NUM)begin
@(posedge convst);
busy<=1'b1;
repeat(30)@(posedge clk);
busy<=1'b0;
@(negedge rd);
adc_din<=stimulus[Pattern];
Pattern<=Pattern+1;
@(posedge clk);
end
repeat(20)@(posedge clk);//延遲20個時鐘周期;
$stop;
end
endmodule
采用Quartus 18.1和modelsim聯合仿真,打開modelsim仿真界面wave后,先刪除該界面自動添加的信號。然后如下圖所示,添加我提前設置好的波形文件wave.do。
圖2 添加波形文件對應結果如下所示,相關模塊的所有信號已經全部添加且分類。

圖3 波形界面之后重新開始仿真,點擊run -all即可,仿真文件運行到$stop位置自動停止仿真。 由于TestBench給ad7606八個通道賦值相同數據,因此最終采集的數據如下圖所示。

圖4 八個通道采集數據通道0的數據處理模塊如下圖所示,首先采集到的16位補碼數據為16’d4107,對應原碼為15’d4107,然后計算對應的模擬電壓((4107) / (2^15)) * 5V = 0.6266V = 626mv,最終將計算結果轉換為BCD碼,與下圖結果完全一致。


圖5 數據處理模塊仿真而uart_byte模塊把八個通道計算結果根據含義保存到對應存儲器中,同時根據計數器的值向下游模塊傳輸數據。下圖所示,每當上游模塊完成一次數據轉換,均會將存儲器的數據刷新。


圖6 存儲數據傳輸數據相關的計數器如下所示,uart_tx_rdy為高電平表示uart發送模塊處于空閑狀態。為了防止uart接收端反應不過來,這里會讓空閑狀態多持續一段時間,才開始發送下字節數據。使用計數器rdy_cnt來計數這段時間。

圖7 數據傳輸時序上圖中ch_cnt用來表示當前傳輸第幾個通道的數據,而byte_cnt用來計數當前發送該通道第幾字節數據。上圖中表示傳輸通道0第0字節數據為8’d65,對應字符A,然后發送第1字節數據8’d68對應D,之后傳輸通道對應數值,一般給用戶顯示都是從1開始的,因此8’d49表示通道1。上述三個數據顯示結果為AD1,后續仿真類似,不再贅述。最后就是uart發送模塊,如下圖所示,發送8’h41(與8’d65相等),先發送低位數據,起始位會占用一位,結果正確。


圖8 uart發送模塊仿真由于工程比較簡單,因此仿真這塊做得都比較簡潔,文末會提供工程,拿到后可以自行仿真。04上板實測 由于手里這塊板子的設計問題,導致最終沒辦法上板,具體問題如下所示。板子上有一個uart的公頭,如下所示。


圖9 板載uart接口對應原理圖如下所示,公頭的2腳接的是板子的uart發送引腳。

圖10 uart原理圖下圖是淘寶上該接口的線材接口信息,母頭線材2腳也是發送引腳?沒找到2腳用于接收的母頭線材,不知道設計這塊板子的工程師是如何想的,黑金自家也沒有賣這種線材。


圖11 淘寶線材在網上找了一張功能類似的截圖,上板后的結果應該與下圖一致。

圖12 串口調試助手顯示雖然不能使用串口顯示數據,但是也可以通過signal tap抓一下實測數據,如果時序沒有問題,依舊可以保證工程沒有問題。如下圖所示,ad7606的八個通道全部懸空,然后下載程序到板子中。

圖13 懸空ad7606八個通道輸入引腳使用signal tap抓取通道0的數據,如下所示,對應數據為16’d11857,轉換為模擬電壓為(16’d11857 / (2^15)) * 5V = 1.809V = 1809mv,與抓取的bcd碼轉換結果一致。


圖14 轉換時序由于uart每發送一個字節數據中間會間隔很長時間,為了看到依次發送的數據對不對,則需要抓取很多數據,signal tap采用如下設置,只抓取o_uart_tx_vld為高電平的數據,舍棄其余無用數據。


圖15 signal tap設置抓取數據如下所示,65對應的字符A,54對應的是通道6,由此可知第一幀數據為通道6的數據,第二幀為通道7的數據。


圖16 抓取發送的uart幀數據與下圖譯碼器部分代碼對比,可以驗證上圖的正確性。


圖17 對應譯碼最后修改signal tap抓取uart發送模塊時序,如下圖所示,發送數據為8’h2e,先發送低位數據。

圖18 抓取uart發送模塊時序相關時序驗證結束,整體時序是沒有問題的,如果換個正常的uart硬件接口可以直接使用。
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
adc
+關注
關注
99文章
6592瀏覽量
547308 -
串口
+關注
關注
14文章
1575瀏覽量
78112 -
上位機
+關注
關注
27文章
952瀏覽量
55337
原文標題:fpga通過uart向上位機傳輸ad7606采集數據
文章出處:【微信號:HXSLH1010101010,微信公眾號:FPGA技術江湖】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
【CANNON申請】兩塊板之間藍牙數據傳輸
申請理由:一直從事單片機的數據通信,想實現一次藍牙的數據傳輸功能。項目描述:兩塊板實現數據傳輸。比如,A板采集芯片內部溫度,
發表于 01-19 16:05
請問ov2640采集單張照片怎么通過wifi傳輸給上位機?
如題,stm32f1控制ov2640采集單張圖片利用wifi發送給上位機,wifi是sta透傳模式,目前上位機接收到的
發表于 05-09 06:06
無線數據傳輸模塊的實際應用
數據傳輸模塊的實際應用一、智能安防安防是物聯網的一大應用市場,傳統安防對人員的依賴性比較大,非常耗費人力,而智能安防能夠通過設備實現智能判斷。目前,智能安防最核心的部分在于智能安防系統,該系統中應用無線
發表于 06-18 04:21
串口線采集數據,波形圖表顯示怎么操作?
我想用labview顯示下位機的波形。做了一個測試程序。 Stm32做了一個dac輸出1k的正弦波,做了一個adc(10khz的速度)來采集這個正弦波,
發表于 08-30 10:51
stm32的幾種數據傳輸總結
引言在一般的項目開發過程中,往往需要兩塊或以上單片機進行通信完成數據傳輸,例如四旋翼無人機在飛行過程中無線傳輸數據回到地面站,治療儀器需要實時將
發表于 08-23 07:32
評論