一板网电子技术论坛

发表于: 2017-7-16 00:08:07 | 显示全部楼层

微控制器上的引导加载程序是非常有用的。它使得我在需要时能够现场进行更新固件。有很多方法可以使用和制作引导加载程序。但是这样的引导程序在FLASH中需要一些空间,而且需要先在空白器件上编程,因此需要一个JTAG编程器。这就是为什么供应商已经开始在他们的器件中包含一个ROM引导加载程序:微控制器出厂时在FLASH中预装了一个引导加载程序。所以,我不需要自己编写引导加载程序,而是使用在ROM中的引导加载程序。

frdm-kl03z-with-rom-bootloader.jpg

和所有的方法一样,这种方法也有利弊。


简介

在一些最近推出的NXP Kinetis微控制器都包括了一个ROM引导加载程序。本实验中我选择了恩智浦FRDM-KL03Z开发板,因为它价格比较低廉(低于20美元),并且包含一个可用于较小应用程序的小型微控制器。而且KL03Z出现在Ultimate Hacking Keyboard上,后者引起了我的兴趣。在本文中,我将分享在使用KL03Z(ARM Cortex-M0 +、32 KB FLASH、2 KB SRAM和24 QFN封装)中的ROM引导加载程序过程中的经验:

mk03z32vfk4-mcu.png


本文中使用的源代码可以在GitHub上找到。


引导加载程序?

NXP Kinetis引导加载程序(KBOOT软件)版本v2可以在www.nxp.com/kboot下载。恩智浦提供三种不同的样式:


•     Flashloader:该引导加载程序是“一次性引导加载程序”,会永久保留在FLASH中,不管是出厂设置还是用户编程。引导加载程序区域可以被应用程序覆盖,因此应用程序可以使用完整的FLASH存储器。但只能更新应用程序一次,除非应用程序本身包含引导加载程序。 Flashloader无需JTAG编程器就可以对器件编程。 KBOOT软件包中带有几个示例应用程序。

•     Flash-Resident Bootloader:此引导程序是位于FLASH存储器中的“传统”的引导加载程序。官方提供了源代码,我可以完全对其进行配置,但是最初需要一个JTAG编程器来编程器件。KBOOT软件包包含了不同开发板的几个示例。

•     ROM Bootloader:此引导加载程序存在于所有Kinetis器件上,并且带有引导ROM,包括KL03Z。它基本上是Flash-Resident Bootloader的特殊版本,编程到器件的ROM中。但是,除了参考手册中的信息之外,还没有提供源文件或任何其他信息。


有很多与Bootloader进行通信的不同方法(I²C、SPI、UART、USB HID、USB MSD、CAN)。引导加载程序使用特殊的通信协议,KBOOT软件包括GUI和命令行实用程序来与设备进行通信。


ROM Bootloader

ROM引导加载程序位于从0x1c00200地址开始的ROM中,并在引导加载程序运行时使用RAM的一部分:

rom-bootloader-memory-map.png

注意:有可用的ROM引导加载程序的源代码和内存映射图是非常有用的。这样的话,就可以从应用程序重新使用一些引导加载程序函数来节省FLASH空间。


引导加载程序可以通过使用以下代码由应用程序调用:

  1. static void RunRomBootloader(void) {
  2.     uint32_t runBootloaderAddress;
  3.     void (*runBootloader)(void *arg);

  4.     /* Read the function address from the ROM API tree. */
  5.     runBootloaderAddress = **(uint32_t **)(0x1c00001c);
  6.     runBootloader = (void (*)(void * arg))runBootloaderAddress;

  7.     /* Start the bootloader. */
  8.     runBootloader(NULL);
  9. }
复制代码

使用上述代码,应用程序可以随时进入引导加载程序。


引导启动

在POR(上电复位)或正常复位期间,可以进行配置微控制器是进入ROM引导加载程序,或者是直接从正常FLASH引导启动。


这是由以下几件事情控制的:

•     FORCEROM:两个位,用于配置始终进入ROM引导加载程序。如果这些位不为零,它始终进入ROM引导加载程序。

       forcerom.png


•     BOOTPIN_OPT:FOPT寄存器的一部分。如果该位为零,它将启用检查可选的引导加载程序使能引脚(KL03Z为NMI):如果该引脚拉低,则它进入ROM引导加载程序

       bootpin_opt.png


•     BOOTCFG0引脚:这是由BOOTPIN_OPT设置启用的引脚(KL03Z为NMI引脚)。


•     BOOTSRC_SEL:FOPT寄存器的一部分。如果设置为10或11,则进入ROM引导加载程序:

       bootsrc_sel-settings.png


以下是引导过程的流程图:

chip-boot-flow-chart.png


进入引导加载程序

如果满足以下条件,则进入ROM引导加载程序:

•     FORCEROM设置为非零值

•     或者:Bootloader引脚配置为BOOTPIN_OPT,该引脚被置位(NMI)

•     或者:BOOTSRC_SEL设置为10或11


NMI引脚位于KL03Z32VFK4的引脚19上:

nmi-pin-on-kl03z32vfk4.png

在FRDM-KL03Z开发板上,NMI引脚连接到SW3按钮:

sw3-nmi-push-button.png

按钮位于FRDM-KL03Z开发板的以下位置:

sw03-nmi-push-button-on-frdm-kl03z.png



引导程序引脚复用

如上所述,NMI引脚可用于进入引导加载程序。 引导加载程序可以通信的引脚在参考手册中有记录。 KL03Z可以通过UART、SPI和I²C进行通信:

bootloader-communication-pins.png


UART(PTB1、PTB2)连接到K20的OpenSDA USB CDC,I2C(PTB3,PTB4)连接到J2插头:

bootloader-uart-and-i2c-communication-pins.png

跳转到指定楼层
发表于: 2017-7-16 14:16:44 | 显示全部楼层

引导加载程序配置区域(BCA)

引导加载程序由FLASH中位于0x3C0的结构体配置(在从地址0x0开始的向量表之后):

bootloader-configuration-area.png


BCA和链接器文件

要配置引导加载程序和BCA,我使用以下实现:


请注意,引导程序将选择第一个“工作”的通信通道! 我有一个问题,在I2C总线上,另一个设备在引导加载程序启动期间发送数据,导致引导程序监听I2C总线而不是UART。 我建议仅在BCA设置中启用所需的通信通道。

  1. /* bootloader_config.c
  2. * KBOOT ROM Bootloader configuration for FRDM-KL03Z Board
  3. */

  4. #include <stdint.h>

  5. #define ENABLE_BCA     (1)
  6.   /*!< 1: define Bootloader Configuration Area mit magic number to use it from ROM bootloader; 0: use default setting (no BCA) */

  7. typedef struct BootloaderConfiguration
  8. {
  9.     uint32_t tag; //!< [00:03] Magic number to verify bootloader configuration is
  10.                   //! valid. Must be set to 'kcfg'.
  11.     uint32_t crcStartAddress; //!< [04:07] Start address for application image CRC
  12.                               //! check. If the bits are all set then Kinetis
  13.                               //! bootloader by default will not perform any CRC
  14.                               //! check.
  15.     uint32_t crcByteCount; //!< [08:0b] Byte count for application image CRC
  16.                            //! check. If the bits are all set then Kinetis
  17.                            //! bootloader by default will not prform any CRC check.
  18.     uint32_t crcExpectedValue; //!< [0c:0f] Expected CRC value for application CRC
  19.                                //! check. If the bits are all set then Kinetis
  20.                                //! bootloader by default will not perform any CRC
  21.                                //! check.
  22.     uint8_t enabledPeripherals; //!< [10:10] Bitfield of peripherals to enable.
  23.                                 //! bit 0 - LPUART, bit 1 - I2C, bit 2 - SPI,
  24.                                 //! bit 3 - CAN, bit 4 - USB
  25.                                 //! Kinetis bootloader will enable the peripheral if
  26.                                 //! corresponding bit is set to 1.
  27.     uint8_t i2cSlaveAddress; //!< [11:11] If not 0xFF, used as the 7-bit I2C slave
  28.                              //! address. If 0xFF, defaults to 0x10
  29.                              //! for I2C slave address.
  30.     uint16_t peripheralDetectionTimeoutMs; //!< [12:13] Timeout in milliseconds
  31.                                            //! for active peripheral detection. If
  32.                                            //! 0xFFFF, defaults to 5 seconds.
  33.     uint16_t usbVid; //!< [14:15] Sets the USB Vendor ID reported by the device
  34.                      //! during enumeration. If 0xFFFF, it defaults to 0x15A2.
  35.     uint16_t usbPid; //!< [16:17] Sets the USB Product ID reported by the device
  36.                      //! during enumeration.
  37.     uint32_t usbStringsPointer; //!< [18:1b] Sets the USB Strings reported by the
  38.                                 //! device during enumeration.
  39.     uint8_t clockFlags; //!< [1c:1c] The flags in the clockFlags configuration
  40.                         //! field are enabled if the corresponding bit is cleared (0).
  41.                         //! bit 0 - HighSpeed Enable high speed mode (i.e., 48 MHz).
  42.     uint8_t clockDivider; //!< [1d:1d] Inverted value of the divider to use for
  43.                           //! core and bus clocks when in high speed mode.
  44. } bootloader_config_t;

  45. /* bits for enabledPeripherals */
  46. #define ENABLE_PERIPHERAL_UART     (1<<0)
  47. #define ENABLE_PERIPHERAL_I2C      (1<<1)
  48. #define ENABLE_PERIPHERAL_SPI      (1<<2)
  49. #define ENABLE_PERIPHERAL_CAN      (1<<3) /* not supported for KL03! */
  50. #define ENABLE_PERIPHERAL_USB_HID  (1<<4) /* not supported for KL03! */
  51. #define ENABLE_PERIPHERAL_USB_MSC  (1<<7) /* not supported for KL03! */

  52. /* Bootloader configuration area, needs to be at address 0x3C0! */
  53. __attribute__((section(".BootloaderConfig"))) const bootloader_config_t BootloaderConfig =
  54.     {
  55. #if ENABLE_BCA
  56.         .tag = 0x6766636B, //!< Magic Number
  57. #else
  58.     .tag = 0xFFFFFFFF, //!< No Magic Number
  59. #endif
  60.         .crcStartAddress = 0xFFFFFFFF, //!< Disable CRC check
  61.         .crcByteCount = 0xFFFFFFFF, //!< Disable CRC check
  62.         .crcExpectedValue = 0xFFFFFFFF, //!< Disable CRC check
  63.         .enabledPeripherals = ENABLE_PERIPHERAL_UART, //ENABLE_PERIPHERAL_UART|ENABLE_PERIPHERAL_I2C|ENABLE_PERIPHERAL_SPI|ENABLE_PERIPHERAL_CAN|ENABLE_PERIPHERAL_USB_HID|ENABLE_PERIPHERAL_USB_MSC, //!< Enabled Peripheral: UART I2C SPI CAN USB-HID
  64.         .i2cSlaveAddress = 0x10, //!< Use default I2C address(0x10)
  65.         .peripheralDetectionTimeoutMs = 5000, //!< Use user-defined timeout(ms)
  66.         .usbVid = 0xFFFF, //!< Use default Vendor ID(0x15A2)
  67.         .usbPid = 0xFFFF, //!< Use default Product ID(0x0073)
  68.         .usbStringsPointer = 0xFFFFFFFF, //!< Use default USB String
  69.         .clockFlags = 0xFF, //!< 0 bit cleared: Enable High speed mode. NOTE: Enabling high speed mode makes UART connection worse or requires pull-up on Rx line!
  70.     .clockDivider = 0xff, //!< Use clock divider(0)
  71.     };

  72. /* 16 bytes at address 0x400 */
  73. __attribute__((used, section(".FlashConfig"))) const uint32_t FOPTConfig[4] = {
  74.   0xFFFFFFFF,
  75.   0xFFFFFFFF,
  76.   0xFFFFFFFF,
  77. // 0xFFFF3DFE // boot from FLASH
  78.   0xFFFFBDFE   // boot from ROM, means this will kick in the bootloader by default
  79. };
复制代码

注意FlashConfig(FOPT)配置!如果没有正确的话,你可能会损坏元件!


使用宏

  1. #define ENABLE_BCA (1)
  2. /*!< 1: define Bootloader Configuration Area mit magic number to use it from ROM bootloader; 0: use default setting (no BCA) */
复制代码

我可以打开/关闭BCA。如果关闭(设置为0),则不会使用BCA签名,并且引导加载程序忽略BCA。


需要在FLASH中分配BootloaderConfig变量,地址为0x3C0。为此我已经分配了一个MEMORY区域

  1. m_bootloader_config (RX) : ORIGIN = 0x000003C0, LENGTH = 0x20 /* ROM Bootloader configuration */
复制代码

链接器文件中的相应位置:

  1. /* placing my named section at given address: */
  2. .myBootloaderConfigBlock :
  3. {
  4. KEEP(*(.BootloaderConfig)) /* keep my variable even if not referenced */
  5. } > m_bootloader_config
复制代码

闪存配置(FOPT)相同:声明一个内存区域:

  1. m_bootloader_config (RX) : ORIGIN = 0x000003C0, LENGTH = 0x20 /* ROM Bootloader configuration */
复制代码

并将其放在链接器部分中,如下所示:

  1. .flash_config :
  2. {
  3. . = ALIGN(4);
  4. KEEP(*(.FlashConfig)) /* Flash Configuration Field (FCF) */
  5. . = ALIGN(4);
  6. } > m_flash_config
复制代码

bootloader-and-flash-configuration-in-linker-file.png


擦除BCA和FLASH

如果配置区域未正确设置,则运行引导加载程序可能会失败。我强烈建议在开始使用FRDM-KL03Z开发板时擦除KL03Z存储器,因为预加载的演示应用程序似乎混淆了引导加载程序。要擦除FLASH,我正在使用SEGGER OpenSDA固件和SEGGER J-Link J-Flash Lite程序,它免费用于非生产目的:


选择器件:

segger-jflashlite-device-detection.png


然后擦除芯片:

erasing-the-chip.png


这样,BCA被擦除并设置为默认的0xFF内存模式。

回复

使用道具 举报

发表于: 2017-7-16 15:05:22 | 显示全部楼层

通过UART连接到引导加载程序

擦除FLASH后,要建立与ROM引导加载程序的初始连接,请将USB电缆连接到OpenSDA USB端口:

usb-usb-cdc-connection.png


确定虚拟COM端口(例如使用Windows设备管理器):

com-port-in-device-manager.png


然后在SW3 / NMI引脚拉低的同时进行复位:

1.    按住“复位”按钮

2.    按住SW3按钮

3.   松开复位按钮,同时仍按住SW3按钮

4.   松开SW3按钮

enter-rom-bootloader-mode.png


目前没有任何可视符号显示微控制器现在处于引导加载程序模式。


注意:ROM引导加载程序有几个问题,请参阅以下勘误表:

errata-e8059.png


因此,我只能使用19200波特率进行连接(并不总是成功!)。所以确保你使用的是19200波特率!


要连接到电路板,请使用KinetisFlashTool(GUI)blhost(命令行工具)。

注意在第一次通信尝试中选择所使用的通信通道(I2C、UART、SPI)和速度(波特率)。要更改通信速度或通信通道,KL03Z必须先复位!


KinetisFlashTool(适用于Windows)位于

  1. <KBOOT Installation path>\NXP_Kinetis_Bootloader_2_0_0\bin\Tools\KinetisFlashTool\win
复制代码

blhost工具位于

  1. <KBOOT Installation path>\NXP_Kinetis_Bootloader_2_0_0\bin\Tools\blhost
复制代码

kinetisflashtool.png

然后单击“connect”,并希望能够连接(否则重试):

kinetisflashtool-connected-to-device.png

用blhost命令行工具,使用

  1. blhost --port COM9,19200 -d -- get-property 1
复制代码

注意确保COM端口尚未使用(关闭KinetisFlashTool)。


-d选项产生一些调试输出,并且使用get-property 1,我要求输出引导程序版本信息。


上电后的第一个命令可能丢失(勘误表中的另一个条目):在这种情况下,请使用CTRL-C取消命令:

  1. blhost --port COM9,19200 -d -- get-property 1

  2. Warning: Operation canceled!
  3. - The target device must be reset before sending any further commands.
  4. ^C
复制代码

然后再重试一次,第二次通常工作:

  1. blhost --port COM9,19200 -d -- get-property 1
  2. [5a a6]
  3. <5a>
  4. Ping responded in 1 attempt(s)
  5. <a7>
  6. <00 00 01 50 00 00 29 ae>
  7. Framing protocol version = 0x50010000, options = 0x0
  8. Inject command 'get-property'
  9. [5a a4 0c 00 4b 33 07 00 00 02 01 00 00 00 00 00 00 00]
  10. <5a>
  11. <a1>
  12. <5a>
  13. <a4>
  14. <0c 00>
  15. <07 7a>
  16. <a7 00 00 02 00 00 00 00 00 00 01 4b>
  17. Successful response to command 'get-property(current-version)'
  18. - took 0.009 seconds
  19. [5a a1]
  20. Response status = 0 (0x0) Success.
  21. Response word 1 = 1258356736 (0x4b010000)
  22. Current Version = K1.0.0
复制代码

成功!我们可以和bootloader通讯!


在勘误表中考虑到e8086后,我进一步改善了以上情况:

e8086.png


所以我添加了一个10K的上拉到LPUART Rx引脚:

10k-pull-up-on-lpuart-rx-pin.png


为UART连接启用更高的波特率

经过大量的实验和错误,我成功得到一个稳定的引导加载器UART连接。以下是我的做法:

1.    向LPUART Rx引脚添加10k上拉电阻

2.    将BCA时钟标志设置为0xFE:.clockFlags = 0xFE

3.    将BCA时钟分频器设置为0xFF:.clockDivider = 0xff

4.    复位,然后使用上述设置进入引导加载程序


这样我就可以使用例如57600的波特率:

  1. blhost --port COM9,57600 -d -- get-property 0x2
  2. [5a a6]
  3. <5a>
  4. Ping responded in 1 attempt(s)
  5. <a7>
  6. <00 00 01 50 00 00 29 ae>
  7. Framing protocol version = 0x50010000, options = 0x0
  8. Inject command 'get-property'
  9. [5a a4 0c 00 3e fb 07 00 00 02 02 00 00 00 00 00 00 00]
  10. <5a>
  11. <a1>
  12. <5a>
  13. <a4>
  14. <0c 00>
  15. <2d c6>
  16. <a7 00 00 02 00 00 00 00 01 00 00 00>
  17. Successful response to command 'get-property(available-peripherals)'
  18. - took 0.013 seconds
  19. [5a a1]
  20. Response status = 0 (0x0) Success.
  21. Response word 1 = 1 (0x1)
  22. Available Peripherals = UART
复制代码
回复

使用道具 举报

发表于: 2017-7-17 09:57:09 | 显示全部楼层

使用引导程序加载二进制文件

我将三个LED闪烁程序放在了GitHub,可以用来测试引导程序:

rom-bootloader-test-programs.png


如果使用Flash工具,请浏览到二进制文件,然后使用Update按钮:

updating-image-file-with-kinetisflashtool.png


与blhost命令行实用程序相同,如下所示:

  1. blhost --port COM9,57600 write-memory 0 "FRDM-KL03Z_LED_blue.bin"
复制代码

如果使用S19 / S-Record文件,则可以使用“flash-image”命令:

  1. blhost --port COM9,57600 flash-image "FRDM-KL03Z_LED_blue.srec"
复制代码

但是,我遇到了这些错误,他们返回错误:kStatus_FlashCommandFailure

  1. blhost --port COM9,57600 write-memory 0 "c:\tmp\FRDM-KL03Z_LED_blue.bin"
  2. Ping responded in 1 attempt(s)
  3. Inject command 'write-memory'
  4. Preparing to send 14784 (0x39c0) bytes to the target.
  5. Successful generic response to command 'write-memory'
  6. (1/1)24%Data phase write aborted by status 0x2712 kStatus_AbortDataPhase
  7. Response status = 105 (0x69) kStatus_FlashCommandFailure
  8. Wrote 3616 of 14784 bytes.
