Linux平台交叉编译树莓派代码入门

前言

理论上编译是直接在使用的设备上连接各种库编译会很方便,但在树莓派上或许会遇到一些问题:

  • VNC、SSH延迟可能特别大,尤其远程连入执行指令缓慢到你觉得你的板子可能在月球上
  • 比在PC和服务器上更长的编译时间和编译过程中由于资源消耗造成的树莓派使用卡顿
  • 直接在树莓派上编译需要连接显示器、键盘、Ethernet等外设,有可能由于位置原因、硬件接口被去除或者集成在其他系统里导致无法连接外设
  • 树莓派无法联网,有时候只能烧入tf卡加载
  • 部署版本中没有GUI等等

为了解决这些问题,可以考虑直接在一台Linux的PC或者服务器上编译好代码之后拷贝到嵌入式环境中使用。而由于嵌入式环境使用的指令集不一样,所以需要使用交叉编译工具链完成编译。

环境配置

在Mac环境编译可以参考这个教程

而在Linux环境可以直接使用树莓派官方提供的工具做交叉编译:

  1. 设定一个文件目录用于交叉编译。
  2. github - raspberrypi tools工程上下载工具链。
  3. 拉下来后确认下工具链被完整下载到了tools文件夹中

依赖库下载

一般来说我们的代码都或多或少需要连接各种外部库,但是交叉编译也就意味着我们编译的ARM二进制代码不能取连接Intel指令集的库文件,所以我们需要得到树莓派环境专用的库。

获得这些库的方法有两个:

  • 直接下载其他人按环境编译好的库,比如像pthreads这样的库或者标准库在上一步中下载的工具链中包含了。
  • 自行先交叉编译库文件,再通过库文件交叉编译最终的可执行文件。在本示例中,我们将先交叉编译wiringPi库,这个库可以提供对树莓派GPIO引脚的操控。

为了编译wiringPi库,我们先到wiringPi下载最新release版本的源文件。

CMake交叉编译工具链配置

重头戏到了,如同正常本机编译的流程,我们依旧使用CMake作为编译工具。但不一样的是,我们需要写一份配置工具链的文件供CMake调用。

针对树莓派工具链,可以参考下面的Toolchain-rpi.cmake文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Define our host system
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_VERSION 1)
# Define the cross compiler locations
SET(CMAKE_C_COMPILER /home/xxxxx/tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc)
SET(CMAKE_CXX_COMPILER /home/xxxxx/tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/bin/arm-linux-gnueabihf-gcc)
# Define the sysroot path for the RaspberryPi distribution in our tools folder
SET(CMAKE_FIND_ROOT_PATH /home/xxxxx/tools/arm-bcm2708/arm-rpi-4.9.3-linux-gnueabihf/arm-linux-gnueabihf/sysroot/)
# Use our definitions for compiler tools
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Search for libraries and headers in the target directories only
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
add_definitions(-Wall -std=gnu11)

主要是需要根据刚刚解压获得的官网上的环境,配置编译中所使用的C、C++编译器和sysroot环境地址,具体的地址可以参考上面三个地址根据实际情况配置。

配置完编译文件之后就可以使用调用该文件了

1
cmake . -DCMAKE_TOOLCHAIN_FILE=toolchain-rpi.cmake

编译wiringPi库

首先我们需要把CMakeLists.txt文件放到wiringPi的工程文件目录下,下载下来的源代码在文件目录/wiringPi/wiringPi

1
2
3
4
5
6
7
8
9
10
11
12
13
cmake_minimum_required(VERSION 3.0)
# Have CMake find our pthreads library within our toolchain (required for this library)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
find_package(Threads REQUIRED)
# add all the *.c files as sources
FILE(GLOB SRC_FILES *.c)
# make this output a shared library (with .so output)
add_library (wiringPi SHARED ${SRC_FILES})
# be sure to include the current source directory for header files
target_include_directories (wiringPi PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
# add the following required libraries:
# Threads, Math, Crypt, and RealTime
target_link_libraries(wiringPi ${CMAKE_THREAD_LIBS_INIT} crypt m rt)

建立build文件夹并编译:

1
2
3
4
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=Toolchain-rpi.cmake
make

编译demo代码

写一个demo简单测试下效果,demo代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
#include <wiringPi.h>
int main (void)
{
wiringPiSetup() ;
pinMode(0, OUTPUT) ;
for(;;)
{
digitalWrite (0, HIGH) ; delay (500) ;
digitalWrite (0, LOW) ; delay (500) ;
}
return 0 ;
}

代码实现了初始化引脚,并使0号引脚输出1Hz的方波。(wiringPi0号引脚对应PIN Map中GPIO17)。

cmake文件为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cmake_minimum_required (VERSION 3.0)
# Name our project
project (blink_example)
# Create a variable that holds the path to our libwiringPi.so file
set (WPI_PATH /home/xxx/wiringPi/wiringPi)
# Add the local ‘include’ directory and the wiringPi directory to grab headers
include_directories (include ${WPI_PATH})
# Actually find the wiringPi library object
find_library(WPI_LIB wiringPi HINTS ${WPI_PATH} NO_CMAKE_FIND_ROOT_PATH)
# Alert the user if we do not find it
#if(NOT WPI_LIB)
# message(FATAL_ERROR “wiringPi library not found”)
#endif()
# Add all the *.c files in our source directory to our executable output
FILE(GLOB_RECURSE SRC_FILES "src/*.c")
add_executable(blink_example ${SRC_FILES})
# Link the pre-compiled wiringPi library to the executable we just declared
target_link_libraries(blink_example ${WPI_LIB})

编译依旧采用引用Toolchain-rpi.cmake的方式编译:

1
2
3
4
mkdir build
cd build
cmake .. -DCMAKE_TOOLCHAIN_FILE=Toolchain-rpi.cmake
make

测试

编译成功后,从wiringPi库的编译输出中拷贝出libwiringpi.so,从Demo工程的编译输出中拷贝出blink_example,拷入树莓派中。

添加环境变量:

1
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/存放libwiringpi.so的地址

运行blink_example即可,此时GPIO17即可输出方便,可通过示波器观察。
(我这里只有万用表)
demo

参考文档: