飞凌全志T527开发板U-Boot添加自定义菜单

news2024/9/23 17:14:16

昨日,终于收到了心心念念的飞凌OK-T527开发板,板子很漂亮,外设丰富,性能强悍,T527创新性地使用了RISC-V架构的协处理器,后期值得研究一下异核的使用:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

有趣的是,板子上电,按任意键进入U-Boot会自动列出一个功能菜单,有切换屏幕等功能:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
基于此,本文将分析如何在U-Boot添加自定义菜单。

一、实验环境介绍
硬件:飞凌OK-T527开发板
软件:全志Longan SDK(U-Boot版本2018)
说明:本次实验不限制平台,请参考实际情况阅读。

二、目标
本文主要分析U-Boot在程序中的执行顺序,又如何在U-Boot阶段调起菜单?相信大家都试过,在U-Boot倒数结束前按任意按键后,会进入U-Boot命令行模式。
这里先留一个问题:如何做到按键按下后,调启的是自己的U-Boot菜单,而不再是进入命令行模式?

三、U-Boot如何自动调起菜单
U-Boot的入口程序文件是 /common/main.c,入口函数main_loop()

/* <u-boot>/common/main.c */

...

/* We come here after U-Boot is initialised and ready to process commands */
/* 在U-Boot初始化并准备好处理命令之后,我们来到这里。 */
void main_loop(void)
{
    const char *s;

    bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");	

    #ifdef CONFIG_VERSION_VARIABLE
    env_set("ver", version_string);  /* set version variable */		
    #endif /* CONFIG_VERSION_VARIABLE */

    cli_init();							//命令初始化有关,初始化 hush shell 相关的变量

    run_preboot_environment_command();	//获取环境变量 perboot 的内容

    #if defined(CONFIG_UPDATE_TFTP)
    update_tftp(0UL, NULL, NULL);		
    #endif /* CONFIG_UPDATE_TFTP */

    s = bootdelay_process();	//此函数会读取环境变量 bootdelay 和 bootcmd 的内容
    if (cli_process_fdt(&s))
        cli_secure_boot_cmd(s);

    autoboot_command(s);		//开启倒计时,并在倒计时结束前检测是否有按键按下

    cli_loop();					//命令行处理函数(即进入U-Boot命令行)
    panic("No CLI available");
}

关键函数是autoboot_command(),该函数的实现在 /common/autoboot.c

/* <u-boot>/common/autoboot.c */

...

void autoboot_command(const char *s)
{
    debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

    if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {	// 倒计时过程中,没有按键按下
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
    int prev = disable_ctrlc(1);    /* disable Control C checking */
#endif

    run_command_list(s, -1, 0);		// 倒计时结束后,启动内核

#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
    disable_ctrlc(prev);    /* restore Control C checking */
#endif
    }

#ifdef CONFIG_MENUKEY
    if (menukey == CONFIG_MENUKEY) {
        s = env_get("menucmd");
        if (s)
            run_command_list(s, -1, 0);
    }
#endif /* CONFIG_MENUKEY */
}

进入 autoboot_command() 后,先看第一个if:

void autoboot_command(const char *s)
{
    ...
        
    if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay))

    ...
}

这里有三个条件:
stored_bootdelay != -1:stored_bootdelay是倒数的总时间,就是常见的3秒、5秒不等;
s:传进来的参数s不能为空;
!abortboot(stored_bootdelay):该函数会从stored_bootdelay开始倒计时,期间判断是否有按键按下。函数实现如下,倒计时过程中若检测到按键按下,则令abort=1。无按键按下,则abort=0。最后返回abort。

/* <u-boot>/common/autoboot.c */

...

static int __abortboot(int bootdelay)
{
        int abort = 0;
        unsigned long ts;

#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   */
                (void) getc();  /* consume input        */
                puts("\b\b\b 0");
                abort = 1;      /* don't auto boot      */
        }

        while ((bootdelay > 0) && (!abort)) {
                --bootdelay;
                /* delay 1000 ms */
                ts = get_timer(0);
                do {
                        if (tstc()) {   /* we got a key press   */
                                abort  = 1;     /* don't auto boot      */
                                bootdelay = 0;  /* no more delay        */
# ifdef CONFIG_MENUKEY
                                menukey = getc();
# else
                                (void) getc();  /* consume input        */
# endif
                                break;
                        }
                        udelay(10000);
                } while (!abort && get_timer(ts) < 1000);

                printf("\b\b\b%2d ", bootdelay);
        }

        putc('\n');

        return abort;
}

...

刚刚说了,abortboot() 函数执行期间有按键按下的话,abortboot() 会返回1,那就不会进入第一个if,程序会接着往下运行直至该函数运行结束。autoboot_command() 结束后继续返回到main_loop(),随后立刻执行cli_loop(),进入我们所熟悉的U-Boot命令行模式。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
至此,就实现了U-Boot倒数期间,有按键按下,则进入U-Boot的命令行模式。
现在继续回到第一个if:

/* <u-boot>/common/autoboot.c */

void autoboot_command(const char *s)
{
    ...
        
    if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {	// 倒计时过程中,没有按键按下
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
    int prev = disable_ctrlc(1);    /* disable Control C checking */
#endif

    run_command_list(s, -1, 0);		// 倒计时结束后,启动内核

#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
    disable_ctrlc(prev);    /* restore Control C checking */
#endif
    }

    ...
}

如果在autoboot倒计时结束前,一直没有按键按下呢?那 abortboot() 最后会返回0,第一个if的三个条件全部满足。进入if,run_command_list() 执行一系列命令后,启动内核。注意,这里的现象是直接启动内核,run_command_list() 后的程序不再执行。

解析到这里,我们得出一个结论:在autoboot倒计时中,如果有按键按下的话,会进入U-Boot的命令行模式。无按键按下则在倒计时结束后直接启动内核。
那现在可以回答第一个问题,如何做到按下按键后,是自启动U-Boot菜单,而不是进入U-Boot命令行呢?答案是在执行cli_loop()之前,我们可以在autoboot检测到按键按下后,调用run_command()函数执行menu命令,从而调起菜单。

void autoboot_command(const char *s)
{
    debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

    if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
    int prev = disable_ctrlc(1);    /* disable Control C checking */
#endif

    run_command_list(s, -1, 0);

#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
    disable_ctrlc(prev);    /* restore Control C checking */
#endif
    }

    //在此处启动菜单
    run_command("menu", 0);
    
#ifdef CONFIG_MENUKEY
    if (menukey == CONFIG_MENUKEY) {
        s = env_get("menucmd");
        if (s)
            run_command_list(s, -1, 0);
    }
#endif /* CONFIG_MENUKEY */
}

四、U-Boot添加自定义命令
难道通过 run_command() 执行menu命令后,菜单就自己出来了?这是一个理所当然的猜想。实际上U-Boot根本不认识menu命令:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
接下来看看如何添加U-Boot命令,参考一下别人的代码:

/* <u-boot>/board/BuS/eb_cpu5282/eb_cpu5282.c */

int do_brightness(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	int rcode = 0;
	ulong side;
	ulong bright;

	switch (argc) {
	case 3:
		side = simple_strtoul(argv[1], NULL, 10);
		bright = simple_strtoul(argv[2], NULL, 10);
		if ((side >= 0) && (side <= 3) &&
			(bright >= 0) && (bright <= 1000)) {
			vcxk_setbrightness(side, bright);
			rcode = 0;
		} else {
			printf("parameters out of range\n");
			printf("Usage:\n%s\n", cmdtp->usage);
			rcode = 1;
		}
		break;
	default:
		printf("Usage:\n%s\n", cmdtp->usage);
		rcode = 1;
		break;
	}
	return rcode;
}

U_BOOT_CMD(
	bright,	3,	0,	do_brightness,
	"here is uboot mymenu\n",
	"here is uboot mymenu, make in 2024-05-15\n"
);

先看最底下的U_BOOT_CMD,这是一个宏,用来添加U-Boot命令:

/* <u-boot>/include/command.h */

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)		\
	U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

_name:命令的名字
_maxargs:添加的命令最多有几个参数
_rep:是否重复(1重复,0不重复),指在U-Boot命令行按下Enter键的时候,重复执行上次的命令
_cmd:执行函数(即执行该命令后,运行哪个函数)
_usage:短帮助信息
_help:长帮助信息

再来看看执行函数do_brightness的声名:

int (*cmd)(struct cmd_tbl_s *cmdtp, int flag, int argc, char *const argv[]);

cmdtp:Table entry describing the command (see above).
flag:A bitmap which may contain the following bit
○ CMD_FLAG_REPEAT - The last command is repeated.
○ CMD_FLAG_BOOTD - The command is called by the bootd command.
○ CMD_FLAG_ENV - The command is called by the run command.
argc:执行命令时,传入的参数数量
argv:传入的参数

五、实践
下面,添加一个U-Boot菜单,不过只作打印,没有实际功能。
/drivers 下创建一个名为mymenu的文件夹:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
mymenu文件夹下创建mymenu.c,内容如下:

#include <common.h>
#include <command.h>
#include <linux/ctype.h>
#include <cli.h>
#include <fs.h>

static int do_mymenu(struct cmd_tbl_s *cmdtp, int flag, int argc, char *const argv[])
{

    printf("\n======== Title ========\n"); 
    printf("== [1] xxxxxx\n");
    printf("== [2] xxxxxx\n");
    printf("== [3] xxxxxx\n");
    printf("== [4] xxxxxx\n");
    printf("=========================\n\n");

    return 0;
}

U_BOOT_CMD(
	menu, 1, 1, do_mymenu,
	"here is uboot menu\n",
	"here is uboot menu, make in 2024-06-27\n"
);

还需在mymenu文件夹下创建一个Makefile文件,内容如下:

obj-y += mymenu.o

最后修改 /drivers/ 下的Makefile,在结尾加上如下内容,表示要编译mymenu路径下的文件:

ifeq ($(CONFIG_SPL_BUILD)$(CONFIG_TPL_BUILD),)
obj-y += mymenu/
endif

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
编译U-Boot,更新U-Boot,重启单板,在U-Boot倒计时结束前,按任意按键就进入我们自定义的菜单:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
剩下的菜单程序编写就是根据实际功能来开发了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1926884.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

智能制造 v3.13.16 发布,ERP、MES 更新

智能制造一体化管理系统 [SpringBoot2 - 快速开发平台]&#xff0c;适用于制造业、建筑业、汽车行业、互联网、教育、政府机关等机构的管理。包含文件在线操作、工作日志、多班次考勤、CRM、ERP 进销存、项目管理、EHR、拖拽式生成问卷、日程、笔记、工作计划、行政办公、薪资模…

0.单片机工作原理

文章目录 最小系统 单片机芯片 时钟电路 复位电路 电源 最小系统 单片机芯片 本次51单片机的芯片为&#xff1a;STC89C52 Flash(闪存)程序存储器&#xff1a;存储程序的空间 SRAM&#xff1a;数据存储器&#xff0c;可用于存放程序执行的中间结果和过程数据 DPTR&#xff1a;…

某客户报表系统Oracle数据库挂起问题分析处理

某客户报表系统Oracle数据库挂起问题分析处理 一、概要 某客户报表系统Oracle数据库在3月5号、6号均出现一节点实例短暂挂起现象&#xff0c;挂起现象有两种&#xff0c;第一是普通用户不能登录数据库&#xff0c;第二是sys用户可以登录数据库&#xff0c;但是做简单的select查…

C判断一个点在三角形上

背景 鼠标操作时&#xff0c;经常要判断是否命中显示控件&#xff0c;特开发此算法快速判断。 原理 三角形三等分点定理是指在任意三角形ABC中&#xff0c;可以找到三个点D、E和F&#xff0c;使得线段AD、BE和CF均等分三角形ABC。 这意味着三个等分点分别位于三个边界上&…

数据湖表格式 Hudi/Iceberg/DeltaLake/Paimon TPCDS 性能对比(Spark 引擎)

当前&#xff0c;业界流行的集中数据湖表格式 Hudi/Iceberg/DeltaLake&#xff0c;和最近出现并且在国内比较火的 Paimon。我们现在看到的很多是针对流处理场景的读写性能测试&#xff0c;那么本篇文章我们将回归到大数据最基础的场景&#xff0c;对海量数据的批处理查询。本文…

具身大模型研究综述

源自&#xff1a;哈工大SCIR 作者&#xff1a;陈一帆&#xff0c;张宇驰&#xff0c;孙楚芮&#xff0c;冯怀绪&#xff0c;宋浩&#xff0c;王寄哲 指导老师&#xff1a;张伟男 注&#xff1a;若出现无法显示完全的情况&#xff0c;可 V 搜索“人工智能技术与咨询”查看完整…

什么叫图像的双边滤波,并附利用OpenCV和MATLB实现双边滤波的代码

双边滤波&#xff08;Bilateral Filtering&#xff09;是一种在图像处理中常用的非线性滤波技术&#xff0c;主要用于去噪和保边。它在空间域和像素值域上同时进行加权&#xff0c;既考虑了像素之间的空间距离&#xff0c;也考虑了像素值之间的相似度&#xff0c;从而能够有效地…

赛氪网荣获2024年中国高校计算机教育大会合作伙伴荣誉

2024年7月13日&#xff0c;在黑龙江哈尔滨召开的“2024年中国高校计算机教育大会&#xff08;CCEC2024&#xff09;”&#xff0c;环球赛乐&#xff08;北京&#xff09;科技有限公司(以下简称”赛氪网“)凭借其在高等教育与科技创新领域的卓越贡献&#xff0c;荣幸地获得了本次…

