概要
前回の記事(📄CH32V003マイコンの汎用実験基板を設計・実装する)で作ったCH32V003マイコンの汎用実験基板に、rv003usbのUSBブートローダーを書き込みます。これで、WCH-Linkを使わずにUSBケーブルだけで直接プログラムを書き込めるようになります。
この記事ではUSB回路、ブートローダーの設定と書き込み、オプションバイトの変更、リセットボタンでブートローダーに入るための処理を紹介します。
rv003usbとブートローダー
CH32V003マイコンにはUSBペリフェラルが内蔵されていませんが、ソフトウェアUSB実装であるrv003usbを使うとUSBが使えるようになります(上位機種のCH32V103やCH32V203だとUSBペリフェラルが載っています)。
今回はrv003usbを使ったUSBブートローダーで、USB経由でプログラムを書き込めるようにします。
USB回路
rv003usbのREADMEに書かれているとおりにUSB周りの回路を組みます。


ピン割り当て
D+ピンとD−ピンの割り当てに制限があるので注意が必要です。
この2ピンはPORTA・PORTC・PORTDいずれかの0~4番(つまり、P[A,C,D]0~4)の中から選ぶ必要があります。また、2ピンは同じポート(PORTAなど)にする必要があります。
今回の基板での割り当てはREADMEどおり(D+: PD3, D−: PD4)にしています。
D−プルアップ(DPU)の制御
USBの接続状態を制御するためにD−ピンに1.5 kΩの抵抗を入れて、その先をPD5ピンに繋いでいます。常に認識状態でいいのならVCC(+3.3 V)に直接プルアップすることでDPUピンを省略できます。
CC抵抗
ホストから5 V電源を得るためにCC1・CC2ピンを5.1 kΩ抵抗でGNDにプルダウンします。
ダンピング抵抗
ノイズの低減と保護のために33 Ωの抵抗をマイコン近くに挿入しています。
ブートローダーの準備
開発環境
- OS:Windows 11
- IDE(ブートローダービルド用):VSCode+PlatformIO拡張機能
- IDE(ユーザープログラムビルド用):MounRiver Studio Ⅱ
rv003usbプロジェクトのクローン
rv003usbはch32funをサブモジュールとして含んでいるので、--recursiveを付けてクローンします。
git clone --recursive https://github.com/cnlohr/rv003usb.gitWCH-Linkドライバーの入れ替え
書き込みとオプションバイトの設定には、ch32funに同梱されているminichlink(ch32fun\minichlink\minichlink.exe)を使います。
minichlinkからWCH-Linkを使うには、ZadigでWCH-LinkのドライバーをWinUSBに入れ替える必要があります。
- Zadigを起動し、「Options」→「List All Devices」をオンにする

- リストから「WCH-Link (Interface 0)」を選ぶ(Interface 0と1のうち、元のドライバーがwinserではなくWCHLink_A64になっている方)

- 「WinUSB」を選んで「Replace Driver」を押す
デバイスマネージャーでドライバーを更新することで元に戻せます。
ブートローダーの設定
ブートローダーは初期設定のままだと、電源を入れてから約5秒間動いたあとユーザープログラムを起動します。今回は、ユーザーボタン(PD2)を押しながら電源を入れたときだけブートローダーに入るよう、bootloader/bootloader.cの#defineを次のように変えます。
#define DISABLE_BOOTLOAD
#define BOOTLOADER_BTN_PORT D
#define BOOTLOADER_BTN_PIN 2
#define BOOTLOADER_BTN_TRIG_LEVEL 0 // 1 = HIGH; 0 = LOW
//#define BOOTLOADER_BTN_PULL 1 // 1 = Pull-Up; 0 = Pull-Down; Optional, comment out for floating input
#define BOOTLOADER_TIMEOUT_PWR 0
//#define BOOTLOADER_TIMEOUT_USB 0この基板のボタンは押すとLowになり、外付けのプルアップ抵抗があるので内部プルアップは使いません。待ち時間とUSBホストの検出は無効にします。
USBのピン設定(bootloader/usb_config.h)は初期設定がこの基板と同じなので、変更しません。
ビルドとWCH-Linkでの書き込み
rv003usbのフォルダーをVSCodeで開き、PlatformIOの環境の切り替え(ツールバー)でenv:bootloaderを選んでビルドします。できた.pio\build\bootloader\firmware.binを、WCH-Linkでブートローダー用の領域に書き込みます。
ch32fun\minichlink\minichlink.exe -w .pio\build\bootloader\firmware.bin bootloader -Bオプションバイトの設定
電源投入時にブートローダーから起動させるにはオプションバイトも書き込みます。このとき、リセットピン(NRST、PD7)の扱いに注意が必要です。
rv003usbのbootloader/Makefileでコメントアウトされていない有効なコマンドの設定値(+a55aff00)は、NRSTを無効にします。この基板はPD7にリセットボタンを繋いでいるので、+a55aef10(NRST有効・電源投入時ブートローダー起動)を書き込むのが正しいです。
ch32fun\minichlink\minichlink.exe -w +a55aef10 optionUSB経由でのプログラム書き込み
ブートローダーへの入り方
ユーザーボタン(PD2)を押しながらUSBケーブルを繋いで電源を入れます。PCにはHIDデバイス(VID:0x1209, PID:0xb003)として認識されます。
.DopCgDqT.jpg)
書き込むプログラムの作成
ブートローダーは通常のプログラムを書き込むフラッシュ領域とは完全に別の専用の領域に書き込まれています。そのため、ユーザープログラム側での特別なリンカスクリプトの設定変更(先頭アドレスのオフセット調整)などは必要ありません。
リセットボタンでブートローダーに入るための処理
オプションバイトの設定値が使われるのは電源投入時だけで、ユーザーボタンを押したままリセットボタンを押して離しても、そのままではブートローダーに入りません。
次のシステムリセットでどちらから起動するかは、FLASH->STATRのFLASH_STATR_MODEビット(1:ブートローダー)で決まります。ただし実際に試すと、リセットボタン(NRST)でリセットした直後はFLASH_STATR_MODEが1でもユーザープログラムが起動しました(FLASH_STATR_MODEはNRSTによるリセットでは消えません)。
そこで、ユーザープログラムのmain関数の先頭に次の処理を入れます。SystemReset_StartModeは、WCHの標準ライブラリ(ch32v00x_flash.c)の関数です。
int main (void) {
if (FLASH->STATR & FLASH_STATR_MODE) {
NVIC_SystemReset();
}
SystemReset_StartMode(Start_Mode_BOOT);
// 以降は通常の処理
}- 電源投入時はオプションバイトの設定に従いブートローダーが起動する
- ブートローダーは
FLASH_STATR_MODEを0にしてユーザープログラムを起動するため、最初のif文はスキップされる SystemReset_StartModeでFLASH_STATR_MODEを1にする- リセットボタンで再起動するとユーザープログラムが実行されるが、
FLASH_STATR_MODEが1なのでif文のNVIC_SystemReset()でソフトウェアリセットされてブートローダーが起動する - ブートローダーは、ユーザーボタンが押されていればそのままとどまり、押されていなければユーザープログラムを起動する
書き込み
ブートローダーに入った状態で、MounRiver Studio Ⅱでビルドしたファイル(objフォルダー内の.bin)をUSB経由で書き込みます。
.\ch32fun\minichlink\minichlink.exe -C b003boot -w [プロジェクトのフォルダー]\obj\ch32v003-bo-brd.bin flash -b-C b003bootで書き込みにrv003usbのブートローダーを使うように指定します。省略すると、WCH-Linkが接続されている場合はそちらが優先されます。
動作確認
前回の記事(📄CH32V003マイコンの汎用実験基板を設計・実装する)で使ったプログラム(一部改変)をUSB経由で書き込んだところ、Lチカプログラムを走らせることができました。
static uint8_t cnt = 0;
void UserPins_Init (void) {
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOD, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init (GPIOD, &GPIO_InitStructure); // UserLED
}
int main (void) {
if (FLASH->STATR & FLASH_STATR_MODE) {
NVIC_SystemReset();
}
SystemReset_StartMode(Start_Mode_BOOT);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
SystemCoreClockUpdate();
Delay_Init();
UserPins_Init();
while (1) {
if (cnt & 0x1) {
GPIO_SetBits (GPIOD, GPIO_Pin_0);
} else {
GPIO_ResetBits (GPIOD, GPIO_Pin_0);
}
cnt++;
Delay_Ms(200);
}
}
まとめ
USBブートローダーをCH32V003マイコンに書き込んで、USB経由でのプログラム書き込みができるようにしました。最初にWCH-Linkでブートローダーとオプションバイトを書き込んでおけば、あとはUSBケーブルだけでプログラムを書き換えられます。
