subrepo: subdir: "components/f4ll" merged: "a4df325" upstream: origin: "ssh://git@codeberg.org/abody/f4ll.git" branch: "master" commit: "a4df325" git-subrepo: version: "0.4.9" origin: "???" commit: "???"
212 lines
6 KiB
C++
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
|