diff --git a/firmware/cap1296.cpp b/firmware/cap1296.cpp index 729e12e..7b4a4ac 100644 --- a/firmware/cap1296.cpp +++ b/firmware/cap1296.cpp @@ -1,43 +1,53 @@ #include "cap1296.h" +#define CAP1296_ADDR 0x28 + +#define REG_SENSITIVITY 0x1F // Sensitivity Control +#define REG_INPUTEN 0x21 // Sensor Input Enable +#define REG_CAL 0x26 // Calibration Activate and Status +#define REG_INTEN 0x27 // Interrupt Enable +#define REG_REPEAERATEEN 0x28 // Repeat Rate Enable +#define REG_CONF2 0x44 // Configuration 2 +#define REG_MAINCONTROL 0x00 // Main Control +#define REG_INPUTSTATUS 0x03 // Sensor Input Status + void cap1296_init() { - // Sensitivity Control Register // Set sensitivity multiplier to x4 and data scaling factor to x256 - i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){0x1F, 0x5F}, 2, false); - // Sensor Input Enable Register - // Do monitor touch inputs CS1, CS2, CS3, CS4 and don't monitor touch inputs CS5, CS6 in the active state - i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){0x21, 0x0F}, 2, false); - // Calibration Activate and Status Register + i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){REG_SENSITIVITY, 0x5F}, 2, false); + + // Monitor touch inputs CS1, CS2, CS3, CS4 and don't monitor touch inputs CS5, CS6 in the active state + i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){REG_INPUTEN, 0x0F}, 2, false); + // Force recalibration of the touch inputs CS1, CS2, CS3, CS4 - i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){0x26, 0x0F}, 2, false); - // Interrupt Enable Register + i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){REG_CAL, 0x0F}, 2, false); + // Enable interrupts for touch inputs CS1, CS2, CS3, CS4 and disable interrupts for touch inputs CS5, CS6 - i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){0x27, 0x0F}, 2, false); - // Repeat Rate Enable Register + i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){REG_INTEN, 0x0F}, 2, false); + // Disable repeat interrupts during button holds for all touch inputs - i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){0x28, 0x00}, 2, false); - // Configuration 2 Register + i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){REG_REPEAERATEEN, 0x00}, 2, false); + // Set interrupts to trigger when a button is released and recalibrate a touch input if its base count is out of limit - i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){0x44, 0x40}, 2, false); + i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[2]){REG_CONF2, 0x40}, 2, false); } void cap1296_clear_int_bit_in_main_control_register() { uint8_t main_control_register_value; - i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[1]){0}, 1, true); + i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[1]){REG_MAINCONTROL}, 1, true); i2c_read_blocking(I2C_PORT, CAP1296_ADDR, &main_control_register_value, 1, false); // Main Control Register // Clear the int bit while maintaining previous values of other bits - const uint8_t main_control_register[2] = {0x00, (uint8_t)(main_control_register_value & 0b11111110)}; + const uint8_t main_control_register[2] = {REG_MAINCONTROL, (uint8_t)(main_control_register_value & 0b11111110)}; i2c_write_blocking(I2C_PORT, CAP1296_ADDR, main_control_register, 2, false); } uint8_t cap1296_read_sensor_input_status_register() { uint8_t touch_status_buffer; - i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[1]){0x03}, 1, true); + i2c_write_blocking(I2C_PORT, CAP1296_ADDR, (const uint8_t[1]){REG_INPUTSTATUS}, 1, true); i2c_read_blocking(I2C_PORT, CAP1296_ADDR, &touch_status_buffer, 1, false); return touch_status_buffer; } diff --git a/firmware/cap1296.h b/firmware/cap1296.h index 5e9c6dd..c05497d 100644 --- a/firmware/cap1296.h +++ b/firmware/cap1296.h @@ -3,10 +3,6 @@ #include "hardware/i2c.h" #include "pindefs.h" -// CAP1296 datasheet: -// https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/00001569B.pdf - -#define CAP1296_ADDR 0x28 #define BUTTON_RELEASE 0 #define CW_BUTTON_PRESS (1 << 0) #define ENABLE_BUTTON_PRESS (1 << 1) @@ -15,4 +11,4 @@ void cap1296_init(); void cap1296_clear_int_bit_in_main_control_register(); -uint8_t cap1296_read_sensor_input_status_register(); \ No newline at end of file +uint8_t cap1296_read_sensor_input_status_register(); \ No newline at end of file diff --git a/firmware/is32fl3193.cpp b/firmware/is32fl3193.cpp index ba61790..5fb9099 100644 --- a/firmware/is32fl3193.cpp +++ b/firmware/is32fl3193.cpp @@ -3,18 +3,28 @@ #include "hardware/i2c.h" #include "is32fl3193.h" +#define IS31_ADDR 0x68 + +#define REG_SHDN 0x00 +#define REG_LEDMODE 0x01 // NB: Switched on datasheet +#define REG_BREATHCTRL 0x02 // NB: Switched on datasheet +#define REG_CURRSET 0x03 +#define REG_PWM0 0x04 +#define REG_PWM1 0x05 +#define REG_PWM2 0x06 +#define REG_DATAUPDATE 0x07 + void rgb_set_rgb(uint8_t r, uint8_t g, uint8_t b) { - uint8_t rgb[4] = {0x04, r, g, b}; + uint8_t rgb[4] = {REG_PWM0, r, g, b}; i2c_write_blocking(I2C_PORT, IS31_ADDR, rgb, 4, false); - i2c_write_blocking(I2C_PORT, IS31_ADDR, (const uint8_t[]){0x07, 0x00}, 2, false); // pwm data + i2c_write_blocking(I2C_PORT, IS31_ADDR, (const uint8_t[]){REG_DATAUPDATE, 0x00}, 2, false); // pwm data } void rgb_set_auto(bool enable, bool led) { - const uint8_t current_driver_settings[2] = {0x00, 0x20}; // {disable current driver, enable current driver} - const uint8_t current_driver_address_value[2] = {0x00, current_driver_settings[led]}; // {enable current register address, enable current register value} - i2c_write_blocking(I2C_PORT, IS31_ADDR, current_driver_address_value, 2, false); + const uint8_t cmd[2] = {REG_SHDN, led ? (uint8_t)0x20 : (uint8_t)0x00}; + i2c_write_blocking(I2C_PORT, IS31_ADDR, cmd, 2, false); const uint8_t rgb_colors[3][3] = {{255, 0, 0}, {1, 20, 7}, {0, 0, 255}}; // r, g, b rgb_set_rgb(rgb_colors[enable][0], rgb_colors[enable][1], rgb_colors[enable][2]); @@ -22,14 +32,13 @@ void rgb_set_auto(bool enable, bool led) void rgb_set_breathing(bool breathing) { - const uint8_t breathing_settings[2] = {0x00, 0x20}; // {no breathing, yes breathing} - const uint8_t breathing_address_value[2] = {0x02, breathing_settings[breathing]}; // {breathing register address, breathing register value} - i2c_write_blocking(I2C_PORT, IS31_ADDR, breathing_address_value, 2, false); + const uint8_t cmd[2] = {REG_BREATHCTRL, breathing ? (uint8_t)0x20 : (uint8_t)0x00}; + i2c_write_blocking(I2C_PORT, IS31_ADDR, cmd, 2, false); } void rgb_init() { gpio_put(IS31_POW_EN, 1); - i2c_write_blocking(I2C_PORT, IS31_ADDR, (const uint8_t[2]){0x03, 0x08}, 2, false); // Set max current to 5 mA + i2c_write_blocking(I2C_PORT, IS31_ADDR, (const uint8_t[2]){REG_CURRSET, 0x08}, 2, false); // Set max current to 5 mA } diff --git a/firmware/is32fl3193.h b/firmware/is32fl3193.h index 4b759b0..a6e75ca 100644 --- a/firmware/is32fl3193.h +++ b/firmware/is32fl3193.h @@ -1,7 +1,5 @@ #pragma once -#define IS31_ADDR 0x68 - void rgb_init(); -void rgb_set_breathing(bool breathing); +void rgb_set_breathing(bool breathing); void rgb_set_auto(bool enable, bool led); diff --git a/firmware/main.cpp b/firmware/main.cpp index f8a35b0..17e2503 100644 --- a/firmware/main.cpp +++ b/firmware/main.cpp @@ -34,13 +34,14 @@ #define MAX_GEAR_RATIO 100 #define MAX_SERIAL_BUFFER_LENGTH 1024u +#define BUTTON_HOLD_TURNS 200 // ISR flag for capacitative touch detected volatile bool alert_flag = false; static void io_alert_irq_callback(unsigned int gpio, long unsigned int events) { alert_flag = true; -}; +} // NB: The gear ratio is configurable without recompilation using // `picotool config -s gear_ratio 3.14` @@ -51,6 +52,7 @@ bi_decl(bi_ptr_string(0, 0, gear_ratio, "2.0", 32)); struct context_t { bool enable = false; bool led = true; + bool power_good = false; uint8_t last_sensor_input_status = BUTTON_RELEASE; }; @@ -70,68 +72,82 @@ struct rotor_cmd_t { // Errors typedef enum { ERROR_SUCCESS = 0, - ERROR_INVALID_TURN_REQ = -1, - ERROR_COMMAND_BUFFER_OVERFLOW = -2, - ERROR_COMMAND_INCOMPLETE = -3, - ERROR_MALFORMED_JSON = -4, - ERROR_NAN_OR_INF_TURN_COMMAND = -5, - ERROR_REMOTE_INTERFACE_LOCKED = -6 + ERROR_INVALID_TURN_REQ = 1, + ERROR_COMMAND_BUFFER_OVERFLOW = 2, + ERROR_COMMAND_MALFORMED = 4, + ERROR_NAN_TURN_COMMAND = 8, + ERROR_REMOTE_INTERFACE_LOCKED = 16, + ERROR_POWER_BAD = 32 } commutator_error_t; -static const char *error_str(int error) -{ - switch (error) - { - case ERROR_SUCCESS: - return "Success"; - case ERROR_INVALID_TURN_REQ: - return "Cannot enqueue turn command when commutator is disabled."; - case ERROR_COMMAND_BUFFER_OVERFLOW: - return "Command exceeded " xstr(MAX_SERIAL_BUFFER_LENGTH) " byte maximum."; - case ERROR_COMMAND_INCOMPLETE: - return "A complete command could not be constructed from the input stream."; - case ERROR_MALFORMED_JSON: - return "Malformed JSON received."; - case ERROR_NAN_OR_INF_TURN_COMMAND: - return "Turn command with value NaN or Inf received."; - case ERROR_REMOTE_INTERFACE_LOCKED: - return "A remote command was received when remote interface was locked."; - default: - return "Invalid error code."; - } +static void print_report(context_t *ctx, int error){ + JsonDocument doc; + doc["gear_ratio"] = gear_ratio_f; + doc["board_rev"] = BOARD_REV; + doc["firmware"] = FIRMWARE_VER; + doc["enable"] = ctx->enable; + doc["led"] = ctx->led; + doc["charge_current"] = ltc4425_charge_current(); + doc["power_good"] = ltc4425_power_good(); + doc["error"] = error; + serializeJson(doc, std::cout); + std::cout << std::endl; } -static int queue_add_button_turn_cmd_blocking(const context_t *ctx, double turns) +static inline bool remote_motor_control_available(const context_t *ctx) { - if (ctx->enable) - { - rotor_cmd_t rotor_cmd = {.tag = rotor_cmd_tag::BUTTON_TURN, .value = {.turns = turns}}; - queue_add_blocking(&rotor_cmd_queue, &rotor_cmd); - return ERROR_SUCCESS; - } - - return ERROR_INVALID_TURN_REQ; + return ctx->last_sensor_input_status == BUTTON_RELEASE || + ctx->last_sensor_input_status == LED_BUTTON_PRESS; } -static int queue_add_turn_cmd_blocking(const context_t *ctx, double turns) +static int queue_add_cmd_blocking(const context_t *ctx, const rotor_cmd_t *cmd) { - if (ctx->enable) + int rc = ERROR_SUCCESS; + + switch (cmd->tag) { - rotor_cmd_t rotor_cmd = {.tag = rotor_cmd_tag::TURN, .value = {.turns = turns}}; - queue_add_blocking(&rotor_cmd_queue, &rotor_cmd); - return ERROR_SUCCESS; + case rotor_cmd_tag::ENABLE: + if (!ctx->power_good) + rc |= ERROR_POWER_BAD; + break; + + case rotor_cmd_tag::BUTTON_TURN: + + if (!ctx->power_good) + rc |= ERROR_POWER_BAD; + if (!ctx->enable) + rc |= ERROR_INVALID_TURN_REQ; + if (std::isnan(cmd->value.turns)) + rc |= ERROR_NAN_TURN_COMMAND; + break; + + case rotor_cmd_tag::TURN: + if (!ctx->power_good) + rc |= ERROR_POWER_BAD; + if (std::isnan(cmd->value.turns)) + rc |= ERROR_NAN_TURN_COMMAND; + if (!remote_motor_control_available(ctx)) + rc |= ERROR_REMOTE_INTERFACE_LOCKED; + if (!ctx->enable) + rc |= ERROR_INVALID_TURN_REQ; + break; } - return ERROR_INVALID_TURN_REQ; + if (rc) return rc; + + queue_add_blocking(&rotor_cmd_queue, cmd); + return ERROR_SUCCESS; } static int process_button_touches(context_t *ctx) { int rc = ERROR_SUCCESS; if (alert_flag) { - rotor_cmd_t rotor_cmd; + + alert_flag = false; cap1296_clear_int_bit_in_main_control_register(); uint8_t sensor_input_status = cap1296_read_sensor_input_status_register(); + switch (sensor_input_status) { case LED_BUTTON_PRESS: @@ -139,31 +155,44 @@ static int process_button_touches(context_t *ctx) rgb_set_auto(ctx->enable, ctx->led); break; case ENABLE_BUTTON_PRESS: - ctx->enable = !ctx->enable; - rotor_cmd = {.tag = rotor_cmd_tag::ENABLE, .value = {.enable = ctx->enable}}; - queue_add_blocking(&rotor_cmd_queue, &rotor_cmd); + { + bool temp = !ctx->enable; + rotor_cmd_t rotor_cmd = {.tag = rotor_cmd_tag::ENABLE, .value = {.enable = temp}}; + rc |= queue_add_cmd_blocking(ctx, &rotor_cmd); + if (!rc) ctx->enable = temp; rgb_set_auto(ctx->enable, ctx->led); break; + } case CW_BUTTON_PRESS: - rotor_cmd = {.tag = rotor_cmd_tag::STOP}; - queue_add_blocking(&rotor_cmd_queue, &rotor_cmd); - rc = queue_add_button_turn_cmd_blocking(ctx, -INFINITY); + { + rotor_cmd_t rotor_cmd = {.tag = rotor_cmd_tag::STOP}; + rc |= queue_add_cmd_blocking(ctx, &rotor_cmd); + rotor_cmd = {.tag = rotor_cmd_tag::BUTTON_TURN, .value = {.turns = -BUTTON_HOLD_TURNS}}; + rc |= queue_add_cmd_blocking(ctx, &rotor_cmd); break; + } case CCW_BUTTON_PRESS: - rotor_cmd = {.tag = rotor_cmd_tag::STOP}; - queue_add_blocking(&rotor_cmd_queue, &rotor_cmd); - rc = queue_add_button_turn_cmd_blocking(ctx, INFINITY); + { + rotor_cmd_t rotor_cmd = {.tag = rotor_cmd_tag::STOP}; + rc |= queue_add_cmd_blocking(ctx, &rotor_cmd); + rotor_cmd = {.tag = rotor_cmd_tag::BUTTON_TURN, .value = {.turns = BUTTON_HOLD_TURNS}}; + rc |= queue_add_cmd_blocking(ctx, &rotor_cmd); break; + } case BUTTON_RELEASE: + { if (ctx->last_sensor_input_status & (CW_BUTTON_PRESS | CCW_BUTTON_PRESS)) { - rotor_cmd.tag = rotor_cmd_tag::STOP; - queue_add_blocking(&rotor_cmd_queue, &rotor_cmd); + rotor_cmd_t rotor_cmd = {.tag = rotor_cmd_tag::STOP}; + rc |= queue_add_cmd_blocking(ctx, &rotor_cmd); } break; + } } + ctx->last_sensor_input_status = sensor_input_status; - alert_flag = false; + + #ifdef DEBUG printf("{button: %02hhx, counter: %i}\n", sensor_input_status, button_counter++); #endif @@ -172,129 +201,47 @@ static int process_button_touches(context_t *ctx) return rc; } -static inline bool remote_available(const context_t *ctx) +static int parse_json_command(context_t *ctx, char *buffer) { - return ctx->last_sensor_input_status == BUTTON_RELEASE || - ctx->last_sensor_input_status == LED_BUTTON_PRESS; -} - -static int process_serial_commands(context_t *ctx) -{ - static char serial_buffer[MAX_SERIAL_BUFFER_LENGTH] = {0}; - static uint16_t serial_buffer_index = 0; - static bool accept_serial_commands_previous = false; - static bool accept_serial_commands = false; - - accept_serial_commands = remote_available(ctx); - - if (accept_serial_commands && !accept_serial_commands_previous) { - while (getchar_timeout_us(0) != PICO_ERROR_TIMEOUT); - serial_buffer_index = 0; - memset(serial_buffer, 0, MAX_SERIAL_BUFFER_LENGTH); - } - - accept_serial_commands_previous = accept_serial_commands; - - if (tud_cdc_available()) { - if (serial_buffer_index >= MAX_SERIAL_BUFFER_LENGTH-1) { - serial_buffer_index = 0; - memset(serial_buffer, 0, MAX_SERIAL_BUFFER_LENGTH); - return ERROR_COMMAND_BUFFER_OVERFLOW; - } - char c = getchar_timeout_us(0); - if (!(serial_buffer_index == 0 && c != '{')) { - // ignore anything preceding a '{' - serial_buffer[serial_buffer_index++] = c; - } - if (serial_buffer_index > 0 && c == '{') { - // since we don't have any nesting jsons, reset the buffer when a - // new '{' arrives. This prevents a singular '{' from bricking - // further jsons from being parsed. - memset(serial_buffer, 0, serial_buffer_index+1); - serial_buffer_index = 0; - serial_buffer[serial_buffer_index++] = c; - } - } - JsonDocument receive; - auto error = deserializeJson(receive, serial_buffer, MAX_SERIAL_BUFFER_LENGTH); - + auto error = deserializeJson(receive, buffer); if (error.code() != DeserializationError::Ok) { - return ERROR_MALFORMED_JSON; + return ERROR_COMMAND_MALFORMED; } - memset(serial_buffer, 0, serial_buffer_index+1); - serial_buffer_index = 0; - - rotor_cmd_t rotor_cmd; int rc = ERROR_SUCCESS; // Enable command if (receive["enable"].is()) { - if (!accept_serial_commands) - { - rc = ERROR_REMOTE_INTERFACE_LOCKED; - } - else - { - ctx->enable = receive["enable"]; - rotor_cmd = {.tag = rotor_cmd_tag::ENABLE, .value = {.enable = ctx->enable}}; - queue_add_blocking(&rotor_cmd_queue, &rotor_cmd); - rgb_set_auto(ctx->enable, ctx->led); - } + bool temp = receive["enable"]; + rotor_cmd_t rotor_cmd = {.tag = rotor_cmd_tag::ENABLE, .value = {.enable = temp}}; + rc |= queue_add_cmd_blocking(ctx, &rotor_cmd); + if (!rc) ctx->enable = temp; + rgb_set_auto(ctx->enable, ctx->led); } // LED command if (receive["led"].is()) { - if (!accept_serial_commands) - { - rc = ERROR_REMOTE_INTERFACE_LOCKED; - } - else - { - ctx->led = receive["led"]; - rgb_set_auto(ctx->enable, ctx->led); - } + ctx->led = receive["led"]; + rgb_set_auto(ctx->enable, ctx->led); } // Turn command, but don't let this command override current button presses if (receive["turn"].is()) { - if (!accept_serial_commands) - { - rc = ERROR_REMOTE_INTERFACE_LOCKED; - } - else - { - double turns = receive["turn"]; - if (std::isnan(turns) || std::isinf(turns)) - { - rc = ERROR_NAN_OR_INF_TURN_COMMAND; - } - else - { - rc = queue_add_turn_cmd_blocking(ctx, turns); - } - } + rotor_cmd_t rotor_cmd = {.tag = rotor_cmd_tag::TURN, .value = {.turns = receive["turn"]}}; + rc |= queue_add_cmd_blocking(ctx, &rotor_cmd); } // Print command if (receive["print"].is()) { - JsonDocument doc; - doc["gear_ratio"] = gear_ratio_f; - doc["board_rev"] = BOARD_REV; - doc["firmware"] = FIRMWARE_VER; - doc["enable"] = ctx->enable; - doc["led"] = ctx->led; - doc["charge_current"] = ltc4425_charge_current(); - doc["power_good"] = ltc4425_power_good(); - serializeJson(doc, std::cout); - std::cout << std::endl; + print_report(ctx, ERROR_SUCCESS); } + #ifdef DEBUG serializeJson(receive, std::cout); std::cout << std::endl; @@ -303,17 +250,63 @@ static int process_serial_commands(context_t *ctx) return rc; } +static int process_serial_input(context_t *ctx) +{ + static char serial_buffer[MAX_SERIAL_BUFFER_LENGTH] = {0}; + static uint16_t serial_buffer_index = 0; + + while (tud_cdc_available()) { + + // overflow guard + if (serial_buffer_index >= MAX_SERIAL_BUFFER_LENGTH - 1) { + serial_buffer_index = 0; + memset(serial_buffer, 0, MAX_SERIAL_BUFFER_LENGTH); + return ERROR_COMMAND_BUFFER_OVERFLOW; + } + + char c = getchar_timeout_us(0); + + if (c == '{') + { + // force index 0 and start over + memset(serial_buffer, 0, serial_buffer_index + 1); + serial_buffer_index = 0; + serial_buffer[serial_buffer_index++] = c; + + } + else if (c == '}') + { + // continue downstairs with current buffer + serial_buffer[serial_buffer_index] = c; + int rc = parse_json_command(ctx, serial_buffer); + + memset(serial_buffer, 0, serial_buffer_index + 1); + serial_buffer_index = 0; + + return rc; + } + else + { + serial_buffer[serial_buffer_index++] = c; + } + } + + return ERROR_SUCCESS; + +} + static void core1_entry() { rotor_t rotor = {AccelStepper(AccelStepper::DRIVER, TMC2130_STEP, TMC2130_DIR), gear_ratio_f, 0.0}; rotor_cmd_t rotor_cmd; rotor_init(&rotor); + static bool await_stop = false; rotor_enable(&rotor, false); while (true) { - if (queue_try_remove(&rotor_cmd_queue, &rotor_cmd)) + if (!await_stop && queue_try_remove(&rotor_cmd_queue, &rotor_cmd)) { switch (rotor_cmd.tag) { @@ -321,34 +314,62 @@ static void core1_entry() rotor_enable(&rotor, rotor_cmd.value.enable); break; case rotor_cmd_tag::TURN: - rotor.motor.setAcceleration(MAX_ACCEL_SPSS(rotor.gear_ratio)); + rotor_set_nomimal_accel(&rotor); rotor_move(&rotor, rotor_cmd.value.turns); break; case rotor_cmd_tag::BUTTON_TURN: - rotor.motor.setAcceleration(MAX_ACCEL_SPSS_BUTTON(rotor.gear_ratio)); + rotor_set_fast_accel(&rotor); rotor_move(&rotor, rotor_cmd.value.turns); break; case rotor_cmd_tag::STOP: - rotor.motor.setAcceleration(MAX_ACCEL_SPSS_BUTTON(rotor.gear_ratio) * 2); - rotor.motor.stop(); + rotor_set_fast_accel(&rotor); + rotor_stop(&rotor); + await_stop = true; break; } } // If the motor has completed its motion and is stopped, reset the internal position counter - if (!rotor.motor.run()) + if (!rotor_run(&rotor)) { + await_stop = false; rotor_stop_and_reset(&rotor); } } } +static int check_power(context_t *ctx) +{ + bool pg = ltc4425_power_good(); + bool power_lost = ctx->power_good && !pg; + bool power_restored = !ctx->power_good && pg; + + int rc = ERROR_SUCCESS; + if (power_lost) + { + ctx->enable = false; + rotor_cmd_t rotor_cmd = {.tag = rotor_cmd_tag::ENABLE, .value = {.enable = ctx->enable}}; + queue_add_blocking(&rotor_cmd_queue, &rotor_cmd); + rgb_set_auto(ctx->enable, ctx->led); + rgb_set_breathing(true); + rc |= ERROR_POWER_BAD; + } + else if (power_restored) + { + rgb_set_breathing(false); + rgb_set_auto(ctx->enable, ctx->led); + } + + ctx->power_good = pg; + return rc; +} + int main() { context_t ctx; gear_ratio_f = atof(gear_ratio); - if (gear_ratio_f < -MAX_GEAR_RATIO || gear_ratio_f > MAX_GEAR_RATIO) + if (gear_ratio_f < -MAX_GEAR_RATIO || gear_ratio_f > MAX_GEAR_RATIO || gear_ratio_f == 0.0) { // In all likelihood the string was nonsense return EXIT_FAILURE; @@ -370,6 +391,7 @@ int main() ltc4425_init(); cap1296_init(); while (!ltc4425_power_good()) { tight_loop_contents(); } // Wait for super caps to charge + ctx.power_good = true; rgb_set_breathing(false); rgb_set_auto(ctx.enable, ctx.led); @@ -383,15 +405,13 @@ int main() // Decode commands and buttons while (true) { - int rc = process_button_touches(&ctx); -#ifdef DEBUG - std::cerr << error_str(rc) << "\n"; -#endif + int rc = ERROR_SUCCESS; - rc = process_serial_commands(&ctx); -#ifdef DEBUG - std::cerr << error_str(rc) << "\n"; -#endif + rc |= process_button_touches(&ctx); + rc |= process_serial_input(&ctx); + rc |= check_power(&ctx); + + if (rc) print_report(&ctx, rc); #ifdef DEBUG printf("%f\n", ltc4425_charge_current()); diff --git a/firmware/pindefs.h b/firmware/pindefs.h index ade95ff..dda1d06 100644 --- a/firmware/pindefs.h +++ b/firmware/pindefs.h @@ -12,7 +12,7 @@ #define IS31_POW_BW 5 // Capacitative touch sensor pin -#define CAP1296_ALERT 11 +#define CAP1296_ALERT 11 // Stepper driver pins #define TMC2130_SPI_PORT spi0 diff --git a/firmware/rotor.cpp b/firmware/rotor.cpp index 3204fa0..548e625 100644 --- a/firmware/rotor.cpp +++ b/firmware/rotor.cpp @@ -1,8 +1,13 @@ #include "rotor.h" +#include "tmc2130.h" #include #include "AccelStepper.h" +#define MAX_SPEED_SPS (USTEPS_PER_REV * SPEED_RPM / 60.0L) +#define MAX_ACCEL_SPSS (USTEPS_PER_REV * ACCEL_RPMM / 60.0L) +#define MAX_ACCEL_SPSS_BUTTON (2 * MAX_ACCEL_SPSS) + void rotor_enable(rotor_t *rotor, bool enable) { if (!enable) @@ -18,27 +23,26 @@ void rotor_init(rotor_t *rotor) tmc2130_init(); // Stepper motor configuration - rotor->motor.setMaxSpeed(MAX_SPEED_SPS(rotor->gear_ratio)); - rotor->motor.setAcceleration(MAX_ACCEL_SPSS(rotor->gear_ratio)); + rotor->motor.setMaxSpeed(MAX_SPEED_SPS); + rotor->motor.setAcceleration(MAX_ACCEL_SPSS); rotor->motor.setMinPulseWidth(2); } int rotor_move(rotor_t *rotor, double turns) { - if (std::isinf(turns)) - { - double dir = std::signbit(turns) ? -1.0 : 1.0; - rotor->target_position = (rotor->motor.currentPosition() / (double)USTEPS_PER_REV / rotor->gear_ratio) + (dir * 100.0); - } - - else if (abs(turns) >= MAX_TURNS(rotor->gear_ratio)) - return -1; - - else - rotor->target_position += turns; - + rotor->target_position += turns; long target_position_steps = lround(rotor->target_position * (double)USTEPS_PER_REV * rotor->gear_ratio); rotor->motor.moveTo(target_position_steps); return 0; +} + +void rotor_set_fast_accel(rotor_t *rotor) +{ + rotor->motor.setAcceleration(MAX_ACCEL_SPSS_BUTTON); +} + +void rotor_set_nomimal_accel(rotor_t *rotor) +{ + rotor->motor.setAcceleration(MAX_ACCEL_SPSS); } \ No newline at end of file diff --git a/firmware/rotor.h b/firmware/rotor.h index 660f491..fb27bc3 100644 --- a/firmware/rotor.h +++ b/firmware/rotor.h @@ -4,18 +4,14 @@ #include "AccelStepper.h" #include "tmc2130.h" +// Turn speed and acceleration (defined at the motor itself) +#define SPEED_RPM 100 +#define ACCEL_RPMM 100 + // Stepper and driver parameters #define DETENTS 200 -#define USTEPS_PER_STEP 64 #define USTEPS_PER_REV (DETENTS * USTEPS_PER_STEP) -#define MAX_TURNS(gear_ratio) (INT_MAX / USTEPS_PER_REV / gear_ratio) - -// Turn speed and acceleration -#define SPEED_RPM 100 -#define ACCEL_RPMM 100 -#define MAX_SPEED_SPS(gear_ratio) (USTEPS_PER_REV * gear_ratio * SPEED_RPM / 60.0L) -#define MAX_ACCEL_SPSS(gear_ratio) (USTEPS_PER_REV * gear_ratio * ACCEL_RPMM / 60.0L) -#define MAX_ACCEL_SPSS_BUTTON(gear_ratio) (2 * MAX_ACCEL_SPSS(gear_ratio)) +//#define MAX_TURNS (INT_MAX / USTEPS_PER_REV) typedef struct rotor_t { @@ -32,4 +28,8 @@ static inline void rotor_stop_and_reset(rotor_t *rotor) void rotor_enable(rotor_t *rotor, bool enable); void rotor_init(rotor_t *rotor); -int rotor_move(rotor_t *rotor, double turns); \ No newline at end of file +int rotor_move(rotor_t *rotor, double turns); +void rotor_set_fast_accel(rotor_t *rotor); +void rotor_set_nomimal_accel(rotor_t *rotor); +inline void rotor_stop(rotor_t *rotor) { rotor->motor.stop(); } +inline bool rotor_run(rotor_t *rotor) { return rotor->motor.run(); } \ No newline at end of file diff --git a/firmware/tmc2130.cpp b/firmware/tmc2130.cpp index 819eea6..20d14a0 100644 --- a/firmware/tmc2130.cpp +++ b/firmware/tmc2130.cpp @@ -4,12 +4,24 @@ #include "hardware/spi.h" #include "tmc2130.h" -#include "rotor.h" #include "math.h" #define WRITE_FLAG (1<<7) #define READ_FLAG (0<<7) +#define REG_GCONF 0x00 +#define REG_GSTAT 0x01 +#define REG_IHOLD_IRUN 0x10 +#define REG_TPOWERDOWN 0x11 +#define REG_TSTEP 0x12 +#define REG_TPWMTHRS 0x13 +#define REG_TCOOLTHRS 0x14 +#define REG_CHOPCONF 0x6C +#define REG_COOLCONF 0x6D +#define REG_DCCTRL 0x6E +#define REG_DRVSTATUS 0x6F +#define REG_PWMCONF 0x70 + static inline void cs_select() { asm volatile("nop \n nop \n nop"); gpio_put(TMC2130_CFG3_CS, 0); // Active low @@ -100,7 +112,7 @@ void tmc2130_init() tmc2130_enable(false); } -int32_t tmc2130_status() +uint32_t tmc2130_status() { return tmc2130_read(REG_DRVSTATUS); } \ No newline at end of file diff --git a/firmware/tmc2130.h b/firmware/tmc2130.h index 34e6ff2..2f0a294 100644 --- a/firmware/tmc2130.h +++ b/firmware/tmc2130.h @@ -2,21 +2,8 @@ #include "pindefs.h" -#define TMC2130_SPI_PORT spi0 - -// TMC2130 registers -#define REG_GCONF 0x00 -#define REG_GSTAT 0x01 -#define REG_IHOLD_IRUN 0x10 -#define REG_TPOWERDOWN 0x11 -#define REG_TSTEP 0x12 -#define REG_TPWMTHRS 0x13 -#define REG_TCOOLTHRS 0x14 -#define REG_CHOPCONF 0x6C -#define REG_COOLCONF 0x6D -#define REG_DCCTRL 0x6E -#define REG_DRVSTATUS 0x6F -#define REG_PWMCONF 0x70 +// We used 64 microsteps per step +#define USTEPS_PER_STEP 64 static inline void tmc2130_enable(bool enable) { @@ -24,4 +11,4 @@ static inline void tmc2130_enable(bool enable) } void tmc2130_init(); -int32_t tmc2130_status(); +uint32_t tmc2130_status();