fc2ブログ

記事一覧

簡易パルスジェネレータの製作(ソフト編)

ソフト(スケッチ)の方もラジオペンチ氏の記事をベースに作成しましたので、まずそれを参照していただくことを前提とし、この記事では主としてtimer1による高速PWM出力の利用およびその他のオリジナルからの変更点について記述することにします。

ラジオペンチ氏の記事を拝見したのはかなり前だったと思いますが、触発されて何かを製作するまでには至りませんでした。でもこれがずっと頭の片隅でくすぶっていたのでしょう、最近になってPWM出力を利用して性能を改良することを思いつきました。結果として、幅広いレンジのパルス幅、デューティ比のパルスが得られました。私が良く使う8ビット系のPIC(例えば16F18xxxシリーズ)ではPWMはせいぜい10ビットの分解能しかありませんが、Arduinoに使われているATmega328のtimer1が16ビットの分解能を持っていたということもラッキーだったと思います。

それでは以下に分り易いように項目番号を付して説明することにします。

1. timer1のPWM出力の利用について
まずATmega328のtimer1の動作について説明しますが、全体的なお話はネット上にある情報にお任せし、本スケッチで使用した機能に狭く絞ることにします(使っているのはtimer1の5つの動作モードの内の、「高速PWMモード」です)。
スケッチ中でこのモードは以下のような動作をしています。下の図を参照しながらお読みください。なおArduinoでATmega328のレジスタを設定するためにライブラリavr/ioをインクルードしておく必要があります。

1.1 高速PWMモードの動作
1.1.1 タイマークロックをカウントするカウンタTCNT1はBOTTOM値(0)からインクリメントされTOP値(本スケッチでは比較レジスタOCR1Aの値としている)に達すると再びBOTTOM値(0)にリセットされます。これを繰り返しますので、カウンタ値は周期的にのこぎり波状に変化します。またこの周期がパルス周期になります。
1.1.2 PWM出力(OC1B)はカウンタ値がBOTTOM値にリセットされたときHigh(またはLow)を、カウンタ値がBOTTOM値とTOP値の間に設定された比較レジスタOCR1Bの値に達したときにLow(またはHigh)を出力します。このPWM出力(OC1B)がHigh(またはLow)である時間がパルス幅になります。各タイミングでHigh、Lowのどちらのレベルを出力するかは所望のパルスの極性に合わせて設定します。
FastPWM01.jpg

1.2 高速PWMモードでのレジスタの設定
上記の説明の中で触れたtimer1のレジスタの設定について少し詳しく説明したいと思います。

1.2.1 パルス周期のレンジに合わせてシステムクロックを変える
下の図のようにパルス周期の値に合わせてタイマークロックを変えます。パルス周期を変えていくとタイマークロックが次々に図中の上下の横線の間をホッピングしていくことになります。パルス幅の方はパルス周期よりも小さく、かつタイマークロックの1クロック時間以上の大きさである必要があります。
timerclock03.jpg
<2018/2/5 間違いがありましたので上の図を修正しました>

このタイマークロックを変えるためにシステムクロックを分周するプリスケーラの設定を変えるにはレジスタTCCRBのCS12-0ビットを設定します。
TCCR1Bsetting.jpg

スケッチ中の該当部分の記述は、以下の通りです。
PG_sketch01.jpg

また、所望のパルスを得るためにロータリエンコーダでパルス幅、パルス周期を変えていくわけですが、パルス周期が右(より長時間)のレンジに移る際に分解能が変わるためパルス幅をどうするのかを決めておく必要があります。本スケッチではパルス幅が非常に小さかった場合移動先のレンジでの最小値(1タイマークロック時間)に設定しています。先ほどの図中にもこのパルス幅の最小値を示してあります。
またそれとは逆にパルス幅の方を小さくしていって、そのレンジでの1クロック時間に達した場合はそれ以上パルス幅が小さくならないようにしています。つまりパルス周期の設定を優先させる動作としているわけです。

1.2.2 パルスの極性を切り替える
レジスタTCCR1AのビットCOM1B1-0ビットにて設定します。下図を見ていただければ分かると思います。
COM1Bsetting.jpg

