#ifndef __ODRIVE_MAIN_H #define __ODRIVE_MAIN_H // Hardware configuration #include #ifdef __cplusplus #include #include #include #include extern "C" { #endif // OS includes #include // extern const float elec_rad_per_enc; extern uint32_t _reboot_cookie; extern uint64_t serial_number; extern char serial_number_str[13]; #ifdef __cplusplus } typedef struct { bool fully_booted; uint32_t uptime; // [ms] uint32_t min_heap_space; // FreeRTOS heap [Bytes] uint32_t max_stack_usage_axis; // minimum remaining space since startup [Bytes] uint32_t max_stack_usage_usb; uint32_t max_stack_usage_uart; uint32_t max_stack_usage_startup; uint32_t max_stack_usage_can; uint32_t max_stack_usage_analog; uint32_t stack_size_axis; uint32_t stack_size_usb; uint32_t stack_size_uart; uint32_t stack_size_startup; uint32_t stack_size_can; uint32_t stack_size_analog; int32_t prio_axis; int32_t prio_usb; int32_t prio_uart; int32_t prio_startup; int32_t prio_can; int32_t prio_analog; USBStats_t& usb = usb_stats_; I2CStats_t& i2c = i2c_stats_; } SystemStats_t; struct PWMMapping_t { endpoint_ref_t endpoint = {0, 0}; float min = 0; float max = 0; }; // @brief general user configurable board configuration struct BoardConfig_t { ODriveIntf::GpioMode gpio_modes[GPIO_COUNT] = { DEFAULT_GPIO_MODES }; bool enable_uart_a = true; bool enable_uart_b = false; bool enable_uart_c = false; uint32_t uart_a_baudrate = 115200; uint32_t uart_b_baudrate = 115200; uint32_t uart_c_baudrate = 115200; bool enable_can_a = true; bool enable_i2c_a = false; ODriveIntf::StreamProtocolType uart0_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT; ODriveIntf::StreamProtocolType uart1_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT; ODriveIntf::StreamProtocolType uart2_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT; ODriveIntf::StreamProtocolType usb_cdc_protocol = ODriveIntf::STREAM_PROTOCOL_TYPE_ASCII_AND_STDOUT; float max_regen_current = 0.0f; float brake_resistance = DEFAULT_BRAKE_RESISTANCE; bool enable_brake_resistor = false; float dc_bus_undervoltage_trip_level = DEFAULT_MIN_DC_VOLTAGE; // brake_duty_cycle += 0% * vbus_voltage == dc_bus_overvoltage_ramp_end => brake_duty_cycle += 100% * * Remarks: * - This feature is active even when all motors are disarmed. * - This feature is disabled if `brake_resistance` is non-positive. */ bool enable_dc_bus_overvoltage_ramp = false; float dc_bus_overvoltage_ramp_start = 1.07f * HW_VERSION_VOLTAGE; //!< See `enable_dc_bus_overvoltage_ramp`. //!< Do not set this lower than your usual vbus_voltage, //!< unless you like fried brake resistors. float dc_bus_overvoltage_ramp_end = 1.07f * HW_VERSION_VOLTAGE; //!< See `enable_dc_bus_overvoltage_ramp`. //!< Must be larger than `dc_bus_overvoltage_ramp_start`, //!< otherwise the ramp feature is disabled. float dc_max_positive_current = INFINITY; // Max current [A] the power supply can source float dc_max_negative_current = -0.01f; // Max current [A] the power supply can sink. You most likely want a non-positive value here. Set to -INFINITY to disable. uint32_t error_gpio_pin = DEFAULT_ERROR_PIN; PWMMapping_t pwm_mappings[4]; PWMMapping_t analog_mappings[GPIO_COUNT]; }; struct TaskTimes { TaskTimer sampling; TaskTimer control_loop_misc; TaskTimer control_loop_checks; TaskTimer dc_calib_wait; }; // Forward Declarations class Axis; class Motor; // TODO: move // this is technically not thread-safe but practically it might be #define DEFINE_ENUM_FLAG_OPERATORS(ENUMTYPE) \ inline ENUMTYPE operator | (ENUMTYPE a, ENUMTYPE b) { return static_cast(static_cast>(a) | static_cast>(b)); } \ inline ENUMTYPE operator & (ENUMTYPE a, ENUMTYPE b) { return static_cast(static_cast>(a) & static_cast>(b)); } \ inline ENUMTYPE operator ^ (ENUMTYPE a, ENUMTYPE b) { return static_cast(static_cast>(a) ^ static_cast>(b)); } \ inline ENUMTYPE &operator |= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast(reinterpret_cast&>(a) |= static_cast>(b)); } \ inline ENUMTYPE &operator &= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast(reinterpret_cast&>(a) &= static_cast>(b)); } \ inline ENUMTYPE &operator ^= (ENUMTYPE &a, ENUMTYPE b) { return reinterpret_cast(reinterpret_cast&>(a) ^= static_cast>(b)); } \ inline ENUMTYPE operator ~ (ENUMTYPE a) { return static_cast(~static_cast>(a)); } #include "autogen/interfaces.hpp" // ODrive specific includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Defined in autogen/version.c based on git-derived version numbers extern "C" { extern const unsigned char fw_version_major_; extern const unsigned char fw_version_minor_; extern const unsigned char fw_version_revision_; extern const unsigned char fw_version_unreleased_; } static Stm32Gpio get_gpio(size_t gpio_num) { return (gpio_num < GPIO_COUNT) ? gpios[gpio_num] : GPIO_COUNT ? gpios[0] : Stm32Gpio::none; } // general system functions defined in main.cpp class ODrive : public ODriveIntf { public: bool save_configuration() override; void erase_configuration() override; void reboot() override { NVIC_SystemReset(); } void enter_dfu_mode() override; bool any_error(); void clear_errors() override; float get_adc_voltage(uint32_t gpio) override { return ::get_adc_voltage(get_gpio(gpio)); } int32_t test_function(int32_t delta) override { static int cnt = 0; return cnt += delta; } void do_fast_checks(); void sampling_cb(); void control_loop_cb(uint32_t timestamp); Axis& get_axis(int num) { return axes[num]; } uint32_t get_interrupt_status(int32_t irqn); uint32_t get_dma_status(uint8_t stream_num); uint32_t get_gpio_states(); uint64_t get_drv_fault(); void disarm_with_error(Error error); Error error_ = ERROR_NONE; float& vbus_voltage_ = ::vbus_voltage; // TODO: make this the actual variable float& ibus_ = ::ibus_; // TODO: make this the actual variable float ibus_report_filter_k_ = 1.0f; const uint64_t& serial_number_ = ::serial_number; // Hardware version is compared with OTP on startup to ensure that we're // running on the right board version. const uint8_t hw_version_major_ = HW_VERSION_MAJOR; const uint8_t hw_version_minor_ = HW_VERSION_MINOR; const uint8_t hw_version_variant_ = HW_VERSION_VOLTAGE; // the corresponding macros are defined in the autogenerated version.h const uint8_t fw_version_major_ = ::fw_version_major_; const uint8_t fw_version_minor_ = ::fw_version_minor_; const uint8_t fw_version_revision_ = ::fw_version_revision_; const uint8_t fw_version_unreleased_ = ::fw_version_unreleased_; // 0 for official releases, 1 otherwise bool& brake_resistor_armed_ = ::brake_resistor_armed; // TODO: make this the actual variable bool& brake_resistor_saturated_ = ::brake_resistor_saturated; // TODO: make this the actual variable float& brake_resistor_current_ = ::brake_resistor_current; SystemStats_t system_stats_; // Edit these to suit your capture needs Oscilloscope oscilloscope_{ nullptr, // trigger_src 0.5f, // trigger_threshold nullptr // data_src TODO: change data type }; ODriveCAN can_; BoardConfig_t config_; uint32_t user_config_loaded_ = 0; bool misconfigured_ = false; uint32_t test_property_ = 0; uint32_t last_update_timestamp_ = 0; uint32_t n_evt_sampling_ = 0; uint32_t n_evt_control_loop_ = 0; bool task_timers_armed_ = false; TaskTimes task_times_; const bool otp_valid_ = ((uint8_t*)FLASH_OTP_BASE)[0] != 0xff; }; extern ODrive odrv; // defined in main.cpp #endif // __cplusplus #endif /* __ODRIVE_MAIN_H */