libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
stm32_uart_gpdma.cpp
1#include "stm32_uart_gpdma.hpp"
2
3#if defined(LIBXR_STM32_UART_GPDMA) && defined(HAL_UART_MODULE_ENABLED)
4
5#include "libxr_assert.hpp"
6#include "stm32_dcache.hpp"
7
8namespace LibXR
9{
10namespace
11{
12
13IRQn_Type GetGpdmaIrq(DMA_Channel_TypeDef* instance)
14{
15#define LIBXR_GPDMA_IRQ_CASE(DMA, CHANNEL) \
16 if (instance == DMA##_Channel##CHANNEL) \
17 { \
18 return DMA##_Channel##CHANNEL##_IRQn; \
19 }
20
21// 按首通道宏判断通道组是否存在;IRQn 是枚举,不能用 defined 检查。
22// Detect channel groups by their first channel macro; IRQn names are enum values.
23#if defined(GPDMA1_Channel0)
24 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 0)
25 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 1)
26 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 2)
27 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 3)
28 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 4)
29 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 5)
30 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 6)
31 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 7)
32#endif
33#if defined(GPDMA1_Channel8)
34 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 8)
35 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 9)
36 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 10)
37 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 11)
38#endif
39#if defined(GPDMA1_Channel12)
40 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 12)
41 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 13)
42 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 14)
43 LIBXR_GPDMA_IRQ_CASE(GPDMA1, 15)
44#endif
45#if defined(GPDMA2_Channel0)
46 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 0)
47 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 1)
48 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 2)
49 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 3)
50 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 4)
51 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 5)
52 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 6)
53 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 7)
54#endif
55#if defined(GPDMA2_Channel8)
56 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 8)
57 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 9)
58 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 10)
59 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 11)
60#endif
61#if defined(GPDMA2_Channel12)
62 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 12)
63 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 13)
64 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 14)
65 LIBXR_GPDMA_IRQ_CASE(GPDMA2, 15)
66#endif
67
68#undef LIBXR_GPDMA_IRQ_CASE
69
70 return NonMaskableInt_IRQn;
71}
72
73class GpdmaNvicMaskGuard
74{
75 public:
76 explicit GpdmaNvicMaskGuard(DMA_HandleTypeDef* dma_handle)
77 : irq_(GetGpdmaIrq(dma_handle->Instance)),
78 valid_(static_cast<int32_t>(irq_) >= 0),
79 was_enabled_(valid_ && (NVIC_GetEnableIRQ(irq_) != 0U))
80 {
81 if (!valid_)
82 {
83 return;
84 }
85
86 if (was_enabled_)
87 {
88 NVIC_DisableIRQ(irq_);
89 __DSB();
90 __ISB();
91 }
92 }
93
94 ~GpdmaNvicMaskGuard()
95 {
96 if (valid_ && was_enabled_)
97 {
98 NVIC_EnableIRQ(irq_);
99 __DSB();
100 __ISB();
101 }
102 }
103
104 GpdmaNvicMaskGuard(const GpdmaNvicMaskGuard&) = delete;
105 GpdmaNvicMaskGuard& operator=(const GpdmaNvicMaskGuard&) = delete;
106
107 [[nodiscard]] bool Valid() const { return valid_; }
108 [[nodiscard]] bool WasEnabled() const { return was_enabled_; }
109
110 private:
111 IRQn_Type irq_;
112 bool valid_;
113 bool was_enabled_;
114};
115
116} // namespace
117
118STM32GpdmaUartAdapter::STM32GpdmaUartAdapter(UART_HandleTypeDef* uart_handle)
119{
120 ASSERT(uart_handle != nullptr);
121 ASSERT((uart_handle->hdmatx == nullptr) ||
122 (IS_GPDMA_INSTANCE(uart_handle->hdmatx->Instance) != 0U));
123 ASSERT((uart_handle->hdmarx == nullptr) ||
124 (IS_GPDMA_INSTANCE(uart_handle->hdmarx->Instance) != 0U));
125 state_.uart_handle_ = uart_handle;
126}
127
128HAL_StatusTypeDef STM32GpdmaUartAdapter::StartLinkedListDmaRx(uint8_t* buffer,
129 size_t total_size,
130 bool in_isr)
131{
132 DMA_HandleTypeDef* const dma_handle = state_.uart_handle_->hdmarx;
133 ASSERT_FROM_CALLBACK(dma_handle != nullptr, in_isr);
134 ASSERT_FROM_CALLBACK(buffer != nullptr, in_isr);
135 ASSERT_FROM_CALLBACK(total_size >= RX_NODE_COUNT, in_isr);
136 ASSERT_FROM_CALLBACK((total_size % RX_NODE_COUNT) == 0U, in_isr);
137 ASSERT_FROM_CALLBACK((total_size / RX_NODE_COUNT) <= UINT16_MAX, in_isr);
138 ASSERT_FROM_CALLBACK(IS_DMA_BLOCK_SIZE(total_size / RX_NODE_COUNT) != 0U, in_isr);
139 ASSERT_FROM_CALLBACK(dma_handle->Parent == state_.uart_handle_ &&
140 IS_GPDMA_INSTANCE(dma_handle->Instance) != 0U,
141 in_isr);
142 ASSERT_FROM_CALLBACK(
143 dma_handle->InitLinkedList.LinkStepMode == DMA_LSM_FULL_EXECUTION &&
144 dma_handle->InitLinkedList.LinkedListMode == DMA_LINKEDLIST_CIRCULAR &&
145 dma_handle->Mode == DMA_LINKEDLIST_CIRCULAR,
146 in_isr);
147
148 if (!state_.rx_queue_built_)
149 {
150 BuildRxQueue(buffer, total_size, in_isr);
151 }
152 else
153 {
154 ASSERT_FROM_CALLBACK(buffer == state_.rx_buffer_, in_isr);
155 ASSERT_FROM_CALLBACK(total_size == state_.rx_total_size_, in_isr);
156 DEV_ASSERT_FROM_CALLBACK(dma_handle->LinkedListQueue == &state_.rx_queue_, in_isr);
157 }
158
159 DEV_ASSERT_FROM_CALLBACK(StopComplete(dma_handle), in_isr);
160 FinalizeStopped(dma_handle, in_isr);
161 // 启动前将描述符与接收缓冲区同步到 DMA 可见的存储。
162 // Make descriptors and RX storage visible to DMA before starting the channel.
163 STM32_CleanDCacheByAddr(&state_, sizeof(state_));
164 STM32_CleanDCacheByAddr(buffer, total_size);
165 STM32_InvalidateDCacheByAddr(buffer, total_size);
166
167 return HAL_UARTEx_ReceiveToIdle_DMA(state_.uart_handle_, buffer,
168 static_cast<uint16_t>(state_.rx_node_size_));
169}
170
172{
173 DEV_ASSERT(state_.uart_handle_->hdmarx != nullptr);
174 const uintptr_t destination = state_.uart_handle_->hdmarx->Instance->CDAR;
175 return reinterpret_cast<uint8_t*>(destination);
176}
177
179{
180 ATOMIC_CLEAR_BIT(state_.uart_handle_->Instance->CR1, USART_CR1_TCIE);
181 const volatile uint32_t cr1 = state_.uart_handle_->Instance->CR1;
182 UNUSED(cr1);
183 __DSB();
184}
185
186bool STM32GpdmaUartAdapter::LaunchStop(DMA_HandleTypeDef* dma_handle,
187 AbortCallback callback,
188 [[maybe_unused]] bool in_isr)
189{
190 ASSERT(dma_handle != nullptr);
191 ASSERT(dma_handle->Parent == state_.uart_handle_);
192 ASSERT(callback != nullptr);
193
194 const auto abort_is_joinable = [&]()
195 {
196 if (dma_handle->XferAbortCallback == callback)
197 {
198 return true;
199 }
200
201 // 保留 HAL 因线路错误安装的 RX 中止回调,它会向同一处理器报告错误。
202 // Preserve HAL's RX line-error abort callback, which notifies the same service.
203 return (dma_handle == state_.uart_handle_->hdmarx) &&
204 (state_.uart_handle_->ErrorCode != HAL_UART_ERROR_NONE) &&
205 (dma_handle->XferAbortCallback != nullptr);
206 };
207
208 if (StopComplete(dma_handle))
209 {
210 return true;
211 }
212
213 GpdmaNvicMaskGuard irq_guard(dma_handle);
214 if (!irq_guard.Valid() || !irq_guard.WasEnabled())
215 {
216 return false;
217 }
218
219 // 屏蔽该 DMA 中断后重新检查,覆盖首次检查后已完成的中止。
220 // Recheck after masking the DMA IRQ in case it completed after the first check.
221 if (StopComplete(dma_handle))
222 {
223 return true;
224 }
225 if (dma_handle->State == HAL_DMA_STATE_ABORT)
226 {
227 return abort_is_joinable();
228 }
229 if (dma_handle->State == HAL_DMA_STATE_READY && IsStopped(dma_handle) &&
230 dma_handle->Lock == HAL_LOCKED)
231 {
232 // HAL IRQ 已停止通道,尚未解锁并回调;由该回调继续推进。
233 // The HAL IRQ has stopped the channel but has not unlocked or notified yet.
234 return true;
235 }
236 if (dma_handle->State != HAL_DMA_STATE_BUSY)
237 {
238 return false;
239 }
240
241 AbortCallback const previous_callback = dma_handle->XferAbortCallback;
242 const HAL_StatusTypeDef result = HAL_DMA_Abort_IT(dma_handle);
243 if (result == HAL_OK)
244 {
245 // DMA 中断仍被屏蔽;只有 HAL 未替换回调时才安装本后端回调。
246 // With the DMA IRQ masked, install our callback only if HAL did not replace it.
247 if (dma_handle->XferAbortCallback == previous_callback)
248 {
249 dma_handle->XferAbortCallback = callback;
250 return true;
251 }
252
253 return abort_is_joinable();
254 }
255
256 return ((dma_handle->State == HAL_DMA_STATE_ABORT) && abort_is_joinable()) ||
257 StopComplete(dma_handle);
258}
259
260bool STM32GpdmaUartAdapter::StopComplete(DMA_HandleTypeDef* dma_handle)
261{
262 if ((dma_handle == nullptr) || !IsStopped(dma_handle) ||
263 (dma_handle->State != HAL_DMA_STATE_READY) || (dma_handle->Lock != HAL_UNLOCKED))
264 {
265 return false;
266 }
267 if ((dma_handle->Mode & DMA_LINKEDLIST) == DMA_LINKEDLIST)
268 {
269 return (dma_handle->LinkedListQueue != nullptr) &&
270 (dma_handle->LinkedListQueue->State == HAL_DMA_QUEUE_STATE_READY);
271 }
272 return true;
273}
274
276{
277 return ((state_.uart_handle_->hdmatx == nullptr) ||
278 StopComplete(state_.uart_handle_->hdmatx)) &&
279 ((state_.uart_handle_->hdmarx == nullptr) ||
280 StopComplete(state_.uart_handle_->hdmarx));
281}
282
283void STM32GpdmaUartAdapter::FinalizeStopped(DMA_HandleTypeDef* dma_handle, bool in_isr)
284{
285 DEV_ASSERT_FROM_CALLBACK(StopComplete(dma_handle), in_isr);
286 DEV_ASSERT_FROM_CALLBACK(dma_handle->Lock == HAL_UNLOCKED, in_isr);
287
288 DisableInterrupts(dma_handle);
289 if ((dma_handle->Mode & DMA_LINKEDLIST) == DMA_LINKEDLIST)
290 {
291 // 错误中断可能保留 CBR1;清零后下次启动才能从链表头装载。
292 // Error IRQs may leave CBR1 set; clear it so the next start reloads the list head.
293 dma_handle->Instance->CBR1 = 0U;
294 }
295 const volatile uint32_t ccr = dma_handle->Instance->CCR;
296 UNUSED(ccr);
297 __DSB();
298 ClearFlags(dma_handle);
299 __DSB();
300}
301
302bool STM32GpdmaUartAdapter::IsStopped(DMA_HandleTypeDef* dma_handle)
303{
304 const uint32_t ccr = dma_handle->Instance->CCR;
305 return (ccr & DMA_CCR_EN) == 0U;
306}
307
308void STM32GpdmaUartAdapter::DisableInterrupts(DMA_HandleTypeDef* dma_handle)
309{
310 __HAL_DMA_DISABLE_IT(dma_handle, DMA_IT_TC | DMA_IT_HT | DMA_IT_DTE | DMA_IT_ULE |
311 DMA_IT_USE | DMA_IT_SUSP | DMA_IT_TO);
312}
313
314void STM32GpdmaUartAdapter::ClearFlags(DMA_HandleTypeDef* dma_handle)
315{
316 __HAL_DMA_CLEAR_FLAG(dma_handle, DMA_FLAG_TC | DMA_FLAG_HT | DMA_FLAG_DTE |
317 DMA_FLAG_ULE | DMA_FLAG_USE | DMA_FLAG_SUSP |
318 DMA_FLAG_TO);
319}
320
321void STM32GpdmaUartAdapter::BuildRxQueue(uint8_t* buffer, size_t total_size, bool in_isr)
322{
323 DMA_HandleTypeDef* const dma_handle = state_.uart_handle_->hdmarx;
324 DEV_ASSERT_FROM_CALLBACK(StopComplete(dma_handle), in_isr);
325 ASSERT_FROM_CALLBACK(dma_handle->LinkedListQueue != nullptr, in_isr);
326 ASSERT_FROM_CALLBACK(dma_handle->LinkedListQueue->Head != nullptr, in_isr);
327
328 DMA_NodeConfTypeDef node_config{};
329 [[maybe_unused]] const auto dma_ex_list_get_node_config_result =
330 HAL_DMAEx_List_GetNodeConfig(&node_config, dma_handle->LinkedListQueue->Head);
331 DEV_ASSERT_FROM_CALLBACK(dma_ex_list_get_node_config_result == HAL_OK, in_isr);
332
333 // 保留 BSP 的请求与端口等属性,明确设置字节接收环的布局。
334 // Preserve BSP request and port attributes while defining the byte RX ring layout.
335 node_config.NodeType = DMA_GPDMA_LINEAR_NODE;
336 node_config.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
337 node_config.Init.Direction = DMA_PERIPH_TO_MEMORY;
338 node_config.Init.SrcInc = DMA_SINC_FIXED;
339 node_config.Init.DestInc = DMA_DINC_INCREMENTED;
340 node_config.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
341 node_config.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
342 node_config.Init.SrcBurstLength = 1U;
343 node_config.Init.DestBurstLength = 1U;
344 node_config.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
345 node_config.Init.Mode = DMA_NORMAL;
346 node_config.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
347 node_config.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
348 node_config.TriggerConfig.TriggerMode = DMA_TRIGM_BLOCK_TRANSFER;
349 node_config.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
350 node_config.TriggerConfig.TriggerSelection = 0U;
351 node_config.RepeatBlockConfig = {};
352
353 const size_t node_size = total_size / RX_NODE_COUNT;
354 node_config.SrcAddress = static_cast<uint32_t>(
355 reinterpret_cast<uintptr_t>(&state_.uart_handle_->Instance->RDR));
356 node_config.DataSize = static_cast<uint32_t>(node_size);
357
358 for (size_t i = 0U; i < RX_NODE_COUNT; ++i)
359 {
360 node_config.DstAddress =
361 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(&buffer[i * node_size]));
362 [[maybe_unused]] const auto dma_ex_list_build_node_result =
363 HAL_DMAEx_List_BuildNode(&node_config, &state_.nodes_[i]);
364 DEV_ASSERT_FROM_CALLBACK(dma_ex_list_build_node_result == HAL_OK, in_isr);
365 [[maybe_unused]] const auto dma_ex_list_insert_node_tail_result =
366 HAL_DMAEx_List_InsertNode_Tail(&state_.rx_queue_, &state_.nodes_[i]);
367 DEV_ASSERT_FROM_CALLBACK(dma_ex_list_insert_node_tail_result == HAL_OK, in_isr);
368 }
369
370 [[maybe_unused]] const auto dma_ex_list_set_circular_mode_result =
371 HAL_DMAEx_List_SetCircularMode(&state_.rx_queue_);
372 DEV_ASSERT_FROM_CALLBACK(dma_ex_list_set_circular_mode_result == HAL_OK, in_isr);
373 [[maybe_unused]] const auto dma_ex_list_un_linkq_result =
374 HAL_DMAEx_List_UnLinkQ(dma_handle);
375 DEV_ASSERT_FROM_CALLBACK(dma_ex_list_un_linkq_result == HAL_OK, in_isr);
376 [[maybe_unused]] const auto dma_ex_list_linkq_result =
377 HAL_DMAEx_List_LinkQ(dma_handle, &state_.rx_queue_);
378 DEV_ASSERT_FROM_CALLBACK(dma_ex_list_linkq_result == HAL_OK, in_isr);
379 DEV_ASSERT_FROM_CALLBACK(dma_handle->LinkedListQueue == &state_.rx_queue_, in_isr);
380
381 state_.rx_buffer_ = buffer;
382 state_.rx_total_size_ = total_size;
383 state_.rx_node_size_ = node_size;
384 state_.rx_queue_built_ = true;
385}
386
387} // namespace LibXR
388
389#endif
static void FinalizeStopped(DMA_HandleTypeDef *dma_handle, bool in_isr)
在停止状态清除旧标志和链表计数 / Clear stale flags and list count while stopped.
static bool StopComplete(DMA_HandleTypeDef *dma_handle)
检查通道及 HAL 链表均已停止 / Check that the channel and HAL list have stopped.
bool AllStopsComplete() const
检查所有已配置的 DMA 通道均已停止 / Check all configured DMA channels have stopped.
bool LaunchStop(DMA_HandleTypeDef *dma_handle, AbortCallback callback, bool in_isr)
启动异步中止或接续已有中止 / Start an asynchronous abort or join an existing abort.
uint8_t * GetLinkedListDmaRxProducer() const
读取一次当前 DMA 写入地址 / Read the current DMA destination address once.
HAL_StatusTypeDef StartLinkedListDmaRx(uint8_t *buffer, size_t total_size, bool in_isr)
构建或复用四节点循环链表并启动 RX / Build or reuse the four-node ring and start RX.
STM32GpdmaUartAdapter(UART_HandleTypeDef *uart_handle)
绑定已初始化的 UART 句柄 / Bind an initialized UART handle.
void CloseTxTerminalSource() const
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
void STM32_InvalidateDCacheByAddr(const void *addr, size_t size)
失效指定内存范围覆盖的 D-Cache cache line Invalidates D-Cache lines covering the specified memory range
void STM32_CleanDCacheByAddr(const void *addr, size_t size)
清理指定内存范围覆盖的 D-Cache cache line Cleans D-Cache lines covering the specified memory range