Zephyr RTOS 嵌入式 C 编程 (使用嵌入式 RTOS POSIX API)8. Zephyr RTOS 与以太网、Wi-Fi 及 TCP/IP

网络连接是物联网的核心,Zephyr 提供了全面的嵌入式 TCP/IP 网络支持。本章假设您已具备计算机网络和 TCP/IP 的基础知识,重点将放在使用 Zephyr RTOS 网络框架开发应用程序上。

Zephyr 的网络栈是 Zephyr OS 的栈实现 。与大多数网络栈类似,它采用分层架构,其中每层接口为其他层提供服务。在 Zephyr 中,通过选择所需的 Kconfig 选项来指定要集成到应用程序中的网络栈功能。

Zephyr 网络栈包含多个组件,其架构概览如图 8-1 至 8-3 所示,从应用层开始直至物理层。

 图 8-1 网络应用

 图 8-2 网络协议

 图 8-3 网络设备驱动

Zephyr 网络应用可以使用 Zephyr 提供的应用层协议库。开发者也可以直接基于 Zephyr 实现的 BSD 套接字 API 来开发网络应用。Zephyr 还提供了 网络管理 API,可用于配置网络及设置网络参数,如网络链路选项、启动扫描(适用时)、监听网络配置事件等。通过 Zephyr 的网络接口 API 可执行分配 IP 地址给网络接口、关闭网络接口等操作。

Zephyr 支持的应用层协议包括 CoAP、LwM2M 和 MQTT。

Zephyr 支持的 TCP/IP 第三层和第四层协议包括 IPv6、IPv4、UDP、TCP、ICMPv4 和 ICMPv6,这些协议通过 BSD 套接字 API 进行访问。

Zephyr 支持的第二层(链路层)网络技术包括以太网、IEEE 802.15.4、蓝牙和 CAN 总线。

底层设备驱动负责网络数据包在物理层的发送和接收。

在实现 Zephyr 网络应用时,通常的做法是将应用程序代码运行在用户空间上下文,而网络协议栈代码运行在内核空间上下文。

Zephyr 网络接口抽象层在提供网络接口通用 API 功能方面起着重要作用,特别是具备启用或禁用网络接口的能力。发送和接收数据的传输都是通过网络接口进行的。在 Zephyr 中,网络接口不能在运行时创建。构建过程中会创建一个包含网络接口信息的特殊链接器段,并在链接时填充这些接口。 网络接口是通过使用 NET_DEVICE_INIT() 宏或其更专门的变体来创建的。对于以太网网络,应使用 ETH_NET_DEVICE_INIT() 宏 ,因为当 CONFIG_NET_VLAN 启用时,它会自动创建 VLAN 接口。 网络初始化宏主要用于网络设备驱动程序的实现中。

可以通过调用 net_if_up() 开启网络接口 ,调用 net_if_down() 关闭接口。在 Zephyr 中,默认行为是当网络设备通电时,网络接口也会自动开启。 网络接口可以通过 struct net_if *指针或网络接口索引来引用。可以通过调用 net_if_get_by_index()从索引解析网络接口,或通过调用 net_if_get_by_iface()从接口指针解析。

对于 IP 网络,必须正确设置网络设备的 IP 地址。IP 地址可以通过 DHCPv4 自动设置,或使用 API 函数如 net_if_ipv4_addr_add() 手动设置。

Zephyr 网络框架提供了数据包优先级分类机制,旨在让高优先级数据包比低优先级数据包更早发送或接收。要支持数据包优先级功能,Zephyr 提供了 CONFIG_NET_TC_TX_COUNT 和 CONFIG_NET_TC_RX_COUNT 配置选项。

CONFIG_NET_TC_TX_COUNT 用于定义网络设备可拥有的 Tx(发送)流量类别数量,而 CONFIG_NET_TC_RX_COUNT 则用于定义网络设备可拥有的 Rx(接收)流量类别数量。

