红外线的光谱位于红色光之外,波长是0.76~1.5μm,比红光的波长还长。红外遥控是利用红外线进行传递信息的一种控制方式,红外遥控具有抗干扰,威廉希尔官方网站
简单,容易编码和解码,功耗小,成本低的优点。红外遥控几乎适用所有家电的控制。
红外遥控系统的主要部分为调制、发射和接收。
1.调制
红外遥控是以调制的方式发射数据,就是把数据和一定频率的载波进行“与”操作,这样既可以提高发射效率又可以降低电源功耗。调制载波频率一般在30khz到60khz之间,大多数使用的是38kHz,占空比1/3的方波,如图2所示,这是由发射端所使用的455kHz晶振决定的。在发射端要对晶振进行整数分频,分频系数一般取12,所以455kHz÷12≈37.9 kHz≈38kHz。
2.发射系统
红外线通过红外发光二极管(LED)发射出去,红外发光二极管(红外发射管)内部构造与普通的发光二极管基本相同,材料和普通发光二极管不同,在红外发射管两端施加一定电压时,它发出的是红外线而不是可见光。
3.一体化红外接收头
红外接收头的种类很多,引脚定义也不相同,一般都有三个引脚,包括供电脚,接地和信号输出脚。
在串行通讯里,我们经常谈及‘mark’和‘space’标记。‘space’是个默认信号,是指发射管关闭状态,在‘space’期间,红外光不被发射。反之在‘mark’状态期间,红外光以特定的频率脉冲形式发射。在接收端,一个‘space’信号以高电平方式重现输出。反之一个‘mark’信号便是以低电平方式重现。请注意,这里的‘mark’和‘space’不是我们需要发送的状态1和0。‘mark’和‘space’以及1和0之间的真正关系取决于被应用的协议。
红外发射信号的引导码是由引导码由“9ms mark + 4.5ms space”构成,表示一组键码的开始。逻辑“1”由“560us mark + 1690 space”组成,symbol period 为2.25ms;逻辑“0”由“560us mark + 560us space”组成,symbol period 为 1.12ms。
本产品采用NEC通信格式,一串数据是32位数据,8位地址8位命令以及各自的反码
接收数据的格式为:起始信号+用户码+用户反码+数据码+数据反码+结束信号
用户码就是custom,用户码为前八位是0x00和其反码0xFF
我们编写其红外接收程序主要是依据上述NEC出通讯编码格式编写,下面是程序部分。
程序分析:
我们在globalvariable.h中对红外数据接收引脚进行定义:
//红外遥控,红外接收器数据线,外部中断 PD10
#define IRIN_PIN GPIO_Pin_10
#define IRIN_GPIO GPIOD
#define IRIN_PORTSOURCE GPIO_PortSourceGPIOD
#define IRIN_PINSOURCE GPIO_PinSource10
#define IRIN_EXITLINE EXTI_Line10
#define IRIN_IRQCH EXTI15_10_IRQn
#define IRIN GPIO_ReadInputDataBit(IRIN_GPIO, IRIN_PIN)//读GPIO_PIN_10的电平,如果为高则为1,低为0
我们对红外模块部分建立一个文件IRControl.c(infrared radiation)和一个头文件IRControl.h
#ifndef __IRCTROL_H_
#define __IRCTROL_H_
#include "stm32f10x.h"
extern unsigned char ir_rec_flag;//接收数据标志位 1 有新数据 0 没有
extern char ctrl_comm;//控制指令
extern unsigned char continue_time;
void IRCtrolInit(void);
void IRIntIsr(void);
#endif
IRControl.c
其次利用定时器TIM3产生一个1微妙的延时,因此在IRControl.c配置定时器中断时可进行如下配置:
#include "IRCtrol.h"
#include "interface.h"
unsigned char ir_rec_flag=0;//接收数据标志位 1 有新数据 0 没有
unsigned char IRCOM[3];//定义数组存放四个数据
void Time3Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 1;
TIM_TimeBaseStructure.TIM_Prescaler = (72 - 1);//72M / 72 = 1us
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Down;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
}
//定时器TIM3产生1us 延时
void DelayUs(vu32 nCount)
{
u16 TIMCounter = nCount;
TIM_Cmd(TIM3, ENABLE);
TIM_SetCounter(TIM3, TIMCounter);
while (TIMCounter>1)
{
TIMCounter = TIM_GetCounter(TIM3);//得到计数器的值
}
TIM_Cmd(TIM3, DISABLE);
}
在IRControl.c中进行外部中断配置:
/********************外部中断配置 红外遥控配置************************/
void IRCtrolInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;//定义一个外部中断相关的结构体
NVIC_InitTypeDef NVIC_InitStructure; //定义一个中断的结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);
GPIO_InitStructure.GPIO_Pin = IRIN_PIN;//配置使能GPIO管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//配置GPIO模式,输入上拉
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置GPIO端口速度
GPIO_Init(IRIN_GPIO , &GPIO_InitStructure);
GPIO_EXTILineConfig(IRIN_PORTSOURCE , IRIN_PINSOURCE);
EXTI_InitStructure.EXTI_Line = IRIN_EXITLINE;//将对应的GPIO口连接到中断线上
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//中断事件类型,下降沿
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//选择模式,中断型
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能该中断
EXTI_Init(&EXTI_InitStructure);//将配置好的参数写入寄存器
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //阶级为0,不可嵌套
NVIC_InitStructure.NVIC_IRQChannel = IRIN_IRQCH;//打开PINA_8的外部中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//主优先级0,最高
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级,最低
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能该模块中断
NVIC_Init(&NVIC_InitStructure); //中断初始化,将结构体定义的数据执行
Time3Init();
}
/********************延时0.14毫秒************************/
void DelayIr(unsigned char x)
{
while(x--)
{
DelayUs(140);
}
}
/********************红外数据接收函数************************/
void IRIntIsr(void)
{
unsigned char j,k,N=0;
//检测误传输信号
DelayIr(15);//任意的延时时间
if (IRIN==1) //读端口PD10数据为1,确认接收到信号
{
return;
}
continue_time = 40;//连发信号,该程序在main.c中,表示指令持续 40*5 = 200ms 无指令停车。5毫秒是tick_5ms产生。
//意思是我的PD10开始检测到了高电平信号,但是超过200毫秒没有别的信号进来,就让小车停车。
//确认IR信号出现
while (!IRIN) //等IR变为高电平,跳过9ms的前导低电平信号。这么写的很有意思,因为起始码是一个持续9毫秒低电平信号,我们默认它产生,此时的IRIN为0,那么对其取反就是检测其是否为高电平,一举两得。
{
DelayIr(1);
}
for (j=0;j<4;j++) //收集四组数据
{
for (k=0;k<8;k++) //每组数据有8位
{
while (IRIN) //等 IR 变为低电平,跳过4.5ms的前导高电平信号。可以理解为起始码为9毫秒低电平和4.5毫秒高电平组成,4.5毫秒高电平结束后就是用户码和数据码,而用户码和数据码的1是由0.65毫秒低电平和0356毫秒的高电平组成(共1.125毫秒),逻辑0则是由0.56毫秒低电平和1.69毫秒高电平组成(共2.25毫秒)。
{
DelayIr(1);
}
while (!IRIN) //等IR变为高电平
{
DelayIr(1);
}
//计算IRIN高电平时长,用以确定是逻辑0还是1。
while(IRIN)
{
DelayIr(1);
N++;
if (N>=30) //超过4.2毫秒自动退出
{
return;
}
}
//根据计时长短进行二进制识别
IRCOM[j]=IRCOM[j] >> 1; //最高位右移一位,数据最高位补“0”,
if (N>=8)
{
IRCOM[j] = IRCOM[j] | 0x80;
} //如果大于8*0.14=1.12毫秒,数据最高位补“1”否则
N=0;//重新对N赋值
}//end for k
}//end for j
k = ~IRCOM[3];//数组计数从0开始,所以数组中的3代表对第四位的数据取反,第四位是数据为的取反,故对数据位反码取反就是数据码本身。
if (IRCOM[2] != k)//对数据进行较验,如果数据码不等于数据反码取反,则退出。
{
return; }
//指令转换
switch(IRCOM[2])//读取数据码
{
case 0x46: ctrl_comm = COMM_UP;break;
case 0x15: ctrl_comm = COMM_DOWN;break;
case 0x44: ctrl_comm = COMM_LEFT;break;
case 0x43: ctrl_comm = COMM_RIGHT;break;
case 0x40: ctrl_comm = COMM_STOP;break;
default : return;
}
ir_rec_flag = 1;
}
主函数main.c:
char ctrl_comm = COMM_STOP;//控制指令
unsigned char continue_time=0;
int main(void)
{
delay_init();
GPIOCLKInit();
IRCtrolInit();
TIM2_Init();
MotorInit();
while(1)
{
if(tick_5ms >= 5)//5ms
{
tick_5ms = 0;
tick_200ms++;
if(tick_200ms >= 40)
{
tick_200ms = 0;
}
continue_time--;//200ms 无接收指令就停车
if(continue_time == 0)
{
continue_time = 1;
CarStop();
}
if(ir_rec_flag == 1)//接收到红外信号
{
ir_rec_flag = 0;
switch(ctrl_comm)
{
case COMM_UP: CarGo();break;
case COMM_DOWN: CarBack();break;
case COMM_LEFT: CarLeft();break;
case COMM_RIGHT: CarRight();break;
case COMM_STOP: CarStop();break;
default : break;
}
}
}
}
}
红外线的光谱位于红色光之外,波长是0.76~1.5μm,比红光的波长还长。红外遥控是利用红外线进行传递信息的一种控制方式,红外遥控具有抗干扰,威廉希尔官方网站
简单,容易编码和解码,功耗小,成本低的优点。红外遥控几乎适用所有家电的控制。
红外遥控系统的主要部分为调制、发射和接收。
1.调制
红外遥控是以调制的方式发射数据,就是把数据和一定频率的载波进行“与”操作,这样既可以提高发射效率又可以降低电源功耗。调制载波频率一般在30khz到60khz之间,大多数使用的是38kHz,占空比1/3的方波,如图2所示,这是由发射端所使用的455kHz晶振决定的。在发射端要对晶振进行整数分频,分频系数一般取12,所以455kHz÷12≈37.9 kHz≈38kHz。
2.发射系统
红外线通过红外发光二极管(LED)发射出去,红外发光二极管(红外发射管)内部构造与普通的发光二极管基本相同,材料和普通发光二极管不同,在红外发射管两端施加一定电压时,它发出的是红外线而不是可见光。
3.一体化红外接收头
红外接收头的种类很多,引脚定义也不相同,一般都有三个引脚,包括供电脚,接地和信号输出脚。
在串行通讯里,我们经常谈及‘mark’和‘space’标记。‘space’是个默认信号,是指发射管关闭状态,在‘space’期间,红外光不被发射。反之在‘mark’状态期间,红外光以特定的频率脉冲形式发射。在接收端,一个‘space’信号以高电平方式重现输出。反之一个‘mark’信号便是以低电平方式重现。请注意,这里的‘mark’和‘space’不是我们需要发送的状态1和0。‘mark’和‘space’以及1和0之间的真正关系取决于被应用的协议。
红外发射信号的引导码是由引导码由“9ms mark + 4.5ms space”构成,表示一组键码的开始。逻辑“1”由“560us mark + 1690 space”组成,symbol period 为2.25ms;逻辑“0”由“560us mark + 560us space”组成,symbol period 为 1.12ms。
本产品采用NEC通信格式,一串数据是32位数据,8位地址8位命令以及各自的反码
接收数据的格式为:起始信号+用户码+用户反码+数据码+数据反码+结束信号
用户码就是custom,用户码为前八位是0x00和其反码0xFF
我们编写其红外接收程序主要是依据上述NEC出通讯编码格式编写,下面是程序部分。
程序分析:
我们在globalvariable.h中对红外数据接收引脚进行定义:
//红外遥控,红外接收器数据线,外部中断 PD10
#define IRIN_PIN GPIO_Pin_10
#define IRIN_GPIO GPIOD
#define IRIN_PORTSOURCE GPIO_PortSourceGPIOD
#define IRIN_PINSOURCE GPIO_PinSource10
#define IRIN_EXITLINE EXTI_Line10
#define IRIN_IRQCH EXTI15_10_IRQn
#define IRIN GPIO_ReadInputDataBit(IRIN_GPIO, IRIN_PIN)//读GPIO_PIN_10的电平,如果为高则为1,低为0
我们对红外模块部分建立一个文件IRControl.c(infrared radiation)和一个头文件IRControl.h
#ifndef __IRCTROL_H_
#define __IRCTROL_H_
#include "stm32f10x.h"
extern unsigned char ir_rec_flag;//接收数据标志位 1 有新数据 0 没有
extern char ctrl_comm;//控制指令
extern unsigned char continue_time;
void IRCtrolInit(void);
void IRIntIsr(void);
#endif
IRControl.c
其次利用定时器TIM3产生一个1微妙的延时,因此在IRControl.c配置定时器中断时可进行如下配置:
#include "IRCtrol.h"
#include "interface.h"
unsigned char ir_rec_flag=0;//接收数据标志位 1 有新数据 0 没有
unsigned char IRCOM[3];//定义数组存放四个数据
void Time3Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 1;
TIM_TimeBaseStructure.TIM_Prescaler = (72 - 1);//72M / 72 = 1us
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Down;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
}
//定时器TIM3产生1us 延时
void DelayUs(vu32 nCount)
{
u16 TIMCounter = nCount;
TIM_Cmd(TIM3, ENABLE);
TIM_SetCounter(TIM3, TIMCounter);
while (TIMCounter>1)
{
TIMCounter = TIM_GetCounter(TIM3);//得到计数器的值
}
TIM_Cmd(TIM3, DISABLE);
}
在IRControl.c中进行外部中断配置:
/********************外部中断配置 红外遥控配置************************/
void IRCtrolInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;//定义一个外部中断相关的结构体
NVIC_InitTypeDef NVIC_InitStructure; //定义一个中断的结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);
GPIO_InitStructure.GPIO_Pin = IRIN_PIN;//配置使能GPIO管脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//配置GPIO模式,输入上拉
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//配置GPIO端口速度
GPIO_Init(IRIN_GPIO , &GPIO_InitStructure);
GPIO_EXTILineConfig(IRIN_PORTSOURCE , IRIN_PINSOURCE);
EXTI_InitStructure.EXTI_Line = IRIN_EXITLINE;//将对应的GPIO口连接到中断线上
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//中断事件类型,下降沿
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//选择模式,中断型
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能该中断
EXTI_Init(&EXTI_InitStructure);//将配置好的参数写入寄存器
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //阶级为0,不可嵌套
NVIC_InitStructure.NVIC_IRQChannel = IRIN_IRQCH;//打开PINA_8的外部中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//主优先级0,最高
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级,最低
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能该模块中断
NVIC_Init(&NVIC_InitStructure); //中断初始化,将结构体定义的数据执行
Time3Init();
}
/********************延时0.14毫秒************************/
void DelayIr(unsigned char x)
{
while(x--)
{
DelayUs(140);
}
}
/********************红外数据接收函数************************/
void IRIntIsr(void)
{
unsigned char j,k,N=0;
//检测误传输信号
DelayIr(15);//任意的延时时间
if (IRIN==1) //读端口PD10数据为1,确认接收到信号
{
return;
}
continue_time = 40;//连发信号,该程序在main.c中,表示指令持续 40*5 = 200ms 无指令停车。5毫秒是tick_5ms产生。
//意思是我的PD10开始检测到了高电平信号,但是超过200毫秒没有别的信号进来,就让小车停车。
//确认IR信号出现
while (!IRIN) //等IR变为高电平,跳过9ms的前导低电平信号。这么写的很有意思,因为起始码是一个持续9毫秒低电平信号,我们默认它产生,此时的IRIN为0,那么对其取反就是检测其是否为高电平,一举两得。
{
DelayIr(1);
}
for (j=0;j<4;j++) //收集四组数据
{
for (k=0;k<8;k++) //每组数据有8位
{
while (IRIN) //等 IR 变为低电平,跳过4.5ms的前导高电平信号。可以理解为起始码为9毫秒低电平和4.5毫秒高电平组成,4.5毫秒高电平结束后就是用户码和数据码,而用户码和数据码的1是由0.65毫秒低电平和0356毫秒的高电平组成(共1.125毫秒),逻辑0则是由0.56毫秒低电平和1.69毫秒高电平组成(共2.25毫秒)。
{
DelayIr(1);
}
while (!IRIN) //等IR变为高电平
{
DelayIr(1);
}
//计算IRIN高电平时长,用以确定是逻辑0还是1。
while(IRIN)
{
DelayIr(1);
N++;
if (N>=30) //超过4.2毫秒自动退出
{
return;
}
}
//根据计时长短进行二进制识别
IRCOM[j]=IRCOM[j] >> 1; //最高位右移一位,数据最高位补“0”,
if (N>=8)
{
IRCOM[j] = IRCOM[j] | 0x80;
} //如果大于8*0.14=1.12毫秒,数据最高位补“1”否则
N=0;//重新对N赋值
}//end for k
}//end for j
k = ~IRCOM[3];//数组计数从0开始,所以数组中的3代表对第四位的数据取反,第四位是数据为的取反,故对数据位反码取反就是数据码本身。
if (IRCOM[2] != k)//对数据进行较验,如果数据码不等于数据反码取反,则退出。
{
return; }
//指令转换
switch(IRCOM[2])//读取数据码
{
case 0x46: ctrl_comm = COMM_UP;break;
case 0x15: ctrl_comm = COMM_DOWN;break;
case 0x44: ctrl_comm = COMM_LEFT;break;
case 0x43: ctrl_comm = COMM_RIGHT;break;
case 0x40: ctrl_comm = COMM_STOP;break;
default : return;
}
ir_rec_flag = 1;
}
主函数main.c:
char ctrl_comm = COMM_STOP;//控制指令
unsigned char continue_time=0;
int main(void)
{
delay_init();
GPIOCLKInit();
IRCtrolInit();
TIM2_Init();
MotorInit();
while(1)
{
if(tick_5ms >= 5)//5ms
{
tick_5ms = 0;
tick_200ms++;
if(tick_200ms >= 40)
{
tick_200ms = 0;
}
continue_time--;//200ms 无接收指令就停车
if(continue_time == 0)
{
continue_time = 1;
CarStop();
}
if(ir_rec_flag == 1)//接收到红外信号
{
ir_rec_flag = 0;
switch(ctrl_comm)
{
case COMM_UP: CarGo();break;
case COMM_DOWN: CarBack();break;
case COMM_LEFT: CarLeft();break;
case COMM_RIGHT: CarRight();break;
case COMM_STOP: CarStop();break;
default : break;
}
}
}
}
}
举报