【STM32标准库】【自制库】NEC协议的红外解码和发送


文章基于适用于STM32F4系列,作者使用STM32F401CCU6开发板。
本文章基于此系列和开发板展开讨论。

NEC编码

在这篇文章中介绍了硬件和协议,传送门

发送

基本思路

这里使用单个管脚发送的方案,因为使用的是小功率的红外LED,因此直接管脚直连LED即可

之前文章中说到,发送高电平发送是以38KHz闪烁的方波,低电平则为不亮

流程/思路:使用定时器中断产生38KHz的方波,在需要发送高电平时打开定时器,发送低电平时关闭定时器即可,按照NEC编码的规则进行编码发送即可

52a19d591fc44ee1b9fbdfd44554a1fc.png

电路如图所示

初始化

GPIO

之前文章中介绍过,传送门

这里需要设置为推挽输出,上拉模式(其他也可)

头文件(IR_NEC.h)

#define IR_NEC_Send_GPIO_RCC RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE)
#define IR_NEC_Send_GPIOx GPIOA
#define IR_NEC_Send_GPIO_Pin GPIO_Pin_0

C文件(IR_NEC.c)

// 发送,GPIO初始化
void IR_NEC_Send_GPIO_init(void)
{
    GPIO_InitTypeDef GPIO_Initstruct;                //声明GPIO初始化结构体
    IR_NEC_Send_GPIO_RCC;                            //打开GPIO时钟
    GPIO_Initstruct.GPIO_Mode = GPIO_Mode_OUT;       //输出模式
    GPIO_Initstruct.GPIO_OType = GPIO_OType_PP;      //推挽输出模式
    GPIO_Initstruct.GPIO_Pin = IR_NEC_Send_GPIO_Pin; //引脚0
    GPIO_Initstruct.GPIO_PuPd = GPIO_PuPd_UP;        //上拉模式
    GPIO_Initstruct.GPIO_Speed = GPIO_High_Speed;    //高速模式
    GPIO_Init(IR_NEC_Send_GPIOx, &GPIO_Initstruct);  //初始化GPIO
}

定时器中断和NVIC

之前文章中介绍过,定时器传送门NVIC传送门

使用通用定时器4,中断为每13us( 2 * 38KHz)触发一次,优先级无需特别关心

头文件(IR_NEC.h)

//定时器
#define IR_NEC_Send_TIM_RCC RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE)
#define IR_NEC_Send_TIM_TIMx TIM4
#define IR_NEC_Send_TIM_IRQn TIM4_IRQn
#define IR_NEC_Send_TIM_Priority_1 1
#define IR_NEC_Send_TIM_Priority_2 2

C文件(IR_NEC.c)

//发送,定时器初始化
void IR_NEC_Send_TIM_init(void)
{
    TIM_TimeBaseInitTypeDef TIM_Init_Struct;              //声明定时器初始化结构体
    NVIC_InitTypeDef NVIC_Init_Struct;                    //声明NVIC初始化结构体
    IR_NEC_Send_TIM_RCC;                                  //打开时钟
    TIM_Init_Struct.TIM_ClockDivision = TIM_CKD_DIV1;     //滤波器不分频
    TIM_Init_Struct.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
    //每次中断触发时间=[(TIM_Period+1)*(TIM_Prescaler+1)/(SystemCoreClock)] (s) //13。15us
    TIM_Init_Struct.TIM_Prescaler = 221 - 1;                                         // 221
    TIM_Init_Struct.TIM_Period = 5 - 1;                                              // 5
    TIM_Init_Struct.TIM_RepetitionCounter = 0;                                       //高级定时器特有,这里写0就行
    TIM_TimeBaseInit(IR_NEC_Send_TIM_TIMx, &TIM_Init_Struct);                        //调用函数初始
    TIM_ITConfig(IR_NEC_Send_TIM_TIMx, TIM_IT_Update, ENABLE);                       //启用溢出中断
                                                                                     //
    NVIC_Init_Struct.NVIC_IRQChannel = IR_NEC_Send_TIM_IRQn;                         //中断名称
    NVIC_Init_Struct.NVIC_IRQChannelCmd = ENABLE;                                    //使能
    NVIC_Init_Struct.NVIC_IRQChannelPreemptionPriority = IR_NEC_Send_TIM_Priority_1; //主优先级
    NVIC_Init_Struct.NVIC_IRQChannelSubPriority = IR_NEC_Send_TIM_Priority_2;        //副优先级
    NVIC_Init(&NVIC_Init_Struct);                                                    //初始化NVIC
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, DISABLE);
}

