シンボリックリンクの圧縮

私が構築しているArduinoのUIAPduinoボード用のパッケージは,自分で作ったコードと外部のコードを組合わせて作っているが,外部のコードはできるだけ分離しておきたいと考えている. しかし,ボードをインストールする際のjsonの構造を調べたところ,圧縮されたファイルは,hardwareまたはtoolsの中の別々のフォルダとして展開される. 分離したファイルを,hardwareの中の特定のフォルダの中に展開するということは出来ないので,少し工夫が必要になる. 思いついた方法は,私の作っているArduino環境に組み込んでいるArduinoCore-APIをtoolsの中のフォルダに置いて,そのファイルをhardwareの下にあるメインのコードから呼び出せるようにして,コードを分離するという手法である.

相対pathでapiへのシンボリックリンクを作れば,toolsに入れたArduinoCore-APIも参照することができるのでは無いかと思って試してみたら,ローカルのArduino環境ではうまく動くことを確認した. ボードマネージャからインストールした時にもこれを再現するためには,hardwareの下のファイルを圧縮して展開したときに,シンボリックリンクが有効でなければならない. zipでシンボリックリンクのまま圧縮するには,-ryオプション付きで実行すると良いという情報を見てやってみたが,展開したらリンクの名前の普通のファイルになってしまった. 一方,以下のようにtar.gzで圧縮したら,展開してもシンボリックリンクとして働いた.

tar -zcvf filename.tar.gz folder/

このようにして,少なくともLinuxにおいては,コードを分離した状態で,自分の作ったボード環境をインストールできるようになった.

Windowsでも,tar.gz圧縮したシンボリックリンクが働くという情報もあったので,試してみた. Arduinoを通常の権限で立ち上げて,インストールしようとしたら,シンボリックリンクを作る権限が無いというエラーが出た. そこで,Arduinoを管理者として実行して,インストールしたところ,シンボリックリンクも有効になって,無事に動かすことに成功した. Windowsで確かめるべきことはこれで終わりだと思うので,そろそろWindowsを消しても良いかな. もう少し待った方が良いかな.

自分で作ったArduino環境を,ボードマネージャからインストールできるようになったと思う. LinuxとWindowsではざっとチェックしたけど,Macは持っていないので,動作チェックができない. どうしようかな.

Read more...

Arduinoのボードマネージャー用のjsonファイル

新しいハードウェア用のパッケージをArduinoにインストールするには,jsonファイルを入力して,ボードマネージャーから指定する. このjsonファイルがどのような構造になっているのかを調べてみた.

基本的には,パッケージの圧縮ファイルと,アーキテクチャ毎のツールの圧縮ファイルが指定されており,すべてのファイルについて,SHA256とファイルサイズを指定する必要がある. SHA256の値が分かっている場合にはそれをコピペすれば良いが,無い場合には,ファイルを用意して以下のコマンドを使って表示された値を使う.

sha256sum filename

また,サイズが分からない場合には,ファイル全体をダウンロードしなくても,以下のようなコマンドでファイルのヘッダを調べることにより,サイズが分かることがある.

wget --spider --server-response url

ちなみに,ローカルにあるファイルを指定する場合には,以下のように記述する必要がある.

file:///home/user/filename.txt

すべてのアーキテクチャで同じファイルを指定する方法が無いか調べたが,分からなかった. jsonファイルを編集すると,ミスをすることもあるが, ブラウザーで開くと,値などが正しく指定されているかや,文法などをチェックできる.

構造が大体分かったので,私がUIAPduino用に作っているch32funを使ったArduino環境を簡単にインストールできるように,jsonファイルを作ってみた. その環境で取り込んで使っているのは,riscv-none-elf-gccとminichlinkとArduinoCoreAPIとch32funとrv003usbである. gccはxpack-dev-toolsの最新のリリースがv15.2.0-1だったので,それを使えば良いだろう. minichlinkはUIAPduinoの本家のファイルを指定すれば良い. ArduinoCoreAPIの最新のリリースがv1.5.2なのでこれを使いたいのだが,hardwareフォルダの中に自動で組み込む方法が分からなかったので,toolにダウンロードさせて,自分でコピーすることにした. ch32funもv1.0rc2というリリースがあったのだが,その中身はch32funでは無く,ch32v003funになっていた. 実質的には同じかも知れないが,環境を構築するときにch32funを使っており,バグなどが起こると嫌なので,ch32funのファイルを自分で組み込むしかないかな. rv003usbについてはリリースがなく,そのフォルダのzipを指定することもできるが,少しでも修正があったらSHA256が一致しなくなるので,これもファイルを自分で組み込むしかないだろう.