スケッチ中の該当部分の記述は以下の通りです。
PG_sketch02.jpg

2. ロータリエンコーダ入力の改善
速報版で途中経過が分かると思いますので、ここでは最終の検討結果を解説します。
まず、loop()関数内の各プログラム要素の実行時間を下表に示します(millis()関数を使って調べましたので、1ms以下は切り捨てられています)。
これを見るとLCD表示が含まれるプログラム要素の実行にかなり時間がかかっていることが分かります。
Simplified_PG_exectime.jpg

また、エンコーダの出力を直接観測してみると下図のようになりました。
これから次のことが分かります。
・回し始めは角速度が少し小さいので1クリック(=4相分)の時間は約20msである
・中間時点では角速度が最も大きくなり、1クリックの時間は約13msになっている
pulgeneosc03.jpg

以上のことから、エンコーダの1クリック分、1相分の変化はLCD出力を含むプログラム要素の実行時間よりもかなり早いことが分かりましたので、最終的に次のようにスケッチを修正しました。
①時間のかかるLCD出力(パルス幅表示、パルス周期表示、デューティ値表示、周波数表示)は変数ChangeFlgを使ってタクトスイッチ、ロータリエンコーダの入力があった場合だけ行う
②前項①に優先してエンコーダに変化があった時はその後一定回数(変数supplcdCでカウント)のloop()関数周回ではLCD出力を行わない。これはエンコーダを一回の操作で数クリック分回した際に連続する変化を取りこぼしなく読み取るためである

以上の方法でパルスの各パラメータをロータリエンコーダを使って気持ちよく設定できるようになりました。

3. その他の変更点
パルス幅、パルス周期の最小の分解能を1μsとし、この1μsを1として、パルス周期の最大値である1/16MHz×1024×65536 = 4.2194304sまでを取り扱うためには変数ton(パルス幅)、tpr(パルス周期)はUnsigned long型とする必要がありました。負の値が扱えないことからスケッチの随所でif分の条件の変更等が必要でした。
また、LCD出力においても、時間で最小1μsの分解能の表示、周波数で最大500kHzまでの表示が必要になりましたので、アドホックにスケッチを変更しました。

ラジオペンチ氏との同時進行的なやり取りもさせていただきながら、なんとか満足できるものができました。改めて氏への感謝を表したいと思います。

最後に仕様をまとめておきます。
<簡易パルスジェネレータ仕様>
・パルス設定範囲 パルス幅:1μs~4.194s、周期:2μs~4.194s
・0-5Vのロジック出力、出力レベル可変のアナログ出力

<ソフトウェア(スケッチ)>
ソフトのバージョンアップにより追記を多数重ねた結果、記事が乱雑で見にくくなりましたのでそれらを整理(削除)しました。
その代わりに覚えとして下図にこれまでの開発経緯をまとめておきます(ソフト(スケッチ)は3種に分化しました)。
Simplified_PG_history01.jpg

スケッチは下表の中のリンクからダウンロードできます。上の図でも触れていますが、リリース中の3種のスケッチはHMIが異なっており、それぞれ特長があります。試していただいてお好みのものを選んでいただくと良いと思います。
日付更新内容リリース中のスケッチ
2018/1/24初版:timer1の高速PWMを利用した簡易パルスジェネレータNA
2018/2/5エンコーダを素早く数クリック分回した際の取りこぼし対策NA
2018/2/6エンコーダをゆっくり回した際のチャタリング対策NA
2018/2/6パルス幅、パルス周期の増減のステップを表示の最小桁に合わせるSimplified_PG_v3r6
2018/2/7パルス幅入力とパルス周期入力の画面を分ける。表示桁数を拡張Simplified_PG_v4r7
2018/2/8上記に加えてアイドル時の自動画面切り替え表示機能を追加Simplified_PG_v5r5

