Files
bobbycar-controller-firmware/can_fc.h
Michael Ehrenreich 67defc0450 Add CAN
2020-10-25 02:30:34 +02:00

217 lines
3.7 KiB
C++

#pragma once
#ifdef __cplusplus
#include <cstdint>
#include <cstddef>
#include <cstring>
#include <algorithm>
#include "can.h"
//#define FC_STRICT
#define FC_PROTOCOL_ERROR()
#define Error_Handler() while(1)
constexpr const uint8_t FC_CHUNK_SIZE = 7;
constexpr const uint8_t FC_TAG_INIT = 0x45;
constexpr const uint8_t FC_TAG_END = 0x7d;
static uint8_t next_tag(uint8_t tag)
{
if (tag == 0)
{
return FC_TAG_INIT;
}
else
{
return tag + 0x04;
}
}
class FCSender
{
public:
FCSender(uint16_t id) : id(id)
{
}
bool tx_pending()
{
return pos_sent != len && pos_sent == pos_acked;
}
bool transfer_in_progress()
{
return data != nullptr && pos_acked != len;
}
bool transfer_finished()
{
return data != nullptr && pos_acked == len;
}
void reset(const uint8_t* data, size_t len)
{
pos_sent = 0;
pos_acked = 0;
tag_sent = 0;
this->data = data;
this->len = len;
}
void handle_frame(const uint8_t* payload, uint8_t payload_len)
{
if (pos_sent == 0)
return;
if (payload_len != 1)
return;
if (payload[0] != tag_sent)
return;
pos_acked = pos_sent;
}
void tx()
{
uint8_t payload[1 + FC_CHUNK_SIZE];
// Still waiting for ack, or done transmitting
if (!tx_pending())
return;
size_t new_pos_sent = std::min(pos_sent + FC_CHUNK_SIZE, len);
uint8_t sent_size = new_pos_sent - pos_sent;
uint8_t new_tag_sent = next_tag(tag_sent);
if (new_tag_sent == FC_TAG_END)
Error_Handler();
payload[0] = new_tag_sent;
memcpy(&payload[1], data + pos_sent, sent_size);
can_tx(id, payload, 1 + sent_size);
pos_sent = new_pos_sent;
tag_sent = new_tag_sent;
}
private:
uint16_t id;
const uint8_t* data;
size_t len;
size_t pos_sent;
size_t pos_acked;
uint8_t tag_sent;
};
class FCReceiver
{
public:
FCReceiver(uint16_t id, uint8_t* data, size_t len) : id(id)
{
reset(data, len);
}
bool ack_pending()
{
return pos_received != pos_acked;
}
bool transfer_in_progress()
{
return ready && data != nullptr && pos_acked != len;
}
bool transfer_finished()
{
return ready && data != nullptr && pos_acked == len;
}
void reset(uint8_t* data, size_t len)
{
// Poor man's mutex
ready = false;
pos_received = 0;
pos_acked = 0;
tag_expected = next_tag(0);
this->data = data;
this->len = len;
ready = true;
}
void handle_frame(const uint8_t* payload, uint8_t payload_len)
{
if (!transfer_in_progress())
return;
if (tag_expected == FC_TAG_END)
return;
if (payload_len < 1)
return;
// Reset receiver if frame with FC_TAG_INIT received when expecting different one
if (payload[0] == FC_TAG_INIT && tag_expected != FC_TAG_INIT)
reset(data, len);
// Don't accept new data if we haven't acked the previous data yet
if (ack_pending())
return;
uint8_t data_len = payload_len - 1;
if (data_len > len - pos_received)
return;
// Ignore all other tags
if (payload[0] != tag_expected)
return;
memcpy(&data[pos_received], &payload[1], data_len);
pos_received += data_len;
}
void ack()
{
uint8_t payload[1];
if (!ack_pending())
return;
// Poor man's mutex
ready = false;
// ISR may have called reset() in the mean time
if (!ack_pending())
{
ready = true;
return;
}
payload[0] = tag_expected;
can_tx(id, payload, sizeof(payload));
pos_acked = pos_received;
tag_expected = next_tag(tag_expected);
ready = true;
}
private:
bool ready;
uint16_t id;
uint8_t* data;
size_t len;
size_t pos_received;
size_t pos_acked;
uint8_t tag_expected;
};
#endif // __cplusplus