文章目录
一、介绍部分
红外遥控简介
硬件电路
基本发送与接收
NEC编码
NEC编码:对红外高低电平等的规则
部分按键的编码示例
最后会多一个上升沿来结束
遥控器对应键码
外部中断
外部中断介绍
外部中断寄存器
外部中断原理图
二、实例
1.显示遥控键码
设计思路:定义三个状态(0、1、2),状态0为最初状态,状态1为start开始状态,状态2开始接收数据,如果是状态0则开始计时并使状态变为1,为状态1时获取计时器时间,通过时间判断得到的命令,若为start信号,则使状态变为2,获取定时器时间并让定时器重新从零开始,通过定时器的时间判断获得的数据,最后获取数据成功后进行验证。
IR.c代码实现如下
#include <REGX52.H>
#include "Time0Init.h"
#include "Int0.h"
unsigned int IR_Time; // 获取计时器所计时时间
unsigned char IR_State; // 红外状态
unsigned char IR_Data[4]; // 发送的数据
unsigned char IR_PData; // 数据位指针
unsigned char IR_DataFlag; // 是否接收到数据
unsigned char IR_RepeatFlag; // 是否重复
unsigned char IR_Address; // 接收地址数据
unsigned char IR_Command; // 接收命令数据
// 初始化计时器与外部中断
void IR_Init(){
Time0_Init();
Int0_Init();
}
// 获取数据标志
unsigned char IR_GetDataFlag(){
if(IR_DataFlag){
IR_DataFlag = 0; // 归零用于下次判断
return 1;
}
return 0;
}
// 获取重复标志
unsigned char IR_GetRepeatFlag(){
if(IR_RepeatFlag){
IR_RepeatFlag = 0; // 归零用于下次判断
return 1;
}
return 0;
}
// 获取地址
unsigned char IR_GetAddress(){
return IR_Address;
}
// 获取命令(键码)
unsigned char IR_GetCommand(){
return IR_Command;
}
// 外部中断函数
void Int0_Routine() interrupt 0
{
if(IR_State == 0){
Timer0_SetCounter(0);
Timer0_Run(1);
IR_State = 1;
}else if(IR_State == 1){
IR_Time = Timer0_GetCounter();
Timer0_SetCounter(0);
//如果计时为12.442ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
if(IR_Time > 12442 - 500 && IR_Time < 12442 + 500){
IR_State = 2;
}
//如果计时为10.368ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
else if(IR_Time > 10368 - 500 && IR_Time < 10368 + 500){
IR_RepeatFlag = 1; // 正在长按
Timer0_Run(0); // 计时器停止
IR_State = 0;
}
else{
IR_State = 1;
}
}else if(IR_State == 2){
// 获取刚刚执行的计时
IR_Time = Timer0_GetCounter();
Timer0_SetCounter(0);
//如果计时为1032us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
if(IR_Time > 1032 - 500 && IR_Time < 1032 + 500){
IR_Data[IR_PData/8] &= ~(0X01 << (IR_PData%8)); // 数据对应位清0
IR_PData++; //数据位置指针自增
}
//如果计时为2074us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
else if(IR_Time > 2074 - 500 && IR_Time < 2074 + 500){
IR_Data[IR_PData/8] |= (0X01 << (IR_PData%8)); // 数据位赋1
IR_PData++; //数据位置指针自增
}
else{
IR_PData = 0;
IR_State = 1;
}
// 获取全部数据后进行验证
if(IR_PData>=32){
// P2 = 0; // 用于测试中断内容是否可行
IR_PData = 0;
// 验证正确
if((IR_Data[0] == ~IR_Data[1]) && (IR_Data[2] == ~IR_Data[3])){
IR_Address = IR_Data[0];
IR_Command = IR_Data[2];
IR_DataFlag = 1;
}
Timer0_Run(0);
IR_State = 0;
}
}
}
main.c主函数内容如下
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "IR.h"
#include "Time0Init.h"
unsigned char Num;
unsigned char Address,Command;
void main(){
LCD_Init();
IR_Init();
LCD_ShowString(1,1,"ADDR CMD Num");
LCD_ShowString(2,1,"00 00 00");
while(1){
// 获取数据或长按状态时获取数据
if(IR_GetDataFlag() || IR_GetRepeatFlag()){
Address = IR_GetAddress();
Command = IR_GetCommand();
LCD_ShowHexNum(2,1,Address,2);
LCD_ShowHexNum(2,7,Command,2);
// 用音量键控制Num值加减
if(Command == 0x15){
Num--;
}
if(Command == 0x09){
Num++;
}
LCD_ShowNum(2,12,Num,2);
}
}
}
Int0.c外部中断配置如下:
#include <REGX52.H>
void Int0_Init(){
IT0 = 1; // 下降沿触发
IE0 = 0; // 中断标志位
EX0 = 1; // 打开中断
EA = 1; // 打开所有中断
PX0 = 1; // 高优先级
}
2.使用红外遥控改写电机调速
模块化电机调试主体内容
#include <REGX52.H>
#include "Timer1.h"
sbit Motor = P1^0;
unsigned char Counter,Compare; // 周期、比较值
// 初始化定时器
void Motor_Init(){
Timer1_Init();
}
// 修改比较值调节速度
void Motor_ChangeSpeed(unsigned char Speed){
Compare = Speed;
}
// 中断函数
void Timer1_Routine() interrupt 3
{
// 100um
TL1 = 0xA4; //设置定时初始值
TH1 = 0xFF; //设置定时初始值
Counter++;
// 设置周期
if(Counter>=100){
Counter = 0;
}
// 与比较值进行比较
if(Counter<Compare){
Motor = 1; // 给电
}else{
Motor = 0; // 不给电
}
}
为与红外模块所使用的定时器区分,为电机模块重新配置一个定时器Timer1
#include <REGX52.H>
// 定时器1初始化
void Timer1_Init(void) //100微秒@11.0592MHz
{
// AUXR &= 0xBF; //定时器时钟12T模式
TMOD &= 0x0F; //设置定时器模式
TL1 = 0xA4; //设置定时初始值
TH1 = 0xFF; //设置定时初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //打开中断
EA = 1; //打开中断总开关
PT1 = 0; //低优先级
}
主函数内容
#include <REGX52.H>
#include "Delay.h"
#include "Motor.h"
#include "IR.h"
unsigned char Command;
void main(){
IR_Init();
Motor_Init();
while(1){
if(IR_GetDataFlag()){
Command = IR_GetCommand();
// 改变挡位(比较值)
// 按键0或电源按键地址
if(Command == 0x16 || Command == 0x45){
Motor_ChangeSpeed(0);
}
// 按键1地址
if(Command == 0x0c){
Motor_ChangeSpeed(40); // 太小会导致电压太小不足以驱动电机
}
// 按键2地址
if(Command == 0x18){
Motor_ChangeSpeed(60);
}
// 按键3地址
if(Command == 0x5e){
Motor_ChangeSpeed(100);
}
}
}
}
总结
- 最主要的还是时序图部分,知道NEC编码对高低电平的标准使什么。
- 这个红外遥控发送的红外线的频率就是38KHz,所以在对红外接收判断时,只需要对时序部分分析。
由于红外的接收很快,单片机执行速度不快,需要借助外部中断。
对于IR.c中下段函数解释
if(IR_Time > 1032 - 500 && IR_Time < 1032 + 500){
IR_Data[IR_PData/8] &= ~(0X01 << (IR_PData%8)); // 数据对应位清0
IR_PData++; //数据位置指针自增
}
else if(IR_Time > 2074 - 500 && IR_Time < 2074 + 500){
IR_Data[IR_PData/8] |= (0X01 << (IR_PData%8)); // 赋1
IR_PData++; //数据位置指针自增
}
第一个if是接收到的是0时,IR_Data[IR_PData/8] &= ~(0X01 << (IR_PData%8)); ,与运算,这里有取反,所以就是对应位置赋值为0。IR_PData相当于数据位的指针,从0依次增加。这里把32位拆分为8位+8位+8位+8位计算,整除运算IR_PData/8,即每个下标放8位(0 ~ 7),取余运算IR_PData%8,即每8位一轮,一直是0、1、2、3、4、5、6、7,所以这种判断方法可以正确的赋值这个32位数组