金牌会员
 
- 积分
- 1383
- 金钱
- 1383
- 注册时间
- 2015-2-3
- 在线时间
- 197 小时
|
发表于 2016-11-28 12:39:27
|
显示全部楼层
本帖最后由 yyx112358 于 2016-11-28 12:45 编辑
不知道你这句“可以进入中断,但是计算出的高低电平不一样,没办法正确解码”是什么意思。看了你发的EV1527时序,不就是用的高、低电平不同的持续时间来确定0、1的吗?
我猜你是有这两种可能,一种是每次采集的高、低电平时间不同没法解码。这种情况下直接用输入捕获捕获低电平时间,不要捕获高、低电平时间避免切换过程造成错过电平变化。同时把定时器中断优先级设定得高一点不要被打断。
还有就是你以前的代码过于简单,只是死循环等待下一个信号直到发完再解码,没有考虑到这个过程中执行其它任务的需求。这种情况下用上ucos也无济于事,因为每个编码只间隔几百us。这时除了进行我上文中的改进之外,请使用状态机进行处理。你可以参考我之前写的NEC解码程序(用的51+外部中断+定时器)[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]//使用了INT0的下降沿触发,TIM3为200us溢出一次。
#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开始计时
}
/*TIM3每NEC_TIM_OVERFLOW_LENGTH_US(200)微秒溢出一次,将NEC_TimCnt++。通过调用NEC_GetDelay()即可获取上一次调用NEC_GetDelay()后的间隔。
INT0_ISR()当中是状态机。static NEC_STAT flag是状态机标志。状态转换方式见4.4.2 。
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)//在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]
文字描述如下:
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。
如果还不是,请发你的代码并详述问题。
|
|