Arduino

2024年2月13日 (火)

トラ技のArduino Uno R4特集でも1/1023

Arduino Uno R4を知っておこうと
トランジスタ技術2024年1月号
パラパラめくりしていたら・・・
1/1023」を発見。

場所はp.106。
R42
記事のタイトルは
  Uno R4低消費電力の実力!
   家庭菜園用バッテリ動作2ch温度ロガー

その中の「R3からR4への乗り換え時の注意!
という章。
P.107の【図5】にはこんなものさしの絵も。
R43
1023と10進じゃなく0x03FFと16進表記にして
半値やら1/4値、3/4値を入れ込むと目盛がはっき
りするかと。

こちらでの「ものさし」表記。
Ss12

・1cmを1mm分解能で
Ss2_20221126092001
※こんな解説かな
・絵の縮尺の関係で、1cm位置が実際は5.0mやった。
・mmでの測定値a (0~9の範囲)から実際の長さを
 求める式は・・・
   a × (5.0 ÷ 10) が答え (単位はm)
          [ ÷9ではない ]

※参照
1023 vs 1024 、255 vs 256 なんでみなさん「2^n-1」が好きなの?

| | コメント (0)

2024年2月10日 (土)

delayMicroseconds(50000)をオシロで確認

トランジスタ技術2024年3月号に掲載してもらった
  ・『実はワナだらけ…確実に動かすArduino Uno R3』
この記事の中の『時間待ちのワナ』、これの実際の様子を
オシロ波形で見てもらいましょう。

Dm11_20240210085601

ch1がオシロトリガ用の波形。
  1サイクル回るごとにPB4をトグルしています。
ch2がdelayMicroseconds時間確認のための波形。
  1msのH/L
  50msのH/L  ←のつもり
を出力して回っています。
しかし実際は・・・
  delayMicroseconds(50000)が0.85msほどに
  なっています。

こんなスケッチを使って見ています。

#define PB5_H       (PORTB |=  (1 << PB5))  // LED on
#define PB5_L (PORTB &= ~(1 << PB5)) // LED off
#define PB4_X (PINB |= (1 << PB4)) // PB4トグル

void setup() {
pinMode(13, OUTPUT); // PB5 基板上LED
pinMode(12, OUTPUT); // PB4 トグル出力(オシロのトリガ用)
}

void loop() {
cli(); // 割り込み禁止でloopを回る
while(1){
PB4_X; // PB4トグル
PB5_H;
delayMicroseconds(1000); // 1ms H
PB5_L;
delayMicroseconds(1000); // 1ms L
PB5_H;
delayMicroseconds(50000); // 50ms(???)
PB5_L;
delayMicroseconds(50000); // 実際は0.85msほど
}
}


※元記事/関連記事
Arduino-UNOでのdelayMicrosecondsの設定は16383までだ!
Arduinoから「タイマー0」を取り上げる(ユーザーが使う)

| | コメント (1)

2024年1月24日 (水)

ダイソー SHOOTING LIGHTLED:撮影用ライト LEDの輝度変化を探る

2024年1月18日:ダイソー SHOOTING LIGHT:撮影用ライト 9SMD
2024年1月20日:ダイソー SHOOTING LIGHT:撮影用ライト PAM2803で駆動
この続き。
9パラされたLED、端っこの一つを外して、30mAで定電流駆動。
照度センサーで劣化具合(輝度変化)を探ってみます。
使ったのは ダイソーのLED電球 実験回路
これにLED駆動用の定電流回路を追加して輝度変化をログします。
こんな回路。
Test3
照度センサーはロームのBH1603。
照度が電流で出力されます。
抵抗で受けてA/Dすればルクス値が読み取れるという仕掛け。

ダイソーのライトから端っこのLEDを外し、別基板にハンダ。
塩ビのエンドキャップの底にくっつけます。
Dl51
Dl52
Dl53
LEDは定電流回路で駆動。
30mAに設定しました。
Dl56

センサーもエンドキャップの底に貼り付け。
Dl54

