这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【FRDMRW612系列】ZephyrLVGL网卡配置实战

共1条 1/1 1 跳转至

【FRDMRW612系列】ZephyrLVGL网卡配置实战

高工
2026-08-14 18:47:22     打赏

zephyr rots frdm_rw512 ethernet

工程创建

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

image-20260814103223759.png

界面设计

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

image-20260814181316159image-20260814181316159.png

image-20260814181331527image-20260814181331527.png

组件列表如下:

image-20260814181350187image-20260814181350187.png

生成代码后,导出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(&eth_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(&eth_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(&eth_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(&eth_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调用就行了。

效果展示

image-20260814183559443image-20260814183559443.png

image-20260814183617404

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

image-20260814183724285image-20260814183724285.png

image-20260814183807976image-20260814183807976.png

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

image-20260814183934313image-20260814183934313.png

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

image-20260814184103994.png





关键词: FRDMRW612     Zephyr     网卡    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]