440 lines
13 KiB
C
440 lines
13 KiB
C
/* 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>© 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 脉冲宽度(单位:μs,1MHz计数频率)
|
||
* @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); // 调用溢出处理函数
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|