LEDとセンサを付けたキャップを塩ビパイプの両端にはめ込み。
これで外からの光は無視できると。
Dl55

1日1回、A/D値をログしてLEDの明るさ変化を記録します。
Dl57
液晶の左下の数字「742」がセンサーからの10bit A/D値。

・制御スケッチ (.inoを.txtにしています)
    ダウンロード - led_lamp_test1.txt

LEDの劣化・・・まとめ

| | コメント (0)

2023年12月 7日 (木)

Arduinoで「ボコスカハンマー」 あれれれれっ?!

ボコスカハンマー」を作ってと依頼がありました。
   ・10秒ゲーム
ベースはArduino UNO R3ですが、ブートローダーを
書いたATmega328Pチップ(28pin DIP)をユニバーサル基板
に乗っけて製作します。
配線を終えて、チップにスケッチを書いたら・・・
ありゃま。 テレビに出る画像に異常がぁ!

このタイトル画面が
Bk21
これ↓に
Bk11

スタート待機画面が
Bk22
これに
Bk12

現在のところ、原因不明。
現用しているビコピコやシャカシャカからチップ抜いて、
今回作った手組み回路に突っ込んだらちゃんと動くので、
配線や使った部品に起因するものではありません。

原因の推定
 ・Arduinoのコンパイル環境が変わって最適化などの
  状態が前と変わってしまったのか。
 ・今回使ったATmega328P、その内部が前のと
  違うのか。(まさか)
     ※パチモンの可能性

ジャギジャギになっている表示そのものは安定して
いるのです。
チラチラはしてません。
Arduinoの環境を昔のに戻すのはちょっとかなわんし。

sleepさせてH-SYNCサイクルでの割り込みを待って
表示という処理を流しています。
チラチラしないということは、どこかで安定した
時間待ちが出ているもよう。

はてさて・・・困ったぞ。

 

※わかったぞ!
 やっぱりコンパイル環境による最適化の違いでした。

水平走査、1ラインを描画出力しているのはこんな処理です。

void v1line(void)
{
byte i;
uint32_t d; // 32bitデータにコピー
d = sft_bff.sft; // シフトデータ
for(i = 0; i < 32; i++){ // 32bit
if(d & 0x80000000){ // MSB 0/1 ?
VSIG_H; // H出力
nop(); nop(); nop(); // wait
nop(); nop(); nop(); ★1
nop();
}
else{
VSIG_L; // L出力
nop(); nop(); nop(); // wait
nop(); nop(); nop(); ★2
nop();
nop();
}
d <<= 1; // 左シフト ★3
}
nop(); nop(); nop(); nop(); // wait
VSIG_L; // L出力で終わる
}

画面を光らせるのはVSIG_H
消すのはVSIG_L
SBI、CBI命令でポートを直接叩いています。
そして、タイミングの調整に「nop」命令を並べています。

H出力の時【★1】はnopを7個実行して、32bit描画データの
左シフト処理【★3】に飛びます。

そしてそして・・・
コンパイラの最適化がすごいのがL出力の時。
VSIG_Lの実行後、その下に記してあるnop命令8個【★2】
は、数を合わせればVSIG_Hのところにあるnopを使ったら
エエやんということで、VSIG_H直後のnopの場所に飛んで
いるのです。
   ※この飛ぶためのjmp命令の分、L処理時間が
    延びてしまった。
【★2】のnop命令は1つだけを残して実行プログラムの中から
抜いてしまっていて、存在していませんでした。
それでもシフト処理に来ますんで、流れは間違っていません。

絵がおかしくなってしまったのは、nopの数合わせはした
けど、描画のタイミングが白黒でズレてしまったというのが
原因でした。

※バックアップがわりにスケッチと回路図
  ・ダウンロード - piko_boko1c.zip

 

| | コメント (0)

2023年10月14日 (土)

Arduino UNO R3で±19.9V表示電圧計

