エントリー

2023年01月の記事は以下のとおりです。

PC-386BookLのバックアップ電池を単3に

 PC-386BookLの最後の改修を行いました。バッテリバックアップの電池を,これまでの単4から単3にしました。

 先日,PC-376BookLを立ち上げようとしたら,起動しません。原因はメモリスイッチが初期化されてしまったからで,このマシンはIDEからのブートでは,セクタサイズをデフォルトの256バイトから512バイトに変更しないといけません。

 バッテリが切れるとメモリスイッチが初期化されてしまい,セクタサイズが256バイトに戻ってしまうので,起動できなくなってしまうのです。なんと面倒な。

 こういうメモリスイッチは低消費電力でバックアップされるものなので,下手をすれば何年も無事なものなわけですが,PC-386BookLではなんと500uAも消費し,しかも電圧が6Vと破格のものが必要です。バックアップに3mWもの電力が必要ってなんなのよ。

 しかもこの電力をカバーするための大きさを持つNi-Cd電池から派手な液漏れがあって(サイズがでかいと当然中野電解液もたくさん入っていますのでね)基板は壊れるしで,ろくなことがありません。

 一般的なマシンは3.6Vの小型のNi-Cdか,コイン電池を使うことが多いバッテリバックアップの電池ですが,PC-386BookLの場合はおそらく,5Vのマイコンを常時動かしているのではないかと推測しています。PC-386BookLではなんと当時としては画期的なレジューム機能を搭載しているのですが,これのために単なるCMOS-SRAMではなく,マイコンを使っているんじゃないかと思っています。

 当時のマイコンは低消費電力のC-MOSでも5Vが当たり前でしたので,6Vということになったんじゃないのかな,と思います。そうなるとマイコン内蔵のRTCがまた結構な電力を食うので,500uAというのもあり得るかなと言うのが私の結論です。

 それはまあいいとして,問題はそんなでかい電池をどこに格納するのかという問題です。もともとバッテリでの駆動はあきらめていますので,駆動用の電池のスペースに入れればそれで済むと言う気もするのですが,それはちょっと美しくありません。

 なので,これまではLスロットの空きに押し込めるサイズとして単4のNi-MHを5本しまい込んでいました。700mAhの電池ですので,ざっと1400時間のバックアップが可能です。だいたい2ヶ月です。

 しかし,歳を取ると2ヶ月なんてのは2週間くらいに感じるものです。あっというまに切れてしまい,起動不能になったというわけです。

 この問題を解決するには,さらに大容量のバッテリを使うしかありません。そこで考えたのが単3への換装でした。これだと2400mAhですので4800時間,実に半年以上もバックアップ可能です。

 考えてみると,駆動用電池でさえもこれ以下の容量しかありませんでした。もちろん数時間しか使えないものでしたが,それをバックアップ用にあてがうというのですから,そりゃ長持ちするでしょう。

 さらにいうと単4のNi-MHは劣化が早く,すぐに充電出来なくなります。これが単3だと長く使えるのでありがたいです。

 問題は収納場所ですが,電池ケースを工夫すると元の場所に上手く格納出来る事もわかりました。さっと分解して問題なく改造が済みました。

 当然ながら起動も日時も正常で,これが最後の改造になると思います。

 しかしながら,200日というのは,長いようで短いような・・・

 

x265のAppleM1対応はどうにかならんのかい

 2020年末にAppleSilicon(AppleM1)を搭載したMacBookAirを購入して,その性能とバッテリライフに心底驚いたわけですが,大きく期待を裏切るものがffmpegによるH.265のエンコード性能の低さでした。

 H.265もだいぶこなれてきましたし,フルHDのMPEG2のトランスコードのパフォーマンスも,実時間の5倍10倍は無理としても,2倍程度は出てくれるだろうと思っていたところ,まさかの0.7倍という実時間を下回る速度でした。

 Rosetta2によるIntelエミュレーションの方が実時間近く出ていたので,ARM64のネイティブでこれっていうのは,Rosetta2の優秀さを示すものであると同時に,x265のARM対応の悪さを示していて,ここまでひどいとさすがにすぐに改善されるだろうと思っていました。

 そもそもM1にはH.265のハードウェアアクセラレータも搭載されているので,これを使えば10倍20倍も夢じゃないとワクワクしていただけに,この期待外れは強烈なものでした。おそらく私だけではなく,世界中の多くの方がそう思ったことでしょう。

 2023年1月現在,未だに未対応のままです。

 結局うちでは,2016年モデルのMacBookProというインテルCPU(2.6GHz)のエンコードが最も高速で,上記と同じ条件だと1.6倍くらい出てくれるので,インテルMacはまだまだ捨てられないなあと言う結論になっています。

 このうち,ハードウェアアクセラレータについては早い段階で使う方法が明らかになっているのですが,高速である代わりに画質がひどく,実質的に使い物になりません,ここで世界は2度目のがっかりを経験することになったのです。

 Rosetta2によるインテルエミュレーションの方が高速というおかしな事が起こるのは,x265のARMのSIMD拡張命令セットへの未対応が原因でしょう。NEONは,SIMDという命令の通り複数の計算を一撃でこなすもので,行列計算に威力を発揮します。これがないと一々計算のための命令を記述しないといけないわけで,それだけで数倍の差がつくことは自明でしょう。

 聞けば2.5倍程度高速化されるという事ですので,ようやくうちのインテルMacに並ぶことになります。クロックはM1の方がずっと高速ですので,M1にも苦手なものがあるんだなあと思うのですが,この時に必要な電力がまさに桁違いですので,やはりM1の優秀さは変わりません。

 さて,そうなってくると私もNEON対応のx265を試したいじゃないですか。

 これまでは,組み込み済みのバイナリを取ってくるか,自分でビルドをしないとダメだったのですが,私はAACのエンコードにfdk-aacを使っているので,どうしてもソースからビルドしないといけません。

 かといってスタティックビルドなんていう面倒な事をやる気もなく,Homebrewで対応してくれることをずっと待っていたのでした。

 x265のM1のNEONパッチはAppleから出ているそうなのですが,いろいろ事情があってHomebrewでの正式なリリースには組み込めずに長い時間放置されているとのことで,実は昨年秋頃に一度だけ組み込まれた事があったそうです。その後現在はまた外されてしまったようですので,私はこのタイミングを逃していたことになります。

 ですが,Homebrewかつ,fdk-aacが組み込める形で,NEONパッチがx265に適用できる簡単な方法が分かったので,ここにメモしておきます。本当に簡単です。