私の作った環境とch32funとrv003usbをzipで圧縮したファイルとjsonファイルはローカルに置いて,それ以外はネットから取って来て,簡単にインストールすることができるようになった. aipのフォルダだけはコピーしないといけないが. シンボリックリングなどをつかって,うまくできないかを考えてみよう. gccのバージョンの違いによる問題が起こる可能性もあるが,後でチェックしよう. 新しく立ち上げたPCには,この方法を使って,インストールしようと思う.

Read more...

UIAPduinoのWindows11での動作確認

Debianを入れる前のWindowsPCが手元にあったので,改良中のUIAPduino用Arduino環境をWindows11でも試してみた. 結果から先に書くと,多少の苦労をしてWindowsでも無事に動くようになり,やはりUSB serialは動かなかったがHIDのターミナルで代替できる.

Windowsを立ち上げたのだが,秘密の質問を登録しろと言われたところで,挫けそうになったが,なんとか耐えて起動させることが出来た. まず,Arduino2.3.8をインストールして,それにAlexanderManderaさんの環境UIAPduino環境を導入した. 私の環境を使えるようにするために,Linuxで~/Arduino/hardwareにおいていたファイルを,そのままWindowsのUsers/username/Documents/Arduino/hardwareに移した. 原理的にはこれで動くはずなのだが,コンパイル時にいろいろとエラーが出て,うまくいかなかった. Arduino1.8.19も試してみたが,駄目だった.

バグというのは,見つかってから考え直すと当たり前に思えるのだが,それまではなかなか原因が分からないものである. Linuxではコンパイルできるのに,Windowsではコンパイルできない原因を,様々な観点から探ってみたが,うまく行かなかった. AlexanderManderaさんの環境ではWindowsでもコンパイルできるので,そのplatform.txtを元に,少しずつエラーを解消していったら,その過程で原因を見つけることに成功した. megaavrでは,USB CDC用の関数がCDC.hで宣言されている. 私はそれを参考にして,CH32V003用のファイルを作り,同じくCDC.hという名前にした. 一方,rv003usbでは,USB CDCを使うために,TinyUSBのcdc.hというファイルを利用している. Linuxでは大文字と小文字を完全に別の文字として扱っているが,Windowsではこれらを同一視することがあり,これらの2つのファイル名が干渉していたのが問題だった. CDC.hなどをSerialCDC.hなどという名前に変えて,若干の調整を行ったら,Windowsでもコンパイルが通るようになった.

また,以前ざっと確認して駄目だったUSB serialをもう一度確認してみたが,やはりWindowsでは動かなかった. USB serialをプログラムしたUIAPduinoをUSBに繋ぐと,COM3はできるのだが,デバイスマネージャではビックリマークがついているし,Arduinoのシリアルモニタも動かなかった. なんとか工夫して動かせる可能性もあるかも知れないが,簡単では無いだろう. 一方で,HIDを利用したminichlinkのターミナルは,予想していた通りにWindowsでもうまく動いた. ch32funのminichlinkのフォルダから,minichlink.exeとlibusb-1.0.dllをダウンロードして,実行すると「VCRUNTIME140.dllが見つからないため」とか文句を言われるので,Visal C++再頒布可能パッケージのX64版をインストールする. すると,以下のコマンドでminichlinkのターミナルが開く.

minichlink.exe -kT -c 0x1209d003

キーボードからの入力は,画面には出ないがマイコンに出力され,送受信が可能である. minichlinkのターミナルを併用することで,一台のUIAPduinoのみでPCと通信できるので,バグ取りなどの効率が上がるだろう.