アナログ回路の実験・調整用に-16V~+16Vの
範囲をポテンショで調整して出力できる回路を
作ってあります。
最大±15Vを入力する回路の動作確認、調整用です。
DC-DCコンバータで±18Vの電源を作って、オペアンプ
を駆動。
その入力電圧を可変して±16V(+ちょっとオーバー)を
出力するような仕掛けになっています。
0Vをまたいで電圧を連続してプラマイできるわけです。

設定電圧の確認はデジタルテスターをつないでという
ふうにしていましたが、「表示器があるほうが便利」
だろうということで、Arduino-UNO R3を使った電圧計を
くっつけてみました。

10bit ADCのAtmega328がベースですので表示は±19.9V
2・1/2桁です。

Ss1_20231014100401
ちゃんとした電圧を見たい時はこれまでと同じように
テスターをつないでということで運用します。
Ss2_20231014100401
±16V発生回路。
Ss4
±9V出力のDC-DCコンバータを2つ使って±18V電源
を得ています。

これに電圧値表示回路をドッキング。
Ss3_20231014100601
電源は外からの5V。

さて、プラマイ電圧の入力回路をどうするか。
Arduino UNOのADC、その入力電圧はプラスだけですが、
 ・2022年11月28日:単電源で反転アンプ マイナスの入力電圧は増幅できます
 ・2015年04月22日:A/Dコンバータの入力レンジを拡大する方法
こんな考え方で、単電源の回路でも±電圧を入力すること
ができます。
以下の図はVrefが2.5VのADCの入力レンジを拡大する方法を
示しています。
Img20150422170532335

この中の、(d)±5V入力 (e)±10V入力の考え方は、
 ・Vin / Vrefの比、基準電圧の何倍の電圧を入力するか
  から、R1とR2の比を決定。 (Vinは正負片側の電圧値で)
 ・Vref2.5Vで10倍の±25Vなら、R1とR2は9:1。
 ・Vin=0Vの時のA/D値を半値にするため、R1とR2の
  並列抵抗値をR3に。
 ・10倍ならR1:R2:R3は90:10:9の比率。

これが抵抗3本で、0~2.5V入力のADCに対して±電圧を
入力する一手法となります。

もう一つの方法が差動アンプです。
S11_20231014101201

(+IN - -IN) × GAIN
として出力が得られるので、
GAINを1/8 (減衰)にして、+INにオフセットとして10Vを
加えておくと、±10Vの範囲を0~2.5Vに変換できます。
  ※ただし、増加減の方向が逆に

Vref電圧(2.5V)を基準に描くと、こんな具合になります。
S12_20231014101401

反転入力がVinで±電圧入力。
非反転入力がオフセット電圧の設定。
Vin=0Vの時のA/D値を、Vrefの半値にします。
非反転入力のオフセット電圧が+になるので、
アンプは単電源で動かせます。

R1とR2で入力電圧の倍率を設定。
通常の差動アンプですとR1=R3、R2=R4にしますが、
±電圧入力回路に限ればこのオフセット電圧は別の
抵抗値で固定してかまいません。

それを考えて描き直すとこのようになります。
S13_20231014101501
R3とR4の比で固定的なオフセット電圧を得ます。

ATmega328の内蔵基準電圧を外に取り出して
使いますのでVref = 1.1V
こんな回路になりました。
Sa1
チップを抜いたArduino UNO R3基板とつないで
スケッチをアップロードします。

Sa2
キャリブレーション(校正)操作は通信で。
  -15.0V、0.0V、+15.0V
この3点のA/D値を記憶(内蔵EEPROMに保存)します。
実電圧とその時のA/D値を使いますので、抵抗の誤差や
Vref値の誤差は校正操作で吸収されてしまいます。

0.0Vを校正点に入れて3点にしたたのは、
  ゼロはゼロと出て欲しいから。
最小値と最大値での2点校正では、ひょっとするとゼロにならない
かもという、リニアリティ特性の懸念があるので。

A/D値から電圧値への計算はあのmap関数を使います。
  ※map関数はこう使って欲しいゾの例です。

制御スケッチ
   ・ダウンロード - pm20volt1.txt

※.inoではなくファイルタイプを.txtにしています。
 UTF8nのテキストファイルです。

7seg LEDをダイナミックスキャンして電流値を表示し
ているこれらのサンプルも参考にしてください。
2022年11月29日:Arduino UNO(のチップ)を使ったUSB電流計
2022年12月 2日:Arduino UNOを使ったUSB電流計 箱に入れて完成
2022年12月14日:Arduino UNOを使ったUSB電流計 4桁表示も

キャリブレーションの方法はこれらとスケッチを参照してください。

※入力レンジに切り替えについてはこれも
2014年02月01日:入力レンジ切り替え回路

※例のmap関数
2020年5月16日:Arduino 10bit A/D値をmap関数でスケーリングする例
2020年5月17日:Arduino なんとかして誤用を正したい:A/Dの1/1023とmap関数
2022年11月6日:Arduino map関数をfloatに

※点滅表示
A&D社の「AD-8724D」という電源、「0~30V・2.5A出力」の
シリーズレギュレータです。
これの電圧表示も「xx.xV」と3桁。

今回の回路でのマイナス側の表示、3桁目が
「-」だけかあるいは「-1」になります。
このためマイナス側の最大(最小というべきか)を
-19.9V」と制限(ソフト的に)しています。

プラス側は「19.9」ではなく「99.9」を
リミットにしてますんで、分圧抵抗を変えた場合でも
-19.9~99.9」の範囲なら表示は可能です。

そして、マイナス、プラスともリミットを越えた時は
表示を点滅するようにしています。
そしてもう一つ点滅する条件がA/D値の範囲。
電圧値に変換する前の値として有効範囲を
チェックしています。
10bitですので0~1023が出てくる値になります。
しかし、5以下と1018以上は、リニアリティが疑問とい
うことで、表示を点滅するようにしてあります。

※Aref端子から内部Vrefを引き出す
2013年04月25日:AVRマイコンのAREFピン
2013年05月02日:AVRマイコンのAREFピン #2
2022年10月05日:サーミスタ103JTで計った温度をシリアル出力

| | コメント (0)

2023年8月10日 (木)

「御詠歌プレーヤー」の製作 (MP3-TF-16Pモジュールの使用例)

お盆の精霊流し、昔はご近所の橋の上からすべり板で
川に浮かべた船に乗せていました。
ちょっと前までは、橋の上でご供物を集めていたのですが、
橋の上だと雨になるとたいへんだということで、現在は
地域の会館で行っています。
  ・御幸橋の上でしていた精霊流し
  ・2016年の様子
その時のBGMが「四国八十八箇所霊場の御詠歌」。
最初はカセットテープで。
それからMP3プレーヤに。 (アンプは別で)
そのMP3プレーヤー、文鎮の佐藤テック君が持っている
のを借りていたのですが、MP3プレーヤのモジュールを
買ったので、連続リピート再生するのを作ってみました。
MP3-TF-16P  という型番。
DFPlayer mini がオリジナルなのでしょうか。
秋月でも扱っています(M-12544)

説明書を見ますとADKEY1端子を51kΩの抵抗
オンしたら連続再生(Loop All、All cycle)になる
と書かれています。
ただし、51kをGNDにつなぎっぱなしでの電源オン
には反応無し。
オープンで電源をオンしてから51kをGNDにつなぐ
操作をしなくちゃなりません。
電源オンで遅延ワンショットかと思ったのですが、
お手軽にプッシュスイッチを使うことにしました。

こんな回路。
Go11
電池を3本使います。
モジュールに直結。
Low Bat警報の出る2.7Vまで下がってもまだ
動いていました。

左右のオーディオ出力に0.5Vほどの直流が
乗っていたので、C2とC3でカットしてから
モノラルに合成しています。

ダイソーで買った樹脂ケースに入れ込みました。
G012
G013
Go14
MP3モジュールの拡大。
Go17
Go18

