RK3399 Linux Manual
编译Linux+QT
安装依赖包
使用如下命令安装所需的软件包:
sudo apt-get update
sudo apt-get install git-core gnupg flex bison gperf libsdl1.2-dev libwxgtk3.0-dev build-essential zip curl zlib1g-dev gcc-multilib g++-multilib genromfs libc6-dev-i386 libncurses5-dev x11proto-core-dev libx11-dev ccache libgl1-mesa-dev libxml2-utils xsltproc unzip gperf lsb-core lib32z1-dev lib32ncurses5-dev lzop
编译uboot
说明:QT系统使用的uboot与内核与安卓完全相同,如果编译过android的uboot和内核,则本小节步骤可以省略。
在android源码目录下执行如下命令编译uboot,编译完成后映像文件RK3399MiniLoaderAll_V1.05.bin(因版本不同,名称不一定相同)以及uboot.img会释放到out/release目录。
./mk.sh -u
编译android内核
说明:QT系统使用的uboot与内核与安卓完全相同,如果编译过android的uboot和内核,则本小节步骤可以省略。
在android源码目录下执行如下命令编译linux内核,编译完成后映像文件kernel.img、resource.img会释放到out/release目录。
./mk.sh -k
编译文件系统
在android源码目录下执行如下命令编译linux映像文件,编译完成后linux映像文件linux-rootfs.img会释放到out/release目录。
./mk.sh -b
在执行./mk.sh -b编译buildroot时,默认编译会少一些插件或库,会弹出一些错误信息。
该提示信息表明编译需要git包,执行如下指令安装:
sudo apt-get install git
该提示表明标准的C++库没有安装,执行如下指令安装:
sudo apt-get install lib32stdc++-4.9-dev
该提示表明缺少libz库,执行如下指令安装:
sudo apt-get install lib32z1
再执行./mk.sh -b指令即可正常编译了。编译完成后,最终打包好的文件系统linux-rootfs.img存放在out/release目录下。它包含了QT5.6的标准库,以及一些常用的QT示例。
生成update-linux.img文件系统
在android源码目录下执行如下命令生成单一android映像文件update-linux.img:
./mk.sh -U
update-linux.img为整个QT系统升级文件的单一映像,包括了uboot,内核,文件系统等。注意,生成update-linux.img的先决条件是已经成功编译了uboot,内核和linux文件系统,缺一不可。
烧写linux QT映像
Windows下烧写映像文件
驱动安装
解压光盘tools\x3399烧写工具\windows目录下的DriverAssitant_v4.5.zip文件,打开“DriverInstall.exe”,点击“驱动安装”,提示安装驱动成功即可。
注意事项:
1.目前支持的操作系统包括:XP,Win7_32,Win7_64,Win8_32,Win8_64。
2.XP系统在驱动安装完后,若还提示“发现新设备”, 安装驱动时选择“自动安装”。
3.若之前已经安装过老版本驱动,请先点击“驱动卸载”后再进行“驱动安装”。
生成统一固件update-linux.img
我们已经在mk脚本中集成了生成统一固件的方法,在编译linux文件系统时(./mk.sh -b -U),会自动生成update-android.img,并释放到out/release目录。
烧录固件update-linux.img
解压光盘tools\x3399烧写工具\windows目录下的AndroidTool_Release_v2.38.zip文件,得到AndroidTool_Release_v2.38文件夹,打开AndroidTool.exe,选择“升级固件”选项卡,点击“固件”,在弹出窗口中选择已经生成的update-linux.img文件,如下图所示。

工具配置好后,插上开发板的TypeC线、串口线以及5V DC电源线,按下RECOVERY(VOL+)键不放的同时,长按POWER键开机,在串口终端会有如下打印:

当uboot检测到RECOVERY键按下时,会有“rockusb key pressed”的打印提示,这时,在烧录工具界面会提示发现一个LOADER设备,然后点击升级,即可开始升级过程(注:如果提示发现一个ADB设备,点击切换按钮切换成LOADER设备即可)。

点击升级,稍等片刻,烧写成功界面如下:

多设备升级固件update-linux.img
该工具适合用户批量刷机,可以同时给多台设备烧录固件。
解压光盘tools\x3399烧写工具\windows目录下的FactoryTool-v1.42e.rar文件,打开FactoryTool.exe,点击“固件”选择update-android.img,勾选“升级”,点击“启动”,如下图所示:
步骤1 点击固件,选择需要使用的update-linux.img;
步骤2 点击启动(选择升级按钮);
步骤3 连接开发板USB、DC电源,按下recovery键(对应VOL+键),对应USB口发现设备,并实现自动升级;然后重复步骤3即可同时升级第二台、第三台设备,升级成功或者失败的设备会在两边的列表中列出,移除成功或者失败的设备后可以继续连接需要升级的设备。

