回路.pdf       Tmain.c      Tsub.c              Top page


AVR ステッピングモーターの連動制御



AVRを使い、ステッピングモーターを2個連動させて制御するシステムです。
ステッピングモーターを高速で制御する場合、8ビットタイマーでは時間軸の分解能が不足するため、16ビットタイマーを割込みで動作させる必要がでてきます。また、ステッピングモーターは、加速度値が一定になるような制御法が求められるため、加減速はタイマー時定数をステップ毎に変更することで速度をスムースに変えてゆく必要があるのですが、ひとつの16ビットタイマーで、別のステッピングモーターを独立して制御することは、ごく低速以外は事実上不可能です。

このページでは2個のAVRを連動制御することで2個のステッピングモーターを独立制御する方法を取り上げます。
なお、この方式は、原理的には2個のステッピングモーターに限定されず、もっと多数の、例えば16個のモーターを平行して制御することもできるはずです。サブ・コントローラーに使うATtiny2313は価格的にも100円ほどですから、高機能な高速プロセッサを1個使うより有利になる見方もあるはずです(高機能なCPUでも16個までは困難かも知れませんが)。


システム構成 (回路.pdf、Tmain.c, Tsub.c 参照)
IC3は、メイン・コントローラーのATmega164P、IC2はサブ・コントローラーのATtiny2313です。
ATmega164PはLCD表示、ステッピングモータTの制御をすると共に、サブ・コントローラーATtiny2313をシリアル通信で制御します。サブ・コントローラーはステッピングモーターDを独自に制御します。

システムクロック
ATmega164PのPB1(CLKO)と、ATtiny2313のPA0(XTAL1)を接続し、システムクロックを同期させるために、
設定Fuse:ATmega164側は、
 Fuse CKDIV8 OFF : Clock 8MHz
 Fuse JTAGEN OFF : PORT C b3-6 IN/OUT
 Fuse CKOUT ON
ATtiny2313側は、
 Fuse CKDIV8 OFF : Clock 8MHz
 Fuses SUT_CKSEL = Ext Clock(+4.1ms) : External clock
とします。こうすることで、2つのコントローラーは同期速度で8MHz動作することが可能になります。
74LS04などでバッファすれば、サブ・コントローラーの個数を増加させることもできます。

シリアル通信
ATmega164PのPD1(TXD )と、ATtiny2313のPD0(RXD)を接続し、ATmega164PのPD0(RXD )と、ATtiny2313のPD1(TXD)を互いに接続、することで、2つのコントローラーは相互にシリアル通信が可能になります。
ATmega164P側は、対tiny2313通信をするために、初期化で
UBRR0H = 0; // bps H
UBRR0L = 25; // bps L 19.2k bps / 8M
UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0); // 送受信許可 受信割込許可
UCSR0C = (3<<UCSZ00); // フレーム形式設定(8ビット,1ストップビット)

ATtiny231側は
UBRRH = 0; // bps H
UBRRL = 25; // bps L 19.2k bps / 8M
UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE); // 送受信許可 受信割込許可
UCSRC = (0<<UMSEL)|(0<<USBS)|(3<<UCSZ0); // フレーム形式設定(8ビット,1ストップビット)
とします。

サンプル回路では、メインとサブ・コントローラーを1対1で直接接続しましたが、ATtiny2313側の送信ポートは74LS06のようなオープンコレクタを使い、プルアップすることで、多数のサブ・コントローラーを並列接続することができます。
ソフト上は、別ページの「AVR ステッピングモーター制御」と同様に、メインコントローラーは通信コマンドの上位4ビットでサブ・コントローラーのアドレスを指定することができます。

モータードライバー
モータードライバーにはSANKENのSLA7033Mを使用しています。このドライバーは現在同社では保守品扱いとなっており、製造中止のアナウンスがされているようですが、どうも国外では、同じ型番の製造が続けられているようで、価格もかなり手ごろになっているようです。これは、IC製造界に広く見られる現象で、国内→台湾→中国/フィリピンなどと、どんどん製造/販売元が変わってゆくことは仕方のないことかもしれません。16桁2行LCDモジュールも同じで、国内では多分どこも製造していないはずですが、あいかわらず、どこの国のモジュールもカナ表示ができるようです。
ところで、最新の国産モータードライバーは、SLA7033Mのようなシンプルな回路構成ではなく、東芝のドライバー同様、保護回路などを積み込んだ少々複雑な構成になっているようですが、外付け部品が増えるのは我慢できるにしても、何かの不具合でシャットダウン動作が発生しても、その原因の解明が不可能、ということにならなければいいのですが、と、やや不安がつきまとう、このごろです(実は、最近実際に経験させられたことなのですが、)
なお、ステッピングモーターの駆動電流値は比較電圧Vefできまり、5Vプルアップ抵抗をR1、GND側抵抗をR2として、電流検出抵抗をRsとすれば
  I =  (R2/(R1+R2)) × (5/Rs)
