Skip to content
Closed
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
Empty file added .codex
Empty file.
20 changes: 20 additions & 0 deletions core/include/librmcs/data/datas.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ enum class DataId : uint8_t {
kUart3 = 14,

kImu = 15,

kI2c0 = 16,
};

struct CanDataView {
Expand Down Expand Up @@ -79,6 +81,20 @@ struct GyroscopeDataView {
int16_t z;
};

struct I2cDataView {
uint8_t slave_address;
std::span<const std::byte> payload;
bool has_register = false;
uint8_t reg_address = 0;
};

struct I2cReadConfigView {
uint8_t slave_address;
uint16_t read_length = 0;
bool has_register = false;
uint8_t reg_address = 0;
};

class DataCallback {
public:
DataCallback() = default;
Expand All @@ -100,6 +116,10 @@ class DataCallback {
virtual void accelerometer_receive_callback(const AccelerometerDataView& data) = 0;

virtual void gyroscope_receive_callback(const GyroscopeDataView& data) = 0;

virtual bool i2c_receive_callback(DataId id, const I2cDataView& data) = 0;

virtual void i2c_error_callback(DataId id, uint8_t slave_address) = 0;
};

} // namespace librmcs::data
85 changes: 85 additions & 0 deletions core/src/protocol/deserializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ coroutine::LifoTask<void> Deserializer::process_stream() {
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;
case FieldId::kI2c0: success = co_await process_i2c_field(id); break;
default: break;
}
if (!success)
Expand Down Expand Up @@ -289,4 +290,88 @@ coroutine::LifoTask<bool> Deserializer::process_imu_field(FieldId) {
co_return true;
}

coroutine::LifoTask<bool> Deserializer::process_i2c_field(FieldId field_id) {
I2cHeader::PayloadEnum payload_type;
uint8_t slave_address = 0;
uint16_t data_length = 0;
bool has_register = false;
{
const auto* header_bytes = co_await peek_bytes(sizeof(I2cHeader));
if (!header_bytes) [[unlikely]]
co_return false;

auto header = I2cHeader::CRef{header_bytes};
payload_type = header.get<I2cHeader::PayloadType>();
has_register = header.get<I2cHeader::HasRegister>();
slave_address = header.get<I2cHeader::SlaveAddress>();
data_length = header.get<I2cHeader::DataLength>();
consume_peeked();
}

if (slave_address > 0x7F) [[unlikely]]
co_return false;

switch (payload_type) {
case I2cHeader::PayloadEnum::kWrite:
case I2cHeader::PayloadEnum::kReadResult: {
data::I2cDataView data_view{};
data_view.slave_address = slave_address;
data_view.has_register = has_register;

if (has_register) {
const auto* register_bytes = co_await peek_bytes(1);
if (!register_bytes) [[unlikely]]
co_return false;
data_view.reg_address = std::to_integer<uint8_t>(register_bytes[0]);
consume_peeked();
}

if (data_length) {
const auto* payload_bytes = co_await peek_bytes(data_length);
if (!payload_bytes) [[unlikely]]
co_return false;
data_view.payload = std::span<const std::byte>{payload_bytes, data_length};
consume_peeked();
} else {
data_view.payload = std::span<const std::byte>{};
}

if (payload_type == I2cHeader::PayloadEnum::kWrite) {
callback_.i2c_write_deserialized_callback(field_id, data_view);
} else {
callback_.i2c_read_result_deserialized_callback(field_id, data_view);
}
break;
}
case I2cHeader::PayloadEnum::kReadRequest: {
data::I2cReadConfigView data_view{};
data_view.slave_address = slave_address;
data_view.read_length = data_length;
data_view.has_register = has_register;

if (has_register) {
const auto* register_bytes = co_await peek_bytes(1);
if (!register_bytes) [[unlikely]]
co_return false;
data_view.reg_address = std::to_integer<uint8_t>(register_bytes[0]);
consume_peeked();
}

callback_.i2c_read_config_deserialized_callback(field_id, data_view);
break;
}
case I2cHeader::PayloadEnum::kError: {
data::I2cDataView data_view{};
data_view.slave_address = slave_address;
data_view.payload = std::span<const std::byte>{};
data_view.has_register = false;
callback_.i2c_error_deserialized_callback(field_id, data_view);
break;
}
default: co_return false;
}

co_return true;
}

} // namespace librmcs::core::protocol
12 changes: 12 additions & 0 deletions core/src/protocol/deserializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ class DeserializeCallback {

virtual void gyroscope_deserialized_callback(const data::GyroscopeDataView& data) = 0;

virtual void i2c_write_deserialized_callback(FieldId id, const data::I2cDataView& data) = 0;

virtual void
i2c_read_config_deserialized_callback(FieldId id, const data::I2cReadConfigView& data) = 0;

virtual void
i2c_read_result_deserialized_callback(FieldId id, const data::I2cDataView& data) = 0;

virtual void i2c_error_deserialized_callback(FieldId id, const data::I2cDataView& data) = 0;

virtual void error_callback() = 0;
};

