サーボモータを動かしてみます
〔マイコンのトップに戻る〕
ここでは産業機械等で利用されているサーボモータではなく、ホビー用途のサーボモータを回してみます、
ホビー用途のサーボモータと言えばラジコンを始め、最近では2足歩行ロボットの関節用に利用されていますね。
実はぁ、知り合いからRCサーボモータを貰ったのです、なら、動かしてみるかぁってんでぇ、記事にしました。
ArduinoはServoライブラリが有るのでこれを利用したら簡単に動作させられます、
ArduinoでのServoライブラリ使用方法は建築発明工作ゼミ2008さんのこちらHPを参照下さい。
この頁ではPIC(12F1822)を使ってサーボモータを動かしてみます。
サーボモータを調べてみると、同じ物が秋月電子のこちら(S03N 2BBM/フタバ)で販売されていました。
上の物は販売ページがなくなっているので、こちら(S03N 2BBM/JR)を載せて置きます。 *3)
接続線は3端子有り、GND(黒線)、5V(赤線)、信号線(白線)のフタバタイプ仕様です、
他にJRタイプ仕様が有り、GND(茶線)、5V(赤線)、信号線(オレンジ線)で信号の配列は同じです。
又、制御方法も同じで、フタバタイプはコネクタの逆挿し防止が有る様です。 *3)
サーボモータによっては360°回転する物も有るようですが、このサーボも含み通常は180°までです。
360°等の連続回転をさせるにはステッピングモータを利用するのではないかと思います。
尚、ステッピングモータを動かす記事はこちらを見て下さい。
PS. *4)
PICの周辺モジュールPSMCを使い、通常の180°タイプと360°回転タイプのサーボモータを動作させた
記事はこちらに書きました。
Foscを16MHzで出来て、サーボモータを3/4個制御可能で操作しやすく、プログラムもスッキリなので
お勧めかもぉ。
サーボモータの制御方法について
動かすにはPWM信号をサーボモータの信号線に出力すれば良いだけです。
左図の様に1周期約20ms(50Hz)のパルス信号を出力します。
パルスのONさせる幅を可変させることによりモータ回転軸の角度が決まります。
今回のこのサーボモータでぇだいたいぃ、幅がぁ、
1520usで90°中央(ニュ−トラル)位置です。
630usで中央から時計回り方向に更に90°回転し、
2350usで中央から反時計回り方向に-90°回転します。
よって制御する幅は630us〜2350usにしましたが、各サーボモータにより調整しましょう。
0°位置
90°位置
180°位置
1周期20msを作るには50HzのPWM信号を作成すれば良いので、PICのCCP1機能(PWM)を利用使用と思い
周期の設定値を計算したら50Hzを作るにはPICのメインクロックを1MHz以下にしないとだめです、
え、ぇぇ〜、他の機器を制御したりするのを考えるとぉ、なんかぁ、いやですねぇ。
だから、16ビットタイマーのTIMER1を使い割り込みで処理する方法を選びました。
又、例えば現在 90°中央位置に回転軸が有るとして、630usを出力したとします。
するとサーボモータは時計回り方向に 90°回転しますが、
回転が終わる前にPWM信号が途絶えると 90°は回転せずに途中で停止してしまいます。
PWM信号が出ていない状態はモータはフリー状態です、力が加わるとモータ軸位置はずれます、
PWM信号が出ているとサーボモータは軸位置がずれても元の位置に戻ろうとします、
モータ軸を手で回転させてみて下さい元の位置に動こうとします。(あまり力は入れないでね)
《 配線図 》
左図サーボモータではリード線が赤・黒・オレンジ(JRタイプ)ですが、
今回使用の物は赤・黒・白(フタバタイプ)です。
制御線の色が異なっています。
尚、サーボモータの制御線は、PICのRA2(5番ピン)デジタル出力
につながっています。
何時もこのHPで利用しているトリマータイプの可変抵抗でなく、
つまみ回し易いので小型のボリュームタイプを使用しています。
手持ちで500KΩを使いましたが抵抗値は適当で良いかと思ふ。
(出来るならアナログ入力で0-1023まで入力出来る物が良さげ)
尚、ボリュームの中央端子はPICのAN3(3番ピン)アナログ入力
につながっています。
このボリュームは左回しで0方向、右回しで1023方向の値が
得られます。
サーボモータを複数個繋いだ場合にどうなるか気にかかる所ですが
1個しかないのでそのうち手に入れたらやって見ようと思うが.........
《サンプルプログラム》
@上記配線図画面の様に配線しましょう。
AMPLAB X(v2.15)を起動させます。 *2)
B下記がPIC12F1822でのサンプルのプログラムソースです、
MPLAB(R) XC8 C Compiler Version 1.32コンパイラを使用しています。
プロジェクトを作成して新規ファイルにコピーペーストして貼り付けて下さい。 *2)
---------------------------------------------------------------------
#include <xc.h>
#include <stdlib.h>
#define _XTAL_FREQ 8000000 // delay用に必要(クロック8MHzを指定)
#define SERVO_PIN RA2 // サーボモータに接続しているピン番号
int TMR1ON_Flag ; // タイマーの割り込み待ちフラグ
// コンフィギュレーション1の設定
#pragma config FOSC = INTOSC // 内部クロック使用する(INTOSC)
#pragma config WDTE = OFF // ウオッチドッグタイマー無し(OFF)
#pragma config PWRTE = ON // 電源ONから64ms後にプログラムを開始する(ON)
#pragma config MCLRE = OFF // 外部リセット信号は使用せずにデジタル入力(RA3)ピンとする(OFF)
#pragma config CP = OFF // プログラムメモリーを保護しない(OFF)
#pragma config CPD = OFF // データメモリーを保護しない(OFF)
#pragma config BOREN = ON // 電源電圧降下常時監視機能ON(ON)
#pragma config CLKOUTEN = OFF // CLKOUTピンをRA4ピンで使用する(OFF)
#pragma config IESO = OFF // 外部・内部クロックの切替えでの起動はなし(OFF)
#pragma config FCMEN = OFF // 外部クロック監視しない(OFF)
// コンフィギュレーション2の設定
#pragma config WRT = OFF // Flashメモリーを保護しない(OFF)
#pragma config PLLEN = OFF // 動作クロックを32MHzでは動作させない(OFF)
#pragma config STVREN = ON // スタックがオーバフローやアンダーフローしたらリセットをする(ON)
#pragma config BORV = HI // 電源電圧降下常時監視電圧(2.5V)設定(HI)
#pragma config LVP = OFF // 低電圧プログラミング機能使用しない(OFF)
// タイマー1割込みの処理
void interrupt InterTimer( void )
{
if (TMR1IF == 1) { // タイマー1の割込み発生か?
TMR1ON_Flag = 0 ;
TMR1IF = 0 ; // タイマー1割込フラグをリセット
}
}
// アナログ値を読み込む処理
unsigned int adconv()
{
unsigned int temp;
GO_nDONE = 1 ; // PICにアナログ値読取り開始を指示
while(GO_nDONE) ; // PICが読取り完了するまで待つ
temp = ADRESH ; // PICは読取った値をADRESHとADRESLのレジスターにセットする
temp = ( temp << 8 ) | ADRESL ; // 10ビットの分解能力です
return temp ;
}
// マップ関数(ある数値から他の数値へレベル変換を行う)
long map(long x, long in_min, long in_max, long out_min, long out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min ;
}
// サーボモータに出力を行う処理(1サイクルのみ出力)
// value : 回転角度に応じたパルスの幅を指定する(単位はus)
void ServoOut(int value)
{
unsigned int c ;
c = 65536 - value ;
TMR1H = (c >> 8) ; // パルス幅(ON)の時間(us)を設定
TMR1L = (c & 0x00ff) ; // 1カウントは1usです
TMR1ON_Flag = 1 ;
SERVO_PIN = 1 ; // パルスON
TMR1ON = 1 ; // タイマー1カウント開始
while(TMR1ON_Flag == 1) ;// 設定した時間で割り込みが発生するまで待つ
TMR1ON = 0 ; // タイマー1カウント停止
SERVO_PIN = 0 ; // パルスOFF
__delay_us(17000) ; // 通常は 20ms - ON時間 だけですが 17msに固定
}
// メインの処理
void main()
{
unsigned int num , x , i ;
OSCCON = 0b01110010 ; // 内部クロックは8MHzとする
ANSELA = 0b00010000 ; // アナログはAN3を使用し、残りをすべてデジタルI/Oに割当
TRISA = 0b00011000 ; // AN3(RA4)/RA0だけ入力その他のピンは出力に割当てる(RA3は入力専用)
PORTA = 0b00000000 ; // 出力ピンの初期化(全てLOWにする)
// A/Dの設定
ADCON1 = 0b10010000 ; // 読取値は右寄せ、A/D変換クロックはFOSC/8、VDDをリファレンスに
ADCON0 = 0b00001101 ; // アナログ変換情報設定(AN3から読込む)
__delay_us(5) ; // アナログ変換情報が設定されるまでとりあえず待つ
// タイマー1の設定(1カウントは1usで設定)
T1CON = 0b01110000 ; // 内部クロック(8MHz)でTIMER1をカウントする、プリスケーラカウント値 1:8
TMR1IF = 0 ; // タイマー1割込フラグを0にする
TMR1IE = 1 ; // タイマー1割り込みを許可する
PEIE = 1 ; // 周辺装置割り込みを許可する
GIE = 1 ; // 全割り込み処理を許可する
__delay_ms(3000) ; // 3秒後に実行する
while(1) {
// 3番ピン(AN3)から可変抵抗の値を読み込む
num = adconv() ;
// 読んだ値(0-1023)を出力するパルスのON幅値(2350-630)に変換する
x = map(num,0,1023,2350,630) ;
// サーボモータに出力する
ServoOut(x) ;
}
}
---------------------------------------------------------------------
CコンパイルとPIC書き込みを実行して下さい。
DPICをブレッドボードに取付けてボリュームを回せばその方向にサーボモータが動くと思います。
ボリュームの回す方向と逆に動く場合は?
ボリュームの配線、5V と GND を入れ替えて下さい、
或は、x = map(num,0,1023,2350,630) を x = map(num,0,1023,630,2350) と書換えて下さい。
A/Dの設定について
アナログI/O
アナログピンの設定は下記のレジスターにて設定します。
ANSELA = 0b00010000 ; アナログ入力を行うピンの指定をします
赤数字右からAN0(7ピン),AN1(6ピン),AN2(5ピン),AN3(3ピン)の順
1でアナログ、0でデジタル、この設定例はAN3をアナログで使用する。
指定したAN3のアナログピンを入力に指定するには下記のレジスターにて設定します。
TRISA = 0b00010000 ; この設定はAN3のみ入力に設定するです。
1で入力、0で出力、右からAN0,AN1,AN2,x,AN3
アナログ変換情報設定
ADCON1 = 0b10010000 ; 緑数字部分でA/D変換時のリファレンス電圧をどうするかの設定です。
00:VDDのPIC電圧を使用する。
10:6番ピン接続の外部VREFを使用する。
11:PIC内蔵の固定電圧をしようする。
赤数字部分でA/D変換を行う速度のクロックを設定します。
000:FOSC/2 001:FOSC/8 010:FOSC/32
100:FOSC/4 101:FOSC/16 110:FOSC/64
又、それ以外の数字部分の設定は今回このまま使用して下さい。
ADCON0 = 0b00001101 ; この設定でAN3から読込めと指示しています
他のAN3以外から読込みたい場合は、赤数字の部分を変更します。
AN0=00000,AN1=00001,AN2=00010,AN3=00011と変更して下さい
赤数字の部分以外の設定は今回このまま使用して下さい。
又、ADCON0の設定には5us程かかります、delayを入れて置きましょう
adconv()
アナログの値を読み取ります、値は0〜1023(0V〜5V)の範囲です。
可変抵抗器によっては0-1023ではなく例えば60-530の範囲だったりしたりします、
(この様な場合サーボモータが0°〜180°まで動作しません)
その場合は、x = map(num,0,1023,2350,630) を x = map(num,60,530,2350,630) に
変更しましょう、0°〜180°まで動作する様になります。
タイマー1の設定について
T1CONの設定
T1CON = 0b01110000 ;
青数字部分でTimer1のクロックソースを選択します。
00 = Fosc/4 01 = Fosc 10 = 外部入力 11 = 内蔵の容量検知オシレータ (CAPOSC)
緑数字部分でプリスケーラの設定を行います。
00 = 1:1 01 = 1:2 10 = 1:4 11 = 1:8
赤数字部分 1=TIMER1を動作させる 0=TIMER1を動作させない
ワンカウントの時間計算
(1/システムクロック周波数) x プリスケーラ設定値
システムクロックが8MHzでワンカウント1usにしたい場合は
クロックソースが 01 = Fosc でプリスケーラの設定が 11 = 1:8 なら
(1/8MHz)x 8 = 1us
サーボモータへの出力パルスON幅を 1520us 出したい場合は
c = 65536 - 1520 ; // c = 64016
TMR1H = (c >> 8) ; // パルス幅(ON)の時間(us)を設定
TMR1L = (c & 0x00ff) ; // 1カウントは1usです
64016 から 65536 までカウントアップしていき 65536 で割り込みが発生します。
__delay_us(17000)
1サイクル(20ms/50Hz)でのパルスOFF時間は、20ms-パルスON時間なので
例えば、1520ms だけONを出力したら、20000-1520=18480ms OFFすれば良いが、
__delay_us()は変数が使えない、なので__delay_us(17000)で固定します。
関数について
ans = map(long x, long in_min, long in_max, long out_min, long out_max) *1)
指定した数値( x )を、他の数値範囲(out_min〜out_max)へレベル変換を行う処理
x : 変換したい数値を指定します
in_min : 変換する数値の最小値
in_max : 変換する数値の最大値
out_min : 変換後数値の最小値
out_max: 変換後数値の最大値
ans : 変換した結果の数値を返します
例)
long x ;
x = map(512,0,1023,2350,630) ; // xは1476となる
x = map(1100,0,1023,2350,630) ; // xは630となる
ServoOut(int value)
サーボモータにPWM信号を1サイクル(1周期)出力します。
value: ONするパルスの幅を指定します(630us-2350us)
630us-2350usの範囲以外の数値も指定可能です、サーボモータによって調整しましょう。
範囲以外の数値をサーボモータに与えた場合は壊れる可能性が有ります。
サーボモータを配線する端子を変えたい場合は
#define SERVO_PIN RA2 // サーボモータに接続しているピン番号
の行 RA2(デジタル出力) を変更しましょう。
《 その他 》
実験風景の写真です。
電池は新品を!
最初は古い電池で動かなく悩んだぞぉぉ
サーボモータを増やす場合はその個数だけ
ServoOut()関数を増やせばOKと思われますが
1個しかないのでにゃんともです。
また、サーボモータが動作時の電圧を測ったら
約150mv程電圧降下が有ったので其れなりに
別電源等の考慮が必要かも。
次回以降はステッピングモータでも動かしてみようかと考えています。
尚、
Arduinoを使ってステッピングモータを動かす記事はこちらを見て下さい。
PICを使ってステッピングモータを動かす記事はこちらを見て下さい。
I2C接続16チャンネル12ビットPWMサーボシールドを使ってサーボモータを動かす記事はこちらを
見て下さい。(2個以上のサーボモータを使うならこれを利用した方が良さげぇ) *3)
記事一部追記(*4) 2020/04/20
記事一部変更(*3) 2018/02/13
MPLAB X用に記事変更(*2) 2015/10/27
map関数をint→longに変更(*1) 2014/01/15
【きむ茶工房ガレージハウス】
Copyright (C) 2006-2020 Shigehiro Kimura All Rights Reserved.