QQ2355892364的个人空间 https://www.eechina.com/space-uid-157723.html [收藏] [复制] [分享] [RSS]

博客

ESP8266模块开发资料与开发问题解决

已有 802 次阅读2019-6-14 17:41

一、存储芯片W25Q系列
esp8266-12f
w25q 系列生产的加工的商家很多,但是里面的分布和命名规则都是一样的。比如华邦的w25q64,spi通讯接口,64就是指 64Mbit 也就是 8M 的容量。而我们平时的8266-12f的 32Mbit 就是 4M 容量。
以 w25q32 为例,里面的存储分布。w25q32把4M容量分为了 64 块,每一块又分为 16 个扇区,而每个扇区占 4K 大小。由此可计算到,w25q32有 32Mbit / 8 * 1024 / 16 / 4 = 64 块 ,有 64 * 16 = 1024 个扇区。
注:1B=8 Bit ,1KB=1024B ,1MB=1024KB
二、ESP8266内存分布
• 程序区:代码编译⽣成的 bin ⽂件,烧录到 Flash 占⽤的区域,请勿改写;
• 系统参数区: esp_iot_sdk 中底层⽤于存放系统参数的区域,请勿改写;
• 用户参数区:上层应⽤程序存储⽤户参数的区域。开发者请根据实际使⽤的 Flash size 设置,可 以参考⽂档“2A-ESP8266__IOT_SDK_User_Manual” 中的 “Flash Map” ⼀章。
2.1 Non-FOTA
2.2 FOTA

三、Flash读写接口
SPI Flash 接口位于 /ESP8266_NONOS_SDK/include/spi_flash.h。
system_param_xxx 接⼝位于 /ESP8266_NONOS_SDK/include/user_interface.h。
3.1 spi_flash_erase_sector
3.2 spi_flash_write
注意: 
    • Flash 请先擦再写。 
    • Flash 读写必须 4 字节对齐。
示例代码: 
    #define N 0x7C
    uint32 data[M];
    spi_flash_erase_sector (N);
    spi_flash_write (N*4*1024, data, M*4);
3.3 spi_flash_read
3.4 system_param_save_with_protect
3.5 system_param_load

