ch32funでCH32V003を使ってみた


WCH社のマイコンCH32を使うようになったが,以前はarduinoのWCH CH32Vサポートを使った. しかし,コンパイルしたときのサイズが大きくなるらしく,フラッシュの小さなマイコンで,複雑なプログラムをする場合には,適切ではないようだ. また,対応していない機能などもあるそうである. そこで,ch32funを試してみることにした. ch32funはもともとはch32v003用に開発されたようだが,他のマイコンにも徐々に対応して来ているが, フラッシュの小さなch32v003には特に有用だろう.

まずはインストールであるが,debianでは思ったよりも簡単だった. 必要なパッケージを以下のようにしてインストールする.

sudo apt-get install build-essential libnewlib-dev gcc-riscv64-unknown-elf libusb-1.0-0-dev libudev-dev

そして,ch32funのサイトからzipをdownloadする. コマンドラインでやりたいときは,下のようにする.

wget -O ch32fun-master.zip https://github.com/cnlohr/ch32fun/archive/refs/heads/master.zip

そして,それを展開したらインストールは完了である. 書き込みソフトであるminichlinkを使えるようにするためには,権限の問題を解決するために,以下を実行する.

sudo cp minichlink/99-minichlink.rules /etc/udev/rules.d/
sudo udevadm control --reload
sudo udevadm trigger

うまくインストール出来たかを確かめるために,examplesには,ch32v003用のプログラムの例があるので,そのどれかのフォルダに行って,コンパイルを試してみると良い. make buildとすると,コンパイルされて,やり直すときには,make cleanとしてから再度実行すれば良い. 単にmakeとすると,minichlinkを使ってマイコンへの書き込みまでやってくれる. 実行する前に,WCH-Link Eなどで接続しておけば良い.

新たなプログラムを作るときには,exampleの中のtemplateをコピーして使うと良い. Makefileでは,TARGETをc言語のファイル名として,includeはch32fun.mkのファイルの場所を指定する. プログラム中では,以下の順で実行する. まずは,初期化としてSystemInit();を実行する. 次に,使う機能を有効化する. 例えば,GPIOをすべて有効にするには,funGpioInitAll();とし,PDだけを使うならfunGpioInitD();とする. そして,やりたいことを記述する. 入力モードにして読み取って,出力モードにして出力するのは,以下のような感じ.

uint8_t v;
funPinMode( PD0, GPIO_CFGLR_IN_FLOAT );
v=funDigitalRead(PD0);
funPinMode( PD0, GPIO_CFGLR_OUT_10Mhz_PP );
funDigitalWrite( PD0, FUN_HIGH );
funDigitalWrite( PD0, FUN_LOW );

入力はアナログ(ANALOG),pull-up pull-down(PUPD),高インピーダンス(FLOAT)が選べる. 出力は周波数として2Mhz,10Mhz,50Mhzが,モードとしてpush-pull(PP)とopen drain(OD)が選べる. レジスタなどをあまり意識しないで,arduinoのように書くことが出来るようになって来ている.

PD1とPD7はそれぞれSWIOとNRSTに割り当てられているので,そのままではGPIOとしては使えないので,注意が必要である. PD7を使えるようにするには,flashのユーザーoptionのRST_MODEを変更しなければならない. これは,./minichlink -Dとすれば実現でき,./minichlink -dとすれば元に戻せる. PD1を使うには,プログラムからalternate function IOを有効化してR32_AFIO_PCFR1レジスタの値を変更しなければならない. 上のコマンドでGPIOを有効にするとAFIOも有効になっているので,以下のようにすると良い.

AFIO->PCFR1&=~AFIO_PCFR1_SWCFG;
AFIO->PCFR1|=AFIO_PCFR1_SWCFG_2;

しかし,これを実行すると,SWIOからのプログラムが出来なくなる. 電源を入れて数秒後にSWIOを無効になるようにして,必要ならその前にプログラムを書き込むようにすれば,便利である.

20pinのCH32V003F4P6でGNDと電源以外の18pinでLチカを試してみた. といっても,LEDを繋がずに,電圧の変化を見ただけだが. そのプログラムが以下とおりである. PD1とPD7が無事に動いて安心したが,PA1とPA2がうまく行っていないことに気がついたが,PAを使うつもりが無かったので,有効にしていなかっただけだった. PA1とPA2はクロックに使うこともできるが,dafaultだとそのままGPIOとして使える. 18pinをGPIOとして使えると,用途が広がるだろう.

#include "ch32fun.h"
#include <stdio.h>
int main()
{
 SystemInit();
 funGpioInitAll();
 funPinMode( PA1, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PA2, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PC0, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PC1, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PC2, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PC3, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PC4, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PC5, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PC6, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PC7, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PD0, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PD2, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PD3, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PD4, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PD5, GPIO_CFGLR_OUT_10Mhz_PP );
 funPinMode( PD6, GPIO_CFGLR_OUT_10Mhz_PP );
// disable NRST to use PD7, by ./minichlink -D
 funPinMode( PD7, GPIO_CFGLR_OUT_10Mhz_PP );
// disable SWIO to use PD1
 Delay_Ms(5000);
 AFIO->PCFR1&=~AFIO_PCFR1_SWCFG;
 AFIO->PCFR1|=AFIO_PCFR1_SWCFG_2;
 funPinMode( PD1, GPIO_CFGLR_OUT_10Mhz_PP );
 while(1)
 {
  Delay_Ms(1000);
  funDigitalWrite(PA1,FUN_HIGH);
  funDigitalWrite(PA2,FUN_HIGH);
  funDigitalWrite(PC0,FUN_HIGH);
  funDigitalWrite(PC1,FUN_HIGH);
  funDigitalWrite(PC2,FUN_HIGH);
  funDigitalWrite(PC3,FUN_HIGH);
  funDigitalWrite(PC4,FUN_HIGH);
  funDigitalWrite(PC5,FUN_HIGH);
  funDigitalWrite(PC6,FUN_HIGH);
  funDigitalWrite(PC7,FUN_HIGH);
  funDigitalWrite(PD0,FUN_HIGH);
  funDigitalWrite(PD1,FUN_HIGH);
  funDigitalWrite(PD2,FUN_HIGH);
  funDigitalWrite(PD3,FUN_HIGH);
  funDigitalWrite(PD4,FUN_HIGH);
  funDigitalWrite(PD5,FUN_HIGH);
  funDigitalWrite(PD6,FUN_HIGH);
  funDigitalWrite(PD7,FUN_HIGH);
  Delay_Ms(1000);
  funDigitalWrite(PA1,FUN_LOW);
  funDigitalWrite(PA2,FUN_LOW);
  funDigitalWrite(PC0,FUN_LOW);
  funDigitalWrite(PC1,FUN_LOW);
  funDigitalWrite(PC2,FUN_LOW);
  funDigitalWrite(PC3,FUN_LOW);
  funDigitalWrite(PC4,FUN_LOW);
  funDigitalWrite(PC5,FUN_LOW);
  funDigitalWrite(PC6,FUN_LOW);
  funDigitalWrite(PC7,FUN_LOW);
  funDigitalWrite(PD0,FUN_LOW);
  funDigitalWrite(PD1,FUN_LOW);
  funDigitalWrite(PD2,FUN_LOW);
  funDigitalWrite(PD3,FUN_LOW);
  funDigitalWrite(PD4,FUN_LOW);
  funDigitalWrite(PD5,FUN_LOW);
  funDigitalWrite(PD6,FUN_LOW);
  funDigitalWrite(PD7,FUN_LOW);
 }
}