で決められます。この回路では約 2A/相 の定電流となります。無論、最大実効電流が常にフルに流れるわけではなく、周波数や負荷によって変わってきます。SLA7033Mは3Aが定格最大電流になっています。

cソースファイル組み込み
AVR Studio に添付したcファイルを組み込むには、新しい適当なフォルダをメインコントローラ、あるいはサブ用に用意し、それぞれのcファイルをコピーしておきます。AVR Studio4をNww projectで立ち上げ、コンとローター選択画面でATmega164PないしATtiny2313を指定するだけでcファイルが組み込まれます。

Tsubの解説
ATtint2313のサブコントローラーでステッピングモーターDを制御するプログラムです。
加速度テーブルspeed_tbl[]は、別ページ「ステッピングモーター制御」に添付したエクセルによる加速計算で生成した、加速度定数テーブルです。モーターの種類により加速度を変更することができます。ちなみに、添付データは日本サーボのKH56KM2-902です。
その下のパターンテーブルは1-2相励磁パターンで、0.9°ステップ角です。
1個置きに2相パターンのみ使えば、1.8°でステップ駆動できます。

このサブコントローラーを制御する方法は、Tmainのソース例を見てもえばわかるように、メインから
  set_dmspeed(1000);
のように、最高速度定数をセットして
  dm_r();
のように回転を指示することで、指定方向に回転が始まります。
停止はサブコントローラー側で、判断します。必要なステップ数、またはPB4のD信号を入力して判断します。

Tmainの解説
ステッピングモーターTの駆動方法はTsubと同じですが、こちらはシリアル経由ではなく、直接実行しています。
set_speed()で速度を決め、m_right(); m_left();m_stop(); m_off(); などの関数で制御することができます。
なお、筆者のアプリケーション装置においては、Tモーターの回転はPD2へエンコーダー信号として入力し、8ビットタイマーの割込み監視でカウントすることでフィードバックさせています。こうすることで、過負荷がかかって脱調することがあった場合、これを検出することができるためです。こうした常時監視が不要なシステムでは、ホームポジションでの検出方法で済むでしょう。

TmainにはLCD表示などの付属的関数もつけておきましたので参考にしてください。
D、TのセンサーはOMRONの EE-SV3 などが適しているでしょう。
電源は、例の場合は24V/3.5A以上とれるスイッチング電源が必要です。

接続
モーターの接続は、コネクタ端子1番から順に、 A, Acom, /A , B, Bcom, /B  です。
操作、表示コネクタは、端子7〜10以外はLCDに接続できます。7〜10は4個の操作スイッチ用信号として使えます。


複雑なシステムの制御
モーターを制御する方法としては、出力軸の回転を検出して、それが望む回転状況と異なる場合、その差分をフィードバックしてモーター出力へ反映する閉ループ制御(サーボ)方式が知られていますが、慣性、摩擦、回路レスポンス、機構のレスポンスとあそびの有無 等の、難しい問題が伴います。
ステッピングモーターは、一般的に、フィードバックを使わないオープン・ループ制御用けと言われています。
無論、電源を切断した場合を含み、自動的に初期化位置が得られるような機構を伴わない限り、何らかの方法で、最低1箇所でホーム位置を知る検出手段がないと、ステッピングモーター機構はその絶対位置を制御することはできません。
ホーム位置がわかれば、ステッピングモーターは脱調しない限り、極めて正確に、その動き(位置)を制御することができます。
ステッピングモーターの最大の特徴は、この「脱調しない範囲内の負荷トルクで使う」ことにあるのですが、もう少し、別な使いかたはないのでしょうか?

