28BYJ-48は、5線のモノポーラのステッピングモーターです。
初回から前回まで、マイコン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コアが3コアに目減りします。
そこで、ここでの試行です。ステッピングモーターを動かすのを、マイコンへ移動すれば、ワンボードコンピューターの1個のコアは、開放され、他の用途に使えます。ワンボードコンピューターからマイコンへ、2.3Mbpsの高速uart通信で8bitコマンドを2個送信するだけです。時間は7μsecです。これ以外、ワンボードコンピューターの1個のコアは他の用途に使えます。マイコン側では、8bitコマンドを2個受信して、ステッピングモーターを指定位置へ動かします。そしてマイコンは、再び8bitコマンドの受信待機です。
配線図です。モノポーラステッピングモーター28BYJ-48を動かすのに、TI社製のULN2003を使います。Uartシリアル通信の配線は、初回と同じです。
次図は新規に作成した基板の写真です。28BYJ-48とULN2003はアマゾンで購入しました。(28BYJ-48+ULN2003)の4セットで1280円だったので、使用した2セットで640円です。これは完全に動きます。ブランドの互換品をうたう商品は、あたりはずれがありますが。この(28BYJ-48+ULN20034)セットは、アマゾンで1ヶ月100点以上売れている、ヒット商品でした。9V電池はDAISOの110円を2個並列で使っています。カメラはラズベリーパイカメラV2(NoIR)です。今回、ラズベリーパイ4B側のOSは、Buster(x32)を使いました。LED、Crystalなどは面実装タイプを使っています。抵抗はリード線タイプ100本入を購入済なので、まだまだ沢山あります。抵抗・整流ダイオードは、裏面にハンダ付していますので、写真では見えません。
ワンボードコンピューター側は、1例としてRaspberry Pi 4B上でPythonプログラムを動かします。高速uart通信で、8bitコマンドを2個を、マイコン側へ送信します。マイコン側で受信した8bitコマンドが、大文字「A」の場合は+45度(時計回り)、小文字「a」の場合は-45度(反時計回り)、大文字「B」の場合は+90度(時計回り)、小文字「b」の場合は-90度(反時計回り)、それぞれ回ります。これ以外は回りません。また1個目の8bitコマンドが、水平方向のステッピングモーターです。2個目の8bitコマンドは、垂直方向です。
#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を使っています。初回の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の画面コピーです。
C18用26K22のConfiguration Bitsの画面コピーです。
スマートなプログラムではありませんが、ぎこちない動作ながら、どうにか動きます。よく見かけるプログラムです。何かのお役に立てるのであれば、変更なども含め、ご自由にお使いください。
最後に、今回使用したモーターに関して: モノポーラのステッピングモーター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にすると確実に動きます。)