当前位置:首页 > 技术 > 正文内容

基于STM32 HAL库的通用红外网关设计与智能家居集成方案

访客 技术 2026年9月16日 10

1. 红外协议时序特征与容差定义

红外遥控通信依赖载波调制与脉冲宽度编码。以主流NEC编码规范为例,系统通常采用38kHz振荡频率进行载波生成。逻辑状态的判定通过发射时隙与空闲时隙的比例实现:逻辑零对应560μs导通与560μs截止,逻辑一则由560μs导通后跟随1680μs截止构成。单帧数据结构包含引导序列、地址域、地址反码、命令域及命令反码,校验机制依赖于按位取反的冗余设计。

在实际硬件采集过程中,由于晶体震荡偏差与温度漂移,时间窗需要预留合理容差。工程上常将关键阈值设定为±100μs区间,以兼容不同产线的制造公差:

// 协议时序阈值常量定义
#define IR_PROTO_LEADER_PULSE_US_MIN    8500
#define IR_PROTO_LEADER_PULSE_US_MAX    9500
#define IR_PROTO_LEADER_SPACE_US_MIN    4000
#define IR_PROTO_LEADER_SPACE_US_MAX    5000
#define IR_PROTO_BIT_PULSE_US_MIN       450
#define IR_PROTO_BIT_PULSE_US_MAX       650
#define IR_PROTO_BIT_ZERO_SPACE_US_MIN  450
#define IR_PROTO_BIT_ZERO_SPACE_US_MAX  650
#define IR_PROTO_BIT_ONE_SPACE_US_MIN   1550
#define IR_PROTO_BIT_ONE_SPACE_US_MAX   1800

部分厂商会引入扩展地址模式或连续重复码结构。在逆向解析阶段,建议通过示波器或逻辑分析仪提取原始波形,确认目标设备的精确参数边界。

2. 硬件选型与定时器捕获配置

主控单元选用STM32F103系列芯片,其内置的高级定时器具备独立的多通道输入捕获功能。配合TSOP系列解调模块(如HS0038B),可实现38kHz信号至TTL电平的转换。电路布局需满足去耦要求,电源引脚并联100nF陶瓷电容,地线走线保持最短路径以降低回路阻抗。

MCU时钟树配置为72MHz主频。定时器预分频器设置为71,使计数频率降至1MHz,达成微秒级时间分辨率。周期寄存器重载最大值设定为65535,覆盖完整引导码时长。

// 定时器实例初始化结构体
TIM_HandleTypeDef htimCaptureInst;
htimCaptureInst.Instance = TIM1;
htimCaptureInst.Init.Prescaler = 71U;
htimCaptureInst.Init.CounterMode = TIM_COUNTERMODE_UP;
htimCaptureInst.Init.Period = 65535U;
htimCaptureInst.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htimCaptureInst.Init.RepetitionCounter = 0U;
htimCaptureInst.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

输入捕获通道启用数字滤波器以抑制高频毛刺。触发极性初始设为上升沿,滤波系数配置为8级采样。

// 捕获通道参数配置
TIM_IC_InitTypeDef sConfigIC = {0};
sConfigIC.ICFilter = 8U;
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
HAL_TIM_IC_ConfigChannel(&htimCaptureInst, &sConfigIC, TIM_CHANNEL_1);

模块供电建议使用独立LDO稳压源,物理安装位置避开强光直射区域,防止环境光造成解调误触发。

3. 中断驱动与数据缓冲机制

高速脉冲流处理需避免在中断服务例程中执行耗时操作。采用双次捕获交替切换极性的策略,计算相邻边沿的时间差即为脉宽值。为避免共享变量竞争,引入轻量级环形队列缓存原始时序数据。

// 脉冲捕获上下文
static volatile IrCaptureState_t capState = CAP_IDLE;
static uint32_t lastEdgeTime = 0U;

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Channel != HAL_TIM_ACTIVE_CHANNEL_1) return;
    
    uint32_t currentTick = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
    uint32_t delta = (currentTick >= lastEdgeTime) ? 
                     (currentTick - lastEdgeTime) : 
                     (0xFFFF - lastEdgeTime + currentTick + 1U);
    lastEdgeTime = currentTick;
    
    switch (capState)
    {
        case CAP_IDLE:
            // 忽略异常短脉冲
            if (delta < IR_PROTO_LEADER_PULSE_US_MIN) break;
            capState = CAP_FIRST_EDGE;
            __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
            break;
            
        case CAP_FIRST_EDGE:
            ir_ringbuf_push(delta);
            capState = CAP_SECOND_EDGE;
            __HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
            break;
            
        case CAP_SECOND_EDGE:
            ir_ringbuf_push(delta);
            capState = CAP_IDLE;
            // 唤醒解码线程或置位标志
            break;
            
        default: break;
    }
}

环形队列采用无锁单生产者设计,适用于中断环境写入与应用程序读取:

#define IR_RING_SIZE 64
static uint32_t ir_buffer[IR_RING_SIZE];
static volatile uint16_t buf_head = 0U;
static volatile uint16_t buf_tail = 0U;
static volatile uint8_t buf_count = 0U;

bool ir_ringbuf_push(uint32_t val)
{
    if (buf_count == IR_RING_SIZE) return false;
    ir_buffer[buf_head++] = val;
    if (buf_head == IR_RING_SIZE) buf_head = 0U;
    buf_count++;
    return true;
}

bool ir_ringbuf_pop(uint32_t *val)
{
    if (buf_count == 0U) return false;
    *val = ir_buffer[buf_tail++];
    if (buf_tail == IR_RING_SIZE) buf_tail = 0U;
    buf_count--;
    return true;
}

解码前可开启串口追踪端口,监控队列深度与丢包率,便于后续容差参数微调。

4. 多协议自适应解码架构

消费电子设备涵盖多种编码标准。系统通过引导头时序特征进行分类,并抽象统一的数据结果结构。解码核心支持按需加载算法模块。

typedef enum { PROTO_UNKNOWN, PROTO_NEC, PROTO_RC5, PROTO_SONY } IrProtocol_t;

typedef struct {
    IrProtocol_t type;
    uint32_t addr;
    uint32_t cmd;
    bool is_repeat;
    uint8_t bit_depth;
} IrDecodeResult_t;

IrProtocol_t ir_detect_from_leader(uint32_t pulse_us, uint32_t space_us)
{
    if (pulse_us >= IR_PROTO_LEADER_PULSE_US_MIN && 
        pulse_us <= IR_PROTO_LEADER_PULSE_US_MAX &&
        space_us >= IR_PROTO_LEADER_SPACE_US_MIN && 
        space_us <= IR_PROTO_LEADER_SPACE_US_MAX) {
        return PROTO_NEC;
    }
    // 其他协议特征匹配逻辑...
    return PROTO_UNKNOWN;
}

各协议解析函数遵循相同签名,便于策略路由:

bool parse_nec_sequence(const uint32_t *pulses, uint16_t count, IrDecodeResult_t *out);
bool parse_rc5_sequence(const uint32_t *pulses, uint16_t count, IrDecodeResult_t *out);
bool parse_sony_sequence(const uint32_t *pulses, uint16_t count, IrDecodeResult_t *out);

针对非标改造设备,可接入特征学习子程序。该模块提取脉宽分布统计量,动态生成匹配模板并持久化至Flash扇区,降低硬编码依赖。

5. 网络桥接与云端通信实现

本地解码结果需映射至IP网络层。MQTT作为轻量级发布订阅协议,适合资源受限的边缘节点。客户端封装负责连接管理、主题路由与JSON序列化。

typedef struct {
    char dev_sn[32];
    const char *broker_host;
    uint16_t broker_port;
    const char *sub_topic;
    const char *pub_topic;
    bool link_established;
} MqttBridgeCtx_t;

void on_mqtt_message(const char *topic, const uint8_t *payload, size_t length)
{
    IrDecodeResult_t target_cfg;
    if (deserialize_command_payload(payload, length, &target_cfg)) {
        trigger_ir_transmitter(target_cfg.type, target_cfg.cmd);
    }
}

