diff --git a/firmware/c_board/app/src/utility/lazy.hpp b/firmware/c_board/app/src/utility/lazy.hpp index ae54b20..d9fd8de 100644 --- a/firmware/c_board/app/src/utility/lazy.hpp +++ b/firmware/c_board/app/src/utility/lazy.hpp @@ -11,6 +11,8 @@ namespace librmcs::firmware::utility { +// Lazy-initialized object with deferred construction. +// Thread-safe single-time initialization guarded by interrupt lock. template class Lazy { public: @@ -25,6 +27,11 @@ class Lazy { constexpr ~Lazy() {} // No need to deconstruct + /*! + * @brief Construct the object on first call; no-op on subsequent calls. + * @return Reference to the constructed object + * @note Thread-safe. Uses interrupt lock for atomic state transitions. + */ constexpr T& init() { const InterruptLockGuard guard; @@ -44,16 +51,46 @@ class Lazy { return object_; } + /*! + * @brief Get pointer to the constructed object, or nullptr if not yet + * initialized. + * @return Pointer to the object, or nullptr if not yet initialized + * @note Does not assert on uninitialized state. Check return before use. + */ + constexpr T* try_get() { + if (!static_cast(*this)) + return nullptr; + return std::addressof(object_); + } + + /*! + * @brief Get pointer to the constructed object + * @return Pointer to the constructed object + * @warning Must only be called after init(). Asserts if uninitialized + * in debug builds. + */ constexpr T* get() { core::utility::assert_debug(static_cast(*this)); return std::addressof(object_); } + /*! + * @brief Member access through the constructed object + * @return Pointer to the constructed object + * @warning Must only be called after init(). Asserts if uninitialized + * in debug builds. + */ constexpr T* operator->() { core::utility::assert_debug(static_cast(*this)); return std::addressof(object_); } + /*! + * @brief Dereference to access the constructed object + * @return Reference to the constructed object + * @warning Must only be called after init(). Asserts if uninitialized + * in debug builds. + */ constexpr T& operator*() { core::utility::assert_debug(static_cast(*this)); return object_; diff --git a/firmware/rmcs_board/app/src/app.cpp b/firmware/rmcs_board/app/src/app.cpp index 469bb04..f393095 100644 --- a/firmware/rmcs_board/app/src/app.cpp +++ b/firmware/rmcs_board/app/src/app.cpp @@ -6,8 +6,10 @@ #include "firmware/rmcs_board/app/src/can/can.hpp" #include "firmware/rmcs_board/app/src/gpio/gpio.hpp" +#include "firmware/rmcs_board/app/src/led/led.hpp" #include "firmware/rmcs_board/app/src/spi/bmi088/accel.hpp" #include "firmware/rmcs_board/app/src/spi/bmi088/gyro.hpp" +#include "firmware/rmcs_board/app/src/timer/tick.hpp" #include "firmware/rmcs_board/app/src/uart/uart.hpp" #include "firmware/rmcs_board/app/src/usb/vendor.hpp" #include "firmware/rmcs_board/app/src/utility/boot_mailbox.hpp" @@ -25,6 +27,8 @@ App::App() { dma_mgr_init(); boot::BootMailbox::clear(); + led::led.init(); + usb::vendor.init(); for (auto& can : can::can_array) @@ -38,6 +42,8 @@ App::App() { spi::bmi088::accelerometer.init(); spi::bmi088::gyroscope.init(); + + timer::tick.init(); } // Non-static to ensure instantiation diff --git a/firmware/rmcs_board/app/src/can/can.hpp b/firmware/rmcs_board/app/src/can/can.hpp index 82c7f18..cef7c17 100644 --- a/firmware/rmcs_board/app/src/can/can.hpp +++ b/firmware/rmcs_board/app/src/can/can.hpp @@ -18,6 +18,7 @@ #include "core/src/protocol/serializer.hpp" #include "core/src/utility/assert.hpp" #include "core/src/utility/immovable.hpp" +#include "firmware/rmcs_board/app/src/led/led.hpp" #include "firmware/rmcs_board/app/src/usb/helper.hpp" #include "firmware/rmcs_board/app/src/utility/lazy.hpp" @@ -78,7 +79,9 @@ class Can : private core::utility::Immovable { if (!data.can_data.empty()) std::memcpy(frame.data_8, data.can_data.data(), data.can_data.size()); - mcan_transmit_via_txfifo_nonblocking(can_base_, &frame, nullptr); + const hpm_stat_t status = mcan_transmit_via_txfifo_nonblocking(can_base_, &frame, nullptr); + if (status != status_success) + led::led->downlink_buffer_full(); } void handle_uplink(core::protocol::FieldId field_id, core::protocol::Serializer& serializer) { diff --git a/firmware/rmcs_board/app/src/gpio/analog_gpio_pin.hpp b/firmware/rmcs_board/app/src/gpio/analog_gpio_pin.hpp index 08f0935..7ea6e7d 100644 --- a/firmware/rmcs_board/app/src/gpio/analog_gpio_pin.hpp +++ b/firmware/rmcs_board/app/src/gpio/analog_gpio_pin.hpp @@ -30,7 +30,13 @@ class AnalogGpioPin : public GpioPin { core::utility::assert_debug(pwm_ioc_function < 32 && pwm_output_index < 8); } - [[nodiscard]] constexpr bool supports_pwm() const noexcept { return pwm_base_ != 0; } + constexpr uintptr_t pwm_base() const { return pwm_base_; } + constexpr uint32_t pwm_ioc_function() const { return pwm_ioc_function_; } + constexpr uint8_t pwm_output_index() const { return pwm_output_index_; } + + PWM_Type* pwm_instance() const { return reinterpret_cast(pwm_base_); } + + [[nodiscard]] constexpr bool supports_pwm() const noexcept { return pwm_base() != 0; } void configure_as_gpio() const { configure_controller(); @@ -40,7 +46,7 @@ class AnalogGpioPin : public GpioPin { void configure_as_pwm() const { core::utility::assert_debug(supports_pwm()); configure_controller(); - configure_ioc_function(pwm_ioc_function_); + configure_ioc_function(pwm_ioc_function()); } void disable_interrupt() const { @@ -50,7 +56,7 @@ class AnalogGpioPin : public GpioPin { void update_pwm_compare_edge_aligned(uint32_t compare_value) const { core::utility::assert_debug(supports_pwm()); - pwm_update_raw_cmp_edge_aligned(pwm_instance(), pwm_output_index_, compare_value); + pwm_update_raw_cmp_edge_aligned(pwm_instance(), pwm_output_index(), compare_value); } hpm_stat_t setup_pwm_waveform_edge_aligned(uint32_t reload, pwm_config_t& pwm_config) const { @@ -61,12 +67,10 @@ class AnalogGpioPin : public GpioPin { cmp_config.cmp = reload + 1; return pwm_setup_waveform( - pwm_instance(), pwm_output_index_, &pwm_config, pwm_output_index_, &cmp_config, 1); + pwm_instance(), pwm_output_index(), &pwm_config, pwm_output_index(), &cmp_config, 1); } private: - PWM_Type* pwm_instance() const { return reinterpret_cast(pwm_base_); } - uintptr_t pwm_base_; uint8_t pwm_ioc_function_; diff --git a/firmware/rmcs_board/app/src/led/led.hpp b/firmware/rmcs_board/app/src/led/led.hpp new file mode 100644 index 0000000..58f5e2b --- /dev/null +++ b/firmware/rmcs_board/app/src/led/led.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include + +#include "firmware/rmcs_board/app/src/led/ws2812.hpp" +#include "firmware/rmcs_board/app/src/utility/lazy.hpp" + +namespace librmcs::firmware::led { + +class Led { +public: + using Lazy = utility::Lazy; + + Led() { ws2812.init(); } + + void reset() { + uplink_full_reset_counter_.store(0, std::memory_order::relaxed); + downlink_full_reset_counter_.store(0, std::memory_order::relaxed); + } + + void uplink_buffer_full() { + uplink_full_reset_counter_.store(5000, std::memory_order::relaxed); + } + + void downlink_buffer_full() { + downlink_full_reset_counter_.store(5000, std::memory_order::relaxed); + } + + void update(uint32_t tick) { + uint16_t uplink_full; + do { + uplink_full = uplink_full_reset_counter_.load(std::memory_order::relaxed); + if (uplink_full == 0) + break; + } while (!uplink_full_reset_counter_.compare_exchange_weak( + uplink_full, uplink_full - 1, std::memory_order::relaxed)); + + uint16_t downlink_full; + do { + downlink_full = downlink_full_reset_counter_.load(std::memory_order::relaxed); + if (downlink_full == 0) + break; + } while (!downlink_full_reset_counter_.compare_exchange_weak( + downlink_full, downlink_full - 1, std::memory_order::relaxed)); + + if (uplink_full && downlink_full) { + if (tick & 128U) + ws2812->set_value(255, 255, 0); + else + ws2812->set_value(0, 255, 255); + } else if (uplink_full) { + if (tick & 128U) + ws2812->set_value(255, 255, 0); + else + ws2812->set_value(0, 0, 0); + } else if (downlink_full) { + if (tick & 128U) + ws2812->set_value(0, 0, 0); + else + ws2812->set_value(0, 255, 255); + } else { + uint32_t brightness = (tick >> 2U) & 511U; + if (brightness > 255U) + brightness = 511U - brightness; + ws2812->set_value(0, static_cast(brightness), 0); + } + } + +private: + std::atomic uplink_full_reset_counter_{0}; + std::atomic downlink_full_reset_counter_{0}; +}; + +inline constinit Led::Lazy led; + +} // namespace librmcs::firmware::led diff --git a/firmware/rmcs_board/app/src/led/ws2812.hpp b/firmware/rmcs_board/app/src/led/ws2812.hpp new file mode 100644 index 0000000..8fa22c8 --- /dev/null +++ b/firmware/rmcs_board/app/src/led/ws2812.hpp @@ -0,0 +1,159 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "board_app.hpp" +#include "core/src/utility/assert.hpp" +#include "core/src/utility/immovable.hpp" +#include "firmware/rmcs_board/app/src/gpio/analog_gpio_pin.hpp" +#include "firmware/rmcs_board/app/src/utility/lazy.hpp" + +namespace librmcs::firmware::led { + +class Ws2812 : private core::utility::Immovable { +public: + using Lazy = utility::Lazy; + + static constexpr size_t kWs2812BitCount = 24; + static constexpr uint32_t kWs2812PwmFrequencyHz = 800'000; + + static_assert(board::kWs2812Pin.supports_pwm()); + static_assert(board::kWs2812Pin.pwm_base() == HPM_PWM1_BASE); + + explicit Ws2812() { + board::init_ws2812_pin(); + + clock_add_to_group(clock_mot0, 0); + const uint32_t pwm_clock_hz = clock_get_frequency(clock_mot0); + core::utility::assert_always(pwm_clock_hz >= kWs2812PwmFrequencyHz); + + const uint32_t pwm_reload = pwm_clock_hz / kWs2812PwmFrequencyHz; + core::utility::assert_always(pwm_reload > 1); + + compare_bit0_ = static_cast(static_cast(pwm_reload) * 8 / 25); + compare_bit1_ = static_cast(static_cast(pwm_reload) * 16 / 25); + compare_reset_ = pwm_reload; + compare_values_[0] = PWM_CMP_CMP_SET(compare_reset_); + compare_values_[kWs2812BitCount + 1] = PWM_CMP_CMP_SET(compare_reset_); + + pwm_deinit(HPM_PWM1); + pwm_set_reload(HPM_PWM1, 0, pwm_reload); + pwm_set_start_count(HPM_PWM1, 0, 0); + + pwm_config_t pwm_config{}; + pwm_get_default_pwm_config(HPM_PWM1, &pwm_config); + pwm_config.enable_output = true; + pwm_config.invert_output = true; + + pwm_cmp_config_t pwm_cmp_config{}; + pwm_get_default_cmp_config(HPM_PWM1, &pwm_cmp_config); + pwm_cmp_config.cmp = compare_reset_; + pwm_cmp_config.update_trigger = pwm_shadow_register_update_on_modify; + pwm_config_cmp(HPM_PWM1, board::kWs2812Pin.pwm_output_index(), &pwm_cmp_config); + + pwm_cmp_config.update_trigger = pwm_shadow_register_update_on_hw_event; + core::utility::assert_always( + pwm_setup_waveform( + HPM_PWM1, board::kWs2812Pin.pwm_output_index(), &pwm_config, + board::kWs2812Pin.pwm_output_index(), &pwm_cmp_config, 1) + == status_success); + + pwm_cmp_config.cmp = pwm_reload - 1; + pwm_cmp_config.update_trigger = pwm_shadow_register_update_on_modify; + core::utility::assert_always( + pwm_load_cmp_shadow_on_match(HPM_PWM1, 8, &pwm_cmp_config) == status_success); + + init_dma(); + + trgm_dma_request_config(HPM_TRGM0, TRGM_DMACFG_0, HPM_TRGM0_DMA_SRC_PWM1_HALFRLD); + pwm_enable_dma_request(HPM_PWM1, PWM_IRQ_HALF_RELOAD); + + pwm_start_counter(HPM_PWM1); + pwm_issue_shadow_register_lock_event(HPM_PWM1); + } + + bool set_value(uint8_t red, uint8_t green, uint8_t blue) { + // The LED is too bright; reduce the brightness. + red /= 8; + green /= 8; + blue /= 8; + + if (dma_channel_is_enable(dma_resource_.base, dma_resource_.channel)) + return false; + + const uint32_t grb = (static_cast(green) << 16) + | (static_cast(red) << 8) | static_cast(blue); + + for (size_t i = 0; i < kWs2812BitCount; i++) { + const bool is_one = (grb & (1UL << (kWs2812BitCount - 1 - i))) != 0; + compare_values_[i + 1] = PWM_CMP_CMP_SET(is_one ? compare_bit1_ : compare_bit0_); + } + + l1c_dc_writeback( + reinterpret_cast(compare_values_.data()), + HPM_L1C_CACHELINE_ALIGN_UP(sizeof(compare_values_))); + + auto& ctrl = dma_resource_.base->CHCTRL[dma_resource_.channel]; + ctrl.SRCADDR = reinterpret_cast(compare_values_.data()); + ctrl.TRANSIZE = compare_values_.size(); + ctrl.CTRL |= DMAV2_CHCTRL_CTRL_ENABLE_MASK; + + return true; + } + +private: + void init_dma() { + dma_mgr_chn_conf_t config; + dma_mgr_get_default_chn_config(&config); + + config.en_dmamux = true; + config.dmamux_src = HPM_DMA_SRC_MOT_0; + config.priority = DMA_MGR_CHANNEL_PRIORITY_LOW; + config.src_addr = reinterpret_cast(compare_values_.data()); + config.dst_addr = + reinterpret_cast(&HPM_PWM1->CMP[board::kWs2812Pin.pwm_output_index()]); + config.src_width = DMA_MGR_TRANSFER_WIDTH_WORD; + config.dst_width = DMA_MGR_TRANSFER_WIDTH_WORD; + config.src_addr_ctrl = DMA_MGR_ADDRESS_CONTROL_INCREMENT; + config.dst_addr_ctrl = DMA_MGR_ADDRESS_CONTROL_FIXED; + config.src_mode = DMA_MGR_HANDSHAKE_MODE_NORMAL; + config.dst_mode = DMA_MGR_HANDSHAKE_MODE_HANDSHAKE; + config.src_burst_size = DMA_MGR_NUM_TRANSFER_PER_BURST_1T; + config.size_in_byte = sizeof(compare_values_); + config.en_infiniteloop = false; + config.interrupt_mask = DMA_MGR_INTERRUPT_MASK_ALL; + + core::utility::assert_always( + dma_mgr_request_specified_resource(&dma_resource_, HPM_HDMA) == status_success + && dma_mgr_setup_channel(&dma_resource_, &config) == status_success); + } + + dma_resource_t dma_resource_{}; + + uint32_t compare_bit0_ = 0; + uint32_t compare_bit1_ = 0; + uint32_t compare_reset_ = 0; + + alignas(HPM_L1C_CACHELINE_SIZE) std::array compare_values_; +}; + +inline constinit Ws2812::Lazy ws2812; + +} // namespace librmcs::firmware::led diff --git a/firmware/rmcs_board/app/src/timer/tick.cpp b/firmware/rmcs_board/app/src/timer/tick.cpp new file mode 100644 index 0000000..21c7ddc --- /dev/null +++ b/firmware/rmcs_board/app/src/timer/tick.cpp @@ -0,0 +1,9 @@ +#include "firmware/rmcs_board/app/src/timer/tick.hpp" + +#include "board_app.hpp" + +namespace librmcs::firmware::board { + +void tick_clock_irq_handler() { timer::tick->irq_handler(); } + +} // namespace librmcs::firmware::board diff --git a/firmware/rmcs_board/app/src/timer/tick.hpp b/firmware/rmcs_board/app/src/timer/tick.hpp new file mode 100644 index 0000000..1f8ac26 --- /dev/null +++ b/firmware/rmcs_board/app/src/timer/tick.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include +#include +#include + +#include "board_app.hpp" +#include "firmware/rmcs_board/app/src/led/led.hpp" +#include "firmware/rmcs_board/app/src/utility/lazy.hpp" + +namespace librmcs::firmware::timer { + +class Tick { +public: + using Lazy = utility::Lazy; + + Tick() { + const uint32_t freq = board::init_tick_clock(); + + gptmr_channel_config_t config; + gptmr_channel_get_default_config(BOARD_TICK_CLOCK, &config); + config.reload = (freq + 500) / 1000U; // 1kHz + gptmr_channel_config(BOARD_TICK_CLOCK, 0, &config, false); + + gptmr_enable_irq(BOARD_TICK_CLOCK, GPTMR_CH_RLD_IRQ_MASK(0)); + intc_m_enable_irq_with_priority(IRQn_GPTMR1, 1); + + gptmr_start_counter(BOARD_TICK_CLOCK, 0); + } + + void irq_handler() { + if (gptmr_check_status(BOARD_TICK_CLOCK, GPTMR_CH_RLD_STAT_MASK(0))) { + gptmr_clear_status(BOARD_TICK_CLOCK, GPTMR_CH_RLD_STAT_MASK(0)); + tick_counter_ = tick_counter_ + 1; + led::led->update(tick_counter_); + } + } + +private: + uint32_t tick_counter_ = 0; +}; + +inline constinit Tick::Lazy tick; + +} // namespace librmcs::firmware::timer diff --git a/firmware/rmcs_board/app/src/uart/uart.hpp b/firmware/rmcs_board/app/src/uart/uart.hpp index 31b4383..31ae22f 100644 --- a/firmware/rmcs_board/app/src/uart/uart.hpp +++ b/firmware/rmcs_board/app/src/uart/uart.hpp @@ -16,6 +16,7 @@ #include "core/src/protocol/serializer.hpp" #include "core/src/utility/assert.hpp" #include "core/src/utility/immovable.hpp" +#include "firmware/rmcs_board/app/src/led/led.hpp" #include "firmware/rmcs_board/app/src/uart/rx_buffer.hpp" #include "firmware/rmcs_board/app/src/uart/tx_buffer.hpp" #include "firmware/rmcs_board/app/src/usb/helper.hpp" @@ -49,7 +50,10 @@ class Uart init_uart(board_config.irq_num, baudrate, parity); } - void handle_downlink(const data::UartDataView& data) { TxBuffer::try_enqueue(data); } + void handle_downlink(const data::UartDataView& data) { + if (!TxBuffer::try_enqueue(data)) + led::led->downlink_buffer_full(); + } void try_transmit() { TxBuffer::try_dequeue(); } diff --git a/firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp b/firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp index e75f4e6..724b4b2 100644 --- a/firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp +++ b/firmware/rmcs_board/app/src/usb/interrupt_safe_buffer.hpp @@ -10,6 +10,7 @@ #include "core/src/protocol/serializer.hpp" #include "core/src/utility/assert.hpp" #include "core/src/utility/immovable.hpp" +#include "firmware/rmcs_board/app/src/led/led.hpp" namespace librmcs::firmware::usb { @@ -42,7 +43,7 @@ class InterruptSafeBuffer final auto writeable = kBatchCount - readable - 1; if (!writeable) { - // TODO: buffer full indication hook (LED/log); platform pending. + led::led->uplink_buffer_full(); return {}; } diff --git a/firmware/rmcs_board/app/src/utility/assert.cpp b/firmware/rmcs_board/app/src/utility/assert.cpp index 4c9f97b..4fc1bfe 100644 --- a/firmware/rmcs_board/app/src/utility/assert.cpp +++ b/firmware/rmcs_board/app/src/utility/assert.cpp @@ -2,6 +2,12 @@ #include +#include +#include +#include + +#include "firmware/rmcs_board/app/src/led/ws2812.hpp" + namespace librmcs::core::utility { const char* volatile assert_file = nullptr; @@ -9,10 +15,18 @@ volatile unsigned int assert_line = 0; const char* volatile assert_function = nullptr; [[noreturn]] void assert_func(const std::source_location& location) { + disable_global_irq(CSR_MSTATUS_MIE_MASK); + assert_file = location.file_name(); assert_line = location.line(); assert_function = location.function_name(); + if (auto* ws2812 = firmware::led::ws2812.try_get()) { + for (int i = 0; i < 10; i++) + board_delay_ms(10); + ws2812->set_value(255, 0, 0); + } + __builtin_trap(); } diff --git a/firmware/rmcs_board/app/src/utility/lazy.hpp b/firmware/rmcs_board/app/src/utility/lazy.hpp index fe04ed1..152b0ee 100644 --- a/firmware/rmcs_board/app/src/utility/lazy.hpp +++ b/firmware/rmcs_board/app/src/utility/lazy.hpp @@ -11,6 +11,8 @@ namespace librmcs::firmware::utility { +// Lazy-initialized object with deferred construction. +// Thread-safe single-time initialization guarded by interrupt lock. template class Lazy { public: @@ -25,6 +27,11 @@ class Lazy { constexpr ~Lazy() {} // No need to deconstruct + /*! + * @brief Construct the object on first call; no-op on subsequent calls. + * @return Reference to the constructed object + * @note Thread-safe. Uses interrupt lock for atomic state transitions. + */ constexpr T& init() { const InterruptLockGuard guard; @@ -44,16 +51,46 @@ class Lazy { return object_; } + /*! + * @brief Get pointer to the constructed object, or nullptr if not yet + * initialized. + * @return Pointer to the object, or nullptr if not yet initialized + * @note Does not assert on uninitialized state. Check return before use. + */ + constexpr T* try_get() { + if (!static_cast(*this)) + return nullptr; + return std::addressof(object_); + } + + /*! + * @brief Get pointer to the constructed object + * @return Pointer to the constructed object + * @warning Must only be called after init(). Asserts if uninitialized + * in debug builds. + */ constexpr T* get() { core::utility::assert_debug(static_cast(*this)); return std::addressof(object_); } + /*! + * @brief Member access through the constructed object + * @return Pointer to the constructed object + * @warning Must only be called after init(). Asserts if uninitialized + * in debug builds. + */ constexpr T* operator->() { core::utility::assert_debug(static_cast(*this)); return std::addressof(object_); } + /*! + * @brief Dereference to access the constructed object + * @return Reference to the constructed object + * @warning Must only be called after init(). Asserts if uninitialized + * in debug builds. + */ constexpr T& operator*() { core::utility::assert_debug(static_cast(*this)); return object_; diff --git a/firmware/rmcs_board/boards/lite/app/board_app.cpp b/firmware/rmcs_board/boards/lite/app/board_app.cpp index 1c28717..c53f5c8 100644 --- a/firmware/rmcs_board/boards/lite/app/board_app.cpp +++ b/firmware/rmcs_board/boards/lite/app/board_app.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -137,11 +138,22 @@ uint32_t init_spi(SPI_Type* ptr) { return init_spi_clock(ptr); } +void init_ws2812_pin() { + kWs2812Pin.configure_controller(); + kWs2812Pin.configure_as_pwm(); + kWs2812Pin.configure_pad_control(0); +} + void init_gpio_pins() { kGpioHardwareDescriptors[0].configure_pioc_function(); kGpioHardwareDescriptors[1].configure_pioc_function(); } +uint32_t init_tick_clock() { + clock_add_to_group(clock_gptmr1, 0); + return clock_get_frequency(clock_gptmr1); +} + void init_user_button_and_switch_pins() { kUserButtonPin.configure_controller(); kUserButtonPin.configure_ioc_function(); @@ -176,6 +188,9 @@ void gpio_bmi088_int_isr() { SDK_DECLARE_EXT_ISR_M(IRQn_GPIO0_Y, gpio_y_isr) void gpio_y_isr() { gpio_irq_handler(GPIO_DI_GPIOY); } +SDK_DECLARE_EXT_ISR_M(IRQn_GPTMR1, tick_isr) +void tick_isr() { tick_clock_irq_handler(); } + SDK_DECLARE_EXT_ISR_M(BOARD_CAN0(IRQn_MCAN, ), can0_isr) void can0_isr() { can_irq_handler(0); } diff --git a/firmware/rmcs_board/boards/lite/app/board_app.hpp b/firmware/rmcs_board/boards/lite/app/board_app.hpp index 7568dce..0f9beb8 100644 --- a/firmware/rmcs_board/boards/lite/app/board_app.hpp +++ b/firmware/rmcs_board/boards/lite/app/board_app.hpp @@ -55,6 +55,11 @@ constexpr GpioPin kUserHsFsSwitchPin = make_gpio_pin(), HPM_PWM1_BASE, IOC_PA06_FUNC_CTL_PWM1_P_6, 6}; + +void init_ws2812_pin(); + inline constexpr AnalogGpioPin kGpioHardwareDescriptors[]{ make_gpio_pin(), make_gpio_pin(), @@ -66,4 +71,9 @@ static_assert(board::spec::kGpioDescriptors.size() == std::size(board::kGpioHard void init_gpio_pins(); void gpio_irq_handler(uint32_t port_index); +#define BOARD_TICK_CLOCK HPM_GPTMR1 + +uint32_t init_tick_clock(); +void tick_clock_irq_handler(); + } // namespace librmcs::firmware::board diff --git a/firmware/rmcs_board/boards/pro/app/board_app.cpp b/firmware/rmcs_board/boards/pro/app/board_app.cpp index 101eee8..41d05e3 100644 --- a/firmware/rmcs_board/boards/pro/app/board_app.cpp +++ b/firmware/rmcs_board/boards/pro/app/board_app.cpp @@ -202,10 +202,23 @@ void init_user_button_and_switch_pins() { kUserHsFsSwitchPin.configure_as_input(); } +void init_ws2812_pin() { + // WS2812 is not populated until next hardware revision. + // Configure as input to prevent driving the line. + kWs2812Pin.configure_controller(); + kWs2812Pin.configure_as_gpio(); + kWs2812Pin.configure_as_input(); +} + void init_gpio_pins() { // Intentionally empty: No PIOC pins need to be configured. } +uint32_t init_tick_clock() { + clock_add_to_group(clock_gptmr1, 0); + return clock_get_frequency(clock_gptmr1); +} + SDK_DECLARE_EXT_ISR_M(IRQn_GPIO0_A, gpio_a_isr) void gpio_a_isr() { gpio_irq_handler(GPIO_DI_GPIOA); } @@ -225,6 +238,9 @@ void gpio_bmi088_int_accel_isr() { gpio_irq_handler(GPIO_DI_GPIOY); } +SDK_DECLARE_EXT_ISR_M(IRQn_GPTMR1, tick_isr) +void tick_isr() { tick_clock_irq_handler(); } + SDK_DECLARE_EXT_ISR_M(BOARD_CAN0(IRQn_MCAN, ), can0_isr) void can0_isr() { can_irq_handler(0); } diff --git a/firmware/rmcs_board/boards/pro/app/board_app.hpp b/firmware/rmcs_board/boards/pro/app/board_app.hpp index bff430c..f404782 100644 --- a/firmware/rmcs_board/boards/pro/app/board_app.hpp +++ b/firmware/rmcs_board/boards/pro/app/board_app.hpp @@ -57,6 +57,11 @@ constexpr GpioPin kUserHsFsSwitchPin = make_gpio_pin(), HPM_PWM1_BASE, IOC_PB04_FUNC_CTL_PWM1_P_0, 0}; + +void init_ws2812_pin(); + inline constexpr AnalogGpioPin kGpioHardwareDescriptors[]{ {make_gpio_pin(), HPM_PWM0_BASE, IOC_PB00_FUNC_CTL_PWM0_P_0, 0}, {make_gpio_pin(), HPM_PWM0_BASE, IOC_PB01_FUNC_CTL_PWM0_P_1, 1}, @@ -81,4 +86,9 @@ static_assert(board::spec::kGpioDescriptors.size() == std::size(board::kGpioHard void init_gpio_pins(); void gpio_irq_handler(uint32_t port_index); +#define BOARD_TICK_CLOCK HPM_GPTMR1 + +uint32_t init_tick_clock(); +void tick_clock_irq_handler(); + } // namespace librmcs::firmware::board