1、红外遥控电路

空闲状态:红外发射管不亮,接收头输出高电平。

发送高电平:红外发射管以38KHz频率闪烁发光,接收头输出低电平。

发送低电平:红外发射管不亮,接收头输出高电平。

NEC协议中,信息传输是基于38KHz载波,也就是说红外线是以载波的方式传递。

2、红外遥控协议

1)、逻辑0定义:

“红外发射头”产生一个逻辑0:发送载波560us  + 不发送载波560us;

“红外接收头”收到的逻辑0:560us低电平+560us高电平。

2)、逻辑1定义:

“红外发射头”产生一个逻辑1:发送载波560us  + 不发送载波1680us;波形图如下:

“红外接收头”收到的逻辑1:560us低电平+1680us高电平

3)、引导码定义:

红外发射头产生一个引导码:发送载波9ms +不发送载波4.5ms。波形图如下:

“红外接收头”收到一个引导码:9ms的低电平+4.5ms高电平。

4)、地址码定义:

“地址码”为8位,表示“被控制的设备地址”。

5)、地址反码定义:

地址反码为8位,是“地址码”按位取反。

6)、命令码定义:

“命令码”为8位,表示“发送的按键值”。

7)、命令反码定义:

命令反码为8位,是“命令码”按位取反。

8)、重复码定义:

当用户长时间按住遥控器的某个按键时,NEC协议会发送重复码,而不是重复发送完整的数据帧。

红外发射头”产生一个重复码:发送载波9ms +不发送载波2.25ms +发送载波560us +不发送载波97.94ms。波形图如下:

“红外接收头”收到低电平:9ms,高电平:2.25ms,低电平:560μs,高电平:97.94ms。波形图如下:

根据上面的波形图,红外接收使用TIM2_CH3引脚捕获红外接收头的输出引脚的高电平时间,当红外发射头停止发送载波时,红外接收头输出为高电平。

3、红外接收头根据接收到的高电平时间来接收数据的原理:

1)、通过检测引导码中的4.5毫秒的特征电平时间,就可以判断红外接收头开始接收新数据。

2)、通过检测重复码中的2.25毫秒的特征电平时间,就可以判断红外接收头接收完成。

3)、通过检测重复码中的560微秒的特征电平时间,就可以判断红外接收头收到逻辑0。

4)、通过检测重复码中的1680微秒的特征电平时间,就可以判断红外接收头收到逻辑1。

#define Read_TIM2_CH3_PIN()     PBin(10)       //红外数据输入脚

u16 HighLevelTimeValue;     //捕获到的高电平时间值

u8 Remote_RX_CompleteFlag; //1表示接收到数据

u8 Remote_RX_StartFlag;    //1表示接收到引导码

u8 Remote_RX_RiseFlag;     //1表示接收到上升沿

u8 DuplicationCode_Cnt;    //“重复码”计数器

u32 Remote_RX_Data=0;   //红外接收到的数据

u8  RepeatCount=0;     //按键按下的次数  

//函数功能:红外遥控初始化:设置TIM2为输入捕获,并使能TIM2更新中断和输入捕获中断

//APB1时钟为72MHz

//arr:自动重装值。

//psc:时钟预分频数

//TIM_CKD_DIV1:定时器时钟 = 输入频率

//TIM_CKD_DIV2:定时器时钟 = 输入频率/2

//TIM_CKD_DIV4:定时器时钟 = 输入频率/4

//Remote_Receiver_Init(10000,72);//当arr=10000,psc=72时,则为10ms,误差为1us;

//计算公式:arr*psc/72000000/1,当arr=1000,psc=72时,则为1ms,误差为1us;

void Remote_Receiver_Init(u16 arr,u16 psc)                

    GPIO_InitTypeDef GPIO_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

    TIM_ICInitTypeDef  TIM_ICInitStructure; 

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

  //设置TIM2定时器的APB1外设时钟

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能PORTB时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //使能重映射功能

    //TIM2_CH3位于PB10引脚

    GPIO_PinRemapConfig(GPIO_PartialRemap2_TIM2,ENABLE);

    //使能TIM2引脚映射

