FPGA仿锆石代码风格组合电路时序电路严格分开之(一)8通道16位AD采集
生活随笔
收集整理的這篇文章主要介紹了
FPGA仿锆石代码风格组合电路时序电路严格分开之(一)8通道16位AD采集
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
硬件介紹:FPGA采用的是黑金的AX530,AD采集模塊采用的是AN706(八通道十六位)。
實現功能:AD采集到8通道十六位的電壓數據,然后經過串口發送到串口助手顯示各個通道的實時電壓數據。
程序源碼介紹:頂層模塊包含AD采集模塊(AD_RX_module),AD數據轉換模塊(AD_Volt),AD數據到串口數據轉換模塊(AD_to_Uart),串口發送模塊(Uart_tx_Module)。
以下將分模塊介紹:
1、頂層程序AD_8C_16B:沒有什么要說的,就是把底層模塊中出現的輸入輸出腳連接起來。
//頂層程序 8chnnel-16bits 2020.7.9開始
//2020.7.14日,編程完畢
//實現功能:采集8路16位的AD數據,并發送到串口助手。采集電壓范圍在-5~5V之間,誤差不超過千分之5
//優化方向:BCD.v中的for循環
module AD_8C_16B //輸入輸出端口聲明,和模塊定義,只有下面這里是逗號(//輸入端口clk_50M,rst_n,AD_data, //AD轉成的16位數據AD_BUSY, //AD的BUSY線AD_FRETDATA, //為1時表示第一個數據來了,不用他,直接在RD的上升沿讀取//輸出端口AD_CS, //AD片選信號,此時用來讀取數據AD_RD, //此時通過變換的時鐘讀取數據,自己定義在上升沿讀取吧AD_RESET, //AD的復位信號,至少高電平50nsAD_OS, //多重濾波操作,先不用AD_CONVERT, //啟動AD轉化TXD);//--------------------------------------------------------------------//--定義外部端口端口//-------------------------------------------------------------------//定義輸入端口input clk_50M; //input output 默認類型都是wire型input rst_n; input [15:0] AD_data; //AD輸入的轉成16位數字信號input AD_BUSY; //AD忙input AD_FRETDATA; //為1時表示第一個數據來了,不用他,直接在RD的上升沿讀取//定義輸出端口output AD_CS; //片選,決定能否開始接收數據output AD_RD; //輸出的時鐘,用于在其上升沿讀出AD數據output AD_RESET; //AD706在每次上電的時候復位一次 output AD_CONVERT; //啟動AD轉化output [2:0] AD_OS; //默認設置為000,不設置采樣倍率output TXD; //--------------------------------------------------------------------//--定義內部端口//------------------------------------------------------------------- //防止被綜合掉,否則signal tapII無法采集,用于wire型 -> /*synthesis keep*///*********AD_RX_module和AD_Volt間的數據傳遞*********wire [127:0] AD_CH; //8個通道的16進制值//*********AD_Volt和AD_to_Uart間的數據傳遞*********wire [19:0] CH1_BCD; //8個通道的顯示的電壓值wire [19:0] CH2_BCD; //8個通道的顯示的電壓值wire [19:0] CH3_BCD; //8個通道的顯示的電壓值wire [19:0] CH4_BCD; //8個通道的顯示的電壓值wire [19:0] CH5_BCD; //8個通道的顯示的電壓值wire [19:0] CH6_BCD; //8個通道的顯示的電壓值wire [19:0] CH7_BCD; //8個通道的顯示的電壓值wire [19:0] CH8_BCD; //8個通道的顯示的電壓值wire [7:0] CH1_Sig; //通道1的正負號的ascwire [7:0] CH2_Sig; //通道2的正負號的ascwire [7:0] CH3_Sig; //通道3的正負號的ascwire [7:0] CH4_Sig; //通道4的正負號的ascwire [7:0] CH5_Sig; //通道5的正負號的ascwire [7:0] CH6_Sig; //通道6的正負號的ascwire [7:0] CH7_Sig; //通道7的正負號的ascwire [7:0] CH8_Sig; //通道8的正負號的asc//*********AD_to_Uart和Uart_tx_Module間的數據傳遞*********wire [ 7:0] data_out; //AD_to_Uart和串口輸出間的數據傳遞wire ctl; //AD_to_Uart和串口輸出間的控制位傳遞//--------------------------------------------------------------------//--邏輯實現//------------------------------------------------------------------- //例化AD_RX采集模塊AD_RX_module AD_RX_Init//輸入輸出端口聲明,和模塊定義,只有下面這里是逗號(//輸入端口.clk_50M (clk_50M),.rst_n (rst_n), .AD_data (AD_data), //AD轉成的16位數據的輸入端口.AD_BUSY (AD_BUSY), //AD的BUSY線.AD_FRETDATA (AD_FRETDATA), //為1時表示第一個數據來了,不用他,直接在RD的上升沿讀取 //輸出端口 .AD_CS (AD_CS), //AD片選信號,此時用來讀取數據.AD_RD (AD_RD), //此時通過變換的時鐘讀取數據,自己定義在上升沿讀取吧.AD_RESET (AD_RESET), //AD的復位信號,至少高電平50ns .AD_OS (AD_OS), //多重濾波操作,先不用.AD_CONVERT (AD_CONVERT), //啟動AD轉化//FPGA內部輸出.AD_CH (AD_CH) //依次八個16位的AD數據輸出給AD值的轉換,CH1在最低16位.只是一堆數據沒有轉成電壓大小的數據);AD_Volt AD_Volt_Init//輸入輸出端口聲明,和模塊定義,只有下面這里是逗號(//輸入端口.clk_50M (clk_50M),.rst_n (rst_n), //硬件復位.AD_RESET (AD_RESET), //軟件復位 //FPGA內部輸入.AD_CH (AD_CH), //穩定的8個通道未轉換的電壓工16*8bits//輸出端口.CH1_BCD (CH1_BCD), //8個通道的顯示的電壓值 20位.CH2_BCD (CH2_BCD), //8個通道的顯示的電壓值.CH3_BCD (CH3_BCD), //8個通道的顯示的電壓值.CH4_BCD (CH4_BCD), //8個通道的顯示的電壓值.CH5_BCD (CH5_BCD), //8個通道的顯示的電壓值.CH6_BCD (CH6_BCD), //8個通道的顯示的電壓值.CH7_BCD (CH7_BCD), //8個通道的顯示的電壓值.CH8_BCD (CH8_BCD), //8個通道的顯示的電壓值.CH1_Sig (CH1_Sig), //八個通道數據的正負號.CH2_Sig (CH2_Sig),.CH3_Sig (CH3_Sig),.CH4_Sig (CH4_Sig),.CH5_Sig (CH5_Sig),.CH6_Sig (CH6_Sig),.CH7_Sig (CH7_Sig),.CH8_Sig (CH8_Sig));//例化AD到串口模塊AD_to_Uart AD_to_Uart_Init//輸入輸出端口聲明,和模塊定義,只有下面這里是逗號(//輸入端口.clk_50M (clk_50M),.rst_n (rst_n), //硬件復位.AD_RESET (AD_RESET), //軟件復位 .CH1_BCD (CH1_BCD), //8個通道的顯示的電壓值 20位.CH2_BCD (CH2_BCD), //8個通道的顯示的電壓值.CH3_BCD (CH3_BCD), //8個通道的顯示的電壓值.CH4_BCD (CH4_BCD), //8個通道的顯示的電壓值.CH5_BCD (CH5_BCD), //8個通道的顯示的電壓值.CH6_BCD (CH6_BCD), //8個通道的顯示的電壓值.CH7_BCD (CH7_BCD), //8個通道的顯示的電壓值.CH8_BCD (CH8_BCD), //8個通道的顯示的電壓值.CH1_Sig (CH1_Sig), //八個通道數據的正負號.CH2_Sig (CH2_Sig),.CH3_Sig (CH3_Sig),.CH4_Sig (CH4_Sig),.CH5_Sig (CH5_Sig),.CH6_Sig (CH6_Sig),.CH7_Sig (CH7_Sig),.CH8_Sig (CH8_Sig), //輸出端口.ctl (ctl), //數據控.data_out (data_out) //數據出);//例化數據發送模塊Uart_tx_Module Uart_tx_init (.clk_50M (clk_50M),.rst_n (rst_n), .ctl (ctl), .data_in (data_out), //輸入.TXD (TXD) );
endmodule //成對
2、AD采集模塊AD_RX_module :用于實現對8個通道的16位電壓值的采集,采集后發送給AD數據轉換模塊(AD_Volt),可以實現把采集到的AD數據(-32767+32767)轉化為(-50000mV+50000mV)實現保留電壓的四位有效數字。然后通過最后的BCD模塊,進行譯碼,實現把(-50000mV~+50000mV)中的每一位數據取出來,用于最后的串口發送。由于FPGA乘除法很占用邏輯資源,所以乘法是通過移位操作實現的,當然也可以用內部的乘法器。
//AD采集模塊8chnnel-16bits 2020.7.9 //此模塊進行數據AD數據采集不負責轉換 module AD_RX_module //輸入輸出端口聲明,和模塊定義,只有下面這里是逗號(//輸入端口clk_50M,rst_n,AD_data, //AD轉成的16位數據AD_BUSY, //AD的BUSY線AD_FRETDATA, //為1時表示第一個數據來了,不用他,直接在RD的上升沿讀取//輸出端口AD_CS, //AD片選信號,此時用來讀取數據AD_RD, //此時通過變換的時鐘讀取數據,自己定義在上升沿讀取吧AD_RESET, //AD的復位信號,至少高電平50nsAD_OS, //多重濾波操作,先不用AD_CONVERT, //啟動AD轉化AD_CH //依次八個16位的AD數據輸出給AD值的轉換,CH1在最低16位.只是一堆數據沒有轉成電壓大小的數據);//--------------------------------------------------------------------//--定義外部端口端口//-------------------------------------------------------------------//定義輸入端口input clk_50M; //input output 默認類型都是wire型input rst_n; input [15:0] AD_data; //AD輸入的轉成16位數字信號input AD_BUSY; //AD忙input AD_FRETDATA; //為1時表示第一個數據來了,不用他,直接在RD的上升沿讀取//定義輸出端口output reg AD_CS; //片選,決定能否開始接收數據output reg AD_RD; //輸出的時鐘,用于在其上升沿讀出AD數據output reg AD_RESET; //AD706在每次上電的時候復位一次 output reg AD_CONVERT; //啟動AD轉化output reg [2:0] AD_OS; //默認設置為000,不設置采樣倍率output reg [127:0] AD_CH ; //穩定的8個通道的16進制值(00001111_11111111),用于發送給轉換模式//--------------------------------------------------------------------//--定義內部端口//------------------------------------------------------------------- reg [29:0] time_cnt; //保證可以記錄13ms的時間,用于關斷模式的上電后復位reg [29:0] time_cnt_n; //時間計數器下一狀態reg [7:0] fsm_time_cnt; //有限狀態機時序計時器,實際只需要5位就行reg [7:0] fsm_time_cnt_n; reg [2:0] AD_RD_cnt; //打出2-1-2的節拍,用于讀取數據reg [2:0] AD_RD_cnt_n; reg AD_CS_n; reg AD_RD_n; reg AD_RESET_n;reg AD_CONVERT_n; wire [2:0] AD_OS_n; reg [127:0] AD_CH_reg; //不穩定的8個通道的16進制值,用于暫存reg [127:0] AD_CH_reg_n; //不穩定的8個通道的16進制電壓值,用于暫存 reg [127:0] AD_CH_n;reg [2:0] fsm_s; //有限狀態機當前狀態 一共4態reg [2:0] fsm_s_n; //有限狀態機下一狀態reg [3:0] CH_cnt; //記錄采集到的通道數,計數到8就清零reg [3:0] CH_cnt_n; //記錄采集到的通道數,計數到8就清零 //--------------------------------------------------------------------//--定義參數//------------------------------------------------------------------- parameter AD_RESET_num=30'd650_000, //650000*20=13msIdle =3'd0, //狀態機初始態Convert =3'd1, //convert置0態Wait_busy1 =3'd2, //等待busy置為1態Wait_busy0 =3'd3, //等待busy置為0態Data_IN =3'd4, //讀取8通道的AD數據態AD_END =3'd5; //數據結束態//--------------------------------------------------------------------//--邏輯實現//------------------------------------------------------------------- //時序電路,----------50Mhz AD復位計數器------------------- always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)AD_OS<=3'b0;elseAD_OS<=AD_OS_n; end//組合電路assign AD_OS_n=3'b000; //不過采樣(貌似就是不濾波)//時序電路,----------50Mhz AD復位計數器------------------- always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)time_cnt<=30'b0;elsetime_cnt<=time_cnt_n; end//組合電路always @(*) begin if(time_cnt<=AD_RESET_num) time_cnt_n=time_cnt+1'b1; elsetime_cnt_n=time_cnt;end//時序電路,----------等待AD706初上電復位------------------- always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)AD_RESET<=1'b0;elseAD_RESET<=AD_RESET_n; end//組合電路always @(*) begin if(time_cnt<=AD_RESET_num) //時間到13ms,就拉低,不再拉回來了AD_RESET_n=1'b1;elseAD_RESET_n=1'b0;end//時序電路,----------有限狀態機狀態切換------------------- always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)fsm_s<=Idle; //默認是空閑狀態elsefsm_s<=fsm_s_n; end//組合電路 ---有限狀態機核心always@(*) //狀態機內部僅僅執行狀態的跳轉,不進行狀態的內部操作begin if(AD_RESET==1) //狀態機復位 fsm_s_n=Idle; elsebegincase(fsm_s) Idle: //初始化態(空閑態),等待AD_CONVERT為0if(fsm_time_cnt==8'd5) //5*20ns=100ns 其實40ns就行,保險一些Debugfsm_s_n=Convert; //進入下一態elsefsm_s_n=fsm_s; //保持當前狀態 Convert: //convert為0 的時間if(fsm_time_cnt==8'd2) //2*20ns=40nsfsm_s_n=Wait_busy1; //進入下一態elsefsm_s_n=fsm_s; //保持當前狀態 Wait_busy1: //等待busy為1,也就是目前busy為0if(fsm_time_cnt==8'd5) //至少5*20ns=100ns,只要小于20us就行fsm_s_n=Wait_busy0; //進入到下一個初始態elsefsm_s_n=fsm_s; //保持當前狀態 Wait_busy0: //此時busy為1,等待busy變成0進入下一態if(AD_BUSY==1) //至少2*20ns=40nsfsm_s_n=fsm_s; //AD_BUSY等于1就保持,1大概會200個時鐘else //直到AD_BUSY為0fsm_s_n=Data_IN; Data_IN: //3個時鐘有一個通道,共需要8*60=480nsif(fsm_time_cnt==8'd24||CH_cnt==8) //至少24*20ns=480ns,產生8個讀取上升沿后,就不再讀取數據fsm_s_n=AD_END; //進入到下一個初始態elsefsm_s_n=fsm_s; AD_END:fsm_s_n=Idle; //直接回到空閑態default:fsm_s_n=Idle; //默認在空閑態endcaseendend //時序電路,----------fsm_time_cnt//有限狀態機時序計時器--------always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)fsm_time_cnt<=8'b0;elsefsm_time_cnt<=fsm_time_cnt_n; end//組合電路always @(*) begin if(fsm_s==fsm_s_n&&AD_RESET==0) //只計數在每個狀態時AD1.1Mhz的周期數fsm_time_cnt_n=fsm_time_cnt+8'b1; //記錄每個狀態的脈沖數else //改變狀態時或者AD_RESET為1時清零fsm_time_cnt_n=8'b0;end//時序電路,----------AD_CONVERT------------------- always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)AD_CONVERT<=1'b0;elseAD_CONVERT<=AD_CONVERT_n; end//組合電路always @(*) begin if(fsm_s!=Convert||AD_RESET==1) //只要不在Convert態或者AD_RESET為1AD_CONVERT_n=1'b1; //就把AD_CONVERT_n置為1else //僅僅在Convert態時才為0AD_CONVERT_n=1'b0;end//時序電路----------Data_IN態中的AD_CS------------------- always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)AD_CS<=1'b0;elseAD_CS<=AD_CS_n; end//組合電路always @(*) begin if(fsm_s==Data_IN&&AD_RESET!=1) AD_CS_n=1'b0; //CS拉低,開啟片選可以讀取數據elseAD_CS_n=1'b1; //關閉片選,不可以讀取數據end//時序電路----------Data_IN態中的AD_RD_cnt打拍子------------------- always @(posedge clk_50M or negedge rst_n) //通過AD_RD交替變換產生2-1-2拍的時鐘beginif(!rst_n)AD_RD_cnt<=2'b0; elseAD_RD_cnt<=AD_RD_cnt_n; end//組合電路always @(*) begin if(AD_RESET==1) //復位 AD_RD_cnt_n=2'b0; else if(fsm_s==Data_IN&&AD_RD_cnt==2'd2) //3個時鐘一個循環,0-1,1-2,2-0AD_RD_cnt_n=2'b0; //重新計數else if(fsm_s==Data_IN)AD_RD_cnt_n=AD_RD_cnt+2'b1; //每個時鐘加1 elseAD_RD_cnt_n=AD_RD_cnt;end //時序電路----------Data_IN態中的AD_RD------------------- always @(posedge clk_50M or negedge rst_n) //通過AD_RD交替變換產生2-1-2拍的時鐘beginif(!rst_n)AD_RD<=1'b0;elseAD_RD<=AD_RD_n; end//組合電路always @(*) begin if(AD_RESET==1) //復位 AD_RD_n=1'b1; //默認高else if(fsm_s==Data_IN&&AD_RD_cnt==2'd2) //高電平一個時鐘AD_RD_n=1'b1; else //低電平兩個時鐘AD_RD_n=1'b0;end //時序電路----------Data_IN態中的CH_cnt------------------- always @(posedge clk_50M or negedge rst_n) //通過AD_RD交替變換產生2-1-2拍的時鐘beginif(!rst_n)CH_cnt<=1'b0;elseCH_cnt<=CH_cnt_n; end//組合電路always @(*) begin if(AD_RESET==1||CH_cnt==8) //軟件復位或者記錄了8個上升沿就復位計數值為0 CH_cnt_n=4'b0; //默認采集了0個通道else if(fsm_s==Data_IN&&(~AD_RD)&AD_RD_n) //上升沿加1CH_cnt_n=CH_cnt+1'b1; else //低電平兩個時鐘CH_cnt_n=CH_cnt;end //時序電路----------Data_IN態中的AD_CH------------------- always @(posedge clk_50M or negedge rst_n) //通過AD_RD交替變換產生2-1-2拍的時鐘beginif(!rst_n)AD_CH_reg<=1'b0;elseAD_CH_reg<=AD_CH_reg_n; end//組合電路always @(*) begin if(AD_RESET==1) //復位 AD_CH_reg_n=128'b0; //默認數據0else if(fsm_s==Data_IN&&(~AD_RD)&AD_RD_n) //上升沿讀一字節AD_CH_reg_n={AD_data,AD_CH_reg[127:16]}; //先讀的是低16位else //非上升沿保持數據AD_CH_reg_n=AD_CH_reg;end //時序電路----------AD_END態用于發送出處理后的8通道AD數據------------------- always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)AD_CH<=128'b0; //默認是默認讀0elseAD_CH<=AD_CH_n; end//組合電路always @(*) begin if(AD_RESET==1) //復位 AD_CH_n=128'b0; //默認數據0else if(fsm_s==AD_END) //此時上8通道AD數據讀取完畢,保存AD_CH_n=AD_CH_reg; //穩定存儲轉換過來的數據elseAD_CH_n=AD_CH;endendmodule //成對2.1、BCD譯碼模塊:用for循環實現移位加三法(還有更好的方法,不采用循環,需要的可以百度或者留言)
//2020.7.12 //BCD譯碼 移位加三法 module BCD(//輸入16進制clk_50M,Num, //0-5000的值rst_n, //硬件復位AD_RESET, //軟件復位 //輸出10進制BCDdec);//--------------------------------------------------------------------//--定義外部端口端口//-------------------------------------------------------------------input clk_50M; //input output 默認類型都是wire型input [15:0] Num; input rst_n;input AD_RESET;output reg[19:0] dec; //--------------------------------------------------------------------//--定義內部端口//-------------------------------------------------------------------reg [35:0] z; //65535轉為bcd需要5*4=20bits,再加上本身的16bit共需要36bitsreg [19:0] dec_n; //dec的bcd碼的寄存器reg [5:0] i; //循環次數//--------------------------------------------------------------------//--定義參數//------------------------------------------------------------------- //--------------------------------------------------------------------//--邏輯實現//------------------------------------------------------------------- //**************BCD碼轉換***************//時序電路always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)dec<=20'b0; //elsedec<=dec_n; end //組合電路always @(*) begin if(AD_RESET) //軟件復位 dec_n=20'b0; elsedec_n=z[35:16];end//組合邏輯 //一個時鐘可以進行完畢吧?always @ (*)beginz = 35'b0; //置 0z[15:0] = Num; //讀入低 16位 // repeat (16) //很難綜合,別用for(i=6'd0;i<6'd16;i=i+1'b1)beginif(z[19:16 ]>4'd4) //大于 4 就加 3,加3后不可能溢出的,所以不用向后進位z[19:16] = z[19:16] + 2'b11;if(z[23:20]>4'd4)z[23:20] = z[23:20] + 2'b11;if(z[27:24]>4'd4) //大于 4 就加 3z[27:24] = z[27:24] + 2'b11;if(z[31:28]>4'd4)z[31:28] = z[31:28] + 2'b11;if(z[35:32]>4'd4)z[35:32] = z[35:32] + 2'b11;z[35:1] = z[34:0]; //整體左移一位end endendmodule3、串口發送前數據的準備模塊AD_to_Uar:用于把模塊2轉換的數據排列好,接下來可以發送給串口發送模塊 4,最終實現數據的串口發送。
//把AD的數據分成一個一個的字節發送 2020.7.12 //下接Uart_tx_Module module AD_to_Uart //輸入輸出端口聲明,和模塊定義,只有下面這里是逗號(//輸入端口clk_50M,rst_n, //硬件復位AD_RESET, //軟件復位 //fpga內部輸入CH1_BCD, //8個通道的顯示的電壓值 20位CH2_BCD, //8個通道的顯示的電壓值CH3_BCD, //8個通道的顯示的電壓值CH4_BCD, //8個通道的顯示的電壓值CH5_BCD, //8個通道的顯示的電壓值CH6_BCD, //8個通道的顯示的電壓值CH7_BCD, //8個通道的顯示的電壓值CH8_BCD, //8個通道的顯示的電壓值CH1_Sig, //八個通道數據的正負號CH2_Sig,CH3_Sig,CH4_Sig,CH5_Sig,CH6_Sig,CH7_Sig,CH8_Sig, //輸出端口//fpga內部輸出ctl, //數據控data_out //數據出);//---------------------------------------------------------------------------//-- 外部端口聲明//---------------------------------------------------------------------------input clk_50M;input AD_RESET;input rst_n;input [19:0] CH1_BCD; //8個通道的顯示的電壓值 20位input [19:0] CH2_BCD; //8個通道的顯示的電壓值input [19:0] CH3_BCD; //8個通道的顯示的電壓值input [19:0] CH4_BCD; //8個通道的顯示的電壓值input [19:0] CH5_BCD; //8個通道的顯示的電壓值input [19:0] CH6_BCD; //8個通道的顯示的電壓值input [19:0] CH7_BCD; //8個通道的顯示的電壓值input [19:0] CH8_BCD; //8個通道的顯示的電壓值input [7:0] CH1_Sig; //八個通道數據的正負號input [7:0] CH2_Sig;input [7:0] CH3_Sig;input [7:0] CH4_Sig;input [7:0] CH5_Sig;input [7:0] CH6_Sig;input [7:0] CH7_Sig;input [7:0] CH8_Sig; output reg ctl;output reg[7:0] data_out; //--------------------------------------------------------------------------- //-- 內部端口聲明 //---------------------------------------------------------------------------reg ctl_n;reg [13:0] time_cnt; //計時器脈沖數 比實際多預留一位reg [13:0] time_cnt_n; //下一時刻的計時器脈沖式reg [ 6:0] date_byte_cnt; //總共不超過128個字節 數據索引reg [ 6:0] date_byte_cnt_n; //總共不超過128個字節//--------------------------------------------------------------------//--定義參數//------------------------------------------------------------------- //波特率115200,發送一個字節(10位)需要大概4300個脈沖(50Mhz),這里取5300個(足夠發送12.2112位了)保證數據發送完畢 parameter byte_time=13'd5300; ////--------------------------------------------------------------------//--邏輯實現//------------------------------------------------------------------- //**************發送時間間隔計數器*************** //時序電路always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)time_cnt<=13'b0;elsetime_cnt<=time_cnt_n; end//組合電路always @(*) begin if(AD_RESET||time_cnt==byte_time) //軟件復位或者發送完畢一個字節復位time_cnt_n=13'b0;elsetime_cnt_n=time_cnt+1'b1;end//**************串口發送控制位*************** //時序電路always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)ctl<=1'b0;elsectl<=ctl_n; end//組合電路always @(*) begin if(!AD_RESET&&time_cnt==byte_time) //每到達一次時間就置位發送標志位為1一次ctl_n=1'b1;elsectl_n=1'b0;end//**************發送數據包的數據索引*************** //時序電路always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)date_byte_cnt<=7'b0;elsedate_byte_cnt<=date_byte_cnt_n; end//組合電路always @(*) begin if(!AD_RESET&&time_cnt==byte_time) //依次移動數組索引date_byte_cnt_n=date_byte_cnt+1'b1;else if(AD_RESET||date_byte_cnt==7'd114) //1-114是數據字節,到了就復位,如果到115的話,會產生一位defalutdate_byte_cnt_n=7'b0; elsedate_byte_cnt_n=date_byte_cnt;end//組合電路,根據哪個是高電平,決定串口輸出的是哪個數據always @(*) begin case(date_byte_cnt) //不需要break7'd1:data_out <= 8'd65; //存儲字符 A 7'd2:data_out <= 8'd68; //存儲字符 D7'd3:data_out <= 8'd49; //存儲字符 17'd4:data_out <= 8'd58; //存儲字符 : 7'd5:data_out <= CH1_Sig; //存儲字符 正負 7'd6:data_out <= CH1_BCD[19:16] + 8'd48; //存儲字符 個位 7'd7:data_out <= 8'd46; //存儲字符 . 7'd8:data_out <= CH1_BCD[15:12] + 8'd48; //存儲字符 小數點后一位7'd9:data_out <= CH1_BCD[11:8] + 8'd48; //存儲字符 小數點后二位7'd10:data_out <= CH1_BCD[7:4] + 8'd48; //存儲字符 小數點后三位7'd11:data_out <= CH1_BCD[3:0] + 8'd48; //存儲字符 小數點后四位7'd12:data_out <= 8'd86; //存儲字符 V7'd13:data_out <= 8'd32; //存儲字符 空格7'd14:data_out <= 8'd32; //存儲字符 空格7'd15:data_out <= 8'd65; //存儲字符 A 7'd16:data_out <= 8'd68; //存儲字符 D7'd17:data_out <= 8'd50; //存儲字符 27'd18:data_out <= 8'd58; //存儲字符 : 7'd19:data_out <= CH2_Sig; //存儲字符 正負 7'd20:data_out <= CH2_BCD[19:16] + 8'd48; //存儲字符 個位 7'd21:data_out <= 8'd46; //存儲字符 . 7'd22:data_out <= CH2_BCD[15:12] + 8'd48; //存儲字符 小數點后一位7'd23:data_out <= CH2_BCD[11:8] + 8'd48; //存儲字符 小數點后二位7'd24:data_out <= CH2_BCD[7:4] + 8'd48; //存儲字符 小數點后三位7'd25:data_out <= CH2_BCD[3:0] + 8'd48; //存儲字符 小數點后四位7'd26:data_out <= 8'd86; //存儲字符 V7'd27:data_out <= 8'd32; //存儲字符 空格7'd28:data_out <= 8'd32; //存儲字符 空格7'd29:data_out <= 8'd65; //存儲字符 A 7'd30:data_out <= 8'd68; //存儲字符 D7'd31:data_out <= 8'd51; //存儲字符 37'd32:data_out <= 8'd58; //存儲字符 : 7'd33:data_out <= CH3_Sig; //存儲字符 正負 7'd34:data_out <= CH3_BCD[19:16] + 8'd48; //存儲字符 個位 7'd35:data_out <= 8'd46; //存儲字符 . 7'd36:data_out <= CH3_BCD[15:12] + 8'd48; //存儲字符 小數點后一位7'd37:data_out <= CH3_BCD[11:8] +8'd48; //存儲字符 小數點后二位7'd38:data_out <= CH3_BCD[7:4] + 8'd48; //存儲字符 小數點后三位7'd39:data_out <= CH3_BCD[3:0] + 8'd48; //存儲字符 小數點后四位7'd40:data_out <= 8'd86; //存儲字符 V7'd41:data_out <= 8'd32; //存儲字符 空格7'd42:data_out <= 8'd32; //存儲字符 空格 7'd43:data_out <= 8'd65; //存儲字符 A 7'd44:data_out <= 8'd68; //存儲字符 D7'd45:data_out <= 8'd52; //存儲字符 47'd46:data_out <= 8'd58; //存儲字符 : 7'd47:data_out <= CH4_Sig; //存儲字符 正負 7'd48:data_out <= CH4_BCD[19:16] + 8'd48; //存儲字符 個位 7'd49:data_out <= 8'd46; //存儲字符 . 7'd50:data_out <= CH4_BCD[15:12] + 8'd48; //存儲字符 小數點后一位7'd51:data_out <= CH4_BCD[11:8] + 8'd48; //存儲字符 小數點后二位7'd52:data_out <= CH4_BCD[7:4] + 8'd48; //存儲字符 小數點后三位7'd53:data_out <= CH4_BCD[3:0] + 8'd48; //存儲字符 小數點后四位7'd54:data_out <= 8'd86; //存儲字符 V7'd55:data_out <= 8'd32; //存儲字符 空格7'd56:data_out <= 32; //存儲字符 空格7'd57:data_out <= 8'd65; //存儲字符 A 7'd58:data_out <= 8'd68; //存儲字符 D7'd59:data_out <= 8'd53; //存儲字符 57'd60:data_out <= 8'd58; //存儲字符 : 7'd61:data_out <= CH5_Sig; //存儲字符 正負 7'd62:data_out <= CH5_BCD[19:16] + 8'd48; //存儲字符 個位 7'd63:data_out <= 8'd46; //存儲字符 . 7'd64:data_out <= CH5_BCD[15:12] + 8'd48; //存儲字符 小數點后一位7'd65:data_out <= CH5_BCD[11:8] + 8'd48; //存儲字符 小數點后二位7'd66:data_out <= CH5_BCD[7:4] + 8'd48; //存儲字符 小數點后三位7'd67:data_out <= CH5_BCD[3:0] + 8'd48; //存儲字符 小數點后四位7'd68:data_out <= 8'd86; //存儲字符 V7'd69:data_out <= 8'd32; //存儲字符 空格7'd70:data_out <= 8'd32; //存儲字符 空格7'd71:data_out <= 8'd65; //存儲字符 A 7'd72:data_out <= 8'd68; //存儲字符 D7'd73:data_out <= 8'd54; //存儲字符 67'd74:data_out <= 8'd58; //存儲字符 : 7'd75:data_out <= CH6_Sig; //存儲字符 正負 7'd76:data_out <= CH6_BCD[19:16] + 8'd48; //存儲字符 個位 7'd77:data_out <= 8'd46; //存儲字符 . 7'd78:data_out <= CH6_BCD[15:12] + 8'd48; //存儲字符 小數點后一位7'd79:data_out <= CH6_BCD[11:8] + 8'd48; //存儲字符 小數點后二位7'd80:data_out <= CH6_BCD[7:4] + 8'd48; //存儲字符 小數點后三位7'd81:data_out <= CH6_BCD[3:0] + 8'd48; //存儲字符 小數點后四位7'd82:data_out <= 8'd86; //存儲字符 V7'd83:data_out <= 8'd32; //存儲字符 空格7'd84:data_out <= 8'd32; //存儲字符 空格7'd85:data_out <= 8'd65; //存儲字符 A 7'd86:data_out <= 8'd68; //存儲字符 D7'd87:data_out <= 8'd55; //存儲字符 77'd88:data_out <= 8'd58; //存儲字符 : 7'd89:data_out <= CH7_Sig; //存儲字符 正負 7'd90:data_out <= CH7_BCD[19:16] + 8'd48; //存儲字符 個位 7'd91:data_out <= 8'd46; //存儲字符 . 7'd92:data_out <= CH7_BCD[15:12] + 8'd48; //存儲字符 小數點后一位7'd93:data_out <= CH7_BCD[11:8] + 8'd48; //存儲字符 小數點后二位7'd94:data_out <= CH7_BCD[7:4] + 8'd48; //存儲字符 小數點后三位7'd95:data_out <= CH7_BCD[3:0] + 8'd48; //存儲字符 小數點后四位7'd96:data_out <= 8'd86; //存儲字符 V7'd97:data_out <= 8'd32; //存儲字符 空格7'd98:data_out <= 8'd32; //存儲字符 空格 7'd99: data_out<= 8'd65; //存儲字符 A 7'd100:data_out<= 8'd68; //存儲字符 D7'd101:data_out<= 8'd56; //存儲字符 87'd102:data_out<= 8'd58; //存儲字符 : 7'd103:data_out<= CH8_Sig; //存儲字符 正負 7'd104:data_out<= CH8_BCD[19:16] + 8'd48; //存儲字符 個位 7'd105:data_out<= 8'd46; //存儲字符 . 7'd106:data_out<= CH8_BCD[15:12] + 8'd48; //存儲字符 小數點后一位7'd107:data_out<= CH8_BCD[11:8] + 8'd48; //存儲字符 小數點后二位7'd108:data_out<= CH8_BCD[7:4] + 8'd48; //存儲字符 小數點后三位7'd109:data_out<= CH8_BCD[3:0] + 8'd48; //存儲字符 小數點后四位7'd110:data_out<= 8'd86; //存儲字符 V7'd111:data_out<= 8'd32 ; //存儲字符 空格7'd112:data_out<= 8'd32 ; //存儲字符 空格 7'd113:data_out<= 8'd10; //換行符7'd114:data_out<= 8'd13 ; //回車符default:data_out=8'd48; //默認發送0,但是應該永遠不會發endcaseend endmodule4、串口發送模塊:波特率采用115200,用于把模塊3中排好的數據發送到串口助手顯示,注意發送的是ascl碼,而且是字符發送方式,不是十六進制發送方式,16進制發送方式會更簡單,但是上位機需要額外的處理才能顯示出AD的電壓值。
// 2020.7.9module Uart_tx_Module //輸入輸出端口聲明,和模塊定義,只有下面這里是逗號(//輸入端口clk_50M,rst_n,//fpga內部輸入ctl, //數據控data_in, //數據入//輸出端口TXD //輸出的端口);//--------------------------------------------------------------------//--定義外部端口端口//-------------------------------------------------------------------input clk_50M;input rst_n;input ctl; //輸入控制位input [7:0] data_in; //數據進入output reg TXD; //--------------------------------------------------------------------//--定義內部端口//-------------------------------------------------------------------reg ctl_flag; //輸入控制位寄存器reg ctl_flag_n; //輸入控制位寄存器reg [8:0] time_cnt; //計數器reg [8:0] time_cnt_n; reg [7:0] data_byte;reg [7:0] data_byte_n;reg [3:0] cnt_FSM; //有限狀態機狀態輪換(發送的數據位數)//0-10計數reg [3:0] cnt_FSM_n; //--------------------------------------------------------------------//--定義參數//------------------------------------------------------------------- parameter bps_num=9'd434; //434個脈沖是波特率115200的一個脈沖//--------------------------------------------------------------------//--邏輯實現//------------------------------------------------------------------- //**************發送控制位***************//時序電路always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)ctl_flag<=1'b0; //elsectl_flag<=ctl_flag_n; end//組合電路,always @(*) begin if(ctl==1) //僅僅會有一個時鐘的ctl為1,控制數據開始執行一系列發送操作,直到發送完一個字節。ctl_flag_n=1'b1; else if(cnt_FSM==4'd10) //從開始位到停止位一共十位,需要記到10ctl_flag_n=1'b0; //數據發送完畢,置位0elsectl_flag_n=ctl_flag;end//***************波特率產生***************//時序電路always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)time_cnt<=1'b0; //elsetime_cnt<=time_cnt_n; end//組合電路,always @(*) begin if(time_cnt==bps_num) //到達一個波特率的時間了time_cnt_n=1'b0; elsetime_cnt_n=time_cnt+1'b1;end//***************位數計算***************//時序電路always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)cnt_FSM<=4'b0; //elsecnt_FSM<=cnt_FSM_n; end//組合電路,always @(*) begin if(time_cnt==bps_num&&ctl_flag==1) //到達一個波特率的時間了且發送數據位已經滿足cnt_FSM_n=cnt_FSM+1'b1; //就把位數寄存器加1else if(cnt_FSM==4'd10) //到達10就清零,一個字節發送完畢cnt_FSM_n=4'b0; elsecnt_FSM_n=cnt_FSM; //否則保持end//***************待發送數據存儲***************//時序電路always @(posedge clk_50M or negedge rst_n)beginif(!rst_n)data_byte<=8'hff; //默認是停止位elsedata_byte<=data_byte_n; end//組合電路,always @(*) begin if(ctl==1) //一個時鐘的控制位來了,就讀入一個時鐘的數據,然后一直保持到讀完9個位data_byte_n=data_in; //讀一個字節數據else if(cnt_FSM==4'd10) //到達9就清零,就把發送的數據置位為1(停止位)data_byte_n=8'hff; elsedata_byte_n=data_byte; //否則保持end//***************數據發送*************** //組合電路,依次發送0-7位always @(*) //begin case(cnt_FSM) //不需要break,4'b0001:TXD=0; //第一位是起始位,起始位為04'b0010:TXD=data_byte[0]; //數據14'b0011:TXD=data_byte[1]; //數據24'b0100:TXD=data_byte[2]; //...34'b0101:TXD=data_byte[3]; //...44'b0110:TXD=data_byte[4]; //...54'b0111:TXD=data_byte[5]; //...64'b1000:TXD=data_byte[6]; //...74'b1001:TXD=data_byte[7]; //...8default:TXD=1'b1; //默認發送高電平,停止位endcaseendendmodule源碼下載鏈接:https://download.csdn.net/download/qq_39521541/12655945
總結
以上是生活随笔為你收集整理的FPGA仿锆石代码风格组合电路时序电路严格分开之(一)8通道16位AD采集的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 问卷设计一:问卷题目哪些有类型和注意要点
- 下一篇: 生成式对抗网络(GAN)相关问题汇总(较