(1)まずこれまでのx265をアンインストールする。
brew uninstall --ignore-dependencies --force x265

(2)次に以下をエディタでひらく。
/opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula/x265.rb

(3)このうち,
head "https://bitbucket.org/multicoreware/x265_git.git", branch: "master"
という行を探して,以下に入れ換える。
head "https://bitbucket.org/multicoreware/x265_git.git", revision: "0b75c44c10e605fe9e9ebed58f04a46271131827"

(4)x265をインストールする。
brew install --HEAD x265

(5)もし元に戻したいなら,編集箇所を元にもどしてから,
brew install homebrew-ffmpeg/ffmpeg/ffmpeg --HEAD


 たったこれだけです。これで特別なオプションを付け足すこともなく,x265がNEONに対応して2,5倍ほど高速にエンコード出来るようになります。

 先程の条件だと,実時間の0.7倍から1.7倍程度に,2.4倍ほど高速化されました。

 NEONの効能はこんなもんではないはずで,もっとコードを最適化すればさらに高速化されると思うのですが,今のところはこの程度です。ようやく7年前のインテルCPUに速度的には並んだというのが情けないなと思うのですが,繰り返すようにこれがファンレスで動いていることがすごいと思います。

 はやくNEONへの対応,そしてM1およびM2への最適化をして欲しいなと思います。

 

電子工作ってちょっとしたブームになってますよね

  • 2023/01/18 14:20
  • カテゴリー:make:

 1月の下旬に,ある展示会の「にぎやかし」として,電子工作を行っている人の作品を展示する企画に偶然声がかかって,急遽展示品を用意することになりました。

 知人からの声かけだったので,他の出展者はすでに知っている人ばかりで,その実力も行動力も十分に知っているだけに,私のような引きこもりで自分のためにしかやらない(=困ってなければなにもしない)工作を趣味をする人間は,正直困ってしまいました。

 そう,今や電子工作もプログラミングも,人付き合いが上手で明るく人間的に素晴らしい人でなくては,務まらない時代です。

 私が若い頃はですね,こちらにその気があってもむしろ向こう側が避けて(以下略)

 てなわけで,人から逃げるようにこの趣味に閉じこもってきた私にとっては,ちょっとした災害レベルの出来事です。これは困った。

 一応主催者は「なんでもいいですよ」「これまでに作ったものでもOKです」「自分のための工作?いいじゃないですか」とニコニコと全肯定してくれるわけですが,今どきの若い人のコミュニケーション術を言葉通り鵜呑みにすると,年寄りは痛い目に遭います。

 ただ,私も電子工作をはじめたのが10歳の頃,以来40年にわたって続けてきた歴史があります。この中でも「これなら十分使えるな」と思えるものをいくつか,出してみることにしました。

