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
最近のコメント