当前位置: 首页 > news >正文

技术笔记2023076 rBoot学习7

技术笔记2023076 rBoot学习7

  继续之前的学习。

代码分析:函数find_image()

// prevent this function being placed inline with main
// to keep main's stack size as small as possible
// don't mark as static or it'll be optimised out when
// using the assembler stub

  首先看一下作者给这个函数写下的注释。为了避免此函数内嵌入main函数而占用main函数的栈空间,此函数被关键字NOINLINE修饰。为了避免此函数在使用汇编存根(stub)时被优化,不能使用static来修饰此函数。

	uint8_t  flag;uint32_t  loadAddr;uint32_t  flashsize;int32_t  romToBoot;uint8_t  updateConfig  =  0;uint8_t  buffer[SECTOR_SIZE];rboot_config  *romconf  = (rboot_config*)buffer;rom_header  *header  = (rom_header*)buffer;

  此函数先声明了一些变量,其中的变量buffer数组的大小是SECTOR_SIZE,也就是Flash一个扇区的大小,此处为4KB

#ifdef BOOT_BAUDRATE// soft reset doesn't reset PLL/divider, so leave as configuredif (get_reset_reason() != REASON_SOFT_RESTART) {uart_div_modify( 0, UART_CLK_FREQ / BOOT_BAUDRATE);}
#endif

  此处建议在Makefile中定义变量RBOOT_BAUDRATE,因为ESP-12F模组上电默认的74880波特率太过奇怪。此处我将变量RBOOT_BAUDRATE定义在了变量RBOOT_FW_BASE的后面,并赋值为115200。

#if  defined  BOOT_DELAY_MICROS  &&  BOOT_DELAY_MICROS  >  0// delay to slow boot (help see messages when debugging)ets_delay_us(BOOT_DELAY_MICROS);
#endifets_printf("\r\nrBoot v1.4.2 - richardaburton@gmail.com\r\n");

  此处建议去掉rboot.h//#define BOOT_DELAY_MICROS 2000000的注释去掉,并给宏定义设置一个合适的值,我将其设置为20000(单位为us)。这样做可以做到等待系统稳定再进行后面的操作。如果我们想设置rBoot阶段串口输出的波特率,就需要在这里等待系统串口稳定。

	// read rom headerSPIRead(0, header, sizeof(rom_header));// print and get flash sizeets_printf("Flash Size: ");flag  =  header->flags2  >>  4;if (flag  ==  0) {ets_printf("4 Mbit\r\n");flashsize  =  0x80000;} else  if (flag  ==  1) {ets_printf("2 Mbit\r\n");flashsize  =  0x40000;} else  if (flag  ==  2) {ets_printf("8 Mbit\r\n");flashsize  =  0x100000;} else  if (flag  ==  3  ||  flag  ==  5) {ets_printf("16 Mbit\r\n");
