這篇文章來源于DevicePlus.com英語網(wǎng)站的翻譯稿。
在本教程的第1部分中,我們介紹了FPGA,并在嵌入式 Micro的Mojo FPGA上完成了一個(gè)簡(jiǎn)單的入門項(xiàng)目。在第2部分中,我們將介紹一個(gè)更復(fù)雜的項(xiàng)目:在Mojo FPGA上實(shí)現(xiàn)硬件PWM。
脈沖寬度調(diào)制(PWM)被廣泛應(yīng)用于嵌入式系統(tǒng)中,用以控制LED亮度、電機(jī)轉(zhuǎn)速,甚至可用于通信。如果您使用的是Arduino,那么在使用analogWrite()函數(shù)的時(shí)候一定遇到過PWM。在Mojo上實(shí)現(xiàn)PWM之前,我們應(yīng)該首先了解一下PWM是如何工作的!
脈沖寬度調(diào)制(PWM)
微控制器和其他嵌入式系統(tǒng)處理器使用數(shù)字信號(hào)來進(jìn)行計(jì)算和執(zhí)行任務(wù)。這些信號(hào)僅以兩種電平狀態(tài)中的一種出現(xiàn):“高”電平(通常為3.3V或5V)和“低”電平(通常為0V)。這兩種電平分別編碼為二進(jìn)制1和0,因此可用于執(zhí)行各種各樣的工作任務(wù)。
但是,如果我們想輸出比“開和關(guān)”更多分級(jí)的電壓呢?在諸如上文中所提到的要求輸出可變強(qiáng)度的應(yīng)用激發(fā)了對(duì)該問題的探索。一種解決方案是將系統(tǒng)連接到稱為“數(shù)模轉(zhuǎn)換器(DAC)”的外部設(shè)備,該設(shè)備從主處理器獲取二進(jìn)制1和0形式的數(shù)字信號(hào)輸入,并輸出幾乎連續(xù)的0V到系統(tǒng)“最大電壓”范圍內(nèi)的電壓值。但是,對(duì)于大多數(shù)嵌入式系統(tǒng)應(yīng)用程序來說,存在一種更簡(jiǎn)單的方法:PWM!
從某種程度上講,PWM利用了人類的感知系統(tǒng)和物理系統(tǒng)(如電機(jī))能夠?qū)ψ兓妮斎脒M(jìn)行快速平均這一事實(shí)。系統(tǒng)生成一個(gè)包含高頻率脈沖的數(shù)字信號(hào),因此很難將每個(gè)脈沖區(qū)分開來。在給定的波形周期內(nèi),信號(hào)在該時(shí)間內(nèi)的某些時(shí)間為高電平,其余時(shí)間為低電平—高電平所占的時(shí)間比例稱為信號(hào)的占空比或工作周期。平均輸出效果(無論是LED的亮度還是電機(jī)轉(zhuǎn)速)取決于PWM信號(hào)的占空比。當(dāng)然,顧名思義,我們可以通過更改波形的占空比(脈沖寬度)來改變輸出效果!下圖顯示了不同占空比及其對(duì)應(yīng)波形的比較。
圖1:不同占空比及其相關(guān)脈沖波形的比較。顯然,當(dāng)信號(hào)一直為高電平,占空比為100%時(shí),出現(xiàn)了最“強(qiáng)烈”的輸出效果
Arduino板上的微控制器IC(如ATMega芯片)具有專用于根據(jù)處理器指令生成PWM信號(hào)的內(nèi)部硬件。這些電路的輸出連接到微控制器IC上的特定引腳。這也意味著只有這些引腳可以提供PWM信號(hào)。但是,在像Mojo這樣的FPGA上,我們可以根據(jù)需要配置內(nèi)部硬件,也就是說我們可以創(chuàng)建任意數(shù)量的硬件PWM電路!
Mojo FPGA上的硬件PWM
在本教程中,我們將探索如何在Verilog中實(shí)現(xiàn)硬件PWM,并了解Verilog代碼的模塊化如何使我們能夠根據(jù)需要在Mojo中配置盡可能多的硬件PWM電路。
以下是您需要遵照實(shí)施的所有內(nèi)容:
? Mojo V3開發(fā)板
我們將在嵌入式Micro提供的Mojo Base Project的基礎(chǔ)上,為該項(xiàng)目構(gòu)建我們自己的Verilog代碼。以這些項(xiàng)目為基礎(chǔ)來構(gòu)建是很有幫助的,因?yàn)樵O(shè)備規(guī)格和ISE中的其他初始化工作都已經(jīng)為我們?cè)O(shè)置好了!如果需要,您可以通過對(duì)項(xiàng)目目錄中的.xise項(xiàng)目文件進(jìn)行重命名來更改項(xiàng)目名稱。我將其命名為“MojoPWM.xise”。
通常,我們首先會(huì)在UCF中表明名稱和引腳編號(hào),用于與Mojo上的I/O引腳建立的不同連接。但是,對(duì)于本項(xiàng)目,我們將使用板載LED,信號(hào)名稱和引腳連接都已經(jīng)指定好了。因此,此處不需要額外的聲明。我們將從創(chuàng)建一個(gè)新的Verilog模塊開始,該模塊將指定我們硬件PWM的行為。并非將代碼直接放入mojo_top模塊中,我們將創(chuàng)建一個(gè)獨(dú)立的模塊以利用模塊化設(shè)計(jì)。如果我們要?jiǎng)?chuàng)建不同的硬件PWM電路來運(yùn)行不同的LED,則只需要?jiǎng)?chuàng)建該種獨(dú)立PWM模塊的新實(shí)例即可,無需復(fù)制和粘貼大量代碼。
右鍵單擊左側(cè)“Hierarchy”窗口中的任意位置,然后點(diǎn)擊名為“New Source…”的選項(xiàng),在“Source Type”的選項(xiàng)列表中,選擇“Verilog Module”,并將文件命名為“PWM.v”,以此將創(chuàng)建出一個(gè)新的Verilog文件,該文件具有用于PWM模塊的框架。
在真正開始編寫代碼之前,我們先來討論一下我們的PWM實(shí)現(xiàn)方法。如前所述,我們用該硬件生成的信號(hào)本身是周期性的,也就是說信號(hào)值與時(shí)間有關(guān)。因此,我們必須根據(jù)不間斷的滴答時(shí)鐘信號(hào)來指定其行為。這個(gè)時(shí)鐘信號(hào)已經(jīng)作為系統(tǒng)輸入包含在mojo_top模塊中了,為方波信號(hào),圖形如下所示:
我們的硬件操作可總結(jié)如下:
? 每個(gè)時(shí)鐘周期(從信號(hào)的上升沿開始),我們將增加內(nèi)部計(jì)數(shù)器的值,該值的范圍為0-255。
? 占空比將作為輸入包含在模塊中,范圍為0-255(就像Arduino的硬件PWM中的那樣)。如果我們的計(jì)數(shù)器值小于占空比,那么輸出信號(hào)將為高電平,否則,輸出信號(hào)將為低電平。
? 在復(fù)位線上收到高電平值時(shí),硬件將復(fù)位計(jì)數(shù)器。
我們選擇值255作為最大計(jì)數(shù)器值,因?yàn)檫@是8位數(shù)值中可以存儲(chǔ)的最大值(11111111)。如果從該值增加1,則計(jì)數(shù)器將回復(fù)到00000000,因?yàn)闀?huì)溢出。要了解有關(guān)二進(jìn)制計(jì)算和整數(shù)的二進(jìn)制表示的更多信息,請(qǐng)點(diǎn)擊下面附錄中的鏈接進(jìn)行查看!
這是一個(gè)時(shí)序圖,表示我們隨內(nèi)部時(shí)鐘信號(hào)的硬件操作:
圖2:占空比為3/255的8位硬件PWM模塊的時(shí)序圖。二進(jìn)制中的最大占空比值與最大計(jì)數(shù)器值相同,均為11111111
我們將以輸入和輸出信號(hào)列表作為開始,進(jìn)行對(duì)PWM模塊的Verilog描述:
input clk,
input rst,
input[7:0] duty,
output sig_drv
您可能已經(jīng)從它們的名稱中看出來了,這四個(gè)信號(hào)分別為時(shí)鐘、復(fù)位、占空比值和PWM輸出信號(hào)。
接下來,我們需要限定輸出信號(hào)sig_drv的數(shù)據(jù)類型。Verilog有兩種數(shù)據(jù)類型,線網(wǎng)(wire)和寄存器(reg)。雖然這兩種類型之間的差異對(duì)我們的應(yīng)用程序來說是很微小的,但是有一個(gè)主要區(qū)別需要注意,就是當(dāng)我們就像在本項(xiàng)目中這樣使用always塊時(shí),只能寫regs而不能寫wires。我們隨后會(huì)討論always塊及其相關(guān)操作。如果信號(hào)列表中的信號(hào)沒有被限定為wire或reg,Verilog將默認(rèn)其聲明為wire類型。在這種情況下,我們需要通過模塊信號(hào)列表之后的以下行將sig_drv描述為reg:
reg sig_drv;
我們還將使用如上所述的8位計(jì)數(shù)器,并且通過always塊對(duì)其進(jìn)行設(shè)置。因此,我們需要聲明一個(gè)8位大小的計(jì)數(shù)器,如下所示:
reg[7:0] counter;
您可能已經(jīng)注意到了,像許多其他編程語言一樣,Verilog是0索引的,這意味著計(jì)數(shù)總是從0開始的。因此,一個(gè)8位計(jì)數(shù)器中的位索引值為數(shù)字0到7。
接下來,我們對(duì)8位計(jì)數(shù)器的向上計(jì)數(shù)和輸出信號(hào)的驅(qū)動(dòng)的邏輯進(jìn)行描述。我們可以使用always來完成!always塊是一種Verilog結(jié)構(gòu),用戶可以指定僅在always塊的觸發(fā)條件被滿足時(shí)才會(huì)進(jìn)行的操作。一個(gè) always塊的基本結(jié)構(gòu)如下:
always @ (…)
begin
…
end
在 “@” 符號(hào)后的括號(hào)內(nèi),用戶需要指定觸發(fā)條件,該條件將決定何時(shí)執(zhí)行塊內(nèi)的邏輯。在我們的項(xiàng)目中,需要兩個(gè)always塊:
always @(*)
begin
end
always @(posedge clk or posedge rst)
begin
end
在第一個(gè)塊中,觸發(fā)條件為“*” ,這意味著只要項(xiàng)目中的任何信號(hào)發(fā)生變化,該塊內(nèi)的邏輯就會(huì)被執(zhí)行。硬件工程師可能將此塊稱為組合邏輯,該邏輯始終將輸出值定義為輸入值的某些函數(shù)。在此塊中,我們將放入在所有時(shí)刻都起作用的邏輯,而不是每個(gè)時(shí)鐘周期只執(zhí)行一次的邏輯,如輸出信號(hào)的驅(qū)動(dòng)。
如前所述,輸出信號(hào)為高電平驅(qū)動(dòng)還是低電平驅(qū)動(dòng)取決于計(jì)數(shù)器相比于占空比的大小。可以通過以下代碼行中的always @ (*) 塊實(shí)現(xiàn)此功能:
if (duty > counter)
begin
sig_drv = 1’b1;
end
else
begin
sig_drv = 1’b0;
end
sig_drv信號(hào)的寬度為1位(只有0和1兩種值…一個(gè)位),因此我們?cè)诮o它分配的值前加上字符“1’b”。從上面的代碼中我們可以看到,當(dāng)占空比大于計(jì)數(shù)器值時(shí),sig_drv線被驅(qū)動(dòng)為1(高),否則被驅(qū)動(dòng)為0(低)。
在第二個(gè)塊中,觸發(fā)條件為posedge clk 或 posedge rst。這意味著當(dāng)時(shí)鐘信號(hào)從低電平變?yōu)楦唠娖交驈?fù)位線從低電平變?yōu)楦唠娖綍r(shí),該塊內(nèi)的邏輯被執(zhí)行,且在每個(gè)時(shí)鐘周期內(nèi)僅執(zhí)行一次。我們將使用該always塊來指定每執(zhí)行一個(gè)時(shí)鐘周期時(shí)的計(jì)數(shù)器增加。可以使用此塊中的以下代碼行完成此操作:
if (rst)
begin
counter <= 8’b0;
end
else
begin
counter <= counter + 1;
end
if語句的第一段指定了當(dāng)復(fù)位線變?yōu)楦唠娖綍r(shí),必須將8位計(jì)數(shù)器復(fù)位為全零。第二段的else條件下指定了如果復(fù)位線不是高電平,則計(jì)數(shù)器的值會(huì)被增加。
我們可以看到分配給計(jì)數(shù)器的值取決于其先前的值。硬件工程師將這種類型的邏輯稱為順序模型,因?yàn)檩敵黾仁禽斎氲暮瘮?shù),也是過去狀態(tài)值的函數(shù)。
關(guān)于該代碼最后需要說明的是 “<=” 運(yùn)算符,即非阻塞賦值運(yùn)算符,用于將值賦給計(jì)數(shù)器變量。當(dāng)我們像往常一樣使用 “=” 運(yùn)算符(阻塞賦值運(yùn)算符)來給信號(hào)賦值時(shí),其實(shí)我們已經(jīng)默許Verilog對(duì)編寫的代碼自上而下來執(zhí)行。換句話說,如果我們連續(xù)編寫了兩個(gè)“=”賦值語句,那么第一個(gè)賦值操作將會(huì)在第二個(gè)賦值操作開始之前完成執(zhí)行。這其實(shí)在某些邏輯上是必要的,因?yàn)槲覀兛赡軙?huì)使用以此方式分配的值進(jìn)行后續(xù)計(jì)算。
但是,在基于高速時(shí)鐘信號(hào)的順序邏輯中,我們實(shí)際上希望所有的值的分配都在某種程度上并行發(fā)生(如果它們彼此獨(dú)立的話),這樣我們就不會(huì)將程序延遲到與下一個(gè)時(shí)鐘邊沿發(fā)生沖突的程度。在本程序中,我們沒有在每個(gè)時(shí)鐘邊沿上指定執(zhí)行多種任務(wù)。但是,如果我們需要這樣做的話,使用非阻塞賦值運(yùn)算符可以完成這項(xiàng)操作。
完整的PWM模塊應(yīng)如下所示:
現(xiàn)在我們已經(jīng)創(chuàng)建了PWM模塊,可以在mojo_top模塊中將其實(shí)例化了!在Verilog中,將一個(gè)模塊在另一個(gè)模塊中實(shí)例化使您可以在更高級(jí)別的模塊中一次或多次調(diào)用子模塊功能,而不必復(fù)制其代碼。就我們的項(xiàng)目來說,我們可以根據(jù)需要?jiǎng)?chuàng)建足夠多的PWM信號(hào)來驅(qū)動(dòng)不同的LED,甚至連接到Mojode 輸出引腳上!要配置PWM信號(hào)來點(diǎn)亮Mojo上的第8個(gè)LED,我們可以添加以下行:
PWM my_pwm(.clk(clk), .rst(rst), .duty(8’b01000000), .sig_drv(led[7]));
該行的第一個(gè)單詞PWM是我們要實(shí)例化的模塊的名稱。這將在我們選擇實(shí)例化多個(gè)副本時(shí)幫助識(shí)別同一PWM模塊的不同實(shí)例。
在模塊名稱后的括號(hào)內(nèi),我們使用了.(signal_name) 格式將更高級(jí)別模塊 (signal_name) 中的信號(hào)分配給子模塊中的相應(yīng)信號(hào) (module_signal_name)。
如果要更改PWM信號(hào)的占空比,我們要做的就是更改傳遞到占空比參數(shù)中的值。如果被驅(qū)動(dòng)的輸出信號(hào),則只需要將作為參數(shù)傳遞的信號(hào)更改為.sig_drv。
您所完成的mojo_top模塊應(yīng)如下所示:
要將代碼上傳到Mojo板上,請(qǐng)按照之前的步驟進(jìn)行操作:在ISE中生成編程文件,加載Mojo Loader應(yīng)用程序,然后將.bin文件上傳到Mojo。
恭喜您!您已經(jīng)在Mojo上實(shí)現(xiàn)硬件PWM了!如果想進(jìn)行進(jìn)一步的實(shí)驗(yàn),請(qǐng)嘗試創(chuàng)建多個(gè)硬件PWM信號(hào)并為其提供不同的占空比參數(shù)!您可以通過修改代碼,來實(shí)現(xiàn)通過一些撥動(dòng)開關(guān)將占空比值輸入到Mojo中嗎?
我們希望您對(duì)自己的第一個(gè)FPGA項(xiàng)目感到滿意!請(qǐng)繼續(xù)關(guān)注來獲取更多有關(guān)FPGA和微控制器的教程!
審核編輯黃宇
-
微控制器
+關(guān)注
關(guān)注
48文章
7455瀏覽量
150858 -
PWM
+關(guān)注
關(guān)注
114文章
5118瀏覽量
213169 -
Arduino
+關(guān)注
關(guān)注
187文章
6457瀏覽量
186503
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論