# Self-made motor controller: Firmware & Test
The firmware of my motor controller is based on the example project "lab13a" in MotorWare, a software package for motor control, provided by Texas Instruments. The "lab13a" is the firmware for three-loops motor controllers. The "lab13a" firmware is easy to use and compatible with both PM and IM motors. However, it does not support CAN-bus communication, magnetic encoder chip AS5147P, and current command interface. I have to implement these functions on my own.
## Firmware design for the motor controller
The "lab13a" firmware provides a Hardware Abstraction Layer (HAL) to manage hardware modules, such as PWM, ADC, SPI, and UART. The HAL also offers Application Programming Interfaces (APIs) for users to set and enable the modules. To make my motor controller compatible with the SPI interface of AS5147P and CAN-bus communication, I have to write my HAL and APIs for these two modules in my firmware. My HAL and APIs are implemented in the source/header files: can.h/.c and F2806x_Mcbsp.h/.c.
The HAL of TI's MotorWare also needs some modifications to be compatible with my HAL and APIs. The modified files are hal_obj.h, hal.h, and hal.c.
In hal_obj.h shown below, I added the eCAN handle and the Mcbsp handle into TI's HAL. With proper setting of the Mcbsp handle, the Mcbsp module could work in master SPI mode and support the SPI interface of AS5147P.
```clike=
typedef struct _HAL_Obj_
{
//'''original Handles of project "lab13a"
//Can BUS Handles
ECAN_Handle ecanaHandle;
//SPIB Handles
McBSP_Handle mcbspaHandle;
} HAL_Obj;
```
In hal.h, I added a Interrupt Service Routine (ISR) for the eCAN1 module.
```clike=
extern interrupt void mainISR(void);
extern interrupt void ecan1ISR(void);
```
In hal.c, I also changed the GPIO setting for the eCAN1 module and the Mcbsp module.
```clike=
void HAL_setupGpios(HAL_Handle handle)
{
//'''original GPIO setting of project "lab13a"
// MDXA of Mcbsp serve as SPISIMO
GPIO_setMode( obj->gpioHandle,GPIO_Number_20,GPIO_20_Mode_MDXA );
// MDRA of Mcbsp serve as SPISOMI
GPIO_setQualification(obj->gpioHandle,GPIO_Number_21,GPIO_Qual_ASync );
GPIO_setMode( obj->gpioHandle,GPIO_Number_21,GPIO_21_Mode_MDRA );
// MCLKXA of Mcbsp serve as SPICLK
GPIO_setQualification(obj->gpioHandle,GPIO_Number_22,GPIO_Qual_ASync );
GPIO_setMode( obj->gpioHandle,GPIO_Number_22,GPIO_22_Mode_MCLKXA );
// MFSXA of Mcbsp serve as SPISTE
GPIO_setQualification(obj->gpioHandle,GPIO_Number_23,GPIO_Qual_ASync );
GPIO_setMode( obj->gpioHandle,GPIO_Number_23,GPIO_23_Mode_MFSXA );
// CAN RX
GPIO_setMode( obj->gpioHandle,GPIO_Number_30,GPIO_30_Mode_CANRXA );
// CAN TX
GPIO_setMode( obj->gpioHandle,GPIO_Number_31,GPIO_31_Mode_CANTXA );
// EQEP2A for AS5147P_ABI
GPIO_setMode( obj->gpioHandle,GPIO_Number_54,GPIO_54_Mode_EQEP2A );
GPIO_setQualification( obj->gpioHandle,GPIO_Number_54,GPIO_Qual_Sample_3 );
// EQEP2B for AS5147P_ABI
GPIO_setMode( obj->gpioHandle,GPIO_Number_55,GPIO_55_Mode_EQEP2B );
GPIO_setQualification( obj->gpioHandle,GPIO_Number_55,GPIO_Qual_Sample_3 );
// EQEP2I for AS5147P_ABI
GPIO_setMode( obj->gpioHandle,GPIO_Number_56,GPIO_56_Mode_EQEP2I );
GPIO_setQualification( obj->gpioHandle,GPIO_Number_56,GPIO_Qual_Sample_3 );
//'''original GPIO setting of project "lab13a"
return;
}
```
### 1. Being compatible with 28A peak current
As I have mentioned in [*Design & Layout*](https://hackmd.io/@YehChiTing/ByHbr2PCH), the peak current of the motor controller is now designed to 28.28A. Since the peak current and the amplifier gain of the phase current sensing circuit have been changed, some corresponding modifications in my firmware are needed.
According to the peak current of my motor controller and the voltage range of the controller ADC, I set USER_IQ_FULL_SCALE_CURRENT_A and USER_ADC_FULL_SCALE_CURRENT_A in user_j1.h.
USER_IQ_FULL_SCALE_CURRENT_A:
Set to 1.2 times the maximum current of my motor controller.
$$
= 28.28 \times 1.2 = 33.936 \Rightarrow 34
$$
USER_ADC_FULL_SCALE_CURRENT_A:
$$
= \Delta V_{ADC}{[R_{shunt}{R43 \over R44}]}^{-1} = 3.3 \times {[0.0025 \times 20]}^{-1} = 66
$$
```clike=
#define USER_IQ_FULL_SCALE_CURRENT_A (34.0) // 20.0 Example for boostxldrv8301_revB typical usage
#define USER_ADC_FULL_SCALE_CURRENT_A (66.0) // 33.0 boostxldrv8301_revB current scaling
```
In drv8301.c, since the gain of the phase current sensing circuit in my motor controller were changed to 20, I adjusted the corresponding setting of ShuntAmpGain to 20VpV.
```clike=
void DRV8301_setupSpi(DRV8301_Handle handle, DRV_SPI_8301_Vars_t *Spi_8301_Vars)
{
//Other setting remain unchanged
// Update Control Register 2
drvRegName = DRV8301_RegName_Control_2;
drvDataNew = ( DRV8301_OcTwMode_Both | \
DRV8301_ShuntAmpGain_20VpV | \
DRV8301_DcCalMode_Ch1_Load | \
DRV8301_DcCalMode_Ch2_Load | \
DRV8301_OcOffTimeMode_Normal );
DRV8301_writeSpi( handle,drvRegName,drvDataNew );
//Other setting remain unchanged
return;
}
```
### 2. CAN-bus communication
The HAL and APIs of the eCAN1 module are defined in can.h/.c files. In the main file (proj_lab13a.c), I could just call the APIs implemented in can.c file to set the eCAN1 module. I also configured two mailboxes to transmit and receive data. The transmitting data frame is a Base CAN data frame with 24-bit data field; the receiving data frame is also a Base CAN data frame with 32-bit data field.
```clike=
HAL_setupECAN(handle);
HAL_enableEcanInt(halHandle);
ECAN_configMailbox( halHandle->ecanaHandle, MailBox0, 0x02aa, Enable_Mbox, Tx_Dir, Standard_ID, DLC_3, Overwrite_on, LAMI_0, Mask_not_used, 0x00000000);
ECAN_setTx_Priority( halHandle->ecanaHandle, MailBox0, Tx_leve31 );
ECAN_configMailbox( halHandle->ecanaHandle, MailBox16, 0x0555, Enable_Mbox, Rx_Dir, Standard_ID, DLC_4, Overwrite_on, LAMI_0, Mask_not_used, 0x00000000 );
ECAN_initMailboxUse( &gECAN_Mailbox, MailBox15, MailBox0, MailBox31, MailBox16 );
ECAN_configMailbox_Int( halHandle->ecanaHandle, MailBox16, Int_enable, Line1 );
ECAN_GlobalInt_Mask( halHandle->ecanaHandle, INT0_ENABLE | INT1_ENABLE );
```
In the function "HAL_setupECAN" of my HAL, I set the eCAN1 module working in normal mode with 1 Mbps Bit rate.
```clike=
void HAL_setupECAN(HAL_Handle handle){
HAL_Obj *obj = (HAL_Obj *)handle;
ECAN_setTXIO(obj->ecanaHandle);
ECAN_setRXIO(obj->ecanaHandle);
ECAN_Mode(obj->ecanaHandle, eCANmode);
ECAN_disableAllMailbox(obj->ecanaHandle);
ECAN_clearMSGCTRL(obj->ecanaHandle);
ECAN_clearMSGID(obj->ecanaHandle);
ECAN_clearCANTA(obj->ecanaHandle);
ECAN_clearCANRMP(obj->ecanaHandle);
ECAN_clearCANGIF0(obj->ecanaHandle);
ECAN_clearCANGIF1(obj->ecanaHandle);
ECAN_disableAllInt(obj->ecanaHandle);
ECAN_configMasterReg(obj->ecanaHandle, Timestamp_reset, Timestamp_is_reset, Normal_operation, Power_normal, MSB, PDR_activity, Requests_normal, ABO_0, No_effect, Mailbox_N0);
ECAN_setBitrate(obj->ecanaHandle, Bitrate_1M); // 90 MHz SYSCLKOUT. 45 MHz CAN module clock Bit rate = 1 Mbps
ECAN_setMailboxIntMask(obj->ecanaHandle, 0x00000000); //Mailbox interrupt is disabled.
ECAN_SelfTest(obj->ecanaHandle, Normal_mode);
}
```
With the setting mentioned above, the eCAN1 module could work very well. However, since there are two interrupts in my firmware (ECAN1INT for ecan1ISR and ADCINT1 for mainISR), I need to deal with the issue of interrupt priority. For better controller stability, the communication-purpose interrupt (ECAN1INT) should never block or preempt the control-loop interrupt (ADCINT1 in my case). But, according to Technical Reference Manual of TMS320F28069M [1], ECAN1INT have higher priority than ADCINT1. To solve the problem, I must find another interrupt source with higher priority for the mainISR because the eCAN module have only one interrupt to use. Thankfully, TMS320F28069M has an alternative high-priority interrupt for the ADC1 module: ADCINT1_HP. ADCINT1_HP has higher priority than ECAN1INT. To use ADCINT1_HP for the ADC1 module, I made some modifications shown below.
In hal.h
```clike=
static inline void HAL_acqAdcInt(HAL_Handle handle,const ADC_IntNumber_e intNumber)
{
HAL_Obj *obj = (HAL_Obj *)handle;
// clear the ADC interrupt flag
ADC_clearIntFlag( obj->adcHandle,intNumber );
#ifdef ADCINT1_HIGHEST_PRIORITY
PIE_clearInt( obj->pieHandle, PIE_GroupNumber_1 );
#else
PIE_clearInt( obj->pieHandle, PIE_GroupNumber_10 );
#endif
return;
} // end of HAL_acqAdcInt() function
static inline void HAL_initIntVectorTable(HAL_Handle handle)
{
HAL_Obj *obj = (HAL_Obj *)handle;
PIE_Obj *pie = (PIE_Obj *)obj->pieHandle;
ENABLE_PROTECTED_REGISTER_WRITE_MODE;
#ifdef ADCINT1_HIGHEST_PRIORITY
pie->ADCINT1_HP = &mainISR;
pie->ECAN1INT = &ecan1ISR;
#else
pie->ADCINT1 = &mainISR;
#endif
DISABLE_PROTECTED_REGISTER_WRITE_MODE;
return;
} // end of HAL_initIntVectorTable() function
```
In hal.c
```clike=
void HAL_enableAdcInts(HAL_Handle handle)
{
HAL_Obj *obj = (HAL_Obj *)handle;
#ifdef ADCINT1_HIGHEST_PRIORITY
// enable the PIE interrupts associated with the ADC interrupts
PIE_enableAdcInt( obj->pieHandle, ADC_IntNumber_1HP );
// enable the ADC interrupts
ADC_enableInt( obj->adcHandle, ADC_IntNumber_1 );
// enable the cpu interrupt for ADC interrupts
CPU_enableInt( obj->cpuHandle, CPU_IntNumber_1 );
#else
// enable the PIE interrupts associated with the ADC interrupts
PIE_enableAdcInt( obj->pieHandle, ADC_IntNumber_1 );
// enable the ADC interrupts
ADC_enableInt( obj->adcHandle, ADC_IntNumber_1 );
// enable the cpu interrupt for ADC interrupts
CPU_enableInt( obj->cpuHandle, CPU_IntNumber_10 );
#endif
return;
} // end of HAL_enableAdcInts() function
void HAL_enableEcanInt(HAL_Handle handle){
HAL_Obj *obj = (HAL_Obj *)handle;
PIE_enableInt(obj->pieHandle, PIE_GroupNumber_9, PIE_InterruptSource_ECANA1);
CPU_enableInt(obj->cpuHandle,CPU_IntNumber_9);
return;
}
```
### 3.Motor Command and Feedback interface for hign-level controller
As mentioned in the last section, I set the data field of transmitting/receiving CAN-bus frame to 24/32-bit . However, the so-called transmitting/receiving CAN-bus frame is for the low-level motor controller. In the perspective of high-level controller, the receiving CAN-bus frame for the low-level motor controller is actually a Command interface for the high-level controller. Likewise, the transmitting one could serve as a Feedback interface. In this section, I will define the Command and Feedback interface for the high-level controller.
#### Command interface
The 32-bit Command interface and its description are shown below. The length of Current command is set to 12-bit because the TMS320F28069M's ADC module for current sensing is 12-bit. Similarly, the 13-bit length of Position command is based on the 12-bit ABI resolution of AS5147P and the additional sign bit.
| Current command | Position command | Flags for motor controller | Reserved bits |
|:--:|:--:|:--:|:--:|
|31 : 20|19 : 7|6 : 5|4 : 0|
| Bit | Field | Description |
|:--:|:--:|:--:|
|31-20|Current command|fixed-point number ranged -1~1 [command_current / USER_IQ_FULL_SCALE_CURRENT_A]|
|19-7|Position command|fixed-point number ranged -1~1 [rev]|
|6|Flag_enableSys|1:enable_motor_controller, 0:disable_motor_controller|
|5|Flag_Run_Ctrl|1:start_control_loop, 0:stop_control_loop|
#### Feedback interface
The 24-bit Feedback interface and its description are shown below.
| Position feedback | States of motor controller | Flags of motor controller | Reserved bits |
|:--:|:--:|:--:|:--:|
|23 : 11|10 : 7|6 : 5|4 : 0|
| Bit | Field | Description |
|:--:|:--:|:--:|
|23-11|Position feedback|fixed-point number ranged -1~1 [rev]|
|10-9|Controller states*|00:error_state, 01: idle_state, 10:offline_state, 11:online_state|
|8-7|Estimater states*|00:error_state, 01:idle_state, 10:Rs_estimation_state, 11:online_state|
|6|Flag_enableSys|1:enable_motor_controller, 0:disable_motor_controller|
|5|Flag_Run_Ctrl|1:start_control_loop, 0:stop_control_loop|
*The Controller states and the Estimater states are defined in ctrl_states.h and est_states.h provided by TI MotorWare.
#### Encoding(Decoding) the Feedback(Command) interface
The low-level motor controller has to decode the Command interface and translate them into command variables. I implemented the decoding process for the Command interface in the ecan1ISR shoen below.
```clike=
interrupt void ecan1ISR(void){
halHandle->ecanaHandle->ECanaMboxes->MBOX0.MDL.all = FB_Flam;
while(halHandle->ecanaHandle->ECanaRegs->CANRMP.bit.RMP16 != 1);
halHandle->ecanaHandle->ECanaRegs->CANTRS.bit.TRS0 = 1;
while(halHandle->ecanaHandle->ECanaRegs->CANTA.bit.TA0 == 0);
halHandle->ecanaHandle->ECanaRegs->CANTA.bit.TA0 = 1;
Comd_Flam = halHandle->ecanaHandle->ECanaMboxes->MBOX16.MDL.all;
I_comd = (0x80000000 & Comd_Flam) + ((0x7ff00000 & Comd_Flam) >> 7);
Pos_comd_prv = Pos_comd;
Pos_comd = (0x80000000 & (Comd_Flam << 12)) + ((0x7ff80000 & (Comd_Flam << 12)) >> 7);
Pos_comd -= ENC_Offset;
Cmd_Perid = halHandle->ecanaHandle->ECanaMOTSRegs->MOTS16;
Vel_comd = _IQmpy(Pos_comd - Pos_comd_prv, _IQdiv(100000, ((Cmd_Perid << 1) + Cmd_Perid)));
gMotorVars.Flag_enableSys = (Comd_Flam >> 6) & 0x00000001;
gMotorVars.Flag_Run_Identify = (Comd_Flam >> 5) & 0x00000001;
halHandle->ecanaHandle->ECanaRegs->CANRMP.all = 0x00010000;
HAL_pieAckInt(halHandle, PIE_GroupNumber_9);
return;
}
```
In contrast with the case of the Command interface, the low-level motor controller has to encode feedback variables into the Feedback interface. Such encoding process is implemented in the mainISR, in where the motor control take place.
```clike=
Pos_FB = STPOSCONV_getPosition_mrev(stObj->posConvHandle) + ENC_Offset;
if(Pos_FB < 0 || Pos_FB > 0x01000000){
gMotorVars.Flag_Run_Identify = 0;
gMotorVars.Flag_enableSys = 0;
}
if(Pos_comd < - ENC_Offset || Pos_comd > (0x01000000 - ENC_Offset)){
gMotorVars.Flag_Run_Identify = 0;
}
FB_Flam = (0x80000000 & Pos_FB) + (((0x00fff000 & Pos_FB) << 7) & 0x7ff80000) + ((gMotorVars.Flag_enableSys << 14) & 0x00004000) + ((gMotorVars.Flag_Run_Identify << 13) & 0x00002000) + (FB_Flam & 0x00079f00);
STPOSCTL_setPositionReference_mrev(stObj->posCtlHandle, Pos_comd);
STPOSCTL_setVelocityReference(stObj->posCtlHandle, Vel_comd);
STPOSCTL_setAccelerationReference(stObj->posCtlHandle, 0);
// Run SpinTAC Position Control
STPOSCTL_run(stObj->posCtlHandle);
// Provide SpinTAC Position Control Torque Output to the FOC
CTRL_setIq_ref_pu(ctrlHandle, I_comd + STPOSCTL_getTorqueReference(stObj->posCtlHandle));
```
### 4.Magnetic encoder chip AS5147P
To support the SPI interface of AS5147P, the Mcbsp module of my motor controller should be configured in master SPI mode. For the Mcbsp module of TMS320F28069M, Texas Instruments provides F2806x_Mcbsp.c/.h with a HAL to users. However, the HAL of F2806x_Mcbsp.c/.h is not compatible with the HAL used in my firmware. The main difference between the two HAL is the encapsulation style for hardware registers. To make F2806x_Mcbsp.c/.h compatible with my firmware, I modified a few lines in the files.
In F2806x_Mcbsp.h, I added an additional HAL (McBSP_Handle) to link the hardware encapsulation of F2806x_Mcbsp.c/.h with the HAL in my firmware.
```clike=
typedef volatile struct McBSP_REGS McBSP_REGS_t;
typedef struct _McBSP_Obj_{
McBSP_REGS_t *McBSPRegs;
} McBSP_Obj;
typedef struct _McBSP_Obj_ *McBSP_Handle;
```
In F2806x_Mcbsp.c, I initiated and set the Mcbsp module with the McBSP_Handle defined in F2806x_Mcbsp.h.
```clike=
McBSP_Handle MCBSPA_SPI_init()
{
McBSP_Handle handle = (McBSP_Handle)calloc(1, sizeof(McBSP_Obj));
//Mcbspa_Handle handle;
if(handle == NULL) {
return (McBSP_Handle)NULL;
}
handle->McBSPRegs = (McBSP_REGS_t *)MCBSPA_BASE_ADDR;
return handle;
}
void MCBSPA_SPI_init_Set(McBSP_Handle MCBSPA_handle) {
struct McBSP_REGS McbShadow;
EALLOW; // Allow access to protected bits
McbShadow.SPCR2.all = MCBSPA_handle->McBSPRegs->SPCR2.all;
McbShadow.SPCR2.all = 0x0000;
MCBSPA_handle->McBSPRegs->SPCR2.all = McbShadow.SPCR2.all;
// Reset Receiver, Right justify word, Digital loopback dis.
McbShadow.SPCR1.all = MCBSPA_handle->McBSPRegs->SPCR1.all;
McbShadow.SPCR1.all = 0x0000;
MCBSPA_handle->McBSPRegs->SPCR1.all = McbShadow.SPCR1.all;
McbShadow.PCR.all = MCBSPA_handle->McBSPRegs->PCR.all;
McbShadow.PCR.all = 0x0F08; // (CLKXM=CLKRM=FSXM=FSRM= 1, FSXP = 1)
MCBSPA_handle->McBSPRegs->PCR.all = McbShadow.PCR.all;
McbShadow.SPCR1.bit.DLB = MCBSPA_handle->McBSPRegs->SPCR1.bit.DLB;
McbShadow.SPCR1.bit.DLB = 0;
MCBSPA_handle->McBSPRegs->SPCR1.bit.DLB = McbShadow.SPCR1.bit.DLB;
// Together with CLKXP/CLKRP determines clocking scheme
McbShadow.SPCR1.bit.CLKSTP = MCBSPA_handle->McBSPRegs->SPCR1.bit.CLKSTP;
McbShadow.SPCR1.bit.CLKSTP = 2;
MCBSPA_handle->McBSPRegs->SPCR1.bit.CLKSTP = McbShadow.SPCR1.bit.CLKSTP;
McbShadow.PCR.bit.CLKXP = MCBSPA_handle->McBSPRegs->PCR.bit.CLKXP;
McbShadow.PCR.bit.CLKXP = 0; // CPOL = 0, CPHA = 0 rising edge no
MCBSPA_handle->McBSPRegs->PCR.bit.CLKXP = McbShadow.PCR.bit.CLKXP;
McbShadow.PCR.bit.CLKRP = MCBSPA_handle->McBSPRegs->PCR.bit.CLKRP;
McbShadow.PCR.bit.CLKRP = 0;
MCBSPA_handle->McBSPRegs->PCR.bit.CLKRP = McbShadow.PCR.bit.CLKRP;
// Single-phase frame, 1 word/frame, No companding (Receive)
McbShadow.RCR2.all = MCBSPA_handle->McBSPRegs->RCR2.all;
McbShadow.RCR2.all = 0x0;
MCBSPA_handle->McBSPRegs->RCR2.all = McbShadow.RCR2.all;
McbShadow.RCR1.all = MCBSPA_handle->McBSPRegs->RCR1.all;
McbShadow.RCR1.all = 0x0;
MCBSPA_handle->McBSPRegs->RCR1.all = McbShadow.RCR1.all;
McbShadow.XCR2.all = MCBSPA_handle->McBSPRegs->XCR2.all;
McbShadow.XCR2.all = 0x0;
MCBSPA_handle->McBSPRegs->XCR2.all = McbShadow.XCR2.all;
McbShadow.XCR1.all = MCBSPA_handle->McBSPRegs->XCR1.all;
McbShadow.XCR1.all = 0x0;
MCBSPA_handle->McBSPRegs->XCR1.all = McbShadow.XCR1.all;
// FSX setup time 1 in master mode. 0 for slave mode (Receive)
McbShadow.RCR2.bit.RDATDLY = MCBSPA_handle->McBSPRegs->RCR2.bit.RDATDLY;
McbShadow.RCR2.bit.RDATDLY = 01;
MCBSPA_handle->McBSPRegs->RCR2.bit.RDATDLY = McbShadow.RCR2.bit.RDATDLY;
// FSX setup time 1 in master mode. 0 for slave mode (Transmit)
McbShadow.XCR2.bit.XDATDLY = MCBSPA_handle->McBSPRegs->XCR2.bit.XDATDLY;
McbShadow.XCR2.bit.XDATDLY = 01;
MCBSPA_handle->McBSPRegs->XCR2.bit.XDATDLY = McbShadow.XCR2.bit.XDATDLY;
McbShadow.RCR1.bit.RWDLEN1 = MCBSPA_handle->McBSPRegs->RCR1.bit.RWDLEN1;
McbShadow.RCR1.bit.RWDLEN1 = 2; // 16-bit word
MCBSPA_handle->McBSPRegs->RCR1.bit.RWDLEN1 = McbShadow.RCR1.bit.RWDLEN1;
McbShadow.XCR1.bit.XWDLEN1 = MCBSPA_handle->McBSPRegs->XCR1.bit.XWDLEN1;
McbShadow.XCR1.bit.XWDLEN1 = 2; // 16-bit word
MCBSPA_handle->McBSPRegs->XCR1.bit.XWDLEN1 = McbShadow.XCR1.bit.XWDLEN1;
McbShadow.SRGR2.all = MCBSPA_handle->McBSPRegs->SRGR2.all;
McbShadow.SRGR2.all = 0x2000; // CLKSM=1, FSGM=0, FPER = 1 CLKG periods
MCBSPA_handle->McBSPRegs->SRGR2.all = McbShadow.SRGR2.all;
McbShadow.SRGR1.all = MCBSPA_handle->McBSPRegs->SRGR1.all;
McbShadow.SRGR1.all = 0x000f; // Frame Width = 1 CLKG period, CLKGDV=1
MCBSPA_handle->McBSPRegs->SRGR1.all = McbShadow.SRGR1.all;
McbShadow.SPCR2.bit.GRST = MCBSPA_handle->McBSPRegs->SPCR2.bit.GRST;
McbShadow.SPCR2.bit.GRST = 1; / Enable the sample rate generator
MCBSPA_handle->McBSPRegs->SPCR2.bit.GRST = McbShadow.SPCR2.bit.GRST;
usDelay(2);
McbShadow.SPCR2.bit.XRST = MCBSPA_handle->McBSPRegs->SPCR2.bit.XRST;
McbShadow.SPCR2.bit.XRST = 1; // Release TX from Reset
MCBSPA_handle->McBSPRegs->SPCR2.bit.XRST = McbShadow.SPCR2.bit.XRST;
McbShadow.SPCR1.bit.RRST = MCBSPA_handle->McBSPRegs->SPCR1.bit.RRST;
McbShadow.SPCR1.bit.RRST = 1; // Release RX from Reset
MCBSPA_handle->McBSPRegs->SPCR1.bit.RRST = McbShadow.SPCR1.bit.RRST;
McbShadow.SPCR2.bit.FRST = MCBSPA_handle->McBSPRegs->SPCR2.bit.FRST;
McbShadow.SPCR2.bit.FRST = 1; // Frame Sync Generator reset
MCBSPA_handle->McBSPRegs->SPCR2.bit.FRST = McbShadow.SPCR2.bit.FRST;
EDIS;
}
```
## Testing the motor controller
I assembled the motor controller and the BLDC motor together.
The test rig for the motor controller is completed.


### Current loop test
Before jumping into the motor control test, I should make sure that the hardware of the motor controller is working well. To test the hardware, I disabled the position and velocity control-loop in my motor control firmware; the only effective control-loop is the current control-loop. Then I slightly increased the Iq value until the motor started spinning.

Measuring the output voltage of the phase current sensing circuit.

### CAN-bus test
I also tested the CAN-bus communication with the Command/Feedback interface. As the figure shown below, the high-level controller (STM32F4) sent the command with the set bit of Flag_enableSys to enable the low-level motor controller. After that, the motor controller immediately echoed the feedback with the position information of the motor.

### Function test with position command
{%youtube 8HP0f2rcWZM %}
## Reference
[1] http://www.ti.com/lit/ug/spruh18h/spruh18h.pdf