示例代码:
/***************Flash 读写结构体声明***************/
struct esp_platform_saved_param {
// 服务器参数
uint8 devkey[40];
uint8 token[40];
uint8 activeflag;
char server_domain[64];
ip_addr_t server_ip;
int server_port;
 // AP参数
uint8 ssid[32];
uint8 password[64];
int authmode;
uint8 ssid_hidden;
// 填充
uint8 pad[2];
};
/***************Flash 读写结构体定义***************/
struct esp_platform_saved_param esp_param;
/***************Flash 写入数据***************/
system_param_save_with_protect(0x7D, &esp_param, sizeof(esp_param));
/***************Flash 读出数据***************/
system_param_load(0x7D, 0, &esp_param, sizeof(esp_param)); 
四、Flash读写保护
4.1 Espressif Flash读写保护示例
4.1.1 实现原理
Espressif Flash 读写保护⽰例,使⽤三个 sector(扇区)实现(每 sector 4KB),提供 4KB 的可 靠存储空间。 将 sector 1 和 sector 2 作为数据 sector,轮流读写,始终分别存放“本次”数据和“前⼀次”数据, 确保了⾄少有⼀份数据存储安全; sector 3 作为 flag sector,标志最新的数据存储 sector。
保护机制如下:
1. 初始上电时,数据存储在 sector 2 中,从 sector 2 中将数据读到 RAM。
2. 第⼀次写数据时,将数据写⼊ sector 1。 此时若突然掉电, sector 1写⼊失败, sector 2 & 3数据未改变;重新上电时,仍是从 sector 2 中 读取数据,不影响使⽤。
我:
3. 改写 sector 3,将标志置为 0,表⽰数据存于 sector 1。 此时若突然掉电, sector 3 写⼊失败, sector 1 & 2 均存有⼀份完整数据;重新上电时,因 sector 3 ⽆有效 flag,默认从 sector 2 中读取数据,则仍能正常使⽤,只是未能包含掉电前对 sector 1 写⼊的数据。
4. 再⼀次写数据时,先从 sector 3 读取 flag,若 flag 为0,则上次数据存于 sector 1,此次应将数 据写⼊ sector 2;若 flag 为⾮ 0,则认为上次数据存于 sector 2,此次应将数据写⼊ sector 1。 此时若写数据出错,请参考步骤 2、 3的说明,同理。
5. 写⼊ sector 1(或 sector 2)完成后,才会写 sector 3,重置 flag。 注意: 只有数据扇区(sector 1或 sector 2)写完之后,才会写 flag sector(sector 3),这样即使 flag sector 写⼊出错,两个数据扇区都已存有完整数据内容,目前默认会读取 sector 2。
4.1.2 软件示例
在 IOT_Demo 中,使⽤ 0x3C000 开始的 4 个 sector(每 sector 4KB),作为⽤户参数存储区。 其中 0x3D000、 0x3E000、 0x3F000 这 3 个 sector 实现了读写保护的功能,并存储了应⽤级参数 esp_platform_saved_param。
图中“有读写保护的存储区”, IOT_Demo 中建议调⽤ system_param_load 和 system_param_save_with_protect 进⾏读写。
system_param_load - 读 Flash ⽤户参数区数据
system_param_save_with_protect - 写 Flash ⽤户参数区数据
参数 struct esp_platform_saved_param 定义了目前乐鑫存储于 Flash 的⽤户应⽤级数据,⽤户只需将⾃⼰要存储的数据添加到结构体 struct esp_platform_saved_param 后⾯,调⽤上述两个函 数进⾏ Flash 读写即可。
4.2 Flash读写保护参考⼀
⽅法: “轮流写⼊”+“⾸部记数”+“尾部校验” 
占⽤空间: 2 个 sector,共计 8KB;提供 4KB 的带数据保护存储空间。
原理:
仍然采⽤两个数据 sector 轮流写⼊来做备份数据保护,只是不再专门设⽴ flag sector。 记⼀个 counter,写⼊数据 sector 的⾸部,每次写⼊时计数加⼀,⽤记数⽐较来判别下⼀次应写⼊哪个 sector;在数据尾部加⼊校验码(CRC、 checksum 等任⼀种校验⽅式),⽤以验证数据的完整性。
(1) 假设初次上电,数据存储在 sector A, sector A 的记数为初始值 0xFF,从 sector A 将数据读⼊ RAM。
(2) 第⼀次数据写⼊ sector B,则在 sector B ⾸部信息中记录 counter 为 1,尾部加⼊校验码。
(3) 再次写⼊数据时,先分别读取 sector A/B 的 counter 值进⾏⽐较,此次应当将数据写⼊ sector A, sector A ⾸部记录 counter 为 2,尾部加⼊校验码。
(4) 若发⽣突然掉电,当前正在写⼊的 sector 数据丢失,重新上电时,先⽐较 sector A/B 的 counter 值,读取 counter 值较⼤的完整 sector,根据 sector 尾部的校验码进⾏校验,当前 sector 数据 是否可靠,若校验通过,则继续执⾏;若校验失败,则读取另⼀个 sector 的数据,校验,并执⾏。
4.2 Flash读写保护参考二
⽅法: “备份扇区”+“尾部校验” 
占⽤空间: 2 个 sector,共计 8KB;提供 4KB 的带数据保护存储空间。
原理:
始终往 sector A 读写数据,每次写⼊时,同样写⼀遍 sector B 作为 sector A 的备份扇区,每个 sector 尾部均加⼊校验码(CRC、 checksum等任⼀种校验⽅式)。
(1) 从 sector A 读取数据,并进⾏校验。
(2) 数据写⼊ sector A,尾部为校验码。
(3) sector A 写⼊完成后,同样的数据也写⼊ sector B 进⾏备份。
(4) 若发⽣突然掉电,当前正在写⼊的 sector 数据丢失,重新上电时,先从 sector A 读取数据,根 据尾部的校验码进⾏校验, sector A 数据是否可靠,若校验通过,则继续执⾏;若校验失败,则 读取 sector B 的数据,校验,并执⾏。
还有一些关于ESP8266在开发中遇到的一些问题的解决方案:
1. 为什么ESP8266 TCP透传过程会丢包?