(1)歪率計

 2011年3月に完成とありますので,もう12年も前に作ったものです。子どもが生まれる前のものですから,彼女にしてみれば自分の知らない父親が作ったもの,ということになります。

 当時オーディオ機器を作る事を積極的にやっていましたが,電圧電流,として波形とくると,次は歪みが数値化したくなります。他の測定器ではどんなに工夫してもきちんと測定できず,とても重要な評価項目なのに,でもその測定器は簡単には手に入らないというラスボスが,歪率計です。

 一般的ではないのは,高価であり,汎用性がないからでしょう。原理そのものは簡単で,歪み率の定義に従った回路が組まれているだけのものですが,規模がとにかく大きいのです。

 歪みのない正弦波をある回路に突っ込み,波形が変わったとします。波形が変わったと言うことは,単一の周波数を持つ正弦波に別の周波数が加わったことになるわけですから,出てきた波形から正弦波の周波数だけをカットするフィルタを通してやれば,増えた余計な周波数成分だけが抽出できます。

 その電圧を測定して,元の正弦波の電圧との比が歪み率です。仕組みは簡単ですよね。

 しかし,例えば0.1%の歪率を測定しようと思ったら60dBものフィルタを用意しなければなりません。

 それだけではだめで,元々の正弦波に歪みがあったらアウトですから,フィルタの性能よりもずっと歪みの少ない正弦波の発振器が必要です。

 当然ですが,正弦波の周波数とフィルタのカットする周波数は完全に同調しないといけませんし,さらにこれを任意の周波数で行えなくてはなりません。

 正弦波の発振器はまだどうにかなるとしても,特定の周波数をカットするBEFと呼ばれるフィルタの周波数は可変出来ないといけないし,しかも60dBのフィルタというのは単一では難しいので実際には何段かにするわけですが,それらもすべて同じ周波数に一致させねばなりません。

 これはあかん,絶望的に難しい。

 こういう特殊な測定器ゆえに代用が利かず,高価とくれば,ラスボスの名にふさわしいものになりますわね。ということで,歪率計を持つことは,その人がどれくらい本気でアンプ作りをやってるかを見る,試金石になるわけです。

 当時の私は,歪み率を測る道具としてどうしても歪率計が欲しくて仕方がなかったのですが,新品は50万円から100万円でとても買えず,程度のいい中古品(それでも20万円近くした)も何度も買い逃し,よくよく縁がないと入手をあきらめていました。

 ある時トランジスタ技術に製作記事が載っていたことを思い出し,中古品を買う予算があれば部品にお金をかけたよい歪率計が作れるはずと,部品集めを始めました。

 この記事では,20dBのフィルタを3段繋いで60dBを実現したもので,0.1%レンジでフルスケールの1/100まで読める電圧計を用いれば0.001%の分解能を誇るものです。ただし測定可能な周波数は10kHz,1kHz,100Hzの3点だけ。

 だけど普通はこの3つで十分アンプの性格はつかめますし,仮に任意の周波数の歪率が測定出来ても,結局のこの3つだけ測定しておしまいになることも多いです。

 発振器は別に作る必要がありますので,やはりトラ技の過去記事から0.0005%までいけそうなウィーンブリッジ発振回路を作る事にしました。もう1つ重要なミリバル(ミリボルトメーター)は手持ちの高感度のものがありますので,これは大丈夫です。

 部品はコツコツと揃えたのですが,なにせ規模が大きく,しかもデリケートなアナログ回路です。なかなか最初の1つがハンダ付け出来ないまま時は過ぎていきましたが,意を決して組み上げて,完成したのが2011年の春だったというわけです。

 思った以上に消費電流が大きくて電源電圧が下がってしまい,仕方がなく電源トランスを一回り大きくしたら今度はレギュレータへの入力電圧が上がって,パスコンの耐圧を越えて焼損,と言う情けない事故を起こしたことも良い思い出になりました。

 出来上がった歪率計は,想像以上に高性能で,私自身も驚きました。これなら十分表に出せるデータが取れます。原理的にこの測定器では,実際よりも良い数値は出てきません。現在測定された歪率がとてもよい値だったとしても,それは決して嘘ではなく,実際はもっと良い数値の可能性だってあるのです。

 分解能である0.001%も真空管アンプには十分過ぎるもので,これまで波形の観測と視聴で決めていた負帰還の抵抗も,きちんと数字で議論出来ます。

 もちろん欠点はたくさんあって,まず3点の周波数しか測定出来ないこと。そして発振器の性能が今一歩で,半導体アンプの測定にはもうちょっと歪みの小さなものが欲しいと言うことと,さらに100Hzでは安定した発振状態に収束するのに時間がかかるという問題がありました。

 加えて致命的だったのは,出力がGNDから浮いている場合,例えばBTLの場合は測定不可能であるということです。この測定器はあくまで入力と出力のGNDが共通である回路の歪率を測定するものであり,対GNDではない電圧を測定することはできません。

 そしてその後少しして,VP-7722Aを手に入れます。中に泥が詰まっていて,電源すら入らない,まさに産業廃棄物を3万円で買った私は,悔しさから心血を注いで修理して,復活させることに成功しました。

 そうなると,この自作の歪率家の出番はなくなり,引退することとなりました。短い期間でしたが,そこでの経験はとても印象的なものでした。

 低周波アナログ回路の集大成となる歪率計を実用レベルで完成させたことで,私は十分な自信を持つことができました。音質云々は別にして,低歪み,低ノイズの回路設計と実装の技術を,それなりに手に入れたと思っています。


