裸机单片机任务调度器实现:基于规范分层(COM/APP/SRV/DRV)架构,(附 任务调度器 / 微秒延时函数 / 串口重定向 源码)

工程目录结构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校验码,并补充变更记录,禁止跳版本迭代。
  ******************************************************************************
  */
相关推荐
gaize12138 小时前
服务器怎么选择与配置才能满足企业需求?
运维·服务器·架构
点灯小铭8 小时前
基于单片机的智能洗碗机控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计
加个鸡腿儿8 小时前
经验分享2:SSR 项目中响应式组件的闪动陷阱与修复实践
前端·css·架构
一条咸鱼_SaltyFish9 小时前
[Day15] 若依框架二次开发改造记录:定制化之旅 contract-security-ruoyi
java·大数据·经验分享·分布式·微服务·架构·ai编程
清风6666669 小时前
基于单片机的电加热炉智能温度与液位PID控制系统设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计·期末大作业
一路往蓝-Anbo10 小时前
第五篇:硬件接口的生死劫 —— GPIO 唤醒与测量陷阱
c语言·驱动开发·stm32·单片机·嵌入式硬件
早日退休!!!10 小时前
ARM A核、ARM M核、X86与RISC-V架构:寄存器作用及上下文处理差异报告
arm开发·架构·risc-v
数说星榆18111 小时前
在线高清泳道图制作工具 无水印 PC
大数据·人工智能·架构·机器人·流程图
逑之11 小时前
C语言笔记16:文件操作
c语言·笔记·单片机