# 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. ![TopView of test setup](https://i.imgur.com/XLTJ7f5.jpg =550x310) ![BottomView of test setup](https://i.imgur.com/3vhrcSM.jpg =550x310) ### 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. ![Current loop test](https://i.imgur.com/VyYXKRs.jpg =310x550) Measuring the output voltage of the phase current sensing circuit. ![Phase current sensing](https://i.imgur.com/W4gDFGW.jpg =550x310) ### 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. ![CAN-bus test](https://i.imgur.com/JtpeCL0.jpg =550x310) ### Function test with position command {%youtube 8HP0f2rcWZM %} ## Reference [1] http://www.ti.com/lit/ug/spruh18h/spruh18h.pdf