最近折騰一個調光顯示屏,廠商把調光的編程接口暴露成了PWM,所以就折騰了一下樹莓派的PWM輸出能力。這裏面有一些散落在不同文檔中的內容,還有一些不知道從什麼文檔裏查到的東西,歸納在這裏,以備後來者之需。
樹莓派的40 pin通用引腳可以輸出PWM信號,用於表示一個連續量以操作電機控制器、調光燈等設備。他的PWM輸出有兩種:軟件PWM其實就是普通的GPIO輸出,在一個線程裏定時開關,優點是所有的GPIO引腳都可以做,缺點是工作頻率、精度都比較低(在新的pigpio庫裏使用高精度時鐘改善了軟件PWM的精度,以前老的python庫實現超過幾百Hz就完全不準了)。而硬件PWM是使用樹莓派CPU內置的PWM硬件,將它的信號導出到40 pin引腳,優點是精度和工作頻率都很高,缺點是可用數量少,只有兩路。
硬件PWM的配置
想要使用硬件PWM,首先顯然是確認有哪些引腳可以輸出硬件PWM。參考/boot/overlays/README的內容,只有GPIO18是在全部樹莓派平台上都能作為硬件PWM輸出腳的。其它引腳是否可用於此,可以使用raspi-gpio程序,例如在我的樹莓派3B上面:
$ raspi-gpio funcs 12
GPIO, DEFAULT PULL, ALT0, ALT1, ALT2, ALT3, ALT4, ALT5
12, DOWN, PWM0, SD4, DPI_D8, AVEOUT_VID8, AVEIN_VID8, ARM_TMS
$ raspi-gpio funcs 41
GPIO, DEFAULT PULL, ALT0, ALT1, ALT2, ALT3, ALT4, ALT5
41, DOWN, PWM1, SD5, TE0, SD1_DAT5, SPI2_MOSI, RXD1
可以看到GPIO12的第零號替代功能是PWM0,而GPIO41的第零號替代功能是PWM1。
然後需要在樹莓派的啓動配置文件/boot/config.txt裏面加載對應的設備樹overlay,並設置參數,比如:
dtoverlay=pwm,pin=12,func=4
就會將PWM開啓在12號GPIO上。注意此處的func號必須與引腳的alt功能序號匹配,有個很奇怪的順序:
Func 0 = Input
Func 1 = Output
Func 2 = Alt 5
Func 3 = Alt 4
Func 4 = Alt 0
Func 5 = Alt 1
Func 6 = Alt 2
Func 7 = Alt 3
所以這裏func設為4,對應alt0,因為raspi-gpio告訴我們12號GPIO的PWM功能在alt0上。
硬件PWM的使用
Linux內核通過sysfs支持硬件PWM,所以這個部分的內容不僅限於樹莓派,實際上所有實現了對應驅動的開發板都一樣。
樹莓派的raspbian系統映像已經提供了對應的驅動,可以直接使用。修改並保存/boot/config.txt之後重啓設備,如果設置正確,可以在目錄/sys/class/pwm中看到一些東西,比如:
$ ls /sys/class/pwm
pwmchip0
$ ls /sys/class/pwm/pwmchip0
device export npwm power pwm0 subsystem uevent unexport
這些偽文件就是Linux內核PWM驅動提供的操縱接口,在shell裏可以通過cat讀,通過echo重定向寫。在任意編程語言裏也可以通過讀寫文件的接口進行同樣的操作。
首先創建一個PWM的導出,向export寫幾,就會創建對應的目錄在pwmchipX裏面:
$ echo 0 >/sys/class/pwm/pwmchip0/export
$ ls /sys/class/pwm/pwmchip0/pwm0
capture duty_cycle enable period polarity power uevent
這裏面,period是以納秒計數的PWM週期,duty_cycle是以納秒計數的每週期高電平時間。比如我想要一個20KHz的PWM,佔空比為80%,那我就應當:
$ cd /sys/class/pwm/pwmchip0/pwm0
$ echo 50000 >period # 兩萬Hz的時長是五萬納秒
$ echo 10000 >duty_cycle # 佔空比80%,那麼20%的時長就是一萬納秒
然後向enable寫0或者1進行開關。
$ echo 1 >enable # 立即開啓
關閉之後不會清空原有設置,再次打開會以之前設置的參數運行PWM。
如果需要釋放資源,向/sys/clas/pwm/pwmchipX/unexport寫對應的序號,會清空對應的PWM導出目錄,並且刪除配置。