ATtiny2313のような簡単なコントローラーでも、ステッピングモーターの相出力制御だけでなく、エンコーダー入力くらいは可能です。少なくとも2相の入力を設けることで、モーター軸の回転方向を含む運動情報を検出することはできます。そして、回転を検出できるのであれば、何らかの方法でフィードバック制御もできるはずです。目標に完全に追従するような複雑な制御方法ではなく、差が一定以上になったら、所定の速度で追従する程度の簡易フィードバック制御なら比較的容易に実現できるはずです。また、総ての制御をサブ・コントローラーで行うのではなく、やや複雑な計算はメイン・コントローラー側で分担するなどの方法も採れるはずです。

複数のマイコンをシリアル通信で結び、多数のモーターを制御できるとしたら、このシステムにはかなり高度な機能を持たせることができる筈です。上例のシステムでは、システム間を行きかう通信データは速度情報とか位置情報だけでしたが、汎用のメモリデータ自体もメインとサブコントローラー間、もしくは各サブ間を伝送することができます。

そして、メインは、各サブ・コントローラーに目標を伝え、実際の機能はサブ・コントローラーが行い、結果をメインに返す。メインはこの結果を見て、更新した目標をサブに伝える。 こんな生物の脳と、末端神経+筋肉に似たシステムも不可能ではない気がします。





◆下はメインとしてAtmega164を使い、ステッピングモーター制御としてATtiny2313を
 使った例。設定としてはATmegaのCKOUTにチェックをいれ、ATtinyは
SUT_CKSEL = Ext Clock とすることでクロックを同期させてシリアル通信の同期を確実
にしている。この例では3組のSLA7026を制御している。






*************** メイン側例 ***************

void ini(void)
{
// ,,,,,,,,,,
// IOポート設定
DDRA = PAIO; // ポート入出力指定 IN=0 OUT=1
DDRB = PBIO;
DDRC = PCIO;
DDRD = PDIO;
all_off();
// USSRT 設定
// 対tiny2313 通信

UBRR0H = 0; // bps H
UBRR0L = 25; // bps L 19.2k bps / 8M
UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0); // 送受信許可 受信割込許可
UCSR0C = (3<<UCSZ00); // フレーム形式設定(8ビット,1ストップビット)

// ,,,,,,,,,,
}

/*
--------------------------
モーター制御
--------------------------
送信コマンド
 0:停止
 1:右回転
 2:左回転

例 set_mspeed(1000,3); // 速度設定(速度,モーターアドレス4bit) 
m_r(3); // 右回転(モーターアドレス4bit)
m_stop(3) // 停止(モーターアドレス4bit)

アドレス
 1  モーター1
 2  モーター2
 3  モーター3
 4  モーター2+3
*/

void m_stop(unsigned char adr) // M 停止 (adr:モーターアドレス)
{
UDR0 =0x00 | (adr<<4);
}

void m_r(unsigned char adr) // M 右回転
{
UDR0 = 0x01 | (adr<<4); // 右回転指示(正回
}

void m_l(unsigned char adr)
{
UDR0 = 0x02 | (adr<<4); // 左回転指示(逆転) 送信
}

// モーター速度を設定するための送信
void set_mspeed(unsigned int s,unsigned char adr) // s=速度, adr=モーターアドレス
{
uint8_t d[4];
char i;

for (i=0; i<3 ; ++i) // L,,,Hの順で送信データ準備
{
d[i] = (uint8_t)s & 0x0f;
s = s>>4;
}
UDR0 = 0x04 | (adr<<4); wt_tx(); // 速度設定コマンド=4
UDR0 = d[3] | (adr<<4); wt_tx();
UDR0 = d[2] | (adr<<4); wt_tx();
UDR0 = d[1] | (adr<<4); wt_tx();
UDR0 = d[0] | (adr<<4); wt_tx();
}

void wt_tx(void) // 送信バッファ空待ち
{ while ( !(UCSR0A & (1 << UDRE0)) ) ; }






*************** サブ側例 ************

// ======== buff ========
volatile unsigned int *speedpt; // 速度ポインタ
volatile unsigned char mod; // 駆動モード (0:停止 1:加速 2:減速 3:ブレーキ)
volatile unsigned char rlf; // 回転方向フラグ
volatile signed char pt_cnt; // 相カウンタ
volatile unsigned char out_paternB; // ポートB出力中パターン
volatile unsigned char out_paternD; // ポートD出力中パターン

