はじめに
今回は32ビットRISC-Vマイコン(CH32V003J4M6)でいろいろプログラムしていきます。これまでプログラムしてきた中でM5stack社のM5stamp C3U(2024/4/20時点switch science価格1287円)が最安でした。これでも十分コスパがいいのですが、無線通信がいらない、IOピンも14本もいらないような用途ではオーバースペックに感じていました。そこでより安価なマイコンを探しておりATtinyシリーズも候補に挙がったのですが、それよりさらに安いマイコンCH32V003J4M6(2024/4/20時点秋月電子通商価格40円)の存在を知り飛びつきました。マイコンの金額が安いこともさることながらプログラムを書き込むエミュレーターも750円と、ATtinyにAruduinoマイコンを使用してプログラムを書き込むことと比べれば破格の安さなので、これは試すしかない!
今回使用するもの
- CH32V003J4M6
秋月電子通商で販売しています。
https://akizukidenshi.com/catalog/g/g118062/
- マイコンDIP変換基板および基板用リードフレーム
CH32V003J4M6の販売ページに適した製品のリンクが掲載されています。それを選べば失敗はしないです(秋月マジ便利)。マイコンのDIP変換基板は「SOP8(1.27mm)DIP変換基板」というものを選び、基板用リードフレームはブレッドボードに差し込めるものなら何でもいいと思います。
- WCH-LinkEエミュレーター
CH32Vシリーズマイコンにプログラムを書き込むなら必須アイテムになります。こちらもCH32V003J4M6の販売ページに製品リンクが掲載されています。
はんだ付け
CH32Vマイコン唯一の欠点かもしれません(私感)。
私がはんだ付けへたなだけかもしれませんが、表面実装マイコンでかつ1.27mmピッチと細かいためはんだ付けが大変でした。この時はマスキングテープでマイコンと基板をテーブルに貼り付けて行いました。
ATtinyシリーズなら秋月電子で変換基板にはんだ付けされているキットが販売されているため、このような作業が楽になります。(秋月電子さんCH32Vマイコンに関しても出してくれないかなぁ。)
CH32Vシリーズには20ピンの0.65mmピッチのものもあると聞きますが、挑戦するのは先になりそうです。
開発環境構築
こちらのサイト参考にさせていただきました。
https://kanpapa.com/today/2023/06/blink-risc-v-ch32v003-arduino.html
エミュレーターの設定からArduino IDEでプログラムできるようになるまでの過程が書かれており、わかりやすかったです。
私のやってきた流れを備忘録として箇条書きで記載します。
- 以下のリンク先にアクセスして、WCH-LinkUtility.ZIPをダウンロードする。
エミュレータのソフトhttps://www.wch.cn/downloads/WCH-LinkUtility_ZIP.html - ダウンロードしたzipファイルを展開し、中にあるWCH-LinkUtility.exeを実行。
- インストールがひと通り完了するとWCH-LinkUtility V2.00が立ち上がる。
- マイコンと接続したWCH LinkEをUSBに接続してWCH-LinkUtility V2.00の下部のほう”Active WCH-Link Mode”のGetボタンを押すと”WCH-LinkDAP-WINUSB”が表示される。このプルダウンメニューから「WCH-LinkRV」を選択してSetを押すと、WCH-LinkEのモードがCH32V用に切り替わる。WCH-LinkUtilityは最初の設定などで使用するくらいで、実際のプログラム書き込みはArduino IDEからいける。
- Arduino IDEにボードマネージャーを追加する。
ファイル→基本設定→追加のボードマネージャのURLの右側のボタンを押す。
ウィンドウが立ち上がり、入力画面にボードマネージャーのURLを入力しOK2回。 - ボードマネージャーのインストール動作が行われ、終わったら、ボードマネージャーの検索欄にCH32と入力して検索し”CH32 MCU EVT Boards”をインストール。
<備忘録ポイント>
バージョンは1.0.3を選択すること。2024/4/9時点では1.0.4が最新だが、インストールに失敗した。1.0.3のインストール後1.0.4へのアップデートも失敗。
- ボードはCH32V00xを選択し、ポートはWCH-LinkEが割り当てられているCOMxを設定。
また調査していくうえでCH32Vマイコンにプログラミングで気を付けなければいけない点をいくつか見かけたのでここでまとめます。
- 8ピンを使用してプログラムを書き込んでいるため、Serial.beginしてしまうとプログラムが書き込めなくなるため、プログラムにSerial.beginは記述しない。
- 8ピンはプログラム書き込みで使用するため、8ピンを使用するプログラム(例えばUART通信におけるTX側など)を書き込んでしまうと、次以降書き込みができなくなってしまう。プログラミングの際は8ピンは使用しないプログラムを組むようにする(実質UART通信は不可)。
- 1, 2番について、Serial.beginの前にプログラムを書き込む時間用のdelayを設けてる手法で解決可能かも?まだ私は未検証です。
参考サイトhttps://qiita.com/Ke_N_551/items/af7f3563f91fef542c69 - WCH-LinkUtilityを使っていると時々「ファームウェア更新が必要です」のようなメッセージが出てくる。これを「更新する」を選ぶと更新に失敗しWCH-LinkEがPCから認識されなくなる。解決するにはもう一台WCH-LinkEが必要になる。決して更新しないこと。
私の場合はダウンロード直後にこのメッセージが出たので、事前にこの情報を知らなければ更新してました。先駆者様には感謝です。
参考サイトhttps://techblog.elspina.space/hardware-ch32v003/
プログラミング
Lチカ
まずは動作確認も含めて簡単なLチカプログラムをやっていきます。
下図のように配線しました。
エミュレーターと接続するのは3本で、うち2本はVDDとGNDなので実質プログラム書き込みは8ピンに接続している1本のみです。
プログラムは以下の通りになりました。
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(PD6, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(PD6, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500); // wait for a second
digitalWrite(PD6, LOW); // turn the LED off by making the voltage LOW
delay(500); // wait for a second
}
電源を入れると1ピンに接続されたLEDがひたすら点滅する簡単なプログラムです。
とりあえず問題なくLチカ動作が確認できました。
ボタン入力
マイコンから信号を出力するプログラムはできたので、次はマイコンへ入力するプログラムを作っていきます。
下図のように配線しました。
Lチカプログラムの時の回路に5ピンにボタン入力を追加したものです。
プログラムは以下の通りになりました。
void setup() {
pinMode(PD6, OUTPUT);
pinMode(PC1, INPUT_PULLDOWN);
}
void loop() {
bool in = digitalRead(PC1);
if(in == HIGH){
digitalWrite(PD6, HIGH);
}else{
digitalWrite(PD6, LOW);
}
}
5ピンに接続されたボタンを押すと1ピンに接続されたLEDが光ります。ボタンを離すと消えます。こちらも簡単な挙動です。
問題なく動作確認できました。
タイマーリセット
少し複雑なプログラムに挑戦します。
プログラムの動きは以下の通りです。
- 電源が入ると、10秒のカウントダウンを始める。LEDは消灯のまま。
- カウントダウンが0になるまでにスイッチが押されるとカウントダウンがリセットされて、10秒からリスタートする。
- カウントダウンが0になるとLEDが点灯する。点灯してからボタンを押すとLEDは消灯し、カウントダウンもリセットされ、再度10秒からリスタートする。
回路図はボタン入力の時のものと同じです。
プログラムは以下の通りです。
//timerset.ino
byte countdowntime;
void setup() {
pinMode(PD6, OUTPUT);
pinMode(PC1, INPUT_PULLDOWN);
countdowntime = 10;
}
void loop() {
bool in = digitalRead(PC1);
if(in == HIGH){
countdowntime = 10;
digitalWrite(PD6, LOW);
}else{
if(countdowntime>0){
countdowntime = countdowntime-1;
delay(500);
}else{
digitalWrite(PD6, HIGH);
}
}
}
プログラム挙動としては概ねできたように感じるのですが、ここで気になった点が2つあります。
- delayの数字が実時間とずれる。
このプログラムではcountdowntime変数に10という数字を入れてそれを1秒ごとに-1することで10秒のカウントダウンを実現しています。
その場合’delay(1000)'(=1秒)と置くことが本来正しいのですが、なぜか秒数が2倍の2秒ごとになってしまう現象が起き、結果20秒のカウントダウンになってしまいました。マイコンのクロック数に関する性能のせいなのか、プログラム長さによるものなのか原因はわからずじまいでした。
よって半分の’delay(500)'(=0.5秒)と置くことで帳尻を合わせています。 - カウントダウン中のボタンの感度が悪い。
これも謎なんですが、カウントダウン完了後に押すボタンはきちんと反応するのですが、カウントダウン中では1秒くらい長押ししないと反応しません。
2番に関しては、プルアップ抵抗がないことが原因かと思い追加しても直りませんでした。そもそもカウントダウン完了後の時はちょっと押すだけで反応するのでプログラムの問題と考えました。
if文のcountdownループ外にボタン操作プログラムがあることが問題かと考え、プログラム修正を行ったものが次のものになります。
//timereset_ver2.ino
byte countdowntime;
void setup() {
pinMode(PD6, OUTPUT);
pinMode(PC1, INPUT_PULLDOWN);
countdowntime = 10;
}
void loop() {
if(countdowntime>0){
bool in = digitalRead(PC1);
if(in == HIGH){
countdowntime = 10;
digitalWrite(PD6, LOW);
}else{
countdowntime--;
delay(500);
}
}else{
digitalWrite(PD6, HIGH);
countdowntime = 10;
}
}
countdownループ内にボタン操作プログラムを入れました。
結果としては悪化しました(;´Д`)。
前のプログラムではカウントダウン完了後は問題なかったのが、このプログラムにしてからcountdownのループ中かそうでないかにかかわらずボタンの反応性が悪化しました。カウントダウン中も完了後も1秒くらい長押ししないと反応しません。
まとめ
最後のプログラムで解決できない謎が出てしまいましたが、致命的に使えないレベルではないと考えてます。
1個当たり40円のマイコンで、エミュレーターや基板なども含めても1,000円超えないくらいの金額でここまでできるのはコスパが高いです。今回のようなピン数も能力もそこまで要求されない用途では選択肢に上がると思います。
今回のプログラム難易度が高くないこともあると思いますが、includeを使用せずにプログラムできるのもポイント高いですね。アップデートの影響でプログラム修正しないといけないリスクが低いのは助かります。ただしCH32V003J4M6は書き込めるプログラム容量が小さいこともあり、今回のようなプログラムでも66%は使ってしまいます。今後プログラムしていくうえで容量とのせめぎあいが発生しそうです。
ともあれCH32VはA/D変換にも対応しており他にもいろいろな機能がありそうですので、次はもう少しレベルを上げてプログラムに挑戦したいです。