FPGA初級項目10——基于SPI的DAC芯片進行數模轉換
DAC芯片介紹
DAC 芯片(數字模擬轉換器)是一種將數字信號轉換為連續模擬信號(如電壓或電流)的集成電路,廣泛應用于電子系統中,連接數字世界與模擬世界。
?
核心功能
數字到模擬轉換:將離散的二進制數字信號(如 0 和 1 的組合)轉換為連續的模擬信號,使計算機、微控制器等數字設備能夠驅動揚聲器、顯示器、傳感器等模擬設備。
應用領域:音頻處理(音樂播放器、手機)、視頻顯示(CRT 顯示器)、通信系統(無線信號調制)、工業控制(執行器驅動)等。
關鍵性能指標
需要明確:輸出電壓由參考電壓決定,輸入的數字量起到一個分壓作用。
分辨率:表示 DAC 能區分的最小電壓變化,通常以位數衡量(如 16 位、24 位)。位數越高,輸出越精細。
精度:包括微分非線性(DNL)和積分非線性(INL),反映輸出與理想值的偏差,精度越高,信號失真越小。
建立時間:從輸入變化到輸出穩定所需的時間,高速應用(如通信)需短建立時間。
動態范圍:最大信號與噪聲的比值,決定 DAC 處理強弱信號的能力(單位:dB)。
典型應用場景
音頻設備:如手機、耳機、Hi-Fi 播放器中的 DAC 芯片(如 AK4490、CS43131),直接影響音質。
工業控制:將數字指令轉換為模擬信號控制電機、閥門等執行器(如 DAC0832)。
通信系統:生成調制信號用于無線傳輸。
醫療設備:控制成像設備或監測儀器的模擬輸出。
選用芯片
這次使用的DAC芯片為TLV5618,其具體參數為:12位的分辨率;1Msps轉換速率;雙通道模擬輸出(可使用兩個通道A,B輸出電壓);最高可達到20M赫茲時鐘輸出
且經過換算關系有:輸出電壓U = 輸入數字量code;
問題分析
1. 該模塊書寫思路與上一篇文章ADC轉換大體上一致,同樣我們寫的是DAC芯片的驅動電路。我們所需明確的學習目標有:學會讀芯片手冊了解相關信息;學習線性序列機LSM的思想;
2. 根據芯片的時序要求,DAC芯片在SCLK的下降沿采樣DIN上的數值(已經穩定),DIN數值在SCLK上升沿變化;值得注意的是:DIN的D15-D12為配置位(配置轉換速率與功率;已經相應的通道選擇);D11-D0才是正常數據位的傳輸。
3. 因為TLV5618芯片為雙通道。那么如果我想要兩個通道同時輸出不同的電壓值該怎么辦呢?例如通道A輸出1V,通道B輸出2V。解決思路就是利用TLV5618芯片內部結構自帶的buffer緩沖器!通過相應的通道選擇模式,第一步先將2V輸入buffer;第二步再將1V輸入通道A,同時buffer輸入給通道B。
4. 再有的就是要嚴格按照芯片的時序要求來設置驅動電路。考慮相關的建立時間與保持時間,例如在開始階段,將CS電平拉低,DIN,SCLK電平拉高,這樣可滿足時序要求;同樣的在結束階段,在SCLK最后一個上升沿來臨時,要保證CS為低電平,否則很有可能采樣不到最后一個數據(芯片手冊要求)。
5. 同時還有一些輔助信號的撰寫(模仿上篇文章的思路),例如使能信號,采樣開始與采樣停止信號的觸發條件等。(詳情看代碼)
好的,解決完上述問題,我們來代碼!
代碼展示
//定義端口輸入輸出
module DAC_driver(Clk,Rst_n,DAC_DATA,Set_Go,Set_Done,DAC_CS_N,DAC_SCLK,DAC_DIN
);input Clk;input Rst_n;input [15:0]DAC_DATA;input Set_Go;output reg Set_Done;output reg DAC_CS_N;output reg DAC_SCLK;output reg DAC_DIN;parameter CLOCK_FREQ = 50_000_000;parameter SCLK_EREQ = 12_500_000;parameter MCNT_DIV_CNT = CLOCK_FREQ / (SCLK_EREQ * 2) - 1;reg [7:0]DIV_CNT;reg [5:0]LSM_CNT;reg [15:0]r_DAC_DATA;//一個暫存器,將用戶輸入的數據暫時存儲
always@(posedge Clk)
if(Set_Go)r_DAC_DATA <= DAC_DATA;
elser_DAC_DATA <= r_DAC_DATA;//使能信號的確立 reg Set_En;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)Set_En <= 1'd0;
else if(Set_Go)Set_En <= 1'd1;
else if((LSM_CNT == 6'd33) && (DIV_CNT == MCNT_DIV_CNT))Set_En <= 1'd0;
elseSet_En <= Set_En;//最小時間單元
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)DIV_CNT <= 0;
else if(Set_En)beginif(DIV_CNT == MCNT_DIV_CNT) DIV_CNT <= 0;else DIV_CNT <= DIV_CNT + 1'd1;end
elseDIV_CNT <= 0;//序列計數器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)LSM_CNT <= 6'd0;
else if(DIV_CNT == MCNT_DIV_CNT)beginif(LSM_CNT <= 6'd33)LSM_CNT <= 6'd0;elseLSM_CNT <= LSM_CNT + 1'd1;end
elseLSM_CNT <= LSM_CNT;//線性序列機的驅動輸出
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)beginDAC_SCLK <= 1'd1;DAC_DIN <= 1'd1;DAC_CS_N <= 1'd1; end
else if(DIV_CNT == MCNT_DIV_CNT)begincase(LSM_CNT)0 : begin DAC_CS_N <= 1'd0; DAC_DIN <= r_DAC_DATA[15]; DAC_SCLK <= 1'd1; end1 : begin DAC_SCLK <= 1'd0; end2 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[14]; end3 : begin DAC_SCLK <= 1'd0; end4 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[13]; end5 : begin DAC_SCLK <= 1'd0; end6 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[12]; end7 : begin DAC_SCLK <= 1'd0; end8 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[11]; end9 : begin DAC_SCLK <= 1'd0; end10 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[10]; end11 : begin DAC_SCLK <= 1'd0; end12 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[9]; end13 : begin DAC_SCLK <= 1'd0; end14 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[8]; end15 : begin DAC_SCLK <= 1'd0; end16 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[7]; end17 : begin DAC_SCLK <= 1'd0; end18 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[6]; end19 : begin DAC_SCLK <= 1'd0; end20 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[5]; end21 : begin DAC_SCLK <= 1'd0; end22 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[4]; end23 : begin DAC_SCLK <= 1'd0; end24 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[3]; end25 : begin DAC_SCLK <= 1'd0; end26 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[2]; end27 : begin DAC_SCLK <= 1'd0; end28 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[1]; end29 : begin DAC_SCLK <= 1'd0; end30 : begin DAC_SCLK <= 1'd1; DAC_DIN <= r_DAC_DATA[0]; end31 : begin DAC_SCLK <= 1'd0; end32 : begin DAC_SCLK <= 1'd1; end33 : begin DAC_CS_N <= 1'd1; enddefault: DAC_CS_N <= 1'd1;endcaseend//產生結束信號
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)Set_Done <= 0;
else if((LSM_CNT == 6'd33) && (DIV_CNT == MCNT_DIV_CNT))Set_Done <= 1'd1;
elseSet_Done <= 1'd0;endmodule
綜合出來的底層系統邏輯圖schematic如下: