代码之家  ›  专栏  ›  技术社区  ›  vicatcu

Arduino引导程序

  •  26
  • vicatcu  · 技术社区  · 14 年前

    有人能解释一下 Arduino bootloader 作品?我不想在这里寻找高层次的答案,我已经阅读了代码,并得到了它的要点。

    Arduino IDE和bootloader代码之间发生了一系列的协议交互,最终导致了大量的内联汇编指令,这些指令在程序通过串行接口传输的情况下对flash进行自编程。

    我不清楚的是在第270行:

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

    …我将其识别为函数指针的声明和初始化为空。在引导加载程序打算委托执行用户加载的代码的地方,随后会调用app_start。

    当然,不知何故 app_start 需要在某个时间点获取一个非空值,以便所有这些值组合在一起。我在引导装载程序代码中没有看到这一点…它是否被引导加载程序加载的程序神奇地链接起来?我假设引导装载程序的主要部分是在芯片复位后进入软件的入口点。

    包裹在70行左右的汇编中的一定是秘密解码器环,它告诉主程序app_的真正开始是什么?或者可能是阿尔杜诺·伊德利用了一些隐含的知识?我所知道的是,如果有人不改变app_start指向0以外的某个地方,bootloader代码将永远自转……那么诀窍是什么呢?

    编辑

    我有兴趣尝试将引导加载程序移植到一个很小的AVR,它没有独立的内存空间来存放引导加载程序代码。在我看来,引导加载程序代码依赖于某些熔丝设置和芯片支持的情况下,我想我真正感兴趣的是如何将引导加载程序移植到一个没有熔丝和硬件支持的芯片上(但仍然具有自编程能力)?

    2 回复  |  直到 14 年前
        1
  •  39
  •   Nick T twasbrillig    10 年前

    关于零

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

    ATMega引导加载程序

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

    但是,如果设置了引导重置保险丝(“bootsrt”),则程序计数器将被初始化为内存上端的一个块的地址(这取决于保险丝的设置方式,请参见 a datasheet (PDF,7 MB)以获取详细信息)。如果你真的想要,从那里开始的代码可以做任何事情。如果你使用一个icsp,你可以把你自己的程序放在那里(引导加载程序通常不能覆盖自己)。

    但通常情况下,这是一个特殊的计划-A 引导加载程序 -能够 从外部源读取数据 (通常通过UART,我 c、罐等)至 重写程序代码 (存储在内部或外部存储器中,具体取决于micro)。bootloader通常会寻找一个“特殊事件”,它实际上可以是任何事情,但是对于开发来说,它是从数据总线上提取新代码最方便的事情。(对于生产,它可能是一个特殊的逻辑级别,因为它几乎可以立即检查。)如果引导加载程序看到特殊事件,它可以进入引导加载模式,在那里它将重新刷新程序内存,否则它将控制权移交给用户代码。

    另一方面,引导装载程序保险丝和上内存块的作用是允许使用引导装载程序 对原始软件的修改(只要它没有一直扩展到引导加载程序的地址中)。bootloader补充说,你可以用原来的十六进制和所需的保险丝来代替闪烁,而可以用原来的十六进制、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 error_count 从上面)。这会从你的程序中窃取RAM吗?好吧,在PC设置为0之后,公开地执行的操作“违反”了适当的C函数(最终会返回)应该做的事情。在其他初始化步骤中,它重置堆栈指针(有效地删除调用堆栈和所有本地变量),回收RAM。全局/静态变量初始化为0,其地址可以与引导加载程序使用的任何内容自由重叠,因为引导加载程序和用户程序是独立编译的。

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

    阁楼装载机

    正如您所提到的,在Attinys上,引导装载程序的保险丝或内存并不奢侈,所以您的代码总是从地址0开始。您可以将引导加载程序放入一些更高的内存页,并将重置向量指向它,然后每当您收到要闪存的新十六进制文件时,获取地址为0:1的命令,将其替换为引导加载程序地址,然后将替换的地址存储到其他地方以调用正常执行。(如果是 RJMP (“ 相对的 跳转”)该值显然需要重新计算)

        2
  •  1
  •   old_timer    11 年前

    编辑

    我有兴趣尝试将引导加载程序移植到一个很小的AVR,它没有独立的内存空间来存放引导加载程序代码。在我看来,引导加载程序代码依赖于某些熔丝设置和芯片支持的情况下,我想我真正感兴趣的是如何将引导加载程序移植到一个没有熔丝和硬件支持的芯片上(但仍然具有自编程能力)?

    根据您的最终目标,创建您自己的引导装载程序可能比移植一个要容易。你真的只需要为那部分学习一些东西。

    1)UART

    2)UART RX

    3)自闪存编程

    它可以单独学习,然后组合成一个引导装载程序。您将需要一个可以使用SPI或任何东西来编写flash的部分,这样,如果您的引导加载程序不工作,或者任何附带的部分出现问题,您仍然可以继续开发。

    不管你是选择投球还是投球,你仍然需要理解这三个基本的方面。