Expand Down Expand Up @@ -115,6 +125,8 @@ class Deserializer : private coroutine::InlineLifoContext<1024> {

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

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

// Await until at least `size` contiguous bytes are available at the current read position.
// Returns a pointer to a contiguous region of at least `size` bytes.
// (from input buffer or pending cache)
Expand Down
15 changes: 15 additions & 0 deletions core/src/protocol/protocol.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,19 @@ struct ImuGyroscopePayload : utility::Bitfield<6> {
using Z = utility::BitfieldMember<32, 16, int16_t>;
};

struct I2cHeader : utility::Bitfield<3> {
enum class PayloadEnum : uint8_t {
kWrite = 0,
kReadRequest = 1,
kReadResult = 2,
kError = 3,
};

using PayloadType = utility::BitfieldMember<4, 2, PayloadEnum>;
using HasRegister = utility::BitfieldMember<6, 1>;
using ErrorFlag = utility::BitfieldMember<7, 1>;
using SlaveAddress = utility::BitfieldMember<8, 7>;
using DataLength = utility::BitfieldMember<15, 9>;
};

} // namespace librmcs::core::protocol
148 changes: 148 additions & 0 deletions core/src/protocol/serializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,130 @@ class Serializer {
return SerializeResult::kSuccess;
}