因为没有设置硬件流控。如果需要避免丢包,请设置硬件流控。透传功能使用的是 TCP 协议,每包数据是 1460 (取决于协议栈),只要网络良好,buffer 空间没有被消耗完,就可以不停地传输数据。对于透传,串口接收数据间隔超过约 20 ms,就会认为数据接收结束,将已经接受的数据传输到网络。如果网络不好,就可能会丢弃一些数据,因此,为避免这种情况,可以将串口设置为流控模式。


2. ESP8266 有几个 UART?

ESP8266 有两个 UART,其中 UARTO 有 TX、RX,可做数据传输;UART1 由于 RX 脚被 SPI-Flash 占用,只能使用 TX,可以做串口调试信息打印。


3. GPIO 电平状态是怎样的?

除了 XPD_DCDC,GPIO 可以配置上拉。关于 GPIO 的上电 IO 口默认状态为:除了 SDIO 6根线 +GPIO4+GPIO5+GPIO16 上电 IO 默认无上拉,其他的 GPIO 口均有上拉。由于是内部配置上拉,所以如需下拉,需外部加下拉方式或者加一个三级管的反相电路。


注意:GPIO 不能到 5V。GPIO4/5 外接 1M 电阻不能上拉到高电平;需 100K 电阻。


4. 如何屏蔽上电打印?

U0TXD 默认上电有系列打印,对此敏感应用可通过 UART 的内部引脚交换功能,在初始化的时候,调用 system_uart_swap 函数,将 U0TXD、U0RXD 分别与 U0RTS (MTDO/GPIO15),U0CTS (MTCK/GPIO13) 交换来屏蔽该上电的系统打印。交换后,硬件上的下载管脚还是使用 U0TXD+U0RXD,通信时需要将 MTDO 对应接到 MCU 的 RXD,MTCK 对应接到 MCU 的 TXD。

5. 为什么 ESP8266 上电时会出现乱码?如何修改波特率?

如果使用的是 26 MHz 晶振,ESP8266 UARTO 上电后的波特率是 74880,所以上电时会有乱码。


客户可以在 user_main() 里面修改 UART 配置,比如:
void ICACHE_FLASH_ATTR
uart_init(UartBautRate uart0_br, UartBautRate uart1_br)
{
      // rom use 74880 baut_rate, here reinitialize 
      UartDev.baut_rate = uart0_br; 
      uart_config(UART0);
      UartDev.baut_rate = uart1_br; 
      uart_config(UART1);
}


6. TCP/UDP 的包长是多少?

单包数据,TCP 单包 1460 字节,UDP 单包 1472 字节。


7. ESP8266 的看门狗有什么作用?

为了提供系统稳定性,以应对多冲突的操作环境,ESP8266 集成了 2 级看门狗机制,包括软件看门狗和硬件看门狗。默认 2 个看门狗都是打开的。 


8. 看门狗的超时间隔是多少?触发超时事件会有什么现象?

硬件看门狗中断时间为 0.8*2048 ms ,即 1638.4 ms ,中断后处理时间为 0.8*8192 ms,即 6553.6 ms 。其中中断处理后时间为硬件看门狗中断发生后,需要进行喂狗操作的时间,如果超过该时间,即会触发硬件看门狗复位。因此,在仅有硬件看门狗的情况下,一个程序段如果运行时间超过 6553.6 ms ,即有可能触发硬件看门狗复位,若超过 8192 ms 则一定会触发复位。软件看门狗建立在 MAC timer 以及系统调度之上,中断时间为 1600 ms,中断后处理时间为 1600 ms。因此,在有软件+硬件看门狗的情况下,一个程序段如果运行时间超过 1600 ms,即有可能会触发软件看门狗复位,若超过 3200 ms 则一定会触发复位。


9. 如果我的应用不需要看门狗,如何关闭看门狗?

当时 SDK 仅支持关闭软件看门狗,支持同时喂软硬件看门狗。可以通过如下方式防止执行时间过长的用户程序导致看门狗复位:
如果一个程序段运行时间在触发软件看门狗和触发硬件看门狗复位之间,则可通过 system_soft_wdt_stop () 的方式关闭软件看门狗,在程序段执行完毕后用 system_soft_wdt_restart () 重新打开软件看门狗。
可以通过在程序段中添加 system_soft_wdt_feed () 来进行喂软硬件狗操作,防止软硬件看门狗复位。


