工程目录结构1
任务调度器 / 微秒延时函数 / 串口重定向 代码按特性放到对应分层文件中。
STM32F429IGTx/ // STM32F429IGTx工程主目录(CubeMX生成)
│ .mxproject // CubeMX工程配置文件(记录CubeMX版本/配置)
│ STM32F429IGTx.ioc // CubeMX核心配置文件(引脚/时钟/外设初始化)
│
├── Doc/ // 项目级文档目录(独立于代码)
│ README.md // 项目及代码简介文档(纯文本说明)
│
├── Core/ // 工程核心层(用户业务代码+底层初始化)(CubeMX生成,用户可补充)
│ ├── Inc/ // Core层头文件目录
│ │ main.h // 主函数头文件(全局变量/函数声明)
│ │ stm32f4xx_it.h // 中断服务函数头文件(声明EXTI/TIM/UART等中断函数)
│ │ usart.h // USART串口初始化头文件(CubeMX生成)
│ │
│ └── Src/ // Core层源文件目录
│ main.c // 工程入口文件(main函数+主循环,需调用UserCode的MainLoop())
│ stm32f4xx_it.c // 中断服务函数实现(EXTI/TIM/UART等中断处理)
│ system_stm32f4xx.c // STM32F4系统时钟配置(CubeMX生成,主频初始化)
│ usart.c // USART串口初始化实现(CubeMX生成,可添加串口收发逻辑)
│
├── Drivers/ // 标准驱动库(CubeMX生成)
│ ├── CMSIS/ // 内核相关文件(core_cm4.h、启动文件等)
│ └── STM32F4xx_HAL_Driver/ // 官方HAL库(含外设驱动.c/.h文件)
│
├── MDK-ARM/ // Keil MDK工程目录(编译/调试配置)
│ │ EventRecorderStub.scvd // 事件记录器配置(调试用)
│ │ JLinkSettings.ini // JLink调试器配置(下载/仿真)
│ │ startup_stm32f429xx.s // 启动文件(栈/堆配置,中断向量表)
│ │ STM32F429IGTx.uvguix.miaol // Keil用户界面配置(窗口布局等)
│ │ STM32F429IGTx.uvoptx // Keil工程选项配置(编译/链接/调试)
│ │ STM32F429IGTx.uvprojx // Keil工程核心文件(双击打开工程)
│ │
│ ├── DebugConfig/ // 调试配置目录
│ ├── RTE/ // RTE(运行时环境)配置
│ └── STM32F429IGTx/
│ STM32F429IGTx.hex // 编译生成的HEX文件(烧录到芯片)
│
└── UserCode/ // 自定义代码目录(对应分层设计)
├── COM/ // 通用公共层
│ base.h // 跨平台基础类型、位操作/数学宏、寄存器访问工具
│ cfg.h // 全局配置中心(delay/uart/scheduler的开关、参数)
│ // ...其他公共层代码
│
├── APP/ // 应用层
│ main_loop.c // 主循环+业务任务(LED翻转、串口打印)
│ main_loop.h // 主循环接口声明(MainLoop函数)
│ // ...其他应用层代码
│
├── SRV/ // 服务层
│ scheduler.c // 任务调度器实现(初始化/创建任务/运行调度)(依赖cfg.h)
│ scheduler.h // 调度器接口声明
│ // ...其他服务层代码
│
├── DRV/ // 驱动层
│ delay.c // 延时驱动实现(依赖cfg.h配置)
│ delay.h // 延时驱动接口(DelayUS/DelayMS)
│ uart_stdio.c // 串口重定向驱动(实现printf/scanf)(依赖cfg.h)
│ // ...其他驱动层代码
│
└── HAL/ // 硬件抽象层
mcu.c // MCU初始化(如TIM6中断/NVIC配置)+ 中断回调
mcu.h // MCU抽象接口声明(McuInit函数)
// ...其他HAL层代码
工程目录结构2
任务调度器 / 微秒延时函数 / 串口重定向 代码放到 公共层,这样有破坏了一点分层思想,不过公用的在一起方便了管理。
STM32F429IGTx/ // STM32F429IGTx工程主目录(CubeMX生成)
│ .mxproject // CubeMX工程配置文件(记录CubeMX版本/配置)
│ STM32F429IGTx.ioc // CubeMX核心配置文件(引脚/时钟/外设初始化)
│
├── Doc/ // 项目级文档目录(独立于代码)
│ README.md // 项目及代码简介文档(纯文本说明)
│
├── Core/ // 工程核心层(用户业务代码+底层初始化)(CubeMX生成,用户可补充)
│ ├── Inc/ // Core层头文件目录
│ │ main.h // 主函数头文件(全局变量/函数声明)
│ │ stm32f4xx_it.h // 中断服务函数头文件(声明EXTI/TIM/UART等中断函数)
│ │ usart.h // USART串口初始化头文件(CubeMX生成)
│ │
│ └── Src/ // Core层源文件目录
│ main.c // 工程入口文件(main函数+主循环,需调用UserCode的MainLoop())
│ stm32f4xx_it.c // 中断服务函数实现(EXTI/TIM/UART等中断处理)
│ system_stm32f4xx.c // STM32F4系统时钟配置(CubeMX生成,主频初始化)
│ usart.c // USART串口初始化实现(CubeMX生成,可添加串口收发逻辑)
│
├── Drivers/ // 标准驱动库(CubeMX生成)
│ ├── CMSIS/ // 内核相关文件(core_cm4.h、启动文件等)
│ └── STM32F4xx_HAL_Driver/ // 官方HAL库(含外设驱动.c/.h文件)
│
├── MDK-ARM/ // Keil MDK工程目录(编译/调试配置)
│ │ EventRecorderStub.scvd // 事件记录器配置(调试用)
│ │ JLinkSettings.ini // JLink调试器配置(下载/仿真)
│ │ startup_stm32f429xx.s // 启动文件(栈/堆配置,中断向量表)
│ │ STM32F429IGTx.uvguix.miaol // Keil用户界面配置(窗口布局等)
│ │ STM32F429IGTx.uvoptx // Keil工程选项配置(编译/链接/调试)
│ │ STM32F429IGTx.uvprojx // Keil工程核心文件(双击打开工程)
│ │
│ ├── DebugConfig/ // 调试配置目录
│ ├── RTE/ // RTE(运行时环境)配置
│ └── STM32F429IGTx/
│ STM32F429IGTx.hex // 编译生成的HEX文件(烧录到芯片)
│
└── UserCode/ // 自定义代码目录(对应分层设计)
├── COM/ // 通用公共层
│ base.h // 跨平台基础类型、位操作/数学宏、寄存器访问工具
│ cfg.h // 全局配置中心(delay/uart/scheduler的开关、参数)
│ scheduler.c // 任务调度器实现(初始化/创建任务/运行调度)(依赖cfg.h)
│ scheduler.h // 调度器接口声明
│ delay.c // 延时驱动实现(依赖cfg.h配置)
│ delay.h // 延时驱动接口(DelayUS/DelayMS)
│ uart_stdio.c // 串口重定向驱动(实现printf/scanf)(依赖cfg.h)
│ // ...其他公共层代码
│
├── APP/ // 应用层
│ main_loop.c // 主循环+业务任务(LED翻转、串口打印)
│ main_loop.h // 主循环接口声明(MainLoop函数)
│ // ...其他应用层代码
│
├── SRV/ // 服务层
│ // ...其他服务层代码
│
├── DRV/ // 驱动层
│ // ...其他驱动层代码
│
└── HAL/ // 硬件抽象层
mcu.c // MCU初始化(如TIM6中断/NVIC配置)+ 中断回调
mcu.h // MCU抽象接口声明(McuInit函数)
// ...其他HAL层代码
代码
基础头文件
cpp
/** @copyright Copyright (c) 2014-2025 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file base.h
* @brief 嵌入式系统基础定义头文件
* @details 提供跨平台嵌入式开发所需的基础类型定义、常量定义、宏定义和工具函数,无冗余外部依赖:
* - 数据类型别名(8/16/32/64位有符号/无符号整数、单/双精度浮点数);
* - 布尔类型、通用状态枚举等自定义;
* - volatile/const访问限定符宏(__I/__O/__IO);
* - 位操作、字节拆分/合并、数学运算等实用宏;
* - 内存操作、寄存器访问、数组长度计算等工具宏;
* - 编译时断言(STATIC_ASSERT)和边界检查机制;
*
* @author 匠在江湖(000000)
* @version V03.00
* @date 2025-10-22
******************************************************************************
* @attention
* -# 平台无关,类型大小依赖编译器与CPU架构,跨平台需验证兼容;
* -# 核心组件(类型/宏/常量)禁删,修改同步团队,新增按"类型→常量→宏→工具"分类;
* -# 宏经do-while(0)保障安全,无参数校验,禁传i++等副作用参数;
* -# 寄存器访问限内存映射外设(需地址对齐),内存操作宏为基础实现,大规模数据建议用内置函数或DMA;
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2014-05-08 | 匠在江湖(000000) | 创建初版 |
* | V02.00 | 2016-04-22 | 匠在江湖(000000) | 优化注释描述<br>新增位操作宏 |
* | V03.00 | 2025-10-22 | 匠在江湖(000000) | 移除外部依赖<br>修复BIT_MASK宏边界溢出<br>新增循环和饱和计数宏 |
******************************************************************************
*/
/** @defgroup Base Base
* @{
*/
#ifndef BASE_H_
#define BASE_H_
/* Includes *******************************************************************/
#ifdef __cplusplus // C++编译环境自动定义__cplusplus宏(plus plus即"+ +"的含义)
extern "C"{ // 声明括号内为C语言代码,确保C++编译器按C规则编译
#endif
/* Exported types *************************************************************/
/** @defgroup Base_exported_types Base exported types
* @ingroup Base
* @{
*/
/** @name volatile和const宏定义
* @note ST库内核头文件(如core_cm4.h)定义了__I/__O/__IO宏,此处兼容实现:
* - volatile:禁止编译器优化,强制每次从内存/寄存器读写(适用于值可能被异步修改的场景);
* 典型应用场景:<br>
* -# 中断服务程序中修改的供其它程序检测的变量需要加volatile;
* -# 当一个中断服务子程序修改一个指向buffer的指针时,指针需加volatile;
* -# 多任务环境下各任务间共享的标志应该加volatile;
* -# 存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同含义;
* <br>
* 示例错误分析:<br>
* @code
* int square(volatile int *ptr) // 错误:重复读取volatile变量导致结果不可靠
* {
* return *ptr * *ptr; // 两次读取*ptr可能因硬件异步修改而不同
* }
* @endcode
* 这段代码的目的是用来返回指针*ptr指向值的平方,但是由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
* @code
* int square(volatile int *ptr)
* {
* int a,b;
* a = *ptr;
* b = *ptr;
* return a * b;
* }
* @endcode
* 由于*ptr的值可能被意想不到的地方改变,因此a和b可能是不同的。正确的代码如下: <br>
* @code
* long square(volatile int *ptr)
* {
* int a = *ptr; // 单次读取后计算,避免异步修改影响
* return a * a;
* }
* @endcode
* <br>
* - const :只读变量,能防止误修改。const比define定义常量更精确(其实const定义的是只读变量,而不是常量),定义了常量的类型。 <br>
* const应用示例:<br>
* -# 只读的状态寄存器,用volatile const修饰。volatile表示可能被意想不到地改变,const表示软件不能修改它。
* -# 示例代码:
* @code
* int tmp; <br>
* const int * p1 = &tmp; // p1可变,*p1不可变,const 修饰的是 *p1,即*p1不可变。
* int * const p2 = &tmp; // p2不可变,*p2可变,const 修饰的是 p2,即p2不可变。
* const int * const p3 = &tmp; // p3不可变,*p3也不可变,前一个const 修饰的是 *p3,后一个const 修饰的是p3,两者都不可变。
* @endcode
* @{
*/
#ifndef __IO
#define __I volatile const ///< 输入寄存器:只读(硬件可改,软件不可写)
#define __O volatile ///< 输出寄存器:只写(软件写入硬件,禁止优化写操作)
#define __IO volatile ///< 输入输出寄存器:可读可写(双向访问,禁止优化)
#endif
/** @} */
/** @name 通用数据类型别名
* @note 数据类型字节数规则:
* - 数据类型字节差异的直接决定者是编译器,但编译器需严格遵循目标CPU架构和操作系统的ABI(应用二进制接口);
* - 开发前需确认编译器数据类型字节数,防止因长度不匹配导致错误;
* - 下表打"*"行表示不同平台下数据类型字节数存在差异:
* | Diff | Data Type | 8 BIT CPU(8051) | 16 BIT CPU(8086) | 32 BIT CPU(x86) | 64 BIT CPU(x86_64) | 核心适配逻辑 |
* |------|---------------|-----------------|------------------|-----------------|-----------------------------|---------------------------------------------------|
* | | char | 1 BYTE | 1 BYTE | 1 BYTE | 1 BYTE | C标准规定的最小存储单位 |
* | | short | 2 BYTE | 2 BYTE | 2 BYTE | 2 BYTE | 至少16位(标准最小范围),与CPU位宽无关 |
* | * | int | 2 BYTE | 2 BYTE | 4 BYTE | 4 BYTE | 与CPU字长匹配以优化运算效率 |
* | * | long int | 4 BYTE | 4 BYTE | 4 BYTE | 8 BYTE(Linux)/4 BYTE(Win) | 64位系统因兼容性保留差异 |
* | | long long int | 8 BYTE | 8 BYTE | 8 BYTE | 8 BYTE | C99标准强制64位(部分8位CPU编译器需开启扩展支持) |
* | | float | 4 BYTE | 4 BYTE | 4 BYTE | 4 BYTE | 遵循IEEE 754单精度标准,与CPU浮点单元适配 |
* | | double | 8 BYTE | 8 BYTE | 8 BYTE | 8 BYTE | 遵循IEEE 754双精度标准,全平台统一 |
* | * | long double | 8 BYTE | 8 BYTE | 8 BYTE(GCC) | 16 BYTE(GCC)/8 BYTE(VS) | 扩展精度,依赖编译器与浮点硬件支持 |
* | * | 指针类型 | 2 BYTE | 2 BYTE | 4 BYTE | 8 BYTE | 与地址总线宽度一致(存储内存地址) |
*
* @{
*/
// 基本数据类型别名(兼容stdint.h)
#ifndef int8_t
typedef signed char int8_t; ///< 有符号8位整数
typedef signed short int int16_t; ///< 有符号16位整数
typedef signed long int int32_t; ///< 有符号32位整数
typedef signed long long int int64_t; ///< 有符号64位整数
typedef unsigned char uint8_t; ///< 无符号8位整数
typedef unsigned short int uint16_t; ///< 无符号16位整数
typedef unsigned long int uint32_t; ///< 无符号32位整数
typedef unsigned long long int uint64_t; ///< 无符号64位整数
#endif
#ifndef float32_t
/** @note float内存布局(IEEE 754单精度):
* - 格式:(-1)^S × 1.M × 2^(E-127)
* - 符号位(S):第31位(0=正,1=负);
* - 指数位(E):第30-23位(8位,偏移127);
* - 尾数位(M):第22-0位(23位,隐含整数部分1);
* 特殊值定义:
* - E全0+M全0 = 零;
* - E全1+M全0 = ±无穷大;
* - E全1+M非0 = 非数字(NaN);
* - E全0+M非0 = 非规约数(接近0的极小值);
* double(双精度):符号位1位,指数位11位(偏移1023),尾数位52位;
*/
typedef float float32_t; ///< 单精度浮点数(4字节)
typedef double float64_t; ///< 双精度浮点数(8字节)
#endif
// 精简数据类型别名(适配嵌入式开发习惯)
#ifndef s8
typedef int8_t s8; ///< 有符号8位整数
typedef int16_t s16; ///< 有符号16位整数
typedef int32_t s32; ///< 有符号32位整数
typedef int64_t s64; ///< 有符号64位整数
typedef uint8_t u8; ///< 无符号8位整数
typedef uint16_t u16; ///< 无符号16位整数
typedef uint32_t u32; ///< 无符号32位整数
typedef uint64_t u64; ///< 无符号64位整数
typedef float32_t f32; ///< 单精度浮点数
typedef float64_t f64; ///< 双精度浮点数
#endif
/** @} */
/// 布尔类型(兼容stdbool.h,避免依赖标准库)
#ifndef BoolTypeDef
typedef enum
{
FALSE = 0, ///< 假/错误
TRUE = 1 ///< 真/正确
}BoolTypeDef;
#endif
/// 基础状态返回类型(统一函数返回值规范)
#ifndef StatusTypeDef
typedef enum
{
OK = 0, ///< 成功/正常
ERROR = 1, ///< 错误/异常
BUSY = 2, ///< 忙/操作中
TIMEOUT = 3, ///< 超时
INVALID = 4 ///< 无效参数/状态
}StatusTypeDef;
#endif
/** @} end of Base_exported_types */
/* Exported constants *********************************************************/
/** @defgroup Base_exported_constants Base exported constants
* @ingroup Base
* @{
*/
/** @name 类型极限值(避免依赖limits.h)
* @{
*/
#ifndef INT8_MIN
#define INT8_MIN ((int8_t)0x80) ///< 最小值:-128
#define INT16_MIN ((int16_t)0x8000) ///< 最小值:-32768
#define INT32_MIN ((int32_t)0x80000000L) ///< 最小值:-2147483648
#define INT64_MIN ((int64_t)0x8000000000000000LL) ///< 最小值:-9223372036854775808
#define INT8_MAX ((int8_t)0x7F) ///< 最大值:127
#define INT16_MAX ((int16_t)0x7FFF) ///< 最大值:32767
#define INT32_MAX ((int32_t)0x7FFFFFFFL) ///< 最大值:2147483647
#define INT64_MAX ((int64_t)0x7FFFFFFFFFFFFFFFLL) ///< 最大值:9223372036854775807
#define UINT8_MAX ((uint8_t)0xFFU) ///< 最大值:255
#define UINT16_MAX ((uint16_t)0xFFFFU) ///< 最大值:65535
#define UINT32_MAX ((uint32_t)0xFFFFFFFFUL) ///< 最大值:4294967295
#define UINT64_MAX ((uint64_t)0xFFFFFFFFFFFFFFFFULL) ///< 最大值:18446744073709551615
#endif
/** @} */
/** @name 位掩码值(快速生成单比特掩码)
* @{
*/
#define B0 (1U << 0) ///< 第0位掩码:0x01
#define B1 (1U << 1) ///< 第1位掩码:0x02
#define B2 (1U << 2) ///< 第2位掩码:0x04
#define B3 (1U << 3) ///< 第3位掩码:0x08
#define B4 (1U << 4) ///< 第4位掩码:0x10
#define B5 (1U << 5) ///< 第5位掩码:0x20
#define B6 (1U << 6) ///< 第6位掩码:0x40
#define B7 (1U << 7) ///< 第7位掩码:0x80
#define B8 (1U << 8) ///< 第8位掩码:0x0100
#define B9 (1U << 9) ///< 第9位掩码:0x0200
#define B10 (1U << 10) ///< 第10位掩码:0x0400
#define B11 (1U << 11) ///< 第11位掩码:0x0800
#define B12 (1U << 12) ///< 第12位掩码:0x1000
#define B13 (1U << 13) ///< 第13位掩码:0x2000
#define B14 (1U << 14) ///< 第14位掩码:0x4000
#define B15 (1U << 15) ///< 第15位掩码:0x8000
#define B16 (1UL << 16) ///< 第16位掩码:0x00010000
#define B17 (1UL << 17) ///< 第17位掩码:0x00020000
#define B18 (1UL << 18) ///< 第18位掩码:0x00040000
#define B19 (1UL << 19) ///< 第19位掩码:0x00080000
#define B20 (1UL << 20) ///< 第20位掩码:0x00100000
#define B21 (1UL << 21) ///< 第21位掩码:0x00200000
#define B22 (1UL << 22) ///< 第22位掩码:0x00400000
#define B23 (1UL << 23) ///< 第23位掩码:0x00800000
#define B24 (1UL << 24) ///< 第24位掩码:0x01000000
#define B25 (1UL << 25) ///< 第25位掩码:0x02000000
#define B26 (1UL << 26) ///< 第26位掩码:0x04000000
#define B27 (1UL << 27) ///< 第27位掩码:0x08000000
#define B28 (1UL << 28) ///< 第28位掩码:0x10000000
#define B29 (1UL << 29) ///< 第29位掩码:0x20000000
#define B30 (1UL << 30) ///< 第30位掩码:0x40000000
#define B31 (1UL << 31) ///< 第31位掩码:0x80000000
/** @} */
/** @name 其他常用常量(工程实用值)
* @{
*/
#define SECONDS_PER_YEAR (365UL * 24UL * 60UL * 60UL) ///< 平年总秒数(忽略闰年)
#define PI 3.14159265358979323846 ///< 圆周率(双精度,满足高精度计算需求)
/** @} */
/** @} end of Base_exported_constants */
/* Exported macro *************************************************************/
/** @defgroup Base_exported_macro Base exported macro
* @ingroup Base
* @{
*/
/** @name 位与字节操作
* @{
*/
#define BIT_MASK(n) (1U << (n)) ///< 输入位位置n(0≤n≤31),返回对应位掩码值
// 进制转换操作(注:LONG_TO_BIN8 需输入0xnnnnnnnnL格式,如BIN8(11111111)实际传入0x11111111L)
#define LONG_TO_BIN8(n) \
( \
((n >> 21) & 0x80) | \
((n >> 18) & 0x40) | \
((n >> 15) & 0x20) | \
((n >> 12) & 0x10) | \
((n >> 9) & 0x08) | \
((n >> 6) & 0x04) | \
((n >> 3) & 0x02) | \
((n ) & 0x01) \
)
#define BIN8(n) LONG_TO_BIN8(0x##n##L) ///< 输入8位二进制字符集(如11111111),返回对应十六进制数值(0xFF)
#define BIN16(n1, n0) ((BIN8(n1)<<8) | (BIN8(n0))) ///< 输入两个8位二进制,返回2字节十六进制数值
#define BIN32(n3, n2, n1, n0) ((BIN16(n3, n2)<<16) | (BIN16(n1, n0))) ///< 输入四个8位二进制,返回4字节十六进制数值
// 基本位操作
#define GET_BIT(x, n) (((x) >> (n)) & 1U) ///< 获取x的第n位值(返回0或1)
#ifndef SET_BIT
#define SET_BIT(r, n) ((r) |= BIT_MASK(n)) ///< 将r的第n位设置为1
#endif
#define CLR_BIT(r, n) ((r) &= ~BIT_MASK(n)) ///< 将r的第n位清除为0
#define CPL_BIT(r, n) ((r) ^= BIT_MASK(n)) ///< 将r的第n位翻转(0→1/1→0)
// 多位操作(掩码方式)
#define GET_BITS(x, m) ((x) & (m)) ///< 获取x中掩码m覆盖的位值
#define SET_BITS(r, m) ((r) |= (m)) ///< 将r中掩码m覆盖的位设置为1
#define CLR_BITS(r, m) ((r) &= ~(m)) ///< 将r中掩码m覆盖的位清除为0
#define CPL_BITS(r, m) ((r) ^= (m)) ///< 将r中掩码m覆盖的位翻转
// 位域操作
#define GET_BITS_FIELD(x, m, n) (((x) & (m)) >> (n)) ///< 获取x中掩码m覆盖的位域,并右移n位对齐
#define SET_BITS_FIELD(r, m, n, v) ((r) = ((r) & ~(m)) | (((v) << (n)) & (m))) ///< 设置r的位域:1.清除m覆盖位;2.新值v移位对齐;3.与清除后的r进行或操作
// 字节拆分
#define GET_BYTE_H4BIT(x) GET_BITS_FIELD(x, 0xF0U, 4) ///< 获取字节x的高4位(半字节)
#define GET_BYTE_L4BIT(x) GET_BITS_FIELD(x, 0x0FU, 0) ///< 获取字节x的低4位(半字节)
#define GET_WORD_HBYTE(w) ((uint8_t)((w) >> 8)) ///< 获取16位字w的高字节
#define GET_WORD_LBYTE(w) ((uint8_t)((w) & 0xFFU)) ///< 获取16位字w的低字节
#define GET_DWORD_BYTE0(dw) ((uint8_t)((dw) & 0xFFU)) ///< 获取32位双字dw的最低字节(Byte0)
#define GET_DWORD_BYTE1(dw) ((uint8_t)(((dw) >> 8) & 0xFFU)) ///< 获取32位双字dw的次低字节(Byte1)
#define GET_DWORD_BYTE2(dw) ((uint8_t)(((dw) >> 16) & 0xFFU)) ///< 获取32位双字dw的次高字节(Byte2)
#define GET_DWORD_BYTE3(dw) ((uint8_t)(((dw) >> 24) & 0xFFU)) ///< 获取32位双字dw的最高字节(Byte3)
// 字节合并(高位在前)
#define MAKE_WORD(hb, lb) ((uint16_t)(((uint16_t)((uint8_t)(hb)) << 8) | (uint8_t)(lb))) ///< 高字节hb+低字节lb合并为16位字
#define MAKE_DWORD(b3, b2, b1, b0) ((uint32_t)(((uint32_t)((uint8_t)(b3)) << 24) | \
((uint32_t)((uint8_t)(b2)) << 16) | \
((uint32_t)((uint8_t)(b1)) << 8) | \
(uint8_t)(b0))) ///< 四字节b3-b0合并为32位双字
// 字节顺序操作
#define SWAP_WORD_BYTES(w) ((uint16_t)((((w) << 8) & 0xFF00U) | (((w) >> 8) & 0x00FFU))) ///< 16位字字节序反转(高低字节交换)
#define SWAP_DWORD_BYTES(dw) ((uint32_t)((((dw) << 24) & 0xFF000000UL) | \
(((dw) << 8) & 0x00FF0000UL) | \
(((dw) >> 8) & 0x0000FF00UL) | \
(((dw) >> 24) & 0x000000FFUL))) ///< 32位双字字节序反转
#define SWAP(a, b) do{ (a) ^= (b); (b) ^= (a); (a) ^= (b); }while(0) ///< 交换a和b的值,注:a和b不能是同地址,否则会清零
/** @} */
/** @name 数学运算
* @{
*/
// 基本数学运算
#define MAX(a, b) ((a) >= (b) ? (a) : (b)) ///< 输入两个参数,返回较大值(注:参数类型需一致)
#define MIN(a, b) ((a) >= (b) ? (b) : (a)) ///< 输入两个参数,返回较小值(注:参数类型需一致)
#define ABS(x) ((x) >= 0 ? (x) : (-(x))) ///< 输入数值x,返回其绝对值(注:仅适用于有符号类型)
// 平均值计算(整数除法向下取整)
#define AVG(a, b) (((a) + (b)) / 2) ///< 两数平均值
#define AVG3(a, b, c) (((a) + (b) + (c)) / 3) ///< 三数平均值
#define AVG4(a, b, c, d) (((a) + (b) + (c) + (d)) / 4) ///< 四数平均值
// 边界操作
#define CLAMP(x, min, max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x))) ///< 将x钳位在[min,max]范围内(注:x禁传自增/自减表达式)
#define IS_IN_RANGE(x, min, max) (((max) >= (x)) && ((x) >= (min))) ///< 判断x是否在[min,max]内(含边界),是则返回1
#define IS_OUT_RANGE(x, min, max) (((max) < (x)) || ((x) < (min))) ///< 判断x是否在[min,max]外(不含边界),是则返回1
/// 循环自增:r达到max后重置为min
#define CIRCULAR_INC(r, min, max) \
do{ \
(r) = ((r) + 1) > (max) ? (min) : ((r) + 1); \
}while(0)
/// 循环自减:r达到min后重置为max
#define CIRCULAR_DEC(r, min, max) \
do{ \
(r) = (((r) - 1) < (min)) ? (max) : ((r) - 1); \
}while(0)
/// 饱和自增:r达到max后不再增加
#define SATURATE_INC(r, max) \
do{ \
if((r) < (max)) (r)++; \
}while(0)
/// 饱和自减:r达到min后不再减少
#define SATURATE_DEC(r, min) \
do{ \
if((min) < (r)) (r)--; \
}while(0)
/** @} */
/** @name 数组操作
* @{
*/
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) ///< 计算数组元素个数(仅适用于数组,禁止传入指针)
#define ARRAY_2D_ROWS_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) ///< 二维数组 - 行数
#define ARRAY_2D_COLS_SIZE(arr) (sizeof((arr)[0]) / sizeof((arr)[0][0])) ///< 二维数组 - 列数
#define ARRAY_2D_SIZE(arr) (ARRAY_2D_ROWS_SIZE(arr) * ARRAY_2D_COLS_SIZE(arr)) ///< 二维数组 - 总元素个数
/** @} */
/** @name 寄存器访问(内存映射外设专用)
* @{
*/
#define REG8(addr) (*(volatile uint8_t *)(addr)) ///< 8位寄存器地址映射(addr需为字节对齐地址)
#define REG16(addr) (*(volatile uint16_t *)(addr)) ///< 16位寄存器地址映射(addr需为半字对齐地址)
#define REG32(addr) (*(volatile uint32_t *)(addr)) ///< 32位寄存器地址映射(addr需为字对齐地址)
#define READ_REG8(addr) (REG8(addr)) ///< 读取8位寄存器值
#define READ_REG16(addr) (REG16(addr)) ///< 读取16位寄存器值
#define READ_REG32(addr) (REG32(addr)) ///< 读取32位寄存器值
#define WRITE_REG8(addr, val) (REG8(addr) = (val)) ///< 写入8位寄存器值
#define WRITE_REG16(addr, val) (REG16(addr) = (val)) ///< 写入16位寄存器值
#define WRITE_REG32(addr, val) (REG32(addr) = (val)) ///< 写入32位寄存器值
/** @} */
/** @name 宏拼接工具
* @{
*/
#define CONCAT(a, b) a##b ///< 双参数宏拼接(如CONCAT(PIN_,5)→PIN_5)
#define CONCAT3(a, b, c) a##b##c ///< 三参数宏拼接
#define CONCAT4(a, b, c, d) a##b##c##d ///< 四参数宏拼接
/** @} */
/** @name 内存操作(基础实现,替代标准库)
* @{
*/
/// 内存填充:将dst起始的size字节设为val(兼容任意数据类型)
#define MEMSET(dst, val, size) \
do{ \
uint8_t _i; \
for(_i = 0; _i < (size); _i++) \
{ \
dst[_i] = val; \
} \
}while(0)
/// 内存复制:将src起始的size字节复制到dst(src/dst无重叠时或dst起始在src前时使用)
#define MEMCPY(dst, src, size) \
do{ \
uint8_t _i; \
for(_i = 0; _i < (size); _i++) \
{ \
dst[_i] = src[_i]; \
} \
}while(0)
/// 反向内存复制:将src起始的size字节倒序复制到dst(src/dst重叠且dst起始在src后时使用)
#define MEMCPY_REV(dst, src, size) \
do{ \
uint8_t _i; \
for(_i = (size); _i > 0; _i--) \
{ \
dst[_i-1] = src[_i-1]; \
} \
}while(0)
/** @} */
/** @name 编译断言(静态检查)
* @note 兼容性说明:
* - C11标准推荐使用_Static_assert(cond, msg),编译器原生支持;
* - 下述实现为兼容C89/C99编译器,cond为假时因数组长度为负编译报错;
* 使用示例:
* STATIC_ASSERT(sizeof(uint16_t) == 2, uint16_t_size_error); // 条件满足则编译通过
* @{
*/
#define STATIC_ASSERT(cond, msg) typedef char static_assert_##msg[(cond) ? 1 : -1] __attribute__((unused)) ///< 编译时断言(抑制未使用变量警告)
/** @} */
/** @} end of Base_exported_macro */
/* Exported functions *********************************************************/
#ifdef __cplusplus
}
#endif
#endif /* BASE_H_ */
/** @} end of Base */
/********************************* END OF FILE ********************************/
配置头文件
cpp
/** @copyright Copyright (c) 2019-2030 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file cfg.h
* @brief 配置参数 头文件
* @details
*
* @author 匠在江湖(000000)
* @version V01.00
* @date 2019-05-08
******************************************************************************
* @attention
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2019-05-08 | 匠在江湖(000000) | 创建初版 |
******************************************************************************
*/
#ifndef CFG_H_
#define CFG_H_
/* Includes *******************************************************************/
#include "mcu.h"
#ifdef __cplusplus
extern "C"{
#endif
//*** <<< Use Configuration Wizard in Context Menu >>> ***
/* Exported types *************************************************************/
/* Exported constants *********************************************************/
// <h> Function Enable Configuration
// <o> Delay Function: Disable/Enable
// <0=> Disable
// <1=> Enable
#define DELAY_FN_EN 1
// <o> Scheduler Function: Disable/Enable
// <0=> Disable
// <1=> Enable
#define SCH_FN_EN 1
// <o> UART Redirection Function: Disable/Enable
// <i> Enable/Disable of printf(...) and scanf(...)
// <0=> Disable
// <1=> Enable
#define UART_STDIO_FN_EN 1
// </h>
#if DELAY_FN_EN
// <h> Delay Function Configuration
// <i> Valid only when delay function is enabled. Match parameters to MCU timer configuration
// <o> Timer Counting Mode Selection
// <0=> Down-count
// <1=> Up-count
#define DTIM_INC_MODE 1
// <o> Timer Count Value per Microsecond
// <1-1000>
#define DTIM_CNT_PER_US 1
// <o> Timer Count Range
// <i> Example: If timer counts 0~99, fill 100 here
// <1-65536>
#define DTIM_CNT_RANGE 1000
// </h>
#endif
#if SCH_FN_EN
// <h> Scheduler Configuration
// <i> Parameter configuration for time-slice scheduler. The scheduler requires a timer as time base,
// <i> which can share the timer with delay function or use an independent timer
// <o> Maximum Number of Tasks
// <1-20>
#define SCH_MAX_TASK_NUM 5
// <o> Scheduler Tick Count Value per Millisecond
// <1-100>
#define SCH_TICK_CNT_VAL_PER_MS 1
// <o> Tick Count Source Selection
// <i> 0 = Internal Counting: Call SchedulerTickISR() in external timer interrupt function;
// <i> 1 = External Counting: Call external counting function (e.g., HAL_GetTick()) internally to get count value;
// <0=> Internal Counting
// <1=> External Counting
#define SCH_TICK_CNT_FROM_EXT 0
// <o> Is Reading Internal Count Variable an Atomic Operation?
// <i> Valid only when internal counting is selected
// <0=> No
// <1=> Yes
#define SCH_TICK_CNT_OF_INT_IS_ATOM 1
// </h>
#endif
/* Exported macro *************************************************************/
// <h> Delay Function External Interface Configuration
// <i> Modify configuration in Text Editor. Implement timer initialization/start externally and match corresponding count register or function
#if DELAY_FN_EN
#define DTIM_GET_CNT() (TIM6->CNT) // Get current timer count value
#endif
// </h>
// <h> UART Redirection External Interface Configuration
// <i> Modify configuration in Text Editor. Implement UART initialization and single-byte transmit/receive functions externally
#if UART_STDIO_FN_EN
extern UART_HandleTypeDef huart1;
#define UART_STDIO_TXD_BYTE(ch) HAL_UART_Transmit(&huart1, (unsigned char*)&ch, 1, 100)
#define UART_STDIO_RXD_BYTE(ch) HAL_UART_Receive(&huart1, &ch, 1, 100)
#endif
// </h>
// <h> Scheduler External Interface Configuration
// <i> Modify configuration in Text Editor. Implement timer initialization/start externally
// <i> 1. Implement timer initialization/start externally
#if SCH_FN_EN
// <i> 2. Match unsigned integer type for count variable, must cover full range (8-bit: 0~0xFF, 32-bit: 0~0xFFFFFFFF)
typedef unsigned long int SchTickCntTypeDef;
// <i> 3. Configure only when external counting is selected. Call external counting function (e.g., HAL_GetTick()) to get current count value
#if SCH_TICK_CNT_FROM_EXT
#define SCH_GET_EXT_TICK() HAL_GetTick()
#endif
#endif
// </h>
/* Exported functions *********************************************************/
//*** <<< end of configuration section >>> ***
#ifdef __cplusplus
}
#endif
#endif /* CFG_H_ */
/********************************* END OF FILE ********************************/
下图是cfg.h通过配置向导注释语法 的效果图,可通过可视化来修改配置代码
下是cfg.h中文示例,编译器编码格式设置不对会导致下文中文会乱码。尽量用上述英文格式,若想用中文就需要设置好编码格式。
cpp
/** @copyright Copyright (c) 2019-2030 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file cfg.h
* @brief 配置参数 头文件
* @details
*
* @author 匠在江湖(000000)
* @version V01.00
* @date 2019-05-08
******************************************************************************
* @attention
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2019-05-08 | 匠在江湖(000000) | 创建初版 |
******************************************************************************
*/
#ifndef CFG_H_
#define CFG_H_
/* Includes *******************************************************************/
#include "mcu.h"
#ifdef __cplusplus
extern "C"{
#endif
//*** <<< Use Configuration Wizard in Context Menu >>> ***
/* Exported types *************************************************************/
/* Exported constants *********************************************************/
// <h> 功能使能配置
// <o> 延时函数功能:使能/禁止
// <0=> 禁止
// <1=> 使能
#define DELAY_FN_EN 1
// <o> 调度器功能:使能/禁止
// <0=> 禁止
// <1=> 使能
#define SCH_FN_EN 1
// <o> 串口重定向功能:使能/禁止
// <i> printf(...)、scanf(...) 的 使能/禁止
// <0=> 禁止
// <1=> 使能
#define UART_STDIO_FN_EN 1
// </h>
#if DELAY_FN_EN
// <h> 延时函数配置
// <i> 延时函数功能 使能,此项配置才有效,依 MCU 定时器配置匹配相应参数
// <o> 定时器计数模式选择
// <0=> 递减
// <1=> 递增
#define DTIM_INC_MODE 1
// <o> 定时器每微秒计数值
// <1-1000>
#define DTIM_CNT_PER_US 1
// <o> 定时器计数量程
// <i> 例如:定时器计数 0~99,此项填 100
// <1-65536>
#define DTIM_CNT_RANGE 1000
// </h>
#endif
#if SCH_FN_EN
// <h> 调度器配置
// <i> 时间片调度器相关参数配置,调度器需要定时器作为调度器时基,可以与延时函数共用定时器,也可单独配置定时器
// <o> 最大任务数
// <1-20>
#define SCH_MAX_TASK_NUM 5
// <o> 每毫秒调度器滴答计数值
// <1-100>
#define SCH_TICK_CNT_VAL_PER_MS 1
// <o> 滴答计数选择
// <i> 0 = 内部计数:需在外部定时器中断函数中调用 SchedulerTickISR ();
// <i> 1 = 外部计数:需在内部调用外部计数函数(如 HAL_GetTick ())获取计数值;
// <0=> 内部计数
// <1=> 外部计数
#define SCH_TICK_CNT_FROM_EXT 0
// <o> 读 内部计数变量 是原子操作吗
// <i> 滴答计数选择内部计数才有效
// <0=> 否
// <1=> 是
#define SCH_TICK_CNT_OF_INT_IS_ATOM 1
// </h>
#endif
/* Exported macro *************************************************************/
// <h> 延时函数 外部接口配置
// <i> 配置在Text Editor中修改,外部需实现定时器初始化 / 启动计数,匹配对应计数寄存器或计数函数
#if DELAY_FN_EN
#define DTIM_GET_CNT() (TIM6->CNT) // 获取定时器当前计数值
#endif
// </h>
// <h> 串口重定向 外部接口配置
// <i> 配置在Text Editor中修改,外部需实现串口初始化及单字节收发函数
#if UART_STDIO_FN_EN
extern UART_HandleTypeDef huart1;
#define UART_STDIO_TXD_BYTE(ch) HAL_UART_Transmit(&huart1, (unsigned char*)&ch, 1, 100)
#define UART_STDIO_RXD_BYTE(ch) HAL_UART_Receive(&huart1, &ch, 1, 100)
#endif
// </h>
// <h> 调度器 外部接口配置
// <i> 配置在Text Editor中修改,外部需实现定时器初始化 / 启动计数
// <i> 1.外部需实现定时器初始化 / 启动计数
#if SCH_FN_EN
// <i> 2.匹配无符号整型计数变量类型,需覆盖满量程(16 位:0~0xFFFF,32 位:0~0xFFFFFFFF)
typedef unsigned long int SchTickCntTypeDef;
// <i> 3.选择外部计数才配置此项,调用外部计数函数(如 HAL_GetTick ())获取当前计数值
#if SCH_TICK_CNT_FROM_EXT
#define SCH_GET_EXT_TICK() HAL_GetTick()
#endif
#endif
// </h>
/* Exported functions *********************************************************/
//*** <<< end of configuration section >>> ***
#ifdef __cplusplus
}
#endif
#endif /* CFG_H_ */
/********************************* END OF FILE ********************************/
中文翻译成英文
调度器代码
cpp
/** @copyright Copyright (c) 2019-2030 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file scheduler.c
* @brief 调度器 源文件
* @details
*
* @author 匠在江湖(000000)
* @version V01.00
* @date 2019-05-08
******************************************************************************
* @attention
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2019-05-08 | 匠在江湖(000000) | 创建初版 |
******************************************************************************
*/
/* Includes *******************************************************************/
#include "scheduler.h"
/* Private typedef ************************************************************/
typedef struct
{
void (*func)(void); // 任务函数指针
SchTickCntTypeDef lastSchTicks; // 上次滴答数
SchTickCntTypeDef intervalSchTicks; // 间隔滴答数
}SchTaskTypeDef; //调度器任务控制块
/* Private constants **********************************************************/
/* Private macro **************************************************************/
/* Private variables **********************************************************/
static SchTaskTypeDef s_taskList[SCH_MAX_TASK_NUM];
/* Private function prototypes ************************************************/
#if !SCH_TICK_CNT_FROM_EXT
static volatile SchTickCntTypeDef s_schTickCnt = 0; // 系统滴答计数
/**
******************************************************************************
* @brief 调度器滴答中断服务函数
* @param None
* @retval None
* @note 在定时器中断服务函数中调用
******************************************************************************
*/
void SchedulerTickISR(void)
{
s_schTickCnt++;
}
#endif
/**
******************************************************************************
* @brief 调度器运行 函数
* @param None
* @retval None
******************************************************************************
*/
void SchedulerRun(void)
{
for(unsigned char i=0; i<SCH_MAX_TASK_NUM; i++)
{
if(s_taskList[i].func != 0)
{
#if SCH_TICK_CNT_FROM_EXT
SchTickCntTypeDef thisSchTicks = SCH_GET_EXT_TICK(); // 调度器获取外部滴答
#elif SCH_TICK_CNT_OF_INT_IS_ATOM
SchTickCntTypeDef thisSchTicks = s_schTickCnt; // 读计数值是原子操作
#else
SchTickCntTypeDef thisSchTicks, checkTicks;
do
{
thisSchTicks = s_schTickCnt;
checkTicks = s_schTickCnt;
}while(thisSchTicks != checkTicks); // 读计数值是非原子操作,两次读取进行验证
#endif
if((thisSchTicks - s_taskList[i].lastSchTicks) >= s_taskList[i].intervalSchTicks)
{
s_taskList[i].lastSchTicks = thisSchTicks;
s_taskList[i].func(); // 执行任务
}
}
}
}
/**
******************************************************************************
* @brief 创建任务 函数
* @param *func - 任务函数指针
* @param time - 间隔时间(毫秒)
* @retval 1--成功;0--失败
******************************************************************************
*/
unsigned char SchedulerCreateTask(void (*func)(void), unsigned short int time)
{
for(unsigned char i=0; i<SCH_MAX_TASK_NUM; i++)
{
if(s_taskList[i].func == 0)
{
s_taskList[i].func = func;
s_taskList[i].lastSchTicks = 0;
s_taskList[i].intervalSchTicks = time * SCH_TICK_CNT_VAL_PER_MS;
return 1;
}
}
return 0;
}
/**
******************************************************************************
* @brief 调度器初始化 函数
* @param None
* @retval None
******************************************************************************
*/
void SchedulerInit(void)
{
for(unsigned char i=0; i<SCH_MAX_TASK_NUM; i++)
{
s_taskList[i].func = 0;
s_taskList[i].lastSchTicks = 0;
s_taskList[i].intervalSchTicks = 0;
}
}
/********************************* END OF FILE ********************************/
cpp
/** @copyright Copyright (c) 2019-2030 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file scheduler.h
* @brief 调度器 头文件
* @details
*
* @author 匠在江湖(000000)
* @version V01.00
* @date 2019-05-08
******************************************************************************
* @attention
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2019-05-08 | 匠在江湖(000000) | 创建初版 |
******************************************************************************
*/
#ifndef SCH_H_
#define SCH_H_
/* Includes *******************************************************************/
#include "cfg.h"
#ifdef __cplusplus
extern "C"{
#endif
/* Exported types *************************************************************/
/* Exported functions *********************************************************/
#if !SCH_TICK_CNT_FROM_EXT
extern void SchedulerTickISR(void); // 选择内部计数时,在定时器中断服务函数中调用
#endif
extern void SchedulerRun(void);
extern unsigned char SchedulerCreateTask(void (*func)(void), unsigned short int time);
extern void SchedulerInit(void);
#ifdef __cplusplus
}
#endif
#endif /* SCH_H_ */
/********************************* END OF FILE ********************************/
延时函数代码
cpp
/** @copyright Copyright (c) 2014-2025 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file delay.c
* @brief 延时函数 源文件
* @details
*
* @author 匠在江湖(000000)
* @version V01.00
* @date 2014-05-08
******************************************************************************
* @attention
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2014-05-08 | 匠在江湖(000000) | 创建初版 |
******************************************************************************
*/
/** @defgroup Delay Delay
* @{
*/
/* Includes *******************************************************************/
#include "cfg.h"
#include "delay.h"
/* Private typedef ************************************************************/
/* Private constants **********************************************************/
/* Private macro **************************************************************/
/* Private variables **********************************************************/
/* Private function prototypes ************************************************/
/**
******************************************************************************
* @brief 延时微秒 函数
* @param us: 要延时的微秒数
* @return None
******************************************************************************
*/
void DelayUS(unsigned long int us)
{
unsigned long int startCnt = DTIM_GET_CNT(); // 起始计数值
unsigned long int currentCnt;
unsigned long int targetCnt = us * DTIM_CNT_PER_US; // 需要的总计数值
unsigned long int elapsedCnt = 0; // 已流逝的计数值
while(elapsedCnt < targetCnt)
{
currentCnt = DTIM_GET_CNT(); // 获取定时器当前计数值
#if DTIM_INC_MODE // 递增模式:处理溢出(current < start 表示溢出)
elapsedCnt += (currentCnt >= startCnt) ? (currentCnt - startCnt) : (currentCnt + DTIM_CNT_RANGE - startCnt);
#else // 递减模式:处理溢出(current > start 表示溢出)
elapsedCnt += (startCnt >= currentCnt) ? (startCnt - currentCnt) : (startCnt + DTIM_CNT_RANGE - currentCnt);
#endif
startCnt = currentCnt; // 重置起始值,避免累计误差
}
}
/**
******************************************************************************
* @brief 延时毫秒 函数
* @param ms
* @return None
******************************************************************************
*/
void DelayMS(unsigned long int ms)
{
while(ms--) DelayUS(1000);
}
/** @} end of Delay */
/********************************* END OF FILE ********************************/
cpp
/** @copyright Copyright (c) 2014-2025 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file delay.h
* @brief 延时函数 头文件
* @details
*
* @author 匠在江湖(000000)
* @version V01.00
* @date 2014-05-08
******************************************************************************
* @attention
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2014-05-08 | 匠在江湖(000000) | 创建初版 |
******************************************************************************
*/
#ifndef DELAY_H_
#define DELAY_H_
/* Includes *******************************************************************/
#ifdef __cplusplus
extern "C"{
#endif
/* Exported types *************************************************************/
/* Exported constants *********************************************************/
/* Exported macro *************************************************************/
/* Exported functions *********************************************************/
/** @defgroup Delay_exported_functions Delay exported functions
* @ingroup Delay
* @{
*/
extern void DelayUS(unsigned long int us); // 超出 ms 级,用 DelayMS() 函数
extern void DelayMS(unsigned long int ms);
/** @} end of Delay_exported_functions */
#ifdef __cplusplus
}
#endif
#endif /* DELAY_H_ */
/********************************* END OF FILE ********************************/
串口重定向代码(实现printf / scanf)
cpp
/** @copyright Copyright (c) 2014-2025 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file uart_stdio.c
* @brief 标准IO(printf/scanf)重定向到串口的源文件
* @details 实现标准IO函数到串口的底层映射,支持多编译器适配:
* - 重写fputc:为printf提供底层字符发送能力;
* - 重写fgetc:为scanf提供底层字符接收能力(含终端回显);
*
* @author 匠在江湖(000000)
* @version V01.00
* @date 2014-05-08
******************************************************************************
* @attention
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2014-05-08 | 匠在江湖(000000) | 创建初版 |
******************************************************************************
*/
/** @defgroup Uart_Stdio UART Standard IO
* @brief UART标准IO重定向模块驱动
* @{
*/
/* Includes *******************************************************************/
#include <stdio.h> // 标准IO库头文件:提供FILE、fputc/fgetc等接口定义
#if defined(__GNUC__)
#include <unistd.h>
#endif
#include "cfg.h"
/* Private typedef ************************************************************/
/* Private constants **********************************************************/
/* Private macro **************************************************************/
/* Private variables **********************************************************/
/* Private function prototypes ************************************************/
/**
******************************************************************************
* @brief Keil编译器半主机模式适配
* @note MicroLIB是Keil精简版C库,若开启"Use MicroLIB"(Target选项),可省略下述代码;
* 但MicroLIB不支持浮点格式化(%f)、部分标准库函数缺失,需根据需求选择。
******************************************************************************
*/
#if defined(__CC_ARM) || defined(__ARMCC_VERSION)
// 关闭半主机模式(重定向串口后无需依赖调试器的半主机交互)
#pragma import(__use_no_semihosting)
// ARMCC v5需手动定义FILE结构体,ARMclang v6+复用标准库定义
#if (__ARMCC_VERSION < 6000000)
struct __FILE
{
int handle; // 文件句柄:串口重定向时仅占位,无实际文件操作意义
};
FILE __stdout = {0}; // 标准输出对象:绑定到串口输出
FILE __stdin = {0}; // 标准输入对象:绑定到串口输入
#else
// ARMclang v6+使用标准库FILE定义,仅需外部声明
extern FILE __stdout;
extern FILE __stdin;
#endif
// 弱定义_ttywrch:半主机模式终端字符输出函数,重定向后无需实现逻辑
int _ttywrch(int ch) { return ch; }
// 弱定义_sys_exit:半主机模式退出函数,防止程序异常终止
void _sys_exit(int x) { while(1); }
#endif
/**
******************************************************************************
* @brief 重定义fputc函数(printf底层接口)
* @param ch :待输出的字符(int型兼容ASCII码范围)
* @param f :输出文件句柄(固定为stdout,此处无实际意义)
* @retval ch :成功输出的字符
* @note printf通过循环调用fputc实现字符串输出,此处映射为串口发送
******************************************************************************
*/
int fputc(int ch, FILE *f)
{
(void)f; // 未使用参数,消除编译器警告
UART_STDIO_TXD_BYTE(ch); // 串口发送字符
return ch;
}
/**
******************************************************************************
* @brief 重定义fgetc函数(scanf底层接口)
* @param f :输入文件句柄(固定为stdin,此处无实际意义)
* @retval ch :从串口接收的字符
* @note scanf通过循环调用fgetc实现字符读取,此处映射为串口接收+终端回显
******************************************************************************
*/
int fgetc(FILE *f)
{
(void)f; // 未使用参数,消除编译器警告
unsigned char ch;
UART_STDIO_RXD_BYTE(ch); // 串口接收字符
UART_STDIO_TXD_BYTE(ch); // 回显字符(模拟终端交互体验)
return ch;
}
/** @} end of Uart_Stdio */
/********************************* END OF FILE ********************************/
MCU代码示例
cpp
/** @copyright Copyright (c) 2014-2025 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file mcu.c
* @brief mcu 源文件
* @details
*
* @author 匠在江湖(000000)
* @version V01.00
* @date 2014-05-08
******************************************************************************
* @attention
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2014-05-08 | 匠在江湖(000000) | 创建初版 |
******************************************************************************
*/
/* Includes *******************************************************************/
#include "mcu.h"
#include "scheduler.h"
/* Private typedef ************************************************************/
/* Private constants **********************************************************/
/* Private macro **************************************************************/
/* Private variables **********************************************************/
extern UART_HandleTypeDef huart1;
extern TIM_HandleTypeDef htim6;
/* Private function prototypes ************************************************/
/**
******************************************************************************
* @brief 定时器周期溢出回调 函数
* @param htim
* @return None
******************************************************************************
*/
#if !SHD_TICK_CNT_FROM_EXT
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 关键:通过Instance区分定时器(多个定时器共用此回调)
if (htim->Instance == TIM6)
{
SchedulerTickISR();
}
}
#endif
/**
******************************************************************************
* @brief MCU初始化 函数
* @param ms
* @return None
******************************************************************************
*/
void McuInit(void)
{
// 开启TIM6更新中断
__HAL_TIM_ENABLE_IT(&htim6, TIM_IT_UPDATE);
// 配置NVIC(中断优先级)
HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 1, 0); // 抢占优先级1,子优先级0(可调整)
HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn); // 使能TIM6的NVIC中断通道
// 启动TIM6(带中断模式)
HAL_TIM_Base_Start_IT(&htim6);
}
/********************************* END OF FILE ********************************/
cpp
/** @copyright Copyright (c) 2014-2025 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file mcu.h
* @brief mcu 头文件
* @details
*
* @author 匠在江湖(000000)
* @version V01.00
* @date 2014-05-08
******************************************************************************
* @attention
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2014-05-08 | 匠在江湖(000000) | 创建初版 |
******************************************************************************
*/
#ifndef MCU_H_
#define MCU_H_
/* Includes *******************************************************************/
#include "main.h"
#include "stm32f4xx_hal.h"
#include "stm32f4xx.h"
#ifdef __cplusplus
extern "C"{
#endif
/* Exported types *************************************************************/
/* Exported constants *********************************************************/
/* Exported macro *************************************************************/
/* Exported functions *********************************************************/
extern void McuInit(void);
#ifdef __cplusplus
}
#endif
#endif /* MCU_H_ */
/********************************* END OF FILE ********************************/
主函数应用示例代码
cpp
/** @copyright Copyright (c) 2014-2025 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file
* @brief 主函数 源文件
* @author 匠在江湖(000000)
* @version V03.00
* @date 2025-10-22
*
* @details
* - 该文件包含程序入口函数`main()`,是整个程序的核心控制文件,负责系统初始化流程调度和主循环逻辑管理;
* - 初始化流程:系统时钟配置 → 外设(GPIO/I2C/SPI/UART)初始化 → 模块(SHT30/NRF24L01/OLED)初始化 → 中断配置与优先级设置;
* - 主循环任务:周期性数据采集(1s/次)、无线通信处理、按键交互、OLED显示,实时监控系统状态(传感器/模块在线状态)。
*
******************************************************************************
* @attention
* 1. 本文件为程序入口,无需被其他用户文件包含,其他文件需使用基础定义时需包含`base.h`;
* 2. 类型别名与C99标准兼容,禁止在其他文件中重定义base.h中的基础类型;
* 3. 宏`UNUSED`用于标记未使用参数,需配合编译器开启`-Wno-unused-parameter`选项避免告警;
* 4. 修改本文件需同步更新版本号,并在修改日志中明确变更内容,禁止修改主流程框架(初始化→主循环)。
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2018-08-17 | 匠在江湖(000000) | 创建初版 |
* | V02.00 | 2022-04-20 | 匠在江湖(000000) | 优化描述 |
* | V03.00 | 2025-10-22 | 匠在江湖(000000) | 删除依赖,修复BUG |
******************************************************************************
*/
/** @defgroup Main Main
* @brief Main module driver
* @{
*/
/* Includes *******************************************************************/
#include <stdio.h> //库头文件
#include "mcu.h" //其他文件头文件
#include "delay.h"
#include "scheduler.h" //其他文件头文件
#include "main_loop.h" //自己头文件
/* Private typedef ************************************************************/
/* Private constants **********************************************************/
/* Private macro **************************************************************/
/* Private variables **********************************************************/
/* Private function prototypes ************************************************/
void Led0Drv(void)
{
HAL_GPIO_TogglePin(LED0_GPIO_Port, LED0_Pin);
printf("LED000 \r\n");
}
void Dislay(void)
{
printf("Dislay \r\n");
}
void Led1Drv(void)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
printf("LED1 \r\n");
// DelayUS(300);
// printf("LED11 \r\n");
// DelayUS(300);
// printf("LED111 \r\n");
}
/**
******************************************************************************
* @brief main 函数
* @param None
* @retval None
******************************************************************************
*/
void MainLoop(void)
{
// HAL层初始化
McuInit();
// 驱动层初始化
// 服务层初始化
// 应用层初始化
// 调度器初始化
SchedulerInit();
// 创建任务
SchedulerCreateTask(Led0Drv, 1000);
SchedulerCreateTask(Dislay, 3000);
SchedulerCreateTask(Led1Drv, 5000);
printf("start \r\n");
while(1)
{
// DelayUS(500000);
// Led0Drv();
// Led1Drv();
SchedulerRun(); // 运行调度器
}
}
/** @} end of Main */
/********************************* END OF FILE ********************************/
cpp
/** @copyright Copyright (c) 2018-2025 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @file
* @brief 主函数 头文件
* @details
*
* @author 匠在江湖(000000)
* @version V01.00
* @date 2018-08-17
******************************************************************************
* @attention
*
* @par 修改日志:
* | 版本 | 日期 | 作者(ID) | 说明 |
* |--------|------------|------------------|----------|
* | V01.00 | 2018-08-17 | 匠在江湖(000000) | 创建初版 |
******************************************************************************
*/
#ifndef MAIN_LOOP_H_
#define MAIN_LOOP_H_
/* Includes *******************************************************************/
#ifdef __cplusplus
extern "C"{
#endif
/* Exported types *************************************************************/
/* Exported constants *********************************************************/
/* Exported macro *************************************************************/
/* Exported functions *********************************************************/
extern void MainLoop(void);
#ifdef __cplusplus
}
#endif
#endif /* MAIN_LOOP_H_ */
/********************************* END OF FILE ********************************/
文档说明 README
cpp
/** @copyright Copyright (c) 2014-2025 Craftsman In Jianghu. All rights reserved.
******************************************************************************
* @mainpage 温湿度采集与远程传输系统
* @brief 基于STM32F103C8T6的环境温湿度采集(SHT30)、无线传输(NRF24L01)、本地交互(OLED+按键)系统
*
* @par 功能简介
* - 初始化模块:MCU外设(GPIO/I2C/SPI/UART)、传感器、无线模块、OLED的统一初始化与自检;
* - 数据采集模块:1s周期读取SHT30数据,通过滑动平均滤波消除干扰,确保采集精度;
* - 无线通信模块:NRF24L01(SPI接口)上传数据至接收端,接收上位机控制指令与状态反馈;
* - 人机交互模块:3个独立按键(功能切换/参数设置/确认),0.96寸OLED实时显示温湿度、通信状态及告警信息;
* - 主调度模块:协调各模块时序,处理传感器离线、通信中断等异常场景,触发声光告警提示。
*
******************************************************************************
* @par 项目信息
* - 项目编号:BA001A
* - 启动日期:2018-06-18
* - 完成日期:2018-10-08
*
* @par 硬件配置
* - MCU 型号:STM32F103C8T6(最小系统板)
* - 系统时钟:内部 48 MHz
* - 供电电压:3.3 V(DC稳压供电)
* - 核心外设:
* - SHT30温湿度传感器(I2C接口,通信速率100kHz,测量精度±0.3℃/±2%RH);
* - NRF24L01无线模块(SPI接口,通信速率1MHz,工作频段2.4GHz);
* - 0.96寸OLED显示屏(I2C接口,分辨率128×64);
* - 3路独立按键(GPIO输入模式,配置上拉电阻防抖动)。
*
* @par 软件信息
* - 开发环境:Keil MDK V5.36 / STM32CubeMX V6.9.0
* - 核心配置:UART1(115200bps/8N1,用于调试打印)、定时器TIM2(1ms中断,提供系统时基)、NVIC优先级分组2;
* - 依赖说明:基于C99标准开发,依赖ST HAL库。
*
******************************************************************************
* @par 版本日志
* | 硬件版本 | 软件版本 | 日期 | MD5校验码 | 作者(ID) | 说明 |
* |----------|----------|------------|-----------|------------------|----------|
* | V1.0 | V1.0 | 2018-08-17 | 0762AC0D | 匠在江湖(000000) | 创建初版 |
* | V1.0 | V2.0 | 2025-10-22 | 7A3F9D2B | 匠在江湖(000000) | 优化滑动平均滤波算法,修复通信中断重连BUG |
*
* @attention
* -# 硬件接线严格参照《BA001A硬件手册》,电源正负极禁止接反(避免烧毁MCU/传感器);
* -# NRF24L01模块需远离强电磁干扰,天线朝向开阔方向,确保通信距离与稳定性;
* -# 定期清理SHT30传感器探头灰尘,每3个月校准一次采集精度;
* -# 代码修改后需同步更新版本号、MD5校验码,并补充变更记录,禁止跳版本迭代。
******************************************************************************
*/