//  GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);

    //禁止JTAG功能,把PB3,PB4作为普通IO口使用  

   

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //选择PIN10,是TIM2_CH3

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

 //设置引脚的最高输出速率为50MHz   

    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_SetBits(GPIOB,GPIO_Pin_10);//PB10输出高电平

                         

    TIM_TimeBaseStructure.TIM_Period = (arr-1);

  //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 

    TIM_TimeBaseStructure.TIM_Prescaler =(psc-1);

  //设置用来作为TIMx时钟频率除数的预分频值

    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;

    //设置时钟分割:TDTS = Tck_tim

    //计算公式:arr*psc/72000000/1,当arr=1000,psc=72时,则为1ms,误差为1us;

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    //TIM向上计数模式

    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    //根据指定的参数初始化TIMx的时间基数单位

    TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;

    //选择输入端IC3映射到TI3上

    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获

    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;

    //TIM输入1、2、3或4被选中,分别连接到IC1、IC2、IC3或IC4,这里是TIM2_CH3

    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;

    // 配置输入分频,不分频

    TIM_ICInitStructure.TIM_ICFilter = 0X3;

    //IC4F=0011配置输入滤波器,8个定时器时钟周期滤波

    TIM_ICInit(TIM2, &TIM_ICInitStructure); //初始化TIM2的输入捕获通道

    TIM_Cmd(TIM2,ENABLE ); // 使能定时器2

   

    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择中断源为TIM2_IRQn

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

    //设置抢占优先级为1

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //设置响应优先级为3

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  //设置中断使能

    NVIC_Init(&NVIC_InitStructure);

    //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

    TIM_ITConfig( TIM2,TIM_IT_Update|TIM_IT_CC3,ENABLE);

    //允许更新中断,允许CC3IE捕获中断

    TIM_ClearITPendingBit(TIM2,TIM_IT_Update|TIM_IT_CC3);

    //清除TIM2更新中断标志位和输入捕获标志位

  Remote_RX_CompleteFlag=0;

  Remote_RX_StartFlag=0;

  Remote_RX_RiseFlag=0;

  DuplicationCode_Cnt=0;   

}

//函数功能:处理红外键盘

// 返回值:

// 0,没有任何按键按下

// 其他,按下的按键键值.

u8 Remote_Receiver_Scan(void)

{

    u8 sta=0;

    u8 t1,t2,addressFlag;

 

    if(Remote_RX_CompleteFlag==1)

    //Remote_RX_CompleteFlag=1标记已经完成一次按键的键值信息采集

    {

        printf("Remote_RX_Data=0x%08X     ",Remote_RX_Data);

        //识别码 + 识别码反码 + 键值 + 键值反码

        t1=Remote_RX_Data>>24;               //得到地址码

        t2=(Remote_RX_Data>>16)&0xff;      //得到地址反码

        addressFlag=0;

        if( (t1==(u8)~t2) && t1 == REMOTE_ID ) addressFlag=1;

        //检验遥控识别码(ID)及地址

        if(addressFlag==1)

        {

            t1=Remote_RX_Data>>8;//键值

            t2=Remote_RX_Data;   //键值反码

            if(t1==(u8)~t2)

            {

                sta = t1;// 键值正确

                DuplicationCode_Cnt = 0;

                Remote_RX_RiseFlag=0;

                Remote_RX_Data = 0;

                Remote_RX_StartFlag=0;

                Remote_RX_CompleteFlag=0;

            }

        }

       

        if( (sta==0)||( Remote_RX_StartFlag==0 ) )

//按键数据错误/也有可能是遥控已经没有按下

        {

            Remote_RX_CompleteFlag=0;//清除接收到有效按键标识

            RepeatCount=0;  //清除按键次数计数器

        }

    }

    return sta;

}

//函数功能:定时器2中断服务程序,10ms溢出一次 

void TIM2_IRQHandler(void)

