Zephyr OS (1)

 

调试 设备管理

  • 调试
  • 设备管理

Debug·Thread analyzer

Thread analyzer 线程分析仪

The thread analyzer module enables all the Zephyr options required to track the thread information, e.g. thread stack size usage and other runtime thread runtime statistics.

线程分析器模块支持跟踪线程信息所需的所有 Zephyr 选项,例如线程堆栈大小使用情况和其他运行时线程运行时统计数据。

The analysis is performed on demand when the application calls thread_analyzer_run() or thread_analyzer_print().

当应用程序调用 thread_analyzer_run()或 thread_analyzer_print()时,根据需要执行分析。

For example, to build the synchronization sample with Thread Analyser enabled, do the following:

例如,若要在启用了线程分析器的情况下生成同步示例,请执行以下操作:

west build -b qemu_x86 samples/synchronization/ -- -DCONFIG_QEMU_ICOUNT=n -DCONFIG_THREAD_ANALYZER=y \
-DCONFIG_THREAD_ANALYZER_USE_PRINTK=y -DCONFIG_THREAD_ANALYZER_AUTO=y \
-DCONFIG_THREAD_ANALYZER_AUTO_INTERVAL=5

When you run the generated application in Qemu, you will get the additional information from Thread Analyzer:

当您在 Qemu 运行生成的应用程序时,您将从线程分析器获得额外的信息:

thread_a: Hello World from cpu 0 on qemu_x86!
Thread analyze:
 thread_b            : STACK: unused 740 usage 284 / 1024 (27 %); CPU: 0 %
 thread_analyzer     : STACK: unused 8 usage 504 / 512 (98 %); CPU: 0 %
 thread_a            : STACK: unused 648 usage 376 / 1024 (36 %); CPU: 98 %
 idle                : STACK: unused 204 usage 116 / 320 (36 %); CPU: 0 %
thread_b: Hello World from cpu 0 on qemu_x86!
thread_a: Hello World from cpu 0 on qemu_x86!
thread_b: Hello World from cpu 0 on qemu_x86!
thread_a: Hello World from cpu 0 on qemu_x86!
thread_b: Hello World from cpu 0 on qemu_x86!
thread_a: Hello World from cpu 0 on qemu_x86!
thread_b: Hello World from cpu 0 on qemu_x86!
thread_a: Hello World from cpu 0 on qemu_x86!
Thread analyze:
 thread_b            : STACK: unused 648 usage 376 / 1024 (36 %); CPU: 7 %
 thread_analyzer     : STACK: unused 8 usage 504 / 512 (98 %); CPU: 0 %
 thread_a            : STACK: unused 648 usage 376 / 1024 (36 %); CPU: 9 %
 idle                : STACK: unused 204 usage 116 / 320 (36 %); CPU: 82 %
thread_b: Hello World from cpu 0 on qemu_x86!
thread_a: Hello World from cpu 0 on qemu_x86!
thread_b: Hello World from cpu 0 on qemu_x86!
thread_a: Hello World from cpu 0 on qemu_x86!
thread_b: Hello World from cpu 0 on qemu_x86!
thread_a: Hello World from cpu 0 on qemu_x86!
thread_b: Hello World from cpu 0 on qemu_x86!
thread_a: Hello World from cpu 0 on qemu_x86!
Thread analyze:
 thread_b            : STACK: unused 648 usage 376 / 1024 (36 %); CPU: 7 %
 thread_analyzer     : STACK: unused 8 usage 504 / 512 (98 %); CPU: 0 %
 thread_a            : STACK: unused 648 usage 376 / 1024 (36 %); CPU: 8 %
 idle                : STACK: unused 204 usage 116 / 320 (36 %); CPU: 83 %
thread_b: Hello World from cpu 0 on qemu_x86!
thread_a: Hello World from cpu 0 on qemu_x86!
thread_b: Hello World from cpu 0 on qemu_x86!

Configuration 配置

Configure this module using the following options.

使用以下选项配置此模块。

  • THREAD_ANALYZER: enable the module.

    启用模块。

  • THREAD_ANALYZER_USE_PRINTK: use printk for thread statistics.

    THREAD_ANALYZER_USE_PRINTK: 使用 printk 进行线程统计。

  • THREAD_ANALYZER_USE_LOG: use the logger for thread statistics.

    THREAD_ANALYZER_USE_LOG: 使用日志记录器进行线程统计。

  • THREAD_ANALYZER_AUTO: run the thread analyzer automatically. You do not need to add any code to the application when using this option.

    THREAD_ANALYZER_AUTO: 自动运行线程分析器。使用此选项时,不需要向应用程序添加任何代码。

  • THREAD_ANALYZER_AUTO_INTERVAL: the time for which the module sleeps between consecutive printing of thread analysis in automatic mode.

    THREAD_ANALYZER_AUTO_INTERVAL 在自动模式下,在线程分析的连续打印之间,模块休眠的时间。

  • THREAD_ANALYZER_AUTO_STACK_SIZE: the stack for thread analyzer automatic thread.

    THREAD_ANALYZER_AUTO_STACK_SIZE: 线程分析器自动线程的堆栈。

  • THREAD_NAME: enable this option in the kernel to print the name of the thread instead of its ID.

    THREAD_NAME: 启用内核中的这个选项来打印线程的名称而不是它的 ID。

  • THREAD_RUNTIME_STATS: enable this option to print thread runtime data such as utilization (This options is automatically selected by THREAD_ANALYZER).

    THREAD_RUNTIME_STATS: 启用此选项以打印线程运行时数据,如利用率(此选项由 THREAD_ANALYZER 自动选择)。

API documentation API 文档

  • group thread_analyzer

    Module for analyzing threads.用于分析线程的模块。

    This module implements functions and the configuration that simplifies thread analysis.

    此模块实现简化线程分析的函数和配置。

Debug·Core Dump 核心转储

The core dump module enables dumping the CPU registers and memory content for offline debugging. This module is called when a fatal error is encountered and prints or stores data according to which backends are enabled.

核心转储模块支持转储 CPU 寄存器和内存内容以进行脱机调试。当遇到致命错误并根据启用后端的数据打印或存储数据时,将调用此模块。

Configuration 配置

Configure this module using the following options.

使用以下选项配置此模块。

  • DEBUG_COREDUMP: enable the module.

    DEBUG_COREDUMP: 启用模块。

Here are the options to enable output backends for core dump:

下面是为核心转储启用输出后端的选项:

  • DEBUG_COREDUMP_BACKEND_LOGGING: use log module for core dump output.

    DEBUG_COREDUMP_BACKEND_LOGGING 对核心转储输出使用日志模块。

  • DEBUG_COREDUMP_BACKEND_NULL: fallback core dump backend if other backends cannot be enabled. All output is sent to null.

    DEBUG_COREDUMP_BACKEND_NULL: 如果其他后端无法启用,则回退核心转储后端。所有输出将发送到 null。

Here are the choices regarding memory dump:

以下是关于内存转储的选择:

  • DEBUG_COREDUMP_MEMORY_DUMP_MIN: only dumps the stack of the exception thread, its thread struct, and some other bare minimal data to support walking the stack in the debugger. Use this only if absolute minimum of data dump is desired.

    DEBUG_COREDUMP_MEMORY_DUMP_MIN: 只转储异常线程的堆栈、其线程结构和其他一些最小数据,以支持在调试器中遍历堆栈。仅当需要绝对最小数据转储时才使用此选项。

Additional memory can be included in a dump (even with the “DEBUG_COREDUMP_MEMORY_DUMP_MIN” config selected) through one or more coredump devices

可以通过一个或多个核转储设备在转储中包含额外的内存(即使选择了“DEBUG_COREDUMP_MEMORY_DUMP_MIN”配置)

Usage 用法

When the core dump module is enabled, during a fatal error, CPU registers and memory content are printed or stored according to which backends are enabled. This core dump data can fed into a custom-made GDB server as a remote target for GDB (and other GDB compatible debuggers). CPU registers, memory content and stack can be examined in the debugger.

启用核心转储模块时,在发生致命错误期间,将根据启用后端的情况打印或存储 CPU 寄存器和内存内容。这个核心转储数据可以作为 GDB (和其他与 GDB 兼容的调试器)的远程目标提供给定制的 GDB 服务器。可以在调试器中检查 CPU 寄存器、内存内容和堆栈。

This usually involves the following steps:

这通常包括以下步骤:

  1. Get the core dump log from the device depending on enabled backends. For example, if the log module backend is used, get the log output from the log module backend.

    根据启用的后端从设备获取核心转储日志。例如,如果使用日志模块后端,则从日志模块后端获取日志输出。

  2. Convert the core dump log into a binary format that can be parsed by the GDB server. For example, scripts/coredump/coredump_serial_log_parser.py can be used to convert the serial console log into a binary file.

    将核心转储日志转换为可由 GDB 服务器解析的二进制格式。例如,可以使用 scripts/coredump/coredump_serial_log_parser.py 将串行控制台日志转换为二进制文件。

  3. Start the custom GDB server using the script scripts/coredump/coredump_gdbserver.py with the core dump binary log file, and the Zephyr ELF file as parameters.

    使用脚本 scripts/coredump/coredump_gdbserver.py 和核心转储二进制日志文件以及 Zephyr ELF 文件作为参数启动自定义 GDB 服务器。

  4. Start the debugger corresponding to the target architecture.

    启动与目标体系结构对应的调试器

Example 例子

This example uses the log module backend tied to serial console. This was done on X86 Emulation (QEMU) where a null pointer was dereferenced.

此示例使用绑定到串行控制台的日志模块后端。这是在取消引用空指针的 X86仿真(QEMU)上完成的。

This is the core dump log from the serial console, and is stored in coredump.log:

这是来自串行控制台的核心转储日志,存储在 coredump. log 中:

Booting from ROM..*** Booting Zephyr OS build zephyr-v2.3.0-1840-g7bba91944a63  ***
Hello World! qemu_x86
E: Page fault at address 0x0 (error code 0x2)
E: Linear address not present in page tables
E:   PDE: 0x0000000000115827 Writable, User, Execute Enabled
E:   PTE: Non-present
E: EAX: 0x00000000, EBX: 0x00000000, ECX: 0x00119d74, EDX: 0x000003f8
E: ESI: 0x00000000, EDI: 0x00101aa7, EBP: 0x00119d10, ESP: 0x00119d00
E: EFLAGS: 0x00000206 CS: 0x0008 CR3: 0x00119000
E: call trace:
E: EIP: 0x00100459
E:      0x00100477 (0x0)
E:      0x00100492 (0x0)
E:      0x001004c8 (0x0)
E:      0x00105465 (0x105465)
E:      0x00101abe (0x0)
E: >>> ZEPHYR FATAL ERROR 0: CPU exception on CPU 0
E: Current thread: 0x00119080 (unknown)
E: #CD:BEGIN#
E: #CD:5a4501000100050000000000
E: #CD:4101003800
E: #CD:0e0000000200000000000000749d1100f803000000000000009d1100109d1100
E: #CD:00000000a71a100059041000060200000800000000901100
E: #CD:4d010080901100e0901100
E: #CD:0100000000000000000000000180000000000000000000000000000000000000
E: #CD:00000000000000000000000000000000e364100000000000000000004c9c1100
E: #CD:000000000000000000000000b49911000004000000000000fc03000000000000
E: #CD:4d0100b4991100b49d1100
E: #CD:f8030000020000000200000002000000f8030000fd03000a02000000dc9e1100
E: #CD:149a1160fd03000002000000dc9e1100249a110087201000049f11000a000000
E: #CD:349a11000a4f1000049f11000a9e1100449a11000a8b10000200000002000000
E: #CD:449a1100388b1000049f11000a000000549a1100ad201000049f11000a000000
E: #CD:749a11000a201000049f11000a000000649a11000a201000049f11000a000000
E: #CD:749a1100e8201000049f11000a000000949a1100890b10000a0000000a000000
E: #CD:a49a1100890b10000a0000000a000000f8030000189b11000200000002000000
E: #CD:f49a1100289b11000a000000189b1100049b11009b0710000a000000289b1100
E: #CD:f49a110087201000049f110045000000f49a1100509011000a00000020901100
E: #CD:f49a110060901100049f1100ffffffff0000000000000000049f1100ffffffff
E: #CD:0000000000000000630b1000189b1100349b1100af0b1000630b1000289b1100
E: #CD:55891000789b11000000000020901100549b1100480000004a891000609b1100
E: #CD:649b1100d00b10004a891000709b110000000000609b11000a00000000000000
E: #CD:849b1100709b11004a89100000000000949b1100794a10000000000058901100
E: #CD:20901100c34a10000a00001734020000d001000000000000d49b110038000000
E: #CD:c49b110078481000b49911000004000000000000000000000c9c11000c9c1100
E: #CD:149c110000000000d49b110038000000f49b1100da481000b499110000040000
E: #CD:0e0000000200000000000000744d0100b4991100b49d1100009d1100109d1100
E: #CD:149c110099471000b4991100000400000800000000901100ad861000409c1100
E: #CD:349c1100e94710008090110000000000349c1100b64710008086100045000000
E: #CD:849c11002d53100000000000d09c11008090110020861000f5ffffff8c9c1100
E: #CD:000000000000000000000000a71a1000a49c1100020200008090110000000000
E: #CD:a49c1100020200000800000000000000a49c11001937100000000000d09c1100
E: #CD:0c9d0000bc9c0000b49d1100b4991100c49c1100ae37100000000000d09c1100
E: #CD:0800000000000000c888100000000000109d11005d031000d09c1100009d1100
E: #CD:109d11000000000000000000a71a1000f803000000000000749d110002000000
E: #CD:5904100008000000060200000e0000000202000002020000000000002c9d1100
E: #CD:7704100000000000d00b1000c9881000549d110000000000489d110092041000
E: #CD:00000000689d1100549d11000000000000000000689d1100c804100000000000
E: #CD:c0881000000000007c9d110000000000749d11007c9d11006554100065541000
E: #CD:00000000000000009c9d1100be1a100000000000000000000000000038041000
E: #CD:08000000020200000000000000000000f4531000000000000000000000000000
E: #CD:END#
E: Halting system
  1. Run the core dump serial log converter:

    运行核心转储串行日志转换器:

    ./scripts/coredump/coredump_serial_log_parser.py coredump.log coredump.bin
    
  2. Start the custom GDB server:

    启动自定义 GDB 服务器:

    ./scripts/coredump/coredump_gdbserver.py build/zephyr/zephyr.elf coredump.bin
    
  3. Start GDB:

    启动 GDB:

    <path to SDK>/x86_64-zephyr-elf/bin/x86_64-zephyr-elf-gdb build/zephyr/zephyr.elf
    
  4. Inside GDB, connect to the GDB server via port 1234:

    在 GDB 内部,通过端口1234连接到 GDB 服务器:

    (gdb) target remote localhost:1234
    
  5. Examine the CPU registers:

    检查 CPU 寄存器:

    (gdb) info registers
    

    Output from GDB:

    eax            0x0                 0
    ecx            0x119d74            1154420
    edx            0x3f8               1016
    ebx            0x0                 0
    esp            0x119d00            0x119d00 <z_main_stack+844>
    ebp            0x119d10            0x119d10 <z_main_stack+860>
    esi            0x0                 0
    edi            0x101aa7            1055399
    eip            0x100459            0x100459 <func_3+16>
    eflags         0x206               [ PF IF ]
    cs             0x8                 8
    ss             <unavailable>
    ds             <unavailable>
    es             <unavailable>
    fs             <unavailable>
    gs             <unavailable>
    
  6. Examine the backtrace:

    检查回溯:

    (gdb) bt
    

    Output from GDB:

    #0  0x00100459 in func_3 (addr=0x0) at zephyr/rtos/zephyr/samples/hello_world/src/main.c:14
    #1  0x00100477 in func_2 (addr=0x0) at zephyr/rtos/zephyr/samples/hello_world/src/main.c:21
    #2  0x00100492 in func_1 (addr=0x0) at zephyr/rtos/zephyr/samples/hello_world/src/main.c:28
    #3  0x001004c8 in main() at zephyr/rtos/zephyr/samples/hello_world/src/main.c:42
    

File Format 文件格式

The core dump binary file consists of one file header, one architecture-specific block, and multiple memory blocks. All numbers in the headers below are little endian.

核心转储二进制文件由一个文件头、一个特定于体系结构的块和多个内存块组成。下面标题中的所有数字都是小尾数。

File Header 文件头

The file header consists of the following fields:

文件头由以下字段组成:

Field Data Type Description
ID char[2] Z, E as identifier of file.作为文件的标识符。
Header version uint16_t Identify the version of the header. This needs to be incremented whenever the header struct is modified. This allows parser to reject older header versions so it will not incorrectly parse the header.
ID版本头。每当修改头结构时,这个值都需要增加。这允许解析器拒绝较旧的头文件版本,这样它就不会错误地解析头文件。
Target code uint16_t Indicate which target (e.g. architecture or SoC) so the parser can instantiate the correct register block parser.
指示哪个目标(例如架构或 SoC) ,以便解析器可以实例化正确的寄存器块解析器。
Pointer size ‘uint8_t’ Size of uintptr_t in power of 2. (e.g. 5 for 32-bit, 6 for 64-bit). This is needed to accommodate 32-bit and 64-bit target in parsing the memory block addresses.
uintptr_t 的大小(以2为幂)。(例如,32位为5,64位为6)。在解析内存块地址时,需要这样来适应32位和64位目标。
Flags uint8_t  
Fatal error reason
致命错误原因
unsigned int Reason for the fatal error, as the same in enum k_fatal_error_reason defined in include/zephyr/fatal.h
导致致命错误的原因,与 include/zephyr/fatal.h 中定义的 enum k_fatal_error_reason 中的原因相同

Architecture-specific Block 特定于体系结构的块

The architecture-specific block contains the byte stream of data specific to the target architecture (e.g. CPU registers)

特定于体系结构的块包含特定于目标体系结构的数据的字节流(例如 CPU 寄存器)

Field Data Type Description
ID char A to indicate this is a architecture-specific block.
A 表示这是特定于体系结构的块。
Header version uint16_t Identify the version of this block. To be interpreted by the target architecture specific block parser.
识别此块的版本。由目标体系结构特定的块解析器解释。
Number of bytes字节数 uint16_t Number of bytes following the header which contains the byte stream for target data. The format of the byte stream is specific to the target and is only being parsed by the target parser.
包含目标数据的字节流的标头后面的字节数。字节流的格式特定于目标,只能由目标解析器解析。
Register byte stream uint8_t[] Contains target architecture specific data.
包含特定于目标体系结构的数据。

Memory Block 内存块

The memory block contains the start and end addresses and the data within the memory region.

内存块包含开始和结束地址以及内存区域内的数据。

Field Data Type Description
ID char M to indicate this is a memory block.
M 表示这是一个内存块。
Header version uint16_t Identify the version of the header. This needs to be incremented whenever the header struct is modified. This allows parser to reject older header versions so it will not incorrectly parse the header.
头的ID版本。每当修改头结构时,这个值都需要增加。这允许解析器拒绝较旧的头文件版本,这样它就不会错误地解析头文件。
Start address uintptr_t The start address of the memory region.内存区域的起始地址。
End address uintptr_t The end address of the memory region.内存区域的结束地址。
Memory byte stream uint8_t[] Contains the memory content between the start and end addresses.包含起始地址和结束地址之间的内存内容。

Adding New Target 添加新目标

The architecture-specific block is target specific and requires new dumping routine and parser for new targets. To add a new target, the following needs to be done:

特定于体系结构的块是特定于目标的,并且需要针对新目标的新转储例程和解析器。要添加一个新目标,需要完成以下工作:

  1. Add a new target code to the enum coredump_tgt_code in include/zephyr/debug/coredump.h.

    在 include/zephyr/debug/coredump.h 中向 enum coredump_tgt_code 添加一个新的目标代码。

  2. Implement arch_coredump_tgt_code_get() simply to return the newly introduced target code.

    实现 arch_coredump_tgt_code_get()只是为了返回新引入的目标代码。

  3. Implement arch_coredump_info_dump() to construct a target architecture block and call coredump_buffer_output() to output the block to core dump backend.

    实现 arch_coredump_info_dump()构造一个目标体系结构块,并调用 coredump_buffer_output()将该块输出到 core dump 后端。

  4. Add a parser to the core dump GDB stub scripts under scripts/coredump/gdbstubs/

    将一个解析器添加到scripts/coredump/gdbstubs/下的核心转储 GDB 桩脚本

    1. Extends the gdbstubs.gdbstub.GdbStub class.

      扩展 gdbstubs.gdbstub.GdbStub 类。

    2. During __init__, store the GDB signal corresponding to the exception reason in self.gdb_signal.

      __init__ 期间,将对应于异常原因的 GDB 信号存储在 self.gdb_signal 中。

    3. Parse the architecture-specific block from self.logfile.get_arch_data(). This needs to match the format as implemented in step 3 (inside arch_coredump_info_dump()).

      解析 self.logfile.get_arch_data()中特定于体系结构的代码块,这需要与步骤3 arch_coredump_info_dump()中实现的格式相匹配。

    4. Implement the abstract method handle_register_group_read_packet where it returns the register group as GDB expected. Refer to GDB’s code and documentation on what it is expecting for the new target.

      实现抽象方法 handle_register_group_read_packet,在这里它按照 GDB 的期望返回寄存器组。请参考 GDB 的代码和文档,了解它对新目标的期望。

    5. Optionally implement handle_register_single_read_packet for registers not covered in the g packet.

      对于 g 包中没有涉及到的寄存器,可以选择实现 handle_register_single_read_packet

  5. Extend get_gdbstub() in scripts/coredump/gdbstubs/init.py to return the newly implemented GDB stub.

    scripts/coredump/gdbstubs/__init__.py 中扩展 get_gdbstub()以返回新实现的 GDB 桩。

GDB stub GDB 桩

Overview 概述

The gdbstub feature provides an implementation of the GDB Remote Serial Protocol (RSP) that allows you to remotely debug Zephyr using GDB.

Gdbstub 特性提供了 GDB 远程串行协议(RSP)的实现,允许您使用 GDB 远程调试 Zephyr。

The protocol supports different connection types: serial, UDP/IP and TCP/IP. Zephyr currently supports only serial device communication.

该协议支持不同的连接类型: 串行、 UDP/IP 和 TCP/IP。 Zephyr 目前只支持串行设备通信。

The GDB program acts as the client while Zephyr acts as the server. When this feature is enabled, Zephyr stops its execution after gdb_init() starts gdbstub service and waits for a GDB connection. Once a connection is established it is possible to synchronously interact with Zephyr. Note that currently it is not possible to asynchronously send commands to the target.

GDB 程序充当客户端,而 Zephyr 充当服务器。启用此特性后,Zephyr 在 gdb_init()启动 gdbstub 服务并等待 GDB 连接之后停止执行。一旦建立了连接,就可以与 Zephyr 进行同步交互。请注意,目前不可能异步地向目标发送命令。

Features 特征

The following features are supported:

支持以下功能:

  • Add and remove breakpoints

    添加和删除断点

  • Continue and step the target

    继续前进,跨过目标

  • Print backtrace

    打印回溯

  • Read or write general registers

    读写通用寄存器

  • Read or write the memory

    读取或写入记忆

Enabling GDB Stub 启用 GDB 桩

GDB stub can be enabled with the CONFIG_GDBSTUB option.

可以使用 CONFIG_GDBSTUB 选项启用 GDB 桩。

Using Serial Backend 使用串行后端

The serial backend for GDB stub can be enabled with the CONFIG_GDBSTUB_SERIAL_BACKEND option.

GDB 桩的串行后端可以通过 CONFIG_GDBSTUB_SERIAL_BACKEND 选项启用。

Since serial backend utilizes UART devices to send and receive GDB commands,

由于串行后端使用 UART 设备来发送和接收 GDB 命令,

  • If there are spare UART devices on the board, set CONFIG_GDBSTUB_SERIAL_BACKEND_NAME to the spare UART device so that printk() and log messages are not being printed to the same UART device used for GDB.

    如果电路板上有备用的 UART 设备,将 CONFIG_GDBSTUB_SERIAL_BACKEND_NAME 设置为备用的 UART 设备,这样 printk()和 log 消息就不会被打印到用于 GDB 的同一个 UART 设备上。

  • For boards with only one UART device, printk() and logging must be disabled if they are also using the same UART device for output. GDB related messages may interleave with log messages which may have unintended consequences. Usually this can be done by disabling CONFIG_PRINTK and CONFIG_LOG.

    对于只有一个 UART 设备的电路板,如果它们也使用相同的 UART 设备进行输出,那么 printk()和日志记录必须被禁用。与GDB相关的消息可能与有意外后果的日志消息交织在一起。通常这可以通过禁用 CCONFIG_PRINTK 和 CONFIG_LOG 来实现。

Debugging 正在调试

Using Serial Backend 使用串行后端

  1. Build with GDB stub and serial backend enabled.

    启用 GDB 桩和串行后端构建

  2. Flash built image onto board and reset the board.

    把镜像烧录到开发板上,并重置板。

    • Execution should now be paused at gdb_init().

      执行应该在 gdb_init()处暂停。

  3. Execute GDB on development machine and connect to the GDB stub.

    在开发机器上执行 GDB 并连接到 GDB 桩。

    target remote <serial device>
    

    For example,

    比如说,

    target remote /dev/ttyUSB1
    
  4. GDB commands can be used to start debugging.

    GDB 命令可用于启动调试。

Example 例子

This is an example using samples/subsys/debug/gdbstub to demonstrate how GDB stub works.

这是一个使用 samples/subsys/debug/gdbstub 演示 GDB stub 如何工作的示例。

  1. Open two terminal windows.

    打开两个终端窗口。

  2. On the first terminal, build and run the sample:

    在第一个终端上,构建并运行示例:

    # From the root of the zephyr repository
    west build -b qemu_x86 samples/subsys/debug/gdbstub
    west build -t run
    
  3. On the second terminal, start GDB:

    在第二个终端,启动GDB:

    <SDK install directory>/x86_64-zephyr-elf/bin/x86_64-zephyr-elf-gdb
    
    1. Tell GDB where to look for the built ELF file:

      告诉 GDB 在哪里查找构建的 ELF 文件:

      (gdb) file <build directory>/zephyr/zephyr.elf
      

      Response from GDB:

      GDB回应:

      Reading symbols from <build directory>/zephyr/zephyr.elf...
      
    2. Tell GDB to connect to the server:

      告诉 GDB 连接到服务器:

      (gdb) target remote localhost:5678
      

      Note that QEMU is setup to redirect the serial used for GDB stub in the Zephyr image to a networking port. Hence the connection to localhost, port 5678.

      注意,设置 QEMU 是为了将用于 Zephyr 映像中的 GDB 桩的串行重定向到网络端口。因此连接到 localhost,端口5678。

      Response from GDB:

      GDB回应:

      Remote debugging using :5678
      arch_gdb_init() at <ZEPHYR_BASE>/arch/x86/core/ia32/gdbstub.c:232
      232     }
      

      GDB also shows where the code execution is stopped. In this case, it is at arch/x86/core/ia32/gdbstub.c, line 232.

      GDB 还显示了代码执行停止的位置,在本例中,它位于 arch/x86/core/ia32/gdbstub.c,第232行。

    3. Use command bt or backtrace to show the backtrace of stack frames.

      使用 bt 或 backtrace 命令显示堆栈帧的回溯。

      (gdb) bt
      #0  arch_gdb_init() at <ZEPHYR_BASE>/arch/x86/core/ia32/gdbstub.c:232
      #1  0x00105068 in gdb_init (arg=0x0) at <ZEPHYR_BASE>/subsys/debug/gdbstub.c:833
      #2  0x00109d6f in z_sys_init_run_level (level=0x1) at <ZEPHYR_BASE>/kernel/device.c:72
      #3  0x0010a40b in z_cstart() at <ZEPHYR_BASE>/kernel/init.c:423
      #4  0x00105383 in z_x86_prep_c (arg=0x9500) at <ZEPHYR_BASE>/arch/x86/core/prep_c.c:58
      #5  0x001000a9 in __csSet() at <ZEPHYR_BASE>/arch/x86/core/ia32/crt0.S:273
      
    4. Use command list to show the source code and surroundings where code execution is stopped.

      使用命令列表显示代码执行停止的源代码和环境。

      (gdb) list
      227     }
      228
      229     void arch_gdb_init(void)
      230     {
      231             __asm__ volatile ("int3");
      232     }
      233
      234     /* Hook current IDT. */
      235     _EXCEPTION_CONNECT_NOCODE(z_gdb_debug_isr, IV_DEBUG, 3);
      236     _EXCEPTION_CONNECT_NOCODE(z_gdb_break_isr, IV_BREAKPOINT, 3);
      
    5. Use command s or step to step through program until it reaches a different source line. Now that it finished executing arch_gdb_init() and is continuing in gdb_init().

      使用命令或步骤逐步执行程序,直到程序到达不同的源代码行。现在它已经完成了 arch_gdb_init()的执行,并继续在 gdb_init()中执行。

      (gdb) s
      gdb_init (arg=0x0) at /home/dleung5/zephyr/rtos/zephyr/subsys/debug/gdbstub.c:834
      834     return 0;
      
      (gdb) list
      829                     LOG_ERR("Could not initialize gdbstub backend.");
      830                     return -1;
      831             }
      832
      833             arch_gdb_init();
      834             return 0;
      835     }
      836
      837     #ifdef CONFIG_XTENSA
      838     /*
      
    6. Use command br or break to setup a breakpoint. This example sets up a breakpoint at main(), and let code execution continue without any intervention using command c (or continue).

      使用命令 br 或 break 设置断点。此示例在 main()上设置了一个断点,并允许代码执行在没有任何干预的情况下使用命令 c (或继续)继续。

      (gdb) break main
      Breakpoint 1 at 0x1005a9: file <ZEPHYR_BASE>/samples/subsys/debug/gdbstub/src/main.c, line 32.
      (gdb) continue
      Continuing.
      

      Once code execution reaches main(), execution will be stopped and GDB prompt returns.

      一旦代码执行到 main() ,将停止执行并返回 GDB 提示符。

      Breakpoint 1, main() at <ZEPHYR_BASE>/samples/subsys/debug/gdbstub/src/main.c:32
      32           ret = test();
      

      Now GDB is waiting at the beginning of main():

      现在 GDB 正在 main()的开头等待:

      (gdb) list
      27
      28     void main(void)
      29     {
      30             int ret;
      31
      32             ret = test();
      33             printk("%d\n", ret);
      34     }
      35
      36     K_THREAD_DEFINE(thread, STACKSIZE, thread_entry, NULL, NULL, NULL,
      
    7. To examine the value of ret, the command p or print can be used.

      要检查 ret 的值,可以使用命令 p 或 print。

      (gdb) p ret
      $1 = 0x11318c
      

      Since ret has not been assigned a value yet, what it contains is simply a random value.

      由于 ret 还没有被赋值,所以它包含的只是一个随机值。

    8. If step (s or step) is used here, it will continue execution until printk() is reached, thus skipping the interior of test(). To examine code execution inside test(), a breakpoint can be set for test(), or simply using si (or stepi) to execute one machine instruction, where it has the side effect of going into the function.

      如果在这里使用 step (s 或 step) ,它将继续执行,直到到达 printk() ,从而跳过 test()的内部。要检查 test()内部的代码执行情况,可以为 test()设置断点,或者仅仅使用 si (或 step i)执行一个指令,这样会产生进入函数的副作用。

      (gdb) si
      test() at <ZEPHYR_BASE>/samples/subsys/debug/gdbstub/src/main.c:13
      13     {
      (gdb) list
      8      #include <zephyr/sys/printk.h>
      9
      10     #define STACKSIZE 512
      11
      12     static int test(void)
      13     {
      14             int a;
      15             int b;
      16
      17             a = 10;
      
    9. Here, step can be used to go through all code inside test() until it returns. Or the command finish can be used to continue execution without intervention until the function returns.

      在这里,可以使用 step 遍历 test()中的所有代码,直到它返回。或者可以使用命令 Finish 继续执行,而不进行干预,直到函数返回。

      (gdb) finish
      Run till exit from #0  test() at <ZEPHYR_BASE>/samples/subsys/debug/gdbstub/src/main.c:13
      0x001005ae in main() at <ZEPHYR_BASE>/samples/subsys/debug/gdbstub/src/main.c:32
      32             ret = test();
      Value returned is $2 = 0x1e
      

      And now, execution is back to main().

      现在,执行回到 main()。

    10. Examine ret again which should have the return value from test(). Sometimes, the assignment is not done until another step is issued, as in this case. This is due to the assignment code is done after returning from function. The assignment code is generated by the toolchain as machine instructions which are not visible when viewing the corresponding C source file.

      再次检查 ret,它应该具有 test()的返回值。有时,在发出另一个步骤之前,不会完成赋值,如本例所示。这是由于赋值代码是在从函数返回之后完成的。分配代码由工具链作为机器指令生成,当查看相应的 C 源文件时,这些机器指令是不可见的。

      (gdb) p ret
      $3 = 0x11318c
      (gdb) s
      33              printk("%d\n", ret);
      (gdb) p ret
      $4 = 0x1e
      
    11. If continue is issued here, code execution will continue indefinitely as there are no breakpoints to further stop execution. Breaking execution in GDB via Ctrl-C does not currently work as the GDB stub does not support this functionality (yet).

      如果在这里发出 continue,代码执行将无限期地继续下去,因为没有断点可以进一步停止执行。通过 Ctrl-C 中断 GDB 中的执行目前还不能工作,因为 GDB 桩还不支持这个功能。

MCUmgr

Overview 概述

The management subsystem allows remote management of Zephyr-enabled devices. The following management operations are available:

管理子系统允许对支持 Zephyr 的设备进行远程管理:

  • Image management

    镜像管理

  • File System management

    文件系统管理

  • Log management (currently disabled)

    日志管理(当前禁用)

  • OS management

    操作系统管理

  • Shell management

over the following transports:

  • BLE (Bluetooth Low Energy)

    (低耗电蓝牙)

  • Serial (UART)

    串行(UART)

  • UDP over IP

    IP 上的 UDP

The management subsystem is based on the Simple Management Protocol (SMP) provided by MCUmgr, an open source project that provides a management subsystem that is portable across multiple real-time operating systems.

管理子系统基于 MCUmgr 提供的简单管理协议(Simple Management Protocol,SMP) ,MCUmgr 是一个开源项目,它提供了一个跨多个实时操作系统可移植的管理子系统。

The management subsystem is split in two different locations in the Zephyr tree:

管理子系统分为 Zephyr 树中的两个不同位置:

  • zephyrproject-rtos/mcumgr repo contains a clean import of the MCUmgr project

    Zephyrproject-rtos/mCumgr repo 包含 MCUmgr 项目的干净导入

  • subsys/mgmt/ contains the Zephyr-specific bindings to MCUmgr

    Subsys/mgmt/包含特定于 Zephyr 的 MCUmgr 绑定

Additionally there is a sample that provides management functionality over BLE and serial.

此外,还有一个示例提供了针对 ABLE 和 Series 的管理功能。

Command-line Tool 命令行工具

MCUmgr provides a command-line tool, mcumgr, for managing remote devices. The tool is written in the Go programming language.

MCUmgr 提供了一个用于管理远程设备的命令行工具 mcumgr,该工具是用 Go 编程语言编写的。

To install the tool:

安装工具:

go install github.com/apache/mynewt-mcumgr-cli/mcumgr@latest

Configuring the transport 配置传输

There are two command-line options that are responsible for setting and configuring the transport layer to use when communicating with managed device:

有两个命令行选项负责设置和配置与托管设备通信时要使用的传输层:

  • --conntype is used to choose the transport used, and

    --conntype 用于选择所使用的传输,并且

  • --connstring is used to pass a comma separated list of options in the key=value format, where each valid key depends on the particular conntype.

    --connstring 用于以 key=value 格式传递逗号分隔的选项列表,其中每个有效的键取决于特定的 conntype。

Valid transports for --conntype are serial, ble and udp. Each transport expects a different set of key/value options:

连接类型的有效传输包括串行、 ble 和 udp。每个传输都需要一组不同的键/值选项:

serial 串口

--connstring accepts the following key values:

--connstring 接受以下键值:

key value
dev the device name for the OS mcumgr is running on (eg, /dev/ttyUSB0, /dev/tty.usbserial, COM1, etc).
正在操作系统中运行的 mcumgr 的设备名称(例如/dev/ttyUSB0、/dev/tty.usbSeries、 COM1等)。
baud the communication speed; must match the baudrate of the server.
通信速度; 必须与服务器的波特率相匹配。
mtu aka Maximum Transmission Unit, the maximum protocol packet size.
也就是最大传输单元最大协议数据包大小。

BLE

key value
ctlr_name an OS specific string for the controller name.
一个操作系统特定的字符串,代表控制器名称串。
own_addr_type can be one of public, random, rpa_pub, rpa_rnd, where random is the default.值。
peer_name the name the peer BLE device advertises, this should match the configuration specified with CONFIG_BT_DEVICE_NAME.
对端的 ABLE 设备公告的名称,这应该匹配用 CONFIG_BT_DEVICE_NAME 指定的配置。
peer_id the peer BLE device address or UUID. Only required when peer_name was not given. The format depends on the OS where mcumgr is run, it is a 6 bytes hexadecimal string separated by colons on Linux, or a 128-bit UUID on macOS.
对等容错设备地址或 UUID。只有在没有给出 peer_name 时才需要。格式取决于运行 mCumgr 的操作系统,它是一个6字节的十六进制字符串,在 Linux 上用冒号分隔,在 macOS 上是一个128位的 UUID。
conn_timeout a float number representing the connection timeout in seconds.
表示以秒为单位的连接超时的浮点数。

UDP

key value
addr can be a DNS name (if it can be resolved to the device IP), IPv4 addr (app must be built with CONFIG_MCUMGR_SMP_UDP_IPV4), or IPv6 addr (app must be built with CONFIG_MCUMGR_SMP_UDP_IPV6)
可以是 DNS 名称(如果可以解析为设备 IP)、 IPv4 addr (应用程序必须使用 CONFIG_MCUMGR_SMP_UDP_IPV4构建)或 IPv6 addr (应用程序必须使用 CONFIG_MCUMGR_SMP_UDP_IPV6构建)
port any valid UDP port.任何有效的 UDP 端口。

Saving the connection config 保存连接配置

The transport configuration can be managed with the conn sub-command and later used with --conn (or -c) parameter to skip typing both --conntype and --connstring. For example a new config for a serial device that would require typing mcumgr --conntype serial --connstring dev=/dev/ttyACM0,baud=115200,mtu=512 can be saved with:

传输配置可以使用 con 子命令进行管理,稍后可以使用--conn (或-c)参数来跳过键入--conntype--connstring 的步骤。例如,一个串行设备的新配置需要输入 mcumgr --conntype serial --connstring dev=/dev/ttyACM0,baud=115200,mtu=512,可以用以下命令保存:

mcumgr conn add acm0 type="serial" connstring="dev=/dev/ttyACM0,baud=115200,mtu=512"

Accessing this port can now be done with:

现在可以通过以下方式访问这个端口:

mcumgr -c acm0

General options 一般选项

Some options work for every mcumgr command and might be helpful to debug and fix issues with the communication, among them the following deserve special mention:

一些选项适用于每个 mcumgr 命令,可能有助于调试和修复通信问题,其中以下选项值得特别提及:

选项 说明
-l <log-level> Configures the log level, which can be one of critical, error, warn, info or debug, from less to most verbose. When there are communication issues, -lDEBUG might be useful to dump the packets for later inspection.
配置日志级别,可以是关键、错误、警告、信息或调试,从较少到最详细。当出现通信问题时,-lDEBUG 可能有助于转储数据包以供以后检查。
-t <timeout> Changes the timeout waiting for a response from the default of 10s to a given value. Some commands might take a long time of processing, eg, the erase before an image upload, and might need incrementing the timeout to a larger value.
将等待响应的超时从默认值10更改为给定值。有些命令可能需要很长时间的处理,例如,上传镜像之前的擦除,并且可能需要将超时值增加到一个更大的值。
-r <tries> Changes the number of retries on timeout from the default of 1 to a given value.
将超时重试次数从默认值1更改为给定值。

List of Commands 命令列表

Not all commands defined by mcumgr (and SMP protocol) are currently supported on Zephyr. The ones that are supported are described in the following table:

Zephyr 当前并不支持 mcumgr (和 SMP 协议)定义的所有命令。下表说明了所支持的方法:

Running mcumgr with no parameters, or -h will display the list of commands.

在不带参数的情况下运行 mcumgr,或者-h 将显示命令列表。

Command命令 Description描述
echo Send data to a device and display the echoed back data. This command is part of the OS group, which must be enabled by setting CONFIG_MCUMGR_CMD_OS_MGMT. The echo command itself can be enabled by setting CONFIG_OS_MGMT_ECHO.
向设备发送数据并显示回显数据。该命令是 OS 组的一部分,必须通过设置 CONFIG_MCUMGR_CMD_OS_MGMT 来启用该命令。通过设置 CONFIG_OS_MGMT_ECHO,可以启用 echo 命令本身。
fs Access files on a device. More info in Filesystem Management.
访问设备上的文件。文件系统管理中的更多信息。
image Manage images on a device. More info in Image Management.
管理设备上的镜像。更多镜像管理信息。
reset Perform a soft reset of a device. This command is part of the OS group, which must be enabled by setting CONFIG_MCUMGR_CMD_OS_MGMT. The reset command itself is always enabled and the time taken for a reset to happen can be set with CONFIG_OS_MGMT_RESET_MS (in ms).
执行设备的软复位。该命令是 OS 组的一部分,必须通过设置 CONFIG_MCUMGR_CMD_OS_MGMT 来启用该命令。重置命令本身始终是启用的,重置发生所需的时间可以用 CONFIG_OS_MGMT_RESET_MS (单位为 ms)设置。
shell Execute a command in the remote shell. This option is disabled by default and can be enabled with CONFIG_MCUMGR_CMD_SHELL_MGMT = y. To know more about the shell in Zephyr check Shell.
在远程 shell 中执行命令。这个选项在默认情况下是禁用的,可以通过 CONFIG_MCUMGR_CMD_SHELL_MGMT = y 来启用。要了解泽弗的 Shell,请查看 Shell。
stat Read statistics from a device. More info in Statistics Management.
从设备上阅读统计数据。更多信息请参阅统计管理。
taskstat Read task statistics from a device. This command is part of the OS group, which must be enabled by setting CONFIG_MCUMGR_CMD_OS_MGMT. The taskstat command itself can be enabled by setting CONFIG_OS_MGMT_TASKSTAT. CONFIG_THREAD_MONITOR also needs to be enabled otherwise a -8 (MGMT_ERR_ENOTSUP) will be returned.
从设备读取任务统计信息。该命令是 OS 组的一部分,必须通过设置 CONFIG_MCUMGR_CMD_OS_MGMT 来启用该命令。通过设置 CONFIG_OS_MGMT_TASKSTAT,可以启用 taskstat 命令本身。CONFIG_THREAD_MONITOR 也需要启用,否则将返回 -8(MGMT_ERR_ENOTSUP)。

taskstat has a few options that might require tweaking. The CONFIG_THREAD_NAME must be set to display the task names, otherwise the priority is displayed. Since the taskstat packets are large, they might need increasing the CONFIG_MCUMGR_BUF_SIZE option.

Taskstat 有一些可能需要调整的选项。必须将 CONFIG_THREAD_NAME 设置为显示任务名称,否则将显示优先级。由于 taskstat 数据包很大,它们可能需要增加 CONFIG_MCUMGR_BUF_SIZE 选项。

Warning 警告

To display the correct stack size in the taskstat command, the CONFIG_THREAD_STACK_INFO option must be set. To display the correct stack usage in the taskstat command, both CONFIG_THREAD_STACK_INFO and CONFIG_INIT_STACKS options must be set.

要在 taskstat 命令中显示正确的堆栈大小,必须设置 CONFIG_THREAD_STACK_INFO 选项。要在 taskstat 命令中显示正确的堆栈用法,必须设置 CONFIG_THREAD_STACK_INFO 和 CONFIG_INIT_STACKS 选项。

On boards where a J-Link OB is present which has both CDC and MSC (virtual Mass Storage Device, also known as drag-and-drop) support, the MSD functionality can prevent mcumgr commands over the CDC UART port from working due to how USB endpoints are configured in the J-Link firmware (for example on the Nordic nrf52840dk) because of limiting the maximum packet size (most likely to occur when using image management commands for updating firmware). This issue can be resolved by disabling MSD functionality on the J-Link device, follow the instructions on Disabling the Mass Storage Device functionality to disable MSD support.

在同时具有 CDC 和 MSC (虚拟大容量存储设备,也称为拖放)支持的 J-Link OB 存在的板上,MSD 功能可以阻止 CDC UART 端口上的 mcumgr 命令的工作,因为如何在 J-Link 固件中配置 USB 端点(例如在北欧 nrf52840dk 上) ,因为限制了最大数据包大小(最有可能发生在使用镜像管理命令更新固件时)。这个问题可以通过在 J-Link 设备上禁用 MSD 功能来解决,按照禁用大容量存储设备功能的说明来禁用 MSD 支持。

Image Management 镜像管理

The image management provided by mcumgr is based on the image format defined by MCUboot. For more details on the internals see MCUboot design and Signing Binaries.

mcumgr 提供的镜像管理基于 MCUboot 定义的镜像格式。有关内部组件的详细信息,请参阅 MCUboot 设计和签名二进制文件。

To list available images in a device:

列出设备中可用的镜像:

mcumgr <connection-options> image list

This should result in an output similar to this:

这应产生类似的结果:

$ mcumgr -c acm0 image list
Images:
  image=0 slot=0
    version: 1.0.0
    bootable: true
    flags: active confirmed
    hash: 86dca73a3439112b310b5e033d811ec2df728d2264265f2046fced5a9ed00cc7
Split status: N/A (0)

Where image is the number of the image pair in a multi-image system, and slot is the number of the slot where the image is stored, 0 for primary and 1 for secondary. This image being active and confirmed means it will run again on next reset. Also relevant is the hash, which is used by other commands to refer to this specific image when performing operations.

其中 image 是多镜像系统中镜像对的数目,槽是存储镜像的槽的数目,0表示主镜像,1表示次镜像。此镜像被激活和确认意味着它将再次运行在下一次复位。同样相关的还有 hash,它被其他命令用来在执行操作时引用这个特定的镜像。

An image can be manually erased using:

可以使用以下方法手动擦除镜像:

mcumgr <connection-options> image erase

The behavior of erase is defined by the server (mcumgr in the device). The current implementation is limited to erasing the image in the secondary partition.

擦除的行为由服务器定义(设备中的 mCumgr)。当前的实现仅限于擦除辅助分区中的映像。

To upload a new image:

上传新镜像:

mcumgr <connection-options> image upload [-n] [-e] [-u] [-w] <signed-bin>
  • -n: This option allows uploading a new image to a specific set of images in a multi-image system, and is currently only supported by MCUboot when the CONFIG_MCUBOOT_SERIAL option is enabled.

    n: 这个选项允许将一个新镜像上传到多镜像系统中的一组特定镜像中,目前只有当启用 CONFIG_MCUBOOT_SERIAL 选项时,MCUboot 才支持这个选项。

  • -e: This option avoids performing a full erase of the partition before starting a new upload.

    -e: 这个选项可以避免在开始新的上传之前完全擦除分区。

Tip 小费

The -e option should always be passed in because the upload command already checks if an erase is required, respecting the CONFIG_IMG_ERASE_PROGRESSIVELY setting.

应该始终传入-e 选项,因为上传命令已经检查是否需要擦除,并遵循 CONFIG_IMG_ERASE_PROGRESSIVELY 设置。

Tip

If the upload command times out while waiting for a response from the device, -t might be used to increase the wait time to something larger than the default of 10s. See general_options.

如果上传命令在等待设备响应时超时,-t 可用于将等待时间增加到大于默认值10的值。请参见 general_options。

Warning 警告

mcumgr does not understand .hex files, when uploading a new image always use the .bin file.

MCumgr 不能理解 .hex 文件,上传新镜像时总是使用 .bin 文件。

  • -u: This option allows upgrading only to newer image version.

    -u: 这个选项只允许升级到较新的镜像版本。

  • -w: This option allows setting the maximum size for the window of outstanding chunks in transit. It is set to 5 by default.

    -w: 这个选项允许设置在传输过程中未完成数据块的窗口的最大大小。默认情况下设置为5。

    Tip

    If the option is set to a value lower than the default one, for example -w 1, less chunks are transmitted on the window, resulting in lower risk of errors. Conversely, setting a value higher than 5 increases risk of errors and may impact performance.

    如果选项被设置为比默认值低的值,例如-w 1,那么在窗口上传输的数据块就会减少,从而降低出错的风险。相反,设置大于5的值会增加出错的风险,并可能影响性能。

After an image upload is finished, a new image list would now have an output like this:

上传完镜像后,新的镜像列表将会有如下输出:

$ mcumgr -c acm0 image upload -e build/zephyr/zephyr.signed.bin
  35.69 KiB / 92.92 KiB [==========>---------------]  38.41% 2.97 KiB/s 00m19

Now listing the images again:

现在再次列出这些镜像:

$ mcumgr -c acm0 image list
Images:
 image=0 slot=0
  version: 1.0.0
  bootable: true
  flags: active confirmed
  hash: 86dca73a3439112b310b5e033d811ec2df728d2264265f2046fced5a9ed00cc7
 image=0 slot=1
  version: 1.1.0
  bootable: true
  flags:
  hash: e8cf0dcef3ec8addee07e8c4d5dc89e64ba3fae46a2c5267fc4efbea4ca0e9f4
Split status: N/A (0)

To test a new upgrade image the test command is used:

要测试一个新的升级映像,使用 test 命令:

mcumgr <connection-options> image test <hash>

This command should mark a test upgrade, which means that after the next reboot the bootloader will execute the upgrade and jump into the new image. If no other image operations are executed on the newly running image, it will revert back to the image that was previously running on the device on the subsequent reset. When a test is requested, flags will be updated with pending to inform that a new image will be run after a reset:

这个命令应该标记一个测试升级,这意味着在下一次重新启动之后,引导加载程序将执行升级并跳转到新映像。如果没有对新运行的映像执行其他映像操作,那么它将在随后的重置中恢复到以前在设备上运行的映像。当请求测试时,标志将更新为挂起状态,以通知在重置后将运行新映像:

$ mcumgr -c acm0 image test e8cf0dcef3ec8addee07e8c4d5dc89e64ba3fae46a2c5267fc4efbea4ca0e9f4
Images:
 image=0 slot=0
  version: 1.0.0
  bootable: true
  flags: active confirmed
  hash: 86dca73a3439112b310b5e033d811ec2df728d2264265f2046fced5a9ed00cc7
 image=0 slot=1
  version: 1.1.0
  bootable: true
  flags: pending
  hash: e8cf0dcef3ec8addee07e8c4d5dc89e64ba3fae46a2c5267fc4efbea4ca0e9f4
Split status: N/A (0)

After a reset the output with change to:

重置后的输出与更改为:

$ mcumgr -c acm0 image list
Images:
 image=0 slot=0
  version: 1.1.0
  bootable: true
  flags: active
  hash: e8cf0dcef3ec8addee07e8c4d5dc89e64ba3fae46a2c5267fc4efbea4ca0e9f4
 image=0 slot=1
  version: 1.0.0
  bootable: true
  flags: confirmed
  hash: 86dca73a3439112b310b5e033d811ec2df728d2264265f2046fced5a9ed00cc7
Split status: N/A (0)

Tip

It’s important to mention that an upgrade only ever happens if the image is valid. The first thing MCUboot does when an upgrade is requested is to validate the image, using the SHA-256 and/or the signature (depending on the configuration). So before uploading an image, one way to be sure it is valid is to run imgtool verify -k <your-signature-key> <your-image>, where -k <your-signature-key can be skipped if no signature validation was enabled.

值得一提的是,只有在镜像有效的情况下才会进行升级。当请求升级时,MCUboot 做的第一件事是使用 SHA-256和/或签名(取决于配置)验证映像。因此,在上传映像之前,确保其有效性的一种方法是运行 imgtool verify -k <your-signature-key> <your-image>,如果没有启用签名验证,则可以跳过-k <your-signature-key

The confirmed flag in the secondary slot tells that after the next reset a revert upgrade will be performed to switch back to the original layout.

第二插槽中的确认标志告诉我们,在下一次重置之后,将执行恢复升级以切换回原始布局。

The command used to confirm that an image is OK and no revert should happen (no hash required) is:

用于确认镜像是正常的,不应该发生还原(不需要散列)的命令是:

mcumgr <connection-options> image confirm [hash]

The confirm command can also be run passing in a hash so that instead of doing a test/revert procedure, the image in the secondary partition is directly upgraded to.

还可以通过散列传递确认命令,这样就不需要执行测试/恢复过程,而是直接将辅助分区中的映像升级到。

Tip

The whole test/revert cycle does not need to be done using only the mcumgr command-line tool. A better alternative is to perform a test and allow the new running image to self-confirm after any checks by calling boot_write_img_confirmed().

整个测试/恢复周期不需要仅使用 mcumgr 命令行工具来完成。一个更好的替代方法是执行一个测试,通过调用 boot_write_img_confirmed(),允许新的运行映像在进行任何检查之后自我确认。

Tip

The maximum size of a chunk communicated between the client and server is set with CONFIG_IMG_MGMT_UL_CHUNK_SIZE. The default is 512 but can be decreased for systems with low amount of RAM down to 128. When this value is changed, the mtu of the port must be smaller than or equal to this value.

客户端和服务器之间通信的块的最大大小由 CONFIG_IMG_MGMT_UL_CHUNK_SIZE 设置。默认值是512,但是对于 RAM 数量低到128的系统可以减少。当更改此值时,端口的 mtu 必须小于或等于此值。

Tip

Building with CONFIG_IMG_MGMT_VERBOSE_ERR enables better error messages when failures happen (but increases the application size).

使用 CONFIG_IMG_MGMT_VERBOSE_ERR 构建可以在发生故障时提供更好的错误消息(但会增加应用程序的大小)。

Statistics Management 统计资料管理

Statistics are used for troubleshooting, maintenance, and usage monitoring; it consists basically of user-defined counters which are tightly connected to mcumgr and can be used to track any information for easy retrieval. The available sub-commands are:

统计信息用于故障排除、维护和使用监视; 它基本上由用户定义的计数器组成,这些计数器与 mcumgr 紧密连接,可用于跟踪任何信息以便于检索。可用的子命令包括:

mcumgr <connection-options> stat list
mcumgr <connection-options> stat <section-name>

Statistics are organized in sections (also called groups), and each section can be individually queried. Defining new statistics sections is done by using macros available under <stats/stats.h>. Each section consists of multiple variables (or counters), all with the same size (16, 32 or 64 bits).

统计信息按部分(也称为组)组织,每个部分都可以单独查询。通过使用 <stats/stats.h> 下可用的宏来定义新的统计信息部分。每个部分由多个变量(或计数器)组成,所有这些变量都具有相同的大小(16、32或64位)。

To create a new section my_stats:

要创建一个新的部分 my_stats:

STATS_SECT_START(my_stats)
  STATS_SECT_ENTRY(my_stat_counter1)
  STATS_SECT_ENTRY(my_stat_counter2)
  STATS_SECT_ENTRY(my_stat_counter3)
STATS_SECT_END;

STATS_SECT_DECL(my_stats) my_stats;

Each entry can be declared with STATS_SECT_ENTRY (or the equivalent STATS_SECT_ENTRY32), STATS_SECT_ENTRY16 or STATS_SECT_ENTRY64. All statistics in a section must be declared with the same size.

每个条目可以用 STATS_SECT_ENTRY (或等效的 STATS_SECT_ENTRY32)、 STATS_SECT_ENTRY16或 STATS_SECT_ENTRY64声明。节中的所有统计信息必须使用相同的大小声明。

The statistics counters can either have names or not, depending on the setting of the CONFIG_STATS_NAMES option. Using names requires an extra declaration step:

根据 CONFIG_STATS_NAMES 选项的设置,统计计数器可以有名称,也可以没有名称。使用名称需要额外的声明步骤:

STATS_NAME_START(my_stats)
  STATS_NAME(my_stats, my_stat_counter1)
  STATS_NAME(my_stats, my_stat_counter2)
  STATS_NAME(my_stats, my_stat_counter3)
STATS_NAME_END(my_stats);

Tip

Disabling CONFIG_STATS_NAMES will free resources. When this option is disabled the STATS_NAME* macros output nothing, so adding them in the code does not increase the binary size.

禁用 CONFIG_STATS_NAMES 将释放资源。禁用此选项时,STATS_NAME* 宏将不输出任何内容,因此将它们添加到代码中不会增加二进制大小。

Tip

CONFIG_STAT_MGMT_MAX_NAME_LEN sets the maximum length of a section name that can can be accepted as parameter for showing the section data, and might require tweaking for long section names.

CONFIG_STAT_MGMT_MAX_NAME_LEN 设置节名的最大长度,该长度可以被接受为显示节数据的参数,并且可能需要对长节名进行调整。

The final steps to use a statistics section is to initialize and register it:

使用统计部分的最后一个步骤是初始化和注册它:

rc = STATS_INIT_AND_REG(my_stats, STATS_SIZE_32, "my_stats");
assert (rc == 0);

In the running code a statistics counter can be incremented by 1 using STATS_INC, by N using STATS_INCN or reset with STATS_CLEAR.

在运行代码中,统计计数器可以使用 STATS_INC 增加1,使用 STATS_INCN 增加 N,或者使用 STATS_CLEAR 重置。

Let’s suppose we want to increment those counters by 1, 2 and 3 every second. To get a list of stats:

假设我们希望这些计数器每秒增加1、2和3。要获得统计数据列表:

$ mcumgr --conn acm0 stat list
stat groups:
  my_stats

To get the current value of the counters in my_stats:

要获取 my_stats 中计数器的当前值:

$ mcumgr --conn acm0 stat my_stats
stat group: my_stats
      13 my_stat_counter1
      26 my_stat_counter2
      39 my_stat_counter3

$ mcumgr --conn acm0 stat my_stats
stat group: my_stats
      16 my_stat_counter1
      32 my_stat_counter2
      48 my_stat_counter3

When CONFIG_STATS_NAMES is disabled the output will look like this:

当 CONFIG_STATS_NAMES 被禁用时,输出如下:

$ mcumgr --conn acm0 stat my_stats
stat group: my_stats
       8 s0
      16 s1
      24 s2

Filesystem Management 文件系统管理

The filesystem module is disabled by default due to security concerns: because of a lack of access control every file in the FS will be accessible, including secrets, etc. To enable it CONFIG_MCUMGR_CMD_FS_MGMT must be set (y). Once enabled the following sub-commands can be used:

出于安全考虑,默认情况下禁用文件系统模块: 由于缺乏访问控制,FS 中的每个文件都可以访问,包括机密等。要启用它,必须设置(y) CONFIG_MCUMGR_CMD_FS_MGMT。一旦启用,可以使用以下子命令:

mcumgr <connection-options> fs download <remote-file> <local-file>
mcumgr <connection-options> fs upload <local-file> <remote-file>

Using the fs command, requires CONFIG_FILE_SYSTEM to be enabled, and that some particular filesystem is enabled and properly mounted by the running application, eg for littlefs this would mean enabling CONFIG_FILE_SYSTEM_LITTLEFS, defining a storage partition Flash map and mounting the filesystem in the startup (fs_mount()).

使用fs命令,需要启用CONFIG_FILE_SYSTEM,一些特定的文件系统被启用并被运行中的应用程序正确挂载,例如对于littlefs,这意味着启用CONFIG_FILE_SYSTEM_LITTLEFS,定义一个存储分区Flash map 并在启动时挂载文件系统([fs_mount()] (https://docs.zephyrproject.org/latest/services/file_system/index.html#c.fs_mount) )。

Uploading a new file to a littlefs storage, mounted under /lfs, can be done with:

将一个新文件上传到 /lfs 下面的 little left 存储器中,可以使用以下方法完成:

$ mcumgr -c acm0 fs upload foo.txt /lfs/foo.txt
25
Done

Where 25 is the size of the file.

其中25是文件的大小。

For downloading a file, let’s first use the fs command (CONFIG_FILE_SYSTEM_SHELL must be enabled) in a remote shell to create a new file:

为了下载文件,让我们首先使用远程 shell 中的 fs 命令(必须启用 CONFIG_FILE_SYSTEM_SHELL)来创建一个新文件:

uart:~$ fs write /lfs/bar.txt 41 42 43 44 31 32 33 34 0a
uart:~$ fs read /lfs/bar.txt
File size: 9
00000000  41 42 43 44 31 32 33 34 0A                       ABCD1234.

Now it can be downloaded using:

现在它可以通过以下方式下载:

$ mcumgr -c acm0 fs download /lfs/bar.txt bar.txt
0
9
Done
$ cat bar.txt
ABCD1234

Where 0 is the return code, and 9 is the size of the file.

其中0是返回代码,9是文件的大小。

Warning 警告

The commands might exhaust the system workqueue, if its size is not large enough, so increasing CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE might be required for correct behavior.

如果系统工作队列的大小不够大,那么这些命令可能会耗尽系统工作队列,因此可能需要增加 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE 来实现正确的行为。

The size of the stack allocated buffer used to store the blocks, while transferring a file can be adjusted with CONFIG_FS_MGMT_DL_CHUNK_SIZE; this allows saving RAM resources.

可以使用 CONFIG_FS_MGMT_DL_CHUNK_SIZE 调整用于存储块的堆栈分配缓冲区的大小; 这允许节省 RAM 资源。

Tip 小费

CONFIG_FS_MGMT_PATH_SIZE sets the maximum PATH accepted for a file name. It might require tweaking for longer file names.

CONFIG_FS_MGMT_PATH_SIZE 设置文件名可接受的最大 PATH。

Bootloader integration 引导程序集成

The Device Firmware Upgrade subsystem integrates the management subsystem with the bootloader, providing the ability to send and upgrade a Zephyr image to a device.

设备固件升级子系统将管理子系统与引导装载程序集成,提供将 Zephyr 映像发送和升级到设备的能力。

Currently only the MCUboot bootloader is supported. See MCUboot for more information.

目前只支持 MCUboot 引导程序。有关详细信息,请参阅 MCUboot。

SMP Protocol Specification SMP 协议规范

This is description of Simple Management Protocol, SMP, that is used by mcumgr to pass requests to devices and receive responses from them.

这是对简单管理协议(Simple Management Protocol,SMP)的描述,mcumgr 使用该协议向设备传递请求并接收来自设备的响应。

SMP is an application layer protocol. The underlying transport layer is not in scope of this documentation.

SMP 是一个应用层协议。底层传输层不在本文档的范围之内。

Frame: The envelope

Each frame consists of header and following it data. The Data Length” field in the header may be used for reassembly purposes if underlying transport layer supports fragmentation. Frame is encoded in “Big Endian” (Network endianness), where field is more than one byte lone, and takes the following form:

每个帧由头部和后面的数据组成。如果基础传输层支持碎片,则可以将标头中的“数据长度”字段用于重新组装目的。帧编码为“ Big Endian”(Network endianness) ,其中字段多于一个字节,并采用以下形式:

3               2               1               0              
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
Res         OP     Flags               Data Length                              
Group ID                               Sequence Num               Command ID              
Data …                                                              

Note 注意

The original specification states that SMP should support receiving both the “Little-endian” and “Big-endian” frames but in reality the mcumgr library is hardcoded to always treat “Network” side as “Big-endian”.

最初的规范指出 SMP 应该支持接收“ Little-endian”和“ Big-endian”帧,但实际上 mCumgr 库是硬编码的,总是将“ Network”端视为“ Big-endian”。

The Data is optional and is not present when Data Length is zero. The encoding of data depends on the target of group/ID.

数据是可选的,当数据长度为零时不存在。数据的编码取决于 group/ID 的目标。

Where meaning of fields is:

字段的意思是:

Field Description
Res This is reserved, not-used field and should be always set to 0.
这是保留的、未使用的字段,应该始终设置为0。
OP Operation code
Flags Reserved for flags; there are no flags defined yet, the field should be set to 0为标志保留; 尚未定义标志,应将字段设置为0
Data Length Length of the Data field数据字段的长度
Group ID Management Group ID’s
管理组的 ID
Sequence Num This is a frame sequence number. The number is increased by one with each request frame. The Sequence Num of a response should match the one in the request.
这是一个帧序列号。每个请求帧增加一个数字。响应的序列号应该与请求中的序列号相匹配。
Command ID This is a command, within Group.
这是组内的命令。
Data This is data payload of the Data Length size. It is optional as Data Length may be set to zero, which means that no data follows the header.
这是数据长度大小的数据有效负载。它是可选的,因为数据长度可以设置为零,这意味着头后面没有数据。

Note 注意

Contents of a Data depends on a value of an OP, a Group ID, and a Command ID.

Data 的内容取决于 OP、组 ID 和命令 ID 的值。

Note 注意

The Res field may be repurposed by Zephyr for protocol version in the future.

将来,Zephyr 可能会将 Res 字段重新用于协议版本。

Operation code

The operation code determines whether an information is written to a device or requested from it and whether a packet contains request to a SMP server or response from it.

操作代码确定信息是写入设备还是从设备请求,以及数据包是否包含对 SMP 服务器的请求或来自 SMP 服务器的响应。

Following operation codes are defined.

定义了以下操作代码。

Decimal ID Operation
0 read request读取请求
1 read response读取响应
2 write request写请求
3 write response写回复

Management Group ID’s

管理组的 ID

The SMP protocol supports predefined common groups and allows user defined groups. Below table presents list of common groups:

SMP 协议支持预定义的通用组并允许用户定义的组:

Decimal ID Group description
0 Default/OS Management Group 默认/操作系统管理组
1 Application/software image management group 应用程序/软件镜像管理组
2 Statistics management 统计管理
3 Application/system configuration (currently not used by Zephyr)应用程序/系统配置(当前 Zephyr 未使用)
4 Application/system log management (currently not used by Zephyr)
应用程序/系统日志管理(当前 Zephyr 不使用)
5 Run-time tests (unused by Zephyr)
运行时测试(Zephyr 未使用)
6 Split image management (unused by Zephyr)
分割映像管理(Zephyr 未使用)
7 Test crashing application (unused by Zephyr)
测试崩溃应用程序(Zephyr 未使用)
8 File management文件管理
9 Shell management
63 Zephyr specific basic commands group
特定于 Zephyr 的基本命令组
64 This is the base group for defining an application specific management groups.
这是用于定义特定于应用程序的管理组的基组。

The payload for above groups, except for 64 which is not defined, is always CBOR encoded. The group 64, and above, are free to be defined by application developers and are not defined within this documentation.

除了没有定义的64之外,上述组的有效负载始终是 CBOR 编码的。组64及以上可以由应用程序开发人员自由定义,并且不在本文档中定义。

Minimal response

Regardless of a command issued, as long as there is SMP client on the other side of a request, a response should be issued containing header followed by CBOR map container. Lack of response is only allowed when there is no SMP service or device is non-responsive.

不管发出什么命令,只要请求的另一端有 SMP 客户端,就应该发出一个包含头部的响应,后面跟着 CBOR 映射容器。只有当没有 SMP 服务或设备没有响应时才允许缺乏响应。

Minimal response SMP data 最小响应 SMP 数据

Minimal response is CBOR directory: 最小响应是 CBOR 目录:

{
    (str)"rc" : (int)
}

where:

key value
“rc” Status/error codes in responses
响应中的状态/错误代码
   

Status/error codes in responses 响应中的状态/错误代码

Decimal ID Meaning
0 No error, OK.没有错误,好的。
1 Unknown error.未知错误。
2 Not enough memory; this error is reported when there is not enough memory to complete response.
内存不足; 当内存不足以完成响应时,将报告此错误。
3 Invalid value; a request contains an invalid value.
无效值; 请求包含无效值。
4 Timeout; the operation for some reason could not be completed in assumed time.
超时; 由于某种原因无法在假定的时间内完成操作。
5 No entry; the error means that request frame has been missing some information that is required to perform action. It may also mean that requested information is not available.
没有输入; 该错误意味着请求框架缺少执行操作所需的某些信息。这也可能意味着请求的信息不可用。
6 Bad state; the error means that application or device is in a state that would not allow it to perform or complete a requested action.
错误状态; 错误意味着应用程序或设备处于不允许它执行或完成请求的操作的状态。
7 Response too long; this error is issued when buffer assigned for gathering response is not big enough.
响应时间太长; 当为收集响应分配的缓冲区不够大时会发生此错误。
8 Not supported; usually issued when requested Group ID or Command ID is not supported by application.
不支持; 通常在应用程序不支持请求的组 ID 或命令 ID 时发出。
9 Corrupted payload received.
有效载荷已损坏。
256 This is base error number of user defined error codes.
这是用户定义的错误代码的基本错误号。

Zephyr uses MGMT_ERR_ prefixed definitions gathered in this header file subsys/mgmt/mcumgr/lib/mgmt/include/mgmt/mgmt.h

Zephyr 使用在这个头文件 subsys/MGMT/mCumgr/lib/MGMT/include/MGMT/MGMT.h 中收集的 MGMT_ERR_前缀定义

Specifications of management groups supported by Zephyr

Zephyr 支持的管理组规范

Device Firmware Upgrade 设备固件升级

Overview 概述

The Device Firmware Upgrade subsystem provides the necessary frameworks to upgrade the image of a Zephyr-based application at run time. It currently consists of two different modules:

设备固件升级子系统提供必要的框架,以便在运行时升级基于Zephyr的应用程序的镜像。它目前由两个不同的模块组成。

  • subsys/dfu/boot/: Interface code to bootloaders

    Subsys/dfu/boot/: 引导加载程序的接口代码

  • subsys/dfu/img_util/: Image management code

    Subsys/dfu/img_util/: 映像管理代码

The DFU subsystem deals with image management, but not with the transport or management protocols themselves required to send the image to the target device. For information on these protocols and frameworks please refer to the Device Management section.

DFU 子系统处理映像管理,但不处理将映像发送到目标设备所需的传输或管理协议本身。有关这些协议和框架的信息,请参阅设备管理部分。

Bootloaders

MCUboot

Zephyr is directly compatible with the open source, cross-RTOS MCUboot boot loader. It interfaces with MCUboot and is aware of the image format required by it, so that Device Firmware Upgrade is available when MCUboot is the boot loader used with Zephyr. The source code itself is hosted in the MCUboot GitHub Project page.

Zephyr 直接与开源、跨 RTOS MCUboot 引导加载程序兼容。它与 MCUboot 接口,并且知道它所需的映像格式,因此当 MCUboot 是与 Zephyr 一起使用的引导加载程序时,设备固件升级是可用的。源代码本身驻留在 MCUbootGitHub 项目页面中。

In order to use MCUboot with Zephyr you need to take the following into account:

为了在 Zephyr 中使用 MCUboot,您需要考虑以下几点:

  1. You will need to define the flash partitions required by MCUboot; see Flash map for details.

    您将需要定义 MCUboot 所需的 Flash 分区; 有关详细信息,请参阅 Flash 映射。

  2. You will have to specify your flash partition as the chosen code partition

    您必须将闪存分区指定为所选的代码分区

/ {
   chosen {
      zephyr,code-partition = &slot0_partition;
   };
};
  1. Your application’s .conf file needs to enable the CONFIG_BOOTLOADER_MCUBOOT Kconfig option in order for Zephyr to be built in an MCUboot-compatible manner

    你的应用程序的.conf文件需要启用CONFIG_BOOTLOADER_MCUBOOTKconfig选项,以使Zephyr以兼容MCUboot的方式构建。

  2. You need to build and flash MCUboot itself on your device

    您需要在您的设备上构建和烧录 MCUboot 本身

  3. You might need to take precautions to avoid mass erasing the flash and also to flash the Zephyr application image at the correct offset (right after the bootloader)

    您可能需要采取预防措施,以避免大量擦除闪存,并在正确的偏移量(在引导加载程序之后)闪存 Zephyr 应用程序映像

More detailed information regarding the use of MCUboot with Zephyr can be found in the MCUboot with Zephyr documentation page on the MCUboot website.

有关使用 Zephyr 进行 MCUboot 的更详细信息,请参阅 MCUboot 网站上有关 Zephyr 文档的 MCUboot 页面。

Over-the-Air Update 无线更新

Overview 概述

Over-the-Air (OTA) Update is a method for delivering firmware updates to remote devices using a network connection. Although the name implies a wireless connection, updates received over a wired connection (such as Ethernet) are still commonly referred to as OTA updates. This approach requires server infrastructure to host the firmware binary and implement a method of signaling when an update is available. Security is a concern with OTA updates; firmware binaries should be cryptographically signed and verified before upgrading.

Over-the-Air (OTA) Update 是一种使用网络连接向远程设备提供固件更新的方法。虽然这个名称意味着无线连接,但是通过有线连接(如以太网)接收到的更新仍然通常称为 OTA 更新。这种方法要求服务器基础设施承载固件二进制文件,并实现一种在更新可用时发送信号的方法。安全性是 OTA 更新所关心的问题; 固件二进制文件在升级之前应该进行加密签名和验证。

The Device Firmware Upgrade section discusses upgrading Zephyr firmware using MCUboot. The same method can be used as part of OTA. The binary is first downloaded into an unoccupied code partition, usually named slot1_partition, then upgraded using the MCUboot process.

设备固件升级部分讨论如何使用 MCUboot 升级 Zephyr 固件。同样的方法也可以作为 OTA 的一部分使用。首先将二进制文件下载到一个未占用的代码分区(通常称为 slot1_section)中,然后使用 MCUboot 进程进行升级。

Examples of OTA

Golioth

Golioth is an IoT management platform that includes OTA updates. Devices are configured to observe your available firmware revisions on the Golioth Cloud. When a new version is available, the device downloads and flashes the binary. In this implementation, the connection between cloud and device is secured using TLS/DTLS, and the signed firmware binary is confirmed by MCUboot before the upgrade occurs.

Golioth 是一个包含 OTA 更新的物联网管理平台。设备配置为观察 GoliothCloud 上可用的固件修订。当新版本可用时,设备下载并flash二进制文件。在该实现中,使用 TLS/DTLS 保护云和设备之间的连接,并且在升级发生之前通过 MCUboot 确认签名的固件二进制文件。

  1. A working sample can be found on the Golioth Zephyr-SDK repository

    可以在 GoliothZephyr-SDK 存储库中找到一个工作示例

  2. The Golioth OTA documentation includes complete information about the versioning process

    GoliothOTA 文档包含有关版本控制过程的完整信息

Eclipse hawkBit™ Eclipse hawkBitTM

Eclipse hawkBit™ is an update server framework that uses polling on a REST api to detect firmware updates. When a new update is detected, the binary is downloaded and installed. MCUboot can be used to verify the signature before upgrading the firmware.

Eclipse hawkBitTM 是一个更新服务器框架,它使用 REST api 上的轮询来检测固件更新。检测到新的更新时,将下载并安装该二进制文件。MCUboot 可用于在升级固件之前验证签名。

There is a Hawkbit Direct Device Integration API sample included in the Zephyr Management Samples section.

Zephyr 管理示例部分包含一个 Hawkbit 直接设备集成 API 示例。

UpdateHub 更新中心

UpdateHub is a platform for remotely updating embedded devices. Updates can be manually triggered or monitored via polling. When a new update is detected, the binary is downloaded and installed. MCUboot can be used to verify the signature before upgrading the firmware.

UpdateHub 是一个用于远程更新嵌入式设备的平台。可以通过轮询手动触发或监视更新。检测到新的更新时,将下载并安装该二进制文件。MCUboot 可用于在升级固件之前验证签名。

There is an UpdateHub embedded Firmware Over-The-Air (FOTA) sample included in the Zephyr Management Samples section.

在 Zephyr Management Samples 部分中包含一个 UpdateHub 嵌入式固件 Over-Air (FOTA)示例。

SMP Server SMP 服务器

A Simple Management Protocol (SMP) server can be used to update firmware via Bluetooth Low Energy (BLE) or UDP. MCUmgr is used to send a signed firmware binary to the remote device where it is verified by MCUboot before the upgrade occurs.

简单管理协议(Simple Management Protocol,SMP)服务器可以通过低耗电蓝牙或 UDP 更新固件。MCUmgr 用于将签名的固件二进制文件发送到远程设备,在升级发生之前由 MCUboot 对其进行验证。

There is an SMP Server Sample included in the Zephyr Management Samples section.

Zephyr 管理示例部分中包含一个 SMPServer 示例。

Lightweight M2M (LWM2M) 轻量级 M2M (LWM2M)

The Lightweight M2M (LWM2M) protocol includes support for firmware update via CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT. Devices securely connect to an LwM2M server using DTLS. An LwM2M client sample is available but it does not demonstrate the firmware update feature.

轻量级 M2M (LWM2M)协议包括通过 CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT 对固件更新的支持。设备使用 DTLS 安全地连接到 LwM2M 服务器。有一个 LwM2M 客户端示例,但它没有演示固件更新特性。

File Systems 文件系统

Zephyr RTOS Virtual Filesystem Switch (VFS) allows applications to mount multiple file systems at different mount points (e.g., /fatfs and /lfs). The mount point data structure contains all the necessary information required to instantiate, mount, and operate on a file system. The File system Switch decouples the applications from directly accessing an individual file system’s specific API or internal functions by introducing file system registration mechanisms.

Zephyr RTOS虚拟文件系统开关(VFS)允许应用程序在不同的挂载点(例如,/fatfs/lfs)挂载多个文件系统。挂载点数据结构包含了实例化、挂载和操作文件系统所需的所有必要信息。文件系统开关通过引入文件系统注册机制,将应用程序与直接访问单个文件系统的特定API或内部功能相分离。

In Zephyr, any file system implementation or library can be plugged into or pulled out through a file system registration API. Each file system implementation must have a globally unique integer identifier; use FS_TYPE_EXTERNAL_BASE to avoid clashes with in-tree identifiers.

在Zephyr中,任何文件系统实现或库都可以通过文件系统注册API插入或拉出。每个文件系统的实现都必须有一个全局唯一的整数标识符;使用 FS_TYPE_EXTERNAL_BASE 以避免与树内标识符冲突。

int fs_register(int type, const struct fs_file_system_t *fs);

int fs_unregister(int type, const struct fs_file_system_t *fs);

Zephyr RTOS supports multiple instances of a file system by making use of the mount point as the disk volume name, which is used by the file system library while formatting or mounting a disk.

Zephyr RTOS 通过使用挂载点作为磁盘卷名来支持文件系统的多个实例,磁盘卷名由文件系统库在格式化或挂载磁盘时使用。

A file system is declared as:

文件系统声明为:

static struct fs_mount_t mp = {
  .type = FS_FATFS,
  .mnt_point = FATFS_MNTP,
  .fs_data = &fat_fs,
};

where

  • FS_FATFS is the file system type like FATFS or LittleFS.

    FS_FATFS 是类似于 FATFS 或 LittleFS 的文件系统类型。

  • FATFS_MNTP is the mount point where the file system will be mounted.

    FATFS_MNTP 是挂载文件系统的挂载点。

  • fat_fs is the file system data which will be used by fs_mount() API.

    fat_fs 是将由 fs_mount() API 使用的文件系统数据

Samples

Samples for the VFS are mainly supplied in samples/subsys/fs, although various examples of the VFS usage are provided as important functionalities in samples for different subsystems. Here is the list of samples worth looking at:

VFS的样本主要在samples/subsys/fs中提供,尽管在不同子系统的样本中提供了VFS使用的各种例子作为重要功能。下面是值得一看的样本列表。

  • samples/subsys/fs/fat_fs is an example of FAT file system usage with SDHC media;

    samples/subsys/fs/fat_fs 是使用 SDHC 媒体的 FAT 文件系统的一个例子;

  • samples/subsys/shell/fs is an example of Shell fs subsystem, using internal flash partition

    samples/subsys/shell/fs是一个Shell fs子系统的例子,使用内部闪存分区。

    formatted to LittleFS;格式化为 LittleFS;

  • samples/subsys/usb/mass/ example of USB Mass Storage device that uses FAT FS driver with RAM

    示例/subsys/USB/Mass/USB 海量存储设备的例子,该设备使用 FAT FS 驱动程序和 RAM

    or SPI connected FLASH, or LittleFS in flash, depending on the sample configuration.

    或 SPI 连接的 FLASH,或者 LittleFS 的 FLASH,这取决于样例配置。

API Reference

Formatted Output 格式化输出

Applications as well as Zephyr itself requires infrastructure to format values for user consumption. The standard C99 library *printf() functionality fulfills this need for streaming output devices or memory buffers, but in an embedded system devices may not accept streamed data and memory may not be available to store the formatted output.

应用程序和Zephyr本身都需要基础设施来格式化数值供用户使用。标准的C99库*printf()功能可以满足流式输出设备或内存缓冲区的这种需要,但在嵌入式系统中,设备可能不接受流式数据,内存也可能无法存储格式化的输出。

Internal Zephyr API traditionally provided this both for printk() and for Zephyr’s internal minimal libc, but with separate internal interfaces. Logging, tracing, shell, and other applications made use of either these APIs or standard libc routines based on build options.

传统上,内部 Zephyr API 为 printk()和 Zephyr 的内部最小 libc 提供了这种服务,但是提供了单独的内部接口。日志记录、跟踪、 shell 和其他应用程序使用这些 API 或基于构建选项的标准 libc 例程。

The cbprintf() public APIs convert C99 format strings and arguments, providing output produced one character at a time through a callback mechanism, replacing the original internal functions and providing support for almost all C99 format specifications. Existing use of s*printf() C libraries in Zephyr can be converted to snprintfcb() to avoid pulling in libc implementations.

cbprintf()公共API转换C99格式的字符串和参数,通过回调机制提供一次产生一个字符的输出,取代了原来的内部函数,并提供对几乎所有C99格式规范的支持。Zephyr中现有的使用s*printf()的C库可以转换为snprintfcb()以避免拉入libc的实现。

Several Kconfig options control the set of features that are enabled, allowing some control over features and memory usage:

几个 Kconfig 选项控制一组已启用的特性,允许对特性和内存使用进行一些控制:

CONFIG_CBPRINTF_LIBC_SUBSTS can be used to provide functions that behave like standard libc functions but use the selected cbprintf formatter rather than pulling in another formatter from libc.

CONFIG_CBPRINTF_LIBC_SUBSTS可以用来提供与标准libc函数类似的函数,但使用选定的cbprintf格式,而不是从libc拉入另一个格式。

In addition CONFIG_CBPRINTF_NANO can be used to revert back to the very space-optimized but limited formatter used for printk() before this capability was added.

此外,CONFIG_CBPRINTF_NANO可以用来恢复到在添加这项功能之前用于printk()的非常空间优化但有限的格式化。

Cbprintf Packaging

Typically, strings are formatted synchronously when a function from printf family is called. However, there are cases when it is beneficial that formatting is deferred. In that case, a state (format string and arguments) must be captured. Such state forms a self-contained package which contains format string and arguments. Additionally, package may contain copies of strings which are part of a format string (format string or any %s argument). Package primary content resembles va_list stack frame thus standard formatting functions are used to process a package. Since package contains data which is processed as va_list frame, strict alignment must be maintained. Due to required padding, size of the package depends on alignment. When package is copied, it should be copied to a memory block with the same alignment as origin.

通常情况下,当调用printf系列的函数时,字符串被同步格式化。然而,在有些情况下,推迟格式化是有益的。在这种情况下,必须捕获一个状态(格式化字符串和参数)。这种状态形成了一个独立的包,它包含了格式字符串和参数。此外,包可以包含作为格式字符串(格式字符串或任何%s参数)一部分的字符串的副本。包的主要内容类似于va_list堆栈框架,因此标准的格式化函数被用来处理一个包。由于包包含的数据是作为va_list框架处理的,必须保持严格的对齐。由于需要填充,包的大小取决于对齐方式。当包被复制时,它应该被复制到一个与原始对齐方式相同的内存块中。

Package can have following variants:

包可以有以下变体:

  • Self-contained - non read-only strings appended to the package. String can be formatted from such package as long as there is access to read-only string locations. Package may contain information where read-only strings are located within the package. That information can be used to convert packet to fully self-contained package.

    附加到包中的自包含非只读字符串。只要能够访问只读字符串的位置,字符串就可以从这样的包中被格式化。包可能包含只读字符串在包中的位置的信息。这些信息可以用来将包转换为完全自足的包。

  • Fully self-contained - all strings are appended to the package. String can be formatted from such package without any external data.

    完全自包含的-所有字符串都附加到包中。字符串可以从这样的包格式化,而不需要任何外部数据。

  • Transient- only arguments are stored. Package contain information where pointers to non read-only strings are located within the package. Optionally, it may contain read-only string location information. String can be formatted from such package as long as non read-only strings are still valid and read-only strings are accessible. Alternatively, package can be converted to self-contained package or fully self-contained if information about read-only string locations is present in the package.

    Transient-只存储参数。包包含指向非只读字符串的指针在包内的位置信息。可选的是,它可能包含只读字符串的位置信息。只要非只读字符串仍然有效并且只读字符串可以被访问,就可以从这样的包中格式化字符串。另外,如果包中有关于只读字符串位置的信息,包可以被转换为自包含的包或完全自包含的

Package can be created using two methods:

包可以使用两种方法创建:

  • runtime - using cbprintf_package() or cbvprintf_package(). This method scans format string and based on detected format specifiers builds the package.

    runtime - 使用 cbprintf_package()或 cbvprintf_package()。此方法扫描格式字符串并根据检测到的格式说明符构建包。

  • static - types of arguments are detected at compile time by the preprocessor and package is created as simple assignments to a provided memory. This method is significantly faster than runtime (more than 15 times) but has following limitations: requires _Generic keyword (C11 feature) to be supported by the compiler and can only create a package that is known to have no string arguments (%s). CBPRINTF_MUST_RUNTIME_PACKAGE can be used to determine at compile time if static packaging can be applied. Macro determines need for runtime packaging based on presence of char pointers in the argument list so there are cases when it will be false positive, e.g. %p with char pointer.

    static - 参数的类型在编译时被预处理器检测到,包被创建为对提供的内存的简单赋值。这种方法明显比运行时快(超过15倍),但有以下限制:需要编译器支持_Generic关键字(C11特性),并且只能创建一个已知没有字符串参数的包(%s)。CBPRINTF_MUST_RUNTIME_PACKAGE可用于在编译时确定是否可以应用静态打包。巨集根据参数列表中是否存在char指针来决定是否需要运行时打包,因此在某些情况下会出现假阳性,例如:%p带有char指针。

Several Kconfig options control behavior of the packaging:

几个 Kconfig 选项控制包的行为:

Cbprintf package conversion Cbprintf 包转换

It is possible to convert package to a variant which contains more information, e.g transient package can be converted to self-contained. Conversion to fully self-contained package is possible if CBPRINTF_PACKAGE_ADD_RO_STR_POS flag was used when package was created.

可以将包转换为包含更多信息的变体,例如瞬态包可以转换为自包含的。如果在创建包时使用了 CBPRINTF_PACKAGE_ADD_RO_STR_POS 标志,则可以转换为完全自包含的包。

cbprintf_package_copy() is used to calculate space needed for the new package and to copy and convert a package.

cbprintf_package_copy()用于计算新包所需的空间并复制和转换包。

Cbprintf package format Cbprintf 包格式

Format of the package contains paddings which are platform specific. Package consists of header which contains size of package (excluding appended strings) and number of appended strings. It is followed by the arguments which contains alignment paddings and resembles va_list stack frame. Finally, package optionally contains appended strings. Each string contains 1 byte header which contains index of the location where address argument is stored. During packaging address is set to null and before string formatting it is updated to point to the current string location within the package. Updating address argument must happen just before string formatting since address changes whenever package is copied.

包的格式包含特定于平台的填充。包由包头组成,包头包含包的大小(不包括附加字符串)和附加字符串的数目。后面是包含对齐填充和类似于 va_list 堆栈帧的参数。最后,包可选地包含附加的字符串。每个字符串包含1字节头,其中包含地址参数存储位置的索引。在包地址设置为空,并在字符串格式化之前,更新为指向包中的当前字符串位置。更新地址参数必须在字符串格式化之前进行,因为每当复制包时地址都会发生变化。

格式 说明
Headersizeof(void *) 1 byte: Argument list size including header and fmt (in 32 bit words)
1字节: 参数列表大小,包括头和 fmt (32位单词)
  1 byte: Number of strings appended to the package
1字节: 附加到包的字符串数
  1 byte: Number of read-only string argument locations
1字节: 只读字符串参数位置的数目
  1 byte: Number of transient string argument locations
1字节: 瞬态字符串参数位置的数目
  platform specific padding to sizeof(void *)
平台特定的 sizeof (void *) 填充
Arguments Pointer to fmt (or null if fmt is appended to the package)
指向 fmt 的指针(如果 fmt 附加到包中,则为 null)
  (optional padding for platform specific alignment)
(平台特定对齐的可选填充)
  argument 0
  (optional padding for platform specific alignment)
(平台特定对齐的可选填充)
  argument 1
 
String location information (optional) Indexes of words within the package where read-only strings are located
包中只读字符串所在的单词的索引
  Indexes of words within the package where transient strings are located
包中暂态字符串所在的单词的索引
Appended strings (optional) 1 byte: Index within the package to the location of associated argument
1字节: 包中相关参数位置的索引
  Null terminated string
终止为空的字符串
 

Warning 警告

If CONFIG_MINIMAL_LIBC is selected in combination with CONFIG_CBPRINTF_NANO formatting with C standard library functions like printf or snprintf is limited. Among other things the %n specifier, most format flags, precision control, and floating point are not supported.

如果选择 CONFIG_MINIMAL_LIBC 并结合 CONFIG_CBPRINTF_NANO 格式和 C 标准库函数(如 printf 或 snprintf) ,则会受到限制。其中不支持% n 说明符、大多数格式标志、精度控制和浮点数。

API Reference