1. 前言
s5pv210(2) — 固件烧写文章中,我们搞定了固件烧写的环境,本节我们开始编译代码。
2. 工具链
arm有多款编译工具链,它们之间的区别参考各版本arm-gcc区别与安装。
2.1 gcc-arm-none-eabi
百度arm-gcc点进去,这里有许多的版本以及对应Linux和Window的版本,下载gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2或gcc-arm-none-eabi-10-2020-q4-major-win32.zip。这个工具链适合编译裸机(相对于Linux应用程序而言)程序,使用小巧的newlib库,生成的代码体积小。
$ tar -jxf gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2
$ cd gcc-arm-none-eabi-10-2020-q4-major/bin
$ ./arm-none-eabi-gcc --version # 查看arm gcc版本
arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10-2020-q4-major) 10.2.1 20201103 (release)
# 将工具链路径加入PATH环境变量,/opt/gcc-arm-none-eabi-10-2020-q4-major/bin是我的工具链路径
$ echo "export PATH=/opt/gcc-arm-none-eabi-10-2020-q4-major/bin:$PATH" >> ~/.bashrc
$ . ~/.bashrc
$ arm-none-eabi-gcc --version # 查看arm gcc版本
arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10-2020-q4-major) 10.2.1 20201103 (release)
2.2 gcc-arm-none-linux-gnueabihf
这个工具链可以编译Linux应用程序,使用Glibc库,编译裸机程序体积比较大,暂时不用它。可以在ubuntu上使用apt
命令安装。
$ sudo apt install gcc-arm-linux-gnueabi
$ arm-linux-gnueabi-gcc --version
arm-linux-gnueabi-gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
也可以到官网下载,我下载的Linux环境下的工具链为gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf.tar.xz。通常GDB依赖的东西比较多,我这里也遇到了依赖问题,最终解决了。去这里Linaro releases storage server也能找到下载页面,各个工具链的区别可以参考 gnueabi和gnueabihf区别和
arm交叉编译器gnueabi、none-eabi、arm-eabi、gnueabihf的区别。
$ tar -xf gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf.tar.xz
$ ./gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gcc -v
gcc version 10.3.1 20210621 (GNU Toolchain for the A-profile Architecture 10.3-2021.07 (arm-10.29))
# 运行arm-none-linux-gnueabihf-gdb会有问题提示缺少libncursesw.so.5
$ whereis libncursesw
libncursesw: /usr/lib/x86_64-linux-gnu/libncursesw.so /usr/lib/x86_64-linux-gnu/libncursesw.a
$ ls /usr/lib/x86_64-linux-gnu/libncursesw.so*
/usr/lib/x86_64-linux-gnu/libncursesw.so.6.2 # 我的ubuntu是6.2版本,应该能兼容5,尝试建立软连接
$ sudo ln -s libncursesw.so.6.2 /usr/lib/x86_64-linux-gnu/libncursesw.so.5
# 又提示缺少libpython2.7.so.1.0,我的ubuntu没有python2.7,安装一下
$ sudo apt install libpython2.7
$ ./gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf/bin/arm-none-linux-gnueabihf-gdb -v
GNU gdb (GNU Toolchain for the A-profile Architecture 10.3-2021.07 (arm-10.29)) 10.2.90.20210621-git
3. 编译工程
拷贝x210v3裸机开发教程\src\buzzer
文件夹到虚拟机,这是一个使用arm-none-eabi-gcc
和make
编译的工程,可以用来验证我们的环境。
# 在虚拟机中计入buzzer目录直接进行编译即可
$ cd buzzer
$ make
编译成功了,但是有个mkv210
不存在,这个程序是用来制作SD卡镜像的,buzzer\mkv210.exe
是windows版本的,需要实现一个Linux的。
4. SD卡镜像制作工具
查看buzzer/source/start.S
文件,其中描述了镜像文件头是4个字节,分别是:镜像长度含16字节头
保留值为0
字节累加和不含16字节头
保留值为0
,镜像长度不要求对齐,代码实现时直接在原始的bin文件上修改第1个word和第三个word即可,我们的电脑多是小端环境,故没有考虑大端情况。详细参考 [project X] tiny210(s5pv210)上电启动流程(BL0-BL2)。
/*
* bl1 header infomation for irom
*
* 0x0 - bl1 size
* 0x4 - reserved (should be 0)
* 0x8 - check sum
* 0xc - reserved (should be 0)
*/
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
实现镜像制作的源码为:
mkv210.c
#include <stdio.h> #include <stddef.h> #include <stdint.h> uint8_t buf[512]; struct { uint32_t len; uint32_t reserved0; uint32_t sum; uint32_t reserved1; } header; int main(int argc, char *argv[]) { FILE *binfp; uint32_t sum; long binlen; long i; int j; if (argc != 2) { fprintf(stderr, "Usage: mkv210 x.bin\n"); return -1; } binfp = fopen(argv[1], "rb+"); if (binfp == NULL) { perror(argv[1]); return -1; } fseek(binfp, 0L, SEEK_END); binlen = ftell(binfp); if (binlen <= 16) { fprintf(stderr, "file is invalid. file sizes small then 16 bytes.\n"); fclose(binfp); remove(".bin.tmp"); return -1; } rewind(binfp); fread(&header, 1, sizeof(header), binfp); if ((header.len < binlen) || (header.len > 16*1024)) { fprintf(stderr, "file is invalid. img size in header is bigger then " "16KB or smaller then file sizes.\n"); fclose(binfp); remove(".bin.tmp"); return 1; } if ((header.reserved0 != 0) || (header.reserved1 != 0)) { fprintf(stderr, "file is invalid. header value should be zero.\n"); return -1; } header.len = binlen; sum = 0; for (i = binlen-sizeof(header); i >= 512; i -= 512) { fread(buf, 512, 1, binfp); for (j = 0; j < 512; j++) { sum += buf[j]; } } fread(buf, i, 1, binfp); for (j = 0; j < i; j++) { sum += buf[j]; } header.sum = sum; fseek(binfp, 0L, SEEK_SET); fwrite(&header, 1, sizeof(header), binfp); fclose(binfp); return 0; }
编译mkv210程序并添加到系统PATH中:
gcc mkv210.c -o mkv210 mkdir ~/bin cp mkv210 ~/bin $ echo "export PATH=/home/xflm/bin:$PATH" >> ~/.bashrc $ . ~/.bashrc
现在进入buzzer
目录编译这个工程就可以了,参考s5pv210(2) — 固件烧写烧写运行。
5. C语言烧录SD卡
这个程序将镜像打包和SD写入整合到了一起。
mks5pv210.c
#include <stdio.h> #include <stddef.h> #include <stdint.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <dirent.h> #include <getopt.h> #define DEVICE_DEFAULT "/dev/sdb" uint8_t buf[4096]; const char *selfname; typedef struct { uint32_t len; uint32_t reserved0; uint32_t sum; uint32_t reserved1; } header_t; typedef enum { OPT_BINFILE = 0x62, /* 'b' */ OPT_DEVICE = 0x64, /* 'd' */ OPT_FORCE = 0x66, /* 'f' */ OPT_HELP = 0x68, /* 'h' */ } opt_e; const struct option longopts[] = { {"binfile", required_argument, NULL, OPT_BINFILE}, {"device", required_argument, NULL, OPT_DEVICE}, {"force", no_argument, NULL, OPT_FORCE}, {"help", no_argument, NULL, OPT_HELP}, {0}, }; const char optstring[] = "b:d:fh"; void help(FILE *fp) { fprintf(fp, "Usage: %s [option]\n" " -b the binary of s5pv210 has a 16-type header, and the " "program ectry address is 0xD0020010\n" " --binfile\n" " -d the device node of the SD card. If it doesn't exist, " "try to write " DEVICE_DEFAULT "\n" " --device\n" " -f don't check sd card, It usually used in the first " "brning of SD\n" " --force\n" " -h show this message\n" " --help\n" , selfname); } int main(int argc, char *argv[]) { FILE *binfp; uint32_t sum; ssize_t binlen, i, retlen; header_t header; int j; int mmfd; const char *device = DEVICE_DEFAULT; const char *binfile = NULL; int force = 0; int opt; selfname = argv[0]; while((opt = getopt_long(argc, argv, optstring, longopts, NULL)) > 0) { switch(opt) { case OPT_BINFILE: binfile = optarg; break; case OPT_DEVICE: device = optarg; break; case OPT_FORCE: force = 1; break; case OPT_HELP: help(stdout); return 0; case '?': help(stderr); return -1; } } if(binfile == NULL) { fprintf(stderr, "no binfile\n"); return -1; } mmfd = open(device, O_RDWR); if(mmfd < 0) { perror(device); return -1; } if(force) { retlen = lseek(mmfd, 512+16, SEEK_SET); if(retlen != 512+16) { perror(device); return -1; } } else { retlen = lseek(mmfd, 512, SEEK_SET); if(retlen != 512) { perror(device); return -1; } retlen = read(mmfd, &header, sizeof(header_t)); if(retlen != sizeof(header_t)) { perror(device); return -1; } if((header.len == 0) || (header.reserved0 != 0) || (header.reserved1 != 0)) { printf("%s is not a s5pv210 sd card: len:%d reserved0:%d sum:%d reserved1:%d\n", device, header.len, header.reserved0, header.sum, header.reserved1); return -1; } } binfp = fopen(binfile, "rb+"); if(binfp == NULL) { perror(binfile); return -1; } fseek(binfp, 0L, SEEK_END); binlen = ftell(binfp); rewind(binfp); retlen = fread(&header, sizeof(header_t), 1, binfp); if(retlen != 1) { perror(binfile); return -1; } if((header.reserved0 != 0) || (header.reserved1 != 0)) { fprintf(stderr, "file is invalid. header value should be zero.\n"); return -1; } header.len = binlen; sum = 0; for(i = binlen-sizeof(header_t); i >= (int)sizeof(buf); i -= sizeof(buf)) { retlen = fread(buf, sizeof(buf), 1, binfp); if(retlen != 1) { perror(binfile); return -1; } retlen = write(mmfd, buf, sizeof(buf)); if(retlen != sizeof(buf)) { perror(device); return -1; } for(j = 0; j < (int)sizeof(buf); j++) { sum += buf[j]; } } retlen = fread(buf, 1, i, binfp); if(retlen != i) { perror(binfile); return -1; } retlen = write(mmfd, buf, i); if(retlen != i) { perror(device); return -1; } for(j = 0; j < i; j++) { sum += buf[j]; } header.sum = sum; rewind(binfp); off_t off = header.len; retlen = lseek(mmfd, -off, SEEK_CUR); if(retlen != 512) { perror(device); } retlen = write(mmfd, &header, sizeof(header_t)); if(retlen != sizeof(header_t)) { perror(device); return -1; } retlen = fwrite(&header, sizeof(header_t), 1, binfp); if(retlen != 1) { perror(binfile); return -1; } fsync(mmfd); close(mmfd); fclose(binfp); return 0; }
编译运行,因为SD卡属于
disk
用户组,需要使用sudo
提权。gcc mks5pv210.c -o mks5pv210 sudo ./mks5pv210 -b output/buzzer.bin
上一篇:s5pv210(2) – 固件烧写
下一篇:s5pv210(4) – 一键自动烧录
目录:s5pv210 – 集合
6. 参考
各版本arm-gcc区别与安装
[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)
linaro交叉编译工具安装配置
gnueabi和gnueabihf区别
arm交叉编译器gnueabi、none-eabi、arm-eabi、gnueabihf的区别