首页 > 系统相关 >Makefile知识点总结(Linux下开发Risc-V单片机实例)

Makefile知识点总结(Linux下开发Risc-V单片机实例)

时间:2024-07-25 15:00:22浏览次数:19  
标签:defs 知识点 变量 文件 makefile Makefile Risc 编译

Makefile

会不会写makefile,从一个侧面决定一个人是否具备完成大型工程的能力。

Makefile和make命令一起配合使用,为什么要使用makefile,原因以及优点在下文解释。

简单辨析一下建立工程的三种方式

  1. Makefile

    使用非常广泛,通用性强,可跨平台

    但是语法比较严格,写一个通用,便于管理,兼容性强的makefile比较困难

  2. cmake

    简单易用,使用广泛,便于管理,可跨平台

    自动生成的makefile过于臃肿

  3. sh脚本

    自由,高度定制,简单易用,可操作性强,方便维护。

    sh建立的工程太少

makefile带来的好处就是“自动化编译”

  • 把源文件编译成中间代码文件 ,在windows下就是.obj文件,在UNIX下是.o文件。这个动作叫做编译(compile)

  • 再把目标文件合成为可执行文件的过程或者称为动作叫做链接

    源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。

    如果函数未被声明,编译器会给出一个警告,但可以生成 ObjectFile.而在链接程序时,链接器会在所有的ObjectFile中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error)。

Makefile告诉make命令如何编译和链接工程中需要的源文件和头文件。

Makefile文件包含了一系列的“规则”,规则的基本结构如下: 目标、依赖、命令

编译规则如下:

1)如果这个工程没有编译过,则需要所有的C文件都要编译并链接

2)如果工程中的某几个C文件被修改,那么我们只编译被修改后的C文件,并链接目标程序。

3)如果这个工程的头文件被改变了,需要编译引用这几个头文件的C文件,并链接目标程序。

make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重新编译,从而自己编译所需要的文件和链接目标程序。

目标文件(Target)包含:执行文件 和 中间目标文件

依赖文件就是冒号之后的.c文件和.h文件。每一个.o文件都有一组依赖文件,而.o文件又是执行文件的依赖文件。

依赖文件的实质就是说明 目标文件由哪些文件生成。换言之,就是目标文件是哪些文件更新的。

定义好依赖文件之后的命令定义了如何生成目标文件的操作系统命令。

make会比较依赖文件和目标文件的修改日期,如果依赖文件的修改日期比目标文件的修改日期新或者目标文件不存在时,执行make后续定义的命令。

在Makefile中使用变量

使用变量来简化规则的编写。变量可以在Makefile的任何地方,引用变量时需要使用$(变量名)的形式。
赋值:=和:=

​ ·使用=进行赋值时,Makefile会延迟展开,这意味着变量的值不会立即确定,而是在每次变量被引用时根据变量的当前值重新计算。可以使用后面定义的变量。最后再展开

​ ·使用:=进行赋值时,Makefile会进行直接展开,这意味着变量的值在赋值时立即确定,并且之后不会改变(除非有其他机制,如override指令),只能使用前面定义好的变量。

+=追加变量的值
$(变量名) 自定义变量名

$@ $< $^ 自动变量

$@表示规则中的目标

$<表示第一个依赖文件

$^表示所有的依赖文件

模式规则允许使用%通配符来匹配文件名,从而可以为一组文件定义相同的编译规则。

CC=gcc
%.o: %.c  
	$(CC) -c $< -o $@

这条规则表示对于所有的.c文件,都使用$(CC) -c命令编译成对应的.o文件

让make自动推导:自动推导文件以及文件依赖关系后面的命令

清空目标文件的规则:伪目标

是那些不真正生成文件的目标,如clean,是为了和同名文件冲突,可以使用.PHONY声明伪目标

.PHONY :clean
clean :
	-rm edit $(objects)

不成文的规矩是 --“clean从来都是放在文件的最后”

Makefile五大项:

​ 显式规则、隐晦规则、变量定义、文件指示和注释

  • 显示规则

    显式规则。显式规则说明了,如何生成一个或多的的目标文件。这是由 Makefile的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。

  • 隐晦规则

    由于makefile有自动推导的功能,所以隐晦的规则可以让我们粗糙地简略地书写Makefile

  • 变量的定义

    在Make file中我们要定义一系列的变量,变量一般都是字符串,这个有点你像C语言中的宏,当Makefile被执行时,其中的变量都会被扩展到相应的引用位置上。

    变量的高级使用方法

    ​ 一是变量值的替换。

    ​ 二是一种变量替换的技术以“静态模式”定义。

    ​ 三是把变量的值再变成变量。

  • 文件指示

    其包含了三个部分

    一是在一个makefile中引用另一个makefile,类似C语言#include

    另一个是指根据某些情况指定Makefile中的有效部分,类似C语言中的预编译#if,

  • 注释

    makefile只有行注释 #

    还值得一提的是:makefile的命令 必须要以[tab]键开始

追加变量值

override指示符

多行变量

有点类似于C语言的define宏定义

define two-lines
echo foo
echo $(bar)
endef

环境变量

make运行时的系统环境变量可以在make开始运行时被载入到makefile文件中,

如果makefile中已经定义了这个变量,或者这个变量由命令行带入,那么系统的环境变量的值将被覆盖。

如果我们在环境变量中设置“CFLAGS”环境变量。那么可以在所有的Makefile中使用这个变量。

Make的工作方式

GNU的make工作方式的执行步骤

1、读入所有的 Makefile。
2、读入被 include 的其它 Makefile。
3、初始化文件中的变量。
4、推导隐晦规则,并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系,决定哪些目标要重新生成。
7、执行生成命令

第一个阶段:1-5步;如果变量被使用,make会把其展开在使用的位置,但不是立马完全展开,如果变量出现在依赖关系的规则中,仅当这条依赖被决定使用了才在内部展开。

第二个阶段:6-7;

自动生成依赖性

在makefile中,我们的依赖关系可能会需要包含一系列的头文件,可以使用编译器的“-M”自动寻找源文件中包含的头文件,并生成一个依赖关系

实例参考

  • 原始版本的Makefile
objects = main.o kbd.o command.o display.o \
		insert.o search.o files.o utils.o

edit : $(objects)
	cc -o edit $(objects)
main.o : main.c defs.h
	cc -c main.c
kbd.o : kbd.c defs.h command.h
	cc -c kbd.c
command.o : command.c defs.h command.h
	cc -c command.c
display.o : display.c defs.h buffer.h
	cc -c display.c
insert.o : insert.c defs.h buffer.h
	cc -c insert.c
search.o : search.c defs.h buffer.h
	cc -c search.c
files.o : files.c defs.h buffer.h command.h
	cc -c files.c
utils.o : utils.c defs.h
	cc -c utils.c
clean :
	rm edit $(objects)
  • 自动推导版本的Makefile
objects = main.o kbd.o command.o display.o \
		insert.o search.o files.o utils.o
 cc = gcc

