AI辅助开发实战:电子科学与技术毕设中的智能系统设计与工程化落地
1. 毕设开发中的典型痛点
电子科学与技术方向的毕设,往往要求“软硬协同”:既要跑通算法,又要能在板子上实时演示。真正动手才知道,下面这几座大山几乎人人都会踩一遍:
传感器驱动调试耗时
从I²C地址写错一位,到忘记开上拉电阻,硬件“沉默”时软件只能干瞪眼。抓逻辑分析仪、对照数据手册逐位核对,动辄一下午。通信协议实现易错
自己手写的UART/Modbus帧解析,边界条件一多就容易越界,上位机偶尔多发一个0x00,板端直接HardFault。算法→嵌入式移植鸿沟
MATLAB里跑通的滤波器,一到C语言就浮点溢出;RAM放不下,FPU没开,实时性不达标,回炉重造。版本管理与可复现性
师兄留下的“毕业设计最终版2.3.1”压缩包,一解压发现缺少.h文件;换台电脑编译,路径一变全红。
一句话:硬件调试烧时间,软件细节烧脑力,两者还常常互相甩锅。
2. AI辅助工具在嵌入式场景下的适用性对比
| 工具 | 在线/离线 | 嵌入式友好度 | 优点 | 槽点 |
|---|---|---|---|---|
| GitHub Copilot | 在线 | ★★★☆ | 补全快,注释→代码能力强 | 需要推送源码到云端,寄存器地址偶尔张冠李戴 |
| CodeWhisperer | 在线 | ★★★ | AWS生态整合好,支持FreeRTOS片段 | 国内网络延迟感人,中文提示词识别一般 |
| 本地LLM(CodeLlama-7B) | 离线 | ★★★★ | 可微调,数据不出内网;能喂数据手册PDF做RAG | 7B模型偶尔“自信”地编出不存在的外设,需要后处理 |
结论:
- 对寄存器级代码,优先选本地LLM+人工review,减少幻觉。
- 对高层协议或算法骨架,Copilot速写框架,再拉回Keil单步。
- 开源协议敏感代码,直接离线处理,避免版权纠纷。
3. 核心实现:用AI生成STM32 HAL库兼容的UART通信代码
目标:让板子通过UART2向上位机循环发送温度值,中断接收上位机指令,缓冲区防溢出。
整体步骤:
需求→提示词
把“HAL库、STM32F103、中断接收、环形缓冲区、CRC8校验”一口气写进提示词,再贴一段数据手册截图(波特率表格),本地LLM秒回一份uart_com.c/.h。工程集成
- 在PlatformIO新建
stm32-cube框架工程,把AI文件丢进src/。 - 用CubeMX重新生成
main.c,勾选USART2 global interrupt,保证向量表对齐。 - 把AI代码的
HAL_UART_RxCpltCallback弱定义替换掉,链接无冲突。
- 在PlatformIO新建
代码示例(已跑通,Nucleo-F103测试)
/* uart_com.h */ #ifndef UART_COM_H #define UART_COM_H #include <stdint.h> #include "stm32_hal.h" /* 由CubeMX生成 */ #define UART_RX_BUF_LEN 128 /* 2^n 对齐,方便掩码 */ #define UART_TX_BUF_LEN 64 void uart_com_init(UART_HandleTypeDef *huart); void uart_com_process(void); /* 主循环调用,消费数据 */ uint8_t uart_com_send_temp(float temp); /* 发送温度帧,带CRC */ #endif/* uart_com.c */ #include "uart_com.h" static UART_HandleTypeDef *g_huart; static volatile uint8_t rx_buf[UART_RX_BUF_LEN]; static volatile uint16_t rx_head = 0, rx_tail = 0; static uint8_t crc8(const uint8_t *data, int len) { uint8_t crc = 0x00; while (len--) { crc ^= *data++; for (int i = 0; i < 8; i++) crc = (crc & 0x80) ? (crc << 1) ^ 0x07 : crc << 1; } return crc; } /* 中断回调 – 双缓冲区切换,避免丢字节 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance != USART2) return; static uint8_t tmp; /* 读取DR寄存器自动清RXNE */ tmp = (uint8_t)(huart->Instance->DR & 0xFF); uint16_t next = (rx_head + 1) & (UART_RX_BUF_LEN - 1); if (next != rx_tail) { /* 满则丢最新字节,留旧数据 */ rx_buf[rx_head] = tmp; rx_head = next; } } /* 主循环消费 */ void uart_com_process(void) { while (rx_tail != rx_head) { uint8_t b = rx_buf[rx_tail]; rx_tail = (rx_tail + 1) & (UART_RX_BUF_LEN - 1); /* TODO: 协议解析、状态机 */ (void)b; /* 先占位,防止编译警告 */ } } /* 发送温度帧:0x55 0xAA [4字节float][1字节CRC] */ uint8_t uart_com_send_temp(float temp) { uint8_t pkt[7] = {0x55, 0xAA}; memcpy(&pkt[2], &temp, 4); pkt[6] = crc8(&pkt[2], 4); return HAL_UART_Transmit(g_huart, pkt, 7, 100) == HAL_OK; } void uart_com_init(UART_HandleTypeDef *huart) { g_huart = huart; /* 启动中断接收,单字节模式 */ HAL_UART_Receive_IT(huart, (uint8_t *)&rx_buf[0], 1); }关键逻辑说明:
- 环形缓冲区用“头/尾指针+掩码”替代取模,节省指令周期。
- 中断里只做“读寄存器+写RAM”,不超50 ns,实时性可控。
- CRC8采用MAXIM格式,AI最初给的是CRC-8/ROHC,查表发现与传感器手册不符,人工替换。
4. AI生成代码的可靠性、内存安全与实时性评估
可靠性
本地LLM把USART2->DR写成USART1->DRR——寄存器名 hallucination,编译直接报错,容易发现;更隐蔽的是中断优先级分组配错,AI默认给NVIC_PRIORITYGROUP_4,若工程里其他外设用GROUP_2,会出现抢占倒挂,需人工回查stm32f1xx_hal_msp.c。内存安全
AI喜欢malloc动态帧缓冲区,嵌入式场景下建议全局静态化,防止碎片。上文代码全部静态数组,编译期即可计算.bss大小。实时性
中断服务程序里,AI一度把crc8函数直接内联,导致72 MHz F103在115200 bps下出现字节间隔超时。解决:把计算移出中断,改用“接收完成”标志,主循环消峰。
5. 生产环境避坑指南
模型幻觉寄存器误配
- 建立“外设寄存器白名单”Excel,比对AI输出。
- 用
#define把官方CMSIS头文件包一层,AI若写出非标寄存器,编译阶段即报错。
版本漂移
- 提示词里锁定HAL版本号,如
stm32f1xx_hal v1.1.8。 - 把CubeMX的
.ioc文件纳入Git,CI自动对比mxconstants变更。
- 提示词里锁定HAL版本号,如
人工校验 checklist(打印出来贴实验室)
- 中断向量表与
startup_stm32f103xb.s是否一致 - 外设时钟
__HAL_RCC_USART2_CLK_ENABLE()有无遗漏 - 看门狗与低功耗是否影响UART DMA睡眠
- 用逻辑分析仪抓10万帧,误码率<1e-5再签字
- 中断向量表与
6. 动手试试:让人机协同再进一步
把毕设里旧版的DHT11驱动拿出来,试着用AI重构:
- 先写提示词:“DHT11时序、STM32F1、HAL延时微秒级、无OS”。
- AI生成后,跑通单总线波形,测量18 ms采样间隔是否满足传感器分辨率。
- 把AI代码的
HAL_Delay换成__NOP忙等,再看功耗变化。 - 记录“AI贡献/人工修改”行数比例,反思:
- 哪些部分AI做得比师兄模板好?
- 哪些寄存器必须人眼核对?
- 如果换成RISC-V,提示词要改哪几个关键字?
当你能把“AI速度”与“人工严谨”用Git提交记录清晰分开,毕设就不再是简单的“调通板子”,而是一份可维护、可迭代的小工程。祝你把更多时间留给创意,而不是重复搬砖。