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はちゃんと「言うことを聞いてくれます」
ので。
試した回路、はこんな様子。
下の銀色のがモーター。
右のユニバーサル基板が駆動回路。
モーターとは3本の電線がつながります。
タイマー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相制御出力。
・スケッチ
・ダウンロード - 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
| 固定リンク
「電子回路工作」カテゴリの記事
- LEDドライバー、どうしよう(2023.09.04)
- 人感センサー用オフディレータイマー回路(2023.08.26)
- 『きもだめし』用の人感センサー(2023.08.25)
- 「御詠歌プレーヤー」の製作 (MP3-TF-16Pモジュールの使用例)(2023.08.10)
- 謎の回路・・・これでもスルーホールになっている(2023.07.28)
「Arduino」カテゴリの記事
- 「御詠歌プレーヤー」の製作 (MP3-TF-16Pモジュールの使用例)(2023.08.10)
- Arduino UNO R3のソケット・・思えば違和感がぁ(2023.07.07)
- 初めて買ったArduino UNO・・・今は(2023.05.25)
- 液晶表示コントローラ HD44780で迎撃(2023.05.16)
- Arduino UNOで3相モーターを回す(2023.05.01)
「BSch3V」カテゴリの記事
- Arduino UNOで3相モーターを回す(2023.05.01)
- ラズピコのピン:自由になりそうだけど定義で固定されている(2023.04.03)
- BSch3Vのパーツライブラリ:MOTOR_3COIL(2022.12.28)
- BSch3V CE3ファイルからコメント文字をピックアップ(2022.11.09)
- よく使うパラレル接続液晶(LCDモジュール)のコネクタ信号(2022.09.04)
コメント
この記事のスケッチを当方のNANOに書いて、問題無く動くことを確認しました。
うちのスケッチで懸念事項だったのが全部解決されていて気持ち良いです。なんだか添削指導していただいたみたいで、ありがとうございます。
投稿: ラジオペンチ | 2023年5月 2日 (火) 09時26分
PB0,1,2にテストパルスを出してます。
これをオシロで見ると、割り込み処理(0.1msタイマーとA-D変換処理)と、PWM値計算の処理時間を確認することができます。
PB2に出てくるPWM処理の時間、11~12usというところでしょうか。
analogRead()するとそれだけで100usほど待たされます。
A-Dが1chだけ(MPX固定)なら「連続変換動作」に。
複数の入力ならタイマーと絡めて変換を開始させ、変換終了割込でデータを取得。
値の平均化も割り込みの中でというのがメイン処理のじゃまになりません。
投稿: 居酒屋ガレージ店主(JH3DBO) | 2023年5月 2日 (火) 09時54分
3相のPWM出力、100us割り込み内で処理してみました。
投稿: 居酒屋ガレージ店主(JH3DBO) | 2023年5月 5日 (金) 11時39分
3ph_mot2試してみました。最高速がえらく上がって良い感じで動いてます。
ここまで制御出来ると、用途によってはパルスモーターより優れているかもしれないですね。
投稿: ラジオペンチ | 2023年5月 6日 (土) 09時41分
速度を決めているVR2の値(A/D入力で8bit値)、これをプログラムでup/downすれば速度を可変することができます。
PWM出力は割り込み内で処理しているので、メイン側ではVR2の値をいじるだけ。
適当にタイマーを作って周期的にup/downすれば加減速操作。
0~359度の値、今は+1だけですが-1すれば逆転操作も。
投稿: 居酒屋ガレージ店主(JH3DBO) | 2023年5月 8日 (月) 10時31分