1.端到端输入
1.1.提示词
目的:实现串口不定长信令的帧接收功能,考虑到减少CPU的压力,采用DMA进行数据搬运。
需求:遵循UTF-8编码;符合HAL库开发风格;变量遵循外设名+驼峰命名法+下划线的规则,宏定义变量名使用全大写(例如:Uart1_DMA_RXBuffer[UART_RX_SIZE];✅ Uart1_RxBuffer,❌ uart1RxBuffer 或 Uart1RxBuffer);避免出现"精简到复杂"形式的代码,尤其是二进制寄存器位移操作,禁止直接操作寄存器(如 USART1->SR、DMA1->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 中勾选 | |
| DMA | USART1_RX 请求 | DMA1 Channel5, 方向 Peripheral to Memory, 模式 Circular, 递增地址, 数据宽度 Byte |
| USART1_TX 请求 | DMA1 Channel4, 方向 Memory to Peripheral, 模式 Normal, 递增地址, 数据宽度 Byte | |
| NVIC | DMA1 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() 在初始化时被调用 |
| 接收到的长度总是 0 | DMA 计数器读取时机错误 | 确保在 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 | 插件 | ------ |