linux下烧写映像文件
生成固件update-linux.img
我们已经在mk脚本中集成了生成统一固件的方法,在编译android文件系统时(./mk.sh -s),会自动生成update-linux.img,并释放到out/release目录。
烧录固件update-linux.img
工具路径:tools\x3399烧写工具\linux\ Linux_Upgrade_Tool_v1.24.zip
在升级之前将update-linux.img拷贝到upgrade_tool相同目录下,运行upgrade_tool(需要sudo)
work@ubuntu:~/3288/Linux_Upgrade_Tool_v1.2/cp rockdev/update.img . work@ubuntu:~/3288/Linux_Upgrade_Tool_v1.2$ sudo ./upgrade_tool
执行结果如下图,发现设备列表,输入要升级的DevNo(设备号)选择设备:
选择设备后弹出工具使用菜单如下图,左侧是功能描述,右侧是命令语法,升级相关操作都在upgrade command列表下,忘记命令语法可以输入H进行查看,清屏输入CS,退出按Q。
- CD 命令: 选择设备, 当执行的命令有包含设备重启操作时, 需重新选择设备,当改变操作设备时需重新选择
- SD 命令:msc 切换到 rockusb 升级模式。 当切换执行成功后, 需要重新选择设备
- UF 命令:升级完整 update.img 固件,当执行成功后需要重新选择设备
- UL 命令:升级 loader 功能,当执行成功后需要重新选择设备
- DI 命令:下载单独 image 镜像到指定扇区,例如升级 kernel.img 或者 system.img 都可以
直接使用此功能.例如下载 kernel.img: DI -k kernel.img parameter //如果之前通过 DI 下 载过 parameter,则再下载 kernel.img 时就可以不用指定最后的 parameter 参数
- DB 命令:下载 boot,在 maskrom状态下,可以通过此功能, 让 maskrom设备进行 Rockusb协议通讯
- EF 命令:擦除整个 nandflash
- LF 命令:低格保留块后面区域,只有在 loader 模式下使用
执行uf update-linux.img开始更新固件,下图为更新过程截图:
Rockusb>uf update-linux.img
备注:也可通过配置config.ini文件配置升级映像文件,只需输入UF即可升级,请用户自行尝试。
使用upgrade_tool指令烧写映像
上一节我们介绍了通过upgrade_tool烧写统一固件update-linux.img的方法,熟悉三星平台的开发者会发现,这种方法并不是很高效,真正操作起来,它远没有fastboot工具来的迅速。其实,upgrade_tool工具同样支持类似于fastboot的烧写方式。
为了烧写方便,可以在mk脚本中,默认在编译系统时,将烧写工具upgrade_tool拷贝到out/release目录。
第一步:打开串口终端,并打开minicom,用于适时监控串口调试信息;
第二步:按住RECOVERY键,连接USB OTG线和电源线,这时uboot打印信息将会提示已经进入USB下载模式。如果接通电源后没来得及按住RECOVERY键,在按住RECOVERY键的同时,再按下复位键即可。
第二步:打开第二个串口终端,进入out/release目录;
第三步:在out/release目录下敲击如下指令,烧写相应的映像。
sudo upgrade_tool di –k <nowiki>kernel.img(烧写内核) sudo upgrade_tool di –s system.img(烧写文件系统) sudo upgrade_tool di resource resource.img(烧写资源文件) sudo upgrade_tool di –r recovery.img(烧写急救文件) sudo upgrade_tool ul RK3399MiniLoaderAll_V1.05.bin (烧写bootloader) sudo upgrade_tool di uboot uboot.img parameter.txt(烧写uboot,必须指定parameter.txt) sudo upgrade_tool di trust trust.img parameter.txt(烧写trust,必须指定parameter.txt) sudo upgrade_tool uf update-linux.img(烧写统一固件)
Rkflashkit
rkflashkit 有图形界面,后加了命令行支持,更是好用。
work@ubuntu~/rktool$ sudo apt-get install build-essential fakeroot work@ubuntu~/rktool$ git clone <nowiki>https//github.com/linuxerwang/rkflashkit work@ubuntu~/rktool$ cd rkflashkit work@ubuntu~/rktool$ ./waf debian work@ubuntu~/rktool$ sudo apt-get install python-gtk2 work@ubuntu~/rktool$ sudo dpkg -i rkflashkit_0.1.4_all.deb
注意:rkflashkit_0.1.4_all.deb会因版本更新,版本数字可能会有所变化,如果执行失败,执行ls命令查看下即可。
work@ubuntu~/rktool/$ sudo rkflashkit
如下是图形界面,在Devices下选择设备,选择要烧写的分区和对应的映像文件,点击Flash image即可。
该工具也支持命令行,使用help命令查看使用方法
work@ubuntu~/rktool/rkflashkit$ rkflashkit --help Usage<cmd<nowiki>> [args] [<cmd> [args]...] part List partition flash @<PARTITION> <IMAGE FILE> Flash partition with image file cmp @<PARTITION> <IMAGE FILE> Compare partition with image file backup @<PARTITION> <IMAGE FILE> Backup partition to image file erase @<PARTITION> Erase partition reboot Reboot device For example, flash device with boot.img and kernel.img, then reboot sudo rkflashkit flash @boot boot.img @kernel.img kernel.img reboot work@ubuntu~/rktool/rkflashkit$
QT文件系统的搭建
前面章节的介绍,都是九鼎创展工程师已经移植的文件系统包,如果换一个平台,一切从零开始,我们如何构建linux QT文件系统呢?本章节将会带您一步步搭建linux文件系统。
下载buildroot
在buildroot官网下载最新的buildroot包,下载地址如下:
[http//buildroot.uclibc.org/download.html http//buildroot.uclibc.org/download.html]
本文使用的是buildroot 2016.02,用户下载最新版本与本文介绍版本配置可能略有不同,根据下文配置适当修改即可。
配置buildroot
将下载的buildroot包拷贝到ubuntu系统用户目录并解压,得到buildroot目录,通过命令终端进入buildroot目录,执行make menuconfig,进入配置界面:
进入Target options菜单,
在Target Architecture中选择ARM(little endian),在Target Binary Format中选择ELF,退回上一级,进入Toolchain目录,按下图配置:
退回上一级,进入System configuration目录,作如下配置:
退回上一级,进入Target packages目录,再进入Graphic libraries and applications (graphic/text)目录,选择Qt5,如下图所示:
注意不要选Qt,它对应QT4.8版本。进入Qt5菜单,按下图配置:
退回menuconfig的开始界面,进入Filesystem images菜单,作如下配置:
到此,buildroot配置完成。默认配置保存在buildroot根目录的.config中,我们可以备份该配置文件,以防后续配置出错。执行如下指令备份配置文件:
cp .config x3399_defconfig
编译buildroot
配置完成后,执行make指令即可编译buildroot了。编译buildroot会会依赖一些第三方插件和库,在第一章的编译文件系统小节中,已经给出了需要安装的包,在编译之前需要提前安装,否则会报错。 编译完成后,文件系统映像rootfs.ext2会生成到output/images目录。
测试QT5.1默认示例
将uboot,内核,文件系统烧写进开发板,进入linux文件系统后,可以进入/usr/lib/qt/examples目录测试QT示例。
进入gui/analogclock目录,执行analogclock文件,指令如下:
./analogclock &
这时,在开发板上可以看到有一个时钟图案被绘制出来(会覆盖默认的QTTEST程序),如下图所示:
进入gui/rasterwindow目录,执行rasterwindow文件,指令如下:
./ rasterwindow &
这时,在开发板上可以看到一个标注有QWindow的图案被绘制出来,如下图所示:
进入qpa/windows目录,执行windows文件,指令如下:
./windows &
这时,在开发板上可以看到有三幅图案被绘制出来,如下图所示:
进入sql/drilldown目录,执行如下指令:
./drilldown &
这时,在开发板上可以看到有四个QT画面被绘制出来,如下图所示:
进入sql/books目录,执行如下指令:
./books &
这时,在开发板上可以看到有一个对话框被绘制出来,如下图所示:
进入sql/masterdetail目录,执行如下指令:
./masterdetail &
这时,在开发板上可以看到有一个对话框绘制出来,如下图所示:
默认buildroot编译出了很多示例,这里不带一一列举,有兴趣的读者可以自行尝试。
安装QT Creator
通常我们使用QT Creator创建基于QT的工程。在QT官网下载最新的QT安装包,下载地址如下:
http://download.qt.io/official_releases/qt/
打开链接页面如下:
点击5.1,下载最新的安装包,得到名为qt-linux-opensource-5.1.1-x86-offline.run的文件,将它拷贝到ubuntu的用户目录,使用如下指令安装:
./ qt-linux-opensource-5.1.1-x86-offline.run
安装完成后,QT Creator也就安装完成了。
编译QT Creator默认示例
点击ubuntu图标,输入qt,将会查找含有qt的文件,同时,安装好的QT Creator也会被列出来,如下图所示:
点击QT图标,QT Creator将会运行,如下图所示:
默认QT的配置是针对X86架构的,这时编译出来的示例只能在PC机上运行。我们打开Image Composition Example示例,它是一个图片叠加显示的示例,找到该示例,单击即可。打开后的界面如下:
点击左下脚绿色的三脚箭头,开始编译工程。在Compile Output栏会显示编译的整个过程。编译完成后,提示如下:
编译完成后,编译出来的映像会自动运行,一个叠加的蝴蝶图像界面显示出来了,界面如下:
下面我们将该示例编译到开发板上运行。使用QT Creator打开上面的示例工程,如下图所示:
选择Projects一栏,可以看到最顶端的框图1,显示Desktop Qt 5.1.1 GCC 64bit,表明它通过64位的GCC编译,框图2表明在debug模式下编译,相对release模式,debug模式下含有大量调试信息,编译出来的映像会比较大。通常发布映像时,我们选择release模式。框图3指定了编译的路径。框图4指定了qmake和交叉编译工具。框图5为调试按键,第一个用于选择编译模式为debug或release,第二个为运行按钮,第三个为单步调试按钮,第四个为编译按钮。框图6为一些输出信息,如Compile Output,会给出整个编译的信息。
在框图1中,点击Tools->Options,如下图:
在左测对话框中选择Build & Run,在后边选择Qt Versions,默认Manual为空,Auto-detected选择的QT5.1默认的qmake,它将运行在X86平台的linux系统上,因此我们要手动添加在ARM平台上运行的qmake。
在buildroot编译文件系统时,我们选中QT5后,将会在buildroot的output/host/usr/bin目录生成支持ARM平台的qmake,点击Add,指向该路径的qmake:
点击Open,即在Manual中添加了支持ARM平台的qmake。再选择Compilers一栏,如下图所示:
默认Manual为空,Auto-detected为支持X86 32位和64位的GCC,这将直接导致编译出来只能在PC机上运行。点击Add,选择GCC,如下图所示:
在Name中重命名,以区别ARM和PC平台,如我们命名为GCC-ARM-LINUX,它将会直接显示在前面QT工程界面的框图1中,到时我们编译QT工程时,能够一目了然,编译出来的到底是PC平台还是ARM平台。在Compiler path中指定交叉编译工具,在buildroot中默认已经自动下载并安装了交叉编译工具,我们指定到如下路径即可:
buildroot/output/host/opt/ext-toolchain/bin/arm-linux-gnueabihf-g++
设置完成后,点击OK,完成设置。
再回到QT的工程界面,我们发现框图1中的配置仍然针对PC机,点击框图1中的Manage Kits,如下图所示:
注意,这时默认Manual仍然为空,点击Add,会弹出一个有很多选项的对话框,我们按如下方式配置:
这里的Name,我们可以点击Qt Versions,选择Manual中的qmake,下面就会有名称出来,将它拷贝过来即可,如下图所示:
在框图一中,点击设置编译环境的下拉箭头,如下图所示:
选择Change Kit->Qt5.1.1(System),更改后的界面如下:
在框图2中选择release,更改后的界面如下:
到此,配置完毕,点击框图5中的编译按钮,在Compile Output中可以看到编译信息如下:
这时,在框图2中指定的目录中已经生成了能够在ARM平台运行的映像了,如下图所示:
将该文件拷贝到x3399开发板上运行,可以看到美丽的蝴蝶图案显示出来了。
qttest测试程序
使用QT_demo测试LED灯
点击上面的四盏灯可以看到开发板led的灯的亮和灭。
使用QT_demo测试蜂鸣器
进入QT5.6.1系统后,默认会运行我们自主编写的测试demo,测试界面如下:
使用QT_demo调节背光
测试界面如下:
滑动圆形滑轮,可对开发板背光进行亮暗调节。
使用QT_demo测试按键
测试界面如下:
按下开发板任一独立按键,图中界面即会显示相应键值,同时,按下时提示[keydown],抬起时提示[keyup]。
使用QT_demo测试音频
将喇叭或耳机接到开发板的对应接口,点击下图中的Play Sound按钮,会播放测试歌曲:
使用QT_demo测试触摸屏
进入如下界面:
单击黄色矩形框,界面会进入全屏模式,这时我们可以任意书写来测试触摸屏了,测试示例图片如下:
使用QT_demo测试网络
将网线连接开发板的有线以太网接口,点击界面中的Network Test按钮,如果网络已经连通,则会添加DNS,如果没有连通,则会提示相应错误,如下图所示:
连接网线后测试时,会有如下提示:
使用QT_demo测试TF卡
将SD卡插入开发板的SD卡槽,点击Tfcard Test,界面上会列出SD卡中的内容。
使用QT_demo测试重启
点击Reboot按钮,开发板将重启。
使用QT_demo测试关机
点击Poweroff按钮,开发板将会关机。
linux底层开发示例
播放mp3
注:linux系统缺省登录账户为root,密码:123456
将存放有mp3文件的TF卡插到开发板的任意卡槽,使用如下命令挂载TF卡:
<nowiki> cd / mkdir sdcard mount /dev/mmcblk1p1 /sdcard cd sdcard
使用如下命令播放:
./mplayer *.avi
./mplayer *.mp3
连接串口后,可以通过PC键盘的0或9调节音量。也可以使用madplayer播放音乐。
在后台运行程序
在上一节中给出了播放音乐的示例,但是这时候mplayer已经占据了终端控制台,在音乐播放完之前,我们无法再使用终端控制台了。又比如我们开发一款产品时,就需要在启动文件系统后运行一个应用程序,如果运行了一个程序,终端控制台就被占用了,那将极大的限制我们的功能。为止,我们可以将程序放在后台运行。使用方法很简单,我们只需在执行的指令后面添加一个”&”即可。如播放音乐时使用如下命令:
./mplayer *.mp3 &
中止程序的运行
中止程序的运行有多种方式,最直接的方式就是直接按ctrl+c。如前面我们正在播放一段音频文件,我们可以按ctrl+c退出程序。但是如果程序在后台运行,那么我们按ctrl+c就不管用了。这时我们可以使用kill命令。
kill+===PID===
kill+===文件名===
屏幕抓图
本文档中的各个图片,都是采用gsnap这个工具进行抓图的。进入QT图形界面后,我们能在LCD上看到丰富多彩的人机交互界面。通过gsnap可以抓取到图形界面精彩的瞬间。在控制台终端输入如下命令:
gsnap test_pic.jpg /dev/fb0
这时在当前目录将会保存test_pic.jpg图像文件。详细的gsnap移植步骤在后面会有详细描述。
挂载TF卡
进入QT图形界面后,在命令终端会有控制台出现,这时可以通过控制台查看文件系统的内容。将TF卡插到开发板的任意卡槽,串口终端会有如下提示:

这时在文件系统的/dev目录将会自动生成一个名叫mmcblk1p1的块设备文件。它就是对应的TF卡的设备文件,使用如下命令挂载TF卡到/sdcard目录:
mkdir /sdcard
mount /dev/mmcblk1p1 /sdcard
查看/scard目录下的内容,即是我们TF卡中的内容,如下图所示:
挂载U盘
进入QT图形界面后,在命令终端会有控制台出现,这时可以通过控制台查看文件系统的内容。插入U盘后,串口终端会有如下提示:

这时在文件系统的/dev目录将会自动生成一个名叫sda4的块设备文件。它就是对应的U盘设备文件,使用如下命令挂载U盘到/udisk目录:
mkdir /udisk
mount /dev/sda4 /udisk
查看/udisk目录下的内容,即是我们U盘中的内容,如下图所示:

保存系统时钟
Linux可以使用date指令更改时间日期。例如:
date -s 201607211433 设置为2016年7月21日14:33分
hwclock -w 把刚设置的时间存入RTC寄存器
hwclock -s 恢复linux系统时钟为RTC寄存器值,一般将该指令放在rcS中开机自动执行。
掉电保存数据到flash
由于本系统采用了ext4文件系统,因此可以很方便的保存数据,确保掉电后数据不丢失。如我们从U盘中拷备一首歌曲到/root目录:
cp /udisk/muyangqu.mp3 /
重启开发板,我们发现在root目录仍然存在刚才拷备的这首歌曲,说明掉电后数据并没有丢失。
设置开机自动运行程序
借助启动脚本可以设置各种程序开机后自动运行,这点很类似于WINDOWS的Autobat自动批处理文件。启动脚本位于/etc/init.d/rcS中,我们可以将自己想要开机运行的程序或是开机执行的指令放在rcS里面。比如我们想制作一个简单的开机音乐,我们就完全可以在rcS中添加如下语句:
./mplayer start.mp3 &
这时,开机后就会播放名叫start.mp3的音乐了。注意start.mp3需要在当前执行指令所在目录。
查看开发板内存信息
X3399开发板默认配置2GB LPDDR3 SDRAM,在uboot启动时,打印信息上会给出RAM大小信息:

在进入文件系统后,可以通过cat命令查询Linux系统分配到的SDRAM大小。执行如下命令:
cat /proc/meminfo

linux应用开发示例
本手册给出的所有应用程序全部在九鼎创展x3399开发板上运行,这里仅给出了一些比较基础,常用的应用程序,旨在为用户打开Linux世界奇妙的大门,用户定能举一反三,编写出属于自己的更加丰富完美的程序。
声明:以下所有应用程序全部为九鼎创展科技有限公司原创作品,所有内容全经我们严格测试,建议用户按照下面步骤动手编译一遍,以增强自己的理解,不推荐直接使用我们提供好的文件。另外,敬请商业人士勿侵犯版权。
Hello World
第一步:生成可执行文件
在x3399_marshmallow目录新建app-ex目录,在app-ex目录新建hello目录,然后在hello目录下新建hello.c和makefile两个文件:
vim hello.c

这是一个最基础的应用程序,如果我们声明了交叉编译工具(export PATH=/home/lixu/projects/3399/x3399_marshmallow/buildroot/output/host/opt/ext-toolchain/bin$PATH)(这里我们用到buildroot中的交叉编译工具),可以直接敲入命令进行编译(由于开发板环境缺少c库,编译时需要加上-static静态编译):
arm-linux-gcc-4.4.3 -o hello hello.c -static
说明:由于开发板文件系统缺少标准函数库,编译时加上-static选项;使用的编译器为aarch64-linux-gnu-gcc。
编译完成后,在当前目录会生成hello可执行文件,我们可以使用file命令查询执行文件是否为ARM体系文件:
第二步:将可执行文件下载到开发板运行
比较常用的方式有以下四种:
- 通过串口和sz/rz工具
- 复制到存储媒介,如SD卡,U盘等
- 通过NFS挂载文件系统,这时不用将可执行文件拷备到开发板了,推荐调试使用这种方式!
- 通过ftp传输
这里介绍第二种方法,以TF卡为例,其他方法请读者自行尝试。
将生成的hello文件拷备到TF卡,再将TF卡插入开发板的TF卡接口,将TF卡mount到/mnt目录:
cd /
mount /dev/mmcblk1p1 /mnt
进入mnt目录,可以看到刚才拷贝的hello文件了:
运行hello:
./hello
打印信息如下:

表明,程序已经成功运行。
前面编译文件我们需要通过手敲命令执行,我们可以通过编写makefile来代替手敲的动作。
在hello目录下新建makefile文件:vim makefile
hello
aarch64-linux-gnu-gcc –o hello hello.c –static
clean
rm –f hello *.o
直接在hello目录下敲make就可以生成hello文件。运行的效果和前面的完全相同。
数学函数库调用
建立程序编译路径:
<nowiki> mkdir math cd math vim math.c
编辑如下内容:
<nowiki>include <stdio.h> include <stdlib.h> include <math.h> int main(void) { double a=9.0; printf("sqrt(%f)=%f\n",a,sqrt(a)); return 0; }
编辑makefile文件,内容如下:
<nowiki> OBJS=math.o LDFLAGS = -lm -static CC = yourtoolchainpath/bin/aarch64-linux-gnu-gcc math${OBJS} ${CC} -o $@ $^ $(LDFLAGS) clean rm -f math *.o
执行make,将生成的可执行文件math下载到开发板上运行,如下图所示:
多线程编程示例
建立程序编译路径:
<nowiki> mkdir thread cd thread vim thread.c
编辑如下内容:
<nowiki>include<stddef.h> include<stdio.h> include<unistd.h> include"pthread.h" void function1(void); void function2(void); int func_flag=0; pthread_mutex_t mutex; main() { pthread_t reader; pthread_mutex_init(&mutex,NULL); pthread_create(&reader,NULL,(void*)&function1,NULL); function2(); } void function2(void) { while(1) { pthread_mutex_lock(&mutex); if(func_flag==0) { printf("excute function2.\n"); func_flag=1; } pthread_mutex_unlock(&mutex); } } void function1(void) { while(1) { pthread_mutex_lock(&mutex); if(func_flag==1) { printf("excute function1.\n"); func_flag=0; } pthread_mutex_unlock(&mutex); } }
编辑makefile文件,内容如下:
<nowiki> OBJS=thread.o LDFLAGS = -lpthread -static CC = yourtoolchainpath/bin/aarch64-linux-gnu-gcc thread${OBJS} ${CC} -o $@ $^ $(LDFLAGS) clean rm -f thread *.o
执行make,将生成的可执行文件thread下载到开发板上运行,如下图所示:

多进程编程示例
在Linux下通用调用fork函数创建新的进程。调用fork时,系统将产生一个与当前进程相同的进程。它与原有的进程具有相同的数据,连接关系和在程序同一处执行时的连续性。通常将原有的进程叫父进程,新创建的进程叫子进程。
fork调用将分两次返回,从父子进程返回。进程创建语法如下:
<nowiki>include <unistd.h> pid_t pid; pid = fork();
如果pid返回0,表示说明从子进程返回,否则从父进程返回,此时返回的是进程的ID号。我们可以通过getpid()函数来获得进程的ID号。
首先建立程序编译目录:
<nowiki> mkdir process cd process vim process.c
编辑如下内容:
<nowiki>include<stdio.h> include<unistd.h> include<sys/types.h> main() { pid_t pid; pid=fork(); if (pid<0) { printf("fork is error!\n"); return 1; } else if (pid == 0) { while (1) { printf("the child process is running now.pid=%d\n",getpid()); sleep(1);//linux延时函数,延时1秒 } } else { while (1) { printf("the perent process is running now.pid=%d\n",getpid()); sleep(1); } } return 0; }
编辑makefile文件,内容如下:
<nowiki> OBJS=process.o LDFLAGS = -lpthread -static CC = yourtoolchainpath/bin/aarch64-linux-gnu-gcc process${OBJS} ${CC} -o $@ $^ $(LDFLAGS) clean rm -f process *.o
执行make编译,将生成的可执行文件下载到开发板运行,仔细观察串口监控信息:

makefile编程示例
在上面的很多测试程序实例中,我们都编写了一些简单的makefile文件。下面我们介绍makefile的基本的语法。
makefile就好比批处理文件,里面写了一系列集合,当运行make编译时,便会按makefile提供的命令及顺序完成编译。
这里我们给出三个文件:main.c,func.c,func.h。主程序在main.c中,在main.c中程序会调用func.c中的函数,func.c中的函数又会用到func.h中定义的变量。
main.c文件内容如下:
<nowiki>include "func.h" extern int fd; int main(int argc,char **argv) { fd = open(DEVICE_NAME,0);//打开设备 if(fd == -1) { printf("open device %s error \n",DEVICE_NAME); return 0; } else { printf("open device %s ok! \n",DEVICE_NAME); } while(1) { glint_led(); } close(fd); return 0; }
该文件会调用glint_led ()函数,这个函数在func.c中。func.c的内容如下:
<nowiki>include "func.h" void glint_led(void) { ioctl(fd,LED_ON); sleep(1); ioctl(fd,LED_OFF); sleep(1); }
这里仅仅是一个读取按键的函数,供main函数调用。该函数需要用到了一些变量,另外还需要一些头文件支持,这些都存放在func.h中,其内容如下:
<nowiki>include <stdio.h> include <stdlib.h> include <unistd.h> include <sys/ioctl.h> define DEVICE_NAME "/dev/vib" define LED_ON 0x11 define LED_OFF 0x22 int fd;
很明显,这是基于LED测试程序,人为的分成的三个文件。我们的目的不在于分离代码,而在于学习makefile的编写方法。
当不使用makefile时,我们使用如下指令编译:
aarch64-linux-gnu-gcc-gcc -o mkfile main.c func.c
编译完成后,将会生成可执行文件mkfile。将它下载到开发板上运行,和前面的按键测试完全相同。现在我们尝试编写第一个属于自己的makefile
<nowiki> mkfilemain.o func.o aarch64-linux-gnu-gcc -o mkfilemain.o func.o main.omain.c aarch64-linux-gnu-gcc -c main.c -o main.o func.ofunc.c func.h aarch64-linux-gnu-gcc -c func.c -o func.o clean rm -f mkfile *.o
执行make后,编译器会依次编译main.c和func.c文件,生成main.o和func.o文件,最后将这两个.o文件打包到可执行文件mkfile中。这时将mkfile文件下载到开发板运行,效果和前面的是一样的。我们可以执行make clean指令清除生成的.o文件和可执行文件。
makefile具有很强大的推理功能,我们完全可以简化上面的代码。优化后的代码如下:
<nowiki> OBJS=main.o func.o CC= aarch64-linux-gnu-gcc mkfile${OBJS} ${CC} -o $@ $^ main.o func.ofunc.h clean rm -f mkfile *.o
可见,这次比上面的完整版要简化多了。前面通过变量OBJS定义了要编译的源文件,变量CC给出了交叉编译工具。$@ 表示目标文件的全称,即mkfile,$^表示所有被依赖的文件,并以空格分开,即main.o func.o。后面的clean为清除指令,执行make clean后会执行 clean后面的指令。需要注意的是,rm指令后面千万不要使用$@符号来表征我们要删除的目标文件,因为这时候$@已经不再表示mkfile了,而表示clean。同样,使用make指令编译,一样能够生成我们需要的目录文件mkfile。
上面的makefile使用了变量以及预定义变量。第一句即定义了变量OBJS,将它赋值为main.o func.o,第二句定义了变量CC,将它赋值为一个交叉编译工具定义。引用变量时,通过${*}表示,这里*表示前面定义的变量。
上面使用了$@和$^两个预定义变量,GNU make主要有以下七种预定义变量:
|
不包含扩展名的目标文件名称 |
|
所有的依赖文件,以空格分开,以出现的先后为序,可能包含重复的依赖文件 |
|
第一个依赖文件的名称 |
|
所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚 |
|
目标的完整名称 |
|
所有的依赖文件,以空格分开,不包含重复的依赖文件 |
|
如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称为mytarget.so(image.o),则$@ 为mytarget.so,而$% 为image.o。 |
对比以上几种编译方式,我们不然发现,其实最简的还是第一种,因为它就一句话就搞定了。那么在makefile中,我们是否也可以精简到只有一句话呢?答案是肯定的。我们继续利用makefile强大的推理功能进行简化,得到如下makefile代码:
<nowiki> OBJS=main.o func.o CC= aarch64-linux-gnu-gcc mkfile${OBJS} ===${CC} -o $@ $^=== clean rm -f mkfile *.o
这次,makefile真正编译的代码,就只有上面红色部分一条指令了。和前面比较,不难发现,单独对main.c和func.c两个文件编译的指令已经去掉了。前面我们提到,makefile具有强大的推理功能,我们在生成目标文件mkfile时,makefile会推理出它需要main.c和func.c两个文件,因此它首先就会去编译这两个文件,最后再执行目标文件的生成。因此我们完全可以将它们省去。
这里只是makefile的一点基础,读者可以借助于其他书籍对makefile作更深一层的了解。
mplayer移植
有两种移植mplayer,第一种方法就是直接下载mplayer源码包,手动配置编译环境,再通过交叉编译环境编译。该方法比较传统,我们有更方便的方法,直接在buildroot中选中mplayer,再make即可,buildroot会自动下载mplayer源码包,自动配置编译环境并编译。
在buildroot的menuconfig配置选项中,进入Target packages-> Audio and video applications选项,选中mplayer,如下图所示:
保存退出,在buildroot下直接make,生成文件系统后更新到开发板,mplayer就打包进文件系统了。
madplayer移植
madplayer的移植方法和mplayer的移植方法完全雷同,直接在buildroot中选中madplayer的选项,再make即可。
屏幕抓图工具gsnap移植
屏幕抓图的方法有多种,有不少爱好者自己动手写抓图小程序。这里我们使用JPEG库来处理。具体用到了jpegsrc.v6b.tar.gz和gsnap.tar.gz两个源码包(这2个压缩包请读者自己到网络上面下载)。
一:安装libjpeg
解压jpeg库源码包,进入根目录:
tar zxf jpegsrc.v6b.tar.gz
cd jpeg-6b
二:配置编译环境:
<nowiki> ./configure --prefix=/home/lixu/projects/3399/x3399_marshmallow/buildroot/output/host/opt/ext-toolchain --exec-prefix=/home/lixu/projects/3399/x3399_marshmallow/buildroot/output/host/opt/ext-toolchain --target=arm-linux --enable-shared --enable-static CC=/home/lixu/projects/3399/x3399_marshmallow/buildroot/output/host/opt/ext-toolchain/bin/aarch64-linux-gnu-gcc AR=/home/lixu/projects/3399/x3399_marshmallow/buildroot/output/host/opt/ext-toolchain/bin/aarch64-linux-gnu-ar rc AR2=/home/lixu/projects/3399/x3399_marshmallow/buildroot/output/host/opt/ext-toolchain/bin/aarch64-linux-gnu-ranlib
三:配置好之后,make编译,如果提示缺少libtool文件:
下载libtool-2.2.6a.tar.gz,解压编译获得libtool工具。
再make clean,重复之前的第二步:配置编译环境。
四:在x3399_marshmallow/buildroot/output/host/opt/ext-toolchain下建立man/man1目录:
cd x3399_marshmallow/buildroot/output/host/opt/ext-toolchain
mkdir -p man/man1
五:编译,安装
make
make install
这时,在x3399_marshmallow/buildroot/output/host/opt/ext-toolchain/man/man1目录下将会生成以下文件:
cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 wrjpgcom.1
在x3399_marshmallow/buildroot/output/host/opt/ext-toolchain/lib目录下生成以下文件:
libjpeg.a libjpeg.la libjpeg.so libjpeg.so.62 libjpeg.so.62.0.0
六:解压gsnap
tar zxf gsnap.tar.gz
cd gsnap
七:修改makefile
<nowiki> all /home/lixu/projects/3399/x3399_marshmallow/buildroot/output/host/opt/ext-toolchain/bin/aarch64-linux-gnu-gcc -g gsnap.c -ljpeg -o gsnap –static clean rm -f gsnap
八:编译,得到可执行文件gsnap
make
九:将jpeg库文件复制到文件系统的lib目录,注意保持文件的链接属性
cp -a libjpeg.s* “文件系统路径”/lib
十:将可执行文件gsnap复制到文件系统的sbin目录
cp gsnap “文件系统路径”/sbin
十一:重新制作文件系统,下载到开发板上,使用如下命令即可截获图形界面:
gsnap 1.jpg /dev/fb0
同样可以将图片保持为bmp,png等其他格式。