RX62Nでステッピングモータ

ステッピングモータ・ドライバを介してステッピングモータを回します．
マイコンとドライバ間の信号は次の通り．
* 回転を示すパルス
* CW/CCW
* ドライバ・レディ

パルスはPWMを使って出力し，パルス数をカウントすることで指定した数のパルスを出力するようにします．

## 開発環境
　e2studioを使いました．コンパイラはGCC for Renesas RXです．

●新しいプロジェクトを作ります．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369631/59728e81-f6f1-7ce0-83e7-aeaceadf2bde.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/369631/cf6e8147-2933-abce-018e-da3abaf7e939.png)


●


## プログラム
　自動生成されたプロジェクトのうち，主に3つのソースコードファイルを編集します．

### メインプログラム
　UART0で受信したテキストコマンドをパースし，`R`コマンドを受信するとRに続く数字を読み取り，その数のパルスをPWMで生成する（デューティ比は50％固定）．
　ステッピングモータの回転速度はパルスの周波数で決まる．これは`S`コマンドで設定できる．
　パルスのカウントはPWMの立下りエッジで割り込みをかけ，割り込みハンドラでカウンタをインクリメントする．
　PWMはTGRBとコンペアマッチで信号`H`となる．
　TGRAとコンペアマッチで信号`L`及びカウンタリセットとなる．このタイミングでTGRA割り込みをかける．

