Files
2025-12-03 11:12:34 +08:00

282 lines
7.6 KiB
C

#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,
};