(2)Si5351Aを使った任意周波数発振モジュール

 これは昨年末に基板を作ったやつです。Si5351Aという便利なPLL ICを使って,異なる3つの周波数をTCXOの精度と安定度で作ることのできるモジュールです。

 ミソは3つ,Si5351Aでは不可能なはずの外部クロック入力,Si5351Aでは許されていないはずの26MHzを源発振にすること,そして起動時に一度だけ使われる設定用のマイコンに米粒AVRことATTiny10を使っていることです。

 外部クロック入力は水晶発振器の端子のうち入力端子に突っ込めばよく,26MHz入力は設定値を計算するソフトがはじき出した設定値の一部をマニュアルで修正して対応しました。なぜ26MHzなんだ,ですか?それは秋月で安く売っているTCXOだからです。

 ATTiny10は今回初めての試みでしたが,これもなんとか突破して,生まれて初めて基板を中国の会社に発注しました。

 結果,14ピンのDIPという一般的な水晶発振器と同じサイズ,同じピン配置で,好きな周波数をTCXOの精度で得ることが出来るようになりました。TCXO精度でなくても,好きな周波数の水晶発振器を手元で作れるというのは夢のような話ですが,それがTCXOという高精度なものなのですから,中学生の頃に夢見た欲しい部品をやっと手に入れたということです。


(3)006P Ni-MHチャージャー

 これも先日書きました。006Pの充電器です。定電流回路を長時間タイマでON/OFFするだけのものですが,10年前に作ったものに2種類の充電プロファイルに対応するための改造を今回おこないました。

 難しかったのは長時間タイマで,ATTiny2313のRAMが足りずに,不意に変数を壊してしまうというバグが発生,再現性がないこともあって解決に苦労しました。なにせ128バイトしかRAMがありませんから,スタックを小さくするしかありません。でもスタックは管理出来ないわけで,十分なゆとりをRAMに取ることしか解決しません。

 結局,足りないRAMはUSARTとGPIOのレジスタに置く事にして回避したのでした。


(4)Nutubeを使ったヘッドフォンアンプ

 一世を風靡したコルグのNutube,待望された一般販売が2016年の末だったので,2017年にはいくつかの作例が世に出ました。その後ブームは下火になってしまいましたが,唯一無二の部品として現在も独特の存在感を放っています。

 そのNutubeをふとしたことから手に入れた私は,無色無臭のダイヤモンドバッファを組み合わせてヘッドフォンアンプを作りました。2016年のことでした。

 面白いのはバイアス切り替えスイッチの搭載です。Nutubeはグリッドバイアスの電圧によって,大きく特性が変わります。そこで,300Bや2A3のような直熱三極管らしいソフトディストーションの特性と,低歪みをねらったハードディストーションの特性をスイッチで切り替えられるようにしてあります。

 どちらも楽しい音で,積極的に切り替えて使うものという位置付けをしていますが,個人的にはソフトディストーションが心地よく,良いヘッドフォンを使えば,直熱三極管のあの音が耳元で再現出来ます。

 

 他にも手作りDCCデコーダやら,ピポっと起動時にあの音が出るSDカードとか,485系や583系でおなじみの車内アナウンスのオルゴールをATTiny85で再現したりと,いろいろ用意はしたのですが,どれもぱっとしないのでやめました。

 また,TCXOなどは実際に12.288MHzと11.2896MHzと8.192MHzの3つを同時に生成するデモを行う予定だったのですが,電源を入れっぱなしで放置するのも気が引けるということで,今回は見送ります。

 先にも書きましたが,今どきの電子工作は見栄えのするものでなければなりません。きらびやかに光ったり,派手な音がしたり,奇抜な形をしていたりというわかりやすさが大切で,どちらかというと何の役に立つか,と言う実用面での評価は今ひとつ重視されていないように感じています。

 決して実用性をおろそかにしていると感じているわけではないのですが,私は動機さえも実用的なところから始まっていて,目的の達成こそが最重要なので,見た目は二の次になりがちです。

 もっとも,そういうアマチュアの工作がプロにかなわないのが見た目であり,それはもう戦前から言われてきたアマチュアの弱点だったのですが,最近は3Dプリンタやら中国での加工やら,良い素材や特殊な塗料も手に入るようになり,随分工作の可能性が広がってきたのは事実で,これまでやりたくても(あるいは出来るんだけども)材料や工作機械の関係であきらめていたことが出来るようになってきたことは,本当に素晴らしい事だと思います。

 しかしながら,かつてのアマチュアの弱点は「中身はプロ顔負け」という枕詞が必ずつくもので,もっと見た目に興味を持てよ,と言う指摘でもありましたから,中身が伴わなかったり,実用性がなかったりするのは,私の中ではちょっと違うかな,と思うところがあります。

 アマチュアですから,やりたいことをやりたいようにやればいいので,同じアマチュアの私があれこれいうのは当然間違っています。間違っているのですが,ゴールが単なるうけ狙いになったりお金儲けになってしまうと,自分は同じ電子工作趣味人に含めて欲しくないなと,そうしたところから距離を置くことを考えてしまいます。

 いかに電子工作が簡単に,手軽になったとはいえ,そこは物理と数学が横たわる世界です。いい大人が「習ってないから知らない分からない」なんてのは体のいい言い訳で,出来なくてもいいから,好きであって欲しいと思います。

 そしてこれから物理や数学を学ぶ子どもたちは,初めてそれらに触れた時に「あーこのことだったのか」とポンと膝を打って欲しいと思うのです。

 