```main.c
#include <iodefine.h>
#include "timer.h"
#include <stdio.h>
#include <stdlib.h>

省略

//UART0 リングバッファ
unsigned char rx_buff[32];	//受信用
unsigned char tx_buff[32];	//送信用

//UART0 データポインタ
volatile int ptr_rx_top,ptr_rx_bottom;
volatile int ptr_tx_top,ptr_tx_bottom;
volatile int tx_flag = 0;  //送信フラグ データ送信中かどうか(0=TX_INACTIVE)

//状態インジケータ用7セグLED
//0:待機状態 1:回転指示受信 2:モータ回転中 3:モータ回転終了 4:モータ回転数指令受信 E:キットカットがありません（エンプティ） U:重大なエラー
int8_t statusLED[10] = {63, 48, 91, 79, 102, 109, 125, 7, 127, 111};

int motorStep; // current number of pulse
int motorStepTarget; // target number of pulse for motor rotation
char pulseDone; //flag of motor rotation was done
unsigned int pwmCyclePercentage;  //percentage of motor rotation speed(default 50%, 1 is minimum)

unsigned char message[64];
unsigned char uartRecieve[32];

void initSCI(int bps)
{
	MSTP(SCI0) = 0;  //Enable SCI0
	PORT2.DDR.BIT.B1 = 0;  //RxD0 is input
	PORT2.ICR.BIT.B1 = 1;  //Enable Input Buffer on RxD0
 	PORT2.DDR.BIT.B0 = 1;  //TxD0 is output
	SCI0.SCR.BYTE = 0;     //Disable Tx/Rx

	SCI0.SMR.BYTE = 0x00;
	//SCI0.SEMR.BIT.ABCS = 1;
	/* 送受信で割込み発生 */
	SCI0.SCR.BIT.RIE = 1;
	SCI0.SCR.BIT.TIE = 1;
    /* 割込み優先度 */
	IPR(SCI0,RXI0) = 0x0F;
	IPR(SCI0,TXI0) = 0x0F;
	/* Clear IR bits for TIE and RIE */
	IR(SCI0, RXI0) = 0;
	IR(SCI0, TXI0) = 0;

	/* 受信・送信割込み許可 */
	IEN(SCI0, RXI0) = 1;
	IEN(SCI0, TXI0) = 1;
	IEN(SCI0, ERI0) = 1;

	SCI0.BRR = PCLK *1000000 / 32 / bps - 1;  //SCI0.BRR = 38;
	SCI0.SCR.BYTE |= 0x30;  //Enable TxRx
	ptr_rx_top = ptr_rx_bottom = 0;  //リングバッファ初期化
	ptr_tx_top = ptr_tx_bottom = 0;
}

/* データの送信 */
void charput(unsigned char output_char)
{
	int tmp;
	tmp = ptr_tx_top + 1;
	tmp = tmp % 32;
	while(tmp == ptr_tx_bottom) ;	//バッファに空きができるまで待つ

	tx_buff[ptr_tx_top] = output_char;
	ptr_tx_top++;
	ptr_tx_top = ptr_tx_top % 32;
	if(tx_flag == 0) {
		tx_flag = 1;
		IEN(SCI0, TXI0) = 1;
		SCI0.SCR.BYTE |= 0xA0; //送信割込み発生
	}
}

/* データの受信　*/
unsigned char charget(void)
{
	unsigned char c;

	if(ptr_rx_bottom == ptr_rx_top)return 255;
	c = rx_buff[ptr_rx_bottom];
	ptr_rx_bottom++;
	ptr_rx_bottom = ptr_rx_bottom % 32;
	return c;
}

/*　文字列の出力 */
void printString(char *s)
{
	while( *s != 0 ) {
		if(( *s == '\r' ) && ( *(s+1) != '\n')) {
			charput('\r');charput('\n');
		} else if( (*s == '\n') && ( *(s-1) != '\r') ) {
			charput('\r');charput('\n');
		} else {
			charput(*s);
		}
		s++;
	}
}

void initPWM(){
	MTUA.TRWER.BIT.RWE = 1;
	MSTP(MTU4) = 0;

	MTUA.TSTR.BIT.CST4 = 0;
	MTUA.TOER.BIT.OE4A = 1;
	IOPORT.PFCMTU.BIT.MTUS4 = 0;

	MTU4.TCR.BIT.TPSC = 0x02;
	MTU4.TCR.BIT.CKEG = 0x00;    //立ち上がりエッジでカウント
	MTU4.TCR.BIT.CCLR = 0x01;    //TGRBのコンペアマッチでクリア
	MTU4.TCNT = 0x0000;		     //カウンタクリア
	MTU4.TIER.BIT.TGIEA = 1;     //TGRA割り込み有効
	MTU4.TIER.BIT.TGIEB = 1;
	MTU4.TIER.BIT.TGIEC = 1;
	IEN(MTU4, TGIA4) = 1;
	IPR(MTU4, TGIA4) = 6;        //割り込みプライオリティの設定必須

	MTU4.TMDR.BYTE = 0x02;       //0x02;//PWMモード1

	MTU4.TIORH.BIT.IOA = 0x01;   //BIT.IOA = 0x02;
	MTU4.TIORH.BIT.IOB = 0x02;
	MTU4.TIORL.BIT.IOC = 0x00;

	unsigned int tmpCycle = 0x180 * pwmCyclePercentage;
	MTU4.TGRA = tmpCycle;
	MTU4.TGRB = tmpCycle >> 1;  //duty is 50%
}

void startPWM(int target){
	if(PORT5.DR.BIT.B1){        //when emergency button have been pushed
		motorStepTarget = 0;
		return;
	}

	PORTC.DR.BYTE = statusLED[2]; //showing motor is rotating
	PORT0.DR.BIT.B5 = 0;       //Motor Enable
	motorStepTarget = target-1;
	MTUA.TSTR.BIT.CST4 =1;     //PWM pulse is stopped
}

int scanCommand(){
	int i = 0;
	int tmpPulseCount = 0;

	do{
		uartRecieve[i++] = charget();
	}while(uartRecieve[i-1] != 255);
	uartRecieve[--i] = '\0';

	printString("\n");
	printString(uartRecieve);
	printString("\n");
	switch(uartRecieve[0]){
	case 'R':
		PORTC.DR.BYTE = statusLED[1]; // showing recieve command of rotation
		tmpPulseCount = atoi(&uartRecieve[1]);
		if(tmpPulseCount < 0) {
			PORT5.DR.BIT.B0 = 1;      //ccw
			startPWM(-tmpPulseCount);
		}else if(tmpPulseCount > 0){
			PORT5.DR.BIT.B0 = 0;      //cw
			startPWM(tmpPulseCount);
		}else{
			tmpPulseCount = 0;
			return 0;
		}
		break;
	case 'S':
		pwmCyclePercentage = atoi(&uartRecieve[1]);
		if(pulseDone) return 0;       //while motor is ratating
		if(pwmCyclePercentage > 0 && pwmCyclePercentage < 101){
			PORTC.DR.BYTE = statusLED[4];
			pwmCyclePercentage = 100 - pwmCyclePercentage;
			unsigned int tmpCycle = 0x180 * pwmCyclePercentage;
			MTU4.TGRA = tmpCycle;
			MTU4.TGRB = tmpCycle >> 1;
		}
		break;
	default:
		return 0;
	}
	return 0;
}

void initIO(){
	PORT5.DDR.BIT.B0 = 1;   //CW/CCW select
	PORT1.DDR.BIT.B5 = 1;   //On Board LED
	PORT5.DDR.BIT.B1 = 0;   //Emergency SW
	PORT0.DDR.BIT.B5 = 1;   //Motor Enable/Disable
	PORT0.DR.BIT.B5 = 1;    //Set Motor Disable
	PORTC.DDR.BYTE = 0xFF;  //7Segment LED for indicater
	PORTC.DR.BYTE = 0;
}

void stopMotor(){
	PORTC.DR.BYTE = statusLED[3]; //
	MTUA.TSTR.BIT.CST4 = 0;
	PORT0.DR.BIT.B5 = 1;          //Motor Disable
	motorStep = 0;
	motorStepTarget = 0;
	pulseDone = 0;
	delay_ms(200);
	PORTC.DR.BYTE = statusLED[0]; //showing mode wait
}

int main(void) {
	pwmCyclePercentage = 50;

	initIO();
	initPWM();
	initTimer();
	initSCI(9600);
	stopMotor();

    while(1) {
    	if(pulseDone){
    		PORT0.DR.BIT.B5 = 1;   //Motor Disable
    		motorStepTarget = 0;
    		pulseDone = 0;
    		printString("ACK\n");  //return ACK Signal to HOST
    	}

    	//when emergency button have been pushed
    	if(PORT5.DR.BIT.B1) stopMotor();

    	if(ptr_rx_bottom != ptr_rx_top){  //when RX buffer was changed, recognizing text command
    		delay_ms(2500);
    		scanCommand();
    	}
    }
    return 0;
}
```