10. 我要在程序里面引入 10 秒的延迟,怎么做最好?

看门狗不支持无限循环。如果客户使用循环做延迟或者进入一个事件太长时间,就会触发硬件看门狗重启。推荐使用 callback 和 timer 的 API 做延迟。


如果要轮询事件,推荐使用中断和 timer 的 API 来做。大多数事件都是关联到 callback 上的,所以大多数情况下,轮询都是可以避免的。


11. 对于 Non-OS SDK,memory leak 问题如何 debug?

可通过定于 MEMLEAK_DEBUG 宏启用 memory leak debug 功能,代码中调用 os_malloc, os_zalloc, os_calloc, os_realloc,os_free 可将调用的文件以及调用的对应行数记录在内存管理链表中,在有需要的地方通过调用 system_print_meminfo() 可打印出 heap 区内存分配情况。


步骤:
1. 修改用户工程目录的 MakeFile,在 CONFIGURATION_DEFINES 后加宏定义:-DMEMLEAK_DEBUG
    如:CONFIGURATION_DEFINES = -DMEMLEAK_DEBUG


2. 在用户代码,如 user_main.c 中,增加如下代码:
  #include "mem.h"
  bool ICACHE_FLASH_ATTR check_memleak_debug_enable (void)
  {
  return MEMLEAK_DEBUG_ENABLE;
  }


3. 在有可能内存泄露的地方调用 system_print_meminfo() ,建议仅在关键代码位置加入此函数进行 debug。


12. 发生“fatal exception”问题如何处理?


可以在 (*.S) 文件中找出对应的地址,添加打印以便定位问题。
Fatal exception (28):
epc1=0x4025bfa6, epc2=0x00000000, epc3=0x00000000, 
excvaddr=0x0000000f, depc=0x00000000
  
比如使用的是 user1.bin,那么就在 user1.S 中找到 0x4025bfa6 地址,并查明对应的函数。(这个比较困难,很多时候,仅仅根据这个及时找到了函数也很难知道具体什么原因导致的,只能做到大概定位)如果使用的是 flash.bin 和 irom0text.bin,可以在 eagle.S 中查找出错的地址。


13. ESP8266 总共有几个 timer ?


ESP8266 有 2 个 timer。一个硬件的 timer,一个软件的 timer。API os_timer 是 DSR 处理,不能产生中断,但是可以产生任务。任务会按照普通等级排队。硬件 timer 能产生中断和任务,中断能触发任务,任务按照普通等级排队。


14. 使用 timer 中断是否有特定条件?


请参考 SDK 的 API 参考:《ESP8266 Non-OS SDK API 参考》和《ESP8266 RTOS SDK API 参考》
     一般情况,使用 Non-OS SDK 时,硬件中断回调里面不要有声明为 ICACHE_FLASH_ATTR 的功能。同时中断回调里不要占用 CPU 太长时间。(这个需要注意,因为我们的绝大部分时候需要用到定时器中断,如果加入的打印信息过多或者注意不要加入ICACHE_FLASH_ATTR 的功能的函数,不然很可能够你吃一壶)


15. 为什么 ESP8266_Non-OS_SDK 中有的函数前面添加了“ICACHE_FLASH_ATTR”宏?(这点很主要,多注意就可以让自己少掉几根头发,最近掉头发比较多,原因和这个有关)


对于 ESP8266_Non-OS_SDK:
添加了“ICACHE_FLASH_ATTR”宏的函数,将存放在 IROM 中,CPU 仅在调用到它们的时候,将它们读到 cache 中运行;没有添加到“ICACHE_FLASH_ATTR”宏的函数,将在一开始上电运行时,就加载到 IRAM 中运行;由于空间有限,我们无法将所有代码都一次性加载到 IRAM 中运行,因此在大部分函数前添加到“ICACHE_FLASH_ATTR”宏,放在 IROM 中。
   
请注意,不要再中断处理函数中调用带有“ICACHE_FLASH_ATTR”宏的函数,否则可能与 Flash 读写操作冲突。
   