Windowsを使うストレスに耐えられなくなって来たので,動作確認の作業はこれで止めにすることにした. 例えば,コードを編集するために,エディタを選んで[一度だけ]を指定していたのだが,面倒になって[常時]を選択したら,拡張子が表示されなくなって,怒りそうになった. 拡張子を常に表示するように設定する方法を調べるのも面倒なので,そのままやったが,他にも様々なストレスを与えてくるので,私にしては頑張った方だろう. このPCは、そろそろDebianで上書きインストールしようと思う.

Windowsで作動することも確認できたので,あとは細かいバグ取りかな. まだ出来ていないことで思い付くことには,SPIとshiftとpulseの動作確認と,I2Cのslaveの実装があるが,他にも何かあるかも知れない.

Read more...

コンパイラによる動作の違い

このところ,UIAPduino用のArduino環境の改良を行っているが, これまではdebian 13上でch32funを使う際にインストールしたriscv64-unknown-elf-gccなどを使ってコンパイルして来た. 私が改良している環境と同じくch32funを使っているAlexanderManderaさんの環境はriscv-none-elf-gccなどを使用している. 一方,WCHやUIAPduinoやSuzuduinoではriscv-none-embed-gccなどを利用している. このように,riscvには幾つかのコンパイラーがあるので,その種類による違いを検証してみた.

これらのパッケージの名称に含まれている elfはExecutable and Linkable Formatの略のようで標準的なパッケージなのを示すのに対して, embedは組み込み用に特化したものを指すようだ. そして,unknownよりもnoneの方が公式の提供元であることを示すようだ. riscv-none-embed-gccは,ch32の開発元のWCH社が出しているパッケージである. riscv-none-elf-gccは,xPackというプロジェクトのものであるようだ. そして,私が使っているriscv64-unknown-elf-gccは,debianプロジェクトが作ったものなのだろう.

すべての機能をチェックするのは大変なので, USB serialとI2Cを使うプログラムをコンパイルして書き込んで,その挙動を比較してみた. まずは,これまで使っていたriscv64-unknown-elf-gccであるが,当然うまく動作する. ちなみに,コンパイルしたときのサイズは15088 bytesであった. 次にriscv-none-elf-gccだが, AlexanderManderaさんの環境がインストールされていれば,別の環境からも使うことができるようで,platform.txt中で以下のような指定をすると,コンパイラを使うことができた.

compiler.path={runtime.tools.riscv-none-elf-gcc.path}/bin/

newlibをincludeしていた部分をいじる必要があったが,喜ばしいことにこれを使っても無事に動いた. サイズは14020 bytesと少し小さくなったが,バージョンが古いからかも知れない. 最後にriscv-none-embed-gccだが,UIAPduinoのファイルを利用して,以下のように指定した.

compiler.path={runtime.tools.riscv-none-embed-gcc-8.2.0.path}/bin/

サイズは14028 byteであったが,残念なことにUSB serialを認識しなかった. I2Cの方の結果をUSB serialで見ているので,I2Cも動いているか分からなかった. コンパイルオプションなどをいじると,もしかしたら動くかも知れないが,大変そうなので試していない. このコンパイラはch32funとの相性が良くないのかも知れない.

結果として,配布パッケージであるriscv-none-elf-gccでも,USBが無事に動くことが確認できた. 簡単にチェックできるtoneやpwmも大丈夫だったし,おそらく他の機能もほぼ動いているだろう. 今後の検証では,このコンパイラを使って行っていこうと思う.

ついでに,Arduinoのversionによる違いについても調べてみた. 私はArduino1.8.19を使っているが,現在はArduino2系列が主流になりつつある. 丁度,立ち上げたばかりのDebian13のPCがあったので,flatpakでArduino2.3をインストールしたら,一旦ファイラーが動かなくなってしまった. 普段使っているPCでやらなくて良かった. udevとdialout groupの設定をして,AlexanderManderaさんの環境をインストールして,Arduino2.3でも試してみたが,ほぼ問題無いように見える. 別のPCでAppImageでも試したが,libfuse2をインストールする必要はあったが,こちらでも問題無いようである.

