libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
mspm0_uart.hpp
1#pragma once
2
3#include <ti/driverlib/dl_dma.h>
4#include <ti/driverlib/dl_uart_main.h>
5
6#include <array>
7#include <atomic>
8#include <cstddef>
9#include <cstdint>
10
11#include "libxr_type.hpp"
12#include "serialized_service.hpp"
13#include "ti_msp_dl_config.h"
14#include "uart.hpp"
15
16namespace LibXR
17{
18
19class MSPM0UART;
20
25{
26 public:
28 explicit MSPM0UARTReadPort(size_t capacity) : ReadPort(capacity) {}
29
31 void SetOwner(MSPM0UART* owner) { owner_ = owner; }
32
33 protected:
35 void OnReadQueueSpaceAvailable(bool in_isr) override;
36
37 private:
38 MSPM0UART* owner_ = nullptr;
39};
40
65class MSPM0UART : public UART
66{
67 friend class MSPM0UARTReadPort;
68
69 public:
71 enum class RxMode : uint8_t
72 {
75 };
76
78 struct Resources
79 {
81 UART_Regs* instance;
83 IRQn_Type irqn;
85 uint32_t clock_freq;
87 uint8_t index;
100 };
101
103 static constexpr uint8_t INVALID_DMA_CHANNEL = 0xFFU;
104
121 MSPM0UART(Resources res, RawData tx_dma_storage, RawData rx_dma_storage,
122 uint32_t tx_queue_size = 5U, uint32_t rx_queue_capacity = 128U,
123 UART::Configuration config = {115200, UART::Parity::NO_PARITY, 8U, 1U});
124
136 ErrorCode SetConfig(UART::Configuration config, bool in_isr = false) override;
137
139 static void WriteFun(WritePort& port, bool in_isr);
141 static void OnInterrupt(uint8_t index);
142
150 static void OnDmaInterrupt();
151
154 static UART::Configuration BuildConfigFromSysCfg(UART_Regs* instance,
155 uint32_t baudrate);
156
158 [[nodiscard]] RxMode GetRxMode() const { return res_.rx_mode; }
160 [[nodiscard]] bool RxHalfInterruptEnabled() const { return res_.rx_half_interrupt; }
161
162 MSPM0UARTReadPort _read_port; // NOLINT(readability-identifier-naming)
163 WritePort _write_port; // NOLINT(readability-identifier-naming)
164
166 static constexpr uint8_t ResolveIndex(IRQn_Type irqn)
167 {
168 switch (irqn)
169 {
170#if defined(UART0_BASE)
171 case UART0_INT_IRQn:
172 return 0U;
173#endif
174#if defined(UART1_BASE)
175 case UART1_INT_IRQn:
176 return 1U;
177#endif
178#if defined(UART2_BASE)
179 case UART2_INT_IRQn:
180 return 2U;
181#endif
182#if defined(UART3_BASE)
183 case UART3_INT_IRQn:
184 return 3U;
185#endif
186#if defined(UART4_BASE)
187 case UART4_INT_IRQn:
188 return 4U;
189#endif
190#if defined(UART5_BASE)
191 case UART5_INT_IRQn:
192 return 5U;
193#endif
194#if defined(UART6_BASE)
195 case UART6_INT_IRQn:
196 return 6U;
197#endif
198#if defined(UART7_BASE)
199 case UART7_INT_IRQn:
200 return 7U;
201#endif
202 default:
203 return INVALID_DMA_CHANNEL;
204 }
205 }
206
207 private:
209 enum class ConfigState : uint32_t
210 {
211 EMPTY = 0U,
212 RESERVED = 1U,
213 PUBLISHED = 2U,
214 };
215
216 static constexpr uint8_t MAX_UART_INSTANCES = 8U;
217 static_assert(DMA_SYS_N_DMA_CHANNEL <= 16U);
218 static constexpr uint8_t MAX_DMA_CHANNELS = static_cast<uint8_t>(DMA_SYS_N_DMA_CHANNEL);
219 static constexpr uint32_t MSPM0_UART_DMA_MAX_TRANSFER_SIZE = 0xFFFFU;
220
221 static constexpr uint32_t EVENT_WRITE = 1U << 0U;
222 static constexpr uint32_t EVENT_DMA_DONE_TX = 1U << 1U;
223 static constexpr uint32_t EVENT_EOT_DONE = 1U << 2U;
224 static constexpr uint32_t EVENT_CONFIG = 1U << 3U;
225 static constexpr uint32_t EVENT_RX_WORK = 1U << 4U;
226
227 static constexpr uint32_t RX_ERROR_INTERRUPT_MASK =
228 DL_UART_INTERRUPT_OVERRUN_ERROR | DL_UART_INTERRUPT_BREAK_ERROR |
229 DL_UART_INTERRUPT_PARITY_ERROR | DL_UART_INTERRUPT_FRAMING_ERROR |
230 DL_UART_INTERRUPT_NOISE_ERROR;
231
232 static constexpr uint32_t RX_INTERRUPT_MASK = DL_UART_INTERRUPT_RX;
233 static constexpr uint32_t RX_TIMEOUT_INTERRUPT_MASK =
234 DL_UART_INTERRUPT_RX_TIMEOUT_ERROR;
235 static constexpr uint32_t RX_GAP_INTERRUPT_MASK = DL_UART_INTERRUPT_LINC0_MATCH;
236 static constexpr uint32_t TX_DONE_INTERRUPT_MASK = DL_UART_INTERRUPT_DMA_DONE_TX;
237 static constexpr uint32_t EOT_INTERRUPT_MASK = DL_UART_INTERRUPT_EOT_DONE;
238
240 void HandleService(uint32_t events, bool in_isr);
242 void FillTx(bool in_isr);
244 void HandleTxDone(bool in_isr);
246 void HandleRxWork(bool in_isr);
248 void HandleMainRx(bool in_isr);
250 void HandleExtendRx(bool in_isr);
252 void NotifyReadSpace(bool in_isr);
254 void HandleUartInterrupt();
256 void HandleRxDmaInterrupt();
257
259 [[nodiscard]] ErrorCode ValidateConfig(UART::Configuration config) const;
261 void ApplyConfig(UART::Configuration config);
263 void TryApplyPublishedConfig(bool in_isr);
265 void ConfigureTxDma();
267 void ConfigureRxDma();
269 void StartDataPath();
271 void StopDataPath();
273 void DiscardRxFifo();
275 void StartTxDma(uint8_t half, size_t size);
276
278 [[nodiscard]] uint8_t* TxHalf(uint8_t half) const
279 {
280 return static_cast<uint8_t*>(tx_dma_storage_.addr_) +
281 static_cast<size_t>(half) * tx_half_size_;
282 }
283
285 [[nodiscard]] size_t RxCapacity() const { return rx_dma_storage_.size_; }
286
288 static uint32_t DmaCompleteMask(uint8_t channel)
289 {
290 return channel < MAX_DMA_CHANNELS ? (1UL << channel) : 0U;
291 }
292
294 static uint32_t DmaEarlyMask(uint8_t channel)
295 {
296 return channel < 8U ? (1UL << (16U + channel)) : 0U;
297 }
298
300 static uint32_t DmaRawMask(uint8_t channel, bool half)
301 {
302 return DmaCompleteMask(channel) | (half ? DmaEarlyMask(channel) : 0U);
303 }
304
306 Resources res_{};
308 RawData tx_dma_storage_{};
310 RawData rx_dma_storage_{};
312 size_t tx_half_size_ = 0U;
314 std::array<size_t, 2U> tx_half_size_used_{};
316 int8_t active_half_ = -1;
319 bool tx_draining_ = false;
321 size_t rx_dma_cursor_ = 0U;
322
324 SerializedService service_{};
326 std::atomic<ConfigState> config_state_{ConfigState::EMPTY};
328 UART::Configuration pending_config_{};
329
331 static MSPM0UART* instance_map_[MAX_UART_INSTANCES];
332};
333
334namespace Detail
335{
336
337// 以下构造辅助函数检查 BSP 声明的 DMA 归属,不增加运行时资源状态。
338// These helpers check BSP-declared DMA ownership without adding runtime state.
339template <IRQn_Type UartIrqn, uint32_t TxDmaChannel, IRQn_Type TxDmaOwnerIrqn,
340 bool TxBinding>
341MSPM0UART::Resources MakeMSPM0MainUartResources(UART_Regs* instance, uint32_t clock_freq,
342 uint8_t tx_dma_trigger)
343{
344 static_assert(TxBinding);
345 static_assert(TxDmaOwnerIrqn == UartIrqn);
346 static_assert(TxDmaChannel < DMA_SYS_N_DMA_CHANNEL);
347 return {instance,
348 UartIrqn,
349 clock_freq,
350 MSPM0UART::ResolveIndex(UartIrqn),
352 false,
353 static_cast<uint8_t>(TxDmaChannel),
354 tx_dma_trigger,
356 0U};
357}
358
359template <IRQn_Type UartIrqn, uint32_t TxDmaChannel, IRQn_Type TxDmaOwnerIrqn,
360 bool TxBinding, uint32_t RxDmaChannel, IRQn_Type RxDmaOwnerIrqn, bool RxBinding,
361 bool ExtendCapable, bool FullRxChannel, bool HalfRxInterrupt>
362MSPM0UART::Resources MakeMSPM0ExtendUartResources(UART_Regs* instance,
363 uint32_t clock_freq,
364 uint8_t tx_dma_trigger,
365 uint8_t rx_dma_trigger)
366{
367 static_assert(TxBinding);
368 static_assert(RxBinding);
369 static_assert(TxDmaOwnerIrqn == UartIrqn);
370 static_assert(RxDmaOwnerIrqn == UartIrqn);
371 static_assert(TxDmaChannel < DMA_SYS_N_DMA_CHANNEL);
372 static_assert(RxDmaChannel < DMA_SYS_N_DMA_FULL_CHANNEL);
373 static_assert(TxDmaChannel != RxDmaChannel);
374 static_assert(ExtendCapable);
375 static_assert(FullRxChannel);
376 static_assert(!HalfRxInterrupt || RxDmaChannel < 8U);
377 return {instance,
378 UartIrqn,
379 clock_freq,
380 MSPM0UART::ResolveIndex(UartIrqn),
382 HalfRxInterrupt,
383 static_cast<uint8_t>(TxDmaChannel),
384 tx_dma_trigger,
385 static_cast<uint8_t>(RxDmaChannel),
386 rx_dma_trigger};
387}
388
389} // namespace Detail
390
391#define LIBXR_MSPM0_UART_CAT_IMPL(left, right) left##right
392#define LIBXR_MSPM0_UART_CAT(left, right) LIBXR_MSPM0_UART_CAT_IMPL(left, right)
393#define LIBXR_MSPM0_UART_PROPERTY(name, suffix) LIBXR_MSPM0_UART_CAT(name, suffix)
394#define LIBXR_MSPM0_UART_DMA_CHANNEL(name) LIBXR_MSPM0_UART_CAT(name, _CHAN_ID)
395#define LIBXR_MSPM0_UART_DMA_PROPERTY(name, suffix) LIBXR_MSPM0_UART_CAT(name, suffix)
396#define LIBXR_MSPM0_UART_DMA_TRIGGER_IMPL(instance, direction) \
397 DMA_##instance##_##direction##_TRIG
398#define LIBXR_MSPM0_UART_DMA_TRIGGER(instance, direction) \
399 LIBXR_MSPM0_UART_DMA_TRIGGER_IMPL(instance, direction)
400
411#define MSPM0_UART_MAIN_INIT(name, tx_dma, tx_storage_addr, tx_storage_size, \
412 tx_queue_size, rx_queue_capacity) \
413 ::LibXR::Detail::MakeMSPM0MainUartResources< \
414 name##_INST_INT_IRQN, static_cast<uint32_t>(LIBXR_MSPM0_UART_DMA_CHANNEL(tx_dma)), \
415 LIBXR_MSPM0_UART_DMA_PROPERTY(tx_dma, _LIBXR_UART_IRQN), \
416 static_cast<bool>(LIBXR_MSPM0_UART_DMA_PROPERTY(tx_dma, _LIBXR_UART_TX))>( \
417 name##_INST, name##_INST_FREQUENCY, \
418 static_cast<uint8_t>(LIBXR_MSPM0_UART_DMA_TRIGGER(name##_INST, TX))), \
419 ::LibXR::RawData{static_cast<void*>(tx_storage_addr), (tx_storage_size)}, \
420 ::LibXR::RawData{}, (tx_queue_size), (rx_queue_capacity), \
421 ::LibXR::MSPM0UART::BuildConfigFromSysCfg(name##_INST, \
422 static_cast<uint32_t>(name##_BAUD_RATE))
423
435#define MSPM0_UART_EXTEND_INIT(name, tx_dma, rx_dma, tx_storage_addr, tx_storage_size, \
436 tx_queue_size, rx_dma_storage_addr, rx_dma_storage_size, \
437 rx_queue_capacity) \
438 ::LibXR::Detail::MakeMSPM0ExtendUartResources< \
439 name##_INST_INT_IRQN, static_cast<uint32_t>(LIBXR_MSPM0_UART_DMA_CHANNEL(tx_dma)), \
440 LIBXR_MSPM0_UART_DMA_PROPERTY(tx_dma, _LIBXR_UART_IRQN), \
441 static_cast<bool>(LIBXR_MSPM0_UART_DMA_PROPERTY(tx_dma, _LIBXR_UART_TX)), \
442 static_cast<uint32_t>(LIBXR_MSPM0_UART_DMA_CHANNEL(rx_dma)), \
443 LIBXR_MSPM0_UART_DMA_PROPERTY(rx_dma, _LIBXR_UART_IRQN), \
444 static_cast<bool>(LIBXR_MSPM0_UART_DMA_PROPERTY(rx_dma, _LIBXR_UART_RX)), \
445 static_cast<bool>(LIBXR_MSPM0_UART_PROPERTY(name, _LIBXR_EXTEND_CAPABLE)), \
446 static_cast<bool>(LIBXR_MSPM0_UART_DMA_PROPERTY(rx_dma, _LIBXR_FULL_CHANNEL)), \
447 static_cast<bool>(LIBXR_MSPM0_UART_DMA_PROPERTY(rx_dma, _LIBXR_HALF_INTERRUPT))>( \
448 name##_INST, name##_INST_FREQUENCY, \
449 static_cast<uint8_t>(LIBXR_MSPM0_UART_DMA_TRIGGER(name##_INST, TX)), \
450 static_cast<uint8_t>(LIBXR_MSPM0_UART_DMA_TRIGGER(name##_INST, RX))), \
451 ::LibXR::RawData{static_cast<void*>(tx_storage_addr), (tx_storage_size)}, \
452 ::LibXR::RawData{static_cast<void*>(rx_dma_storage_addr), (rx_dma_storage_size)}, \
453 (tx_queue_size), (rx_queue_capacity), \
454 ::LibXR::MSPM0UART::BuildConfigFromSysCfg(name##_INST, \
455 static_cast<uint32_t>(name##_BAUD_RATE))
456
457} // namespace LibXR
使用双缓冲 DMA 发送的 MSPM0 串口 / MSPM0 UART with double-buffered DMA TX.
static constexpr uint8_t ResolveIndex(IRQn_Type irqn)
根据 UART 中断号取得实例编号 / Resolve an instance index from its UART IRQ.
static void OnDmaInterrupt()
分派本驱动登记的接收 DMA 中断 / Dispatch registered UART RX DMA interrupts.
static constexpr uint8_t INVALID_DMA_CHANNEL
无 DMA 通道的标记 / Sentinel for no DMA channel.
bool RxHalfInterruptEnabled() const
查询是否使用接收半传输通知 / Check RX half-transfer notification use.
ErrorCode SetConfig(UART::Configuration config, bool in_isr=false) override
提交串口配置 / Submit a UART configuration.
static UART::Configuration BuildConfigFromSysCfg(UART_Regs *instance, uint32_t baudrate)
MSPM0UART(Resources res, RawData tx_dma_storage, RawData rx_dma_storage, uint32_t tx_queue_size=5U, uint32_t rx_queue_capacity=128U, UART::Configuration config={115200, UART::Parity::NO_PARITY, 8U, 1U})
构造串口并配置所选 DMA 路径 / Construct the UART and selected DMA paths.
RxMode
接收路径 / RX path.
@ EXTEND_DMA
扩展 UART DMA / Extend UART DMA.
@ MAIN_BYTE_IRQ
主 UART 字节中断 / Main UART byte interrupts.
static void OnInterrupt(uint8_t index)
按实例编号分派 UART 中断 / Dispatch a UART IRQ by instance index.
RxMode GetRxMode() const
获取构造时选择的接收路径 / Get the selected RX path.
通知串口恢复接收的读端口 / Read port that resumes UART reception.
void OnReadQueueSpaceAvailable(bool in_isr) override
发布接收空间可用通知 / Publish RX space availability.
void SetOwner(MSPM0UART *owner)
在串口初始化时绑定后端 / Bind the backend during UART initialization.
MSPM0UARTReadPort(size_t capacity)
按字节容量构造接收队列 / Construct an RX queue with the byte capacity.
可写原始数据视图 / Mutable raw data view
size_t size_
数据字节数 / Data size in bytes
void * addr_
数据起始地址 / Data start address
基于 SPSC 字节队列的读端口 / Read endpoint backed by an SPSC byte queue.
Definition read_port.hpp:30
通用异步收发传输(UART)基类 / Abstract base class for Universal Asynchronous Receiver-Transmitter (UART)
Definition uart.hpp:19
@ NO_PARITY
无校验 / No parity
基于 SPSC 字节队列的写端口 / Write endpoint backed by an SPSC byte queue.
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ EMPTY
为空 / Empty
void(* WriteFun)(WritePort &port, bool in_isr)
写入进度通知函数类型 / Write progress notification function type.
由 BSP 提供的 UART 与 DMA 绑定 / UART and DMA bindings supplied by the BSP.
uint8_t dma_rx_channel
接收 FULL-DMA 通道,Main 使用无效编号 / RX full channel, invalid for Main.
RxMode rx_mode
选择的接收路径 / Selected RX path.
uint32_t clock_freq
UART 输入时钟频率 / UART input clock frequency.
uint8_t dma_rx_trigger
UART 接收 DMA 触发源,Main 为零 / RX DMA trigger, zero for Main.
UART_Regs * instance
UART 寄存器 / UART registers.
bool rx_half_interrupt
是否使用接收半传输中断 / Enable RX half-transfer interrupts.
IRQn_Type irqn
UART 中断号 / UART interrupt number.
uint8_t index
ResolveIndex 返回的编号 / Index returned by ResolveIndex.
uint8_t dma_tx_channel
发送 DMA 通道 / TX DMA channel.
uint8_t dma_tx_trigger
UART 发送 DMA 触发源 / UART TX DMA trigger.
UART 配置结构体 / UART configuration structure.
Definition uart.hpp:44