对于 ESP8266_RTOS_SDK:
函数默认存放在 IROM 中,无须再添加“ICACHE_FLASH_ATTR”宏。中断处理函数也可以定义在 IROM 中。如果开发者需要将一些频繁调用的函数定义在 IRAM 中,在函数前添加“IRAM_ATTR”宏即可。


16. 为什么编译 Non-OS SDK 时会发生 IRAM_ATTR 错误?


如果需要在 IRAM 中执行功能,就不需要加“ICACHE_FLASH_ATTR”的宏,那么该功能就是放在 IRAM 中执行。


17. 为什么编译的时候会发生“irom0_0_seg”错误?


它表示代码量太大,IROM 区域存放不下了。
我们可以在 SDK_v0.9.5 (及之后)的软件版本中,尝试如下步骤,解决这个问题:


1. 使用默认设置,编译生成 eagle.flash.bin 和 eagle.irom0text.bin。
(1) 如果 size of eagle.flash.bin + size of eagle.irom0text.bin >= 236KBytes:
很抱歉,您的代码量太大了,只能换大些的 Flash。
(2) 如果 size of eagle.flash.bin + size of eagle.irom0text.bin < 236KBytes:
请继续步骤 2。
   
2. 在路径 SDK/ld 下修改文件“eagle.app.v6.new.512.app1.ld"。
irom0_0_seg:           org = 0x40201010, len = 0x2B000
根据步骤 1 中编译的“eagle.irom0text.bin”大小,改写上述 len 的值。
示例:如果“eagle.irom0text.bin”大小为 179 KB,则可修改配置如下:
irom0_0_seg:           org = 0x40201010, len = 0x2D000
   
3. 重新编译 user1.bin 选择 boot_v.1.2+。


补充说明:
代码中,
函数前未加  ICACHE_FLASH_ATTR 的,编译到 IRAM 中,最大 32 KB;
函数前加了 ICACHE_FLASH_ATTR 的,编译到 IROM 中;


因为 RAM 的空间有限,因此做了这两个部分的区分:
IRAM 中的代码,会在上电初始就完整加载到 RAM 中;
IROM 中的代码是用到的时候才从 Flash 加载到 cache 中执行。


18. ESP8266 有 main 吗?


ESP8266 没有 main,程序入口为 user_init。

19. 操作指针有什么需要注意的?


内存必须 4 字节对齐读取,指针做转换时请确保为 4 字节对齐,否则转换失败,不能正常使用。例如,请勿直接指针转换 float temp = *((float*)data);而是使用 os_memcpy (memcpy) 实现。


20. RTOS SDK 和 Non-OS SDK 有何区别?


主要差异点如下:


Non-OS SDK
Non-OS SDK 主要使用定时器和回调函数的方式实现各个功能事件的嵌套,达到特定条件下触发特定功能函数的目的。Non-OS SDK 使用espconn 接口实现网络操作,用户需要按照 espconn 接口的使用规则进行软件开发。


RTOS SDK
RTOS 版本 SDK  使用 freeRTOS 系统,引入 OS 多任务处理的机制,用户可以使用 freeRTOS 的标准接口实现资源管理、循环操作、任务内延时、任务间信息传递和同步等面向任务流程的设计方式。具体接口使用方法参考  freeRTOS 官方网站的使用说明或者 USING THE FREERTOS REAL TIME KERNEL -  A Practical Guide 这本书中的介绍。
RTOS 版本 SDK 的网络操作接口是标准 lwIP API,同时提供了 BSD Socket API 接口的封装实现,用户可以直接按照 socket API 的使用方式来开发软件应用,也可以直接编译运行其他平台的标准 Socket 应用,有效降低平台切换的学习成本。
RTOS 版本 SDK 引入了 cJSON 库,使用该库函数可以更加方便的实现对 JSON 数据包的解析。
RTOS 版本兼容 Non-OS SDK 中的 Wi-Fi 接口、SmartConfig 接口、Sniffer 相关接口、系统接口、定时器接口、FOTA 接口和外围驱动接口,不支持 AT 实现。

路过

鸡蛋

鲜花

握手

雷人

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 立即注册

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
返回顶部