高速uartシリアル通信で、Stepper-motor (28BYJ-48) を動かす

28BYJ-48は、5線のモノポーラのステッピングモーターです。
 [初回](https://qiita.com/retro_C18_4B/items/e224f302d4185a82fcc5)から[前回](https://qiita.com/retro_C18_4B/items/c7b3a34a35fc55a30c3a)まで、マイコンPIC18FとワンボードコンピューターであるRPI4B(ラズベリーパイ4B)の2.3Mbpsでの高速uart通信を実施しました。

今回は、マイコンPIC側では、uart通信により8bitコードを2個受信して、ステッピングモーター (28BYJ-48) を動かすだけです。一方、Raspberry Pi、Nano Pi、Banana Piなどは、4～6個のコアを搭載した高性能ワンボードコンピューターです。32bitまたは64bitのMPUが1.0～2.4GHzで動作し、高速大容量メモリーも搭載していますから、桁違いに大きな画像ファイルも処理できます。さらに各コアで、カメラからの入力、画像解析、画面表示などの時間の掛かる処理を分担できます。

さらにステッピングモーターを、直接ワンボードコンピューターから動かすこともできます。しかし実際にステッピングモーターを動かす場合、大幅な待ち時間があります。待ち時間を短くすると、ステッピングモーターを素早く動かせそうですが、待ち時間を短縮し過ぎるとステッピングモーターは誤動作します。ステッピングモーターは、内部に複数のコイルが配置されていて、各コイルを順番にON/OFFして回しています。しかしコイルですから、ON→OFFにしても、まだ電流は流れ続けようとして、尾をひきます。逆に、OFF→ONにしても、電流が流れない状態を維持しようとして電流が流れるのを妨げ、少し遅延した後に設定した電流が流れ始めます。この遅延している時に、OFF→ON→OFFと繰り返すと、やっと電流が少し流れ始めた時に、再びOFFとなり電流がゼロになります。つまり回りません。ゆえに遅延することを考慮して、待ち時間が必要です。ワンボードコンピューターの1個のコアは、時間的に99％以上が待ち時間のステッピングモータに常時占有されます。実質的に、ワンボードコンピューターの4コアが３コアに目減りします。

そこで、ここでの試行です。ステッピングモーターを動かすのを、マイコンへ移動すれば、ワンボードコンピューターの1個のコアは、開放され、他の用途に使えます。ワンボードコンピューターからマイコンへ、2.3Mbpsの高速uart通信で8bitコマンドを2個送信するだけです。時間は７μsecです。これ以外、ワンボードコンピューターの1個のコアは他の用途に使えます。マイコン側では、8bitコマンドを2個受信して、ステッピングモーターを指定位置へ動かします。そしてマイコンは、再び8bitコマンドの受信待機です。

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3970076/03e58ac6-57fe-4189-9d10-c4b5bc36cfd7.png)