产生38KHz方波

中断触发频率是2 * 38KHz

思路是每触发一次中断改变电平变换,IR_NEC_Send_Square变量的值每次加1,到2归零,根据此数值改变电平。

C文件(IR_NEC.c)

u8 IR_NEC_Send_Square = 0; //方波

//产生38kHz方波
void TIM4_IRQHandler(void)
{
    if (TIM_GetITStatus(IR_NEC_Send_TIM_TIMx, TIM_IT_Update) != RESET)
    {
        if (IR_NEC_Send_Square)
            GPIO_SetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
        else
            GPIO_ResetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
        IR_NEC_Send_Square++;
        if (IR_NEC_Send_Square >= 2)
            IR_NEC_Send_Square = 0;

        TIM_ClearITPendingBit(IR_NEC_Send_TIM_TIMx, TIM_IT_Update); //将中断标志清除
    }
}

协议发送

引导码

引导码是9ms闪亮和4.5ms不亮

在闪亮时打开定时器,并将其计数值写为0即可

 TIM_SetCounter(IR_NEC_Send_TIM_TIMx, 0);
 TIM_Cmd(IR_NEC_Send_TIM_TIMx, ENABLE);

不亮时只需要关闭定时器即可

TIM_Cmd(IR_NEC_Send_TIM_TIMx, DISABLE);

下面是代码

C文件(IR_NEC.c)

//发送引导码
__STATIC_INLINE void IR_NEC_Send_Guide(void)
{
    GPIO_ResetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
    TIM_SetCounter(IR_NEC_Send_TIM_TIMx, 0);
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, ENABLE);
    Delay_us(9000); // 9ms闪
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, DISABLE);
    GPIO_ResetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
    Delay_us(4500); // 4.5ms灭
    //共13.5ms
}

数据码

数据码0

数据码0是0.56ms闪亮和0.56ms不亮(不同设备的容错范围可能不同,可以在这附近实验一下)

C文件(IR_NEC.c)

下面是代码

//发送数据0
__STATIC_INLINE void IR_NEC_Send_0(void)
{
    GPIO_ResetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
    TIM_SetCounter(IR_NEC_Send_TIM_TIMx, 0);
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, ENABLE);
    Delay_us(560); // 0.56ms闪
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, DISABLE);
    GPIO_ResetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
    Delay_us(560); // 0.56ms灭
    //共1.12ms
}

数据码1

数据码0是0.56ms闪亮和1.96ms不亮(不同设备的容错范围可能不同,可以在这附近实验一下)

C文件(IR_NEC.c)

下面是代码

//发送数据1
__STATIC_INLINE void IR_NEC_Send_1(void)
{
    GPIO_ResetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
    TIM_SetCounter(IR_NEC_Send_TIM_TIMx, 0);
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, ENABLE);
    Delay_us(560); // 0.56ms闪
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, DISABLE);
    GPIO_ResetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
    Delay_us(1690); // 1.69ms灭
    //共2.25ms
}

结束位

结束位是0.65ms闪亮之后变为不亮

C文件(IR_NEC.c)

下面是代码

//结束位
void IR_NEC_Send_End(void)
{
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, ENABLE);
    Delay_us(650); // 6ms亮
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, DISABLE);
    GPIO_ResetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
}

重复码

重复码是9ms闪亮和2.25ms不亮,需要将时间长度补齐到100ms左右

C文件(IR_NEC.c)

下面是代码

//发送重复码
void IR_NEC_Send_Repect(void)
{
    GPIO_ResetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
    TIM_SetCounter(IR_NEC_Send_TIM_TIMx, 0);
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, ENABLE);
    Delay_us(9000); // 9ms亮
    TIM_Cmd(IR_NEC_Send_TIM_TIMx, DISABLE);
    GPIO_ResetBits(IR_NEC_Send_GPIOx, IR_NEC_Send_GPIO_Pin);
    Delay_us(2250); // 2.25ms灭
                    //共11.25ms
    IR_NEC_Send_End();
    Delay_ms(100);
}

数据组合和发送

因为有些厂商使用的不是标准的NEC,因此我们使用一个数值来存放发送的数据,可以是n Byte数据

传入时传入数值地址和数据字节数即可

void IR_NEC_Send_Code(u8 *Dat, u32 Len)

示例调用

u8 DAT[5] = {0X10, 0X68, 0X80, 0X03, 0X0};
IR_NEC_Send_Code(DAT, 5);

数据是从Byte的高位到低位发送

发送流程

  1. 发送引导码
  2. 从高位到低位发送全部字节
  3. 发送结束位
  4. 延迟

C文件(IR_NEC.c)

// NEC编码发送
void IR_NEC_Send_Code(u8 *Dat, u32 Len)
{
    u32 zj;
    IR_NEC_Send_Guide();          //引导码
    for (int j = 0; j < Len; j++) //循环数组
    {
        zj = Dat[j];
        for (int i = 0; i < 8; i++) //从高到低
        {
            if (zj & (0X80))
            {
                IR_NEC_Send_1();
            }
            else
            {
                IR_NEC_Send_0();
            }
            zj <<= 1;
        }
    }
    IR_NEC_Send_End(); //结束位
    Delay_ms(40);
}

接收

接收使用的是1838一体式红外解码模块

这个模块会将收到的数据反相,因此闪亮是低电平,不亮是高电平

分析

观察一下接收到的数据
d1d15a1356174ee1b68c890d52c1d4ce.png

再回忆一下各个码类的长度

就可以轻易发现,每个编码的持续时间各不相同,而且都是以低电平开始,以高电平结束,空闲状态为高电平

因此,我们可以使用外部中断(大多数时间没有信号输入)的下降沿触发(高转低为每个码字的开始也是结束),计算每个码字的持续时间(不同码字的时间不同),来进行解码

细节分析,开始的时候接收到引导码开始,结束的时候因为结束位的存在,因此可以将最后一个数据码的计时补全

思路

1.首先需要检测引导码

即在第一个下降沿开始计时,到第二个下降沿读取时间,为13.5ms为引导码(即图中蓝色部分所标记的下降沿)

9257cfb3be644fe3ac505d7116d00368.png

2.数据码

上一个码字的结束也就是这个码字的开始,这个码字的结束也是下个码字的结束

总时间是1.12ms是数据0
总时间是2.25ms是数据1

因此,这是第一个数据码字(bit),在红色部分的下降沿直接计算即可

26a8787abea240b48216a93865ac038a.png

因此我们需要在这个箭头指出的下降沿做两件事:
1.读取定时器的值(计算上个码字的时间)
2.清空计时器(为这个码字的时间计算做准备)

fdfe5d337ecd4db59e0865cdafe60478.png

再举一个例子,这次我们举出最后一个码字(bit)

在绿色部分之间计算时间即可,这个图中后一个绿色的下降沿是结束位的开始

a3c8a997bcff4636bbd4a9565d667839.png

同样的,我们需要在红色箭头处做两件事
1.读取定时器的值(计算上个码字的时间)
2.清空计时器(为这个码字的时间计算做准备)

在蓝色箭头需要读取定时器的值即可,并关闭定时器

b96b75b242f749e89ee552c2e2bd175c.png

3.重复码

同样思路,读取两次下降沿之间的时间,为11.25ms是重复码,意味着重写上次的数据

a4e262ea681746768b01884dbc932a2e.png

初始化

需要初始化的外设为

  1. GPIO
  2. 外部中断和NVIC
  3. 定时器中断和NVIC

GPIO

上拉输入模式,之前介绍过了,GPIO传送门

头文件(IR_NEC.h)

// GPIO
#define IR_NEC_Read_GPIO_RCC RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE)
#define IR_NEC_Read_GPIOx GPIOB
#define IR_NEC_Read_GPIO_Pin GPIO_Pin_0

C文件(IR_NEC.c)

