2020年12月5日土曜日

raspberry pi : C言語で LED 調光 (PWM 制御)

目的:

C言語で PWMを使用した LED調光を行う。

環境/構成:

PWM は 2系統あり、それぞれ割り当てることができる GPIO  は

  • PWM Channel 0 : GPIO12、GPIO18
  • PWM Channel 1 : GPIO13、GPIO19

である。

環境/構成は、C言語で LED ON/OFF と同様であるが、LED 接続回路 は 下図の通り

PWM に設定できる GPIO 4本にLED を接続。


PWM制御:

 wiringpi を使用して PWM制御を行うには、
  1. wiringPiSetupGpio で wiringpi の初期設定を行う。
    (Gpio でなくても良いが、ここでは Gpio番号を使用する前提とする。)
  2. pinMode で PWM_OUTPUT を指定する。
  3. pwmSetMode で PWM のモード (バランス/マーク・スペース) を指定する。
  4. pwmSetClock で 1レンジ当たりのクロック数を設定する。
  5. pwmSetRange で 1周期のレンジ数 (分解能) を設定する。
  6. pwmWrite で パルス幅('H' のレンジ数) を設定する。

を行う。

pinMode の設定:

pinMode (GPIO番号, PWM_OUTPUT)
でpinMode を設定する。
これを実行すると、PWMが初期状態 (バランスモード) に戻ると思われる。
( 設定した GPIO番号(channel) と異なるGPIO番号(channel) にも影響 )
この為、PWMの設定(モード,クロック,レンジ)は pinMode を設定した後に行う必要がある。

PWM のモード

PWM のモードには バランスモード とマーク・スペースモードがある。
マーク・スペースモード
一定周期で パルスが発生し、パルス幅を変化させる。
pwmSetMode(PWM_MODE_MS)
バランスモード
デューティーが 50% に近づくほど 周期が短くなる。
pwmSetMode(PWM_MODE_BAL)
モードの設定は、channel 0,1 共通。

パルス周期、パルス幅の設定

パルス周期、パルス幅の設定は、pwmSetClock, pwmSetRange, pwmWrite で行う。
パランスモードの場合、デューティー 0 (100%) の場合の周期が設定値になる模様。
ベースのクロックは 19.2MHz。
pwmSetClock(clock)
1レンジ当たりのクロック数を設定する。
1レンジの長さは (1/19.2MHz)*クロック数 となる。
クロック数の設定は、channel 0,1 共通。
pwmSetRange(range)
1周期のレンジ数(PWM周期の分解能) を設定する。
PWMの周期は レンジ数 * クロック数 * (1/19.2MHz) となる。
PWMの周波数は 19.2MHz/(レンジ数 * クロック数) となる。
レンジ数の設定は、channel 0,1 共通。
pwmWrite(GPIO,width)
GPIO番号とパルス幅(レンジ数)を設定する。
パルス幅(時間)は パルス幅(レンジ数) * クロック数 * (1/19.2MHz) となる。

pwmSetClock(40)
pwmSetRange(256)
pwmWrite(12,128)
の場合、
周波数 : 19.2/(40*256) = 0.001875MHz
= 1.875KHz
デューティ 50%

プログラム例:

C言語での PWM を使用した LED の 調光 の例を示す。
引数として、GPIO番号とパルス幅(レンジ数) を指定する。
1レンジ当たりのクロック数は 40, 1周期のレンジ数 は 256 固定。
尚、GPIO番号のチェックは行っていない。

#include <stdio.h>
#include <stdlib.h>
#include <wiringPi.h>

#define LED_PIN 12

int main (int argc, char *argv[])
{
  int pin_no = 0 ;
  int num = 0 ;

  if (argc == 1) {
    pin_no = LED_PIN ;
    num = 256 ;
  } else if (argc == 2) {
    pin_no = atoi(argv[1]) ;
    num = 256 ;
  } else {
    pin_no = atoi(argv[1]) ;
    num    = atoi(argv[2]) ;
  }

    printf("pin_no : %d\n",pin_no) ;
    printf("num    : %d\n",num) ;

  wiringPiSetupGpio() ;
  pinMode (pin_no, PWM_OUTPUT) ;
  pwmSetMode(PWM_MODE_MS) ;
  pwmSetClock(40) ;
  pwmSetRange(256) ;

  pwmWrite(pin_no , num) ;

  return 0 ;
}

コンパイル 及び 実行:

コンパイルは、
gcc -o led_on led_on.c -lwiringPi

実行は、
sudo ./led_on 12 128
の様に行う。(GPIO番号 12 を デューティー 50% (パルス幅 128) で点灯)

実行すると、設定した GPIO番号に接続した LED が 点灯する。
尚、GPIO 12 と 18,  13 と 19 は 同じ PWM Channel  の為、片方の LED の 明るさを変えると もう一方の LED の明るさも変わる。
例えば、
GPIO#12 を パルス幅 128 で 点灯した後、GPIO#18 を パルス幅 64 で点灯すると、GPIO#12 の LED も GPIO#18 と 同じ明るさに変わる。

本プログラムでは、実行毎に pinMode の設定から行っているため、既に点灯(調光)したGPIO があると、後から実行した時の pinMode 設定から PWM設定完了までの間、先に点灯した GPIO の設定が変わってしまう。(一瞬の為、見ていてもわからないが、、、)
通常は先に、使用するGPIOの設定を全て行ってから、pwmWrite で パルス幅の設定のみを行うべきかと思う。

また、このプログラムの実行は sudo を付けて実行する必要がある。
sudo を付けない場合、

pinMode PWM: Unable to do this when using /dev/gpiomem. Try sudo?

の様なメッセージが表示され、実行できない。
/dev/gpiomem のパーミッション は、

crw-rw---- 1 root gpio 246, 0 12月  3 04:10 /dev/gpiomem

の様になっているが、gpio に所属しているユーザでも上のメッセージが表示された。
sudo 無しで実行できるようにしたいが、どうすれば良いか 不明。



 

0 件のコメント:

コメントを投稿