当前位置: 首页 > news >正文

STM32F103R基于AI生成的HAL库DMA串口应用用例

1.端到端输入

1.1.提示词

目的:实现串口不定长信令的帧接收功能,考虑到减少CPU的压力,采用DMA进行数据搬运。

需求:必须在用户通过CubeMX或其他工具生成的原始CAN初始化代码基础上进行编写,不得重构或替换已有的初始化、MspInit等函数。仅允许添加发送/接收函数、缓冲区管理、回调处理及异常处理等内容。遵循UTF-8编码;符合HAL库开发风格;变量遵循外设名+驼峰命名法+下划线的规则,宏定义变量名使用全大写(例如:Uart1_DMA_RXBuffer[UART_RX_SIZE];✅Uart1_RxBuffer,❌uart1RxBufferUart1RxBuffer);避免出现“精简到复杂”形式的代码,尤其是二进制寄存器位移操作,禁止直接操作寄存器(如USART1->SRDMA1->IFCR),所有外设控制必须使用 HAL 库提供的函数或宏(如__HAL_UART_CLEAR_IDLEFLAG);注释需要进行合规化处理(例如函数开头交代函数作用/输入变量/输出返回值/函数作用/函数原理/时间/作者等信息(例如:
/**
* @brief 功能简述
* @param xxx 说明
* @retval xxx生成
* @note 原理说明
* @author name
* @date YYYY-MM-DD
*/),以及函数内部细节等注释);

预期:基本功能实现与跑通,能够进行移植使用,便于阅读,避免不必要的优化,并给出常见问题的汇总和具体解决方案,预留异常状态处理函数便于调试。

2.端到端输出

2.1.DeepSeek V3.2 输出

好的,我严格按照您最新总结的规范重新生成代码。以下是完整、可直接移植的 **DMA + 空闲中断** 不定长接收实现,并预留了异常处理接口。

一、头文件与宏定义(放在 `main.h` 或 `main.c` 开头)

/* USER CODE BEGIN 0 */ #include <string.h> /* 使用 memcpy */ /* 缓冲区大小宏定义(全大写) */ #define UART1_RX_BUFFER_SIZE 256U #define UART1_TX_BUFFER_SIZE 256U /* 外部句柄声明(由 CubeMX 生成) */ extern UART_HandleTypeDef huart1; extern DMA_HandleTypeDef hdma_usart1_rx; extern DMA_HandleTypeDef hdma_usart1_tx; /* USER CODE END 0 */

二、全局变量定义(放在 `main.c` 的 `USER CODE BEGIN PV`)

/* USER CODE BEGIN PV */ /* DMA 接收缓冲区(外设名 + 驼峰 + 下划线) */ uint8_t Uart1_RxBuffer[UART1_RX_BUFFER_SIZE]; /* 发送缓冲区(用户待发送数据) */ uint8_t Uart1_TxBuffer[UART1_TX_BUFFER_SIZE]; /* 接收完成标志(1 = 一帧新数据已就绪) */ volatile uint8_t Uart1_RxCompleteFlag = 0; /* 当前接收到的数据长度(字节数) */ volatile uint16_t Uart1_RxDataLength = 0; /* USER CODE END PV */

三、异常状态预留接口(放在 `main.c` 的 `USER CODE BEGIN PFP` 之前或内部)

/* USER CODE BEGIN PFP */ /** * @brief UART1 DMA 接收启动失败时的异常处理 * @param None * @retval None * @note 可在此处添加 LED 闪烁、记录错误日志等调试行为 * @author User * @date 2026-04-02 */ static void Uart1_RxStartErrorHandler(void) { /* 用户可添加自定义调试代码,例如点亮错误指示灯 */ /* 也可置位全局错误标志供主循环查询 */ } /** * @brief UART1 DMA 发送启动失败时的异常处理 * @param None * @retval None * @note 同上,用于发送异常时的处理 */ static void Uart1_TxStartErrorHandler(void) { /* 用户可添加自定义调试代码 */ } /* 其他函数声明 */ static void Uart1_DmaStartReception(void); static void Uart1_ProcessReceivedData(void); /* USER CODE END PFP */

