STM32从零到量产开发:四路继电器工业控制模块开发--- 继电器模块bsp_di.h与 bsp_di.c文件
bsp_di.h代码实现
c
/**
******************************************************************************
* @file bsp_di.h
* @brief 4通道光耦隔离数字输入驱动 - 对应产品规格书 3.3 数字输入端子
*
* 硬件映射 (来自 CubeMX main.h):
* DI1 -> PB0, DI2 -> PB1, DI3 -> PB10, DI4 -> PB11
*
* 极性说明:
* GPIO 配置为上拉输入, 光耦导通时引脚被拉低
* 规格书 3.3: "输入为低有效" -> 读到低电平 = 外部有输入信号
* 本驱动对上层统一暴露逻辑值: 1=有输入, 0=无输入
*
* 消抖机制说明:
* 软件定时消抖,多次采样电平保持 DEBOUNCE_CONFIRM_MS 毫秒一致,才更新稳定状态
* 扫描依赖系统毫秒时基 HAL_GetTick()
******************************************************************************
*/
#ifndef __BSP_DI_H
#define __BSP_DI_H
#ifdef __cplusplus
extern "C" {
#endif
#include "main.h"
/* 数字输入总通道数量 */
#define DI_CH_NUM 4u
/* 软件消抖确认时长配置说明 */
/* 如果按键输入硬件已经有消抖电容,工业场景软件消抖取5~10ms */
/* 如果是无光耦隔离/外部干接点/传感器输入,软件消抖不能省,建议设 3~5ms */
#define DI_DEBOUNCE_MS 10u
/**
* @brief 初始化4路数字输入GPIO,初始化消抖缓存状态
* @note GPIO硬件模式由MX_GPIO_Init完成配置,本函数仅初始化内部消抖变量
*/
void BSP_DI_Init(void);
/**
* @brief DI通道消抖定时扫描函数
* @note 必须在主循环固定周期调用,推荐扫描周期 1~5ms;依赖 HAL_GetTick() 获取系统毫秒时基
*/
void BSP_DI_Scan(void);
/**
* @brief 获取单路DI经过软件消抖后的稳定逻辑状态(业务正常读取接口)
* @param ch: 通道编号 1~4
* @retval 1=外部有输入信号,0=无输入信号
*/
uint8_t BSP_DI_Get(uint8_t ch);
/**
* @brief 批量获取全部4路DI消抖后稳定状态,返回位图掩码
* @retval 4位有效掩码
* bit0对应DI1、bit1对应DI2、bit2对应DI3、bit3对应DI4;bit=1代表该通道有输入
*/
uint8_t BSP_DI_GetAll(void);
/**
* @brief 获取单路DI当前实时原始电平(未经过软件消抖,仅调试排查硬件时使用)
* @param ch: 通道编号 1~4
* @retval 1=硬件检测到输入,0=无输入
*/
uint8_t BSP_DI_GetRaw(uint8_t ch);
#ifdef __cplusplus
}
#endif
#endif /* __BSP_DI_H */
bsp_di.c代码实现
c
/**
******************************************************************************
* @file bsp_di.c
* @brief 4通道数字输入驱动实现 (含软件消抖)
******************************************************************************
*/
#include "bsp_di.h"
#include "main.h"
/* ===================== 硬件引脚定义 ===================== */
/* DI 引脚映射结构体 (通道号 1~4 对应数组索引 0~3) */
typedef struct
{
GPIO_TypeDef *port;
uint16_t pin;
} di_pin_t;
/* 硬件引脚映射表 */
static const di_pin_t s_di_tbl[DI_CH_NUM] =
{
{ DI1_GPIO_Port, DI1_Pin },
{ DI2_GPIO_Port, DI2_Pin },
{ DI3_GPIO_Port, DI3_Pin },
{ DI4_GPIO_Port, DI4_Pin },
};
/* ===================== 消抖状态结构定义 ===================== */
/* 单通道消抖状态结构体 */
typedef struct
{
uint8_t raw; /* 实时原始引脚电平,仅底层扫描使用,上层禁止读取 */
uint8_t stable; /* 消抖完成稳定状态,业务/Modbus读取 */
uint32_t last_change; /* 电平跳变时间戳(ms),消抖计时基准 */
} di_debounce_t;
/* 四路独立消抖状态缓存 */
static di_debounce_t s_di[DI_CH_NUM];
/* ===================== 内部底层私有函数 ===================== */
/**
* @brief 底层读取单通道原始引脚电平
* @param idx 内部数组下标 0~3
* @retval 1 = 低电平有输入,0 = 无输入
* @note 硬件电平适配层,仅此处修改高低有效逻辑,上层全部无感
*/
static uint8_t DI_ReadRawLevel(uint8_t idx)
{
return (HAL_GPIO_ReadPin(s_di_tbl[idx].port, s_di_tbl[idx].pin) == GPIO_PIN_RESET) ? 1u : 0u;
}
/* ===================== 对外公共接口 ===================== */
/**
* @brief DI模块初始化,初始化所有通道消抖状态
*/
void BSP_DI_Init(void)
{
uint8_t i;
uint32_t now = HAL_GetTick();
for (i = 0u; i < DI_CH_NUM; i++)
{
s_di[i].raw = DI_ReadRawLevel(i);
s_di[i].stable = s_di[i].raw;
s_di[i].last_change = now;
}
}
/**
* @brief 周期消抖扫描函数,主循环定时调用
* @note 检测电平跳变重置计时,稳定达到消抖时长后更新可靠状态
*/
void BSP_DI_Scan(void)
{
uint8_t i;
uint8_t cur;
uint32_t now = HAL_GetTick();
for (i = 0u; i < DI_CH_NUM; i++)
{
cur = DI_ReadRawLevel(i);
if (cur != s_di[i].raw)
{
s_di[i].raw = cur;
s_di[i].last_change = now;
}
else
{
if ((cur != s_di[i].stable) && ((now - s_di[i].last_change) >= DI_DEBOUNCE_MS))
{
s_di[i].stable = cur;
}
}
}
}
/**
* @brief 获取单路消抖完成后的DI状态
* @param ch 通道号 1~4
* @retval 0无输入 / 1有输入,非法通道返回0
*/
uint8_t BSP_DI_Get(uint8_t ch)
{
if (ch < 1u || ch > DI_CH_NUM)
{
return 0u;
}
return s_di[ch - 1u].stable;
}
/**
* @brief 获取四路DI整体状态掩码
* @retval bit0~bit3 对应 DI1~DI4,bit置1代表有输入
*/
uint8_t BSP_DI_GetAll(void)
{
uint8_t i;
uint8_t mask = 0u;
for (i = 0u; i < DI_CH_NUM; i++)
{
if (s_di[i].stable)
{
mask |= (uint8_t)(1u << i);
}
}
return mask;
}
/**
* @brief 获取单路未消抖原始电平,仅调试诊断使用
* @param ch 通道号 1~4
* @retval 1低电平有输入 / 0无输入,非法通道返回0
*/
uint8_t BSP_DI_GetRaw(uint8_t ch)
{
if (ch < 1u || ch > DI_CH_NUM)
{
return 0u;
}
return DI_ReadRawLevel(ch - 1u);
}