更新情報:
2018/02/13 Simplified_PG_v4r6 → Simplified_PG_v4r7 LCD表示に関わるバグ(1箇所)を修正
2018/02/13 Simplified_PG_v5r2 → Simplified_PG_v5r3 同上
2021/12/24 Simplified_PG_v5r3 → Simplified_PG_v5r4 「アトミック操作」対応修正(居酒屋ガレージ店主様のご指摘を受けて)
2021/12/25 Simplified_PG_v5r4 → Simplified_PG_v5r5 tcnt2base(TCNT2のスタート値)修正(居酒屋ガレージ店主様のご指摘を受けて)


スポンサーサイト



コメント

動作確認しました

今日は、こちらでもブレッドボードで動かして、オシロやユニバーサルカウンタで様子を見てみました。ソフトはSimplified_PG_3r1.txtです。

ハードのカウンタで動いてるだけあって、正確なタイミングでパルスが出て気持ちいいですね。パルジェネはこうでなくちゃ、それに1μsまで指定出来るのもいいですね。

以下、ちょっと気になった点をレポートします。

1) パルス幅表示と実際のパルス幅が少し違う場合がありました。
具体的には、周期35msで4μsステップ、周期263msで16μsステップ、周期1.049sで64μステップでパルス幅が変わるのだと思いますが、表示(設定)は1μs刻みのままです。
これが仕様だと言われればそれまでですが、表示と実際のパルス幅は一致していて欲しいです。


2) 合わせにくい値がある
例えば周期を32.70msとか。今やったら出来ました。どうしたんでしょう、

重箱の隅をつつくような指摘ですみません。全体としてはとても良く出来ていてすばらしいと思いました。

レポート有難うございます

こんばんは。

1)の原因ははなんとなく思い当たります。
2)の問題は私自身も経験したような気がしますが、たまに起こったので放置プレイにしたのでしょう。もしかしたらエンコーダがらみかなぁ?多分これは違うと思いますが、エンコーダのクリックの位置とA相、B相の接点の位置の関係も気になっています。
折角追試してレポートもしていただいたので、しっかり動作をチェックして完成度を上げたいと思います。

エンコーダ入力に問題あり

おそらくご指摘いただいた2)の方の問題だと思うのですが、エンコーダを非常にゆっくり回した際に値が飛んだり、逆の方向の増減になったりすることがあります。オシロをながめつつ、Serial.printなどを使って原因を追求したいと思います。

2)の問題はエンコーダのせいかも?

エンコーダを「非常に」ゆっくりワンクリック分回すと、ReadEnc()からの応答(変数X)が「-1,-20,1」、「1,20」、「-1,20」などと20倍速も交えた連発で返ります。
例えば「32.70ms」に向かって0.01msずつ近づけようとゆっくり回すとなかなか合わせられない事態が生じます。むしろクリッとある程度の速度で回すと正常応答の確率が高まります。
明日オシロを使って波形を確認します。

2画面バージョンをアップしました

大体使っていただけるレベルになっているのではないかと思います。

従来の形のものに加えて、ソフト編に2つの画面を使ったバージョンをアップしました。内部処理も表示もすっきり伸び伸びとなったので、こちらの方がお勧めかも。

re:2画面バージョンをアップしました

今晩は、

Simplified_PG_v4r2をテストしてみました。こういう表示にするのも悪くないですね、表示のために値を丸める必要も無いですし。(デユーティーが1/10で表示されているようです)

v3の時から気になっていた点ですが、パルス周期を伸ばしてパルス幅の刻みが変わった時に表示と実際のパルス幅が少し一致しない現象がありました。例えば、
・周期:32.7ms、パルス幅:10μs (この時点では1μs単位で設定可能)
 ↓ 周期を増加
・周期:32.768msにすると、パルス幅は4μsの倍数になるはずが、表示は10μsのままです。しかし実際に出力されるパルス幅は8μsになっていて、表示と実際の出力がちょっとずれています。

さらに、この状態からパルス幅を増加させると、表示は14,18,22と変わるのに、実際に出力されるパルス幅は、12,16,20μsとなっています。ここは表示を実際の出力に合わせておいた方が良いと思います。刻みが16μs,64μsとなる場合も同じ現象が出ていました。