// 读取,GPIO初始化
void IR_NEC_Read_GPIO_init(void)
{
    GPIO_InitTypeDef GPIO_Initstruct;                //声明GPIO初始化结构体
    IR_NEC_Read_GPIO_RCC;                            //打开GPIO时钟
    GPIO_Initstruct.GPIO_Mode = GPIO_Mode_IN;        //输入模式
    GPIO_Initstruct.GPIO_OType = GPIO_OType_OD;      //开漏输入模式
    GPIO_Initstruct.GPIO_Pin = IR_NEC_Read_GPIO_Pin; //引脚0
    GPIO_Initstruct.GPIO_PuPd = GPIO_PuPd_UP;        //上拉模式
    GPIO_Initstruct.GPIO_Speed = GPIO_High_Speed;    //高速模式
    GPIO_Init(IR_NEC_Read_GPIOx, &GPIO_Initstruct);  //初始化GPIO
}

外部中断和NVIC

使用下降沿中断,外部中断传送门NVIC传送门

头文件(IR_NEC.h)

//外部中断
#define IR_NEC_Read_EXIT_Link SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource0)
#define IR_NEC_Read_EXIT_Pin EXTI_Line0
#define IR_NEC_Read_EXIT_IRQn EXTI0_IRQn
#define IR_NEC_Read_EXIT_Priority_1 1
#define IR_NEC_Read_EXIT_Priority_2 1

C文件(IR_NEC.c)

//读取,外部中断初始化
void IR_NEC_Read_EXTI_init(void)
{
    EXTI_InitTypeDef EXTI_Initstruct;                      //创建外部中断初始化结构体
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //打开时钟
    IR_NEC_Read_EXIT_Link;                                 //将GPIO与外部中断连接
    EXTI_Initstruct.EXTI_Line = IR_NEC_Read_EXIT_Pin;      //配置的是外部中断0
    EXTI_Initstruct.EXTI_LineCmd = ENABLE;                 //使能
    EXTI_Initstruct.EXTI_Mode = EXTI_Mode_Interrupt;       //选择中断模式
    EXTI_Initstruct.EXTI_Trigger = EXTI_Trigger_Falling;   //下降沿模式
    EXTI_Init(&EXTI_Initstruct);                           //初始化外部中断0
}
//读取,配置NVIC
void IR_NEC_Read_EXTI_NVIC(void)
{
    NVIC_InitTypeDef NVIC_Initstruct;                                                //声明NVIC初始化结构体
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                                  //选定NVIC分组
    NVIC_Initstruct.NVIC_IRQChannel = IR_NEC_Read_EXIT_IRQn;                         //配置的外部中断0
    NVIC_Initstruct.NVIC_IRQChannelCmd = ENABLE;                                     //使能
    NVIC_Initstruct.NVIC_IRQChannelPreemptionPriority = IR_NEC_Read_EXIT_Priority_1; //主优先级
    NVIC_Initstruct.NVIC_IRQChannelSubPriority = IR_NEC_Read_EXIT_Priority_2;        //副优先级
    NVIC_Init(&NVIC_Initstruct);                                                     //初始化外部中断0的NVIC
}

定时器中断和NVIC

这里我们要保证解码的时间范围内不会触发中断,即最大时间要超过14ms
为了便于计算,我们将分频后的频率设为1MHz,这样一个计数值为1us
定时器传送门

头文件(IR_NEC.h)

//定时器
#define IR_NEC_Read_TIM_RCC RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE)
#define IR_NEC_Read_TIM_TIMx TIM3
#define IR_NEC_Read_TIM_IRQn TIM3_IRQn
#define IR_NEC_Read_TIM_Priority_1 2
#define IR_NEC_Read_TIM_Priority_2 2

C文件(IR_NEC.c)

