From 8c2223de2028f5f15f53f3c4cf2999d10b25a8e4 Mon Sep 17 00:00:00 2001 From: Brandon Kinman Date: Sun, 8 Apr 2018 12:13:32 -0700 Subject: [PATCH 01/32] System version checking now works as expected --- tools/odrive/protocol.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/odrive/protocol.py b/tools/odrive/protocol.py index 18f44a41..c7305b19 100644 --- a/tools/odrive/protocol.py +++ b/tools/odrive/protocol.py @@ -6,14 +6,14 @@ import sys import abc -# if sys.version_info >= (3, 4): -ABC = abc.ABC -# else: -# ABC = abc.ABCMeta('ABC', (), {}) +if (sys.version_info[0], sys.version_info[1]) >= (3, 4): + ABC = abc.ABC +else: + ABC = abc.ABCMeta('ABC', (), {}) -# if sys.version_info <= (3, 3): -# from monotonic import monotonic -# time.monotonic = monotonic +if (sys.version_info[0], sys.version_info[1]) <= (3, 3): + from monotonic import monotonic + time.monotonic = monotonic SYNC_BYTE = 0xAA CRC8_INIT = 0x42 From 39ef119515e6315d0f8c771e6c522e71f98a3d71 Mon Sep 17 00:00:00 2001 From: Brandon Kinman Date: Sun, 8 Apr 2018 15:29:49 -0700 Subject: [PATCH 02/32] Making explore_odrive compatible with python27 --- tools/explore_odrive.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/explore_odrive.py b/tools/explore_odrive.py index 9efaebb7..ebe23bf8 100755 --- a/tools/explore_odrive.py +++ b/tools/explore_odrive.py @@ -1,3 +1,5 @@ +from __future__ import print_function + #!/usr/bin/env python3 """ Load an odrive object to play with in the IPython interactive shell. From b4fa53c84980aff073bfabd38c5baf72c7384ba5 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 9 Apr 2018 19:32:36 -0700 Subject: [PATCH 03/32] fix encoder offset calibration and error handling --- Firmware/MotorControl/axis.cpp | 6 ++++++ Firmware/MotorControl/encoder.cpp | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index fe3760aa..c0488dfa 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -251,14 +251,20 @@ void Axis::run_state_machine_loop() { switch (current_state_) { case AXIS_STATE_MOTOR_CALIBRATION: status = motor_.run_calibration(); + if (!status) + error_ |= ERROR_MOTOR_FAILED; break; case AXIS_STATE_ENCODER_INDEX_SEARCH: status = encoder_.run_index_search(); + if (!status) + error_ |= ERROR_ENCODER_FAILED; break; case AXIS_STATE_ENCODER_OFFSET_CALIBRATION: status = encoder_.run_offset_calibration(); + if (!status) + error_ |= ERROR_ENCODER_FAILED; break; case AXIS_STATE_SENSORLESS_CONTROL: diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index 19a39792..fad27267 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -103,7 +103,7 @@ bool Encoder::run_offset_calibration() { // Temporarily disable index search so it doesn't mess // with the offset calibration bool old_use_index = config_.use_index; - config_.use_index = true; + config_.use_index = false; float voltage_magnitude; if (axis_->motor_.config_.motor_type == MOTOR_TYPE_HIGH_CURRENT) From 44fd8090e190d8bb38afa6d1ce4f3a36637454a9 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 10 Apr 2018 00:25:10 -0700 Subject: [PATCH 04/32] add overvoltage protection The motor phases will go into floating state as soon as an overvoltage condition is detected. --- Firmware/MotorControl/axis.cpp | 9 +++------ Firmware/MotorControl/axis.hpp | 2 -- Firmware/MotorControl/communication.cpp | 4 +++- Firmware/MotorControl/odrive_main.hpp | 5 +++++ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index c0488dfa..bde389d4 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -92,18 +92,15 @@ void Axis::set_step_dir_enabled(bool enable) { } } -// @brief Returns true if the power supply is within range -bool Axis::check_PSU_brownout() { - return vbus_voltage >= config_.dc_bus_brownout_trip_level; -} - // @brief Returns true if everything is ok. // Sets error and returns false otherwise. bool Axis::do_checks() { if (!motor_.do_checks()) return error_ |= ERROR_MOTOR_FAILED, false; - if (!check_PSU_brownout()) + if (!(vbus_voltage >= board_config.dc_bus_undervoltage_trip_level)) return error_ |= ERROR_DC_BUS_UNDER_VOLTAGE, false; + if (!(vbus_voltage <= board_config.dc_bus_overvoltage_trip_level)) + return error_ |= ERROR_DC_BUS_OVER_VOLTAGE, false; return true; } diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index f6415422..72dac9b3 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -30,7 +30,6 @@ struct AxisConfig_t { // For M0 this has no effect if enable_uart is true float counts_per_step = 2.0f; - float dc_bus_brownout_trip_level = 8.0f; //make_protocol_definitions()), make_protocol_object("axis1", axes[1]->make_protocol_definitions()), diff --git a/Firmware/MotorControl/odrive_main.hpp b/Firmware/MotorControl/odrive_main.hpp index 1b4b8850..f1d91007 100644 --- a/Firmware/MotorControl/odrive_main.hpp +++ b/Firmware/MotorControl/odrive_main.hpp @@ -23,6 +23,11 @@ struct BoardConfig_t { bool enable_uart = true; float brake_resistance = 0.47f; // [ohm] + float dc_bus_undervoltage_trip_level = 8.0f; // Date: Tue, 10 Apr 2018 18:53:58 -0700 Subject: [PATCH 05/32] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 192a3c6b..16613f9a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ All non-power I/O is 3.3V output and 5V tolerant on input, except: You need one or two [brushless motors](https://hackaday.io/project/11583-odrive-high-performance-motor-control/log/37666-hobby-motors-in-your-robots), [quadrature incremental encoder(s)](https://discourse.odriverobotics.com/t/which-encoders-to-choose/63/2), and a power resistor. -*The power resistor values you need depends on your motor setup, and peak/average decelleration power. A good starting point would be a [0.47 ohm, 50W resistor](https://www.digikey.com/product-detail/en/te-connectivity-passive-product/HSA50R47J/A102181-ND/2056131).* +The power resistor values you need depends on your motor setup, and peak/average decelleration power. A good starting point would be a [0.47 ohm, 50W resistor](https://www.digikey.com/product-detail/en/te-connectivity-passive-product/HSA50R47J/A102181-ND/2056131). **Warning! Failure to use a break resistor may result in damage to your ODrive and/or power supply!** Wire up the motor phases into the 3-phase screw terminals, and the power resistor to the AUX terminal. Wire up the power source (12-24V) to the DC terminal, make sure to pay attention to the polarity. Do not apply power just yet. From bd292b4775d5964e82a49287f793943f0fa512b0 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Fri, 13 Apr 2018 16:17:05 -0700 Subject: [PATCH 06/32] fix comment --- Firmware/MotorControl/axis.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 72dac9b3..601b2367 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -13,7 +13,7 @@ enum AxisState_t { AXIS_STATE_STARTUP_SEQUENCE = 2, // Date: Sat, 14 Apr 2018 23:03:03 -0700 Subject: [PATCH 07/32] add property read/write functionality to ASCII protocol Command examples: r vbus_voltage w axis0.config.enable_step_dir 1 --- Firmware/MotorControl/ascii_protocol.cpp | 40 ++++++++- Firmware/MotorControl/protocol.hpp | 105 ++++++++++++++++++++++- 2 files changed, 141 insertions(+), 4 deletions(-) diff --git a/Firmware/MotorControl/ascii_protocol.cpp b/Firmware/MotorControl/ascii_protocol.cpp index 08eecc1d..ad24b1b5 100644 --- a/Firmware/MotorControl/ascii_protocol.cpp +++ b/Firmware/MotorControl/ascii_protocol.cpp @@ -18,7 +18,9 @@ /* Global variables ----------------------------------------------------------*/ /* Private constant data -----------------------------------------------------*/ -#define MAX_LINE_LENGTH 64 +#define MAX_LINE_LENGTH 256 +#define TO_STR_INNER(s) #s +#define TO_STR(s) TO_STR_INNER(s) /* Private variables ---------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ @@ -131,8 +133,40 @@ void ASCII_protocol_process_line(const uint8_t* buffer, size_t len, StreamSink& respond(response_channel, use_checksum, "Flash Size: %#x KiB", STM_ID_GetFlashSize()); respond(response_channel, use_checksum, "Serial number: %s", serial_number_str); -// } else if (cmd[0] == 'r') { // read property -// } else if (cmd[0] == 'w') { // write property + } else if (cmd[0] == 'r') { // read property + char name[MAX_LINE_LENGTH]; + int numscan = sscanf(cmd, "r %" TO_STR(MAX_LINE_LENGTH) "s", name); + if (numscan < 1) { + respond(response_channel, use_checksum, "invalid command format"); + } else { + Endpoint* endpoint = application_endpoints->get_by_name(name, sizeof(name)); + if (!endpoint) { + respond(response_channel, use_checksum, "invalid property"); + } else { + char response[10]; + bool success = endpoint->get_string(response, sizeof(response)); + if (!success) + respond(response_channel, use_checksum, "not implemented"); + else + respond(response_channel, use_checksum, response); + } + } + } else if (cmd[0] == 'w') { // write property + char name[MAX_LINE_LENGTH]; + char value[MAX_LINE_LENGTH]; + int numscan = sscanf(cmd, "w %" TO_STR(MAX_LINE_LENGTH) "s %" TO_STR(MAX_LINE_LENGTH) "s", name, value); + if (numscan < 1) { + respond(response_channel, use_checksum, "invalid command format"); + } else { + Endpoint* endpoint = application_endpoints->get_by_name(name, sizeof(name)); + if (!endpoint) { + respond(response_channel, use_checksum, "invalid property"); + } else { + bool success = endpoint->set_string(value, sizeof(value)); + if (!success) + respond(response_channel, use_checksum, "not implemented"); + } + } } else if (cmd[0] == 'h') { // HALT for(size_t i = 0; i < AXIS_COUNT; i++){ diff --git a/Firmware/MotorControl/protocol.hpp b/Firmware/MotorControl/protocol.hpp index bd853608..cb0f666b 100644 --- a/Firmware/MotorControl/protocol.hpp +++ b/Firmware/MotorControl/protocol.hpp @@ -407,12 +407,15 @@ class Endpoint { public: //const char* const name_; virtual void handle(const uint8_t* input, size_t input_length, StreamSink* output) = 0; + virtual bool get_string(char * output, size_t length) { return false; }; + virtual bool set_string(char * buffer, size_t length) { return false; } }; class EndpointProvider { public: virtual size_t get_endpoint_count() = 0; virtual void write_json(size_t id, StreamSink* output) = 0; + virtual Endpoint* get_by_name(char * name, size_t length) = 0; virtual void register_endpoints(Endpoint** list, size_t id, size_t length) = 0; }; @@ -456,6 +459,9 @@ public: void register_endpoints(Endpoint** list, size_t id, size_t length) { // no action } + Endpoint* get_by_name(const char * name, size_t length) { + return nullptr; + } std::tuple<> get_names_as_tuple() const { return std::tuple<>(); } }; @@ -485,6 +491,12 @@ public: subsequent_members_.write_json(id + TMember::endpoint_count, output); } + Endpoint* get_by_name(const char * name, size_t length) { + Endpoint* result = this_member_.get_by_name(name, length); + if (result) return result; + else return subsequent_members_.get_by_name(name, length); + } + void register_endpoints(Endpoint** list, size_t id, size_t length) /*final*/ { this_member_.register_endpoints(list, id, length); subsequent_members_.register_endpoints(list, id + TMember::endpoint_count, length); @@ -516,6 +528,14 @@ public: write_string("]}", output); } + Endpoint* get_by_name(const char * name, size_t length) { + size_t segment_length = strlen(name); + if (!strncmp(name, name_, length)) + return member_list_.get_by_name(name + segment_length + 1, length - segment_length - 1); + else + return nullptr; + } + void register_endpoints(Endpoint** list, size_t id, size_t length) { member_list_.register_endpoints(list, id, length); } @@ -529,6 +549,11 @@ ProtocolObject make_protocol_object(const char * name, TMembers&&.. return ProtocolObject(name, std::forward(member_list)...); } + +// TODO: move to cpp_utils +#define ENABLE_IF_SAME(a, b, type) \ + template typename std::enable_if_t::value, bool> + template class ProtocolProperty : public Endpoint { public: @@ -585,6 +610,71 @@ public: write_string("}", output); } + Endpoint* get_by_name(const char * name, size_t length) { + if (!strncmp(name, name_, length)) + return this; + else + return nullptr; + } + + + // *** ASCII protocol handlers *** + + ENABLE_IF_SAME(std::decay_t, float, bool) + get_string_ex(char * buffer, size_t length, int) { + snprintf(buffer, length, "%f", *property_); + return true; + } + ENABLE_IF_SAME(std::decay_t, int32_t, bool) + get_string_ex(char * buffer, size_t length, int) { + snprintf(buffer, length, "%ld", *property_); + return true; + } + ENABLE_IF_SAME(std::decay_t, uint32_t, bool) + get_string_ex(char * buffer, size_t length, int) { + snprintf(buffer, length, "%lu", *property_); + return true; + } + ENABLE_IF_SAME(std::decay_t, bool, bool) + get_string_ex(char * buffer, size_t length, int) { + buffer[0] = (*property_) ? '1' : '0'; + buffer[1] = 0; + return true; + } + bool get_string_ex(char * buffer, size_t length, ...) { + return false; + } + bool get_string(char * buffer, size_t length) final { + return get_string_ex(buffer, length, 0); + } + ENABLE_IF_SAME(TProperty, float, bool) + set_string_ex(char * buffer, size_t length, int) { + return sscanf(buffer, "%f", property_) == 1; + } + ENABLE_IF_SAME(TProperty, int32_t, bool) + set_string_ex(char * buffer, size_t length, int) { + return sscanf(buffer, "%ld", property_) == 1; + } + ENABLE_IF_SAME(TProperty, uint32_t, bool) + set_string_ex(char * buffer, size_t length, int) { + return sscanf(buffer, "%lu", property_) == 1; + } + ENABLE_IF_SAME(TProperty, bool, bool) + set_string_ex(char * buffer, size_t length, int) { + int val; + if (sscanf(buffer, "%d", &val) != 1) + return false; + *property_ = val; + return true; + } + bool set_string_ex(char * buffer, size_t length, ...) { + return false; + } + bool set_string(char * buffer, size_t length) final { + //__asm ("bkpt"); + return set_string_ex(buffer, length, 0); + } + void register_endpoints(Endpoint** list, size_t id, size_t length) { if (id < length) list[id] = this; @@ -686,7 +776,7 @@ struct PropertyListFactory { template -class ProtocolFunction : Endpoint { +class ProtocolFunction : public Endpoint { public: static constexpr size_t endpoint_count = 1 + MemberList...>::endpoint_count; template @@ -722,6 +812,10 @@ public: write_string("]}", output); } + Endpoint* get_by_name(const char * name, size_t length) { + return nullptr; // can't address functions by name + } + void register_endpoints(Endpoint** list, size_t id, size_t length) { if (id < length) list[id] = this; @@ -765,6 +859,14 @@ public: void register_endpoints(Endpoint** list, size_t id, size_t length) final { return member_list_.register_endpoints(list, id, length); } + Endpoint* get_by_name(char * name, size_t length) final { + for (size_t i = 0; i < length; i++) { + if (name[i] == '.') + name[i] = 0; + } + name[length-1] = 0; + return member_list_.get_by_name(name, length); + } T& member_list_; }; @@ -775,5 +877,6 @@ void set_application_endpoints(EndpointProvider* endpoints); extern Endpoint* endpoints_[]; extern size_t n_endpoints_; extern const size_t max_endpoints_; +extern EndpointProvider* application_endpoints; #endif From 6e60ec4631bcb72d96124c52eaa0aa9a9a53262c Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 15 Apr 2018 17:38:37 -0700 Subject: [PATCH 08/32] Check for dc bus overvoltage --- Firmware/Board/v3/Inc/main.h | 2 ++ Firmware/MotorControl/low_level.c | 26 ++++++++++++++++++++------ Firmware/MotorControl/low_level.h | 6 ++++-- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Firmware/Board/v3/Inc/main.h b/Firmware/Board/v3/Inc/main.h index ac9bd857..4292821d 100644 --- a/Firmware/Board/v3/Inc/main.h +++ b/Firmware/Board/v3/Inc/main.h @@ -170,8 +170,10 @@ #if HW_VERSION_VOLTAGE == 48 #define VBUS_S_DIVIDER_RATIO 19.0f +#define VBUS_OVERVOLTAGE_LEVEL 52.0f #elif HW_VERSION_VOLTAGE == 24 #define VBUS_S_DIVIDER_RATIO 11.0f +#define VBUS_OVERVOLTAGE_LEVEL 26.0f #else #error "unknown board voltage" #endif diff --git a/Firmware/MotorControl/low_level.c b/Firmware/MotorControl/low_level.c index c1e99742..45c7529c 100644 --- a/Firmware/MotorControl/low_level.c +++ b/Firmware/MotorControl/low_level.c @@ -68,7 +68,8 @@ Motor_t motors[] = { .current_setpoint = 0.0f, // [A] .calibration_current = 10.0f, // [A] .resistance_calib_max_voltage = 1.0f, // [V] - You may need to increase this if this voltage isn't sufficient to drive calibration_current through the motor. - .dc_bus_brownout_trip_level = 8.0f, // [V] + .dc_bus_undervoltage_trip_level = 8.0f, // [V] + .dc_bus_overvoltage_trip_level = VBUS_OVERVOLTAGE_LEVEL, // [V] .phase_inductance = 0.0f, // to be set by measure_phase_inductance .phase_resistance = 0.0f, // to be set by measure_phase_resistance .motor_thread = 0, @@ -176,7 +177,8 @@ Motor_t motors[] = { .current_setpoint = 0.0f, // [A] .calibration_current = 10.0f, // [A] .resistance_calib_max_voltage = 1.0f, // [V] - You may need to increase this if this voltage isn't sufficient to drive calibration_current through the motor. - .dc_bus_brownout_trip_level = 8.0f, // [V] + .dc_bus_undervoltage_trip_level = 8.0f, // [V] + .dc_bus_overvoltage_trip_level = VBUS_OVERVOLTAGE_LEVEL, // [V] .phase_inductance = 0.0f, // to be set by measure_phase_inductance .phase_resistance = 0.0f, // to be set by measure_phase_resistance .motor_thread = 0, @@ -1320,8 +1322,16 @@ bool check_DRV_fault(Motor_t* motor) { } //Returns true if everything is OK (no fault) -bool check_PSU_brownout(Motor_t* motor) { - if(vbus_voltage < motor->dc_bus_brownout_trip_level) +bool check_vbus_undervoltage(Motor_t* motor) { + if(vbus_voltage < motor->dc_bus_undervoltage_trip_level) + return false; + return true; +} + +//Returns true if everything is OK (no fault) +// TODO This will be less repetitive with the refactoring +bool check_vbus_overvoltage(Motor_t* motor) { + if(vbus_voltage > motor->dc_bus_overvoltage_trip_level) return false; return true; } @@ -1338,8 +1348,12 @@ bool do_checks(Motor_t* motor) { DRV8301_readData(&motor->gate_driver, local_regs); return false; } - if (!check_PSU_brownout(motor)) { - motor->error = ERROR_DC_BUS_BROWNOUT; + if (!check_vbus_undervoltage(motor)) { + motor->error = ERROR_DC_BUS_UNDERVOLTAGE; + return false; + } + if (!check_vbus_overvoltage(motor)) { + motor->error = ERROR_DC_BUS_OVERVOLTAGE; return false; } return true; diff --git a/Firmware/MotorControl/low_level.h b/Firmware/MotorControl/low_level.h index e92774e4..1ed956db 100644 --- a/Firmware/MotorControl/low_level.h +++ b/Firmware/MotorControl/low_level.h @@ -51,7 +51,8 @@ typedef enum { ERROR_DRV_FAULT, ERROR_NOT_IMPLEMENTED_MOTOR_TYPE, ERROR_ENCODER_CPR_OUT_OF_RANGE, - ERROR_DC_BUS_BROWNOUT, + ERROR_DC_BUS_UNDERVOLTAGE, + ERROR_DC_BUS_OVERVOLTAGE, } Error_t; // Note: these should be sorted from lowest level of control to @@ -151,7 +152,8 @@ typedef struct { float current_setpoint; float calibration_current; float resistance_calib_max_voltage; - float dc_bus_brownout_trip_level; + float dc_bus_undervoltage_trip_level; + float dc_bus_overvoltage_trip_level; float phase_inductance; float phase_resistance; osThreadId motor_thread; From 27574234d5991e1c15725ae86bcbc460f6575233 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Mon, 16 Apr 2018 20:04:02 -0700 Subject: [PATCH 09/32] improve resilience and safety of entering DFU mode - Disable interrupts before setting the reboot cookie - Delay after NVIC_SystemReset before jumping to bootloader (see comment for an explanation) - Make a variable for the reboot cookie - Do reboot cookie checks before C++ static initializers --- Firmware/Board/v3/Src/main.c | 64 ++++++++++++++++--------- Firmware/Board/v3/startup_stm32f405xx.s | 2 + Firmware/MotorControl/communication.cpp | 3 +- Firmware/MotorControl/odrive_main.hpp | 2 + 4 files changed, 47 insertions(+), 24 deletions(-) diff --git a/Firmware/Board/v3/Src/main.c b/Firmware/Board/v3/Src/main.c index 64810aac..67c638df 100644 --- a/Firmware/Board/v3/Src/main.c +++ b/Firmware/Board/v3/Src/main.c @@ -81,12 +81,47 @@ void MX_FREERTOS_Init(void); /* USER CODE BEGIN 0 */ -void jump_to_builtin_bootloader(void) { - __set_MSP(0x20001000); - // http://www.st.com/content/ccc/resource/technical/document/application_note/6a/17/92/02/58/98/45/0c/CD00264379.pdf/files/CD00264379.pdf - void (*builtin_bootloader)(void) = (void (*)(void))(*((uint32_t *)0x1FFF0004)); - builtin_bootloader(); - for (;;); +uint32_t _reboot_cookie __attribute__ ((section (".noinit"))); +extern char _estack; // provided by the linker script + +// Gets called from the startup assembly code +void early_start_checks(void) { + /* We could jump to the bootloader directly on demand without rebooting + but that requires us to reset several peripherals and interrupts for it + to function correctly. Therefore it's easier to just reset the entire chip. */ + if(_reboot_cookie == 0xDEADBEEF) { + _reboot_cookie = 0xCAFEFEED; //Reset bootloader trigger + + /* + * This wait loop solves an obscure timing issue, but we don't exactly understand why. + * When the transition NVIC_SystemReset() => STM bootloader happens very quickly, + * there is a yet unexplained phenomenon where the ODrive would emit an audible click, + * followed by one the following symptoms: + * - Device reboots in normal mode (possibly due to the bootloader exiting immidiately) + * - Device goes into DFU mode and then the power supply turns off + * This manifests in the DFU script detecting the device in DFU mode but then + * losing the device immidiately after. + * There were no motors/encoders/brake resistor connected when testing this. As far as + * we can tell, the only way for the software to cause a short circuit is through the + * brake FETs. + */ + for (size_t i = 0; i < 1000000; ++i) { + __NOP(); + } + + __set_MSP((uintptr_t)&_estack); + // http://www.st.com/content/ccc/resource/technical/document/application_note/6a/17/92/02/58/98/45/0c/CD00264379.pdf/files/CD00264379.pdf + void (*builtin_bootloader)(void) = (void (*)(void))(*((uint32_t *)0x1FFF0004)); + builtin_bootloader(); + } + + /* The bootloader might fail to properly clean up after itself, + so if we're not sure that the system is in a clean state we + just reset it again */ + if(_reboot_cookie != 42) { + _reboot_cookie = 42; + NVIC_SystemReset(); + } } /* USER CODE END 0 */ @@ -100,22 +135,6 @@ int main(void) { /* USER CODE BEGIN 1 */ - /* We could jump to the bootloader directly on demand without rebooting - but that requires us to reset several peripherals and interrupts for it - to function correctly. Therefore it's easier to just reset the entire chip. */ - if(*((unsigned long *)0x2001C000) == 0xDEADBEEF) { - *((unsigned long *)0x2001C000) = 0xCAFEFEED; //Reset bootloader trigger - jump_to_builtin_bootloader(); - } - - /* The bootloader might fail to properly clean up after itself, - so if we're not sure that the system is in a clean state we - just reset it again */ - if(*((unsigned long *)0x2001C000) != 42) { - *((unsigned long *)0x2001C000) = 42; - NVIC_SystemReset(); - } - // This procedure of building a USB serial number should be identical // to the way the STM's built-in USB bootloader does it. This means // that the device will have the same serial number in normal and DFU mode. @@ -155,7 +174,6 @@ int main(void) MX_DMA_Init(); MX_ADC1_Init(); MX_ADC2_Init(); - MX_CAN1_Init(); MX_TIM1_Init(); MX_TIM8_Init(); MX_TIM3_Init(); diff --git a/Firmware/Board/v3/startup_stm32f405xx.s b/Firmware/Board/v3/startup_stm32f405xx.s index ea0e76a9..34c01d1d 100644 --- a/Firmware/Board/v3/startup_stm32f405xx.s +++ b/Firmware/Board/v3/startup_stm32f405xx.s @@ -107,6 +107,8 @@ LoopFillZerobss: /* Call the clock system intitialization function.*/ bl SystemInit + bl early_start_checks + /* Call static constructors */ bl __libc_init_array /* Call the application's entry point.*/ diff --git a/Firmware/MotorControl/communication.cpp b/Firmware/MotorControl/communication.cpp index 7bd1a391..87711595 100644 --- a/Firmware/MotorControl/communication.cpp +++ b/Firmware/MotorControl/communication.cpp @@ -140,7 +140,8 @@ StreamToPacketConverter uart4_stream_input(uart4_channel); /* Function implementations --------------------------------------------------*/ void enter_dfu_mode() { - *((unsigned long *)0x2001C000) = 0xDEADBEEF; + __asm volatile ("CPSID I\n\t":::"memory"); // disable interrupts + _reboot_cookie = 0xDEADBEEF; NVIC_SystemReset(); } diff --git a/Firmware/MotorControl/odrive_main.hpp b/Firmware/MotorControl/odrive_main.hpp index f1d91007..f1ebb703 100644 --- a/Firmware/MotorControl/odrive_main.hpp +++ b/Firmware/MotorControl/odrive_main.hpp @@ -46,6 +46,8 @@ extern BoardConfig_t board_config; constexpr size_t AXIS_COUNT = 2; extern Axis *axes[AXIS_COUNT]; +extern uint32_t _reboot_cookie; + // TODO: move // this is technically not thread-safe but practically it might be #define DEFINE_ENUM_FLAG_OPERATORS(ENUMTYPE) \ From cd5b9014c1d90ca03cab563c629ad32b420a0209 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 17 Apr 2018 13:29:17 -0700 Subject: [PATCH 10/32] include PyWin32 as dependency for windows, add "Developer Preview" note --- tools/odrivetool | 8 +++++++- tools/setup.py | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/odrivetool b/tools/odrivetool index 0bcc39b7..b66db5c7 100755 --- a/tools/odrivetool +++ b/tools/odrivetool @@ -79,9 +79,15 @@ else: printer = lambda x: None logger = Logger(verbose=args.verbose) -logger.debug(str(args)) print("ODrive control utility v" + odrive.__version__) +if ".dev" in odrive.__version__: + print("") + logger.warn("Developer Preview") + print(" If you find issues, please report them") + print(" on https://github.com/madcowswe/ODrive/issues") + print(" or better yet, submit a pull request to fix it.") + print("") app_shutdown_token = Event() diff --git a/tools/setup.py b/tools/setup.py index 1c43b0a1..7fde17bd 100644 --- a/tools/setup.py +++ b/tools/setup.py @@ -74,7 +74,8 @@ setup( 'PyUSB', # Required to access USB devices from Python through libusb 'PySerial', # Required to access serial devices from Python 'IntelHex', # Used to by DFU to load firmware files - 'matplotlib' # Required to run the liveplotter + 'matplotlib', # Required to run the liveplotter + 'pywin32 >= 1.0;platform_system=="Windows"' # Required for fancy terminal features on Windows ], package_data={'': ['version.txt']}, include_package_data=True, From 8c6110e4992dc50e47c67e48cdc4a22b9c11c10f Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 17 Apr 2018 13:29:38 -0700 Subject: [PATCH 11/32] improve tostring dump of remote objects --- tools/odrive/remote_object.py | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tools/odrive/remote_object.py b/tools/odrive/remote_object.py index 58da44ab..81182c77 100644 --- a/tools/odrive/remote_object.py +++ b/tools/odrive/remote_object.py @@ -85,6 +85,17 @@ class RemoteProperty(): # TODO: Currenly we wait for an ack here. Settle on the default guarantee. self._parent.__channel__.remote_endpoint_operation(self._id, buffer, True, 0) + def dump(self): + if self._name == "serial_number": + # special case: serial number should be displayed in hex (TODO: generalize) + val_str = "{:012X}".format(self.get_value()) + elif self._name == "error": + # special case: errors should be displayed in hex (TODO: generalize) + val_str = "0x{:04X}".format(self.get_value()) + else: + val_str = str(self.get_value()) + return "{} = {} ({})".format(self._name, val_str, self._property_type.__name__) + class RemoteFunction(object): """ Represents a callable function that maps to a function call on a remote object @@ -96,6 +107,10 @@ class RemoteFunction(object): raise ObjectDefinitionError("unspecified endpoint ID") self._trigger_id = int(id_str) + self._name = json_data.get("name", None) + if self._name is None: + self._name = "[anonymous]" + self._inputs = [] for param_json in json_data.get("arguments", []) + json_data.get("inputs", []): # TODO: deprecate "arguments" keyword param_json["mode"] = "r" @@ -108,6 +123,9 @@ class RemoteFunction(object): self._inputs[i].set_value(args[i]) self._parent.__channel__.remote_endpoint_operation(self._trigger_id, None, True, 0) + def dump(self): + return "{}({})".format(self._name, ", ".join("{}: {}".format(x._name, x._property_type.__name__) for x in self._inputs)) + class RemoteObject(object): """ Object with functions and properties that map to remote endpoints @@ -156,8 +174,21 @@ class RemoteObject(object): self.__sealed__ = True channel._channel_broken.subscribe(self._tear_down) + def dump(self, indent, depth): + if depth <= 0: + return "..." + lines = [] + for key, val in self._remote_attributes.items(): + if isinstance(val, RemoteObject): + val_str = indent + key + (": " if depth == 1 else ":\n") + val.dump(indent + " ", depth - 1) + else: + val_str = indent + val.dump() + lines.append(val_str) + return "\n".join(lines) + def __str__(self): - return str(dir(self)) # TODO: improve print output + return self.dump("", depth=2) + def __repr__(self): return self.__str__() From 0eddbfc11c44e0d21e0009f0b8c644df9538301b Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 17 Apr 2018 19:59:44 -0700 Subject: [PATCH 12/32] Add udev-setup command to odrivetool --- tools/odrive/utils.py | 13 +++++++++++++ tools/odrivetool | 5 +++++ tools/setup.py | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 810780d0..5834ff85 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -7,6 +7,8 @@ import sys import time import threading import platform +import subprocess +import os try: if platform.system() == 'Windows': @@ -109,6 +111,17 @@ def rate_test(device): FramePerSec = loopsPerSec/loopsPerFrame print("Frames per second: " + str(FramePerSec)) +def setup_udev_rules(logger): + if platform.system() != 'Linux': + logger.error("This command only makes sense on Linux") + if os.getuid() != 0: + logger.warn("you should run this as root, otherwise it will probably not work") + with open('/etc/udev/rules.d/50-odrive.rules', 'w') as file: + file.write('SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0d3[0-9]", MODE="0666"\n') + subprocess.run(["udevadm", "control", "--reload-rules"], check=True) + subprocess.run(["udevadm", "trigger"], check=True) + logger.info('udev rules configured successfully') + ## Exceptions ## diff --git a/tools/odrivetool b/tools/odrivetool index b66db5c7..2feb9b2c 100755 --- a/tools/odrivetool +++ b/tools/odrivetool @@ -31,6 +31,7 @@ dfu_parser.add_argument('file', metavar='HEX', help='The .hex file to be flashed subparsers.add_parser('liveplotter', help="Upgrade the ODrive's Firmware") subparsers.add_parser('drv-status', help="Show status of the on-board DRV8301 chips (for debugging only)") subparsers.add_parser('rate-test', help="Estimate the average transmission bandwidth over USB") +subparsers.add_parser('udev-setup', help="Linux only: Gives users on your system permission to access the ODrive by installing udev rules") # General arguments parser.add_argument("-p", "--path", metavar="PATH", action="store", @@ -126,6 +127,10 @@ try: my_odrive = odrive.discovery.find_any(path=args.path, serial_number=args.serial_number) rate_test(my_odrive) + elif args.command == 'udev-setup': + from odrive.utils import setup_udev_rules + setup_udev_rules(logger) + else: raise Exception("unknown command: " + args.command) diff --git a/tools/setup.py b/tools/setup.py index 7fde17bd..a7ac5980 100644 --- a/tools/setup.py +++ b/tools/setup.py @@ -58,6 +58,12 @@ if creating_package: with open(version_file_path, mode='w') as version_file: version_file.write(version) +# TODO: find a better place for this +if not creating_package: + import platform + if platform.system() == 'Linux': + import odrive.utils + odrive.utils.setup_udev_rules(odrive.utils.Logger()) setup( name = 'odrive', From 7b9b0f884b73e2a0e1ed5886dcfca1def2f67805 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 17 Apr 2018 20:03:19 -0700 Subject: [PATCH 13/32] Add batch file for windows as a trampoline for the actual script The installation does not work unless pywin32 is already installed. See: https://github.com/mhammond/pywin32/issues/1197 --- tools/odrivetool.bat | 2 ++ tools/setup.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 tools/odrivetool.bat diff --git a/tools/odrivetool.bat b/tools/odrivetool.bat new file mode 100644 index 00000000..765e31cb --- /dev/null +++ b/tools/odrivetool.bat @@ -0,0 +1,2 @@ +@echo off +python %~dp0\odrivetool \ No newline at end of file diff --git a/tools/setup.py b/tools/setup.py index a7ac5980..85ddd5cd 100644 --- a/tools/setup.py +++ b/tools/setup.py @@ -68,7 +68,7 @@ if not creating_package: setup( name = 'odrive', packages = ['odrive', 'odrive.dfuse'], # this must be the same as the name above - scripts = ['odrivetool', 'odrive_demo.py'], + scripts = ['odrivetool', 'odrivetool.bat', 'odrive_demo.py'], version = version, description = 'Control utilities for the ODrive high performance motor controller', author = 'Oskar Weigl', @@ -81,7 +81,7 @@ setup( 'PySerial', # Required to access serial devices from Python 'IntelHex', # Used to by DFU to load firmware files 'matplotlib', # Required to run the liveplotter - 'pywin32 >= 1.0;platform_system=="Windows"' # Required for fancy terminal features on Windows + 'pywin32==222;platform_system=="Windows"' # Required for fancy terminal features on Windows ], package_data={'': ['version.txt']}, include_package_data=True, From 3eeffd6f7ecfc7d399865c8e25c6e70e275f49ae Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Tue, 17 Apr 2018 22:33:29 -0700 Subject: [PATCH 14/32] add checksum output --- tools/odrive/discovery.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/odrive/discovery.py b/tools/odrive/discovery.py index 18b5107a..fe4a37f0 100644 --- a/tools/odrive/discovery.py +++ b/tools/odrive/discovery.py @@ -53,6 +53,7 @@ def find_all(path, serial_number, printer("device responded on endpoint 0 with something that is not ASCII") return printer("JSON: " + json_string) + printer("JSON checksum: 0x{:02X} 0x{:02X}".format(json_crc16 & 0xff, (json_crc16 >> 8) & 0xff)) try: json_data = json.loads(json_string) except json.decoder.JSONDecodeError as error: From f3b03fb67cc793d23e7366211d4f09e6c837b0d8 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Thu, 19 Apr 2018 18:03:51 -0700 Subject: [PATCH 15/32] change meaning of "hardware_variant" from {0, 1} to voltage --- Firmware/Makefile | 11 ++++++----- Firmware/MotorControl/commands.cpp | 15 +++++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Firmware/Makefile b/Firmware/Makefile index f4b505dc..e1d5fc09 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -35,7 +35,7 @@ erase_config: # - product ID (01: ODrive) # - hardware major version # - hardware minor version -# - hardware variant (00: 24V, 01: 48V) +# - hardware variant (equal to the board nominal voltage) # Bits in the OTP can only ever be set to 0 but never back to 1. # Therefore do not try to run this command on the same board # twice with different data. @@ -57,9 +57,9 @@ ifeq ($(ODRV_FACTORY),TRUE) -c 'mwb 0x1fff7800 0xFE' -c 'sleep 10' \ -c 'mwb 0x1fff7801 0x01' -c 'sleep 10' \ -c 'mwb 0x1fff7802 0x01' -c 'sleep 10' \ - -c 'mwb 0x1fff7803 0x03' -c 'sleep 10' \ - -c 'mwb 0x1fff7804 0x04' -c 'sleep 10' \ - -c 'mwb 0x1fff7805 0x01' -c 'sleep 10' \ + -c 'mwb 0x1fff7803 3' -c 'sleep 10' \ + -c 'mwb 0x1fff7804 4' -c 'sleep 10' \ + -c 'mwb 0x1fff7805 48' -c 'sleep 10' \ -c 'reset run' \ -c exit @@ -72,7 +72,8 @@ else @echo " 1. open the Makefile and look at the write_otp target" @echo " 2. understand the structure of the OTP" @echo " 3. edit the bytes that are written to match your board version" - @echo "Run this command again, this time with ODRV_FACTORY=TRUE" + @echo "Run this command again, this time with ODRV_FACTORY=TRUE appended" + @echo "to the command in the terminal" endif clean: diff --git a/Firmware/MotorControl/commands.cpp b/Firmware/MotorControl/commands.cpp index c7a8ce95..4f8453d8 100644 --- a/Firmware/MotorControl/commands.cpp +++ b/Firmware/MotorControl/commands.cpp @@ -115,16 +115,23 @@ void enter_dfu_mode() { } #if HW_VERSION_MAJOR == 3 +// Determine start address of the OTP struct: +// The OTP is organized into 16-byte blocks. +// If the first block starts with "0xfe" we use the first block. +// If the first block starts with "0x00" and the second block starts with "0xfe", +// we use the second block. This gives the user the chance to screw up once. +// If none of the above is the case, we consider the OTP invalid (otp_ptr will be NULL). const uint8_t* otp_ptr = - *(uint8_t*)0x1fff7800 == 0xfe ? (uint8_t*)0x1fff7800 : - *(uint8_t*)0x1fff7800 != 0x00 ? NULL : - *(uint8_t*)0x1fff7810 == 0xfe ? (uint8_t*)0x1fff7810 : NULL; + (*(uint8_t*)FLASH_OTP_BASE == 0xfe) ? (uint8_t*)FLASH_OTP_BASE : + (*(uint8_t*)FLASH_OTP_BASE != 0x00) ? NULL : + (*(uint8_t*)(FLASH_OTP_BASE + 0x10) != 0xfe) ? NULL : + (uint8_t*)(FLASH_OTP_BASE + 0x10); // Read hardware version from OTP if available, otherwise fall back // to software defined version. const uint8_t board_version_major = otp_ptr ? otp_ptr[3] : HW_VERSION_MAJOR; const uint8_t board_version_minor = otp_ptr ? otp_ptr[4] : HW_VERSION_MINOR; -const uint8_t board_version_variant = otp_ptr ? otp_ptr[5] : (HW_VERSION_VOLTAGE == 24 ? 0 : 1); +const uint8_t board_version_variant = otp_ptr ? otp_ptr[5] : HW_VERSION_VOLTAGE; #else #error "not implemented" #endif From dee8efef22d3bf83cde641e5acaec5a367fc1f36 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Fri, 20 Apr 2018 21:14:14 -0700 Subject: [PATCH 16/32] initialize USB interrupt pump before initializing USB device --- Firmware/Board/v3/Src/freertos.c | 3 ++- Firmware/MotorControl/axis_c_interface.h | 14 -------------- Firmware/MotorControl/communication.cpp | 16 +++++++++------- Firmware/MotorControl/communication.h | 3 ++- 4 files changed, 13 insertions(+), 23 deletions(-) delete mode 100644 Firmware/MotorControl/axis_c_interface.h diff --git a/Firmware/Board/v3/Src/freertos.c b/Firmware/Board/v3/Src/freertos.c index b247994a..9d266640 100644 --- a/Firmware/Board/v3/Src/freertos.c +++ b/Firmware/Board/v3/Src/freertos.c @@ -53,7 +53,7 @@ /* USER CODE BEGIN Includes */ #include "freertos_vars.h" -#include "axis_c_interface.h" +#include "communication.h" int odrive_main(void); /* USER CODE END Includes */ @@ -126,6 +126,7 @@ void MX_FREERTOS_Init(void) { osSemaphoreDef(sem_usb_tx); sem_usb_tx = osSemaphoreCreate(osSemaphore(sem_usb_tx), 1); + init_deferred_interrupts(); /* USER CODE END RTOS_SEMAPHORES */ /* USER CODE BEGIN RTOS_TIMERS */ diff --git a/Firmware/MotorControl/axis_c_interface.h b/Firmware/MotorControl/axis_c_interface.h deleted file mode 100644 index f891e19b..00000000 --- a/Firmware/MotorControl/axis_c_interface.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __AXIS_C_INTERFACE_H -#define __AXIS_C_INTERFACE_H - -#ifdef __cplusplus -extern "C" { -#endif - -void axis_thread_entry(void const * temp_motor_ptr); - -#ifdef __cplusplus -} -#endif - -#endif /* __AXIS_C_INTERFACE_H */ diff --git a/Firmware/MotorControl/communication.cpp b/Firmware/MotorControl/communication.cpp index 87711595..ce1d2360 100644 --- a/Firmware/MotorControl/communication.cpp +++ b/Firmware/MotorControl/communication.cpp @@ -128,8 +128,8 @@ private: } uart4_stream_output; #if defined(UART_PROTOCOL_NATIVE) -PacketToStreamConverter uart4_packet_sender(uart4_stream_output); -BidirectionalPacketBasedChannel uart4_channel(endpoints, NUM_ENDPOINTS, uart4_packet_sender); +PacketToStreamConverter uart4_packet_output(uart4_stream_output); +BidirectionalPacketBasedChannel uart4_channel(uart4_packet_output); StreamToPacketConverter uart4_stream_input(uart4_channel); #endif @@ -145,16 +145,18 @@ void enter_dfu_mode() { NVIC_SystemReset(); } +void init_deferred_interrupts(void) { + // Start USB interrupt handler thread + osThreadDef(task_usb_pump, usb_deferred_interrupt_thread, osPriorityAboveNormal, 0, 512); + thread_usb_pump = osThreadCreate(osThread(task_usb_pump), NULL); +} + void init_communication(void) { printf("hi!\r\n"); // Start command handling thread osThreadDef(task_cmd_parse, communication_task, osPriorityNormal, 0, 5000 /* in 32-bit words */); // TODO: fix stack issues thread_cmd_parse = osThreadCreate(osThread(task_cmd_parse), NULL); - - // Start USB interrupt handler thread - osThreadDef(task_usb_pump, usb_update_thread, osPriorityAboveNormal, 0, 512); - thread_usb_pump = osThreadCreate(osThread(task_usb_pump), NULL); } @@ -304,7 +306,7 @@ void set_cmd_buffer(uint8_t *buf, uint32_t len) { usb_len = len; } -void usb_update_thread(void * ctx) { +void usb_deferred_interrupt_thread(void * ctx) { (void) ctx; // unused parameter for (;;) { diff --git a/Firmware/MotorControl/communication.h b/Firmware/MotorControl/communication.h index 88e37921..18522ffe 100644 --- a/Firmware/MotorControl/communication.h +++ b/Firmware/MotorControl/communication.h @@ -13,10 +13,11 @@ extern "C" { #endif +void init_deferred_interrupts(void); void init_communication(void); void communication_task(void * ctx); void set_cmd_buffer(uint8_t *buf, uint32_t len); -void usb_update_thread(void * ctx); +void usb_deferred_interrupt_thread(void * ctx); void USB_receive_packet(const uint8_t *buffer, size_t length); extern uint64_t serial_number; From abdb037d7502c12467fa66e601230dbed9406e6f Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Fri, 20 Apr 2018 21:20:33 -0700 Subject: [PATCH 17/32] move communication related files --- Firmware/Board/v3/Src/freertos.c | 2 +- Firmware/Board/v3/Src/main.c | 9 ++++----- Firmware/Board/v3/Src/usbd_cdc_if.c | 3 +-- Firmware/Board/v3/Src/usbd_desc.c | 2 +- Firmware/MotorControl/main.cpp | 2 +- Firmware/MotorControl/nvm_config.hpp | 2 +- Firmware/MotorControl/odrive_main.hpp | 2 +- Firmware/MotorControl/utils.h | 16 ---------------- Firmware/Tupfile.lua | 11 ++++++----- .../ascii_protocol.cpp | 0 .../ascii_protocol.h | 0 .../communication.cpp | 0 .../communication.h | 0 Firmware/{MotorControl => communication}/crc.hpp | 0 .../{MotorControl => communication}/protocol.cpp | 0 .../{MotorControl => communication}/protocol.hpp | 0 16 files changed, 16 insertions(+), 33 deletions(-) rename Firmware/{MotorControl => communication}/ascii_protocol.cpp (100%) rename Firmware/{MotorControl => communication}/ascii_protocol.h (100%) rename Firmware/{MotorControl => communication}/communication.cpp (100%) rename Firmware/{MotorControl => communication}/communication.h (100%) rename Firmware/{MotorControl => communication}/crc.hpp (100%) rename Firmware/{MotorControl => communication}/protocol.cpp (100%) rename Firmware/{MotorControl => communication}/protocol.hpp (100%) diff --git a/Firmware/Board/v3/Src/freertos.c b/Firmware/Board/v3/Src/freertos.c index 9d266640..0d178a56 100644 --- a/Firmware/Board/v3/Src/freertos.c +++ b/Firmware/Board/v3/Src/freertos.c @@ -53,7 +53,7 @@ /* USER CODE BEGIN Includes */ #include "freertos_vars.h" -#include "communication.h" +#include int odrive_main(void); /* USER CODE END Includes */ diff --git a/Firmware/Board/v3/Src/main.c b/Firmware/Board/v3/Src/main.c index 67c638df..74974bd9 100644 --- a/Firmware/Board/v3/Src/main.c +++ b/Firmware/Board/v3/Src/main.c @@ -59,8 +59,7 @@ #include "gpio.h" /* USER CODE BEGIN Includes */ -#include "utils.h" -#include "communication.h" +#include /* USER CODE END Includes */ /* Private variables ---------------------------------------------------------*/ @@ -138,9 +137,9 @@ int main(void) // This procedure of building a USB serial number should be identical // to the way the STM's built-in USB bootloader does it. This means // that the device will have the same serial number in normal and DFU mode. - uint32_t uuid0 = *(uint32_t *) (ID_UNIQUE_ADDRESS + 0); - uint32_t uuid1 = *(uint32_t *) (ID_UNIQUE_ADDRESS + 4); - uint32_t uuid2 = *(uint32_t *) (ID_UNIQUE_ADDRESS + 8); + uint32_t uuid0 = *(uint32_t *)(UID_BASE + 0); + uint32_t uuid1 = *(uint32_t *)(UID_BASE + 4); + uint32_t uuid2 = *(uint32_t *)(UID_BASE + 8); uint32_t uuid_mixed_part = uuid0 + uuid2; serial_number = ((uint64_t)uuid_mixed_part << 16) | (uint64_t)(uuid1 >> 16); diff --git a/Firmware/Board/v3/Src/usbd_cdc_if.c b/Firmware/Board/v3/Src/usbd_cdc_if.c index b500c6c4..0b5b1807 100644 --- a/Firmware/Board/v3/Src/usbd_cdc_if.c +++ b/Firmware/Board/v3/Src/usbd_cdc_if.c @@ -53,8 +53,7 @@ /* USER CODE BEGIN INCLUDE */ #include "cmsis_os.h" #include "freertos_vars.h" -#include "utils.h" -#include "communication.h" +#include #include /* USER CODE END INCLUDE */ diff --git a/Firmware/Board/v3/Src/usbd_desc.c b/Firmware/Board/v3/Src/usbd_desc.c index ea8584d8..fc9079d9 100644 --- a/Firmware/Board/v3/Src/usbd_desc.c +++ b/Firmware/Board/v3/Src/usbd_desc.c @@ -53,7 +53,7 @@ #include "usbd_conf.h" /* USER CODE BEGIN INCLUDE */ -#include "communication.h" +#include /* USER CODE END INCLUDE */ /* Private typedef -----------------------------------------------------------*/ diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index cd360578..aef0fd5e 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -1,7 +1,7 @@ #include "odrive_main.hpp" #include "nvm_config.hpp" -#include "communication.h" +#include BoardConfig_t board_config; EncoderConfig_t encoder_configs[AXIS_COUNT]; diff --git a/Firmware/MotorControl/nvm_config.hpp b/Firmware/MotorControl/nvm_config.hpp index 7784322b..bf0f9134 100644 --- a/Firmware/MotorControl/nvm_config.hpp +++ b/Firmware/MotorControl/nvm_config.hpp @@ -12,7 +12,7 @@ #include #include "nvm.h" -#include "crc.hpp" +#include /* Private defines -----------------------------------------------------------*/ diff --git a/Firmware/MotorControl/odrive_main.hpp b/Firmware/MotorControl/odrive_main.hpp index f1ebb703..058fbc7d 100644 --- a/Firmware/MotorControl/odrive_main.hpp +++ b/Firmware/MotorControl/odrive_main.hpp @@ -61,7 +61,7 @@ inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast(~static_c // ODrive specific includes -#include +#include #include #include #include diff --git a/Firmware/MotorControl/utils.h b/Firmware/MotorControl/utils.h index a4d7a6e0..550a51cb 100644 --- a/Firmware/MotorControl/utils.h +++ b/Firmware/MotorControl/utils.h @@ -8,11 +8,6 @@ extern "C" { #include -/** - * @brief Unique ID register address location - */ -#define ID_UNIQUE_ADDRESS (0x1FFF7A10) - /** * @brief Flash size register address */ @@ -59,17 +54,6 @@ extern "C" { */ #define STM_ID_GetFlashSize() (*(uint16_t *)(ID_FLASH_ADDRESS)) -/** - * "Returns" the given 32-bit value of the UUID. - * - * Parameters: - * - uint8_t x: - * Value between 0 and 2, corresponding to 4-bytes you want to read from 96bits (12bytes) - * - * Returned data is 32-bit - */ -#define STM_ID_GetUUID(x) ((x >= 0 && x < 3) ? (*(uint32_t *)(ID_UNIQUE_ADDRESS + 4 * (x))) : 0) - #ifdef M_PI #undef M_PI #endif diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index aa122ca5..87d6243e 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -117,7 +117,7 @@ for src in string.gmatch(vars['C_INCLUDES'] or '', "%S+") do end -- TODO: cleaner separation of the platform code and the rest -stm_includes += 'MotorControl' +stm_includes += '.' stm_includes += 'Drivers/DRV8301' stm_sources += boarddir..'/Src/syscalls.c' build{ @@ -137,21 +137,22 @@ build{ sources={ 'Drivers/DRV8301/drv8301.c', 'MotorControl/utils.c', - 'MotorControl/ascii_protocol.cpp', 'MotorControl/low_level.cpp', 'MotorControl/nvm.c', 'MotorControl/axis.cpp', - 'MotorControl/communication.cpp', - 'MotorControl/protocol.cpp', 'MotorControl/motor.cpp', 'MotorControl/encoder.cpp', 'MotorControl/controller.cpp', 'MotorControl/sensorless_estimator.cpp', 'MotorControl/main.cpp', + 'communication/communication.cpp', + 'communication/ascii_protocol.cpp', + 'communication/protocol.cpp', 'FreeRTOS-openocd.c' }, includes={ 'Drivers/DRV8301', - 'MotorControl' + 'MotorControl', + '.' } } diff --git a/Firmware/MotorControl/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp similarity index 100% rename from Firmware/MotorControl/ascii_protocol.cpp rename to Firmware/communication/ascii_protocol.cpp diff --git a/Firmware/MotorControl/ascii_protocol.h b/Firmware/communication/ascii_protocol.h similarity index 100% rename from Firmware/MotorControl/ascii_protocol.h rename to Firmware/communication/ascii_protocol.h diff --git a/Firmware/MotorControl/communication.cpp b/Firmware/communication/communication.cpp similarity index 100% rename from Firmware/MotorControl/communication.cpp rename to Firmware/communication/communication.cpp diff --git a/Firmware/MotorControl/communication.h b/Firmware/communication/communication.h similarity index 100% rename from Firmware/MotorControl/communication.h rename to Firmware/communication/communication.h diff --git a/Firmware/MotorControl/crc.hpp b/Firmware/communication/crc.hpp similarity index 100% rename from Firmware/MotorControl/crc.hpp rename to Firmware/communication/crc.hpp diff --git a/Firmware/MotorControl/protocol.cpp b/Firmware/communication/protocol.cpp similarity index 100% rename from Firmware/MotorControl/protocol.cpp rename to Firmware/communication/protocol.cpp diff --git a/Firmware/MotorControl/protocol.hpp b/Firmware/communication/protocol.hpp similarity index 100% rename from Firmware/MotorControl/protocol.hpp rename to Firmware/communication/protocol.hpp From 741a51442f0d5af225b17d0bb61d2f20264cefef Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Fri, 20 Apr 2018 23:44:55 -0700 Subject: [PATCH 18/32] split communication into multiple files --- Firmware/Board/v3/Inc/freertos_vars.h | 14 +- Firmware/Board/v3/Src/freertos.c | 33 ++- Firmware/Board/v3/Src/main.c | 3 +- Firmware/Board/v3/Src/usbd_cdc_if.c | 7 +- Firmware/Board/v3/Src/usbd_desc.c | 2 +- Firmware/MotorControl/axis.cpp | 2 +- Firmware/MotorControl/axis.hpp | 4 +- Firmware/MotorControl/board_config_v3.h | 27 +- Firmware/MotorControl/controller.cpp | 2 +- Firmware/MotorControl/controller.hpp | 4 +- Firmware/MotorControl/encoder.cpp | 3 +- Firmware/MotorControl/encoder.hpp | 4 +- Firmware/MotorControl/low_level.cpp | 2 +- Firmware/MotorControl/low_level.h | 4 +- Firmware/MotorControl/main.cpp | 4 +- Firmware/MotorControl/motor.cpp | 3 +- Firmware/MotorControl/motor.hpp | 4 +- .../{odrive_main.hpp => odrive_main.h} | 63 +++-- .../MotorControl/sensorless_estimator.cpp | 3 +- Firmware/Tupfile.lua | 2 + Firmware/communication/ascii_protocol.cpp | 2 +- Firmware/communication/ascii_protocol.h | 27 +- Firmware/communication/communication.cpp | 237 +----------------- Firmware/communication/communication.h | 7 - Firmware/communication/interface_uart.cpp | 101 ++++++++ Firmware/communication/interface_uart.h | 14 ++ Firmware/communication/interface_usb.cpp | 103 ++++++++ Firmware/communication/interface_usb.h | 17 ++ 28 files changed, 365 insertions(+), 333 deletions(-) rename Firmware/MotorControl/{odrive_main.hpp => odrive_main.h} (89%) create mode 100644 Firmware/communication/interface_uart.cpp create mode 100644 Firmware/communication/interface_uart.h create mode 100644 Firmware/communication/interface_usb.cpp create mode 100644 Firmware/communication/interface_usb.h diff --git a/Firmware/Board/v3/Inc/freertos_vars.h b/Firmware/Board/v3/Inc/freertos_vars.h index c1e122a0..2eb52d7d 100644 --- a/Firmware/Board/v3/Inc/freertos_vars.h +++ b/Firmware/Board/v3/Inc/freertos_vars.h @@ -3,15 +3,9 @@ #define __FREERTOS_H // List of semaphores -osSemaphoreId sem_usb_irq; -osSemaphoreId sem_uart_dma; -osSemaphoreId sem_usb_rx; -osSemaphoreId sem_usb_tx; - -// List of threads -osThreadId thread_motor_0; -osThreadId thread_motor_1; -osThreadId thread_cmd_parse; -osThreadId thread_usb_pump; +extern osSemaphoreId sem_usb_irq; +extern osSemaphoreId sem_uart_dma; +extern osSemaphoreId sem_usb_rx; +extern osSemaphoreId sem_usb_tx; #endif /* __FREERTOS_H */ \ No newline at end of file diff --git a/Firmware/Board/v3/Src/freertos.c b/Firmware/Board/v3/Src/freertos.c index 0d178a56..28ead46d 100644 --- a/Firmware/Board/v3/Src/freertos.c +++ b/Firmware/Board/v3/Src/freertos.c @@ -53,7 +53,8 @@ /* USER CODE BEGIN Includes */ #include "freertos_vars.h" -#include +#include "usb_device.h" +extern PCD_HandleTypeDef hpcd_USB_OTG_FS; int odrive_main(void); /* USER CODE END Includes */ @@ -63,11 +64,9 @@ osThreadId defaultTaskHandle; /* USER CODE BEGIN Variables */ // List of semaphores osSemaphoreId sem_usb_irq; - -// List of threads -osThreadId thread_motor_0; -osThreadId thread_motor_1; -osThreadId thread_cmd_parse; +osSemaphoreId sem_uart_dma; +osSemaphoreId sem_usb_rx; +osSemaphoreId sem_usb_tx; // Place FreeRTOS heap in core coupled memory for better performance __attribute__((section(".ccmram"))) @@ -94,6 +93,28 @@ __weak void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTask configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is called if a stack overflow is detected. */ } + +void usb_deferred_interrupt_thread(void * ctx) { + (void) ctx; // unused parameter + + for (;;) { + // Wait for signalling from USB interrupt (OTG_FS_IRQHandler) + osStatus semaphore_status = osSemaphoreWait(sem_usb_irq, osWaitForever); + if (semaphore_status == osOK) { + // We have a new incoming USB transmission: handle it + HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); + // Let the irq (OTG_FS_IRQHandler) fire again. + HAL_NVIC_EnableIRQ(OTG_FS_IRQn); + } + } +} + +void init_deferred_interrupts(void) { + // Start USB interrupt handler thread + osThreadDef(task_usb_pump, usb_deferred_interrupt_thread, osPriorityAboveNormal, 0, 512); + osThreadCreate(osThread(task_usb_pump), NULL); +} + /* USER CODE END 4 */ /* Init FreeRTOS */ diff --git a/Firmware/Board/v3/Src/main.c b/Firmware/Board/v3/Src/main.c index 74974bd9..bafa4d3f 100644 --- a/Firmware/Board/v3/Src/main.c +++ b/Firmware/Board/v3/Src/main.c @@ -59,7 +59,8 @@ #include "gpio.h" /* USER CODE BEGIN Includes */ -#include +#include +#include "freertos_vars.h" /* USER CODE END Includes */ /* Private variables ---------------------------------------------------------*/ diff --git a/Firmware/Board/v3/Src/usbd_cdc_if.c b/Firmware/Board/v3/Src/usbd_cdc_if.c index 0b5b1807..1a9c43c4 100644 --- a/Firmware/Board/v3/Src/usbd_cdc_if.c +++ b/Firmware/Board/v3/Src/usbd_cdc_if.c @@ -52,8 +52,7 @@ /* USER CODE BEGIN INCLUDE */ #include "cmsis_os.h" -#include "freertos_vars.h" -#include +#include #include /* USER CODE END INCLUDE */ @@ -291,9 +290,7 @@ static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length) static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) { /* USER CODE BEGIN 6 */ - - set_cmd_buffer(Buf, *Len); - osSemaphoreRelease(sem_usb_rx); + usb_process_packet(Buf, *Len); return (USBD_OK); /* USER CODE END 6 */ diff --git a/Firmware/Board/v3/Src/usbd_desc.c b/Firmware/Board/v3/Src/usbd_desc.c index fc9079d9..856d05e2 100644 --- a/Firmware/Board/v3/Src/usbd_desc.c +++ b/Firmware/Board/v3/Src/usbd_desc.c @@ -53,7 +53,7 @@ #include "usbd_conf.h" /* USER CODE BEGIN INCLUDE */ -#include +#include /* USER CODE END INCLUDE */ /* Private typedef -----------------------------------------------------------*/ diff --git a/Firmware/MotorControl/axis.cpp b/Firmware/MotorControl/axis.cpp index bde389d4..2877816d 100644 --- a/Firmware/MotorControl/axis.cpp +++ b/Firmware/MotorControl/axis.cpp @@ -4,7 +4,7 @@ #include "gpio.h" #include "utils.h" -#include "odrive_main.hpp" +#include "odrive_main.h" Axis::Axis(const AxisHardwareConfig_t& hw_config, AxisConfig_t& config, diff --git a/Firmware/MotorControl/axis.hpp b/Firmware/MotorControl/axis.hpp index 601b2367..2a49b92e 100644 --- a/Firmware/MotorControl/axis.hpp +++ b/Firmware/MotorControl/axis.hpp @@ -1,8 +1,8 @@ #ifndef __AXIS_HPP #define __AXIS_HPP -#ifndef __ODRIVE_MAIN_HPP -#error "This file should not be included directly. Include odrive_main.hpp instead." +#ifndef __ODRIVE_MAIN_H +#error "This file should not be included directly. Include odrive_main.h instead." #endif // Warning: Do not reorder these enum values. diff --git a/Firmware/MotorControl/board_config_v3.h b/Firmware/MotorControl/board_config_v3.h index 3e2d965d..791246de 100644 --- a/Firmware/MotorControl/board_config_v3.h +++ b/Firmware/MotorControl/board_config_v3.h @@ -20,25 +20,25 @@ #endif -struct AxisHardwareConfig_t { +typedef struct { GPIO_TypeDef* step_port; uint16_t step_pin; GPIO_TypeDef* dir_port; uint16_t dir_pin; osPriority thread_priority; -}; +} AxisHardwareConfig_t; -struct EncoderHardwareConfig_t { +typedef struct { TIM_HandleTypeDef* timer; GPIO_TypeDef* index_port; uint16_t index_pin; -}; -struct MotorHardwareConfig_t { +} EncoderHardwareConfig_t; +typedef struct { TIM_HandleTypeDef* timer; uint16_t control_deadline; float shunt_conductance; -}; -struct GateDriverHardwareConfig_t { +} MotorHardwareConfig_t; +typedef struct { SPI_HandleTypeDef* spi; GPIO_TypeDef* enable_port; uint16_t enable_pin; @@ -46,15 +46,18 @@ struct GateDriverHardwareConfig_t { uint16_t nCS_pin; GPIO_TypeDef* nFAULT_port; uint16_t nFAULT_pin; -}; -struct BoardHardwareConfig_t { +} GateDriverHardwareConfig_t; +typedef struct { AxisHardwareConfig_t axis_config; EncoderHardwareConfig_t encoder_config; MotorHardwareConfig_t motor_config; GateDriverHardwareConfig_t gate_driver_config; -}; +} BoardHardwareConfig_t; -const BoardHardwareConfig_t hw_configs[] = { { +extern const BoardHardwareConfig_t hw_configs[2]; + +#ifdef __MAIN_CPP__ +const BoardHardwareConfig_t hw_configs[2] = { { .axis_config = { .step_port = GPIO_1_GPIO_Port, .step_pin = GPIO_1_Pin, @@ -111,5 +114,7 @@ const BoardHardwareConfig_t hw_configs[] = { { .nFAULT_pin = nFAULT_Pin, } } }; +#endif + #endif // __BOARD_CONFIG_H diff --git a/Firmware/MotorControl/controller.cpp b/Firmware/MotorControl/controller.cpp index 3e1b5661..db3be18d 100644 --- a/Firmware/MotorControl/controller.cpp +++ b/Firmware/MotorControl/controller.cpp @@ -1,5 +1,5 @@ -#include "odrive_main.hpp" +#include "odrive_main.h" Controller::Controller(ControllerConfig_t& config) : diff --git a/Firmware/MotorControl/controller.hpp b/Firmware/MotorControl/controller.hpp index b5767b6f..5284cada 100644 --- a/Firmware/MotorControl/controller.hpp +++ b/Firmware/MotorControl/controller.hpp @@ -1,8 +1,8 @@ #ifndef __CONTROLLER_HPP #define __CONTROLLER_HPP -#ifndef __ODRIVE_MAIN_HPP -#error "This file should not be included directly. Include odrive_main.hpp instead." +#ifndef __ODRIVE_MAIN_H +#error "This file should not be included directly. Include odrive_main.h instead." #endif // Note: these should be sorted from lowest level of control to diff --git a/Firmware/MotorControl/encoder.cpp b/Firmware/MotorControl/encoder.cpp index fad27267..d6d44483 100644 --- a/Firmware/MotorControl/encoder.cpp +++ b/Firmware/MotorControl/encoder.cpp @@ -1,6 +1,5 @@ -//#include "encoder.hpp" -#include "odrive_main.hpp" +#include "odrive_main.h" Encoder::Encoder(const EncoderHardwareConfig_t& hw_config, diff --git a/Firmware/MotorControl/encoder.hpp b/Firmware/MotorControl/encoder.hpp index ae4b3c0e..6c456b28 100644 --- a/Firmware/MotorControl/encoder.hpp +++ b/Firmware/MotorControl/encoder.hpp @@ -1,8 +1,8 @@ #ifndef __ENCODER_HPP #define __ENCODER_HPP -#ifndef __ODRIVE_MAIN_HPP -#error "This file should not be included directly. Include odrive_main.hpp instead." +#ifndef __ODRIVE_MAIN_H +#error "This file should not be included directly. Include odrive_main.h instead." #endif struct EncoderConfig_t { diff --git a/Firmware/MotorControl/low_level.cpp b/Firmware/MotorControl/low_level.cpp index 98ac5620..41d58990 100644 --- a/Firmware/MotorControl/low_level.cpp +++ b/Firmware/MotorControl/low_level.cpp @@ -19,7 +19,7 @@ #include #include -#include "odrive_main.hpp" +#include "odrive_main.h" /* Private defines -----------------------------------------------------------*/ diff --git a/Firmware/MotorControl/low_level.h b/Firmware/MotorControl/low_level.h index 2bd9fe04..e3784788 100644 --- a/Firmware/MotorControl/low_level.h +++ b/Firmware/MotorControl/low_level.h @@ -2,8 +2,8 @@ #ifndef __LOW_LEVEL_H #define __LOW_LEVEL_H -#ifndef __ODRIVE_MAIN_HPP -#error "This file should not be included directly. Include odrive_main.hpp instead." +#ifndef __ODRIVE_MAIN_H +#error "This file should not be included directly. Include odrive_main.h instead." #endif #ifdef __cplusplus diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index aef0fd5e..f404bcc1 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -1,7 +1,7 @@ -#include "odrive_main.hpp" +#define __MAIN_CPP__ +#include "odrive_main.h" #include "nvm_config.hpp" -#include BoardConfig_t board_config; EncoderConfig_t encoder_configs[AXIS_COUNT]; diff --git a/Firmware/MotorControl/motor.cpp b/Firmware/MotorControl/motor.cpp index 0229e697..d508422e 100644 --- a/Firmware/MotorControl/motor.cpp +++ b/Firmware/MotorControl/motor.cpp @@ -2,8 +2,7 @@ #include #include "drv8301.h" -//#include "motor.hpp" -#include "odrive_main.hpp" +#include "odrive_main.h" Motor::Motor(const MotorHardwareConfig_t& hw_config, diff --git a/Firmware/MotorControl/motor.hpp b/Firmware/MotorControl/motor.hpp index b2a79f88..bdb8720d 100644 --- a/Firmware/MotorControl/motor.hpp +++ b/Firmware/MotorControl/motor.hpp @@ -1,8 +1,8 @@ #ifndef __MOTOR_HPP #define __MOTOR_HPP -#ifndef __ODRIVE_MAIN_HPP -#error "This file should not be included directly. Include odrive_main.hpp instead." +#ifndef __ODRIVE_MAIN_H +#error "This file should not be included directly. Include odrive_main.h instead." #endif #include "drv8301.h" diff --git a/Firmware/MotorControl/odrive_main.hpp b/Firmware/MotorControl/odrive_main.h similarity index 89% rename from Firmware/MotorControl/odrive_main.hpp rename to Firmware/MotorControl/odrive_main.h index 058fbc7d..10ce1d13 100644 --- a/Firmware/MotorControl/odrive_main.hpp +++ b/Firmware/MotorControl/odrive_main.h @@ -1,38 +1,25 @@ -#ifndef __ODRIVE_MAIN_HPP -#define __ODRIVE_MAIN_HPP +#ifndef __ODRIVE_MAIN_H +#define __ODRIVE_MAIN_H -// stdlib includes -#include - -// System includes -#include +#ifdef __cplusplus +extern "C" { +#endif // STM specific includes #include // Sets up the correct chip specifc defines required by arm_math #define ARM_MATH_CM4 // TODO: might change in future board versions #include +// OS includes +#include + // Hardware configuration #if HW_VERSION_MAJOR == 3 -#include +#include "board_config_v3.h" #else #error "unknown board version" #endif -// @brief general user configurable board configuration -struct BoardConfig_t { - bool enable_uart = true; - float brake_resistance = 0.47f; // [ohm] - float dc_bus_undervoltage_trip_level = 8.0f; //(~static_c #include #include #include +#include -// defined in main.cpp +#endif // __cplusplus + + +// general system functions defined in main.cpp void save_configuration(void); void erase_configuration(void); -#endif /* __ODRIVE_MAIN_HPP */ +#endif /* __ODRIVE_MAIN_H */ diff --git a/Firmware/MotorControl/sensorless_estimator.cpp b/Firmware/MotorControl/sensorless_estimator.cpp index 4ae081f4..1098b38c 100644 --- a/Firmware/MotorControl/sensorless_estimator.cpp +++ b/Firmware/MotorControl/sensorless_estimator.cpp @@ -1,6 +1,5 @@ -//#include "sensorless_estimator.hpp" -#include "odrive_main.hpp" +#include "odrive_main.h" SensorlessEstimator::SensorlessEstimator() { diff --git a/Firmware/Tupfile.lua b/Firmware/Tupfile.lua index 87d6243e..2d24ff2a 100644 --- a/Firmware/Tupfile.lua +++ b/Firmware/Tupfile.lua @@ -148,6 +148,8 @@ build{ 'communication/communication.cpp', 'communication/ascii_protocol.cpp', 'communication/protocol.cpp', + 'communication/interface_uart.cpp', + 'communication/interface_usb.cpp', 'FreeRTOS-openocd.c' }, includes={ diff --git a/Firmware/communication/ascii_protocol.cpp b/Firmware/communication/ascii_protocol.cpp index ad24b1b5..98f1cd02 100644 --- a/Firmware/communication/ascii_protocol.cpp +++ b/Firmware/communication/ascii_protocol.cpp @@ -7,7 +7,7 @@ /* Includes ------------------------------------------------------------------*/ -#include "odrive_main.hpp" +#include "odrive_main.h" #include "communication.h" #include "ascii_protocol.h" #include diff --git a/Firmware/communication/ascii_protocol.h b/Firmware/communication/ascii_protocol.h index 680dd9f3..82830e10 100644 --- a/Firmware/communication/ascii_protocol.h +++ b/Firmware/communication/ascii_protocol.h @@ -1,34 +1,21 @@ -#ifndef ASCII_PROTOCOL_H -#define ASCII_PROTOCOL_H - -#ifndef __ODRIVE_MAIN_HPP -#error "This file should not be included directly. Include odrive_main.hpp instead." -#endif +#ifndef __ASCII_PROTOCOL_H +#define __ASCII_PROTOCOL_H #ifdef __cplusplus extern "C" { #endif /* Includes ------------------------------------------------------------------*/ + +#include "protocol.hpp" + #include #include #include + /* Exported types ------------------------------------------------------------*/ - -typedef enum { - SERIAL_PRINTF_IS_NONE, - SERIAL_PRINTF_IS_USB, - SERIAL_PRINTF_IS_UART, -} SerialPrintf_t; - /* Exported constants --------------------------------------------------------*/ /* Exported variables --------------------------------------------------------*/ -extern SerialPrintf_t serial_printf_select; -// Exposed comms table during refactor transition -extern float* exposed_floats[]; -extern int* exposed_ints[]; -extern bool* exposed_bools[]; -extern uint16_t* exposed_uint16[]; /* Exported macro ------------------------------------------------------------*/ /* Exported functions --------------------------------------------------------*/ @@ -39,4 +26,4 @@ void ASCII_protocol_parse_stream(const uint8_t* buffer, size_t len, StreamSink& } #endif -#endif /* ASCII_PROTOCOL_H */ +#endif /* __ASCII_PROTOCOL_H */ diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index ce1d2360..618a6215 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -1,30 +1,23 @@ /* Includes ------------------------------------------------------------------*/ -// TODO: remove this option -// and once the legacy protocol is phased out, remove the seq-no hack in protocol.py -// todo: make clean switches for protocol -#define ENABLE_ASCII_PROTOCOL - #include "communication.h" -//#include "low_level.h" -#include "odrive_main.hpp" + +#include "interface_usb.h" +#include "interface_uart.h" + +#include "odrive_main.h" #include "protocol.hpp" #include "freertos_vars.h" #include "utils.h" -#ifdef ENABLE_ASCII_PROTOCOL -#include "ascii_protocol.h" -#endif #include #include -#include -#include -#include -#include - -#define UART_TX_BUFFER_SIZE 64 +//#include +//#include +//#include +//#include /* Private defines -----------------------------------------------------------*/ /* Private macros ------------------------------------------------------------*/ @@ -32,110 +25,11 @@ /* Global constant data ------------------------------------------------------*/ /* Global variables ----------------------------------------------------------*/ -extern PCD_HandleTypeDef hpcd_USB_OTG_FS; -extern USBD_HandleTypeDef hUsbDeviceFS; uint64_t serial_number; char serial_number_str[13]; // 12 digits + null termination /* Private constant data -----------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ - -static uint8_t* usb_buf; -static uint32_t usb_len; - -// FIXME: the stdlib doesn't know about CMSIS threads, so this is just a global variable -static thread_local uint32_t deadline_ms = 0; - - -#if !defined(USB_PROTOCOL_NONE) - -class USBSender : public PacketSink { -public: - int process_packet(const uint8_t* buffer, size_t length) { - // cannot send partial packets - if (length > USB_TX_DATA_SIZE) - return -1; - // wait for USB interface to become ready - if (osSemaphoreWait(sem_usb_tx, deadline_to_timeout(deadline_ms)) != osOK) - return -1; - // transmit packet - uint8_t status = CDC_Transmit_FS( - const_cast(buffer) /* casting this const away is safe because... - well... it's not actually. Stupid STM. */, length); - return (status == USBD_OK) ? 0 : -1; - } -} usb_packet_output; - -#if !defined(USB_PROTOCOL_NATIVE) -class TreatPacketSinkAsStreamSink : public StreamSink { -public: - TreatPacketSinkAsStreamSink(PacketSink& output) : output_(output) {} - int process_bytes(const uint8_t* buffer, size_t length) { - // Loop to ensure all bytes get sent - while (length) { - size_t chunk = length < USB_TX_DATA_SIZE ? length : USB_TX_DATA_SIZE; - if (output_.process_packet(buffer, length) != 0) - return -1; - buffer += chunk; - length -= chunk; - } - return 0; - } - size_t get_free_space() { return SIZE_MAX; } -private: - PacketSink& output_; -} usb_stream_output(usb_packet_output); -#endif - -#if defined(USB_PROTOCOL_NATIVE) -BidirectionalPacketBasedChannel usb_channel(usb_packet_output); -#elif defined(USB_PROTOCOL_NATIVE_STREAM_BASED) -PacketToStreamConverter usb_packetized_output(usb_stream_output); -BidirectionalPacketBasedChannel usb_channel(usb_packetized_output); -#endif - -#if defined(USB_PROTOCOL_NATIVE_STREAM_BASED) -StreamToPacketConverter usb_native_stream_input(usb_channel); -#endif - -#endif // !defined(USB_PROTOCOL_NONE) - - -#if !defined(UART_PROTOCOL_NONE) -class UART4Sender : public StreamSink { -public: - int process_bytes(const uint8_t* buffer, size_t length) { - // Loop to ensure all bytes get sent - while (length) { - size_t chunk = length < UART_TX_BUFFER_SIZE ? length : UART_TX_BUFFER_SIZE; - // wait for USB interface to become ready - // TODO: implement ring buffer to get a more continuous stream of data - if (osSemaphoreWait(sem_uart_dma, deadline_to_timeout(deadline_ms)) != osOK) - return -1; - // transmit chunk - memcpy(tx_buf_, buffer, chunk); - if (HAL_UART_Transmit_DMA(&huart4, tx_buf_, chunk) != HAL_OK) - return -1; - buffer += chunk; - length -= chunk; - } - return 0; - } - - size_t get_free_space() { return SIZE_MAX; } -private: - uint8_t tx_buf_[UART_TX_BUFFER_SIZE]; -} uart4_stream_output; - -#if defined(UART_PROTOCOL_NATIVE) -PacketToStreamConverter uart4_packet_output(uart4_stream_output); -BidirectionalPacketBasedChannel uart4_channel(uart4_packet_output); -StreamToPacketConverter uart4_stream_input(uart4_channel); -#endif - -#endif // !defined(UART_PROTOCOL_NONE) - - /* Private function prototypes -----------------------------------------------*/ /* Function implementations --------------------------------------------------*/ @@ -145,18 +39,12 @@ void enter_dfu_mode() { NVIC_SystemReset(); } -void init_deferred_interrupts(void) { - // Start USB interrupt handler thread - osThreadDef(task_usb_pump, usb_deferred_interrupt_thread, osPriorityAboveNormal, 0, 512); - thread_usb_pump = osThreadCreate(osThread(task_usb_pump), NULL); -} - void init_communication(void) { printf("hi!\r\n"); // Start command handling thread osThreadDef(task_cmd_parse, communication_task, osPriorityNormal, 0, 5000 /* in 32-bit words */); // TODO: fix stack issues - thread_cmd_parse = osThreadCreate(osThread(task_cmd_parse), NULL); + osThreadCreate(osThread(task_cmd_parse), NULL); } @@ -220,107 +108,12 @@ void communication_task(void * ctx) { set_application_endpoints(&endpoint_provider); comm_stack_info = uxTaskGetStackHighWaterMark(nullptr); -#if !defined(UART_PROTOCOL_NONE) - //DMA open loop continous circular buffer - //1ms delay periodic, chase DMA ptr around - - #define UART_RX_BUFFER_SIZE 64 - static uint8_t dma_circ_buffer[UART_RX_BUFFER_SIZE]; - - // DMA is set up to recieve in a circular buffer forever. - // We dont use interrupts to fetch the data, instead we periodically read - // data out of the circular buffer into a parse buffer, controlled by a state machine - HAL_UART_Receive_DMA(&huart4, dma_circ_buffer, sizeof(dma_circ_buffer)); - uint32_t last_rcv_idx = UART_RX_BUFFER_SIZE - huart4.hdmarx->Instance->NDTR; -#endif - - // Re-run state-machine forever - for (;;) { -#if !defined(UART_PROTOCOL_NONE) - // Check for UART errors and restart recieve DMA transfer if required - if (huart4.ErrorCode != HAL_UART_ERROR_NONE) { - HAL_UART_AbortReceive(&huart4); - HAL_UART_Receive_DMA(&huart4, dma_circ_buffer, sizeof(dma_circ_buffer)); - } - // Fetch the circular buffer "write pointer", where it would write next - uint32_t new_rcv_idx = UART_RX_BUFFER_SIZE - huart4.hdmarx->Instance->NDTR; - - deadline_ms = timeout_to_deadline(PROTOCOL_SERVER_TIMEOUT_MS); - // Process bytes in one or two chunks (two in case there was a wrap) - if (new_rcv_idx < last_rcv_idx) { -#if defined(UART_PROTOCOL_NATIVE) - uart4_stream_input.process_bytes(dma_circ_buffer + last_rcv_idx, - UART_RX_BUFFER_SIZE - last_rcv_idx); -#endif -#if defined(UART_PROTOCOL_ASCII) - ASCII_protocol_parse_stream(dma_circ_buffer + last_rcv_idx, - UART_RX_BUFFER_SIZE - last_rcv_idx, uart4_stream_output); -#endif - last_rcv_idx = 0; - } - if (new_rcv_idx > last_rcv_idx) { -#if defined(UART_PROTOCOL_NATIVE) - uart4_stream_input.process_bytes(dma_circ_buffer + last_rcv_idx, - new_rcv_idx - last_rcv_idx); -#endif -#if defined(UART_PROTOCOL_ASCII) - ASCII_protocol_parse_stream(dma_circ_buffer + last_rcv_idx, - new_rcv_idx - last_rcv_idx, uart4_stream_output); -#endif - last_rcv_idx = new_rcv_idx; - } -#endif - -#if !defined(USB_PROTOCOL_NONE) - // When we reach here, we are out of immediate characters to fetch out of UART buffer - // Now we check if there is any USB processing to do: we wait for up to 1 ms, - // before going back to checking UART again. - const uint32_t usb_check_timeout = 1; // ms - osStatus sem_stat = osSemaphoreWait(sem_usb_rx, usb_check_timeout); - if (sem_stat == osOK) { - deadline_ms = timeout_to_deadline(PROTOCOL_SERVER_TIMEOUT_MS); -#if defined(USB_PROTOCOL_NATIVE) - usb_channel.process_packet(usb_buf, usb_len); -#elif defined(USB_PROTOCOL_NATIVE_STREAM_BASED) - usb_native_stream_input.process_bytes(usb_buf, usb_len); -#elif defined(USB_PROTOCOL_ASCII) - ASCII_protocol_parse_stream(usb_buf, usb_len, usb_stream_output); -#endif - USBD_CDC_ReceivePacket(&hUsbDeviceFS); // Allow next packet - } -#endif - -#if defined(USB_PROTOCOL_NONE) && defined(UART_PROTOCOL_NONE) - osDelay(1); // don't starve other threads -#endif - } - - // If we get here, then this task is done - vTaskDelete(osThreadGetId()); -} - -// Called from CDC_Receive_FS callback function, this allows motor_parse_cmd to access the -// incoming USB data -void set_cmd_buffer(uint8_t *buf, uint32_t len) { - usb_buf = buf; - usb_len = len; -} - -void usb_deferred_interrupt_thread(void * ctx) { - (void) ctx; // unused parameter + serve_on_uart(); + serve_on_usb(); for (;;) { - // Wait for signalling from USB interrupt (OTG_FS_IRQHandler) - osStatus semaphore_status = osSemaphoreWait(sem_usb_irq, osWaitForever); - if (semaphore_status == osOK) { - // We have a new incoming USB transmission: handle it - HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); - // Let the irq (OTG_FS_IRQHandler) fire again. - HAL_NVIC_EnableIRQ(OTG_FS_IRQn); - } + osDelay(1000); // nothing to do } - - vTaskDelete(osThreadGetId()); } extern "C" { @@ -337,7 +130,3 @@ int _write(int file, const char* data, int len) { #endif return len; } - -void HAL_UART_TxCpltCallback(UART_HandleTypeDef* huart) { - osSemaphoreRelease(sem_uart_dma); -} diff --git a/Firmware/communication/communication.h b/Firmware/communication/communication.h index 18522ffe..9d1dff2e 100644 --- a/Firmware/communication/communication.h +++ b/Firmware/communication/communication.h @@ -13,15 +13,8 @@ extern "C" { #endif -void init_deferred_interrupts(void); void init_communication(void); void communication_task(void * ctx); -void set_cmd_buffer(uint8_t *buf, uint32_t len); -void usb_deferred_interrupt_thread(void * ctx); -void USB_receive_packet(const uint8_t *buffer, size_t length); - -extern uint64_t serial_number; -extern char serial_number_str[13]; #ifdef __cplusplus } diff --git a/Firmware/communication/interface_uart.cpp b/Firmware/communication/interface_uart.cpp new file mode 100644 index 00000000..d83442af --- /dev/null +++ b/Firmware/communication/interface_uart.cpp @@ -0,0 +1,101 @@ + +#include "interface_uart.h" +#include "protocol.hpp" + +#include "ascii_protocol.h" + +#include + +#include +#include +#include + +#define UART_TX_BUFFER_SIZE 64 +#define UART_RX_BUFFER_SIZE 64 + +// DMA open loop continous circular buffer +// 1ms delay periodic, chase DMA ptr around +static uint8_t dma_rx_buffer[UART_RX_BUFFER_SIZE]; +static uint32_t dma_last_rcv_idx; + +// FIXME: the stdlib doesn't know about CMSIS threads, so this is just a global variable +static thread_local uint32_t deadline_ms = 0; + + +class UART4Sender : public StreamSink { +public: + int process_bytes(const uint8_t* buffer, size_t length) { + // Loop to ensure all bytes get sent + while (length) { + size_t chunk = length < UART_TX_BUFFER_SIZE ? length : UART_TX_BUFFER_SIZE; + // wait for USB interface to become ready + // TODO: implement ring buffer to get a more continuous stream of data + if (osSemaphoreWait(sem_uart_dma, deadline_to_timeout(deadline_ms)) != osOK) + return -1; + // transmit chunk + memcpy(tx_buf_, buffer, chunk); + if (HAL_UART_Transmit_DMA(&huart4, tx_buf_, chunk) != HAL_OK) + return -1; + buffer += chunk; + length -= chunk; + } + return 0; + } + + size_t get_free_space() { return SIZE_MAX; } +private: + uint8_t tx_buf_[UART_TX_BUFFER_SIZE]; +} uart4_stream_output; + +PacketToStreamConverter uart4_packet_output(uart4_stream_output); +BidirectionalPacketBasedChannel uart4_channel(uart4_packet_output); +StreamToPacketConverter uart4_stream_input(uart4_channel); + +static void uart_server_thread(void * ctx) { + (void) ctx; + + for (;;) { + // Check for UART errors and restart recieve DMA transfer if required + if (huart4.ErrorCode != HAL_UART_ERROR_NONE) { + HAL_UART_AbortReceive(&huart4); + HAL_UART_Receive_DMA(&huart4, dma_rx_buffer, sizeof(dma_rx_buffer)); + } + // Fetch the circular buffer "write pointer", where it would write next + uint32_t new_rcv_idx = UART_RX_BUFFER_SIZE - huart4.hdmarx->Instance->NDTR; + + deadline_ms = timeout_to_deadline(PROTOCOL_SERVER_TIMEOUT_MS); + // Process bytes in one or two chunks (two in case there was a wrap) + if (new_rcv_idx < dma_last_rcv_idx) { + uart4_stream_input.process_bytes(dma_rx_buffer + dma_last_rcv_idx, + UART_RX_BUFFER_SIZE - dma_last_rcv_idx); + ASCII_protocol_parse_stream(dma_rx_buffer + dma_last_rcv_idx, + UART_RX_BUFFER_SIZE - dma_last_rcv_idx, uart4_stream_output); + dma_last_rcv_idx = 0; + } + if (new_rcv_idx > dma_last_rcv_idx) { + uart4_stream_input.process_bytes(dma_rx_buffer + dma_last_rcv_idx, + new_rcv_idx - dma_last_rcv_idx); + ASCII_protocol_parse_stream(dma_rx_buffer + dma_last_rcv_idx, + new_rcv_idx - dma_last_rcv_idx, uart4_stream_output); + dma_last_rcv_idx = new_rcv_idx; + } + + osDelay(1); + }; +} + +void serve_on_uart() { + // DMA is set up to recieve in a circular buffer forever. + // We dont use interrupts to fetch the data, instead we periodically read + // data out of the circular buffer into a parse buffer, controlled by a state machine + HAL_UART_Receive_DMA(&huart4, dma_rx_buffer, sizeof(dma_rx_buffer)); + dma_last_rcv_idx = UART_RX_BUFFER_SIZE - huart4.hdmarx->Instance->NDTR; + + // Start UART communication thread + osThreadDef(uart_server_thread_def, uart_server_thread, osPriorityNormal, 0, 512); + osThreadCreate(osThread(uart_server_thread_def), NULL); +} + +void HAL_UART_TxCpltCallback(UART_HandleTypeDef* huart) { + osSemaphoreRelease(sem_uart_dma); +} diff --git a/Firmware/communication/interface_uart.h b/Firmware/communication/interface_uart.h new file mode 100644 index 00000000..02c47331 --- /dev/null +++ b/Firmware/communication/interface_uart.h @@ -0,0 +1,14 @@ +#ifndef __INTERFACE_UART_HPP +#define __INTERFACE_UART_HPP + +#ifdef __cplusplus +extern "C" { +#endif + +void serve_on_uart(void); + +#ifdef __cplusplus +} +#endif + +#endif // __INTERFACE_UART_HPP diff --git a/Firmware/communication/interface_usb.cpp b/Firmware/communication/interface_usb.cpp new file mode 100644 index 00000000..0bca55c1 --- /dev/null +++ b/Firmware/communication/interface_usb.cpp @@ -0,0 +1,103 @@ + +#include "interface_usb.h" +#include "protocol.hpp" + +#include + +#include +#include +#include +#include +#include + +static uint8_t* usb_buf; +static uint32_t usb_len; + +// FIXME: the stdlib doesn't know about CMSIS threads, so this is just a global variable +static thread_local uint32_t deadline_ms = 0; + + + +class USBSender : public PacketSink { +public: + int process_packet(const uint8_t* buffer, size_t length) { + // cannot send partial packets + if (length > USB_TX_DATA_SIZE) + return -1; + // wait for USB interface to become ready + if (osSemaphoreWait(sem_usb_tx, deadline_to_timeout(deadline_ms)) != osOK) + return -1; + // transmit packet + uint8_t status = CDC_Transmit_FS( + const_cast(buffer) /* casting this const away is safe because... + well... it's not actually. Stupid STM. */, length); + return (status == USBD_OK) ? 0 : -1; + } +} usb_packet_output; + +#if !defined(USB_PROTOCOL_NATIVE) +class TreatPacketSinkAsStreamSink : public StreamSink { +public: + TreatPacketSinkAsStreamSink(PacketSink& output) : output_(output) {} + int process_bytes(const uint8_t* buffer, size_t length) { + // Loop to ensure all bytes get sent + while (length) { + size_t chunk = length < USB_TX_DATA_SIZE ? length : USB_TX_DATA_SIZE; + if (output_.process_packet(buffer, length) != 0) + return -1; + buffer += chunk; + length -= chunk; + } + return 0; + } + size_t get_free_space() { return SIZE_MAX; } +private: + PacketSink& output_; +} usb_stream_output(usb_packet_output); +#endif + +#if defined(USB_PROTOCOL_NATIVE) +BidirectionalPacketBasedChannel usb_channel(usb_packet_output); +#elif defined(USB_PROTOCOL_NATIVE_STREAM_BASED) +PacketToStreamConverter usb_packetized_output(usb_stream_output); +BidirectionalPacketBasedChannel usb_channel(usb_packetized_output); +#endif + +#if defined(USB_PROTOCOL_NATIVE_STREAM_BASED) +StreamToPacketConverter usb_native_stream_input(usb_channel); +#endif + + +static void usb_server_thread(void * ctx) { + (void) ctx; + + for (;;) { + const uint32_t usb_check_timeout = 1; // ms + osStatus sem_stat = osSemaphoreWait(sem_usb_rx, usb_check_timeout); + if (sem_stat == osOK) { + deadline_ms = timeout_to_deadline(PROTOCOL_SERVER_TIMEOUT_MS); +#if defined(USB_PROTOCOL_NATIVE) + usb_channel.process_packet(usb_buf, usb_len); +#elif defined(USB_PROTOCOL_NATIVE_STREAM_BASED) + usb_native_stream_input.process_bytes(usb_buf, usb_len); +#elif defined(USB_PROTOCOL_ASCII) + ASCII_protocol_parse_stream(usb_buf, usb_len, usb_stream_output); +#endif + USBD_CDC_ReceivePacket(&hUsbDeviceFS); // Allow next packet + } + } +} + +// Called from CDC_Receive_FS callback function, this allows the communication +// thread to handle the incoming data +void usb_process_packet(uint8_t *buf, uint32_t len) { + usb_buf = buf; + usb_len = len; + osSemaphoreRelease(sem_usb_rx); +} + +void serve_on_usb() { + // Start USB communication thread + osThreadDef(usb_server_thread_def, usb_server_thread, osPriorityNormal, 0, 512); + osThreadCreate(osThread(usb_server_thread_def), NULL); +} diff --git a/Firmware/communication/interface_usb.h b/Firmware/communication/interface_usb.h new file mode 100644 index 00000000..3602843f --- /dev/null +++ b/Firmware/communication/interface_usb.h @@ -0,0 +1,17 @@ +#ifndef __INTERFACE_USB_HPP +#define __INTERFACE_USB_HPP + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void usb_process_packet(uint8_t *buf, uint32_t len); +void serve_on_usb(void); + +#ifdef __cplusplus +} +#endif + +#endif // __INTERFACE_USB_HPP From 9d2b56998ff59f616ca64d3b8466f1e8632976f6 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 21 Apr 2018 00:13:28 -0700 Subject: [PATCH 19/32] Update protocol.py --- tools/odrive/protocol.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/odrive/protocol.py b/tools/odrive/protocol.py index c7305b19..aced6cc9 100644 --- a/tools/odrive/protocol.py +++ b/tools/odrive/protocol.py @@ -6,12 +6,12 @@ import sys import abc -if (sys.version_info[0], sys.version_info[1]) >= (3, 4): +if sys.version_info >= (3, 4): ABC = abc.ABC else: ABC = abc.ABCMeta('ABC', (), {}) -if (sys.version_info[0], sys.version_info[1]) <= (3, 3): +if sys.version_info < (3, 3): from monotonic import monotonic time.monotonic = monotonic From 98106a3dc9200cdf889216c8c3378aca35be7a4c Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 21 Apr 2018 14:33:54 -0700 Subject: [PATCH 20/32] add cppstandard --- Firmware/.vscode/c_cpp_properties.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index de6eb463..92b2b983 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -37,7 +37,9 @@ "C:/Program Files (x86)/GNU Tools ARM Embedded" ], "limitSymbolsToIncludedHeaders": true - } + }, + "cStandard": "c11", + "cppStandard": "c++14" }, { "name": "Linux", From 52be9da107793eddc1c1217de675eecfc583697e Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 21 Apr 2018 15:41:33 -0700 Subject: [PATCH 21/32] wait any structure more readable --- tools/odrive/dfu.py | 4 ++-- tools/odrive/utils.py | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/tools/odrive/dfu.py b/tools/odrive/dfu.py index aad9a3d8..339d2554 100755 --- a/tools/odrive/dfu.py +++ b/tools/odrive/dfu.py @@ -13,6 +13,7 @@ import array import fractions import usb.core import odrive.discovery +from odrive.utils import Event from odrive.dfuse import * try: @@ -244,8 +245,7 @@ def launch_dfu(args, app_shutdown_token): serial_number = args.serial_number - find_odrive_cancellation_token = threading.Event() - app_shutdown_token.subscribe(lambda: find_odrive_cancellation_token.set()) + find_odrive_cancellation_token = Event(app_shutdown_token) print("Waiting for ODrive...") diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 5834ff85..efc15acb 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -9,6 +9,7 @@ import threading import platform import subprocess import os +from odrive.utils import Event try: if platform.system() == 'Windows': @@ -33,7 +34,7 @@ def start_liveplotter(get_var_callback): import matplotlib.pyplot as plt - cancellation_token = threading.Event() + cancellation_token = Event() global vals vals = [] @@ -175,7 +176,7 @@ class Event(): handler() finally: self._mutex.release() - return lambda: self.unsubscribe(handler) + return handler def unsubscribe(self, handler): self._mutex.acquire() @@ -201,16 +202,16 @@ class Event(): def wait_any(*events, timeout=None): """ Blocks until any of the specified events are triggered. - Returns the number of the event that was triggerd or raises + Returns the index of the event that was triggerd or raises a TimeoutException """ or_event = threading.Event() - unsubscribe_functions = [] + subscriptions = [] for event in events: - unsubscribe_functions.append(event.subscribe(lambda: or_event.set())) + subscriptions.append((event, event.subscribe(lambda: or_event.set()))) or_event.wait(timeout=timeout) - for unsubscribe_function in unsubscribe_functions: - unsubscribe_function() + for event, sub in subscriptions: + event.unsubscribe(sub) for i in range(len(events)): if events[i].is_set(): return i From 9af85d544eb15dd4055ebd0a3e04cb8f19d8d1ba Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 21 Apr 2018 15:53:08 -0700 Subject: [PATCH 22/32] change ODRV_FACTORY to OTP_CONFIRM --- Firmware/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/Makefile b/Firmware/Makefile index d587105a..fb2a9115 100644 --- a/Firmware/Makefile +++ b/Firmware/Makefile @@ -46,7 +46,7 @@ erase_config: # FLASH_CR = (1 << FLASH_CR_PG); // unlock flash memory # [write OTP] write_otp: -ifeq ($(ODRV_FACTORY),TRUE) +ifeq ($(OTP_CONFIRM),TRUE) # Data: openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg \ -c init \ @@ -72,7 +72,7 @@ else @echo " 1. open the Makefile and look at the write_otp target" @echo " 2. understand the structure of the OTP" @echo " 3. edit the bytes that are written to match your board version" - @echo "Run this command again, this time with ODRV_FACTORY=TRUE" + @echo "Run this command again, this time with OTP_CONFIRM=TRUE" endif clean: From 89ee84ca398c5339a10f208d49c3a1bc8873c439 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 21 Apr 2018 17:44:25 -0700 Subject: [PATCH 23/32] releae with setuptools, fix some help text --- tools/odrive/shell.py | 2 +- tools/odrive/utils.py | 1 - tools/odrivetool | 20 +++++++++++--------- tools/setup.py | 10 +++++----- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/tools/odrive/shell.py b/tools/odrive/shell.py index af2e9432..2dceeba5 100644 --- a/tools/odrive/shell.py +++ b/tools/odrive/shell.py @@ -8,7 +8,7 @@ from odrive.enums import * # pylint: disable=W0614 def print_banner(): print('Please connect your ODrive.') - print('Type help() for help.') + print('You can also type help() or quit().') def print_help(args): print('') diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index efc15acb..82eba5db 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -9,7 +9,6 @@ import threading import platform import subprocess import os -from odrive.utils import Event try: if platform.system() == 'Windows': diff --git a/tools/odrivetool b/tools/odrivetool index 2feb9b2c..8bd4b524 100755 --- a/tools/odrivetool +++ b/tools/odrivetool @@ -81,22 +81,24 @@ else: logger = Logger(verbose=args.verbose) -print("ODrive control utility v" + odrive.__version__) -if ".dev" in odrive.__version__: - print("") - logger.warn("Developer Preview") - print(" If you find issues, please report them") - print(" on https://github.com/madcowswe/ODrive/issues") - print(" or better yet, submit a pull request to fix it.") - print("") +def print_version(): + print("ODrive control utility v" + odrive.__version__) app_shutdown_token = Event() try: if args.version == True: - pass + print_version() elif args.command == 'shell': + print_version() + if ".dev" in odrive.__version__: + print("") + logger.warn("Developer Preview") + print(" If you find issues, please report them") + print(" on https://github.com/madcowswe/ODrive/issues") + print(" or better yet, submit a pull request to fix it.") + print("") import odrive.shell odrive.shell.launch_shell(args, logger, printer, app_shutdown_token) diff --git a/tools/setup.py b/tools/setup.py index 85ddd5cd..946e6900 100644 --- a/tools/setup.py +++ b/tools/setup.py @@ -10,7 +10,7 @@ To build and package the python tools into a tar archive: python setup.py sdist Warning: Before you proceed, be aware that you can upload a -specific version only ever once. After that you need to increment +specific version only once ever. After that you need to increment the hotfix number. Deleting the release manually on the PyPi website does not help. @@ -19,10 +19,10 @@ Use TestPyPi while developing. To build, package and upload the python tools to TestPyPi, run: python setup.py sdist upload -r pypitest To make a real release ensure you're at the release commit -and then run the above command without the "test". +and then run the above command without the "test" (so just "pypi"). To install a prerelease version from test index: - sudo pip install --index-url https://test.pypi.org/simple/ --no-cache-dir odrive + sudo pip install --pre --index-url https://test.pypi.org/simple/ --no-cache-dir odrive PyPi access requires that you have set up ~/.pypirc with your @@ -32,7 +32,7 @@ to publish packages with the name odrive. # TODO: add additional y/n prompt to prevent from erroneous upload -from distutils.core import setup +from setuptools import setup import os import sys @@ -77,6 +77,7 @@ setup( url = 'https://github.com/madcowswe/ODrive', keywords = ['odrive', 'motor', 'motor control'], install_requires = [ + 'ipython', # Used to do the interactive parts of the odrivetool 'PyUSB', # Required to access USB devices from Python through libusb 'PySerial', # Required to access serial devices from Python 'IntelHex', # Used to by DFU to load firmware files @@ -84,7 +85,6 @@ setup( 'pywin32==222;platform_system=="Windows"' # Required for fancy terminal features on Windows ], package_data={'': ['version.txt']}, - include_package_data=True, classifiers = [], ) From 9af3a7135445009e0c27fba4caec6a5de1986be8 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 21 Apr 2018 17:45:42 -0700 Subject: [PATCH 24/32] Update CHANGELOG.md --- Firmware/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/CHANGELOG.md b/Firmware/CHANGELOG.md index 8d3d8b27..4f1c94ca 100644 --- a/Firmware/CHANGELOG.md +++ b/Firmware/CHANGELOG.md @@ -2,7 +2,7 @@ Please add a note of your changes below this heading if you make a Pull Request. ### Added - * `make write_otp` command to burn the board version onto the ODrive's one-time programmable memory. If you have an ODrive v3.4 or older, you should run this once for a better firmware update user experience in the future. Run the command without any options for more details. Once set, the board version is exposed through the `board_version_[...]` properties. + * `make write_otp` command to burn the board version onto the ODrive's one-time programmable memory. If you have an ODrive v3.4 or older, you should run this once for a better firmware update user experience in the future. Run the command without any options for more details. Once set, the board version is exposed through the `hw_version_[...]` properties. * bake Git-derived firmware version into firmware binary. The firmware version is exposed through the `fw_version_[...]` properties. * infrastructure to publish the python tools to PyPi. See `tools/setup.py` for details. From c0405e051a95dbda3b2370ff2a4ad0052f12a407 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 13 Apr 2018 17:17:24 -0700 Subject: [PATCH 25/32] split plotting behaviour across windows and notwindows --- tools/odrive/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 82eba5db..16f47fcf 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -67,8 +67,10 @@ def start_liveplotter(get_var_callback): while not cancellation_token.is_set(): plt.clf() plt.plot(vals) - #time.sleep(1/plot_rate) - fig.canvas.flush_events() + if platform.system() == "Windows": + plt.pause(1/plot_rate) + else: + fig.canvas.flush_events() threading.Thread(target=fetch_data).start() threading.Thread(target=plot_data).start() From 57acc2e26180f2e16fd273e6941c5e070f851a2d Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 21 Apr 2018 17:51:18 -0700 Subject: [PATCH 26/32] add cppstandard --- Firmware/.vscode/c_cpp_properties.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Firmware/.vscode/c_cpp_properties.json b/Firmware/.vscode/c_cpp_properties.json index de6eb463..92b2b983 100644 --- a/Firmware/.vscode/c_cpp_properties.json +++ b/Firmware/.vscode/c_cpp_properties.json @@ -37,7 +37,9 @@ "C:/Program Files (x86)/GNU Tools ARM Embedded" ], "limitSymbolsToIncludedHeaders": true - } + }, + "cStandard": "c11", + "cppStandard": "c++14" }, { "name": "Linux", From 9d884e59703cb3848425e1fd3014397dcca557c7 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 21 Apr 2018 18:17:50 -0700 Subject: [PATCH 27/32] Update CHANGELOG.md --- Firmware/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Firmware/CHANGELOG.md b/Firmware/CHANGELOG.md index 7b959fca..8131e200 100644 --- a/Firmware/CHANGELOG.md +++ b/Firmware/CHANGELOG.md @@ -25,6 +25,7 @@ Please add a note of your changes below this heading if you make a Pull Request. * Most of the code from `lowlevel.c` moved to `axis.cpp`, `encoder.cpp`, `controller.cpp`, `sensorless_estimator.cpp`, `motor.cpp` and the corresponding header files * Refactoring of the developer-facing communication protocol interface. See e.g. `axis.hpp` or `controller.hpp` for examples on how to add your own fields and functions * Change of the user-facing field paths. E.g. `my_odrive.motor0.pos_setpoint` is now at `my_odrive.axis0.controller.pos_setpoint`. Names are mostly unchanged. +* Rewrite of the top-level per-axis state-machine * The build is now configured using the `tup.config` file instead of editing source files. Make sure you set your board version correctly. See [here](README.md#configuring-the-build) for details. * The toplevel directory for tup is now `Firmware`. If you used tup before, go to `Firmware` and run `rm -rd ../.tup; rm -rd build/*; make`. * Update CubeMX generated STM platform code to version 1.19.0 From 80b3bab5530d0930fdd4974f264edef803f23047 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Sat, 21 Apr 2018 20:09:24 -0700 Subject: [PATCH 28/32] cancel receiver thread correctly on app shutdown --- tools/odrive/dfu.py | 2 +- tools/odrive/discovery.py | 13 ++++++++----- tools/odrive/protocol.py | 10 +++++----- tools/odrive/serial_transport.py | 6 ++++-- tools/odrive/shell.py | 1 + tools/odrive/usbbulk_transport.py | 5 +++-- tools/odrive/utils.py | 7 +++++-- tools/odrivetool | 18 ++++++++++++------ 8 files changed, 39 insertions(+), 23 deletions(-) diff --git a/tools/odrive/dfu.py b/tools/odrive/dfu.py index 339d2554..d1b59f82 100755 --- a/tools/odrive/dfu.py +++ b/tools/odrive/dfu.py @@ -251,7 +251,7 @@ def launch_dfu(args, app_shutdown_token): # Scan for ODrives not in DFU mode and put them into DFU mode once they appear # We only scan on USB because DFU is only possible over USB - odrive.discovery.find_all(args.path, serial_number, put_odrive_into_dfu_mode, find_odrive_cancellation_token) + odrive.discovery.find_all(args.path, serial_number, put_odrive_into_dfu_mode, find_odrive_cancellation_token, app_shutdown_token) # Poll libUSB until a device in DFU mode is found while not app_shutdown_token.is_set(): diff --git a/tools/odrive/discovery.py b/tools/odrive/discovery.py index fe4a37f0..a6536638 100644 --- a/tools/odrive/discovery.py +++ b/tools/odrive/discovery.py @@ -24,7 +24,8 @@ def noprint(text): def find_all(path, serial_number, did_discover_object_callback, - cancellation_token, printer=noprint): + search_cancellation_token, + channel_termination_token, printer=noprint): """ Starts scanning for ODrives that match the specified path spec and calls the callback for each ODrive that is found. @@ -75,21 +76,23 @@ def find_all(path, serial_number, the_rest = ':'.join(search_spec.split(':')[1:]) if prefix in channel_types: threading.Thread(target=channel_types[prefix], - args=(the_rest, serial_number, did_discover_channel, cancellation_token, printer)).start() + args=(the_rest, serial_number, did_discover_channel, search_cancellation_token, channel_termination_token, printer)).start() else: raise Exception("Invalid path spec \"{}\"".format(search_spec)) -def find_any(path="usb", serial_number=None, cancellation_token=None, timeout=None, printer=noprint): +def find_any(path="usb", serial_number=None, + search_cancellation_token=None, channel_termination_token=None, + timeout=None, printer=noprint): """ Blocks until the first matching ODrive is connected and then returns that device """ result = [ None ] - done_signal = Event(cancellation_token) + done_signal = Event(search_cancellation_token) def did_discover_object(obj): result[0] = obj done_signal.set() - find_all(path, serial_number, did_discover_object, done_signal, printer) + find_all(path, serial_number, did_discover_object, done_signal, channel_termination_token, printer) try: done_signal.wait(timeout=timeout) finally: diff --git a/tools/odrive/protocol.py b/tools/odrive/protocol.py index 566e1802..9ea1dd88 100644 --- a/tools/odrive/protocol.py +++ b/tools/odrive/protocol.py @@ -206,7 +206,7 @@ class Channel(PacketSink): _resend_timeout = 0.1 # [s] _send_attempts = 5 - def __init__(self, name, input, output, printer): + def __init__(self, name, input, output, cancellation_token, printer): """ Params: input: A PacketSource where this channel will source packets from on @@ -223,8 +223,8 @@ class Channel(PacketSink): self._expected_acks = {} self._responses = {} self._my_lock = threading.Lock() - self._channel_broken = Event() - self.start_receiver_thread(Event()) # TODO: use app_shutdown_token + self._channel_broken = Event(cancellation_token) + self.start_receiver_thread(Event(self._channel_broken)) # TODO: use app_shutdown_token def start_receiver_thread(self, cancellation_token): """ @@ -246,7 +246,7 @@ class Channel(PacketSink): # Process response # This should not throw an exception, otherwise the channel breaks self.process_packet(response) - print("receiver thread is exiting") + #print("receiver thread is exiting") except Exception: self._printer("receiver thread is exiting: " + traceback.format_exc()) finally: @@ -296,7 +296,7 @@ class Channel(PacketSink): self._my_lock.release() # Wait for ACK until the resend timeout is exceeded try: - if wait_any(ack_event, self._channel_broken, timeout=self._resend_timeout) != 0: + if wait_any(self._resend_timeout, ack_event, self._channel_broken) != 0: raise ChannelBrokenException() except odrive.utils.TimeoutException: attempt += 1 diff --git a/tools/odrive/serial_transport.py b/tools/odrive/serial_transport.py index dd595c6d..8a8b350a 100644 --- a/tools/odrive/serial_transport.py +++ b/tools/odrive/serial_transport.py @@ -6,6 +6,7 @@ PacketSource/PacketSink interfaces for serial ports. import os import re import time +import traceback import serial import serial.tools.list_ports import odrive.protocol @@ -53,10 +54,11 @@ def find_pyserial_ports(): return [x.device for x in serial.tools.list_ports.comports()] -def discover_channels(path, serial_number, callback, cancellation_token, printer): +def discover_channels(path, serial_number, callback, cancellation_token, channel_termination_token, printer): """ Scans for serial ports that match the path spec. This function blocks until cancellation_token is set. + Channels spawned by this function run until channel_termination_token is set. """ if path == None: # This regex should match all desired port names on macOS, @@ -86,7 +88,7 @@ def discover_channels(path, serial_number, callback, cancellation_token, printer output_stream = odrive.protocol.PacketToStreamConverter(serial_device) channel = odrive.protocol.Channel( "serial port {}@{}".format(port_name, ODRIVE_BAUDRATE), - input_stream, output_stream, printer) + input_stream, output_stream, channel_termination_token, printer) channel.serial_device = serial_device except serial.serialutil.SerialException: printer("Serial device init failed. Ignoring this port. More info: " + traceback.format_exc()) diff --git a/tools/odrive/shell.py b/tools/odrive/shell.py index 2dceeba5..8c441097 100644 --- a/tools/odrive/shell.py +++ b/tools/odrive/shell.py @@ -78,6 +78,7 @@ def launch_shell(args, logger, printer, app_shutdown_token): odrive.discovery.find_all(args.path, args.serial_number, lambda dev: did_discover_device(dev, logger, app_shutdown_token), app_shutdown_token, + app_shutdown_token, printer=printer) # Check if IPython is installed diff --git a/tools/odrive/usbbulk_transport.py b/tools/odrive/usbbulk_transport.py index fbfcfcee..2ed8ba11 100644 --- a/tools/odrive/usbbulk_transport.py +++ b/tools/odrive/usbbulk_transport.py @@ -139,10 +139,11 @@ class USBBulkTransport(odrive.protocol.PacketSource, odrive.protocol.PacketSink) return 64 -def discover_channels(path, serial_number, callback, cancellation_token, printer): +def discover_channels(path, serial_number, callback, cancellation_token, channel_termination_token, printer): """ Scans for USB devices that match the path spec. This function blocks until cancellation_token is set. + Channels spawned by this function run until channel_termination_token is set. """ if path == None or path == "": bus = None @@ -181,7 +182,7 @@ def discover_channels(path, serial_number, callback, cancellation_token, printer bulk_device.init() channel = odrive.protocol.Channel( "USB device bus {} device {}".format(usb_device.bus, usb_device.address), - bulk_device, bulk_device, printer) + bulk_device, bulk_device, channel_termination_token, printer) channel.usb_device = usb_device # for debugging only except usb.core.USBError as ex: if ex.errno == 13: diff --git a/tools/odrive/utils.py b/tools/odrive/utils.py index 16f47fcf..6d8c8cc1 100755 --- a/tools/odrive/utils.py +++ b/tools/odrive/utils.py @@ -144,7 +144,7 @@ class Event(): self._subscribers = [] self._mutex = threading.Lock() if not trigger is None: - trigger.subscribe(self.set()) + trigger.subscribe(lambda: self.set()) def is_set(self): return self._evt.is_set() @@ -170,6 +170,8 @@ class Event(): handler is invoked immediately. Returns a function that can be invoked to unsubscribe. """ + if handler is None: + raise TypeError self._mutex.acquire() try: self._subscribers.append(handler) @@ -200,11 +202,12 @@ class Event(): self.set() threading.Thread(target=delayed_trigger, daemon=True).start() -def wait_any(*events, timeout=None): +def wait_any(timeout=None, *events): """ Blocks until any of the specified events are triggered. Returns the index of the event that was triggerd or raises a TimeoutException + Param timeout: A timeout in seconds """ or_event = threading.Event() subscriptions = [] diff --git a/tools/odrivetool b/tools/odrivetool index 8bd4b524..a455df08 100755 --- a/tools/odrivetool +++ b/tools/odrivetool @@ -3,13 +3,22 @@ ODrive command line utility """ +from __future__ import print_function +import sys import argparse import odrive.discovery from odrive.utils import Logger, Event # Flush stdout by default -import functools -print = functools.partial(print, flush=True) +# Source: +# https://stackoverflow.com/questions/230751/how-to-flush-output-of-python-print +old_print = print +def print(*args, **kwargs): + kwargs.pop('flush', False) + old_print(*args, **kwargs) + file = kwargs.get('file', sys.stdout) + # Why might file=None? IDK, but it works for print(i, file=None) + file.flush() if file is not None else sys.stdout.flush() ## Parse arguments ## @@ -69,10 +78,6 @@ if args.command is None: args.command = 'shell' args.no_ipython = False -# We are interactively printing status messages, so flush by default -import functools -print = functools.partial(print, flush=True) - # TODO: deprecate printer - use logger instead if (args.verbose): printer = print @@ -103,6 +108,7 @@ try: odrive.shell.launch_shell(args, logger, printer, app_shutdown_token) elif args.command == 'dfu': + print_version() import odrive.dfu odrive.dfu.launch_dfu(args, app_shutdown_token) From f6265404942d58d2e54e1328320ad141eab02c49 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Sat, 21 Apr 2018 20:19:21 -0700 Subject: [PATCH 29/32] remove unused functions --- tools/odrive/usbbulk_transport.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tools/odrive/usbbulk_transport.py b/tools/odrive/usbbulk_transport.py index 2ed8ba11..5a3bd772 100644 --- a/tools/odrive/usbbulk_transport.py +++ b/tools/odrive/usbbulk_transport.py @@ -132,12 +132,6 @@ class USBBulkTransport(odrive.protocol.PacketSource, odrive.protocol.PacketSink) self._was_damaged = True raise odrive.protocol.ChannelDamagedException() - def send_max(self): - return 64 - - def receive_max(self): - return 64 - def discover_channels(path, serial_number, callback, cancellation_token, channel_termination_token, printer): """ From b1f9be531d04672047cadd15bbf0585af450367d Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Sat, 21 Apr 2018 20:22:30 -0700 Subject: [PATCH 30/32] request more bytes at a time when reading JSON --- tools/odrive/protocol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/odrive/protocol.py b/tools/odrive/protocol.py index 9ea1dd88..d8c49f3a 100644 --- a/tools/odrive/protocol.py +++ b/tools/odrive/protocol.py @@ -319,7 +319,7 @@ class Channel(PacketSink): # TODO: handle device that could (maliciously) send infinite stream buffer = bytes() while True: - chunk_length = 64 + chunk_length = 512 chunk = self.remote_endpoint_operation(endpoint_id, struct.pack(" Date: Sat, 21 Apr 2018 20:23:54 -0700 Subject: [PATCH 31/32] Update protocol.py --- tools/odrive/protocol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/odrive/protocol.py b/tools/odrive/protocol.py index d8c49f3a..4a4b3012 100644 --- a/tools/odrive/protocol.py +++ b/tools/odrive/protocol.py @@ -224,7 +224,7 @@ class Channel(PacketSink): self._responses = {} self._my_lock = threading.Lock() self._channel_broken = Event(cancellation_token) - self.start_receiver_thread(Event(self._channel_broken)) # TODO: use app_shutdown_token + self.start_receiver_thread(Event(self._channel_broken)) def start_receiver_thread(self, cancellation_token): """ From e581cc9c90ba4db9be7ac539895d234bea596519 Mon Sep 17 00:00:00 2001 From: Samuel Sadok Date: Sat, 21 Apr 2018 14:21:29 -0700 Subject: [PATCH 32/32] move enable_dfu_mode() to main.cpp --- Firmware/MotorControl/main.cpp | 6 ++++++ Firmware/MotorControl/odrive_main.h | 1 + Firmware/communication/communication.cpp | 6 ------ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Firmware/MotorControl/main.cpp b/Firmware/MotorControl/main.cpp index 6cf7ae62..b7ec03d5 100644 --- a/Firmware/MotorControl/main.cpp +++ b/Firmware/MotorControl/main.cpp @@ -53,6 +53,12 @@ void erase_configuration(void) { NVM_erase(); } +void enter_dfu_mode(void) { + __asm volatile ("CPSID I\n\t":::"memory"); // disable interrupts + _reboot_cookie = 0xDEADBEEF; + NVIC_SystemReset(); +} + extern "C" { int odrive_main(void); void vApplicationStackOverflowHook(void) { for(;;); } diff --git a/Firmware/MotorControl/odrive_main.h b/Firmware/MotorControl/odrive_main.h index ed28b832..a66fcb74 100644 --- a/Firmware/MotorControl/odrive_main.h +++ b/Firmware/MotorControl/odrive_main.h @@ -85,5 +85,6 @@ inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast(~static_c // general system functions defined in main.cpp void save_configuration(void); void erase_configuration(void); +void enter_dfu_mode(void); #endif /* __ODRIVE_MAIN_H */ diff --git a/Firmware/communication/communication.cpp b/Firmware/communication/communication.cpp index 37f4385b..27e08bfb 100644 --- a/Firmware/communication/communication.cpp +++ b/Firmware/communication/communication.cpp @@ -65,12 +65,6 @@ const uint8_t fw_version_unreleased = FW_VERSION_UNRELEASED; // 0 for official r /* Private function prototypes -----------------------------------------------*/ /* Function implementations --------------------------------------------------*/ -void enter_dfu_mode() { - __asm volatile ("CPSID I\n\t":::"memory"); // disable interrupts - _reboot_cookie = 0xDEADBEEF; - NVIC_SystemReset(); -} - void init_communication(void) { printf("hi!\r\n");