题外话:因半导体价格涨幅较大和国外形势,开始考虑尽量使用国产芯片(支持国货),先从单片机开始,目前暂定要求不高的场合选择应用较多的STC系列,其中STC8G和STC8H系列为STC主推的型号。
注:程序大部分来源于网络,并做了较大幅度的修改,原因是不是用于红外数据传输,而是作为光电开关来用。
参考NEC编码做了简化,便于延长发射管寿命。
发射管为IR204、接收头为IRM-H638T/TR2、单片机为STC8G1K08A。
#include "STC8.h"
#include "intrins.h" //使用nop()函数需引用此文件
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;
//280us @11.0592MHz
#define T1_START() TL1 = 0xE7; \
TH1 = 0xF3; \
ET1 = 1; \
TR1 = 1
//引脚定义
sbit LED=P5^4; //指示灯
sbit IR_IO=P3^3; //红外发射
//sbit KEY=P5^5; //按键
//数据定义
bit IR_Flag=0; //红外数据接收完成标志位
u8 IR_time=0; //下降沿之间的时间计数值
u8 IR_data[9]; //引导码+8个位数据各自占用的时间
bit IR_TimeFlag; //红外发射延时到标志
unsigned char count; //280us
#define IR_SENDDATA 0X05 //红外发射的数据
static u8 IR_bit; //变量IR_bit用于指示红外数据的位数
//函数声明
void delay(u16 count); //延时函数声明
void Time0_init(void); //定时计数器0初始化函数
void Time1_init(void); //定时计数器1初始化函数
void Int0_init(void); //外部中断0初始化函数
u8 IR_decode(void); //红外解码函数
//-------------------------红外发送高电平----------------------------
void Wait_High(a)//a*280uS
{
unsigned char k;
IR_TimeFlag = 0;
count = a;
T1_START();
while(!IR_TimeFlag) //38khz载波
{
_nop_();
_nop_();
k = 23;
while (--k);
IR_IO = ~IR_IO;
}
IR_IO = 1;
}
//-------------------------红外发送低电平----------------------------
void Wait_Low(a)//a*280uS
{
IR_IO = 1;
IR_TimeFlag = 0;
count = a;
T1_START();
while(!IR_TimeFlag);
}
//-------------------------红外发送单字节数据----------------------------
void Send_IR(u8 dat)
{
unsigned char i,j;
Wait_High(2);//0.56ms
Wait_Low(4);//1.12ms
for(j=0;j<8;j++)
{
Wait_High(1);//0.28ms
if(dat&0x01)
{
Wait_Low(2);// 0.56ms
}
else
{
Wait_Low(1);//0.28ms
}
dat = dat>>1;
}
Wait_High(1);//0.28ms
}
//-------------------------主函数----------------------------
void main(void)
{
u8 num=0;
//STC8G系列单片机除P3.0和P3.1外,所有I/O上电均为高阻输入状态,需先配置再使用
P3M0 |=0x08; //P3.3引脚为推挽输出模式
P3M1 &=0xF7; //P3.3引脚为推挽输出模式
RSTCFG &=0xEF; //复位引脚P5.4当做普通I/O引脚
P5M0 |=0x10; //P5.4引脚为推挽输出模式
P5M1 &=0xEF; //P5.4引脚为推挽输出模式
P5M0 &=0xDF; //P5.5引脚为准双向口模式
P5M1 &=0xDF; //P5.5引脚为准双向口模式
Time0_init(); //定时计数器0初始化函数
Time1_init(); //定时计数器1初始化函数
Int0_init(); //外部中断0初始化函数
EA = 1; //开中断
delay(100); //等待配置稳定
IR_IO = 1; //红外发射置高
while(1)
{
Send_IR(IR_SENDDATA); //红外发送数据
delay(20000);
if(IR_Flag) //红外数据接收完成
{
IR_Flag=0;
if(IR_decode() == IR_SENDDATA) LED = ~LED;//红外接收正确改变指示灯状态
}
}
}
//-------------------------延时函数----------------------------
void delay(u16 count)
{
while(count--)
{
_nop_();
}
}
//-------------------------定时器0初始化----------------------------
void Time0_init(void) //139微秒@11.0592MHz
{
AUXR &= 0x7F; //定时器时钟12T模式,工作方式2
TMOD &= 0xF0;
TMOD |= 0x02;
TL0 = 0x80;
TH0 = 0x80;
TF0 = 0; //清除标志
ET0 = 1; //使能T0中断
TR0 = 1; //启动T0
}
//-------------------------定时器1初始化---------------------------
void Time1_init(void) //278微秒@11.0592MHz
{
AUXR |= 0x40;//模式2 1T
TMOD &= 0x0F;
}
//-------------------------外部中断0初始化-------------------------
void Int0_init(void)
{
IT0 = 1; //触发方式为下降沿触发
EX0 = 1; //使能INT0中断
}
//-------------------------红外接收解码-------------------------
u8 IR_decode(void)
{
u8 j,k;
u8 IR_Value = 0;
k=1; //先让变量k等于1,因为k为0时取出的将会是“引导码的时间间隔”
for(j=0;j<=7;j++) //内层循环8次为了拼合8个数据位为1个字节
{
if(IR_data[k]>5) //若“时间间隔”比5大那肯定是“1码”反之为“0码”
IR_Value|=0x80; //通过按位或运算高位填1
if(j<7) //若数据没有拼合完8次
IR_Value>>=1; //通过右移运算“腾出”位置准备下一位判定
k++; //下标变量自增
}
return IR_Value; //返回红外接收的数据
}
//-------------------------外部中断0服务函数-------------------------
void INT0_ISR() interrupt 0
{
if(IR_time>8) //判断引导码(0.56ms+1.12ms)
IR_bit=0; //清除位数变量,确保当前IR_bit为0,表示引导码
IR_data[IR_bit]=IR_time; //存储相应位时间宽度
IR_time=0; //清零时间宽度计数值
IR_bit++; //位数变量自增
if(IR_bit==9) //如果达到了9位(引导码+8个数据位)
{
IR_Flag=1; //红外数据接收完成标志位置1
IR_bit=0; //位数变量清零
}
}
//-------------------------定时器0中断服务函数-------------------------
void TIMER0_ISR() interrupt 1
{
IR_time++;
}
//-------------------------定时器1中断服务函数-------------------------
void TIMER1_ISR() interrupt 3
{
count--;
if(count == 0)
{
IR_TimeFlag = 1;
ET1 = 1;//
}
}