左図の様に1周期約20ms(50Hz)のパルス信号を出力します。
左図サーボモータではリード線が赤・黒・オレンジ(JRタイプ)ですが、
---------------------------------------------------------------------
#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書き込みを実行して下さい。
c = 65536 - 1520 ; // c = 64016
TMR1H = (c >> 8) ; // パルス幅(ON)の時間(us)を設定
TMR1L = (c & 0x00ff) ; // 1カウントは1usです
64016 から 65536 までカウントアップしていき 65536 で割り込みが発生します。
実験風景の写真です。
記事一部追記(*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.