Arduino 引导程序 [英] Arduino Bootloader

查看:35
本文介绍了Arduino 引导程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

谁能解释一下 Arduino 引导加载程序 有效吗?我不是在这里寻找高级答案,我已经阅读了代码并了解了它的要点.

Can someone please explain how the Arduino bootloader works? I'm not looking for a high level answer here, I've read the code and I get the gist of it.

Arduino IDE 和引导加载程序代码之间发生了一系列协议交互,最终产生了许多内联汇编指令,这些指令通过串行接口传输的程序对闪存进行自编程.

There's a bunch of protocol interaction that happens between the Arduino IDE and the bootloader code, ultimately resulting in a number of inline assembly instructions that self-program the flash with the program being transmitted over the serial interface.

我不清楚的是第 270 行:

What I'm not clear on is on line 270:

void (*app_start)(void) = 0x0000; 

...我认为它是函数指针的声明和初始化为 NULL.在引导加载程序旨在委托执行用户加载的代码的地方有对 app_start 的后续调用.

...which I recognize as the declaration, and initialization to NULL, of a function pointer. There are subsequent calls to app_start in places where the bootloader is intended to delegate to execution of the user-loaded code.

当然,不知何故 app_start 需要在某个时候获得一个非 NULL 值,以便将所有这些组合在一起.我没有在引导加载程序代码中看到它......它是否被引导加载程序加载的程序神奇地链接了?我认为引导加载程序的 main 是芯片复位后进入软件的入口点.

Surely, somehow app_start needs to get a non-NULL value at some point for this to all come together. I'm not seeing that in the bootloader code... is it magically linked by the program that gets loaded by the bootloader? I presume that main of the bootloader is the entry point into software after a reset of the chip.

在大约 70 行汇编代码中包起来的一定是秘密解码器环,它告诉主程序 app_start 到底在哪里?或者可能是 Arduino IDE 利用了一些隐含的知识?我所知道的是,如果有人不将 app_start 更改为指向 0 以外的某个位置,则引导加载程序代码将永远自旋……所以有什么诀窍?

Wrapped up in the 70 or so lines of assembly must be the secret decoder ring that tells the main program where app_start really is? Or perhaps it's some implicit knowlege being taken advantage of by the Arduino IDE? All I know is that if someone doesn't change app_start to point somewhere other than 0, the bootloader code would just spin on itself forever... so what's the trick?

编辑

我有兴趣尝试将引导加载程序移植到没有用于引导加载程序代码的单独内存空间的 Tiny AVR.很明显,引导加载程序代码依赖于某些保险丝设置和芯片支持,我想我真正感兴趣的是将引导加载程序移植到没有这些保险丝和硬件的芯片上需要什么支持(但仍具有自编程能力)?

I'm interested in trying to port the bootloader to an Tiny AVR that doesn't have separate memory space for boot loader code. As it becomes apparent to me that the bootloader code relies on certain fuse settings and chip support, I guess what I'm really interested in knowing is what does it take to port the bootloader to a chip that doesn't have those fuses and hardware support (but still has self-programming capability)?

推荐答案

On NULL

地址 0 不是空指针.空指针"是更抽象的东西:适用函数应该识别为无效的特殊值.C 表示特殊值是 0,而 语言 表示取消引用它是未定义的行为",但在简单的微控制器世界中,它通常具有非常明确的效果.

On NULL

Address 0 does not a null pointer make. A "null pointer" is something more abstract: a special value that applicable functions should recognize as being invalid. C says the special value is 0, and while the language says dereferencing it is "undefined behavior", in the simple world of microcontrollers it usually has a very well-defined effect.

通常,在复位时,AVR 的程序计数器 (PC) 被初始化为 0,因此微控制器开始在地址 0 处执行代码.

Normally, on reset, the AVR's program counter (PC) is initialized to 0, thus the microcontroller begins executing code at address 0.

