複数(24個)のRCサーボ制御信号を2つのITUで作る
異なるパルス幅を持つ一連の連続パルスを連続して発生する動作を2つのITUで作る

Copyright(C) 21Jan2010
coskx

24のRCサーボを同時に制御するための24の独立したパルス幅のパルスの生成が必要である。24のITUを使えば簡単だが,ITUは5つしかないので工夫が必要となる。RCサーボの周期は20msec程度で,その中のパルスの幅が0.4〜2.2msec程度である。そこでリセット同期PWMモードのITUで24のパルス幅のパルスを順番に作って,同時にあいているポートに何番目のパルスかを出力すればうまくゆきそう。

この方法の特徴
(1)パルス幅は0.32μsec刻みで設定できる
(2)外付け回路が必要

ここでは,ITU3,4をリセット同期PWMモードで使って3のパルス幅を持つパルスをTIOCB3,TOCXA4,TOCXB4に順番に8回つくり,同時にP1のbit0-bit7にH信号を同期して順番に出力し,24このパルス発生を繰り返すようにしている。24のAND回路を外付け(TIOCB3とP1のbit0,TIOCB3とP1のbit1,TIOCB3とP1のbit2,...)すれば,24このRCサーボを制御できるようになる。(24個のAND回路の代わりに3個の8出力デマルチプレクサ74138を使う方法もあるが,研究してほしい。)
ITU3,4はリセット同期PWMモード動作を行ない,PWM周期は2.5msecとした。8パルスを作ると,20msecとなります。割り込みを用いて,1つのパルスが終了するごとに次のパルス幅を与え,3つずつ8回のパルス発生で24のパルス幅を持つパルスを順番につくっている。(クロックが0.32μsecなのでパルス周期,パルス幅には0.16μsec程度の誤差が生ずる)

TIOCB3(CN1-17),TOCXA4(CN1-20),TOCXB4(CN1-21)が出力となっている。
(TIOCB3はPB1と共用,TOCXA4はPB4と共用,TOCXB4はPB5と共用になっている。)


 

プログラムのダウンロード

DownLoad

RCservoCTRL24.c

このプログラムでは,μsecの単位にこだわっているが,CPUクロックに適したカウント方法を用いれば,プログラムはもう少し簡単になる。

/*
24系統RCサーボパルス発生システム
ITU3,4(リセット同期 PWM モード)とどこかのポート(8ビット)を使用
割り込みを4つ使用

20msec間に1.0msecから2.0msecまでのパルスを3系統×8個出力する

TIOCB3,TOCXA4,TOCXB4が出力となっている。
(OLS3=OLS4=1)

TIOCB3 はGRB3で制御される
TOCXA4 はGRA4で制御される
TOCXB4 はGRB4で制御される
GRA3とのコンペアマッチでTCNT3がクリアされる
クリアの周期は2.5msecとする
(これが8回で20msecになる)


TIOCB3は系統0,系統1,系統2,...,系統7
TOCXA4は系統8,系統9,系統10,...,系統15
TOCXB4は系統16,系統17,系統18,...,系統23
を周期8で連続して出力する
(1度に3系統ずつ出力される)
ポート1からの8ビットで01,02,04,08,10,20,40,80
を出すようにすれば,24個系統のAND回路で24系統の信号に分離できる。

バッファレジスタは使わない

初期設定手順
1.カウント動作停止
TSTRのSTR3ビットを0にクリアし、
TCNT3のカウント動作を停止する。
リセット同期PWMモードの設定は、TCNT3が
停止した状態で行なう。

2.カウンタクロックの選択
TCRのTPSC2〜TPSC0ビットでチャネル3の
カウンタクロックを選択する。
(外部クロックを選択した場合は、TCRのCKEG1、
CKEG0ビットで外部クロックのエッジを選択
する。今回これはなし)

3.カウンタクリア要因の選択
TCR3のCCLR1、CCLR0ビットでカウンタクリア
要因をGRA3のコンペアマッチに設定する。

4.リセット同期PWMモードの設定
TFCRのCMD1、CMD0ビットでリセット同期PWM
モードを設定する。
TIOCA3、TIOCB3、TIOCA4、TIOCB4、TOCXA4、
TOCXB3端子は自動的にPWM出力端子となる。

5.TCNTの設定
TCNT3は、H'0000としてください。TCNT4は、
設定する必要はない。

6.GRの設定
GRA3は周期レジスタである。GRA3には、周期を
設定してください。GRB3、GRA4、GRB4には、
PWM出力波形変化タイミングを設定する。
ただし、設定値は、TCNT3とコンペアマッチする
範囲で設定する。
  X ≦ GRA3 (X:設定値)

7.GRA3のコンペアマッチで割り込むように設定
割り込みA参照

8.GRB3、GRA4、GRB4のうち一番大きな値のところでコンペアマッチ
割り込みを設定
割り込みB参照

7.カウント動作開始
TSTRのSTR3ビットを1にセットして、
TCNT3のカウント動作を開始してください。..


実行時の割り込み
割り込み2
GRB3、GRA4、GRB4を設定し,
一番大きな値のところでコンペアマッチ割り込みを設定

割り込み1
0-7を繰り返す周期カウンタを1アップする
P1のビットパターンを変更する。変更順は
00000001
00000010
00000100
00001000
   :
10000000


IMIA3の割り込み:36 0x90-0x93
IMIB3の割り込み:37 0x94-0x97
IMIA4の割り込み:40 0xA0-0xA3
IMIB4の割り込み:41 0xA4-0xA7

このプログラムでのシリアル通信制御テスト
A,B,C,...V,W,Xの入力 系統0〜24のパルスをそれぞれ50μsec長くする
a,b,c,...v,w,xの入力 系統0〜24のパルスをそれぞれ50μsec短くする
0の入力              系統0〜24のパルスをすべて1500μsecの長さにする
1の入力              系統0〜24のパルスをすべて異なる長さにする

*/

#include "3048fone.h"
#include "h8_3048fone.h"

#define SELECT_PORT P1

/*パルス幅は最初1000,1000,1200,1000,1000,1000,2000,2000μsec*/
/*              1200,1100,2000,1200,1800,1700,1700,2000μsec*/
/*              1500,1100,2000,1300,1400,1500,2000,1600μsec*/
/*とする*/
/*★注意1 使用していない系統の値は1500に固定すること*/
/*★注意2 値は1000〜2000とする*/
int pulseWidth[24]={  /*単位μsec*/
    1000,1000,1200,1000,1000,1000,2000,2000,
    1200,1100,2000,1200,1800,1700,1700,2000,
    1500,1100,2000,1300,1400,1500,2000,1600
};

const unsigned char portout[8]={1,2,4,8,0x10,0x20,0x40,0x80};
volatile int IntCounter=0; /*割り込み回数を数えるカウンタ 0-7を繰り返す*/

/*****初期化関数*****/
void initMultiPulseGen()
{
    ITU.TSTR.BIT.STR3=0; /*ITU ch3カウントストップ*/
    ITU3.TCR.BIT.TPSC=3; /*φ/8を使用(1クロック8/25μsec (=0.32μsec))*/
    ITU3.TCR.BIT.CCLR=1; /*GRAとのコンペアマッチでTCNTクリア*/
    ITU.TFCR.BIT.CMD=3;/*TFCRのCMD1、CMD0ビットでリセット同期PWMモード*/
    ITU.TOCR.BIT.OLS3=0;/*マニュアル記述が逆だ*/
    ITU.TOCR.BIT.OLS4=0;/*マニュアル記述が逆だ*/
    ITU3.TCNT=0; /*カウンタクリア*/
    ITU3.GRA=( ( (unsigned short int)(2500*25) )>>3 )-1; /*FRA3 2.5msec周期でTCNTはクリア これは固定*/
}

void startMultiPulse()
{
    ITU3.TIER.BIT.IMIEA=1; /*GRA3コンペアマッチで割込*/
    E_INT(); /*CPU割り込み許可*/
    IntCounter=0;
/* ITU3.TCNT=ITU3.GRA-5;*/
    SELECT_PORT.DR.BYTE=portout[IntCounter];
    ITU.TSTR.BIT.STR3=1; /*ITU ch3カウントスタート*/
}

#pragma asm
    .SECTION    MYVEC, DATA, LOCATE=H'000090
    .ORG        H'000090  ;IMIA3
    .DATA.L     _imia3INT
    .DATA.L     _imib3INT
    .ORG        H'0000A0  ;IMIA4
    .DATA.L     _imia4INT
    .DATA.L     _imib4INT
    .SECTION    P,CODE,ALIGN=2 ;これを忘れてはいけない
#pragma endasm

 

#pragma interrupt(imia3INT)/*この名前の関数は割り込みルーチン仕様である*/
                           /*プログラム中から呼び出してはならない*/
