InternLM2-Chat-1.8B开发入门:STM32嵌入式项目代码注释生成

张开发
2026/4/20 12:35:41 15 分钟阅读

分享文章

InternLM2-Chat-1.8B开发入门:STM32嵌入式项目代码注释生成
InternLM2-Chat-1.8B开发入门STM32嵌入式项目代码注释生成1. 引言如果你是做STM32开发的肯定遇到过这样的场景接手一个老项目或者翻看自己几个月前写的代码面对一堆寄存器操作和中断服务函数一时半会儿想不起来这段代码到底在干什么。手动写注释吧费时费力还容易遗漏关键点。现在有个新思路可以试试让AI来帮你。InternLM2-Chat-1.8B是一个轻量级的大语言模型虽然参数不大但在代码理解和生成方面表现不错。最关键的是它可以在本地部署对硬件要求不高非常适合我们嵌入式开发者自己折腾。这篇教程我就带你一步步走通这个流程怎么用InternLM2-Chat-1.8B为你手头的STM32项目代码自动生成注释和文档。整个过程不复杂你不需要是AI专家只要会基本的Python和命令行操作就行。我们会从准备环境开始到设计针对硬件代码的提问技巧最后把生成的注释整合回你的Keil或者STM32CubeIDE工程里。学完这篇你就能给自己的代码库快速“补课”让代码可读性大幅提升也方便以后维护和团队协作。2. 环境准备与模型部署首先我们得把InternLM2-Chat-1.8B模型在本地跑起来。别担心步骤很清晰。2.1 基础环境搭建你需要一台装有Linux的电脑Windows用WSL2也可以或者有NVIDIA显卡的机器更好。我这里以Ubuntu 22.04为例。安装Python和包管理工具确保你的Python版本在3.8以上。sudo apt update sudo apt install python3 python3-pip git创建虚拟环境这是个好习惯能避免包版本冲突。python3 -m venv lm-env source lm-env/bin/activate安装深度学习框架我们使用PyTorch。根据你是否有GPU去PyTorch官网选择对应的安装命令。如果没有GPU就用CPU版本。# 例如对于Linux系统、使用pip安装、CUDA 11.8的情况 pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 如果只有CPU安装这个 # pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu2.2 获取模型与运行环境InternLM2的模型权重和推理代码是开源的。下载模型你可以从ModelScope魔搭社区或者Hugging Face下载。这里以ModelScope为例速度通常更快。# 安装ModelScope库 pip install modelscope # 在Python交互环境中下载模型 python3 -c from modelscope import snapshot_download; snapshot_download(Shanghai_AI_Laboratory/internlm2-chat-1_8b, cache_dir./model)执行后模型文件会保存在当前目录的./model/Shanghai_AI_Laboratory/internlm2-chat-1_8b文件夹下。安装推理依赖除了PyTorch我们还需要transformers等库。pip install transformers sentencepiece accelerateaccelerate库能帮助模型更高效地运行在CPU或GPU上。2.3 编写一个简单的加载与测试脚本我们来写一个Python脚本确保模型能正常加载并响应。 创建一个文件叫test_model.pyfrom transformers import AutoTokenizer, AutoModelForCausalLM import torch # 指定你下载的模型路径 model_path ./model/Shanghai_AI_Laboratory/internlm2-chat-1_8b print(正在加载分词器...) tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) print(正在加载模型...这可能要几分钟请耐心等待...) model AutoModelForCausalLM.from_pretrained( model_path, torch_dtypetorch.float16, # 使用半精度减少内存占用如果CPU运行则改为 torch.float32 device_mapauto, # 自动分配模型层到可用的设备GPU/CPU trust_remote_codeTrue ) model.eval() # 设置为评估模式 print(模型加载成功) print(\n--- 开始测试对话 ---) # 构建一个简单的对话 history [] query 你好请介绍一下你自己。 print(f用户: {query}) response, history model.chat(tokenizer, query, historyhistory) print(f助手: {response}) print(\n--- 测试完成 ---)运行这个脚本python test_model.py如果看到模型回复了自我介绍恭喜你环境部署成功第一次加载模型时间会比较长之后会快很多。3. 为STM32代码设计有效的Prompt模型跑起来了但直接扔一段STM32代码给它它生成的注释可能泛泛而谈。我们需要设计专门的“提问方式”Prompt引导它关注嵌入式开发特有的重点。3.1 理解STM32代码注释的核心需求一段STM32代码哪些部分最需要清晰的注释寄存器配置比如RCC-AHB1ENR | RCC_AHB1ENR_GPIOAEN;这行代码在使能哪个外设的时钟硬件初始化序列GPIO初始化、定时器配置、ADC校准等步骤的顺序和目的。中断服务函数这个中断响应什么事件里面为什么要清除标志位底层驱动函数这个函数操作了哪些硬件寄存器输入输出参数对硬件状态有何影响业务逻辑与硬件交互点哪里是软件逻辑在读取传感器哪里是在控制电机。我们的Prompt要引导模型特别关注这些硬件相关的点。3.2 构建基础Prompt模板一个有效的Prompt通常包含以下几个部分角色设定告诉模型它要扮演什么角色。任务指令明确告诉它要做什么。输入格式说明你给它的代码是什么。输出格式规定它应该怎么回复你。重点强调特别指出需要关注哪些嵌入式相关的细节。下面是一个基础的模板你是一个经验丰富的嵌入式软件工程师尤其精通STM32系列微控制器开发。你的任务是为提供的C语言代码片段生成清晰、准确、实用的中文注释。 请遵循以下规则 1. 为每一行或每一个关键代码块添加行内注释。 2. 在关键函数的上方添加函数功能、参数及返回值的详细说明。 3. 重点注释以下内容 - 对STM32外设寄存器如RCC, GPIO, USART, TIM等的读写操作说明其目的。 - 中断服务函数解释触发条件和函数内关键操作。 - 硬件初始化流程说明每一步的作用。 - 任何与硬件定时、延时、状态读取相关的代码。 4. 注释语言使用中文力求简洁明了避免过度冗长。 以下是需要注释的代码片段 [这里粘贴你的代码] 请直接输出添加了注释后的完整代码。3.3 针对不同代码类型的Prompt微调对于外设初始化代码可以在Prompt开头更具体 “以下是一段STM32的GPIO初始化代码请重点注释每一行寄存器配置的含义特别是GPIO模式、速度、上下拉电阻设置的选择原因。”对于中断服务函数可以强调 “这是一个STM32的定时器中断服务函数。请解释1) 是哪个定时器的什么中断 2) 中断标志位是如何被清除的 3) 中断服务函数中实现的核心业务逻辑是什么”对于复杂的驱动函数可以要求 “这是一个读取STM32内部温度传感器的函数。请为每一行代码添加注释并最终在函数上方用一段话总结该函数的工作流程、硬件依赖和注意事项。”多尝试几次你就能找到最适合你代码风格的Prompt。4. 实战从代码到注释的完整流程现在我们用一个真实的STM32代码片段走一遍完整的流程。4.1 准备待注释的代码假设我们有一个简单的LED闪烁程序使用STM32的HAL库但注释不全。// main.c 片段 #include main.h #include gpio.h TIM_HandleTypeDef htim2; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM2_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); HAL_TIM_Base_Start_IT(htim2); while (1) { } } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim-Instance TIM2) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); } } static void MX_TIM2_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; htim2.Instance TIM2; htim2.Init.Prescaler 7999; htim2.Init.CounterMode TIM_COUNTERMODE_UP; htim2.Init.Period 999; htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(htim2) ! HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(htim2, sClockSourceConfig) ! HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(htim2, sMasterConfig) ! HAL_OK) { Error_Handler(); } }4.2 编写Python脚本进行批量注释我们不能每次都手动复制粘贴。写个脚本来自动化处理工程里的.c和.h文件。# generate_comments.py import os from transformers import AutoTokenizer, AutoModelForCausalLM import torch def load_model_and_tokenizer(model_path): 加载模型和分词器 print(f正在从 {model_path} 加载模型...) tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_path, torch_dtypetorch.float16, device_mapauto, trust_remote_codeTrue ) model.eval() print(模型加载完毕。) return tokenizer, model def generate_comment_for_code(tokenizer, model, code_snippet): 使用模型为代码片段生成注释 prompt_template 你是一个经验丰富的嵌入式软件工程师尤其精通STM32系列微控制器开发。你的任务是为提供的C语言代码片段生成清晰、准确、实用的中文注释。 请遵循以下规则 1. 为每一行或每一个关键代码块添加行内注释。 2. 在关键函数的上方添加函数功能、参数及返回值的详细说明。 3. 重点注释以下内容 - 对STM32外设寄存器如RCC, GPIO, USART, TIM等的读写操作说明其目的。 - 中断服务函数解释触发条件和函数内关键操作。 - 硬件初始化流程说明每一步的作用。 - 任何与硬件定时、延时、状态读取相关的代码。 4. 注释语言使用中文力求简洁明了避免过度冗长。 以下是需要注释的代码片段{code}请直接输出添加了注释后的完整代码。 prompt prompt_template.format(codecode_snippet) # 调用模型生成 inputs tokenizer(prompt, return_tensorspt, truncationTrue, max_length2048).to(model.device) with torch.no_grad(): outputs model.generate(**inputs, max_new_tokens1024, temperature0.2, do_sampleTrue) response tokenizer.decode(outputs[0], skip_special_tokensTrue) # 从回复中提取代码部分模型可能会重复一些指令我们只取最后一部分 # 这里简单处理查找代码块标记 或者直接返回模型生成的全部内容 if in response: # 尝试提取被 包裹的代码 parts response.split() if len(parts) 3: return parts[1].strip() # 取第一个代码块的内容 # 如果没有代码块标记返回整个回复可能需要后续清洗 return response def process_file(file_path, tokenizer, model): 处理单个源文件 print(f处理文件: {file_path}) try: with open(file_path, r, encodingutf-8) as f: original_code f.read() except: print(f 无法读取文件 {file_path}跳过。) return # 如果文件太大可以分块处理。这里假设文件不大。 commented_code generate_comment_for_code(tokenizer, model, original_code) # 保存结果到新文件 new_file_path file_path.replace(.c, _commented.c).replace(.h, _commented.h) with open(new_file_path, w, encodingutf-8) as f: f.write(commented_code) print(f 已生成注释文件: {new_file_path}) def main(): model_path ./model/Shanghai_AI_Laboratory/internlm2-chat-1_8b project_dir ./your_stm32_project # 替换为你的STM32工程目录 tokenizer, model load_model_and_tokenizer(model_path) # 遍历工程目录下的.c和.h文件 for root, dirs, files in os.walk(project_dir): for file in files: if file.endswith((.c, .h)): full_path os.path.join(root, file) process_file(full_path, tokenizer, model) print(\n所有文件处理完成请检查生成的 *_commented.* 文件。) if __name__ __main__: main()注意运行前将project_dir变量改为你的STM32项目路径。首次运行需要对整个模型进行推理速度取决于你的硬件。对于大项目建议先挑选核心文件进行测试。4.3 解析与集成模型输出运行脚本后你会得到类似下面的输出节选自模型对MX_TIM2_Init函数的注释/** * brief 初始化TIM2定时器配置为基本定时模式用于产生周期性中断。 * note 定时器时钟源为内部时钟预分频器和自动重载值共同决定中断周期。 */ static void MX_TIM2_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; htim2.Instance TIM2; /* 选择TIM2外设 */ htim2.Init.Prescaler 7999; /* 预分频器值将系统时钟分频。假设系统时钟为80MHz则定时器时钟为80MHz/(79991)10kHz */ htim2.Init.CounterMode TIM_COUNTERMODE_UP; /* 向上计数模式 */ htim2.Init.Period 999; /* 自动重载值计数到999后产生更新事件并清零。结合预分频中断频率为10kHz/(9991)10Hz */ htim2.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; /* 时钟分频用于死区时间控制此处为不分频 */ htim2.Init.AutoReloadPreload TIM_AUTORELOAD_PRELOAD_DISABLE; /* 禁止自动重载预装载修改ARR值立即生效 */ if (HAL_TIM_Base_Init(htim2) ! HAL_OK) { /* 初始化定时器基础单元 */ Error_Handler(); } sClockSourceConfig.ClockSource TIM_CLOCKSOURCE_INTERNAL; /* 选择内部时钟作为定时器时钟源 */ if (HAL_TIM_ConfigClockSource(htim2, sClockSourceConfig) ! HAL_OK) { /* 配置时钟源 */ Error_Handler(); } sMasterConfig.MasterOutputTrigger TIM_TRGO_RESET; /* 主模式触发输出复位此处未使用触发输出功能 */ sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_DISABLE; /* 禁止主从模式 */ if (HAL_TIMEx_MasterConfigSynchronization(htim2, sMasterConfig) ! HAL_OK) { /* 配置主从同步模式 */ Error_Handler(); } }集成到工程人工审核这是最关键的一步你必须仔细检查AI生成的注释。模型可能理解错误特别是对于非常规或高度定制的硬件操作。确保注释准确反映了代码意图。选择性合并将审核无误的注释手动合并到你的原始源代码文件中。可以直接复制整个函数也可以逐行对照添加。保持风格统一调整注释格式使其与你项目中已有的注释风格保持一致比如使用/* */还是//注释的详细程度等。5. 进阶技巧与注意事项用熟了之后你可以尝试下面这些方法让注释生成的效果更好。5.1 提升注释质量的技巧分块处理对于很长的函数或文件不要一次性全部喂给模型。可以按函数或逻辑块拆分这样模型能更专注生成质量更高。提供上下文在Prompt里除了当前代码段可以简单说明这个函数属于哪个模块例如“这是STM32F4xx系列芯片上用于控制WS2812B LED灯带的PWM驱动函数的一部分”。迭代优化如果第一次生成的注释不满意可以把模型的输出和你的修改意见作为新的对话历史再次提问。例如“你刚才生成的注释里对预分频器的解释有误实际系统时钟是16MHz。请根据这个信息重新生成注释。”结合Doxygen风格如果你希望生成更规范的API文档可以在Prompt中要求输出Doxygen格式的注释/** brief ... */模型通常也能很好地理解。5.2 常见问题与处理生成内容不全或截断这是因为模型有最大生成长度限制。可以在调用model.generate()时增加max_new_tokens参数比如2048同时也要确保输入不要过长。注释过于啰嗦或偏离重点调整你的Prompt更明确地强调“简洁”和“关注硬件操作”。可以加入负面示例“避免解释通用的C语言语法如if语句或while循环除非其条件与硬件状态直接相关。”模型不理解特定寄存器或库函数InternLM2-Chat-1.8B的训练数据可能未覆盖所有最新的HAL库或芯片型号。对于它明显解释错误的地方需要你手动修正。你也可以在Prompt中提供简短的寄存器定义说明。性能问题在CPU上运行生成大量代码的注释会比较慢。建议只对核心、复杂的函数使用此方法简单的初始化代码手动注释可能更快。6. 总结整体尝试下来用InternLM2-Chat-1.8B来辅助生成STM32代码注释是一个挺有意思且有一定实用价值的探索。它特别适合处理那些结构清晰但缺乏文档的寄存器配置代码和中断逻辑能快速帮你补上“为什么这么写”的说明省去不少查手册的时间。不过也要清醒地认识到它不是一个全自动的解决方案。模型的理解能力有限尤其面对高度优化、技巧性很强的底层驱动时可能会“一本正经地胡说八道”。所以人工审核和修正这一步绝对不能省。把它看作一个强大的“实习生”它能帮你起草初稿但最终把关和定稿的责任还在你自己身上。对于嵌入式团队来说可以考虑将这套流程集成到CI/CD中比如在提交代码时自动对修改的函数生成注释建议供开发者参考和采纳。这能逐步提升整个代码库的可读性和可维护性。如果你刚开始接触建议从一个文件或几个关键函数入手熟悉整个流程和Prompt的调优。用好了它确实能成为你开发工具箱里一个提高效率的好帮手。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。

更多文章