bool push_device_event(const IrDecodeResult_t *evt)
{
    char json_buf[128] = {0};
    sprintf(json_buf, "{\"addr\":%lu,\"cmd\":%lu,\"proto\":\"%s\"}", 
            evt->addr, evt->cmd, protocol_name(evt->type));
    return mqtt_publish(pub_topic, json_buf, strlen(json_buf), 1, 0) == 0;
}

云端流处理平台可通过可视化编排引擎对接。以下为Node-RED工作流的核心逻辑片段,用于指令转发:

// 接收MQTT入站消息 -> 路由解析 -> 下发红外动作
msg.payload.device_id = global.get('gw_sn');
msg.topic = 'devices/' + msg.payload.dev_sn + '/action';
return msg;

传输层建议启用QoS Level 1保障至少一次投递,结合本地事件队列实现断网续传。

6. 嵌入式低功耗调度策略

电池型终端需优化静态与动态功耗。系统维护多层级运行状态,依据任务负载切换外设时钟与内核频率。红外解调依赖外部中断维持监听能力,主核可进入低功耗睡眠。

typedef enum {
    POWER_ACTIVE, POWER_IDLE, POWER_STOP, POWER_STANDBY
} PwrLevel_t;

void set_power_target(PwrLevel_t level)
{
    switch (level)
    {
        case POWER_ACTIVE:
            SystemCoreClockUpdate();
            __HAL_RCC_GPIOA_CLK_ENABLE();
            __HAL_RCC_TIM1_CLK_ENABLE();
            break;
        case POWER_IDLE:
            __HAL_RCC_GPIOA_CLK_DISABLE();
            HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
            break;
        case POWER_STOP:
            HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
            break;
        case POWER_STANDBY:
            HAL_PWR_EnterSTANDBYMode();
            break;
        default: break;
    }
}

中断唤醒处理器恢复时钟树,清除挂起标志并触发信号处理流水线:

void EXTI0_IRQHandler(void)
{
    if (__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET)
    {
        __HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
        set_power_target(POWER_ACTIVE);
        isr_start_demodulation();
    }
}

电源监测模块定期采集ADC数值,换算为毫安时容量估算值,低于阈值时上报低电量告警事件。

BatteryInfo_t query_battery_state(void)
{
    BatteryInfo_t stat;
    stat.raw_mv = adc_read_vbat_channel();
    stat.percentage = map_voltage_to_soc(stat.raw_mv);
    stat.insert_flag = check_charge_det_pin();
    return stat;
}

7. 典型设备状态同步与自动化逻辑

红外链路属于开环单向传输,执行机构无法回传实际工况。网关侧需维护虚拟状态机映射物理设备属性。以空调控制器为例,需跟踪开关机、温区设定、风向等离散参量。

typedef struct {
    bool power_on;
    uint8_t target_temp_c;
    uint8_t fan_level;
    uint8_t swing_dir;
    EnvProfile_t current_mode;
} ApplianceProxy_t;

void sync_proxy_state(const IrDecodeResult_t *raw_cmd, ApplianceProxy_t *proxy)
{
    switch (extract_action_code(raw_cmd))
    {
        case ACT_POWER_TOGGLE:
            proxy->power_on = !proxy->power_on;
            break;
        case ACT_TEMP_INC:
            if (proxy->target_temp_c < 30) proxy->target_temp_c++;
            break;
        case ACT_MODE_COOL:
            proxy->current_mode = MODE_COOLING;
            break;
        default: break;
    }
}

自动化调度器整合时间基准与环境传感数据,动态调整控制策略:

void run_schedule_engine(ApplianceProxy_t *proxy)
{
    TimeStamp cur = rtos_get_tick_time();
    EnvironmentalReadings env = sensor_fetch_all();
    
    if (cur.hour >= 23 || cur.hour < 5) {
        proxy->target_temp_c = adjust_for_sleep(env.temp, env.humidity);
    } else if (!location_tracked()) {
        proxy->power_on = false;
    } else {
        apply_comfort_curve(proxy, env, cur);
    }
}

部署阶段推荐采用插件化架构,将信号收发、协议解析、网络桥接分离为独立组件。建立开源时序库共享各型号编码表,可大幅缩短新设备适配周期。整体方案通过边缘侧协议转换与云端场景编排,实现非联网老设备的低成本智能化升级。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。