/* * ll_hsusart_impl.h * * Created on: Oct 29, 2019 * Author: abody */ #include #include namespace f4ll { template 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(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(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(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