stm32OTA升级学习笔记(2)
实验结果如上图APP1区上电后等待服务器下发固件包确认完毕后开始传输通过帧池缓存收到的数据同时使用crc校验检验每帧的数据是否正确加入传输完成帧通知mcu传输结束并校验总数据crc是否正确传输完进入request_upgrade_task修改更新标志位并开始擦除重写flash。void request_upgrade_task(void *pvParameters) { (void)pvParameters; while (1) { /* 等待 task_ota_flash 发来的升级通知 */ xTaskNotifyWait(0, 0, NULL, portMAX_DELAY); if (xWifisendTaskHandle ! NULL) xTaskNotify(xWifisendTaskHandle, 0x02, eSetValueWithOverwrite); ota_config_t config; config.magic OTA_MAGIC; config.update_flag 1; config.version CURRENT_VERSION; config.firmware_size g_firmware_size; config.crc32 g_firmware_crc; __disable_irq(); //关闭全局中断防止读写过程被打断 // 擦除配置区所在页写入 config flash_erase(CONFIG_BASE, sizeof(ota_config_t)); flash_write(CONFIG_BASE, (uint8_t*)config, sizeof(ota_config_t)); // 软复位 NVIC_SystemReset(); } }/* Flash 写任务 */ void task_ota_flash(void *pvParameters) { fw_frame_t frame; uint32_t write_addr; uint32_t received_count 0; /* 已成功写入的数据帧数用于收齐校验 */ int32_t erased_page -1; /* 已擦除的 OTA 区页号-1 表示本次尚未擦除 */ while (1) { if (xQueueReceive(xFrameQueue, frame, portMAX_DELAY) ! pdTRUE) continue; /* 判断是否是结束帧 */ if (frame.current_packet PACKET_END) { uint32_t fw_size 0; uint32_t fw_crc 0; /* 结束帧负载data[0:4]固件总长(小端), data[4:8]整包 CRC32(小端) */ if (frame.data_len 8) { fw_size (uint32_t)frame.slot-buf[0] | ((uint32_t)frame.slot-buf[1] 8) | ((uint32_t)frame.slot-buf[2] 16) | ((uint32_t)frame.slot-buf[3] 24); fw_crc (uint32_t)frame.slot-buf[4] | ((uint32_t)frame.slot-buf[5] 8) | ((uint32_t)frame.slot-buf[6] 16) | ((uint32_t)frame.slot-buf[7] 24); } frame_pool_free(frame.slot); /* 收齐校验已收数据帧数必须等于总包数否则丢弃本次升级 */ if (fw_size ! 0 received_count frame.total_packets) { g_firmware_size fw_size; g_firmware_crc fw_crc; xTaskNotify(xrequest_upgrade_taskHandle, 0x01, eSetValueWithOverwrite); } received_count 0; erased_page -1; /* 本次传输结束下次重新擦除 */ continue; /* 结束帧不写 Flash直接继续 */ } /* 新一轮传输开始清零计数并强制重新擦除 */ if (frame.current_packet 0) { received_count 0; erased_page -1; } write_addr OTA_BUFFER_BASE ((uint32_t)frame.current_packet * DATA_FRAME_LEN); /* 边界保护不能写到 config 区及以后 */ if (write_addr DATA_FRAME_LEN CONFIG_BASE) { frame_pool_free(frame.slot); continue; } /* Flash 只能把 1 写成 0写入前必须先擦除所在页按页惰性擦除每 4 帧一次 */ { int32_t page (int32_t)((write_addr - OTA_BUFFER_BASE) / FLASH_PAGE_SIZE); if (page ! erased_page) { if (flash_erase(OTA_BUFFER_BASE (uint32_t)page * FLASH_PAGE_SIZE, FLASH_PAGE_SIZE) ! Status_OK) { frame_pool_free(frame.slot); continue; } erased_page page; } } /* 写 Flash只写实际长度不写 padding */ if (flash_write(write_addr, frame.slot-buf, frame.data_len) Status_OK) received_count; /* 写入成功计入已收帧数 */ else { /* 写失败可以记录错误忽略 */ } /* 归还 slot */ frame_pool_free(frame.slot); } }在数据解析任务中使用状态机判断当前接收状态void wifi_get_task(void *pvParameters) { uint8_t byte; uint8_t line[AT_LINE_MAX]; size_t line_len 0; parser_t parser; /* 初始化解析器 */ memset(parser, 0, sizeof(parser)); parser.state 0; /* ST_HEADER_1 */ while (1) { /* 一次读一个字节 */ if (xStreamBufferReceive(uartStreamBuffer, byte, 1, portMAX_DELAY) ! 1) continue; /* ---------- 状态机 ---------- */ switch (parser.state) { /* 状态 0等 0x55同时处理 AT 行 */ case 0: /* ST_HEADER_1 */ /* AT 行处理遇到 \n 提交一行 */ if (byte \n line_len 0) { line[line_len] \0; xQueueSend(atQueue, line, 0); line_len 0; } else if (byte ! \r) { if (line_len AT_LINE_MAX - 1) line[line_len] byte; } /* 检测帧头起点 */ if (byte FRAME_HEADER_1) { parser.state 1; /* ST_HEADER_2 */ } break; /* 状态 1等 0x44 */ case 1: /* ST_HEADER_2 */ if (byte FRAME_HEADER_2) { /* 帧头确认从帧池分配 slot */ parser.slot frame_pool_alloc(); if (parser.slot NULL) { parser.state 0; /* 无空闲 slot丢弃这一帧 */ break; } parser.crc32 0; parser.crc_byte_idx 0; parser.data_idx 0; parser.state 2; /* ST_TOTAL */ } else if (byte FRAME_HEADER_1) { /* 连续 0x55保持在 ST_HEADER_2 */ parser.state 1; } else { /* 不是 0x44回起点当前字节按 AT 处理 */ if (byte \n line_len 0) { line[line_len] \0; xQueueSend(atQueue, line, 0); line_len 0; } else if (byte ! \r) { if (line_len AT_LINE_MAX - 1) line[line_len] byte; } parser.state 0; } break; /* 状态 2读总包数 */ case 2: /* ST_TOTAL */ parser.total_packets byte; parser.state 3; /* ST_CURRENT */ break; /* 状态 3读当前包号 */ case 3: /* ST_CURRENT */ parser.current_packet byte; parser.data_len 0; parser.len_byte_idx 0; parser.state 4; /* ST_LEN */ break; /* 状态 4读实际长度2 字节小端 */ case 4: /* ST_LEN */ parser.data_len | (uint16_t)((uint16_t)byte (parser.len_byte_idx * 8)); parser.len_byte_idx; if (parser.len_byte_idx 2) { if (parser.data_len 0 || parser.data_len DATA_FRAME_LEN) { /* 非法长度回起点释放 slot */ frame_pool_free(parser.slot); parser.slot NULL; parser.state 0; } else { parser.crc_byte_idx 0; parser.state 5; /* ST_CRC */ } } break; /* 状态 5读 4 字节 CRC32小端 */ case 5: /* ST_CRC */ parser.crc32 | ((uint32_t)byte) (parser.crc_byte_idx * 8); parser.crc_byte_idx; if (parser.crc_byte_idx 4) { parser.data_idx 0; parser.state 6; /* ST_DATA */ } break; /* 状态 6读数据固定 DATA_FRAME_LEN 字节 */ case 6: /* ST_DATA */ parser.slot-buf[parser.data_idx] byte; if (parser.data_idx DATA_FRAME_LEN) { parser.state 7; /* ST_TAIL_1 */ } break; /* 状态 7等 0x33 */ case 7: /* ST_TAIL_1 */ if (byte FRAME_TAIL_1) { parser.state 8; /* ST_TAIL_2 */ } else { frame_pool_free(parser.slot); parser.slot NULL; parser.state 0; } break; /* 状态 8等 0x22帧完成 */ case 8: /* ST_TAIL_2 */ if (byte FRAME_TAIL_2) { /* 帧完成校验 CRC */ parser.crc_calc calculate_crc32_ex( parser.total_packets, parser.current_packet, parser.data_len, parser.slot-buf ); if (parser.crc_calc parser.crc32) { /* CRC 通过交给 Flash 写入 */ fw_frame_t frame; frame.total_packets parser.total_packets; frame.current_packet parser.current_packet; frame.data_len parser.data_len; frame.crc32 parser.crc32; frame.slot parser.slot; if (xQueueSend(xFrameQueue, frame, 0) ! pdTRUE) { /* 队列满归还 slot */ frame_pool_free(parser.slot); } /* 注意slot 在这里不 free由 Flash 任务 free */ } else { /* CRC 失败归还 slot */ frame_pool_free(parser.slot); } parser.slot NULL; parser.state 0; line_len 0; } else { frame_pool_free(parser.slot); parser.slot NULL; parser.state 0; } break; default: parser.state 0; break; } } }APP1区同样基于freertos来写更新程序仅为精简串口输出程序。如有需要可以到我的github仓库拉取0571xu/bootloader_APP1code一起学习daydayup

相关新闻

【C++算法打怪专栏】LeetCode 349. 两个数组的交集(哈希集合解法总结)

【C++算法打怪专栏】LeetCode 349. 两个数组的交集(哈希集合解法总结)

【C算法打怪专栏】LeetCode 349. 两个数组的交集(哈希集合解法总结) 一、 题目链接 LeetCode 官方网址:LeetCode 349. 两个数组的交集 二、 核心思路 去重与匹配:题目要求输出的交集结果中每个元素必须是唯一的,且不…

2026/10/15 21:35:51 阅读更多 →
Codex多模型接入实战:网关路由与算力成本优化

Codex多模型接入实战:网关路由与算力成本优化

最近被算力问题折腾得不轻。团队里跑AI辅助开发的服务器队列动不动就要等半小时,云端API的免费配额早就见底,付费额度又贵得肉疼;更要命的是,单一模型处理稍微大一点的代码库,干到一半就上下文溢出,整个任务…

2026/10/15 21:35:51 阅读更多 →
MySQL内置函数实战指南:从字符串到窗口函数的用法与性能陷阱

MySQL内置函数实战指南:从字符串到窗口函数的用法与性能陷阱

写SQL这事儿,入门靠增删改查,进阶拼的就是函数用得熟不熟。拿我自己的经历说,前几年给某业务部门做日报统计数据,要按天汇总订单、还得分渠道、算同比环比,当时我对窗口函数和各种日期函数也不熟,愣是写了一…

2026/10/15 21:35:51 阅读更多 →

最新新闻

SpringBoot+Vue党员教育管理系统设计与实现:从需求分析到部署排坑

SpringBoot+Vue党员教育管理系统设计与实现:从需求分析到部署排坑

最近刚把一个计算机毕业设计项目完整跑通,用的是 SpringBoot Vue 的组合,题目是“党员教育和管理系统”,也可以理解成一套党校线上指导平台。如果你正好在选毕业设计题目,或者公司内部需要做一套线上学习培训系统,这个…

2026/10/15 22:27:31 阅读更多 →
CC Switch 多环境切换实战:把 Claude Code 的 endpoint 改到 TaoToken

CC Switch 多环境切换实战:把 Claude Code 的 endpoint 改到 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 22:27:31 阅读更多 →
基于SSM+Vue的景区容灾管理系统设计与实现

基于SSM+Vue的景区容灾管理系统设计与实现

又到一年毕业设计的季节,后台私信里很多同学都在问差不多的一个问题:SSM Vue 到底做什么题目比较稳?既要工作量够,又要技术栈主流,还得好写论文、好答辩。今天想拆解的,正是这类题目里很有代表性的一类——…

2026/10/15 22:27:31 阅读更多 →
Vue看板娘教程详细版:用TaoToken统一Key接入Live2D交互组件

Vue看板娘教程详细版:用TaoToken统一Key接入Live2D交互组件

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 22:27:31 阅读更多 →
中文短文本场景下可调试的Transformer Encoder实战

中文短文本场景下可调试的Transformer Encoder实战

1. 这不是又一篇“Transformer原理科普”,而是一份NLP工程师的实战手记你点开这个标题,大概率是刚学完《Attention Is All You Need》那篇论文,或者被BERT、GPT这些词轰炸得有点晕,想找个能真正上手、能调参、能看懂模型内部在“想…

2026/10/15 22:27:31 阅读更多 →
wow.js滚动触发动画原理与实战:IntersectionObserver轻量封装

wow.js滚动触发动画原理与实战:IntersectionObserver轻量封装

简介:这是一份轻量级网页滚动动画实现方案,面向前端初学者与网页设计师,解决网站静态内容缺乏视觉动效、用户交互体验平淡的问题。资源基于流行的wow.js库封装,通过CSS类绑定实现元素进入视口时的自动动画触发,支持淡入…

2026/10/15 22:26:22 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →