14 Ethernet(以太网)
1 以太网介绍
1.1 rk3568 网卡介绍
提到网络,很多朋友肯定听说过“网卡”一词,因为以前的电脑如果需要上网,就要和现在的独显台式电脑一样买一个网卡插上以后才可以上网。原因是随着技术的不断发展,现在只需要一个芯片就可以实现有线网卡功能,因此网卡芯片都直接放到了主板上(一般来说是跟在网线接口的后面)。
嵌入式网络硬件分为两部分: MAC 和 PHY ,有些 SoC 说自己支持网络,说的就是他内部集成网络 MAC 外设,一般常见的通用 SoC 都会集成网络 MAC 外设 ,比如RK系列、I.MX系列和STM32MP1系列等,集成进去的好处是可以显著提升网速(比如使用网络专用的DMA),可以支持到10/100/1000M网速,外接一个PHY芯片即可 (如果没有集成MAC外设,大多使用MAC+PHY 一体的网络芯片),MAC 通过 MII/RMII 接口或者 GMII/RGMII 接口来与外部的 PHY 芯片连接,完成网络数据传输。
RK3568 内核集成了两个 10M/100M/1000M 的网络 MAC,符合 IEEE802.3-2002 标准, MAC层支持全双工或者半双工模式下运行。 MAC 可编程,有直接存储器接口的专用 DMA,将数据格式化符合 IEEE802.3-2002 标准的数据包,并把这些数据传输到以太网的物理接口中(PHY),还可以把数据包从 RXFIFO 移动到微处理器的存储器中。RK3568 内部 ENET 外设主要特性如下:
- ①、支持全工和半双工操作。
- ②、全双工流控制操作(IEEE 802.3X 暂停包和优先级流控制)
- ③、报头和帧起始数据(SFD)在发送模式下自动插入、在接收中自动删除。
- ④、可逐帧控制 CRC 和 pad 自动生成
- ⑤、可编程数据包长度,支持标准以太网数据包或高达 16KB 的巨型以太网数据包
- ⑥、可编程数据包间隙
- ⑦、两组 FIFO:一个具有可编程阈值功能的 4096 字节发送 FIFO 和一个具有可配置阈值功能的 4096 字节接收 FIFO
1.2 IP地址、子网掩码、默认网关、DNS服务器基本介绍
计算机要实现网络通信,就必须要有一个用于快速定位的网络地址,IP是32位二进制数据,通常以十进制表示,并以“.”分隔。IP地址就是计算机在网络中的唯一身份ID,与现实世界中快递的配送需要有具体的住宅地址是一个道理,IP地址 = 网络地址 + 主机地址(又称:主机号和网络号组成)
那IP地址的网络地址和主机地址怎么区分呢?就要用到子网掩码,子网掩码的值为255时,对应位的IP地址为网络地址位,比如IP地址:192.168.22.88 ,子网掩码位 255.255.255.0 ,那192.168.22 就是网络地址。网络地址用于确定找到你所在的局域网比如找到你家或者公司的网络,主机地址用于确定你所局域网的位置,比如说192.168.22.88 是你加客厅的电视的网络IP,192.168.22.66是你家电脑的网络IP。在断网的情况下,局域网中的设备是可以通信的,比如说手机连接路由器的网络以后就可以输入IP进入到其后台进行管理。
而网关就可以理解为把数据从局域网发到外接的工具,具体解析过程不做介绍。
由于IP地址不好记,我们上网通常输入的是一个网址,比如“baidu.com",DNS的功能就是把这个网址名和IP对应起来,只需要输入网址就可以进入到对应IP的服务器。
2 以太网板卡接口

3 以太网使用---命令行方式
3.1 设备树解析
提示
下文的文件路径:out/kernel/src_tmp/linux-5.10/arch/arm64/boot/dts/rockchip 需要先编译码源。
本工程中以太网节点的配置按照设备树的三层结构如下:
基础定义层,以gmac0为例( rk3568.dtsi ):
gmac0: ethernet@fe2a0000 {
compatible = "rockchip,rk3568-gmac", "snps,dwmac-4.20a";
reg = <0x0 0xfe2a0000 0x0 0x10000>;
interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "macirq", "eth_wake_irq";
rockchip,grf = <&grf>;
clocks = <&cru SCLK_GMAC0>, <&cru SCLK_GMAC0_RX_TX>,
<&cru SCLK_GMAC0_RX_TX>, <&cru CLK_MAC0_REFOUT>,
<&cru ACLK_GMAC0>, <&cru PCLK_GMAC0>,
<&cru SCLK_GMAC0_RX_TX>, <&cru CLK_GMAC0_PTP_REF>,
<&cru PCLK_XPCS>;
clock-names = "stmmaceth", "mac_clk_rx",
"mac_clk_tx", "clk_mac_refout",
"aclk_mac", "pclk_mac",
"clk_mac_speed", "ptp_ref",
"pclk_xpcs";
resets = <&cru SRST_A_GMAC0>;
reset-names = "stmmaceth";
snps,mixed-burst;
snps,tso;
status = "disabled";
mdio0: mdio {
compatible = "snps,dwmac-mdio";
#address-cells = <0x1>;
#size-cells = <0x0>;
};
};compatible: 指定兼容性,支持RK3568 GMAC和标准DWC以太网MAC 4.20areg: 寄存器地址范围(0xfe2a0000-0xfe2affff)interrupts: MAC中断号27和唤醒中断号24,均为高电平触发clocks: 包含9个时钟源,涵盖MAC核心时钟、RX/TX时钟、参考时钟等snps,mixed-burst: 启用混合突发传输模式snps,tso: 启用TCP分段卸载功能mdio0: 内嵌MDIO总线控制器,用于PHY管理status: 默认禁用状态
引脚配置层( rk3568-pinctrl.dtsi ):
//GMAC0 RGMII模式引脚配置:
gmac0_miim: gmac0-miim {
rockchip,pins =
/* gmac0_mdc */
<2 RK_PC3 2 &pcfg_pull_none>,
/* gmac0_mdio */
<2 RK_PC4 2 &pcfg_pull_none>;
};
gmac0_rgmii_clk: gmac0-rgmii-clk {
rockchip,pins =
/* gmac0_rxclk */
<2 RK_PA5 2 &pcfg_pull_none>,
/* gmac0_txclk */
<2 RK_PB0 2 &pcfg_pull_none_drv_level_1>;
};
gmac0_rgmii_bus: gmac0-rgmii-bus {
rockchip,pins =
/* gmac0_rxd2 */
<2 RK_PA3 2 &pcfg_pull_none>,
/* gmac0_rxd3 */
<2 RK_PA4 2 &pcfg_pull_none>,
/* gmac0_txd2 */
<2 RK_PA6 2 &pcfg_pull_none_drv_level_2>,
/* gmac0_txd3 */
<2 RK_PA7 2 &pcfg_pull_none_drv_level_2>;
};
gmac0_tx_bus2: gmac0-tx-bus2 {
rockchip,pins =
/* gmac0_txd0 */
<2 RK_PB3 1 &pcfg_pull_none_drv_level_2>,
/* gmac0_txd1 */
<2 RK_PB4 1 &pcfg_pull_none_drv_level_2>,
/* gmac0_txen */
<2 RK_PB5 1 &pcfg_pull_none>;
};
...........
//GMAC1提供两种引脚配置模式:
/* M0模式 - 使用GPIO3组 */
gmac1m0_rgmii_clk: gmac1m0-rgmii-clk {
rockchip,pins =
/* gmac1_rxclkm0 */
<3 RK_PA7 3 &pcfg_pull_none>,
/* gmac1_txclkm0 */
<3 RK_PA6 3 &pcfg_pull_none_drv_level_1>;
};
/* M1模式 - 使用GPIO4组 */
gmac1m1_miim: gmac1m1-miim {
rockchip,pins =
/* gmac1_mdcm1 */
<4 RK_PB6 3 &pcfg_pull_none>,
/* gmac1_mdiom1 */
<4 RK_PB7 3 &pcfg_pull_none>;
};gmac0: 使用GPIO2组引脚,支持RGMII千兆以太网gmac1m0: 使用GPIO3组引脚,适用于单网口设计gmac1m1: 使用GPIO4组引脚,适用于双网口设计
最后看板级配置层( rk3568-toybrick-x0.dtsi ):
&gmac0 {
phy-mode = "rgmii";
clock_in_out = "output";
snps,reset-gpio = <&gpio2 RK_PD3 GPIO_ACTIVE_LOW>;
snps,reset-active-low;
snps,reset-delays-us = <0 20000 100000>;
assigned-clocks = <&cru SCLK_GMAC0_RX_TX>, <&cru SCLK_GMAC0>;
assigned-clock-parents = <&cru SCLK_GMAC0_RGMII_SPEED>, <&cru CLK_MAC0_2TOP>;
assigned-clock-rates = <0>, <125000000>;
pinctrl-names = "default";
pinctrl-0 = <&gmac0_miim
&gmac0_tx_bus2
&gmac0_rx_bus2
&gmac0_rgmii_clk
&gmac0_rgmii_bus>;
tx_delay = <0x2d>;
rx_delay = <0x13>;
phy-handle = <&rgmii_phy0>;
status = "okay";
};
&mdio0 {
rgmii_phy0: phy@0 {
compatible = "ethernet-phy-ieee802.3-c22";
reg = <0x0>;
};
};&gmac0: 引用基础定义中的gmac0节点phy-mode = "rgmii": 配置为RGMII千兆以太网模式clock_in_out = "output": 时钟输出模式snps,reset-gpio: PHY复位引脚使用GPIO2_D3,低电平有效snps,reset-delays-us: 复位时序:0ms预延时,20ms复位持续时间,100ms复位后延时assigned-clock-rates: 设置GMAC时钟频率为125MHz- tx_delay/rx_delay : RGMII接口的发送/接收延时补偿
- phy-handle : 关联到rgmii_phy0 PHY设备
- rgmii_phy0 : IEEE 802.3标准兼容的PHY芯片,MDIO地址为0
3.2 应用层测试网络的方法
在开发板中,可以使用如下常用命令测试网络(注:x.x.x.x 为网卡地址):
ifconfig # 查看设备中的网络接口
ping x.x.x.x # 通过与特定网络交换数据包,测试网络是否正常
ifconfig eth0 X.X.X.X up # 打开网络
ifconfig eth0 X.X.X.X down # 关闭网络3.3 网络测试功能的具体演示
我们以以太网接口1为例,连接网线进行测试:

插好后在终端输入指令 ifconfig 查看网口信息:

系统成功识别到两个以太网接口,并可以看到插入网线的接口处被分配了一个网络IP,并显示了广播地址,子网掩码等信息。
下面输入指令 ping baidu.com 对网络连接进行测试:

发送三组数据,无丢包,此时系统已经可以成功连接以太网。
4. 以太网使用---官方库方式
资料路径
hap包:\05-开发资料\01-OpenHarmory 开发资料\外设测试APP\HAP\NET_TEST.hap
码源:\05-开发资料\01-OpenHarmory 开发资料\外设测试APP\SRC\NATEWORK_TEST
我们这里还为大家写了一个网络测试的历程,不过使用的是官方网络库@kit.NetworkKit,本质上也是NAPI,不过 OpenHarmony 官方已经为我们做好了封装,只需要调用即可。
由于 OpenHarmony 官方 ArkTS 网络库已经足够我们的网络开发需求。这次我们使用自带的网络库进行编写,可以极大的提高我们的开发速度同时也为大家拓展一种开发方式!
4-1 测试APP实现的效果
下面来看一下测试APP实现的效果:
第一栏中会显示网络的连接状态、连接类型和本机的IP地址

点击网络测试,会发送一个 HTTP 请求对网络进行测试

