以太网(LAN8720A,RMII)Zephyr 移植记录
目标平台:CPKCOR-RA8D1B + CPKEXP-EKRA8X1,Zephyr 4.4.0。
成果:DHCP 自动获取 IP(实测 192.168.3.4),RTT shell 可 net ping 通网关,IP 同步显示在 LCD 上。
网络链路
应用(socket / net shell) → Zephyr 原生 TCP/IP 栈(net_if / IPv4 / DHCPv4) → ETHERC + EDMAC(eth,renesas,ra-ethernet) 老式控制器,非 RA8x2 的 RMAC → MDIO/PHY 子系统(mdio,renesas,ra-mdio + ethernet-phy) → LAN8720AI PHY(RMII,百兆)
SoC dtsi(zephyr/dts/arm/renesas/ra/ra8/ra8x1.dtsi)已定义 eth(0x40354100)与mdio 节点,均 disabled,phy-connection-type = "rmii" 已默认。
驱动:zephyr/drivers/ethernet/eth_renesas_ra.c(FSP r_ether)。注意不是 eth_renesas_ra_rmac——RMAC 属于 RA8x2 系列(ra8x2.dtsi),RA8D1 不适用。
pinctrl 挂在 mdio 节点上(binding 强制要求),eth 节点只需phy-handle + local-mac-address + status。
关键参数(均已真机验证)
RMII 引脚("_B" 组,与 ek_ra8m1 完全一致)
信号引脚信号引脚
| MDC | P401 | TXD0 | P700 |
| MDIO | P402 | REF50CK(输入) | P701 |
| LINKSTA | P403 | RXD0 | P702 |
| TX_EN | P405 | RXD1 | P703 |
| TXD1 | P406 | RX_ER / CRS_DV | P704 / P705 |
pinctrl 分组照抄 ek_ra8m1-pinctrl.dtsi 的 ether_default(RA_PSEL_ETH_RMII, drive-strength = high),引脚与 CPKEXP 原理图逐网核对一致。
PHY
项值
| 芯片 | LAN8720AI(PHY ID 0x7C0F1) |
| MDIO 地址 | 0(PHYAD0 默认内部下拉) |
| nRST | P510,扩展板外接下拉,固件必须拉高(见坑 1) |
| REFCLK | PHY 侧 25MHz 晶振倍频出 50MHz,经跳线送 P701(REFCLKO 模式) |
| 复位时序 | reset-gpios 拉低 1ms,释放后等 100ms 再走 MDIO 软复位 |
硬件前提(跳线)
拔下 CEU 摄像头模组(RMII 与 CEU 管脚复用,使用 SDRAM 时二选一)
接好扩展板上全部 10 个 RMII 跳线(RXD1/RXD0/RXER/RXDV/MDIO/MDC/REFCLK/TXEN/TXD0/TXD1),含 REFCLK-P701——"断开 REFCLK" 是 CEU 摄像头场景的要求,用以太网时恰恰相反

