mruby/cを使って直流電圧を測定してみる

# はじめに

しまねソフト研究開発センター（通称、ITOC）が中心となり開発を進めているmruby/cについて、色々と機能が揃ってきたので実際にできることをまとめてみようと思います。

[mruby/c](https://github.com/mrubyc/mrubyc)

# 必要なもの

- Windows 10
- [CY8CKIT-059 PSoC® 5LP Prototyping Kit](http://japan.cypress.com/documentation/development-kitsboards/cy8ckit-059-psoc-5lp-prototyping-kit-onboard-programmer-and) → 購入先は[こちら](https://amzn.to/2LgCGxx)

# 開発環境

Cypressのマイコンを使用することから、PSoC Creatorをダウンロードしてインストールします。

[PSoC® Creator™ Integrated Design Environment (IDE)](http://japan.cypress.com/products/psoc-creator-integrated-design-environment-ide)

# サンプルコードのダウンロード

以下のURLより「FlashLED3」のコードをダウンロードします。

[mruby／cをPSoC5LPで動かす｜Quick Start](http://www.s-itoc.jp/activity/research/mrubyc/mrubyc_tutorial/742)

なお、詳細はチュートリアルを参照下さい。

[mruby/c チュートリアル](http://www.s-itoc.jp/activity/research/mrubyc/mrubyc_tutorial/)

ダウンロードしたファイルを展開すると中に「FlashLED」というフォルダがあるので、適当な場所に解凍します。

# サンプルコードの動作確認

フォルダの中にある「FlashLED.cywrk」をダブルクリックし、開発環境を立ち上げる。
起動したら以下のような画面が表示されます。

![001.png](https://qiita-image-store.s3.amazonaws.com/0/114240/31fdf2ba-9f45-cd33-c047-d2aa66ca6754.png)

起動後、メニューにある「Build」-「Build Design01」を実行し、コンパイルできることを確認。

コンパイルできない場合は、PSoC Creatorのバージョンが古い可能性があるので、最新版をインストールし直して下さい。

コンパイルが正常に終了したら、「Debug」-「Program」を実行し、プログラムをPSoCに転送。

転送するとオンボードのLEDが点滅します。
また、スイッチを押している間、点灯します。

# ハードウェアの設定

ハードウェアの動作について、入出力の設定を以下の手順で行います。

1. 左のパネル上部にある「TopDesign.cysch」をダブルクリックして開きます。
1. Ports and Pinsの中にある「Analog Pin」を配置し、名前を「ADC_1」とします。
1. 右のパネルにあるAnalogのADCから「SAR ADC」をデザイン上に配置し、名前を「ADC_V」とします。
また、Sample modeを「Software trigger」とし、InputをReferenceを「Internal Vref」とします。  
![003.png](https://qiita-image-store.s3.amazonaws.com/0/114240/beba2b0a-1139-d348-c1d4-2128ffc7bd62.png)
1. DigitalのLogicから「Logic Low」をデザイン上に配置します。
1. 「Logic Low」の端子を先ほど配置した「ADC_V」のsoc端子と接続する。
1. Analog PinをSAR ADCの「+」へ、Logic LowをSAR ADCの「soc」へ接続する。

完成すると以下のようになります。
![004.png](https://qiita-image-store.s3.amazonaws.com/0/114240/2aa032b0-6648-644f-3db3-1e515e900580.png)

以上でハードウェアの設定が終了します。

# ソフトウェアの設定

次に、PSoCの持っている機能をmruby/cで使用できるようにします。

まずは、左のパネルの中にある「Source Files」から「main.c」を見つけてダブルクリックします。

電圧を測定するためにデバイス側で必要な機能をmruby/cで利用できる関数としてC言語で記述します。
追加するソースコードは以下のとおり。

```c:main.c
//================================================================
/*! ADC A/D変換の開始
*/
static void c_adc_start_convert(mrb_vm *vm, mrb_value v[], int argc)
{
  ADC_V_StartConvert();
}

//================================================================
/*! ADC A/D変換完了
*/
static void c_adc_is_end_conversion(mrb_vm *vm, mrb_value v[], int argc)
{
  int ret = ADC_V_IsEndConversion(ADC_V_RETURN_STATUS);
  SET_INT_RETURN(ret);
}

//================================================================
/*! ADC A/D変換結果の取得
*/
static void c_adc_get_result16(mrb_vm *vm, mrb_value v[], int argc)
{
  int ret = ADC_V_GetResult16();
  SET_INT_RETURN(ret);
}
```

次に、main.cの中にあるmain関数にmruby/cから上記で作成した関数を読み出すための定義を追加。

```c:main.c
  mrbc_define_method(0, mrbc_class_object, "adc_start_convert", c_adc_start_convert);
  mrbc_define_method(0, mrbc_class_object, "adc_is_end_conversion", c_adc_is_end_conversion);
  mrbc_define_method(0, mrbc_class_object, "adc_get_result16", c_adc_get_result16);
```

また、アナログ値をデジタルに変換する機能を呼び出すため、以下の命令文をmain関数に追加。

```c:main.c
  ADC_V_Start();
```

以上でソフトウェアの設定は終了です。

変更箇所を含めたmain.cの全文は以下のとおりです。

```c:main.c
#include <project.h>
#include "mrubyc.h"

//#include "sample1.c"
//#include "sample2.c"
#include "sample3.c"


#define MEMORY_SIZE (1024*30)
static uint8_t memory_pool[MEMORY_SIZE];

//================================================================
 /*! オンボードSW 現在状態の読み込み
*/
static void c_sw1_read(mrb_vm *vm, mrb_value *v, int argc)
{
  int sw1 = SW1_Read();
  SET_INT_RETURN(sw1);
}

//================================================================
/*! オンボードLED ON/OFF
*/
static void c_led1_write(mrb_vm *vm, mrb_value *v, int argc)
{
  LED1_Write(GET_INT_ARG(1));
}

//================================================================
/*! ADC A/D変換の開始
*/
static void c_adc_start_convert(mrb_vm *vm, mrb_value v[], int argc)
{
  ADC_V_StartConvert();
}

//================================================================
/*! ADC A/D変換完了
*/
static void c_adc_is_end_conversion(mrb_vm *vm, mrb_value v[], int argc)
{
  int ret = ADC_V_IsEndConversion(ADC_V_RETURN_STATUS);
  SET_INT_RETURN(ret);
}

//================================================================
/*! ADC A/D変換結果の取得
*/
static void c_adc_get_result16(mrb_vm *vm, mrb_value v[], int argc)
{
  int ret = ADC_V_GetResult16();
  SET_INT_RETURN(ret);
}

//================================================================
/*! HAL （別途説明します）
*/
int hal_write(int fd, const void *buf, int nbytes)
{
  UART_1_PutArray( buf, nbytes );
  return nbytes;
}
int hal_flush(int fd)
{
  return 0;
}


//================================================================
/*! タイマー割込ハンドラ
*/
CY_ISR(isr_1)
{
  mrbc_tick();
}


int main()
{
  CyGlobalIntEnable;
  mrbc_init(memory_pool, MEMORY_SIZE);

  isr_1_StartEx(isr_1);
  UART_1_Start();   // 追加
  
  ADC_V_Start();
  
  mrbc_define_method(0, mrbc_class_object, "sw1_read",   c_sw1_read);
  mrbc_define_method(0, mrbc_class_object, "led1_write", c_led1_write);

  mrbc_define_method(0, mrbc_class_object, "adc_start_convert", c_adc_start_convert);
  mrbc_define_method(0, mrbc_class_object, "adc_is_end_conversion", c_adc_is_end_conversion);
  mrbc_define_method(0, mrbc_class_object, "adc_get_result16", c_adc_get_result16);

  //mrbc_create_task( sample1, 0 );
  //mrbc_create_task( sample2, 0 );
  mrbc_create_task( sample3, 0 );
  mrbc_run();

 return 0;
}
```

ここで、「sample1.c」と「sample2.c」は使用しないのでコメントアウトし、新たに「sample3.c」というファイルを利用することとし、main.cに読み込むよう設定を追記してあります。

# 電圧測定のコードを作成

上述したA/D変換に関する関数を適宜実行しながら、値を取得するスクリプトをRubyで作成します。

```ruby:sample3.rb

while true
  
  # A/D変換の開始
  adc_start_convert()
  
  # A/D変換完了まで待機
  while adc_is_end_conversion() != 0; end
  
  # A/D変換結果の取得
  r16 = adc_get_result16()
  value = 2.048 * r16 / 0xfff
  
  # 結果の出力（ここではシリアルポート）
  puts value
  
  # 1秒間待機
  sleep 1
  
end
```

この作成したファイルを「Design01.cydsn」というフォルダに保存。

# ビルドと転送

サンプルコードと同じ手順で動作確認します。
ここで、エラーなく処理が進めば成功...のはず。

PSoC Creatorで「Pins」を表示するとADC_INで使用しているピン番号が確認できるので、ここに電圧を印加します。

![005.png](https://qiita-image-store.s3.amazonaws.com/0/114240/9ab07e0d-541f-5be2-f277-dd8cd4ed6f5f.png)

なお、印加できる電圧は0.0〜2.048[V]なので、ご注意下さい。

# 動作確認

チュートリアルの[Chapter03](http://www.s-itoc.jp/activity/research/mrubyc/mrubyc_tutorial/737)でシリアルコンソールが使用できるようになっているので、測定した電圧をRubyで取得してみます。

なぜかここからはMacで...(^_^;)

PSoCに接続したUSBケーブルをMacに接続すると「/dev」中にポートが追加されます。
ここでは「/dev/tty.usbmodem1423」でした。

ちなみに、SerialPortというgemを使用するので、インストールされていない場合は、以下のコマンドでインストールして下さい。

```
gem install serialport
```

準備ができたら、以下のとおりスクリプトを作成します。


```rb:test.rb
#!/usr/bin/env ruby

require 'serialport'
sp = SerialPort.new('/dev/tty.usbmodem1423', 57600, 8, 1, 0)

while true do
  print "#{sprintf("%7.3f",sp.gets.to_f)}\r"
end
```

これを以下のコマンドで実行。

```
$ ruby test.rb
  1.829
```

できた！