Windowsでも同じようなコンパイラを使えば動くだろうが,私の手元には確認できる環境が無い. Windowsの入ったPCを買っても,すぐにLinuxを上書インストールしてしまうので. Debianを入れる前のWindowsPCもあるので,試してみたいとも思わないでも無いのだが,Windowsを使って3分ぐらい経つと,耐え切れなくなって使うのを止めてしまうんだよな. USB serialは動かないのは確かめたし,今回は止めておこうかな.

Read more...

UIAPduinoでtoneとinterrupt

構築中のUIAPduino用のArduino環境に対して, 先週末に実装したつもりだったtoneとinterruptには,やはりバグがあった. なんとか,一応は動くようになったので,どのようなバグがあったかを説明したい.

まず,toneについてだが,32bitのCPUなので,timerも32bitだろうと思ってコードを書いたのだが,実はtimerは16bitだったので,その部分の修正が必要だった. prescalerが16bitだったので,実質的にはほぼ32bitとして扱えるので,比較的広い周波数の指定ができる状態を保持したまま,無難なコードに書き直した. その過程で,最初は挙動が思った通りにならなかったが,それはArduinoCoreAPIでtoneという関数が宣言されているためだった. また,durationというパラメータを誤って解釈していたのだが,何ms後に自動で音の発生を止めるという意味だった. 最初はdelayで誤魔化していたが,PWMを作っているタイマーで,波の数をカウントして止めることにした. しかし,タイマー割り込みのやり方の情報があまり見つからなかったので,試行錯誤が必要だった. update時の割り込みを許可して,updateをoverflowまたはunderflowに限定して,割り込み関数を有効にしたら,一つの波が終わったときに割り込みがかかる.

TIM1->DMAINTENR |= TIM_UIE;
TIM1->CTLR1 |= TIM_URS;
NVIC_EnableIRQ( TIM1_UP_IRQn );

そして,割り込みの中で,以下のようにupdateをクリアしないと何度も割り込みがかかってしまう.

TIM1->INTFR &= ~( TIM_UIF );

1を書き込んだらクリアされると思ったら,0を書き込む必要があった. 別の割り込みでは1の方だったので,そっちを試していたら,なかなかうまく行かなかった.

次に,interruptだが,二つの大きなバグがあった. 一つは,rv003usbの中でピンのinterruptを使っているので,USBを使っているときには使えないということだ. バグを発見するために,USB serialを使って動作を確認していると,何だか変な挙動をすることから,そのことに気が付いた. そこで,USBを使っているときには,interruptのコードは取り込まないようにしたが,これは仕方無いだろう. USBを使っていないときには,きちんと動くようにするためには,もう一つのバグを解決する必要があったが,それはC言語とC++の共存に関係する問題だった. attachInterruptなどの関数は,ArduinoCoreAPIで宣言されているので,それらの関数はC++で書かないといけないが,割り込み処理の関数はch32funで定義されているので,C言語で書かないといけないのである. それを意識して,少しだけ修正したら,無事に動くようになった. 気付いてみると当たり前なのだが,これらの事に気が付くのに時間がかかった. こちらは,プログラムの本質的な部分には問題は無かったのだが,上記のような相性の問題だった.

連休中に組み込んだあと2つの機能のshiftとpulseは,それほど複雑では無いので,バグは無いと思いたいが,あるかも. と思ってArduinoCoreAPIを確認したら,2つとも関係する関数の宣言が行われていたので,それに合わせて少し調整を行った. しかし,これらの機能の動作を確かめるのは面倒なので,あまり使わないだろうし,後回しにして良いだろう.

rv003usbも使えるようになったし,コンパイルしたサイズはかなり小さくなったし,目指していたUIAPduino用のArduino環境の条件は,ほぼ満たすものが出来てきたように思う. そういえば,ic2のslaveがまだだった. これもあまり使わないし,動作確認も面倒なんだよな. SPIもとりあえず書いたけど,動作確認していない. ほぼ出来上がってくると,興味がだんだんと薄れてしまうので,今後どこまで仕上げるかな.

[2025/3/25追記] rv003usbのフォルダを眺めていたら,demo_extiという名前で,USBとピンinterruptを共存させる例があった. 仕様上,USBで使っているピンと同じ番号のピンは使えないが,それ以外のピンなら, RV003_ADD_EXTI_MASKでマスクを指定して, RV003_ADD_EXTI_HANDLERで処理を行うことができるようだ. しかし,機械語を使って書かなければならなくて,attachInterruptを実現するのは難しそうなので,今は諦めよう.

