#include "ir_encoder.h" #include "gptimer.h" #include "asm/power_interface.h" #define LOG_TAG_CONST PERI #define LOG_TAG "[ir_encode]" #define LOG_ERROR_ENABLE #define LOG_DEBUG_ENABLE #define LOG_INFO_ENABLE /* #define LOG_DUMP_ENABLE */ #define LOG_CLI_ENABLE #include "debug.h" #define IR_NEC_PULSE_UNIT 563 #define IR_NEC_BIT_1_H 1 #define IR_NEC_BIT_1_L 3 #define IR_NEC_BIT_0_H 1 #define IR_NEC_BIT_0_L 1 #define IR_NEC_HEAD_H 16 #define IR_NEC_HEAD_L 8 #define IR_NEC_END_H 1 #define IR_NEC_END_L 1 #define IR_NEC_END_REPEAT_L 75//196 #define IR_NEC_REPEAT_H 16 #define IR_NEC_REPEAT_L 4 #define IR_NEC_REPEAT_END_H 1 #define IR_NEC_REPEAT_END_L 175 #define IR_NEC_REPEAT 0x00FF00FF enum state : u8 { IR_IDLE, IR_HEAD, IR_DATA, IR_END, IR_REPEAT, IR_REPEAT_END, }; enum : u8 { DATA_BIT0 = 0, DATA_BIT1, HEAD_BIT, END_BIT, REPEAT_BIT, REPEAT_END_BIT, }; struct ir_encode_info { u32 ir_data; //存储发送的红外数据,4*8bit = (cmd_not + cmd +addr_not + addr) enum state state; //状态机,记录当前发送处于哪个阶段 u8 pwm_tid; //用于pwm功能的timer_id u8 timer_tid; //用于timer功能的timer_id u8 high_level_count; //高电平持续count个单位时间 u8 low_level_count; //低电平持续count个单位时间 u8 repeat_en; //重复码使能标志 u8 pwm_high_ctrl: 1; //高电平使能标志位,避免重复开关 u8 pwm_low_ctrl: 1; //低平使能标志位,避免重复开关 u8 bit_count: 6; //已发送bit个数 }; #define IR_ENCODER_MALLOC_ENABLE 0 #if IR_ENCODER_MALLOC_ENABLE #include "malloc.h" #else static struct ir_encode_info _ir_encode; #endif static struct ir_encode_info *ir_encode; static u32 ir_encoder_malloc() { #if IR_ENCODER_MALLOC_ENABLE ir_encode = (struct ir_encode_info *)malloc(sizeof(struct ir_encode_info)); #else ir_encode = &_ir_encode; #endif memset(ir_encode, 0, sizeof(struct ir_encode_info)); return 0; } static u32 ir_encoder_free() { memset(ir_encode, 0, sizeof(struct ir_encode_info)); #if IR_ENCODER_MALLOC_ENABLE free(ir_encode); #else ir_encode = NULL; #endif return 0; } static u32 ir_pwm_ctrl() { if (ir_encode->pwm_high_ctrl) { gptimer_pwm_enable(ir_encode->pwm_tid); ir_encode->pwm_high_ctrl = 0; ir_encode->high_level_count--; } else if (ir_encode->high_level_count) { ir_encode->high_level_count--; } else if (ir_encode->pwm_low_ctrl) { gptimer_pwm_disable(ir_encode->pwm_tid); ir_encode->pwm_low_ctrl = 0; ir_encode->low_level_count--; } else if (ir_encode->low_level_count) { ir_encode->low_level_count--; } if (ir_encode->low_level_count) { return 0; } else { return 1; } } static void ir_encode_tx_bit(u32 bit_state) { switch (bit_state) { case HEAD_BIT: ir_encode->high_level_count = IR_NEC_HEAD_H; ir_encode->low_level_count = IR_NEC_HEAD_L; break; case DATA_BIT0: ir_encode->high_level_count = IR_NEC_BIT_0_H; ir_encode->low_level_count = IR_NEC_BIT_0_L; break; case DATA_BIT1: ir_encode->high_level_count = IR_NEC_BIT_1_H; ir_encode->low_level_count = IR_NEC_BIT_1_L; break; case END_BIT: ir_encode->high_level_count = IR_NEC_END_H; if (ir_encode->repeat_en) { ir_encode->low_level_count = IR_NEC_END_REPEAT_L; } else { ir_encode->low_level_count = IR_NEC_END_L; } break; case REPEAT_BIT: ir_encode->high_level_count = IR_NEC_REPEAT_H; ir_encode->low_level_count = IR_NEC_REPEAT_L; break; case REPEAT_END_BIT: ir_encode->high_level_count = IR_NEC_REPEAT_END_H; ir_encode->low_level_count = IR_NEC_REPEAT_END_L; break; default : break; } ir_encode->pwm_high_ctrl = 1; ir_encode->pwm_low_ctrl = 1; } static void ir_encode_irq(u32 tid, void *arg) { u32 bit_state_change = ir_pwm_ctrl(); //检查当前bit是否发送完成,切换下一个状态 if (bit_state_change == 0) { return; } switch (ir_encode->state) { case IR_IDLE: ir_encode->state = IR_HEAD; break; case IR_HEAD: ir_encode_tx_bit(HEAD_BIT); ir_encode->state = IR_DATA; ir_encode->bit_count = 0; break; case IR_DATA: u32 bit_level = ir_encode->ir_data & BIT(0); ir_encode_tx_bit(bit_level); ir_encode->ir_data >>= 1; ir_encode->bit_count++; if (ir_encode->bit_count == 32) { ir_encode->state = IR_END; } break; case IR_END: ir_encode_tx_bit(END_BIT); ir_encode->state = IR_REPEAT; break; case IR_REPEAT: if (ir_encode->repeat_en) { ir_encode_tx_bit(REPEAT_BIT); ir_encode->state = IR_REPEAT_END; } else { gptimer_pause(ir_encode->timer_tid); ir_encode->state = IR_IDLE; } break; case IR_REPEAT_END: ir_encode_tx_bit(REPEAT_END_BIT); ir_encode->state = IR_REPEAT; break; default: break; } } void ir_encoder_init(u32 gpio, u32 freq, u32 duty) { log_info("func:%s(), line:%d\n", __func__, __LINE__); ir_encoder_malloc(); const struct gptimer_config pwm_config = { .pwm.freq = freq, //设置频率 .pwm.port = (gpio / IO_GROUP_NUM), //设置输出IO .pwm.pin = BIT(gpio % IO_GROUP_NUM), //设置输出IO .pwm.duty = duty, //设置占空比 .mode = GPTIMER_MODE_PWM, //设置工作模式 }; #ifdef TCFG_IR_ENCODER_TIMER_TID ir_encode->pwm_tid = (u8)gptimer_init(TCFG_IR_ENCODER_TIMER_TID, &pwm_config); //用户配置固定timer #else ir_encode->pwm_tid = (u8)gptimer_init(TIMERx, &pwm_config); //内部分配空闲timer #endif log_info("pwm_config tid = %d\n", ir_encode->pwm_tid); gpio_set_mode(IO_PORT_SPILT(gpio), PORT_OUTPUT_LOW); gptimer_start(ir_encode->pwm_tid); gptimer_pwm_disable(ir_encode->pwm_tid); const struct gptimer_config timer_config = { .timer.period_us = IR_NEC_PULSE_UNIT, //设置定时周期 .irq_cb = ir_encode_irq, //设置中断回调函数 .irq_priority = 3, //设置中断优先级 .mode = GPTIMER_MODE_TIMER, //设置工作模式 }; #ifdef TCFG_IR_ENCODER_PWM_TID ir_encode->timer_tid = (u8)gptimer_init(TCFG_IR_ENCODER_PWM_TID, &timer_config); //用户配置固定timer #else ir_encode->timer_tid = (u8)gptimer_init(TIMERx, &timer_config); //内部分配空闲timer #endif log_info("timer_config tid = %d\n", ir_encode->timer_tid); } void ir_encoder_deinit() { log_info("func:%s(), line:%d\n", __func__, __LINE__); gptimer_deinit(ir_encode->pwm_tid); gptimer_deinit(ir_encode->timer_tid); ir_encode->state = IR_IDLE; ir_encoder_free(); } u32 ir_encoder_tx(u8 ir_addr, u8 ir_cmd, u8 repeat_en) { if (ir_encode->state != IR_IDLE) { return -1; } u32 cmd_not = (~ir_cmd & 0xFF) << 24; u32 addr_not = (~ir_addr & 0xFF) << 8; ir_encode->ir_data = cmd_not | (ir_cmd << 16) | addr_not | ir_addr; ir_encode->repeat_en = repeat_en; log_info("ir_data 0x%08x repeat_en : %d\n", ir_encode->ir_data, ir_encode->repeat_en); gptimer_start(ir_encode->timer_tid); return 0; } void ir_encoder_repeat_stop() { ir_encode->repeat_en = 0; } static u8 ir_encoder_status_get() { if (ir_encode->state == IR_IDLE) { return 1; } else { return 0; } } REGISTER_LP_TARGET(ir_encoder_lp_target) = { .name = "ir_encoder", .is_idle = ir_encoder_status_get, };