我由于做软件业务的需要,在这几年开发经历中,发现一个现象:各家芯片厂商boot开放的资料较少,不支持或少量支持定制化功能。可能也是需求少吧,毕竟对基线的改动需要的工作量也不小。但这也导致各家芯片的boot开发体验都不是太顺畅,开发者要自己摸索boot的一些定制化实现方案。
这篇内容接着上一篇 => 【填坑】ESP32 bootloader初探(上),看看bootloader里我是怎么搞定外设使用的。
在上篇中,已经初步了解过bootloader的文件结构,一些需要注意的特点,以及究竟如何修改bootloader文件,让自己的功能得到实现。
到这里,来看下具体到boot功能开发中,外设开发有哪些要解决的问题。
我在boot里主要就用到了串口和定时器两种外设。在官方说明里,boot支持的外设操作并不多,不包括我这次要用的这两种。需要自己编写函数,再放入驱动接口在内,给自己调用。
HAL层 —— 目录\component\hal
uart_driver_install
和uart_param_config
来初始化串口;uart_wait_tx_done
或uart_tx_chars
来发送;timer_init
来初始化定时器等等。LL层 —— 目录\components\hal\esp32c3\include\hal
还是建议使用hal层的接口,这一层改完,未来适配不同型号的芯片,更加通用灵活,改动也不必太大。
我的做法是依照了driver的流程,把临界区保护和动态申请空间的接口都去除了,只保留对硬件驱动的流程,这已经对大部分driver接口都适用了。
esp_intr_alloc_intrstatus
。不需要像app那样动态申请空间给空闲的中断号,boot里直接固定一个中断号就行了,可以根据该接口调用的位置,看看传入参数是怎么样的,再到接口内部实现去看有哪些流程可以直接干掉的。
头、源文件找不到
串口和定时器开发中,都有遇到一些或大或小的问题,记录下来,看到的小伙伴也可以避坑了。
esp_intr_alloc_intrstatus
接口里面都能找到,只不过要自己删减不支持和不会运行到的步骤,也不算太复杂。boot流程完成后,因为我们自己开启了外设的各种配置包括中断号等等的,切记要调用关闭这两个外设的初始化和中断的接口。否则跑到app你再去使用这些外设可能会有异常,别造成更多开发问题。
终于啊,bootloader外设都单独调试好了,我们把更多的逻辑流程放入进去,跟外设功能结合起来。可算要完工了,编译一下… 出错了。boot编译出来太大,超过了默认分配给boot的32K大小。那没办法,只能再扩充到64K了。
make menuconfig
,如下图操作:在这次的boot开发中,我还涉及到存储数据到分区中,app再去用的功能。这就必须使用一个自定义的分区来放数据。接下来记录,新建一个自定义分区,在boot和app中怎么用。
按照上述方法创建好自定义分区后,怎么在boot和app程序中使用起来?
在app中
esp_partition_t *p_partition = NULL;
//初始化接口
bool my_flash_init(void)
{
p_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, (const char *)"my_partition");
if (p_partition == NULL || p_partition->size == 0)
{
my_DebugPrint("my partition error!");
return false;
}
p_partition->encrypted = 0;
my_DebugPrint("my partition: type=%d, subtype=0x%02x, address=0x%x, size=0x%x, label=\"%s\", encrypted=%d",
p_partition->type,
p_partition->subtype,
p_partition->address,
p_partition->size,
p_partition->label,
p_partition->encrypted);
return true;
}
//读接口
esp_err_t esp_partition_read(const esp_partition_t* partition,
size_t src_offset, void* dst, size_t size);
//写接口
esp_err_t esp_partition_write(const esp_partition_t* partition,
size_t dst_offset, const void* src, size_t size);
//擦除接口
esp_err_t esp_partition_erase_range(const esp_partition_t* partition,
size_t offset, size_t size);
在boot中
/*********** bootloader_start.c begin ************/
...
void __attribute__((noreturn)) call_start_cpu0(void)
{
...
bootloader_state_t bs = {0};
int boot_index = select_partition_number(&bs);
if (boot_index == INVALID_INDEX) {
bootloader_reset();
}
+++ my_bl_init_flash(bs);
...
}
...
/*********** bootloader_start.c end ************/
bootloader_state_t my_ps = {0};
//初始化接口
void my_bl_init_flash(bootloader_state_t ps)
{
ESP_LOGI(LOG_TAG, "my_bl_init_flash: app_offset = 0x%x, app_size = 0x%x\n", ps.factory.offset, ps.factory.size);
ESP_LOGI(LOG_TAG, "my_bl_init_flash: ota[0] offset = 0x%x, ota[0] size = 0x%x\n", ps.ota[0].offset, ps.ota[0].size);
memcpy(&my_ps, &ps, sizeof(bootloader_state_t));
}
//读接口
esp_err_t bootloader_flash_read(size_t src_addr, void *dest, size_t size, bool allow_decrypt);
//写接口
esp_err_t bootloader_flash_write(size_t dest_addr, void *src, size_t size, bool write_encrypted);
//擦除接口
esp_err_t bootloader_flash_erase_sector(size_t sector);
关于链接文件.ld的修改,主要决定了boot中能够使用的RAM资源有多少。
我自己在摸索RAM的大小分配上浪费了很多精力,建议先看官方文档 《ESP32-C3 技术参考手册》 的第三章“系统和存储器”章节 => 去看看,里面详细描述了各个存储区的地址范围。
记录到这,总算接近尾声了。多唠叨几句,开发中碰到问题,需要求助外部时,优先在ESP的论坛请教,官方的回复挺及时的,而且也可靠。
虽然如此,在提问前还是要多思考,多假设验证;求助时要会提有效的问题,而不是一股脑抛出心里想到的各种假设又不去做基本的验证,大家时间都很宝贵的。
最后,善用技术文档、各种手册资料,希望你在开发路上能披荆斩棘,不惧困难。共勉…
(*^▽^*)