RT1060--串口中断传输详解(SDK)

RT1060--串口中断传输详解(SDK)

串口中断传输详解(SDK)

1. evkmimxrt1060_lpuart_interrupt

使用 NXP 的 fsl_lpuart.h 库,通过中断方式实现 LPUART 的数据接收和回显。

它使用了一个ring buffer来暂存接收到的数据,然后在主循环中将缓冲区的数据发送出去。

代码解释

  1. 宏定义:

    • DEMO_LPUART: 定义使用的 LPUART 外设为 LPUART1
    • DEMO_LPUART_CLK_FREQ: 定义 LPUART 的时钟频率。
    • DEMO_LPUART_IRQn: 定义 LPUART 的中断号。
    • DEMO_LPUART_IRQHandler: 定义 LPUART 的中断处理函数名。
    • DEMO_RING_BUFFER_SIZE: 定义环形缓冲区的大小为 16 字节。
  2. 全局变量:

    • g_tipString: 提示字符串,程序启动时发送到串口。
    • demoRingBuffer: 环形缓冲区,用于存储接收到的数据
    • txIndex: 发送索引,指向下一个要发送的数据在环形缓冲区中的位置。
    • rxIndex: 接收索引,指向下一个要写入接收数据的环形缓冲区位置。
  3. DEMO_LPUART_IRQHandler() 函数:

    LPUART 中断处理函数:

    • 如果Rx Data 寄存器满,则:
      • 读取receiver register数据: (LPUART_ReadByte())。
      • 读完之后检查环形缓冲区是否已满,如果未满,则将接收到的数据写入环形缓冲区,并更新 rxIndex
  4. main() 函数:

    • 初始化开发板和 LPUART。
    • 发送提示字符串。
    • 使能 LPUART 的接收满中断 (kLPUART_RxDataRegFullInterruptEnable)。
    • 使能 LPUART 中断 (EnableIRQ(DEMO_LPUART_IRQn))。
    • 进入主循环:
      • 检查 LPUART 发送寄存器是否为空 (kLPUART_TxDataRegEmptyFlag)。
      • 检查环形缓冲区是否非空。
      • 如果发送寄存器为空且缓冲区非空,则从环形缓冲区读取数据 (demoRingBuffer[txIndex]),并通过 LPUART_WriteByte() 发送出去,并更新 txIndex

数据/中断/callback调用流程图

详细流程解释:

  1. 中断处理:
    • 读取接收到的数据并存入环形缓冲区 demoRingBuffer
    • 更新接收索引 rxIndex
  2. 主循环处理:
    • 主循环不断检查 LPUART 发送寄存器是否为空以及环形缓冲区是否非空。
    • 如果条件满足,则从环形缓冲区读取数据,并通过 LPUART_WriteByte() 发送出去。
    • 更新发送索引 txIndex
  3. 数据发送: LPUART 硬件将数据发送到串口接收端。

关键点

  • 环形缓冲区: 用于在中断处理函数和主循环之间传递数据,避免数据丢失。
  • 中断驱动: 使用中断来处理接收事件,提高了 CPU 效率。
  • 非阻塞发送: 主循环中轮询检查发送寄存器是否为空和ring buffer非空,用于读取数据并发送,更新txIndex;

2. evkmimxrt1060_lpuart_interrupt_transfer

使用 LPUART 进行回显。

程序通过 LPUART 接收用户输入的 8 个字符,然后将这 8 个字符回显给用户终端。

代码解释

