ELF2 开发板安装和使用
收到开发板后,打开包装,包含以下内容:开发板、电源适配器、尼龙柱x4、螺钉x4、TypeC USB线。将尼龙柱和螺钉安装到开发板上。效果如下图所示。

板载CPU为RK3588,包括4核Cortex-A76和4核Cortex-A55、独立的NEON协处理器和神经网络加速处理器NPU。ELF2板载的硬件分布如下


1搭建开发环境
按照开发板配套的资料“01-教程文档/01-2系统开发教程/《ELF2开发板编译手册.pdf》”中的第三章在笔记本上搭建ubuntu开发环境。笔记本为联想的Y470,内存8G,使用的系统是Ubuntu22.04。
1.1安装必要的库
首先安装必要的Linux包,按下“ALT+CTRL+T”或者其他方式打开终端,输入下列指令
sudo apt-get update
sudo apt-get install openssh-server vim git fakeroot
sudo apt-get install ssh make gcc libssl-dev liblz4-tool expect expect-dev g++ patchelf chrpath gawk texinfo chrpath diffstat binfmt-support qemu-user-static live-build bison flex cmake gcc-multilib g++-multilib unzip device-tree-compiler ncurses-dev libgucharmap-2-90-dev bzip2 expat gpgv2 cpp-aarch64-linux-gnu libgmp-dev libmpc-dev bc python-is-python3 python2
安装下列库使用网络配置工具和menuconfig
sudo apt-get install libncurses*
sudo apt-get install net-tools
1.2安装交叉编译链
RK3588属于arm64架构,拷贝"06-常用工具\06-1编译工具安装脚本"aarch64-buildroot-linux-gnu_sdk-buildroot.tar.gz"到Ubuntu系统的/home/xxx/目录下,执行下列指令解压。
tar -zvxf aarch64-buildroot-linux-gnu_sdk-buildroot.tar.gz
进入aarch64-buildroot-linux-gnu_sdk-buildroot目录执行其中的relocate-sdk.sh,更新开发工具的目录信息
cd aarch64-buildroot-linux-gnu_sdk-buildroot/
./relocate-sdk.sh
1.3Qt Creator 安装
将qt-creator-opensource-linux-x86_64-4.7.0.run拷贝到开发环境/home/xxx/目录下,执行下面命令。
./qt-creator-opensource-linux-x86_64-4.7.0.run
接下来根据弹出的指引界面完成QT Creator的安装。
至此,用于编译ELF2 Linux SDK源码的工具都已就绪,接下来进行Linux 源码的编译
1.4搭建TFTP服务器
安装tftp服务端
sudo apt-get install tftpd=hpa
配置tftp服务器,修改/etc/default/tftpd-hpa
sudo vim /etc/default/tftpd-hpa
添加的内容为
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/home/ept/tftpboot" #用于tftp上传和下载的目录
TFTP_ADDRESS=":69"
TFTP_OPTIONS="-l-c-s"
创建用于tftp服务的文件夹并修改其权限
sudo mkdir /home/ept/tftpboot
sudo chmod 777 /home/ept/tftpboot
重启tftp服务器
sudo service tftpd-hpa restart
2.ELF2 Linux SDK编译
开发板配套了用于板卡开发的SDK包“ELF2-linux-source”,提供了包括uboot、kernel、QT、AI应用等在内的开发支持。在用户目录/home/xxx/中创建存放SDK包“ELF2-linux-source”的目录work,将用户资料中的源码文件ELF2-linux-source.tar.bz2.0* 拷贝到创建的目录。将压缩包解压到指定的文件夹中。
tar -xvf ELF2-linux-source.tar.bz2
切换目录到解压出来的ELF2-linux-source
文件夹中
cd ELF2-linux-source
在目录中执行以下指令,设置SDK中开发板的硬件配置以及文件系统类型。
./build.sh chip

在弹出的界面中首先应用的平台,这里选择1即为ELF2板卡;接下来选择编译的文件系统:选择1为编译Ubuntu22.04文件系统;选择2为编译buildroot构建的文件系统。
完成上述操作后,输入以下指令进行一次全编译,同时记录编译过程中的信息到buildlog文件中。
./build.sh > buildlog
编译完成后,rockdev下存放的是不同镜像文件的链接,可以在output目录下找到对应的镜像文件的链接。

3.在Kernel中添加自定义模块
Device Driver是Linux驱动开发中的核心,Device Driver以模块的方式添加的Linux Kernel,其编译方式分为In-Tree和Out-Tree。采用In-tree编译方式的比如音频子系统ALSA、网络驱动等,采用树外编译的比如各种传感器的驱动。本次测评的ALSA中的ASoC采用In-Tree的编译方式。这里对In-Tree的编译方式进行介绍,以一个简单的模块添加到内核编译中为例。
3.1模块功能和代码
这里以一个输出信息到终端的字符设备为例。模块的代码如下
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/version.h>
MODULE_LICENSE("Dual BSD/GPL");
static unsigned int major;
static struct class* elf2_class;
static struct cdev elf2_dev;
int elf2_open(struct inode* inode,struct file* flip)
{
pr_info("Open elf2 char dev.\r\n");
return 0;
}
int elf2_release(struct inode* inode,struct file* flip)
{
pr_info("Close elf2 char dev.\r\n");
return 0;
}
ssize_t elf2_read(struct file* flip,char __user * buf,size_t count,loff_t* offset)
{
pr_info("Try to read data.\r\n");
return 0;
}
ssize_t elf2_write(struct file* flip,const char __user * buf,size_t count,loff_t* offset)
{
pr_info("Try to Write data.\r\n");
return count;
}
struct file_operations elf2_fops = {
.open = elf2_open,
.release= elf2_release,
.read = elf2_read,
.write = elf2_write,
};
static int __init hello_init(void)
{
struct device* elf2_device;
int error;
dev_t dev=0;
printk("ELF2 chardev example init.\r\n");
error=alloc_chrdev_region(&dev,0,1,"elf2_char");
if(error<0)
{
pr_err("Can not get major number.\r\n");
return error;
}
major=MAJOR(dev);
pr_info("elf2 char dev major num= %d\n",major);
elf2_class=class_create(THIS_MODULE,"ELF2_char_class");
if(IS_ERR(elf2_class))
{
pr_err("Error creating elf2 char class.\n");
unregister_chrdev_region(MKDEV(major,0),1);
return PTR_ERR(elf2_class);
}
cdev_init(&elf2_dev,&elf2_fops);
elf2_dev.owner=THIS_MODULE;
cdev_add(&elf2_dev,dev,1);
elf2_device=device_create(elf2_class,
NULL,
dev,
NULL,
"elf2_char"
);
if(IS_ERR(elf2_device))
{
pr_err("Error creating elf2 char device.\n");
class_destroy(elf2_class);
unregister_chrdev_region(dev,1);
return -1;
}
pr_info("elf2 char module loaded.\n");
return 0;
}
static void __exit hello_exit(void)
{
unregister_chrdev_region(MKDEV(major,0),1);
device_destroy(elf2_class,MKDEV(major,0));
cdev_del(&elf2_dev);
class_destroy(elf2_class);
printk("elf2 char module unload/n");
}
module_init(hello_init);
module_exit(hello_exit);
模块的功能也很简单,不同的操作都只输出一条信息。在kernel/drivers创建usr文件夹,在其中建立usr_dev.c,将上述的源代码保存在其中。
3.2KConfig和makefile设置
Linux Kernel的编译系统kbuild采用KConfig和makefile结合的的方式灵活地控制内核模块的编译,KConfig为用户提供图形化的配置界面,供用户选择kernel中需要编译的模块以及编译方式(决定代码是否编译到内核镜像中)。
kbuild系统中的obj-x变量用于控制模块的编译方式,其中的x可以为y、m、n。根据x的不同的,决定模块的编译方式。
- x为m时,obj-m变量控制的模块会作为独立的模块文件生成;
- x为y时,obj-y变量控制的模块会作为内核镜像的一部分;
内核中涉及的模块很多,用户手动管理不同模块的是否编译以及编译方式显然不可能。KConfig工具模块进行管理。上述控制上述模块是否编译的KConfig设定如下:
config ELF2_OPTION
tristate "User defined module"
default m
help
Build user defined module example
在kernel/drivers/usr
文件夹中建立KConfig文件,添加上述内容,在其所在的上一级文件夹的Kconfig文件中添加文件路径用于Kconfig程序查找该选项
source "drivers/usr/Kconfig"
其中文件路径为kernel文件夹下的相对路径,这样在menuconfig中就可以对设定的选项进行配置。
执行ELf2-linux-source文件夹中的脚本./build.sh kconfig
即可进入Linux内核的配置界面,在Device Drivers
下可以看到新添加的KConfig选项。将选项设置为"*"表示编译到内核中。退出并保存新的配置。

