cvl-robot's diary

研究ノート メモメモ https://github.com/dotchang/

コマンド方式サーボモータのコマンド体系の研究

(編集中)
日本国内でよく使われているコマンド方式サーボモータのコマンド体系を調べて、良い方法を探ります。

1. 近藤科学

kondo-robot.com

送信コマンド
CMD + SC + 3~N-1(DATA)

返値
CMD + SC + 3~N-1(DATA) + R_CMD + SC + DATA(SPD)

SCはサブコマンドの略らしい。
チェックサムは、なし。
単純を目指しているようですが、いろいろなバージョンがありあまり整理されていない感じ。

2. FUTABA

www.futaba.co.jp

送信パケット
Header + ID + Flag + Address + Length + Count + Data + Checksum

リターンパケット
Header + ID + Flags + Address + Length + Count + Data + Sum

チェックサムは、
Check Sum=ID XOR Flag XOR Address XOR Count XOR Data

比較的使いやすい命令体系だと思いますが、イレギュラーな書式があったり、メモリ上のデータの配置が悪かったりして、ちょっと痒さが残る感じ。

3. Robotis-DYNAMIXEL

Dynamixel

命令コマンド
0xFF + 0xFF + ID + LENGTH + INSTRUCTION + PARAMETER1 + ・・・ + PARAMETER_N + CHECK_SUM

返信コマンド
0xFF + 0xFF + ID + LENGTH + ERROR + PARAMTER1 + PARAMTER2 + ・・・ + PARAMETER_N + CHECK_SUM

チェックサムは、
Check Sum=~(ID + Length + Instruction + Parameter1 + ・・・ + Parameter_N)
~はNOT Bit演算子。素直な足し算方式なので、パラメータの順番の入れ替わりに弱い。

命令体系やメモリ上のデータの並びはとても整理されていて、よくできています。
真似をするならコレですかね。


RS485
第125号(2010年11月2日発行) 技術レポート「RS-485の通信プロトコルについて」|ソフテックだより|株式会社ソフテック

チェックサムの例(単純足し算)
http://www.cypress.com/file/250776/download

特性の分からない3相DCブラシレスモーターをソフトウェアエンコーダを搭載したTiのマイコンボードLAUNCHXL-F28069MとモータドライバBOOSTXL-DRV8305EVMを使って動かす(その6)

(編集中)
アナログ入出力だけでなく、シリアル接続なども使えた方がシステム構築には便利です。
ただ、C2000シリーズのマイコンでSCI(UART)接続しようというのは、Linuxの10倍ぐらい面倒くさいです。

Githubからサンプルプログラム

LAUNCHXL-F28027F+BOOSTXL-DRV8305EVMの組み合わせで、SCI(UART)を使うサンプルプロジェクトとして、こちらの掲示板の
InstaSPIN F28069MPZT : how to use UART/SCI communication to terminal? - InstaSPIN Motor Solutions Forum - C2000™ Microcontrollers - TI E2E Community
のスレッドで紹介されているAndrew Buckinさんのproj_lab05aの改造版
github.com
を参照すると、理解が早そうです。

ソースコードのdiffを取って、変更箇所を見ていきましょう。6つのソースファイルがあります。

proj_lab05a.c
hal/hal.c
hal/hal.h
hal/hal_obj.h
sci/sci.c
sci/sci.h

比較元のファイルの場所は、次の通りです。
C:\ti\motorware\motorware_1_01_00_16\sw\solutions\instaspin_foc\src
C:\ti\motorware\motorware_1_01_00_16\sw\modules\hal\boards\boostxldrv8305_revA\f28x\f2806x\src
C:\ti\motorware\motorware_1_01_00_16\sw\drivers\sci\src\32b\f28x\f2802x

proj_lab05a.c

// **************************************************************************
// the defines

#define uint8_t char

#define ECHO_ON 1 // set 1 to show input and 0 to disable it

#define LED_BLINK_FREQ_Hz   5

#define BUF_LEN 8 // length of the command buffer


// **************************************************************************
// the globals

uint32_t ECap1IntCount = 0;
uint32_t CAP_DATA[] = {0,0/*,0,0*/};

char cmdBuf[BUF_LEN]; // Input/command buffer
uint8_t bufPos = 0; // Position of the next char in the buffer
uint8_t cmdReady = 0; // Indicates that a command is ready for execution and blocks further input until reset to 0.

//Added by Maya
#ifdef SCI
	char * msg;
	uint16_t ReceivedChar = 0;
	//uint16_t RXISRCount = 0;
	//uint16_t SysCounter = 0;

#endif
//Added by Maya
#ifdef SCI

interrupt void sciaISR(void) {
	//RXISRCount++;

	ReceivedChar = HAL_sciaRead(halHandle) & 0x00FF;

	//Handling escape.
	if(ReceivedChar != 27){
		//Handling carriage return/new line.
		if(ReceivedChar != '\r' && ReceivedChar != '\n' && cmdReady == 0){
			//Show input.
			if(ECHO_ON){
				HAL_sciaWrite(halHandle, ReceivedChar);
			}
			//Handling backspace/delete.
			if(ReceivedChar != 127 && ReceivedChar != 8){
				bufPos++;
				//If the end of the buffer wasn't reached, add new char to the buffer.
				if(bufPos < BUF_LEN){
					cmdBuf[bufPos-1] = (char)ReceivedChar; //Adding char.
					cmdBuf[bufPos] = '\0'; //Appending null to indicate the end of the string.
				}
			}else{
				//If the beginning of the buffer wasn't reached, remove the previous char from the buffer.
				if(bufPos > 0){
					bufPos--;
					//Only delete if within buffer range.
					if(bufPos < BUF_LEN){
						cmdBuf[bufPos] = '\0';
					}
				}
			}
		}else if(cmdReady == 0){ //If enter was pressed, start new line and set cmdReady flag.
			msg = "\n\r";
			HAL_sciaWriteMsg(halHandle, msg);
			cmdReady = 1;
		}
	}else{ //If escape is pressed, reset command buffer and clear cmdReady flag.
		cmdReady = 0;
		bufPos = 0;
		cmdBuf[0] = '\0';
	}

	HAL_sciaClearRxFifoOvf(halHandle);
	HAL_sciaClearRxFifoInt(halHandle);

	HAL_pieAckInt(halHandle,PIE_GroupNumber_9);		// Issue PIE ack INT9

	return;
}

#endif
//Added by Dmitri Ranfft on 16.09.2015
#ifdef CAP
__interrupt void ecap1ISR(void)
{
	//scia_msg("interrupt\n\r");
    // Cap input is syc'ed to SYSCLKOUT so there may be
    // a +/- 1 cycle variation

	CAP_DATA[0] = CAP_getCap1(halHandle->capHandle);
	CAP_DATA[1] = CAP_getCap2(halHandle->capHandle);
	//CAP_DATA[2] = CAP_getCap3(halHandle->capHandle);
	//CAP_DATA[3] = CAP_getCap4(halHandle->capHandle);

    ECap1IntCount++;

    CAP_clearInt(halHandle->capHandle, CAP_Int_Type_CEVT2);
    CAP_clearInt(halHandle->capHandle, CAP_Int_Type_Global);
    CAP_rearm(halHandle->capHandle);

    // Acknowledge this interrupt to receive more interrupts from group 4
    PIE_clearInt(halHandle->pieHandle, PIE_GroupNumber_4);
}
#endif

eCAPというのが、また聞きなれないC2000特有の機能で、こちらに解説があります。
C2000の便利なeCAP機能を使って見ましょう - プロセッサ(DSP / ARM / MCU) - Japanese E2E (日本語コミュニティ) - TI E2E Community

