Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions firmware/c_board/app/src/utility/lazy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace librmcs::firmware::utility {

// Lazy-initialized object with deferred construction.
// Thread-safe single-time initialization guarded by interrupt lock.
template <typename T, typename... Args>
class Lazy {
public:
Expand All @@ -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;

Expand All @@ -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<bool>(*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<bool>(*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<bool>(*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<bool>(*this));
return object_;
Expand Down
6 changes: 6 additions & 0 deletions firmware/rmcs_board/app/src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -25,6 +27,8 @@ App::App() {
dma_mgr_init();
boot::BootMailbox::clear();

led::led.init();

usb::vendor.init();

for (auto& can : can::can_array)
Expand All @@ -38,6 +42,8 @@ App::App() {

spi::bmi088::accelerometer.init();
spi::bmi088::gyroscope.init();

timer::tick.init();
}

// Non-static to ensure instantiation
Expand Down
5 changes: 4 additions & 1 deletion firmware/rmcs_board/app/src/can/can.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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) {
Expand Down
16 changes: 10 additions & 6 deletions firmware/rmcs_board/app/src/gpio/analog_gpio_pin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_Type*>(pwm_base_); }

[[nodiscard]] constexpr bool supports_pwm() const noexcept { return pwm_base() != 0; }

void configure_as_gpio() const {
configure_controller();
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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_Type*>(pwm_base_); }

uintptr_t pwm_base_;

uint8_t pwm_ioc_function_;
Expand Down
77 changes: 77 additions & 0 deletions firmware/rmcs_board/app/src/led/led.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#pragma once

#include <atomic>
#include <cstdint>

#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>;

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<uint8_t>(brightness), 0);
}
}

private:
std::atomic<uint16_t> uplink_full_reset_counter_{0};
std::atomic<uint16_t> downlink_full_reset_counter_{0};
};

inline constinit Led::Lazy led;

} // namespace librmcs::firmware::led
Loading
Loading