edit : $(objects)
	cc -o edit $(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
	rm edit $(objects)

实例Makefile解析

偶然看到一个实例:

在linux下使用纯命令行开发调试单片机(刚好单片机用的是RISC-V单片机 CH32系列)

大致工具就是使用OPENOCD进行的一个编程,先下载RISC-V内核芯片的交叉编译工具链和调试下载器,然后在linux下下载OPENOCD进行编译和安装,使用官网提供的外设库文件,openocd属于第三方软件,移植安装的过程大致分为(a)配置config文件,在这里选择使用哪个调试工具,例如jtag,jlink什么之类的。然后运行之后会生成makefile文件(b)然后编译(c)然后下载安装。

openocd把hex文件写到flash里面去的、openocd是一个服务进程

下面摘抄了某个开源的makefile(ch32)

CROSS_COMPILER_PREFIX = C:/MounRiver/MounRiver_Studio/toolchain/RISC-V Embedded GCC/bin/riscv-none-embed-
#CROSS_COMPILER_PREFIX = $(HOME)/MRS_Toolchain_Linux_x64_V1.80/RISC-V Embedded GCC/bin/riscv-none-embed-

OPENOCD = C:/MounRiver/MounRiver_Studio/toolchain/OpenOCD/bin/openocd.exe
OPENOCD_ARGS = -f C:/MounRiver/MounRiver_Studio/toolchain/OpenOCD/bin/wch-riscv.cfg
#OPENOCD = $(HOME)/MRS_Toolchain_Linux_x64_V1.80/OpenOCD/bin/openocd
#OPENOCD_ARGS = -f $(HOME)/MRS_Toolchain_Linux_x64_V1.80/OpenOCD/bin/wch-riscv.cfg
#OPENOCD = /usr/local/bin/openocd
#OPENOCD_ARGS = -f interface/wlink.cfg -f target/wch-riscv.cfg

#ARCH = -march=rv32imafc -mabi=ilp32f
#ARCH = -march=rv32imac -mabi=ilp32
ARCH = -march=rv32ecxw -mabi=ilp32e

CH32_STD_LIB_DIR = ../ch32-standard-library/ch32v00x
#CH32_STD_LIB_DIR = $(HOME)/playground/ch32-standard-library/ch32v00x

CROSS_C_SOURCE_FILES += $(wildcard $(CH32_STD_LIB_DIR)/peripheral/src/*.c)
CROSS_C_SOURCE_FILES += $(wildcard $(CH32_STD_LIB_DIR)/core/*.c)
CROSS_C_SOURCE_FILES += $(wildcard ./src/*.c)

CROSS_ASM_SOURCE_FILES += $(CH32_STD_LIB_DIR)/sample/startup.S

CROSS_C_FLAGS += -fno-common -fno-builtin -Os
CROSS_C_FLAGS += -DCHIP_CH32V00X

CROSS_LD_FLAGS += -Wl,--no-relax -specs=nosys.specs -specs=nano.specs -nostartfiles \
-T$(CH32_STD_LIB_DIR)/sample/default.ld

#CROSS_LD_FLAGS += -lm

CROSS_C_INCLUDES = $(CH32_STD_LIB_DIR)/peripheral/inc $(CH32_STD_LIB_DIR)/core ./src

OPENOCD_FLASH_COMMANDS = -c "program $< verify" -c wlink_reset_resume -c exit

include ./miscellaneous-makefiles/cross-gcc-mcu.mk

target_detail: $(BUILD_DIR)/$(TARGET).elf
	$(CROSS_OBJDUMP) -S -D -M xw $< > $<.lss
	$(CROSS_SIZE) --radix=16 --format=SysV $<

OPENOCD

1.调试

​ Openocd允许开发人员在嵌入式操作系统中执行单步调试、断电设置、查看寄存器状态、访问内存操作,这对识别和解决软件或硬件问题非常有用。

2.编程

​ 可以用于烧写程序(固件)到嵌入式设备的内存中,确保正确的程序在硬件上允许。

3.支持多种调试接口

​ 支持多种调试接口标准,包括JTAG和SWD,适合不同类型的嵌入式芯片。

4.支持多种目标设备

​ 支持许多不同芯片和处理器体系结构的调试支持。

5.可拓展性

​ 开源,可根据需要进行定制和扩展,以满足特定的需求。

·openocd --file | -f 加载配置文件,后跟配置文件名
·interface:指定调试接口,如 interface/stlink.cfg
·adapter_khz:配置适配器的时钟频率。

OpenOCD的cfg文件配置方法

  • 启动openocd服务,需要指定interface和target(target和board可以选择,最小系统板选target, 板载DRAM, 外部Flash的可以带上board的cfg文件)

标签:defs,知识点,变量,文件,makefile,Makefile,Risc,编译
From: https://blog.csdn.net/Stay_Hun_forward/article/details/140690064

相关文章

  • 音视频编解码常用知识点(转载)
    ##视频播放器原理视频播放器播放一个互联网上的视频文件,需要经过以下几个步骤:解协议,解封装,解码视音频,视音频同步。如果播放本地文件则不需要解协议,为以下几个步骤:解封装,解码视音频,视音频同步。他们的过程如图所示。*<strong>解协议的作用</strong>,就是将流媒体协议的数据,解析为......
  • 架构知识点(一)
    执行阶段(ExecutionStage)执行阶段是CPU流水线中的一个步骤,通常发生在取指阶段(InstructionFetch,IF)和解码阶段(InstructionDecode,ID)之后。在执行阶段,CPU会进行以下操作:执行算术或逻辑操作:根据指令类型,ALU会执行加法、减法、逻辑运算等操作。处理移位操作:如果指令需要,执......
  • Java后端开发知识点积累20240724
    1.使用流(Stream)API和lambda表达式来从一个dateBaseList列表中提取所有的title字段,并将这些title值收集到一个新的列表中dateBaseList.stream().map(InspectionManageEntity::getTitle).collect(Collectors.toList());2.@PathVariable注解作用@PathVariable是Spring框架中的......
  • 求职面试 - 计算机网络面试知识点
    计算机网络面试知识点1.计算机网络基础1.1主机间的通信方式客户端-服务器(C/S)客户端是服务的请求放,服务器是服务的提供方。对等(P2P)不用区分谁是客户端,谁是服务器,双方都能够向对方请求与提供服务。1.2电路&分组交换分组交换每个分组由首部和尾部组成,包含源地址......
  • 求职面试 - Spring 面试知识点
    Spring面试知识点1.Spring特点Spring主要有如下特点:轻量级:Spring是非侵入式,其中的对象不依赖Spring的特定类;控制反转(IoC):通过IoC,促进了低耦合,一个对象依赖的其他对象通过被动的方式传递进来,而不用该对象主动创建或查找;面向切面(AOP):支持面向切面编程,将应用业务逻辑......
  • .NET Core 核心知识点(四) -- 初会依赖注入
    控制反转、服务定位器、依赖注入  控制反转:使用对象或者服务的时候,不需要自己去创建/new服务,而是在使用的时候直接声明,容器会自动分配一个服务实例。相当于自己用发电机发电使用和利用电网公司的电的区别,自己发电,我需要一台发电机,安装发电机,自己设置电压,频率等等,而使用电......
  • Java中的优先级队列(PriorityQueue)(如果想知道Java中有关优先级队列的知识点,那么只看这
        前言:优先级队列(PriorityQueue)是一种抽象数据类型,其中每个元素都关联有一个优先级,元素按照优先级顺序进行处理。✨✨✨这里是秋刀鱼不做梦的BLOG✨✨✨想要了解更多内容可以访问我的主页秋刀鱼不做梦-CSDN博客先让我们看一下本文大致的讲解内容:目录1.优......
  • Python入门知识点 7--散列类型与字符编码
    1、初识散列类型(无序序列)数据类型分为3种:   前面已经学过了两种类型   1.数值类型:int/float/bool只能存储单个数据      2.序列类型:str/list/tuple,有序的存储多个数据--有序类型,有下标,可以进行索引切片步长操作          3.散列类型......
  • Python入门知识点 6--序列类型的方法
    1、初识序列类型方法序列类型的概念:数据的集合,在序列类型里面可以存放任意的数据也可以对数据进行更方便的操作这个操作就是叫增删改查(crud)(增加(Creat),读取查询(Retrieve),更新(Update),删除(Delete)几个单词的首字母简写)增删改查是操作数据最底层的操作(从本质......
  • 数据库入门知识点 1--初识MySQL数据库
    1、数据库(1)json,wps,txt,md,···都是保存文本数据的(数据交互麻烦,数据安全问题)(2)列表,元组,字典,集合,···(保存临时的数据,对数据进行处理的时候保存。)银行卡----存入的钱(不允许随意修改的)游戏的数据-----数据不存档--没有安全保障----第二天就会回到解放前。(3)使用专门的数据库......