串口空闲中断原理与用DMA实现不定长数据接收(STM32F4)

嵌入式裸机&RTOS开发杂谈系列

第二章 串口空闲中断原理与用DMA实现不定长数据接收(STM32F4)



前言

在串口实际应用中,经常会碰到不定长数据的接收。本文讨论用DMA+串口的空闲中断,实现不定长数据接收。


一、串口空闲中断原理

第一次接触这个概念的人可能会有一个疑问,在没有数据时会不会触发空闲中断?答案:不会。
先来抽象个实际接收不定长数据的情况,总线第一次空闲,总线有一帧数据在传输,总线第二次空闲…按照这一规律循环直到传输结束。
实际上总线第一次空闲不会产生空闲中断,总线第二次空闲才会产生空闲中断,简单点来说:一直没有接收到数据是不会产生空闲中断的,只有当已经开始接收数据了,然后数据停止传输一段时间(一个字节传输的时间)之后才会触发空闲中断。

1.触发条件

当串口接收数据后,检测到RX引脚空闲时间超过1个字符帧传输时间时触发空闲中断。例如:波特率9600时,1字符帧(8位数据+1停止位)传输时间为1.041ms,若空闲时间超过此值则触发中断。

2.中断特性

空闲中断标志(IDLE)需手动清除,通过先读USART_SR再读USART_DR实现。
仅在一次接收数据后检测空闲状态,避免误触发。

二、DMA+空闲中断

在STM32F4的串口通信中,DMA(直接内存访问)与空闲中断的结合为数据的收发提供了一种高效且灵活的方式。

1.接收不定长数据

思路:DMA搬运串口接收的数据到缓冲区,开启串口空闲中断,当数据停止传输一段时间(一个字符传输所需的时间)时就会触发串口空闲中断(此时一帧不定长的数据也接收完毕了,没接收完毕不会停止一段时间),在串口空闲中断的服务函数中对缓冲区的数据进行处理。

// 配置步骤(以USART1为例)
void USART1_Init() {
    // 1. 使能GPIO、USART、DMA时钟 
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
 
    // 2. 配置GPIO为复用推挽输出(TX)和浮空输入(RX)
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
 
    // 3. 配置USART参数(波特率115200,8N1)
    USART_InitTypeDef USART_InitStruct = {115200, USART_WordLength_8b, ...};
    USART_Init(USART1, &USART_InitStruct);
 
    // 4. 配置DMA接收(外设→内存)
    DMA_InitTypeDef DMA_InitStruct = {
        .DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR,
        .DMA_Memory0BaseAddr = (uint32_t)rx_buffer,
        .DMA_BufferSize = RX_BUF_SIZE,
        .DMA_DIR = DMA_DIR_PeripheralToMemory,
        .DMA_Mode = DMA_Mode_Circular,  // 循环模式适配不定长数据 
        .DMA_Priority = DMA_Priority_High 
    };
    DMA_Init(DMA2_Stream5, &DMA_InitStruct);
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
 
    // 5. 使能空闲中断 
    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
    NVIC_EnableIRQ(USART1_IRQn);
}

// 中断服务函数 
void USART1_IRQHandler() {
    // 串口空闲中断
    if (USART_GetITStatus(USART1, USART_IT_IDLE)) {
        USART_ReceiveData(USART1);  // 清除IDLE标志 
        DMA_Cmd(DMA2_Stream5, DISABLE);
        
        // 计算接收数据长度 
        uint16_t data_len = RX_BUF_SIZE - DMA_GetCurrDataCounter(DMA2_Stream5);
        process_data(rx_buffer, data_len);  // 处理数据 
        
        // 重启DMA接收 
        DMA_SetCurrDataCounter(DMA2_Stream5, RX_BUF_SIZE);
        DMA_Cmd(DMA2_Stream5, ENABLE);
    }
}

2. DMA发送数据

思路:直接调用发送函数,数据大小自由定义。

void USART1_SendData_DMA(uint8_t *data, uint16_t len) {
    // 配置DMA发送(内存→外设)
    DMA_InitTypeDef DMA_InitStruct = {
        .DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR,
        .DMA_Memory0BaseAddr = (uint32_t)data,
        .DMA_BufferSize = len,
        .DMA_DIR = DMA_DIR_MemoryToPeripheral,
        .DMA_Mode = DMA_Mode_Normal  // 单次传输模式 
    };
    DMA_Init(DMA2_Stream7, &DMA_InitStruct);
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    
    // 等待发送完成(或使用DMA传输完成中断)
    while (DMA_GetFlagStatus(DMA2_Stream7, DMA_FLAG_TCIF7) == RESET);
}

3.关键点说明

1. DMA模式选择

接收:使用普通模式(DMA_Mode_Circular),接收完成后需重新配置。
发送:使用普通模式(DMA_Mode_Normal),发送完成后需重新配置。

2. 中断优化

接收时仅开启空闲中断,关闭RXNE中断以减少CPU负担。
发送完成后可通过DMA传输完成中断(DMA_IT_TC)通知CPU。

### IntelliJ IDEA 中通义 AI 功能介绍 IntelliJ IDEA 提供了一系列强大的工具来增强开发体验,其中包括与通义 AI 相关的功能。这些功能可以帮助开发者更高效地编写代并提高生产力。 #### 安装通义插件 为了使用通义的相关特性,在 IntelliJ IDEA 中需要先安装对应的插件: 1. 打开 **Settings/Preferences** 对话框 (Ctrl+Alt+S 或 Cmd+, on macOS)。 2. 导航到 `Plugins` 页面[^1]。 3. 在 Marketplace 中搜索 "通义" 并点击安装按钮。 4. 完成安装后重启 IDE 使更改生效。 #### 配置通义服务 成功安装插件之后,还需要配置通义的服务连接信息以便正常使用其提供的各项能力: - 进入设置中的 `Tools | Qwen Coding Assistant` 菜单项[^2]。 - 填写 API Key 和其他必要的认证参数。 - 测试连接以确认配置无误。 #### 使用通义辅助编程 一旦完成上述准备工作,就可以利用通义来进行智能编支持了。具体操作如下所示: ##### 自动补全代片段 当输入部分语句时,IDE 将自动提示可能的后续逻辑,并允许一键插入完整的实现方案[^3]。 ```java // 输入 while 循环条件前半部分... while (!list.isEmpty()) { // 激活建议列表选择合适的循环体内容 } ``` ##### 解释现有代含义 选中某段复杂的表达式或函数调用,右键菜单里会有选项可以请求通义解析这段代的作用以及优化意见。 ##### 生产测试案例 对于已有的业务逻辑模块,借助于通义能够快速生成单元测试框架及初始断言集,减少手动构建的成本。 ```python def test_addition(): result = add(2, 3) assert result == 5, f"Expected 5 but got {result}" ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值