current_v/Src/nec_infrared.c
2025-12-31 08:21:43 +08:00

440 lines
13 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file nec_infrared.c
* @author Motor Control SDK Team, Yuwell Software XiangenWang
* @brief Voice Recognition Module Initialization Section,
including peripheral initialization and message node insertion, etc.
* @version 1.0
* @changelog version 1.0 初始版本 2025.11.13
- 新增:新建第一个版本的软件,待完善解析命令后的程序执行部分
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2025 Yuwell Software Danyang.Jiangsu.China.
* All rights reserved.</center></h2>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of Yuwell Software nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for Yuwell Software.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY Yuwell Software AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL Yuwell Software OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* USER CODE END Header */
#include "nec_infrared.h"
#include "string.h"
#include "fm33lg0xx_queue.h"
#include "fm33lg0xx_event.h"
#include "ht24lc02_eeprom.h"
/************************** 全局变量定义 **************************/
Infrared infrared_info; // 初始化红外结构体状态默认为IDLE
/************************** 私有函数声明 **************************/
static uint32_t nec_calc_pulse_width(Infrared* info, uint32_t current_val);
/**
* @brief 红外模块初始化函数
* @param NONE
* @retval NONE
* @note 1. 配置红外接收引脚为输入上拉模式LPTIM32为1MHz计数频率64MHz系统时钟分频64
* 2. 配置LPTIM32输入捕获为双边沿触发使能捕获和溢出中断
* 3. 配置NVIC中断优先级使能LPTIM32中断
* @author jarvis
* @date 2025.11.25
*/
void nec_infrared_init(void)
{
FL_LPTIM32_InitTypeDef timInit;
FL_LPTIM32_IC_InitTypeDef timICInit;
FL_GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.pin = NEC_INFRARED_GPIO_PIN;
GPIO_InitStruct.mode = FL_GPIO_MODE_DIGITAL;
GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.pull = FL_ENABLE;
GPIO_InitStruct.remapPin = FL_DISABLE;
GPIO_InitStruct.analogSwitch = FL_DISABLE;
FL_GPIO_Init(NEC_INFRARED_GPIO_PORT, &GPIO_InitStruct);
FL_LPTIM32_StructInit(&timInit);
timInit.clockSource = FL_CMU_LPTIM32_CLK_SOURCE_APBCLK; // 系统频率为64Mhz
timInit.mode = FL_LPTIM32_OPERATION_MODE_NORMAL;
timInit.prescalerClockSource = FL_LPTIM32_CLK_SOURCE_INTERNAL;
timInit.prescaler = FL_LPTIM32_PSC_DIV64; // 当前计数频率分频为1Mhz
timInit.autoReload = 300000 - 1; // 最大波长为500ms
timInit.onePulseMode = FL_LPTIM32_ONE_PULSE_MODE_CONTINUOUS;
timInit.triggerEdge = FL_LPTIM32_ETR_TRIGGER_EDGE_BOTH;
timInit.countEdge = FL_LPTIM32_ETR_COUNT_EDGE_FALLING;
FL_LPTIM32_Init(LPTIM32, &timInit);
timICInit.ICSource = FL_LPTIM32_IC1_CAPTURE_SOURCE_GROUP0;
timICInit.ICEdge = FL_LPTIM32_IC_EDGE_FALLING; // 双边缘捕获
FL_LPTIM32_IC_Init(LPTIM32, FL_LPTIM32_CHANNEL_3, &timICInit);
FL_LPTIM32_ClearFlag_CC(LPTIM32, FL_LPTIM32_CHANNEL_3); // 清除标志
FL_LPTIM32_ClearFlag_Update(LPTIM32);
FL_LPTIM32_EnableIT_CC(LPTIM32, FL_LPTIM32_CHANNEL_3); // 中断使能
FL_LPTIM32_EnableIT_Update(LPTIM32);
NVIC_DisableIRQ(LPTIMx_IRQn); // 使能并配置NVIC
NVIC_SetPriority(LPTIMx_IRQn,1); // 设置中断优先级
NVIC_EnableIRQ(LPTIMx_IRQn);
FL_LPTIM32_Enable(LPTIM32); // 使能LPTIM32
infrared_info.ir_state = IR_STATE_IDLE;
infrared_info.pulse_width_index = 0;
infrared_info.code_464_ing = 0;
}
/**
* @brief 计算红外脉冲宽度(处理定时器溢出)
* @param info: 指向红外结构体的指针
* @param current_val: 当前捕获的定时器计数值
* @retval 脉冲宽度单位μs1MHz计数频率
* @note 1. 若当前值大于等于上一次值,直接相减;否则处理溢出(当前值+500000-上一次值)
* 2. 500000为LPTIM32的自动重装值最大计数
* @author jarvis
* @date 2025.12.12
*/
static uint32_t nec_calc_pulse_width(Infrared* info, uint32_t current_val)
{
uint32_t pulse_width = 0;
info->ic_value_last = info->ic_value_current;
info->ic_value_current = current_val;
// 处理定时器溢出(当前值 < 上一次值)
if (info->ic_value_current >= info->ic_value_last)
{
pulse_width = info->ic_value_current - info->ic_value_last;
}
else
{
// 溢出后的值 = 当前值 + (自动重装值 + 1) - 上一次值
pulse_width = (info->ic_value_current + 500000)- info->ic_value_last;
}
return pulse_width;
}
/**
* @brief 解析NEC红外遥控指令并处理对应逻辑
* @note 该函数用于解析NEC协议的红外脉冲宽度数据提取有效指令数据
* 并根据不同的指令码执行对应的事件触发和状态管理逻辑
* @param info: 指向红外数据处理结构体的指针,包含脉冲宽度数组、解析后的数据、
* 错误标记、状态标记等信息
* @retval 无
* @attention 1. 仅处理NEC协议的32位数据部分需确保脉冲宽度数组前33个元素有效
* 2. 解析过程中若检测到无效脉冲宽度,会立即标记解码错误并返回
* 3. 指令处理涉及全局事件组操作,需保证全局事件组的线程安全
*/
static void nec_command_decode(Infrared* info)
{
// 对数据进行解析并将数据下发到消息队列中
uint8_t i = 0;
info->received_data = 0;
info->received_data_index = 0;
MsgQueueItem queue_item;
MsgQueueItem filter_item;
// 解析数组
if((info->pulse_width_array[0] > NEC_START_FALLING_MIN)&&(info->pulse_width_array[0] < NEC_START_FALLING_MAX))
{
for(i = 1;i < 33; i++)
{
if((info->pulse_width_array[i] > NEC_DATA0_FALLING_MIN)&&(info->pulse_width_array[i] < NEC_DATA0_FALLING_MAX))
{
info->received_data_index++;
}else if((info->pulse_width_array[i] > NEC_DATA1_FALLING_MIN)&&(info->pulse_width_array[i] < NEC_DATA1_FALLING_MAX))
{
info->received_data += (1 << info->received_data_index++);
}else{
info->decode_error = true;
info->received_data = 0;
info->received_data_index = 0;
return;
}
}
}
info->decode_error = false;
// vOxygenEventGroupSetBits(&global_event, EVENT_OK_BLINK);
// OK灯闪烁一下
if(info->received_data == NEC_CODE_NUM_4)
{
if(info->code_464_ing == 2)
{
vOxygenEventGroupSetBits(&global_event, EVENT_464_CLEAR); // 获取到第三个4则下发清零事件
info->code_464_ing = 0;
}else if(info->code_464_ing == 0){
info->code_464_ing = 1; // 标记正在校准中
vOxygenEventGroupSetBits(&global_event, EVENT_OK_BLINK);
}else{
info->code_464_ing = 0;
}
}
if(info->received_data == NEC_CODE_NUM_6)
{
if(info->code_464_ing == 1)
{
vOxygenEventGroupSetBits(&global_event, EVENT_OK_BLINK);
info->code_464_ing = 2;
}else{
info->code_464_ing = 0;
}
}
#if PROGREMA_DEBUG_MODE
if(info->received_data == NEC_CODE_TIMER)
{
vOxygenEventGroupSetBits(&global_event, EVENT_OK_BLINK);
// 接收到定时按键时不进行任何操作
queue_item.type = MSG_TYPE_HOUR_SAVE;
queue_item.data.hour_meter.hour = 0;
modify_or_add_queue_node_by_type(&global_queue, MSG_TYPE_HOUR_SAVE, queue_item);
queue_item.type = MSG_TYPE_MINUTE_SAVE;
queue_item.data.hour_meter.minute = 0;
modify_or_add_queue_node_by_type(&global_queue, MSG_TYPE_MINUTE_SAVE, queue_item);
ht24lc02_word_write(7200, INDEX_E4_ELEC_HIGH);
ht24lc02_word_write(20, INDEX_E1_PRESS_LOW);
ht24lc02_word_write(280, INDEX_E2_PRESS_HIGH);
ht24lc02_word_write(180, INDEX_E7_220V_LOW);
filter_item.data.filter_server.last_clean_filter_hours = 0;
filter_item.data.filter_server.last_replace_filter_hours = 0;
filter_item.data.filter_server.clean_filter_time_need_stored = true;
filter_item.data.filter_server.replace_filter_time_need_stored = true;
modify_or_add_queue_node_by_type(&global_queue, MSG_TYPE_FILTER_TIME_SAVE, filter_item);
}
#endif
}
/**
* @brief 红外遥控的捕获中断处理函数
* @param info: 指向红外结构体的指针
* @param Cap_Val: 捕获的定时器计数值
* @retval NONE
* @note 1. 计算脉冲宽度检测NEC协议的起始位、重复帧、数据位、长按位
* 2. 数据位分为0和1累计8位为1字节4字节为一帧数据完成后校验
* 3. 入参为NULL时直接返回
* @author jarvis
* @date 2025.11.25
*/
static void user_infrared_IRQHandle(Infrared* info, uint32_t Cap_Val)
{
if (info == NULL)
{
return;
}
info->pulse_width = nec_calc_pulse_width(info, Cap_Val); // 计算脉冲宽度
if((info->pulse_width >= NEC_START_FALLING_MIN)&&(info->pulse_width <= NEC_START_FALLING_MAX))
{
memset(info->pulse_width_array, 0, sizeof(info->pulse_width_array));
info->pulse_width_index = 0;
info->ir_state = IR_STATE_WAIT_DATA_END;
}
info->pulse_width_array[info->pulse_width_index++] = info->pulse_width;
info->clear_delay = 0;
if(info->pulse_width_index >= 33)
{
info->pulse_width_index = 0;
info->ir_state = IR_STATE_IDLE;
info->ok_count++;
// 对接收到的数据进行解析
nec_command_decode(info);
}
}
/**
* @brief 红外遥控的溢出中断处理函数
* @param info: 指向红外结构体的指针
* @retval NONE
* @note 1. 处理无信号时的脏数据清理,重置信号标记
* 2. 处理通信超时计数到10时清空超时标志和通信值
* 3. 入参为NULL时直接返回
* @author jarvis
* @date 2025.11.25
*/
static void user_infrared_Updata_IRQHandle(Infrared* info)
{
if (info == NULL)
{
return;
}
// 两种情况下重新刷新内容 1.红外处于IDLE模式下 2.处于IR_STATE_WAIT_DATA_END模式超过两个周期100ms
if((info->ir_state == IR_STATE_IDLE)&&(info->pulse_width_array[0] != 0))
{
memset(info->pulse_width_array, 0, sizeof(info->pulse_width_array));
info->pulse_width_index = 0;
}
if(info->ir_state == IR_STATE_WAIT_DATA_END)
{
if(info->clear_delay >= 2)
{
memset(info->pulse_width_array, 0, sizeof(info->pulse_width_array));
info->pulse_width_index = 0;
info->clear_delay = 0;
info->ir_state = IR_STATE_IDLE;
}else{
info->clear_delay++;
}
}
}
/**
* @brief LPTIM32中断服务函数
* @param NONE
* @retval NONE
* @note 1. 处理捕获中断和溢出中断,分别调用对应的红外处理函数
* 2. 先清除中断标志,再处理数据,避免中断嵌套导致的问题
* @author jarvis
* @date 2025.11.25
*/
void LPTIM_IRQHandler(void)
{
uint32_t Cap_Val;
if(FL_LPTIM32_IsActiveFlag_CC(LPTIM32, FL_LPTIM32_CHANNEL_3))
{
FL_LPTIM32_ClearFlag_CC(LPTIM32, FL_LPTIM32_CHANNEL_3);
Cap_Val = FL_LPTIM32_ReadCaptureCH3(LPTIM32);
user_infrared_IRQHandle(&infrared_info, Cap_Val); // 调用红外解码函数
}
if(FL_LPTIM32_IsActiveFlag_Update(LPTIM32))
{
FL_LPTIM32_ClearFlag_Update(LPTIM32);
user_infrared_Updata_IRQHandle(&infrared_info); // 调用溢出处理函数
}
}