工程创建
创建一个基于FRDM_RW612的空白工程。

界面设计
根据网络的配置,我设计了DHCP的开关,以及静态IP地址、DNS、网关、子掩码等标签,以及连接状态,网卡的mac地址,以及一个连接按键。同时为了输入对应的数据,我还添加了一个容器,里面放了一个标签以及键盘。




组件列表如下:


生成代码后,导出zephyr工程到zephyr rtos下面的app目录下面。
代码实现
1、prj.conf
在配置文件中,主要是给lvgl分配栈空间,打开网卡的配置等开关,源码如下:
CONFIG_LV_Z_MEM_POOL_SIZE=65536
CONFIG_MAIN_STACK_SIZE=8192
CONFIG_DISPLAY=y
CONFIG_LOG=y
CONFIG_LVGL=y
CONFIG_LV_USE_LOG=y
CONFIG_LV_FONT_MONTSERRAT_12=y
CONFIG_LV_FONT_MONTSERRAT_14=y
# Ethernet networking (on-chip ENET MAC + RMII PHY, enabled by the board dtsi)
CONFIG_NETWORKING=y
CONFIG_NET_IPV6=n
CONFIG_NET_IPV4=y
CONFIG_NET_ARP=y
CONFIG_NET_UDP=y
CONFIG_NET_DHCPV4=y
CONFIG_DNS_RESOLVER=y
CONFIG_NET_L2_ETHERNET_MGMT=y
CONFIG_NET_LOG=y
CONFIG_NET_SHELL=y
# Network buffers
CONFIG_NET_BUF_RX_COUNT=16
CONFIG_NET_BUF_TX_COUNT=16
CONFIG_NET_BUF_DATA_SIZE=2048
2 网络实现
我在custom目录下面添加了eth_config.c/h
主要是在初始化时创建几个事件回调函数,同时在init中对事件进行初始化。源代码如下:
/*
* Copyright 2026 NXP
* NXP Proprietary. This software is owned or controlled by NXP and may only be used strictly in
* accordance with the applicable license terms. By expressly accepting such terms or by downloading, installing,
* activating and/or otherwise using the software, you are agreeing that you have read, and that you agree to
* comply with and are bound by, such license terms. If you do not agree to be bound by the applicable license
* terms, then you may not retain, install, activate or otherwise use the software.
*/
/*********************
* INCLUDES
*********************/
#include <stdio.h>
#include <string.h>
#include "lvgl.h"
#include "eth_config.h"
#include <zephyr/kernel.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/ethernet_mgmt.h>
#include <zephyr/net/dhcpv4.h>
#include <zephyr/net/dns_resolve.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(eth_config, LOG_LEVEL_INF);
/*********************
* DEFINES
*********************/
#define ETH_UI_TIMER_MS 100 /* how often the LVGL timer drains the event queue */
#define DHCP_TIMEOUT_MS 20000 /* no address within this time = connect failed */
#define IPV4_STR_MAX_LEN 15 /* "255.255.255.255" */
#define COLOR_VALUE_NORMAL 0x000000 /* default text color of the value labels */
#define COLOR_VALUE_GRAYED 0x9e9e9e /* grayed out while DHCP is on */
/* Default values shown in the labels at startup */
#define DEFAULT_IP "192.168.3.111"
#define DEFAULT_NETMASK "255.255.255.0"
#define DEFAULT_GATEWAY "192.168.3.1"
#define DEFAULT_DNS "192.168.3.1"
/**********************
* TYPEDEFS
**********************/
enum eth_ui_event_type {
ETH_EVT_IP_READY, /* DHCP (or static) IPv4 address was added to the iface */
ETH_EVT_CARRIER_ON, /* Ethernet cable plugged in (link up) */
ETH_EVT_CARRIER_OFF, /* Ethernet cable unplugged (link down) */
};
struct eth_ui_event {
enum eth_ui_event_type type;
};
/**********************
* STATIC PROTOTYPES
**********************/
static void eth_mgmt_event_handler(struct net_mgmt_event_callback *cb,
uint64_t mgmt_event, struct net_if *iface);
static void eth_ui_timer_cb(lv_timer_t *timer);
static void dhcp_timeout_cb(lv_timer_t *timer);
static void value_label_event_cb(lv_event_t *e);
static void edit_ta_event_cb(lv_event_t *e);
static void switch_dhcp_event_cb(lv_event_t *e);
static void button_connect_event_cb(lv_event_t *e);
static void keyboard_show(gg_ui_t *ui, lv_obj_t *target_label);
static void keyboard_hide(gg_ui_t *ui);
static void set_labels_editable(gg_ui_t *ui, bool editable);
static void remove_all_ipv4_addrs(void);
static int apply_static_config(gg_ui_t *ui);
static void dhcp_begin(gg_ui_t *ui);
static void set_status(gg_ui_t *ui, const char *text);
/**********************
* STATIC VARIABLES
**********************/
/* Ethernet interface (first/default one) */
static struct net_if *eth_iface;
/* net_mgmt event callbacks. NOTE: one callback per layer — the dispatcher
* requires the event's layer and layer-code to be exactly equal to the
* mask's, so IPv4 (L3) and Ethernet (L2) events need separate callbacks. */
static struct net_mgmt_event_callback ipv4_cb;
static struct net_mgmt_event_callback carrier_cb;
/* Event queue: net_mgmt event thread -> LVGL thread */
K_MSGQ_DEFINE(eth_msgq, sizeof(struct eth_ui_event), 4, 4);
/* Hidden textarea backing the keyboard (holds the text being edited) */
static lv_obj_t *edit_ta;
/* Value label currently being edited (NULL when the keyboard is hidden) */
static lv_obj_t *edit_target;
/* DHCP timeout timer: paused while no DHCP request is in flight; fires once
* (then pauses itself) if no address is obtained within DHCP_TIMEOUT_MS */
static lv_timer_t *dhcp_timeout_timer;
/**********************
* GLOBAL FUNCTIONS
**********************/
void eth_config_init(gg_ui_t *ui)
{
eth_iface = net_if_get_default();
if (eth_iface == NULL) {
LOG_ERR("no default network interface found");
return;
}
net_mgmt_init_event_callback(&ipv4_cb, eth_mgmt_event_handler,
NET_EVENT_IPV4_ADDR_ADD);
net_mgmt_add_event_callback(&ipv4_cb);
net_mgmt_init_event_callback(&carrier_cb, eth_mgmt_event_handler,
NET_EVENT_ETHERNET_CARRIER_ON |
NET_EVENT_ETHERNET_CARRIER_OFF);
net_mgmt_add_event_callback(&carrier_cb);
/* Show the real MAC address of the interface. The generated label is only
* 180 px wide (it was designed for a dotted-quad IP), so let it grow to
* fit the 17-character MAC string */
struct net_linkaddr *la = net_if_get_link_addr(eth_iface);
char mac_str[3 * 6];
lv_obj_set_width(ui->screen.label_mac, LV_SIZE_CONTENT);
snprintf(mac_str, sizeof(mac_str), "%02X:%02X:%02X:%02X:%02X:%02X",
la->addr[0], la->addr[1], la->addr[2],
la->addr[3], la->addr[4], la->addr[5]);
lv_label_set_text(ui->screen.label_mac, mac_str);
/* Sensible defaults for the value labels */
lv_label_set_text(ui->screen.label_IP, DEFAULT_IP);
lv_label_set_text(ui->screen.label_metmask, DEFAULT_NETMASK);
lv_label_set_text(ui->screen.label_gatway, DEFAULT_GATEWAY);
lv_label_set_text(ui->screen.label_dns, DEFAULT_DNS);
/* The keyboard container starts hidden; it pops up when a value label
* is clicked */
lv_obj_add_flag(ui->screen.container_keyboard, LV_OBJ_FLAG_HIDDEN);
/* Hidden textarea backing the keyboard: the keyboard needs a textarea to
* edit, but the design shows the edited text in
* container_keyboard_label_data instead. */
edit_ta = lv_textarea_create(ui->screen.screen);
if (edit_ta == NULL) {
LOG_ERR("textarea create failed (LVGL out of memory?)");
return;
}
lv_obj_add_flag(edit_ta, LV_OBJ_FLAG_HIDDEN);
lv_textarea_set_max_length(edit_ta, IPV4_STR_MAX_LEN);
lv_textarea_set_one_line(edit_ta, true);
lv_keyboard_set_textarea(ui->screen.container_keyboard_keyboard, edit_ta);
/* Value labels open the keyboard when clicked */
lv_obj_add_event_cb(ui->screen.label_IP, value_label_event_cb,
LV_EVENT_CLICKED, ui);
lv_obj_add_event_cb(ui->screen.label_metmask, value_label_event_cb,
LV_EVENT_CLICKED, ui);
lv_obj_add_event_cb(ui->screen.label_gatway, value_label_event_cb,
LV_EVENT_CLICKED, ui);
lv_obj_add_event_cb(ui->screen.label_dns, value_label_event_cb,
LV_EVENT_CLICKED, ui);
lv_obj_add_event_cb(edit_ta, edit_ta_event_cb, LV_EVENT_ALL, ui);
lv_obj_add_event_cb(ui->screen.switch_dhcp, switch_dhcp_event_cb,
LV_EVENT_VALUE_CHANGED, ui);
lv_obj_add_event_cb(ui->screen.button_connect, button_connect_event_cb,
LV_EVENT_CLICKED, ui);
/* DHCP timeout timer (paused until a DHCP request starts) */
dhcp_timeout_timer = lv_timer_create(dhcp_timeout_cb, DHCP_TIMEOUT_MS, ui);
lv_timer_pause(dhcp_timeout_timer);
/* DHCP is on by default: value labels are not editable and an address
* is requested automatically (only if the cable is already plugged in;
* otherwise the carrier-on event will trigger it) */
lv_obj_add_state(ui->screen.switch_dhcp, LV_STATE_CHECKED);
set_labels_editable(ui, false);
if (net_if_flag_is_set(eth_iface, NET_IF_LOWER_UP)) {
dhcp_begin(ui);
} else {
set_status(ui, "Cable Unplugged");
}
/* LVGL-side timer consuming network events (runs in the LVGL thread,
* so it is safe to touch widgets here) */
lv_timer_create(eth_ui_timer_cb, ETH_UI_TIMER_MS, ui);
}
/**********************
* STATIC FUNCTIONS
**********************/
/* net_mgmt event thread: forward state changes to the LVGL thread via the
* message queue (widgets must not be touched here) */
static void eth_mgmt_event_handler(struct net_mgmt_event_callback *cb,
uint64_t mgmt_event, struct net_if *iface)
{
ARG_UNUSED(cb);
ARG_UNUSED(iface);
struct eth_ui_event evt;
switch (mgmt_event) {
case NET_EVENT_IPV4_ADDR_ADD:
LOG_INF("IPv4 address added");
evt.type = ETH_EVT_IP_READY;
k_msgq_put(ð_msgq, &evt, K_NO_WAIT);
break;
case NET_EVENT_ETHERNET_CARRIER_ON:
LOG_INF("Ethernet cable plugged in");
evt.type = ETH_EVT_CARRIER_ON;
k_msgq_put(ð_msgq, &evt, K_NO_WAIT);
break;
case NET_EVENT_ETHERNET_CARRIER_OFF:
LOG_INF("Ethernet cable unplugged");
evt.type = ETH_EVT_CARRIER_OFF;
k_msgq_put(ð_msgq, &evt, K_NO_WAIT);
break;
default:
break;
}
}
/* LVGL timer: drain pending network events and update the UI */
static void eth_ui_timer_cb(lv_timer_t *timer)
{
gg_ui_t *ui = lv_timer_get_user_data(timer);
struct eth_ui_event evt;
while (k_msgq_get(ð_msgq, &evt, K_NO_WAIT) == 0) {
switch (evt.type) {
case ETH_EVT_IP_READY: {
/* Read the current address straight from the interface (the
* event payload is not available without
* CONFIG_NET_MGMT_EVENT_INFO) */
struct net_in_addr *addr =
net_if_ipv4_get_global_addr(eth_iface, NET_ADDR_PREFERRED);
char ip[NET_IPV4_ADDR_LEN];
if (addr != NULL &&
net_addr_ntop(AF_INET, addr, ip, sizeof(ip)) != NULL) {
lv_timer_pause(dhcp_timeout_timer);
lv_label_set_text(ui->screen.label_IP, ip);
set_status(ui, "Connected");
}
break;
}
case ETH_EVT_CARRIER_ON:
/* Cable plugged in: with DHCP on, request an address; with a
* static config the previously set address is still there */
if (lv_obj_has_state(ui->screen.switch_dhcp, LV_STATE_CHECKED)) {
dhcp_begin(ui);
} else if (net_if_ipv4_get_global_addr(eth_iface,
NET_ADDR_PREFERRED) != NULL) {
set_status(ui, "Connected");
} else {
set_status(ui, "Disconnected");
}
break;
case ETH_EVT_CARRIER_OFF:
/* Cable unplugged: stop any ongoing DHCP attempt */
lv_timer_pause(dhcp_timeout_timer);
net_dhcpv4_stop(eth_iface);
set_status(ui, "Cable Unplugged");
break;
default:
break;
}
}
}
/* DHCP timeout (self-pausing): no address arrived within DHCP_TIMEOUT_MS */
static void dhcp_timeout_cb(lv_timer_t *timer)
{
gg_ui_t *ui = lv_timer_get_user_data(timer);
lv_timer_pause(timer);
if (net_if_ipv4_get_global_addr(eth_iface, NET_ADDR_PREFERRED) == NULL) {
LOG_WRN("DHCP timeout, no address obtained");
set_status(ui, "Connect Failed");
}
}
/* Start a fresh DHCP round: drop old addresses, request a new lease and
* arm the timeout timer */
static void dhcp_begin(gg_ui_t *ui)
{
net_dhcpv4_stop(eth_iface);
remove_all_ipv4_addrs();
set_status(ui, "Connecting...");
net_dhcpv4_start(eth_iface);
lv_timer_reset(dhcp_timeout_timer);
lv_timer_resume(dhcp_timeout_timer);
}
/* Value label click: open the keyboard to edit this label */
static void value_label_event_cb(lv_event_t *e)
{
gg_ui_t *ui = lv_event_get_user_data(e);
keyboard_show(ui, lv_event_get_target(e));
}
/* Edit textarea events: mirror the text into label_data and write it back
* to the target label when the keyboard OK key is pressed (LV_EVENT_READY) */
static void edit_ta_event_cb(lv_event_t *e)
{
gg_ui_t *ui = lv_event_get_user_data(e);
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_VALUE_CHANGED) {
lv_label_set_text(ui->screen.container_keyboard_label_data,
lv_textarea_get_text(edit_ta));
} else if (code == LV_EVENT_READY) {
const char *text = lv_textarea_get_text(edit_ta);
struct net_in_addr addr;
if (edit_target == NULL) {
keyboard_hide(ui);
return;
}
if (net_addr_pton(AF_INET, text, &addr) == 0) {
lv_label_set_text(edit_target, text);
keyboard_hide(ui);
} else {
LOG_WRN("invalid IPv4 address: %s", text);
set_status(ui, "Invalid IP");
/* keyboard stays open so the user can fix the input */
}
} else if (code == LV_EVENT_CANCEL) {
keyboard_hide(ui);
}
}
/* DHCP switch: ON grays out the value labels (not editable), OFF enables
* manual editing */
static void switch_dhcp_event_cb(lv_event_t *e)
{
gg_ui_t *ui = lv_event_get_user_data(e);
bool dhcp_on = lv_obj_has_state(ui->screen.switch_dhcp, LV_STATE_CHECKED);
set_labels_editable(ui, !dhcp_on);
if (!dhcp_on) {
set_status(ui, "Disconnected");
}
}
/* Connect button: apply the configuration */
static void button_connect_event_cb(lv_event_t *e)
{
gg_ui_t *ui = lv_event_get_user_data(e);
bool dhcp_on = lv_obj_has_state(ui->screen.switch_dhcp, LV_STATE_CHECKED);
keyboard_hide(ui);
/* Restart from a clean slate */
net_dhcpv4_stop(eth_iface);
remove_all_ipv4_addrs();
if (dhcp_on) {
dhcp_begin(ui);
} else {
if (apply_static_config(ui) == 0) {
set_status(ui, "Connected");
} else {
set_status(ui, "Connect Failed");
}
}
}
/* Show the keyboard container and start editing target_label */
static void keyboard_show(gg_ui_t *ui, lv_obj_t *target_label)
{
const char *text = lv_label_get_text(target_label);
edit_target = target_label;
lv_textarea_set_text(edit_ta, text);
lv_label_set_text(ui->screen.container_keyboard_label_data, text);
lv_obj_remove_flag(ui->screen.container_keyboard, LV_OBJ_FLAG_HIDDEN);
}
/* Hide the keyboard container and end the current edit */
static void keyboard_hide(gg_ui_t *ui)
{
edit_target = NULL;
lv_obj_add_flag(ui->screen.container_keyboard, LV_OBJ_FLAG_HIDDEN);
}
/* Enable/disable editing of the four value labels; grayed out when not
* editable (DHCP on) */
static void set_labels_editable(gg_ui_t *ui, bool editable)
{
lv_obj_t *labels[] = {
ui->screen.label_IP,
ui->screen.label_metmask,
ui->screen.label_gatway,
ui->screen.label_dns,
};
lv_color_t color = lv_color_hex(editable ? COLOR_VALUE_NORMAL
: COLOR_VALUE_GRAYED);
for (size_t i = 0; i < ARRAY_SIZE(labels); i++) {
lv_obj_set_style_text_color(labels[i], color,
LV_PART_MAIN | LV_STATE_DEFAULT);
if (editable) {
lv_obj_add_flag(labels[i], LV_OBJ_FLAG_CLICKABLE);
} else {
lv_obj_remove_flag(labels[i], LV_OBJ_FLAG_CLICKABLE);
}
}
}
/* Remove all IPv4 unicast addresses from the interface */
static void remove_all_ipv4_addrs(void)
{
struct net_if_ipv4 *ipv4 = eth_iface->config.ip.ipv4;
if (ipv4 == NULL) {
return;
}
for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) {
if (ipv4->unicast[i].ipv4.is_used &&
ipv4->unicast[i].ipv4.address.family == AF_INET) {
net_if_ipv4_addr_rm(eth_iface,
&ipv4->unicast[i].ipv4.address.in_addr);
}
}
}
/* Program the manually entered static configuration into the interface.
* Returns 0 on success, <0 on error. */
static int apply_static_config(gg_ui_t *ui)
{
struct net_in_addr addr, netmask, gw;
const char *dns_str = lv_label_get_text(ui->screen.label_dns);
if (net_addr_pton(AF_INET, lv_label_get_text(ui->screen.label_IP),
&addr) != 0 ||
net_addr_pton(AF_INET, lv_label_get_text(ui->screen.label_metmask),
&netmask) != 0 ||
net_addr_pton(AF_INET, lv_label_get_text(ui->screen.label_gatway),
&gw) != 0 ||
net_addr_pton(AF_INET, dns_str, &(struct net_in_addr){0}) != 0) {
LOG_ERR("invalid static configuration");
return -EINVAL;
}
if (net_if_ipv4_addr_add(eth_iface, &addr, NET_ADDR_MANUAL, 0) == NULL) {
LOG_ERR("failed to add IPv4 address");
return -ENOMEM;
}
net_if_ipv4_set_netmask_by_addr(eth_iface, &addr, &netmask);
net_if_ipv4_set_gw(eth_iface, &gw);
const char *servers[] = { dns_str, NULL };
if (dns_resolve_reconfigure(dns_resolve_get_default(), servers, NULL,
DNS_SOURCE_MANUAL) != 0) {
LOG_ERR("failed to set DNS server");
return -EIO;
}
LOG_INF("static config applied: IP %s", lv_label_get_text(ui->screen.label_IP));
return 0;
}
static void set_status(gg_ui_t *ui, const char *text)
{
lv_label_set_text(ui->screen.label_status, text);
}
eth_config.h
/*
* Copyright 2026 NXP
* NXP Proprietary. This software is owned or controlled by NXP and may only be used strictly in
* accordance with the applicable license terms. By expressly accepting such terms or by downloading, installing,
* activating and/or otherwise using the software, you are agreeing that you have read, and that you agree to
* comply with and are bound by, such license terms. If you do not agree to be bound by the applicable license
* terms, then you may not retain, install, activate or otherwise use the software.
*/
#ifndef ETH_CONFIG_H
#define ETH_CONFIG_H
#ifdef __cplusplus
extern "C" {
#endif
#include "../generated/gui_guider.h"
/**
* Initialize the Ethernet network configuration UI logic on screen.
*
* Wires up the widgets created by the generated code:
* - label_IP / label_metmask / label_gatway / label_dns: click to edit the
* value with the on-screen keyboard; container_keyboard pops up, the text
* being edited is mirrored into container_keyboard_label_data and is only
* written back to the label when the keyboard OK key is pressed.
* - switch_dhcp: ON = obtain address automatically (the four value labels
* are grayed out and not editable), OFF = use the manually entered
* static configuration.
* - button_connect: applies the configuration (starts DHCP, or programs the
* static IP/netmask/gateway/DNS into the network interface).
* - label_status shows the connection state and label_mac shows the real
* MAC address of the interface.
*
* Must be called once from custom_init() on the LVGL thread.
*
* @param ui Pointer to the GUI Guider UI struct.
*/
void eth_config_init(gg_ui_t *ui);
#ifdef __cplusplus
}
#endif
#endif /* ETH_CONFIG_H */
在头文件中,只需要一个eth_config_init暴跌给custom.c调用就行了。
效果展示



打开终端ping 一下路由,以及用PC向开发板Ping,延时都非常低。




在非DHCP中,点击网络地址以及其他的,都可以唤出对象进行输入:


拨插网线,可以实时侦测到断线与联网,在连接状态中实时反馈:

我要赚赏金