あと、ここまでアレンジしたいただいたなら、もう元のプログラムは遠慮なくいじっていただいて結構です。プログラムのどこかに出典を書いていただくだけで充分です。

それと、せっかくここまで改良していただいたので、どこかのタイミングで私のブログに記事を書いて、こちらを紹介させていただきたいと思いますが、いかがでしょう。

有難うございます

試していただいて有難うございます。
デューティの件は表示桁を拡張した際にプログラミングでポカをやったようです。パルス周期を伸ばしてパルス幅の刻みが変わった際の不整合もなんとなく原因が思い当たります。たぶん両方とも短時間で直せると思います。

実は今、アイドル状態がある一定時間続くと2画面の表示を2秒周期程度で交互に表示することを考えていました。何か操作するとこの状態に入る前の入力画面に戻ります。と言うような仕様です。タイマー2の割り込みを利用しようかと考えています。

ともかくまずご指摘いただいたバグをまず修正して、バージョンを上げます。
次の段階でアイドル時の交互表示機能を組み込みます。
貴殿のブログで紹介していただけるのは有難いです。紹介していただくのはどちらのタイミングでも結構です。よろしくお願いいたします。

了解です

ついでに差し出がましいですが、下記2点アドバイスというか私がやっていることを紹介しておきます。

1)Arduino IDEの設定で、「コンパイラの警告」を「全て」にしておくと、文法的におかしな点を指摘してくれるので、コードの品質が上がります。Webなどで公開する場合はこれやった方がいいです。と言っても私が始めたのは1年前くらいからですが、

2)Webで公開する時のプログラムのファイルはエンコードをShift-JISに変換しておくと、ブラウザでそのまま読める場合が多いので親切です。(IEやiPhoneのSafariとか)
ちなみに、Arduino IDEが作る noのファイルはUTF-8になっているので、ブラウザでそのまま見ると文字化けすることが多いです。もちろんダイウロードしてしかるべきツールで見ればいいのですが、面倒です。

アドバイス有難うございます

アドバイスにある通りに実行してみました。
Arduino IDEからはsignedとunsignedとの比較に対して警告が沢山でていますね(が、スケッチは修正していません)。

文字化けについては、IEでのアップロードの確認の際に私も困っておりました。お恥ずかしいのですがShift-JISへどうやって変換するのか知らず、またそれを真面目に調べたことがありませんでした。
四苦八苦して先ほどWordを使って初めて変換に成功しました。
早速サイトにもS-JISのものを上書きしたのですが、まだ更新されていないようです。
ともかく少し知恵が付きました。有難うございました。

v4r3の動作について

ご指摘いただいたバグは潰したつもりなのですが、実はまだ微妙な問題が残っております。
1ステップの増減分が変わる境界を上から横切る(つまり値を減少していく)場合、1ステップが大きいままで減算されるのです(正に横切るタイミングで一度だけですが)。これを修正するのは分量としては大きくはないですが、プログラムが煩雑になってしまうのでやりたくないというのが現時点での思いです。ここですっ飛ばした数値へは下の方からアプローチすれば到達できますから良いのではというのは甘い考えですかね?

v4r4に差し替えました

先ほどコメントした微妙な問題が起きないように修正しました。

試してみました

Simplified_PG_v5r1まで試してみました。

どのバージョンが良いかは使ってみないと判らない気がしました。
私が作ったやつもそうですが、ロータリーエンコーダーを廻した時に、どこの値が変わるかが判り難いので、カーソルを出して表示しておけば良かったと、今になって思っています。

コメント、有難うございます

こんばんは。
私は3つ共「リリース中」として載せるつもりでおりました。
使う人に選んでもらえればよいと思っています。
どこがどう違うかもう少し説明した方がよいと思いますが。
Cursor()、noCursor()は途中のバージョンで試した気がします。追加するのは簡単です(修正すべきスケッチは3つありますが)。

カーソル付きv5r2

Simplified_PG_v5r2をアップしました。
控えめですが、HMIとしては親切でしょう。

Simplified_PG_v4r6にします