SDカードに入れた曲(御詠歌)を順に再生する
だけの機能です。
四国霊場1番札所、霊山寺(りょうぜんじ)から
88番札所、大窪寺(おおくぼじ)まで、それぞれの
札所の御詠歌がえんえんと流れます。
ただそれだけの機能です。

再生の順序はファイル名に関係なくて、SDカード
に書き込んだ順番になります。
今回のように「何番札所」という曲順が重要なときは、
PCでのコピー操作、一括じゃなく、めんどうでも
ファイルを一つずつコピーしなくちゃなりません。
  ※一つのフォルダにどれだけのファイルを
   入れられるとか、フォルダによる
   再生順序はどうなるかまでは不明っす。

※昔、別のモジュールで自動再生を試したことがあります。
  ・2016年06月16日 御詠歌を自動再生
この時は、モジュール単独での自動リピート機能がどうも
うまく行かずで断念しました。
   リピート再生が途中で中断
   曲が変わらない
   外部からの制御無しでの運転はなんか
   おかしい

その後、このモジュール、Arduino UNOを使った
BGMランダム再生装置で使っています。
  ファイルを拾って乱数化して再生開始。
  全曲再生完了後、異なった順番にして再リピート。
  この乱数化するとき、直前の曲番をチェックして
  重ならないよう。 (直前の20曲が出ないよう)
F11_20230810172701
制御はArduino。
F12_20230810172801

制御スケッチ。
  ・ダウンロード - mp3_mk138.txt
    「.ino」じゃなく.txtファイルに

再生するファイルの数はArduino側で設定。
   (モジュール側のTXDは使っていません)

| | コメント (2)

2023年7月 7日 (金)

Arduino UNO R3のソケット・・思えば違和感がぁ

Arduinoの初体験 が2012年12月。

この時からの違和感・・・
  基板での信号接続が「ソケット」で「メス」。

手組みした基板も含めて、基板側にハンダするのは
2列のピンヘッダだったりXH(JST)コネクタだった
りと・・・「オス」のピン。
  ところがArduino UNOだとソケット!。

ブレッドボードに使う「ジャンパ線を刺せる」という
のが大きな理由だと「納得」したいんですが、ほんとは
「これキライや」のトップになるかと。

| | コメント (0)

2023年5月25日 (木)

初めて買ったArduino UNO・・・今は

Arduino初体験 が2012年12月。 (10年以上前だわ)

その基板の現在がこれ。
 ・どこかから拾ったプラケースをベースに。
 ・16MHzセラミック発振子を水晶発振子に交換。
 ・ブートローダ書き込み済みのATmega328Pと
  通信(スケッチアップロード)するための
  ケーブルを付加。
 ・乗っているチップは何度も交換。

Aa21_20230525110601

ちょっとした実験や試運転で便利なのが
アナログ入力ポートにつなぐ4つのボリューム。
スイッチも2つ付けてます。
こんな回路です。
Aa1_20230525110701

ボリュームのスライダーに0.1uFのコンデンサを
入れておくと安心。

発端はこんな3つのボリューム。
Aa22_20230525110801

+5VとGNDを取れるようにピンヘッダをハンダして
います。

ケチって片面のユニバーサル基板なんで、配線は
ちょっと面倒。

AnalogReadで読んだA/D値(0~1023)を使って
Delay値を設定したり、動作パラメータを変えたりと
リアルタイムでできますんで、なかなか便利です。

使ったボリュームは秋月電子通商のつまみ付半固定抵抗
  (足の1・2・3の順に注意:刻印あり)
  似てるけど3386K-EY5-103TRじゃない!


※応用のためのスケルトン・スケッチ
4つのボリュームと2つのスイッチを入力するための
応用基本スケッチを示しておきます。
 ・ボリューム値の入力は1ms割り込みで処理。
 ・64回平均。
 ・4つあるので256msごとにデータが確定。
 ・勝手にスキャンするのでAnalogReadのように
  100us待たされるということがない。
 ・割込禁止にしなくてもいつでも読めるよう
  VR値は0~255の8bitで。
    ad_avr[ch]を読めばok。