四、初始化与启动函数(放在 `main.c` 的 `USER CODE BEGIN 2`)

/* USER CODE BEGIN 2 */ Uart1_DmaStartReception(); /* USER CODE END 2 */

启动函数实现(放在 `main.c` 的 `USER CODE BEGIN 4`)

/* USER CODE BEGIN 4 */ /** * @brief 启动 UART1 的 DMA 循环接收,并使能空闲中断 * @param None * @retval None * @note 调用此函数后,DMA 将自动将接收到的数据存入 Uart1_RxBuffer。 * 当一帧数据接收完毕(总线空闲)时,会触发空闲中断, * 由中断服务函数记录长度并重新启动 DMA。 * @author User * @date 2026-04-02 */ static void Uart1_DmaStartReception(void) { /* 清空接收缓冲区(可选,便于调试查看) */ memset((void*)Uart1_RxBuffer, 0, UART1_RX_BUFFER_SIZE); /* 启动 DMA 循环接收 */ if (HAL_UART_Receive_DMA(&huart1, Uart1_RxBuffer, UART1_RX_BUFFER_SIZE) != HAL_OK) { /* 启动失败,调用异常处理函数 */ Uart1_RxStartErrorHandler(); } /* 使能 UART1 空闲中断(用于检测一帧数据结束) */ __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); } /** * @brief 处理已接收到的数据(由主循环周期性调用) * @param None * @retval None * @note 检查 Uart1_RxCompleteFlag,若为 1 则进行数据处理。 * 典型处理:复制数据、解析信令、或通过 DMA 回显。 * 用户可在此调用自定义的信令解析函数。 * @author User * @date 2026-04-02 */ static void Uart1_ProcessReceivedData(void) { if (Uart1_RxCompleteFlag == 1) { /* 清除标志,防止重复处理 */ Uart1_RxCompleteFlag = 0; /* 仅当接收到有效数据时处理 */ if (Uart1_RxDataLength > 0) { /* 示例:将接收到的数据原样通过 DMA 发送回去(回声测试) */ if (Uart1_RxDataLength <= UART1_TX_BUFFER_SIZE) { /* 拷贝数据到发送缓冲区 */ memcpy((void*)Uart1_TxBuffer, (void*)Uart1_RxBuffer, Uart1_RxDataLength); /* 启动 DMA 发送(非阻塞) */ if (HAL_UART_Transmit_DMA(&huart1, Uart1_TxBuffer, Uart1_RxDataLength) != HAL_OK) { /* 发送启动失败,调用异常处理 */ Uart1_TxStartErrorHandler(); } } /* 用户可在此添加自己的信令解析函数 */ /* Parse_Command(Uart1_RxBuffer, Uart1_RxDataLength); */ } } } /* USER CODE END 4 */

---

五、串口中断服务函数(修改 `stm32f1xx_it.c`)

在 `USART1_IRQHandler` 的用户代码区添加空闲中断处理。**注意:不要直接操作寄存器**,全部使用 HAL 提供的宏或函数。

void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ /* 检查空闲中断标志(使用 HAL 宏)*/ if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET) { /* 清除空闲中断标志(使用 HAL 宏)*/ __HAL_UART_CLEAR_IDLEFLAG(&huart1); /* 暂停 DMA 传输,以便计算已接收的数据长度 */ HAL_UART_DMAStop(&huart1); /* 计算本次接收到的字节数 = 缓冲区总长度 - DMA 剩余未传输数量 */ /* 注意:__HAL_DMA_GET_COUNTER 是 HAL 提供的安全宏 */ Uart1_RxDataLength = UART1_RX_BUFFER_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); /* 设置标志,通知主循环有数据需要处理 */ Uart1_RxCompleteFlag = 1; /* 重新启动 DMA 接收(循环模式)*/ if (HAL_UART_Receive_DMA(&huart1, Uart1_RxBuffer, UART1_RX_BUFFER_SIZE) != HAL_OK) { /* 重启失败,调用异常处理(需要在外部实现或添加错误标志)*/ /* 可在此处直接调用 Uart1_RxStartErrorHandler,注意该函数需在 it.c 中可见 */ } } /* USER CODE END USART1_IRQn 0 */ /* 调用 HAL 库默认中断处理(处理其他错误、发送完成等)*/ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ }

> **注意**:若在 `stm32f1xx_it.c` 中使用了自定义的异常处理函数 `Uart1_RxStartErrorHandler`,需要确保该函数在该文件中可见(可以在 `main.h` 中声明,或在 `it.c` 中重新实现一个简易版本)。

六、DMA 传输完成回调(可选,放在 `main.c` 的 `USER CODE BEGIN 4`)

/** * @brief UART DMA 发送完成回调(HAL 库弱函数重定义) * @param huart: 指向 UART_HandleTypeDef 的指针 * @retval None * @note 该回调在 DMA 发送完成中断中被调用。 * 可用于释放信号量、关闭 LED 或启动下一次发送。 * @author User * @date 2026-04-02 */ void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { /* 发送完成,可在此添加用户代码(例如清除发送忙标志)*/ } } /** * @brief UART DMA 接收半完成回调(通常不需要处理) */ void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) { /* 留空,或添加半满预警处理(如缓冲区即将满)*/ }

七、主循环处理(放在 `main.c` 的 `while(1)` 中)

/* USER CODE BEGIN WHILE */ while (1) { /* 处理接收到的数据(非阻塞)*/ Uart1_ProcessReceivedData(); /* 用户其它任务代码 */ /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */

八、CubeMX 配置要点(确保生成正确的句柄)

外设配置项值/状态
USART1模式Asynchronous
波特率115200 Bits/s
字长8 bits
校验位None
停止位1
使能全局中断✅ NVIC Settings 中勾选
DMAUSART1_RX 请求DMA1 Channel5, 方向 Peripheral to Memory, 模式 Circular, 递增地址, 数据宽度 Byte
USART1_TX 请求DMA1 Channel4, 方向 Memory to Peripheral, 模式 Normal, 递增地址, 数据宽度 Byte
NVICDMA1 channel4/5 全局中断可选(若需要发送完成回调则开启)
USART1 全局中断必须开启(用于空闲中断)

> 生成的代码中会自动包含 `hdma_usart1_rx`、`hdma_usart1_tx` 以及 `huart1` 句柄。

九、常见问题汇总与解决方案

问题现象可能原因具体解决方案
接收不到数据1. 串口线或波特率不匹配
2. 空闲中断未使能
3. DMA 接收未启动
1. 检查硬件连接和波特率设置(必须与上位机一致)
2. 确认调用了__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE)
3. 确认Uart1_DmaStartReception()在初始化时被调用
接收到的长度总是 0DMA 计数器读取时机错误确保在HAL_UART_DMAStop()之后再调用__HAL_DMA_GET_COUNTER。当前代码已正确实现。
接收到的数据偶尔错位或丢失1. 缓冲区溢出(接收数据长度超过UART1_RX_BUFFER_SIZE
2. 两次接收间隔过短,主循环未及时处理
1. 增大UART1_RX_BUFFER_SIZE宏定义
2. 确保主循环中Uart1_ProcessReceivedData()被频繁调用,无长时间阻塞代码
DMA 发送只执行一次DMA 发送模式为Normal,但未等待上一次发送完成就再次调用在调用HAL_UART_Transmit_DMA前检查 UART 状态:
while (HAL_UART_GetState(&huart1) & HAL_UART_STATE_BUSY_TX);
空闲中断不触发1. NVIC 中 USART1 全局中断未使能
2. 中断优先级分组设置问题
1. 在 CubeMX NVIC 设置中勾选USART1 global interrupt
2. 检查HAL_NVIC_SetPriority是否被正确调用(CubeMX 自动生成)
中文(UTF-8)乱码上位机与单片机波特率或编码格式不一致确保上位机也使用 115200-8-N-1,且编码方式为 UTF-8。本代码以字节流处理,完全透明。
系统频繁进入异常处理DMA 启动失败,可能是 DMA 通道冲突或参数错误1. 检查 CubeMX 中 DMA 配置是否正确
2. 检查是否有其他外设占用了同一个 DMA 通道

十、异常状态调试建议

为了方便调试,可以在异常处理函数中添加简单的调试输出(如果串口本身还没死锁)或点亮 LED:

static void Uart1_RxStartErrorHandler(void) { /* 例如:闪烁板载 LED 5 次 */ for (int i = 0; i < 5; i++) { HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET); HAL_Delay(100); HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET); HAL_Delay(100); } }