Read more...

UIAPduino用のArduino環境の改良

先週末は,三連休で時間があったので,最近構築しているch32funをベースにしたUIAPduino用のArduino環境をいじっていた. いくつかの進展があったので,私の目標とするところまでかなり近付いたように思う.

まず,Arduinoでコンパイルする際のやり方が書いてあるplatform.txtを全面的に見直した. 少しいじると,すぐにエラーが出ることが多く,これまでは必要になる度に,最低限の修正を加えるということを繰り返して來たために,見通しが悪くなってしまっていた. Arduinoのコンパイルでは,recipe.c.o.patternなどに基づいてC言語,C++,機械語のソースをそれぞれコンパイルして,recipe.c.combine.patternに基づいて リンクが行われる. そこに与えるオプションを,ボードの種類などによって変えられるようにするのだが,共通のオプションを使っている部分はまとめたりして,見た目をすっきりとさせた. さらに,オプションの意味を調べて,削除したり追加したりもした. その際,ch32funではch32fun.mkにコンパイルのやり方が書いてあるので,そのオプションも参考にした. 最終的なコンパイルサイズを小さくするためのいくつかの工夫が見られたが,リンクのオプションで–whole-archiveという指定を–no-whole-archiveに変えることによって,サイズがかなり小さくなった. これまでは,この指定のために,使っていない関数についても組込まれてしまっていたようだ. 例えば,blinkは768バイトとなり,ch32funだと616バイトだが,スイッチの設定などを変更する部分などを考慮すると,無難なサイズでは無いだろうか. デフォルトでは–no-whole-archiveらしいので,わざわざそれを変えているということに意味があるのならば,不都合が生じる場合もあるかも知れない. しかし,残念なことに,UIAPduinoを持って帰るのを忘れてしまったので,まだ十分な検証ができていない.

ハードに触れないので,ソフトをいじろうということで,他にもいくつか変更を加えてみた. いつか組込もうと思っていたピン割り込みは,megaAVRのコードとch32v003のマニュアルを参考に,実装してみた. ToneはPWMと共通点が多いので,その中に組込む形で取り入れてみた. ついでに,alternate functionを利用して,すべてのピンでPWMを使えるようにした. pulseはWCHのコードにch32funのコマンドを入れて実現した. shiftはWCHのコードがそのまま利用できそうだと思ったら,api/Common.h中の宣言とかち合ったので,megaAVRのコードをそのまま使った. これらも動作の確認はこれからだが. ピン割り込みにはまだバグがある気がする.

なかなか原因が分らなかったSPIのコンパイルもできるようになった. その主な原因は,api/HardwareSPI.hで

virtual ~HardwareSPI() { }

と定義されていて,これが悪さをしていた. クラス名に~を付けると,オブジェクトが削除されるときに実行されるデストラクタを意味しており,このときにdeleteが呼ばれて,それが定義されていないというエラーが起きていたのである. この行をコメントアウトしたら,コンパイルできるようになった. しかし,ArduinoCoreAPIの中身は変更しないという方針なので,他の方法を模索する必要があった. deleteは,AVRmegaではnew.cppの中で定義されている. new.hとnew.cppを持って来て, platform.txtのcppのコンパイルオプションに,

-fno-exceptions -fno-use-cxa-atexit -std=gnu++11

を加えたら,freeが無いというエラーに変わった. freeは,通常はstdlib.h中で定義されているのだが,ch32v003にはfreeが無いのかも知れない. そこで,new.cppの関数の中身を失くしたら,うまく行った. まだSPIにはバグはあるかも知れないが,一番大きな問題がクリアされただろう.

i2cのslaveも少しコードを加えたけど,割り込みの部分がmasterのコードを干渉しそうなので,それをどうするのか悩んで,そこで止まっている. これで,組込む予定だった機能で,組込めていないのは,i2cのslaveのみになった. 本体が無かったので,動作確認が出来ていないのが大きな問題なのだが,一つずつ検証して行こうと思う.

