PWMを使用可能にするには
前回はGPIO制御でLEDの点灯・消灯を
今回はハードウェアPWM制御でLEDの明るさを変えてみましょう。
NanoPi NEOは1チャネルのPWM(PWM0)を搭載していますが、ArmbianのデフォルトではPWM機能とピンを共有(Multiplex)しているUART0がシリアルコンソール用途のために有効になっており、PWM機能は使用できない状態になっています。まず最初にDevice Tree overlaysという仕組みでUART0機能を無効・PWM機能を有効にします。ここでは詳しい説明は割愛し、PWMを有効にする方法だけ記載します。
まず、ピンの状態を確認します。PWM0はPA5ピンにアサインされています(Allwinner H3のデータシートで確認可能です)。
# su パスワード: # cat /sys/kernel/debug/pinctrl/1c20800.pinctrl/pinmux-pins :(省略) pin 4 (PA4): 1c28000.serial (GPIO UNCLAIMED) function uart0 group PA4 pin 5 (PA5): 1c28000.serial (GPIO UNCLAIMED) function uart0 group PA5 :(省略)
前述の通りPA5はUART0にアサインされている状態です。これをPWM0に切り替えます。具体的には/boot/armbianEnv.txtを開き内容を一部変更します。
「armbianEnv.txt」変更点①
「console=serial」の行の先頭に「#」を追加してコメントアウトする。
「armbianEnv.txt」変更点②
「overlays」の行に「pwm」と追記する。
以下のような感じです。
$ sudo vi /boot/armbianEnv.txt
verbosity=1
bootlogo=false
#console=serial
disp_mode=1920x1080p60
overlay_prefix=sun8i-h3
overlays=usbhost1 usbhost2 pwm
rootdev=UUID=4898d57e-5d6d-4c9c-8e8a-96f3009f5fb5
rootfstype=ext4
usbstoragequirks=0x2537:0x1066:u,0x2537:0x1068:u
※上記設定を行ってもUSB接続でのコンソール機能は利用可能です。
尚、本設定を有効にするためにはNanoPi NEOを再起動する必要があります。
Device Tree overlaysに関してもっと詳しく知りたい方は以下を参照ください。
PWM機能が有効になったことを確認
再起動したらPWM機能が有効になっていることを確認します。以下の通りになっていれば無事PWM機能が有効になっています。
# su パスワード: # cat /sys/kernel/debug/pinctrl/1c20800.pinctrl/pinmux-pins :(省略) pin 4 (PA4): (MUX UNCLAIMED) (GPIO UNCLAIMED) pin 5 (PA5): 1c21400.pwm (GPIO UNCLAIMED) function pwm0 group PA5 :(省略) # ls /sys/class/pwm/ pwmchip0
PWMを制御するには
前回のGPIO制御同様にsysfsで制御します。基本的な制御方法はGPIOと同じです。
# ls /sys/class/pwm/pwmchip0 device export npwm power subsystem uevent unexport # echo 0 > /sys/class/pwm/pwmchip0/export # ls /sys/class/pwm/pwmchip0 device export npwm power pwm0 subsystem uevent unexport # ls /sys/class/pwm/pwmchip0/pwm0/ capture duty_cycle enable period polarity power uevent # cat /sys/class/pwm/pwmchip0/pwm0/enable 0
以下の項目を設定することでPWMを制御できます。
period
PWM信号の周期を設定する。nsec単位の値。
duty_cycle
PWM信号のアクティブ時間を設定する。nsec単位の値で、periodより小さい値である必要がある。
polality
PWM信号の極性を設定する。このプロパティへの書き込みは、PWMチップが変更をサポートしている場合にのみ機能する。PWMがdisableの場合にのみ変更できる。値は “normal” または “inversed” 。
enable
PWM信号の有効(1)/無効(0)を設定する。
上記項目については以下「Pulse Width Modulation (PWM) interface」の「Using PWMs with the sysfs interface」節に記載があります。
制御内容を設計
今回の目的は「PWM制御でLEDの明るさを制御する」です。簡単にいうと「電圧のON/OFFを高速に行い、かつONとOFFの時間割合を変えることで明るさを変える」です。
調べたところ周波数は200Hz~1kHz程度が一般的な模様です。今回はせっかくのハードPWM使用なので1kHzにしてみます。
1kHzということは1/1000=0.001=1ms周期です。前述の通りperiodはnsec単位のため、1,000,000を設定します。
duty_cycleには1msの間で信号がHiになっている時間を設定します。こちらもnsec単位です。今回はduty比(ONになっている時間の割合)を10%~100%で徐々に明るくしていき100%~10%で徐々に暗くしていく、それを繰り返すこととします。
更に、10%から100%(もしくは100%から10%)に到達するまでの時間は1秒とします。2%ずつ変化させるとして、90[%]/2[%]=45、つまり1秒間に45回制御するということになるので制御間隔は1[s]/45[回]≒0.022[s]=22[ms]となります。
C言語でプログラミング
sysfsをopen/writeするという制御の大筋はGPIO編と大差ないため細かい説明は割愛します。コメントを見ながらソースを読んでみてください。
$ vi led-soft-flashing.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int fd = -1;
int ret;
struct stat st = { 0 };
unsigned long periodValue;
unsigned long dutyValue;
char valueStr[16];
int strSize;
const struct timespec tms = { 0U, 22U * 1000U * 1000U }; // 22ms
// Unexport pwm0 if it exists.
ret = stat("/sys/class/pwm/pwmchip0/pwm0", &st);
if(ret == 0) {
fd = open("/sys/class/pwm/pwmchip0/unexport",O_WRONLY);
if(write(fd, "0", 1) != 1) {
perror("write-1");
exit(EXIT_FAILURE);
}
close(fd);
}
// Export pwm0.
fd = open("/sys/class/pwm/pwmchip0/export",O_WRONLY);
if(write(fd, "0", 1) != 1) {
perror("write-2");
exit(EXIT_FAILURE);
}
close(fd);
// Set polarity to normal.
fd = open("/sys/class/pwm/pwmchip0/pwm0/polarity",O_WRONLY);
if(write(fd, "normal", 6) != 6) {
perror("write-3");
exit(EXIT_FAILURE);
}
close(fd);
// Set period value.
fd = open("/sys/class/pwm/pwmchip0/pwm0/period",O_RDWR);
periodValue = 1000000; // 1kHz
strSize = snprintf(valueStr ,16, "%lu", periodValue);
if(write(fd, valueStr, strSize) != strSize) {
perror("write-4");
exit(EXIT_FAILURE);
}
close(fd);
// Set initial duty cycle value.
fd = open("/sys/class/pwm/pwmchip0/pwm0/duty_cycle",O_RDWR);
dutyValue = 100000; // 10%
strSize = snprintf(valueStr, 16, "%lu", dutyValue);
if(write(fd, valueStr, strSize) != strSize) {
perror("write-5");
exit(EXIT_FAILURE);
}
close(fd);
// Set pwm0 enable.
fd = open("/sys/class/pwm/pwmchip0/pwm0/enable",O_WRONLY);
if(write(fd, "1", 1) != 1) {
perror("write-6");
exit(EXIT_FAILURE);
}
close(fd);
while(1) {
// duty 10%->100%
do {
nanosleep(&tms, NULL);
//
fd = open("/sys/class/pwm/pwmchip0/pwm0/duty_cycle",O_RDWR);
dutyValue += 20000; // +2%
strSize = snprintf(valueStr ,16, "%lu", dutyValue);
if(write(fd, valueStr, strSize) != strSize) {
perror("write-7");
exit(EXIT_FAILURE);
}
close(fd);
} while(dutyValue < 1000000);
// duty 100%->10%
do {
nanosleep(&tms, NULL);
//
fd = open("/sys/class/pwm/pwmchip0/pwm0/duty_cycle",O_RDWR);
dutyValue -= 20000; // -2%
strSize = snprintf(valueStr, 16, "%lu", dutyValue);
if(write(fd, valueStr, strSize) != strSize) {
perror("write-8");
exit(EXIT_FAILURE);
}
close(fd);
} while(dutyValue > 100000);
}
exit(EXIT_SUCCESS);
}
今回のプログラムは永久ループになっていますので停止させる際はCtrl-Cを入力してください。
LED回路の接続
前述の通りPA5ピンはUART0-RXピンと兼用です。SDカードスロットを上にしてみたときに左側の内側下から4番目のピンになります。
Lフワの様子
LED回路を接続したらプログラムを実行してみましょう。
$ gcc led-soft-flashing.c $ sudo ./a.out
こんな感じでふわっと点滅します[1]頑張ってgif作りました。
以下にオシロ画像を貼っておきます。こんな感じでdutyが変化していきます。
Hiが2V程度になっていますが、こんなもんなんでしょうかね。
今回の記事で使用した機材紹介
Xsdjasd NanoPi NEO用開発ボード + ヒートシンク + メタルケースキット Allwinner H3 クアッドコア 512MB RAM Openwrt/LEDE コンプリート マシン
13% オフPenkeef NanoPi NEO オープン ソース H3 開発ボード + ヒートシンク DDR3 RAM 512MB クアッドコア -A7 Openwrt Armbian
¥3,229 (2024年12月5日 14:42 GMT +09:00 時点 - 詳細はこちら価格および発送可能時期は表示された日付/時刻の時点のものであり、変更される場合があります。本商品の購入においては、購入の時点で当該の Amazon サイトに表示されている価格および発送可能時期の情報が適用されます。)lucka NanoPi NEO用開発ボード + ヒートシンク + メタルケースキット Allwinner H3 クアッドコア 512MB RAM Openwrt/LEDE コンプリート マシン
¥4,660 (2024年12月5日 14:42 GMT +09:00 時点 - 詳細はこちら価格および発送可能時期は表示された日付/時刻の時点のものであり、変更される場合があります。本商品の購入においては、購入の時点で当該の Amazon サイトに表示されている価格および発送可能時期の情報が適用されます。)KIOXIA(キオクシア) 旧東芝メモリ microSD 64GB UHS-I Class10 (最大読出速度100MB/s) Nintendo Switch動作確認済 国内サポート正規品 メーカー保証5年 KLMEA064G
15% オフエレコム エコ USBケーブル スマートフォン対応 2.0 A-microB 1.2m U2C-JAMB12BK
58% オフTP-Link WIFI 無線LAN 子機 11n/11g/b デュアルモード対応モデル 英語パッケージ TL-WN725N(EU)
16% オフELEGOO 50 PCS オスメスジャンパーワイヤ200mm (170 タイポイント ブレッドボード付き)
¥880 (2024年12月5日 22:25 GMT +09:00 時点 - 詳細はこちら価格および発送可能時期は表示された日付/時刻の時点のものであり、変更される場合があります。本商品の購入においては、購入の時点で当該の Amazon サイトに表示されている価格および発送可能時期の情報が適用されます。)WayinTop 発光ダイオード 金属皮膜抵抗器 セット 5mmLED 砲弾型 白/黄/緑/赤/青 40個ずつ 合計200個 抵抗器 10Ω~1MΩ 30種類 20本ずつ 合計600個 800pcs入り
¥1,450 (2024年12月6日 03:34 GMT +09:00 時点 - 詳細はこちら価格および発送可能時期は表示された日付/時刻の時点のものであり、変更される場合があります。本商品の購入においては、購入の時点で当該の Amazon サイトに表示されている価格および発送可能時期の情報が適用されます。)共立電気計器 (KYORITSU) 1012 キューマルチメータ
29% オフRIGOL デジタル・オシロスコープ DS1102Z-E 2アナログチャンネル+100MHz周波数帯域+1GSa/sリアルタイム・サンプルレート+24Mポイントレコード長+8bit高解像度+最大10V/div垂直軸レンジ+最高30000wfms/s高速波形取り込みレート【国内正規品】【メーカー直営3年保証】【日本語取扱説明書対応】【ハイコストパフォーマンス】
5% オフ注釈
↑1 | 頑張ってgif作りました |
---|
コメント