随后可将网络数据包优先级映射到流量类别,使高优先级数据包能够优先于低优先级数据包被处理。这需要为每个优先级建立独立队列,每个队列由单独线程处理,且流量类别值越高对应的线程优先级值越低。对于传输类别而言,当类别数量为 0 时,传输网络流量将直接推送至驱动程序而不经过任何队列。在接收类别中,计数值为 0 意味着所有网络流量将从驱动程序直接推送至应用线程,不经过任何中间 RX 队列。设备驱动程序与应用之间存在接收套接字队列。禁用 RX 线程将导致网络设备驱动程序(通常运行在 IRQ 上下文中)直接处理数据包直至应用层。这种处理方式的后果是:若 RX 处理耗时相对于数据包到达速率较长,则其他传入数据包可能会丢失。

当启用了用户空间支持时,在当前实现中至少需要启用 1 个 TX 线程和 1 个 RX 线程。

Zephyr 可用于为适用的网络技术(如以太网 )配置混杂模式。若启用 CONFIG_NET_PROMISCUOUS_MODE 配置项,则网络设备驱动能接收的所有数据包都将被接受。如未配置混杂模式,则仅接受目标地址为以太网设备 MAC 地址的数据包。通常,混杂模式用于监控所有流量,在内存和处理资源有限的嵌入式系统中一般不会配置此模式。

Zephyr 与网络管理

RFC 2863 标准涉及管理信息库(MIB) 中与网络接口管理相关的受管对象部分。该标准定义了 Zephyr 的两种接口状态: 管理状态和运行状态 。 管理状态表示接口处于开启(ON)或关闭(OFF)状态。在 Zephyr 中,这由应用程序控制的 NET_IF_UP 标志表示,可通过调用 net_if_up()或 net_if_down()函数来修改其值。但接口处于开启状态并不意味着它已准备好收发数据包。 运行状态表示接口的内部状态,会在以下情况更新:应用程序启停接口导致管理状态变更、驱动/第二层通知接口 PHY(第一层)状态变化、或驱动/第二层通知接口加入/离开网络。

PHY 状态由 NET_IF_LOWER_UP 标志表示,可通过 net_if_carrier_on()和 net_if_carrier_off()函数进行修改。默认情况下,新初始化的接口会设置该标志。以以太网为例,当网线连接或断开时,载波状态将发生改变。

网络关联状态由 NET_IF_DORMANT 标志表示,可通过 net_if_dormant_on()和 net_if_dormant_off()进行修改。对于 Wi-Fi 而言,当 Wi-Fi 驱动成功连接到接入点时,休眠状态会发生变化。Wi-Fi 驱动在初始化期间将休眠状态设为 ON,当检测到已建立 Wi-Fi 网络连接时,则将休眠状态设为 OFF。

Zephyr 网络 API 提供了多个用于检测接口状态的函数,例如 net_if_is_admin_up()、net_if_is_carrier_ok() 以及 net_if_is_dormant()。

Zephyr 的网络支持相当全面,其可用的应用层 API 列表包含了许多物联网应用中常用的协议,例如
  • CoAP 协议

  • CoAP 客户端

  • HTTP 客户端

  • 轻量级 M2M(LwM2M)

  • MQTT

  • MQTT-SN

  • SNTP(简单网络时间协议)

传输层和链路层 API 包括
  • BSD 套接字

  • 用于配置 IPv4/IPv6 的 API

  • 用于配置 DNS 名称解析的 API

  • 为应用程序添加网络管理和监控功能的各种 API

更多详细信息可在 Zephyr 项目文档[1]中找到。

