f4ll/src/packet_usart.cpp

212 lines
6 KiB
C++

/*
* ll_hsusart_impl.h
*
* Created on: Oct 29, 2019
* Author: abody
*/
#include <f4ll/packet_usart.h>
#include <string.h>
namespace f4ll {
template <typename T> static inline T round_up_to_4(T input)
{
return (input + 3) & (((T)-1) - 3);
}
packet_usart::packet_usart(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx)
: usart_core(usart, dma, stream_rx, stream_tx)
{
crc_handler::instance().attach_slot(m_crc_slot);
LL_USART_EnableIT_IDLE(usart);
LL_USART_EnableIT_ERROR(usart);
}
void packet_usart::rx_processed(bool second)
{
m_rx_buffers[second].busy = false;
m_rx_buffers[second].error = false;
}
void packet_usart::set_callback(ihs_usart_callback *callback, uintptr_t callback_param)
{
m_user_callback = callback;
m_user_callback_param = callback_param;
}
void packet_usart::post_packet(uint8_t const *payload, uint8_t length, bool wait_for_crc_queue)
{
uint16_t payload_length = round_up_to_4((uint16_t)length);
build_header(m_tx_buffer.pkt, m_tx_serial_nr++, length);
if (payload) {
memcpy(m_tx_buffer.pkt.payload, payload, length);
}
m_tx_buffer.requested_length = sizeof(m_tx_buffer.pkt.header) + payload_length + sizeof(uint32_t);
m_tx_buffer.busy = true;
m_tx_buffer.error = false;
crc_handler::instance().enqueue(
m_crc_slot, 0, &m_tx_buffer.pkt, sizeof(packet_header) + payload_length, nullptr,
reinterpret_cast<uintptr_t>(m_tx_buffer.pkt.payload + payload_length));
while (wait_for_crc_queue && crc_handler::instance().is_queued(m_crc_slot, 0))
;
setup_transmit(&m_tx_buffer.pkt, m_tx_buffer.requested_length);
++m_stats.sent;
}
void packet_usart::setup_receive()
{
m_rx_buffers[m_rx_buffer_selector].requested_length = sizeof(m_rx_buffers[m_rx_buffer_selector].pkt);
usart_core::setup_receive(&m_rx_buffers[m_rx_buffer_selector], sizeof(m_rx_buffers[m_rx_buffer_selector].pkt));
}
//////////////////////////////////////
// UsartCore pure virtual functions //
//////////////////////////////////////
void packet_usart::receiver_idle(void)
{
uint16_t rcvdLen =
m_rx_buffers[m_rx_buffer_selector].requested_length - LL_DMA_GetDataLength(m_rx_dma.get_dma(), m_rx_dma.get_stream());
if (rcvdLen >= sizeof(packet_header)) {
if (check_header(m_rx_buffers[m_rx_buffer_selector].pkt.header)) {
if (rcvdLen >= sizeof(packet_header) + round_up_to_4((uint16_t)m_rx_buffers[m_rx_buffer_selector].pkt.header.payload_length) +
sizeof(uint32_t)) {
LL_DMA_DisableStream(m_rx_dma.get_dma(), m_rx_dma.get_stream());
} else {
++m_stats.premature_payload;
}
} else {
m_rx_buffers[m_rx_buffer_selector].error = 1;
LL_DMA_DisableStream(m_rx_dma.get_dma(), m_rx_dma.get_stream());
}
} else {
++m_stats.premature_hdr;
}
}
void packet_usart::transmission_complete(void)
{
LL_USART_DisableDirectionTx(m_usart); // enforcing an idle frame
LL_USART_EnableDirectionTx(m_usart);
m_tx_buffer.busy = 0;
}
void packet_usart::framing_error(void) {}
void packet_usart::overrun(void) {}
void packet_usart::rx_dma_transfer_complete(void)
{
if (check_header(m_rx_buffers[m_rx_buffer_selector].pkt.header)) {
crc_handler::instance().enqueue(
m_crc_slot, 1, &m_rx_buffers[m_rx_buffer_selector].pkt,
sizeof(packet_header) + round_up_to_4((uint16_t)m_rx_buffers[m_rx_buffer_selector].pkt.header.payload_length), this,
m_rx_buffer_selector);
} else {
++m_stats.hdr_error;
m_rx_buffers[m_rx_buffer_selector].error = true;
}
switch_rx_buffers();
}
void packet_usart::rx_dma_half_transfer(void) {}
void packet_usart::rx_dma_error(dma_helper::dma_error_type reason)
{
(void)reason;
m_rx_buffers[m_rx_buffer_selector].error = 1;
++m_stats.rx_dma_error;
switch_rx_buffers();
}
void packet_usart::tx_dma_transfer_complete(void)
{
LL_USART_EnableIT_TC(m_usart);
LL_DMA_DisableStream(m_tx_dma.get_dma(), m_tx_dma.get_stream());
}
void packet_usart::tx_dma_half_transfer(void) {}
void packet_usart::tx_dma_error(dma_helper::dma_error_type reason)
{
(void)reason;
m_tx_buffer.error = 1;
++m_stats.tx_dma_error;
}
///////////////////////
// Private functions //
///////////////////////
void packet_usart::build_header(packet &packet, uint8_t serial_nr, uint8_t length)
{
uint8_t hash = STARTMARKER;
packet.header.start_byte = STARTMARKER;
packet.header.serial = serial_nr;
hash ^= serial_nr;
packet.header.payload_length = length;
hash ^= length;
packet.header.hash = hash;
}
bool packet_usart::check_header(packet_header &header)
{
return header.start_byte == STARTMARKER && (header.start_byte ^ header.serial ^ header.payload_length) == header.hash;
}
void packet_usart::switch_rx_buffers(void)
{
++m_stats.rcvd;
m_rx_buffer_selector = !m_rx_buffer_selector;
if (m_rx_buffers[m_rx_buffer_selector].busy) {
++m_stats.overrun;
}
setup_receive();
}
///////////////////////////
// crc_handler::ICallback //
///////////////////////////
void packet_usart::crc_succeeded(uintptr_t callback_param, uint32_t crc, uint8_t task)
{
(void)task;
Buffer &buf(m_rx_buffers[static_cast<int>(callback_param)]);
buf.busy = 1;
if (*(uint32_t *)(buf.pkt.payload + round_up_to_4((uint16_t)buf.pkt.header.payload_length)) != crc) {
buf.error = 1;
buf.error_info = crc;
++m_stats.payload_errror;
}
if (m_user_callback) {
buf.busy = !m_user_callback->packet_received(this, m_user_callback_param, buf.pkt);
}
}
void packet_usart::crc_failed(uintptr_t callback_param, uint32_t crc, uint8_t task)
{
(void)crc;
(void)task;
Buffer &buf(m_rx_buffers[static_cast<int>(callback_param)]);
buf.busy = buf.error = true;
buf.error_info = 0;
++m_stats.payload_errror;
if (m_user_callback) {
buf.busy = !m_user_callback->packet_received(this, m_user_callback_param, buf.pkt);
}
}
} // namespace f4ll