STM32基础知识回顾
你必须深入掌握。✅ 一、什么是枚举?枚举是一种。
基础知识回顾










一、GPIO(General Purpose Input/Output)通用输入输出
✅ GPIO 输出(Output)
-
指 MCU 控制引脚电平输出:高电平(如 3.3V)或低电平(0V)。
-
常用于:
-
控制 LED 灯亮灭
-
驱动继电器
-
控制蜂鸣器
-
控制电机(结合驱动芯片)
-
STM32 中设置:
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
✅ GPIO 输入(Input)
-
指 MCU 检测外部电平的状态。
-
常用于:
-
读取按键是否按下(高/低电平)
-
接收传感器信号(如红外模块的检测输出)
-
-
STM32 中设置:
-
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 悬空输入GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;//悬空输入
二、DO(Digital Output)数字输出
-
“DO” 是某些模块或传感器手册里的术语,意思是“数字输出”。
-
一般模块提供 DO 引脚,输出的是 数字信号(高/低电平),适合连接到 MCU 的 GPIO 输入引脚读取。
✅ 举例:
-
红外避障模块:
-
检测到障碍 → DO 输出低电平
-
没检测到 → DO 输出高电平
-
-
你可以这样读取:
-
if(GPIO_ReadInputDataBit(GPIO,GPIO_Pin_1)==0) { //有障碍 }
三、AO(Analog Output)模拟输出
-
“AO” 表示“模拟信号输出”,输出是连续电压,不是只有高低电平。
-
需要 MCU 具备**ADC(模拟转数字)**功能来读取。
✅ 举例:
-
光敏电阻模块(带 AO 引脚):
-
白天电压高,晚上电压低
-
-
STM32 读取方式是 ADC:
-
u16 val = ADC_GetConversionValue(ADC1);//获取模拟值(0~4095)
✅ 总结对比表
| 项目 | 名称全称 | 电平类型 | 接入 MCU 的方式 | 典型用途 |
|---|---|---|---|---|
| GPIO Output | 通用输出 | 数字(高/低) | MCU 控制输出 | 控制 LED、继电器、电机等 |
| GPIO Input | 通用输入 | 数字(高/低) | MCU 读取输入 | 按键检测、开关检测 |
| DO | 数字输出 | 数字(高/低) | MCU 用 GPIO 输入读取 | 红外、烟雾、震动检测模块等 |
| AO | 模拟输出 | 模拟电压(如 0~3.3V) | MCU 用 ADC 通道采集 | 温度、光强、气体浓度类传感器 |
STM32指针
✅ 1. 什么是“指针”?
指针就是保存变量地址的变量。
在 STM32 中,地址代表着 寄存器、IO口、外设控制单元 —— 所以用指针就能直接操作硬件本体!
✅ 2. 为什么 STM32 中广泛使用指针?
| 原因 | 说明 |
|---|---|
| 操作寄存器 | STM32 寄存器(如 GPIOA->ODR)本质是“固定地址”,必须通过指针操作 |
| 高效率 | 访问内存、传递数据更快、更节省资源 |
| 访问外设 | ADC、USART、TIM 等外设驱动库,大量使用结构体指针指向外设基地址 |
| 内存操作 | 在 FreeRTOS 动态内存分配、DMA 传输、缓冲区管理中频繁使用指针 |
✅ 3. STM32 中常见指针使用案例
🎯 示例1:GPIO 寄存器地址操作(裸机常见)
#define LED1 (*(volatile unsigned int *)0x4001080C)
// 解释:强制将地址0x4001080C(GPIOC ODR)转换为指向 unsigned int 的指针,并取值
LED1 = 0x00000020; // 设置 PC5 输出高电平
🎯 示例2:结构体指针操作寄存器(STM32官方库用法)
GPIOA->ODR |= (1 << 5); // 置高 PA5(点亮 LED)
GPIOA 是 GPIO_TypeDef 类型的结构体指针,预定义在 stm32f10x.h 中,内部封装了 GPIO 的所有寄存器地址。
🎯 示例3:函数传递指针(高效传参)
void send_data(uint8_t* buffer, uint16_t len) {
while (len--) {
USART_SendData(USART1, *buffer++);
}
}
传递数组时用指针效率更高,适用于 DMA、UART、SPI 数据发送等。
🎯 示例4:动态内存分配与指针(高级用法)
uint8_t* data = (uint8_t*)malloc(100); // 分配100字节空间
✅ 4. STM32 中常用指针类型总结
| 类型 | 作用 | 示例 |
|---|---|---|
uint8_t * |
指向字节型数据(8位) | 串口接收缓冲区指针 |
uint16_t * |
指向半字型数据(16位) | ADC结果数组 |
volatile uint32_t * |
指向 32位寄存器,防止编译优化 | 寄存器直接操作 |
结构体指针 |
操作外设寄存器组 | GPIOA->CRL, TIM2->CNT 等 |
✅ 5. 指针关键概念提醒
| 概念 | 说明 |
|---|---|
* 解引用 |
取出指针所指地址的内容(如 *ptr = 1) |
& 取地址 |
得到变量的地址(如 &a) |
-> 结构体指针访问成员 |
如 GPIOA->ODR,等价于 (*GPIOA).ODR |
const |
指针不可改值或地址,如 const uint8_t*、uint8_t* const |
volatile |
禁止编译器优化,一定要加在寄存器操作中 |
✅ 6. STM32指针+结构体:寄存器访问的底层原理
以 GPIO 为例:
#define GPIOA_BASE (0x40010800UL)
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
-
GPIO_TypeDef是结构体,包含CRL,CRH,IDR,ODR等寄存器 -
这句宏就是将
GPIOA_BASE强制转换为指针并访问内部成员
✅ 7. 指针错误常见场景
| 错误类型 | 示例 | 问题描述 |
|---|---|---|
| 空指针使用 | uint8_t* ptr = NULL; *ptr = 1; |
程序崩溃 |
| 未初始化指针 | uint8_t* p; *p = 1; |
未分配内存 |
| 数组越界 | for(i=0; i<=len; i++) *(p+i) |
内存越界 |
忘记加 volatile |
uint32_t *reg = 0x4001100C; |
编译器优化掉寄存器读写 |
结构体
✅ 一、结构体是啥?一句话理解:
结构体是多个不同类型变量的集合,用于描述一个“整体”对象。
举个通俗例子:我们要描述一个“学生”,他有名字、年龄、成绩等。用结构体把这些信息打包在一起,逻辑清晰,操作方便。
✅ 二、结构体基本语法
// 定义结构体类型
struct Student {
char name[20];
int age;
float score;
};
// 声明变量
struct Student stu1;
🔎 你可以把 struct Student 看作是自定义的“数据模板”。
✅ 三、结构体变量的使用
strcpy(stu1.name, "张三"); // 字符串复制
stu1.age = 20; // 成员赋值
stu1.score = 92.5; // 成员赋值
成员访问用 . 操作符。
✅ 四、结构体指针
struct Student* p = &stu1;
printf("姓名:%s\n", p->name); // 使用 -> 访问成员
嵌入式开发中大量使用结构体指针,比如 GPIOA->ODR。
✅ 五、结构体类型重命名(推荐写法)
为了简洁,我们常用typedef方式:
typedef struct {
char name[20];
int age;
float score;
} Student;
Student stu2; // 不用再写 struct
✅ 六、结构体嵌套使用
typedef struct {
int year;
int month;
int day;
} Date;
typedef struct {
char name[20];
Date birthday; // 结构体中嵌套结构体
float score;
} Student;
✅ 七、结构体数组
Student class1[50]; // 一个有 50 个学生的数组
class1[0].age = 18;
strcpy(class1[1].name, "李四");
✅ 八、结构体在STM32中的典型应用
STM32 的寄存器访问就是基于结构体+指针:
GPIOA->ODR |= (1 << 5); // 设置 PA5 高电平
这是因为 GPIOA 是一个结构体指针:
typedef struct
{
__IO uint32_t CRL;
__IO uint32_t CRH;
__IO uint32_t IDR;
__IO uint32_t ODR;
// ...
} GPIO_TypeDef;
__IO 是 volatile 关键字宏定义,用来防止编译器优化寄存器访问。
✅ 九、结构体作为函数参数(常见于驱动代码)
void printStudent(Student s) {
printf("Name: %s\n", s.name);
}
// 或使用指针更高效
void printStudentPtr(Student* s) {
printf("Name: %s\n", s->name);
}
✅ 十、结构体 vs 联合体 vs 枚举(简要对比)
| 类型 | 说明 | 用途 |
|---|---|---|
| 结构体 struct | 多成员共享不同内存 | 组合多个变量 |
| 联合体 union | 所有成员共用一块内存 | 节省空间,传感器数据封包 |
| 枚举 enum | 一组具名整数常量 | 状态机、命令字、类型标识 |
✅ 十一、实际开发建议
-
✅ 写结构体推荐用
typedef,命名更清晰 -
✅ 结构体中可以嵌套数组、指针、结构体
-
✅ 多个模块信息封装成结构体,便于管理
-
✅ STM32 各外设驱动,基本全是结构体+指针组合
-
✅ 如果你在做 STM32 项目,例如封装一个
按键模块或传感器数据结构,结构体是组织数据的最佳方式
✅ 示例:STM32 项目中结构体组织按键状态
typedef struct {
uint8_t key_id;
uint8_t is_pressed;
uint32_t last_press_time;
} KeyInfo;
KeyInfo key1 = {1, 0, 0};
void KeyScan(KeyInfo* k) {
if (ReadGPIO(k->key_id) == 0) {
k->is_pressed = 1;
k->last_press_time = millis();
}
}
枚举
**枚举(enum)**是 C 语言中非常实用的一种数据类型,在嵌入式开发、状态机设计、通信协议、自定义命令字等场景中极其常见,你必须深入掌握。
✅ 一、什么是枚举?
枚举是一种自定义类型,用于表示一组具名的整型常量,让代码更清晰、逻辑更明确。
举个例子:
enum Color {
RED, // 0
GREEN, // 1
BLUE // 2
};
就是在定义一个“颜色类型”,每个颜色对应一个整数值,但有名字更有意义!
✅ 二、基本语法
enum Weekday {
MONDAY, // 0
TUESDAY, // 1
WEDNESDAY, // 2
THURSDAY, // 3
FRIDAY = 10,// 自定义值,从10开始
SATURDAY, // 11
SUNDAY // 12
};
默认从 0 开始,后续自动加一。你也可以指定初始值。
✅ 三、枚举变量使用方式
enum Weekday today;
today = FRIDAY;
if (today == SUNDAY) {
printf("休息!");
}
虽然本质是整数,但使用名字让代码语义清晰。
✅ 四、推荐写法(typedef 简洁定义)
typedef enum {
OFF = 0,
ON = 1
} SwitchState;
SwitchState led_state = ON;
这样你使用时就可以省略 enum,代码更整洁。
✅ 五、在 STM32/嵌入式中的典型应用场景
✅ 1. 状态机状态定义
typedef enum {
IDLE,
START,
RUNNING,
ERROR
} SystemState;
SystemState sys_state = IDLE;
用于状态机控制:
switch(sys_state) {
case IDLE: // 初始化或等待输入
break;
case START: // 启动某个流程
break;
case RUNNING: // 正在工作
break;
case ERROR: // 出错处理
break;
}
✅ 2. 指令类型、传感器状态等
typedef enum {
CMD_NONE,
CMD_START,
CMD_STOP,
CMD_RESET
} CommandType;
typedef enum {
SENSOR_OK,
SENSOR_FAULT,
SENSOR_DISCONNECTED
} SensorStatus;
✅ 3. 与宏配合增强可读性
#define LED_ON 1
#define LED_OFF 0
typedef enum {
LED1,
LED2,
LED3
} LedType;
void ControlLed(LedType led, uint8_t state);
✅ 六、枚举 vs 宏 vs 结构体
| 特性 | 宏定义 (#define) |
枚举 (enum) |
结构体 (struct) |
|---|---|---|---|
| 功能 | 常量 | 有名字的常量组 | 数据集合 |
| 是否可调试 | ❌ 不可调试 | ✅ 可调试 | ✅ 可调试 |
| 类型安全 | ❌ | ✅ | ✅ |
| 表达清晰度 | 一般 | 高 | 高 |
| 使用场景 | 单一常量 | 状态、类型分类 | 数据打包 |
✅ 七、枚举背后是整数!
enum Level {
LOW = 0,
MID = 1,
HIGH = 2
};
printf("%d", HIGH); // 输出 2
你可以像整数一样进行比较、赋值、数组索引等操作。
✅ 八、进阶:结合结构体管理状态系统
typedef enum {
MODE_INIT,
MODE_IDLE,
MODE_WORK,
MODE_SLEEP
} WorkMode;
typedef struct {
uint8_t id;
WorkMode mode;
uint16_t temperature;
} Device;
Device dev = { .id = 1, .mode = MODE_IDLE, .temperature = 0 };
这在 STM32 项目中非常常见,尤其在多模块、复杂逻辑系统中。
✅ 九、常见错误
| 错误 | 示例 | 原因 |
|---|---|---|
忘记指定 typedef |
enum Mode m = 1; |
用法繁琐,不推荐 |
| 名称冲突 | enum LED { ON, OFF }; 与宏重名 |
建议使用独立命名空间,如 LED_ON, LED_OFF |
| 数值偏移不清楚 | enum CMD { A=2, B, C }; |
不清楚 B=3, C=4,需注意自增 |
✅ 总结一句话:
枚举是让你的代码“带名字的整数”,用于分类、状态管理、命令控制等场景,是 STM32 工程中不可或缺的核心工具。
STM32其他外设
✅ STM32常用外设总览(建议收藏)
| 外设模块 | 作用 | 常用功能 | 项目举例 |
|---|---|---|---|
| GPIO | 通用输入输出 | 控制LED、读取按键、电平输入输出 | 智能门锁、LED跑马灯 |
| EXTI | 外部中断 | 按键中断触发、传感器报警 | 烟雾报警、红外检测 |
| ADC | 模拟信号采集 | 电压采集、传感器读取 | 光敏/温湿度传感器 |
| DAC | 模拟信号输出 | 波形生成、音频输出 | 简易音频输出、模拟电压控制 |
| TIM | 定时器 | 延时、PWM、测频、编码器输入 | 电机驱动、舵机控制 |
| PWM | 脉冲宽度调制 | 调光、调速、舵机控制 | LED调光、风扇速度控制 |
| USART/UART | 串口通信 | 数据通信、模块串口控制 | 蓝牙通信、调试信息打印 |
| SPI | 串行外设接口 | OLED、Flash、传感器通信 | SPI OLED屏、FLASH存储 |
| I2C | 串行通信总线 | EEPROM、OLED、温湿度模块 | DHT12、I2C OLED |
| RTC | 实时时钟 | 时钟显示、闹钟功能 | 智能钟表、定时开关 |
| DMA | 内存与外设高速搬运 | 提高效率,释放CPU | 图像数据传输、ADC加速 |
| NVIC | 中断控制器 | 中断优先级管理 | 多中断协调处理 |
| WDG | 看门狗 | 防止程序死机 | 系统自动复位保护 |
| FLASH | 片上存储器 | 参数保存 | 保存配置或解锁记录 |
| SysTick | 系统定时器 | 系统节拍(ms) | delay函数、任务调度 |
| RCC | 时钟控制 | 外设/系统时钟配置 | 时钟优化、低功耗控制 |
| USB | 通用串行总线 | USB通信 | 虚拟串口、U盘 |
| CAN | 工业级通信协议 | 车辆/工业设备通信 | 电动车控制、CAN网关 |
✅ 1. EXTI —— 外部中断
🎯 用途:
-
当 GPIO 口电平发生变化时,触发中断服务函数
-
实现响应快、低功耗唤醒
💡 实例:
按键中断、PIR人体红外传感器触发、RFID刷卡通知
📌 配置流程:
-
配置引脚为输入
-
配置 EXTI 线路
-
设置中断优先级
-
编写中断服务函数(
void EXTIx_IRQHandler(void))
✅ 2. ADC —— 模拟信号采集
🎯 用途:
采集模拟电压,如光敏、温湿度、气体传感器等
💡 实例:
电池电压检测、环境监测、ADC电位器
📌 配置流程:
-
开启 ADC 时钟
-
配置采样通道和分辨率
-
启动转换,读取值
-
可选 DMA 加速
📌 读取范围:
STM32F1 ADC 12位 → 04095(对应 03.3V)
✅ 3. TIM+PWM —— 定时与脉宽输出
🎯 用途:
-
精确延时、定时触发(定时器)
-
控制 LED 亮度、电机速度(PWM)
💡 实例:
风扇调速、舵机、RGB灯光
📌 TIM 应用方向:
| 模式 | 用途 |
|---|---|
| 基本定时 | 毫秒延时 |
| 输出比较 | PWM 控制、周期输出 |
| 输入捕获 | 测频、测速 |
| 编码器模式 | 编码器测量电机转动 |
✅ 4. USART/UART —— 串口通信
🎯 用途:
-
串口调试、外设通信(蓝牙、GPS、WiFi)
-
通信模块控制(JDY-31、HC-05、ESP8266)
💡 实例:
蓝牙接收手机指令、串口打印调试、与人脸模块通信
📌 常见配置:
-
波特率(9600, 115200)
-
接收中断 / DMA
-
串口重映射(AFIO)
✅ 5. SPI —— 高速串行通信
🎯 用途:
-
与 OLED、Flash、NRF24L01、SD卡 等设备通信
💡 实例:
驱动 SPI OLED 显示图像,读写 SPI Flash
📌 接线:
| 引脚 | 名称 | 说明 |
|---|---|---|
| SCK | 时钟线 | |
| MOSI | 主设备输出/从设备输入 | |
| MISO | 主设备输入/从设备输出 | |
| CS | 片选信号 |
✅ 6. I2C —— 多设备总线通信
🎯 用途:
-
简化连线,与传感器/模块进行通信
-
支持多个设备挂载在同一总线上(唯一地址)
💡 实例:
接入 OLED、AT24C02、DHT12、MPU6050 等模块
📌 接线:
只需要两根线:SCL(时钟)、SDA(数据)
✅ 7. RTC —— 实时时钟
🎯 用途:
-
保持年月日时间、设定闹钟
-
使用外部低速晶振 LSE 保证掉电不丢失时间
💡 实例:
智能门锁记录时间、蔬菜烘干机自动定时、智能衣柜紫外消毒
✅ 8. DMA —— 内存外设搬运器
🎯 用途:
-
提高数据传输效率,不占用 CPU
-
常用于 ADC+DMA、SPI+DMA、UART+DMA
💡 实例:
音频数据、图像帧缓存、ADC 采样波形
✅ 9. WDG —— 看门狗(程序自恢复机制)
🎯 用途:
-
防止程序死循环或卡死,自动复位
-
两种类型:独立看门狗 IWDG、窗口看门狗 WWDG
💡 实例:
工业级稳定控制、长时间运行系统保护
✅ 10. SysTick —— 系统节拍定时器
🎯 用途:
-
实现毫秒级定时(配合
delay_ms、FreeRTOS)
数组
✅ 1. STM32 里的数组
📌 什么是数组?
数组是 一组相同类型变量的集合,在内存中连续存储,方便统一管理和访问。
int a[5] = {1, 2, 3, 4, 5};
你可以通过下标访问:
a[0] = 10; // 修改第一个元素
printf("%d", a[2]); // 输出第3个元素:3
🔧 在 STM32 中数组的应用场景:
| 场景 | 示例 |
|---|---|
| 存储ADC采样数据 | uint16_t adc_buf[100]; |
| OLED图像数据缓存 | uint8_t oled_buf[1024]; |
| USART串口接收缓冲区 | uint8_t uart_rx[128]; |
| 按键状态集合 | uint8_t key_status[4]; |
✅ 2. CRL、CRH、IDR、ODR 是什么?
它们是 GPIO 寄存器(register) 的组成部分,用于配置和操作 IO 口的行为。
STM32 中每个 GPIO 端口(如 GPIOA、GPIOB)都对应一个寄存器结构体:
typedef struct
{
__IO uint32_t CRL; // 配置低8位IO口(PIN0~PIN7)
__IO uint32_t CRH; // 配置高8位IO口(PIN8~PIN15)
__IO uint32_t IDR; // 输入数据寄存器(读取IO电平)
__IO uint32_t ODR; // 输出数据寄存器(设置IO输出电平)
// ...
} GPIO_TypeDef;
📌 各成员功能:
| 名称 | 作用 | 说明 |
|---|---|---|
| CRL (Config Reg Low) | 设置 PIN0~7 的输入/输出模式 | 比如设置 PA0 为输出 |
| CRH (Config Reg High) | 设置 PIN8~15 的模式 | 比如设置 PA13 为浮空输入 |
| IDR (Input Data Reg) | 读取当前 GPIO 电平 | 读按键、传感器输入状态 |
| ODR (Output Data Reg) | 设置输出电平 | 点亮 LED,控制继电器 |
✅ 示例:
GPIOA->ODR |= (1 << 5); // 设置 PA5 为高电平
uint8_t val = GPIOB->IDR & (1 << 1); // 读取 PB1 输入状态
✅ 3. CNT 是什么?
这是 定时器(TIM)模块的计数器寄存器。
TIM2->CNT
📌 功能说明:
-
CNT用于存储当前定时器的计数值 -
你可以读取它来获得计时/测速/编码器位置信息
✅ 示例应用:
uint16_t timer_val = TIM2->CNT; // 获取定时器当前值
✅ 4. 什么是寄存器(Register)?
在 STM32 中,寄存器是 MCU 芯片内部的一段特殊内存,用于控制外设行为。
🔧 举例:
-
想设置 LED 开关,你不是直接操作 LED,而是去写 GPIO 的 ODR 寄存器
-
想设置 USART 波特率,你要去写 USART 的 BRR 寄存器
🧠 通俗类比:
寄存器就像一个“开关面板”,STM32 芯片内部有成百上千个这样的“按钮”,你控制这些按钮,才能让 LED亮/电机转/数据发/屏幕显。
✅ 5. 什么是动态内存(Dynamic Memory)?
在 C 语言中,有两种内存分配方式:
| 类型 | 特点 | 示例函数 | 生命周期 |
|---|---|---|---|
| 静态内存 | 编译时分配 | 全局变量、数组 | 程序运行期间 |
| 动态内存 | 运行时手动申请 | malloc() free() |
手动释放,否则内存泄漏 |
✅ 示例:
int* p = (int*)malloc(10 * sizeof(int)); // 分配 10 个 int 空间
p[0] = 123;
free(p); // 手动释放内存
⚠️ 注意:
STM32中默认不推荐大量使用 malloc/free,因为:
-
内存很小(一般只有几十 KB)
-
容易内存碎片
-
不适合实时系统(FreeRTOS除外)
| 占位符 | 含义 |
|---|---|
%d |
十进制整数(int) |
%f |
浮点数 |
%c |
字符(char) |
%s |
字符串(char*) |
%x |
十六进制输出 |
%% |
输出一个百分号 % |
✅ 总结回顾
| 概念 | 解释 | 示例 |
|---|---|---|
| 数组 | 连续同类型变量集合 | int a[5]; |
| CRL/CRH | 设置 GPIO 口模式 | GPIOA->CRL = ... |
| IDR/ODR | GPIO 输入输出 | val = GPIOA->IDR; |
| CNT | 定时器当前值 | TIM2->CNT |
| 寄存器 | 控制 MCU 外设的地址接口 | USART1->DR |
| 动态内存 | 程序运行时分配内存 | malloc() |
%s |
格式符:字符串 | printf("str: %s", str); |
✅ 一、/ 是“除法运算符”(division)
📌 基本含义:
int a = 10 / 3; // 结果是 3(整数除法,舍去小数部分)
如果你除的是两个整数,结果还是整数,小数部分会被截断(向零舍弃)。
🔍 示例:
int x = 13 / 4; // 结果是 3(不是 3.25)
float y = 13.0 / 4; // 结果是 3.25
✅ 二、% 是“取模运算符”(modulo,求余数)
📌 基本含义:
int a = 10 % 3; // 结果是 1
也就是说,a = 10 除以 3 的余数
🔍 示例:
int x = 13 % 4; // 结果是 1,因为 13 = 3×4 + 1
int y = 20 % 5; // 结果是 0,因为能整除
✅ 三、取模 % 的实际用途非常广!
✅ 1. 控制“循环步进”或“周期性计数”
int i = 0;
i = (i + 1) % 4; // i 从 0 到 3 循环变化
📌 用于:
-
状态机 0~3 状态轮换
-
限制数组下标不越界(如环形缓冲区)
✅ 2. 判断是否能整除
if (num % 2 == 0) {
// num 是偶数
}
📌 用于:
-
判断奇偶
-
判断 10 的倍数、100 的倍数
✅ 3. 控制 LED 闪烁频率
if (tick % 500 == 0) {
LED_Toggle();
}
📌 每 500 个时钟节拍闪烁一次
✅ 四、组合应用:除法 + 取模
int time = 3661; // 3661 秒 = 1 小时 1 分钟 1 秒
int hours = time / 3600; // 1 小时
int minutes = (time % 3600) / 60; // 1 分钟
int seconds = time % 60; // 1 秒
✅ 五、注意点(⚠️常见误区)
| 误区 | 说明 |
|---|---|
% 不能用于浮点数 |
表达式如 5.5 % 2.0 是错误的 |
/ 整数除法可能丢失精度 |
5 / 2 = 2,不是 2.5 |
% 结果与符号有关(负数) |
-5 % 3 结果是 -2,不是 1 |
✅ 六、应用于 STM32 项目的例子
🎯 1. LED 每秒闪烁一次
if (SysTick_Counter % 1000 == 0) { // 每 1000ms 触发
Toggle_LED();
}
🎯 2. 环形缓冲区写入接收数据
uint8_t buffer[64];
uint8_t write_index = 0;
void USART1_IRQHandler() {
buffer[write_index] = USART1->DR;
write_index = (write_index + 1) % 64; // 防止越界
}
✅ 七、总结对比
| 运算符 | 名称 | 功能 | 示例 | 结果 |
|---|---|---|---|---|
/ |
除法 | 整数或小数相除 | 13 / 4 |
3(整数) |
% |
取模 | 整数余数 | 13 % 4 |
1 |
✅ 结语
/%是组合使用频率极高的两个运算符,不仅用于算法计算,还被广泛用于状态控制、数据帧拆分、环形数组等嵌入式工程中。

USE_STDPERIPH_DRIVER
OD:开漏输出模式
PP:推挽模式
更多推荐



所有评论(0)