有人可以解释Arduino bootloader的工作原理吗?我不是在寻找一个高级答案,我已经阅读了代码,我得到了它的要点。
在Arduino IDE和引导加载程序代码之间发生了许多协议交互,最终导致许多内联汇编指令自动编程闪存,程序通过串行接口传输。
我不清楚的是第270行:
void (*app_start)(void) = 0x0000;
...我认为是函数指针的声明和初始化为NULL。随后在引导加载程序要委托执行用户加载代码的位置调用app_start。
当然,某种程度上app_start
需要在某个时刻获得非NULL值,以便将所有这些组合在一起。我在引导加载程序代码中没有看到它......它是否被引导加载程序加载的程序神奇地链接了?我认为引导加载程序的主要部分是芯片复位后进入软件的入口点。
在70个左右的程序集中包含必须是秘密解码器环,告诉主程序app_start到底在哪里?或者也许是Arduino IDE利用了一些隐含的知识?我所知道的是,如果有人没有将app_start更改为指向0以外的其他位置,那么引导程序代码将永远自行旋转......那么诀窍是什么?
修改
我有兴趣尝试将引导加载程序移植到没有用于引导加载程序代码的单独内存空间的Tiny AVR。由于我很明显引导加载程序代码依赖于某些保险丝设置和芯片支持,我想我真正感兴趣的是将引导加载程序移植到没有这些保险丝和硬件的芯片需要做些什么。支持(但仍具有自编程能力)?
答案 0 :(得分:40)
地址0不是空指针make。 “空指针”是更抽象的东西:适用函数应该识别为无效的特殊值。 C表示特殊值为0,而语言表示取消引用它是“未定义的行为”,在微控制器的简单世界中,它通常具有非常明确的效果。
通常,复位时,AVR的程序计数器(PC)初始化为0,因此微控制器开始在地址0处执行代码。
但是,如果设置了引导复位保险丝(“BOOTRST”),程序计数器将被初始化为存储器上端块的地址(这取决于保险丝的设置方式,请参阅a datasheet(PDF,7 MB)具体细节)。从那里开始的代码可以做任何事情 - 如果你真的想要你可以把自己的程序放在那里,如果你使用ICSP(引导程序通常不能覆盖自己)。
通常,它是一个特殊的程序 - 引导程序 - 能够从外部源读取数据(通常通过UART,我 2 C,CAN等)重写程序代码(存储在内部或外部存储器中,具体取决于微程序)。引导加载程序通常会寻找一个“特殊事件”,它实际上可以是任何东西,但是对于开发来说,最方便的是数据总线上它将从中提取新代码。 (对于生产,它可能是一个特殊的逻辑电平,因为它几乎可以立即检查。)如果引导加载程序看到特殊事件,它可以进入bootloading-mode,它将重新刷新程序存储器,否则它通过控制关闭用户代码。
另外,引导加载程序保险丝和高端内存块的要点是允许使用带有 no 修改原始软件的引导加载程序(只要它不扩展所有的进入引导程序的地址)。可以使原始HEX,引导加载程序和修改过的保险丝闪烁,而不是仅使用原始HEX和所需的保险丝闪烁,并添加presto,bootloader。
无论如何,对于Arduino,我相信它使用STK500中的协议,它会尝试通过UART进行通信,如果它在规定的时间内没有响应:
uint32_t count = 0;
while(!(UCSRA & _BV(RXC))) { // loops until a byte received
count++;
if (count > MAX_TIME_COUNT) // 4 seconds or whatever
app_start();
}
或者如果通过获得意外响应而导致错误太多:
if (++error_count == MAX_ERROR_COUNT)
app_start();
它将控制权传递给位于0的主程序。在上面看到的Arduino源代码中,这是通过调用app_start();
来完成的,定义为void (*app_start)(void) = 0x0000;
。
因为它作为C函数调用,在PC跳到0之前,它会将当前PC值推送到堆栈,堆栈中还包含引导加载程序中使用的其他变量(例如count
和{{1 }} 从上面)。这会从您的程序中窃取RAM吗?好吧,在PC设置为0之后,执行的操作明显“违反”正确的C函数(最终将返回)应该执行的操作。在其他初始化步骤中,它重置堆栈指针(有效地消除调用堆栈和所有局部变量),回收RAM。全局/静态变量初始化为0,其地址可以与引导加载程序使用的任何内容自由重叠,因为引导加载程序和用户程序是独立编译的。
引导加载程序的唯一持久影响是对硬件(外设)寄存器的修改,良好的引导加载程序不会处于有害状态(打开可能在您尝试休眠时浪费电源的外设)。通常还可以完全初始化您将使用的外围设备,因此即使引导程序执行了一些奇怪的操作,您也可以根据需要进行设置。
在ATtinys上,正如你所提到的,引导程序保险丝或内存并不奢侈,所以你的代码总是从地址0开始。你可以将你的引导加载程序放到更高的内存页面并指向你的RESET向量在它,然后每当你收到一个新的hex文件闪存时,采取地址0:1的命令,用引导加载程序地址替换它,然后将替换的地址存储在其他地方,以调用正常执行。 (如果它是error_count
(“相对跳转”),显然需要重新计算该值)
答案 1 :(得分:1)
修改
我有兴趣尝试将引导加载程序移植到没有用于引导加载程序代码的单独内存空间的Tiny AVR。由于我很明显引导加载程序代码依赖于某些保险丝设置和芯片支持,我想我真正感兴趣的是将引导加载程序移植到没有这些保险丝和硬件的芯片需要做些什么。支持(但仍具有自编程能力)?
根据您的最终目标,可能更容易创建自己的引导加载程序而不是尝试移植一个。你真的只需要学习那部分的一些项目。
1)uart tx
2)uart rx
3)自动闪存编程
可以单独学习,然后组合到引导程序中。您将需要一个可以使用spi或其他任何东西编写闪存的部件,这样如果您的引导程序无法正常工作或者随附的部件变得混乱,您仍然可以继续开发。
无论您是自己移动还是自己移动,您仍然需要了解与该部分相关的三个基本内容。