配線図です。モノポーラステッピングモーター28BYJ-48を動かすのに、TI社製のULN2003を使います。Uartシリアル通信の配線は、[初回](https://qiita.com/retro_C18_4B/items/e224f302d4185a82fcc5)と同じです。
次図は新規に作成した基板の写真です。28BYJ-48とULN2003はアマゾンで購入しました。(28BYJ-48+ULN2003)の4セットで1280円だったので、使用した2セットで640円です。これは完全に動きます。ブランドの互換品をうたう商品は、あたりはずれがありますが。この(28BYJ-48+ULN20034)セットは、アマゾンで１ヶ月100点以上売れている、ヒット商品でした。9V電池はDAISOの110円を2個並列で使っています。カメラはラズベリーパイカメラV2(NoIR)です。今回、ラズベリーパイ4B側のOSは、Buster(x32)を使いました。LED、Crystalなどは面実装タイプを使っています。抵抗はリード線タイプ100本入を購入済なので、まだまだ沢山あります。抵抗・整流ダイオードは、裏面にハンダ付していますので、写真では見えません。

![photo-45.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3970076/c377a6e2-ce0e-9395-1296-c9c0942ab120.jpeg)

ワンボードコンピューター側は、1例としてRaspberry Pi 4B上でPythonプログラムを動かします。高速uart通信で、8bitコマンドを2個を、マイコン側へ送信します。マイコン側で受信した8bitコマンドが、大文字「A」の場合は＋45度（時計回り）、小文字「a」の場合は－45度（反時計回り）、大文字「B」の場合は＋90度（時計回り）、小文字「b」の場合は－90度（反時計回り）、それぞれ回ります。これ以外は回りません。また1個目の8bitコマンドが、水平方向のステッピングモーターです。2個目の8bitコマンドは、垂直方向です。
``` Python
#uart-stepping-motor-dual.py
#Serial Console OFF, Serial ON
#RPI4B Buster(x32) Python 3.7.3 (x32) Thonny, and RUN  or 
#RPI4B bookworm(x64) Python 3.11.2 Thonny, and RUN    

import serial
    
#Open SERIALttyS0
SERIALttyS0 = serial.Serial('/dev/ttyS0', 2304000, timeout=1)
    
#Set SendMessage
SendMessage = "Aa"
    
#Send Command
SERIALttyS0.write(SendMessage.encode())
    
#Receive ReturnMessage and print
binary_ReturnMessage = SERIALttyS0.readline()
ReturnMessage = binary_ReturnMessage.decode()
print(ReturnMessage)
    
#Close SERIALttyS0
SERIALttyS0.close()
```
マイコン側は、1例としてPIC18F2620（またはPIC18F26K22）を使います。マイコン側は、8bitコマンド2個を受信して、2個のステッピングモーターを動かすだけのプログラムです。シンプルなプログラムです。今回は、モノポーラのステッピングモーターの28BYJ-48とパワーユニットULN2003を使っています。[初回](https://qiita.com/retro_C18_4B/items/e224f302d4185a82fcc5)のuart通信に、ステッピングモーター用ドライバーを追加しただけです。2相励磁で動かします。今回も、xc8とC18のプログラムです。PIC18FのXC8プログラムです。
``` 
//main.c    MPLAB  X IDE v6.00,  XC8 Compiler v2.4.0, (C90),  PICkit3
#include <xc.h>

#define	_XTAL_FREQ		36864000
#define LED_A0			LATA0
#define FXMA_CS0_TRIS	TRISC0
#define FXMA_CS0_IO		LATC0
#define TAIN1_TRIS		TRISA1
#define TAIN1_IO		LATA1
#define TBIN1_TRIS		TRISA2
#define TBIN1_IO		LATA2
#define TAIN2_TRIS		TRISA3
#define TAIN2_IO		LATA3
#define TBIN2_TRIS		TRISA4
#define TBIN2_IO		LATA4
#define PAIN1_TRIS		TRISB1
#define PAIN1_IO		LATB1
#define PBIN1_TRIS		TRISB2
#define PBIN1_IO		LATB2
#define PAIN2_TRIS		TRISB3
#define PAIN2_IO		LATB3
#define PBIN2_TRIS		TRISB4
#define PBIN2_IO		LATB4
//PIC18F2620
#pragma config OSC = HSPLL
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#pragma config PWRT = ON
#pragma config BOREN = OFF
#pragma config BORV = 3
#pragma config WDT = OFF
#pragma config WDTPS = 32768
#pragma config CCP2MX = PORTBE
#pragma config PBADEN = OFF
#pragma config LPT1OSC = OFF
#pragma config MCLRE = ON
#pragma config STVREN = OFF
#pragma config LVP = OFF
#pragma config XINST = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CP2 = OFF
#pragma config CP3 = OFF
#pragma config CPB = OFF
#pragma config CPD = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRT2 = OFF
#pragma config WRT3 = OFF
#pragma config WRTC = OFF
#pragma config WRTB = OFF
#pragma config WRTD = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTR2 = OFF
#pragma config EBTR3 = OFF
#pragma config EBTRB = OFF
////PIC18F26k22
//#pragma config FOSC = HSHP
//#pragma config PLLCFG = ON
//#pragma config PRICLKEN = ON
//#pragma config FCMEN = OFF
//#pragma config IESO = OFF
//#pragma config PWRTEN = ON
//#pragma config BOREN = OFF
//#pragma config BORV = 190
//#pragma config WDTEN = OFF
//#pragma config WDTPS = 32768
//#pragma config CCP2MX = PORTC1
//#pragma config PBADEN = OFF
//#pragma config CCP3MX = PORTB5
//#pragma config HFOFST = OFF
//#pragma config T3CMX = PORTC0
//#pragma config P2BMX = PORTB5
//#pragma config MCLRE = EXTMCLR
//#pragma config STVREN = OFF
//#pragma config LVP = OFF
//#pragma config XINST = OFF
//#pragma config CP0 = OFF
//#pragma config CP1 = OFF
//#pragma config CP2 = OFF
//#pragma config CP3 = OFF
//#pragma config CPB = OFF
//#pragma config CPD = OFF
//#pragma config WRT0 = OFF
//#pragma config WRT1 = OFF
//#pragma config WRT2 = OFF
//#pragma config WRT3 = OFF
//#pragma config WRTC = OFF
//#pragma config WRTB = OFF
//#pragma config WRTD = OFF
//#pragma config EBTR0 = OFF
//#pragma config EBTR1 = OFF
//#pragma config EBTR2 = OFF
//#pragma config EBTR3 = OFF
//#pragma config EBTRB = OFF

static unsigned char vBANK01[256];
static unsigned char vBuffer[32];
near uint8_t i, DirectionT, DirectionP;
near unsigned char Temp1;
near uint16_t Wi, Wi_last, iT, iT_last, iP, iP_last;
near uint32_t Di;

//   stepper-motor functions
void tCW_pCW(near uint16_t iT_last, near uint16_t iP_last);
void tCCW_pCW(near uint16_t iT_last, near uint16_t iP_last);
void tCW_pCCW(near uint16_t iT_last, near uint16_t iP_last);
void tCCW_pCCW(near uint16_t iT_last, near uint16_t iP_last);

void main()
{
    //Analog setting 2620
    ADCON0 = 0x00;
    ADCON1 = 0x00;
    ////Analog setting 26k22
    //ANSELA = 0b00000000 ;    // all digital
    //ANSELB = 0b00000000 ;
    //ANSELC = 0b00000000 ;
    TRISA  = 0b00000000 ;     // all output
    TRISB  = 0b00000000 ;
    TRISC  = 0b00000000 ;
    PORTA  = 0b00000000 ;
    PORTB  = 0b00000000 ;
    PORTC  = 0b00000000 ;
    /// uart init
    // C port	//1			0		0		1		0		0		0		0
    //			//RX		TX		SDO		SDI		SCK		C2		C1		C0
    TRISC = 0x90;
    // TXSTA	//0			0		1		0		0		1		0		0
    //			//CSRC		TX9		TXEN	SYNC	-		BRGH	TRMT	TX9D
    TXSTA = 0x24;
    // RCSTA	//1			0		0		1		0		0		0		0
    //			//SPEN		RX9		SREN	CREN	ADDEN	FERR	OERR	RX9D
    RCSTA = 0x90;
    // BAUDCON	//0			0		0		0		0		0		0		0
    //			//ABDOVF	RCIDL	RXDTP	TXCKP	BRG16	-		WUE		ABDEN
    BAUDCON = 0x00;	
    // SPBRG	(36.864 MHz)	
    // SPBRG = 0x13,  013H=19, 36.864/(16(1+19)) =	115.2 Kbps
    // SPBRG = 0x04,  004H=4,  36.864/(16(1+4))  =	460.8 Kbps
    // SPBRG = 0x01,  001H=1,  36.864/(16(1+1))  =	1.152 Mbps
    // SPBRG = 0x00,  000H=0,  36.864/(16(1+0))  =	2.304 Mbps
    SPBRG = 0x00;

    FXMA_CS0_TRIS =0;
    FXMA_CS0_IO=0;

    while(1) {
        LED_A0 = 1;
        Wi=0;
        Wi_last=1;
        for(; ; )
        {
            if (RCSTAbits.OERR)
            {
                RCSTAbits.CREN = 0;
                Temp1 = RCREG;
                RCSTAbits.CREN = 1;
            }
            if(RCIF==1) 
            {
                vBANK01[Wi]  = RCREG;
                Wi++;
            }
            if(Wi>Wi_last) {  break;  }
        }
        vBuffer[0] = vBANK01[0];
        vBuffer[1] = vBANK01[1];
        vBuffer[2] = 0x0A;  // LF
        Wi=0;
        Wi_last=2;
        for(; ; )
        {
            //2620
            if(TRMT==1)
            ////26k22
            //if(TXSTAbits.TRMT==1)
            {
                TXREG  =  vBuffer[Wi];
                Wi++;
            }
            if(Wi>Wi_last) {  break;  }
        }

        /// 2-phase mode on full-step drive
        LED_A0 = 0;

        // preparation of the received commands
        switch(vBuffer[0])
        {
            case 'A':
                DirectionT =1;    // CW
                iT_last=64;         // 45 degree
                break;
            case 'a':
                DirectionT =2;    // CCW
                iT_last=64;         // 45 degree
                break;
            case 'B':
                DirectionT =1;    // CW
                iT_last=128;       // 90 degree
                break;
            case 'b':
                DirectionT =2;    // CCW
                iT_last=128;       // 90 degree
                break;
            default:    
                DirectionT =1;    // CW
                iT_last=0;          // 0 degree
        }
        switch(vBuffer[1])
        {
            case 'A':
                DirectionP =1;    // CW
                iP_last=64;         // 45 degree
                break;
            case 'a':
                DirectionP =2;    // CCW
                iP_last=64;         // 45 degree
                break;
            case 'B':
                DirectionP =1;    // CW
                iP_last=128;       // 90 degree
                break;
            case 'b':
                DirectionP =2;    // CCW
                iP_last=128;       // 90 degree
                break;
            default:    
                DirectionP =1;    // CW
                iP_last=0;          // 0 degree
        }

        /// drive motor
        if(DirectionT==1 && DirectionP==1)              // T-CW & P-CW
        { 
            tCW_pCW(iT_last, iP_last);
        }
        if(DirectionT==2 && DirectionP==1)              // T-CCW & P-CW
        { 
            tCCW_pCW(iT_last, iP_last);
        }
        if(DirectionT==1 && DirectionP==2)              // T-CW & P-CCW
        { 
            tCW_pCCW(iT_last, iP_last);
        }
        if(DirectionT==2 && DirectionP==2)              // T-CCW & P-CCW
        { 
            tCCW_pCCW(iT_last, iP_last);
        }
    }
    FXMA_CS0_IO=1;
}

//////   stepper-motor functions
void tCW_pCW(near uint16_t iT_last, near uint16_t iP_last)
{
    /// T-CW & P-CW
    uint16_t iiT, iiP;
    iiT=0;
    iiP=0;
    for(; ; )
    {
        if(iiT>=iT_last && iiP>=iP_last) {  break;  }
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =0 ;
            TAIN2_IO =0 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =0 ;
            PAIN2_IO =0 ;
            PBIN2_IO =1 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =0 ;
            TAIN2_IO =1 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =0 ;
            PAIN2_IO =1 ;
            PBIN2_IO =1 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =1 ;
            TAIN2_IO =1 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =1 ;
            PAIN2_IO =1 ;
            PBIN2_IO =0 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =1 ;
            TAIN2_IO =0 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =1 ;
            PAIN2_IO =0 ;
            PBIN2_IO =0 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        iiT++;
        iiP++;
    }
}

void tCCW_pCW(near uint16_t iT_last, near uint16_t iP_last)
{
    /// T-CCW & P-CW
    uint16_t iiT, iiP;
    iiT=0;
    iiP=0;
    for(; ; )
    {
        if(iiT>=iT_last && iiP>=iP_last) {  break;  }
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =1 ;
            TAIN2_IO =0 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =0 ;
            PAIN2_IO =0 ;
            PBIN2_IO =1 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =1 ;
            TAIN2_IO =1 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =0 ;
            PAIN2_IO =1 ;
            PBIN2_IO =1 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =0 ;
            TAIN2_IO =1 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =1 ;
            PAIN2_IO =1 ;
            PBIN2_IO =0 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =0 ;
            TAIN2_IO =0 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =1 ;
            PAIN2_IO =0 ;
            PBIN2_IO =0 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        iiT++;
        iiP++;
    }
}

void tCW_pCCW(near uint16_t iT_last, near uint16_t iP_last)
{
    /// T-CW & P-CCW
    uint16_t iiT, iiP;
    iiT=0;
    iiP=0;
    for(; ; )
    {
        if(iiT>=iT_last && iiP>=iP_last) {  break;  }
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =0 ;
            TAIN2_IO =0 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =1 ;
            PAIN2_IO =0 ;
            PBIN2_IO =0 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =0 ;
            TAIN2_IO =1 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =1 ;
            PAIN2_IO =1 ;
            PBIN2_IO =0 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =1 ;
            TAIN2_IO =1 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =0 ;
            PAIN2_IO =1 ;
            PBIN2_IO =1 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =1 ;
            TAIN2_IO =0 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =0 ;
            PAIN2_IO =0 ;
            PBIN2_IO =1 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        iiT++;
        iiP++;
    }
}

void tCCW_pCCW(near uint16_t iT_last, near uint16_t iP_last)
{
    /// T-CCW & P-CCW
    uint16_t iiT, iiP;
    iiT=0;
    iiP=0;
    for(; ; )
    {
        if(iiT>=iT_last && iiP>=iP_last) {  break;  }
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =1 ;
            TAIN2_IO =0 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =1 ;
            PAIN2_IO =0 ;
            PBIN2_IO =0 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =1 ;
            TAIN2_IO =1 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =1 ;
            PAIN2_IO =1 ;
            PBIN2_IO =0 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =0 ;
            TAIN2_IO =1 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =0 ;
            PAIN2_IO =1 ;
            PBIN2_IO =1 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =0 ;
            TAIN2_IO =0 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =0 ;
            PAIN2_IO =0 ;
            PBIN2_IO =1 ;
        }
        __delay_ms(2) ;        //  28BYJ-48
        iiT++;
        iiP++;
    }
}
``` 

PIC18FのC18プログラムです。includeに必要なHardwareProfile.hとConfiguration Bitsの画面コピーも添付します。
``` 
/// MainDemo.c     MPLAB_IDE_8_92,  MPLAB_C18_3.46,  PICkit3
#include <p18f2620.h>
//#include <p18f26k22.h>/
#include <GenericTypeDefs.h>
#include <delays.h>
#include <HardwareProfile.h>

#pragma udata bank1  = 0x100
static unsigned char vBANK01[256];
#pragma udata bank2 = 0x200
static unsigned char vBuffer[32];

#pragma udata access my_access
near unsigned char Temp1;
near BYTE i, DirectionT, DirectionP;
near WORD Wi, Wi_last, iT, iT_last, iP, iP_last;
near DWORD Di;

//   stepping motor functions
void tCW_pCW(near WORD iT_last, near WORD iP_last);
void tCCW_pCW(near WORD iT_last, near WORD iP_last);
void tCW_pCCW(near WORD iT_last, near WORD iP_last);
void tCCW_pCCW(near WORD iT_last, near WORD iP_last);

#pragma code
void main(void){
	//2620
	ADCON0 = 0x00;
	ADCON1 = 0x00;
	////26k22
	//ANSELA = 0b00000000 ;    // all digital
	//ANSELB = 0b00000000 ;
	//ANSELC = 0b00000000 ;
	TRISA  = 0b00000000 ;      // all output
	TRISB  = 0b00000000 ;
	TRISC  = 0b00000000 ;
	PORTA  = 0b00000000 ;
	PORTB  = 0b00000000 ;
	PORTC  = 0b00000000 ;
	// C port		//1			0		0		1		0		0		0		0
	//				//RX		TX		SDO		SDI		SCK		C2		C1		C0
	TRISC = 0x90;
	// TXSTA		//0			0		1		0		0		1		0		0
	//				//CSRC		TX9		TXEN	SYNC	-		BRGH	TRMT	TX9D
	TXSTA = 0x24;
	// RCSTA		//1			0		0		1		0		0		0		0
	//				//SPEN		RX9		SREN	CREN	ADDEN	FERR	OERR	RX9D
	RCSTA = 0x90;
	// BAUDCON		//0			0		0		0		0		0		0		0
	//				//ABDOVF	RCIDL	RXDTP	TXCKP	BRG16	-		WUE		ABDEN
	BAUDCON = 0x00;	
	// SPBRG	(36.864 MHz)	
	// SPBRG = 0x13,  013H=19,  36.864/(16(1+19)) =	115.2 Kbps
	// SPBRG = 0x04,  004H=4,  36.864/(16(1+4)) =	460.8 Kbps
	// SPBRG = 0x01,  001H=1,  36.864/(16(1+1)) =	1.152 Mbps
	// SPBRG = 0x00,  000H=0,  36.864/(16(1+0)) =	2.304 Mbps
	SPBRG = 0x00;

	FXMA_CS0_TRIS = 0;
	FXMA_CS0_IO = 0;

	while(1){
		LED0_IO = 1;
		for(Wi = 0; Wi < 2; Wi++)
		{
			if (RCSTAbits.OERR) 
			{
				RCSTAbits.CREN = 0;
				Temp1 = RCREG;
				RCSTAbits.CREN = 1;
			}
			_asm BTFSS  PIR1, 5, 0 _endasm      // RCIF in P1R1 = 5
			_asm BRA -2	       _endasm
			vBANK01[Wi]  = RCREG;
		}
		vBuffer[0] = vBANK01[0];
		vBuffer[1] = vBANK01[1];
		vBuffer[2] = 0x0A;  // LF
		for(i = 0; i < 3; i++)		
		{
			_asm BTFSS TXSTA, 1, 0 _endasm      // TRMT in TXSTA = 1
			_asm BRA   -2	       _endasm
			TXREG  =  vBuffer[i];
		}

		/// 2-phase mode on full-step drive
		LED0_IO = 0;
		// preparation of the received commands
		switch(vBuffer[0])
		{
			case 'A':	
				DirectionT =1;    // CW
				iT_last=64;         // 45 degree
				break;
			case 'a':
				DirectionT =2;    // CCW
				iT_last=64;         // 45 degree
				break;
			case 'B':
				DirectionT =1;    // CW
				iT_last=128;       // 90 degree
				break;
			case 'b':
				DirectionT =2;    // CCW
				iT_last=128;       // 90 degree
				break;
			default:    
				DirectionT =1;    // CW
				iT_last=0;          // 0 degree
		}
		switch(vBuffer[1])
		{
			case 'A':
				DirectionP =1;    // CW
				iP_last=64;         // 45 degree
				break;
			case 'a':
				DirectionP =2;    // CCW
				iP_last=64;         // 45 degree
				break;
			case 'B':
				DirectionP =1;    // CW
				iP_last=128;       // 90 degree
				break;
			case 'b':
				DirectionP =2;    // CCW
				iP_last=128;       // 90 degree
				break;
			default:    
				DirectionP =1;    // CW
				iP_last=0;          // 0 degree
		}

		/// drive motor
		if(DirectionT==1 && DirectionP==1)              // T-CW & P-CW
		{ 
			tCW_pCW(iT_last, iP_last);
		}
		if(DirectionT==2 && DirectionP==1)              // T-CCW & P-CW
		{ 
			tCCW_pCW(iT_last, iP_last);
		}
		if(DirectionT==1 && DirectionP==2)              // T-CW & P-CCW
		{ 
			tCW_pCCW(iT_last, iP_last);
		}
		if(DirectionT==2 && DirectionP==2)              // T-CCW & P-CCW
		{ 
			tCCW_pCCW(iT_last, iP_last);
		}
	}
	FXMA_CS0_IO = 1;		
}

#pragma tmpdata
//////   stepper-motor functions
void tCW_pCW(near WORD iT_last, near WORD iP_last)
{
    /// T-CW & P-CW
    WORD iiT, iiP;
    iiT=0;
    iiP=0;
    for(; ; )
    {
        if(iiT>=iT_last && iiP>=iP_last) {  break;  }
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =0 ;
            TAIN2_IO =0 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =0 ;
            PAIN2_IO =0 ;
            PBIN2_IO =1 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =0 ;
            TAIN2_IO =1 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =0 ;
            PAIN2_IO =1 ;
            PBIN2_IO =1 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =1 ;
            TAIN2_IO =1 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =1 ;
            PAIN2_IO =1 ;
            PBIN2_IO =0 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =1 ;
            TAIN2_IO =0 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =1 ;
            PAIN2_IO =0 ;
            PBIN2_IO =0 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        iiT++;
        iiP++;
    }
}

void tCCW_pCW(near WORD iT_last, near WORD iP_last)
{
    /// T-CCW & P-CW
    WORD iiT, iiP;
    iiT=0;
    iiP=0;
    for(; ; )
    {
        if(iiT>=iT_last && iiP>=iP_last) {  break;  }
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =1 ;
            TAIN2_IO =0 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =0 ;
            PAIN2_IO =0 ;
            PBIN2_IO =1 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =1 ;
            TAIN2_IO =1 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =0 ;
            PAIN2_IO =1 ;
            PBIN2_IO =1 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =0 ;
            TAIN2_IO =1 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =1 ;
            PAIN2_IO =1 ;
            PBIN2_IO =0 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =0 ;
            TAIN2_IO =0 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =1 ;
            PAIN2_IO =0 ;
            PBIN2_IO =0 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        iiT++;
        iiP++;
    }
}

void tCW_pCCW(near WORD iT_last, near WORD iP_last)
{
    /// T-CW & P-CCW
    WORD iiT, iiP;
    iiT=0;
    iiP=0;
    for(; ; )
    {
        if(iiT>=iT_last && iiP>=iP_last) {  break;  }
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =0 ;
            TAIN2_IO =0 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =1 ;
            PAIN2_IO =0 ;
            PBIN2_IO =0 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =0 ;
            TAIN2_IO =1 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =1 ;
            PAIN2_IO =1 ;
            PBIN2_IO =0 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =1 ;
            TAIN2_IO =1 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =0 ;
            PAIN2_IO =1 ;
            PBIN2_IO =1 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =1 ;
            TAIN2_IO =0 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =0 ;
            PAIN2_IO =0 ;
            PBIN2_IO =1 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        iiT++;
        iiP++;
    }
}

void tCCW_pCCW(near WORD iT_last, near WORD iP_last)
{
    /// T-CCW & P-CCW
    WORD iiT, iiP;
    iiT=0;
    iiP=0;
    for(; ; )
    {
        if(iiT>=iT_last && iiP>=iP_last) {  break;  }
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =1 ;
            TAIN2_IO =0 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =1 ;
            PAIN2_IO =0 ;
            PBIN2_IO =0 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =1 ;
            TAIN2_IO =1 ;
            TBIN2_IO =0 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =1 ;
            PAIN2_IO =1 ;
            PBIN2_IO =0 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =0 ;
            TBIN1_IO =0 ;
            TAIN2_IO =1 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =0 ;
            PBIN1_IO =0 ;
            PAIN2_IO =1 ;
            PBIN2_IO =1 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        if(iiT<iT_last){
            TAIN1_IO =1 ;
            TBIN1_IO =0 ;
            TAIN2_IO =0 ;
            TBIN2_IO =1 ;
        }
        if(iiP<iP_last){
            PAIN1_IO =1 ;
            PBIN1_IO =0 ;
            PAIN2_IO =0 ;
            PBIN2_IO =1 ;
        }
        Delay10KTCYx(2) ;        //  28BYJ-48
        iiT++;
        iiP++;
    }
}
``` 

C18用のHardwareProfile.hです。
``` 
// HardwareProfile.h
#ifndef __HARDWARE_PROFILE_H
#define __HARDWARE_PROFILE_H
// Clock frequency value.  This value is used to calculate Tick Counter value
	#define GetSystemClock()		(36864000ul)		  // 9.216MHz Crystal,  PLLx4
	#define GetInstructionClock()	(GetSystemClock()/4)
	#define GetPeripheralClock()	GetInstructionClock()
// IO pins
	#define 	LED0_TRIS		(TRISAbits.TRISA0)	// LED
	#define 	LED0_IO			(LATAbits.LATA0)
    #define		FXMA_CS0_TRIS	(TRISCbits.TRISC0)	// fxma OE
	#define		FXMA_CS0_IO		(LATCbits.LATC0)
	#define		TAIN1_TRIS		(TRISAbits.TRISA1)
	#define		TAIN1_IO		(LATAbits.LATA1)
	#define		TBIN1_TRIS		(TRISAbits.TRISA2)
	#define		TBIN1_IO		(LATAbits.LATA2)
	#define		TAIN2_TRIS		(TRISAbits.TRISA3)
	#define		TAIN2_IO		(LATAbits.LATA3)
	#define		TBIN2_TRIS		(TRISAbits.TRISA4)
	#define		TBIN2_IO		(LATAbits.LATA4)
	#define		PAIN1_TRIS		(TRISBbits.TRISB1)
	#define		PAIN1_IO		(LATBbits.LATB1)
	#define		PBIN1_TRIS		(TRISBbits.TRISB2)
	#define		PBIN1_IO		(LATBbits.LATB2)
	#define		PAIN2_TRIS		(TRISBbits.TRISB3)
	#define		PAIN2_IO		(LATBbits.LATB3)
	#define		PBIN2_TRIS		(TRISBbits.TRISB4)
	#define		PBIN2_IO		(LATBbits.LATB4)
#endif
```
C18用2620のConfiguration Bitsの画面コピーです。
![Configuration Bits PIC18F2620.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3970076/40e9025e-3034-73f3-8fe0-15c9ac0d6e60.png)

C18用26K22のConfiguration Bitsの画面コピーです。
![Configuration Bits PIC18F26K22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3970076/48b94953-15af-9933-e89f-94d4630011aa.png)

スマートなプログラムではありませんが、ぎこちない動作ながら、どうにか動きます。よく見かけるプログラムです。何かのお役に立てるのであれば、変更なども含め、ご自由にお使いください。

最後に、今回使用したモーターに関して：　モノポーラのステッピングモーター28BYJ-48は、今回の120Hz以上速く動かすことはできません。0.703度/8msですから、32msでは2.812度しか動きません。45度回転させるのに、約500msもかかります。しかしHOLD Torqueは、わずか250mAの消費電流で約0.4 Kgf･cmもありますから、コスパは最高です。重量もわずか40gです。モバイルバッテリーで、微小角を動かしながらの静止保持に最適です。今回は、ラズパイカメラ等のジンバルなども想定していました。

一方、さらに大きなHOLD Torqueが必要な場合、バイポーラのステッピングモーターST-42BYH1004-5013（Nema17相当品）は、HOLD Torqueが10倍の4.4 Kgf･cmもあります。パワーユニットをDRV8835に変え、プログラム中のfunctionsの待ち時間を変えるだけで動かすことができます。ただし消費電流が1個1Aですから、2個で2Aです。相当大きなモバイルバッテリーが必要です。さらに重量も10倍の400gです。XYプロッター・3Dプリンターに使われているNema17に近い性能があり、小さいながら本格的なステッピングモーターです。そして28BYJ-48とは異なり、相当速く回転させることができます。XYプロッターが、素早く動いて位置決めするイメージです。スッ、スッ、と素早く動きます。しかし値段も10倍します。構造的にも、さらに精密ですから。

結局、28BYJ-48の回転が遅い根本的な原因は、トルクをかせぐために、ギヤ比を1/64にしているためのようです。ギヤ歯車を外すと、比較的簡素な構造の28BYJ-48は、トルクが得られません。28BYJ-48の位置付けは、ホビー用のようです。（再び、但しホビーながら、待ち時間を2ms→5msにすると確実に動きます。）