树莓派使用 ENC28J60
前言
一些老的、Mini 的 ARM 开发板上没有预留网口,这样在调试升级内核或应用程序时很不方便。纵使有串口下载工具,但其速度也是慢地捉急。这种情况下,使用其它接口来扩展出一个网口无疑是一个比较好的方法。
ENC28J60 就是一个使用 SPI 接口来扩展网口的模块,今天我们就来演示下在树莓派上如何使用 ENC28J60。
环境
硬件:树莓派3b+、ENC28J60
软件:buildroot、kernel-5.10.92
摸索
驱动
既然想要使用 ENC28J60,那么肯定得有一份它的驱动程序。
作为一款广泛使用的模块,内核理应有它的驱动程序,搜一下

果然,内核有它的驱动程序,且其已经被编译成了内核模块:
liyongjun@Box:~/project/board/buildroot$ find RPi3/build/linux-custom/drivers/ -name "enc28j60.ko"
RPi3/build/linux-custom/drivers/net/ethernet/microchip/enc28j60.ko
硬件
有了驱动,接下来就要看硬件如何连接了。
知道是连 SPI 接口,但是 SPI 接口有 SPI0、SPI1、SPI2 等,要连哪个呢?另外还有 CS、INT 引脚要连接树莓派的哪个引脚?
那就去看设备树。

驱动的匹配属性为 “microchip,enc28j60”,那就在设备树中搜下该参数

搜到了两个设备树文件,(Device Tree Overlays 相关知识请自行搜索,或查看这篇文章)
arch/arm/boot/dts/overlays/enc28j60-overlay.dts
arch/arm/boot/dts/overlays/enc28j60-spi2-overlay.dts
说明 enc28j60 可以接在不同的 SPI 接口上工作,这里我们研究 enc28j60-overlay.dts,即 SPI0。

target = <&spi0>;:指定"插件"设备树的父节点是 spi0
所以,查看 SPI0 的引脚分配


9、10、11 分别是 MISO、MOSI、SCK
8、7 是 CS 引脚,为什么配置两个引脚作为 CS 引脚这里表示不理解,本实验中选取 8 引脚作为 CS 引脚。
而 INT 引脚是在 enc28j60-overlay.dts 中配置的,使用 25 引脚,所以最终的硬件连接为
RPi3b+ ENC28J60
----------------------------
+3V3 VCC
GPIO10/MOSI SI
GPIO9/MISO SO
GPIO11/SCLK SCK
GND GND GPIO25 INT
GPIO8/CE0# CS
实物连接