最初は,とりあえず動くものを作ろうと考えていて,Arduinoやch32funの中身を理解するつもりは無かったのだが,いろいろな問題を解決する過程で,それらを避けて通るわけにはいけなかった. 特に,ArduinoからUIAPduinoのUSBを使えるようにするために,rv003usbを組込むところでは,様々な試行錯誤が必要だった. ch32v003の細かい仕様を知らなくても,Arduinoから使えるようにするために始めたはずなのに,目的と手段が入れ替わってしまったかな.

Read more...

UIAPduino環境のコンパイルサイズの謎

私がUIAPduino用に新しく作っているArduino環境で,なぜかblink.inoのコンパイルサイズが大きくなってしまう. UIAPduinoの公式よりは三割程度は小さいのだが,明らかに大きすぎる気がする. 試行錯誤しながら,その理由を考えていたのだが,ヒントが見つかった気がするので,そのことについて説明する.

私の環境でのBlink.inoのコンパイルサイズは,AlexanderManderaさんの環境よりも2.3kバイトほど大きい. まずは冗長な処理を行っていたピンの定義などを簡略化して40バイトぐらいは小さくなったが,2kバイトには程遠い. 他の部分もいじってみたが,それほど無駄なことをしていないように見える.

ch32funの部分に無駄が無いかを調べていて,FUNCONF_TINYVECTORというパラメータを見つけた. AlexanderManderaさんの環境でも,-DTINYVECTORという指定をしていて,なんだろうと思っていたが,これに対応するようだ. このオプションを有効にしたら,私の環境でもコンパイルサイズが2kバイト以上小さくなった. 一番大きな要因はここにあったようだ. しかし,このオプションを有効にすると,割り込みが使えなくなる. 例えば,PWMなども働かなくなってしまったし,USBも使えなくなった. それでは意味が無いので,TINYVECTORは使わないことにした.

ch32v003には,40個ほどの割り込みがあり,そのベクターテーブルは,合計でも160バイト程度のはずである. TINYVECTORはそのほとんどを無くしてしまうというオプションなのだが,普通に考えると160バイト程度しか,コンパイルサイズに影響しないはずである. しかし,Arduinoでコンパイルすると,2kバイト以上の違いになる. 割り込み一つあたり50バイト程度が,何かに使われていると予想される. 少なくとも割り込み時に実行される40個ぐらいの関数を定義するのに,それぞれ多少のバイト数は必要になるだろう. Wireのコードをいじっていたときに,一つの処理を関数に置き換えるとあまりバイト数が変わらないけど,同一の二つの処理を関数に置き換えると数十バイト大きくなるということがあった. 一つの処理の場合には,最適化によって,関数としては使われず,二回使ったときに,関数として使われるとすると,一つの割り込み関数で50バイト増えるのも無難な気がする. ch32funの中では,ユーザーが定義していない割り込みは,DefaultIRQHandlerのaliasになっているので,実質一つの関数なのだが,Arduinoでは何らかの理由でalias指定が働かないとすると,2kバイト大きくなることが説明できる. これが正しいとすると,このalias指定を有効にして,その時に使用する割り込みのみを関数として使うようにすれば,2kバイト弱はサイズが小さくできる可能性があるのでは無いだろうか.

ch32funを使っているという共通点のあるAlexanderManderaさんの環境と,コンパイルサイズが違う原因が分かってすっきりした. 機能の面も合わせて考えると,ようやく私の環境がこの環境に劣っている点は,ほぼ無くなったと言える. USBを使えるという大きな利点があるものの,WireのslaveやSPIにはまだ対応できていない. 私はあまり使わないので忘れていたけど,ピン割り込みも対応するかな. 今後は,Arduino環境でのalias指定という観点に着目して,コンパイルサイズを小さくできる可能性について,もう少し調べてみたい.

Read more...

UIAPduinoでコードゴルフ