volatile unsigned int max_speed; //最高速度

volatile static unsigned char rx[5]; // 受信バッファ
volatile unsigned char rx_cnt; // 受信データカウンタ
volatile unsigned char d_len; // データ数カウンタ
volatile unsigned char brk_ct; // ステッピングモーター ブレーキタイマー

volatile unsigned char adr; //駆動モーター指定アドレス変数
volatile unsigned char PBmsk; //PB用出力マスク
volatile unsigned char PDmsk; //PD用出力マスク

// ======== proto type ========
void lptm(unsigned ms);
void outp(void);

void m_off(void);
void m_stop(void);
void m_right(void);
void m_left(void);

void ini(void);
void lptm(unsigned);

//==============================================
// 速度テーブル
// 50step------加速に使える最大step
//const unsigned int speed_tbl[] =
PROGMEM unsigned int speed_tbl[] =
{ // a=0.093 CF近似
3350,3038,2755,2499,2267,
2056,1865,1691,1534,1391,
1262,1144,1038,941,854,
774,702,637,578,524,
475,431,391,354,321,
291
};

// パターンテーブル
//const char *patern = "\x08\x0a\x02\x06\x04\x05\x01\x09"; // positive sig
const char *patern = "\x07\x05\x0d\x09\x0b\x0a\x0e\x06"; // negative sig


/*
======================================
割り込み処理
======================================
*/

/*---------------------------
8ビットタイマー割込み処理
クロック信号をoff
---------------------------*/

ISR(TIMER0_OVF_vect) //SIGNAL(SIG_OVERFLOW0) 8ビットタイマー0 オーバーフロー割込み
{
TCNT0 = I_TC; // タイマー再スタート 20ms
if(brk_ct)
{
brk_ct--;
if(!brk_ct)
{
PORTB |= M1 | M0; // モーターoff
PORTD |= M2; // モーターoff
}
}

}


//----------- RS-232C 受信割込み

ISR(USART_RX_vect)
{
unsigned char data;
data = UDR; //1バイト受信

adr = (data & 0xf0)>>4; //アドレス4bit取り出し

switch(adr) //指定されたアドレス以外のビットをマスクして禁止
{
case 1: //M0
PBmsk = M1;
PDmsk = M2;
break;

case 2: //M1
PBmsk = M0;
PDmsk = M2;
break;

case 3: //M2
PBmsk = M0 | M1;
PDmsk = ~M2;
break;

case 4: // M1+M2
PBmsk = M0;
PDmsk = ~M2;
break;
}


data &= 0x0f;
if(d_len) // 設定データを受信中
{
rx[rx_cnt++] = data; // 設定データセーブしてバッファをシフト
d_len--; // データ長カウンタは-1
if(!d_len) // 設定データ終了
{
if(rx[0]==4) //最高速度設定
{
max_speed = ((rx[1] & 0x0f)<<12) | ((rx[2] & 0x0f)<<8) | ((rx[3] & 0x0f)<<4) | (rx[4]&0x0f);
}
}
}
else //コマンド受信
{
rx[0] = data; //コマンドを受信バッファ先頭にセーブ
rx_cnt = 1; //受信カウンタは1
switch(data)
{
case 0: // モーターoffコマンド[0]受信
d_len = 0; // 後続データ無し
mod = 2; // 減速停止
break;

case 1: // 右回転開始コマンド[1]受信
d_len = 0; // 後続データ無し
rlf = 0; // 右
speedpt = speed_tbl; // スピードポインタ初期化
TCNT1 = 0xfffe; // TCNT割込みがすぐ掛かるように
mod = 1; // 回転

break;

case 2: // 左回転開始コマンド[2]受信
d_len = 0; // 後続データ無し
rlf = 1; // 左
mod = 1; // 回転

break;

case 4: //最高speed指定コマンド[4]受信
d_len = 4; // データ数セットして後続のデータ受信へ
break;

case 5: //保持コマンド受信
d_len = 0;
mod = 3;
}
}
}


/* -------------------------------------------------
16ビットタイマー割り込みによる ステッピングモーター制御
-------------------------------------------------
*/