今晩は、ラジオペンチです。
当方のブログに紹介記事を書かせていただいたので、ご報告します。
http://radiopench.blog96.fc2.com/blog-entry-807.html

ソフトはいろいろ試しました。
v3系は、表示と実際の波形がわずかに違う場合があるのでやめときます。画面サイズが大きければv3がいいです。

v5系は今どのモードになっているか勘違いし易いのでこれもパス。カーソルの有無を見ればいいのですが、

ということで私はv4系を使わしていただこうかと思います。いろいろ勝手な要望を書いたのに、すぐに反映していただいてありがとうございます。

バージョンはお好みでどうぞ

私はv5r2かなぁ。
交互表示モードに移行するまでの時間は自由に設定できますから、長くすればv4の動作に限りなく近づきます。
修正箇所は#define holdMenuT 600のところです(現状6秒です)。

まぁ、理想形は「16x4行のLCDを使って全部同時表示」かも知れません。LCDを買い直しになりますが。
加えて水晶を16MHzのTCXOに交換するのもよいかも。こちらはマルツで取り寄せてもらうことになりますが。

全くキリがありませんね。

ソフト更新のお知らせ

簡易パルスジェネレータのソフト、Simplified_PG_v4r6およびSimplified_PG_v5r2を更新しました。理由はLCD表示関係で1箇所バグがあったためです。このままでもどういうわけか実際の表示には影響はありませんでしたが、ともかく誤りは誤りですので更新しました。お手数ですが、ソフト編の記事から再度ダウンロードをお願いします。

re:バージョンはお好みでどうぞ

ラジオペンチです、お世話になっています。

20文字2行なら入る? 16文字4行なら1,2行目をまとめてビッグフォントにするとか。それと、20MHzクロックにして時間粒度を50nsにするとか、
まったく、キリが無いですね。

あと、ソフトバージョンアップ了解です。今改造作業中なので、あとでv4r7に書き換えたいと思います。

アトミック操作を

タイマー割り込みで使われるこの2つのint変数。
メイン側で操作する時は割り込み禁止状態で。
  「cli(); ~ sei();」
timeout // timer2で計るタイムアウト時間
t2ovfCnt // timer2 overflow回数カウンタ

Re: アトミック操作を

ご指摘の通りですね。
後ほどプログラムをチェックして修正しておきます。
有難うございました。

No title

3年以上前の記事についてのコメントでしたのでプログラムに何を書いたかさっぱり思い出せず、慌ててプログラムをチェックしてみたのですが、
①元々、Arduinoのシステムサイド(delay、toneなど)で利用していないTimer1をパルス発生用に使っている。
②Timer1に係わる割り込みは利用していない(TIMSK1はデフォルトのまま。割り込みルーチンも未記述)。

ということなので、とりあえずこのままで良いのではないかと思っています。

タイマー2です

タイマー2のオーバーフロー割り込みが有効になっていて、2つのデータをメイン側で操作されています。
t2ovfCntはゼロクリアだけですが、基本的に割り込みと輻輳する可能性のあるデータはアトミック操作ということで。
  URLに関連ページのアドレスを入れてます。

v5r3でした

一番新しい(?)2018/2/8のSimplified_PG_v5r3を見ていました。
2/6のと2/7のは大丈夫です。
表示切り替えのタイミングにタイマー割り込みでの時間管理が必要になったということなんでしょね。

Re: v5r3でした

居酒屋ガレージ店主様

大変お手数をおかけしました。
問題の部分を確認しました。
先ほどはプログラム「Simplified_PG_v5r3」をチェックしていなかったようです。
済みません。
修正しておきます。

1バイトのタイマーに

intで割り込みタイマーを構成すると、メイン側での割り込み禁止と再開の操作が必須です。
そこで・・・分解能が不要なタイマーは1バイトにしちゃってます。
10ms周期なら2.55秒、0.1秒周期なら、25.5秒が最大と、割り切ってタイマーを使い分けます。
多くの場合がダウンカウントタイマーで、タイムアップ=0を見て処理を始めます。
タイマーの設定は1バイト値の直書き込み。
タイムアップのチェックはzeroかnot zeroか。
これならアトミック操作は不要です。

Re: v5r3でした

居酒屋ガレージ店主様

プログラムを修正しておきました。
どうも有難うございました。

オーバーフロー割り込みでの周期設定

オーバーフロー割り込みでの周期(周波数)設定、まとめてみました。 (URLを)
「重箱の隅モード」ですが、ご一読いただければ。

Re: オーバーフロー割り込みでの周期設定

URLのリンク先の記事を拝見しました。
私は、3年前も含めてタイマ/クロックの動作については、このリンク先で解説されている通りに理解していたと思います。
「102」とした理由(不可解です)や、「102」としてしまうような誤りが他にも少なからずありそうだ、ということが大きな問題だと感じています。
ブログでプログラムを一般に公開しているのですから、努力して正しいものを置くようにする必要がありますね。もっと、しっかりチェックしなければ...。

プログラムの誤りを指摘していただいたことに感謝しています。


CK/1024の前3桁

「#define tcnt2base 102」のコメントに「102で約10ms(CK/1024)」と書かれていますんで、この「1024」を1/10した(前3桁)を、パッと書かれて、動かしたらそれなりの割り込み周期が出てきたのでOKとしたのでは・・・と、推測。

Re: CK/1024の前3桁

「時蕎麦」のお勘定シーンを連想しました。
老人力は増すばかりでしょうから、気を付けなければ...。

パルスジェネレータ作ってみました

パルスジェネレータ、作ってみました。
http://igarage.cocolog-nifty.com/blog/2022/08/post-ab8b48.html

Re: パルスジェネレータ作ってみました

色々と改良されたという記事を拝見しました。引用していただいて有難うございます。

この記事のスケッチ中のロータリエンコーダを扱う部分はラジオペンチさんのオリジナルから全く変更していません。
私の方もロータリエンコーダの読みの誤動作が気になってその後の製作では新たに作成したコードを使用しております。
コンセプトとしては、「ミスカウントは許容する」が、「誤カウントはダメ、ダメ」、です。
以前は、一定方向に回し続けているのに突然数値が変化する方向が逆転したり、いきなりとんでもない値が入力されたりすることがありましたが、それは解消されていると思います。

この記事の「簡易パルスジェネレータ」については、ハードが消滅してしまっていて動作確認ができない、ということでロータリエンコーダを扱う部分は更新していません(例の「アトミック処理」は入れましたが)。
しかし、ブログ上のその他の記事には新しいコードを使って更新したスケッチがいくつかあります。ご参考までに挙げておきます。

1.メインからコールする関数として
記事「高精度パルスジェネレータの製作(その5:ハードウェアDDSの組み込み)」
スケッチ「High-Precision_PG_wDDS4」
https://blog-imgs-153.fc2.com/v/a/b/vabenecosi/High-Precision_PG_wDDS4.txt

2.割り込みとして
記事「ArduinoのIRリモコン用ライブラリ(IRremote)を徹底的に試してみる」
スケッチ「IR_remote_tester7」
https://blog-imgs-153.fc2.com/v/a/b/vabenecosi/IR_remote_tester7.txt
このスケッチでは、キーを押すことで上位桁を増減するルーチンも入れています。何しろ最大でHEX8桁の数値を入力する場面もありますので、必然です(ちょっと動作が変なところもありますが、放置してます)。

コメントの投稿

非公開コメント

プロフィール

vabenecosi

Author:vabenecosi
ン十年間アキバに通い続けるオジサンです。自転車&バイク=二輪で旅するのも好きです。
本ブログはリンクフリーです。
本ブログの記事の内容を元にした製作等の行為は全て自己責任で行ってください。
またコメントを頂いてもスパム対策、あるいは管理者の判断により返信しない場合や削除する場合がありますが、ご了承ください。
旅関係の記事を書くこともあるでしょうが、専ら「アルバム」にて旅の写真のみを提供するつもりです。「アルバム」はPC画面では右下方に、スマホ画面では左上のメニューボタンの中にリンクがあります。

アルバム

FC2カウンター