/*IMIA3の割り込み関数*/
void imia3INT()
{
    int *pwPtr;
    unsigned short int grb3,gra4,grb4;
    ITU4.TIER.BIT.IMIEB=0; /*GRB4コンペアマッチで割込なし*/
    ITU4.TIER.BIT.IMIEA=0; /*GRA4コンペアマッチで割込なし*/
    ITU3.TIER.BIT.IMIEB=0; /*GRB3コンペアマッチで割込なし*/
    pwPtr=&pulseWidth[0+IntCounter];
    ITU3.GRB=grb3=( ( (unsigned short int)((*pwPtr)*25) )>>3 )-1; /*GRB3 系統0の値を設定*/
    pwPtr+=8;
    ITU4.GRA=gra4=( ( (unsigned short int)((*pwPtr)*25) )>>3 )-1; /*GRA4 系統8の値を設定*/
    pwPtr+=8;
    ITU4.GRB=grb4=( ( (unsigned short int)((*pwPtr)*25) )>>3 )-1; /*GRB4 系統16の値を設定*/
    if (grb3<gra4) {
        if (gra4<grb4) { /*ITU4.GRBが最大*/
            ITU4.TIER.BIT.IMIEB=1; /*GRB4コンペアマッチで割込*/
        } else { /*ITU4.GRAが最大*/
            ITU4.TIER.BIT.IMIEA=1; /*GRA4コンペアマッチで割込*/
        }
    } else {
        if (grb3<grb4) { /*ITU4.GRBが最大*/
            ITU4.TIER.BIT.IMIEB=1; /*GRB4コンペアマッチで割込*/
        } else { /*ITU3.GRBが最大*/
            ITU3.TIER.BIT.IMIEB=1; /*GRB3コンペアマッチで割込*/
        }
    }
    /*ここで3つの割り込みフラッグをクリアする必要がある*/
    ITU3.TSR.BIT.IMFB=0; /*Clear IMFB3*/
    ITU4.TSR.BIT.IMFA=0; /*Clear IMFA4*/
    ITU4.TSR.BIT.IMFB=0; /*Clear IMFB4*/
    /*次のフラッグは自分のもの*/
    ITU3.TSR.BIT.IMFA=0; /*Clear IMFA3*/
}


#pragma interrupt(imib3INT)/*この名前の関数は割り込みルーチン仕様である*/
                           /*プログラム中から呼び出してはならない*/
/*IMIB3の割り込み関数*/
/*割り込みテーブルはIntTbl.srcに書いてある*/
void imib3INT()
{
    IntCounter++;
    IntCounter&=7;
    SELECT_PORT.DR.BYTE=portout[IntCounter];
    ITU3.TSR.BIT.IMFB=0; /*Clear IMFB3*/
}

#pragma interrupt(imia4INT)/*この名前の関数は割り込みルーチン仕様である*/
                           /*プログラム中から呼び出してはならない*/
/*IMIA4の割り込み関数*/
/*割り込みテーブルはIntTbl.srcに書いてある*/
void imia4INT()
{
    IntCounter++;
    IntCounter&=7;
    SELECT_PORT.DR.BYTE=portout[IntCounter];
    ITU4.TSR.BIT.IMFA=0; /*Clear IMFA4*/
}

#pragma interrupt(imib4INT)/*この名前の関数は割り込みルーチン仕様である*/
                           /*プログラム中から呼び出してはならない*/
/*IMIB4の割り込み関数*/
/*割り込みテーブルはIntTbl.srcに書いてある*/
void imib4INT()
{
    IntCounter++;
    IntCounter&=7;
    SELECT_PORT.DR.BYTE=portout[IntCounter];
    ITU4.TSR.BIT.IMFB=0; /*Clear IMFB4*/
}


main()
{
    char ch;
    int i;
    initSCI1(); /*SCI-ch1の初期化*/
    SELECT_PORT.DDR=0xff; /*P3はすべて出力に設定*/
    initMultiPulseGen(); /*multiwidthpulse発生器の初期化*/
    startMultiPulse(); /*multiwidthpulse発生器起動*/
    while (1)
    {
        /*ここでpulseWidth[]の値を変更すれば出力パルス幅が変化する*/
        ch=getCharSCI1();
        if ('A'<=ch && ch<='X') {
            i=ch-'A';
            pulseWidth[i]+=50;
            if (2000<pulseWidth[i]) pulseWidth[i]=2000;
        } else if ('a'<=ch && ch<='x') {
            i=ch-'a';
            pulseWidth[i]-=50;
            if (pulseWidth[i]<1000) pulseWidth[i]=1000;
        } else if (ch=='0') {
            for (i=0;i<24;i++) pulseWidth[i]=1500;
        } else if (ch=='1') {
            for (i=0;i<24;i++) pulseWidth[i]=1000+40*i;
        }
    }
}

オシロスコープによる波形観察

ch1はP1B0信号で,系統0,8,16と同期したHパルスが20msec周期で発生している。
ch2はTIOCB3で,系統0,1,2,3,4,5,6,7のパルスが発生している。起動直後なので,
1000,1000,1200,1000,1000,1000,2000,2000μsecのパルスが順に発生し,8パルスでもとに
戻っているのがわかる。なお,P1B0のパルスと同期しているのは,系統0の1000μsecの
パルスである。

ch1はP1B0信号で,系統0,8,16と同期したHパルスが20msec周期で発生している。
ch2はTOCXA4で,系統8,9,10,11,12,13,14,15のパルスが発生している。起動直後なので,
1200,1100,2000,1200,1800,1700,1700,2000μsecのパルスが順に発生し,8パルスでもとに
戻っているのがわかる。なお,P1B0のパルスと同期しているのは,系統8の1200μsecの
パルスである。