起動すると256msごとに値をシリアル出力します。
  A/D 8bit:VR1,2,3,4 SW1,2
  105 178 123 249 0 0
  105 178 123 255 0 0
   :
  0 64 128 255 0 0
  0 64 128 255 0 0
  0 64 128 255 1 0 SWはオンで1
  0 64 128 255 1 1
  0 64 128 255 0 1
  0 64 128 255 0 0

ダウンロード -  ad_vr4.txt
   .inoではなくUTF8Nのテキストです。


※タイマー割り込みとADC変換完了割り込み

ソースを見てもらえれば、その手順がわかるかと。
さまざまなライブラリ、確かに便利です。
でも、Arduino UNOのATmega328Pマイコン
あたりなら、レジスタの直接操作はそんなに
難しくはありません。
マイコンに備わっているさまざまな機能を引き
出すには、データーシートをにらみながらの
プログラミングをしなければなりません。

| | コメント (0)

2023年5月16日 (火)

液晶表示コントローラ HD44780で迎撃

オリジナルデバイス:なんぎな日記(2023年5月10日)
に、HD44780が乗った液晶表示モジュールが出ていたので、
こちらからも迎撃してみます。

2種類のモジュールを発掘できました。

50_20230516102701
上側、東芝のが1987年4月製のようです。
50a

HD44780、このサフィックスで内蔵しているキャラジェネ
のデータが変わります。
手持ちのHD44780データシート(1997年8月)、これで
示されていたのは「A00 A01 A02」の3種類。

・A00
51_20230516102701
・A01
52_20230516102701  
・A02
53_20230516102701

「A01」と「A02」はずいぶん賑やかに見えます。

CG RAMエリアの8文字、コード0x00~0x07と0x08~0x0F
は同じものがは表示されます。
C言語では、0x00はnullターミネートされる文字列で
使われます。
ですので、0x08~0x0FをCG RAMの文字コードに割り当て
たいところなんですが、「BS」や「HT」「LF」「CR」の
制御コードと被ってしまいますんで、CG RAMを使う時は
ちょっと気遣いが必要です。


※関連
液晶表示モジュールを4ビットモードで使ったときの空きピン処理

※Arduinoに絡めて
Arduinoでの液晶制御、一般には「LiquidCrystal」ライブラリを
使います。
8ビットモードだと、
  LiquidCrystal(rs, E, d0~d7)   :10bit
  LiquidCrystal(rs, rw, E, d0~d7) :11bit
4ビットモードだと
  LiquidCrystal(rs, E, d0~d3)   :6bit
  LiquidCrystal(rs, rw, E, d0~d3) :7bit
と、いちばん接続線数が少ない接続は
「4ビットモード」で「rs」と「E」を使った場合の
6本になります。

もう1ビット増やして「rw」を指定したら、
  「busyチェックしてくれるので早くなるかも
は、期待できません。
LiquidCrystalライブラリはbusyチェックは完全に無視。
delayMicroseconds()」を使った時間待ちでタイミングを
作っています。

さて。。。この時間待ち。
LiquidCrystalライブラリのソース「LiquidCrystal.cpp」を
読みますと、初期化(begin)のところで、
  delayMicroseconds(50000);
と「50ms待ち」をしているところがあります。

しかし、delayMicrosecondsにはこんな制限が・・・
Arduino-UNOでのdelayMicrosecondsの設定は16383までだ!

液晶初期化のdelayMicroseconds(50000)、
 ・ほんとにこれでエエの?
 ・動いてるからエエんとちゃう。
っということしか言えません。

| | コメント (0)

2023年5月 1日 (月)

Arduino UNOで3相モーターを回す

ラジオペンチblog:DVDのスピンドルモーターを低速回転させる-続編
に倣って、ジャンク箱から発掘した小さな3相モーターを
回してみました。

ラジオペンチさんの記事を見て、
 「光学ドライブかHDDか何からだったかは忘れた
  けど、小さな3相モーターがあったはず」