开发基于 TCP/IP 网络的应用程序时,务必仅包含和配置必要的组件,因为应用程序是整体构建的最终产物,包含大量不必要的网络功能会耗尽有限的存储资源。本章并不旨在全面介绍 Zephyr 中所有的网络功能,而是着重讲解构建实用物联网应用时必须理解和掌握的核心组件。因此,重点将放在 IPv4 协议上,IPv6 属于更高级的课程范畴。示例应用将采用 Zephyr 实现的 BSD 套接字 API 进行开发,硬件平台选用内置以太网功能的 STM32 Nucleo 开发板——搭载 ARM Cortex M7 处理器的 Nucleo-F767ZI 开发板(SoC 系统级芯片)。

Nucleo-F767ZI 开发板

STM32F767ZI 微控制器采用 ARM Cortex M7 内核,具备 2MB 闪存和 512KB SRAM,支持外部中断的 GPIO 接口,24 通道 12 位 ADC,12 位 DAC 通道,USART/UART 串行外设,以及 I2C 和 SPI 外设。该处理器还包含通用定时器、高级控制定时器、基本定时器、低功耗定时器和看门狗定时器,同时集成 CAN 2.0B、SDMMC、USB 2.0 OTG HS/FS 高速/全速接口、随机数生成器以及以太网外设。

开发板配备三个用户 LED 指示灯、两个按钮(USER 和 RESET)以及一个以太网 RJ45 接口。

开发板实物图(图 8-4)与布局示意图(图 8-5)如下所示。

 

图 8-4 Nucleo-F767ZI 开发板

 

图 8-5 Nucleo-F767ZI 开发板布局

 

使用 STM32 Nucleo-F767ZI 开发板构建和调试 Zephyr 网络编程示例

Zephyr 网络编程示例涵盖了 TCP/IP 网络编程的多个方面,包括 dhcpv4_client、dns_resolve、sockets、ipv4_autoconf、lldp(链路层发现协议)以及 telnet。同时还提供了面向物联网的协议示例,包括 lwm2m_client(轻量级 M2M 客户端)和 mqtt_publisher 示例。

在为不同开发板构建 Zephyr 网络示例时可能会遇到问题,因为 Zephyr 项目的目标是支持广泛的开发板,这导致关于个别开发板"特性和功能"的文档有时较为有限。在构建和测试网络应用程序时,通常需要排查并仔细检查诸如手动分配 IP 地址和子网掩码的情况,或者如果地址是自动分配的,则需要正确设置和配置 DHCP。在实际网络中运行应用程序还需要掌握一定的网络配置和故障排除知识,这可能包括处理防火墙配置、网络路由和 NAT(网络地址转换)等问题,以及 IPv4 和 IPv6 配置细节,例如默认网关的 IP 地址和要使用的 DNS 解析器的 IP 地址。

Zephyr 源代码仓库包含全面的网络套接字示例集,其中包括演示简易 HTTP 服务器、基础多线程 HTTP 服务器、基于 TCP 和 UDP 运行的 echo 及客户端服务器、HTTP 客户端以及自定义 TCP 套接字客户端等示例。本章将重点探讨 UDP/TCP 回显服务器示例、异步 select 示例以及 WebSocket 客户端示例。这些示例大体对应 Linux TCP/IP 编程入门时涉及的典型范例。不同之处在于 Zephyr 应用程序采用单体架构,所有必需组件都必须内置其中。历史上 Zephyr 曾集成 CivetWeb 嵌入式 HTTP 服务器 ,但当前其 CivetWeb 模块已被弃用并移除。官方声明原因是维护第三方 Civet 项目分支作为外部模块的复杂性过高。

Zephyr HTTP 服务器模块的要求是:需与现有 Zephyr 构建模块对接,支持 ZTest 框架、HTTP、WebSocket、JSON 和 mbedTLS 库,并包含针对套接字、线程和文件系统操作的 POSIX API 支持,以及 Kconfig 和构建系统以实现服务器选项的调优。同时要求支持 Zephyr 可迭代区段功能,以便更灵活地定义 HTTP 服务和资源。

BSD 套接字 API

BSD 套接字 API(POSIX 标准的一部分)最初是在伯克利与 TCP/IP 协议原始版本的开发过程中设计的。

Zephyr 包含了该协议的实现,其设计目标是尽可能减少开销。Zephyr 的 BSD 套接字 API 采用命名空间约定,即在原生 BSD 套接字 API 名称前添加 zsock_前缀,例如用 zsock_socket()和 zsock_close()分别对应 BSD 的 socket()和 close()函数。这样做是为了避免与 libc 或其他 POSIX 兼容库中可能存在的名称(如 close())产生冲突。通过启用配置选项 CONFIG_NET_SOCKETS_POSIX_NAMES 可以暴露原生 POSIX 名称。支持的 BSD 函数包括:socket()、close()、recv()、recvfrom()、send()、sendto()、connect()、bind()、listen()、accept()、fcntl()(用于设置非阻塞模式)、getsockopt()、setsockopt()、poll()、select()、getaddrinfo()和 getnameinfo()。

Zephyr 网络 API 的实现尽可能利用了 POSIX API 的短读/短写特性,旨在最小化开销和代码复杂度。该特性允许在 SOCK_STREAM 类型套接字上执行 recv() 和 send() 等调用时,实际处理(接收或发送)的数据量可能少于用户请求的数量。例如,调用 recv(sock, 1000, 0) 可能返回 100,表示仅读取了 100 字节(短读)。要获取剩余的 900 字节, 应用程序需要重复调用 call() 直到接收完所有数据。

Zephyr 回显服务器示例概述

Zephyr 的 echo-server 示例应用实现了一个 UDP/TCP 服务器,该服务器监听传入的 IPv4(如果配置支持,还包括 IPv6)数据包(由 echo 客户端发送)并原样返回。此外,通过提供合适的 overlay-tls.conv 覆盖配置文件,可以将项目配置为包含 TLS 1.2 支持。示例代码可在 Zephyr 代码库[2]中找到。尽管 echo 服务器的概念很简单,但实际代码涵盖了在 Zephyr RTOS 中实现 TCP 和 UDP 服务器应用的诸多方面。在实际应用中,由于 Zephyr RTOS 运行的硬件资源限制,并发服务器应用的数量可能会很少。

最新版本的服务器示例支持客户端连接,服务器端口号配置为 4242。由于 TCP 和 UDP 端口号相互独立,该应用程序将同时使用 TCP 端口 4242 和 UDP 端口 4242。

可以通过设置第二块运行回声客户端应用的开发板,或通过在 Windows 或 Linux PC 上使用 C、C++或 Python 等语言实现合适的测试程序来测试回声服务器 。

构建服务器的 west 命令如下所示:
west build -p -b nucleo_f767zi  <path to echo_server project source code directory>

可使用命令 west flash --runner jlink 将代码烧录至开发板。

通过 USB 串行链路发送至 PuTTY 的开发板启动消息
*** Booting Zephyr OS build v2.7.0-rc2-7-g0d538447144c  ***
[00:00:01.560,000] <inf> net_config: Initializing network
[00:00:01.560,000] <inf> net_config: Waiting interface 1 (0x2002260c) to be up...
[00:00:02.051,000] <inf> net_config: Interface 1 (0x2002260c) coming up

实际上,这个示例包含多个组成部分,其中还包括 Zephyr OS Services shell 命令库模块,我们将在接下来进行介绍。

Zephyr OS 服务模块

Zephyr OS Services 模块使得创建和部署带有用户自定义命令集的解释器 shell 成为可能。相比简单的按钮和 LED 交互,该模块能够支持更复杂的交互和反馈功能。

该模块提供了一个(简化版) 类 Unix shell,具有许多实用功能。包括支持多实例、与日志框架协同工作,以及同时支持静态(编译时提供)和动态(应用程序运行时实现)命令。

其他实用功能还包括:支持字典命令、使用 Tab 键实现命令自动补全,以及一系列内置实用命令如 clear、shell、colors、echo、history 和 resize。最近执行的命令可通过上下方向键或元键查看。命令行文本编辑支持使用左右方向键、退格键、删除键、Home 键和 Insert 键。该模块还支持 ANSI 转义码和多行命令编辑、显示命令帮助的处理程序、支持通配符*和?的使用、支持元键操作,同时兼容 getopt 和 getopt_long 功能。通过相关 Kconfig 配置选项,可仅包含所需功能以优化应用程序内存使用。

Shell 可构建支持 Zephyr 网络命令的功能,从而能够运行 Linux 命令行网络命令。可用命令如下方屏幕截图所示。

Zephyr 代码库中的示例 zephyr\samples\net\sockets\echo_server 可用于探索部分 Zephyr shell 网络命令。关于将 Zephyr shell 集成到应用程序的更多详细信息,请参阅 Zephyr 文档 https://docs.zephyrproject.org/latest/services/shell/index.xhtml

图 8-6 展示了可用的网
<think>我们正在处理一个关于在Linux x86_64系统上运行Zephyr SDK 0.17.3-rc1编译出的二进制文件时出现“Exec format error”的问题。这个错误通常意味着系统尝试执行的二进制文件格式不正确,可能的原因包括: 1. 架构不匹配:例如,为ARM架构编译的二进制文件在x86_64上运行。 2. 文件损坏:下载或编译过程中文件损坏。 3. 文件格式错误:例如,文件不是有效的可执行文件(可能是脚本但没有正确指定解释器)。 4. 缺少依赖:动态链接器缺失或库不兼容(但通常会有不同的错误提示)。 根据用户提供的信息,他们使用的是Zephyr SDK 0.17.3-rc1,目标平台是Linux x86_64。因此,我们期望编译出的二进制文件应该是x86_64架构的。但出现“Exec format error”表明系统无法识别该文件的格式。 ### 解决步骤 #### 1. **确认文件类型和架构** 使用`file`命令检查二进制文件的类型和架构: ```bash file <你的二进制文件> ``` 预期输出应该是类似: ``` ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=..., with debug_info, not stripped ``` 如果不是x86-64架构,则说明编译目标错误。 #### 2. **检查编译目标** 在Zephyr中,编译目标由`BOARD`环境变量或`-b`参数指定。确保你编译时指定的板子是x86_64架构的,例如: ```bash west build -b native_posix_64 samples/hello_world ``` 注意:`native_posix_64`是用于64位x86 Linux的模拟板。其他板子可能生成不同架构的二进制。 #### 3. **检查Zephyr SDK环境** 确认你使用Zephyr SDK版本是否正确,并且环境变量设置正确: ```bash echo $ZEPHYR_SDK_INSTALL_DIR ``` 确保该路径指向的是Zephyr SDK 0.17.3-rc1的安装目录。 同时,检查工具链是否可用: ```bash $ZEPHYR_SDK_INSTALL_DIR/sysroots/x86_64-pokysdk-linux/usr/bin/x86_64-zephyr-elf/x86_64-zephyr-elf-gcc --version ``` 如果这个命令报错,说明SDK安装可能有问题。 #### 4. **重新安装Zephyr SDK** 如果怀疑SDK安装损坏,可以尝试重新安装: ```bash # 下载SDK安装器 wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.17.3-rc1/zephyr-sdk-0.17.3-rc1_linux-x86_64.tar.xz # 解压 tar xvf zephyr-sdk-0.17.3-rc1_linux-x86_64.tar.xz cd zephyr-sdk-0.17.3-rc1 # 运行安装脚本 ./setup.sh ``` 在安装过程中,选择安装位置并同意许可协议。 #### 5. **检查文件权限** 确保二进制文件有可执行权限: ```bash chmod +x <你的二进制文件> ``` #### 6. **使用`strace`调试** 如果以上步骤无法解决,使用`strace`查看详细的执行过程: ```bash strace ./<你的二进制文件> ``` 在输出中,你可以看到系统调用失败的具体位置。 #### 7. **检查系统是否支持** 虽然你使用的是x86_64 Linux,但也要确认内核支持该可执行文件的格式。通常现代Linux系统都支持ELF64。 ### 常见问题 - **问题1**:编译时未指定正确的板子,导致生成了非x86_64的二进制文件。 - **问题2**:Zephyr SDK安装不完整或损坏。 - **问题3**:二进制文件在下载或复制过程中损坏。 ### 相关问题 1. 如何为Zephyr项目指定正确的目标板? 2. Zephyr SDK安装过程中常见问题有哪些? 3. 如何在Linux系统下调试“Exec format error”错误? 4. 如何验证Zephyr SDK安装是否成功? 5. Zephyr项目编译出的二进制文件有哪些类型? 请根据上述步骤逐一排查问题。如果问题仍然存在,请提供`file`命令的输出和编译命令的详细信息。</think>### 解决 Zephyr SDK 0.17.3-rc1 Linux x86_64 执行格式错误 当在 Linux x86_64 系统上运行 Zephyr SDK 编译的二进制文件时出现 `Exec format error`,通常由以下原因导致: #### 1. **架构不匹配问题**(最常见) ```bash # 检查文件架构类型 file your_binary_file.elf # 预期正确输出(x86_64架构): # ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked... ``` - 若输出显示 `ARM` 或其他架构,说明编译目标错误 - **解决方案**: ```bash # 重新编译时指定x86_64目标板 west build -b native_posix_64 samples/your_app ``` > 使用 `native_posix_64` 板级配置确保生成x86_64架构可执行文件[^2] #### 2. **文件权限问题** ```bash # 添加执行权限 chmod +x your_binary_file.elf # 验证权限 ls -l your_binary_file.elf # 应有类似输出:-rwxr-xr-x ``` #### 3. **SDK 环境配置问题** ```bash # 检查当前环境变量 echo $ZEPHYR_BASE echo $ZEPHYR_SDK_INSTALL_DIR # 重新初始化环境(在zephyr目录下) source zephyr-env.sh ``` - 若环境变量未设置,手动配置: ```bash export ZEPHYR_SDK_INSTALL_DIR=/opt/zephyr-sdk-0.17.3-rc1 export PATH=$PATH:$ZEPHYR_SDK_INSTALL_DIR/sysroots/x86_64-pokysdk-linux/usr/bin ``` #### 4. **二进制文件损坏** - 验证文件完整性: ```bash # 检查文件大小(异常小则可能损坏) du -h your_binary_file.elf # 重新编译并比较哈希值 sha256sum your_binary_file.elf ``` #### 5. **系统兼容性问题** - 确认内核支持ELF格式: ```bash # 检查当前内核架构 uname -m # 应输出 x86_64 # 检查ELF支持 grep CONFIG_BINFMT_ELF /boot/config-$(uname -r) # 应输出:CONFIG_BINFMT_ELF=y ``` #### 完整解决流程: ```mermaid graph TD A[出现Exec format error] --> B[检查文件架构] B -->|非x86_64| C[重新编译指定native_posix_64板] B -->|x86_64| D[检查文件权限] D --> E[添加执行权限] E --> F[验证环境变量] F --> G[重新初始化SDK环境] G --> H[测试运行] H -->|仍失败| I[检查文件完整性] I --> J[重新编译并验证] ``` ### 补充建议 1. **使用最新SDK版本**: ```bash # 升级Zephyr SDK west update west zephyr-export ``` 2. **清理构建缓存**: ```bash west build -t clean rm -rf build/ ``` > **关键提示**:Zephyr的 `native_posix` 板级支持在Linux主机上直接运行模拟二进制文件,但必须区分32位(`native_posix`)和64位(`native_posix_64`)版本[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akluse

失业老程序员求打赏,求买包子钱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值