看门狗的超时间隔是多少?触发超时事件会有什么现象?
硬件看门狗中断时间为 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 则一定会触发复位。
如果我的应用不需要看门狗,如何关闭看门狗?
当时 SDK 仅支持关闭软件看门狗,支持同时喂软硬件看门狗。可以通过如下方式防止执行时间过长的用户程序导致看门狗复位:
- 如果一个程序段运行时间在触发软件看门狗和触发硬件看门狗复位之间,则可通过 system_soft_wdt_stop () 的方式关闭软件看门狗,在程序段执行完毕后用 system_soft_wdt_restart () 重新打开软件看门狗。
- 可以通过在程序段中添加 system_soft_wdt_feed () 来进行喂软硬件狗操作,防止软硬件看门狗复位。
我要在程序里面引入 10 秒的延迟,怎么做最好?
看门狗不支持无限循环。如果客户使用循环做延迟或者进入一个事件太长时间,就会触发硬件看门狗重启。推荐使用 callback 和 timer 的 API 做延迟。
如果要轮询事件,推荐使用中断和 timer 的 API 来做。大多数事件都是关联到 callback 上的,所以大多数情况下,轮询都是可以避免的。
对于 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。
对于 RTOS SDK, memory leak 问题如何 debug ?
暂不支持该功能。可以参考 FreeRTOS 的 debug 方法。
发生“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 中查找出错的地址。
ESP8266 总共有几个 timer ?
ESP8266 有 2 个 timer。一个硬件的 timer,一个软件的 timer。API os_timer 是 DSR 处理,不能产生中断,但是可以产生任务。任务会按照普通等级排队。硬件 timer 能产生中断和任务,中断能触发任务,任务按照普通等级排队。
使用 timer 中断是否有特定条件?
请参考 SDK 的 API 参考:《ESP8266 Non-OS SDK API 参考》和《ESP8266 RTOS SDK API 参考》(链接为:espressif.com/zh-hans/support/download/documents)。
一般情况,使用 Non-OS SDK 时,硬件中断回调里面不要有**为 ICACHE_FLASH_ATTR 的功能。同时中断回调里不要占用 CPU 太长时间。
如何调整 Tx Power?
“system_phy_set_max_tpw” 用于设置 RF Tx Power 最大值,单位:0.25 dBm。目前 Flash download tool 中已开放给客户自行配置并生成 esp_init_data_default.bin。其中关于 Tx Power 的调整如下所示:
- LowPowerEn:同时设置每个模式下的 Tx Power。
- BackOffEn:同时设置每个模式下 Tx Power 需要减小的值。
- PowerLimiten:限制 Tx Power 的最大值。
- 设置确认之后点击 GenInitBin,并替换原先的 esp_init_data_default.bin。
为什么 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”宏即可。
为什么编译 Non-OS SDK 时会发生 IRAM_ATTR 错误?
如果需要在 IRAM 中执行功能,就不需要加“ICACHE_FLASH_ATTR”的宏,那么该功能就是放在 IRAM 中执行。
为什么编译的时候会发生“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 中执行。
ESP8266 有 main 吗?
ESP8266 没有 main,程序入口为 user_init。
操作指针有什么需要注意的?
内存必须 4 字节对齐读取,指针做转换时间请确保为 4 字节对齐,否则转换失败,不能正常使用。例如,请勿直接指针转换 float temp = *((float*)data);而是使用 os_memcpy (memcpy) 实现。
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 实现。
哪些接口需要在 user_init 中调用,否则容易出现问题,或者不生效?
- wifi_set_ip_info、wifi_set_macaddr 仅在 user_init 中调用生效,其他地方调用不生效。
- system_timer_reinit 建议在 user_init 中调用,否则调用后,需要重新 arme 所有 timer。
- wifi_station_set_config 如果在 user_init 中调用,底层会自动连接对应路由,不需要再调用 wifi_station_connect 来进行连接。否则,需要调用 wifi_station_connect 进行连接。
- wifi_station_set_auto_connect 设置上电启动时是否自动连接已记录的路由;例如,关闭自动连接功能,如果在 user_init 中调用,则当前这次上电就不会自动连接路由,如果在其他位置调用,则下次上电启动不会自动连接路由。
Light-sleep 如何通过 GPIO 或网络事件唤醒?
在 Light-sleep 模式下,CPU 在暂停状态下不会响应来自**硬件接口的信号与中断,因此需要配置通过外部 GPIO 信号将 ESP8266 唤醒,唤醒过程小于 3 ms。
wifi_station_disconnect();
wifi_set_opmode(NULL_MODE); // set WiFi mode to null mode
wifi_fpm_set_sleep_type(LIGHT_SLEEP_T);
wifi_fpm_open();
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTCK_U, FUNC_GPIO13);
gpio_pin_wakeup_enable(13, GPIO_PIN_INTR_LOLEVEL);//建议低电平唤醒
wifi_fpm_set_wakeup_cb(ssc_fpm_wakup_call);
wifi_fpm_do_sleep(FPM_SLEEP_MAX_TIME);
ESP8266 FRC1 的 hw_timer 如何使用?
1. 模型:
(1) FRC1 的参考时钟为 80M,分频系数可以配置为 1 分频,16 分频,256 分频,不同的分频影响每个 tick 的时长。
(2) FRC1 为递减型 timer,当 COUNT_VALUE 的值减到 0 后会触发中断。每一个 tick,COUNT_VALUE 的值都减 1。
(3) FRC1 可以配置为自动填装模式和非自动填装模式。
自动填装模式:当发生中断后,COUNT_VALUE 会自动取 FRC1_LOAD_VALUE 的值,放到本身,做自减操作。
非自动填装模式:当发生中断后,COUNT_VALUE 会切到最大值 0x7fffff 开始计。
(4) FRC1 的终端可以配置为 FRC1 中断源和 NMI 中断源。
NMI 中断被称为 CPU 不可屏蔽中断。NMI 中断在 ESP8266 对应 LEVEL3 的中断,其他中断对应 LEVEL1 的中断,NMI 中断可以打断任意比其优先级低的中断。
2. 关于 SDK HW_TIMER 的注意事项
SDK 中 hw_timer 的分频系数为 16,每个 tick 的时长为 0.2 μs。hw_timer_arm 函数配置的系数单位为 μs,最大值为 1677000 μs。
如何优化 ESP8266 应用的内存使用?
通过以下四种方法可以优化 ESP8266 应用的内存使用,减少应用的内存占用空间。
1. 将字符串放到 Flash 中:
(1) 有些字符串可以放在 Flash 中,特别是长字符串,例如 HTML 请求和响应模板。
比如,一个字符串原来是用 define 定义的:
#define test_string "hello world”
现在可以定义成如下:
static const char test_string[] ICACHE_RODATA_ATTR = "hello world";
(2) 当用 ICACHE_RODATA_ATTR 定义字符串常量时,需要对数据内容进行四字节对齐读取。由于 Flash 中的数据需要四字节对其读取,所以定义一个宏获取对齐后的字符串长度:
#define GET_ALIGN_STRING_LEN(str) ((strlen(str) + 3) & ~3)
使用字符串时,动态分配一个新的数组对象,读写 Flash 中的数据。然后用 os_memcpy API 来复制数据内容:
unsigned int str_len = GET_ALIGN_STRING_LEN(test_string);
char *tmp_string = (char *)os_malloc(str_len);
os_memcpy(tmp_string, test_string, str_len);
(3) 在用户的应用代码里使用 tmp_string 进行操作,而不使用 test_string。此方法除了减少应用的 RAM 占用空间,也能解决由于对 Flash 中的数据进行非对齐读取时,在应用中引起的 exception。
…….
(4) 当用户代码中,无需再使用通过以上方法读取的数据,需要释放之前分配的内存空间
os_free(tmp_string);
注意:如果不释放之前分配的内存空间,重复分配内存将会减少核心功能所需的内存,导致 API 出现功能异常或失败。
2. 把 const 数据放到 Flash:
(1) uint32 类型的数组可以直接放到 Flash,比如:
const uint32 array[4] ICACHE_RODATA_ATTR = {0x11111111, 0x22222222, 0x33333333, 0x44444444};
可以直接使用 array[0]。
(2) 对于 uint8 和 uint16 类型的数组,要注意读取数据的时候要四字节对齐,
比如:
const uint8 array[7] ICACHE_RODATA_ATTR = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
(3) 如果需要按字节读取 char 数组当中的元素,可从软件上进行处理,先按四字节读取,然后再按偏移取当中的一个字节。如果直接读取 array[0],会导致 crash。
(4) 对于数据结构,通常做法是分配比用户需要读取的结构更大的内存,从 Flash 四字节读取数据到内存。如同方法 1,在代码中依然使用对象指针。修改代码操作内存中的数据结构,而不是读取数组。
3. 将调试字符串放到 Flash 中:
现在默认的 printf 打印的字符串都还是放在 RAM 区,占用部分内存。如果用户无需频繁打印日志文件,或者调试字符串太长,可以使用优化的 os_printf 把打印的字符串放到 Flash 而不是 RAM 中。
4. 避免使用全局数组变量:
全局数组变量会在应用的整个生存期中占用不必要的内存。为减少全局数组变量的使用,乐鑫提供了动态内存分配 API。在基于事件的编程中,请使用 os_malloc 和 os_free 来动态分配所需的内存空间。但注意,我们不建议过于频繁地分配和释放大小不等的内存空间。
如何让 ESP8266 上电后快速连接 AP?
ESP8266 与某个 AP 连接后,会将该 AP 的信道信息存储在 RTC memory 中。
- 当软件复位 ESP8266,或 ESP8266 从 Deep-sleep 模式中唤醒之后,ESP8266 会从 RTC memory 中读取 AP 的信道信息,并尝试连接该信道中的 AP。
- 但如果上电启动或硬件复位 ESP8266,RTC memory 会被清空。因此,ESP8266 会扫描所有的信道,这会占用一些时间。
用户上电启动或硬件复位 ESP8266 时,可通过以下方式存储 AP 的信道信息,以避免 ESP8266 重新扫描所有信道寻找上次连接的 AP。这将有助于减少 ESP8266 启动后的连接时间。
- 在 ESP8266 与 AP 连接后,调用函数 wifi_get_channel 来读取当前 AP 的信道信息,然后将该信息存储到 SPI Flash 中。在写入 AP 的信道信息之前,请确保该信道信息有效。
- ESP8266 上电或硬件复位时,用户固件将从 Flash 中读取之前存储的信道信息。通过调用函数WRITE_PERI_REG(0x600011f4, 1 << 16 | channel) 将该 AP 的信道信息写入 RTC memory 中。之后便可以从 RTC memory 获取信道信息,加快 ESP8266 与 AP 的连接。
- 使能自动连接功能后,ESP8266 会从 RTC memory 中读取 AP 的信道信息,并尝试连接该信道中的 AP。
只有信道信息会被存储在 RTC memory 中。当调用函数 wifi_station_set_config 时,配置的其它信息(比如 SSID 和密码)已存储在 Flash 中。
注意:如果应用要求 ESP8266 频繁上电或硬件复位,建议客户使用片外 RTC memory 来备份信道信息。由于 Flash 内存的写入周期有限,不建议对 Flash 内存频繁地写入。