最近,Arduinoからch32funを介してUIAPduinoを使える環境を構築している. その環境でi2cとUSB serialを使ったプログラムを作ったら,flashにギリギリのサイズになってしまった. いろいろ調べていたら,ch32funのdebug printが有効になっていたので,それをオフにしたら多少は余裕ができた. もう少しバイナリのサイズを小さく出来無いかと思って,少しいじってみた. 現時点では,ArduinoCoreAPIは組込むと決めているので,その縛りの下でArduinoが組み込んでいるC言語やC++のソースをコンパイルした後のバイナリを小さくするようにするコードゴルフのようなものかな. しかし,コンパイラがある程度の最適化を行っているので,単純では無い. 例えるならば,日本語を通訳に流暢な英語にしてもらっているときに,その英語を短かくするように,日本語を作るようなものかな. 繰り返し行われている操作を,関数にして使うようにしたら,四回ぐらいしか使わない場合には,サイズが大きくなってしまうが,それより多く使うと,小さくなった. 小技を使って4バイトずつ縮めたり,USB serialの方もいじったりして,100バイト程度は短くなった. RingBufferを使うようにしたら,ソースコードは短かくなったが,バイナリは若干大きくなってしまったので,元に戻した. ついでにi2cをslaveに対応させようとして少し書き足したら,100バイトぐらい増えてしまって,元と同じぐらいに戻ってしまった. 使ってない関数は,最適化されたら関係無いと思ったけど,なんでだろう. 折角なので, UPAIduino環境で,最適化が-Osと-Os LTOのとき, 私の構築している環境で,with USB serialとwithout USBのとき, AlexanderManderaさんの環境の, 5つの場合について,コンパイルしたときに生成されるbinファイルのサイズを比較してみた. プログラムはプログラムの例から,いろいろな機能を使う場合を選んでみた. 結果は以下の通りである. overとついているのは,flashのサイズを超えた容量を示している. Xは実装されていないことを表している. condition Blink AnalogInput DigitalReadSerial AnalogInOutSerial i2c_scanner adafruit_aht10_test UIAPduino -Os 7140 8656 7616 12648 13032 11024 over UIAPduino -Os LTO 5696 6248 5872 8276 11040 error my own with USB serial 4460 4644 4440 4844 5752 1232 over my own without USB 3968 4168 3992 4338 5308 772 over AlexanderMandera 1640 1932 2132 X X X この表を見て気がついたのが,UIAPduinoでは,-Osがデフォルトになっているが,-Os LTOの方がサイズが小さくなって良さそうだということだ. UIAPduinoを使う場合には,-Os LTOで使うと良いだろう. バイナリのサイズという点では,AlexanderManderaさんの環境がかなり小さい. 私の環境は,Blinkはそれなりに大きなサイズになっているが,機能を増やしても,あまりサイズが変わらないという特徴を持っているようだ. 私の環境には最新のch32funを使っているのだが,その特徴なのかも知れない. ちなみに,シームレススイッチを組み込んでいるので,それが76バイトぐらいは大きくなっている原因だという点を考慮して欲しい. i2c_scannerはWireを使っていると言っても,ほぼ何も通信しないプログラムなので,UIAPduinoできちんとしたWireをArduinoから使えるのは,今のところ私の環境だけかも知れない. まだ,slaveが使えないが. また,adafruit_aht10_testを動かせるようにするには,かなり小さくしないといけないので,ちょっと厳しい気がする.
Read more...

UIAPduinoのHIDでターミナル

UIAPduinoでデバッグプリントするには,いくつかの方法がある. シリアルやSWIOなどに出力するのは簡単なのだが,それらを読み取るために別の機器が必要となる. ch32funや私が構築中のArduino環境では,rv003usbを介してUSB serialを使うこともでき,一つのUIAPduinoのみで済むのだが,Windowsでは使えない. そこで,rv003usbにdemo_terminalとして例のある,USB HIDを用いたデバッグプリントが重要になる. 確かめていないが,HIDを使った手法だと,Windowsでも使えるのでは無いだろうか.

今回は私が構築中のUIAPduino用Arduino環境に,この機能を追加してみた. USB serialと切り替える必要があるので,usb_config.hをincludeする際に,条件によって別のファイルを読み込むようにした. funconfig.hも少し修正した. USB serialのように,新たな関数をいろいろと定義しないといけないのかなと思っていたが,その必要も無く動くようになってしまった. 細かいところはch32funとrv003usbがやってくれているようである.

linuxでは,権限の問題をクリアするために,以下のコマンドを実行する.

