diff --git a/inc/f4ll/console_handler.h b/inc/f4ll/console_handler.h index 34da08c..9972539 100644 --- a/inc/f4ll/console_handler.h +++ b/inc/f4ll/console_handler.h @@ -5,10 +5,10 @@ * Author: abody */ -#ifndef LL_CONSOLEHANDLER_H_ -#define LL_CONSOLEHANDLER_H_ +#pragma once #include +#include #include namespace f4ll { @@ -18,7 +18,9 @@ class console_handler : public usart_core, public singleton friend class singleton; public: - void PrintStats(uint8_t id, packet_usart &usart); + void print(char const *s); + void flush(); + size_t append(char const *s); private: console_handler(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx); @@ -35,10 +37,8 @@ private: virtual void tx_dma_half_transfer(void) override; virtual void tx_dma_error(dma_helper::dma_error_type reason) override; - char m_buffer[128]; - uint16_t m_used = 0; + ringbuffer<128> m_tx_buffer; + iringbuffer::size_type m_in_flight_size = 0; }; } /* namespace f4ll */ - -#endif /* LL_CONSOLEHANDLER_H_ */ diff --git a/inc/f4ll/ringbuffer.h b/inc/f4ll/ringbuffer.h new file mode 100644 index 0000000..acf39d1 --- /dev/null +++ b/inc/f4ll/ringbuffer.h @@ -0,0 +1,218 @@ +/* + * ringbuffer.h + * + * Created on: May 31, 2025 + * Author: Attila Body + */ + +#pragma once + +#include +#include +#include + +namespace f4ll { + +class iringbuffer +{ +public: + using size_type = size_t; + + /// @brief Copies data to the ring buffer (without committing it) + /// @param data Pointer to the data to copy + /// @param len Length of the data to copy + /// @retval Length of the data copied (differs when not all data can fit) + virtual size_type put(uint8_t const *data, size_type len) = 0; + + /// @brief Commits the data already placed into the buffer and notifies the + /// consumer about it's availability + virtual void commit() = 0; + + /// @brief Waits until all the data from the ring buffer gets consumed. + // void flush(); + + /// @brief Gets a pointer to the next chunk of committed data in the buffer + /// without registering the consumption. + /// The caller should also call report_consumption using the returned + /// chunk length after it finished processing the data. + /// @param[in] len_requested Length of the data requested from the buffer. + /// The length of the actual data provided + /// might be actually smaller (because either reaching the end of + /// the buffer or not enough data in the buffer). + /// @param[out] data Receives a pointer to the first byte of the available + /// data in the buffer + /// @param[out] len Receives the length of the chunk available in the buffer. + /// Will not exceed len_requested. + /// @retval true if the buffer has more available data, false otherwise. + virtual bool get_chunk(size_type len_requested, uint8_t const *&data, size_type &len) const = 0; + + /// @brief Marks the chunk returned by ringbuffer_GetChunk as available. + /// @param consumed The length of the chunk as returned by + /// ringbuffer_GetChunk(..., len) + virtual void consumed(size_type len) = 0; + + /// @brief Returns the number of uncommited bytes in the ring buffer. + virtual size_type uncommited() const = 0; + + /// @brief Returns the number of commited bytes in the ring buffer. + virtual size_type commited() const = 0; + + /// @brief Discards the uncommited data in the ring buffer. + virtual void discard() = 0; + + /// @brief Returns the size of the internal buffer. + /// One byte in the buffer is always reserved as separator + virtual size_type size() const = 0; + + /// @brief Returns the size of the used place in the buffer + // (including non-commited data) + virtual size_type used() const = 0; + + /// @brief Returns the free storage capacity of the buffer + virtual size_type unused() const = 0; +}; + +template class ringbuffer : public iringbuffer +{ +public: + ringbuffer() = default; + + size_type put(uint8_t const *data, size_type len) override; + void commit() override; + bool get_chunk(size_type len_requested, uint8_t const *&data, size_type &len) const override; + void consumed(size_type len) override; + size_type uncommited() const override; + size_type commited() const override; + void discard() override; + size_type size() const override; + size_type used() const override; + size_type unused() const override; + +private: + uint8_t m_buffer[SZ]; //!< Data bufer + size_type m_head = 0; //!< Write position + size_type m_head_shadow = 0; //!< Shadowed write position for collecting data + //!< before committing it + size_type m_tail = 0; //!< Read position + + size_type marker_diff(size_type m1, size_type m2) const; +}; + +// +template iringbuffer::size_type ringbuffer::put(uint8_t const *data, size_type len) +{ + size_type chunk1 = 0; + size_type chunk2 = 0; + + if (!data || !len) { + return 0; + } + + size_type max_len = unused(); + len = len < max_len ? len : max_len; + + chunk1 = sizeof(m_buffer) - m_head_shadow; + if (chunk1 >= len) { + chunk1 = len; + } else { + chunk2 = len - chunk1; + } + + std::memcpy(m_buffer + m_head_shadow, data, chunk1); + m_head_shadow += chunk1; + if (m_head_shadow == sizeof(m_buffer)) { + m_head_shadow = 0; + } + + if (chunk2) { + std::memcpy(m_buffer, data + chunk1, chunk2); + m_head_shadow += chunk2; + if (m_head_shadow == sizeof(m_buffer)) { + m_head_shadow = 0; + } + } + + return len; +} + +template void ringbuffer::commit() +{ + m_head = m_head_shadow; +}; + +template bool ringbuffer::get_chunk(size_type len_requested, uint8_t const *&data, size_type &len) const +{ + if (!len_requested) { + return false; + } + + size_type head = m_head; + size_type tail = m_tail; + size_type chunk_size = head >= tail ? head - tail : sizeof(m_buffer) - tail; + + if (!chunk_size) { + len = 0; + return false; + } + + if (chunk_size > len_requested) { + chunk_size = len_requested; + } + data = m_buffer + tail; + len = chunk_size; + + tail += chunk_size; + if (tail == sizeof(m_buffer)) { + tail = 0; + } + + return tail != head; +} + +template void ringbuffer::consumed(size_type len) +{ + if (!len) { + return; + } + m_tail += len; + if (m_tail == sizeof(m_buffer)) { + m_tail = 0; + } +} + +template iringbuffer::size_type ringbuffer::uncommited() const +{ + return marker_diff(m_head_shadow, m_head); +} + +template iringbuffer::size_type ringbuffer::commited() const +{ + return marker_diff(m_head, m_tail); +} + +template void ringbuffer::discard() +{ + m_head_shadow = m_head; +} + +template iringbuffer::size_type ringbuffer::size() const +{ + return SZ; +} + +template iringbuffer::size_type ringbuffer::used() const +{ + return marker_diff(m_head_shadow, m_tail); +} + +template iringbuffer::size_type ringbuffer::unused() const +{ + return SZ - used() - 1; +} + +template iringbuffer::size_type ringbuffer::marker_diff(size_type m1, size_type m2) const +{ + return (m1 >= m2) ? (m1 - m2) : (SZ - m2 + m1); +} + +} // namespace f1ll \ No newline at end of file diff --git a/src/console_handler.cpp b/src/console_handler.cpp index 02c990f..694e107 100644 --- a/src/console_handler.cpp +++ b/src/console_handler.cpp @@ -6,8 +6,11 @@ */ #include +#include #include +#include + namespace f4ll { console_handler::console_handler(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_t stream_rx, uint32_t stream_tx) @@ -17,7 +20,19 @@ console_handler::console_handler(USART_TypeDef *usart, DMA_TypeDef *dma, uint32_ void console_handler::receiver_idle(void) {} -void console_handler::transmission_complete(void) {} +void console_handler::transmission_complete(void) +{ + m_tx_buffer.consumed(m_in_flight_size); + if (m_tx_buffer.commited()) { + uint8_t const *chunk; + m_tx_buffer.get_chunk(m_tx_buffer.size(), chunk, m_in_flight_size); + if (m_in_flight_size) { + setup_transmit(chunk, m_in_flight_size); + } + } else { + m_in_flight_size = 0; + } +} void console_handler::framing_error(void) {} @@ -45,35 +60,41 @@ void console_handler::tx_dma_error(dma_helper::dma_error_type reason) (void)reason; } -#define ADDINFO(b, s, u) \ - b += strcpy_ex(b, s); \ - b += uitodec(b, u); - -void console_handler::PrintStats(uint8_t id, packet_usart &usart) +size_t console_handler::append(char const *s) { - char ids[] = " : "; - char *buffer = m_buffer; - packet_usart::stats const &stats(usart.get_stats()); + size_t len = strlen(s); + if (!len) { + return 0; + } + return len - m_tx_buffer.put(reinterpret_cast(s), len); +} - ids[0] = id + '0'; - buffer += strcpy_ex(buffer, ids); - ADDINFO(buffer, " s: ", stats.sent); - ADDINFO(buffer, " r: ", stats.rcvd); - ADDINFO(buffer, " sk: ", stats.skiped); - ADDINFO(buffer, " or: ", stats.overrun); - ADDINFO(buffer, " he: ", stats.hdr_error); - ADDINFO(buffer, " pe: ", stats.payload_errror); - buffer += strcpy_ex(buffer, ",0x"); - buffer += uitohex(buffer, stats.pep1, 8); - buffer += strcpy_ex(buffer, ",0x"); - buffer += uitohex(buffer, stats.pep2, 8); - ADDINFO(buffer, " rde: ", stats.rx_dma_error); - ADDINFO(buffer, " tde: ", stats.tx_dma_error); - ADDINFO(buffer, " pmh: ", stats.premature_hdr); - ADDINFO(buffer, " pmp: ", stats.premature_payload); - buffer += strcpy_ex(buffer, "\r\n"); +void console_handler::flush() +{ + bool busy; - setup_transmit(m_buffer, buffer - m_buffer + 1); + if (!m_tx_buffer.uncommited()) { + return; + } + m_tx_buffer.commit(); + { + irq_lock l; + busy = m_in_flight_size != 0; + } + if (busy) { + return; + } + uint8_t const *chunk; + m_tx_buffer.get_chunk(m_tx_buffer.size(), chunk, m_in_flight_size); + if (m_in_flight_size) { + setup_transmit(chunk, m_in_flight_size); + } +} + +void console_handler::print(char const *s) +{ + append(s); + flush(); } } /* namespace f4ll */