在Makefile中可以看到obj-$(CONFIG_XXX)
模式的控制脚本,根据CONFIG_XXX
变量的值可以确定obj-$(CONFIG_XXX)
相关的模块的编译方式。在makefile中添加类似的控制项,从而实现对自定义模块的条件编译。与KConfig文件的添加过程类似,在kernel/drivers/usr
中添加makefile文件,向其中添加以下内容。
obj-$(CONFIG_ELF2_OPTION) +=usr_dev.o
同时在上一级目录的makefile中添加
obj-y += usr/
到此,Kconfig和makefile的配置就算完成了。
3.3Linux Kernel编译
上述的源代码文件、KConfig和makefile选项设置完成后,执行ELF2_linux_source文件夹下的build.sh脚本并添加kernel选项,即可对Linux内核进行一次编译。
./build.sh kernel
在编译界面可以谈到输出信息中对上述添加的文件进行了编译,并生成了新的boot.img用于烧录。

将生成的boot.img文件复制到tftp服务的文件夹中。
cp kernel/boot.img /home/ept/tftpboot/boot.img
3.4kernel镜像烧录
参考RK原厂资料中"07-RK原厂资料/cn/Common/UBOOT/《Rockchip_Developer_Guide_UBoot_TFTP_Upgrade_CN.pdf》"的3.3节通过uboot中的tftflash指令将编译好的kernel镜像烧录到开发板中。
在开发板开机过程,按住"Ctrl+C"进行boot模式,首先设定相关的网络配置:
setenv serverip '192.168.0.20' #主机的IP地址
setenv ipaddr '192.168.0.30' #开发板的IP地址
setenv gateway '192.168.0.1'
setenv netmask '255.255.255.0'
saveenv
设置完成,使用ping指令检测网络通讯是否正常。

在命令行中输入以下指令
tftpflash 0x20000000 boot.img boot
这样就可以单独更新kernel镜像,完成更新后,输入reset
指令重新启动开发板,登入系统后可以查看dev文件夹下新增了之前添加的模块,。

3.5测试模块
编写以下测试程序,检测模块是否可以正常输出
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
static char elf2_dev[]="/dev/elf2_char";
int main(int argc,char * argv[])
{
int fd,ret;
char readbuf[10],writebuf[10];
fd=open(elf2_dev,O_RDWR);
if(fd<0)
{
printf("Can't open file %s\r\n",elf2_dev);
return -1;
}
ret=read(fd,readbuf,10);
ret=write(fd,writebuf,10);
ret=close(fd);
return 0;
}
在环境变量PATH路径中添加交叉编译器的路径,为了方便在/home/ept/.bashrc
中添加以下脚本,这样在打开终端后,会自动向PATH中添加交叉编译器的路径。
export PATH=/home/ept/elf2/work/ELF2-linux-source/prebuilts/gcc/linux-x86/aarch64/gcc-arm-10.3-2021.07-x86_64-aarch64-none-linux-gnu/bin/:$PATH
对上述源码进行编译,输出得到二进制文件。
aarch64-none-linux-gnu-gcc elf2_char_app.c -o elf2_char_app
在Linux上安装FileZilla,参考FileZilla官网的安装指南。

运行FileZilla,链接到开发板,将编译好的文件传输到开发板上。

通过chmod 777 elf2_char_app
指令修改其权限,使其可以执行。执行上述文件后,在终端得到输出信息,可以确定上述模块可以正常执行。

总结
按照ELF2开发板的指引,可以很方便地搭建开发环境,配套的资料也很全,通过查找资料可以解决在开发过程中遇到的大部分问题。