DDR2の4GBのSO-DIMMを買うことになるとは

  • 2023/01/17 13:49
  • カテゴリー:散財

 AVRなどのマイコンのソフトは,ツールの関係でWindowsを使うことが多いです。私は生活マシンをMacにしてますので,開発マシンはそのお下がりになることが多く,現在はなんとMacBookProのEarly2008(MB134J)という骨董品にWindowsを入れて使っています。

 こいつを置き換えた15インチのMacBookProが2016年のLateで,普通はこれをお下がりにしてWindowsを入れても「古い」と罵られることを請け合いですが,そこからさらに古いマシンでもWindowsが動いているというのは,なんとWindowsというのは古いマシンを大事にしてくれているのだろうと思います。

 話が逸れますが,MacはOSのアップデートが割に簡単に打ち切られるので,長く使うには向かないと思います。移行が簡単で移行後の制約もほとんどないことを考えると,さっさと新しいマシンを買ってしまえと言うのがAppleからのメッセージです。

 で,そのWindowsを入れたMacBookProですが,軽い開発なら十分な速度で動いていると思います。2.6GHzのCore2Duoでキャッシュは6MBですから,シングルスレッドの性能はそんなに低くはないと思います。(いや,低いのですよ・・・)

 LCDが15インチで広いことは開発には有用ですが,一方でメモリの速度が遅いこと,HDDが遅い(SATAです)から,ここもボトルネックになっています。

 実は,このマシンを開発マシンとしてWindows8.1を入れるとき,使い物にならないくらい遅いマシンになったらどうしようかと思っていました。やってみると案外快適で,レトロPCのエミュレータも問題なく動いています。

 ただ,これ以前のMacOSを入れて使っている時既に,最大まで増設してあった6GBのメインメモリが壊れていて,やむなく4GBで使っていた事が引っかかっていました。

 壊れたのは2GBだけか?というなかれ。このあたりは当時らしいちょっとした事情が絡んできます。

 MacBookPro2008には,メモリの増設スロットが2つあります。ここにDDR2のSO-DIMMを差し込むのですが,スロットは2つですから,例えば4GBにするには2GBを2枚使うことになります。決して512MBを8枚というわけにはいかないのです。

 当時のDRAMは2GBit品が一般に出回っている容量としては最大のもので,これを8個使ったDIMMが安価な2GByteということになってきます。ということは,MacBookPro2008では4GBまでが手軽なメモリの上限となってきます。

 一方で認識可能な最大容量はというと,4Gbit品を使って6GByteまでなら認識出来ることがわかっています。一応仕様上は4GByteまでとなっていますが,4GBのDIMMを2枚で6GBまでは認識します。これは2GBと4GBの組み合わせでも可能です。

 残念なのは,同仕様のメモリを別バンクに配置することで可能になるメモリインターリーブが2GBと4GBの組み合わせでは機能しないという事なのですが,おそらく10%程度のパフォーマンス改善に役立ったと思うと,容量を取るか速度を取るかがなかなか悩ましいところです。

 私の場合,当初は2GBと4GBの組み合わせで使っていたものが,4GBのDIMMが壊れてしまって仕方なく4GBで長く使ってきたということになります。記録によれば,4GBのDIMMが壊れてトータル4GBで使うようになったのが2016年の5月とのこと。

 マシンも古いし,特に問題も感じなかったので,おそらくこのまま壊れるまで使うんだろうなと思っていたところへ,Windows8.1のEoSです。無償でWindows10に移行出来るうちにということで,私も年末にWindows10に移行したのですが,随分と動作が引っかかるようになってしまいました。

 特に開発関係のツールを起動すると,しばらく反応が戻ってこないこともあるくらいで,これはおそらくメモリ不足でしょう。ということで,もとの6GBに戻したいなあとおもっていました。

 しかし,前述の通り,当時でさえもDDRのSO-DIMMで4GBというのは売れ筋から外れたちょっと変わったメモリです。当時私はこれを6000円くらいで買っているようですが,2GBのものなら1000円台で買えたんじゃないかと思います。

 すでにDDR2は作られていないでしょうし,私のような変わり者のために在庫してくれているお店が,プレミア価格で販売出来ることを夢見ている状況でしょうから,決して安くないはず。

 調べてみると,やはり高価です。が,1つだけ5000円というのを見つけました。4GBのDIMMが5000円とは,今の尺度で見れば高いのですが,DDR2であること,当時もこれくらいの値段はしたということを考えると,悪い買い物ではありません。

 この機会を逃すと買えなくなると思って,ポチっておきました。ただ,今刺さっている2GBの2枚のうち,1枚を入れ替える事になりますので,トータル6GBになることを考えると,2GBの増設に5000円となりますから,ますますバカらしくなりますが,それあもう仕方がありません。

 届いたメモリはなかなか良さそうなもので,当たり前の事ですが取り付けると正常に起動,問題なく動作しました。狙い通り引っかかりのようなものも出なくなり,少し重いなと感じる事以外は実用上全く問題なしです。

 大規模開発を行うわけでもなく,せいぜい数kByteのメモリのプログラムをビルドするくらいですから,本当はVisualCodeStudioを動かすためといっていいといいほどなわけで,調べてみると空きメモリは約3GBとなっていました。もしメモリの増設をおこなわなかったからこれが1GBになっていたわけで,そうなると確かにスワップも頻繁に起きるでしょうし,今回のプラス2GBは期待以上の効果をもたらすかも知れません。


RAMの少ないAVRにありがちなこと

  • 2023/01/05 12:52
  • カテゴリー:make:


 年明けにちょっとしたイベントにこれまでに工作したものを作例としていくつか展示することになってしまい,何を出そうか,出すものをそのまま出すか改良して出すかなど,いろいろ考えて年末年始を過ごしました。

 出そうと思ったものに,006Pのニッケル水素電池の充電器があります。

https://g-shoes.net/blog/index.php/view/553

 もともと自分のためだけに作ってきたものですし,見栄えも良くありませんので,少し体裁を整えてからと思っていたのですが,ところで今って006Pのニッケル水素なんて手に入るのか?と疑問に思い,調べてみました。

 結論から言うと入手は問題なく,私が入手していた秋月のGP製も(値上がりしてるけど)大丈夫ですし,ありがたいことに東芝が販売するようになったことから,ヨドバシなどの量販店でも普通に手に入るようになっていました。しかも安い。

 入手性から考えると,006Pのニッケル水素と言えばこの東芝のものをさすことが多くなった昨今,これが充電出来ないと意味がないなと充電プロファイルを追加して,Version2への改造を行うことにしました。

 秋月のGP製(以下GP)も東芝のものも7セルで8.4Vであることには変わりはないのですが,前者は250mAh,後者は200mAhと結構な違いがあります。

 充電の方法も,GP製は0.3C(つまり75mA)で4.5時間に対し,東芝のものは専用充電器の仕様から推測すると0.2C(つまり40mA)で6時間の充電です。

 充電電流も時間も異なりますから,結構手の込んだ改造を覚悟しますが,まずは充電電流の対応からです。

 この充電器はトランジスタを2つ使った定電流回路で,エミッタ抵抗で電流を決めています。75mAを流すために,最終的にはカットアンドトライで8.32Ω(実測)としてあります。

 40mAを流すにはこれに8Ωほどの抵抗を直列に繋ぎ,75mAに切り替えるときにはこの8Ωをショートすることで出来そうです。心配な事は100mA近い電流が流れるのでスイッチが大丈夫かという話と,0.1Ωで大きく電流値が変わる世界なので,スイッチの接触抵抗が影響してこないか,と言う点です。

 まあ,素人の工作ですので,セルフクリーニングを期待出来るスライドスイッチで,ちょっと高価な信頼性の高いものでとりあえずやってみます。

 実験はとりあえず成功,スイッチの切り替えで電流が75mAと40mAで切り替わります。スイッチの接触抵抗もそうですが,どっちかというと電池スナップの接触抵抗の方が影響が大きいみたいです。

 で,このスイッチに2回路のものを使い,位置によってHとLをマイコンに伝達するようにしておきます。それで充電時間を切り替えれば良いだけですので,超簡単!なはずでした。

 突っ込まれると思うので先に言い訳しておきますが,電流の切り替えもマイコンで行えば綺麗なのですよ,それにスイッチに直接充電電流を流すというのも気持ちが悪く,正しくはリレーを使うべきでしょう。

 0.1ΩオーダーですからトランジスタやFETはオン抵抗が見えてきますし,温度変化もありますのであまり使いたくありません。リレーは信頼性もありますし,電流も流せますから,こういう用途にはぴったりです。

 しかし,今回あきらめたのは基板の面積でした。定電流回路の基板は小さく,もう空きスペースがありません。リレーなど全然無理です。

 なのでギリギリのサイズとして小型のスライドスイッチを使うことにしたというわけです。

 さて,ハードウェアの改造が済んだので,あとはマイコンのソフトの修正です。この時はATTiny2313Aを好んで使っていましたが,幸いなことにPD2が1つ使われずに余っていました。ここを充電電流の設定検出に使いましょう。

 ソースをさっと修正,やることは簡単で,スイッチの状態を内部で持っておき,これと実際の状態が変わっていたら状態を更新して,状態にあわせて充電時間をプリセットしたり表示を変えたりするだけの話です。

 なにせもともと動いていたソフトです。それも,タイマで1秒ごとに割り込みをかけ,グローバル変数に確保してある時分秒を1秒ずつ減らしていき,ゼロになったら充電完了にするだけのもので,今回はこの部分には手を入れませんから,楽ちん楽ちん。

 しかし,そうは問屋が卸しません。

 実際に動かしてみると,突然20秒くらい減ったり,1時間ずつ減ったりして,あっという間に充電が終わってしまいます。さらにボタンが効かなくなったりして,もう散々な結果です。

 元は10年も前の作品ですし,もしかしたらコンパイラのバージョンが変わったとか,いろいろ考えてみたのですが,元のコードをコンパイルしてみれば問題なく,新しいコードではひどい結果になります。

 違いはどこにあるかと調べてみると,RAM使用容量が違っていました。これまでは103バイト,新しいコードは109バイトで,6バイトの違いがありました。しかし使用率は80%台ですし,これがこれほどの違いの原因とは思いませんでした。

 しかし,違いは違いです。ちょっと細工をしてRAMを2バイトほど減らしてみると,問題の発生頻度が激減しました。しかし,時間が大きく変わってしまうことは少なくなったとは言え起きてしまうので,使い物になりません。

 でも,なんとなく原因はつかめました。対策の効果もあります。ならばその方向に突き進むだけです。と意気揚々と作業を始めますが,もともと128バイトしかないATTiny2313Aですから,空きRAMを数バイト増やすことも至難の業です。

 例えば,スライドスイッチの状態を保持する変数は,スイッチの変化を知るには必ず必要な変数です。スライドスイッチそのものが1ビットのメモリだと思えばなんとかなりそうな気もしますが,変化をつかまえるには過去の情報を持つしかなく,それにはメモリが必要です。

 1ビットのために1バイト割り当てていたのはもったいないので,ほかのフラグとバインドを試みますが,どうも上手くいかないばかりか,頑張ったところで1バイト減るだけですから,6バイトにはほど遠いです。

 そこでスイッチの状態を監視することはやめて,スイッチの状態をなにかのきっかけで調べ,これで充電時間を変えるという割り切りを行いました。スイッチを動かしただけではだめで,起動時かリセット時に取りこまれる仕様にしましたが,これもなかなか使い勝手が良くありません。

 関数の呼び出しをやめて直接埋め込むとか,そうした血なまぐさい努力でなんとか106バイトまで減らした結果,上手く動いていそうだったのでこのまま長期試験に突っ込んでみました。すると,20分ほど経過すると充電が終わっています。やっぱりまだダメみたいです。

 そもそも,これは時分秒を3つのグローバル変数に割り当ててあり,これが何かの拍子に壊されることで起きている問題です。ならばと,充電のON/OFFを保持している変数を減らすため,タイマのカウントをON/OFFするレジスタを使って充電のON/OFFの状態を確認することにします。

 当然ですがなにも問題はなく,1バイト減りました。これでかなり安定してきましたが,それでも時々時間が書き換わってしまいます。

 やっぱりもとの103バイトまで減らさないとまずそうです。この段階で105バイト。

 ここで私は閃きました。タイマのレジスタで充電ON/OFFを保持出来るなら,他のグローバル変数だってどっかのレジスタで保持出来ないか?

 試しに,スライドスイッチの状態を使っていないPORTAのレジスタに書き込んでみました。PORTAは3本しかないので3ビットのメモリとしてしか使えませんが,もともと1ビットあれば十分なフラグですので問題ありません。

 これが実に上手く動き,メモリを消費せずに割り切った仕様が復活したので,長期テストにかけました。しかし,やっぱり時々時間が変わってしまいます。

 ですが,私はすっかり気をよくしました。他に空いたレジスタを探してみましょう。

 時間は最大でも6時間ですので,3ビットあれば十分。ならばUSARTのボーレートの設定レジスタのうち,上位4ビット(UBRRH)使いましょう。分は60秒まで必要ですから下位の8ビット(UBRRL)にします。

 残念ながら秒は割り込みハンドラで減算する変数ですので,volatileのグローバル変数でなければならないですから,これはこのままとします。それでも一気に2バイト減っています。ということはもとの103バイトに届いたということです。やったー。

 テストを行うと,もう問題は出ません。ちゃんと4時間30分なり6時間までカウントしてくれます。ボタンの操作も問題はありません。

 ちょっとしたバグをいくつか潰して完成です。いやー,3日もかかってしまいました。

 機能的にはあきらめることなく,思った通りの動作がプログラムできました。

 しかしですね,RAMの使用量が103バイトならOKで,105バイトなら何でダメなんですかね,103バイトでもダメな場合だってあるんじゃないか,そもそも原因は何なんだ,というのが,ずっと引っかかっています。

 それで,真面目に調べてみました。

 まず,ATTiny2313AのRAMはわずか128バイトです。ここに変数やらスタックやら確保されます。使われているRAMのサイズというのは.dataと.bss(と.noinit)の合計ですから,スタックは含まれていません。

 更に調べていくと,当たり前の事ですが,スタックはRAMのお尻の方から使われていきます。もしスタックがバンバン使われると,変数の領域を上書きしてしまうでしょう。

 mapファイルはリンクマップファイルなのでどのグローバル変数がどのアドレスにあるかはよく分かりません。そこでelfファイルを使って,以下の呪文を唱えます。

