金牌会员
- 积分
- 1383
- 金钱
- 1383
- 注册时间
- 2015-2-3
- 在线时间
- 197 小时
|
发表于 2017-7-24 10:06:15
|
显示全部楼层
本帖最后由 yyx112358 于 2017-7-24 10:07 编辑
我发一下我写的STC15的NEC码解码(不是RC5等编码),用的是外部中断+定时器(定时器只是负责自加全局计时变量,所以可以加入其它代码),不同的是思路是使用状态机,所以实时性非常强,我觉得你可以借鉴一下这个思路。我状态机的转换关系如下:
1. ERROR状态下,检测是否出现起始码,是则进入RCVDAT,否则留在ERROR
2. RCVDAT状态下且接收到32位以下,连续接收,每次如果接收到0、1则储存入中间变量tmp,接收到的位数cnt++。否则回到ERROR。
3. RCVDAT状态下且接收完32位后,进行校验(是否源码等于反码)。成功,则储存为结果addr、cmd,进入REPEAT状态,清零tmp,cnt,repeat。否则,回到ERROR
4. REPEAT状态下,如果接收到重复码,则repeat++。否则回到ERROR。
不用外部中断和定时器就只能不停地while循环扫描,实时性很低堵塞程序还容易被打断,但是也可以实现。
[mw_shl_code=c,true]#ifndef _INFRA_H_
#define _INFRA_H_
#include "main.h"
/* 红外遥控
1.
INFRA->INT0->P3.2
2.使用定时器统计外部中断间隔来区分不同编码
硬件上使用上拉电阻进行反相
使用状态机实现非阻塞读取
*/
sbit INFRA=P3^2;//INT0
typedef struct s_DEV_INFRA
{
u8 addr;//地址
u8 cmd;//命令
u8 repeat;//重复次数(最大255次即连按25s以上)
void(*Init)(void);//初始化函数
}DEV_INFRA;//外部程序接口
typedef enum
{
INFRARED_ERROR =0,
INFRARED_11 =162,
INFRARED_12 =98,
INFRARED_13 =226,
INFRARED_21 =34,
INFRARED_22 =2,
INFRARED_23 =194,
INFRARED_31 =224,
INFRARED_32 =168,
INFRARED_33 =144,
INFRARED_41 =104,
INFRARED_42 =152,
INFRARED_43 =176,
INFRARED_51 =48,
INFRARED_52 =24,
INFRARED_53 =122,
INFRARED_61 =16,
INFRARED_62 =56,
INFRARED_63 =90,
INFRARED_71 =66,
INFRARED_72 =74,
INFRARED_73 =82 }INFRA_KEY;//遥控器编码
typedef enum
{
NEC_RCVDAT,//数据读取状态
NEC_REPEAT,//重复码状态
NEC_ERROR//空闲状态,未接收和接收错误状态
}NEC_STAT;//状态机状态
extern DEV_INFRA DEV_Infra0;
void INFRA_Init(void);
#endif
[/mw_shl_code]
[mw_shl_code=c,true]#include "Infra.h"
#include "Uart.h"
#define NEC_TIM_OVERFLOW_LENGTH_US (200)//定时器计数周期,理论上越小越好
static void NEC_Init(void);
static u32 NEC_GetDelay(void);
DEV_INFRA DEV_Infra0={0,0,0,NEC_Init};
//INFRA->INT0->P3.2
//TIM3,200us@30MHz
//使用TIM3测量INFRA引脚上低电平之间的周期
static void NEC_Init(void)
{
CLRBIT(P3M0,2);
CLRBIT(P3M1,2);
INFRA=1;
IT0=1;//上升、下降沿
EX0=1;
T4T3M |= 0x02; //定时器时钟1T模式
T3L = 0x100-(SYSCLK/1000000*NEC_TIM_OVERFLOW_LENGTH_US)%0x100;//0x90; //设置定时初值
T3H = 0x100-(SYSCLK/1000000*NEC_TIM_OVERFLOW_LENGTH_US)/0x100;//0xE8; //设置定时初值
SETBIT(IE2,5); //开中断
T4T3M |= 0x08; //定时器3开始计时
}
static u16 NEC_TimCnt=0;//定时器溢出计数(周期NEC_TIM_OVERFLOW_LENGTH_US微秒)
void TIM3_ISR(void) interrupt 19
{
NEC_TimCnt++;
}
//获取与上一次调用NEC_GetDelay()后的间隔
static u32 NEC_GetDelay(void)
{
static u16 lastcnt=0;
u16 delay=NEC_TimCnt-lastcnt;
lastcnt=NEC_TimCnt;
return delay*NEC_TIM_OVERFLOW_LENGTH_US;
}
//硬件电路上对信号做了反相,故可用下降沿计数
//起始码->8位地址码->8位地址反码->8位命令码->8位命令反码->重复码->重复码->...
void INT0_ISR(void) interrupt 0
{
u32 t=0;
static NEC_STAT flag=NEC_ERROR;//状态机标志
//统计高低电平
t=NEC_GetDelay();
#ifdef _DEBUG_
// sprintf("%ld\t",t);
// UART_SendString(str);
#endif
switch(flag)
{
case NEC_ERROR://扫描是否出现起始码
{
if( t>=12000 && t<=15000 )//起始码
{
flag=NEC_RCVDAT;//进入数据接收状态
}
break;
}
case NEC_RCVDAT://数据接收状态
{
static u8 cnt=0;//接收位数计数
static u32 tmp=0;//接收
if(cnt<32)
{
tmp<<=1;
cnt++;
if( t>=1600 && t<= 2600)//编码1
{
tmp++;
}
else if( t>=600 &&t<=1400)//编码0
{
// tmp&=~(0x01);
}
else//都不是,发送错误
{
flag=NEC_ERROR;
cnt=0;
tmp=0;
break;
}
}
if(cnt==32)//接收完32bit
{
if( ((u8)(tmp>>24)==(u8)(~(tmp>>16))) &&
((u8)((tmp>>8))==(u8)~tmp) )//地址、命令校验
{
DEV_Infra0.addr=tmp>>24;
DEV_Infra0.cmd=~tmp&0xFF;
DEV_Infra0.repeat=0;
flag=NEC_REPEAT;
}
else//校验失败
{
flag=NEC_ERROR;
}
cnt=0;
tmp=0;
}
break;
}
case NEC_REPEAT://重复码
{
if( t>=30000 && t<=42000)//【这个地方与其它资料不同,并非长98ms】
{
flag=NEC_REPEAT;
}
else if((t>=10000)&&(t<=12000))
{
if(DEV_Infra0.repeat<0xFF)
{
DEV_Infra0.repeat++;
}
flag=NEC_REPEAT;
}
else if( t>12000 && t<=15000 )//起始码
{
flag=NEC_RCVDAT;//进入数据接收状态
}
else
{
flag=NEC_ERROR;
}
break;
}
default:
flag=NEC_ERROR;
break;
}
}
[/mw_shl_code]
|
|