//读取,定时器初始化
void IR_NEC_Read_TIM_init(void)
{
    TIM_TimeBaseInitTypeDef TIM_Init_Struct;              //声明定时器初始化结构体
    NVIC_InitTypeDef NVIC_Init_Struct;                    //声明NVIC初始化结构体
    IR_NEC_Read_TIM_RCC;                                  //打开时钟
    TIM_Init_Struct.TIM_ClockDivision = TIM_CKD_DIV1;     //滤波器不分频
    TIM_Init_Struct.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
    //每次中断触发时间=[(TIM_Period+1)*(TIM_Prescaler+1)/(SystemCoreClock)] (s)
    TIM_Init_Struct.TIM_Prescaler = 84 - 1;
    TIM_Init_Struct.TIM_Period = 0xffff - 1;
    TIM_Init_Struct.TIM_RepetitionCounter = 0;                 //高级定时器特有,这里写0就行
    TIM_TimeBaseInit(IR_NEC_Read_TIM_TIMx, &TIM_Init_Struct);  //调用函数初始
    TIM_ITConfig(IR_NEC_Read_TIM_TIMx, TIM_IT_Update, ENABLE); //启用溢出中断

    NVIC_Init_Struct.NVIC_IRQChannel = IR_NEC_Read_TIM_IRQn;                         //中断名称
    NVIC_Init_Struct.NVIC_IRQChannelCmd = ENABLE;                                    //使能
    NVIC_Init_Struct.NVIC_IRQChannelPreemptionPriority = IR_NEC_Read_TIM_Priority_1; //主优先级1
    NVIC_Init_Struct.NVIC_IRQChannelSubPriority = IR_NEC_Read_TIM_Priority_2;        //副优先级1
    NVIC_Init(&NVIC_Init_Struct);                                                    //初始化NVIC
    TIM_Cmd(IR_NEC_Read_TIM_TIMx, DISABLE);                                          //关闭定时器
}

解码代码的思路

从上文中可以看出,整个解码过程分为2个大的部分

  1. 读取引导码或重复码
  2. 按顺序读取数据

因此我们使用状态机的思想来编写解码程序

84c00c512dcd48dea29b8d02ae1ef2e2.png

就像这个图中所表示的这样

代码

为了适配不同厂商的某些不标准的NEC编码,我们首先定义一个Byte数组,具体有几个字节的数据可以根据需要修改

头文件(IR_NEC.h)

#define N 4
extern u8 IR_NEC_Read_Dat[N]; //解码的数据
extern u8 IR_NEC_Read_OK;     //解码成功标志

C文件(IR_NEC.c)

u8 IR_NEC_Read_Dat[N] = {0};
u8 IR_NEC_Read_Dat2[N] = {0};

整体代码

C文件(IR_NEC.c)