但是,如果设置了引导复位熔丝(BOOTRST"),则程序计数器将被初始化为内存上端块的地址(该地址取决于熔丝的设置方式,有关详细信息,请参阅数据表(PDF,7 MB).从那里开始的代码可以做任何事情——如果你真的想要你可以把你自己的程序放在那里,如果你使用 ICSP(引导加载程序通常不能覆盖自己).

However, if the Boot Reset Fuse ("BOOTRST") is set, the program counter is instead initialized to an address of a block at the upper end of the memory (where that is depends on how the fuses are set, see a datasheet (PDF, 7 MB) for specifics). The code that begins there can do anything—if you really wanted you could put your own program there if you use an ICSP (bootloaders generally can't overwrite themselves).

但通常,它是一个特殊的程序——一个引导加载程序——能够从外部源读取数据(通常通过 UART,I2sup>C、CAN 等)以重写程序代码(存储在内部或外部存储器中,取决于微).引导加载程序通常会寻找一个特殊事件".这实际上可以是任何东西,但对于开发来说,最方便的是数据总线上的某些东西,它将从中提取新代码.(对于生产,它可能是引脚上的特殊逻辑电平,因为它几乎可以立即检查.)如果引导加载程序看到特殊事件,它可以进入引导加载模式,在那里它会刷新程序存储器,否则它会通过控制关闭用户代码.

Often though, it's a special program—a bootloader—that is able to read data from an external source (often via UART, I2C, CAN, etc.) to rewrite program code (stored in internal or external memory, depending on the micro). The bootloader will typically look for a "special event" which can literally be anything, but for development is most conveniently something on the data bus it will pull the new code from. (For production it might be a special logic level on a pin as it can be checked nearly-instantly.) If the bootloader sees the special event, it can enter bootloading-mode, where it will reflash the program memory, otherwise it passes control off to user code.

顺便说一句,引导加载程序保险丝和高端内存块的重点是允许使用引导加载程序,无需对原始软件进行修改(只要它不扩展所有进入引导加载程序的地址).不是只用原始的 HEX 和所需的保险丝刷写,您可以刷写原始的十六进制、引导加载程序和修改后的保险丝,并快速添加引导加载程序.

As an aside, the point of the bootloader fuse and upper memory block is to allow the use of a bootloader with no modifications to the original software (so long as it doesn't extend all the way up into the bootloader's address). Instead of flashing with just the original HEX and desired fuses, one can flash the original HEX, bootloader, and modified fuses, and presto, bootloader added.

无论如何,就 Arduino 而言,我相信它使用了 STK500,它尝试通过 UART 进行通信,如果在分配的时间内没有得到响应:

Anyways, in the case of the Arduino, which I believe uses the protocol from the STK500, it attempts to communicate over the UART, and if it gets either no response in the allotted time:

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();
}

或者如果它因收到意外响应而出错太多:

or if it errors too much by getting an unexpected response:

if (++error_count == MAX_ERROR_COUNT)
    app_start();

它将控制权传递回位于 0 的主程序.在上面看到的 Arduino 源代码中,这是通过调用 app_start(); 完成的,定义为 void (*app_start)(void) = 0x0000;.

It passes control back to the main program, located at 0. In the Arduino source seen above, this is done by calling app_start();, defined as void (*app_start)(void) = 0x0000;.

因为它是一个 C 函数调用,所以在 PC 跳到 0 之前,它会将当前 PC 值推送到堆栈中,该堆栈还包含引导加载程序中使用的其他变量(例如 counterror_count 来自上面).这会从您的程序中窃取 RAM 吗?那么,在PC设置为0之后,明目张胆地执行的操作违反"了一个合适的 C 函数(最终会返回)应该做什么.在其他初始化步骤中,它重置堆栈指针(有效地清除调用堆栈和所有局部变量),回收 RAM.全局/静态变量初始化为0,因为bootloader和用户程序是独立编译的,所以它的地址可以自由地与bootloader使用的任何重叠.

Because it's couched as a C function call, before the PC hops over to 0, it will push the current PC value onto the stack which also contains other variables used in the bootloader (e.g. count and error_count from above). Does this steal RAM from your program? Well, after the PC is set to 0, the operations that are executed blatantly "violate" what a proper C function (that would eventually return) should do. Among other initialization steps, it resets the stack pointer (effectively obliterating the call stack and all local variables), reclaiming RAM. Global/static variables are initialized to 0, the address of which can freely overlap with whatever the bootloader was using because the bootloader and user programs were compiled independently.

引导加载程序唯一持久的影响是对硬件(外设)寄存器的修改,一个好的引导加载程序不会使这些寄存器处于有害状态(打开可能在您尝试睡眠时浪费电力的外设).完全初始化您将使用的外围设备通常是一种很好的做法,因此即使引导加载程序做了一些奇怪的事情,您也可以按照自己的意愿进行设置.

The only lasting effects from the bootloader are modifications to hardware (peripheral) registers, which a good bootloader won't leave in a detrimental state (turning on peripherals that might waste power when you try to sleep). It's generally good practice to also fully initialize peripherals you will use, so even if the bootloader did something strange you'll set it how you want.

在 ATtinys 上,正如您所提到的,引导加载程序保险丝或内存并不奢侈,因此您的代码将始终从地址 0 开始.您可以将引导加载程序放入更高的内存页面并指向您的 RESET 向量在那里,然后每当你收到一个新的十六进制文件来闪存时,采取地址 0:1 的命令,用引导加载程序地址替换它,然后将替换的地址存储在其他地方以调用正常执行.(如果是RJMP(相对跳转"),显然需要重新计算该值)

On ATtinys, as you mentioned, there is no luxury of the bootloader fuses or memory, so your code will always start at address 0. You might be able to put your bootloader into some higher pages of memory and point your RESET vector at it, then whenever you receive a new hex file to flash with, take the command that's at address 0:1, replace it with the bootloader address, then store the replaced address somewhere else to call for normal execution. (If it's an RJMP ("relative jump") the value will obviously need to be recalculated)

这篇关于Arduino 引导程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