146【dualCH32V003】92ByteでPD6から高速矩形波を出してみたにょ【40円マイコン】
はじめに
奇しくも本日は4月1日(エイプリルフール)、さすがに92ByteでLチカとか、しかも3MHz超えの脅威の速度で実現するなんてね(///)
CH32V003J4M6(SOP8)の超小型ファームウェアチャレンジを続けています。
今回は Arduino IDE 2 の枠組みを残したまま、どれだけ小さくできるか に挑戦しました。
これまでは主に環境整備(Arduino IDE 2のインストール、CH32V003対応ボードパッケージの設定など)に力を入れてきたので、ようやくここまで小さくまとめることができました。さらに dual CH32V003 ボード を作成し、動作確認も行いました。
主な最適化ポイント
この 92 Byte を実現するために、以下の変更を行いました。
- Arduino IDE 2 の標準スタートアップをバイパスするため、
_start関数をnaked属性 +.text.entryセクションで直接定義 setup()/loop()は空のまま残しつつ、一切の Arduino 高レベル関数を使わずレジスタ直叩きのみに- LTO(Link Time Optimization) を有効化してデッドコードを徹底除去
- GPIO トグルを
BSHRレジスタの Set/Reset を2命令で交互実行する極めてタイトなループに最適化 #define USE_MAXHZで内部 HSI 24MHz を PLL で 48MHz に倍増するクロック設定を追加
platform.txt の編集内容(重要)
単に -flto -Os を追加するだけでなく、setup() / loop() をリンクしないように以下の編集を行いました。
platform.txt(または platform.local.txt)に追加・修正した主な行:
compiler.c.extra_flags=-flto -Os -ffunction-sections -fdata-sections
compiler.cpp.extra_flags=-flto -Os -ffunction-sections -fdata-sections
compiler.c.elf.extra_flags=-flto -Os -Wl,--gc-sections -Wl,--undefined=_start
これにより:
- LTO + サイズ最適化が効く
- 不要な Arduino スタートアップ関数(
setup()/loop()関連)がリンク時に削除されやすくなる _startを明示的に undefined 扱いにして naked エントリポイントを優先
この組み合わせが 92 Byte 達成の鍵 になりました。
結果
| 項目 | 内容 |
|---|---|
| バイナリサイズ | 約 92 Byte(LTO 有効時) |
| 出力波形 | 数百 kHz ~ 1 MHz クラスのきれいな高速矩形波 |
| 外部部品 | 一切なし |
ソースコード
#define USE_MAXHZ
void loop() {}
void setup() {}
extern "C" {
void _start(void) __attribute__((section(".text.entry"), naked));
void _start(void) {
volatile unsigned int* const RCC_CR = (unsigned int*)0x40021000;
volatile unsigned int* const RCC_CFGR = (unsigned int*)0x40021004;
volatile unsigned int* const RCC_APB2 = (unsigned int*)0x40021018;
volatile unsigned int* const GPIO_CFG = (unsigned int*)0x40011400; // PD CFGLR
volatile unsigned int* const GPIO_BSHR= (unsigned int*)0x40011410;
#ifdef USE_MAXHZ
*RCC_CR |= (1 << 24); // 内部HSIをベースにPLLで48MHz化
while ((*RCC_CR & (1 << 25)) == 0); // PLL ready 待ち
*RCC_CFGR = 0;
*RCC_CFGR |= (1 << 0); // System clock switch
#endif
*RCC_APB2 |= (1 << 5); // IOPDEN(GPIOD クロック有効化)
// PD6 を Push-Pull 出力(最大速度)に設定
*GPIO_CFG = (*GPIO_CFG & ~(0xFu << 24)) | (0x3u << 24);
const unsigned int set = (1u << 6);
const unsigned int rst = (1u << 22);
while (1) {
*GPIO_BSHR = set; // PD6 High
*GPIO_BSHR = rst; // PD6 Low
}
}
}
dual CH32V003 での動作確認
先日作成した dual CH32V003 ボード(2つの CH32V003J4M6 を搭載)に、この 92 Byte ファームウェアを書き込んで動作確認しました。
- 両方のチップで PD6 から安定した矩形波が出力されていることをオシロで確認
- 片方は内部 HSI 24 MHz ベース、もう片方は
#USE_MAXHZ有効で 48 MHz モードに設定 - 2チップ同時動作でもクロック干渉や電源ノイズがほとんどなく、きれいな波形が得られました
この dual 構成は、センサー用途や小型 IoT デバイスで便利そうです。
書き込み・確認手順(Arduino IDE 2)
- Arduino IDE 2.x を起動
- CH32V003 対応ボードパッケージをインストール
- 上記の
platform.txt編集後、コンパイル - WCH-LinkE などで書き込み
- オシロで PD6 を観測 → きれいな矩形波が出れば成功

参考リソース
- Arduino Core for CH32V003(AlexanderMandera 版)
- OpenWCH 公式 Arduino Core
- CH32V003 Reference Manual(PDF)
この 92 Byte というサイズ感と、dual CH32V003 での動作確認、どれも CH32 RTA として面白いと思います。elchika の皆さんはどう思いますか?ぜひコメントお待ちしています!
#CH32V003 #SOP8 #Arduino #LTO #極小ファームウェア #dualCH32 #CH32RTA
投稿者の人気記事





-
chrmlinux03
さんが
前の水曜日の11:06
に
編集
をしました。
(メッセージ: 初版)
-
chrmlinux03
さんが
前の水曜日の11:09
に
編集
をしました。
-
chrmlinux03
さんが
前の水曜日の11:51
に
編集
をしました。
(メッセージ: バイト数違ってた)
-
chrmlinux03
さんが
前の水曜日の12:23
に
編集
をしました。
Opening
chrmlinux03
前の水曜日の13:20
ログインしてコメントを投稿するさっそく Grok に抜かれました
https://x.com/chrmlinux03/status/2039195473970692238
extern "C" void _start(void) attribute((naked, section(".text.entry")));
void _start(void) {
RCC->APB2PCENR |= 1<<5;
GPIOD->CFGLR = (GPIOD->CFGLR & ~(0xFu<<24)) | (3u<<24);
register uint32_t *b asm("a0") = &GPIOD->BSHR;
register uint32_t s asm("a1") = 1u << 6;
register uint32_t r asm("a2") = 1u << 22;
while(1) {
*b = s;
*b = r;
}
}
void setup(){}
void loop(){}