| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| #pragma once |
| |
| #include <atomic> |
| |
| #include "fsl_dma.h" |
| #include "fsl_inputmux.h" |
| #include "fsl_usart_dma.h" |
| #include "pw_bytes/byte_builder.h" |
| #include "pw_bytes/span.h" |
| #include "pw_status/status.h" |
| #include "pw_stream/stream.h" |
| #include "pw_sync/interrupt_spin_lock.h" |
| #include "pw_sync/thread_notification.h" |
| |
| namespace pw::stream { |
| |
| class UartDmaStreamMcuxpresso final : public NonSeekableReaderWriter { |
| public: |
| // Configuration structure |
| struct Config { |
| USART_Type* usart_base; // Base of USART control struct |
| uint32_t baud_rate; // Desired communication speed |
| usart_parity_mode_t parity; // Parity setting |
| usart_stop_bit_count_t stop_bits; // Number of stop bits to use |
| DMA_Type* dma_base; // Base of DMA control struct |
| uint32_t rx_dma_ch; // Receive DMA channel |
| uint32_t tx_dma_ch; // Transmit DMA channel |
| inputmux_signal_t rx_input_mux_dmac_ch_request_en; // Rx input mux signal |
| inputmux_signal_t tx_input_mux_dmac_ch_request_en; // Tx input mux signal |
| ByteSpan buffer; // Receive ring buffer |
| }; |
| |
| UartDmaStreamMcuxpresso(const Config& config) |
| : config_(config), rx_data_{.ring_buffer = config.buffer} {} |
| |
| ~UartDmaStreamMcuxpresso(); |
| |
| UartDmaStreamMcuxpresso(const UartDmaStreamMcuxpresso& other) = delete; |
| |
| UartDmaStreamMcuxpresso& operator=(const UartDmaStreamMcuxpresso& other) = |
| delete; |
| |
| pw::Status Init(uint32_t srcclk); |
| |
| private: |
| // Usart DMA TX data structure |
| struct UsartDmaTxData { |
| ConstByteSpan buffer; // TX transaction buffer |
| size_t tx_idx; // Position within TX transaction |
| dma_handle_t dma_handle; // DMA handle |
| usart_transfer_t transfer; // USART TX transfer structure |
| std::atomic_uint8_t busy; // Flag to prevent concurrent access to TX queue |
| pw::sync::ThreadNotification notification; // TX completion notification |
| }; |
| |
| // Usart DMA RX data structure |
| struct UsartDmaRxData { |
| ByteSpan ring_buffer; // Receive ring buffer |
| size_t ring_buffer_read_idx; // ring buffer reader index |
| size_t ring_buffer_write_idx; // ring buffer writer index |
| size_t |
| data_received; // data received and acknowledged by completion callback |
| size_t data_copied; // data copied out to receiver |
| // completion callback will be executed when completion size decreases to 0 |
| // bytes |
| size_t completion_size; |
| usart_transfer_t transfer; // USART RX transfer structure |
| dma_handle_t dma_handle; // DMA handle |
| std::atomic_uint8_t |
| busy; // Flag to prevent concurrent access to RX ring buffer |
| pw::sync::ThreadNotification notification; // RX completion notification |
| }; |
| |
| // Since we are calling USART_TransferGetReceiveCountDMA we may only |
| // transfer DMA_MAX_TRANSFER_COUNT - 1 bytes per DMA transfer. |
| static constexpr size_t kUsartDmaMaxTransferCount = |
| DMA_MAX_TRANSFER_COUNT - 1; |
| |
| // A reader may at most wait for 25% of the ring buffer size before data |
| // needs to be copied out to the caller. |
| static constexpr size_t kUsartRxRingBufferSplitCount = 4; |
| |
| StatusWithSize DoRead(ByteSpan) override; |
| Status DoWrite(ConstByteSpan) override; |
| |
| // Helper functions |
| static IRQn_Type GetInterrupt(const DMA_Type* base); |
| void Deinit(); |
| void TriggerReadDma() PW_EXCLUSIVE_LOCKS_REQUIRED(interrupt_lock_); |
| void TriggerWriteDma(); |
| |
| StatusWithSize TransferGetReceiveDMACount() |
| PW_LOCKS_EXCLUDED(interrupt_lock_); |
| StatusWithSize TransferGetReceiveDMACountLockHeld() |
| PW_EXCLUSIVE_LOCKS_REQUIRED(interrupt_lock_); |
| size_t GetReceiveTransferRemainingBytes(); |
| static void TxRxCompletionCallback(USART_Type* base, |
| usart_dma_handle_t* state, |
| status_t status, |
| void* param); |
| Status WaitForReceiveBytes(size_t bytes_needed); |
| void CopyReceiveData(ByteBuilder& bb, size_t copy_size); |
| |
| pw::sync::InterruptSpinLock |
| interrupt_lock_; // Lock to synchronize with interrupt handler and to |
| // guarantee exclusive access to DMA control registers |
| usart_dma_handle_t uart_dma_handle_; // USART DMA Handle |
| struct UsartDmaTxData tx_data_; // TX data |
| struct UsartDmaRxData rx_data_; // RX data |
| |
| Config const config_; // USART DMA configuration |
| bool |
| initialized_; // Whether the USART and DMA channels have been initialized |
| }; |
| |
| } // namespace pw::stream |