avr-nm -fsysv -n hoge.elf

 こうすると,変数のアドレスが出てきます。素晴らしい。

 RAMは0x00800060からですので,時分秒やフラグをグローバル変数に取って109バイトを使ってしまったケースでは,

__data_start        |00800060|   D  |            NOTYPE|        |     |.data
hour                |008000c6|   D  |            OBJECT|00000002|     |.data
min                 |008000c8|   D  |            OBJECT|00000002|     |.data
__bss_start         |008000ca|   B  |            NOTYPE|        |     |.bss
__data_end          |008000ca|   D  |            NOTYPE|        |     |.data
_edata              |008000ca|   D  |            NOTYPE|        |     |.data
mode                |008000ca|   B  |            OBJECT|00000002|     |.bss
sec                 |008000cc|   B  |            OBJECT|00000001|     |.bss
__bss_end           |008000cd|   B  |            NOTYPE|        |     |.bss
_end                |008000cd|   N  |            NOTYPE|        |     |.stab

 となります。secという秒を保持するグローバル変数は0x008000ccに取られていますし,0x008000cdまではスタックが迫ってきても大丈夫とわかります。

 一方で,秒以外のグローバル変数をすべてレジスタに追い出し103バイトに収めたケースでは,

__data_start        |00800060|   D  |            NOTYPE|        |     |.data
__bss_start         |008000c6|   B  |            NOTYPE|        |     |.bss
__data_end          |008000c6|   D  |            NOTYPE|        |     |.data
_edata              |008000c6|   D  |            NOTYPE|        |     |.data
sec                 |008000c6|   B  |            OBJECT|00000001|     |.bss
__bss_end           |008000c7|   B  |            NOTYPE|        |     |.bss
_end                |008000c7|   N  |            NOTYPE|        |     |.stab

 となり,secのアドレスが0x008000c6に下がり,確かに6バイト分だけ減っています。

 それでも,128バイトのRAMのうち109バイトを使っているだけですから,スタックは19バイトも確保出来るはず。これが25バイトになったからといって,どれだけ助かるかという話は,スタックがどれだけ使われるのかを考えないといけなくなります。

 しかし,スタックの使用量というのは簡単には計算出来ないものです。関数を再帰的に呼び出せばそれこそ無限にスタックが使われますし,どんな関数でいくつスタックを使うかを知るのはコンパイラだけです。

 ただ,割り込みを使うとレジスタの待避でスタックをたくさん使うことは想像できるわけで,gccが吐き出したアセンブルリストを眺めてみました。

 まず,気になっていた割り込みハンドラです。やってることは本当に最小限度で,secというグローバル変数をデクリメントしているだけです。