hal

hal.c

607--609

  // Added by Dmitri Ranfft on 16.09.2015
  // Initialize the eCAP
  obj->capHandle = CAP_init((void *)CAP1_BASE_ADDR, sizeof(CAP_Obj));

652--656

  //Added by Maya
  #ifdef SCI
  	// Init SCI A registers
  	obj->sciaHandle = SCI_init((void *) SCIA_BASE_ADDR, sizeof(SCI_Obj));
  #endif

787--795

 //Added by Maya
#ifdef SCI
 HAL_setupSCI(handle);
#endif

 //Added by Dmitri Ranfft on 16.09.2015
#ifdef CAP
 HAL_setupCAP(handle);
#endif

994--1020

  // PWMC_H
  // Edited by Dmitri Ranfft on 16.09.2015 (changed from EPWM3 to 4)
  GPIO_setMode(obj->gpioHandle,GPIO_Number_6,GPIO_6_Mode_EPWM4A);

  // PWMC_L
  // Edited by Dmitri Ranfft on 16.09.2015 (changed from EPWM3 to 4)
  GPIO_setMode(obj->gpioHandle,GPIO_Number_7,GPIO_7_Mode_EPWM4B);

  // eCAP1
  // Added by Dmitri Ranfft on 16.09.2015
  GPIO_setPullUp(obj->gpioHandle, GPIO_Number_5, GPIO_PullUp_Enable);
  GPIO_setQualification(obj->gpioHandle, GPIO_Number_5, GPIO_Qual_Sync);
  GPIO_setMode(obj->gpioHandle,GPIO_Number_5,GPIO_5_Mode_ECAP1);

    // EN_GATE
  // Edited by Dmitri Ranfft on 16.09.2015 (changed from GPIO 6 to 4)
  GPIO_setMode(obj->gpioHandle,GPIO_Number_4,GPIO_4_Mode_GeneralPurpose);
  GPIO_setLow(obj->gpioHandle,GPIO_Number_4);
  GPIO_setDirection(obj->gpioHandle,GPIO_Number_4,GPIO_Direction_Output);
  
    // WAKE
  // Edited by Dmitri Ranfft on 16.09.2015 (changed from GPIO 7 to 12)
  GPIO_setMode(obj->gpioHandle,GPIO_Number_12,GPIO_12_Mode_GeneralPurpose);
  GPIO_setHigh(obj->gpioHandle,GPIO_Number_12);
  GPIO_setDirection(obj->gpioHandle,GPIO_Number_12,GPIO_Direction_Output);

  // No Connection
  // Disabled by Dmitri Ranfft on 16.09.2015
  //GPIO_setMode(obj->gpioHandle,GPIO_Number_12,GPIO_12_Mode_GeneralPurpose);

修正前は、

  // PWMC_H
  GPIO_setMode(obj->gpioHandle,GPIO_Number_4,GPIO_4_Mode_EPWM3A);

  // PWMC_L
  GPIO_setMode(obj->gpioHandle,GPIO_Number_5,GPIO_5_Mode_EPWM3B);

    // EN_GATE
  GPIO_setMode(obj->gpioHandle,GPIO_Number_6,GPIO_6_Mode_GeneralPurpose);
  GPIO_setLow(obj->gpioHandle,GPIO_Number_6);
  GPIO_setDirection(obj->gpioHandle,GPIO_Number_6,GPIO_Direction_Output);
  
    // WAKE
  GPIO_setMode(obj->gpioHandle,GPIO_Number_7,GPIO_7_Mode_GeneralPurpose);
  GPIO_setHigh(obj->gpioHandle,GPIO_Number_7);
  GPIO_setDirection(obj->gpioHandle,GPIO_Number_7,GPIO_Direction_Output);

  // No Connection
  GPIO_setMode(obj->gpioHandle,GPIO_Number_12,GPIO_12_Mode_GeneralPurpose);

1037--1045

  // nFAULT
  //GPIO_setMode(obj->gpioHandle,GPIO_Number_28,GPIO_28_Mode_TZ2_NOT); // Edited by Dmitri Ranfft on 15.09.2015
  // SCI_RX
  GPIO_setMode(obj->gpioHandle,GPIO_Number_28,GPIO_28_Mode_SCIRXDA);

  // No connection (TX)
  //GPIO_setMode(obj->gpioHandle,GPIO_Number_29,GPIO_29_Mode_GeneralPurpose); // Edited by Dmitri Ranfft on 15.09.2015
  // SCI_TX
  GPIO_setMode(obj->gpioHandle,GPIO_Number_29,GPIO_29_Mode_SCITXDA);

修正前は、

  // nFAULT
  GPIO_setMode(obj->gpioHandle,GPIO_Number_28,GPIO_28_Mode_TZ2_NOT);

  // No connection (TX)
  GPIO_setMode(obj->gpioHandle,GPIO_Number_29,GPIO_29_Mode_GeneralPurpose);

1348--1502

