好久没有做嵌入式Arm Linux 的开发了。最近要将open62541 的应用程序移植到i.mx6u 嵌入式控制器。网络上讲解i.mx6 交叉编译的文章太多了。但是都过于复杂,大多数使用虚拟机实现。其实在ubuntu OS 下,开发ARM 嵌入式应用软件相对是相当简单的。这里记录了在ubuntu Linux 下,交叉编译i.mx 应用软件的过程。
安装交叉编译工具及其基本库
目标设备为两种ARM
NXP i.mx6 基于Arm V7
RK3399
arm-linux-gnueabihf-gcc
imx6 是arm v7 架构,使用arm-linux-gnueabihf-gcc 和 arm-linux-gnueabihf-g++
sudo apt-get update
sudo apt-get install gcc-arm-linux-gnueabihf
sudo apt-get install g++-arm-linux-gnueabihf
值得注意的是 安装这两个交叉编译器之后,在usr/lib 中自动建立了 /gcc-cross/arm-linux-gnueabihf/11目录,内部包含了gcc 基本的库,例如libc.so 。自己编译的库也copy 到这个目录中。
g++-aarch64-linux-gnu
对于Arm v8 64位架构的处理器,例如RK3399 的 cortex-A53 ,需要使用gcc-aarch64-linux-gnu和g++-aarch64-linux-gnu
sudo apt-get install gcc-aarch64-linux-gnu
sudo apt-get install gcc-aarch64-linux-gnu
关于与目标机的兼容性
遇到的最大问题是libc.so.6 等动态库不兼容的问题,我部分解决的方法是
1. 将目标机器上的lib 考到编译机上。
2。 应用程序使用静态编译。
测试 gcc
为了测试安装的交叉编译工具,先编写了一个Test 程序
#include <stdio.h>
#include <math.h>
int main(){
int x;
x=pow(3,2);
printf("%d\n",x);
}
编译命令
arm-linux-gnueabihf-gcc test1.c -o test1
编译open62541 的依赖库
1 如果open62541 Enable NodesetLoad ,需要依赖库libxml.so。
编译安装方式:
下载 源码
使用cmake
libxml 的选项比较多,容易出错。我做了一些选择。
2 如果open62541 Enable History ,需要依赖库sqlite3
编译安装方式:
编译 open62541
由于之前安装了ubuntu open62541 库,include 放置在usr/local/include 中。当交叉编译时,仍然使用它,可能会有点差错,比如Eable pub/sub 时,要UA_ENABLE_AMALGAMATION 打勾。
libxml.so 要指向/gcc-cross/arm-linux-gnueabihf/11。
编译完成后,copy 到/gcc-cross/arm-linux-gnueabihf/11 中:
sudo cp libopen62541.so /usr/lib/gcc-cross/arm-linux-gnueabihf/11/libopen62541.so
编译Opcua 应用程序
编辑最小server
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
#include <open62541/server.h>
/* Build Instructions (Linux)
* - g++ server.cpp -lopen62541 -o server */
using namespace std;
int main() {
UA_Server *server = UA_Server_new();
// add a variable node to the adresspace
UA_VariableAttributes attr = UA_VariableAttributes_default;
UA_Int32 myInteger = 42;
UA_Variant_setScalarCopy(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
attr.description = UA_LOCALIZEDTEXT_ALLOC("en-US","the answer");
attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US","the answer");
UA_NodeId myIntegerNodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME_ALLOC(1, "the answer");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
parentReferenceNodeId, myIntegerName,
UA_NODEID_NULL, attr, NULL, NULL);
/* allocations on the heap need to be freed */
UA_VariableAttributes_clear(&attr);
UA_NodeId_clear(&myIntegerNodeId);
UA_QualifiedName_clear(&myIntegerName);
UA_StatusCode retval = UA_Server_runUntilInterrupt(server);
UA_Server_delete(server);
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}
编译命令
aarch64-linux-gnu-g++ server.cpp -o server -lopen62541 -lxml2 -L /home/yao/yao2023/RK3399Project/aarch64-linux-gnu/lib -static -I /usr/local/include --std=c++2a
由于open62541 中包含了xml 动态库。因此要-lxml2.如果将xml编译成为静态库 可能就没有这个问题。
编译成为静态库,避免libc 等动态库不兼容的问题。
复制到目标机运行
scp ./server root@192.168.31.108:/home/application/server
编译 IEC61499 runtime
aarch64-linux-gnu-g++ ./*.cpp ./FunctionBlocks/*.cpp -I ./modbusTCP -o uaForteA -lopen62541 -lxml2 -I /usr/local/include --std=c++2a -L /home/yao/yao2023/RK3399Project/aarch64-linux-gnu/lib -static
复制到目标机运行
Go 语言的交叉编译
在我们的项目中,有Web 实现设备管理,采用Go 语言编写web 相对NodeJS 更精简一些。下面记录了Go 语言应用程序的交叉编译,这是非常简单的。
测试代码
# shared.go
package main
import "C"
//export Sum
func Sum(a, b int) int {
return a + b
}
func main(){}
32-bit arm
CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc go build -buildmode=c-shared -o share.so
64-bit arm
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc-5.3.1 go build -buildmode=c-shared -o share.so
构建了一个动态库。