STM32从零到量产开发:四路继电器工业控制模块开发— 继电器模块(bsp_relay)设计说明
·
STM32从零到量产开发:四路继电器工业控制模块开发— 继电器模块(bsp_relay)设计说明
1. 模块概述
bsp_relay 模块是 STM32 工业控制板级支持包(BSP)中的核心组件,专为四路继电器输出端子设计。该模块对上层应用暴露统一的逻辑接口(1=吸合,0=断开),屏蔽了底层硬件“低电平有效”的极性差异。此外,模块内置了寿命管理机制,支持动作次数统计与超限告警,满足工业级设备运维监控需求。
核心特性
- 基础控制:支持单路及批量设置、读取、翻转继电器通断状态。
- 状态缓存:内部维护状态位图,避免频繁读取 GPIO 寄存器,提高运行效率。
- 寿命管理:记录单路累计动作次数(仅在真实翻转时计数),并提供寿命超限告警位图。
- 安全保护:所有接口均包含通道边界合法性校验。
2. 硬件引脚映射
本模块依赖 CubeMX 生成的 main.h 中的引脚宏定义。硬件映射关系如下:
| 通道号 | 逻辑状态 | 硬件电平 | GPIO 端口 | 引脚宏 |
|---|---|---|---|---|
| RELAY 1 | 吸合 (ON) | 低电平 (RESET) | GPIOB | RELAY1_Pin (PB6) |
| RELAY 1 | 断开 (OFF) | 高电平 (SET) | GPIOB | RELAY1_Pin (PB6) |
| RELAY 2 | 吸合 (ON) | 低电平 (RESET) | GPIOB | RELAY2_Pin (PB7) |
| RELAY 2 | 断开 (OFF) | 高电平 (SET) | GPIOB | RELAY2_Pin (PB7) |
| RELAY 3 | 吸合 (ON) | 低电平 (RESET) | GPIOB | RELAY3_Pin (PB8) |
| RELAY 3 | 断开 (OFF) | 高电平 (SET) | GPIOB | RELAY3_Pin (PB8) |
| RELAY 4 | 吸合 (ON) | 低电平 (RESET) | GPIOB | RELAY4_Pin (PB9) |
| RELAY 4 | 断开 (OFF) | 高电平 (SET) | GPIOB | RELAY4_Pin (PB9) |
极性说明:GPIO 初始化为
SET(高电平)表示断开(OFF),写低电平表示吸合(ON)。本驱动对上层统一暴露逻辑值:1= 吸合,0= 断开。
3. API 接口详解
3.1 模块初始化
void BSP_Relay_Init(void);
- 功能描述:初始化继电器模块,上电时将全部 4 路继电器置为断开状态。
- 参数:无。
- 返回值:无。
- 实现细节:硬件 GPIO 初始化由
MX_GPIO_Init完成。此函数通过HAL_GPIO_WritePin将 PB6~PB9 统一输出高电平,并将内部状态缓存s_relay_state清零。
3.2 单路状态设置
void BSP_Relay_Set(uint8_t ch, uint8_t state);
- 功能描述:设置指定通道的继电器通断状态。
- 参数:
ch:通道号,取值范围1~4。超出范围将触发边界保护直接返回。state:目标状态,RELAY_ON(1) 吸合 /RELAY_OFF(0) 断开。
- 返回值:无。
- 实现细节:
- 校验通道合法性。
- 对比目标状态与内部缓存状态,若状态无变化则直接返回,避免无效操作。
- 状态发生真实翻转时,更新硬件引脚电平,更新内部状态缓存,并将该通道的动作计数
s_relay_act_count累加 1。
3.3 单路状态获取
uint8_t BSP_Relay_Get(uint8_t ch);
- 功能描述:获取指定继电器当前的逻辑通断状态。
- 参数:
ch:通道号1~4。 - 返回值:
RELAY_ON(1) 吸合 /RELAY_OFF(0) 断开。非法通道返回0。 - 实现细节:直接从内部状态缓存
s_relay_state中提取对应 bit 位,无需读取硬件寄存器。
3.4 批量状态设置
void BSP_Relay_SetAll(uint8_t mask);
- 功能描述:采用位图掩码批量设置所有继电器状态。
- 参数:
mask:4位有效掩码。bit0对应 RELAY1,bit1对应 RELAY2,以此类推。1= 吸合,0= 断开。
- 返回值:无。
- 实现细节:内部通过循环调用
BSP_Relay_Set逐路完成设置,自动继承状态防抖与计数逻辑。
3.5 批量状态获取
uint8_t BSP_Relay_GetAll(void);
- 功能描述:批量读取所有继电器当前状态。
- 参数:无。
- 返回值:4位有效掩码,
bit0~bit3对应通道 1~4,高 4 位强制清零。
3.6 单路状态翻转
void BSP_Relay_Toggle(uint8_t ch);
- 功能描述:翻转单路继电器当前状态(吸合 ↔ 断开)。
- 参数:
ch:通道号1~4。 - 返回值:无。
- 实现细节:读取当前状态后取反,调用
BSP_Relay_Set执行。若状态发生实际翻转,会自动累加动作计数。
3.7 动作次数获取
uint32_t BSP_Relay_GetActionCount(uint8_t ch);
- 功能描述:获取指定继电器上电后的累计动作次数,用于寿命统计。
- 参数:
ch:通道号1~4。 - 返回值:累计动作次数(
uint32_t)。掉电不保存,仅状态真实翻转时计数 +1。非法通道返回0。
3.8 寿命告警获取
uint8_t BSP_Relay_GetLifeAlarmMask(void);
- 功能描述:获取继电器寿命超限告警位图。
- 参数:无。
- 返回值:4位告警掩码。
bit0= RELAY1 …bit3= RELAY4。bit=1代表该通道动作次数超出寿命阈值。 - 实现细节:遍历内部计数数组,当动作次数
>= RELAY_LIFE_THRESHOLD(默认 100,000 次)时置位对应标志。
4. 内部实现逻辑简述
4.1 状态缓存机制
模块内部定义了静态变量 s_relay_state(uint8_t 类型),使用低 4 位分别映射 4 路继电器的逻辑状态。所有的读取操作(Get / GetAll)均基于此缓存进行,极大降低了总线访问频率,提升了系统实时性。
4.2 硬件抽象层
通过 relay_pin_t 结构体与 s_relay_tbl 静态常量数组,将通道号(1~4)与底层硬件(GPIO_Port, Pin)解耦。后续若发生硬件引脚变更,仅需修改此映射表,无需改动业务逻辑代码。
4.3 寿命管理策略
- 阈值定义:通过宏
RELAY_LIFE_THRESHOLD定义寿命上限(当前设为 100,000 次)。 - 精准计数:仅在
BSP_Relay_Set中检测到目标状态与旧状态不一致时,才执行硬件写入与计数累加,杜绝了重复赋值导致的计数虚高问题。 - 告警输出:
BSP_Relay_GetLifeAlarmMask提供实时的位图查询,方便上层应用通过单一接口获取全局健康状态。
bsp_relay.h
/**
******************************************************************************
* @file bsp_relay.h
* @brief 4通道继电器驱动 - 对应产品规格书 3.2 继电器输出端子
*
* 硬件映射 (来自 CubeMX main.h):
* RELAY1 -> PB6, RELAY2 -> PB7, RELAY3 -> PB8, RELAY4 -> PB9
*
* 极性说明:
* GPIO 初始化为 SET(高电平)= 断开(OFF)
* 写低电平 = 吸合(ON)
* 本驱动对上层暴露逻辑值: 1=吸合, 0=断开
*
* 接口说明:
* 1. 基础控制:单路/批量设置、读取、翻转继电器通断状态
* 2. 寿命管理:动作次数统计、寿命超限告警位图,用于设备运维监控(需求#3)
******************************************************************************
*/
#ifndef __BSP_RELAY_H
#define __BSP_RELAY_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
/* 通道数量 */
#define RELAY_CH_NUM 4u
/* 逻辑状态定义 (上层使用) */
#define RELAY_OFF 0u
#define RELAY_ON 1u
/**
* @brief 初始化继电器 (全部置为断开RELAY_OFF)
* @note GPIO引脚硬件初始化由 MX_GPIO_Init 完成,本函数仅统一设置上电初始断开状态
*/
void BSP_Relay_Init(void);
/**
* @brief 设置单路继电器通断状态
* @param ch: 通道号 1~4,超出范围会做边界保护
* @param state: RELAY_ON 吸合 / RELAY_OFF 断开
*/
void BSP_Relay_Set(uint8_t ch, uint8_t state);
/**
* @brief 获取单路继电器当前逻辑通断状态
* @param ch: 通道号 1~4
* @retval RELAY_ON 吸合 / RELAY_OFF 断开
*/
uint8_t BSP_Relay_Get(uint8_t ch);
/**
* @brief 批量设置所有继电器状态,采用位图掩码控制
* @param mask: 4位有效掩码
* bit0 对应 RELAY1,bit1 对应 RELAY2,bit2 对应 RELAY3,bit3 对应 RELAY4
* bit位=1 对应通道吸合,bit位=0 对应通道断开
*/
void BSP_Relay_SetAll(uint8_t mask);
/**
* @brief 批量读取所有继电器当前状态,返回位图掩码
* @retval 4位有效掩码
* bit0 对应 RELAY1,bit1 对应 RELAY2,bit2 对应 RELAY3,bit3 对应 RELAY4
* bit位=1 对应通道当前吸合,bit位=0 对应通道断开
*/
uint8_t BSP_Relay_GetAll(void);
/**
* @brief 翻转单路继电器当前状态:吸合→断开 / 断开→吸合
* @param ch: 通道号 1~4
* @note 状态发生实际翻转时,会自动累加该通道动作计数
*/
void BSP_Relay_Toggle(uint8_t ch);
/**
* @brief 获取单路继电器累计动作次数,用于寿命统计
* @param ch: 通道号 1~4
* @retval 上电后累计动作次数,掉电不保存;仅状态真实翻转时计数+1
*/
uint32_t BSP_Relay_GetActionCount(uint8_t ch);
/**
* @brief 获取继电器寿命超限告警位图
* @retval 4位告警掩码
* bit0=RELAY1 ... bit3=RELAY4,bit=1 代表该通道动作次数超出寿命阈值(需求#3)
*/
uint8_t BSP_Relay_GetLifeAlarmMask(void);
#ifdef __cplusplus
}
#endif
#endif /* __BSP_RELAY_H */
bsp_relay.c
/**
******************************************************************************
* @file bsp_relay.c
* @brief 4通道继电器驱动实现
******************************************************************************
*/
#include "bsp_relay.h"
#include "main.h"
/* ===================== 硬件与阈值宏定义 ===================== */
/* 寿命告警阈值:单路动作次数超过此值视为达到寿命,置位告警标志 */
#define RELAY_LIFE_THRESHOLD 100000u
/* ===================== 硬件引脚结构体 ===================== */
/* 继电器引脚映射结构体 */
typedef struct
{
GPIO_TypeDef *port;
uint16_t pin; //HAL库管脚类型uint16_t,与工程引脚宏匹配
} relay_pin_t;
/* 继电器通道硬件引脚映射表 ,(通道号 1~4 对应数组索引 0~3)*/
static const relay_pin_t s_relay_tbl[RELAY_CH_NUM] =
{
{ RELAY1_GPIO_Port, RELAY1_Pin },
{ RELAY2_GPIO_Port, RELAY2_Pin },
{ RELAY3_GPIO_Port, RELAY3_Pin },
{ RELAY4_GPIO_Port, RELAY4_Pin },
};
/* ===================== 模块内部静态变量 ===================== */
/* 4路继电器状态缓存:bit0=通道1、bit1=通道2、bit2=通道3、bit3=通道4,避免频繁读取GPIO */
static uint8_t s_relay_state = 0u;
/* 每路继电器累计动作次数,仅状态真实翻转时计数,用于继电器寿命管理 */
static uint32_t s_relay_act_count[RELAY_CH_NUM] = {0u, 0u, 0u, 0u};
/* ===================== 对外公共接口 ===================== */
/**
* @brief 继电器模块初始化,上电全部断开
* @note 继电器低电平吸合,初始化输出高电平断开所有通道
*/
void BSP_Relay_Init(void)
{
HAL_GPIO_WritePin(GPIOB, RELAY1_Pin | RELAY2_Pin | RELAY3_Pin | RELAY4_Pin, GPIO_PIN_SET);
s_relay_state = 0u;
}
/**
* @brief 设置单路继电器通断
* @param ch: 通道号 1~4
* @param state: 0断开,非0吸合
* @note 状态无变化时直接返回,仅真实翻转时更新硬件、状态缓存、动作计数
*/
void BSP_Relay_Set(uint8_t ch, uint8_t state)
{
uint8_t idx;
uint8_t old_state;
/* 通道合法性校验 */
if (ch < 1u || ch > RELAY_CH_NUM)
{
return;
}
idx = ch - 1u;
old_state = (s_relay_state >> idx) & 0x01u;
/* 标准化目标状态:0=断开,1=吸合 */
uint8_t target = (state != 0u) ? 1u : 0u;
if (target == old_state)
{
return;
}
if (target)
{
/* 吸合:低有效,输出RESET */
HAL_GPIO_WritePin(s_relay_tbl[idx].port, s_relay_tbl[idx].pin, GPIO_PIN_RESET);
s_relay_state |= (uint8_t)(1u << idx); //位操作是int类型32位,整型提升后截断低8位
}
else
{
/* 断开:输出SET高电平 */
HAL_GPIO_WritePin(s_relay_tbl[idx].port, s_relay_tbl[idx].pin, GPIO_PIN_SET);
s_relay_state &= (uint8_t)~(1u << idx);
}
s_relay_act_count[idx]++;
}
/**
* @brief 批量设置全部4路继电器
* @param mask: 状态掩码 bit0~bit3对应通道1~4,1吸合 0断开
*/
void BSP_Relay_SetAll(uint8_t mask)
{
uint8_t i;
for (i = 0u; i < RELAY_CH_NUM; i++)
{
BSP_Relay_Set(i + 1u, (uint8_t)((mask >> i) & 0x01u));
}
}
/**
* @brief 获取单路继电器当前状态
* @param ch: 通道1~4
* @retval 0断开 / 1吸合,非法通道返回0
*/
uint8_t BSP_Relay_Get(uint8_t ch)
{
if (ch < 1u || ch > RELAY_CH_NUM)
{
return 0u;
}
return (s_relay_state >> (ch - 1u)) & 0x01u;
}
/**
* @brief 获取四路继电器整体状态掩码
* @retval 低4位有效,bit0~bit3对应1~4通道,高4位清零屏蔽无效位
*/
uint8_t BSP_Relay_GetAll(void)
{
return s_relay_state & 0x0Fu;
}
/**
* @brief 翻转单路继电器当前状态
* @param ch: 通道1~4
*/
void BSP_Relay_Toggle(uint8_t ch)
{
if (ch < 1u || ch > RELAY_CH_NUM)
{
return;
}
BSP_Relay_Set(ch, (uint8_t)(BSP_Relay_Get(ch) ? 0u : 1u));
}
/**
* @brief 获取单路继电器累计动作次数
* @param ch: 通道1~4
* @retval 动作总次数,非法通道返回0
*/
uint32_t BSP_Relay_GetActionCount(uint8_t ch)
{
if (ch < 1u || ch > RELAY_CH_NUM)
{
return 0u;
}
return s_relay_act_count[ch - 1u];
}
/**
* @brief 获取继电器寿命告警位图
* @retval bit0~bit3对应1~4通道,bit=1表示该路动作次数超寿命阈值
*/
uint8_t BSP_Relay_GetLifeAlarmMask(void)
{
uint8_t i;
uint8_t mask = 0u;
for (i = 0u; i < RELAY_CH_NUM; i++)
{
if (s_relay_act_count[i] >= RELAY_LIFE_THRESHOLD)
{
mask |= (uint8_t)(1u << i);
}
}
return mask;
}
更多推荐


所有评论(0)