/* 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 * *

© Copyright (c) 2025 Yuwell Software Danyang.Jiangsu.China. * All rights reserved.

* * 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); // 调用溢出处理函数 } }