RT1060--串口edma详解(SDK)

RT1060--串口edma详解(SDK)

串口edma详解

这次我们来一起看一下,基于 NXP MIMXRT1060 开发板的串口通信示例工程,学习如何使用 EDMA(增强型直接内存访问)来实现高效的串口数据传输。

主要特点:

  1. 使用 LPUART (低功耗串口)进行通信
  2. 利用 EDMA 实现数据传输,减少 CPU 干预
  3. 实现了一个回显功能 - 将接收到的数据发送回去

关键数据流

  • 接收流程: 串口输入 -> LPUART接收寄存器 -> EDMA传输 -> 接收缓冲区(g_rxBuffer)

  • 发送流程: 发送缓冲区(g_txBuffer) -> EDMA传输 -> LPUART发送寄存器 -> 串口输出

lpuart_edma_transfer.c 工作流程

  1. 系统初始化:
  • 配置 LPUART(波特率115200,使能收发)
  • 配置 DMAMUX(设置 DMA 通道映射)
  • 初始化 EDMA(并创建传输句柄)
  1. 主循环逻辑:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
while (1)
{
/* If RX is idle and g_rxBuffer is empty, start to read data to g_rxBuffer. */
if ((!rxOnGoing) && rxBufferEmpty)
{
rxOnGoing = true;
LPUART_ReceiveEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, &receiveXfer);
}

/* If TX is idle and g_txBuffer is full, start to send data. */
if ((!txOnGoing) && txBufferFull)
{
txOnGoing = true;
LPUART_SendEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, &sendXfer);
}

/* If g_txBuffer is empty and g_rxBuffer is full, copy g_rxBuffer to g_txBuffer. */
if ((!rxBufferEmpty) && (!txBufferFull))
{
memcpy(g_txBuffer, g_rxBuffer, ECHO_BUFFER_LENGTH);
rxBufferEmpty = true;
txBufferFull = true;
}
}
  • 检查接收缓冲区状态,空闲则启动新的接收
  • 检查发送缓冲区状态,满则启动新的发送
  • 在接收完成且发送空闲时,将数据从接收缓冲区复制到发送缓冲区
  1. 状态管理:
  • 使用回调函数处理传输完成事件
  • 通过状态标志(rxBufferEmpty, txBufferFull等)协调收发过程

仔细分析callback机制

首先看到几个关键的回调函数:

  1. LPUART_UserCallback - 这是用户层的回调函数,在main中注册
  2. LPUART_SendEDMACallback - EDMA发送完成的回调
  3. LPUART_ReceiveEDMACallback - EDMA接收完成的回调
  4. LPUART_TransferEdmaHandleIRQ - LPUART的EDMA中断处理函数

从代码中可以看到调用链路:

  1. 在main中通过LPUART_TransferCreateHandleEDMA注册了用户回调:

    1
    2
    3
    /* Create LPUART DMA handle. */
    LPUART_TransferCreateHandleEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, LPUART_UserCallback, NULL, &g_lpuartTxEdmaHandle,
    &g_lpuartRxEdmaHandle);
  2. 当EDMA传输完成时,会触发中断,调用LPUART_TransferEdmaHandleIRQ();

    1
    2
    3
    4
    5
    6
    7
    8
    /* Save the handle in global variables to support the double weak mechanism. */
    s_lpuartHandle[instance] = handle;
    /* Set LPUART_TransferEdmaHandleIRQ as DMA IRQ handler */
    s_lpuartIsr[instance] = LPUART_TransferEdmaHandleIRQ;
    /* Disable all LPUART internal interrupts */
    LPUART_DisableInterrupts(base, (uint32_t)kLPUART_AllInterruptEnable);
    /* Enable interrupt in NVIC. */
    (void)EnableIRQ(s_lpuartIRQ[instance]);
  3. 在EDMA的回调中会更新状态标志并调用用户回调:

1
2
3
LPUART_SendEDMACallback
LPUART_ReceiveEDMACallback
--> LPUART_UserCallback

以下是LPUART EDMA传输的回调流程图:

1
2
3
4
5
6
7
8
9
10
graph TD
A[EDMA传输开始] --> B[EDMA传输完成中断]
B --> C[LPUART_TransferEdmaHandleIRQ]
C --> D{传输类型?}
D -->|发送完成| E[LPUART_SendEDMACallback]
D -->|接收完成| F[LPUART_ReceiveEDMACallback]
E --> G[LPUART_UserCallback]
F --> G
G -->|TX完成| H[设置txBufferFull=false<br>txOnGoing=false]
G -->|RX完成| I[设置rxBufferEmpty=false<br>rxOnGoing=false]

所以整个调用链是:

  1. EDMA完成传输
  2. 触发LPUART传输完成中断
  3. 调用LPUART_TransferEdmaHandleIRQ
  4. LPUART_TransferEdmaHandleIRQ调用用户回调
  5. 用户回调更新状态标志

这个机制实现了:

  1. 硬件中断与软件处理的解耦
  2. 灵活的状态管理
  3. 异步操作的完成通知

总结

  1. 初始化流程:
  • main函数中创建EDMA handle
  • 注册LPUART_UserCallback作为用户回调
  • 配置EDMA通道和中断
  1. 发送流程:
  • LPUART_SendEDMA触发发送
  • EDMA完成后触发中断
  • 通过回调链更新状态标志
  1. 接收流程:
  • LPUART_ReceiveEDMA启动接收
  • EDMA完成后触发中断
  • 通过回调链更新状态标志
  1. 状态管理:
  • txOnGoing/rxOnGoing表示传输进行状态
  • txBufferFull/rxBufferEmpty表示缓冲区状态
  • 这些标志在回调中更新,在主循环中使用

这种回调机制实现了:

  1. 异步处理EDMA传输
  2. 状态管理的解耦
  3. 用户代码与底层驱动的分离
作者

Gavin

发布于

2024-12-18

更新于

2024-12-18

许可协议

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×