电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
i.MX6ULL嵌入式Linux开发2-uboot移植实践
分 享
扫描二维码分享
i.MX6ULL嵌入式Linux开发2-uboot移植实践
i.MX6ULL
uboot
嵌入式Linux
码农爱学习
关注
发布时间: 2021-07-20
丨
阅读: 1821
[上篇文章](https://www.icxbk.com/article/detail/2294.html),我们介绍了如何使用NXP原厂的uboot进行编译和烧写,将uboot运行在自己的开发板上。NXP原厂的uboot,直接烧录到我的开发板中,LCD的驱动是不正常的,需要进行修改。本篇我们就来继续研究uboot,**使得uboot能匹配我们自己的开发板**。 修改uboot以匹配开发板的方式有两种,一种是在NXP原厂开发板**i.MX 6ULL EVK**的文件上进行修改,另一种仿造NXP的开发板文件,添加自己的开发板文件。 为了能更多的了解uboot,我们使用代码改动较大的第二种方式进行uboot的移植。 在修改uboot之前,先来看一下uboot的源码结构。 # 1 uboot源码结构分析 uboot的源码如下,这里是源码编译后的结果,包含编译后的文件。 ![](https://cf01.ickimg.com/bbsimages/202107/eeb90ccfd03ba85d72351b4743ad4dea.png) 这里文件的含义如下: ![](https://cf01.ickimg.com/bbsimages/202107/2b17622f94b731f45b088eca33ceb20c.png) # 2 uboot移植实践 ## 2.1 添加开发板配置文件 首先是**创建自己开发板的配置文件**,该文件可参考原厂开发板的配置文件,在`configs`文件夹下,将原来的默认配置文件`mx6ull_14x14_evk_emmc_defconfig`复制一份,并重命名为`mx6ull_myboard_defconfig`,该文件即用于作为自己开发板的配置文件。 然后进行**内容修改**,将原始内容: ``` CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ullevk/imximage.cfg,MX6ULL_EVK_EMMC_REWORK" CONFIG_ARM=y CONFIG_ARCH_MX6=y CONFIG_TARGET_MX6ULL_14X14_EVK=y CONFIG_CMD_GPIO=y ``` 修改为: ![](https://cf01.ickimg.com/bbsimages/202107/6a7918f91bb2b0365c00c38f148b7c31.png) ## 2.2 添加开发板对应的头文件 在目录 `include/configs` 下添加自己开发板对应的头文件,复制`mx6ullevk.h`,并重命名为`mx6ull_myboard.h`,将文件中的 ```c #ifndef __MX6ULLEVK_CONFIG_H #define __MX6ULLEVK_CONFIG_H ``` 修改为: ![](https://cf01.ickimg.com/bbsimages/202107/bee376a6723768e365bfa422909813d0.png) 该文件里面有很多宏定义,这些宏定义基本用于配置uboot,如果我们自己要想使能或者禁止uboot的某些功能,那就要在这里面修改。 > 在ubuntu中,可以安装VS Code软件来辅助查看代码,在ubuntu中安装vscode,需要先下载deb格式的安装包,然后使用类似如下的指令即可进行安装: > > ```sh > sudo dpkg -i code_1.58.0-1625728071_amd64.deb > ``` > > 安装完之后,我们可以将图标添加到ubuntu桌面上,ubuntu安装的所有软件图标都在目录`/usr/share/applications`中,找到 Visual Studio Code 的图标,然后点击鼠标右键,选择复制到->桌面即可。 ## 2.3 添加开发板对应的板级文件夹 uboot中每个板子都有一个对应的文件夹来存放板级文件(如开发板上外设驱动文件等)。NXP的I.MX系列芯片的所有板级文件夹都存放在 `board/freescale/`目录下,在这个目录下有个名为`mx6ullevk`的文件夹,原厂开发板的板级文件夹。 复制 mx6ullevk,将其重命名为`mx6ull_myboard`,进入`mx6ull_myboard`目录中, 将其中的`mx6ullevk.c`文件重命名为`mx6ull_myboard.c`。 ### 2.3.1 修改Makefile文件 首先是修改 board/freescale/mx6ull_myboard 目录下的`Makefile`文件 将原始内容: ```makefile # (C) Copyright 2015 Freescale Semiconductor, Inc. # # SPDX-License-Identifier: GPL-2.0+ # obj-y := mx6ullevk.o extra-$(CONFIG_USE_PLUGIN) := plugin.bin $(obj)/plugin.bin: $(obj)/plugin.o $(OBJCOPY) -O binary --gap-fill 0xff $< $@ ``` 其中的依赖项修改为: ```makefile obj-y := mx6ull_myboard.o ``` ![](https://cf01.ickimg.com/bbsimages/202107/62c0fbeaa53ef53c403c1f3673525e2f.png) 这样才会编译`mx6ull_myboard.c`这个文件。 ### 2.3.2 修改imximage.cfg文件 然后修改 board/freescale/mx6ull_myboard 目录下的`imximage.cfg`文件 将`imximage.cfg`中的下面一句: ``` PLUGIN board/freescale/mx6ullevk/plugin.bin 0x00907000 ``` 改为: ``` PLUGIN board/freescale/mx6ull_myboard/plugin.bin 0x00907000 ``` ![](https://cf01.ickimg.com/bbsimages/202107/ab7f7bcaa43bb0f8516529c78c5a25d3.png) ### 2.3.3 修改Kconfig文件 接着修改 board/freescale/mx6ull_myboard 目录下的`Kconfig`文件 将原始内容: ```sh if TARGET_MX6ULL_14X14_EVK || TARGET_MX6ULL_9X9_EVK config SYS_BOARD default "mx6ullevk" config SYS_VENDOR default "freescale" config SYS_CONFIG_NAME default "mx6ullevk" endif ``` 修改为: ![](https://cf01.ickimg.com/bbsimages/202107/b0e10bb1b8f10250e93378372c521c73.png) ### 2.3.4 修改MAINTAINERS文件 再接着修改 board/freescale/mx6ull_myboard 目录下的`MAINTAINERS`文件 将原始内容: ```sh MX6ULLEVK BOARD M: Peng Fan
S: Maintained F: board/freescale/mx6ullevk/ F: include/configs/mx6ullevk.h F: configs/mx6ull_14x14_evk_defconfig F: configs/mx6ull_9x9_evk_defconfig ``` 修改为: ![](https://cf01.ickimg.com/bbsimages/202107/d20f26583653388ed0abb9458d003241.png) ### 2.3.5 重命名板子的c文件 将 board/freescale/mx6ull_myboard 目录下原来的`mx6ullevk.c`重命名为`mx6ull_myboard.c` ![](https://cf01.ickimg.com/bbsimages/202107/2775fbf1872f34fe616b4468e62fc4c0.png) ## 2.4 修改U-Boot图形界面配置文件 最后修改`arch/arm/cpu/armv7/mx6/`目录下的`Kconfig`文件 > 注意这里的Kconfig和`board/freescale/mx6ull_myboard`目录下的Kconfig是不一样的。 在207行插入一些内容: ```sh config TARGET_MX6ULL_MYBOARD bool "Support mx6ull_myboard" select MX6ULL select DM select DM_THERMAL ``` ![](https://cf01.ickimg.com/bbsimages/202107/0838bde4fd743a6520ecf10b71fdedde.png) 然后,在最后一行的`endif`的前一行添加如下内容: ```sh source "board/freescale/mx6ull_myboard/Kconfig" ``` ![](https://cf01.ickimg.com/bbsimages/202107/3392f02bf0d16a1b759849b02259a574.png) ## 2.5 创建编译脚本 在uboot-imx-rel_imx_4.1.15_2.1.0_ga目录下新建一个名为`build_myboard.sh`的 shell 脚本,写入如下内容: ```sh make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- mx6ull_myboard_defconfig make V=1 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8 ``` 至此,以上完成的工作,相当于将NXP原厂开发板相关的配置文件,重新复制了一份,并对板子名称修改为了自己板子的名字。 此时执行`./build_myboard.sh`,等待编译完成后输入如下命令: ```sh grep -nR "mx6ull_myboard.h" ``` 如果有很多文件都引用了这个头文件, 那就说明新板子添加成功: ![](https://cf01.ickimg.com/bbsimages/202107/2361b796bce2cf7887d2251f73b2632c.png) 将uboot进行编译并运行,实际的效果应该和原厂uboot的效果一样(LCD无法显示)。 ![](https://cf01.ickimg.com/bbsimages/202107/e51f170a56d43ee44743b7ceedce8599.png) **总结一下刚才都有哪些修改**: 右端灰色的为原厂开发板的相关文件,黄色的为模仿原厂文件,新添加并修改的自己开发板的文件。 ![](https://cf01.ickimg.com/bbsimages/202107/4665f1c57c3f677857d348ef2bd4e3e5.png) 下面进行LCD驱动的修改。 # 3 LCD驱动的修改 一般uboot中修改驱动都是在对应板子c文件和h文件,即`board/freescale/mx6ull_myboard/mx6ull_myboard.c`和 `include/configs/mx6ull_myboard.h`这两个文件。 一般修改 LCD 驱动重点注意以下几点: - LCD 所使用的 GPIO,查看 uboot 中 LCD 的 IO 配置是否正确 - LCD 背光引脚 GPIO 的配置 - LCD 配置参数是否正确 **正点原子**以及**野火**的I.MX6ULL开发板的LCD原理图和NXP官方的开发板一致,也就是LCD的IO和背光IO都是一样的, 所以IO部分就不用修改了,只需修改之后的LCD参数。 ## 3.1 修改c文件配置 打开文件 `mx6ull_myboard.c`,需要修改下面这段内容: ```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 中,定义如下: ```c 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` 中,定义如下: ```c 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,也就是不使用隔行扫描。 这些参数需要与实用的LCDd的参数一致。 **野火的7寸RGB屏幕**(GT911,800x480)的一些参数如下: | 参数 | 值 | | ------ | ---- | | width | 800 | | height | 480 | | HBP | 46 | | HFP | 22 | | VBP | 23 | | VFP | 22 | | HSW | 1 | | VSW | 1 | 注意像素时钟`pixclock`的计算方法:以野火的 7 寸RGB屏为例,屏幕要求的像素时钟为27.4MHz,因此:`pixclock=(1/27400000)*10^12=36496` > 像素时钟就是 RGB LCD 的时钟信号,以 GT911这款屏幕为例,显示一帧图像所需要的时钟数就是: > (VSPW+VBP+LINE+VFP) * (HSPW + HBP + HOZVAL + HFP) > = (1 + 23 + 480+ 22) * (1+ 46+ 800+ 22) > = 526* 869 > = 457094。 > 显示一帧图像需要457094个时钟数, 那么显示60帧就是: 457094* 60 = 27425640≈27.4M,所以像素时钟就是27.4MHz 由以上的屏幕参数,可以得出GT911屏幕的配置参数如下: ```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 = "GT911", .xres = 800, .yres = 480, .pixclock = 36496, .left_margin = 46, //HBPD .right_margin = 22, //HFPD .upper_margin = 23, //VBPD .lower_margin = 22, //VFPD .hsync_len = 1, //HSPW .vsync_len = 1, //VSPW .sync = 0, .vmode = FB_VMODE_NONINTERLACED } } }; ``` ## 3.2 修改h文件配置 另外还要修改`include/configs/`路径下的`mx6ull_myboard.h`,找到所有如下语句: ```c panel=TFT43AB ``` 修改为: ```c panel=GT911 //与mx6ull_myboard.c中修改的名称保持一致 ``` 修改完成以后重新编译一遍 uboot 并烧写到 SD 中启动。 ## 3.3 编译测试 将修改后的uboot编译下载以后,LCD 驱动一般就会工作正常了,LCD 上会显示 NXP 的 logo。 ![](https://cf01.ickimg.com/bbsimages/202107/3084617f057fd49725d961fa85710dea.jpg) 但某些情况有可能还会遇到LCD 并没有工作,还是黑屏,这是什么原因呢? 在 uboot 命令模式输入“`print`”来查看环境变量 panel 的值,会发现panel的值要是TFT43AB(或其他的,反正不是GT911): ``` panel=TFT43AB script=boot.scr Environment size: 2431/8188 bytes => ``` 这是因为之前有将环境变量保存到EMMC中,uboot启动以后会先从EMMC中读取环境变量,如果EMMC中没有环境变量的话才会使用 mx6ull_alientek_emmc.h 中的默认环境变量。 如果EMMC中的环境变量panel不等于GT911,那么LCD显示肯定不正常,我们只需要在uboot中修改panel的值为GT911即可,在uboot的命令模式下输入如下命令: ``` setenv panel GT911 saveenv ``` 上述命令修改环境变量panel为GT911并保存后,按下复位键重启uboot,此时 LCD 驱动就工作正常了。 # 4 网络测试 I.MX6ULL内部有个以太网MAC外设,也就是ENET,需要外接一个PHY芯片来实现网络通信功能,也就是**内部MAC+外部PHY芯片**的方案。 I.MX6ULL有两个网络接口ENET1和ENET2,野火的开发板提供了这两个网络接口,其中ENET1和ENET2都使用是和原厂开发板一样的` KSZ8081`作为PHY芯片。 因此,网络驱动部分的uboot不需要修改,下面就只是来测试一下网路功能。 ## 4.1 连接网线并查看启动情况 首先将开发板通过网线连接到局域网的路由器中(自己的电脑也要在同一个局域网,这样ubuntu虚拟机则也在同一个局域网)。 ![](https://cf01.ickimg.com/bbsimages/202107/42a8f0760017ebab1a9d9f37460c9d88.jpg) 然后启动uboot,串口查看相关的打印信息,如下图,可以看到网络端口的FEC1(注意是uboot程序中默认设置的,不是因为网线插在了左边就自动识别FEC1),但是提示网络地址未设置。 ![](https://cf01.ickimg.com/bbsimages/202107/f62f03490a5465c6f197ceaaa694233c.png) ## 4.2 设置网络参数 下面就来设置一下,首先是设置开发板的IP,在设置之前,先借助Windows电脑的cmd的`ping+ip`指令来测试某个IP是否被使用,如我的`192.168.5.102`未被使用,就可以设为开发板的IP。 ![](https://cf01.ickimg.com/bbsimages/202107/a9bf76af45029dcbce454bafed767b11.png) 除了设置开发板的IP,还要设置一些其它的网络参数,具体如下: ```c setenv ipaddr 192.168.5.102 //开发板 IP 地址 setenv ethaddr 00:04:9f:04:d2:35 //开发板网卡 MAC 地址 setenv gatewayip 192.168.5.1 //开发板默认网关 setenv netmask 255.255.255.0 //开发板子网掩码 setenv serverip 192.168.5.101 //服务器地址,也就是 Ubuntu 地址 saveenv //保存环境变量 ``` 开发板的MAC地址是一个长度为48位(6个字节)的地址,每个字节间通过冒号间隔,理论上只要局域网内各网络设备不冲突,该地址可任意设置。 局域网的默认网关和子网掩码需要根据自己的实际情况设置(不知道是多少的,可以借助Windows电脑的cmd中的`ipconfig`指令来查看) 服务器的地址就是ubuntu虚拟机的地址(可以通过linux的`ifconfig`指令来查看) ## 4.3 测试另一个网口 打开 include/configs/mx6ull_alientek_emmc.h ,将`CONFIG_FEC_ENET_DEV`修改为 0, 重新编译uboot并烧写到SD卡中。 ![](https://cf01.ickimg.com/bbsimages/202107/f2de99fee349d1e6bd318f95012025bc.png) 将网线连接到开发板右边的网口上,按照之前的测试方法再次测试: ![](https://cf01.ickimg.com/bbsimages/202107/897df502c45304d30c2257b0239ce9de.png) # 5 uboot启动Linux内核测试 uboot的最终目的就是启动Linux内核,所以需要通过启动Linux内核来判断uboot移植是否成功。 启动Linux内核。我们测试两种启动Linux内核的方法: - 从EMMC启动 - 从网络启动 **从EMMC启动**也就是将编译出来的**Linux镜像文件zImage**和**设备树文件**保存在EMMC中,uboot从EMMC中读这两个文件并启动。 由于我们板子的EMMC中可能还没有linux镜像文件和设备树文件,所以先不测试这种方法。 **从网络启动**,是指将linux镜像文件和根文件系统都放到Ubuntu下某个指定的文件夹中,然后通过nfs或者tftp等传输方式将系统文件(zImage和设备树文件)从Ubuntu中直接下载到开发板的内存中,EMMC中则不需要有系统文件。这种方式的作用就是方便调试,免去将代码固化到开发板的过程。当然,当开发板掉电,内存的系统文件就没了。 下面就来通过网络调试的方法来测试uboot是否能正常启动Linux内核。 **在测试之前,先来介绍一下在ubuntu虚拟机上如何搭建tftp来传输文件**。 ## 5.1 tftp服务搭建 Ubuntu上搭建TFTP服务器,需要安装`tftp-hpa`和`tftpd-hpa`,命令如下: ```sh sudo apt-get install tftp-hpa tftpd-hpa sudo apt-get install xinetd ``` TFTP也需要一个文件夹来存放文件,在用户目录下新建一个目录,示例命令如下: ```sh mkdir /home/xxpcb/myTest/tftpdir chmod 777 /home/xxpcb/myTest/tftpdir ``` 最后配置 tftp, 安装完成以后,新建文件`/etc/xinetd.d/tftp`, 如果没有/etc/xinetd.d 目录的话自行创建,然后在里面输入如下内容: ```sh server tftp { socket_type = dgram protocol = udp wait = yes user = root server = /usr/sbin/in.tftpd server_args = -s /home/xxpcb/myTest/tftpdir/ disable = no per_source = 11 cps = 100 2 flags = IPv4 } ``` 完了以后启动tftp服务,命令如下: ```sh sudo service tftpd-hpa start ``` 打开`/etc/default/tftpd-hpa`文件,将其修改为如下所示内容: ```sh # /etc/default/tftpd-hpa TFTP_USERNAME="tftp" TFTP_DIRECTORY="/home/xxpcb/myTest/tftpdir" TFTP_ADDRESS=":69" TFTP_OPTIONS="-l -c -s" ``` `TFTP_DIRECTORY`就是我们上面创建的tftp文件夹目录,以后我们就将所有需要通过TFTP传输的文件都放到这个文件夹里面,并且要给予这些文件相应的权限。 最后输入如下命令, 重启 tftp 服务器: ```sh sudo service tftpd-hpa restart ``` 至此,tftp服务器已经搭建好了,可以先来测试一下功能是否正常。 ## 5.2 tftp文件传输测试 测试tftp功能是否正常,主要分为两步: - 首先是**将某个zImage镜像文件拷贝到ubuntu虚拟机的tftpboot文件夹中**,并且给予 zImage 相应777的权限。 - 然后是**通过开发板uboot的串口交互指令将文件从ubuntu传输到开发板的内存**。 uboot串口交互指令中的**tftp命令格式**如下: ```sh tftpboot [loadAddress] [[hostIPaddr:]bootfilename] ``` `loadAddress`是文件在DRAM中的存放地址,`[[hostIPaddr:]bootfilename]`是要从Ubuntu中下载的文件。 tftp传输文件,不需要输入文件在Ubuntu中的完整路径,只需要输入文件名即可。 比如我们现在**将tftpboot文件夹里面的zImage文件下载到开发板DRAM的0X80800000地址处**,命令如下: ```sh tftp 80800000 zImage ``` ![](https://cf01.ickimg.com/bbsimages/202107/64ce70ef08ad718eac58f97ea127ae6d.png) > 注:此次测试时,我的ubuntu虚拟机(作为tftp服务器)的IP变了,所以我又重新设置了ubuntu的IP ## 5.3 测试从网络启动Linux - 设置环境变量 这两个环境变量的具体含义先不展开讨论。 ```sh setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw' setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull-14x14-evk-emmc.dtb; bootz 80800000 - 83000000' saveenv ``` - 通过tftp将zImage和设备树下载到板子的RAM中 就是通过网路的方式(tftp)将系统文件下载到板子的内存中,这里使用的**野火提供的yocto的zImage和dtb文件**,将两个文件辅助到ubuntu的tftp服务器目录,依次输入如下指令: ```sh tftp 80800000 zImage tftp 83000000 imx6ull-14x14-evk-emmc.dtb ``` - 启动内核 ```sh bootz 80800000 - 83000000 ``` ![](https://cf01.ickimg.com/bbsimages/202107/b0f4a096f8e7e03086f6ac6861bd7a20.png) 可以看到**Starting kernel ...**的字样,表示内核已经启动。 再看看下板子,已经有启动画面了: ![](https://cf01.ickimg.com/bbsimages/202107/f89e612b786c77a8706cae40ca480fcd.jpg) 在过一会儿,会出现系统的图形界面,只是现在还不能操作,触摸没反应。 ![](https://cf01.ickimg.com/bbsimages/202107/ede2be800ba9d64d851897ab84e5094f.jpg) 至此,uboot的移植基本完成,可以启动Linux内核。启动内核之后,uboot的使命就完成了。
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
码农爱学习
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交