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
10 changes: 10 additions & 0 deletions core/include/librmcs/data/datas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ struct UartDataView {
bool idle_delimited = false;
};

struct GpioDigitalDataView {
uint8_t channel;
bool high;
};

struct GpioAnalogDataView {
uint8_t channel;
uint16_t value;
};

struct AccelerometerDataView {
int16_t x;
int16_t y;
Expand Down
44 changes: 44 additions & 0 deletions core/src/protocol/deserializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ coroutine::LifoTask<void> Deserializer::process_stream() {
case FieldId::kUart1:
case FieldId::kUart2:
case FieldId::kUart3: success = co_await process_uart_field(id); break;
case FieldId::kGpio: success = co_await process_gpio_field(id); break;
case FieldId::kImu: success = co_await process_imu_field(id); break;
default: break;
}
Expand Down Expand Up @@ -150,6 +151,49 @@ coroutine::LifoTask<bool> Deserializer::process_uart_field(FieldId field_id) {
co_return true;
}

coroutine::LifoTask<bool> Deserializer::process_gpio_field(FieldId) {
GpioHeader::PayloadEnum payload_type;
std::uint8_t channel = 0;
{
const auto* header_bytes = co_await peek_bytes(sizeof(GpioHeader));
if (!header_bytes) [[unlikely]]
co_return false;

auto header = GpioHeader::CRef{header_bytes};
payload_type = header.get<GpioHeader::PayloadType>();
channel = header.get<GpioHeader::Channel>();
consume_peeked();
}

switch (payload_type) {
case GpioHeader::PayloadEnum::kDigitalWriteLow:
case GpioHeader::PayloadEnum::kDigitalWriteHigh: {
data::GpioDigitalDataView data_view{};
data_view.channel = channel;
data_view.high = payload_type == GpioHeader::PayloadEnum::kDigitalWriteHigh;
callback_.gpio_digital_deserialized_callback(data_view);
break;
}
case GpioHeader::PayloadEnum::kAnalogWrite: {
const auto* payload_bytes = co_await peek_bytes(sizeof(GpioAnalogPayload));
if (!payload_bytes) [[unlikely]]
co_return false;

auto payload = GpioAnalogPayload::CRef{payload_bytes};
data::GpioAnalogDataView data_view{};
data_view.channel = channel;
data_view.value = payload.get<GpioAnalogPayload::Value>();
consume_peeked();

callback_.gpio_analog_deserialized_callback(data_view);
break;
}
default: co_return false;
}

co_return true;
}

coroutine::LifoTask<bool> Deserializer::process_imu_field(FieldId) {
ImuHeader::PayloadEnum payload_type;
{
Expand Down
6 changes: 6 additions & 0 deletions core/src/protocol/deserializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ class DeserializeCallback {

virtual void uart_deserialized_callback(FieldId id, const data::UartDataView& data) = 0;

virtual void gpio_digital_deserialized_callback(const data::GpioDigitalDataView& data) = 0;

virtual void gpio_analog_deserialized_callback(const data::GpioAnalogDataView& data) = 0;

virtual void accelerometer_deserialized_callback(const data::AccelerometerDataView& data) = 0;

virtual void gyroscope_deserialized_callback(const data::GyroscopeDataView& data) = 0;
Expand Down Expand Up @@ -101,6 +105,8 @@ class Deserializer : private coroutine::InlineLifoContext<1024> {

coroutine::LifoTask<bool> process_uart_field(FieldId field_id);

coroutine::LifoTask<bool> process_gpio_field(FieldId field_id);

coroutine::LifoTask<bool> process_imu_field(FieldId field_id);

// Await until at least `size` contiguous bytes are available at the current read position.
Expand Down
20 changes: 20 additions & 0 deletions core/src/protocol/protocol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,26 @@ struct UartHeaderExtended
, layouts::UartHeaderLayout
, layouts::UartHeaderExtendedLayout {};

struct GpioHeader : utility::Bitfield<2> {
enum class PayloadEnum : uint8_t {
kDigitalWriteLow = 0b0000,
kDigitalWriteHigh = 0b0001,
kAnalogWrite = 0b0010,
kDigitalRead = 0b0100,
kAnalogRead = 0b0110,
kDigitalReadResultLow = 0b1000,
kDigitalReadResultHigh = 0b1001,
kAnalogReadResult = 0b1010,
};

using PayloadType = utility::BitfieldMember<4, 4, PayloadEnum>;
using Channel = utility::BitfieldMember<8, 8>;
};

struct GpioAnalogPayload : utility::Bitfield<2> {
using Value = utility::BitfieldMember<0, 16, uint16_t>;
};

struct ImuHeader : utility::Bitfield<1> {
enum class PayloadEnum : uint8_t {
kAccelerometer = 0,
Expand Down
67 changes: 67 additions & 0 deletions core/src/protocol/serializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,53 @@ class Serializer {
return SerializeResult::kSuccess;
}

SerializeResult write_gpio_digital(const data::GpioDigitalDataView& view) noexcept {
const auto payload_type = view.high ? GpioHeader::PayloadEnum::kDigitalWriteHigh
: GpioHeader::PayloadEnum::kDigitalWriteLow;
const std::size_t required = required_gpio_size(FieldId::kGpio, payload_type);
LIBRMCS_VERIFY_LIKELY(required, SerializeResult::kInvalidArgument);

auto dst = buffer_.allocate(required);
LIBRMCS_VERIFY_LIKELY(!dst.empty(), SerializeResult::kBadAlloc);
utility::assert_debug(dst.size() == required);
std::byte* cursor = dst.data();

write_field_header(cursor, FieldId::kGpio);

auto header = GpioHeader::Ref(cursor);
cursor += sizeof(GpioHeader);
header.set<GpioHeader::PayloadType>(payload_type);
header.set<GpioHeader::Channel>(view.channel);

utility::assert_debug(cursor == dst.data() + dst.size());
return SerializeResult::kSuccess;
}

SerializeResult write_gpio_analog(const data::GpioAnalogDataView& view) noexcept {
const std::size_t required =
required_gpio_size(FieldId::kGpio, GpioHeader::PayloadEnum::kAnalogWrite);
LIBRMCS_VERIFY_LIKELY(required, SerializeResult::kInvalidArgument);

auto dst = buffer_.allocate(required);
LIBRMCS_VERIFY_LIKELY(!dst.empty(), SerializeResult::kBadAlloc);
utility::assert_debug(dst.size() == required);
std::byte* cursor = dst.data();

write_field_header(cursor, FieldId::kGpio);

auto header = GpioHeader::Ref(cursor);
cursor += sizeof(GpioHeader);
header.set<GpioHeader::PayloadType>(GpioHeader::PayloadEnum::kAnalogWrite);
header.set<GpioHeader::Channel>(view.channel);

auto payload = GpioAnalogPayload::Ref(cursor);
cursor += sizeof(GpioAnalogPayload);
payload.set<GpioAnalogPayload::Value>(view.value);

utility::assert_debug(cursor == dst.data() + dst.size());
return SerializeResult::kSuccess;
}

SerializeResult write_imu_accelerometer(const data::AccelerometerDataView& view) noexcept {
const std::size_t required = required_imu_size(FieldId::kImu, ImuPayload::kAccelerometer);
LIBRMCS_VERIFY_LIKELY(required, SerializeResult::kInvalidArgument);
Expand Down Expand Up @@ -231,6 +278,26 @@ class Serializer {
return total;
}

static std::size_t
required_gpio_size(FieldId field_id, GpioHeader::PayloadEnum payload) noexcept {
const std::size_t field_header_bytes = required_field_header_size(field_id);
const std::size_t gpio_header_bytes = sizeof(GpioHeader);
std::size_t payload_bytes = 0;
switch (payload) {
case GpioHeader::PayloadEnum::kDigitalWriteLow:
case GpioHeader::PayloadEnum::kDigitalWriteHigh: payload_bytes = 0; break;
case GpioHeader::PayloadEnum::kAnalogWrite:
payload_bytes = sizeof(GpioAnalogPayload);
break;
default: return 0;
}

const std::size_t total = (field_header_bytes + gpio_header_bytes - 1) + payload_bytes;
utility::assert_debug(total <= kProtocolBufferSize);

return total;
}

enum class ImuPayload : std::uint8_t { kAccelerometer = 0, kGyroscope = 1 };

static std::size_t required_imu_size(FieldId field_id, ImuPayload payload) noexcept {
Expand Down
4 changes: 4 additions & 0 deletions firmware/c_board/app/src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <usb_otg.h>

#include "firmware/c_board/app/src/can/can.hpp"
#include "firmware/c_board/app/src/gpio/gpio.hpp"
#include "firmware/c_board/app/src/led/led.hpp"
#include "firmware/c_board/app/src/spi/bmi088/accel.hpp"
#include "firmware/c_board/app/src/spi/bmi088/gyro.hpp"
Expand Down Expand Up @@ -41,6 +42,8 @@ App::App() {
timer::timer.init();

MX_GPIO_Init();
MX_TIM1_Init();
MX_TIM8_Init();
MX_DMA_Init();
MX_SPI1_Init();
MX_CAN1_Init();
Expand All @@ -58,6 +61,7 @@ App::App() {
uart::uart1.init();
uart::uart2.init();
uart::uart_dbus.init();
gpio::gpio.init();
spi::bmi088::accelerometer.init();
spi::bmi088::gyroscope.init();
}
Expand Down
4 changes: 3 additions & 1 deletion firmware/c_board/app/src/gpio/gpio.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#include "firmware/c_board/app/src/gpio/gpio.hpp"

#include <cstdint>

#include <gpio.h>
#include <main.h>
#include <stm32f4xx_hal_gpio.h>

#include "firmware/c_board/app/src/spi/bmi088/accel.hpp"
#include "firmware/c_board/app/src/spi/bmi088/gyro.hpp"
Expand Down
73 changes: 73 additions & 0 deletions firmware/c_board/app/src/gpio/gpio.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#pragma once

#include <cstdint>

#include <main.h>
#include <tim.h>

#include "core/include/librmcs/data/datas.hpp"
#include "core/src/utility/assert.hpp"
#include "core/src/utility/immovable.hpp"
#include "firmware/c_board/app/src/utility/lazy.hpp"

namespace librmcs::firmware::gpio {

class Gpio : private core::utility::Immovable {
public:
using Lazy = utility::Lazy<Gpio>;

Gpio() {
core::utility::assert_always(HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1) == HAL_OK);
core::utility::assert_always(HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2) == HAL_OK);
core::utility::assert_always(HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3) == HAL_OK);
core::utility::assert_always(HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4) == HAL_OK);
core::utility::assert_always(HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1) == HAL_OK);
core::utility::assert_always(HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2) == HAL_OK);
core::utility::assert_always(HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3) == HAL_OK);

set_compare(1, 0);
set_compare(2, 0);
set_compare(3, 0);
set_compare(4, 0);
set_compare(5, 0);
set_compare(6, 0);
set_compare(7, 0);
}

void handle_digital_write(const data::GpioDigitalDataView& data) {
if (!is_supported_channel(data.channel))
return;

set_compare(data.channel, data.high ? kCounterPeriod : 0);
}

void handle_analog_write(const data::GpioAnalogDataView& data) {
if (!is_supported_channel(data.channel))
return;

set_compare(data.channel, duty_to_compare(data.value));
}

private:
static constexpr uint32_t kCounterPeriod = 60000;

static bool is_supported_channel(uint8_t channel) { return channel >= 1 && channel <= 7; }

static uint32_t duty_to_compare(uint16_t duty) {
return ((static_cast<uint32_t>(duty) * kCounterPeriod) + 32767U) / 65535U;
}

void set_compare(uint8_t channel, uint32_t compare) {
core::utility::assert_debug_lazy([&]() noexcept { return is_supported_channel(channel); });
*channel_compares_[channel - 1] = compare;
}

volatile uint32_t* channel_compares_[7] = {
&htim1.Instance->CCR1, &htim1.Instance->CCR2, &htim1.Instance->CCR3, &htim1.Instance->CCR4,
&htim8.Instance->CCR1, &htim8.Instance->CCR2, &htim8.Instance->CCR3,
};
};

inline constinit Gpio::Lazy gpio;

} // namespace librmcs::firmware::gpio
9 changes: 9 additions & 0 deletions firmware/c_board/app/src/usb/vendor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "core/src/utility/assert.hpp"
#include "core/src/utility/immovable.hpp"
#include "firmware/c_board/app/src/can/can.hpp"
#include "firmware/c_board/app/src/gpio/gpio.hpp"
#include "firmware/c_board/app/src/uart/uart.hpp"
#include "firmware/c_board/app/src/usb/interrupt_safe_buffer.hpp"
#include "firmware/c_board/app/src/usb/usb_descriptors.hpp"
Expand Down Expand Up @@ -101,6 +102,14 @@ class Vendor
}
}

void gpio_digital_deserialized_callback(const data::GpioDigitalDataView& data) override {
gpio::gpio->handle_digital_write(data);
}

void gpio_analog_deserialized_callback(const data::GpioAnalogDataView& data) override {
gpio::gpio->handle_analog_write(data);
}

void accelerometer_deserialized_callback(const data::AccelerometerDataView& data) override {
(void)data;
}
Expand Down
14 changes: 14 additions & 0 deletions firmware/c_board/bsp/cubemx/Core/Inc/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,12 @@ void SystemClock_Config(void);
#define SPI1_MISO_GPIO_Port GPIOB
#define SPI1_SCK_Pin GPIO_PIN_3
#define SPI1_SCK_GPIO_Port GPIOB
#define PWM7_Pin GPIO_PIN_7
#define PWM7_GPIO_Port GPIOI
#define PWM6_Pin GPIO_PIN_6
#define PWM6_GPIO_Port GPIOI
#define PWM5_Pin GPIO_PIN_6
#define PWM5_GPIO_Port GPIOC
#define LED_R_Pin GPIO_PIN_12
#define LED_R_GPIO_Port GPIOH
#define LED_G_Pin GPIO_PIN_11
Expand All @@ -279,9 +285,17 @@ void SystemClock_Config(void);
#define INT1_ACC_Pin GPIO_PIN_4
#define INT1_ACC_GPIO_Port GPIOC
#define INT1_ACC_EXTI_IRQn EXTI4_IRQn
#define PWM3_Pin GPIO_PIN_13
#define PWM3_GPIO_Port GPIOE
#define INT1_GYRO_Pin GPIO_PIN_5
#define INT1_GYRO_GPIO_Port GPIOC
#define INT1_GYRO_EXTI_IRQn EXTI9_5_IRQn
#define PWM1_Pin GPIO_PIN_9
#define PWM1_GPIO_Port GPIOE
#define PWM2_Pin GPIO_PIN_11
#define PWM2_GPIO_Port GPIOE
#define PWM4_Pin GPIO_PIN_14
#define PWM4_GPIO_Port GPIOE
#define SPI1_MOSI_Pin GPIO_PIN_7
#define SPI1_MOSI_GPIO_Port GPIOA
#define CS1_GYRO_Pin GPIO_PIN_0
Expand Down
6 changes: 6 additions & 0 deletions firmware/c_board/bsp/cubemx/Core/Inc/tim.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,24 @@ extern "C" {
#include "stm32f4xx_hal_tim.h" // IWYU pragma: export
/* USER CODE END Includes */

extern TIM_HandleTypeDef htim1;

extern TIM_HandleTypeDef htim2;

extern TIM_HandleTypeDef htim5;

extern TIM_HandleTypeDef htim8;

extern TIM_HandleTypeDef htim9;

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_TIM1_Init(void);
void MX_TIM2_Init(void);
void MX_TIM5_Init(void);
void MX_TIM8_Init(void);
void MX_TIM9_Init(void);

void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
Expand Down
Loading