Arduino引导程序 [英] Arduino Bootloader

查看:209
本文介绍了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的值才能将它们全部组合在一起.我没有在引导加载程序代码中看到这一点...它是否与由引导加载程序加载的程序进行了神奇的链接?我认为引导加载程序的主要部分是芯片复位后进入软件的入口.

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感兴趣,该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)?

推荐答案

在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).

尽管它通常是一个特殊的程序- bootloader ,它能够从外部源读取数据(通常通过UART,I 2 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.

顺便说一句,引导加载程序保险丝和上部存储模块的作用是允许使用对原始软件进行了 no 修改的引导加载程序(只要它不会扩展所有进入引导加载程序的地址).不仅可以闪烁原始的十六进制和所需的保险丝,还可以闪烁原始的十六进制,自举程序和修改过的保险丝,并可以预先存储自举程序.

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源代码中,这是通过调用定义为void (*app_start)(void) = 0x0000;app_start();完成的.

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,其地址可以与引导加载程序使用的地址自由地重叠,因为引导加载程序和用户程序是独立编译的.

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天全站免登陆