/* * Copyright (c) 2019, Erich Styger * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include "McuULN2003config.h" #include "McuULN2003.h" #include "McuGPIO.h" #include "McuWait.h" #if McuLib_CONFIG_CPU_IS_KINETIS #include "fsl_port.h" #endif #if MCUULN2003_CONFIG_USE_FREERTOS_HEAP #include "McuRTOS.h" #endif #if McuULN2003_CONFIG_USE_ACCELERATION static const uint8_t default_accel_delays[] = {2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 8, 8, 10, 10, 10}; static const McuULN2003_Accel_t McuULN2003_DefaultAccelTable = { /* list of delay values: the larger the value, the more delays between the step sequences. */ .nofEntries = sizeof(default_accel_delays), .delays = default_accel_delays, }; #endif typedef struct McuULN2003_Motor_t { int32_t pos; /* actual stepper motor position counter */ McuULN2003_StepMode stepMode; /* full or half stepping mode */ bool inverted; /* if motor direction is inverted */ uint8_t tablePos; /* current pos in the stepper logic table */ uint32_t id; /* optional ID */ bool noGPIO; /* if no GPIO handles shall be allocated */ #if McuULN2003_CONFIG_USE_ACCELERATION struct { const McuULN2003_Accel_t *table; uint8_t accelIdx; /* current index into acceleration delay table, starting from the end of the table to index zero */ uint8_t subAccelCnt; /* sub-position acceleration counter, used to count down the delay */ } accel; #endif void (*stepCallback)(McuULN2003_Handle_t motor, const bool w[McuULN2003_NOF_MOTOR_GPIO_PINS]); McuGPIO_Handle_t pin[McuULN2003_NOF_MOTOR_GPIO_PINS]; /* the 4 winding of the motor */ } McuULN2003_Motor_t; #define McuULN2003_DELAY_HALF_STEP_MODE() McuWait_WaitOSms(2) #define McuULN2003_DELAY_FULL_STEP_MODE() McuWait_WaitOSms(4) typedef bool McuULN2003_PinStatus[McuULN2003_NOF_MOTOR_GPIO_PINS]; static const bool disableTable[McuULN2003_NOF_MOTOR_GPIO_PINS] = {false, false, false, false}; /* half stepping mode with 8 steps * For the wire colors and stepper table, see https://leap.tardate.com/kinetics/steppermotors/28byj48/ */ const bool McuULN2003_stepTableHalfStepsFw[McuULN2003_NOF_STEPS_HALF_STEP_MODE][McuULN2003_NOF_MOTOR_GPIO_PINS] = { /* blue pink yellow orange */ {true, true, false, false}, {false, true, false, false}, {false, true, true, false}, {false, false, true, false}, {false, false, true, true }, {false, false, false, true }, {true, false, false, true }, {true, false, false, false}, }; const bool McuULN2003_stepTableHalfStepsBw[McuULN2003_NOF_STEPS_HALF_STEP_MODE][McuULN2003_NOF_MOTOR_GPIO_PINS] = { /* blue pink yellow orange */ {true, false, false, false}, {true, false, false, true }, {false, false, false, true }, {false, false, true, true }, {false, false, true, false}, {false, true, true, false}, {false, true, false, false}, {true, true, false, false}, }; /* full stepping mode with 4 steps */ const bool McuULN2003_stepTableFullStepsFw[McuULN2003_NOF_STEPS_FULL_STEP_MODE][McuULN2003_NOF_MOTOR_GPIO_PINS] = { /* blue pink yellow orange */ /* https://leap.tardate.com/kinetics/steppermotors/28byj48/ */ {true, true, false, false}, {false, true, true, false}, {false, false, true, true}, {true, false, false, true }, }; const bool McuULN2003_stepTableFullStepsBw[McuULN2003_NOF_STEPS_FULL_STEP_MODE][McuULN2003_NOF_MOTOR_GPIO_PINS] = { /* blue pink yellow orange */ {true, false, false, true }, {false, false, true, true }, {false, true, true, false}, {true, true, false, false}, }; /* default configuration, used for initializing the config */ static const McuULN2003_Config_t defaultConfig = { .stepMode = McuULN2003_STEP_MODE_HALF, .inverted = false, .id = 0, .stepCallback = NULL, .noGPIO = false, .hw[0] = { #if McuLib_CONFIG_NXP_SDK_USED && !McuLib_CONFIG_IS_KINETIS_KE .gpio = NULL, #elif McuLib_CONFIG_CPU_IS_STM32 .gpio = NULL, #endif #if McuLib_CONFIG_CPU_IS_KINETIS .port = NULL, #elif McuLib_CONFIG_CPU_IS_LPC .port = 0, #endif .pin = 0, }, .hw[1] = { #if McuLib_CONFIG_NXP_SDK_USED && !McuLib_CONFIG_IS_KINETIS_KE .gpio = NULL, #elif McuLib_CONFIG_CPU_IS_STM32 .gpio = NULL, #endif #if McuLib_CONFIG_CPU_IS_KINETIS .port = NULL, #elif McuLib_CONFIG_CPU_IS_LPC .port = 0, #endif .pin = 0, }, .hw[2] = { #if McuLib_CONFIG_NXP_SDK_USED && !McuLib_CONFIG_IS_KINETIS_KE .gpio = NULL, #elif McuLib_CONFIG_CPU_IS_STM32 .gpio = NULL, #endif #if McuLib_CONFIG_CPU_IS_KINETIS .port = NULL, #elif McuLib_CONFIG_CPU_IS_LPC .port = 0, #endif .pin = 0, }, .hw[3] = { #if McuLib_CONFIG_NXP_SDK_USED && !McuLib_CONFIG_IS_KINETIS_KE .gpio = NULL, #elif McuLib_CONFIG_CPU_IS_STM32 .gpio = NULL, #endif #if McuLib_CONFIG_CPU_IS_KINETIS .port = NULL, #elif McuLib_CONFIG_CPU_IS_LPC .port = 0, #endif .pin = 0, } }; void McuULN2003_GetDefaultConfig(McuULN2003_Config_t *config) { assert(config!=NULL); memcpy(config, &defaultConfig, sizeof(*config)); } uint32_t McuULN2003_GetID(McuULN2003_Handle_t motor) { return ((McuULN2003_Motor_t *)motor)->id; } #if McuULN2003_CONFIG_USE_ACCELERATION void McuULN2003_SetAccelerationTable(McuULN2003_Handle_t motor, const McuULN2003_Accel_t *table) { McuULN2003_Motor_t *handle; handle = (McuULN2003_Motor_t*)motor; handle->accel.table = table; handle->accel.accelIdx = 0; handle->accel.subAccelCnt = 0; } #endif static void SetStep(McuULN2003_Handle_t motor, const bool w[McuULN2003_NOF_MOTOR_GPIO_PINS]) { for(int i=0; ipin[i], w[i]); /* change GPIO pins */ } } McuULN2003_Handle_t McuULN2003_InitMotor(McuULN2003_Config_t *config) { McuGPIO_Config_t gpio_config; /* config for the SDK */ McuULN2003_Motor_t *handle; assert(config!=NULL); #if MCUULN2003_CONFIG_USE_FREERTOS_HEAP handle = (McuULN2003_Motor_t*)pvPortMalloc(sizeof(McuULN2003_Motor_t)); /* get a new device descriptor */ #else handle = (McuULN2003_Motor_t*)malloc(sizeof(McuULN2003_Motor_t)); /* get a new device descriptor */ #endif assert(handle!=NULL); if (handle!=NULL) { /* if malloc failed, will return NULL pointer */ memset(handle, 0, sizeof(McuULN2003_Motor_t)); /* init all fields */ #if McuULN2003_CONFIG_USE_ACCELERATION McuULN2003_SetAccelerationTable((McuULN2003_Handle_t*)handle, &McuULN2003_DefaultAccelTable); #endif handle->id = config->id; handle->pos = 0; handle->stepMode = config->stepMode; handle->inverted = config->inverted; handle->tablePos = 0; handle->noGPIO = config->noGPIO; handle->stepCallback = config->stepCallback; if (handle->stepCallback==NULL) { handle->stepCallback = SetStep; /* assign default GPIO callback */ } if (!config->noGPIO) { McuGPIO_GetDefaultConfig(&gpio_config); for(int i=0; ihw[i], sizeof(gpio_config.hw)); /* copy hardware info */ gpio_config.isHighOnInit = false; handle->pin[i] = McuGPIO_InitGPIO(&gpio_config); /* create gpio handle */ } } } return handle; } McuULN2003_Handle_t McuULN2003_DeinitMotor(McuULN2003_Handle_t motor) { McuULN2003_Motor_t *m = (McuULN2003_Motor_t*)motor; assert(m!=NULL); for(int i=0; ipin[i]); } #if MCUULN2003_CONFIG_USE_FREERTOS_HEAP vPortFree(m); #else free(m); #endif return NULL; } void McuULN2003_SetStepMode(McuULN2003_Handle_t motor, McuULN2003_StepMode mode) { ((McuULN2003_Motor_t*)motor)->stepMode = mode; } McuULN2003_StepMode McuULN2003_GetStepMode(McuULN2003_Handle_t motor) { return ((McuULN2003_Motor_t*)motor)->stepMode; } void McuULN2003_PowerOff(McuULN2003_Handle_t motor) { McuULN2003_Motor_t *m = (McuULN2003_Motor_t*)motor; m->stepCallback(motor, disableTable); m->tablePos = 0; } static void McuULN2003_TableMakeStep(McuULN2003_Handle_t motor, bool forward) { McuULN2003_Motor_t *m = (McuULN2003_Motor_t*)motor; const McuULN2003_PinStatus *table; size_t maxTableIndex; if (m->stepMode==McuULN2003_STEP_MODE_HALF) { if ((forward && !m->inverted) || (!forward && m->inverted)) { table = McuULN2003_stepTableHalfStepsFw; } else { table = McuULN2003_stepTableHalfStepsBw; } maxTableIndex = McuULN2003_NOF_STEPS_HALF_STEP_MODE; } else { /* McuULN2003_STEP_MODE_FULL */ if ((forward && !m->inverted) || (!forward && m->inverted)) { table = McuULN2003_stepTableFullStepsFw; } else { table = McuULN2003_stepTableFullStepsBw; } maxTableIndex = McuULN2003_NOF_STEPS_FULL_STEP_MODE; } m->stepCallback(m, table[m->tablePos]); m->tablePos++; if (m->tablePos>=maxTableIndex) { /* full sequence reached */ m->tablePos = 0; if (forward) { m->pos++; } else { m->pos--; } } } bool McuULN2003_StepCallback(McuULN2003_Handle_t motor, bool forward) { McuULN2003_Motor_t *m = (McuULN2003_Motor_t*)motor; #if McuULN2003_CONFIG_USE_ACCELERATION if (m->accel.accelIdx>0) { /* only do it if not during acceleration */ if (m->accel.subAccelCnt>0) { /* sub-delay still going on? */ m->accel.subAccelCnt--; /* delay */ return false; /* not reached end of delay sequence */ } else { /* subAccelCnt reached zero: get to next table index value */ m->accel.accelIdx--; m->accel.subAccelCnt = m->accel.table->delays[m->accel.accelIdx]; } } #endif if (forward) { McuULN2003_TableMakeStep(motor, true); /* forward */ } else { McuULN2003_TableMakeStep(motor, false); /* backward */ } return m->tablePos==0; /* reached position */ } #if McuULN2003_CONFIG_USE_ACCELERATION void McuULN2003_AccelerationStart(McuULN2003_Handle_t motor) { McuULN2003_Motor_t *m = (McuULN2003_Motor_t*)motor; m->accel.accelIdx = m->accel.table->nofEntries-1; m->accel.subAccelCnt = m->accel.table->delays[m->accel.table->nofEntries-1]; } #endif #if McuULN2003_CONFIG_USE_ACCELERATION void McuULN2003_AccelerationEnd(McuULN2003_Handle_t motor) { McuULN2003_Motor_t *m = (McuULN2003_Motor_t*)motor; m->accel.accelIdx = 0; m->accel.subAccelCnt = 0; } #endif bool McuULN2003_MoveCallback(McuULN2003_Handle_t motor, int32_t targetPos) { McuULN2003_Motor_t *m = (McuULN2003_Motor_t*)motor; if (m->pos < targetPos) { McuULN2003_TableMakeStep(motor, true); /* forward */ return false; } else if (m->pos > targetPos) { McuULN2003_TableMakeStep(motor, false); /* backward */ return false; } McuULN2003_PowerOff(motor); return true; /* reached position */ } void McuULN2003_IncStep(McuULN2003_Handle_t motor) { McuULN2003_Motor_t *m = (McuULN2003_Motor_t*)motor; int32_t pos = m->pos+1; do { McuULN2003_TableMakeStep(motor, true); if (m->stepMode==McuULN2003_STEP_MODE_HALF) { McuULN2003_DELAY_HALF_STEP_MODE(); } else { /* McuULN2003_STEP_MODE_FULL */ McuULN2003_DELAY_FULL_STEP_MODE(); } } while(m->pos!=pos); } void McuULN2003_DecStep(McuULN2003_Handle_t motor) { McuULN2003_Motor_t *m = (McuULN2003_Motor_t*)motor; int32_t pos = m->pos-1; do { McuULN2003_TableMakeStep(motor, false); if (m->stepMode==McuULN2003_STEP_MODE_HALF) { McuULN2003_DELAY_HALF_STEP_MODE(); } else { /* McuULN2003_STEP_MODE_FULL */ McuULN2003_DELAY_FULL_STEP_MODE(); } } while(m->pos!=pos); } void McuULN2003_SetPos(McuULN2003_Handle_t motor, int32_t pos) { McuULN2003_Motor_t *m = (McuULN2003_Motor_t*)motor; m->pos = pos; } int32_t McuULN2003_GetPos(McuULN2003_Handle_t motor) { return ((McuULN2003_Motor_t*)motor)->pos; } void McuULN2003_Step(McuULN2003_Handle_t motor, int32_t steps) { if (steps>0) { while(steps>0) { McuULN2003_IncStep(motor); steps--; } } else { while(steps<0) { McuULN2003_DecStep(motor); steps++; } } } void McuULN2003_Deinit(void) { /* nothing needed */ } void McuULN2003_Init(void) { /* nothing needed */ }