IR红外遥控器和接收器

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、自发自收,测试结果:

相关推荐
晨非辰6 小时前
【数据结构入坑指南】--《层序分明:堆的实现、排序与TOP-K问题一站式攻克(源码实战)》
c语言·开发语言·数据结构·算法·面试
czy87874756 小时前
用C语言实现桥接模式
c语言·桥接模式
普密斯科技6 小时前
图像尺寸测量仪应用Type-C接口:精准检测,赋能科技
c语言·开发语言·科技
degen_7 小时前
第二次进入PEICORE流程
c语言·笔记
Want59517 小时前
C/C++大雪纷飞①
c语言·开发语言·c++
一念&20 小时前
每日一个C语言知识:C 共用体
c语言
草莓工作室21 小时前
数据结构9:队列
c语言·数据结构·队列
小龙报1 天前
《算法每日一题(1)--- 第31场蓝桥算法挑战赛》
c语言·开发语言·c++·git·算法·学习方法
violet-lz1 天前
数据结构八大排序:归并排序-原理+C语言实现+优化+面试题
c语言·数据结构·排序算法