ESP8266_08基于flash的数据掉电保护

本文介绍ESP8266模块如何利用flash存储器实现数据的掉电保护功能,通过具体实例展示了如何选择、擦除、写入及读取指定扇区,实现类似EEPROM的效果。

ESP8266_01搭建开发环境

ESP8266_02程序的编译与下载

ESP8266_03SDK与Makefile的基本用法

ESP8266_04管脚控制与软件定时器

ESP8266_05 ESP8266有几个串口?

ESP8266_06硬件定时器与IO中断

ESP8266_07基于PWM的呼吸灯

ESP8266_08基于flash的数据掉电保护

ESP8266_09基于IIC控制的OLED屏幕

ESP8266_10 ESP8266的STATION模式

ESP8266_11 ESP8266的UDP广播

ESP8266_12 ESP8266客户端模式下的TCP通信

ESP8266_13服务器端模式下的TCP通信

ESP8266_14 SOFTAP模式下的服务器端和客户端

ESP8266_15天气预报之TCP的GET操作

ESP8266_16天气预报之JSON数据的生成与解析

ESP8266_17简单网络时间协议-SNTP

ESP8266_18 MQTT协议接入ONENET平台

ESP8266_19MQTT协议接入ONENET平台_订阅主题

ESP8266_20 基于ONENET平台的数据上传之TCP的POST操作

ESP8266_21基于ESP8266的一键配网

ESP8266_22基于自身ADC的电压采样

ESP8266_23基于硬件定时器的红外遥控器解码

         这一节主要研究一下flash的用法,目的嘛,实现数据的掉电保护。

         听起来像EEPROM?

         确实很像,但不是!以STM32为例,片上是没有EEPROM的。但是,可以把一部分ROM当做EEPROM,通过程序进行擦写,最终实现的效果和EEPROM几乎是一样的。

         那,怎么选这段ROM?一般是选flash的最后一个页(它是以页为单位的)。因为烧程序的时候,程序是从前往后开始烧录的,只要你的程序没有大到占用了最后一个页,那就能用。

         ESP8266在这方面其实跟32很像,区别主要有两点:

         1、ESP8266是以扇区为单位,一个扇区4KB。

         2、ESP8266的最后4个扇区不能动,所以咱们要用的话,可以选倒数第5个扇区。

         看下面的图(下图及相关说明转自乐鑫的相关手册):

         上面两个图分别是不支持在线升级和支持在线升级的固件,在flash中的布局情况。

         系统程序:用于存放系统运行必要的固件。

         用户数据:当系统数据未占满整个Flash空间时,空闲区域均可用于存放用户数据。

用户参数:地址由用户自定义,IO T _Demo 中设置为0X3C000开始的4个扇区,用户可以设置为任意未占用的地址。

系统参数:固定为 Hash 的最后4个扇区。

BOOT信息:位于 FOTA 固件的分区1,存放FOTA升级预留信息。

预留:位于 FOTA 固件的分区2,与分区1 BOOT信息区对应的预留区域。

注:FLASH中每扇区为4KB

       注:上述信息参考手册2a-esp8266-sdk_getting_started_guide_cn。

       根据上面给出的截图和信息,可以知道,如果要实现类似EEPROM的效果,需要把数据存到“用户数据”这一部分。用户数据在flash中有两部分,只要是没有被占用的,都可以。为了简单计算,这里建议大家使用倒数第五个扇区。

       如果后期你对flash的布局了解的足够多了,可以使用任意可用的扇区,但是在初期,建议你还是先这么用。

       接下来说用法,结合串口做演示(终于不用LED了)。最终实现的效果,上电的时候先把一组数据写入flash,然后循环读取这部分数据,并通过串口打印出来。开始之前先包含以下头文件:

       #include "spi_flash.h"

       相关的读写函数都在里面了。

 

       步骤简单的令人发指,读写都算进去,只需要4步:

       1、选择你要写入的扇区

       因为每个人手里的模块flash大小都有可能不一样,所以要先根据你的flash大小,计算一下你的倒数第五个扇区的编号是多少。

       以我的为例,我的模块是16MBit的,也就是2MB,换算成KB是2048KB,再换算成扇区的个数是2048/4,得到512.

       512个扇区,它们的编号从0开始,也就是0~511,那么倒数第五个的编号就是507.

       所以我这里的代码写成这样:

       #define MY_ESP8266_FLASH  2048

       #define USER_DATA_SEC         (MY_ESP8266_FLASH / 4 - 1 - 4)

       uint32 hello[5] = {1,2,3,4,5};

       uint32 read[5];

       你们只需要把MY_ESP8266_FLASH后面的数值改成你的就行了。后面的两个数组一个用来存放被写入的数据,另一个用来存放读出的数据。

       为什么是uint32类型?后面说!

 

       2、擦除该扇区

       无论你要写哪个扇区,一律先擦后写!

       spi_flash_erase_sector(USER_DATA_SEC);

       好简单,不解释~

 

       3、写入数据

       spi_flash_write(USER_DATA_SEC * 4 * 1024, hello, 5 * 4);

       该函数共有三个参数:

       第一个参数:写入flash的目的地址。虽然前面换算了半天的扇区,这里还是要换算回去。

       第二个参数:被写入数据的指针。

       第三个参数:数据长度,也就是被写入数据的大小。因为uint32占用4个字节,所以用数组元素个数乘以4.

       回到刚才的问题,为什么一定要uint32类型?

       答:规定!flash读写必须4字节对齐,所以定义的时候尽量是uint32类型。

 

       4、读出数据

       spi_flash_read(USER_DATA_SEC * 4 * 1024, read, 5 * 4);

       读取的地址、存放的位置、读取的长度,好简单~~~

 

       注:参考手册2c-esp8266_non_os_sdk_api_reference_cn的45页,和99a-esp8266_flash_rw_operation_cn_v1.0。

 

       程序里使用了一个软件定时器,每隔3秒通过串口打印输出一下读取到的数据,波特率115200.

       保存、清理、编译、下载一条龙,然后重新上电,效果如下所示:

         到此,flash的用法说完了。

         链接:https://pan.baidu.com/s/1UTi45EmPB-6iguJrLUoiFw

        提取码:btfy

