2024年10月24日木曜日

出入り検知器をPCB化する(その14)サーボ・モータを動かす2

 サーボモータ(SG-90)をPIC16F18313で動かすための基本的な計算が出来たところで実際のプログラムを作る。サーボモータを動かすために必要なパルス波形は次のようなものである。

2つのパルスの周期(20ms)はTMR2で決める。2つのサーボモータの角度に対応する2つのパルス幅(0.5ms~2.4ms)はパルス幅変調器(PWM5及びPWM6)で決める。

 サーボモータがある角度にある時、突然別の角度を指示するとサーボモータは最速(仕様では0.12sec/60度=500度/秒)でこれに追従する。これが目的とする動作に合っているのか、またサーボモータの負荷を高め寿命を縮めることにならないか気になるところである。そこでTMR2+PWMで生成できる最小の分解能である3度毎にステップを進める事とする。もし20ms毎に割り込みをかけて3度進めると150度/秒となり、良さそうである。TMR2にはポストスケーラがあるので、これを使うとこの1倍から1/16倍までのスピード制御が可能であり、即ち9.4度/秒~150度/秒の速度制御が可能である。

ロジックとしてはTMR2(のポストスケーラ)により割込みがかかったときサーボが目的とする角度に達していない場合は要回転を意味するフラグを立てる。さらにパルスの立下りで(IOCの機能を使って)割り込みをかけ、要回転フラグが立っている場合は次のパルス幅に相当する値をPWMに書き込む。そのタイミングがパルスの立下り後である理由はパルスの立下り時に処理を始めると処理のための時間的余裕が十分あるからである。時間的には20ms以内に処理を終わらせればよいので問題は無いだろう。なおIOC(Interrupt On Change)とはPICのピンの信号の変化を捉えて割込みを起こす機能であり、立ち上がりと立下りを個別に捉え割り込みを起こす事ができる。通常は入力信号に対して使う機能だと思うが、ここでは出力信号に対して使っている。

ソフトの開発と並行して汎用基板にプロトタイプのハード(コントローラ)を作った。 大きさは概ね2cm×4cmに収まった。

基板に載っている部品はPIC16F18313と電源のパスコン、LEDと電流制限用抵抗だけで他に接続用に電源コネクタ、サーバモータ用3Pコネクタが2つ、ホストと繋ぐシリアル通信用コネクタが1つあるだけである。シリアルインターフェイスはUSBを介してTeraTermから文字ベースでテストできるようにした。

電源を入れると各部の初期化を行い、TME2やPWMを起動、1.45msのパルスを作ってサーボを中点まで移動、その後パルス幅を0としてサーボを休止、割り込み可としてSLEEPする。

RS-Kager2-Servo313-032 2024/10/23 for RS-Kager2 ver 0.27
 Copyright(C)2024 MYcrosLip
*** Init Servo.
*** Start Interrupt.
*** Start Servo.
*** Sleep Loop.

あとはシリアル通信で受信したコマンドを解釈してPWMを制御しサーボを3度ステップで動かす。目的の角度に達したらパルスを止める。3度進むのに必要な時間は7.5msであるから50Hzの1周期(20ms)以内に終わり、タイマーなどを使った遅延操作は不要である。

実装した主なコマンドは<nn>H、<nn>V、Z、<mm>Gである。Hは水平サーボ、Vは垂直サーボの軸を回転(指向)させるコマンドで<nn>は-90度~90度の範囲であり、Zは原点(0,0)に復帰、Gは速度制御(<mm>は引数で1~16)である。

次の実行例で、90Hは水平サーボを90度に向け、90Vは垂直サーボを90度に向け、Zで原点に戻っている。小文字のhやvは動作確認のため1ステップ進むごとに表示させている。

90Hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
90Vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
Zhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhvhv

 サーボの動作中でも次のコマンドを受け付ける。この場合、実行中のコマンドの終了を待たずにキャンセルし、後で来たコマンドを処理する。この動作を2つのサーボに対して各々独立して行う(逆に言えば処理待ちキューを作らずに済ませたという事)。

これで外部から文字ベースでサーボを制御できるようになった。

(続く)

 


 

2024年10月19日土曜日

出入り検知器をPCB化する(その13)サーボ・モータを動かす1

 出入り検知器にサーボ・モータを動かす機能を追加できないか相談を受けた。水平と垂直の2方向の動きをサーボで作る。そこでSG-90という最も安価なサーボを秋月で入手して動かしてみる事に。

SG-90にはSG90-HVという軸が360度回転するタイプもあるが、入手したSG-90は可動域が180度のタイプである。SG-90の角度を制御するには電源5Vを加え、次のような20ms周期のパルス幅変調の信号を制御端子に加え角度を制御する。

 角度は0.5ms~2.4msのパルス幅に対応して0度~180度に変化する。中点(90度)のパルス幅は1.45msである。パルスを0.05ms(50μs)単位で変化させると180度を39ステップ、即ち4.7度単位で制御する事ができる。SG-90のデッドバンド巾は1μs(本当?)なので問題にはならないだろう。

この幅のパルスを作る事は難しくない。問題は周期の50Hz(20ms)との関係である。ポイントは大きなデューティ比 0.5/20~1.45/20~2.4/20 を細かく制御する必要がある事である。これを整数比に直すと10/400~29/400~48/400 であり、20msの1/400である50μs単位で制御する事になるが、これは試算値であって実際のSG-90がこれをどれ程の精度で追従できるかは分からない。

8ピンのPIC16F18313には2個のPWM(パルス幅変調器)が内蔵されていて丁度良い。このPWMはタイマー2(TMR2)と組み合わせて使う。


TMR2はFosc/4のクロックで動く8ビットカウンタであり、1/1, 1/4, 1/16, 1/64 のプリスケーラが利用可能。これを使ってFosc/4=500KHz、プリスケーラ=1/64、カウンタの上限(PR2)を155に設定すると約50Hz(=500KHz/64/156)が取り出せる。なおTMR2のポストスケーラ(1/1~1/16)からは直接信号が取り出せず割り込み用にしか使えない。

TMR2と組み合わせて使うPWMのパルス幅は10ビットで指定する。つまり8ビットカウンタの1カウント値の1/4まで指定でき、これは約32μsステップ(=20ms/156/4)単位で指定できる事になる。これを使って改めてパルス幅の指定範囲を計算するとカウント値は 0.5ms÷32μs≒15、1.45ms÷32μs≒45、2.4ms÷32μs=75 という値となる。つまり90度で30カウント差だから1カウントが3度の回転に相当する。

(続く)


 

 

2024年10月15日火曜日

出入り検知器をPCB化する(その12)

 これまで作った歴代作品を並べてみた。残念ながら汎用基板を2段重ねした第1.5世代は部品を使い回ししたため残っていない。

当初は人の動きを捉えて喋るだけで良かったものが、それに加えてリアルタイムクロック(RTC)や気圧センサーを加え気圧変化を喋り、さらにメロディを奏でるまでになった。C言語で書いたプログラムもメロディ生成に2千行、本体は1万行程度になった。そして開発は今後も続く。本バージョンの締めとして若干の修正を加えた第4.2版を赤色のレジストで発注した。

これで終わりかと思ったが、更なるリクエストがあり、 開発は続く。

(続く)


 



 


2024年7月31日水曜日

出入り検知器をPCB化する(その11)

メロディ生成をPIC16F18326に変えたので、それに合わせて第4版のPCBを発注した。版を追うごとに基板の密度が上がっている。秋月で買える安い部品だけで作っているのでICのパッケージも様々である。これまでと同じ大きさの基板に収めるのはボチボチ限界かな。

1週間ほどで基板が出来上がってきたので早速秋月に走り部品を調達して組み立てた。

ソフトに幾つかの修正を加えてメロディIC(PIC16F18326)を接続できた。

RS-Kager2 Ver0.23 for RH-Kager2 Rev 0.40 2024/07/30-1
 StateMachine: 3.1 SpeakMessage: 0.4
 Copyright(C)2023,2024 by MYcrosLip. (DEBUG MODE)
... Checking Melody Player - Tone326-197:103
--- Melody Player is ready.

... Checking Voice Synthesizer -VF1a
--- Voice Synthesizer is ready.
--- I2C_INIT: I2C Bus is Ready.
--- Extend EEPROM OK.
... EEPROM-ID = RS-Kager2 Ver0.23 2024/07/30-1
... RTC RAW10: 02 80 08 22 23 09 31 03  07 24
--- RTC is active.
--- RTC: 24/07/31(WED) 09:23:22
--- Barom is active.
!!! Barom: FIFO is not full; Wait a dozen seconds.
--- Air Pressure: 996.0 hPa (Offset 9.2 hPa).
--- Power Supply : 5.09 V
--- Environment : OFFICE mode.
--- Speak air pressure 1 time(s) in an hour.
--- Speak the time before speak the pressure is ON
--- Speak random at S3 is : ON
--- Speak Battery Low is ON.
--- Current LED Brightness is 30 %
--- Start Interrupt Handling.
 

メロディが時々ハングアップしたが、これは暫定的に作っていたインターフェイスの中の待ち行列(QUE)がCでお決まりのNULLポインタを起こしていた。複数の曲をキューイングして演奏する状況は考えられないのでQUEの使用は止め、逆に曲の途中で別の曲を強制的に割り込ませる機能を追加した。これにはメロディICとPIC18F27Q43の間の連携が必要であるが、メロディICに予め演奏の強制停止コマンドを組み込んでおいたおかげですんなりと実現できた。

(その後)

第4版のPCBに若干の修正を加えて第4.1版を作った。変更点はPICのバグ回避のための変更1件と利用上の都合に伴う2件である。

 出来ればこれを最終版にし、あとはソフトに任せたい。

 (続く)

 

2024年7月18日木曜日

出入り検知器をPCB化する(その10)メロディ生成4

 次はテンポの処理である。

単音の場合は適当に遅延を挟むことでメロディを演奏することが出来たが和音の場合は各音符の長さが同一ではないため単純な遅延では処理できない。 そこで32分音符の長さを基準とした割り込みとソフトのカウンタで処理する事とした。

実際にはスタッカートやテヌートも処理したいので音符の長さを12分割した間隔で割り込みをかけ発音を制御する。最高のレートだとBPM=200の場合は約3ms毎に割り込む事になる。

割込みにはTimer6(TMR2)を使った。250KHzのクロックをプリスケーラで16分周するとポストスケーラを使わなくても丁度良い具合になる。 BPM=40で244分周、BPM=200で49分周と、8ビットの分周器に丁度収まるスケールだ。

ここまで出来たら、あとは譜面の記法を決めればよい。TeraTermで操作できるよう譜面は全て文字で表記するようにした。前に書いたように音の高さはMIDIの音階と同じコードを使うがメモリーを節約するため16進数で表記する。音の長さは次の様にした。

音符は音の高さ2文字と長さ1文字の3文字で表す(休符は音階=0とする)。更にチャネル指定を「:c:」で、BPMを「%hh」、音階のオフセットを「$±h」で表す。1音を3文字で表すよう数値は16進標記とした。3文字の間にはスペースや幾つかの区切り記号を挿入することが出来る。

譜面は改行で終わる1行の文字列である。譜面を最後まで読み込んだら内部でチャネル(音源)ごとの楽譜に変換し、32分音符の1/12の時間間隔の割込みに従って同時進行で演奏(音をON/OFF)する。