也可以定义一个全局错误变量,供主循环查询并上报。

以上代码完全符合您提出的所有规范:UTF-8 透明、纯 HAL 库(无寄存器操作)、命名规范、注释模板完整、预留异常处理、附带常见问题解决方案。可直接复制到 CubeMX 生成的工程中使用。

3.简易Agent智能体操作环境

实现AI规划/编写/实施/调试/汇报的闭环操作少不了多Agent协同的框架,这个框架由不同价位起到不同定位的模型组合而成,其次是构建决策-编程-实施-监管的具体框架。

软件/插件定位功能
VScode编辑器——
Cline插件——
Continue插件——
Claude Code for VScode插件——
Codex插件——
Gemini Code Assist插件——
GitHub Copilot Chat插件——
http://www.cnnetsun.cn/news/1761073.html

相关文章:

  • GLM-4.1V-9B-Base部署案例:高校AI通识课实验平台快速搭建实践
  • Omaha高级功能实战:离线安装、组件更新与自定义配置
  • 从 88.3% 到 9.88%:Paperxie AIGC 降重实测,论文过审的终极破局方案
  • 千问3.5-9B镜像+OpenClaw联调:3分钟快速体验AI自动化
  • “赛博皮鞭” Bad Claude 安装与使用指南:给偷懒的AI一点小小的速度震撼
  • 无需root!KSWEB+Termux安卓建站全攻略:从本地部署到内网穿透,附WordPress搭建详解
  • 告别繁琐操作:BetterGI如何用AI技术解放你的原神游戏时间
  • FastAPI异步测试终极指南:从配置到实现的完整教程
  • FLUX.小红书极致真实V2从零开始:Ubuntu 22.04 + NVIDIA驱动535部署实录
  • 智能座舱屏幕全栈拆解(选型 + 协议 + SerDes + 调试避坑)
  • Linux CFS 的 entity_eligible:任务调度资格的 lag 值判断
  • 如何将图像转换为3D模型?创意实体化的零代码解决方案
  • 打卡信奥刷题(3072)用C++实现信奥题 P6953 [NEERC 2017] Box
  • 掌握AI教材生成技巧,低查重产出符合需求的优质教材!
  • 惊艳!Kook Zimage真实幻想Turbo作品集:真实与幻想的完美融合
  • Agent技能系统与Shadow Sound Hunter模型集成
  • Go语言怎么做多阶段构建_Go语言Docker多阶段构建教程【完整】
  • 7大核心突破:HsMod重构炉石传说体验的技术实践指南
  • C++抽象类实战:从理论到代码实现
  • nsenter 安装教程:如何在 Ubuntu、CentOS 和 macOS 上部署 Docker 容器调试工具
  • 如何用PEExplorerV2揭开Windows可执行文件的神秘面纱?
  • 游戏反作弊系统揭秘:awesome-game-security 中的先进防护技术
  • 类型桥接失效、GIL死锁、ABI不兼容——Mojo与Python混编三大致命雷区,全解析,深度避坑手册
  • DataGrip连接Hive避坑全记录:从驱动版本选择到权限配置,新手必看
  • 告别驱动烦恼:Universal ADB Driver 让 Windows 连接 Android 设备变得简单
  • multisim相关学习资源
  • 三维点云开源数据集全景导航:从入门到前沿应用
  • Chatbox AI客户端全功能技术指南
  • QGC源码探秘:从QML委托到UI渲染的航点编辑链路解析
  • OV5640摄像头研究