/** * \file * \brief Motor driver implementation. * \author Erich Styger, erich.styger@hslu.ch * \license SPDX-License-Identifier: BSD-3-Clause * This module a driver for up to two small DC motors. */ #include "platform.h" #if PL_CONFIG_USE_MOTORS #include "Motor.h" #include "McuGPIO.h" #include "Timer.h" #include "McuUtility.h" #if PL_HAS_MOTOR_BRAKE #include "BrakeL.h" #include "BrakeR.h" #endif #if PL_HAS_MOTOR_CURRENT_SENSE #include "SNS0.h" #endif #if PL_HAS_MOTOR_INAB #include "DIRRB.h" #include "DIRLB.h" #endif static MOT_MotorDevice motorL, motorR; static bool isMotorOn = TRUE; static McuGPIO_Handle_t MOT_DirL, MOT_DirR; MOT_MotorDevice *MOT_GetMotorHandle(MOT_MotorSide side) { if (side==MOT_MOTOR_LEFT) { return &motorL; } else { return &motorR; } } #if PL_HAS_MOTOR_BRAKE void MOT_SetBrake(MOT_MotorDevice *motor, bool on) { if (on && !motor->brake) { motor->BrakePutVal(1); motor->brake = TRUE; } else if (!on && motor->brake) { motor->BrakePutVal(0); motor->brake = FALSE; } } bool MOT_GetBrake(MOT_MotorDevice *motor) { return motor->brake; } #endif #if PL_HAS_MOTOR_CURRENT_SENSE void MOT_MeasureCurrent(MOT_MotorDevice *motorA, MOT_MotorDevice *motorB) { #define SAMPLE_GROUP_SIZE 1U SNS0_TResultData MeasuredValues[SAMPLE_GROUP_SIZE]; LDD_ADC_TSample SampleGroup[SAMPLE_GROUP_SIZE]; SampleGroup[0].ChannelIdx = 0U; /* Create one-sample group */ (void)SNS0_CreateSampleGroup(motorA->SNSdeviceData, (LDD_ADC_TSample *)SampleGroup, SAMPLE_GROUP_SIZE); /* Set created sample group */ (void)SNS0_StartSingleMeasurement(motorA->SNSdeviceData); /* Start continuous measurement */ while (!SNS0_GetMeasurementCompleteStatus(motorA->SNSdeviceData)) {}; /* Wait for conversion completeness */ (void)SNS0_GetMeasuredValues(motorA->SNSdeviceData, (LDD_TData *)MeasuredValues); /* Read measured values */ motorA->currentValue = MeasuredValues[0]; SampleGroup[0].ChannelIdx = 1U; /* Create one-sample group */ (void)SNS0_CreateSampleGroup(motorB->SNSdeviceData, (LDD_ADC_TSample *)SampleGroup, SAMPLE_GROUP_SIZE); /* Set created sample group */ (void)SNS0_StartSingleMeasurement(motorB->SNSdeviceData); /* Start continuous measurement */ while (!SNS0_GetMeasurementCompleteStatus(motorB->SNSdeviceData)) {}; /* Wait for conversion completeness */ (void)SNS0_GetMeasuredValues(motorB->SNSdeviceData, (LDD_TData *)MeasuredValues); /* Read measured values */ motorB->currentValue = MeasuredValues[0]; } static void WriteCurrent(MOT_MotorDevice *motor, const McuShell_StdIOType *io) { unsigned char buf[26]; uint16_t val; buf[0] = '\0'; McuUtility_strcat(buf, sizeof(buf), (unsigned char*)", current val 0x"); McuUtility_strcatNum16Hex(buf, sizeof(buf), motor->currentValue); McuShell_SendStr(buf, io->stdOut); val = motor->currentValue/(0xFFFF/3300); /* 3300 mV full scale */ buf[0] = '\0'; McuUtility_strcat(buf, sizeof(buf), (unsigned char*)", mV "); McuUtility_strcatNum16u(buf, sizeof(buf), val); McuShell_SendStr(buf, io->stdOut); val = motor->currentValue/(0xFFFF/2000); /* 2000 mA full scale */ buf[0] = '\0'; McuUtility_strcat(buf, sizeof(buf), (unsigned char*)", mA "); McuUtility_strcatNum16u(buf, sizeof(buf), val); McuShell_SendStr(buf, io->stdOut); } #endif static void PWMLSetRatio16(uint16_t ratio) { /* lower ratio is higher speed */ ratio = 0xffff-ratio; /* because already inverted */ ratio = ratio/(0xffff/100); /* scale to 0...100 */ TMR_SetLeftMotorPWMDutyPercent(ratio); } static void PWMRSetRatio16(uint16_t ratio) { ratio = 0xffff-ratio; /* because already inverted */ ratio = ratio/(0xffff/100); /* scale to 0...100 */ TMR_SetRightMotorPWMDutyPercent(ratio); } static void DirLPutVal(bool val) { McuGPIO_SetValue(MOT_DirL, val); } static void DirRPutVal(bool val) { McuGPIO_SetValue(MOT_DirR, val); } #if PL_HAS_MOTOR_BRAKE static void BrakeLPutVal(bool val) { BrakeL_PutVal(val); } static void BrakeRPutVal(bool val) { BrakeR_PutVal(val); } #endif /* PL_HAS_MOTOR_BRAKE */ void MOT_SetVal(MOT_MotorDevice *motor, uint16_t val) { if (isMotorOn) { #if PL_HAS_MOTOR_BRAKE if (val==0xffff) { /* stop */ motor->currPWMvalue = 0; motor->SetRatio16(0); /* for max brake */ MOT_SetBrake(motor, TRUE); } else { motor->currPWMvalue = val; MOT_SetBrake(motor, FALSE); motor->SetRatio16(val); } #else motor->currPWMvalue = val; (void)motor->SetRatio16(val); #endif } else { /* have motor stopped */ motor->currPWMvalue = 0xFFFF; (void)motor->SetRatio16(0xFFFF); } } void MOT_OnOff(bool on) { isMotorOn = on; if (!on) { MOT_SetSpeedPercent(MOT_GetMotorHandle(MOT_MOTOR_LEFT), 0); MOT_SetSpeedPercent(MOT_GetMotorHandle(MOT_MOTOR_RIGHT), 0); } } uint16_t MOT_GetVal(MOT_MotorDevice *motor) { return motor->currPWMvalue; } #if MOTOR_HAS_INVERT void MOT_Invert(MOT_MotorDevice *motor, bool inverted) { motor->inverted = inverted; } #endif void MOT_SetSpeedPercent(MOT_MotorDevice *motor, MOT_SpeedPercent percent) { uint32_t val; if (percent>100) { /* make sure we are within 0..100 */ percent = 100; } else if (percent<-100) { percent = -100; } motor->currSpeedPercent = percent; /* store value */ if (percent<0) { MOT_SetDirection(motor, MOT_DIR_BACKWARD); percent = -percent; /* make it positive */ } else { MOT_SetDirection(motor, MOT_DIR_FORWARD); } val = ((100-percent)*0xffff)/100; /* H-Bridge is low active! */ MOT_SetVal(motor, (uint16_t)val); } void MOT_UpdatePercent(MOT_MotorDevice *motor, MOT_Direction dir) { motor->currSpeedPercent = ((0xffff-motor->currPWMvalue)*100)/0xffff; if (dir==MOT_DIR_BACKWARD) { motor->currSpeedPercent = -motor->currSpeedPercent; } } MOT_SpeedPercent MOT_GetSpeedPercent(MOT_MotorDevice *motor) { return motor->currSpeedPercent; } void MOT_ChangeSpeedPercent(MOT_MotorDevice *motor, MOT_SpeedPercent relPercent) { relPercent += motor->currSpeedPercent; /* make absolute number */ if (relPercent>100) { /* check for overflow */ relPercent = 100; } else if (relPercent<-100) { /* and underflow */ relPercent = -100; } MOT_SetSpeedPercent(motor, relPercent); /* set speed. This will care about the direction too */ } void MOT_SetDirection(MOT_MotorDevice *motor, MOT_Direction dir) { if (dir==MOT_DIR_FORWARD ) { #if MOTOR_HAS_INVERT motor->DirPutVal(motor->inverted?0:1); #else motor->DirPutVal(1); #endif if (motor->currSpeedPercent<0) { motor->currSpeedPercent = -motor->currSpeedPercent; } } else if (dir==MOT_DIR_BACKWARD) { #if MOTOR_HAS_INVERT motor->DirPutVal(motor->inverted?1:0); #else motor->DirPutVal(0); #endif if (motor->currSpeedPercent>0) { motor->currSpeedPercent = -motor->currSpeedPercent; } } } MOT_Direction MOT_GetDirection(MOT_MotorDevice *motor) { if (motor->currSpeedPercent<0) { return MOT_DIR_BACKWARD; } else { return MOT_DIR_FORWARD; } } #if PL_CONFIG_USE_SHELL static void MOT_PrintHelp(const McuShell_StdIOType *io) { McuShell_SendHelpStr((unsigned char*)"motor", (unsigned char*)"Group of motor commands\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows motor help or status\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" on|off", (unsigned char*)"Enables or disables motor\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" (L|R) forward|backward", (unsigned char*)"Change motor direction\r\n", io->stdOut); McuShell_SendHelpStr((unsigned char*)" (L|R) duty ", (unsigned char*)"Change motor PWM (-100..+100)%\r\n", io->stdOut); #if PL_HAS_MOTOR_BRAKE McuShell_SendHelpStr((unsigned char*)" (L|R) brake (on|off)", (unsigned char*)"Enable/disable brake for motor\r\n", io->stdOut); #endif } static void MOT_PrintStatus(const McuShell_StdIOType *io) { unsigned char buf[32]; McuShell_SendStatusStr((unsigned char*)"Motor", (unsigned char*)"Motor status\r\n", io->stdOut); #ifdef MOTOR_HAS_INVERT McuShell_SendStatusStr((unsigned char*)" inverted L", MOT_GetMotorHandle(MOT_MOTOR_LEFT)->inverted?(unsigned char*)"yes\r\n":(unsigned char*)"no\r\n", io->stdOut); McuShell_SendStatusStr((unsigned char*)" inverted R", MOT_GetMotorHandle(MOT_MOTOR_RIGHT)->inverted?(unsigned char*)"yes\r\n":(unsigned char*)"no\r\n", io->stdOut); #endif McuShell_SendStatusStr((unsigned char*)" on/off", isMotorOn?(unsigned char*)"on\r\n":(unsigned char*)"off\r\n", io->stdOut); McuShell_SendStatusStr((unsigned char*)" motor L", (unsigned char*)"", io->stdOut); buf[0] = '\0'; McuUtility_Num16sToStrFormatted(buf, sizeof(buf), (int16_t)motorL.currSpeedPercent, ' ', 4); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"% 0x"); McuUtility_strcatNum16Hex(buf, sizeof(buf), MOT_GetVal(&motorL)); McuUtility_strcat(buf, sizeof(buf),(unsigned char*)(MOT_GetDirection(&motorL)==MOT_DIR_FORWARD?", fw":", bw")); #if PL_HAS_MOTOR_BRAKE McuUtility_strcat(buf, sizeof(buf),(unsigned char*)(MOT_GetBrake(&motorL)?", brake on":", brake off")); #endif McuShell_SendStr(buf, io->stdOut); McuShell_SendStr((unsigned char*)"\r\n", io->stdOut); McuShell_SendStatusStr((unsigned char*)" motor R", (unsigned char*)"", io->stdOut); buf[0] = '\0'; McuUtility_Num16sToStrFormatted(buf, sizeof(buf), (int16_t)motorR.currSpeedPercent, ' ', 4); McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"% 0x"); McuUtility_strcatNum16Hex(buf, sizeof(buf), MOT_GetVal(&motorR)); McuUtility_strcat(buf, sizeof(buf),(unsigned char*)(MOT_GetDirection(&motorR)==MOT_DIR_FORWARD?", fw":", bw")); #if PL_HAS_MOTOR_BRAKE McuUtility_strcat(buf, sizeof(buf),(unsigned char*)(MOT_GetBrake(&motorR)?", brake on":", brake off")); #endif McuShell_SendStr(buf, io->stdOut); McuShell_SendStr((unsigned char*)"\r\n", io->stdOut); #if PL_HAS_MOTOR_CURRENT_SENSE WriteCurrent(&motorA, io); McuShell_SendStr((unsigned char*)"\r\n", io->stdOut); WriteCurrent(&motorB, io); McuShell_SendStr((unsigned char*)"\r\n", io->stdOut); #endif } uint8_t MOT_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) { uint8_t res = ERR_OK; int32_t val; const unsigned char *p; if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"motor help")==0) { MOT_PrintHelp(io); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"motor status")==0) { MOT_PrintStatus(io); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"motor L forward")==0) { MOT_SetDirection(&motorL, MOT_DIR_FORWARD); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"motor R forward")==0) { MOT_SetDirection(&motorR, MOT_DIR_FORWARD); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"motor L backward")==0) { MOT_SetDirection(&motorL, MOT_DIR_BACKWARD); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"motor R backward")==0) { MOT_SetDirection(&motorR, MOT_DIR_BACKWARD); *handled = TRUE; } else if (McuUtility_strncmp((char*)cmd, (char*)"motor L duty ", sizeof("motor L duty ")-1)==0) { if (!isMotorOn) { McuShell_SendStr((unsigned char*)"Motor is OFF, cannot set duty.\r\n", io->stdErr); res = ERR_FAILED; } else { p = cmd+sizeof("motor L duty"); if (McuUtility_xatoi(&p, &val)==ERR_OK && val >=-100 && val<=100) { MOT_SetSpeedPercent(&motorL, (MOT_SpeedPercent)val); *handled = TRUE; } else { McuShell_SendStr((unsigned char*)"Wrong argument, must be in the range -100..100\r\n", io->stdErr); res = ERR_FAILED; } } } else if (McuUtility_strncmp((char*)cmd, (char*)"motor R duty ", sizeof("motor R duty ")-1)==0) { if (!isMotorOn) { McuShell_SendStr((unsigned char*)"Motor is OFF, cannot set duty.\r\n", io->stdErr); res = ERR_FAILED; } else { p = cmd+sizeof("motor R duty"); if (McuUtility_xatoi(&p, &val)==ERR_OK && val >=-100 && val<=100) { MOT_SetSpeedPercent(&motorR, (MOT_SpeedPercent)val); *handled = TRUE; } else { McuShell_SendStr((unsigned char*)"Wrong argument, must be in the range -100..100\r\n", io->stdErr); res = ERR_FAILED; } } } else if (McuUtility_strncmp((char*)cmd, (char*)"motor on", sizeof("motor on")-1)==0) { isMotorOn = TRUE; *handled = TRUE; } else if (McuUtility_strncmp((char*)cmd, (char*)"motor off", sizeof("motor off")-1)==0) { MOT_SetSpeedPercent(&motorL, 0); MOT_SetSpeedPercent(&motorR, 0); isMotorOn = FALSE; *handled = TRUE; #if PL_HAS_MOTOR_BRAKE } else if (McuUtility_strcmp((char*)cmd, (char*)"motor L brake on")==0) { MOT_SetBrake(&motorL, TRUE); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"motor L brake off")==0) { MOT_SetBrake(&motorL, FALSE); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"motor R brake on")==0) { MOT_SetBrake(&motorR, TRUE); *handled = TRUE; } else if (McuUtility_strcmp((char*)cmd, (char*)"motor R brake off")==0) { MOT_SetBrake(&motorR, FALSE); *handled = TRUE; #endif } return res; } #endif /* PL_CONFIG_USE_SHELL */ void MOT_Deinit(void) { MOT_DirL = McuGPIO_DeinitGPIO(MOT_DirL); MOT_DirR = McuGPIO_DeinitGPIO(MOT_DirR); } static void MOT_DirPinInit(void) { McuGPIO_Config_t gpioConfig; /* DIRL: PTC9 * DIRR: PTC8 */ McuGPIO_GetDefaultConfig(&gpioConfig); gpioConfig.hw.gpio = GPIOC; gpioConfig.hw.port = PORTC; gpioConfig.hw.pin = 9; gpioConfig.isInput = false; gpioConfig.isHighOnInit = true; MOT_DirL = McuGPIO_InitGPIO(&gpioConfig); gpioConfig.hw.pin = 8; MOT_DirR = McuGPIO_InitGPIO(&gpioConfig); } void MOT_Init(void) { MOT_DirPinInit(); #if PL_HAS_MOTOR_BRAKE motorL.BrakePutVal = BrakeLPutVal; motorR.BrakePutVal = BrakeRPutVal; #endif #if MOTOR_HAS_INVERT motorL.inverted = FALSE; motorR.inverted = FALSE; #endif motorL.DirPutVal = DirLPutVal; motorR.DirPutVal = DirRPutVal; motorL.SetRatio16 = PWMLSetRatio16; motorR.SetRatio16 = PWMRSetRatio16; MOT_SetSpeedPercent(&motorL, 0); MOT_SetSpeedPercent(&motorR, 0); TMR_MotorPWMStart(); } #endif /* PL_CONFIG_USE_MOTORS */