CH32V103のADC特性

概要

CH32V103のADC特性を計測してみました。しかしSerial周りでハマってしまい、思ったより面倒だったです。

対象ボード

上記のボードを使ってADC特性を計測してみました。

上記のSerial経由のSCPIで計測すれば楽勝だと思ったのですが、そう簡単には動きませんでした。

ADC値取得

void setup() {
  Serial.begin(115200);
}

void loop() {
  Serial.println(analogRead(PA0));
  delay(100);
}

Arduino環境であればほぼ同じコードで取得可能ですね。ちょっと違うのがPIN指定方法です。

PINADCユニット
PA0ADC_IN0
PA1ADC_IN1
PA2ADC_IN2
PA3ADC_IN3
PA4ADC_IN4
PA5ADC_IN5
PA6ADC_IN6
PA7ADC_IN7
PB0ADC_IN8
PB1ADC_IN9
PC0ADC_IN10
PC1ADC_IN11
PC2ADC_IN12
PC3ADC_IN13
PC4ADC_IN14
PC5ADC_IN15

データシートを確認してADCが使えるPINを確認する必要があります。今回は一番上にあるPA0を利用しました。

SCPI端末にする

#include "Vrekrer_scpi_parser.h"

SCPI_Parser scpi;

void setup() {
  Serial.begin(115200);

  scpi.RegisterCommand("*IDN?",
                       [](SCPI_C commands, SCPI_P parameters, Stream& interface) {
                         interface.println("CH32V103,Arduino SCPI,#00,v0.0.0");
                       });

  // Analog InPAn(RAW)
  scpi.RegisterCommand("ARInPA0",
                       [](SCPI_C commands, SCPI_P parameters, Stream& interface) {
                         interface.println(analogRead(PA0));
                       });
}

void loop() {
  scpi.ProcessInput(Serial, "\n");
  delay(1);
}

とりあえずPINは決め打ちで最低限必要なIDNと測定用コマンドだけ準備しました。

しかし動きません。。。

Serial.available()が未実装

Serial.available()が-1の固定値を返す実装になっていました。しかしながらwhile(Serial.available())みたいに処理するので戻り値はbool型を想定していて-1はtrueになって無限ループになってしまいます。

int HardwareSerial::available(void)
{
  return !serial_rx_active(&_serial);
}

そこで、available()を実装する必要がでてきました。とりあえず実際の読み込み関数をみたら状態取得関数があったので持ってきました。

しかし動きません。。。

CH32V103のArduinoはUARTまわりが弱く、取りこぼしが発生しやすい作りになっていました。

  while (Serial.available()) {
    int c = Serial.read();
    Serial.printf("%02X\n", c);
  }

こんな感じの処理で受信しつつ、文字があったら送信する処理がうまく動きません。printfで送信すると受信処理がブロックされて取りこぼしが発生します。受信バッファがないようですので、ほぼリアルタイムで受信し続ける必要があります。

  String command = Serial.readStringUntil('\n');
  Serial.println(command);

いろいろ実験していると上記のコードはかなり安定して受信できました。

int Stream::timedRead()
{
  int c;
  _startMillis = millis();
  do {
    c = read();
    if (c >= 0) {
      return c;
    }
  } while (millis() - _startMillis < _timeout);
  return -1;     // -1 indicates timeout
}

処理を追っていくと、上記のような取得コードでした。タイムアウトまで文字が来るのを待ち続けます。

int HardwareSerial::available(void)
{
  uint32_t _startMillis = millis();
  while(1){
    if(!serial_rx_active(&_serial)){
      return true;
    }
    if(100 < millis() - _startMillis){
      // timeout
      return false;
    }
  }
}

そこで自作のSerial.available()でもタイムアウトまで待ち続ける処理を入れました。通常available()はその瞬間にデータがなければfalseでもよいはずなのですが、Arduinoの場合loop()まで処理を戻してしまうと他の処理をして、受信エラーになることが多かったです。

なんとか動くようになりましたが、まだ受信取りこぼしが発生します。。。SCPI制御側でコマンドが失敗したら再送する処理をいれてなんとか動かすことができるようになりました。

いろいろ実験したところ、Arduino IDEからだと10msのタイムアウトだとたまに取りこぼしが発生しました。そこで100msに変えたところある程度安定しました。送信側も1文字ずつ送信しているのですが、たまに遅延が発生することがあり、そこでたまたまタイムアウト発生してしまうと受信が途切れて受信失敗するようです。

ADC特性

CH32V103は特記すべきことがない、ちゃんと使えるADCでした。ESP32のADCはなんであんなにポンコツなんだろう、、、

まとめ

CH32V103のADCはかなり素直で使いやすそうですね。精度はESP32と同じ4095で3.3Vになる12ビットになります。ただUARTの受信はちょっと使いにくそうでした。送信は送り続ければいいのですが、受信はいろいろ大変そうですね。

UARTまわりは、もう少しWCHのコードを確認をしてみたいと思います。データシートをかんたんに確認したところ、割り込み処理とか使って受信バッファを作る必要がある気がします。

コメント