Files
433_STM32/Core/Src/io_monitor.c

446 lines
17 KiB
C
Raw Normal View History

/**
******************************************************************************
* @file io_monitor.c
* @brief IO状态监控模块实现
* @author Application Layer
* @version 1.2
******************************************************************************
* @attention
*
*
* 1. 10ms定时扫描CPU占用
* 2. 3
* 3.
*
*
* v1.2 -
* v1.1 - -5
******************************************************************************
*/
#include "io_monitor.h"
#include "uart2_print.h"
#include "multi_uart_router.h"
#include "main.h"
#include <string.h>
/*==============================================================================
*
*============================================================================*/
/* DEBUG_IO_MONITOR: 调试日志开关置1时启用调试输出置0时禁用 */
#define DEBUG_IO_MONITOR 1
#if DEBUG_IO_MONITOR
/* 调试日志宏,带模块前缀"[IO]"方便在串口调试终端中过滤日志 */
#define DEBUG_LOG(fmt, ...) UART2_Print_Printf("[IO] " fmt "\r\n", ##__VA_ARGS__)
#else
/* 禁用调试日志时,将宏定义为空,避免生成无用代码 */
#define DEBUG_LOG(fmt, ...)
#endif
/*==============================================================================
*
*============================================================================*/
/**
* @brief IO通道数据结构
* @note
*
*
* (DI)
* (GPIO端口和引脚)
* 使
*
*
* - port: GPIO端口指针(GPIOB)
* - pin: GPIO引脚编号(GPIO_PIN_4)HAL库读取函数
* - current_state: 0=1=
* - debounce_counter:
* - last_raw_state:
* - change_count:
*/
typedef struct {
GPIO_TypeDef *port; /**< GPIO端口指针指向外设寄存器基地址 */
uint16_t pin; /**< GPIO引脚编号对应HAL库的引脚定义 */
uint8_t current_state; /**< 经去抖处理后的稳定状态0=低电平1=高电平 */
uint8_t debounce_counter; /**< 去抖计数器,连续采样到相同值的次数 */
uint8_t last_raw_state; /**< 上一次采样的原始电平,用于变化检测 */
uint32_t change_count; /**< 状态变化累计次数,用于性能监控 */
} io_channel_t;
/*==============================================================================
*
*============================================================================*/
/**
* @brief
* @note GPIO硬件连接
*
*
* - 0: GPIOB, GPIO_PIN_4
* - 1: GPIOB, GPIO_PIN_5
* - 2: GPIOB, GPIO_PIN_6
* - 3: GPIOB, GPIO_PIN_7
*
* 0
*/
static io_channel_t di_channels[IO_CHANNEL_COUNT] = {
{GPIOB, GPIO_PIN_4, 0, 0, 0, 0},
{GPIOB, GPIO_PIN_5, 0, 0, 0, 0},
{GPIOB, GPIO_PIN_6, 0, 0, 0, 0},
{GPIOB, GPIO_PIN_7, 0, 0, 0, 0}
};
/**
* @brief
* @note 10ms固定间隔扫描
* HAL_GetTick()
*/
static uint32_t last_scan_tick = 0;
/**
* @brief 使
* @note true时允许状态变化自动上报false时禁止上报
*
*/
static bool report_enabled = true;
/**
* @brief IO事件回调函数指针
* @note IO状态变化将通过回调函数上报
* NULL时使用默认的UART2_Print_String输出
*
*
* void callback(uint8_t channel, uint8_t state, const char *event_msg)
*
* 使
* -
* - NULL恢复默认UART2输出方式
*/
static io_event_callback_t g_event_callback = NULL;
/*==============================================================================
*
*============================================================================*/
/**
* @brief
* @note
* DI_EVENT消息的校验和计算
*
* @param data: ()
* @param len: ()
* @return : uint8_t类型的异或结果
*
*
* 0
*/
static uint8_t calc_checksum(const char *data, uint8_t len);
/**
* @brief DI状态变化事件
* @note ASCII格式的状态变化消息
* : $DI_EVENT,<channel>,<state>*<checksum>\r\n
* UART2
*
* @param channel: 0()
* @param state: 0=1=()
* @return
*
*
* 1. MultiUART_SendString发送到UART1(RF433)
* 2. (g_event_callback)
* 3. UART2仅用于调试日志输出
*/
static void send_di_event(uint8_t channel, uint8_t state);
/*==============================================================================
*
*============================================================================*/
/**
* @brief
* @note
* DI_EVENT消息的校验和计算
*
* @param data: ()
* @param len: ()
* @return : uint8_t类型的异或结果
*
*
* 0
*/
static uint8_t calc_checksum(const char *data, uint8_t len)
{
uint8_t cs = 0;
for (uint8_t i = 0; i < len; i++) {
cs ^= (uint8_t)data[i];
}
return cs;
}
/**
* @brief DI状态变化事件
* @note ASCII格式的状态变化消息
* : $DI_EVENT,<channel>,<state>*<checksum>\r\n
* UART2
*
* @param channel: 0()
* @param state: 0=1=()
* @return
*
*
* 1. MultiUART_SendString发送到UART1(RF433)
* 2. (g_event_callback)
* 3. UART2仅用于调试日志输出
*/
static void send_di_event(uint8_t channel, uint8_t state)
{
char msg[32];
uint8_t cs;
/* 构造消息主体channel+1将0-base转换为1-base的用户可见编号 */
int len = snprintf(msg, sizeof(msg), "$DI_EVENT,%d,%d*", channel + 1, state);
/* 计算异或校验和,跳过'$'符号只对正文部分计算 */
cs = calc_checksum(msg + 1, len - 1);
/* 将校验和追加到消息末尾,格式为两位十六进制数 */
snprintf(msg + len, sizeof(msg) - len, "%02X\r\n", cs);
/* 输出调试日志到UART2记录状态变化 */
DEBUG_LOG("CH%d -> %s", channel + 1, state ? "HIGH" : "LOW");
/*----------------------------------------------------------
* UART1(RF433模块)
* IO事件的主要路由通道线
*----------------------------------------------------------*/
MultiUART_SendString(PORT_UART1, msg);
/* 输出完整消息到UART2方便调试查看 */
DEBUG_LOG("RF433 TX: \"%s\"", msg);
/*----------------------------------------------------------
*
*
*----------------------------------------------------------*/
if (g_event_callback != NULL) {
g_event_callback(channel, state, msg);
}
}
/*==============================================================================
*
*============================================================================*/
/**
* @brief IO监控模块初始化
* @note
*
* @param
* @return
*
*
* 1. GPIO引脚的当前电平状态
* 2. 1()
* 3.
* 4. 使
*
*
* - 102
* 0
*/
void IO_Monitor_Init(void)
{
/* 遍历所有IO通道进行初始化 */
for (int i = 0; i < IO_CHANNEL_COUNT; i++) {
io_channel_t *ch = &di_channels[i];
/* 读取GPIO当前电平HAL_GPIO_ReadPin返回GPIO_PinState类型 */
ch->current_state = HAL_GPIO_ReadPin(ch->port, ch->pin) ? 1 : 0;
/* 记录原始状态作为上次采样值 */
ch->last_raw_state = ch->current_state;
/* 去抖计数器初始化为1已完成首次稳定采样 */
ch->debounce_counter = 1;
/* 变化计数清零 */
ch->change_count = 0;
}
/* 初始化扫描时间戳为0确保首次扫描立即执行 */
last_scan_tick = 0;
/* 使能自动上报功能 */
report_enabled = true;
/* 输出初始化完成日志,显示初始各通道状态掩码 */
DEBUG_LOG("Init OK, initial states: 0x%02X", IO_Monitor_GetAllStates());
}
/**
* @brief IO监控定时任务
* @note
*
* @param
* @return
*
* -
* 1: ->
* 2: ->
* 3: (IO_DEBOUNCE_COUNT)
* -> current_state
*
*
* - IO_DEBOUNCE_COUNT通常定义为33
* - IO_SCAN_PERIOD_MS(10ms)20-30ms
* -
*/
void IO_Monitor_Task(void)
{
/* 获取当前系统时间戳(毫秒) */
uint32_t current_tick = HAL_GetTick();
/*----------------------------------------------------------
*
* IO_SCAN_PERIOD_MS时才执行新扫描
* CPU资源浪费
*----------------------------------------------------------*/
if (current_tick - last_scan_tick < IO_SCAN_PERIOD_MS) {
return;
}
/* 更新扫描时间戳 */
last_scan_tick = current_tick;
/*----------------------------------------------------------
*
*----------------------------------------------------------*/
for (int i = 0; i < IO_CHANNEL_COUNT; i++) {
io_channel_t *ch = &di_channels[i];
/* 读取GPIO引脚当前电平转换为0/1表示 */
uint8_t raw_state = HAL_GPIO_ReadPin(ch->port, ch->pin) ? 1 : 0;
/*----------------------------------------------------------
*
*----------------------------------------------------------*/
if (raw_state != ch->last_raw_state) {
/* 分支1: 采样值发生变化 -> 重置去抖计数器 */
ch->debounce_counter = 0;
ch->last_raw_state = raw_state;
} else {
/* 分支2: 采样值与上次相同 -> 累加去抖计数器 */
if (ch->debounce_counter < IO_DEBOUNCE_COUNT) {
ch->debounce_counter++;
}
/* 分支3: 计数器达到阈值且与稳定状态不一致 -> 确认状态变化 */
else if (ch->current_state != raw_state) {
/* 更新为新确认的稳定状态 */
ch->current_state = raw_state;
/* 增加变化统计计数 */
ch->change_count++;
/* 事件上报(已使能情况下) */
if (report_enabled) {
send_di_event(i, raw_state);
}
}
}
}
}
/**
* @brief
* @note
*
* @param channel: 0()
* @return : 0=1=0
*
* 使
* - (0-3)0
* -
*/
uint8_t IO_Monitor_GetState(uint8_t channel)
{
/* 参数边界检查超出范围的通道返回0 */
if (channel >= IO_CHANNEL_COUNT) {
return 0;
}
return di_channels[channel].current_state;
}
/**
* @brief
* @note 便
*
* @param
* @return : uint8_t类型
*
*
* - bit0: 0
* - bit1: 1
* - bit2: 2
* - bit3: 3
*
* 0x05(0101b)0=1=2=3=
*/
uint8_t IO_Monitor_GetAllStates(void)
{
uint8_t states = 0;
/* 遍历所有通道,将各通道状态按位组合成掩码 */
for (int i = 0; i < IO_CHANNEL_COUNT; i++) {
if (di_channels[i].current_state) {
states |= (1 << i);
}
}
return states;
}
/**
* @brief 使
* @note
*
* @param enable: true=使false=()
* @return
*
* 使
* -
* -
*/
void IO_Monitor_EnableReport(bool enable)
{
report_enabled = enable;
DEBUG_LOG("Report %s", enable ? "enabled" : "disabled");
}
/**
* @brief
* @note
*
* @param channel: 0()
* @return : uint32_t类型0
*
* 使
* -
* - ()
*/
uint32_t IO_Monitor_GetChangeCount(uint8_t channel)
{
/* 参数边界检查超出范围返回0 */
if (channel >= IO_CHANNEL_COUNT) {
return 0;
}
return di_channels[channel].change_count;
}
/**
* @brief IO事件回调函数
* @note IO状态变化将通过回调函数上报
*
* @param callback: NULL则使用默认UART2输出()
* @return
*
* 使
* - send_di_event()
* - NULL恢复默认UART2输出方式
* - : void callback(uint8_t channel, uint8_t state, const char *event_msg)
*/
void IO_Monitor_SetEventCallback(io_event_callback_t callback)
{
g_event_callback = callback;
DEBUG_LOG("Event callback %s", callback ? "set" : "cleared");
}