echo -ne "KERNEL==\"hidraw*\", SUBSYSTEM==\"hidraw\", MODE=\"0664\", GROUP=\"plugdev\"\n" | sudo tee /etc/udev/rules.d/20-hidraw.rules
sudo udevadm control --reload-rules
sudo udevadm trigger

UIAPduinoに付属したminichlinkではうまく動かなかったので,ch32funに付属のminichlinkをつかって,以下のコマンドを実行する.

./minichlink -kT -c 0x1209d003

そして,inoの中でprintfで出力すると,その内容がターミナルに表示される.

UIAPduinoのためのch32funを使ったArduino環境は,ほぼ完成の領域に近づいたと言えるだろう. SPIはArduinoCoreAPIのHardwareSPI.hのどこかが悪さをしているみたいで,まだエラーが取れていないけど,このファイルを使うのを止めた方が早いかな. それぞれの機能が正常に作動するかや,複数の機能を使ったときにCH32V003のflashに入るかなど,検証すべき点は残されているが,大きな山は超えただろう. また,ArduinoIDEの設定については,理解していない部分が多い状態で,なんとか動く状態にしているので,無駄や美しくない部分も多いだろうから,それらを洗練していく必要はあるかも知れない. あとは,自分しか使わないのは勿体無い気がするので,どういった形で公開ができるかも考えないと.

Read more...

UIAPduinoのシームレススイッチ

UIAPduinoを書き込みモードにするには,RSTスイッチを押したままUSBに接続する必要がある. マイコンのプログラムを作るときには,何度も書き換える必要があることが多く,その度にUSBを抜き挿しするのは面倒である. UIAPduinoの公式ページには,RSTスイッチで書き込みモードと実行モードを切り換える方法が,SeamlessSwitch.inoとして以下のようなコードと共に示されている.

void setup() {
  if (FLASH->STATR & (1<<14)) NVIC_SystemReset();
  SystemReset_StartMode(Start_Mode_BOOT);
  pinMode(PD4, OUTPUT);
  // ...
}

しかし,私が構築した環境はch32funを使っており,SystemReset_StartModeという関数が無い. そこで,同等の関数を以下のように定義してみた.

void SystemReset_StartMode(uint32_t Mode)
{
  FLASH->KEYR = FLASH_KEY1;
  FLASH->KEYR = FLASH_KEY2;
  FLASH->BOOT_MODEKEYR = FLASH_KEY1;
  FLASH->BOOT_MODEKEYR = FLASH_KEY2;
  FLASH->STATR &= ~(1<<14);
  if(Mode == Start_Mode_BOOT){
    FLASH->STATR |= (1<<14);
  }
  FLASH->CTLR |= CR_LOCK_Set;
}

そして,指定したコードを実行してみたのだが,期待する挙動とならない. かなりの試行錯誤を経て,ようやくその原因が分った. ch32funでは,PD4は0x34=52という数値が割り当てられている. 一方,UIAPduinoではこのピンには14という番号がつけられており,私の環境ではその番号でピンを指定するようにしたので,PD4ではなく14をOUTPUTにする必要があったのである. このコードをmain.cppに組み込んでみたところ,無事にスイッチで書込と実行のモード切り替えできるようになった.

当たり前のことなのだが,USB serialなどを使っている場合には,USBがPD4を使うので,このPINを使うことが出来無い. そのため,スイッチによるモード切り替えも動作しない. その場合には,bootloaderに入るけどもUSBの初期化が出来無くなるのかなと思っていたが,実際にはリセット後で実行モードが立上がる. この理由が分らないが,bootloaderがUSBを認識できなかったら,実行モードに切り替わるのかな. いずれにせよ,USBを使っている場合には,やはり抜き挿しが必要となる.

2026/3/18追記 main.cppの中でUSBを組込む直前に,USBのD-に使われている14ピンを少しの間だけLOWにするようにしたら,書き込み後に自動でUSBを認識するようになった. bootloaderにもUSBの認識前に14ピンをLOWにするコードを入れられたら,USBを使っている場合でも,RSTでモード切り替えができるかもしれない. しかし,bootloaderのサイズはかつかつらしいので,難しいかも.

Read more...