六、杰里AD14————红外模块详解
目录
1.request_irq(IRQ_IRTMR, IRQ_IRTMR_IP, irtmr_ir_isr, 0);
一、红外脉冲获取方式
1.使用金思拓虚拟仪器进行进行红外测量:
注意VDDIO和IRVCC连接
不知道金思拓虚拟仪使用具体方法的可看这篇文章:http://t.csdn.cn/Z65jC
如果出现仪器无法连接,可能是驱动未安装,看这篇文章:
kingst逻辑分析仪驱动安装说明.pdf-原创力文档 (book118.com)
二、分析红外波形:
使用如下按键按下第二个按键上一曲时:
得到如下波形:
1.时序逻辑分析
经过查找资料可明显看出该红外波形为:NEC红外线编码协议
2.什么是NEC红外线编码协议
具体可看这篇文章简洁明了:红外协议_51CTO博客_红外nec协议
一、NEC标准
在NEC标准中,部分遥控码表示方法如下,并且数据都是使用LSB低位先发送方式传输。当某个按键按下时,系统首先会发送一个完整的全码,而当某个按键被一直按着持续108ms以上还没松开时,系统就会发送不携带任何数据的连发码,其中前16位为用户识别码,用于区分其他的红外遥控设备,避免不同机种的遥控码之间互相干扰,接下来的16位是8位的操作码和8位的操作反码,用于判断数据是否准确接收。
1.遥控码表示方式
遥控码 表示方式
逻辑0 0.56ms高电平+0.565ms低电平
逻辑1 0.56ms高电平+1.69ms低电平
引导码 9ms高电平+4.5ms低电平
连发码 9ms起始码+2.5ms结束码
全码 引导码+用户码(8bit)+用户反码(8bit)+数据码(8bit)+数据反码8(bit)
3.对比分析
该信号全码由:Leading burst + space + IADDR + ADDR + CMD +ICMD 组成。
Leading burst + space:引导码
IADDR + ADDR:反用户码(8bit)+户反码(8bit)
CMD +ICMD:数据码(8bit)+数据反码8(bit)
三、代码实现即详解
在杰里AD14的SDK当中已经封装好了IR按键的调用方法:
int irflt_init(void *node, void *arg)
{
//timer1
ir_log("ir key init >>>\n");
struct irflt_platform_data *user_data = (struct irflt_platform_data *)arg;
request_irq(IRQ_IRTMR, IRQ_IRTMR_IP, irtmr_ir_isr, 0);
//PORT->IRFLT->TIMER
/* set_ir_clk(); */
/* return 0; */
ir_input_io = user_data->irflt_io;
ir_input_io_sel(user_data->irflt_io);
ir_output_timer_sel(user_data->timer);
irflt_config();
log_irflt_info();
return 0;
}
该函数是一个初始化函数,用于初始化红外遥控器驱动。
首先,通过传入的参数arg,将其转换为struct irflt_platform_data类型的指针user_data,用于获取红外遥控器的平台数据。
接下来,调用request_irq函数注册中断处理函数irtmr_ir_isr,并指定中断号IRQ_IRTMR和中断优先级IRQ_IRTMR_IP。
然后,通过调用ir_input_io_sel函数设置红外输入引脚。
再次,通过调用ir_output_timer_sel函数设置红外输出引脚对应的定时器。
最后,调用irflt_config函数对红外遥控器进行配置,并调用log_irflt_info函数打印红外遥控器的相关信息。
最终,返回0表示初始化成功。
1.request_irq(IRQ_IRTMR, IRQ_IRTMR_IP, irtmr_ir_isr, 0);
IRTMR: Interrupt Request Time Measurement Register(中断请求时间测量寄存器)
IR: Interrupt Request(中断请求)
ISR: Interrupt Service Routine(中断服务例程)
调用request_irq函数注册中断处理函数irtmr_ir_isr,并指定中断号IRQ_IRTMR和中断优先级IRQ_IRTMR_IP。 irtmr_ir_isr()如下:
___interrupt
static void irtmr_ir_isr(void)
{
u16 bCap1;
u8 cap = 0;
static u8 cnt = 0;
IRTMR->CON |= BIT(6);
bCap1 = IRTMR->PRD;
IRTMR->CNT = 0;
cap = bCap1 / irtmr_prd;
/* ir_log("cnt = %d, cap = 0x%x", cnt++, cap); */
if (cap <= 1) {
ir_code.wData >>= 1;
ir_code.bState++;
ir_code.boverflow = 0;
} else if (cap == 2) {
ir_code.wData >>= 1;
ir_code.bState++;
ir_code.wData |= 0x8000;
ir_code.boverflow = 0;
}
/*13ms-Sync*/
/*
else if ((cap == 13) || (cap < 8) || (cap > 110))
{
ir_code.bState = 0;
}
else
{
ir_code.boverflow = 0;
}
*/
else if ((cap == 13) && (ir_code.boverflow < 8)) {
ir_code.bState = 0;
ir_busy = 1;
} else if ((cap < 8) && (ir_code.boverflow < 5)) {
ir_code.bState = 0;
} else if ((cap > 110) && (ir_code.boverflow > 53)) {
ir_code.bState = 0;
} else if ((cap > 20) && (ir_code.boverflow > 53)) { //溢出情况下 (12M 48M)
ir_code.bState = 0;
} else {
ir_code.boverflow = 0;
}
if (ir_code.bState == 16) {
ir_code.wUserCode = ir_code.wData;
}
if (ir_code.bState == 32) {
log_info("[0x%X]\n", ir_code.wData);
}
}
这段代码是一个中断服务程序,用于处理红外遥控器的中断事件。具体解析如下:
首先定义了一些变量,包括:
bCap1(用于保存红外接收到的数据)、
cap(用于保存解码后的数据)、
cnt(计数器)等。
设置IRTMR控制寄存器的第6位为1,表示清除中断标志。
bCap1 = IRTMR->PRD:读取IRTMR的周期寄存器的值,并清零计数器。根据读取到的数据计算出cap(irtmr_prd = prd_cnt;//187 prd_cnt = clk;)
根据cap的值(即上升沿或下降沿的时间)进行不同的处理逻辑:如果cap小于等于1即得到一个逻辑0的信号 0.56ms高电平+0.565ms低电平,
则将ir_code.wData右移1位,bState加1,boverflow置0。此时bCap1为200左右
如果cap等于2,即得到一个逻辑1的信号 0.56ms高电平+1.69ms低电平
则将ir_code.w右移1位,bState加1,并将ir_code.wData的最高位设置为1, boverflow置0。此时bCap1为400左右。
如果cap等于13并boverflow小于8,
则将bState置0,表示开始接收红外码,并将ir_busy置1。
如果cap小于8并且boverflow小于5,
则将bState置0,表示接收的红外码无效。
如果cap大于110并且boverflow大于53,
则将bState置0,表示接收的红外码无效。
如果cap大于20并且boverflow大于53,
则将bState置0,表示接收的红外码无效(针对溢出情况,根据系统时钟频率进行了调整)。
其他情况下,将boverflow置0。
如果bState等于16,则表示已经接收完用户码,将ir_code.wData赋值给ir_code.wUserCode。
如果bState等于32,则表示已经接收完整个红外码,打印出接收到的红外码。
以上就是这段代码的主要解析。
2.打印一下cnt和cap的状态变化
逻辑0 0.56ms高电平+0.565ms低电平
逻辑1 0.56ms高电平+1.69ms低电平cap的值受bCap1的值影响:bCap1 = IRTMR->PRD;
3.底层逻辑
TMR2定时器作为红外模块的时钟源:在上升沿/下降沿捕获模式下,TMR2_PRD 寄存器是作为捕获寄存器使用的,当捕获发生时, TMR2_CNT 的值会被复制到 TMR2_PRD 中 。
逻辑0 0.56ms高电平+0.565ms低电平
逻辑1 0.56ms高电平+1.69ms低电平
逻辑0和逻辑1的上升沿/下降沿的时间是不同的,所以TMR2_PRD 寄存器所捕获的代表时间的数值是不同的。bCap1对应的数值大概为:
逻辑0 0.56ms高电平+0.565ms低电平 bCap1为220左右
逻辑1 0.56ms高电平+1.69ms低电平 bCap1为410左右
4.将大部分变量打印观察
1.引导码
2.用户码
3. 数据码
这里有一个小问题还未找到原因,这条数值突然偏小。但当我取消打印时又可以了。
可能是因为多打印了一条语句消耗了时间,导致时钟计数发生错误。
这里cap等于2才合理,即bCap1>187*2=374才对
5.红外码数据结构
typedef struct _IR_CODE {
u16 wData; //<低8位数据码,高8位数据反码
u8 bState; //<当前红外码的个数
u16 wUserCode; //<用户码和用户反码
u8 boverflow; //<溢出
} IR_CODE;
四、具体使用同方法其他类型按键
引脚选择
1.打开使能
2. 添加按键过滤器
3.头文件添加
4.添加按键消息表
五、注意事项
该按键表仅仅对应我的红外遥控器按键。所以不需要修改对应驱动表。如果你使用不同的遥控器还请修改驱动表。
假如:
1.你测得的第一个按键的时序信号如图:
2.则数据码为45(前提是你的用户码是对的,即ff00)
3.那么我们将驱动表中45对应的位置改为你的按键0即可
irkey_get_value(void)将对应数据码转化成键值
get_irkey_value();获取数据码
static u8 irkey_get_value(void)
{
u8 key_num = NO_KEY;
u8 ir_value = get_irkey_value();
if (ir_value != 0xff) {
key_num = IRTabFF00[ir_value];
}
return key_filter(key_num);
}
u8 get_irkey_value(void)
{
u8 tkey = 0xff;
if (ir_code.bState != 32) {
return tkey;
}
if ((((u8 *)&ir_code.wData)[0] ^ ((u8 *)&ir_code.wData)[1]) == 0xff) {
if (ir_code.wUserCode == 0xFF00)
{
log_info("<%d>",(u8)ir_code.wData); //丢弃高8位数据反码,打印低八位数据码
#if FPGA
tkey = IRTabFF00[(u8)ir_code.wData];
#else
tkey = (u8)ir_code.wData;
#endif
}
} else {
ir_code.bState = 0;
}
return tkey;
}
本文来自博客园,作者:种星记,转载请注明原文链接:https://www.cnblogs.com/zhongxingji/p/17697220.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!