故障描述
LicheePI Zero 核心板,配备 32M SPI-NOR-Flash,Console口 UART0 通过 RS232 接口芯片连接到上位机(如下图)。在测试中发现在设备上电后等待很久都不能 ping 通设备,使用 RS232 转 TTL 电缆连接上位机,打入回车键,发现是 u-boot 的提示符 ==>,说明设备自动进入到了u-boot 状态,导致启动失败。
使用上位机通过Console手动输入 boot 命令,可正常启动。正常启动后,执行操作系统的 reboot 指令,主板可以正常启动,不会进入到 u-boot。而拔出上位机电缆,断电后重新上电时,复现进入到 u-boot 的故障。
相同的主板,如果不接 RS232 转换芯片时,不会出现进入到 u-boot 的情况。
分析和测试
根据上述比较,猜测问题可能出在 RS232 转换接口上。如果不接上位机电缆,上电时,U5 的13/14 脚均为低电平。如果连接了上位机电缆,U5 的对应管脚会被正确地上拉/下拉。考虑到这一点,基本可以确定是 u-boot 扫描到了Console 口有“按键”输入,从而终止自动启动(autoboot)。
打开 VS Code,全局搜索“Hit any key to stop autoboot:”,最终定位到 .../u-boot/common/autoboot.c 的 __abortboot(int) 函数。通过分析代码,得知 __abortboot 的算法:当捕捉到任何一个按键输入时,即终止 boot,进入到 u-boot 命令行环境。
解决方法
找到了可能的原因,将这一部分的代码修改成如下内容。
static int __abortboot(int bootdelay)
{
int abort = 0;
unsigned long ts;
int gotc = 0;
#ifdef CONFIG_MENUPROMPT
printf(CONFIG_MENUPROMPT);
#else
printf("Hit any key to stop autoboot: %2d ", bootdelay);
#endif
/*
* Check if key already pressed
*/
if (tstc())
{ /* we got a key press */
/* Test gotc == RETURN_KEY || gotc == SPACE_BAR
If true, set abort = 1; others let abort alone.
*/
#if (0)
(void)getc(); /* consume input */
puts("\b\b\b 0");
abort = 1; /* don't auto boot */
#else
gotc = getc(); /* save key pressed */
#if (1)
printf("We catched key press as: %02X\r\n", gotc);
#endif
if (gotc == 0x0D || gotc == 0x20)
{
/*puts("\b\b\b 0");*/ /* Clear counting down number and filled with 0 */
abort = 1; /* Set abort boot flag */
}
#endif
}
while ((bootdelay > 0) && (!abort))
{
--bootdelay;
/* delay 1000 ms */
ts = get_timer(0);
do
{
if (tstc())
{ /* we got a key press */
#if (0)
abort = 1; /* don't auto boot */
bootdelay = 0; /* no more delay */
#ifdef CONFIG_MENUKEY
menukey = getc();
#else
(void)getc(); /* consume input */
#endif
#else
#ifdef CONFIG_MENUKEY
menukey = getc();
if (menukey == 0x0D || menukey == 0x20)
{
abort = 1; /* don't auto boot */
bootdelay = 0; /* no more delay */
}
#else
gotc = getc(); /* consume input */
#if (1)
printf("We catched key press as: %02X\r\n", gotc);
#endif
if (gotc == 0x0D || gotc == 0x20)
{
abort = 1; /* don't auto boot */
bootdelay = 0; /* no more delay */
}
#endif
#endif
break;
}
udelay(10000);
} while (!abort && get_timer(ts) < 1000);
printf("\b\b\b%2d ", bootdelay);
}
putc('\n');
return abort;
}
#if(0) 块中的代码是原有的代码,#else中的代码是修改后的代码。主要思路:
- 第一次捕获按键输入时,判断是否为回车键(0x0D)或者空格键(0x20),如果是回车键或者空格键,才设置abort标志。
- 循环捕获按键输入时,也做上述同样的判断,符合条件时才设置abort标志。
- gotc变量保存getc()的结果。
- if(1) printf("We catched key press as: %02X\r\n", gotc); #endif 块纯粹为测试保留,可以设置为 if(0)。
- 尽量保留原有的 CONFIG_xxx条件。
使用
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j16
将 u-boot-sunxi-with-spl.bin 文件拷贝到 buildroot 目录,重建整个 flashimg.bin,通过上位机烧录到Flash中,等待主板正常启动一次。然后断电,拆掉上位机电缆。连接好主板的以太网口,在上位机上 ping 主板。将主板上电。一开始是 ping 不通的(还没启动完成),等待大约10秒钟后,上位机可以 ping 通主板,这时执行
telnet 192.168.0.xxx(主板的IP地址)
可以正常登录。说明故障得以解决。
反复断电/上电大约 100次,没有捕捉到主板“误入”u-boot的情况。
参考链接
感谢 roostnew 的思路,本文对如何修改 u-boot 的代码补充了一下。