mrubyc_arduinoでPWM


mrubyc_arduinoにGPIOとADCを組込むことに成功したので、次はPWMだろうということでやってみたが、思ったよりも苦労した。 mruby/cのPWMでは、ONの割合と周波数を変えられるようになっている。 しかし、ArduinoのanalogWriteでは割合のみで、toneでは周波数のみしか変えられ無い。 仕方が無いので、これらを独立に変えるのは諦めて、割合のみか周波数のみを、それぞれanalogWriteとtoneで指定することにした。 次に問題だったのが、toneは本来は音をならすためのもので、音の継続時間を無しでも指定しても動くようにするために、C++でtone関数がオーバーロードされている。 そのため、C言語から呼び出すのが難しく、cppとした。

まず、pwm.hは、externをどこに入れるべきか試行錯誤したが、次のようにした。

#ifndef _PWM_H
#define _PWM_H

#include <Arduino.h>

#ifdef __cplusplus
extern "C" {
#endif

#include "mrubyc.h"

typedef struct PWM_HANDLE {
  uint8_t pin_num;
  uint16_t period;
  uint8_t duty;
} PWM_HANDLE;

void mrbc_init_class_pwm(void);

#ifdef __cplusplus
}
#endif

#endif

問題のpwm.cppだが、これらの関数はc言語から呼ばれるので、全体をexternで囲って、次のようにした。

#include "pwm.h"

extern "C" {

void c_pwm_new(mrb_vm *vm, mrb_value *v, int argc){
  v[0] = mrbc_instance_new(vm, v[0].cls, sizeof(PWM_HANDLE));
  PWM_HANDLE *handle = (PWM_HANDLE *)v[0].instance->data;
  handle->pin_num = GET_INT_ARG(1);
  MRBC_KW_ARG(frequency, freq, duty);
  if( MRBC_ISNUMERIC(duty) )
    analogWrite( handle->pin_num, MRBC_TO_INT(duty)*255/100.0 );
  if( MRBC_ISNUMERIC(frequency) )
    tone( handle->pin_num, MRBC_TO_INT(frequency), 0 );
  if( MRBC_ISNUMERIC(freq) )
    tone( handle->pin_num, MRBC_TO_INT(freq), 0 );
  MRBC_KW_DELETE(frequency, freq, duty);
}

static void c_pwm_duty(mrbc_vm *vm, mrbc_value *v, int argc){
  PWM_HANDLE *handle = (PWM_HANDLE *)v[0].instance->data;
  analogWrite( handle->pin_num, MRBC_TO_INT(v[1])*255/100.0 );
}

static void c_pwm_frequency(mrbc_vm *vm, mrbc_value *v, int argc){
  PWM_HANDLE *handle = (PWM_HANDLE *)v[0].instance->data;
  tone( handle->pin_num, MRBC_TO_INT(v[1]), 0 );
}

static void c_pwm_period_us(mrbc_vm *vm, mrbc_value *v, int argc){
  PWM_HANDLE *handle = (PWM_HANDLE *)v[0].instance->data;
  uint16_t us = GET_INT_ARG(1);
  double freq = (us == 0 ? 0 : 1e6 / us);
  tone( handle->pin_num, freq, 0 );
}

static void c_pwm_pulse_width_us(mrbc_vm *vm, mrbc_value *v, int argc){
  PWM_HANDLE *handle = (PWM_HANDLE *)v[0].instance->data;
}

void mrbc_init_class_pwm(void){
  mrbc_class *pwm = mrbc_define_class(0, "PWM", mrbc_class_object);
  mrbc_define_method(0, pwm, "new", c_pwm_new);
  mrbc_define_method(0, pwm, "duty", c_pwm_duty);
  mrbc_define_method(0, pwm, "frequency", c_pwm_frequency);
  mrbc_define_method(0, pwm, "period_us", c_pwm_period_us);
  mrbc_define_method(0, pwm, "pulse_width_us", c_pwm_pulse_width_us);
}

}

pulse_width_usは、割合と周波数の両方の影響を受けるので、実装は諦めた。 また、Floatを無くす可能性も考慮して、引数はすべて整数とした。

一応、ESP32で、LEDの強度または点滅速度を変化させるプログラムは動くことが確認できた。 周波数を変化させる時には、rubyの書き方の問題で、エラーが起きたが、mrubyではスクリプトの書き方に注意が必要なのかも知れない。