SerializeResult write_i2c_write(FieldId field_id, const data::I2cDataView& view) noexcept {
const std::size_t required = required_i2c_size(
field_id, I2cHeader::PayloadEnum::kWrite, view.payload.size(), view.has_register);
LIBRMCS_VERIFY_LIKELY(required, SerializeResult::kInvalidArgument);
LIBRMCS_VERIFY_LIKELY(view.slave_address <= 0x7F, 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, field_id);

auto header = I2cHeader::Ref(cursor);
cursor += sizeof(I2cHeader);
header.set<I2cHeader::PayloadType>(I2cHeader::PayloadEnum::kWrite);
header.set<I2cHeader::HasRegister>(view.has_register);
header.set<I2cHeader::ErrorFlag>(false);
header.set<I2cHeader::SlaveAddress>(view.slave_address);
header.set<I2cHeader::DataLength>(static_cast<uint16_t>(view.payload.size()));

if (view.has_register)
*cursor++ = static_cast<std::byte>(view.reg_address);

if (!view.payload.empty()) {
std::memcpy(cursor, view.payload.data(), view.payload.size());
cursor += view.payload.size();
}

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

SerializeResult
write_i2c_read_config(FieldId field_id, const data::I2cReadConfigView& view) noexcept {
const std::size_t required = required_i2c_size(
field_id, I2cHeader::PayloadEnum::kReadRequest, view.read_length, view.has_register);
LIBRMCS_VERIFY_LIKELY(required, SerializeResult::kInvalidArgument);
LIBRMCS_VERIFY_LIKELY(view.slave_address <= 0x7F, 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, field_id);

auto header = I2cHeader::Ref(cursor);
cursor += sizeof(I2cHeader);
header.set<I2cHeader::PayloadType>(I2cHeader::PayloadEnum::kReadRequest);
header.set<I2cHeader::HasRegister>(view.has_register);
header.set<I2cHeader::ErrorFlag>(false);
header.set<I2cHeader::SlaveAddress>(view.slave_address);
header.set<I2cHeader::DataLength>(view.read_length);

if (view.has_register)
*cursor++ = static_cast<std::byte>(view.reg_address);

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

SerializeResult
write_i2c_read_result(FieldId field_id, const data::I2cDataView& view) noexcept {
const std::size_t required = required_i2c_size(
field_id, I2cHeader::PayloadEnum::kReadResult, view.payload.size(), view.has_register);
LIBRMCS_VERIFY_LIKELY(required, SerializeResult::kInvalidArgument);
LIBRMCS_VERIFY_LIKELY(view.slave_address <= 0x7F, 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, field_id);

auto header = I2cHeader::Ref(cursor);
cursor += sizeof(I2cHeader);
header.set<I2cHeader::PayloadType>(I2cHeader::PayloadEnum::kReadResult);
header.set<I2cHeader::HasRegister>(view.has_register);
header.set<I2cHeader::ErrorFlag>(false);
header.set<I2cHeader::SlaveAddress>(view.slave_address);
header.set<I2cHeader::DataLength>(static_cast<uint16_t>(view.payload.size()));

if (view.has_register)
*cursor++ = static_cast<std::byte>(view.reg_address);

if (!view.payload.empty()) {
std::memcpy(cursor, view.payload.data(), view.payload.size());
cursor += view.payload.size();
}

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

SerializeResult write_i2c_error(FieldId field_id, uint8_t slave_address) noexcept {
const std::size_t required =
required_i2c_size(field_id, I2cHeader::PayloadEnum::kError, 0, false);
LIBRMCS_VERIFY_LIKELY(required, SerializeResult::kInvalidArgument);
LIBRMCS_VERIFY_LIKELY(slave_address <= 0x7F, 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, field_id);

auto header = I2cHeader::Ref(cursor);
cursor += sizeof(I2cHeader);
header.set<I2cHeader::PayloadType>(I2cHeader::PayloadEnum::kError);
header.set<I2cHeader::HasRegister>(false);
header.set<I2cHeader::ErrorFlag>(true);
header.set<I2cHeader::SlaveAddress>(slave_address);
header.set<I2cHeader::DataLength>(0);

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

private:
static constexpr bool is_i2c_field_id(FieldId field_id) { return field_id == FieldId::kI2c0; }

static constexpr bool use_extended_field_header(FieldId field_id) {
utility::assert_debug(field_id != FieldId::kExtend);
return static_cast<std::uint8_t>(field_id) > 0xF;
Expand Down Expand Up @@ -437,6 +560,31 @@ class Serializer {
return total;
}

static std::size_t required_i2c_size(
FieldId field_id, I2cHeader::PayloadEnum payload, std::size_t data_len,
bool has_register) noexcept {
LIBRMCS_VERIFY_LIKELY(is_i2c_field_id(field_id), 0);
LIBRMCS_VERIFY_LIKELY(data_len <= ((1U << 9) - 1U), 0);
switch (payload) {
case I2cHeader::PayloadEnum::kWrite:
case I2cHeader::PayloadEnum::kReadRequest:
case I2cHeader::PayloadEnum::kReadResult:
case I2cHeader::PayloadEnum::kError: break;
default: return 0;
}

const std::size_t field_header_bytes = required_field_header_size(field_id);
const std::size_t i2c_header_bytes = sizeof(I2cHeader);
const std::size_t register_bytes = has_register ? 1 : 0;
const std::size_t payload_bytes =
payload == I2cHeader::PayloadEnum::kReadRequest ? 0 : data_len;
const std::size_t total =
(field_header_bytes + i2c_header_bytes - 1) + register_bytes + payload_bytes;
LIBRMCS_VERIFY_LIKELY(total <= kProtocolBufferSize, 0);

return total;
}

SerializeBuffer& buffer_;
};

Expand Down
5 changes: 5 additions & 0 deletions firmware/c_board/app/src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <device/usbd.h>
#include <dma.h>
#include <gpio.h>
#include <i2c.h>
#include <main.h>
#include <spi.h>
#include <tim.h>
Expand All @@ -12,6 +13,7 @@

#include "firmware/c_board/app/src/can/can.hpp"
#include "firmware/c_board/app/src/gpio/gpio.hpp"
#include "firmware/c_board/app/src/i2c/i2c.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 @@ -48,6 +50,7 @@ App::App() {
MX_SPI1_Init();
MX_CAN1_Init();
MX_CAN2_Init();
MX_I2C2_Init();
MX_USART1_UART_Init();
MX_USART3_UART_Init();
MX_USART6_UART_Init();
Expand All @@ -61,6 +64,7 @@ App::App() {
uart::uart1.init();
uart::uart2.init();
uart::uart_dbus.init();
i2c::i2c0.init();
gpio::gpio.init();
spi::bmi088::accelerometer.init();
spi::bmi088::gyroscope.init();
Expand All @@ -79,6 +83,7 @@ App::App() {
can::can2->try_transmit();
usb::vendor->try_transmit();
spi::spi1->update();
i2c::i2c0->update();
usb::vendor->try_transmit();
uart::uart1->try_transmit();
usb::vendor->try_transmit();
Expand Down
Loading