#ifdef BOOT_BIG_FLASHflashsize =  0x200000;
#elseflashsize  =  0x100000; // limit to 8Mbit
#endif} else  if (flag  ==  4  ||  flag  ==  6) {ets_printf("32 Mbit\r\n");
#ifdef BOOT_BIG_FLASHflashsize =  0x400000;
#elseflashsize  =  0x100000; // limit to 8Mbit
#endif} else  if (flag  ==  8) {ets_printf("64 Mbit\r\n");
#ifdef BOOT_BIG_FLASHflashsize =  0x800000;
#elseflashsize  =  0x100000; // limit to 8Mbit
#endif} else  if (flag  ==  9) {ets_printf("128 Mbit\r\n");
#ifdef BOOT_BIG_FLASHflashsize =  0x1000000;
#elseflashsize  =  0x100000; // limit to 8Mbit
#endif} else {ets_printf("unknown\r\n");// assume at least 4mbitflashsize  =  0x80000;}// print spi modeets_printf("Flash Mode: ");if (header->flags1  ==  0) {ets_printf("QIO\r\n");} else  if (header->flags1  ==  1) {ets_printf("QOUT\r\n");} else  if (header->flags1  ==  2) {ets_printf("DIO\r\n");} else  if (header->flags1  ==  3) {ets_printf("DOUT\r\n");} else {ets_printf("unknown\r\n");}// print spi speedets_printf("Flash Speed: ");flag  =  header->flags2  &  0x0f;if (flag  ==  0) ets_printf("40 MHz\r\n");else  if (flag  ==  1) ets_printf("26.7 MHz\r\n");else  if (flag  ==  2) ets_printf("20 MHz\r\n");else  if (flag  ==  0x0f) ets_printf("80 MHz\r\n");else  ets_printf("unknown\r\n");

  这里使用SPI接口将Flash上的头部信息读出并保存在变量header中。其中包含有Flash大小、模式和速度信息。这些信息我们都可以在Makefile中设置,设置好以后会被编译进bin文件中。对于ESP-12F来说,应该这样设置:SPI_SIZE ?= 4MSPIMODE ?= DOUTSPI_SPEED ?= 40

	// read boot configSPIRead(BOOT_CONFIG_SECTOR  *  SECTOR_SIZE, buffer, SECTOR_SIZE);// fresh install or old version?if (romconf->magic  !=  BOOT_CONFIG_MAGIC  ||  romconf->version  !=  BOOT_CONFIG_VERSION) {// create a default config for a standard 2 rom setupets_printf("Writing default boot config.\r\n");ets_memset(romconf, 0x00, sizeof(rboot_config));romconf->magic  =  BOOT_CONFIG_MAGIC;romconf->version  =  BOOT_CONFIG_VERSION;default_config(romconf, flashsize);// write new config sectorSPIEraseSector(BOOT_CONFIG_SECTOR);SPIWrite(BOOT_CONFIG_SECTOR  *  SECTOR_SIZE, buffer, SECTOR_SIZE);}// try rom selected in the config, unless overriden by gpio/temp bootromToBoot  =  romconf->current_rom;

  这里先将Flash第一个扇区的数据读入缓冲区buffer中,而前面romconf指向了buffer。那么此时romconf就读出了rBoot的配置结构体信息。接着,我们比较magicversion是否与之前的宏定义相同。按照rBoot项目的readme中说的,我们在进行boot版本迭代时,可以通过修改version来区分新旧版本。如果此处if为真,则说明Flash中存在新的boot,此时要将Flash的第一个扇区覆盖。之后获取当前需要加载的rom的索引。

	// check valid rom number// gpio/temp boots will have already validated thisif (romconf->current_rom  >=  romconf->count) {// if invalid rom selected try rom 0ets_printf("Invalid rom selected, defaulting to 0.\r\n");romToBoot  =  0;romconf->current_rom  =  0;updateConfig  =  1;}// check rom is validloadAddr  =  check_image(romconf->roms[romToBoot]);// check we have a good romwhile (loadAddr  ==  0) {ets_printf("Rom %d at %x is bad.\r\n", romToBoot, romconf->roms[romToBoot]);// for normal mode try each previous rom// until we find a good one or run outupdateConfig  =  1;romToBoot--;if (romToBoot  <  0) romToBoot  =  romconf->count  -  1;if (romToBoot  ==  romconf->current_rom) {// tried them all and all are bad!ets_printf("No good rom available.\r\n");return  0;}loadAddr  =  check_image(romconf->roms[romToBoot]);
}

  之前拿到了当前需要加载的rom的索引。如果之前拿到的索引是否超出rom总数,则认为此索引是无效的,并默认加载第一个rom。之后再去获取rom的地址。如果获取rom地址失败,则说明此rom已经被损坏了。如果所有rom都已经损坏了,那么只能直接返回0,代表没有找到image了。

	// re-write config, if requiredif (updateConfig) {romconf->current_rom  =  romToBoot;SPIEraseSector(BOOT_CONFIG_SECTOR);SPIWrite(BOOT_CONFIG_SECTOR  *  SECTOR_SIZE, buffer, SECTOR_SIZE);}ets_printf("Booting rom %d at %x, load addr %x.\r\n", romToBoot, romconf->roms[romToBoot], loadAddr);// copy the loader to top of iramets_memcpy((void*)_text_addr, _text_data, _text_len);// return address to load fromreturn  loadAddr;

  获取到rom的地址后,将当前的rom的索引写入配置结构体中。随后将buffer写入boot所在的扇区,覆盖了之前的配置,在下次启动时将使用当前的配置。然后,将_text_data位置,长度为_text_len的数据加载到_text_addr中。最后返回rom的地址,find_image()函数就结束了。

