问题背景继电器模块的触发方式有两种高电平触发常见于低电平驱动型继电器模组和低电平触发常见于光耦隔离型模组。如果业务代码直接操作 GPIO 而不区分触发模式换一个模块就要改代码。本文给出一个简洁的继电器驱动通过active_mode字段封装两种触发模式的差异提供初始化、开关、翻转等标准接口。驱动设计核心思路驱动使用relay_active_t枚举区分触发模式typedef enum { RELAY_ACTIVE_HIGH 0, // 高电平触发输出 SET 吸合 RELAY_ACTIVE_LOW // 低电平触发输出 RESET 吸合 } relay_active_t;所有操作函数relay_on、relay_off都根据active_mode计算实际物理电平调用方只需关心开/关逻辑。toggle 实现relay_toggle通过HAL_GPIO_ReadPin读取当前引脚电平结合active_mode判断继电器是否已吸合然后翻转。这种读-判断-写的方式比维护内部状态变量更可靠状态始终与硬件一致。API 参考函数说明relay_init(cb, gpio_x, gpio_pin, active_mode, set_pin)初始化并关闭继电器relay_on(cb)吸合继电器relay_off(cb)释放继电器relay_set(cb, val)设置状态0关, 非0开relay_toggle(cb)翻转状态使用示例#include driver_relay.h relay_control_t relay1; int main(void) { // 初始化高电平触发GPIOB_PIN_12 relay_init(relay1, (uint32_t)GPIOB, GPIO_PIN_12, RELAY_ACTIVE_HIGH, (relay_gpio_set_func_t)HAL_GPIO_WritePin); relay_on(relay1); // 吸合 HAL_Delay(1000); relay_off(relay1); // 释放 HAL_Delay(1000); relay_toggle(relay1); // 再次吸合 HAL_Delay(1000); relay_set(relay1, 0); // 通过变量控制释放 while (1); }注意事项gpio_x参数为uint32_t类型传入 GPIO 端口时需要强转set_pin函数指针不能为 NULL否则relay_init返回RELAY_ERRORrelay_toggle内部调用HAL_GPIO_ReadPin需要该引脚已在 CubeMX 中正确配置初始化后继电器默认处于关闭释放状态总结本驱动仅 ~150 行代码核心价值在于用active_mode字段消除了不同继电器模块在触发方式上的差异让上层代码完全透明。完整源码driver_relay.h/** * file driver_relay.h * author Jiang Ying * brief 继电器驱动头文件 * version V1.0.0 * date 2026.03.28 * note 该文件包含继电器驱动函数声明提供初始化、开启、关闭、 * 设置和切换功能。用户可以通过调用这些函数来控制继电器的状态。 */ #ifndef _DRIVER_RELAY_H #define _DRIVER_RELAY_H /* Includes ------------------------------------------------------------------*/ #include stm32f1xx_hal.h #include stdint.h /** * brief 继电器状态枚举 * note 定义继电器的两种状态关闭和开启 */ typedef enum { RELAY_ERROR 0, RELAY_OK !RELAY_ERROR }relay_state_t; /** * brief 继电器状态枚举 * note 定义继电器的两种状态关闭和开启 */ typedef enum { RELAY_RESET 0, RELAY_SET }relay_set_t; /** * brief 继电器触发模式枚举 * note 定义继电器的不同触发模式 */ typedef enum { RELAY_ACTIVE_HIGH 0, /* 高电平触发模式GPIO输出高电平时继电器吸合 */ RELAY_ACTIVE_LOW /* 低电平触发模式GPIO输出低电平时继电器吸合 */ }relay_active_t; /** * brief GPIO设置函数指针类型 * note 用于设置GPIO引脚状态的函数指针类型 */ typedef relay_state_t (*relay_gpio_set_func_t)(uint32_t, uint32_t, uint32_t); /** * brief 继电器控制结构体 * note 包含继电器的GPIO配置、触发模式和控制函数 */ typedef struct { uint32_t gpio_x; /* GPIO端口 */ uint32_t gpio_pin; /* GPIO引脚 */ relay_active_t active_mode; /* 继电器触发模式 */ relay_gpio_set_func_t set_pin; /* GPIO设置函数指针 */ }relay_control_t; relay_state_t relay_init(relay_control_t *cb, uint32_t gpio_x, uint32_t gpio_pin, relay_active_t active_mode, relay_gpio_set_func_t set_pin); relay_state_t relay_on(relay_control_t *cb); relay_state_t relay_off(relay_control_t *cb); relay_state_t relay_set(relay_control_t *cb, uint32_t val); relay_state_t relay_toggle(relay_control_t *cb); #endif /* _DRIVER_RELAY_H */ driver_relay.c/** * file driver_relay.c * author Jiang Ying * brief 继电器驱动源文件 * version V1.0.0 * date 2026.03.28 */ /* Includes ------------------------------------------------------------------*/ #include driver_relay.h /** * brief 打开继电器 * param cb 继电器控制结构体指针 * return 继电器操作状态 * note 根据继电器的触发模式决定输出高电平还是低电平 */ relay_state_t relay_on(relay_control_t *cb) { relay_state_t state RELAY_ERROR; uint32_t pin_state; /* 根据触发模式决定实际输出的电平 */ if (cb-active_mode RELAY_ACTIVE_HIGH) { pin_state RELAY_SET; /* 高电平触发模式输出高电平吸合继电器 */ } else { pin_state RELAY_RESET; /* 低电平触发模式输出低电平吸合继电器 */ } state cb-set_pin(cb-gpio_x, cb-gpio_pin, pin_state); return state; } /** * brief 关闭继电器 * param cb 继电器控制结构体指针 * return 继电器操作状态 * note 根据继电器的触发模式决定输出高电平还是低电平 */ relay_state_t relay_off(relay_control_t *cb) { relay_state_t state RELAY_ERROR; uint32_t pin_state; /* 根据触发模式决定实际输出的电平 */ if (cb-active_mode RELAY_ACTIVE_HIGH) { pin_state RELAY_RESET; /* 高电平触发模式输出低电平释放继电器 */ } else { pin_state RELAY_SET; /* 低电平触发模式输出高电平释放继电器 */ } state cb-set_pin(cb-gpio_x, cb-gpio_pin, pin_state); return state; } /** * brief 设置继电器状态 * param cb 继电器控制结构体指针 * param val 继电器状态值0表示关闭非0表示打开 * return 继电器操作状态 * note 根据继电器的触发模式和状态值决定输出高电平还是低电平 */ relay_state_t relay_set(relay_control_t *cb, uint32_t val) { if (val) { return relay_on(cb); /* 打开继电器 */ } else { return relay_off(cb); /* 关闭继电器 */ } } /** * brief 初始化继电器控制结构体 * param cb 继电器控制结构体指针 * param gpio_x GPIO端口 * param gpio_pin GPIO引脚 * param active_mode 继电器触发模式 * param set_pin GPIO设置函数指针 * return 继电器操作状态 * note 配置继电器控制结构体并初始化继电器为关闭状态 */ relay_state_t relay_init(relay_control_t *cb, uint32_t gpio_x, uint32_t gpio_pin, relay_active_t active_mode, relay_gpio_set_func_t set_pin) { relay_state_t state RELAY_ERROR; if (cb NULL || set_pin NULL) { return RELAY_ERROR; } /* 配置继电器控制结构体 */ cb-gpio_x gpio_x; cb-gpio_pin gpio_pin; cb-active_mode active_mode; cb-set_pin set_pin; /* 初始化继电器为关闭状态 */ state relay_off(cb); return state; } /** * brief 切换继电器状态 * param cb 继电器控制结构体指针 * return 继电器操作状态 * note 如果继电器当前是打开状态则关闭如果是关闭状态则打开 */ relay_state_t relay_toggle(relay_control_t *cb) { relay_state_t state RELAY_ERROR; uint32_t current_state; uint32_t new_state; /* 获取当前引脚状态 */ if (cb-active_mode RELAY_ACTIVE_HIGH) { /* 高电平触发模式下引脚为高表示继电器打开 */ current_state (HAL_GPIO_ReadPin((GPIO_TypeDef*)cb-gpio_x, cb-gpio_pin) GPIO_PIN_SET) ? 1 : 0; } else { /* 低电平触发模式下引脚为低表示继电器打开 */ current_state (HAL_GPIO_ReadPin((GPIO_TypeDef*)cb-gpio_x, cb-gpio_pin) GPIO_PIN_RESET) ? 1 : 0; } /* 切换状态 */ new_state current_state ? 0 : 1; /* 设置新状态 */ state relay_set(cb, new_state); return state; }