配置
树莓派硬件比较特殊,开启 SPI 需要修改板子中的配置文件,如下
# mount /dev/mmcblk0p1 /boot
# cat /boot/config.txt
# Please note that this is only a sample, we recommend you to change it to fit
# your needs.
# You should override this file using BR2_PACKAGE_RPI_FIRMWARE_CONFIG_FILE.
# See http://buildroot.org/manual.html#rootfs-custom
# and http://elinux.org/RPiconfig for a description of config.txt syntaxstart_file=start.elf
fixup_file=fixup.datkernel=zImage# To use an external initramfs file
#initramfs rootfs.cpio.gz# Disable overscan assuming the display supports displaying the full resolution
# If the text shown on the screen disappears off the edge, comment this out
disable_overscan=1# How much memory in MB to assign to the GPU on Pi models having
# 256, 512 or 1024 MB total memory
gpu_mem_256=100
gpu_mem_512=100
gpu_mem_1024=100# fixes rpi (3B, 3B+, 3A+, 4B and Zero W) ttyAMA0 serial console
dtoverlay=miniuart-bt# enable autoprobing of Bluetooth driver without need of hciattach/btattach
dtoverlay=krnbt=ondtparam=spi=on
dtoverlay=enc28j60
需要手动在 config.txt 文件末尾处添加
dtparam=spi=on
dtoverlay=enc28j60
这两个参数猜测是给 start.elf 使用的,因为在其文件中搜出了相应的字符串
liyongjun@Box:~/project/board/buildroot$ strings RPi3/images/rpi-firmware/start.elf | grep "dtparam"
dtparam: %s=%s
Unknown dtparam '%s' - ignored
dtparam
dtparams
liyongjun@Box:~/project/board/buildroot$ strings RPi3/images/rpi-firmware/start.elf | grep "dtoverlay"
dtoverlay
安装驱动
# insmod /lib/modules/5.10.92-v7/kernel/drivers/spi/spi-bcm2835.ko
# insmod /lib/modules/5.10.92-v7/kernel/drivers/net/ethernet/microchip/enc28j60.ko
[ 6214.374882] enc28j60 spi0.0: Ethernet driver 1.02 loaded
查看网口
# ifconfig -a
eth0 Link encap:Ethernet HWaddr B8:27:EB:8A:BC:F4 UP BROADCAST MULTICAST MTU:1500 Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)eth1 Link encap:Ethernet HWaddr 76:30:92:53:5E:E4 BROADCAST MULTICAST MTU:1500 Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)Interrupt:200 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0UP LOOPBACK RUNNING MTU:65536 Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
多出了一个 eth1,说明 ENC28J60 已经被成功驱动起来了
使能网卡
# ifconfig eth1 up
[ 6239.461085] enc28j60 spi0.0 eth1: link down
[ 6239.469427] enc28j60 spi0.0 eth1: normal mode
[ 6239.477886] enc28j60 spi0.0 eth1: multicast mode
# [ 6240.462780] enc28j60 spi0.0 eth1: link up - Half duplex# ifconfig
eth0 Link encap:Ethernet HWaddr B8:27:EB:8A:BC:F4 UP BROADCAST MULTICAST MTU:1500 Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)eth1 Link encap:Ethernet HWaddr 76:30:92:53:5E:E4 UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1RX packets:4 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:769 (769.0 B) TX bytes:0 (0.0 B)Interrupt:200 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0UP LOOPBACK RUNNING MTU:65536 Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
使能网卡后,并且插上网线,看到 eth1 已经处于 RUNNING 状态,但是还没有 IP,那就手动触发下 dhcpc
# udhcpc -i eth1
udhcpc: started, v1.36.0
[ 6256.013810] enc28j60 spi0.0 eth1: multicast mode
[ 6256.027504] enc28j60 spi0.0 eth1: multicast mode
udhcpc: broadcasting discover
udhcpc: broadcasting select for 192.168.31.239, server 192.168.31.1
udhcpc: lease of 192.168.31.239 obtained from 192.168.31.1, lease time 43200
[ 6256.824266] enc28j60 spi0.0 eth1: multicast mode
[ 6256.832197] enc28j60 spi0.0 eth1: multicast mode
deleting routers
adding dns 192.168.31.1# ifconfig
eth0 Link encap:Ethernet HWaddr B8:27:EB:8A:BC:F4 UP BROADCAST MULTICAST MTU:1500 Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)eth1 Link encap:Ethernet HWaddr 76:30:92:53:5E:E4 inet addr:192.168.31.239 Bcast:192.168.31.255 Mask:255.255.255.0UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1RX packets:27 errors:0 dropped:1 overruns:0 frame:0TX packets:2 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:4775 (4.6 KiB) TX bytes:684 (684.0 B)Interrupt:200 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0UP LOOPBACK RUNNING MTU:65536 Metric:1RX packets:0 errors:0 dropped:0 overruns:0 frame:0TX packets:0 errors:0 dropped:0 overruns:0 carrier:0collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
这样,eth1 获得了 IP 地址 192.168.31.239
ping 下外网试试
# ping www.baidu.com
PING www.baidu.com (180.101.50.242): 56 data bytes
64 bytes from 180.101.50.242: seq=0 ttl=53 time=9.520 ms
64 bytes from 180.101.50.242: seq=1 ttl=53 time=10.309 ms
64 bytes from 180.101.50.242: seq=2 ttl=53 time=9.635 ms
可以 ping 通,同时,插拔网线也有相应的 up/down 事件。
# [ 106.197318] enc28j60 spi0.0 eth1: link up - Half duplex
#
# [ 113.533593] enc28j60 spi0.0 eth1: link down
附录
# ethtool eth1
Settings for eth1:Supported ports: [ TP ]Supported link modes: 10baseT/Half 10baseT/Full Supported pause frame use: NoSupports auto-negotiation: NoSupported FEC modes: Not reportedAdvertised link modes: Not reportedAdvertised pause frame use: NoAdvertised auto-negotiation: NoAdvertised FEC modes: Not reportedSpeed: 10Mb/sDuplex: HalfPort: Twisted PairPHYAD: 0Transceiver: internalAuto-negotiation: offMDI-X: UnknownCurrent message level: 0x00000036 (54)probe link ifdown ifup
ENC28J60 是一款 10Mbps 速率的以太网 MAC+PHY 芯片,和单片机的通信接口为 SPI,SPI 最高时钟频率为 20MHz。
ENC28J60 支持半双工和全双工模式,但是不支持自动协商。在支持自动协商的网络环境中,ENC28J60 默认的工作模式是半双工模式。
黄色灯表示 Activity,即闪烁一次,代表有数据传输一次;绿色灯表示 Link up,即绿色灯常亮,代表网口正常工作。
性能测试(phy rate:10Mbps 半双工)
TCP
PS D:\Tools\iperf-3.0.11-win64> .\iperf3.exe -c 192.168.31.239
Connecting to host 192.168.31.239, port 5201
[ 4] local 192.168.31.211 port 1648 connected to 192.168.31.239 port 5201
[ ID] Interval Transfer Bandwidth
[ 4] 0.00-1.00 sec 896 KBytes 7.34 Mbits/sec
[ 4] 1.00-2.00 sec 640 KBytes 5.24 Mbits/sec
[ 4] 2.00-3.00 sec 768 KBytes 6.29 Mbits/sec
[ 4] 3.00-4.00 sec 768 KBytes 6.29 Mbits/sec
[ 4] 4.00-5.00 sec 768 KBytes 6.29 Mbits/sec
[ 4] 5.00-6.00 sec 768 KBytes 6.29 Mbits/sec
[ 4] 6.00-7.00 sec 640 KBytes 5.24 Mbits/sec
[ 4] 7.00-8.00 sec 768 KBytes 6.29 Mbits/sec
[ 4] 8.00-9.00 sec 768 KBytes 6.30 Mbits/sec
[ 4] 9.00-10.00 sec 768 KBytes 6.29 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth
[ 4] 0.00-10.00 sec 7.38 MBytes 6.19 Mbits/sec sender
[ 4] 0.00-10.00 sec 7.19 MBytes 6.03 Mbits/sec receiveriperf Done.
UDP
PS D:\Tools\iperf-3.0.11-win64> .\iperf3.exe -c 192.168.31.239 -b10M -u
Connecting to host 192.168.31.239, port 5201
[ 4] local 192.168.31.211 port 63892 connected to 192.168.31.239 port 5201
[ ID] Interval Transfer Bandwidth Total Datagrams
[ 4] 0.00-1.00 sec 1.08 MBytes 9.03 Mbits/sec 138
[ 4] 1.00-2.00 sec 1.20 MBytes 10.0 Mbits/sec 153
[ 4] 2.00-3.00 sec 1.19 MBytes 9.95 Mbits/sec 152
[ 4] 3.00-4.00 sec 1.20 MBytes 10.0 Mbits/sec 153
[ 4] 4.00-5.00 sec 1.24 MBytes 10.4 Mbits/sec 159
[ 4] 5.00-6.01 sec 1.15 MBytes 9.62 Mbits/sec 147
[ 4] 6.01-7.01 sec 1.14 MBytes 9.57 Mbits/sec 146
[ 4] 7.01-8.01 sec 1.15 MBytes 9.62 Mbits/sec 147
[ 4] 8.01-9.00 sec 1.13 MBytes 9.57 Mbits/sec 145
[ 4] 9.00-10.00 sec 1.14 MBytes 9.56 Mbits/sec 146
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth Jitter Lost/Total Datagrams
[ 4] 0.00-10.00 sec 11.6 MBytes 9.74 Mbits/sec 0.548 ms 0/3 (0%)
[ 4] Sent 3 datagramsiperf Done.
驱动信息
# ethtool -i eth1
driver: enc28j60
version: 1.02
firmware-version:
expansion-rom-version:
bus-info: spi0.0
supports-statistics: no
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no
相关文章:
树莓派使用 ENC28J60
前言 一些老的、Mini 的 ARM 开发板上没有预留网口,这样在调试升级内核或应用程序时很不方便。纵使有串口下载工具,但其速度也是慢地捉急。这种情况下,使用其它接口来扩展出一个网口无疑是一个比较好的方法。 ENC28J60 就是一个使用 SPI 接口…...
跟我学C++中级篇——模板友元的应用
一、友元 友元在以前分析过,而且一般编程是不推荐使用友元的,原因是友元破坏了类的封装性。但凡事总有例外,在某些情况下,用友元还是比较方便的,那么该用还得用,不能因噎废食。普通的友元,各种…...
软件测试基础篇——MySQL
MySQL 1、数据库技术概述 数据库database:存放和管理各种数据的仓库,操作的对象主要是【数据data】,科学的组织和存储数据,高效的获取和处理数据SQL:结构化查询语言,专为**关系型数据库而建立的操作语言&…...
FreeRTOS(二值信号量)
资料来源于硬件家园:资料汇总 - FreeRTOS实时操作系统课程(多任务管理) 目录 一、信号量的概念 1、信号量的基本概念 2、信号量的分类 二、二值信号量的定义与应用 1、二值信号量的定义 2、二值信号量的应用 三、二值信号量的运作机制 1、FreeRTOS任务间二值…...
leetcode面试题:动物收容所(考查对队列的理解和运用)
题目: 有家动物收容所只收容狗与猫,且严格遵守“先进先出”的原则。在收养该收容所的动物时,收养人只能收养所有动物中“最老”(由其进入收容所的时间长短而定)的动物,或者可以挑选猫或狗(同时…...
【Linux命令行与Shell脚本编程】第十八章 文本处理与编辑器基础
Linux命令行与Shell脚本编程 第十八章 文本处理与编辑器基础 文章目录 Linux命令行与Shell脚本编程第十八章 文本处理与编辑器基础 文本处理与编辑器基础8.1.文本处理8.1.1.sed编辑器8.1.1.1.在命令行中定义编辑器命令8.1.1.2.在命令行中使用多个编辑器命令8.1.1.3.从文件中读…...
2023牛客暑期多校训练营7
Beautiful Sequence 贪心,二进制,构造 Cyperation 模拟 ,数学 We Love Strings 分块,二进制枚举,二进制容斥dp Writing Books 签到 根据相邻两个异或值B,因为前小于等于后,故从高到低遍历B的每一…...
centos7升级glibc2.28
1 概述 centos7自带的glibc对于某些软件是太旧的,决定将glibc升级至2.28。 2 安装过程 2.1 下载glibc源码 mkdir -p /opt/third-party && cd /opt/third-party wget http://ftp.gnu.org/gnu/glibc/glibc-2.28.tar.gz tar -xf glibc-2.28.tar.gz cd glibc…...
腾讯云香港服务器租用_2核2G20M_2核4G30M
腾讯云香港服务器租用费用表,目前中国香港地域轻量应用服务器可选配置2核2G20M、2核2G30M、2核4G30M,操作系统可选Windows和Linux,不只是香港云服务器,新加坡、硅谷、法兰克福和东京服务器均有活动,腾讯云服务器网分享…...
十三、ESP32PS2摇杆(ADC)
1. 运行效果 在上下左右操作PS2摇杆的时候,会检测到数据 2. 滑动电阻...
网络安全的相关知识点
网络安全威胁类型: 1.窃听:广播式网络系统。 2.假冒 3.重放:重复一份报文或者报文的一部分,以便产生一个被授权的效果。 4.流量分析 5.数据完整性破坏 6.拒绝服务 7.资源的非授权使用 8.陷门和特洛伊木马:木马病毒有客…...
算法练习(6):牛客在线编程06 递归/回溯
package jz.bm;import java.io.PushbackInputStream; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays;public class bm6 {/*** BM55 没有重复项数字的全排列*/ArrayList<ArrayList<Integer>> res new ArrayList<>()…...
C#使用OpenCv(OpenCVSharp)图像局部二值化处理实例
本文实例演示C#语言中如何使用OpenCv(OpenCVSharp)对图像进行局部二值化处理。 目录 图像二值化原理 局部二值化 自适应阈值 实例 效果...
MySQL多表关联查询
目录 1. inner join: 2. left join: 3. right join: 4.自连接 5.交叉连接: 6、联合查询 7、子查询 1. inner join: 代表选择的是两个表的交差部分。 内连接就是表间的主键与外键相连,只取得键值一致…...
flutter开发实战-CustomClipper裁剪长图帧动画效果
flutter开发实战-CustomClipper裁剪长图帧动画效果 在开发过程中,经常遇到帧动画的每一帧图显示在超长图上,需要处理这种帧动画效果。我这里使用的是CustomClipper 一、CustomClipper CustomClipper继承于Listenable abstract class CustomClipper e…...
CSS 中的优先级规则是怎样的?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐内联样式(Inline Styles)⭐ID 选择器(ID Selectors)⭐类选择器、属性选择器和伪类选择器(Class, Attribute, and Pseudo-class Selectors)⭐元素选择器和伪元素选择器…...
概率图模型(Probabilistic Graphical Model,PGM)
概率图模型(Probabilistic Graphical Model,PGM),是一种用图结构来描述多元随机变量之间条件独立性的概率模型。它可以用来表示复杂的概率分布,进行有效的推理和学习,以及解决各种实际问题,如图…...
Oracle 知识篇+会话级全局临时表在不同连接模式中的表现
标签:会话级临时表、全局临时表、幻读释义:Oracle 全局临时表又叫GTT ★ 结论 ✔ 专用服务器模式:不同应用会话只能访问自己的数据 ✔ 共享服务器模式:不同应用会话只能访问自己的数据 ✔ 数据库驻留连接池模式:不同应…...
MySQL 数据库文件的导入导出
目录 数据库的导出 导出整个数据库 导出数据库中的数据表 导出数据库结构 导出数据库中表的表结构 导出多个数据库 导出所有数据库 数据库的导入 数据库的导出 mysqldump -h IP地址 -P 端口 -u 用户名 -p 数据库名 > 导出的文件名 用管理员权限打开cmd进入MySQL的bi…...
找不到资产文件project.assets.json
NuGet 在“obj”文件夹中写入名为 project.assets.json 的文件,.NET SDK 使用该文件来获取有关要传递到编译器的包的信息 。 如果在生成过程中找不到资产文件 project.assets.json,则会发生此错误。 1.执行命令的方式解决 点击工具,分别展开命…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