代码结构:

  1. 宏定义:定义了一些宏和变量,例如使用的 LPUART 实例 (DEMO_LPUART)、时钟频率 (DEMO_LPUART_CLK_FREQ)、回显缓冲区长度 (ECHO_BUFFER_LENGTH) 。

  2. callback声明:声明了一个 LPUART 用户回调函数 LPUART_UserCallback

  3. 变量:定义了一些全局变量,例如用于存储句柄 (g_lpuartHandle)、提示信息字符串 (g_tipString)、发送缓冲区 (g_txBuffer)、接收缓冲区 (g_rxBuffer) 等,还有一些标志位用于指示缓冲区状态和传输状态:

    1
    2
    3
    4
    volatile bool rxBufferEmpty            = true;
    volatile bool txBufferFull = false;
    volatile bool txOnGoing = false;
    volatile bool rxOnGoing = false;
  4. **LPUART 用户回调函数 (LPUART_UserCallback)**:该函数会在 LPUART 传输完成 (发送或接收) 时被调用。它更新发送和接收缓冲区的相关标志位 (txBufferFull、txOnGoing、rxBufferEmpty、rxOnGoing)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /* LPUART user callback */
    void LPUART_UserCallback(LPUART_Type *base, lpuart_handle_t *handle, status_t status, void *userData)
    {
    userData = userData;

    if (kStatus_LPUART_TxIdle == status) // 实际上就是发送完成!
    {
    txBufferFull = false;
    txOnGoing = false;
    }

    if (kStatus_LPUART_RxIdle == status) // 实际上对应接收完成!
    {
    rxBufferEmpty = false;
    rxOnGoing = false;
    }
    }
  5. 主函数 (main)

    • 初始化时钟、引脚复用等。

    • 配置 LPUART:设置波特率、启用发送和接收功能等。

    • 创建 LPUART 传输句柄。

      LPUART_TransferCreateHandle(DEMO_LPUART, &g_lpuartHandle, LPUART_UserCallback, NULL);

    • 发送提示信息字符串。

      LPUART_TransferSendNonBlocking(DEMO_LPUART, &g_lpuartHandle, &xfer);

    • 进入循环,实现回显功能:

      • 如果接收空闲且接收缓冲区为空,则启动接收数据到接收缓冲区。

        LPUART_TransferReceiveNonBlocking(DEMO_LPUART, &g_lpuartHandle, &receiveXfer, NULL);

      • 如果发送空闲且发送缓冲区满,则启动发送数据。

        LPUART_TransferSendNonBlocking(DEMO_LPUART, &g_lpuartHandle, &sendXfer);

      • 如果接收非空且发送不满,则将接收缓冲区内容复制到发送缓冲区,并更新缓冲区标志位。

        1
        2
        3
        memcpy(g_txBuffer, g_rxBuffer, ECHO_BUFFER_LENGTH);
        rxBufferEmpty = true;
        txBufferFull = true;

流程图:

数据流:

  1. 初始化阶段:
    • 配置 LPUART。
  2. 发送提示信息:
    • 将提示信息字符串拷贝到发送缓冲区。
    • 启动发送。
  3. 回显循环:
    • 接收数据:
      • 如果接收空闲且接收缓冲区为空,则启动接收数据。
      • 接收完成触发回调函数,更新接收缓冲区标志位。
    • 发送数据:
      • 如果发送空闲且发送缓冲区满,则启动发送数据。
      • 发送完成触发回调函数,更新发送缓冲区标志位。
    • 复制数据:
      • 如果接收非空且发送不满,则将接收缓冲区内容复制到发送缓冲区。
      • 更新接收和发送缓冲区标志位。

中断和回调流程:

  1. 当数据接收完成或发送完成后,会产生中断。
  2. 中断服务程序会将控制权转移到 LPUART 驱动程序。
  3. 驱动程序会调用用户回调函数 LPUART_UserCallback
  4. 回调函数中,根据传输完成类型 (发送/接收) 更新相关的缓冲区标志位。

注意:

  • 该代码使用非阻塞式传输,即发送或接收启动后,程序不会等待传输完成,而是继续执行其他操作。
  • 回调函数中只更新了标志位,并没有直接操作数据缓冲区。数据处理是在主循环中进行的。

代码关键点

  • 非阻塞传输: 使用了 LPUART_TransferSendNonBlockingLPUART_TransferReceiveNonBlocking 函数,这意味着发送和接收操作会在后台进行,不会阻塞主循环。
  • 回调函数: LPUART_UserCallback 函数用于处理传输完成事件。在这个函数中,主要任务是更新标志位,例如 txOnGoingtxBufferFullrxOnGoingrxBufferEmpty
  • 缓冲区管理: 使用了 g_txBufferg_rxBuffer 作为发送和接收缓冲区,并通过 memcpy 函数在两个缓冲区之间复制数据。
  • 主循环逻辑: 主循环负责启动发送和接收操作,并在缓冲区之间复制数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