下面对涉及的几个相关函数进行介绍:
4.2 网络相关API函数介绍
1. connection.getDefaultNet()
- 功能:获取当前设备的默认网络连接句柄
- 返回值:
NetHandle对象(表示活动网络连接)
2. connection.getNetCapabilities(netHandle)
- 功能:获取网络能力信息
- 返回值:
bearerTypes:网络承载类型数组
3. connection.getConnectionProperties(netHandle)
- 功能:获取网络连接属性
4. http.createHttp()
- 功能:创建HTTP请求对象
5. httpRequest.request(url, options)
- 功能:发送HTTP请求
6. httpRequest.destroy()
- 功能:销毁HTTP请求对象
4.3 网络测试APP的代码实现
这里还是为大家贴上ets代码,供有需要的朋友学习:
import { hilog } from '@kit.PerformanceAnalysisKit';
import { connection } from '@kit.NetworkKit';
import { socket } from '@kit.NetworkKit';
import { http } from '@kit.NetworkKit';
const DOMAIN = 0x0000;
@Entry
@Component
struct Index {
@State currentIpAddress: string = '获取中...';
@State networkMessage: string = 'Network Test Ready';
@State networkResult: string = '';
@State isNetworkLoading: boolean = false;
@State targetHost: string = 'www.baidu.com';
@State connectionType: string = '未知';
@State isConnected: boolean = false;
build() {
Row() {
Column() {
Text('ShiMate Pi')
.fontSize(40)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
Text('网络信息与测试')
.fontSize(30)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 10 })
// 网络状态信息卡片
Column() {
Text('网络状态信息')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 15 })
Row() {
Text('连接状态: ')
.fontSize(18)
.fontWeight(FontWeight.Medium)
Text(this.isConnected ? '已连接' : '未连接')
.fontSize(18)
.fontColor(this.isConnected ? Color.Green : Color.Red)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 10 })
Row() {
Text('连接类型: ')
.fontSize(18)
.fontWeight(FontWeight.Medium)
Text(this.connectionType)
.fontSize(18)
.fontColor(Color.Blue)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 10 })
Row() {
Text('本机IP地址: ')
.fontSize(18)
.fontWeight(FontWeight.Medium)
Text(this.currentIpAddress)
.fontSize(18)
.fontColor(Color.Blue)
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 15 })
Button('刷新网络信息')
.fontSize(16)
.width('100%')
.height(40)
.onClick(() => {
this.getNetworkInfo();
})
}
.width('90%')
.padding(20)
.backgroundColor(Color.White)
.borderRadius(10)
.border({ width: 1, color: Color.Gray })
.margin({ bottom: 30 })
// 网络测试部分
Divider()
.width('90%')
.margin({ bottom: 20 })
Text('网络连接测试')
.fontSize(30)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
// 目标主机输入框
Row() {
Text('目标主机: ')
.fontSize(18)
.fontWeight(FontWeight.Medium)
TextInput({ placeholder: '请输入主机地址', text: this.targetHost })
.fontSize(16)
.width('60%')
.onChange((value: string) => {
this.targetHost = value;
})
}
.width('90%')
.justifyContent(FlexAlign.SpaceBetween)
.margin({ bottom: 20 })
Text(this.networkMessage)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 })
Button('执行网络测试')
.fontSize(20)
.width('80%')
.height(50)
.enabled(!this.isNetworkLoading)
.onClick(() => {
this.runNetworkTest();
})
.margin({ bottom: 20 })
if (this.isNetworkLoading) {
Text('正在执行网络测试...')
.fontSize(16)
.fontColor(Color.Blue)
.margin({ bottom: 10 })
}
if (this.networkResult) {
Column() {
Text('网络测试结果:')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 5 })
Scroll() {
Text(this.networkResult)
.fontSize(14)
.fontColor(Color.Black)
.backgroundColor(Color.Gray)
.padding(10)
.borderRadius(5)
.width('100%')
.textAlign(TextAlign.Start)
}
.width('100%')
.height(200)
.scrollable(ScrollDirection.Vertical)
.scrollBar(BarState.Auto)
}
.width('90%')
}
}
.width('100%')
.justifyContent(FlexAlign.Start)
.alignItems(HorizontalAlign.Center)
.padding({ top: 10 })
}
.height('100%')
}
aboutToAppear() {
// 页面加载时获取网络信息
this.getNetworkInfo();
}
private async getNetworkInfo() {
try {
// 获取网络连接状态
const netHandle = await connection.getDefaultNet();
if (netHandle) {
this.isConnected = true;
// 获取网络能力信息
const netCapabilities = await connection.getNetCapabilities(netHandle);
if (netCapabilities) {
// 判断连接类型
if (netCapabilities.bearerTypes.includes(connection.NetBearType.BEARER_WIFI)) {
this.connectionType = 'WiFi';
} else if (netCapabilities.bearerTypes.includes(connection.NetBearType.BEARER_CELLULAR)) {
this.connectionType = '移动网络';
} else if (netCapabilities.bearerTypes.includes(connection.NetBearType.BEARER_ETHERNET)) {
this.connectionType = '以太网';
} else {
this.connectionType = '其他';
}
}
// 获取IP地址 - 使用JSON序列化方法
const linkProperties = await connection.getConnectionProperties(netHandle);
if (linkProperties) {
// 通过序列化信息提取第一个 IPv4 地址
const serialized: string = JSON.stringify(linkProperties);
const match: RegExpMatchArray | null = serialized.match(/\b(?:\d{1,3}\.){3}\d{1,3}\b/);
const ip: string = match ? match[0] : '';
this.currentIpAddress = ip && ip !== '127.0.0.1' ? ip : '无';
} else {
this.currentIpAddress = '无';
}
} else {
this.isConnected = false;
this.connectionType = '未连接';
this.currentIpAddress = '无';
}
} catch (error) {
hilog.error(DOMAIN, 'NetworkInfo', 'Failed to get network info: %{public}s', String(error));
this.isConnected = false;
this.connectionType = '获取失败';
this.currentIpAddress = '获取失败';
}
}
private async runNetworkTest() {
this.isNetworkLoading = true;
this.networkMessage = '网络测试进行中...';
this.networkResult = '';
if (!this.targetHost || this.targetHost.trim() === '') {
this.networkResult = '错误: 请输入有效的目标主机地址';
this.networkMessage = '网络测试失败';
this.isNetworkLoading = false;
return;
}
try {
const startTime = Date.now();
// 使用HTTP请求测试网络连接
const httpRequest = http.createHttp();
const url = this.targetHost.startsWith('http') ? this.targetHost : `https://${this.targetHost}`;
const response = await httpRequest.request(url, {
method: http.RequestMethod.GET,
connectTimeout: 10000,
readTimeout: 10000,
header: {
'User-Agent': 'OpenHarmony-NetworkTest/1.0'
}
});
const endTime = Date.now();
const responseTime = endTime - startTime;
let resultText = `网络测试结果:\n`;
resultText += `目标主机: ${this.targetHost}\n`;
resultText += `请求URL: ${url}\n`;
resultText += `响应时间: ${responseTime}ms\n`;
resultText += `HTTP状态码: ${response.responseCode}\n`;
resultText += `响应头: ${JSON.stringify(response.header, null, 2)}\n`;
if (response.responseCode >= 200 && response.responseCode < 400) {
resultText += `连接状态: 成功\n`;
this.networkMessage = '网络测试成功';
} else {
resultText += `连接状态: 失败 (HTTP ${response.responseCode})\n`;
this.networkMessage = '网络测试失败';
}
// 如果响应体不太大,显示部分内容
if (response.result && typeof response.result === 'string' && response.result.length < 500) {
resultText += `响应内容预览: ${response.result.substring(0, 200)}...\n`;
}
this.networkResult = resultText;
httpRequest.destroy();
hilog.info(DOMAIN, 'Network_Test', 'Network test completed for %{public}s: %{public}d ms', this.targetHost, responseTime);
} catch (error) {
const errorMessage = String(error);
this.networkResult = `网络测试失败:\n目标主机: ${this.targetHost}\n错误信息: ${errorMessage}\n\n可能的原因:\n1. 网络连接不可用\n2. 目标主机无法访问\n3. DNS解析失败\n4. 防火墙阻止连接`;
this.networkMessage = '网络测试出错';
hilog.error(DOMAIN, 'Network_Test', 'Network test error for %{public}s: %{public}s', this.targetHost, errorMessage);
} finally {
this.isNetworkLoading = false;
}
}
}