{                

    if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)//TIM2产生更新中断

    {

        if( Remote_RX_StartFlag==1 )//Remote_RX_StartFlag=1表示收到引导码

        {  

            Remote_RX_RiseFlag=0;//取消上升沿已经被捕获标记

            if( DuplicationCode_Cnt==0X00 )// “重复帧”计数器为0

                Remote_RX_CompleteFlag=1;

         //Remote_RX_CompleteFlag=1标记已经完成一次按键的键值信息采集

            if( DuplicationCode_Cnt<14 ) //“重复帧”计数器,小于14

                DuplicationCode_Cnt++;//遥控器在发送中

            else//DuplicationCode_Cnt是“重复帧”计数器,等于15

            {

                Remote_RX_StartFlag=0;//清空引导标识

                DuplicationCode_Cnt=0;//“重复帧”计数器清零

            }                                     

        }                              

    }

    if(TIM_GetITStatus(TIM2,TIM_IT_CC3)!=RESET)//TIM2产生捕获中断

    {    

        if( Read_TIM2_CH3_PIN() )//上升沿捕获

        {

            TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Falling);

            //CC3P=1设置为下降沿捕获

            TIM_SetCounter(TIM2,0);

//一旦捕获到上升沿,则清空定时器值,为的是在下降沿处读取高电平的时间值

      //引导码:9ms高电平 + 4.5ms低电平,合计13.5ms

      //0码 :560us高电平 + 560us低电平,合计1.125ms

      //1码 :560us高电平 + 1.6875ms低电平,合计2.25ms

      //结束码 :560us高电平

            Remote_RX_RiseFlag=1;//Remote_RX_RiseFlag=1表示捕获到上升沿

        }

        else//下降沿捕获

        {

            HighLevelTimeValue=TIM_GetCapture3(TIM2);

            //读取CCR3也可以清CC3IF标志位,这个值是“捕获到的高电平时间”

            TIM_OC3PolarityConfig(TIM2,TIM_ICPolarity_Rising);

            // CC3P=0   设置为上升沿捕获

            if(Remote_RX_RiseFlag==1)

            {

                if(Remote_RX_StartFlag==1)//接收到了引导码

                {                  

                    if(HighLevelTimeValue>300&&HighLevelTimeValue<800)                  {//红外接收头收到的逻辑0:560us低电平+560us高电平。

                        Remote_RX_Data<<=1; // 左移一位.

                        Remote_RX_Data|=0;  // 接收到0,最低为添加0   

                    }

                    else if(HighLevelTimeValue>1400&&HighLevelTimeValue<1800)

                    {//红外接收头收到的逻辑1:560us低电平+1680us高电平

                        Remote_RX_Data<<=1; // 左移一位.

                        Remote_RX_Data|=1;  // 接收到1,最低为添加1

                    }

                    else if(HighLevelTimeValue>2200&&HighLevelTimeValue<2600)

                    {//红外接收头收到一个重复帧:9ms低电平 + 2.25ms高电平

                        RepeatCount++; //按键次数增加1次

                        DuplicationCode_Cnt=0;

                        //清空计时器,标记已经完成一次按键的键值信息采集    

                    }

                }

                else if(HighLevelTimeValue>4200&&HighLevelTimeValue<4700)

                {//红外接收头收到一个引导码:9ms的低电平+4.5ms高电平。

                    Remote_RX_StartFlag=1;

                    //Remote_RX_StartFlag=1表示收到引导码

                    RepeatCount=0;//清除按键次数计数器

                }                      

            }

            Remote_RX_RiseFlag=0;//Remote_RX_RiseFlag=0表示捕获到下降沿

        }                                                         

    }

    TIM_ClearITPendingBit(TIM2,TIM_IT_Update|TIM_IT_CC3);

  //清除TIM2更新中断标志位和输入捕获标志位

}

4、红外发射头工作的原理:

使用TIM4通道2输出PWM波,占空比为50%,重装在频率为38KHz。

PWM输出的周期为ARR*PR*1/72000000/1,单位为秒。

令ARR=1894,PR=1,则周期为:

T=1894*1/72/1=26.30555555555556us

f=(72000000*1)/(1894*1)= 38014.78352692714Hz=38.01478352692714KHz

#define REMOTE_Send_Enable()   TIM_Cmd(TIM4, ENABLE)  //使能定时器4

#define REMOTE_Send_Disable()  TIM_Cmd(TIM4, DISABLE) //使能定时器4

//函数功能:将PB7配置为TIM4_CH2输出

void TIM4_CH2_Pin_Config(void)

{

  GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能定时器4时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);

    //使能GPIO外设和AFIO复用功能模块时钟

  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_7; //选择PIN7,是TIM4的CH2

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //设置引脚为复用推挽输出   

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

   //设置引脚的最高输出速率为50MHz

  GPIO_Init(GPIOB, &GPIO_InitStructure);

}

//PWM输出的周期为ARR*PR*1/72000000/1,单位为秒

//PWM输出比较极性的宽度为(CCR1_Val/2)*720*1/72000000/1,单位为秒

//CCR1_Val表示PWM信号电平跳变值

//Remote_Send_Init(1894,1)//周期为26.30555555555556us,f=38.01478352692714KHz

void Remote_Send_Init(u32 ARR,u32 PR)

{

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

    TIM_OCInitTypeDef TIM_OCInitStructure;

    TIM4_CH2_Pin_Config();

    TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;

   //设置时钟分频系数,TIMx_CR1中的CKD[1:0]=00,Tdts=Tck_int;

    TIM_TimeBaseInitStructure.TIM_Period = ARR-1;//设置周期为ARR-1

    TIM_TimeBaseInitStructure.TIM_Prescaler = PR-1;//设置TIMx预分频器为PR-1

    TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;

//设置定时器计数模式:向上计数模式,TIM1_CR1中的CMS[1:0]=00,TIM1_CR1中的DIR=0

  TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;

    //设置周期次数计数寄存器的值为1,在PWM中,该设置不影响;

    //PWM输出的周期为ARR*PR*1/72000000/1,单位为秒

    //PWM输出比较极性的宽度为(ARR/2)*720*1/72000000/1,单位为秒

    TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);