const  uint32_t  entry_addr  =  0x4010fcb4;const  uint32_t  _text_addr  =  0x4010fc00;
const  uint32_t  _text_len  =  192;
const  uint8_t  _text_data[]  = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x00, 0x1c, 0x4b, 0x00, 0x40, 0x12, 0xc1, 0xc0, 0xc9, 0xe1, 0x8b, 0x31, 0xcd,
0x02, 0x0c, 0x84, 0xe9, 0xc1, 0xf9, 0xb1, 0x09, 0xf1, 0xd9, 0xd1, 0xc2, 0xcc, 0x08, 0x01, 0xf9,
0xff, 0xc0, 0x00, 0x00, 0xf8, 0x31, 0xe2, 0x01, 0x09, 0x86, 0x10, 0x00, 0x2d, 0x0c, 0x3d, 0x01,
0x0c, 0x84, 0x01, 0xf4, 0xff, 0xc0, 0x00, 0x00, 0x8b, 0xcc, 0x78, 0x01, 0xd8, 0x11, 0x46, 0x09,
0x00, 0x21, 0xef, 0xff, 0x5d, 0x0d, 0xd7, 0xb2, 0x02, 0x20, 0x52, 0x20, 0x2d, 0x0c, 0x3d, 0x07,
0x4d, 0x05, 0x59, 0x51, 0x79, 0x41, 0x01, 0xeb, 0xff, 0xc0, 0x00, 0x00, 0x58, 0x51, 0x78, 0x41,
0x5a, 0xcc, 0x5a, 0x77, 0x50, 0xdd, 0xc0, 0x56, 0x6d, 0xfd, 0x0b, 0x6e, 0x60, 0xe0, 0x74, 0x56,
0x9e, 0xfb, 0x08, 0xf1, 0x2d, 0x0f, 0xc8, 0xe1, 0xd8, 0xd1, 0xe8, 0xc1, 0xf8, 0xb1, 0x12, 0xc1,
0x40, 0x0d, 0xf0, 0x00, 0xfd, 0x00, 0x05, 0xf8, 0xff, 0x0d, 0x0f, 0xa0, 0x02, 0x00, 0x0d, 0xf0,
};

  _text_data_text_len_text_addr等变量都可以在build目录下的rboot-hex2a.h中找到。令人意外的是,此文件竟然处于build目录下,这就说明它不是写出来的,而是通过某种方式生成的。

$(RBOOT_BUILD_BASE)/rboot-hex2a.h: $(RBOOT_BUILD_BASE)/rboot-stage2a.elf@echo "E2 $@"$(Q)  $(ESPTOOL2) -quiet -header $<  $@ .text

  通过分析Makefile,我们可以看到,rboot-hex2a.h依赖于rboot-stage2a.elf,并通过esptool2生成的。关于生成rboot-hex2a.h的具体过程,可以参考这篇文章:ESP8266 Bootloader开源代码解析之rboot(一)。

http://www.lryc.cn/news/97525.html

相关文章:

  • 收藏这6个抠图工具,一键抠图不用愁!
  • 四,Eureka 第四章
  • k8s常见的资源对象使用
  • JavaScript 简单实现观察者模式和发布订阅模式
  • 高通WLAN框架学习(37)-- TDLS(Tunneled Direct Link Setup)通道直接链路建立
  • 高算力AI模组前沿应用:基于ARM架构的SoC阵列式服务器
  • 老年公寓人员定位管理系统:提升安全与关怀的智能解决方案
  • 每日一题之两个字符串的删除操作
  • nacos安装与基础配置
  • GitHub Copilot:让开发编程变得像说话一样简单
  • 并发编程中锁的优化
  • 笔试题:统计字符串中某字符串在其出现的字符个数
  • Java NIO Files类读取文件流方式详解
  • Mybatis快速入门,Mybatis的核心配置文件
  • go语言中defer执行顺序
  • webpack xxx is not a constructor
  • 安装支持vs2019的MFC(解决MSBuild 错误 MSB8041、MSB8042)
  • 校园电气安全风险分析及预防措施 安科瑞 许敏
  • 机器学习之十大经典算法
  • 系统架构设计师 11:未来信息综合技术
  • Docker 数据管理[文件互访] 端口映射[暴露端口提供服务] 容器互联[指定容器名防止IP变动]
  • 【stable diffusion】保姆级入门课程04-Stable diffusion(SD)图生图-局部重绘的用法
  • 制作Java8环境Docker镜像
  • 抖音SEO源码开发指南:介绍如何开发抖音SEO源码的基本步骤和要点。
  • 【SDOF振荡器的非线性-非弹性多轴时间响应分析】用于SDOF振荡器非线性非弹性时程分析的鲁棒性分析研究(Matlab代码实现)
  • VMPWN的入门系列-1
  • 将标签中某一个类别添加到另一个标签中
  • 将指定图片控件中的图片闪烁
  • 【MySQL】表的增删查改
  • Python 爬虫的学习 day01 ,爬虫的基本知识, requests 和 charde模块, get函数的使用