//Added by Maya
void HAL_setupSCI(HAL_Handle handle) {
#ifdef SCI
	HAL_Obj *obj = (HAL_Obj *) handle;
	char *msg;
	//FIFO
	// SCIFFTX = 0xE040
		SCI_enableChannels(obj->sciaHandle);			// SCI reset
		SCI_enableTxFifoEnh(obj->sciaHandle);		// SCI FIFO transmit enable
		SCI_enableTxFifo(obj->sciaHandle);			// SCI FIFO transmit reset/reenable
		SCI_clearTxFifoInt(obj->sciaHandle);			// SCI clear FIFO transmit interrupt flag
		SCI_disableTxFifoInt(obj->sciaHandle);		// disable FIFO transmit interrupt
		SCI_setTxFifoIntLevel(obj->sciaHandle, SCI_FifoLevel_Empty);	// FIFO interrupt level
		// SCIFFRX = 0x2041
		SCI_enableRxFifo(obj->sciaHandle);			// SCI FIFO receive reset/reenable
		SCI_clearRxFifoInt(obj->sciaHandle);			// SCI clear FIFO receive interrupt flag
		SCI_disableRxFifoInt(obj->sciaHandle);		// disable FIFO receive interrupt
		SCI_setRxFifoIntLevel(obj->sciaHandle, SCI_FifoLevel_1_Word); // FIFO interrupt level

		SCI_enableRxFifoInt(obj->sciaHandle);

	// SCI stop bit, parity, loopback, char bits, idle/address mode (SCICCR = 0x07)
	SCI_setNumStopBits(obj->sciaHandle, SCI_NumStopBits_One);	// SCICCR bit 7
	SCI_setParity(obj->sciaHandle, SCI_Parity_Odd);				// SCICCR bit 6
	SCI_disableParity(obj->sciaHandle);							// SCICCR bit 5
	//SCI_enableParity(obj->sciaHandle);
	SCI_disableLoopBack(obj->sciaHandle);						// SCICCR bit 4
	SCI_setMode(obj->sciaHandle, SCI_Mode_IdleLine);			// SCICCR bit 3
	SCI_setCharLength(obj->sciaHandle, SCI_CharLength_8_Bits);// SCICCR bit 0-2

	// TX enable, RX enable, RX ERR INT enable, SLEEP, TXWAKE (SCICTL1 = 0x03)
	SCI_disableRxErrorInt(obj->sciaHandle);						// SCICTL1 bit 6
	SCI_disable(obj->sciaHandle);								// SCICTL1 bit 5
	SCI_disableTxWake(obj->sciaHandle);							// SCICTL1 bit 3
	SCI_disableSleep(obj->sciaHandle);							// SCICTL1 bit 2
	SCI_enableTx(obj->sciaHandle);								// SCICTL1 bit 1
	SCI_enableRx(obj->sciaHandle);								// SCICTL1 bit 0

	// TXINT enable, RXINT enable, TXEMPTY, TXRDY (SCICTL2 = 0x03)
	SCI_enableRxInt(obj->sciaHandle);							// SCICTL2 bit 1
	SCI_disableTxInt(obj->sciaHandle);							// SCICTL2 bit 0

	// SCIH-SCIL BAUD - SCI_BAUD = (LSPCLK/(SCI_BRR*8)) - 1
	SCI_setBaudRate(obj->sciaHandle, SCI_BaudRate_9_6_kBaud);

	// Reset SCI
	SCI_enable(obj->sciaHandle);

	msg = "\r\n\n  ********** SCI setup is done! **********\0";
	SCI_writeMsg(obj->sciaHandle, msg);
	//PIE_enableSciInt(obj->pieHandle, SCI_RXA);	// enable SCI interrupt
	PIE_enableInt(halHandle->pieHandle, PIE_GroupNumber_9, PIE_InterruptSource_SCIARX);
	CPU_enableInt(obj->cpuHandle, CPU_IntNumber_9);	// enable CPU interrupt

#endif // of SCI
}
#ifdef SCI
//Added by Maya
//SCIA
SCI_FifoStatus_e HAL_sciaGetRxFifoStatus(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	return (SCI_getRxFifoStatus(obj->sciaHandle));
} // end of HAL_scigetRXFIFOStatus() function
//Added by Maya
void HAL_sciaWrite(HAL_Handle handle, const uint16_t data) {
	HAL_Obj *obj = (HAL_Obj *) handle;

	SCI_write(obj->sciaHandle, data);
}
//Added by Maya
void HAL_sciaWriteMsg(HAL_Handle handle, char * msg) {
	HAL_Obj *obj = (HAL_Obj *) handle;

	SCI_writeMsg(obj->sciaHandle, msg);
}
//Added by Maya
uint16_t HAL_sciaRead(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;

	return (SCI_read(obj->sciaHandle));
}
//Added by Maya
void HAL_sciaClearRxFifoOvf(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	SCI_clearRxFifoOvf(obj->sciaHandle);
}
//Added by Maya
void HAL_sciaClearRxFifoInt(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	SCI_clearRxFifoInt(obj->sciaHandle);
}
//Added by Maya
void HAL_sciaClearTxFifoInt(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	SCI_clearTxFifoInt(obj->sciaHandle);
}
void HAL_sciaEnableRxInt(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	SCI_enableRxInt(obj->sciaHandle);
}
void HAL_sciaDisableRxInt(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	SCI_disableRxInt(obj->sciaHandle);
}
void HAL_sciaEnableTxInt(HAL_Handle handle) {

	HAL_Obj *obj = (HAL_Obj *) handle;
	SCI_enableTxInt(obj->sciaHandle);
}
void HAL_sciaDisableTxInt(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	SCI_disableTxInt(obj->sciaHandle);
}
bool HAL_sciaTxReady(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	return (SCI_txReady(obj->sciaHandle));
}
bool HAL_sciaTxEmpty(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	return (SCI_txEmpty(obj->sciaHandle));
}
bool HAL_sciaRxParityError(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	return (SCI_rxParityError(obj->sciaHandle));
}
bool HAL_sciaRxOverrunError(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	return (SCI_rxOverrunError(obj->sciaHandle));
}
bool HAL_sciaRxFrameError(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	return (SCI_rxFrameError(obj->sciaHandle));
}
void HAL_sciaEnable(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	SCI_enable(obj->sciaHandle);
}
void HAL_sciaDisable(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	SCI_disable(obj->sciaHandle);
}
#endif //End of SCI
//Added by Maya, for PIE
void HAL_pieAckInt(HAL_Handle handle, const PIE_GroupNumber_e groupNumber) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	PIE_clearInt(obj->pieHandle, groupNumber);
}
void HAL_enablePieInt(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	PIE_enable(obj->pieHandle);
}
void HAL_disablePieInt(HAL_Handle handle) {
	HAL_Obj *obj = (HAL_Obj *) handle;
	PIE_disable(obj->pieHandle);
}

1513--1556

// Added by Dmitri Ranfft on 16.09.2015
void HAL_setupCAP(HAL_Handle handle)
{
#ifdef CAP
	HAL_Obj *obj = (HAL_Obj *) handle;
    CLK_enableEcap1Clock(obj->clkHandle);

    CAP_disableInt(obj->capHandle, CAP_Int_Type_All);    // Disable all capture interrupts
    CAP_clearInt(obj->capHandle, CAP_Int_Type_All);      // Clear all CAP interrupt flags
    CAP_disableCaptureLoad(obj->capHandle);              // Disable CAP1-CAP4 register loads
    CAP_disableTimestampCounter(obj->capHandle);         // Make sure the counter is stopped

    // Configure peripheral registers
    //CAP_setCapOneShot(obj->capHandle);                   // One-shot
    CAP_setCapContinuous(obj->capHandle);
    CAP_setStopWrap(obj->capHandle, CAP_Stop_Wrap_CEVT2);// Stop at 4 events
    CAP_setCapEvtPolarity(obj->capHandle, CAP_Event_1, CAP_Polarity_Rising);    // Falling edge
    CAP_setCapEvtPolarity(obj->capHandle, CAP_Event_2, CAP_Polarity_Falling);     // Rising edge
    //CAP_setCapEvtPolarity(obj->capHandle, CAP_Event_3, CAP_Polarity_Rising);    // Falling edge
    //CAP_setCapEvtPolarity(obj->capHandle, CAP_Event_4, CAP_Polarity_Falling);     // Rising edge

    CAP_setCapEvtReset(obj->capHandle, CAP_Event_1, CAP_Reset_Enable);   // Difference operation
    CAP_setCapEvtReset(obj->capHandle, CAP_Event_2, CAP_Reset_Enable);   // Difference operation
    //CAP_setCapEvtReset(obj->capHandle, CAP_Event_3, CAP_Reset_Enable);   // Difference operation
    //CAP_setCapEvtReset(obj->capHandle, CAP_Event_4, CAP_Reset_Enable);   // Difference operation

    CAP_enableSyncIn(obj->capHandle);                    // Enable sync in
    CAP_setSyncOut(obj->capHandle, CAP_SyncOut_SyncIn);  // Pass through

    CAP_enableCaptureLoad(obj->capHandle);

    CAP_enableTimestampCounter(obj->capHandle);          // Start Counter
    //CAP_rearm(obj->capHandle);                           // arm one-shot
    CAP_enableCaptureLoad(obj->capHandle);               // Enable CAP1-CAP4 register loads
    CAP_enableInt(obj->capHandle, CAP_Int_Type_CEVT2);   // 4 events = interrupt



    // Enable CPU INT4 which is connected to ECAP1-4 INT:
    CPU_enableInt(obj->cpuHandle, CPU_IntNumber_4);
    // Enable eCAP INTn in the PIE: Group 3 interrupt 1-6
    PIE_enableCaptureInt(obj->pieHandle);
#endif
}
hal.h

166--173

//Added by Maya
#ifdef SCI
	extern interrupt void sciaISR(void);
#endif
//Added by Dmitri Ranfft on 16.09.2015
#ifdef CAP
	extern __interrupt void ecap1ISR(void);
#endif

487--496

  //Added by Maya
#ifdef SCI
  pie->SCIRXINTA = &sciaISR;