/////////////////////////配置PWM通道2///////////////////////////////////

  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择TIM脉冲宽度调制模式1   

  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

  //使能输出比较状态

  TIM_OCInitStructure.TIM_Pulse = ARR/2;

    //设置跳变值,当计数器计数到这个值时,电平发生跳变,这里设置占空比为50%

  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  //设置PWM输出比较极性为为高电平

  TIM_OC2Init(TIM4, &TIM_OCInitStructure);

  //根据TIM_OCInitStructure所指定的参数初始化TIM4通道2

  TIM_OC2PreloadConfig(TIM4, TIM_OCPreload_Enable);

  //PWM输出比较2预装载使能

  TIM_ARRPreloadConfig(TIM4, ENABLE);   //使能TIM4重载寄存器ARR

  TIM_CtrlPWMOutputs(TIM4,ENABLE);    //TIM4定时器的PWM主输出模式使能

//  TIM_SetCompare2(TIM4,ARR/2);//占空比为50%

//  TIM_Cmd(TIM4, ENABLE);            //使能定时器4

//  TIM_Cmd(TIM4, DISABLE);  //失能TIM4,防止第一个脉冲异常

  REMOTE_Send_Disable();//不使能发送

}

//函数功能:发送引导码

void Remote_Send_L(void)

{

    REMOTE_Send_Enable(); //使能发送

    delay_us(8890);//9000

    REMOTE_Send_Disable();//不使能发送

    delay_us(4490);//4500

}

//函数功能:发送重复码

void Remote_Send_DuplicationCode(void)

{

  REMOTE_Send_Enable(); //使能发送

  delay_us(8890);//9000

  REMOTE_Send_Disable();//不使能发送

  delay_us(2240);//2250

  REMOTE_Send_Enable(); //使能发送

  delay_us(550);//560

  REMOTE_Send_Disable();//不使能发送

  delay_us(97930);//97940

}

//函数功能:发送一个逻辑1

void Remote_Send_High_Level(void)

{

    REMOTE_Send_Enable(); //使能发送

    delay_us(550);//560

    REMOTE_Send_Disable();//不使能发送

    delay_us(1670);//1680

}

//函数功能:发送一个逻辑0

void Remote_Send_Low_Level(void)

{

    REMOTE_Send_Enable(); //使能发送

    delay_us(550);//560

    REMOTE_Send_Disable();//不使能发送

    delay_us(550);//560

}

//函数功能:发送一个字节

void Remote_Send_a_Byte(uint8_t data)

{

    signed char i;

    for (i = 7; i >=0; i--)

    {

        if (data&(1<<i))

        {

            Remote_Send_High_Level();

        }

        else

        {

            Remote_Send_Low_Level();

        }

    }

}

//函数功能:发送一个按键值

void Remote_Send_Data(uint8_t keyValue)

{

    u8 address;

    address=REMOTE_Send_ID;

    Remote_Send_L();

    Remote_Send_a_Byte(address);

    Remote_Send_a_Byte(~address);

    Remote_Send_a_Byte(keyValue);

    Remote_Send_a_Byte(~keyValue);

    Remote_Send_DuplicationCode();

    Remote_Send_DuplicationCode();

    Remote_Send_DuplicationCode();

    delay_ms(2000);

}

5、main.c如下:

#include "stm32f10x.h"//使能uint8_t,uint16_t,uint32_t,uint64_t,int8_t,int16_t,int32_t,int64_t
#include "stdio.h"  //getchar(),putchar(),scanf(),printf(),puts(),gets(),sprintf()
#include "string.h" //使能strcpy(),strlen(),memset()
#include "delay.h"
#include "USART4.h"
#include "LED.h"
#include "Remote_Receiver.h"
#include "Remote_Send.h"

const char CPU_Reset_REG[]="\r\nCPU reset!\r\n";
int main(void)
{
	u8 ret,i;
//  uint8_t tmpNumberOf_RemanentLengthBytes;//“解码前剩余长度的字节数”
  	
//	SCB->VTOR = 0x8000000;//中断向量表重定义

//	SystemInit();
	delay_init();//延时函数初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
	USART4_Serial_Interface_Enable(115200);
	printf("%s",CPU_Reset_REG);//调试串口输出"\r\nCPU reset!\r\n"
	LED_Init();
	LED0_ON();
	Remote_Receiver_Init(10000,72);//当arr=10000,psc=72时,则为10ms,误差为1us;

	Remote_Send_Init(1894,1);//周期为26.30555555555556us,f=38.01478352692714KHz
	delay_ms(3000);
	while(1)
	{
		for(i=0;i<20;i++)
		{
		  Remote_Send_Data(i);
		  printf("Send %u    ",i);
		  ret=Remote_Receiver_Scan();
		  printf("ret=0x%02X\r\n",ret);
		}
	}
}

6、自发自收,测试结果:

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