【嵌入式狂刷100题】- 1基础知识部分
准备新开专栏【嵌入式狂刷100题】😶🌫️😶🌫️🤧加油!!!,内容包括
- 基础知识部分
- 操作系统部分
- 处理器架构部分
- 外设驱动部分
- 通信协议部分
- 存储器管理部分
- 硬件设计部分
- 多媒体部分
- 调试故障排除部分
- 编码开发部分
【嵌入式狂刷100题】- 1基础知识部分
- 1. 请简述一下嵌入式系统开发的流程?
- 2. 嵌入式系统与计算机系统的区别是什么?
- 3. 请简要介绍一下C语言在嵌入式系统中的特点和应用。
- 3.1 C语言在嵌入式系统中的特点
- 3.2 C语言在嵌入式系统中的应用
- 3.3 C语言在嵌入式系统中的开发工具
- 4. 嵌入式系统中的位域有什么作用?请举例说明使用场景。
- 4.1 位域的作用
- 4.2 位域的定义
- 4.3 位域的使用场景
- 5. 嵌入式系统中常见的数据类型有哪些?请介绍其对应的数据范围和存储方式。
- 5.1 示例输出(在32位系统上)
- 5.2示例输出(在64位系统上)
- 5.3主要区别
- 5.4总结
- 6. 嵌入式系统中如何进行硬件资源的访问和控制?
- 6.1 硬件寄存器的访问
- 6.2. 配置硬件模块
- 6.3 使用中断
- 6.4 使用DMA(直接内存访问)
- 6.5 使用硬件抽象层(HAL)
- 6.6 调试和验证
- 6.7注意事项
- 6.8总结
- 7. 嵌入式系统中常见的编译器有哪些?请介绍其特点和使用方法。
- 8. 请简介绍一下Makefile的使用方法和作用。
- 8.1 Makefile 的作用
- 8.2 Makefile 的基本结构
- 8.3示例:简单的 Makefile
- 8.3.1 定义变量
- 8.3.2 定义规则
- 8.3.3 定义伪目标
- 8.3.4 使用方法
- 8.4 Makefile 的使用方法
- 8.5 Makefile 的常见语法
- 8.6Makefile 的进阶用法
- 8.7总结
1. 请简述一下嵌入式系统开发的流程?
- 需求分析
系统需要采集温度数据并通过 Wi-Fi 上传到服务器。
温度采集精度为 ±0.5°C,上传频率为每分钟一次。 - 系统设计
硬件:选择 STM32 微控制器,搭配温度传感器和 Wi-Fi 模块。
软件:使用 FreeRTOS 实现多任务调度,开发温度采集和 Wi-Fi 通信任务。 - 硬件开发
设计电路原理图,绘制 PCB。
制作原型板并调试。 - 软件开发
开发温度传感器驱动程序。
实现 Wi-Fi 通信协议。
集成 FreeRTOS,配置任务调度。 - 系统集成
将软件烧录到硬件中,验证温度采集和 Wi-Fi 通信功能。 - 测试验证
测试温度采集精度。
测试 Wi-Fi 通信的稳定性。
测试系统在高温环境下的可靠性。 - 部署与维护
将系统部署到客户现场。
提供用户手册和技术支持
2. 嵌入式系统与计算机系统的区别是什么?
嵌入式系统和计算机系统是两种不同的计算系统,尽管它们都基于相似的硬件和软件技术,但在设计目标、应用场景、资源使用等方面存在显著差异。以下是它们的主要区别:
-
设计目标
- 嵌入式系统:设计目标是完成特定的任务或功能,通常用于控制、监测或执行特定操作。嵌入式系统通常是为特定应用定制的,强调实时性、可靠性和低功耗。
- 计算机系统:设计目标是通用的计算和信息处理,能够运行多种应用程序,适用于广泛的任务。计算机系统强调灵活性、多功能性和高性能。
-
硬件资源
- 嵌入式系统:通常资源有限,包括处理器、内存、存储等。硬件配置根据具体需求进行优化,以降低成本、功耗和尺寸。
- 计算机系统:硬件资源相对丰富,包括高性能的处理器、大容量内存和存储设备,能够支持复杂的应用程序和多任务处理。
-
软件复杂度
- 嵌入式系统:软件通常较为简单,专注于特定任务的实现。操作系统(如果有的话)通常是轻量级的实时操作系统(RTOS)或裸机编程。
- 计算机系统:软件复杂度较高,通常运行通用操作系统(如Windows、Linux、macOS),支持多任务、多用户和复杂的应用程序。
-
实时性
- 嵌入式系统:许多嵌入式系统需要实时响应,即在规定的时间内完成任务。实时性是嵌入式系统设计的重要考虑因素。
- 计算机系统:通常不强调实时性,任务调度和响应时间较为宽松,优先考虑多任务处理和用户体验。
-
应用场景
- 嵌入式系统:广泛应用于工业控制、家用电器、汽车电子、医疗设备、智能家居等领域,通常嵌入到更大的系统中。
- 计算机系统:主要用于个人计算、服务器、数据中心、办公自动化等领域,作为独立的计算设备使用。
- 开发工具和环境
- 嵌入式系统:开发工具和环境通常较为专用,可能需要特定的硬件调试工具、交叉编译器和低级别的编程语言(如C、汇编)。
- 计算机系统:开发工具和环境较为通用,支持多种编程语言和开发框架,开发过程相对简单。
- 功耗和尺寸
- 嵌入式系统:通常对功耗和尺寸有严格要求,特别是在便携式设备和电池供电的系统中。
- 计算机系统:功耗和尺寸通常不是主要限制因素,特别是在台式机、服务器等设备中。
- 成本
- 嵌入式系统:成本通常较低,硬件和软件都经过优化以降低生产和维护成本。
- 计算机系统:成本相对较高,硬件配置和软件许可费用较高。
总结
嵌入式系统和计算机系统在设计目标、硬件资源、软件复杂度、实时性、应用场景等方面有显著差异。嵌入式系统更注重特定任务的实现、资源优化和实时性,而计算机系统则强调通用性、多功能性和高性能。
3. 请简要介绍一下C语言在嵌入式系统中的特点和应用。
C语言是嵌入式系统开发中最常用的编程语言,因其高效、灵活、接近硬件且资源占用低,成为嵌入式领域的首选语言。以下是C语言在嵌入式系统中的特点和应用:
3.1 C语言在嵌入式系统中的特点
- 高效性:
- C语言编译后的代码执行效率高,接近汇编语言,适合资源受限的嵌入式系统。
- 直接操作硬件(如寄存器、内存地址),能够充分发挥硬件性能。
-
灵活性:
- 支持底层硬件操作,能够直接访问内存、寄存器和外设。
- 提供指针和位操作,适合嵌入式系统中对硬件的精细控制。
-
可移植性:
- C语言具有较好的跨平台特性,通过编译器可以适配不同的嵌入式处理器架构(如ARM、AVR、MSP430等)。
- 标准库和工具链支持广泛,便于移植和复用代码。
-
资源占用低:
- C语言生成的代码体积小,占用内存少,适合资源有限的嵌入式系统(如微控制器)。
-
成熟度高:
- C语言在嵌入式领域应用广泛,拥有丰富的开发工具、库和社区支持。
- 大多数嵌入式操作系统(如FreeRTOS、Zephyr)和驱动程序都是用C语言编写的。
-
易于学习:
- 语法简洁,学习曲线相对平缓,适合嵌入式开发初学者。
3.2 C语言在嵌入式系统中的应用
-
微控制器(MCU)开发:
- C语言是微控制器开发的主流语言,广泛应用于8位、16位和32位MCU(如STM32、AVR、PIC、ESP32等)。
- 用于编写固件、驱动程序、外设控制(如GPIO、UART、I2C、SPI)等。
-
实时操作系统(RTOS):
- 大多数RTOS(如FreeRTOS、uC/OS、Zephyr)是用C语言编写的。
- 用于开发多任务调度、任务间通信、内存管理等实时功能。
-
设备驱动程序 :
- C语言适合编写底层硬件驱动程序,如传感器、显示屏、通信模块等的驱动。
-
嵌入式应用程序:
- 用于开发嵌入式系统的应用程序,如智能家居、工业控制、汽车电子、医疗设备等。
-
Bootloader和启动代码:
- C语言结合汇编语言,用于编写系统的启动代码和Bootloader,完成硬件初始化和加载操作系统。
-
协议栈开发:
- 用于实现通信协议栈,如TCP/IP、CAN、Modbus、Bluetooth等。
-
算法实现:
- 用于实现嵌入式系统中的算法,如信号处理、控制算法、加密算法等。
3.3 C语言在嵌入式系统中的开发工具
-
编译器:
- GCC(GNU Compiler Collection):支持多种嵌入式处理器架构。
- Keil、IAR、Code Composer Studio:专为嵌入式开发设计的集成开发环境(IDE)。
-
调试工具:
- JTAG/SWD调试器:用于硬件调试。
- GDB:用于软件调试。
-
模拟器/仿真器:
- QEMU:用于模拟嵌入式硬件环境。
- Proteus:用于电路仿真和代码测试。
总结
C语言在嵌入式系统中具有高效、灵活、资源占用低等特点,广泛应用于微控制器开发、RTOS、驱动程序、协议栈等领域。其成熟度高、工具链丰富,是嵌入式开发的首选语言。
4. 嵌入式系统中的位域有什么作用?请举例说明使用场景。
在嵌入式系统中,位域(Bit Field) 是一种特殊的数据结构,用于高效地管理和操作存储空间中的特定位。位域允许将一个整型变量(如int
或unsigned int
)划分为多个小的位段,每个位段可以单独访问和操作。这在资源受限的嵌入式系统中非常有用,因为它可以节省内存并提高代码的可读性。
4.1 位域的作用
-
节省内存:
- 位域可以将多个布尔值或小范围整数值存储在一个整型变量中,减少内存占用。
- 例如,8个布尔值可以存储在一个
unsigned char
(8位)中,而不是使用8个单独的bool
变量。
-
提高代码可读性:
- 位域可以通过命名位段来明确表示其含义,使代码更易读和维护。
- 例如,用一个位域表示设备状态(如“电源状态”、“连接状态”等),比直接操作位掩码更直观。
-
直接操作硬件寄存器:
- 嵌入式系统中的硬件寄存器通常使用特定位来控制设备功能,位域可以方便地映射和操作这些寄存器。
4.2 位域的定义
在C语言中,位域通过结构体(struct
)定义,语法如下:
struct {type [member_name] : width;
};
type
:位域的类型,通常是unsigned int
或int
。member_name
:位段的名称。width
:位段的宽度(位数)。
例如:
struct {unsigned int power : 1; // 1位,表示电源状态unsigned int mode : 2; // 2位,表示模式unsigned int error : 1; // 1位,表示错误状态
} status;
4.3 位域的使用场景
以下是嵌入式系统中位域的常见使用场景:
- 设备状态管理
- 位域可以用于表示设备的各种状态,如电源状态、连接状态、错误标志等。
- 示例:
struct {unsigned int power_on : 1; // 电源状态(0:关闭,1:打开)unsigned int connected : 1; // 连接状态(0:未连接,1:已连接)unsigned int error_flag : 1; // 错误标志(0:正常,1:错误) } device_status;
-
硬件寄存器映射
- 嵌入式系统中的硬件寄存器通常使用特定位来控制设备功能,位域可以方便地映射和操作这些寄存器。
- 示例:
typedef struct {unsigned int enable : 1; // 使能位unsigned int mode : 2; // 模式位unsigned int reserved : 5; // 保留位 } ControlRegister; ControlRegister *ctrl_reg = (ControlRegister *)0x1000; // 映射到硬件寄存器地址 ctrl_reg->enable = 1; // 使能设备
-
协议数据包解析
- 在通信协议中,数据包通常包含多个字段,每个字段占用特定位。位域可以方便地解析和构建这些数据包。
- 示例:
struct {unsigned int type : 4; // 数据包类型(4位)unsigned int length : 8; // 数据包长度(8位)unsigned int crc : 4; // CRC校验(4位) } packet_header;
-
节省存储空间
- 当需要存储多个布尔值或小范围整数值时,位域可以节省内存。
- 示例:
struct {unsigned int flag1 : 1;unsigned int flag2 : 1;unsigned int flag3 : 1;unsigned int value : 5; // 5位整数值 } data;
-
状态机实现
- 位域可以用于实现状态机,每个位段表示状态机的一个状态或条件。
- 示例:
struct {unsigned int state : 2; // 状态(0:空闲,1:运行,2:暂停,3:错误)unsigned int trigger : 1; // 触发条件(0:未触发,1:触发) } state_machine;
注意事项
- 可移植性:
- 位域的具体实现可能因编译器和平台而异,需谨慎使用。
- 位域宽度限制:
- 位域的总宽度不能超过基础类型的位数(如
unsigned int
通常为32位)。
- 位域的总宽度不能超过基础类型的位数(如
- 内存对齐:
- 位域可能会受到内存对齐的影响,导致内存布局与预期不符。
总结
位域在嵌入式系统中是一种高效的工具,适用于设备状态管理、硬件寄存器映射、协议数据包解析等场景。它可以节省内存、提高代码可读性,并方便地操作特定位。然而,使用时需注意可移植性和内存对齐问题。
5. 嵌入式系统中常见的数据类型有哪些?请介绍其对应的数据范围和存储方式。
在C语言中,可以使用 sizeof
运算符来获取各种数据类型所占的字节数。以下是一个简单的C语言程序,用于输出常见数据类型的字节大小:
#include <stdio.h>int main() {printf("Size of char: %zu bytes\n", sizeof(char));printf("Size of unsigned char: %zu bytes\n", sizeof(unsigned char));printf("Size of short: %zu bytes\n", sizeof(short));printf("Size of unsigned short: %zu bytes\n", sizeof(unsigned short));printf("Size of int: %zu bytes\n", sizeof(int));printf("Size of unsigned int: %zu bytes\n", sizeof(unsigned int));printf("Size of long: %zu bytes\n", sizeof(long));printf("Size of unsigned long: %zu bytes\n", sizeof(unsigned long));printf("Size of long long: %zu bytes\n", sizeof(long long));printf("Size of unsigned long long: %zu bytes\n", sizeof(unsigned long long));printf("Size of float: %zu bytes\n", sizeof(float));printf("Size of double: %zu bytes\n", sizeof(double));printf("Size of long double: %zu bytes\n", sizeof(long double));printf("Size of pointer (void*): %zu bytes\n", sizeof(void*));return 0;
}
代码说明:
-
sizeof
运算符:sizeof
返回指定类型或变量的大小(以字节为单位)。%zu
是sizeof
返回值的格式说明符,用于size_t
类型。
-
输出内容:
- 程序会输出每种数据类型在当前平台上的字节大小。
5.1 示例输出(在32位系统上)
Size of char: 1 bytes
Size of unsigned char: 1 bytes
Size of short: 2 bytes
Size of unsigned short: 2 bytes
Size of int: 4 bytes
Size of unsigned int: 4 bytes
Size of long: 4 bytes
Size of unsigned long: 4 bytes
Size of long long: 8 bytes
Size of unsigned long long: 8 bytes
Size of float: 4 bytes
Size of double: 8 bytes
Size of long double: 12 bytes
Size of pointer (void*): 4 bytes
注意事项:
- 不同平台(如32位和64位系统)可能会导致某些数据类型的大小不同。
- 如果需要在嵌入式系统中运行,请确保编译器和目标平台支持这些数据类型。
运行此代码后,您可以直接看到各数据类型在当前环境中的字节大小。
在64位系统上,某些数据类型的大小会与32位系统不同,尤其是指针类型和 long
类型。以下是64位系统上运行上述代码的典型输出:
5.2示例输出(在64位系统上)
Size of char: 1 bytes
Size of unsigned char: 1 bytes
Size of short: 2 bytes
Size of unsigned short: 2 bytes
Size of int: 4 bytes
Size of unsigned int: 4 bytes
Size of long: 8 bytes
Size of unsigned long: 8 bytes
Size of long long: 8 bytes
Size of unsigned long long: 8 bytes
Size of float: 4 bytes
Size of double: 8 bytes
Size of long double: 16 bytes
Size of pointer (void*): 8 bytes
5.3主要区别
-
指针类型 (
void*
):- 在64位系统上,指针类型通常占用 8 字节(64位),而在32位系统上占用 4 字节。
-
long
类型:- 在64位系统上,
long
类型通常占用 8 字节,而在32位系统上占用 4 字节。
- 在64位系统上,
-
long double
类型:- 在64位系统上,
long double
类型通常占用 16 字节,而在32位系统上可能占用 12 字节。
- 在64位系统上,
-
其他类型:
char
、short
、int
、float
、double
等类型的大小通常与32位系统一致。
5.4总结
64位系统与32位系统的主要区别在于指针类型和 long
类型的大小。通过运行上述代码,您可以清楚地看到当前平台上各数据类型的字节大小。
6. 嵌入式系统中如何进行硬件资源的访问和控制?
在嵌入式系统中,硬件资源的访问和控制是核心任务之一。嵌入式系统通常直接与硬件交互,通过读取和写入硬件寄存器、使用中断、DMA(直接内存访问)等技术来实现对硬件资源的访问和控制。以下是常见的方法和步骤:
6.1 硬件寄存器的访问
硬件寄存器是嵌入式系统中与硬件交互的主要接口。通过读写寄存器,可以配置硬件设备、获取状态或发送命令。
步骤:
-
确定寄存器地址:
- 从硬件手册中查找寄存器的内存映射地址。每个硬件模块(如GPIO、UART、定时器等)都有特定的寄存器地址。
- 例如,GPIO端口A的数据寄存器地址可能是
0x40020000
。
-
定义寄存器指针:
- 使用指针访问寄存器。通常将寄存器地址强制转换为适当的指针类型(如
volatile unsigned int*
)。 - 示例:
#define GPIOA_DATA_REG (*(volatile unsigned int *)0x40020000)
- 使用指针访问寄存器。通常将寄存器地址强制转换为适当的指针类型(如
-
读写寄存器:
- 通过指针读写寄存器。注意使用
volatile
关键字,防止编译器优化对寄存器的访问。 - 示例:
GPIOA_DATA_REG = 0x01; // 写入寄存器 unsigned int value = GPIOA_DATA_REG; // 读取寄存器
- 通过指针读写寄存器。注意使用
6.2. 配置硬件模块
硬件模块(如GPIO、UART、定时器等)通常需要通过寄存器进行配置。
示例:配置GPIO引脚
- 设置引脚模式(输入/输出)。
- 配置引脚的上拉/下拉电阻。
- 设置引脚速度(输出速率)。
- 示例:
#define GPIOA_MODE_REG (*(volatile unsigned int *)0x40020004) #define GPIOA_ODR_REG (*(volatile unsigned int *)0x40020014)void configure_gpio() {GPIOA_MODE_REG |= 0x01; // 设置引脚为输出模式 }void set_gpio_high() {GPIOA_ODR_REG |= 0x01; // 设置引脚为高电平 }void set_gpio_low() {GPIOA_ODR_REG &= ~0x01; // 设置引脚为低电平 }
6.3 使用中断
中断是嵌入式系统中处理异步事件的重要机制。通过配置中断,可以在硬件事件(如按键按下、定时器溢出等)发生时立即响应。
步骤:
- 配置中断源:
- 使能硬件模块的中断功能(如GPIO中断、定时器中断等)。
- 配置中断优先级:
- 设置中断优先级(如果支持嵌套中断)。
- 编写中断服务程序(ISR):
- 在ISR中处理中断事件。
- 使能全局中断:
- 在代码中使能全局中断。
示例:GPIO中断*
void gpio_isr() {// 处理中断事件if (GPIOA_INT_STATUS & 0x01) {// 清除中断标志GPIOA_INT_CLEAR = 0x01;}
}void configure_gpio_interrupt() {GPIOA_INT_ENABLE |= 0x01; // 使能GPIO中断NVIC_EnableIRQ(GPIOA_IRQn); // 使能NVIC中断
}
6.4 使用DMA(直接内存访问)
DMA用于在内存和外设之间直接传输数据,减少CPU的负担。
步骤:
- 配置DMA通道:
- 设置源地址、目标地址、传输长度和传输模式。
- 启动DMA传输:
- 使能DMA通道并开始传输。
- 处理DMA中断:
- 在DMA传输完成后处理中断。
示例:使用DMA传输数据
void configure_dma() {DMA_SRC_ADDR = (uint32_t)source_buffer;DMA_DST_ADDR = (uint32_t)destination_buffer;DMA_LENGTH = 100; // 传输100字节DMA_CONTROL |= DMA_ENABLE; // 使能DMA
}void dma_isr() {if (DMA_STATUS & DMA_COMPLETE) {// 处理传输完成事件}
}
6.5 使用硬件抽象层(HAL)
许多嵌入式开发框架(如STM32 HAL、ESP-IDF等)提供了硬件抽象层(HAL),简化了硬件资源的访问和控制。
示例:使用STM32 HAL配置GPIO
#include "stm32f4xx_hal.h"void configure_gpio() {GPIO_InitTypeDef GPIO_InitStruct = {0};__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟GPIO_InitStruct.Pin = GPIO_PIN_5;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}void set_gpio_high() {HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
}void set_gpio_low() {HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
6.6 调试和验证
- 使用调试工具:
- 使用JTAG/SWD调试器单步执行代码,观察寄存器和变量的值。
- 打印调试信息:
- 通过串口或调试接口输出调试信息。
- 测试硬件功能:
- 通过简单的测试程序验证硬件功能(如点亮LED、读取按键状态等)。
6.7注意事项
- 硬件手册:
- 仔细阅读硬件手册,了解寄存器的功能和配置方法。
- 时序要求:
- 某些硬件操作需要严格的时序,确保代码满足时序要求。
- 中断优先级:
- 合理配置中断优先级,避免中断嵌套导致的问题。
- 资源冲突:
- 避免多个任务或模块同时访问同一硬件资源,导致冲突。
6.8总结
在嵌入式系统中,硬件资源的访问和控制主要通过以下方式实现:
- 直接读写硬件寄存器。
- 配置硬件模块(如GPIO、UART、定时器等)。
- 使用中断处理异步事件。
- 使用DMA减少CPU负担。
- 借助硬件抽象层(HAL)简化开发。
通过合理使用这些方法,可以高效地控制嵌入式系统的硬件资源,实现复杂的功能。
7. 嵌入式系统中常见的编译器有哪些?请介绍其特点和使用方法。
嵌入式系统中常见的编译器包括:
GCC:开源、多平台支持,适合多种架构。
Clang/LLVM:高性能、模块化设计,适合现代开发。
IAR:商业编译器,高度优化,适合工业应用。
Keil:ARM专用,集成开发环境,适合ARM Cortex-M系列。
ARM Compiler:ARM官方编译器,高度优化。
XC Compiler:Microchip专用,支持PIC和dsPIC系列。
TI Compiler:TI专用,支持MSP430、C2000系列
8. 请简介绍一下Makefile的使用方法和作用。
Makefile
是一个用于自动化构建和管理项目的工具,广泛应用于C/C++等编程语言的开发中。它通过定义规则(rules)来描述如何编译和链接源代码,从而简化了项目的构建过程。以下是 Makefile
的使用方法和作用:
8.1 Makefile 的作用
- 自动化构建:
- 通过
Makefile
,只需运行make
命令即可自动完成编译、链接等任务,无需手动输入复杂的命令。
- 通过
- 依赖管理:
Makefile
可以跟踪文件之间的依赖关系,只重新编译修改过的文件,提高构建效率。
- 多平台支持:
- 通过
Makefile
,可以编写跨平台的构建脚本,适配不同的操作系统和编译器。
- 通过
- 灵活性:
Makefile
支持自定义规则和变量,可以根据项目需求灵活配置。
8.2 Makefile 的基本结构
一个 Makefile
通常由以下部分组成:
- 变量:定义可重用的值(如编译器、编译选项等)。
- 规则:定义如何生成目标文件,包括目标、依赖和命令。
- 伪目标:用于执行特定任务(如清理生成的文件)。
8.3示例:简单的 Makefile
# 定义变量
CC = gcc
CFLAGS = -Wall -O2
TARGET = my_program# 定义规则
$(TARGET): main.o utils.o$(CC) $(CFLAGS) -o $(TARGET) main.o utils.omain.o: main.c$(CC) $(CFLAGS) -c main.cutils.o: utils.c$(CC) $(CFLAGS) -c utils.c# 定义伪目标
clean:rm -f $(TARGET) *.o
这是一个典型的 Makefile
示例,用于编译一个简单的C语言项目。以下是对该 Makefile
的详细解释和用法说明:
8.3.1 定义变量
CC = gcc
CFLAGS = -Wall -O2
TARGET = my_program
CC
:指定编译器为gcc
。CFLAGS
:指定编译选项,-Wall
启用所有警告,-O2
启用优化。TARGET
:指定最终生成的可执行文件名为my_program
。
8.3.2 定义规则
$(TARGET): main.o utils.o$(CC) $(CFLAGS) -o $(TARGET) main.o utils.o
- 目标:
$(TARGET)
(即my_program
)。 - 依赖:
main.o
和utils.o
。 - 命令:使用
gcc
将main.o
和utils.o
链接成可执行文件my_program
。
main.o: main.c$(CC) $(CFLAGS) -c main.c
- 目标:
main.o
。 - 依赖:
main.c
。 - 命令:使用
gcc
编译main.c
,生成目标文件main.o
。
utils.o: utils.c$(CC) $(CFLAGS) -c utils.c
- 目标:
utils.o
。 - 依赖:
utils.c
。 - 命令:使用
gcc
编译utils.c
,生成目标文件utils.o
。
8.3.3 定义伪目标
clean:rm -f $(TARGET) *.o
- 目标:
clean
。 - 命令:删除生成的可执行文件
my_program
和所有目标文件(*.o
)。 - 伪目标:
clean
不生成任何文件,仅用于执行清理任务。
8.3.4 使用方法
-
编译项目:
- 在终端中运行
make
命令,make
会默认执行第一个目标(即$(TARGET)
)。 - 示例:
make
- 这将生成可执行文件
my_program
。
- 在终端中运行
-
清理生成的文件:
- 运行
make clean
命令,删除生成的可执行文件和目标文件。 - 示例:
make clean
- 运行
-
重新编译:
- 如果修改了源代码文件(如
main.c
或utils.c
),只需再次运行make
,make
会自动重新编译修改过的文件。
- 如果修改了源代码文件(如
8.4 Makefile 的使用方法
-
编写 Makefile:
- 在项目根目录下创建一个名为
Makefile
的文件,并定义变量、规则和伪目标。
- 在项目根目录下创建一个名为
-
运行 Makefile:
- 在终端中运行
make
命令,make
会默认执行第一个目标(通常是编译生成可执行文件)。 - 示例:
make
- 在终端中运行
-
指定目标:
- 可以指定要执行的目标。例如,清理生成的文件:
make clean
- 可以指定要执行的目标。例如,清理生成的文件:
-
调试 Makefile:
- 使用
-n
选项可以查看make
将执行的命令,而不实际执行:make -n
- 使用
8.5 Makefile 的常见语法
-
变量定义:
- 变量用于存储可重用的值,使用
=
或:=
定义。 - 示例:
CC = gcc CFLAGS = -Wall -O2
- 变量用于存储可重用的值,使用
-
规则定义:
- 规则由目标、依赖和命令组成。
- 格式:
目标: 依赖命令
- 示例:
main.o: main.c$(CC) $(CFLAGS) -c main.c
-
伪目标:
- 伪目标用于执行特定任务,不生成文件。
- 使用
.PHONY
声明伪目标。 - 示例:
.PHONY: clean clean:rm -f $(TARGET) *.o
-
自动变量:
- 自动变量用于简化规则的编写。常见的有:
$@
:目标文件。$<
:第一个依赖文件。$^
:所有依赖文件。
- 示例:
$(TARGET): main.o utils.o$(CC) $(CFLAGS) -o $@ $^
- 自动变量用于简化规则的编写。常见的有:
-
条件判断:
- 使用
ifeq
和ifneq
进行条件判断。 - 示例:
ifeq ($(OS),Windows_NT)RM = del elseRM = rm -f endif
- 使用
8.6Makefile 的进阶用法
-
多目录项目:
- 对于多目录项目,可以在每个子目录中编写
Makefile
,并在根目录的Makefile
中调用子目录的Makefile
。 - 示例:
SUBDIRS = src liball:for dir in $(SUBDIRS); do \$(MAKE) -C $$dir; \done
- 对于多目录项目,可以在每个子目录中编写
-
依赖生成:
- 使用
gcc -MM
自动生成依赖关系,避免手动维护。 - 示例:
%.d: %.c$(CC) -MM $< > $@include $(SRCS:.c=.d)
- 使用
-
跨平台支持:
- 使用条件判断和变量适配不同的操作系统和编译器。
- 示例:
ifeq ($(OS),Windows_NT)CC = gccRM = del elseCC = clangRM = rm -f endif
8.7总结
Makefile
是自动化构建项目的强大工具,通过定义变量、规则和伪目标,可以简化编译、链接和清理等任务。它的主要特点包括:
- 自动化:通过
make
命令自动执行构建任务。 - 依赖管理:只重新编译修改过的文件,提高效率。
- 灵活性:支持自定义规则和变量,适应不同需求。
掌握 Makefile
的使用方法,可以显著提高开发效率,特别是在大型项目中。
相关文章:
【嵌入式狂刷100题】- 1基础知识部分
准备新开专栏【嵌入式狂刷100题】😶🌫️😶🌫️🤧加油!!!,内容包括 基础知识部分操作系统部分处理器架构部分外设驱动部分通信协议部分存储器管理部分硬件设计部分多媒体部分调试故障排除部分编码开发部…...
【模板】计算几何入门
来源 计算几何基本模板(二维) 目录 基本设置点 向量 Point(Vector)点积(数量积、内积)向量积,叉积两点间距离向量的模单位向量两向量的夹角判断点在直线的哪边逆转角 线 直线表达式Line判断…...
PostgreSQL 数据库中导入大量数据
在 PostgreSQL 数据库中导入大量数据,可根据数据来源和格式选择不同的方法。以下为你详细介绍几种常见的方式: 1. 使用 COPY 命令(适用于本地数据文件) COPY 命令是 PostgreSQL 内置的高效数据导入工具,适合处理本地的数据文件。 步骤 准备数据文件 确保你的数据文件格…...
DeepSeek和Kimi在Neo4j中的表现
以下是2个最近爆火的人工智能工具, DeepSeek:DeepSeek Kimi: Kimi - 会推理解析,能深度思考的AI助手 1、提示词: 你能帮我生成一个知识图谱吗,等一下我会给你一篇文章,帮我从内容中提取关键要素,然后以N…...
xQueueSendToBack的中文释义和裸机调用
如果不在 FreeRTOS 环境下运行,而是裸机环境中实现类似的功能,需要移除 xQueueSendToBack 的依赖,并直接调用 CAN 发送函数。以下是修改后的代码和实现思路: 1. FreeRTOS 中的 xQueueSendToBack 功能 作用:将消息发送…...
2025年- G24-Lc98-217.包含重复(使用hashSet解决)-java版
1.题目描述 2.思路 思路一: 我的想法是直接用集合来判断,如果集合的元素不能添加说明之前已经存在这个元素,也就是发现了重复元素,所以返回false。 补充一: Map、ArrayList的定义和声明 3.代码实现 class Soluti…...
【树莓派驱动验证步骤】
终端操作和输出: 清理项目 adaraspberrypi:~/mt3502 $ make clean make -C /lib/modules/6.6.51rpt-rpi-v8/build M/home/ada/mt3502 clean make[1]: 进入目录“/usr/src/linux-headers-6.6.51rpt-rpi-v8”CLEAN /home/ada/mt3502/Module.symvers make[1]: 离开…...
百度SEO和必应SEO优化方法
如需SEO服务,可以搜索:深圳市信科网络科技有限公司。 一、搜索引擎生态格局:流量入口的重新洗牌 2025 年,中国 PC 端搜索引擎市场正经历戏剧性变革。StatCounter 数据显示,必应凭借 Edge 浏览器的预装优势与 ChatGPT …...
2025年3月AI搜索发展动态与趋势分析:从技术革新到生态重构
025年3月AI搜索发展动态与趋势分析:从技术革新到生态重构 一、行业动态:巨头布局与技术升级 谷歌推出“AI模式”,重新定义搜索体验 谷歌上线全新“AI模式”,集成多模态交互与实时数据能力,用户可通过文本、图片或语音…...
封闭图形个数
0封闭图形个数 - 蓝桥云课 小蓝对蓝桥王国的数字大小规则十分感兴趣。现在,他将给定你n个数a1, a2, ..., an,请你按照蓝桥王国的数字大小规则,将这n数从小到大排序,并输出排序后结果。 输入格式 第一行包含一个整数n࿰…...
VSCode 抽风之 两个conda环境同时在被激活
出现了神奇的(toolsZCH)(base) 提示符,如下图所示: 原因大概是:conda 环境的双重激活:可能是 conda 环境没有被正确清理或初始化,导致 base 和 toolsZCH 同时被激活。 解决办法就是 :conda deactivate 两次…...
Django 生产环境静态文件处理
python manage.py collectstatic 是 Django 提供的一个非常重要的管理命令,用于将项目中的静态文件收集到一个指定的目录中。这在部署 Django 项目时尤其重要,因为静态文件需要被 Web 服务器(如 Nginx 或 Apache)提供服务…...
语法: result=frexp(value, exp);
FREXP()是C语言里的内部函数,根据需要了解。 语法: resultfrexp(value, &exp); 参数: value是一个浮点数; exp是一个有符号的整型数; 返回值: 返回值result是一个浮点数,其有效范围是 0.5(含)到 1.0(不含&…...
ArcGIS Pro 制作风台路径图:从数据到可视化
一、引言 台风,作为自然界极具破坏力的气象现象之一,其路径的精准预测与直观呈现对于防灾减灾工作至关重要。 在数字化时代,借助专业的地理信息系统(GIS)软件,如 ArcGIS Pro,我们能够高效地将…...
#pandas #python#数据标注 pd.crosstab()
题目: device_status.txt 存储了工业互联网平台上收集的设备运行状态数据,数 据中有以下内容: device_id:设备编号,字符串类型,长度为 8 status_time:状态时间,日期时间类型&…...
self Attention为何除以根号dk?(全新角度)
全网最独特解析:self Attention为何除根号dk? 一、假设条件:查询向量和键向量服从正态分布 假设查询向量 q i q_i qi和键向量 k j k_j kj的每个分量均为独立同分布的随机变量,且服从标准正态分布,即:…...
SpringBoot @Scheduled注解详解
Scheduled 是 Spring Framework 中用于实现定时任务的核心注解,能够方便地配置方法在特定时间或周期执行。以下是详细解析: 1. 启用定时任务 在 Spring Boot 中,需在配置类添加 EnableScheduling 注解以启用定时任务支持: Co…...
在大数据开发中spark是指什么?
hello宝子们...我们是艾斯视觉擅长ui设计和前端数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩! 在数字经济蓬勃发展的今天,数据已成为驱动商业决策、科学研究和城市治理的核心燃料。面对…...
从点灯开始的51单片机生活
陵谷纷纭新事改,筑台土石未应迟。 目录 sfr与sbit?不靠定时器的delay_ms延时函数所谓寄存器 sfr与sbit? 这第一课咱们主要来先理解一下sfr与sbit,以下可能是咱们这些新手朋友常见的点灯代码: #include<regx52.h&g…...
AI大模型落地:昆仑技术的东方解法
DeepSeek的横空出世,一举打破“算力封锁”的神话,标志着中国AI企业在AI大模型技术路径上取得彻底突破。 不过,DeepSeek等AI大模型的突破,固然大幅推动AI产业的整体发展,但算力基础设施能否跟上,也将决定未…...
Spring Boot 与 MyBatis Plus 整合 KWDB 实现 JDBC 数据访问
引言 本文主要介绍如何在 IDEA 中搭建一个使用 Maven 管理的 Spring Boot 应用项目工程,并结合在本地搭建的 KWDB 数据库(版本为:2.0.3)来演示 Spring Boot 与 MyBatis Plus 的集成,以及对 KWDB 数据库的数据操作…...
VSCode+arm-none-eabi-gcc交叉编译+CMake构建+OpenOCD(基于STM32的标准库/HAL库)
前言:什么是CMake? Answer:简而言之,CMake是Make的maker。 一、CMake的安装 进入CMake官网的下载地址Get the Software,根据系统安装对应的Binary distributions。 或者在CMake——国内镜像获取二进制镜像安装包。 …...
MarsCode AI实战:利用DeepSeek 快速搭建你的口语学习搭子
资料来源:火山引擎-开发者社区 成品抢先看! 自从MarsCode AI Chat模型全新升级,接入 Deepseek-R1、Deepseek-V3和豆包大模型1.5 三大模型,越来越多朋友注意到了AI编程能给我们带来的无限可能,也开始跃跃欲试想要尝试从…...
导出的使用
一.导出的具体使用步骤 1.在web开发中,导出是很常见的一个功能,当我进行个人项目练习的时候,导出的时候无法控制列宽以及居中样式,后续发现导出插件无法进行修改,整个插件较为简便易懂的同时,对于EX的控制…...
【OCR】总结github上开源 OCR 工具:让文字识别更简单
前言 在数字化的时代,光学字符识别(OCR)技术成为了我们处理文档、图像文字信息的得力助手。它能够将图像中的文字信息转换为可编辑和可处理的文本数据,极大地提高了信息处理的效率。今天,我要给大家介绍一些优秀的开源…...
struts1+struts2项目兼容升级到了spring boot 2.7
原项目比较复杂,集成了各种框架(struts1 struts2 spring3等),趁工作之余练练手,学习一下springboot。大概花了一周时间才调通。 一、调整jar版本,寻找合适的版本。 第一步、首先原项目JDK6,要…...
Odoo 18 中的列表(list) 、表单(Form)、数据透视表、图表视图、看板视图、活动视图、日历视图等综合应用实例
Odoo 18 中的 视图应用实例 在 Odoo 中,视图是用户界面中表示业务对象的重要组成部分。无论您是扩展现有功能还是创建全新的功能,业务对象都至关重要。这些对象通过不同类型的视图向用户展示,而 Odoo 会根据 XML 描述动态生成这些视图。 列…...
单元测试mock
一、背景 现在有A类,B类,C类,A类依赖B类,依赖C类,如果想要测试A类中的某个方法的业务逻辑。A类依赖其他类,则把其他类给mock,然后A类需要真实对象。这样就可以测试A类中的方法。 举例:Ticket类需要调用Flight类和Pas…...
PDF文件转Markdown,基于开源项目marker
首先我们来问下deepseek 为啥要选marker呢 基于深度学习,一看就逼格拉满。搞科研必备,效果应该不会太差。跟其他的阿猫阿狗工具没法比。 看下官网 https://github.com/VikParuchuri/marker 一看头像是个印度佬,自吹——又快又好。…...
mysql中find_in_set()函数用法详解及增强函数
MySQL的 FIND_IN_SET()函数是一种特殊的函数,它主要用于搜索一个字符串在一个逗号分隔的字符串列表中的位置。 函数的基本语法 FIND_IN_SET(str, strlist) 其中,str是你想要查找的字符串,而 strlist是一个包含多个以逗号分隔的字符串的列表…...
深入理解 JavaScript/TypeScript 中的假值(Falsy Values)与逻辑判断 ✨
🕹️ 深入理解 JavaScript/TypeScript 中的假值(Falsy Values)与逻辑判断 在 JavaScript/TypeScript 开发中,if (!value) 是最常见的条件判断之一。它看似简单,却隐藏着语言的核心设计逻辑,也是许多开发者…...
批量合并 PPT 文件,支持合并成单个文件也支持按文件夹合并
合并多个 PPT 为一个 PPT 文档是我们经常会碰到的需求,合并后不仅更容易管理,在某些场景(比如批量打印)下也非常的有用,那当我们需要批量合并多个 PPT 文档地时候,我们有没有比较高效的方法呢?今…...
Java复习
在开篇前首先申明一下,本文虽不够系统,但复习够用,尤其是快速回忆( •̀ ω •́ )✧与提问。 主打一个速度。 本文将会从Java的基础语法、面向对象、API、字符串、集合、进阶...等六方面讲起。 一、Java的基础语法: 1、Java入门…...
keepalived+nginx+tomcat高可用
1.要求 角色主机名软件IP地址用户client192.168.72.90keepalivedvip192.168.72.100mastermasterkeepalived, nginx192.168.72.30backupbackupkeepalived, nginx192.168.72.32webtomcat1tomcat192.168.72.41webtomcat2tomcat192.168.72.42 1.搭建Tomcat 1.1下载jdk wget http…...
RK3568 Android11 sh366006驱动
sh366006.c /* 谁愿压抑心中怒愤冲动咒骂这虚与伪与假从没信要屈膝面对生命纵没有别人帮一生只靠我双手让我放声疯狂叫囔今天的他 呼风可改雨不可一世太嚣张 --《不可一世》Beyond */ #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h…...
实现分布式锁需要考虑哪些问题?
🔒 什么是分布式锁? 分布式锁是在分布式系统中控制共享资源访问的机制,用于解决高并发场景下数据不一致、操作冲突等问题。核心目标是保证跨进程 / 跨节点的互斥性,常见实现方案包括:数据库锁、Redis 锁、ZooKeeper 锁…...
【UI设计】一些好用的免费图标素材网站
阿里巴巴矢量图标库https://www.iconfont.cn/国内最大的矢量图标库之一,拥有 800 万 图标资源。特色功能包括团队协作、多端适配、定制化编辑等,适合企业级项目、电商设计、中文产品开发等场景。IconParkhttps://iconpark.oceanengine.com/home字节跳动…...
mysql-大批量插入数据的三种方式和使用场景
1.批量插入三种方式 INSERT INTO … SELECTINSERT INTO … VALUES (…)LOAD DATA INFILE ‘/path/to/datafile.csv’ INTO TABLE table_name 2.批量插入 2.1 INSERT INTO … SELECT 用途:从另一个表中选择数据并插入到目标表中。 语法示例: INSERT …...
创建自己的github.io
1、创建GitHub账号 GitHub地址:https://github.com/ 点击Sign up创建账号 如果已创建,点击Sign in登录 2、创建仓库 假设Owner为username,则Repository name为username.github.io说明: 1、Owner为用户名 2、Repository name为仓…...
Oracle 常用语法汇总
系列文章目录 本文对Oracle 常用的语法进行汇总 文章目录 系列文章目录一、Oracle 表&表字段操作:1.1 DDL语句(数据定义语言)Create、Alter、Drop、Truncate:1.1.1 建表:建表:注释COMMENT :表中字段的约束:表中字…...
java小白日记38(集合-List)
List接口基本介绍 List接口是collection接口的子接口 (1)List集合类中元素有序(即添加顺序和取出顺序一致)、且可以重复 (2)List集合中的每个元素都有其对应的顺序索引,即支持索引 …...
高能ISP模块功能说明
先看一些常见缩写: BPS:Bayer processing segment CPP:Camera post processor DE:Detailed enhancement EIS:Electronic image stabilization IFE:Image front-end engine IPE:Image-proc…...
单臂路由实验
单臂路由实验 文章目录 单臂路由实验单臂路由简介工作原理优点与缺点应用场景 实验拓扑实验需求:实验步骤:1.PC 配置 IP 地址2.PC3 属于 Vlan10,PC4 属于 Vlan20,配置单臂路由实现 Vlan10 和 Vlan20 三层互通2.1.在 SW2 上创建 Vl…...
SpringMVC全局异常处理机制
异常处理机制 异常处理的两种方式: 编程式异常处理:是指在代码中显式地编写处理异常的逻辑。它通常涉及到对异常类型的检测及其处理,例如使用 try-catch 块来捕获异常,然后在 catch 块中编写特定的处理代码,或者在 f…...
UDS诊断、ECU刷写、自动化测试、车联网测试、DTC故障注入测试、坏境测试、可靠性测试、压力测试、性能测试等
每日直播时间:(直播方式:腾讯会议) 周一到周五:20:00-23:00 周六与周日:9:00-17:00 向进腾讯会议学习的,可以关注我并后台留言 直播内容ÿ…...
C++的常用容器嵌套
在 C 中,数据结构之间的嵌套是非常常见的,尤其是在处理复杂数据时。以下是几种最常用的数据结构嵌套方式及其典型应用场景的总结: 1. std::vector 嵌套 std::vector 定义:std::vector<std::vector<T>>。用途…...
Mac - Cursor 配置 + GPT 4.0/4.5/o1/o3/Deepseek Api 使用
前言 新换了电脑,所以需要新配置一些环境。已经安装好了Goland,但近期可能有GoJava前端的需求,所以使用Cursor。 除去学校各种奇奇怪怪,这已经是一年多来配置的第4台Windows和第四台Mac的Golang环境了。。。且是自己工作外买的第…...
【数据挖掘】Python基础环境安装配置
【数据挖掘】Python基础环境安装配置 一、摘要二、安装Python3.13.2三、安装Jupyter Notebook四、安装Numpy和Pandas以及matplotlib五、安装scikit-learn库和seaborn库 一、摘要 本文主要介绍如何在Windows上安装Python3.13.2,然后基于该Python版本安装Jupyter not…...
详解string类+迭代器
迭代器 概念:在 C 中,迭代器是访问容器(如数组、列表、向量、字符串等)元素的一种方式。迭代器提供了一种统一的接口,使得你可以使用相同的代码来遍历不同类型的容器。迭代器本质上是一个指针或者指针的封装࿰…...
OpenCV DNN 模块使用指南
OpenCV DNN 模块使用指南 一、模块概述 OpenCV 的 DNN(深度神经网络)模块为开发者提供了强大的深度学习功能,能够加载并运行多种格式的预训练深度学习模型。此模块广泛应用于图像分类、目标检测、语义分割等众多计算机视觉任务。接下来&…...