#endif

  //Added by Dmitri Ranfft on 16.09.2015
#ifdef CAP
  pie->ECAP1_INT = &ecap1ISR;
  //PIE_registerPieIntHandler(obj->pieHandle, PIE_GroupNumber_4, PIE_SubGroupNumber_1, (intVec_t)&ecap1ISR);
#endif

1306--1333

// Added by Maya
extern HAL_Handle halHandle;
extern void HAL_setupSCI(HAL_Handle handle);
//SCIA
extern SCI_FifoStatus_e HAL_sciaGetRxFifoStatus(HAL_Handle handle);
extern void HAL_sciaWrite(HAL_Handle handle, const uint16_t data);
extern void HAL_sciaWriteMsg(HAL_Handle handle, char * msg);
extern uint16_t HAL_sciaRead(HAL_Handle handle);
extern void HAL_sciaClearRxFifoOvf(HAL_Handle handle);
extern void HAL_sciaClearRxFifoInt(HAL_Handle handle);
extern void HAL_sciaClearTxFifoInt(HAL_Handle handle);
extern void HAL_sciaEnableRxInt(HAL_Handle handle);
extern void HAL_sciaDisableRxInt(HAL_Handle handle);
extern void HAL_sciaEnableTxInt(HAL_Handle handle);
extern void HAL_sciaDisableTxInt(HAL_Handle handle);
extern bool HAL_sciaTxReady(HAL_Handle handle);
extern bool HAL_sciaTxEmpty(HAL_Handle handle);
extern bool HAL_sciaRxParityError(HAL_Handle handle);
extern bool HAL_sciaRxOverrunError(HAL_Handle handle);
extern bool HAL_sciaRxFrameError(HAL_Handle handle);
extern void HAL_sciaEnable(HAL_Handle handle);
extern void HAL_sciaDisable(HAL_Handle handle);
// Added by Maya
extern void HAL_pieAckInt(HAL_Handle handle, const PIE_GroupNumber_e groupNumber);
extern void HAL_enablePieInt(HAL_Handle handle);

// Added by Dmitri Ranfft on 16.09.2015
extern void HAL_setupCAP(HAL_Handle handle);
hal_obj.h

41--60

// drivers
#include "sw/drivers/adc/src/32b/f28x/f2802x/adc.h"
#include "sw/drivers/cap/src/32b/f28x/f2802x/cap.h" // Added by Dmitri Ranfft on 16.09.2015
#include "sw/drivers/clk/src/32b/f28x/f2802x/clk.h"
#include "sw/drivers/cpu/src/32b/f28x/f2802x/cpu.h"
#include "sw/drivers/flash/src/32b/f28x/f2802x/flash.h"
#include "sw/drivers/gpio/src/32b/f28x/f2802x/gpio.h"
#include "sw/drivers/osc/src/32b/f28x/f2802x/osc.h"
#include "sw/drivers/pie/src/32b/f28x/f2802x/pie.h"
#include "sw/drivers/pll/src/32b/f28x/f2802x/pll.h"
#include "sw/drivers/pwm/src/32b/f28x/f2802x/pwm.h"
#include "sw/drivers/pwmdac/src/32b/f28x/f2802x/pwmdac.h"
#include "sw/drivers/pwr/src/32b/f28x/f2802x/pwr.h"
#include "sw/drivers/spi/src/32b/f28x/f2802x/spi.h"
#include "sw/drivers/timer/src/32b/f28x/f2802x/timer.h"
#include "sw/drivers/wdog/src/32b/f28x/f2802x/wdog.h"
#include "sw/drivers/drvic/drv8305/src/32b/f28x/f2802x/drv8305.h"
//Added by Maya
//#include "sw/drivers/sci/src/32b/f28x/f2802x/sci.h"
#include "sci.h"

131-133

  //Added by Dmitri Ranfft on 16.09.2015
  CAP_Handle    capHandle;        //!< the eCAP handle

173

 SCI_Handle    sciaHandle;       //!< the sci handle, added by Maya

sci

sci.c

同一ファイルです。

sci.h

369--376

//Added by Maya
typedef enum
{
  SCI_RXA=0,      		//!< Denotes SCI receive A
  SCI_TXA=1,     			//!< Denotes SCI transmit A
  SCI_RXB=2,    			//!< Denotes SCI receive B
  SCI_TXB=3   			//!< Denotes SCI transmit B
} SCI_Type_e;

728--731 in static inline void SCI_write(SCI_Handle sciHandle,const uint16_t data)

  //Added by Maya
 #ifdef SCI_FIFO
   while(SCI_getTxFifoStatus(sciHandle) != 0) {};
 #endif

739--792

// Added by Maya
static inline bool SCI_txEmpty(SCI_Handle sciHandle)
{
  SCI_Obj *sci = (SCI_Obj *)sciHandle;
  bool status;

  status = (sci->SCICTL2 & SCI_SCICTL2_TXEMPTY_BITS) >> 6;

  return((bool)status);
} // end of SCI_txEmpty() function

//Added by Maya
static inline void SCI_writeMsg(SCI_Handle sciHandle, char * msg)
{
    int i;
    i = 0;
    while(msg[i] != '\0')
    {
    	SCI_write(sciHandle, msg[i]);
        i++;
    }
}

static inline bool SCI_rxParityError(SCI_Handle sciHandle)
{
  SCI_Obj *sci = (SCI_Obj *)sciHandle;
  bool status;

  status = (sci->SCIRXST & SCI_SCIRXST_PE_BITS) >> 2;

  return((bool)status);
} // end of SCI_rxParityError() function

static inline bool SCI_rxOverrunError(SCI_Handle sciHandle)
{
  SCI_Obj *sci = (SCI_Obj *)sciHandle;
  bool status;

  status = (sci->SCIRXST & SCI_SCIRXST_OE_BITS) >> 3;

  return((bool)status);
} // end of SCI_rxParityError() function

static inline bool SCI_rxFrameError(SCI_Handle sciHandle)
{
  SCI_Obj *sci = (SCI_Obj *)sciHandle;
  bool status;

  status = (sci->SCIRXST & SCI_SCIRXST_FE_BITS) >> 4;

  return((bool)status);
} // end of SCI_rxParityError() function

//End of addition by Maya

見比べる

差分の抽出が済みましたので、見比べていきましょう。
ピン変更までしてeCAPとやらを何に使っているのか、よくわかりませんね???
SCIを使うためには、基本的にMayaさんの変更箇所を見ていけばよいようです。
たかがシリアルで、こんなに面倒くさいと、このマイコン開発にリソースを割いていていいのかどうか不安になります。

動かして確認してみる

Mayaさんのコメント部分を自分のソースコードにコピーペーストし、sci.hとsci.cをプロジェクトに追加します。
また、sci.hの最初の方に

#define SCI 1

を書き加えます。

シリアル接続を確認するために、teratermでUSB COMPortに接続し、9600bpsの速度に設定します。
デバッグモードでFlashにproj_lab05b.outをアップロードすると、
"\r\n\n ********** SCI setup is done! **********\0";
の文字が表示されるはずなのですが、最初の3文字ぐらいしか出ません。

 ***9

ただ、何かは応答してくれているようです。
キーボードで文字を入力すると、エコーバックされてteraterm上に表示されます。BackSpaceキーを押すとカーソルが一つ戻ります。
Enterキーを押すと、ロックされた状態になりますが、Escを押すと、再び入力可能な状態になります。

ちゃんと動いているようですね。

FIFOを有効にして、通信欠落を減らす

sci.hのdefinesの中に
#define SCI 1
に加えて
#define SCI_FIFO 1
を追加します.

