在上一篇博文中,我们能够确定级联控制器回路的稳定P-I参数。有了这些知识,我们想向您展示我们是如何创建一个小的示例程序的,并放入Landungsrbücke的固件中。
此代码的基本功能是初始化步进电机控制的TMC4671。这是在init_Basics(…)中通过编写几个配置寄存器来完成的,而reset_Basics(…)将此工作中使用的所有寄存器重置为定义的0状态。这将取代IDE配置向导中使用默认按钮的第一步。
配置ABN(…)-功能将在功率因数调整转子至0后将ABN计数设置为0。要做到这一点,我们还需要对磁通施加电压。
init_PosMode(…)设置TMC4671进入位置模式的正确寄存器。
使用TMC4671_setAbsolutTargetPosition(…)将控制器的目标位置设置为所需值,使电机转动,直到达到目标位置。这里的绝对位置总是指我们在configure_ABN(…)函数中配置的零位置。
注:如果您希望相对于实际位置移动,可以使用函数TMC4671_setRelativeTargetPosition(…)。
为了找到我的设置的最小和最大位置,我在“查找目标位置”(…)中选择了两个无法到达的目标位置。对我来说,这是500000和-500000。控制器等待一定时间以确保它到达结束位置。然后用函数TMC4671_getActualPosition(…)保存实际位置。另一个方向也进行同样的设置。两个端点的值现在可以用于新的目标位置,可以再次使用TMC4671_setAbsolutTargetPosition(…)来设置。
运行时也可以使用线性坡道加速和减速, 使用函数TMC_ramp_linear_compute(…)和类型为TMC_LinearRamp的结构来实现这个功能(您可以在文件LinearRamp1.h和LinearRamp1.c中的API中找到更多信息)。这将在软件中创建一个包含我们的轨迹信息的斜坡。我还使用上述文件中的相应函数设置了结构的一些参数。我可以使用结构中的TMC_ramp_linear_set_target position(…)设置渐变的目标位置。之后,我使用TMC4671_setAbsolutTargetPosition(calculated_ramp)中计算出的斜坡位置, 循环工作直到达到实际目标位置,。
斜坡有什么作用?我们不是设定一条指令可以到达的绝对目标位置,而是将轨迹划分为多个子目标位置。在循环的每次迭代中,我们增加目标位置,控制器将调整到新的目标。然而,Landungsbrücke增加目标位置的速度比我们控制器的自适应速度高,因此随着时间的推移,目标位置和实际电机位置之间的差异增大。当实际位置和目标位置之间的偏移量增加时,这会导致电机加速。
减速则相反。Landungsbrücke将降低设定目标位置的速度,控制器将慢慢跟上,直到到达终点位置。
为了提高马达的速度,我把实际的斜坡位置乘以1024倍。这必须通过将软件斜坡的目标位置除以1024来补偿。这样我们就可以根据我们的应用来调整斜坡,并增加马达的加速度和速度。
为什么要使用(软件)渐变?基本上,它给了我们一个更稳定的方法来达到我们的目标位置,因为我们有一个确定的加速和减速的方法,它大大减少了超过目标的可能性。
Landungsbrücke在TMC4671寄存器中交替这些目标位置,因此电机将交替滑动的位置。
使用完成的代码,我为Landungsrbücke的引导加载程序生成一个十六进制文件,并使用TMCL-IDE更新它。
希望你能利用一些显示的例子,以便你转换数字信息为你的项目的物理运动!
使用以下代码,将初始化我的驱动器,执行编码器初始化,并在两个末端停止上执行重设原点。然后,执行来回运动。
我可以用TMCL-IDE将固件上传到Landungsbrücke,开机后它会自动启动。我可以通过RTMI监控系统行为。
代码示例:
#include "boards/Board.h" #include "hal/derivative.h" #include "hal/HAL.h" #include "tmc/IdDetection.h" #include "tmc/TMCL.h" #include "tmc/VitalSignsMonitor.h" #include "tmc/BoardAssignment.h" #include "tmc/ramp/LinearRamp1.h" #include "TMC-API/tmc/ic/TMC4671/TMC4671.h" #include "TMC-API/tmc/ic/TMC4671/TMC4671_Register.h" //As I use the names of its registers this needs to be included //mydefines #define STATE_START_INIT 1 #define STATE_RUN_1 2 #define STATE_RUN_2 3 #define UD_EXT 3202 //Torque-Flux Limits 15000 #define TORQUE_FLUX_LIMIT 10000 //Velocity Limit 10000 #define VELOCITY_LIMIT 10000 //P-I-defines #define TORQUE_FLUX_P 4952 #define TORQUE_FLUX_I 10939 #define VELOCITY_P 2200 #define VELOCITY_I 2000 #define POSITION_P 45 #define POSITION_I 0 //for checking motor supply voltage #define VM_FACTOR 713 #define ADC_VM_RES 65535 //Landungsbruecke define -- change for Startrampe //Software-Ramp defines #define STEPDIR_FREQUENCY (1 << 17) #define STEPDIR_MAX_VELOCITY (1 << 20) #define STEPDIR_MAX_ACCELERATION 2147418111 #define STEPDIR_DEFAULT_ACCELERATION 100000 #define STEPDIR_DEFAULT_VELOCITY STEPDIR_MAX_VELOCITY #define RAMP_SPEED_FACTOR 1024 //end mydefines void init_PosMode(uint8_t motor) { //Set registers needed for position mode //tmc4671_writeInt(motor, TMC4671_VELOCITY_SELECTION, TMC4671_VELOCITY_PHI_M_ABN); tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_TARGET_DDT_LIMITS, 32767); //max tmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 32767); //max tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_LIMITS, TORQUE_FLUX_LIMIT); tmc4671_writeInt(motor, TMC4671_PID_ACCELERATION_LIMIT, 2147483647); tmc4671_writeInt(motor, TMC4671_PID_VELOCITY_LIMIT, VELOCITY_LIMIT); tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_LOW, 0x80000001); tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_HIGH, 0x7FFFFFFF); tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0x00000003); //phi_e_abn tmc4671_writeInt(motor, TMC4671_VELOCITY_SELECTION, TMC4671_VELOCITY_PHI_M_ABN); tmc4671_writeInt(motor, TMC4671_POSITION_SELECTION, TMC4671_POSITION_PHI_M_ABN); //phi_m_abn tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0x00000003); //position mode //The following gain-values were determined using the USB-2-RTMI and following the PI-tuning guide tmc4671_setTorqueFluxPI(motor, TORQUE_FLUX_P, TORQUE_FLUX_I); //(motor, P, I) tmc4671_setVelocityPI(motor, VELOCITY_P, VELOCITY_I); tmc4671_setPositionPI(motor, POSITION_P, POSITION_I); } //Check VM uint32_t current_VM() { uint32_t VM; VM = *HAL.ADCs->VM; // read ADC value for motor supply VM VM = (VM*VM_FACTOR)/ADC_VM_RES; // calculate voltage from ADC value return VM; } //End Check VM void init_Basics(uint8_t motor) { //Configure basic registers like motor type, adc-offset tmc4671_writeInt(motor, TMC4671_MOTOR_TYPE_N_POLE_PAIRS, 0x00020032); //x0002 - Stepper, x0032 = 50 polepairs tmc4671_writeInt(motor, TMC4671_PWM_POLARITIES, 0x00000000); tmc4671_writeInt(motor, TMC4671_PWM_MAXCNT, 0x00000F9F); //Maxcount of PWM-counter tmc4671_writeInt(motor, TMC4671_PWM_BBM_H_BBM_L, 0x00001919); //Break before make times tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0x00000007); //turn on PWM //Configure ADC -- Values were taken from the ide after the set offset option was used tmc4671_writeInt(motor, TMC4671_ADC_I_SELECT, 0x18000100); // configure ADC-Inputs tmc4671_writeInt(motor, TMC4671_dsADC_MCFG_B_MCFG_A, 0x00100010); // configure ADCs tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_A, 0x20000000); // turn on clock-signal for group-A ADCs tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_B, 0x00000000); // turn off clock-signal for group-B ADCs tmc4671_writeInt(motor, TMC4671_dsADC_MDEC_B_MDEC_A, 0x014E014E); // Constants for filter tmc4671_writeInt(motor, TMC4671_ADC_I0_SCALE_OFFSET, 0x01005D80); // Fill in the offsets determined in the IDE tmc4671_writeInt(motor, TMC4671_ADC_I1_SCALE_OFFSET, 0x01005CEF); // Fill in the offsets determined in the IDE // Open loop settings tmc4671_writeInt(motor, TMC4671_OPENLOOP_MODE, 0x00000000); tmc4671_writeInt(motor, TMC4671_OPENLOOP_ACCELERATION, 0x0000003C); tmc4671_writeInt(motor, TMC4671_OPENLOOP_VELOCITY_TARGET, 0x00000000); tmc4671_writeInt(motor, TMC4671_OPENLOOP_PHI, 0x00000000); // Feedback selection tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0x00000000); tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0x00000008); //tmc4671_writeInt(motor, TMC4671_UQ_UD_EXT, 0x00000F9F); tmc4671_writeInt(motor, TMC4671_ABN_DECODER_PPR, 0x00009C40); //Set Limits tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_TARGET_DDT_LIMITS, 0x7FFF); //max tmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 32767); //max tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_LIMITS, TORQUE_FLUX_LIMIT); tmc4671_writeInt(motor, TMC4671_PID_ACCELERATION_LIMIT, 2147483647); tmc4671_writeInt(motor, TMC4671_PID_VELOCITY_LIMIT, VELOCITY_LIMIT); tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_LOW, 0x80000001); tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_HIGH, 0x7FFFFFFF); } void reset_Basics(uint8_t motor) { //reset registers of TMC4671 that will later be used to 0 wait(1500); tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0); tmc4671_writeInt(motor, TMC4671_MOTOR_TYPE_N_POLE_PAIRS, 0); tmc4671_writeInt(motor, TMC4671_PWM_POLARITIES, 0); tmc4671_writeInt(motor, TMC4671_PWM_MAXCNT, 0); tmc4671_writeInt(motor, TMC4671_PWM_BBM_H_BBM_L, 0); tmc4671_writeInt(motor, TMC4671_ADC_I_SELECT, 0); tmc4671_writeInt(motor, TMC4671_dsADC_MCFG_B_MCFG_A, 0); tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_A, 0); tmc4671_writeInt(motor, TMC4671_dsADC_MCLK_B, 0); tmc4671_writeInt(motor, TMC4671_dsADC_MDEC_B_MDEC_A, 0); tmc4671_writeInt(motor, TMC4671_ADC_I0_SCALE_OFFSET, 0); tmc4671_writeInt(motor, TMC4671_ADC_I1_SCALE_OFFSET, 0); tmc4671_writeInt(motor, TMC4671_OPENLOOP_MODE, 0); tmc4671_writeInt(motor, TMC4671_OPENLOOP_ACCELERATION, 0); tmc4671_writeInt(motor, TMC4671_OPENLOOP_VELOCITY_TARGET, 0); tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0); tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0); tmc4671_writeInt(motor, TMC4671_UQ_UD_EXT, 0); tmc4671_writeInt(motor, TMC4671_ABN_DECODER_PPR, 0); tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_TARGET_DDT_LIMITS, 0); tmc4671_writeInt(motor, TMC4671_PIDOUT_UQ_UD_LIMITS, 0); tmc4671_writeInt(motor, TMC4671_PID_TORQUE_FLUX_LIMITS, 0); tmc4671_writeInt(motor, TMC4671_PID_ACCELERATION_LIMIT, 0); tmc4671_writeInt(motor, TMC4671_PID_VELOCITY_LIMIT, 0); tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_LOW, 0); tmc4671_writeInt(motor, TMC4671_PID_POSITION_LIMIT_HIGH, 0); tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0); tmc4671_writeInt(motor, TMC4671_VELOCITY_SELECTION, 0); tmc4671_writeInt(motor, TMC4671_POSITION_SELECTION, 0); tmc4671_writeInt(motor, TMC4671_MODE_RAMP_MODE_MOTION, 0); tmc4671_writeInt(motor, TMC4671_PID_POSITION_TARGET, 0); tmc4671_writeInt(motor, TMC4671_PID_POSITION_ACTUAL, 0); tmc4671_setTorqueFluxPI(motor, 0, 0); //(motor, P, I) tmc4671_setVelocityPI(motor, 0, 0); // (motor, P, I) tmc4671_setPositionPI(motor, 0, 0); // (motor, P, I) } void init_softwareRamp(TMC_LinearRamp *linearRamp) { tmc_ramp_linear_init(linearRamp); tmc_ramp_linear_set_mode(linearRamp, TMC_RAMP_LINEAR_MODE_POSITION); tmc_ramp_linear_set_maxVelocity(linearRamp, STEPDIR_DEFAULT_VELOCITY); tmc_ramp_linear_set_acceleration(linearRamp, STEPDIR_DEFAULT_ACCELERATION); } void find_targetPositions(uint8_t motor, int32_t *lower_End, int32_t*upper_End) { tmc4671_setAbsolutTargetPosition(motor, 500000); // ~ 7.5 motor turns wait(1500); /* After slide reached one end of the setup the absolute position is stored in memory For convenience I reduce the value of the target position to be saved a little, * as driving into the endposition with full force is annoyingly loud :) * if this is your goal just remove the +/- 1000 */ *lower_End = (tmc4671_getActualPosition(motor)-1000); tmc4671_setAbsolutTargetPosition(motor, 0); //return to the starting position wait(1000); // Assumed an unreachable position so the slide reaches the other side of the setup tmc4671_setAbsolutTargetPosition(motor, -500000); // ~ -7.5 motor turns wait(1500); // After slide reached the other end of the setup the absolute position is stored in memory *upper_End = (tmc4671_getActualPosition(motor)+1000); tmc4671_setAbsolutTargetPosition(motor, 0); wait(1500); } void configure_ABN(uint8_t motor, uint16_t startVoltage) { // set ABN_DECODER_PHI_E_OFFSET to zero tmc4671_writeRegister16BitValue(motor, TMC4671_ABN_DECODER_PHI_E_PHI_M_OFFSET, BIT_16_TO_31, 0); // select phi_e_ext tmc4671_writeRegister16BitValue(motor, TMC4671_PHI_E_SELECTION, BIT_0_TO_15, 1); // set an initialization voltage on UD_EXT (to the flux, not the torque!) tmc4671_writeRegister16BitValue(motor, TMC4671_UQ_UD_EXT, BIT_16_TO_31, 0); tmc4671_writeRegister16BitValue(motor, TMC4671_UQ_UD_EXT, BIT_0_TO_15, startVoltage); // set the "zero" angle tmc4671_writeRegister16BitValue(motor, TMC4671_PHI_E_EXT, BIT_0_TO_15, 0); tmc4671_writeInt(motor, TMC4671_ABN_DECODER_MODE, 0x00001000); //Set PHI_E to 0° angle so the rotor alignes itself with it tmc4671_writeInt(motor, TMC4671_PHI_E_SELECTION, 0x00000000); wait(4000); tmc4671_readRegister16BitValue(motor, TMC4671_ABN_DECODER_PHI_E_PHI_M, BIT_16_TO_31); //After the rotor has aligned this will set the ABN_DECODER_COUNT to 0 so there is a defined 0-position tmc4671_writeInt(motor, TMC4671_ABN_DECODER_COUNT, 0); } void tmc4671setPositionModeAndRun(uint8_t motor, uint8_t *initState, uint16_t startVoltage, int32_t *upper_End, int32_t *lower_End, TMC_LinearRamp *linearRamp) { /*State Machine with state-pointer *initState and 3 states * STATE_START_INIT: Sets TMC4671-registers for my application and configures the ABN-Encoder * Drives to the ends of the track to get the actual min/max position values * Drives to the first end position * * STATE_RUN_1: Drives to one end and changes direction when the *lower_End is reached * * STATE_RUN_2: Drives to the other end and changes direction when *upper_End is reached */ switch (*initState) { case STATE_START_INIT: //reset TMC4671 registers reset_Basics(motor); //set TMC4671 registers with basic data like motortype, number of polepairs and so on init_Basics(motor); //initialize the software ramp init_softwareRamp(linearRamp); //configure ABN configure_ABN(motor, startVoltage); //Enter position-Mode init_PosMode(motor); //configure the end positions and store them in *lower_End and *upper_End find_targetPositions(motor, lower_End, upper_End); *initState = STATE_RUN_1; //set the first target position using the software ramp tmc_ramp_linear_set_targetPosition(linearRamp, (*lower_End/RAMP_SPEED_FACTOR)); break; case STATE_RUN_1: //drive from one end to the other: positive direction tmc_ramp_linear_compute(linearRamp); tmc4671_setAbsolutTargetPosition(motor, (RAMP_SPEED_FACTOR*tmc_ramp_linear_get_rampPosition(linearRamp))); //When the target position is reached we will change direction if(tmc4671_getActualPosition(motor) >= (*lower_End-RAMP_SPEED_FACTOR)) { wait(50); *initState = STATE_RUN_2; tmc_ramp_linear_set_targetPosition(linearRamp, (*upper_End/RAMP_SPEED_FACTOR)); break; } *initState = STATE_RUN_1; break; case STATE_RUN_2: //drive from one end to the other: negative direction tmc_ramp_linear_compute(linearRamp); tmc4671_setAbsolutTargetPosition(motor, (RAMP_SPEED_FACTOR*tmc_ramp_linear_get_rampPosition(linearRamp))); //When the target position is reached we will change direction if(tmc4671_getActualPosition(motor) <= (*upper_End + RAMP_SPEED_FACTOR)) { wait(50); *initState = STATE_RUN_1; tmc_ramp_linear_set_targetPosition(linearRamp, (*lower_End/RAMP_SPEED_FACTOR)); break; } *initState = STATE_RUN_2; break; default: *initState = 0; break; } } const char *VersionString = MODULE_ID"V308"; // module id and version of the firmware shown in the TMCL-IDE /* Keep as is! These lines are important for the update functionality. */ #if defined(Landungsbruecke) const uint8_t Protection[] __attribute__ ((section(".cfmconfig")))= { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //Backdoor key 0xFF, 0xFF, 0xFF, 0xFF, //Flash protection (FPPROT) 0x7E, //Flash security (FSEC) 0xF9, //Flash option (FOPT) 0xFF, //reserved 0xFF //reserved }; struct BootloaderConfig { uint32_t BLMagic; uint32_t drvEnableResetValue; }; // This struct gets placed at a specific address by the linker struct BootloaderConfig __attribute__ ((section(".bldata"))) BLConfig; #endif /* Check if jumping into bootloader is forced */ /* */ /* In order to jump to bootloader e.g. because of an accidental infinite loop */ /* in a modified firmware you may short ID_CLK and ID_CH0 pins on start up. */ /* This will force the entrance into bootloader mode and allow to replace bad firmware. */ void shallForceBoot() { // toggle each pin and see if you can read the state on the other // leave if not, because this means that the pins are not tied together HAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CLK); HAL.IOs->config->toInput(&HAL.IOs->pins->ID_CH0); HAL.IOs->config->setHigh(&HAL.IOs->pins->ID_CLK); if(!HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CH0)) return; HAL.IOs->config->setLow(&HAL.IOs->pins->ID_CLK); if(HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CH0)) return; HAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CH0); HAL.IOs->config->toInput(&HAL.IOs->pins->ID_CLK); HAL.IOs->config->setHigh(&HAL.IOs->pins->ID_CH0); if(!HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CLK)) return; HAL.IOs->config->setLow(&HAL.IOs->pins->ID_CH0); if(HAL.IOs->config->isHigh(&HAL.IOs->pins->ID_CLK)) return; // not returned, this means pins are tied together tmcl_boot(); } /* Call all standard initialization routines. */ static void init() { #if defined(Landungsbruecke) // Default value: Driver enable gets set high by the bootloader BLConfig.drvEnableResetValue = 1; #endif HAL.init(); // Initialize Hardware Abstraction Layer IDDetection_init(); // Initialize board detection tmcl_init(); // Initialize TMCL communication tmcdriver_init(); // Initialize dummy driver board --> preset EvalBoards.ch2 tmcmotioncontroller_init(); // Initialize dummy motion controller board --> preset EvalBoards.ch1 VitalSignsMonitor.busy = 1; // Put state to busy Evalboards.driverEnable = DRIVER_ENABLE; Evalboards.ch1.id = 0; // preset id for driver board to 0 --> error/not found Evalboards.ch2.id = 0; // preset id for driver board to 0 --> error/not found // We disable the drivers before configurating anything HAL.IOs->config->toOutput(&HAL.IOs->pins->DIO0); HAL.IOs->config->setHigh(&HAL.IOs->pins->DIO0); IdAssignmentTypeDef ids = { 0 }; IDDetection_initialScan(&ids); // start initial board detection IDDetection_initialScan(&ids); // start second time, first time not 100% reliable, not sure why - too fast after startup? if(!ids.ch1.id && !ids.ch2.id) { shallForceBoot(); // only checking to force jump into bootloader if there are no boards attached HAL.IOs->config->toOutput(&HAL.IOs->pins->ID_CLK); HAL.IOs->config->toInput(&HAL.IOs->pins->ID_CH0); } Board_assign(&ids); // assign boards with detected id VitalSignsMonitor.busy = 0; // not busy any more! } /* main function */ int main(void) { //initialise some pointers //state pointer for fsm uint8_t init_State = STATE_START_INIT; uint8_t *initState; initState = &init_State; //store one end of the track int32_t upper_end = 0; int32_t *upper_End; upper_End= &upper_end; //store the other end int32_t lower_end = 0; int32_t *lower_End; lower_End= &lower_end; //convenience uint8_t motor = 0; //"flipflop" uint8_t block = 0; //struct used for the software ramp - see \TMC-API\tmc\ramp\LinearRamp1.h for typedef etc. TMC_LinearRamp adressRamp; TMC_LinearRamp *aRamp; aRamp = &adressRamp; init(); // Main loop while(1) { // Check all parameters and life signs and mark errors vitalsignsmonitor_checkVitalSigns(); // Perodic jobs of Motion controller/Driver boards Evalboards.ch1.periodicJob(systick_getTick()); Evalboards.ch2.periodicJob(systick_getTick()); // Process TMCL communication tmcl_process(); if (Evalboards.ch1.id != ID_TMC4671) continue; if(current_VM()<20){ tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0); // Turn off PWM after unexpected reset reset_Basics(motor); // Set the registers needed in this example to a defined default state continue; } if(block == 0) { tmc4671_writeInt(motor, TMC4671_PWM_SV_CHOP, 0); //Turn of PWM-Register after SPI was initialized reset_Basics(motor); block = 1; } tmc4671setPositionModeAndRun(motor, initState, UD_EXT, lower_End, upper_End, aRamp); } return 0; }
评论前必须登录!
注册