ISR(TIMER1_COMPA_vect)
{
 338:    1f 92           push    r1
 33a:    0f 92           push    r0
 33c:    0f b6           in    r0, 0x3f    ; 63
 33e:    0f 92           push    r0
 340:    11 24           eor    r1, r1
 342:    8f 93           push    r24
    sec--;
 344:    80 91 cc 00     lds    r24, 0x00CC
 348:    81 50           subi    r24, 0x01    ; 1
 34a:    80 93 cc 00     sts    0x00CC, r24
}
 34e:    8f 91           pop    r24
 350:    0f 90           pop    r0
 352:    0f be           out    0x3f, r0    ; 63
 354:    0f 90           pop    r0
 356:    1f 90           pop    r1
 358:    18 95           reti

 こんな感じです。スタックの消費量は4つですね。割り込みはこれ以外にないので,多重割り込みは発生しません。

 これなら問題ないはずと言いたいところですが,実はこの充電器,LCDに4ビットパラレルの古いものを使っています。しかも悪いことに,LCDの表示に関係するコードは,様々なAVRに対応出来る用に作られたもので,私が作ったものではありません。

 ここも確認してみたところ,低レベルの書き込み部分で4つ,これをコールする上位が2つずつで計4つ,合計で8つ使っている感じです。

 もしLCDになにか文字列を書き出すときに割り込みがかかったら,12バイトのスタックが詰まれてしまうがわかります。これでも19バイトには届きませんが,なにかあったらアウトになるくらいギリギリです。

 正確なところはもう追いかけるのをあきらめましたが,残り105バイトでも異常動作をしたこと,そして103バイトでは正常だったことを思うと,どうもこのあたりに境界があると見て良いでしょう。

 結局103バイトで大丈夫という確証を得るのは無理だったわけですが,そこは実績で納得することとし,境界線が105バイトにあるということで,納得することにしました。

 AVR,特にRAMが64バイトや128バイトしかないtiny系ではよく問題になることのようで,Cで書くことに慣れてしまうと,ついついこういうミクロな部分を見落としがちになります。

 結果が甚大なものになるだけに,小型のマイコンを使う時には気を付けないといけないポイントだと,今さらながらに思った次第です。

 アセンブラで書けばまだ意識するんですが,私がAVRを使っている理由はCで実用的なコードが書けるからで,今から全部をアセンブラで書くと言うのもちょっと勇気がありません。

 詰まるところ,アセンブラ的にCを使い,十分なテストで乗り切る事が,AVRとの付き合い方かも知れないと思います。

ページ移動

  • 前のページ
  • 次のページ
  • ページ
  • 1

ユーティリティ

2023年01月

1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 - - - -

検索

エントリー検索フォーム
キーワード

ユーザー

新着画像

過去ログ

Feed