ところが、メモリの初期化がうまくいかずに起動直後はFIFOにゴミがあり、これを吐き出させる前にhal.cの中で文字列を出力させる次の2行を実行すると、わけのわからないゴミを延々と吐き続けて、クリアできないという状況に陥ります。
なので、この2行をコメントアウトします。

msg = "\r\n\n  ********** SCI setup is done! **********\0";
	SCI_writeMsg(obj->sciaHandle, msg);

sciaISRが回り始めた当初はまだゴミを少し吐くので、これを吐き出させた後に通信に使用するとうまい行くようです。
残念ながら、sci.hに定義されたどのコマンドを用いても、起動直後のバッファのゴミを削除することができませんでした。

重要な知見

LAUNCHXL-F28027F 不推奨のススメ

LAUNCHXL-F28027FにBOOSTXL-DRV8305EVM経由で電源を供給したときに、うまく初期化できません。外部リセットを行う以外の解決方法が見つかりません。
したがって、組み込み用途で使用するときに、大変不便します。

LAUNCHXL-F28027Fは、メモリ上での開発ができずコンパイルしたバイナリを毎回Flashメモリに焼きこむ必要があります。
ところが、フラッシュの耐久性の問題か、ロムライタのソフトウェアの問題か50回もしないうちに書き込み不可能になってしまいます。
書き込み不可能になったマイコンボードは捨てるしかほかありませんので、コンパイル50回毎に2000円が飛んでいくことになります。
C2000PiccoloシリーズおよびInstaSPINは、かなり特殊なソフトウェア体系(整理されてない)ので、ある程度使えるエキスパートになるまでには相当な時間がかかります。

同じシリーズの上位機種LAUNCHXL-F28069Mはとりあえずこれらの問題はありませんので、サイズはデカいですが値段はあまり変わらないのでこちらを推奨します。

SCI受信割り込み(例えば、SCIRXINTに紐づけられたsciaISR)の中で一文字たりとも送信してはいけない

まれにモータの制御が飛び一瞬停止するなど異常動作を起こすので、大暴走して事故の原因になりかねません。
本来、SCITXINTを有効にして、適切な送信割り込みを掛けるべきなのだと思いますが、すると今度はモータ制御がうまく行かなくなります。まだ原因が特定できません。
main関数のforeverループの中でループ一回に一文字づつ送るなどの方が無難なようです。mainISRではありません。mainISRは、adcの割り込みの呼び出し先です。
文字列長など、セグメンテーションエラーを起こさないように十分留意してください。

//Added by Maya
#ifdef SCI

interrupt void sciaISR(void) {
	//RXISRCount++;

	ReceivedChar = HAL_sciaRead(halHandle) & 0x00FF;

	//Handling escape.
	if(ReceivedChar != 27){
		//Handling carriage return/new line.
		if(ReceivedChar != '\r' && ReceivedChar != '\n' && cmdReady == 0){
			//Show input.
			if(ECHO_ON){
				// \HAL_sciaWrite(halHandle, ReceivedChar); ///// Caution! Danger!
			}
			//Handling backspace/delete.
			if(ReceivedChar != 127 && ReceivedChar != 8){
				bufPos++;
				//If the end of the buffer wasn't reached, add new char to the buffer.
				if(bufPos < BUF_LEN){
					cmdBuf[bufPos-1] = (char)ReceivedChar; //Adding char.
					cmdBuf[bufPos] = '\0'; //Appending null to indicate the end of the string.
				}
			}else{
				//If the beginning of the buffer wasn't reached, remove the previous char from the buffer.
				if(bufPos > 0){
					bufPos--;
					//Only delete if within buffer range.
					if(bufPos < BUF_LEN){
						cmdBuf[bufPos] = '\0';
					}
				}
			}
		}else if(cmdReady == 0){ //If enter was pressed, start new line and set cmdReady flag.
			msg = "\n\r";
			// HAL_sciaWriteMsg(halHandle, msg); ///// Caution! Danger!
			cmdReady = 1;
		}
	}else{ //If escape is pressed, reset command buffer and clear cmdReady flag.
		cmdReady = 0;
		bufPos = 0;
		cmdBuf[0] = '\0';
	}

	HAL_sciaClearRxFifoOvf(halHandle);
	HAL_sciaClearRxFifoInt(halHandle);

	HAL_pieAckInt(halHandle,PIE_GroupNumber_9);		// Issue PIE ack INT9

	return;
}

#endif

今日の友達がいない漫画

タイトルに反して、主人公湯神くんの周りには登場人物多い。

特性の分からない3相DCブラシレスモーターをソフトウェアエンコーダを搭載したTiのマイコンボードLAUNCHXL-F28069MとモータドライバBOOSTXL-DRV8305EVMを使って動かす(その5)

(編集中)
簡単に実験できるように、アナログスイッチやボリュームでモータを制御できると便利です。
Tiのデータシートを参考に、DRV8305EVM用のアナログコントローラを設計してみます。

1.ハードウェアの設計と製作

f:id:cvl-robot:20161019143057p:plain
できました、これです。

速度指令値と加速度指令値用にボリュームを2つ、回転方向指示用にスイッチを1つ、予備用にもうひとつスイッチ、Fault表示用にLEDを一つ、付けます。
マイコンボードのデータシートと、ドライバーボードのデータシートを見比べて、ドライバーボード側で使っていないピンを探します。
f:id:cvl-robot:20161019143355p:plain
ボリューム用にADC入力のピンが2つ必要ですので探していくと、ADCINA6とADCINB6がギリギリ空いていました。
同様にスイッチ用のGPIOピンを探していくと、P12とP22がかろうじて空いていました。(!)は、クイックスタートガイドのPDFによると,「割り込み能力があるI/Oピンを意味します。」だそうです。
マイコンの3.3VピンからLEDを直接駆動するほどの電流を引くのは難しいようなので、74HC245をバッファ代わりに挿入します。

マイコンボードには多種多様なインターフェースピンが用意されているものの、そのほとんどをドライバーボードが占有してしまうのでユーザが使えるのは極一部なのですね。
簡単な部品しかないので基板は簡単に作ることができると思います。
20ピンフラットケーブル等でBOOSTXL-DRV8305EVMのJ1側の端子に接続します。

また、実験用には、電圧計と電流計が付いていて、スイッチをすぐ切ることができる、十分な電流容量を持った電源装置があると便利です。
モーターIDに失敗すると、モーターは回らないまま異音を上げて大電流が流れるなんて言う事態に、よくなります。

LAUNCHXL-F28027FとBOOSTXL-DRV8305EVMのピンも確認しておきます。
f:id:cvl-robot:20161020152238p:plain
ピン配置はAIO2を除けばだいたい同じです。ただ、ピン番号は全然違いますので、名前を調整する必要があります。

2.ソフトウェアの改造

proj_lab03bを基に、ソフトウェアの改造を行っていきます。

手元に用意するべき資料は2つあり
・TMS320C2000:Piccolo MCUのソフトウェア開発入門
・MotorWare Software Architecture
です。前者はWEBから、後者はMotorWareのアプリケーションから入手できます。

ADCの取り扱いはこの資料も有用です。
http://www.tij.co.jp/jp/lit/ug/jaju167/jaju167.pdf

[追記]
ピン配置の見比べが超絶面倒くさいです。
F28027F
f:id:cvl-robot:20161219215744p:plain
F28069M
f:id:cvl-robot:20161219215750p:plain
f:id:cvl-robot:20161219215753p:plain

メモ:HiRes時代の自作オーディオ その2:2016年10月ベストチョイス編

もし今、自分がオーディオシステムを組むなら、これをチョイス。
パワーアンプはNCore400、プリ代わりのDACはTerra-Berry。

NC400 mono kit

1台650ユーロでモノなので、2台分で1300ユーロ。およそ15万円。
www.hypexshop.com

Marantz MA-10

BTL 2chなので、中身にNCoreを4枚使っています。
www.marantz.jp

Terra-Berry

I2S入力のDAC。27,000円(税込み)。入力インターフェースは別途用意する必要がある。バランスケーブルが必要になります.
RaspberryPi Terra-Berry - テラテクノス株式会社

USB入力にしたいので、Amanero社のCombo384を選択.\12,800-
www.amanero.com

ここまで小計\189,800-。安くはないですが、きっと最高。
スピーカーはJBLの38口径のウーハーを積んだ43シリーズが良いなぁー。

追記:2017/10/27

SonarWorks Reference 3 Complete with Mic

汎用のオーディオ出力の補正にも使える音響補正ソフトウェアが来ました!
現在の最新版は、Reference 4! 製品準備中のようです。

今日のおとうふ

Aiの遺伝子の著者山田胡瓜先生がTwitterで絶賛していた漫画。必死に何もしません。

ICP(Iterative Closest Point)法を勉強したいときのメモ

(編集中)

広島大学玉木先生のスライド

sssslide.com

かみやんさんの『Efficient Variants of the ICP Algorithm』(サーベイ論文)の解説

d.hatena.ne.jp

SparseICP(c++ソースコード、提案手法の比較用にオーソドックスな方のソースもある)

github.com
Windowsでビルドしたい場合は、同梱のEigenが古くてエラーを起こすので、最新版で上書きすること。

最終頁と上記ソースコードを見比べると、理解が早い

https://igl.ethz.ch/projects/ARAP/svd_rot.pdf

コンピュータビジョン 最先端ガイド3

増田先生の第2章にcソースコードもある.

コンピュータビジョン最先端ガイド 3 (CVIMチュートリアルシリーズ)

コンピュータビジョン最先端ガイド 3 (CVIMチュートリアルシリーズ)

MotorwareのGUI Composerの起動が面倒くさいことの対処

MotorWareのExampleの実行方法は二つあります。
1. コンパイル出力(proj_lab02b.out)をappProgram.outと改名してGUI Composerの実行ファイルと同じフォルダにコピーしてGUIを起動する方法
2. IDE(Code Composer Studio)にGUI Composerをzip化して読み込ませてDebugモードでoutファイルを読み込んで起動する方法

そのままでは、どっちも超絶面倒くさいです。
お勧めは、1の方法に簡単なバッチファイルを書いて、半自動で起動できるようにしておく方法です。

例えば、次のようなファイルをコンパイル出力先のフォルダに作ります。

proj_lab02b.bat

copy proj_lab02b.out "C:\ti\guicomposer\webapps\InstaSPIN_F2802xF_UNIVERSAL\appProgram.out"
cd "C:\ti\guicomposer\webapps\InstaSPIN_F2802xF_UNIVERSAL\"
InstaSPIN_UNIVERSAL.exe

特性の分からない3相DCブラシレスモーターをソフトウェアエンコーダを搭載したTiのマイコンボードLAUNCHXL-F28069MとモータドライバBOOSTXL-DRV8305EVMを使って動かす(その4)

(編集中)
最重要ファイルであるところのmotorware_selecting_user_variables.xlsxのv2p1 2014-May-28版を見てみましょう。
MotorwareのResources->Training: User's Guides, Labs, Tutorials->InstaSPIN Projects Setting User Variablesの下にあります。

1. FILL IN THESE VALUES FOR YOUR USER.H, MOTOR, and INVERTER HW

(お使いのuser.h, モータ,インバータハードウェアに合わせて、これらの値を埋めてください。)

USER_SYSTEM_FREQ_MHz90[MHz]
Maximum Bus Voltage24[V]
Maximum Target RPM40000[RPM]
USER_MOTOR_NUM_POLE_PAIRS2[PAIRS]
USER_PWM_FREQ_kHz45[kHz]
USER_NUM_PWM_TICKS_PER_ISR_TICK3[ticks]
USER_NUM_ISR_TICKS_PER_CTRL_TICK1[ticks]
USER_NUM_CTRL_TICKS_PER_CURRENT_TICK1[ticks]
USER_NUM_CTRL_TICKS_PER_EST_TICK1[ticks]
USER_ZEROSPEEDLIMIT0.002

sub-calculations:
Target Hz = (Maximum Target RPM) * (USER_MOTOR_NUM_POLE_PAIRS) / 60
POLE = 2 * (USER_MOTOR_NUM_POLE_PAIRS)
ISR_Hz = (USER_PWM_FREQ_Hz) / (USER_NUM_PWM_TICKS_PER_ISR_TICK)
CTRL_Hz = (ISR_Hz) / (USER_NUM_ISR_TICKS_PER_CTRL_TICK)
CURRENT_Hz = (CTRL_Hz) / (USER_NUM_CTRL_TICKS_PER_CURRENT_TICK)
EST_Hz = (CURRENT_Hz) / (USER_NUM_CTRL_TICKS_PER_EST_TICK)

2. The following are set by HW design, use defaults for TI EVM or your own HW

(次の値はハードウェアデザインによって決定されますので,Tiの評価ボードまたはお使いのハードウェアのための規定値をお使いください。)

USER_VOLTAGE_FILTER_POLE_Hz364.682[Hz]
USER_ADC_FULL_SCALE_CURRENT_A33.00[A]
USER_ADC_FULL_SCALE_VOLTAGE_V26.3147[V]

3. THESE VALUES ARE RECOMMENDED FOR USE once TRUE checks are satisfied

 (TRUEの確認が満たされると,これらの値は使用できます。)

Ideal USER_IQ_FULL_SCALE_FREQ_Hz for Motor

(モータのための理想的なUSER_IQ_FULL_SCALER_FREQ_Hz)
Target Hz with 10% buffer
(10%の余裕を加えた目標周波数)

Maximum allowed USER_IQ_FULL_SCALE_FREQ_HZ for HW

(ハードウェアのための最大許容可能なUSER_IQ_FULL_SCALE_FREQ_HZ)
4 * USER_VOLTAGE_FILTER_POLE_Hz [with 5% buffer]
(USER_VOLTAGE_FILTER_POLE_Hzの4倍から5%の余裕を持たせた値)

USER_IQ_FULL_SCALE_FREQ_Hz

Lower of Ideal and Maximum
(理想と最大のうち小さい方)

Maximum RPM Supported

(サポートされる最大回転数)
Rotor Hz can be 1.98 * USER_IQ_FULL_SCALE_FREQ_HZ; EST speed will roll-over outside boundary!!!!
(ローターの周波数はUSER_IQ_FULL_SCALE_FREQ_Hzの1.98倍を取り得るため,推定速度は境界の外に転がり出得ます。)

USER_MOTOR_FLUX_EST_FREQ_Hz

5-150 Hz; ~10% of Maximum Target Hz but use low as possible where Ls and Flux can be ID'd consistently
(5-150Hz; 最大目標周波数のおよそ10%、ただしLsとFluxが一貫して特定できる範囲で出来るだけ低い値を使ってください。)

USER_MAX_ACCEL_EST_Hzps

If USER_MOTOR_FLUX_EST_FREQ_Hz > 50 Hz must increase the acceleration during ID to avoid a timeout
(もしUSER_MOTOR_FLUX_EST_FREQ_Hzが50Hzよりも大きければ,タイムアウトを避けるためにIDの間の加速を増加してください。)

