NXP官方uboot仓库地址为:https://github.com/Freescale/u-boot-fslc。
这里为了保持版本统一,使用正点原子资料包中提供的原厂linux:uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2
(版本是2016.03)。
设置临时环境变量:
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
清理构建:
make distclean
查看uboot/configs目录下imx6ull相关的单板:
NXP官方EVK支持nand和emmc两种,因为我手头的开发板是emmc的,所以这里也选择emmc版本的EVK。
配置单板:
make mx6ull_14x14_evk_emmc_defconfig
配置好之后编译:
make
编译成功之后可以看到三个uboot的文件,作用如下。
imxdownload u-boot.bin /dev/sdb
将SD卡插入板子,选择从SD卡启动,查看串口日志:
(1)uboot正常启动;
(2)uboot正确识别到DDR,大小为512MB;
(3)检查 SD 卡和EMMC是否正常:
// 列出当前mmc设备
mmc list
// 查看设备0的信息
mmc dev 0
mmc info
// 查看设备1的信息
mmc dev 1
mmc info
SD卡大小16GB,正常:
EMMC大小为8GB,正常:
(4)LCD显示设备检查
uboot打印日志中提示LCD显示设备是Display: TFT43AB (480x272)
,这里我实际连接的是正点原子7寸RGB屏幕,不匹配,所以LCD无显示。
(5)网络设备检查
uboot打印日志中提示未找到网络设备:
Booting from net ...
No ethernet found.
No ethernet found.
接下来,我们就基于NXP官方提供的uboot,针对正点原子imx6ull开发板进行一些配置参数的修改,修复LCD和网络问题。
(1)新建单板目录,复制官方EVK的单板目录:
cd board/freescale
cp -rf mx6ullevk/ mx6ullatk
进入新建的目录,进行修改。
① 重命名c文件:
cd mx6ullatk/
mv mx6ullevk.c mx6ullatk.c
② 修改Makefile:
obj-y := mx6ullatk.o
③ 修改imximage.bin文件:
#ifdef CONFIG_USE_PLUGIN
/*PLUGIN plugin-binary-file IRAM_FREE_START_ADDR*/
PLUGIN board/freescale/mx6ullatk/plugin.bin 0x00907000
#else
修改Kconfig文件:
if TARGET_MX6ULL_14X14_ATK
config SYS_BOARD
default "mx6ullatk"
config SYS_VENDOR
default "freescale"
config SYS_CONFIG_NAME
default "mx6ullatk"
endif
修改 MAINTAINERS 文件:
MX6ULLATK BOARD
M: Mculover666 <[email protected]>
S: Maintained
F: board/freescale/mx6ullatk/
F: include/configs/mx6ullatk.h
F: configs/mx6ull_14x14_atk_defconfig
(2)新建开发板默认配置文件:
cd configs/
cp mx6ull_14x14_evk_emmc_defconfig mx6ull_14x14_atk_emmc_defconfig
修改该文件中的下面两行内容:
CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ullatk/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
CONFIG_TARGET_MX6ULL_14X14_ATK=y
(3)新建开发板头文件:
cd include/configs
cp mx6ullevk.h mx6ullatk.h
修改头文件中的内容:
#ifndef __MX6ULLATK_CONFIG_H
#define __MX6ULLATK_CONFIG_H
将文件最后关于android的支持去除:
// #if defined(CONFIG_ANDROID_SUPPORT)
// #include "mx6ullevk_android.h"
// #endif
(4)在menuconfig中添加新的单板
仿照其它单板文件,添加:
config TARGET_MX6ULL_14X14_ATK
bool "Support mx6ull_14x14_atk"
select MX6ULL
select DM
select DM_THERMAL
在临近末尾处仿照添加:
source "board/freescale/mx6ullatk/Kconfig"
(5)新的单板编译测试
至此,添加一个新的单板到uboot中完成,编译测试:
make distclean
make mx6ull_14x14_atk_emmc_defconfig
make
编译成功后,将bin烧写到SD卡,启动查看效果:
和原厂uboot运行效果一样,新建单板成功。
uboot中修改驱动配置在xxx.h
和xxx.c
两个文件中,xxx为单板名称。
检查LCD驱动的思路为:
① 检查LCD引脚配置
② 检查屏幕参数配置
① 检查LCD引脚配置:查看原理图,本文所使用的开发板和官方开发板的LCD引脚相同,无需修改;
② 检查屏幕参数配置
本文中使用的是正点原子7寸RGB屏幕,在board/freescale/mx6ullatk/mx6ullatk.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 = "TFT7016",
.xres = 1024,
.yres = 600,
.pixclock = 19351,
.left_margin = 140,
.right_margin = 160,
.upper_margin = 20,
.lower_margin = 12,
.hsync_len = 20,
.vsync_len = 3,
.sync = 0,
.vmode = FB_VMODE_NONINTERLACED
} } };
接着在include/configs/mx6ullatk.h
文件中,修改uboot默认环境变量设置:
"panel=TFT7016\0" \
修改之后,重新编译uboot,烧写到SD卡中运行:
IMX6ULL有两个ENET外设,本文所使用的开发板与官方EVK开发板的区别在于:使用RMII接口连接LAN8720A PHY 芯片,且复位引脚不同,其余引脚相同。
所以修改网络驱动的思路为:
ENET1原理图如下:
ENET2原理图如下:
两个复位引脚分别对应的引脚如下表:
信号 | 引脚 |
---|---|
ENET1_RST | SNVS_TAMPER7/GPIO5_IO07 |
ENET2_RST | SNVS_TAMPER8/GPIO5_IO08 |
① 修改PHY芯片器件地址
在include/configs/mx6ullatk.h
文件中修改两个PHY芯片的器件地址:
#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
② 修改复位引脚驱动
复位引脚在EVK开发板上被74LV595使用,如图:
74LV595是串行转并行的一个芯片,通常用来扩展IO引脚,在本文所使用的开发板上未使用,所以在board/freescale/mx6ullatk/mx6ullatk.c
和include/configs/mx6ullatk.h
文件中去掉相关代码,去除之后一定要确保编译成功才可以进行后续工作。
接下来在mx6ullatk.c
中添加复位引脚配置:
#define ENET1_RESET IMX_GPIO_NR(5, 7)
#define ENET2_RESET IMX_GPIO_NR(5, 8)
/*
* pin conflicts for fec1 and fec2, GPIO1_IO06 and GPIO1_IO07 can only
* be used for ENET1 or ENET2, cannot be used for both.
*/
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_TX_DATA0__ENET1_TDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_TX_DATA1__ENET1_TDATA01 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_TX_EN__ENET1_TX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 | MUX_PAD_CTRL(ENET_CLK_PAD_CTRL),
MX6_PAD_ENET1_RX_DATA0__ENET1_RDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET1_RX_DATA1__ENET1_RDATA01 | 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),
/* RST */
MX6_PAD_SNVS_TAMPER7__GPIO5_IO07 | MUX_PAD_CTRL(NO_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_TX_DATA0__ENET2_TDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_TX_DATA1__ENET2_TDATA01 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 | MUX_PAD_CTRL(ENET_CLK_PAD_CTRL),
MX6_PAD_ENET2_TX_EN__ENET2_TX_EN | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_RX_DATA0__ENET2_RDATA00 | MUX_PAD_CTRL(ENET_PAD_CTRL),
MX6_PAD_ENET2_RX_DATA1__ENET2_RDATA01 | 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),
/* RST */
MX6_PAD_SNVS_TAMPER7__GPIO5_IO08 | MUX_PAD_CTRL(NO_PAD_CTRL),
};
再添加一个硬复位函数,在ETH初始化的时候,硬件复位外部PHY芯片:
static void board_eth_hard_reset(int fec_id)
{
if (fec_id == 0) {
gpio_direction_output(ENET1_RESET, 1);
gpio_set_value(ENET1_RESET, 0);
mdelay(20);
gpio_set_value(ENET1_RESET, 1);
} else {
gpio_direction_output(ENET2_RESET, 1);
gpio_set_value(ENET2_RESET, 0);
mdelay(20);
gpio_set_value(ENET2_RESET, 1);
}
}
int board_eth_init(bd_t *bis)
{
setup_iomux_fec(CONFIG_FEC_ENET_DEV);
board_eth_hard_reset(CONFIG_FEC_ENET_DEV);
return fecmxc_initialize_multi(bis, CONFIG_FEC_ENET_DEV,
CONFIG_FEC_MXC_PHYADDR, IMX_FEC_BASE);
}
编译uboot,下载到SD卡中,运行查看日志:
代码中搜索 "address not set"来定位问题,很快就找到根源了:
uboot从环境变量中加载网卡MAC地址,结果MAC地址为空,所以输出错误提示。
解决方案非常简单,在uboot中设置一下MAC地址即可:
setenv ethaddr 00:04:9f:04:d2:35
saveenv
重启之后可以看到无错误提示,设置ip地址、网关地址、子网掩码:
setenv ipaddr 192.168.1.101
setenv gatewayip 192.168.1.1
setenv netmask 255.255.255.0
saveenv
接着ping开机主机测试:
ping 192.168.1.100
报如下错误:
这个问题实在是太难定位了,查询正点原子移植资料寻求解决办法,修复LAN8720A的代码,编辑drivers/net/phy/phy.c
文件中的函数 genphy_update_link
,在函数刚进入时添加如下代码:
#ifdef CONFIG_PHY_SMSC
static int lan8720_flag = 0;
int bmcr_reg;
if (lan8720_flag == 0) {
bmcr_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
while (phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR) & 0x8000) {
udelay(100);
}
phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, bmcr_reg);
lan8720_flag = 1;
}
#endif /* CONFIG_PHY_SMSC */
重新编译,烧写,测试结果如下:
同理,在include/configs/mx6ullatk.h
文件中将默认网卡设为0,测试ETH1是否正常:
#define CONFIG_FEC_ENET_DEV 0
重新编译uboot、烧写,将网线查到ETH1网口上,测试结果如下:
修改board/freescale/mx6ullatk/mx6ullatk.c
文件:
int checkboard(void)
{
puts("Board: i.MX6ULL ATK ALPHA\n");
return 0;
}
检查emmc中是否有内核文件和设备树文件:
查看uboot传递给kernel的启动参数:
这个和本文所使用的开发板出厂烧录的kernel不一致,修改一下保存:
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
saveenv
(1)手动启动
手动加载内核到内存中:
mmc dev 1
fatload mmc 1:1 80800000 zImage
fatload mmc 1:1 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb
启动内核:
bootz 80800000 - 83000000
可以看到内核成功启动。
(2)使用bootcmd启动
查看nxp uboot官方提供的bootcmd值:
不使用这个,直接将上述手动启动的命令写为一条命令,放在bootcmd环境变量中:
setenv bootcmd 'mmc dev 1;fatload mmc 1:1 80800000 zImage;fatload mmc 1:1 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb;bootz 80800000 - 83000000;'
saveenv
设置完成后,直接启动:
boot
确保tftp服务正常,且内核和设备树文件已放置在tftp根目录。
setenv serverip 192.168.1.102
设置启动参数:
setenv bootargs 'console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw'
保存环境变量:
saveenv
下载内核到内存中:
tftp 80800000 zImag
tftp 83000000 imx6ull-14x14-emmc-7-1024x600-c.dtb
bootz 80800000 - 83000000
启动成功。