ch32v203のSWD機能


mrubyc_arduinoでは、hal_writeに何らかの出力命令を定義しておくと、それが標準出力として取り扱われる。 標準出力には通常はSerialを使うことが多いが、SerialはUARTからも使えるので、標準出力は他の出力に割り当てることができる。 CH32マイコンでは、Single Wire Debug(SWD)機能があり、プログラムの書き込みもこの機能を使って行うことができるが、入出力としても使うことができる。 この機能をArduinoから扱うことができれば、これをmrubyc_arduinoの標準入出力に割り当てることが可能になる。 ch32v203について、SWD機能の使い方について調べてみた。

SWD機能については、ch32v203のリファレンスマニュアルにも使い方がほとんど書いてないので、その機能を使っているプログラムのソースから使い方を読み取るしか無い。 まず、Arduinoのpackageの中を調べてみたが、この機能についての記述は見付からなかった。 そこで、WCH社のプログラムを調べてみると、EVT/EXAM/SRC/Debug/に、この機能についての関数が定義されていることを発見した。 Arduinoでも同様のファイルは、packages/WCH/hardware/ch32v/1.0.4/system/CH32V20x/SRC/Debug/にあるのだが、こちらの方が古いファイルのようで、若干の違いがあり、その機能についての関数が無い。 新しい方のdebug.cには、アクセスするためのアドレスや、初期化と出力に対応する関数が、以下のように定義されている。

#define DEBUG_DATA0_ADDRESS  ((volatile uint32_t*)0xE0000380)
#define DEBUG_DATA1_ADDRESS  ((volatile uint32_t*)0xE0000384)
void SDI_Printf_Enable(void)
int _write(int fd, char *buf, int size)

これらをArduinoにうまく組み込めば、使えるようになりそうなことまでは分かった。

一方、このSWDのデータを表示するLinuxで使えるツールとしては、ch32funのminichlinkが便利である。 しかし、minichlinkと上記のdebug.cとは相性が悪いようだ。 そこで、ch32funで使われているSWDのコードを使うことにした。 そのためのヘッダとして、swd.hを作った。

#ifndef __CH32_SWD_H
#define __CH32_SWD_H

#include "stdio.h"

#ifdef __cplusplus
extern "C" {
#endif

#define FUNCONF_DEBUGPRINTF_TIMEOUT (1<<31) // Wait for a very very long time.

#define DMDATA0 ((volatile uint32_t*)0xe0000380)
#define DMDATA1 ((volatile uint32_t*)0xe0000384)

void poll_input( void );
int _write(int fd, const char *buf, int size);
void SetupDebugPrintf( void );
  
#ifdef __cplusplus
}
#endif

#endif

そして、このファイルをincludeして、 ch32fun.cの中のhandle_debug_input()からSetupDebugPrintf()までを抜き出してWEAK指定を取り、 swd.cの中にまとめた。 これらのファイルを使って、hal_writeから_write(0, (char*)buf, nbytes)を呼び出すようにすると、無事にminichlinkのターミナルに、標準出力として表示させることに成功した。

また、minichlinkのターミナルでは、出力以外に入力もできるようになっている。 マイコン側では、出力時やpoll_input()の実行時に、この入力を受け付けているようである。 しかし、mrubyc_arduinoからpoll_inputを呼び出しても、うまく入力を受信できなかった。 出力時には入力を受け入れることができており、試行錯誤の末に_write(0, “”, 1)を実行すると、うまくいくことを発見した。 このコマンドでは、null文字を一文字表示するように指定されているが、表示は行われないので、入力だけを受け付けることができるのである。 poll_inputがうまく働かない原因はブラックボックスが多過ぎてよく分かっていないが、 poll_inputでは入力した瞬間に読み取るためのもので、_writeでは入力の少し後でも読み取れるようになっていると私は予想している。 そして、inoファイル中で以下のような感じで変数lastに文字を受け取るようにした。

static volatile char inbytes=0;
static volatile uint8_t last=' ';
extern "C" {
void handle_debug_input( int numbytes, uint8_t * data ) {
  inbytes+=numbytes;
  last = data[0];
}
}

本当はバッファを作って複数の文字を受け取るようにするべきだが、minichlinkのターミナルからの入力は一文字ずつだし面倒だったので、最後の文字だけを処理するようにした。

ch32v203では、標準入出力がUARTだと、それを読み取るのが面倒だったのだが、 書き込み時に使用するSWDを標準入出力にできたので、配線はそのままで使えて、かなり便利になった。 これで、ch32v203をmrubyc_arduinoで使うにあたって、実装したいと思っていた機能は、ほぼ完成したと思う。