MCAL CddUart模块调用异步接收/发送函数时间过长优化方案
-
问题背景
在CddUart模块中使用DMA的方式进行异步通信时,调用异步函数的API单次用时达30us,对于串口调用效率有较高的需求时,API调用时间过长。
问题原因
经测试发现,CddUart 在每次调用异步收发函数时,均通过 CddDma_SetLogicChannelTransfer 对 DMA 通道进行完整配置。CddDma_SetLogicChannelTransfer 调用链路如下:
CddDma_SetLogicChannelTransfer() └── Dma_Lld_SetChannelTransfer() └── Dma_Lld_PushConfigToReg() // 写入全部 CTS 寄存器 ├── DMA_CTSSetEngineStall() // CSR ├── DMA_CTSSetSrcAddr() // SADDR ├── DMA_CTSSetDestAddr() // DADDR ├── DMA_CTSSetAttribute() // TCR ├── DMA_CTSSetSrcOffset() // SOFF ├── DMA_CTSSetDestOffset() // DOFF ├── DMA_CTSSetSrcLastAdjust() // STO ├── DMA_CTSSetDestLastAdjust / RAM reload // DTO_RLD ├── DMA_SetErrorIntCmd() // CHEIE ├── DMA_CTSSetMajorCompleteIntCmd() // CSR ├── DMA_CTSSetSrcMinorLoopOffsetCmd() // BCNT.LOEN ├── DMA_CTSSetDstMinorLoopOffsetCmd() // BCNT.LOEN ├── DMA_CTSSetTransferLoopOffset() // BCNT.LOEN ├── DMA_CTSSetNbytes() // BCNT ├── DMA_CTSSetChannelLoopLink() // TCNTRV/TCNT ├── DMA_CTSSetChannelTriggerLink() // CSR ├── DMA_CTSSetTriggerCount() // TCNTRV/TCNT └── DMA_DisDmaReqAfterCTSDoneCmd() // CSR每次发送/接收都重写 全部 DMA 寄存器,但其中大部分参数在整个生命周期中保持不变。
使用CddDma_SetLogicChannelTransfer 优点在于与CddDma模块保持统一管理,可移植性变高,且遵循AUTOSAR的架构。但缺点是太多冗余代码,且CddDma_SetLogicChannelTransfer 函数内部有很多子函数跳转,这是消耗时间的主要原因。优化方案
每次调用异步收发函数时,不再调用CddDma_SetLogicChannelTransfer ,而是通过直接修改寄存器的方式修改需要改变的参数。
将 DMA 配置拆分为两个阶段:- Install(初始化阶段):在
LinFlexD_Lld_Init中调用CddDma_SetLogicChannelTransfer ,完成全部 DMA 寄存器的一次性配置。 - Update(运行时阶段):在每次收发时,仅通过直接寄存器写入更新变化参数。
优化前后对比
优化前:
static CddUart_StatusType LinFlexD_Lld_StartSendUsingDma(...) { TxDmaTransferConfig = *DmaChannelTransferConfigArray[...]; // 拷贝模板 UartState->TxBuff = TxBuff; UartState->TxSize = TxSize; // ... if (/* 8-bit */) { SchM_Enter_...(); TxDmaTransferConfig.SrcTransferSize = ...; // 冗余 TxDmaTransferConfig.DestTransferSize = ...; // 冗余 TxDmaTransferConfig.SrcOffset = ...; // 冗余 TxDmaTransferConfig.DestOffset = ...; // 冗余 TxDmaTransferConfig.TransferLoopByteCount = ...; // 冗余 #if YTM32 // Dummy 通道全量重配(冗余) DummyDmaTransferConfig.SrcTransferSize = ...; // ... 约 15 行冗余代码 ... CddDma_SetLogicChannelTransfer(DummyChannel, ...); #endif SchM_Exit_...(); CddDma_SetLogicChannelTransfer(TxChannel, // 写入全部寄存器 TxBuff, &Base->DATA.DATA8[0], &TxDmaTransferConfig); } // else 16-bit 同理 ... CddDma_InstallCallback(...); // 每次重新注册回调(冗余) CddDma_InstallErrorCallback(...); CddDma_StartChannel(...); // ... }优化后:
static CddUart_StatusType LinFlexD_Lld_StartSendUsingDma(...) { volatile DMA_Type *DmaBase = DMA0; uint8 TxCh = (uint8)UartState->TxDMAChannel; uint32 TriggerCount; UartState->TxBuff = TxBuff; UartState->TxSize = TxSize; // ... if (/* 8-bit */) { TriggerCount = TxSize / (1U << DMA_TRANSFER_SIZE_1_BYTE); SchM_Enter_...(); DmaBase->CTS[TxCh].SADDR = (uint32)TxBuff; // ← 直接写 SADDR LinFlexD_Lld_SetDmaTriggerCount(DmaBase, TxCh, TriggerCount); // ← 直接写 TCNTRV/TCNT #if YTM32 LinFlexD_Lld_SetDmaTriggerCount(DmaBase, DummyChannel, TriggerCount); #endif SchM_Exit_...(); } // else 16-bit 同理 ... CddDma_StartChannel(TxChannel); // 使能发送器 ... }优化结果
优化后单次调用异步收发API从原来的30us降为10us。


附上代码
若有需要可直接替换此demo的 CddUart_Lld_LinFlexD.c
适用版本: YTM32B1ME0/YTM32B1MD1 2_3_0
CddUart_WithDma_Demo.zip - Install(初始化阶段):在
快速上手云途开发生态
发帖前请查看
帮助改进和优化YT CONFIG TOOL,有机会抽取YTM32B1ME0 EVB哦...