graph TD
subgraph 初始化
A[初始化 LPUART] --> B{发送提示信息}
B --> C[启动发送<br>LPUART_TransferSendNonBlocking]
C --> D{等待发送完成<br> txOnGoing }
end
D --> E[回显循环]
subgraph 接收
E --> F{接收空闲 & 接收缓冲区为空<br> !rxOnGoing && rxBufferEmpty }
F -- 是 --> G[启动接收<br>LPUART_TransferReceiveNonBlocking]
G --> H[LPUART 硬件接收数据<br>RxDataRegFullFlag 置位]
H --> I[产生中断信号]
I --> J[NVIC 处理中断<br>调用中断服务例程<br>LPUART_TransferHandleIRQ]
J --> K[中断服务例程<br>调用 LPUART_UserCallback<br>kStatus_LPUART_RxIdle<br>rxBufferEmpty = false<br>rxOnGoing = false]
end
subgraph 发送
E --> L{发送空闲 & 发送缓冲区满<br> !txOnGoing && txBufferFull }
L -- 是 --> M[启动发送<br>LPUART_TransferSendNonBlocking]
M --> N[LPUART 硬件发送数据]
N --> O[发送完成<br>产生中断信号]
O --> P[NVIC 处理中断<br>调用中断服务例程<br>LPUART_TransferHandleIRQ]
P --> Q[中断服务例程<br>调用 LPUART_UserCallback<br>kStatus_LPUART_TxIdle<br>txBufferFull = false<br>txOnGoing = false]
end
E --> R{接收缓冲区非空 & 发送缓冲区未满<br> !rxBufferEmpty && !txBufferFull}
R -- 是 --> S[数据复制<br>memcpy<br>rxBufferEmpty = true<br>txBufferFull = true]
K --> E
Q --> E
S --> E

代码逻辑解释:

  1. 初始化: 程序首先初始化 LPUART 外设,然后发送一个提示信息。

  2. 回显循环:

    程序进入一个无限循环,不断检查以下条件:

    • 接收:
      • 如果当前没有正在接收数据 (!rxOnGoing) 并且接收缓冲区为空 (rxBufferEmpty),则启动一次非阻塞的接收操作 (LPUART_TransferReceiveNonBlocking)。
      • 当 LPUART 硬件接收到数据后,RxDataRegFullFlag 标志位会被置位,触发中断。
      • NVIC 接收到中断请求后,会调用中断服务例程。在中断服务例程中,会调用用户定义的回调函数 LPUART_UserCallback
        • 回调函数根据 kStatus_LPUART_RxIdle 状态将 rxBufferEmpty 设置为 false,将 rxOnGoing 设置为 false,表明接收已完成。
    • 发送:
      • 如果当前没有正在发送数据 (!txOnGoing) 并且发送缓冲区已满 (txBufferFull),则启动一次非阻塞的发送操作 (LPUART_TransferSendNonBlocking)。
      • LPUART 硬件发送完数据后,会产生中断。
      • NVIC 处理中断,并调用回调函数 LPUART_UserCallback
        • 回调函数根据 kStatus_LPUART_TxIdle 状态将 txBufferFull 设置为 false,将 txOnGoing 设置为 false,表明发送已完成。
    • 数据复制:如果接收缓冲区非空 (!rxBufferEmpty) 并且发送缓冲区未满 (!txBufferFull),则使用 memcpy 函数将接收缓冲区的数据复制到发送缓冲区,并将 rxBufferEmpty 设置为 truetxBufferFull 设置为 true,为下一次接收和发送做准备。

关于中断和回调