SIGNAL(SIG_OVERFLOW1)
{
switch(mod)
{

case 0:
brk_ct = BRK_TM; // ブレーキタイマー始動
break;

case 1: // 加速モード
speedpt++; //スピードポインタを上にシフト
if (*speedpt < max_speed) //最高速限定を越したら
{
speedpt--;
TCNT1 = 0xffff - max_speed; // 最高速度を制限
}
else
{
TCNT1 = 0xffff - *speedpt; // 加速
}
outp(); //出力
break;

case 2: // 減速モード
speedpt--; // スピードポインタを下へシフト
if (speedpt <= speed_tbl) // 最低速度以下なら
{
speedpt = speed_tbl;
mod = 0; // 停止へ
}
else
{ TCNT1 = 0xffff - *speedpt;}
outp(); //パターン出力
break;
}
}

/*
ステッピングモーターにパターンを出力
adrで指定されるモーター(FM/CM1/CM2)を駆動する
*/


void outp(void)
{
#define PTADR (patern + pt_cnt)

PORTB = out_paternB = (*PTADR | *PTADR<<4) | PBmsk ; //非駆動ビットにマスクをして出力
PORTD = out_paternD = (*PTADR <<2) | PDmsk; //非駆動ビットにマスクをして出力

if(rlf) // 右回転
{
pt_cnt++; // 相カウンタをシフト
if (pt_cnt>=8) // 相カウンタが最後まで行ったら
{ pt_cnt = 0; } // リセット
}
else // 左回転
{
pt_cnt--;
if (pt_cnt < 0)
{ pt_cnt = 7; }
}
}

//======================================
// メイン
//======================================
int main(void)
{
ini(); // デバイス初期化
sei(); // 割込有効
lptm(50);
while (1)
{

}
}

/*
--------------------------
デバイス初期化
--------------------------
*/
void ini(void)
{
pgm_read_word(speed_tbl); // SRAM開放

DDRB = PBIO;
PORTB = 0xff; // off output
DDRD = PDIO;
PORTD = 0xff;

UBRRH = 0; // bps H
UBRRL = 25; // bps L 19.2k bps / 8M

UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE); // 送受信許可 受信割込許可
UCSRC = (0<<UMSEL)|(0<<USBS)|(3<<UCSZ0); // フレーム形式設定(8ビット,1ストップビット)

TCCR1A = 0;
TCCR1B = 0x02; //16ビットタイマー時定数 8MHz/8 = 1μsec/count
TCCR0B = 0x05; //8ビットタイマー 8MHz/1024 = 0.1

TIMSK = 0b10000010; // 割込制御:16ビットオーバーフロー割込可、8ビットオーバーフロー割込不可

// TOIE1------TOIE1-
TCCR1A = 0; // 16ビットタイマー :ノーマルタイマカウンタ動作
TCCR1B = 0x02; //16ビットタイマー時定数設定 8MHz/8 = 1μsec/count
TCCR0B = 0x05; //8ビットタイマー 8MHz/1024 = 0.128m/count
TCNT0 = I_TC; // 0.128msでダウンカウント

mod = 0; // モーター停止
pt_cnt = 0; // 出力パターン相カウンタを初期化
speedpt = speed_tbl; // 速度テーブルの初期化
max_speed = 800; // デフォルト最高速度
brk_ct = 0; // 割込みブレーキタイマー
}


/*
--------------------------
ステッピングモーター制御
--------------------------
例 max_speed = 500; // 最高速度設定
m_right(); // 回転方向指示
// または m_left();
m_stop(); // モーター停止
m_off(); // モーター励磁off
*/



// motor off
void m_off(void)
{
cli(); // 割込み禁止
mod = 0;
PORTB &= ~FM; // sleep 信号 H
PORTB |= FM; // sleep 信号 L
sei(); // 割込み有効
}

// 減速して停止
void m_stop(void)
{
cli(); // 割込み禁止
mod = 2;
sei(); // 割込み有効
}


// 右回転
void m_right(void)
{
cli(); // 割込み禁止
rlf = 0; // R
mod = 1; // 回転
sei(); // 割込み有効
}


// 左回転
void m_left(void)
{
cli(); // 割込み禁止
rlf = 1; // L
mod = 1; // 回転
sei(); // 割込み有効
}


/*
======================================
基本ファンクション
======================================
*/

// 1msec×N ループタイマー
void lptm(unsigned ms)
{
volatile int i;
while(ms)
{
for (i = 440; i > 0; i--); // 8MHz
ms--;
}
return;
}












inserted by FC2 system