次のような4和音の譜面を1オクターブ上げてBPM=100で演奏してみた(この譜面はプログラム中にデータとして記述して分かり易いよう改行を入れている部分であるが、実際は「"」や改行を外し1行にする必要がある)。

       "%64$+c"
    ":1:43E 41E 46E 48G 4dG 4aE_48G 4dG 4aE 46E 48E 43E 413"
    ":3:3EE 3cE 3eE 45E     45E_45E     46E 43E 43E 3cE 3c3"
    ":2:3aE 39E 3aE 3cE     41E_3cE     41E 3eE 40E 37E 393"
    ":4:37E 35E 37E 35E     32E_35E     3aE 37E 30E 34E 353"


 これを解釈し内部で使う音源ごとの楽譜に変換したものは次のようなものである(各音源の最初の16バイトの16進ダンプ)。

  --1-- 54 43 54 41 54 46 74 48  74 4d 54 4a 74 48 74 4d
  --2-- 54 3a 54 39 54 3a 54 3c  54 41 54 3c 54 41 54 3e
  --3-- 54 3e 54 3c 54 3e 54 45  54 45 54 45 54 46 54 43
  --4-- 54 37 54 35 54 37 54 35  54 32 54 35 54 3a 54 37

これを演奏するとこのような音になる。

(その後)

3連符を表現したくて割込み頻度を3倍にして、32分音符の間に3×12回割り込むようにした。これで音符の長さを表すパラメータが3倍となり3で割り切れるようになる。表記も改訂した。

この機能を使って3連符のあるこんな演奏ができる。我ながら上出来である。 

(さらにその後)

音源3,4はパルス幅変調ができるので、試してみた。パルス幅を50%、25%、12.5%、…と半分ずつにしていくと、50%~6.25%までは音質が変わり、それを超えると音量が低下する。これは主旋律を目立たせる技として使えそうである。

(さらにその後)

オシロスコープを使って割り込み処理にどの程度時間を取られているか観測してみたところ約2msかかっていた。一方200BPMの時32分音符に費やされる時間は約37ms。実際にはそれを12分割して管理していたが、さらに3連符処理のためその1/3倍としている。つまり割込み処理を1ms以下で終える必要がある。一方実際には処理にその倍の時間がかかっており100BPM以上の演奏には追従できない事が分かる。 これを解決するためCPUクロック(Fosc)を4倍に引き上げて4MHzとした。これで1回の割込み処理が500μs程度で終わり時間に余裕が生まれた(実際には36回(或いはその倍数)に1回起こる音符の切り替え処理時にもっと多くの時間がかかるが聞いていても分からない)。またクロックの変更に伴って影響を受けるUARTの通信速度や音の高さに関係するパラメータを変更した。幸いにも音符の高さを表すパラメータはクロックを細工する事で再計算せずに当初の音域をカバーする事が出来た。詳しくは後述する。

(つづく)



2024年7月16日火曜日

出入り検知器をPCB化する(その9)メロディ生成3

 4チャネルのミキサーを作る。

4chの発振器の出力をPIC外で抵抗ネットワークでも組んで合成すれば済むわけだが、ここではデジタル的に行えないか。4つの信号のXORを取れば済むと考えCLCを3つ組み合わせてミキサーを構成してみた。しかしサッパリ動かない。かなり長期間悩んだが問題はPIC16F18326のデータシートの次の図であった。

図を見てAND-XOR回路とみてプログラムを組んだ。即ち1番目と3番目を入力としてXORとして出力させるために2番目と4番目の空き入力をHとした。しかし図は間違いで、実際には図上の記述の通りOR-XOR回路で2番目と4番目の空き入力をLとしなければ動かなかった。細かい字を読みたくない老眼の弊害が現れた訳だ。

これを使ってCやCmの和音を発生させたが歪が酷くて聞くに堪えない。方形波は奇数倍の高調波を多く含むのでこうなるのか。偶数倍の高調波の多いのこぎり波だったらまだマシだったかもしれないが・・・。

それだったらと、4つの波形をデジタル・サンプリングして合成する事に挑戦した。実際には各波形は1か0かの2値波形だし、16F18326のD/A変換器は5ビット(32値)なので、この間をどうマッピングするかが課題になる。単純に各波形に 8の重みを付けて足し合わせたらよいのか。或いはエネルギーは振幅の2乗に比例するから32の2乗を4等分した平方根の重み付けで良いのか。サンプリング周波数(割り込み)を4KHzで試してみた。インストラクションサイクルが250k/sなのでサンプリング当たり62命令しか実行できない。実際に動かしてみると割込みは1.8K回/秒程度が限界でとても追いついていない。サンプリング周波数を1KHzに落とすと500Hzまで再現できるはずであるが、やってみても音にならない。Fosc/4を少なくとも4倍の1MHzくらいまで上げる必要がありそうで、そうするとNCO1の精度が悪くなるし、全体的なパラメータの見直し・変更が必要になる。

そこでパラメータの大幅変更を伴うデジタル・サンプリングは後回しとし、とりあえず14ピンソケットを8ピンソケットに重ね、4つの発振器の信号をピンに取り出して抵抗ネットワークを空中に組んで音を出してみたところ、素人の耳で聞いた限りではまぁ使えそうである。


先ずはこれで進める事とした。

(その10へ続く)

 

2024年7月12日金曜日

出入り検知器をPCB化する(その8)メロディ生成2

メモリー容量の関係でPIC16F18313では単音しか発生できなかった。これをPIC16F18326に換えることで機能拡大ができそうである。とりあえず基板を改造してPIC16F18326とオーディオアンプが使えるテスト環境を作った。

14ピンのPIC16F18326はPIC16F18313用に用意した8ピンソケットに一部だけ挿し、残りは宙ぶらりんの状態にしている。

和音を発生させるためには複数の発振器が必要であり、そのためにNCO1以外に使えそうなのがTMR0、TMR1、及びTMR2であるが、TMR1は任意の分周比の連続波形が発生できないので除外する。 

TMR0は次のような構成である。

クロックは幾つか選択できるが、後の計算の結果HFINTOSC(16MHz)を選択した。プリスケーラは1/2^n(n=0..15)が選択できる。中央のカウンタ部には8ビットモードと16ビットモードがあるが、8ビットモードでないと任意の分周比が設定できない。8ビットの分周器を経た後はポストスケーラで1/1~1/16の分周比を選択できる。最後にD-FFで1/2に分周し方形波として出力する。クロック周波数、プリスケーラ、分周器をどう組み合わせれば目的とする周波数を発生できるかExcelで最も誤差の少ない組み合わせを求めた。その結果全音階に渡って誤差を数Hz以内に収めることが出来た。

TMR2(Timer2、Timer4、Timer6)は次のような構成である。

この回路をそのまま音階発生に使うには次の欠点がある。

  1. 入力のクロックがFosc/4固定である事。
  2. 分周比の設定が8ビットしかできない事。
  3. 音階発生にはポストスケーラが使えない(信号が取り出せない)。
  4. そのままではPIPで出力を取り出せない。
  5. 発生周波数(分周比)によってduty比が変化する。

1,のFoscは1MHzとしており、これは他のデバイスのパラメータとの関係で安易に変更できない。従ってクロックは250KHz固定になる。4,5,を解決するため次の様にCCPと組み合わせてPWM(パルス幅変調器)として使う。duty比は他に合わせて50%とするが、これを変化させて音量・音質に変化を持たせることもできるだろう。

プリスケーラと8ビットの分周器を組み合わせると最低音の2音階を除いて予定した全ての音を発生させることが出来る事が分かった(ただし最高音部は周波数誤差が50Hz以上に大きくなる)。何処まで使えるかは実際に耳で聞いて決めるが最高音部は周波数が高すぎて現実的には使わないだろう。

音階のパラメータと誤差を纏めると次表のようになる(一部省略)。NCO1はこういう目的には優れものだという事が分かる。

TMR2はTimer2、Timer4、Timer6の3個搭載されているので、後述のミキシングを考えてTimer2、Timer4の2個を使い、NCO1とTMR0と併せて全体で4音を発生できるようにした。

Timer6は テンポの作成に使う。

(その9に続く)