- 博客(88)
- 收藏
- 关注
原创 基于 STM32 + ESP8266 + W25Q64 的双核 OTA 底层架构总结
目录第一战役:App 端固件下载与“三级缓存”防丢包机制 (App -> SPI Flash)1. 核心挑战:速度差与堵塞2. 解决方案 A:提前擦除(空间换时间)3. 解决方案 B:神级“三级缓存”架构4. 下载收尾动作第二战役:Bootloader 固件搬运与内部 Flash 烧录 (SPI Flash -> Internal Flash)1. 验明正身(读标志位)2. 内部 Flash 擦写机制(Bootloader 核心代码解读)第三战役:灵魂跃迁——现场清理与内核级跳转 (Bootloader -
2026-04-29 10:18:47
521
原创 嵌入式底层核心架构详解 (Cortex-M3)
分为 APB2(高速区,如 SPI1、USART1、GPIO)和 APB1(低速区,如 IIC、普通定时器)。CPU的“极速口袋”。分为特殊寄存器(PC、SP、PSR)和通用寄存器(R0-R15),是 ALU 干活时的数据暂存区。(SP向下移),然后把下一个任务的堆栈数据弹回 CPU 寄存器(恢复现场),PC 指针跳跃执行新任务。拥有 ICode(取指)、DCode(读常量)、System 总线(读写变量/外设)。启动文件(汇编配置环境)、HAL库(被动函数箱)、中断(硬件机制)都不是内核。
2026-04-25 12:08:26
397
原创 SPI 协议底层通信原理详解
而 SPI 使用的是推挽输出,内部晶体管直接驱动高低电平,上升/下降沿非常陡峭,因此速率可以非常高。此时,主机的寄存器里装的是从机原来的数据,从机的寄存器里装的是主机原来的数据,主机想和哪个从机通信,就把连接该从机的 SS 线拉低(低电平有效)。我们需要从机的数据,但 SPI 必须有主机的时钟驱动才能工作,时钟一动就会发送数据。我们把有效数据放进寄存器发出去,同时时钟也会把从机的数据交换进我们的寄存器。(如上升沿或下降沿,取决于模式)时,主机和从机的移位寄存器同时整体向左移一位。结束时 SS 拉高。
2026-04-25 10:15:28
391
原创 STM32 串口通信 (UART) 全栈底层复习指南
有了双缓冲,移位寄存器可以继续收下一个字节,CPU 只要在下一个字节收完之前,把 RDR 里的数据读走就行,大大降低了 CPU 的压力。外部 RX 线上的数据是一位一位(串行)进来的,移位寄存器就像一个抽屉,每来一个 Bit,它就把抽屉里的数据往左推一格,直到 8 个 Bit 全部收齐。它是串口的“心脏起搏器”。它的根本在于**“双方约定相同的波特率,并用规定的速率去采样电平数据”**。设备 A 的 TX(发送数据线)连设备 B 的 RX(接收数据线),设备 A 的 RX 连设备 B 的 TX。
2026-04-22 15:33:07
849
原创 C++继承详解
(注:子类其实继承了父类的 private 数据,占据了内存,只是编译器不让你直接访问它)继承方式决定了基类成员到达派生类后,访问权限会发生怎样的“降级”。:先调用基类构造,再调用派生类构造。:先调用派生类析构,再调用基类析构。当派生类和基类出现同名成员变量或同名成员函数时,:一个派生类可以同时继承多个基类,用逗号隔开。:只有本类内可以访问,子类和类外都不能访问。访问基类被隐藏的成员:加上基类的作用域。:无论继承多少层,派生类多少个,可以访问,类外不能访问。访问派生类成员:直接点。:类内类外都能访问。
2026-04-21 16:15:33
265
原创 STM32 启动模式与固件更新机制 (底层深度解析)
要理解启动和烧录,必须先理解 ARM Cortex-M 内核的。当芯片上电或复位时,CPU 的程序计数器(PC 指针)被硬件强制设定为去寻找虚拟地址。它会从取出第一个数据,作为的初始值。它会从取出第二个数据,作为,也就是程序真正开始执行的第一条指令的地址。物理存储器有自己的真实地址,通过配置 BOOT 引脚,总线矩阵会将不同的物理地址“伪装”成,骗过 CPU,从而决定芯片从哪里开始运行。
2026-04-20 18:46:18
483
原创 C/C++ 中 static 关键字的蜕变:从局部控制到面向对象的共享机制
存储位置static成员变量全都在数据区,不占对象内部的内存。生命周期:与整个程序的生命周期相同,程序启动时分配,结束时释放。调用方式:强烈建议使用的方式调用,彰显其共享本质。权限限制:静态成员函数没有this指针,因此只能碰静态数据,绝不能碰普通数据!
2026-04-19 17:36:00
412
原创 深入理解 C++ 内存模型与对象底层机制:this 指针的秘密
今天,我们就从 C++ 的四大内存分区说起,扒开编译器的外衣,彻底搞懂对象与this指针的底层逻辑。
2026-04-19 16:27:25
339
原创 MCU内存管理详解
但在一些带有无线网络功能的SoC(如ESP8266/ESP32)中,由于固件较大,经常使用外部SPI Flash,并通过**XIP(Execute In Place,就地执行)**技术,让CPU直接通过SPI总线读取并在Flash中执行代码,而不需要全盘拷贝到RAM中。当你使用IDE(比如Keil或者CLion + GCC)编译完一段嵌入式C代码后,编译器会把你的程序切分成不同的“段”(Segment)。,它们在运行时的归宿是SRAM,但它们在断电时的“种子值”(也就是初始值)必须保存在Flash中。
2026-04-18 17:15:15
475
原创 从单片机开发看透网络底层:Wi-Fi、TCP/IP 与 HTTP 的通俗解析
经历千辛万苦,数据到了服务器。但服务器一天接几亿条数据,怎么知道你是要查天气还是看视频?这就需要一套“全球统一的串口通信帧格式”,HTTP 就是干这个的。帧头 + 命令码 + 数据段 + 校验和。服务器按照这个标准格式解析,秒懂你的诉求,然后原封不动地用这套格式把 JSON 天气数据发回来。
2026-04-18 16:35:38
356
原创 STM32+ESP8266 智能手表实战:天气获取与阿里云时钟同步
这是我前面的手表项目,在我的智能手表项目中,天气显示和时间同步是两个非常核心的功能。本文将详细复盘我是如何通过 STM32 驱动 ESP8266 模块,连接 Wi-Fi 后通过 TCP 协议访问获取当地天气与温度,并连接实现精准的时间对齐的。这套逻辑经过了无数次 Debug 和优化,彻底解决了单片机内存溢出、死机、时间解析错乱等问题,包含了我对 C 语言指针、内存管理以及 FreeRTOS 任务调度的深度总结。
2026-04-17 21:10:19
1768
原创 C++ 堆内存管理:new/delete 与 malloc/free 的终极对决
在 C/C++ 开发中,我们经常需要在堆区(Heap)手动申请和释放内存。从 C 语言过渡到 C++,我们迎来了全新的new和delete,它们究竟比 C 语言老前辈malloc和free强在哪里?这篇文章带你一次性搞透!
2026-04-17 11:09:12
406
原创 C++ 面向对象基础:类、访问权限,构造函数,析构函数
类内定义:直接在类的花括号内写完函数体。适合简短的函数。类外定义:类内部只写声明,外部写实现。此时函数名前必须加上作用域解析符。适合复杂的函数,让类的结构一目了然。public:// 1. 类内定义cout << "吃骨头" << endl;// 2. 类外定义的第一步:内部只写声明// 2. 类外定义的第二步:外部写实现,必须加 Dog::cout << "汪汪汪!" << endl;Dog dog1;dog1.eat();return 0;
2026-04-16 14:30:25
237
原创 C++ 核心特性浅析:默认参数、函数重载与底层原理
在从 C 语言过渡到 C++ 的过程中,函数特性的增强是最直观的体验之一。和,并从编译器的角度扒一扒它们底层的实现原理。
2026-04-15 10:44:15
264
原创 C++引用详解
引用在 C++ 语法层面上是个新东西,但在编译器底层,它就是个指针,只不过指针的指向被锁死了(只读)。相比于传值(拷贝整个对象,慢)和传指针(需要写解引用符号,丑),传引用是 C++ 最推荐的传参方式。这也是为什么 C++ 引入引用:为了让你拥有指针的效率,却不用写繁琐且容易出错的。函数可以返回引用,这样可以把函数调用当成左值(放在等号左边)来使用。如果你只是想读取一个大对象(比如一个很大的结构体),不想拷贝它,也。,引用必须绑定到一个合法存在的实体上。:一旦引用了 A,就不能再引用 B。
2026-04-14 15:09:39
70
原创 C++命名空间
命名空间里面还能再套一层。这在大型项目中很常见,比如“硬件驱动层”下面再分“显示屏”和“传感器”。在 C 语言中,为了防止两个模块都有初始化函数,我们只能把名字变长(比如。而在 C++ 中,命名空间把这些冲突“包裹”起来了,名字可以极致精简。如果嵌套的层级太深,每次调用都要写很长一串名字,可以通过取别名来简化。(注:如果你以后用 C++17 或更新的标准,嵌套声明可以简写为。虽然命名空间是“逻辑”划分,但为了代码整洁,我们通常会配合头文件(函数想要用到这些被“包裹”起来的函数和变量时,必须带上名字和。
2026-04-14 11:29:42
170
原创 从裸机到 FreeRTOS:STM32 智能手表重构之路
针对简单按键,旧状态机会在一次完整的物理按压中,连续触发并发送“一直按住”、“按下瞬间”、“松开瞬间”等多条状态消息。其次,DMA 传输结束不代表 I2C 物理层发完,若立即进入低功耗切断时钟,会导致最后的 bit 死在总线上。将 STM32 的 60 秒无操作进入 STOP 模式的逻辑,也交给了单次软件定时器,并完美融合了 FreeRTOS 的 Tickless Idle 低功耗模式。引入了 20ms 软件定时器,实现了零 CPU 占用的完美消抖,并在其中集成了单击、双击、长按的复杂状态机逻辑。
2026-04-12 16:46:16
523
1
原创 FreeRTOS软件定时器详解
软件定时器是 RTOS 中极其优雅的机制。它用最少的内存和 CPU 开销,完美解决了传统裸机代码中满屏都是的丑陋代码。只要你牢记**“不阻塞回调”和“分清中断 API”**这两条铁律,它将是你构建复杂工业级系统最顺手的兵器!
2026-04-11 21:51:26
624
原创 详解FreeRTOS信号量
比如用户疯狂按了 5 次按键,中断释放了 5 次信号量,任务就会依次被唤醒 5 次,确保事件不丢失。比如系统只有 3 个内存块,初始值为 3,最多允许 3 个任务同时获取,第 4 个任务来拿就会被阻塞排队。当高优先级任务因为等锁被卡住时,系统会临时提高拿锁的低优先级任务的级别,催它赶紧执行完还钥匙。(比如 DMA 传输完成触发中断,在中断中 Give,唤醒挂起死等的 GUI_Task)。获取(Take)减 1,释放(Give)加 1。:防止任务 A 写到一半,任务 B 就来读,造成“数据撕裂”。
2026-04-11 09:49:06
256
原创 FreeRTOS消息队列与裸机全局变量的区别
单片机任务在处理时,会按照先进先出的顺序,一个一个地从队列中拿出消息来处理。如果要传递极其复杂的数据(比如按键的 ID + 按下时长 + 当前电压值),需要定义一大堆全局变量配合使用,不仅代码杂乱无章,耦合度也极高,极易出 Bug。若要保证安全,开发者必须手动加锁(比如频繁地关中断、开中断,或者加互斥量),极其繁琐。这意味着,你可以用极小的开销,在不同任务之间传递任意复杂类型的数据包裹。,还非常耗电,更可怕的是它会霸占 CPU,导致其他任务得不到执行。读取消息时,我们可以设置一个超时时间(Tick)。
2026-04-09 15:04:46
211
原创 STM32 裸机中断与 FreeRTOS 中断管理的四大核心差异
在移植 FreeRTOS 时,我们经常会在很多新手会照抄这行代码,但并不理解其背后的深意。
2026-04-08 11:24:59
414
原创 为什么在 FreeRTOS 中必须将 HAL 库的 Timebase 更改为其他定时器?
让 FreeRTOS 独享低优先级的 SysTick 负责调度,让 HAL 库使用独立的高优先级定时器负责延时,彻底消除隐患。为了保证延时的精准不被打断,HAL 库通常会将 SysTick 的中断优先级设置得极高(默认最高,通常为 0 或 1)。为了做到“井水不犯河水”,引入 FreeRTOS 时,必须在配置中将 HAL 库的时基源(CubeMX 里的。假设系统中触发了一个优先级为 6 的外部中断,程序进入了该中断回调函数(ISR)。的底层机制是不断轮询计数值,等待 SysTick 中断来使其递增。
2026-04-07 09:09:03
109
原创 Freertos中PendSV与sysTick
SysTick 可能在其他高优先级中断(如 UART)还没退出时就被触发(虽然它在排队)。如果在这一过程中你强行改动堆栈指针,一旦此时被更高级的中断抢占,高优先级中断会在一个“坏掉”的 MSP 基础上压栈,瞬间导致系统跑飞。SysTick 中运行的是内核 C 代码(处理延时、调度算法),编译器会自动在。它会打断 PendSV,在 MSP 上压栈,运行完后再回到 PendSV。通过汇编指令,从**新的 PSP(任务 B 的栈)**中弹出。,找到当前优先级最高且处于就绪态的任务(假设是任务 B)。
2026-04-06 16:29:45
248
原创 基于 STM32 的模块化多功能手表系统:从架构设计到低功耗深度实践
本项目是一个基于STM32(使用HAL库)的多功能手表系统,整体架构位模块化状态机架构,具有低功耗管理,多任务切换和典型的嵌入式交互逻辑。
2026-04-06 11:25:33
1225
1
原创 非阻塞按键(单击,双击,长按)
我们使用的按键都有按键抖动,当按下按键和松开按键都会有20ms的抖动,如果不处理这40ms的抖动,很影响单片机正常读取数据,平时我们都用while循环死等这40ms的抖动,在这40ms内单片机不会运行其他程序,这样很浪费单片机性能,在那些实时性要求不高的项目用while消抖不会影响程序正常运行,但在那些要求实时性很高的项目用while消抖显然不行,所以出现了中断扫描消抖。
2026-03-22 20:38:18
298
原创 LVGL学习笔记
官方链接可以通过标志来实现自定义组件,标志可以让组件赋予一些特性(将二级屏幕赋予可点击的特性,再将屏幕大小改变一下就成为按钮了)//读取回调函数的传参//自定义按钮//删除边框,border:边框 width:宽度//设置背景颜色//设置位置,大小//设置阴影//设置阴影颜色, shadow:阴影 GREY:灰色//设置阴影宽度//设置阴影位置//添加标记,可点击//添加回调函数,被点击触发//删除mylist对象//设置mylist对象大小。
2026-03-21 17:12:19
516
原创 智能家居安防系统
基于stm32f103c8t6,开发的智能家居安防系统火焰检测功能烟雾浓度检测功能温湿度显示人体红外检测报警功能当出现火焰或空气烟雾浓度超标时,触发报警功能,并开启风扇,将有无火焰状态,空气中烟雾浓度,环境温度湿度,有无人体活动,都显示在OLED屏上。
2026-03-14 16:41:20
563
原创 回顾C语言
数组是一种数据结构,是一种存放数据的方式,把一种类型的数据放在一起,在创建数组时,数组元素个数不能为0。数组创建:类型 arr 【常量】二维数组:arr【】【7】
2025-11-24 13:20:27
303
原创 OLED驱动
我们发送数据时可以指定发送的是命令还是数据,芯片从1处获取数据,获取到的数据分,命令和数据,是命令传入2,是数据传入32是命令译码器,执行传输的命令3是内置显示存储器,决定显示在屏幕上的内容4是显示控制器,他的任务就是将3中的内容显示到屏幕上,也就是驱动器9是震荡器,用于提供屏幕时钟CL是时钟输入脚,CLS是时钟时钟源选择脚,CLS接高电平用内部时钟,CLS接低电平接外部时钟(CL),一般接内部时钟8电流控制和电压控制。
2025-11-23 21:56:16
1049
原创 回顾通信方式
串口是一对一的串行非实时全双工的一种通信协议,他也有两根线,Tx,Rx,两台设备之间通信,Tx是发送数据,Rx是接收数据一台设备的Tx连另一台设备的Rx,两台设备通信必须设置好相同的波特率,发送方以规定的速度发送数据,接收方也用相同的速度接收速度,串口是低位先行。
2025-11-22 22:00:18
938
原创 PID详解
PID是一种控制算法,通过调节PID系数使系统误差达到最小化,常用来调节PWM波控制,PID算法有三个系数Kp(比列系数),Ki(积分系数),Kd(微分系数),这三个系数是由我们自己通过实际显现调节的得来的。
2025-11-21 19:13:56
728
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