CURRENT Hz > MAX_Hz * 7

If FALSE increase CURRENT kHz or reduce MAX_HZ; Standard good practice for control systems
(もしFALSEならばCURRENT kHzを増加させるか、MAX_Hzを現象させてください。制御システムのための標準的な良い慣習値)

EST <= CTRL

If FALSE correct; No need to run the estimator if results are not being updated in control loop
(もしFALSEならば、制御ループ中に更新され無いことを意味するので、推定は実行されません。)

EST > 10 * USER_VOLTAGE_FILTER_POLE_Hz (+10% margin)

If FALSE increase EST Rate
(もしFALSEならEST Rateを増やしてください。)

EST > 8 * TARGET_Hz

If FALSE, increase effective EST Frequency using TICKs
(もしFALSEなら、TICKsを使って有効なEST周波数に増加さえてください。)

FLUX_EST_FREQ > ZEROSPEEDLIMIT * FULL_SCALE_FREQ

If FALSE, lower ZEROSPEEDLIMIT
(もしFLASEならば, ZEROSPEEDLIMITを減らしてください。)

USER_IQ_FULL_SCALE_CURRENT_A

Slightly >= 0.5 * ADC_FULL_SCALE_CURRENT_A
(0.5*ADC_FUKK_SCALE_CURRENT_Aよりわずかに大きい値にしてください。)

starting USER_IQ_FULL_SCALE_VOLTAGE_V

 Initially set to bus voltage until flux is identified
(磁束が特定されるまで最初に設定されるバス電圧値)

4. Once Motor ID is attempted, update these as best you can and check IQ_V Scaling

 (モーター特定を実行したら,これらの値を出来るだけ最適に調整して、IQ_Vの値幅を確認してください。)

USER_MOTOR_RATED_FLUX0.023[V/Hz]
USER_MOTOR_Ls_d0.00005[H]

sub-calculations:
Minumiu Flux Measurement = (new USER_IQ_FULL_SCALE_VOLTAGE_V) / (EST_Hz * 1000) / 0.7
= Minimum Flux that can be measured with new USER_IQ_FULL_SCALE_VOLTAGE_V (cell I20)
(=最小磁束はnew_USER_IQ_FULL_SCALE_VOLTAGE_Vで計ることができます。)

4. Check after valid USER_MOTOR_RATED_FLUX Identification
(有効なUSER_MOTOR_RATED_FLUX特定後の確認)

Minimum Flux Measurement < 0.9 * RATED_FLUX

(最小磁束測定値が0.9*RATED_FLUXより小さいこと。)
  If FALSE, reduce new USER_IQ_FULL_SCALE_VOLTAGE as low as Bus Voltage / 2 + 10% buffer
(もしFALSEならば,new USER_IQ_FULL_SCALE_VOLTAGEを電源電圧の半分に10%の余裕を持たせた値よりも小さくしてください。)

IQ_VOLTAGE < RATED_FLUX * EST Hz

If FALSE, reduce new USER_IQ_FULL_SCALE_VOLTAGE or increase EST_Hz
(もしFALSEならば、new USER_IQ_FULL_SCALE_VOLTAGEを減らすか、EST_Hzを増やしてください。)

new USER_IQ_FULL_SCALE_VOLTAGE_V

Use larger of a) or b). Minimum of Bus Voltage / 2 + 10% buffer if required to make I18 & I19 TRUE
(a)またはb)の大きな方を使ってください。Minimum Flux Measurement < 0.9 * RATED_FLUXとIQ_VOLTAGE < RATED_FLUX * EST_Hzの条件を満たすためには、電源電圧の半分に10%の余裕を持たせた値にしてください。
a) Bus Voltage
(電源電圧)
Typical Minimum, but can reduce as low as Bus Voltage / 2 + 10% buffer
(形式上の最小値、ただしBus Voltageの半分に10%の余裕を持たせた電圧程度まで減りえます。)
b) Bemf Generated @ Target Hz + 10% buffer
(目標周波数+10%余裕における逆起電力)

5. Ideal Pole Design when you build wwn HW

(ユーザが自身のHWを作る際の理想的な極設計)

Minimum Pole200[Hz MIN]
Ideal* Pole >=366.667[Hz]
Half** Pole >=183.3333[Hz]

* Use Ideal pole to keep Target Hz < USER_IQ_FULL_SCALE_FREQ_Hz but note that as pole Hz increases you are more susceptible to drift/error and should use higher precision Vph filter Capacitors
(理想的な極を使うためにTarget Hz < USER_IQ_FULL_SCALE_FREQ_Hzを維持してください。しかし,極周波数の増加は、ドリフトやエラーに影響されやすくするので高い精度のVphフィルタキャパシタを使うように、気を付けてください。)

** You may use a lower pole (down to half the value of the ideal) that is less sensitive to capacitor error/offset/drift. Set USER_IQ_FULL_SCALE_FREQ_HZ <= 4 * Lowered Pole * 0.95 buffer. Target Hz can reach +/- 1.98 * USER_IQ_FULL_SCALE_FREQ_Hz

Performance difference between the two is typically very marginal though. Do NOT use a filter pole < Minimum Pole!!!

user.hへの反映

マイコンボードとインバータボードに依存して、テンプレートのuser.hが用意されています。たとえば、LAUNCHXL-F28027F+BOOSTXL-DRV8305EVMの組み合わせの場合は、

C:\ti\motorware\motorware_1_01_00_16\sw\solutions\instaspin_foc\boards\boostxldrv8305_revA\f28x\f2802xF\src

にあります。この中から、編集可能な値をピックアップしてみます。

CURRENTS AND VOLTAGES

#define USER_IQ_FULL_SCALE_FREQ_Hz (800.0) // 800 Example with buffer for 8-pole 6 KRPM motor to be run to 10 KRPM with field weakening; Hz =(RPM * Poles) / 120
#define USER_IQ_FULL_SCALE_VOLTAGE_V (24.0) // 24.0 Set to Vbus
#define USER_ADC_FULL_SCALE_VOLTAGE_V (44.30) // BOOSTXL-DRV8305EVM = 44.30 V
#define USER_IQ_FULL_SCALE_CURRENT_A (24.0) // BOOSTXL-DRV8305EVM = 24.0 A
#define USER_ADC_FULL_SCALE_CURRENT_A (47.14) // BOOSTXL-DRV8305EVM = 47.14 A
#define USER_NUM_CURRENT_SENSORS (3) // 3 Preferred setting for best performance across full speed range, allows for 100% duty cycle
#define USER_NUM_VOLTAGE_SENSORS (3) // 3 Required
#define I_A_offset (1.210729778) // BOOSTXL-DRV8305EVM = 1.047175646
#define I_B_offset (1.209441483) // BOOSTXL-DRV8305EVM = 1.044038892
#define I_C_offset (1.209092796) // BOOSTXL-DRV8305EVM = 1.040363491
#define V_A_offset (0.5084558129) // BOOSTXL-DRV8305EVM = 0.5256254077
#define V_B_offset (0.5074239969) // BOOSTXL-DRV8305EVM = 0.5250559449
#define V_C_offset (0.5065535307) // BOOSTXL-DRV8305EVM = 0.5247237682

CLOCKS & TIMERS

#define USER_SYSTEM_FREQ_MHz (60.0)
#define USER_PWM_FREQ_kHz (45.0) //30.0 Example, 8.0 - 30.0 KHz typical; 45-80 KHz may be required for very low inductance, high speed motors
#define USER_MAX_VS_MAG_PU (0.5) // Set to 0.5 if a current reconstruction technique is not used. Look at the module svgen_current in lab10a-x for more info.
#define USER_EST_HANDLE_ADDRESS (0x600)
#define USER_VD_SF (0.95)

DECIMATION

#define USER_NUM_PWM_TICKS_PER_ISR_TICK (3)
#define USER_NUM_ISR_TICKS_PER_CTRL_TICK (1) // 2 Example, controller clock rate (CTRL) runs at PWM / 2; ex 30 KHz PWM, 15 KHz control
#define USER_NUM_CTRL_TICKS_PER_CURRENT_TICK (1) // 1 Typical, Forward FOC current controller (Iq/Id/IPARK/SVPWM) runs at same rate as CTRL.
#define USER_NUM_CTRL_TICKS_PER_EST_TICK (1) // 1 Typical, FAST estimator runs at same rate as CTRL;
#define USER_NUM_CTRL_TICKS_PER_SPEED_TICK (15) // 15 Typical to match PWM, ex: 15KHz PWM, controller, and current loop, 1KHz speed loop
#define USER_NUM_CTRL_TICKS_PER_TRAJ_TICK (15) // 15 Typical to match PWM, ex: 10KHz controller & current loop, 1KHz speed loop, 1 KHz Trajectory

LIMITS

#define USER_ZEROSPEEDLIMIT (0.5 / USER_IQ_FULL_SCALE_FREQ_Hz) // 0.002 pu, 1-5 Hz typical; Hz = USER_ZEROSPEEDLIMIT * USER_IQ_FULL_SCALE_FREQ_Hz
#define USER_MAX_ACCEL_Hzps (20.0) // 20.0 Default
#define USER_MAX_ACCEL_EST_Hzps (5.0) // 5.0 Default, don't change
#define USER_IDRATED_FRACTION_FOR_L_IDENT (1.0) // 1.0 Default, don't change
#define USER_IDRATED_DELTA (0.00002)
#define USER_SPEEDMAX_FRACTION_FOR_L_IDENT (1.0) // 1.0 Default, don't change
#define USER_FLUX_FRACTION (1.0) // 1.0 Default, don't change
#define USER_POWERWARP_GAIN (1.0) // 1.0 Default, don't change
#define USER_R_OVER_L_EST_FREQ_Hz (300) // 300 Default for high speed motors, can reduce to 100 if RoverL from Motor ID is < 2000

POLES

#define USER_VOLTAGE_FILTER_POLE_Hz (344.62) // BOOSTXL-DRV8305 = 344.62 Hz
#define USER_OFFSET_POLE_rps (20.0) // 20.0 Default, do not change
#define USER_FLUX_POLE_rps (100.0) // 100.0 Default, do not change
#define USER_DIRECTION_POLE_rps (6.0) // 6.0 Default, do not change
#define USER_SPEED_POLE_rps (100.0) // 100.0 Default, do not change
#define USER_DCBUS_POLE_rps (100.0) // 100.0 Default, do not change
#define USER_EST_KAPPAQ (1.5) // 1.5 Default, do not change

Define each motor with a unique name and ID number

#define Anaheim_BLY172S 102
#define USER_MOTOR Anaheim_BLY172S

#define USER_MOTOR_TYPE MOTOR_Type_Pm // Motor_Type_Pm (All Synchronous: BLDC, PMSM, SMPM, IPM) or Motor_Type_Induction (Asynchronous ACI)
#define USER_MOTOR_NUM_POLE_PAIRS (4) // PAIRS, not total poles. Used to calculate user RPM from rotor Hz only
#define USER_MOTOR_Rr (NULL) // Induction motors only, else NULL
#define USER_MOTOR_Rs (0.3968007) // Identified phase to neutral resistance in a Y equivalent circuit (Ohms, float)
#define USER_MOTOR_Ls_d (0.0006708066) // For PM, Identified average stator inductance (Henry, float)
#define USER_MOTOR_Ls_q (0.0006708066) // For PM, Identified average stator inductance (Henry, float)
#define USER_MOTOR_RATED_FLUX (0.03433958) // Identified TOTAL flux linkage between the rotor and the stator (V/Hz)
#define USER_MOTOR_MAGNETIZING_CURRENT (NULL) // Induction motors only, else NULL
#define USER_MOTOR_RES_EST_CURRENT (1.0) // During Motor ID, maximum current (Amperes, float) used for Rs estimation, 10-20% rated current
#define USER_MOTOR_IND_EST_CURRENT (-1.0) // During Motor ID, maximum current (negative Amperes, float) used for Ls estimation, use just enough to enable rotation
#define USER_MOTOR_MAX_CURRENT (5.0) // CRITICAL: Used during ID and run-time, sets a limit on the maximum current command output of the pded Speed PI Controller to the Iq controller
#define USER_MOTOR_FLUX_EST_FREQ_Hz (20.0) // During Motor ID, maximum commanded speed (Hz, float), ~10% rated

user.hとmotorware_selecting_user_variables.xlsxを並べて開いて、データを相互に編集してやる必要があります。
1.最初に、user.hからエクセルの黄色い枠の中にデータをコピペしていきます。モニターで黄色が見にくいのでこのページでは、赤になっています。
2.次に、エクセルの緑の枠の値をuser.hにコピーして持ってきます。USER_MAX_ACCEL_EST_Hzpsは変えるな、とuser.hに書いてあるので変えない方が良いでしょう。
3.実機でモータ同定を行ってから、両方の青枠のデータを更新します。モータ同定はうまくいかないことの方が多いので、うまく回るかどうかを確認してから反映してください。
4.水色で示したモータの巻き線抵抗や、インバータの電流電圧検出抵抗の値をuser.hに更新します。

Tiのホームページの素晴らしい宣伝文句とは裏腹に、おしゃれなGUIなどでは設定できませんので、エディタ等でテキストベースで設定を書き換えます。
(ここまで出来ていて、何故自動ソフトウェア化しないのだろう??)

実際のパラメータ決定法

上記の計算結果をuser.hに入れて、コンパイルして、実行しようとしてもまずうまくいきません。
USER_MOTOR_RATED_FLUXの値が下限リミットに引っかかってしまい、エラーで弾かれるか、まともに動かないという結果になりがちです。
解決方法としては、excelのUSER_MOTOR_RATED_FLUXを使わない、というのが本末転倒ですが現実的なようです。

GUI ComposerのInstaSPIN-FOC MotorWare Instrumentationタブの右下の方に、Variable Overflow Checksがあり、この中の

Flux * Full Scale Freq [V] <

の項目がうまく設定できているかどうかのカギです。
要するに、この項目が下限電圧値を示していてFull Scale Freqの値はエクセルシート上ですでに計算していますから、Fluxの値をこれから直接計算してしまいましょう。
下限電圧値は、「電源電圧の半分に10%の余裕を持たせた値」としていますので、バス電圧が24Vの場合

24[V]/2*1.1=13.2[V]

です。
USER_IQ_FULL_SCALE_FREQ_Hzはエクセルシートの右側で、例えば800Hzとか513.3Hzなんていうオーダーの数値になっています。
試しに513.3Hzとすると、

Flux = 13.2[V] / 513.3[Hz] = 0.02571596 [V/Hz]

となります。エクセルと10倍程度違ったり、自動同定で見つけさせた値とかけ離れていたりしますが、こちらの値の方が上手くいくことが多いです。

これが、InstaSPIN-MOTION用だとまた少し違う項目が出てきます。が、それはまた次に。


[1]parameter settings: http://www.ti.com/lit/ug/spruhj1f/spruhj1f.pdf