(2)杰里AD14————红外模块详解

目录

一、红外脉冲获取方式

1.使用金思拓虚拟仪器进行进行红外测量:

二、分析红外波形:

 1.时序逻辑分析

 2.什么是NEC红外线编码协议

3.对比分析

三、代码实现即详解 

1.request_irq(IRQ_IRTMR, IRQ_IRTMR_IP, irtmr_ir_isr, 0);

2.打印一下cnt和cap的状态变化

 3.底层逻辑

4.将大部分变量打印观察 

 1.引导码

2.用户码 

3. 数据码

5.红外码数据结构

四、具体使用同方法其他类型按键

 五、注意事项


一、红外脉冲获取方式

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;
}
  • 该代码是一个函数,返回一个u8类型的值。
  • 函数首先定义一个无符号8位整数tkey,并初始化为0xff。
  • 接下来,函数检查ir_code的bState属性是否等于32。如果不等于32,则返回tkey的当前值。
  • 然后,函数检查ir_code的wData属性的低8位和高8位的异或结果是否等于0xff。如果等于0xff,则继续执行。
  • 在wUserCode等于0xFF00的情况下,函数会打印出wData的低8位数据码,并根据FPGA宏定义的情况下,将tkey赋值为IRTABFF00[(u8)ir_code.wData]或(ir_code.wData的低8位)。
  • 如果wData的低8位和高8位的异或结果不等于0xff,则将ir_code的bState属性重置为0。
  • 最后,函数返回tkey的值。
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编写红外模块的过程中,首先要在Keil环境中创建一个新的工程。然后,需要包含相关的头文件,例如关于红外模块的引脚定义、寄存器地址和功能函数的头文件。接下来,我们可以定义一些全局变量来存储红外模块的接收和发送数据。 在主函数中,我们需要初始化红外模块。这可以通过设置引脚方向和模式、配置中断、启用外部中断等步骤来完成。初始化完成后,我们可以进入一个无限循环,不断接收和处理红外模块的信号。 接收红外信号的过程中,可以采用轮询或者中断的方式。轮询方式下,我们可以不断读取红外模块的状态寄存器,并判断是否有数据接收到。如果有数据,则可以通过相关的函数来读取红外接收缓冲区中的数据。 在处理红外信号时,我们可以根据具体的需求来进行解码、判断和执行相应的操作。例如,可以根据红外遥控器的按键编码来判断用户按下了哪个按键,并根据不同的按键编码执行不同的操作,例如控制外部设备的开关或者调节亮度等操作。 发送红外信号的过程中,我们可以通过设置红外发射引脚的状态来控制红外LED的开关,从而发送特定的红外信号。发送红外信号的频率和编码格式需要根据具体的红外通信协议来设置。 最后,我们可以通过Keil的编译、下载和调试功能,将编写好的红外模块程序烧录到目标硬件上,并进行调试和测试。根据具体的需求和硬件平台的不同,可能还需要根据硬件接口和引脚进行一些适配和配置工作,以使红外模块正常工作。 总的来说,通过在Keil环境下编写红外模块的程序,我们可以实现红外信号的接收和发送,并根据具体的需求来进行相应的操作和控制。这可以为我们提供更多的应用场景和扩展性,例如遥控器、智能家居等领域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值