っと、ジャンク箱を漁りました。
発見できたので、真似っこしてみました。

仕事では、RX220マイコンと L6234 という
3相モータードライブICを使って「回した」こと
があります。
RX220には3相駆動用のPWM出力が付いてい
ますが、Arduino UNOのATmega328Pは
別個のタイマーユニットを操作しなくてはなりません。

3つあるタイマー、そのうちタイマー0がdelay()などの
タイマー処理で使われています。
それを・・・Arduinoから「タイマー0」を取り上げる(ユーザーが使う)
の手法で「我がモノ」にします。

初期化(setup)から、もう、Arduinoの環境からは外れて
しまいます・・・
でも、ATmega328Pはちゃんと「言うことを聞いてくれます」
ので。

試した回路、はこんな様子。
M11_20230501175601
下の銀色のがモーター。
右のユニバーサル基板が駆動回路。
モーターとは3本の電線がつながります。
2ph1
タイマー0のOC0AとOC0B出力。
そしてタイマー2のOC2B出力で、3つのPWM出力を
制御します。
  ※N-ch、P-ch MOS FETの記号は
   簡略化して記述。
  ・パワーMOSFETの回路記号:MOSFETの矢印
  ※ゲートのプルダウン抵抗はゲートの浮き防止。
   浮くと貫通電流が流れるかも。
  ※3相モータの回路記号↓ (BSch3V用)
     bsch3v_lib_230502.zip
   6本線を3本線にしたのはSHAPES.LIBの中に。

オシロ波形、緑・青・赤の3つがこの3相制御出力。
M12_20230501175601

・スケッチ
  ・ダウンロード - 3ph_mot1a.txt

  ※.inoではなく.txtにしています。
  ※ちょいと虫を発見したんで
    mot1→mot1aに入れ換え

タイマー0とタイマー2は似たような8bitタイマー。
  ※タイマー1は16bit
それを「同期」させて3相の制御パルスを得ます。
二つを「8bit位相基準PWM」に初期化して、同期したPWM制御
ができるようにします。

スケッチをピックアップすると・・・

//  タイマー0,2  PWM出力 OC0A,OC0B,OC2B
TIMSK0 = 0b00000000; // 割込禁止
TIMSK2 = 0b00000000;
// ||+--- TOIE
// |+---- OCIEA
// +----- OCIEB
GTCCR = 0b10000011; // ★1
// | |+--- PSRSYNC タイマ0,1前置分周器リセット
// | +---- PSRACY タイマ2前置分周器リセット
// +---------- TSM 同期処理
TCNT0 = 0; // タイマーカウント値クリア
TCNT2 = 0;
OCR0A = 128; // U相 中央値
OCR0B = 128; // V相 (-1せず)
OCR2B = 128; // W相 (H=128:L=127)
TCCR0A = 0b10100001;
// |||| ++--- 8bit位相基準PWM
// ||++------- OCR0B 非反転PWM
// ++--------- OCR0A 非反転PWM
TCCR0B = 0b00000010;
// || |+++--- CS 1/8 2MHz →3.92kHz
// || +------ WGM02
// ++--------- FOC0
TCCR2A = 0b00100001;
// |||| ++--- 8bit位相基準PWM
// ||++------- OCR2B 非反転PWM
// ++--------- OCR2A Portで
TCCR2B = 0b00000010;
// || |+++--- CS 1/8 2MHz
// || +------ WGM22
// ++--------- FOC2
GTCCR = 0b00000000; // ★2
// +---------- TSM=0で計数開始
  :
★1でタイマー0とタイマー2の前置分周回路を停止。
★2で計数再開。

★1でクロックの供給が止まり、その間に二つのタイマーを
「8bit位相基準PWM」に初期化しています。
   ※タイマー1は16bitなので
    ちょっと、この二つとは
    違います。これをタイマー
    割り込みに使います。
PWMの周波数は、
  16MHz/8 = 2MHz
  1/255  1/2 → 3.92kHz
