链接: C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂
1. LCD 驱动修改
一般 uboot 中修改驱动基本都是在 xxx.h 和 xxx.c 这两个文件中进行的, xxx 为板子名称,即board/freescale/mx6ull_myboard/mx6ull_myboard.c
和 include/configs/mx6ull_myboard.h
这两个文件。
一般修改 LCD 驱动重点注意以下几点:
- LCD 所使用的 GPIO,查看 uboot 中 LCD 的 IO 配置是否正确
- LCD 背光引脚 GPIO 的配置
- LCD 配置参数是否正确
正点原子的 I.MX6U-ALPHA 开发板 LCD 原理图和 NXP 官方 I.MX6ULL 开发板一致,也就是 LCD 的 IO 和背光 IO 都一样的,所以 IO 部分就不用修改了。
1.1 修改c文件配置
需要修改的之后 LCD 参数,打开文件 mx6ull_myboard.c
,找到如下所示内容:
struct display_info_t const displays[] = {{
.bus = MX6UL_LCDIF1_BASE_ADDR,
.addr = 0,
.pixfmt = 24,
.detect = NULL,
.enable = do_enable_parallel_lcd,
.mode = {
.name = "TFT43AB",
.xres = 480,
.yres = 272,
.pixclock = 108695,
.left_margin = 8,
.right_margin = 4,
.upper_margin = 2,
.lower_margin = 4,
.hsync_len = 41,
.vsync_len = 10,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED
} } };
先来分析一下这段代码,该代码定义了一个变量displays
,类型为display_info_t
,这个结构体是LCD信息结构体,其中包括了LCD的分辨率,像素格式,LCD的各个参数等。
display_info_t
定义在文件 arch/arm/include/asm/imx-common/video.h 中,定义如下:
struct display_info_t {
int bus;
int addr;
int pixfmt;
int (*detect)(struct display_info_t const *dev);
void (*enable)(struct display_info_t const *dev);
struct fb_videomode mode;
};
这里的pixfmt
是像素格式,也就是一个像素点是多少位,如果是RGB565的话就是16位,如果是RGB888的话就是24位,一般使用 RGB888。
结构体display_info_t
还有个mode
成员变量,此成员变量也是个结构体,为fb_videomode
,定义在文件 include/linux/fb.h
中,定义如下:
struct fb_videomode {
const char *name; /* optional */
u32 refresh; /* optional */
u32 xres;
u32 yres;
u32 pixclock;
u32 left_margin;
u32 right_margin;
u32 upper_margin;
u32 lower_margin;
u32 hsync_len;
u32 vsync_len;
u32 sync;
u32 vmode;
u32 flag;
};
结构体b_videomode
里面的成员变量为LCD的参数,这些成员变量函数如下:
-
name
:LCD 名字,要和环境变量中的 panel 相等 -
xres 、yres
:LCD X 轴和 Y 轴像素数量 -
pixclock
:像素时钟,每个像素时钟周期的长度,单位为皮秒 -
left_margin
:HBP(horizontal back porch),水平同步后肩 -
right_margin
:HFP(horizontal front porch),水平同步前肩 -
upper_margin
:VBP(vertical back porch),垂直同步后肩 -
lower_margin
:VFP(vertical front porch),垂直同步前肩 -
hsync_len
:HSPW(horizontal sync pulse width),行同步脉宽 -
vsync_len
:VSPW(vertical sync pulse width),垂直同步脉宽 -
vmode
:大多数使用 FB_VMODE_NONINTERLACED,也就是不使用隔行扫描。
这些参数需要与实用的LCD的参数一致,唯一不同的像素时钟 pixclock 的含义不同 。
「正点原子的7寸RGB屏幕」(ATK7016,1024x600)的一些参数如下:
参数 | 值 |
---|---|
width | 1024 |
height | 600 |
HBP | 140 |
HFP | 160 |
VBP | 20 |
VFP | 12 |
HSPW | 20 |
VSPW | 3 |
像素时钟
像素时钟就是 RGB LCD 的时钟信号,以 ATK7016 这款屏幕为例,显示一帧图像所需要的时钟数就是:
= (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP)
= (3 + 20 + 600 + 12) * (20 + 140 + 1024 + 160)
= 635 * 1344
= 853440。
显示一帧图像需要853440个时钟数,那么显示60帧就是: 853440 * 60 = 51206400≈51.2M,所以像素时钟就是 51.2MHz。 因此:
pixclock=(1/51200000)*10^12=19531
在根据其他的屏幕参数,可以得出 ATK7016 屏幕的配置参数如下:
struct display_info_t const displays[] = {{
.bus = MX6UL_LCDIF1_BASE_ADDR,
.addr = 0,
.pixfmt = 24,
.detect = NULL,
.enable = do_enable_parallel_lcd,
.mode = {
.name = "TFT7016",
.xres = 1024,
.yres = 600,
.pixclock = 19531,
.left_margin = 100, //HBPD
.right_margin = 88, //HFPD
.upper_margin = 39, //VBPD
.lower_margin = 21, //VFBD
.hsync_len = 48, //HSPW
.vsync_len = 3, //VSPW
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED
} } };
1.2 修改h文件配置
另外还要修改include/configs/
路径下的mx6ull_myboard.h
,找到所有如下语句:
panel=TFT43AB
将其改为:
panel=TFT7016
也就是设置 panel 为 TFT7016, panel 的值要与displays.name 成员变量的值一致。修改完成以后重新编译一遍 uboot 并烧写到 SD 中启动
1.3 编译测试
将修改后的uboot编译下载以后,LCD 驱动一般就会工作正常了,LCD 上会显示 NXP 的 logo
U-Boot 2016.03 (Jun 26 2023 - 22:21:59 +0800)
CPU: Freescale i.MX6ULL rev1.1 69 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 48C
Reset cause: WDOG
Board: MX6ULL 14x14 EVK
I2C: ready
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
Display: TFT7016 (1024x600)
Video: 1024x600x24
In: serial
Out: serial
Err: serial
switch to partitions #0, OK
mmc0 is current device
Net: Board Net Initialization Failed
No ethernet found.
Normal Boot
Hit any key to stop autoboot: 0
2. 网络驱动修改
2.1 I.MX6U-ALPHA 开发板网络简介
I.MX6ULL
内部有个以太网MAC外设,也就是ENET,需要外接一个PHY芯片来实现网络通信功能,也就是 「内部MAC+外部PHY芯片」 的方案。
I.MX6UL/ULL
有两个网络接口 ENET1
和 ENET2
,正点原子的 I.MX6U-ALPHA
开发板提供了这两个网络接口,其中 ENET1
和 ENET2
都使用 LAN8720A
作为 PHY 芯片。 NXP 官方的I.MX6ULL EVK
开发板使用 KSZ8081
这颗 PHY 芯片。更换 PHY 芯片以后需要调整网络驱动,使网络工作正常。
ENET1
ENET1 的网络 PHY 芯片为 LAN8720A
,通过 RMII 接口与 I.MX6ULL
相连,正点原子I.MX6U-ALPHA
开发板的 ENET1 引脚与 NXP 官方的 I.MX6ULL EVK
开发板基本一样,唯独复位引脚不同。正点原子I.MX6U-ALPHA
开发板的 ENET1
复位引脚ENET1_RST
接到了 I.M6ULL
的 SNVS_TAMPER7
这个引脚上。
I.MX6ULL 通过 MDIO接口来读取 PHY 芯片的内部寄存器, MDIO 接口有两个引脚, ENET_MDC 和 ENET_MDIO,ENET_MDC 提供时钟, ENET_MDIO 进行数据传输。一个 MDIO 接口可以管理 32 个 PHY 芯片,同一个 MDIO 接口下的这些 PHY 使用不同的器件地址来做区分, MIDO 接口通过不同的器件地址即可访问到相应的 PHY 芯片。
I.MX6U-ALPHA
开发板 ENET1 上连接的 LAN8720A
器件地址为 0X0,所示要修改 ENET1 网络驱动重点就三点:
①、 ENET1 复位引脚初始化。
②、 LAN8720A 的器件 ID。
③、 LAN8720 驱动
ENET2
关于 ENET2 网络驱动的修改也注意一下三点:
①、 ENET2 的复位引脚,ENET2 的复位引脚 ENET2_RST 接到了I.MX6ULL 的 SNVS_TAMPER8
上。
②、 ENET2 所使用的 PHY 芯片器件地址,PHY 器件地址为 0X1。
③、 LAN8720 驱动, ENET1 和 ENET2 都使用的 LAN8720,所以驱动是一样的。
2.2 网络 PHY 地址修改
首先修改 uboot 中的 ENET1 和 ENET2 的 PHY 地址和驱动,打开 mx6ull_myboard.h这个文件,找到如下代码:
#define CONFIG_FEC_ENET_DEV 1
#if (CONFIG_FEC_ENET_DEV == 0)
#define IMX_FEC_BASE ENET_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR 0x2
#define CONFIG_FEC_XCV_TYPE RMII
#elif (CONFIG_FEC_ENET_DEV == 1)
#define IMX_FEC_BASE ENET2_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR 0x1
#define CONFIG_FEC_XCV_TYPE RMII
#endif
#define CONFIG_ETHPRIME "FEC"
#define CONFIG_PHYLIB
#define CONFIG_PHY_MICREL
#endif
-
第 331 行的宏 CONFIG_FEC_ENET_DEV 用于选择使用哪个网口,默认为 1,也就是选择ENET2
-
第 335 行为 ENET1 的 PHY 地址,默认是 0X2
-
第 339 行为 ENET2 的 PHY 地址,默认为 0x1
根据前面的分析可知,正点原子的 I.MX6U-ALPHA 开发板 ENET1 的 PHY 地址为0X0, ENET2 的 PHY 地址为 0X1,所以需要将第 335 行的宏 CONFIG_FEC_MXC_PHYADDR改为 0x0。
如果要使用 LAN8720A,那么就得将 CONFIG_PHY_MICREL 改为 CONFIG_PHY_SMSC,也就是使能 uboot 中的 SMSC 公司中的 PHY 驱动,因为 LAN8720A 就是 SMSC 公司生产的。
所以示例代码有三处要修改:
①、修改 ENET1 网络 PHY 的地址。
②、修改 ENET2 网络 PHY 的地址。
③、使能 SMSC 公司的 PHY 驱动。
修改后的网络 PHY 地址参数如下所示:
#define CONFIG_FEC_ENET_DEV 1
#if (CONFIG_FEC_ENET_DEV == 0)
#define IMX_FEC_BASE ENET_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR 0x0
#define CONFIG_FEC_XCV_TYPE RMII
#elif (CONFIG_FEC_ENET_DEV == 1)
#define IMX_FEC_BASE ENET2_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR 0x1
#define CONFIG_FEC_XCV_TYPE RMII
#endif
#define CONFIG_ETHPRIME "FEC"
#define CONFIG_PHYLIB
#define CONFIG_PHY_SMSC
#endif
2.3 删除 uboot 中 74LV595 的驱动代码
1、uboot 中网络 PHY 芯片地址修改完成以后就是网络复位引脚的驱动修改了,打开mx6ull_myboard.c,找到如下代码:
#define IOX_SDI IMX_GPIO_NR(5, 10)
#define IOX_STCP IMX_GPIO_NR(5, 7)
#define IOX_SHCP IMX_GPIO_NR(5, 11)
#define IOX_OE IMX_GPIO_NR(5, 8)
以 IOX 开头的宏定义是 74LV595 的相关 GPIO,因为 NXP 官方I.MX6ULL EVK 开发板使用 74LV595 来扩展 IO,两个网络的复位引脚就是由 74LV595 来控制的。正点原子的 I.MX6U-ALPHA 开发板并没有使用 74LV595,因此我们将代码的代码删除掉,替换为
#define ENET1_RESET IMX_GPIO_NR(5, 7)
#define ENET2_RESET IMX_GPIO_NR(5, 8)
NET1 的复位引脚连接到 SNVS_TAMPER7 上,对应 GPIO5_IO07, ENET2 的复位引脚连接到 SNVS_TAMPER8 上,对应 GPIO5_IO08。
2、继续在 mx6ull_myboard.c 中找到如下代码:
static iomux_v3_cfg_t const iox_pads[] = {
/* IOX_SDI */
MX6_PAD_BOOT_MODE0__GPIO5_IO10 | MUX_PAD_CTRL(NO_PAD_CTRL),
/* IOX_SHCP */
MX6_PAD_BOOT_MODE1__GPIO5_IO11 | MUX_PAD_CTRL(NO_PAD_CTRL),
/* IOX_STCP */
MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL),
/* IOX_nOE */
MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
示例代码是 74LV595 的 IO 配置参数结构体,将其删除掉。
3、继续在mx6ull_myboard.c 中找到函数 iox74lv_init
static void iox74lv_init(void)
{
int i;
gpio_direction_output(IOX_OE, 0);
............
gpio_direction_output(IOX_STCP, 1);
};
void iox74lv_set(int index)
{
int i;
............
gpio_direction_output(IOX_STCP, 1);
};
iox74lv_init 函数是 74LV595 的初始化函数, iox74lv_set 函数用于控制 74LV595 的 IO 输出电平,将这两个函数全部删除掉!
4、在 mx6ull_myboard.c 中找到 board_init 函数,此函数是板子初始化函数,会被board_init_r 调用, board_init 函数内容如下:
int board_init(void)
{
.......
imx_iomux_v3_setup_multiple_pads(iox_pads, ARRAY_SIZE(iox_pads));
iox74lv_init();
.......
return 0;
}
board_init 会调用 imx_iomux_v3_setup_multiple_pads 和 iox74lv_init 这两个函数来初始化74lv595 的 GPIO,将这两行删除掉。
至此, mx6ull_myboard.c 中关于 74LV595 芯片的驱动代码都删除掉了,接下来就是添加 I.MX6U-ALPHA 开发板两个网络复位引脚了。
2.4 添加开发板网络复位引脚驱动
在 mx6ull_myboard.c 中找到如下所示代码:
static iomux_v3_cfg_t const fec1_pads[] = {
MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
........
MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
};
static iomux_v3_cfg_t const fec2_pads[] = {
MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
........
MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
};
结构体数组 fec1_pads 和 fec2_pads 是 ENET1 和 ENET2 这两个网口的 IO 配置参数,在这两个数组中添加两个网口的复位 IO 配置参数,完成以后如下所示:
static iomux_v3_cfg_t const fec1_pads[] = {
MX6_PAD_GPIO1_IO06__ENET1_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
MX6_PAD_GPIO1_IO07__ENET1_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
........
MX6_PAD_ENET1_RX_ER__ENET1_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_RX_EN__ENET1_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_PAD_CTRL), /* 初始化ETH1 RESET引脚 */
};
static iomux_v3_cfg_t const fec2_pads[] = {
MX6_PAD_GPIO1_IO06__ENET2_MDIO | MUX_PAD_CTRL(MDIO_PAD_CTRL),
MX6_PAD_GPIO1_IO07__ENET2_MDC | MUX_PAD_CTRL(ENET_PAD_CTRL),
........
MX6_PAD_ENET2_RX_EN__ENET2_RX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_RX_ER__ENET2_RX_ER | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_SNVS_TAMPER8__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL), /* 初始化ETH2 RESET引脚 */
};
别是 ENET1 和 ENET2 的复位 IO 配置参数。
继续在文件 mx6ull_myboard.c 中找到函数 setup_iomux_fec,此函数默认代码如下:
static void setup_iomux_fec(int fec_id)
{
if (fec_id == 0)
imx_iomux_v3_setup_multiple_pads(fec1_pads,
ARRAY_SIZE(fec1_pads));
else
imx_iomux_v3_setup_multiple_pads(fec2_pads,
ARRAY_SIZE(fec2_pads));
}
函数 setup_iomux_fec 就是根据 fec1_pads 和 fec2_pads 这两个网络 IO 配置数组来初始化I.MX6ULL 的网络 IO。需要在其中添加网络复位 IO 的初始化代码,并且复位一下 PHY 芯片,修改后的 setup_iomux_fec 函数如下:
static void setup_iomux_fec(int fec_id)
{
if (fec_id == 0)
{
imx_iomux_v3_setup_multiple_pads(fec1_pads,
ARRAY_SIZE(fec1_pads));
gpio_direction_output(ENET1_RESET, 1);
gpio_set_value(ENET1_RESET, 0);
mdelay(100);
gpio_set_value(ENET1_RESET, 1);
}
else
{
imx_iomux_v3_setup_multiple_pads(fec2_pads,
ARRAY_SIZE(fec2_pads));
gpio_direction_output(ENET2_RESET, 1);
gpio_set_value(ENET2_RESET, 0);
mdelay(100);
gpio_set_value(ENET2_RESET, 1);
}
}
分别对应 ENET1 和 ENET2 的复位 IO 初始化,将这两个 IO 设置为输出并且硬件复位一下 LAN8720A
2.5 更新 PHY 的连接状态和速度
打开文件drivers/net/phy/phy.c,找到函数 genphy_update_link,这是个通用 PHY 驱动函数,此函数用于更新 PHY 的连接状态和速度。使用 LAN8720A 的时候需要在此函数中添加一些代码,修改后的函数 genphy_update_link 如下所示:
nt genphy_update_link(struct phy_device *phydev)
{
unsigned int mii_reg;
#ifdef CONFIG_PHY_SMSC
static int lan8720_flag = 0;
int bmcr_reg = 0;
if(lan8720_flag == 0)
{
bmcr_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); /* Read the default value of BCMR register */
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET); /* Software reset*/
mdelay(10);
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr_reg); /* Write the default value to BCMR register */
lan8720_flag = 1;
}
#endif
2.6 烧写调试
至此网络的复位引脚驱动修改完成,重新编译 uboot,然后将 u-boot.bin 烧写到 SD 卡中并启动, uboot 启动信息
U-Boot 2016.03 (Jun 26 2023 - 22:07:29 +0800)
U-Boot 2016.03 (Jun 26 2023 - 22:21:59 +0800)
CPU: Freescale i.MX6ULL rev1.1 69 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 48C
Reset cause: WDOG
Board: MX6ULL 14x14 EVK
I2C: ready
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
Display: TFT7016 (1024x600)
Video: 1024x600x24
In: serial
Out: serial
Err: serial
switch to partitions #0, OK
mmc0 is current device
Net: FEC1
Error: FEC1 address not set.
Normal Boot
Hit any key to stop autoboot: 0
可以看到“Net: FEC1”这一行,提示当前使用的 FEC1 这个网口,也就是 ENET2。
配置u-boot中环境变量设置命令如下所示:
setenv ipaddr 192.168.1.50
setenv ethaddr b8:ae:1d:01:00:00
setenv gatewayip 192.168.1.1
setenv netmask 255.255.255.0
setenv serverip 192.168.1.253
saveenv
=> ping 192.168.1.253
FEC1 Waiting for PHY auto negotiation to complete.... done
Using FEC1 device
host 192.168.1.253 is alive
有“host 192.168.1.250 is alive”这句,说明 ping 主机成功,说明ENET2网络工作正常。
2.7 测试一下 ENET1
打开 mx6ull_alientek_emmc.h,将 CONFIG_FEC_ENET_DEV 改为 0,然后重新编译一下 uboot 并烧写到 SD 卡中重启。重启开发板,
U-Boot 2016.03 (Jun 26 2023 - 22:21:59 +0800)
CPU: Freescale i.MX6ULL rev1.1 69 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 48C
Reset cause: WDOG
Board: MX6ULL 14x14 EVK
I2C: ready
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
Display: TFT7016 (1024x600)
Video: 1024x600x24
In: serial
Out: serial
Err: serial
switch to partitions #0, OK
mmc0 is current device
Net: FEC0
Normal Boot
Hit any key to stop autoboot: 0
有“Net: FEC0”这一行,说明当前使用的 FEC0 这个网卡,也就是ENET1,同样的 ping 一下主机
=> ping 192.168.1.253
FEC0 Waiting for PHY auto negotiation to complete.... done
Using FEC0 device
host 192.168.1.253 is alive
ping 主机也成功,说明 ENET1 网络也工作正常,至此, I.MX6UALPHA 开发板的两个网络都工作正常了文章来源:https://www.toymoban.com/news/detail-520062.html
2.8 其他需要修改的地方
在 uboot 启动信息中会有“Board: MX6ULL 14x14 EVK”这一句,也就是说板子名字为“ MX6ULL 14x14 EVK”,要将其改为我们所使用的板子名字,比如“ MX6ULL MYBOARD”。打开文件 mx6ull_myboard.c,找到函数checkboard,将其改为如下所示内容:文章来源地址https://www.toymoban.com/news/detail-520062.html
int checkboard(void)
{
if (is_mx6ull_9x9_evk())
puts("Board: MX6ULL 9x9 EVK\n");
else
puts("Board: MX6ULL MYBOARD\n");
return 0;
}
U-Boot 2016.03 (Jun 26 2023 - 22:39:30 +0800)
CPU: Freescale i.MX6ULL rev1.1 69 MHz (running at 396 MHz)
CPU: Industrial temperature grade (-40C to 105C) at 42C
Reset cause: POR
Board: MX6ULL MYBOARD
I2C: ready
DRAM: 512 MiB
MMC: FSL_SDHC: 0, FSL_SDHC: 1
Display: TFT7016 (1024x600)
Video: 1024x600x24
In: serial
Out: serial
Err: serial
switch to partitions #0, OK
mmc0 is current device
Net: FEC1
Normal Boot
Hit any key to stop autoboot: 0
到了这里,关于U-Boot移植 (2)- LCD 驱动修改和网络驱动修改的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!