目录
[1.1 本地编译:](#1.1 本地编译:)
[1.2 交叉编译是什么:](#1.2 交叉编译是什么:)
[1.3 为什么要交叉编译:](#1.3 为什么要交叉编译:)
[1.4 什么是宿主机?什么是目标机?](#1.4 什么是宿主机?什么是目标机?)
[1.5 如何进行交叉编译:](#1.5 如何进行交叉编译:)
[2.1 下载交叉编译工具:](#2.1 下载交叉编译工具:)
[2.2 将交叉编译工具链添加到环境变量:](#2.2 将交叉编译工具链添加到环境变量:)
[2.2.1 临时有效的添加环境变量:](#2.2.1 临时有效的添加环境变量:)
[2.2.2 永久有效的添加环境变量:](#2.2.2 永久有效的添加环境变量:)
[3.1 交叉编译实战(一):](#3.1 交叉编译实战(一):)
[3.2 交叉编译实战(二):](#3.2 交叉编译实战(二):)
[4.1 交叉编译WiringPi库:](#4.1 交叉编译WiringPi库:)
[4.2 把树莓派的WiringPi库拿来](#4.2 把树莓派的WiringPi库拿来)
[4.2.1 将树莓派的libwiringPi.so.2.52上传到虚拟机:](#4.2.1 将树莓派的libwiringPi.so.2.52上传到虚拟机:)
[4.2.2 创建软链接:](#4.2.2 创建软链接:)
[4.2.3 交叉编译:](#4.2.3 交叉编译:)
一、交叉编译的认知
编译 是指将源代码文件(如C/C++文件)经过预处理、编译、汇编和链接等步骤,转换为可执行文件的过程 。将源代码转换成机器代码的过程称为编译(Compile),编译的工作需要编译器(Complier)来完成。
1.1 本地编译:
- 本地编译是指在当前的编译平台上,生成能在当前平台上运行的可执行文件。
例如,在x86平台上,使用 x86平台上的工具,开发针对x86平台本身的可执行程序,这个编译过程称为本地编译。 以一个简单的例子来说明本地编译,假设有一个hello.c文件,它包含以下内容:
cpp
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
我们想要在x86平台上进行本地编译,并在x86平台上运行这个程序。可以使用以下命令:
cpp
gcc hello.c -o hello
1.2 交叉编译是什么:
- **交叉编译 是在一个平台上生成另一个平台上的可执行代码。**例如:
-
我们再windows上面编写C51代码,并编译成可执行代码,如xx.hex,是在c51上面运行,不是在windows上面运行
-
我们在ubuntu上面编写树莓派的代码,并编译成可执行代码,如a.out,是在树莓派上面运行,不是在ubuntu linux上面运行
1.3 为什么要交叉编译:
-
平台上不允许或不能够安装我们所需要的编译器 ,比如C51,因为目的平台上的资源贫乏,无法运行我们所需要编译器
-
树莓派比C51内存大的多,是不是就不需要交叉编译了?错,也要!树莓派有时又是因为目的平台还没有建立,连操作系统都没有,根本谈不上运行什么编译器。操作系统也是代码,也要编译! 因为平台运行需要两样至少东西:bootloader(启动引导代码) 以及操作系统核心
1.4 什么是宿主机?什么是目标机?
-
宿主机 (host) :编辑和编译程序的平台,一般是基于X86的PC机,通常也被称为主机。
-
目标机 (target):**用户开发的系统,通常都是非X86平台。**host编译得到的可执行代码在target上运行。
1.5 如何进行交叉编译:
- 交叉编译需要用到工具,一般称作为:交叉编译器 或者交叉编译工具链。
二、交叉编译工具链的安装
2.1 下载交叉编译工具:
-
我们交叉编译工具链的安装应该是在宿主机上,也就是我们的Linux虚拟机,在宿主机上编译出能在树莓派ARM平台上运行的程序
-
树莓派交叉编译工具链下载地址:[GitHub - raspberrypi/tools](https://github.com/raspberrypi/tools)
cpp
https://github.com/raspberrypi/tools
然后把我们下载下来的压缩文件通过FileZilla上传到我们的虚拟机上面:
然后使用下面指令解压:
cpp
unzip tools-master.zip
解压之后我们进入下面这个路径下:
进入到该路径后我们可以看到:arm-linux-gnueabihf-gcc 这个软链接是指向arm-linux-gnueabihf-gcc-4.8.3的,软链接是不占内存的,作用就是指向真正的可执行程序,在使用中就可以使用软链接的名字来调用真正的可执行程序。
所以arm-linux-gnueabihf-gcc就是我们树莓派的交叉编译工具链,使用和gcc是一样的,比如:XXX.c,编译的语句是:
cpp
./arm-linux-gnueabihf-gcc XXX.c //只不过把"gcc"替换成了"./arm-linux-gnueabihf-gcc"
2.2 将交叉编译工具链添加到环境变量:
现在我们已经拥有了交叉编译工具链,我们知道我们的交叉编译工具链在下面这个路径下:
cpp
/home/shiyahao/lessonPi/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
我们在编译的时候还要将这么长的路径加到我们交叉编译之前,就会让我们的编译变得非常的繁琐和麻烦,我希望我们在使用这个交叉编译工具链的时候可以像gcc那样,不管在什么路径下都可以编译,这样就得配置环境变量了
2.2.1 临时有效的添加环境变量:
临时的添加环境变量,其实在前几节学习动态库的制作时就有提到过,那就是使用export ,临时的意思就是"仅在添加完环境变量后的当前窗口有效",也就是说如果再开一个窗口就又识别不到新添加的环境变量了。
- 使用下面指令查看当前的环境变量:
cpp
echo $PATH
这一长串中:前面的**/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games: 是不变的**,然后使用下面指令来添加我们新的环境变量:
cpp
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:想要添加的路径
cpp
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/shiyahao/lessonPi/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
运行之后我们退回到根目录下,也就是**/home/shiyahao**下,然后输入下面指令:
cpp
arm-linux-gnueabihf-gcc-4.8.3 -v //-v查看编译信息
我们可以看到在其他路径下我们可以暂时使用交叉编译器了 ,此时我们新开一个终端窗口,再次输入arm-linux-gnueabihf-gcc -v就无法识别了,所以这个环境变量只能临时有效。
2.2.2 永久有效的添加环境变量:
临时有效虽然一句命令就能搞定,但是有风险,因为终端随时可能关闭,然后添加的临时环境变量就失效了,所以还是学习一下如何一劳永逸的修改环境变量:
- 修改工作目录下的 .bashrc 隐藏文件(该文件用于配置命令终端的)
cpp
vi /home/shiyahao/.bashrc
然后在文件的末尾添加我们的交叉编译工具链的路径:
其实,bashrc隐藏文件 就是一个脚本,永久有效的核心思路就是通过脚本自动运行export,和动态库写脚本写export的思路是一样的。
- 然后运行下面指令,用于加载配置文件,生效配置:
cpp
source /home/shiyahao/.bashrc
此时我们再开一个新的终端,并运行arm-linux-gnueabihf-gcc-4.8.3 -v:
可见,这样就永久有效的将交叉编译器的目录添加到了环境变量中去了!
三、交叉编译实战
3.1 交叉编译实战(一):
现在在我们的宿主机的根目录下有一个hello.c文件,我们通过gcc编译器编译出可以在本机X86平台上运行的程序,我们可以通过下面指令查看:
cpp
file 程序名
我们可以看到这个程序只能在X86-64平台上运行,是不能在我们的树莓派ARM平台上运行的,我们想要在我们的树莓派ARM平台上运行,我们就得用基于树莓派ARM平台的交叉编译器来编译 (arm-linux-gnueabihf-gcc-4.8.3)
我们看到编译出来的程序是在ARM平台上运行的,我们把它拷贝到树莓派上,指令如下:
cpp
scp hello_2 pi@192.168.31.123:/home/pi
可以看到我们交叉编译的程序成功在树莓派上运行
3.2 交叉编译实战(二):
-
现在我们尝试把我们之前的FTP云盘项目中的客户端交叉编译到我们的树莓派上运行,我们的虚拟机运行服务器程序,让宿主机和树莓派实现FTP通信。
-
这是我们之前的FTP云盘的项目:
cpp
arm-linux-gnueabihf-gcc-4.8.3 clientFTP.c -o RaspberryclientFTP //交叉编译FTP客户端代码
file RaspberryclientFTP //产看文件属性
scp RaspberryclientFTP pi@192.168.31.123:/home/pi //将我们交叉编译出来的程序上传到树莓派
可以看到我们成功的交叉编译了FTP的客户端程序,并成功在树莓派上运行,实现了宿主机和树莓派之间的FTP通信
四、带WiringPi库的交叉编译实战
在之前的交叉编译实战中,成功的将Ubuntu虚拟机上面的代码交叉编译成了能够在树莓派上运行的程序,但是之前的交叉编译代码不需要链接库,如果代码包含了WiringPi库或者线程库,那么在gcc编译时就需要链接库,对于这种需要链库的C代码进行交叉编译不能直接无脑的在"arm-linux-gnueabihf-gcc XXX.c"后加上"-lwiringPi",其原因是库文件也是当前平台下的文件,并不能被树莓派平台所识别!
- 首先先将我们的WiringPi库上传或者下载到我们的根目录(home/CLC)下:
- 然后进入这个文件夹后,运行build安装:
- 在**/usr/local/lib/**路径下就可以看到这个wiringPi的动态库:
- 通过file指令查看文件属性:
可以看到这个动态库只能在当前平台(X86-64)上运行,是不能在我们的ARM树莓派平台上运行的,所以我们这里有两种方法:
4.1 交叉编译WiringPi库:
既然这个库文件无法被树莓派平台识别,那就先交叉编译库文件,再链库并交叉编译代码,但是对于动态库的交叉编译和之前的方法可能不同,所以这种方法暂时不展开。
4.2 把树莓派的WiringPi库拿来
4.2.1 将树莓派的libwiringPi.so.2.52上传到虚拟机:
直接把树莓派上面使用的WiringPi拿过来也可以解决我们的问题:
cpp
cd /usr/lib //进入树莓派/usr/lib目录下
ls //查看当前目录下的文件
然后通过file指令查看该文件的属性:
然后通过下面指令发送到我们的虚拟机上面:
cpp
scp /usr/lib/libwiringPi.so.2.52 CLC@192.168.31.90:/home/CLC/lessonPi
4.2.2 创建软链接:
参考文章:Linux创建连接命令 ln -s创建软连接 - 张娜nana - 博客园 (cnblogs.com)
- 软链接的概念:
-
软链接文件有类似于Windows的快捷方式。
-
在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。
-
你选定的位置上生成一个文件的镜像,不会占用磁盘空间
- 硬链接的概念:
- 它会在你选定的位置上生成一个和源文件大小相同的文件
- 硬链接如何生成:
cpp
ln libwiringPi.so.2.52 libwiringPi.so
- 软链接如何生成:
cpp
ln -s libwiringPi.so.2.52 libwiringPi.so
指令 参数 要被链接的文件 软链接文件名字
然后再通过file命令查看一下虚拟机的libwiringPi.so.2.52的文件属性:
确实是树莓派ARM平台上的WiringPi动态库
4.2.3 交叉编译:
现在有了树莓派ARM平台上的WiringPi库,可以尝试交叉编译带有WiringPi库的C代码了,比如下面这个代码:
cpp
#include <stdio.h>
#include <wiringPi.h>
#define BEEP 7
int main (void)
{
wiringPiSetup () ; //初始化wiringPi库
pinMode (BEEP, OUTPUT); //配置输入输出模式
while(1){
digitalWrite (BEEP, LOW) ; //蜂鸣器响
delay (1000) ; // mS
digitalWrite (BEEP, HIGH) ; //蜂鸣器不响
delay (1000) ;
}
return 0;
}
然后使用下面指令进行交叉编译:
cpp
arm-linux-gnueabihf-gcc beep.c -L ./ -lwiringPi -I /home/CLC/WiringPi/wiringPi -o BEEP
-
使用"-L ":指定动态库的优先查找目录为当前目录
-
使用"-I"(大写i):指定头文件的优先查找目录为/home/mjm/WiringPi/wiringPi
我们在编译的时候出错了,然后重新向虚拟机导入版本2.50的wiringPi动态库,然后重新生成软链接,然后在编译就OK了
使用下面指令将交叉编译出来的BEEP程序上传到树莓派:
cpp
scp BEEP pi@192.168.31.123:/home/pi/waishe
然后在树莓派上成功运行,蜂鸣器开始了滴滴声