LPUART_UserCallback 是一个用户自定义的回调函数,它会在 LPUART 驱动程序中的中断处理函数 LPUART_TransferHandleIRQ 中,当特定的传输事件发生后被调用。具体来说,以下几种情况下会调用 LPUART_UserCallback

  1. 接收完成: 当使用 LPUART_TransferReceiveNonBlocking 函数启动非阻塞接收,并且所有请求的数据都已接收完毕时,LPUART_TransferHandleIRQ 函数会在处理接收数据就绪中断时调用 LPUART_UserCallback,并传递状态 kStatus_LPUART_RxIdle
  2. 发送完成: 当使用 LPUART_TransferSendNonBlocking 函数启动非阻塞发送,并且所有数据都已发送完毕时,LPUART_TransferHandleIRQ 函数会在处理发送完成中断时调用 LPUART_UserCallback,并传递状态 kStatus_LPUART_TxIdle
  3. 接收硬件溢出: 当 LPUART 硬件发生接收溢出错误时,LPUART_TransferHandleIRQ 函数会在处理接收溢出中断时调用 LPUART_UserCallback,并传递状态 kStatus_LPUART_RxHardwareOverrun。这个状态表示有数据在接收之前就被覆盖了,导致数据丢失。

完整的接收流程和发送流程

接收流程:

  1. 应用程序调用 LPUART_TransferReceiveNonBlocking 函数启动非阻塞接收。
  2. LPUART_TransferReceiveNonBlocking 函数配置 LPUART 硬件,使能接收中断,并将接收请求的信息保存在 lpuart_handle_t 结构体中。
  3. LPUART 硬件接收到数据后,触发中断。
  4. CPU 响应中断,执行中断服务例程 LPUART_TransferHandleIRQ
  5. LPUART_TransferHandleIRQ 函数检查中断状态,如果是接收数据就绪中断,则调用 LPUART_TransferHandleReceiveDataFull 函数。
  6. LPUART_TransferHandleReceiveDataFull函数将数据从LPUART接收FIFO读取到handle指定的缓冲区。
  7. LPUART_TransferHandleReceiveDataFull函数检查是否所有请求的数据都已接收完毕,如果是,则在LPUART_TransferHandleIRQ中调用handle->callback,即 LPUART_UserCallback,并传递 kStatus_LPUART_RxIdle 状态。

发送流程:

  1. 应用程序调用 LPUART_TransferSendNonBlocking 函数启动非阻塞发送。
  2. LPUART_TransferSendNonBlocking 函数配置 LPUART 硬件,使能发送中断,并将发送请求的信息保存在 lpuart_handle_t 结构体中。
  3. 当发送数据寄存器为空时,LPUART 硬件触发中断。
  4. CPU 响应中断,执行中断服务例程 LPUART_TransferHandleIRQ
  5. LPUART_TransferHandleIRQ 函数检查中断状态,如果是发送数据寄存器空中断,则调用 LPUART_TransferHandleSendDataEmpty 函数。
  6. LPUART_TransferHandleSendDataEmpty函数将数据从handle指定的缓冲区写入LPUART发送FIFO。
  7. LPUART_TransferHandleSendDataEmpty函数检查是否所有数据都已发送完毕。如果是,则当发送完成后,在LPUART_TransferHandleIRQ中调用 handle->callback,即 LPUART_UserCallback,并传递 kStatus_LPUART_TxIdle 状态。

总结:

LPUART_UserCallback 是一个在中断上下文中被调用的回调函数。它允许用户在 LPUART 传输完成后执行自定义的操作,例如:

  • 设置标志位,通知应用程序传输已完成。
  • 启动下一次传输。
  • 处理接收到的数据。
  • 进行错误处理。

通过使用回调函数,可以实现事件驱动的编程模型,提高程序的效率和响应性。用户不需要在主循环中轮询 LPUART 的状态,而是通过回调函数在传输完成后得到通知。

因此,LPUART_UserCallback 的调用时机取决于 LPUART 的传输状态和中断事件,主要发生在接收完成、发送完成和接收溢出等情况下。它在整个 LPUART 驱动程序中起到了连接驱动程序和用户应用程序的重要作用。

作者

Gavin

发布于

2024-12-17

更新于

2024-12-17

许可协议

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

×