<think>好的,我现在要帮助用户解决如何在STM32标准库环境下编写ESP8266_SendString函数的问题。用户之前已经得到过一个代码示例,现在需要更详细的指导。 首先,我需要回顾之前的对话,确保不重复信息,同时补充更多细节。之前的回答包括了硬件连接、初始化代码、发送字符串函数和示例指令。用户现在可能希望了解函数的实现细节、可能的优化以及错误处理。 接下来,用户的需求是编写ESP8266_SendString函数,特别是在STM32标准库环境下。我需要考虑以下几个方面: 1. **函数的基本结构**:确保逐字符发送,并检查发送完成标志。 2. **错误处理**:是否需要添加超时机制,防止无限等待。 3. **代码优化**:是否可以使用DMA或中断来提高效率。 4. **代码示例的完整性**:是否包含初始化部分和必要的注释,方便用户理解。 5. **实际应用中的注意事项**:比如波特率设置、指令格式、延时处理等。 用户可能遇到的一些问题包括发送不成功、数据丢失或未收到响应。因此,在解释代码时,需要指出可能的问题点,例如检查USART_FLAG_TXE还是USART_FLAG_TC,这两者的区别是什么,以及为什么选择其中一个。 另外,用户可能在之前的代码中使用了Delay_ms函数,但需要提醒他们在实际项目中应避免使用阻塞延时,而改用非阻塞方法或状态机来处理。不过,对于简单应用,阻塞延时可能足够。 还需要考虑代码的可移植性和可维护性,例如将发送函数封装成模块,方便在其他项目中重用。同时,提及如何扩展功能,比如添加接收处理或校验机制。 最后,确保回答结构清晰,分步骤讲解,并附上相关问题,帮助用户深入理解。</think>以下是基于STM32标准库实现$ESP8266\_SendString$函数的完整代码示例和关键实现细节: ### 一、核心代码实现 ```c // 阻塞式发送单字节 void USART2_SendByte(uint8_t data) { USART_SendData(USART2, data); while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); // 等待发送完成[^1] } // 增强型字符串发送函数 void ESP8266_SendString(const char *cmd) { uint32_t timeout = 1000000; // 超时计数器 while(*cmd != '\0') { USART2_SendByte(*cmd); cmd++; // 简单超时检测 if(timeout-- == 0) break; } USART2_SendByte('\r'); // 补发回车 USART2_SendByte('\n'); // 补发换行 } ``` ### 二、实现原理详解 1. **字符传输机制**: - 采用**轮询等待**方式确保每个字符完整发送 - 检查$USART\_FLAG\_TXE$标志位(发送数据寄存器空)[^2] - 数学表达式:$T_{char} = \frac{10}{Baudrate} \times 8$(单个字符传输时间) 2. **时序控制**: ```c // 典型115200波特率下的时序 | 起始位 | D0 | D1 | D2 | D3 | D4 | D5 | D6 | D7 | 停止位 | ``` 每个字节传输需要$10 \times \frac{1}{115200} \approx 86.8\mu s$ 3. **错误处理增强版**: ```c void ESP8266_SendString_Advanced(const char *cmd) { uint32_t start_tick = SysTick->VAL; while(*cmd) { USART_SendData(USART2, *cmd++); // 带超时机制的等待 uint32_t timeout = 100000; // 根据时钟频率调整 while(timeout-- && (USART2->SR & USART_FLAG_TXE) == 0); if(timeout == 0) { // 触发错误回调 USART_ClearFlag(USART2, USART_FLAG_TC); break; } } // 发送终止符 USART_SendData(USART2, '\r'); USART_SendData(USART2, '\n'); } ``` ### 三、最佳实践建议 1. **波特率选择**: - 推荐使用$115200$或$460800$等高速波特率 - 计算公式:$Baud = \frac{f_{CK}}{16 \times USARTDIV}$[^3] 2. **内存优化**: - 使用$const$修饰符节省RAM空间: ```c void Send_AT(const char *at_cmd) { ESP8266_SendString(at_cmd); // 字符常量存储在Flash } ``` 3. **DMA优化方案**: ```c // DMA发送配置(以DMA1 Channel7为例) void USART2_DMA_Init(void) { DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); DMA_DeInit(DMA1_Channel7); DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = 0; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel7, &DMA_InitStructure); USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE); } ``` ### 四、调试技巧 1. 用示波器验证时序: - 测量$PA2$引脚波形 - 验证$Start\ Bit(0) + 8\ Data\ Bits + Stop\ Bit(1)$结构 2. 逻辑分析仪解码: - 配置异步串行协议解码器 - 检查$\r\n$结束符是否正确附加
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值