overlay 配置(boards/cpkcor_ra8d1b.overlay)
&ioport5 {
status = "okay"; /* P510 = PHY nRST 用,不开会编译报 __device_dts_ord 错误 */
};
ð {
local-mac-address = [02 00 00 00 00 01]; /* LAA 本地管理地址 */
phy-handle = <&phy>;
status = "okay";
};
&mdio {
pinctrl-0 = <ðer_default>;
pinctrl-names = "default";
status = "okay";
phy: ethernet-phy@0 {
compatible = "ethernet-phy";
reg = <0>;
reset-gpios = <&ioport5 10 GPIO_ACTIVE_LOW>;
reset-assert-duration-us = <1000>;
reset-deassertion-timeout-ms = <100>;
status = "okay";
};
};
/* + &pinctrl 里的 ether_default 分组(见上表,照抄 ek_ra8m1) */prj.conf 关键项
CONFIG_NETWORKING=y CONFIG_NET_L2_ETHERNET=y CONFIG_MDIO=y # 初始化顺序:MDIO(50) < PHY(55) < eth 驱动(默认 60),否则 check_init_priorities 报错 CONFIG_MDIO_INIT_PRIORITY=50 CONFIG_PHY_INIT_PRIORITY=55 CONFIG_ENTROPY_GENERATOR=y # DHCP 需要随机数(板 dts 已 chosen trng) CONFIG_NET_IPV4=y / ARP / UDP / TCP, CONFIG_NET_IPV6=n CONFIG_NET_DHCPV4=y CONFIG_NET_MGMT=y / NET_MGMT_EVENT=y / NET_SHELL=y / NET_LOG=y
应用侧:DHCP 不会自动启动,必须显式调用(参照 samples/net/dhcpv4_client):
net_dhcpv4_start(net_if_get_default());
拿到地址用 net_mgmt 订阅 NET_EVENT_IPV4_ADDR_ADD 事件获知(回调签名第二参数 Zephyr 4.4 起为 uint64_t)。
主代码main.c
/*
* CPKCOR-RA8D1B + CPKEXP-EKRA8X1:LVGL 显示演示 + LED 心跳
*
* 硬件连接:
* - LCD:H0233S001 模组(222x480,ST7796U 驱动 IC),MIPI-DSI 接口
* (专用 D-PHY 引脚,1 lane video mode),显示链路为
* GLCDC -> MIPI-DSI host -> ST7796U
* - LCD 复位:P000(低有效,panel 驱动管理)
* - LCD 背光:P001(高有效,GLCDC 节点 backlight-gpios 管理)
* - 显存:SDRAM(0x68000000),GLCDC 帧缓冲行步长 64 字节对齐为 224px;
* LVGL 渲染 buffer 为静态分配(10% 屏大小),flush 后经 GLCDC 驱动
* 逐行 memcpy 进对齐显存
* - 用户 LED1:PA01(绿色,高电平点亮)
* - 以太网:扩展板 LAN8720AI PHY(RMII 百兆,"_B" 组引脚 P401/P402/
* P405/P406/P700~P705),Zephyr 原生 TCP/IP 栈,DHCP 自动获取 IP;
* RMII 与 CEU 摄像头管脚复用,使用时须拔下摄像头并改跳线
*
* UI 内容:标题 + RGB 三色块(验证颜色通道) + 进度条动画 + DHCP 获取的
* IP 地址显示;每 500ms 翻转 LED 并打印心跳(含 DSI host 状态检查)。
*/
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/display.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/net/net_if.h>
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/net_event.h>
#include <zephyr/net/net_ip.h>
#include <zephyr/net/dhcpv4.h>
#include <lvgl.h>
/* 诊断:MIPI DSI host 状态寄存器(基址 0x40346000)
* LINKSR(0x10):bit8 VRUN = 视频模式运行中
* VMSR(0x410):bit22 VBUFUDF = 视频 FIFO 下溢,bit23 VBUFOVF = 上溢 */
#define DSI_LINKSR (*(volatile uint32_t *)0x40346010UL)
#define DSI_VMSR (*(volatile uint32_t *)0x40346410UL)
#define DSI_VMSCR (*(volatile uint32_t *)0x40346414UL)
#define LED_PERIOD_MS 500
static const struct device *display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
static lv_obj_t *bar;
static lv_obj_t *count_label;
static lv_obj_t *ip_label;
/* DHCP/IPv4 地址就绪标志与字符串(网络事件回调置位,主循环刷 UI) */
static volatile bool ip_ready;
static char ip_str[NET_IPV4_ADDR_LEN] = "waiting DHCP...";
static struct net_mgmt_event_callback ip_cb;
/* IPv4 地址添加(DHCP 成功)回调:只取地址置标志,不碰 LVGL */
static void ipv4_addr_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_event,
struct net_if *iface)
{
struct in_addr *addr;
if (mgmt_event != NET_EVENT_IPV4_ADDR_ADD) {
return;
}
addr = net_if_ipv4_get_global_addr(iface, NET_ADDR_PREFERRED);
if (addr == NULL) {
return;
}
net_addr_ntop(AF_INET, addr, ip_str, sizeof(ip_str));
ip_ready = true;
printk("DHCP got IPv4 addr: %s\n", ip_str);
}
/* RGB 三色块,验证颜色通道是否正确(BGR/MADCTL 配置) */
static void create_color_swatch(lv_obj_t *parent, lv_color_t color, lv_coord_t x)
{
lv_obj_t *rect = lv_obj_create(parent);
lv_obj_set_size(rect, 60, 40);
lv_obj_set_pos(rect, x, 60);
lv_obj_set_style_bg_color(rect, color, 0);
lv_obj_set_style_border_width(rect, 0, 0);
lv_obj_set_style_radius(rect, 4, 0);
}
static void ui_init(void)
{
lv_obj_t *scr = lv_screen_active();
lv_obj_t *title;
title = lv_label_create(scr);
lv_label_set_text(title, "CPK-RA8D1 LVGL");
lv_obj_align(title, LV_ALIGN_TOP_MID, 0, 10);
create_color_swatch(scr, lv_color_hex(0xFF0000), 6);
create_color_swatch(scr, lv_color_hex(0x00FF00), 81);
create_color_swatch(scr, lv_color_hex(0x0000FF), 156);
bar = lv_bar_create(scr);
lv_obj_set_size(bar, 190, 16);
lv_obj_align(bar, LV_ALIGN_TOP_MID, 0, 130);
lv_bar_set_range(bar, 0, 100);
count_label = lv_label_create(scr);
lv_label_set_text(count_label, "count: 0");
lv_obj_align(count_label, LV_ALIGN_TOP_MID, 0, 160);
ip_label = lv_label_create(scr);
lv_label_set_text(ip_label, ip_str);
lv_obj_align(ip_label, LV_ALIGN_TOP_MID, 0, 190);
}
int main(void)
{
uint32_t count = 0;
int ret;
if (!device_is_ready(display_dev)) {
printk("display device not ready\n");
return 0;
}
if (gpio_is_ready_dt(&led)) {
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
if (ret != 0) {
printk("led0 cfg failed ret=%d\n", ret);
}
}
display_blanking_off(display_dev);
/* 订阅 IPv4 地址事件;显式启动 DHCP 客户端(参照 dhcpv4_client 例程) */
net_mgmt_init_event_callback(&ip_cb, ipv4_addr_handler, NET_EVENT_IPV4_ADDR_ADD);
net_mgmt_add_event_callback(&ip_cb);
net_dhcpv4_start(net_if_get_default());
ui_init();
printk("ra8d1_blink lvgl demo start\n");
while (1) {
/* LVGL 任务处理 */
lv_bar_set_value(bar, (count / 2) % 101, LV_ANIM_ON);
lv_label_set_text_fmt(count_label, "count: %u", count);
if (ip_ready) {
lv_label_set_text(ip_label, ip_str);
ip_ready = false;
}
lv_timer_handler();
gpio_pin_toggle_dt(&led);
++count;
/* DSI host 状态检查:异常时打印 */
uint32_t vmsr = DSI_VMSR;
if (vmsr & (BIT(20) | BIT(22) | BIT(23))) {
printk("dsi vmsr=%08lx%s%s%s\n", (unsigned long)vmsr,
(vmsr & BIT(20)) ? " TIMERR" : "",
(vmsr & BIT(22)) ? " VBUFUDF" : "",
(vmsr & BIT(23)) ? " VBUFOVF" : "");
DSI_VMSCR = vmsr;
}
k_msleep(LED_PERIOD_MS);
}
return 0;
}踩坑记录
1. PHY 完全无应答:phy_mii: Failed to reset PHY (x): -116
根因:扩展板把 PHY 的 nRST(P510)外接下拉电阻——出厂缺省(CEU 摄像头模式) PHY 一直被硬件复位,MDIO 自然无任何响应,换哪个地址(0/1)都一样超时(-116 = ETIMEDOUT)。解决:PHY 节点加 reset-gpios(phy_mii 驱动支持,先硬复位脉冲再软复位),并启用ioport5。LAN8720 的 RXER/PHYAD0 默认下拉,地址就是 0,不用猜。
2. 链路正常但永远拿不到 IP
现象:link up 100Mb 全双工,但无任何 dhcpv4 日志。根因:Zephyr 的 DHCPv4 客户端 不会随接口 up 自动首次启动(首次启动由 net_config 库或应用负责),只配置了CONFIG_NET_DHCPV4=y 没人调用 start。解决:应用里显式 net_dhcpv4_start()。 排障手段:CONFIG_NET_DHCPV4_LOG_LEVEL_DBG=y + CONFIG_ETHERNET_LOG_LEVEL_DBG=y能清楚看到 DISCOVER/OFFER 交互,定位完记得关掉。 (附:D-Cache 不是问题,本板 CONFIG_DCACHE 默认未开。)
3. 设备初始化优先级校验失败
ERROR: /soc/ethernet@40354100 is initialized before its dependency ethernet-phy@0。 eth/MDIO/PHY 三者 *_INIT_PRIORITY 默认都是 60,链接顺序不保证依赖顺序。解决:MDIO=50 < PHY=55 < eth(60)。
4. uart3 虚拟串口无输出
板 dts 控制台是 uart3(SCI3,P409),文档称经 R207/R208 接 J-Link OB 的 USB CDC, 但实测 COM 口(COM44)无任何数据(复位抓 boot banner 也没有),原因未查。解决:按项目惯例控制台走 RTT——CONFIG_USE_SEGGER_RTT=y、CONFIG_RTT_CONSOLE=y、CONFIG_UART_CONSOLE=n。RTT Viewer 连接参数:device R7FA8D1BH,SWD; 控制块地址 0x22000410(自动搜索一般能找到)。
5. RTT shell 与 LOG_BACKEND_RTT 冲突
shell 切 RTT(CONFIG_SHELL_BACKEND_RTT=y)后编译报static assertion failed: Conflicting log RTT backend enabled on the same channel。 shell RTT 与 LOG_BACKEND_RTT 不能同用 RTT 通道 0。解决:关掉 LOG_BACKEND_RTT,日志改走 shell 后端(CONFIG_SHELL_LOG_BACKEND=y), 日志和 shell 命令在同一个 RTT 终端里输出,互不影响。
6. DHCP 日志里的 "option unknown" 不是错误
option unknown: 6(DNS 服务器)/ 213(厂商选项)只是 DBG 提示解析器不处理这些 选项,IP/掩码/网关/租期均正常。需要 DHCP 下发 DNS 时再开 CONFIG_DNS_RESOLVER 相关选项。
验证方法
west build -b cpkcor_ra8d1b app/ra8d1_blink + west flash
RTT Viewer 看启动日志:phy_mii: PHY (0) ID 7C0F1 → Link speed 100 Mb, full duplex→ DHCP got IPv4 addr: ...
shell(同一 RTT 终端):net iface / net ping 192.168.3.1


4. 从 PC ping <板子IP> 反向验证

5. 在lvgl屏上也显示出获取到的当前IP地址

【工程源代码】
我要赚赏金