复制代码

只有通过反复试验,我才发现,如果我先进行一次flash-erase-all,它才会成功:

  1. blhost --port COM9,57600 -d -- flash-erase-all
  2. [5a a6]
  3. <5a>
  4. Ping responded in 1 attempt(s)
  5. <a7>
  6. <00 00 01 50 00 00 29 ae>
  7. Framing protocol version = 0x50010000, options = 0x0
  8. Inject command 'flash-erase-all'
  9. [5a a4 08 00 0c 22 01 00 00 01 00 00 00 00]
  10. <5a>
  11. <a1>
  12. <5a>
  13. <a4>
  14. <0c 00>
  15. <2d 84>
  16. <a0 00 08 02 00 00 00 00 01 00 00 00>
  17. Successful generic response to command 'flash-erase-all'
  18. - took 0.034 seconds
  19. [5a a1]
  20. Response status = 0 (0x0) Success.
复制代码

然后执行编程:

  1. blhost --port COM9,57600 write-memory 0 "c:\tmp\FRDM-KL03Z_LED.bin"
  2. Ping responded in 1 attempt(s)
  3. Inject command 'write-memory'
  4. Preparing to send 17544 (0x4488) bytes to the target.
  5. Successful generic response to command 'write-memory'
  6. (1/1)100% Completed!
  7. Successful generic response to command 'write-memory'
  8. Response status = 0 (0x0) Success.
  9. Wrote 17544 of 17544 bytes.
复制代码

我没有任何说明,因为我的flash没有被保护。至少我找到了一种方法可以让它工作。


总结

ROM引导程序是一件非常好的事情:我不需要为引导程序保留FLASH空间。相反,引导加载程序被烧录到ROM中,我可以通过恩智浦KL03Z上的I²C、UART和SPI进行通信。请注意勘误表,并采取正确的步骤和连接设置,希望能够正常工作。KL03Z器件勘误表会影响ROM引导加载程序的几个部分,需要一些解决方法。


下一步是什么?我正在探索和使用BusPal技术:这部分是非常有趣的,因为它允许使用微控制器与另一个微控制器的引导程序进行访问。这样,一个器件可以更新另一个器件。恩智浦提供的项目是针对IAR和Keil,但是我已经设法使用免费开放的GNU工具将BusPal连接到FRDM-KL25Z。


希望本文有助于您快速入门FRDM-KL03Z或任何其他开发板上启动引导程序。并且不要忘记要检查你的器件勘误表;-)。


参考链接

■    Kinetis Bootloader和Tools下载页面:www.nxp.com/kboot

■    本文中GitHub上使用的源代码:https://github.com/ErichStyger/m ... L03Z/FRDM-KL03Z_LED

■    恩智浦Freedom Board:http://www.nxp.com/freedom

■    FRDM-KL03Z开发板:http://www.nxp.com/products/soft ... l03-mcus:FRDM-KL03Z

■    FRDM-KL03Z勘误表:http://www.nxp.com/docs/en/errata/KL03Z_1N86K.pdf

■    Ultimate Hacking Keyboard:https://ultimatehackingkeyboard.com/

■    串行引导加载程序:https://mcuoneclipse.com/2013/04 ... h-processor-expert/

■    引导加载程序的终端:https://mcuoneclipse.com/2016/06 ... serial-bootloaders/


译者注:本文翻译自https://mcuoneclipse.com/2017/07 ... p-frdm-kl03z-board/。如有错漏,敬请指正。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 37 | 回复: 54



Archiver|手机版|小黑屋|

GMT+8, 2018-6-22 17:22 , Processed in 0.046875 second(s), 31 queries . Powered by Discuz! X3.4

YiBoard一板网 © 2015-2018 地址:河北省石家庄市长安区高营大街 ( 京ICP备16014155号 )

快速回复 返回顶部 返回列表