SpringBoot详细解析

1.什么是springboot springboot也是spring公司开发的一款框架。为了简化spring项目的初始化搭建的。那么spring对应springboot有什么缺点呢&#xff1f; spring项目搭建的缺点: 配置麻烦依赖tomcat启动慢 2.springboot的特点 自动配置 Spring Boot的自动配置是一个运行时&…

Docker之在外执行docker内部命令(十一)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+AOSP…

react + redux 状态管理操作

目录 1 概念2 Redux 安装3 创建子模块并导入4 中间件为 react 注入 store5 在组件中使用 store 数据6 修改 store 数据7 提交 action 传参8 异步状态操作9 redux 调试工具 1 概念 Redux 是一个全局状态管理的 JS 库 2 Redux 安装 在react中使用redux&#xff0c;官方要求安…

「网络通信」HTTP 协议

HTTP &#x1f349;简介&#x1f349;抓包工具&#x1f349;报文结构&#x1f34c;请求&#x1f34c;响应&#x1f34c;URL&#x1f95d;URL encode &#x1f34c;方法&#x1f34c;报文字段&#x1f95d;Host&#x1f95d;Content-Length & Content-Type&#x1f95d;User…

Ubuntu使用K3S一分钟快速搭建K8S集群

快速入门指南 | Rancher文档 准备3台服务器 Master节点安装脚本# K3s 提供了一个安装脚本&#xff0c;可以方便的在 systemd 或 openrc 的系统上将其作为服务安装。这个脚本可以在 https://get.k3s.io 获得。要使用这种方法安装 K3s&#xff0c;只需运行以下命令&#xff1a;…

[超级详细系列]ubuntu22.04配置深度学习环境(显卡驱动+CUDA+cuDNN+Pytorch)--[3]安装cuDNN与Pytorch

本次配置过程的三篇博文分享分别为为&#xff1a; [超级详细系列]ubuntu22.04配置深度学习环境(显卡驱动CUDAcuDNNPytorch)--[1]安装显卡驱动 [超级详细系列]ubuntu22.04配置深度学习环境(显卡驱动CUDAcuDNNPytorch)--[2]安装Anaconda与CUDA [超级详细系列]ubuntu22.04配置深…

sql server 练习题5

课后作业 在homework库下执行 作业1&#xff1a; 案例&#xff1a;根据用户分数划分等级。小于60分为不及格&#xff0c;[60,80)为及格&#xff0c;[80,90)为良好&#xff0c;大于等于90分以上为优秀。 建表语句&#xff1a; CREATE TABLE Grades ( ID INT PRIMARY KEY, Name V…

数电基础 - 时序逻辑电路

目录 一. 简介 二. 分析方法 三. 常用的时序逻辑电路 四. 冒险现象 五. 总结 一. 简介 时序逻辑电路是数字电路的重要组成部分&#xff0c;与组合逻辑电路不同&#xff0c;它在任何时刻的输出不仅取决于当时的输入信号&#xff0c;还与电路原来的状态有关。 时序逻辑电路…

pytorch 是如何调用 cusolver API 的调用

0&#xff0c;环境 ubuntu 22.04 pytorch 2.3.1 x86 RTX 3080 cuda 12.2 1, 示例代码 以potrs为例&#xff1b; hello_cholesk.py """ hello_cholesky.py step1, Cholesky decompose; step2, inverse A; step3, Cholesky again; python3 hello_cholesky.py -…

【Django+Vue3 线上教育平台项目实战】构建课程详情页与集成视频播放功能

文章目录 前言一、课程列表页面a.后端代码b.前端代码 二、课程详情页面a. 视频播放功能的集成1.获取上传视频的链接地址2.集成在前端页面中1>使用vue-alipayer视频播放组件2>使用video标签 b. 页面主要内容展示1.后端代码1>分析表2>核心逻辑 2.前端代码3.效果图 前…

网络编程笔记

网络编程 1. 概念 1.1 局域网 局域网&#xff1a;局域网将一定区域的各种计算机、外部设备和数据连接起来形成计算机通信的私有网络 广域网&#xff1a;又称广域网、外网、公网。是连接不同地区局域网或城域网计算机通信的远程公共网络 1.2 IP 本质是一个整形数&#xff…

electron实现右键菜单保存图片功能

1.创建窗口&#xff0c;加载页面&#xff0c;代码如下&#xff1a; //打开窗口const {ipcMain, BrowserWindow} require("electron") const saveImage require("../ipcMain/saveImage") let win null; ipcMain.handle(on-open-event, (event, args) &g…