### 割り込みハンドラ

```generate/inthandler.c
#include "interrupt_handlers.h"
#include <iodefine.h>

extern unsigned int utimer;
extern unsigned char rx_buff[32];	//受信用
extern unsigned char tx_buff[32];	//送信用
extern volatile int ptr_rx_top,ptr_rx_bottom;  //データポインタ
extern volatile int ptr_tx_top,ptr_tx_bottom;
extern volatile int tx_flag;  //送信フラグ データ送信中かどうか(0=TX_INACTIVE)
extern int motorStep;
extern int motorStepTarget;
extern char pulseDone;
extern void printString(char*);

省略

// Timer
//;0x0074  CMTU0_CMT1
void  INT_Excep_CMT1_CMI1(void){
	utimer++;
}

省略

// PWM Count
//;0x0218  MTU4_TGIA4
void  INT_Excep_MTU4_TGIA4(void){
	  motorStep++;
	  if(motorStep > motorStepTarget){
		  MTUA.TSTR.BIT.CST4 =0;
		  motorStep = 0;
		  pulseDone = 1;
	  }
}

省略

//(UART0)
//;0x0358  SCI0_ERI0
void  INT_Excep_SCI0_ERI0(void){
	SCI0.SSR.BYTE &= ~0x38;	//エラーフラグクリア
}

//;0x035C  SCI0_RXI0
void  INT_Excep_SCI0_RXI0(void){
	/* Read data */
	PORT1.DR.BIT.B5 ^= 1;
	rx_buff[ptr_rx_top] = SCI0.RDR;
	ptr_rx_top++;
	ptr_rx_top = ptr_rx_top % 32;
}

//;0x0360  SCI0_TXI0
void  INT_Excep_SCI0_TXI0(void){
	/* Write the character out */

	SCI0.TDR = tx_buff[ptr_tx_bottom];
	ptr_tx_bottom++;
	ptr_tx_bottom = ptr_tx_bottom % 32;
	//すべてのデータを送信したら割込み禁止
	if( ptr_tx_bottom == ptr_tx_top ) {
		IEN(SCI0, TXI0) = 0;
		tx_flag = 0;
	}
}


省略

//　(UART2)
//;0x0378  SCI2_ERI2
void  INT_Excep_SCI2_ERI2(void){
	SCI2.SSR.BYTE &= ~0x38;	//エラーフラグクリア
}

//;0x037C  SCI2_RXI2
void  INT_Excep_SCI2_RXI2(void){
	/* Read data */
	PORT1.DR.BIT.B5 ^= 1;
	rx_buff[ptr_rx_top] = SCI2.RDR;
	ptr_rx_top++;
	ptr_rx_top = ptr_rx_top % 32;
}

//;0x0380  SCI2_TXI2
void  INT_Excep_SCI2_TXI2(void){
	/* Write the character out */
	SCI2.TDR = tx_buff[ptr_tx_bottom];
	ptr_tx_bottom++;
	ptr_tx_bottom = ptr_tx_bottom % 32;
	//すべてのデータを送信したら割込み禁止
	if( ptr_tx_bottom == ptr_tx_top ) {
		IEN(SCI2, TXI2) = 0;
		tx_flag = 0;
	}
}
```

```generate/hwinit.c

省略

### ハードウェアの設定
　システムクロックの設定をします．
　UARTの設定に影響するので注意が必要です．

void HardwareSetup(void)
{
	SYSTEM.SCKCR.LONG = 0x00C10100;  //システムクロックなどの設定

省略
```