になります。

★2の計数再開でタイマー0とタイマー2の「頭」が
そろいます。

もう一つ、高速化のための手法。
三角関数データをROM内のテーブルに配置します。
「gawk」を使って配列を作り、それをPROGMEMで
ROMに配置します。
角度0~359度から「±127」の「8bitの数値=sin値」が
得られるようにしておきます。
  ※PWMの設定値が8bitなので
これで浮動小数点演算→三角関数の演算を使わなくて
良いようになります。

下表のように角度0~359度からテーブル読み出しで
8bitの「sin値」が出てきます。

/*****  SIN 0~359      *****/
// 振幅±127, 中央値は0
const int8_t sin_tbl[] PROGMEM ={
0, 2, 4, 6, 8, 11, 13, 15, 17, 19, // 0
22, 24, 26, 28, 30, 32, 35, 37, 39, 41, // 10
    :
  125, 125, 125, 126, 126, 126, 126, 126, 126, 126, // 80
127, 126, 126, 126, 126, 126, 126, 126, 125, 125, // 90
125, 124, 124, 123, 123, 122, 122, 121, 120, 120, // 100
    :
-22, -19, -17, -15, -13, -11, -8, -6, -4, -2, // 350
};

ツールは「gawk」。
  文字を処理するなら、コレ!。

駆動周期でPWM値を更新します。

    pwm_3ph[0] = degpwm(rot_deg,       ad_vr[0]); // U相
pwm_3ph[1] = degpwm(rot_deg + 120, ad_vr[0]); // V相
pwm_3ph[2] = degpwm(rot_deg + 240, ad_vr[0]); // W相
cli(); // 割り込み禁止で
OCR0A = pwm_3ph[0]; // U相 PWM duty設定
OCR0B = pwm_3ph[1]; // V相 (-1せず)
OCR2B = pwm_3ph[2]; // W相
sei(); // 割り込み有効に戻す
rot_deg++; // 0~359度
if(rot_deg >= 360) rot_deg = 0;

120度位相差の3つの信号からPWM設定値を得て、
それをタイマー0、2の「OCR」レジスタに書き込んで
sin波に合致したデューティ比のパルスを得ます。
ad_vr[0]は、「PWM振幅調整用」の8bitデータです。

正弦波データテーブルを読んでいる処理がこれ。

/****  角度からPWMデータに変換   *****/
// 角度 0~359 → 8bit PWMデータ(0~255)
// VR1値(8bit)でPWMの振幅を設定
// 中央値は128
byte degpwm(short deg, short vr1)
{
short a, g;
byte d;
while(deg < 0){ // マイナスならdeg+360
deg = deg + 360; // プラスに
}
// deg = deg % 360; // 0~359の範囲に(割り算やめて)
while(deg >= 360){ // 360を越えている場合はdeg-360
deg = deg - 360;
}
// SIN値に変換
a = (short)(int8_t)pgm_read_byte(&sin_tbl[deg]); // 0~359度→sin
g = (a * vr1) / 256; // VR1 : PWM振幅調整 ±127最大
d = (byte)(g + 128); // 8bitの中央値
return d;
}

g = (a * vr1) / 256; のところ
ぎりぎりで符号付16bit乗算が可能。
  aは±127でvr1は0~255。
aとgをlongにして乗算するとずいぶん遅くなります
んで、shortの範囲にしておきたかった。


今回の実験(お休み中の腕試し)はここまで。

※面白がって追試
3相PWM出力の処理時間が11~12usだったので、
「これなら100us割り込み内で処理できるぞ」
っと、タイマー割り込み内でPWM出力を制御する
ように書き直してみました。
お試し下さい。
  ・ダウンロード - 3ph_mot2.txt
    ※.inoではなく.txtにしています。

VR2を絞って最高速度にすると
360度の経過が1秒間に27~28回。
  1度の変化が0.1ms。
  0~359度で 36ms。
  1秒 ÷ 36ms = 27.78


| | コメント (5)

より以前の記事一覧