// NEC解码函数,外部中断下降沿调用
void IR_NEC_Read_Decode(void (*val)(void))
{
    if (IR_NEC_Read_ins == 0) //检测初始低电平
    {//标号0
        IR_NEC_Read_ins = 1;
        TIM_Cmd(IR_NEC_Read_TIM_TIMx, ENABLE);
        TIM_SetCounter(IR_NEC_Read_TIM_TIMx, 0);
    }
    else if (IR_NEC_Read_ins == 1) //判断初始低电平到第二个低电平时间
    {
        IR_NEC_Read_Time = TIM_GetCounter(IR_NEC_Read_TIM_TIMx);
        if (IR_NEC_Read_Time > 13500 - 500 && IR_NEC_Read_Time < 13500 + 500) // 13.5ms左右 引导码
        {//标号1.1
            IR_NEC_Read_ins = 2;
            TIM_SetCounter(IR_NEC_Read_TIM_TIMx, 0);
            IR_NEC_Read_OK = 0;
            for (int i = 0; i < N; i++)
                IR_NEC_Read_Dat[i] = 0;
            IR_NEC_Read_zj = 0;
        }
        else if (IR_NEC_Read_Time > 11250 - 1000 && IR_NEC_Read_Time < 11250 + 1000) // 11.25ms左右 重复码
        {//标号1.2
            IR_NEC_Read_ins = 0;
            IR_NEC_Read_OK = 2;
            for (int i = 0; i < N; i++)
                IR_NEC_Read_Dat[i] = IR_NEC_Read_Dat2[i];

            TIM_SetCounter(IR_NEC_Read_TIM_TIMx, 0);
        }
        else //超时或时间过短 复位
        {
            IR_NEC_Read_ins = 0;
        }
    }
    else if (IR_NEC_Read_ins == 2) //开始解码
    {//标号2
        IR_NEC_Read_Time = TIM_GetCounter(IR_NEC_Read_TIM_TIMx);
        if (IR_NEC_Read_Time > 1120 - 500 && IR_NEC_Read_Time < 1120 + 500) // 1.12ms 写入0
        {
            IR_NEC_Read_zj <<= 1;   //向左移位
            IR_NEC_Read_zj &= 0xfe; //最低位置零
            IR_NEC_Read_Decode_i++;
        }
        else if (IR_NEC_Read_Time > 2250 - 500 && IR_NEC_Read_Time < 2250 + 500) // 2.25ms 写入1
        {
            IR_NEC_Read_zj <<= 1;
            IR_NEC_Read_zj |= 0x01;
            IR_NEC_Read_Decode_i++;
        }
        else //出错复位
        {
            IR_NEC_Read_ins = 0;
            IR_NEC_Read_Decode_i = 0;
            IR_NEC_Read_Decode_j = 0;
            IR_NEC_Read_zj = 0;
        }

        if (IR_NEC_Read_Decode_i >= 8) // uchar每位写入数据
        {
            IR_NEC_Read_Decode_i = 0;
            IR_NEC_Read_Dat[IR_NEC_Read_Decode_j] = IR_NEC_Read_zj;
            IR_NEC_Read_Decode_j++;
            IR_NEC_Read_zj = 0;
        }
        if (IR_NEC_Read_Decode_j >= N) //数据数组的不同位写入数据
        {
            IR_NEC_Read_Decode_i = 0;
            IR_NEC_Read_Decode_j = 0;
            IR_NEC_Read_ins = 0;
            IR_NEC_Read_OK = 1;
            IR_NEC_Read_zj = 0;
            for (int i = 0; i < N; i++)
                IR_NEC_Read_Dat2[i] = IR_NEC_Read_Dat[i];
            TIM_Cmd(IR_NEC_Read_TIM_TIMx, DISABLE);
            val();
        }

        TIM_SetCounter(IR_NEC_Read_TIM_TIMx, 0);
    }
}

 如图所示,在数据部分(以引导码为开始的)除了指出的两个下降沿,其余均为标号2状态

这是重复码的模式

 成品

链接:百度网盘
提取码:jjbd

CSDNicon-default.png?t=M4ADhttps://download.csdn.net/download/m0_57585228/85518936

githubicon-default.png?t=M4ADhttps://github.com/HZ1213825/STM32F4_NEC_Code_Decode

  • 0
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
好的,这个问题我可以帮你回答。下面是一个简单的STM32F103C8T6单片机NEC红外信号解码程序的示例代码: ```c #include "stm32f10x.h" #define IR_PORT GPIOA #define IR_PIN GPIO_Pin_0 volatile uint16_t ir_code = 0; volatile uint8_t ir_state = 0; void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) != RESET) { EXTI_ClearITPendingBit(EXTI_Line0); if (ir_state == 0) { TIM2->CNT = 0; TIM_Cmd(TIM2, ENABLE); ir_state = 1; } else if (ir_state == 1) { ir_code = TIM2->CNT; TIM_Cmd(TIM2, DISABLE); ir_state = 2; } } } void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); ir_state = 0; } } int main(void) { GPIO_InitTypeDef GPIO_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); GPIO_InitStructure.GPIO_Pin = IR_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(IR_PORT, &GPIO_InitStructure); EXTI_InitStructure.EXTI_Line = EXTI_Line0; EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); TIM_TimeBaseStructure.TIM_Period = 0xFFFF; TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ClearFlag(TIM2, TIM_FLAG_Update); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); while (1) { if (ir_code != 0) { // decode NEC infrared signal // ... ir_code = 0; } } } ``` 这个示例程序使用了外部中断和定时器来解码NEC红外信号。当红外接收器接收到一个NEC红外信号的起始脉冲时,会触发外部中断。在中断处理函数中,根据当前状态分别启动和停止定时器,以计算红外信号的脉宽。当定时器溢出时,会触发另一个中断处理函数,重置状态。在主循环中,如果检测到一个完整的红外信号,则进行解码处理。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值