转载: https://blog.csdn.net/cssBIS/article/details/105745025
最近学习了网上的红外解码程序保存学习一下
使用的是N76E003芯片
借鉴了好多地方
只是简单的启动LED灯
使用的是日本 NEC协议
外部定时器中断1
定时器模式2,8位自动重装 256步溢出一次
16M晶振
判断语句那里自己集合了好多想了一个案例
定义一个16位数字
将代码的用户码反码和键码正码一起判断
嘿嘿嘿
uint i;
i = IRcord[2] << 8;
i |= IRcord[1];//将第一个用户反码和按键码结合判断
这里就是我写的全部的程序
只是一个简单的驱动程序
#include "N76E003.h"
#include "SFR_Macro.h"
#include "Function_define.h"
#include "Common.h"
#include "Delay.h"
#define uchar unsigned char
#define uint unsigned int
sbit IR = P1^7; //定义红外脉冲数据接口 外部中断1输入口
sbit SCLK = P3^0; //初始时钟
sbit RST = P1^6; //CE端
sbit DATA = P0^7; //数据读写端
uchar IRtime; //检测红外高电平持续时间(脉宽)
uchar IRcord[4]; //此数组用于储存分离出来的4个字节的数据(用户码2个字节+键值码2个字节)
uchar IRdata[33]; //此数组用于储存红外的33位数据(第一位为引导码用户码16+键值码16)
bit IRpro_ok, IRok; //第一个用于红外接收4个字节完毕。IRok用为检测脉宽完毕
void Init_T1()
{
TIMER1_MODE2_ENABLE;//定时器0和定时器1工作方式2,8位自动重装
TH1 = 0X00;//高8位装入0那么定时器溢出一次的时间是256个机器周期
TL0 = 0X00;
EA = 1; //总中断
ET1 = 1; //定时器1中断
TR1 = 1; //启动定时器1
IT1 = 1; //设置外部中断1为跳沿触发方式,来一个下降沿触发一次
EX1 = 1; //启动外部中断1
}
void Time1() interrupt 3 //定义定时器1
{
IRtime++; //检测脉宽,1次为192uS
}
void EXT1_T1() interrupt 2 //定义外部中断1
{
static uchar i;
static bit startflag;
if(startflag)
{
if((IRtime < 78) &&(IRtime >= 63))
/*判断是否是引导码,低电平电平9000us+高4500us
这个自己可以算我以11.0592来算了NEC协议的引导码低8000-10000+高4000-5000
如果已经接收了引导码那么i不会被置0就会开始依次存入脉宽*/
i = 0;//如果是引导码那么执行i=0把他存到IRdata的第一个位
IRdata[i] = IRtime; //以T0的溢出次数来计算脉宽,把这个时间存到数组里面到后面判断
IRtime = 0; //计数清零,下一个下降沿的时候在存入脉宽
i++; //计数脉宽存入的次数
if(i == 33) //如果存入34次 数组的下标是从0开始i等于33表示执行了34次
{
IRok = 1;
i = 0; //把脉宽计数清零准备下次存入
}
}
else
{
IRtime = 0; //引导码开始进入把脉宽计数清零开始计数
startflag = 1; //开始处理标志位置1
}
}
void IRcordpro()
{
uchar i,j, k,cord,value;/*i用于处理4个字节,j用于处理一个字节中每一位,k用于33次脉宽中的哪一位
cord用于取出脉宽的时间判断是否符合1的脉宽时间*/
k = 1; //从第一位脉宽开始取,丢弃引导码脉宽
for(i = 0; i < 4;i++)
{
for(j = 0; j < 8; j++)
{
cord = IRdata[k]; //把脉宽存入cord
if(cord > 7) //如果脉宽大于我11.0592的t0溢出率为约278us*5=1390那么判断为1//1.3ms
value = value |0x80; /*接收的时候是先接收最低位,
把最低位先放到value的最高位在和0x08按位或一下
这样不会改变valua的其他位的数值只会让他最高位为1*/
if(j < 7)//限制条件
value = value >> 1; //value位左移依次接收8位数据。
k++;//每执行一次脉宽位加1
}
IRcord[i] = value;//每处理完一个字节把它放入IRcord数组中。
value = 0;//清零value方便下次在存入数据
}
IRpro_ok = 1;//接收完4个字节后IRpro ok置1表示红外解码完成
}
void Init_GPIO()
{
Set_All_GPIO_Quasi_Mode;//所有口设置为双向口
P01_PushPull_Mode;//左路输出
P02_PushPull_Mode;//左路输出
P03_PushPull_Mode;//左路输出
P04_PushPull_Mode;//左路输出
P10_PushPull_Mode;//左路输出
P00_PushPull_Mode;//左路输出
P11_PushPull_Mode;//左路输出
P17_Input_Mode;//P17输入上拉模式
clr_P00;
clr_P01;
clr_P02;
clr_P03;
clr_P04;
clr_P10;
clr_P11;
clr_P12;
clr_P13;
}
void main ()
{
Init_T1();
Init_GPIO();
while(1)
{
if(IRok) //判断脉宽是否检测完毕
{
IRcordpro(); //根据脉宽解码出4个字节的数据
IRok = 0; //重新等待脉宽检测
if(IRpro_ok) //解码完成
{
uint i;
i = IRcord[2] << 8;
i |= IRcord[1];//将第一个用户反码和按键码结合判断
switch(i)
{
case 0x45ff: P04 = ~P04;break;
case 0x44ff: P03 = ~P03;break;
case 0x40ff: P10 = ~P10;break;
case 0x43ff: P11 = ~P11;break;
case 0x47ff: P02 = ~P02;break;
case 0x15ff: P00 = ~P00;break;
default:break;
}
}
}
}
}
离线
上面是之前多次写的这里添加按键长短按程序,抄写了其他人的程序
原文网址: http://www.51hei.com/bbs/dpj-58436-1.html
#include "N76E003.h"
#include "Common.h"
#include "Delay.h"
#include "SFR_Macro.h"
#include "Function_define.h"
//【用户必填项:USER_H、USER_L、Check_EN、CPU_Fosc、IR】
#define USER_H 0x80 //用户码高8位
#define USER_L 0x7F //用户码低8位
#define Check_EN 0 //是否要校验16位用户码:不校验填0,校验则填1
#define CPU_Fosc 16000000 //输入主频,自适应解码(单位:Hz,范围:6MHz ~ 40MHz)
#define CA_S 8 //长按时间设置,单位:108mS(即 108mS整数倍,10倍以上为宜)
sbit IR = P1^7; //定义红外脉冲数据接口 外部中断1输入口
//无符号类型
#define uint8 unsigned char
#define uint16 unsigned int
/*┈┈┈┈┈┈┈┈┈┈ 基准 ┈┈┈┈┈┈┈┈┈┈┈*/
#define Boot_Limit (((9000+4500) +2000)/Step) //引导码周期上限
#define Boot_Lower (((9000+4500) -2000)/Step) //引导码周期下限
#define Bit1_Limit ((2250 +800)/Step) //“1”周期上限
#define Bit0_Limit ((1125 +400)/Step) //“0”周期上限
#define Step 400 //红外采样步长:400us
#define TH_H ((65536 - Step*(CPU_Fosc/300)/40000)/256) //定时器高8位基准赋值
#define TH_L ((65536 - Step*(CPU_Fosc/300)/40000)%256) //定时器低8位基准赋值
uint8 IR_BT; //解码效果返回:0无效,1有效,2短按,3长按
uint8 NEC[4] = 0; //解码存放:16位用户码、操作码正反码
uint8 cntCA; //长按计数
uint16 cntStep; //步数计
bit IRa,IRb; //电位状态保存
bit IRsync; //同步标志
uint8 BitN; //位码装载数
void KZ0(); //短按处理
void KZ1(); //长按处理
void IR_Init();
void IR_NEC();
/***************** 主函数 ********************/
void main(void)
{
Set_All_GPIO_Quasi_Mode;//所有口设置为双向口
P17_Input_Mode;
P15 = 0;
P14 = 0;
IR_Init(); //红外线解码初始化
NEC[4] = 0;
while(1)
{
//遥控检测
if((IR_BT==2)||(IR_BT==3))
{
if(IR_BT==2)
{
switch(NEC[2])
{
case 0x45: P15 = ~P15; break;
default:break;
}
}
else
{
switch(NEC[2])
{
case 0x45: P14 = ~P14;break;
default:break;
}
}
IR_BT =0; //清有效标志
}
}
void IR_Init()
{
TMOD &= 0xF0; //清定时器0
TMOD |= 0x01; //定时器0:16位定时器
TH0 = TH_H; //每一步的时间
TL0 = TH_L;
EA = 1; //总中断
//ET1 = 1; //定时器1中断
ET0 = 1;
//TR1 = 1; //启动定时器1
TR0 = 1;
/*IT1 = 1; //设置外部中断1为跳沿触发方式,来一个下降沿触发一次
EX1 = 1; //启动外部中断1*/
}
/*┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
函数:红外线NEC周期采样解码法(定时中断,下降沿查询周期时间)
全局变量:IR_BT = 0无效
1有效,待继续判断长、短按(如不需要判断长、短按,则直接使用)
2短按
3长按
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈*/
void IR_NEC()
{
TL0 = TH_L; //重赋值
TH0 = TH_H;
cntStep++; //步数采样
if(IR_BT == 1)if(cntStep>300)IR_BT = 2;//解码有效后,如果无长按,120ms(400us×300)后默认短按
IRb = IRa; //上次电位状态
IRa = IR; //当前电位状态
if(IRb && !IRa) //是否下降沿(上次高,当前低)判断动作
{
if(cntStep > Boot_Limit) //超过同步时间
{
if(IR_BT==1)if(++cntCA>CA_S)IR_BT=3; //解码有效后,继续按住遥控>CA_S即长按
IRsync=0; //同步位清0
}
else if(cntStep > Boot_Lower){IRsync = 1;BitN = 32; }//同步位置1,装载位码数
else if(IRsync)
{
if(cntStep > Bit1_Limit)IRsync = 0;
else
{
NEC[3] >>= 1;
if(cntStep > Bit0_Limit)NEC[3] |= 0x80; //“0”与“1”
if(--BitN == 0)
{
IRsync = 0;
#if (Check_EN == 1)
if((NEC[0]==USER_H)&&(NEC[1]==USER_L)&&(NEC[2]==~NEC[3])) //校验16位用户码、操作码正反码
{ IR_BT=1; cntCA=0; } //解码有效,接下来判断:短按?长按?
#else
if(NEC[2]==~NEC[3]){ IR_BT=1; cntCA=0; } //校验操作码正反码
#endif
}
else if((BitN & 0x07)== 0) //NEC[3]每装满8位,移动保存一次(即 BitN%8 == 0)
{ NEC[0]=NEC[1]; NEC[1]=NEC[2]; NEC[2]=NEC[3]; }
}
}
cntStep = 0; //步数计清0
}
}
/********************** 定时器1断函数************************/
void time0(void) interrupt 1
{
IR_NEC();
}
离线
转载: https://blog.csdn.net/u014798590/article/details/95913702
电路图
如下图所示
其中EC11_A接P17、EC11_B接P30;电容103为0.01uf
信号分析
在这里插入图片描述
由上图中可以看出,当我们使用A相上升沿触发中断时,B相如果为高电平,则是顺时针旋转,反之则是逆时针旋转。
#include "N76E003.h"
#include "SFR_Macro.h"
#include "Function_define.h"
#include "Common.h"
#include "Delay.h"
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit LED =P0^5;
int temp;
void main (void)
{
Set_All_GPIO_Quasi_Mode;//所有引脚配置为双向模式
P17_Input_Mode;//编码器A设定为输入
P30_Input_Mode;//编码器B设定为输入
LED=0;//初始化LED端口
set_EA; //开启总中断
set_EPI; //开启管脚中断(位于拓展中断中)
clr_PIPS1;
set_PIPS0;//选择P1.7为中断输入源
//Enable_BIT7_HighLevel_Trig; //高电平
//Enable_BIT7_FallEdge_Trig; //下降沿
Enable_BIT7_RasingEdge_Trig;//上升沿
//Enable_BIT7_LowLevel_Trig; //低电平
while(1)
{
}
}
//-------管脚中断中断服务程序---------
void PinInterrupt (void) interrupt 7
{
if(PIF==0x80&&P17)//P11位于一通道
{
Timer0_Delay1ms(1);//消抖
if(P17)
{
if(P30)temp++;//顺时针
else{temp--;}//逆时针
}
}
PIF=0;//清除外部中断标志
LED=~LED;
}
离线
转载 https://blog.csdn.net/qq_29246181/article/details/105438180
sbit EC_B = P0 ^ 0; // 编码器 P0.0 端口
sbit EC_A = P1 ^ 3; // 编码器 P1.3 端口
static uint8_t EC_A_Val = 0, EC_B_Val = 0;
static uint8_t EC_A_old = 0, EC_B_old = 0;
/*********************************************
函数名:EncoderReading
功 能:读取编码器数据
形 参:
返回值:0--无动作 1--正转 2--反转
备 注:
作 者:薛建强
时 间:2019/06/11
**********************************************/
uint8_t EncoderReading(void)
{
/*捕获一次*/
EC_A_Val = EC_A;
EC_B_Val = EC_B;
/*************EC_B上升沿*******************/
if (EC_A_Val == EC_A_old && EC_B_Val > EC_B_old)
{
/*重新给旧值赋值*/
EC_B_old = EC_B_Val;
EC_A_old = EC_A_Val;
if (EC_A_Val == 0) //反转
{
return 2;
}
}
/*************EC_B下升沿*******************/
if (EC_A_Val == EC_A_old && EC_B_Val < EC_B_old)
{
/*重新给旧值赋值*/
EC_B_old = EC_B_Val;
EC_A_old = EC_A_Val;
if (EC_A_Val == 1) //反转
{
return 2;
}
}
/*************EC_A上升沿*******************/
if (EC_B_Val == EC_B_old && EC_A_Val > EC_A_old)
{
/*重新给旧值赋值*/
EC_B_old = EC_B_Val;
EC_A_old = EC_A_Val;
if (EC_B_Val == 0) //正转
{
return 1;
}
}
/*************EC_A下升沿*******************/
if (EC_B_Val == EC_B_old && EC_A_Val < EC_A_old)
{
/*重新给旧值赋值*/
EC_B_old = EC_B_Val;
EC_A_old = EC_A_Val;
if (EC_B_Val == 1) //正转
{
return 1;
}
}
return 0;
}
使用方式:
KEY_state = EncoderReading(); //编码器调光度
switch (KEY_state)
{
case 1://正转
break;
case 2://反转
break;
}
IO电平轮询法驱动编码器。
离线
转载: https://bbs.21ic.com/icview-2556720-1-1.html
用timer 2 capture作抓pwm
初始化:
P05_Input_Mode;
TIMER2_CAP0_Capture_Mode;
IC6_P05_CAP0_BothEdge_Capture;
set_ECAP;//Enable Capture interrupt
set_TR2;//Triger Timer2
set_EA;//enable interrupts
中断:
void Capture_ISR (void) interrupt 12
{
clr_CAPF0; //clear capture0 interrupt flag
if(P05==0)
{
input_capture_state=1;
low_input = (C0H<<8)&0xff00;
low_input |= C0L; //For capture mode CxL/CxH with data capture from I/O pin
}else
{
input_capture_state=2;
high_input = (C0H<<8)&0xff00;
high_input |= C0L;
}
clr_TF2;
}
离线
https://bbs.21ic.com/icview-2556850-1-1.html
大家看看我理解双边沿捕获是否正确
捕获有双边沿和单边沿触发。
单边我们都知道可以测出一个信号的周期。这个周期包含高电平部分和低电平部分。
而双边沿触发,测到的可能是低电平的部分或者是高电平的部分。
以上理解都没错,OK?
单独讨论双边沿:
当一个信号触发后,比如从一个边沿进入了另外一个边沿的时候,打个比方,从高电平开始,进入了低电平。这个时候会捕获一个值,这个值是不是就是高电平的长度?而这个时候进入了低电平是不是可以判断一下,如果是低电平那么刚才测的数据属于高电平部分?
最后作者自己找到了答案:
对不起,楼主你的分析是正确的,今晚上我加班把timer2的相关寄存器都实际运行检查了一遍,终于查到了原因,原来我也是这样理解的,但今天我用厂家的BSP1.03的例程库改写测试了几遍,三种模式的结果都一模样,才提出这个问题。结果今晚上检查的结果是厂家的例程库的头文件有问题,我觉得厂家的提供的例程库不够严谨。原头文件
#define IC2_P10_CAP0_BothEdge_Capture CAPCON0|=SET_BIT4;CAPCON1|=0x30;CAPCON1&=0xBF;CAPCON2|=SET_BIT4;CAPCON3&=0xF0;CAPCON3|=0x02;
改为
#define IC2_P10_CAP0_BothEdge_Capture CAPCON0|=SET_BIT4;CAPCON1|=0x32;CAPCON1&=0xBF;CAPCON2|=SET_BIT4;CAPCON3&=0xF0;CAPCON3|=0x02;就正常了,本来想减少时间,结果给厂家的例程库给陷害了
离线
离线
我写的按键库
key.c:
#include "library/sys.h"
#include "library/key.h"
struct KEY gKey;
void ReadKeyState(union KeyState *keyList, uint8_t v1)
{
static union KeyState oldKeyList[Key_Num];
static uint8_t i = 0;
static uint8_t j = 0;
static uint8_t auto_adj_cnt = 0;
static uint16_t down_cnt[Key_Num];
static uint16_t up_cnt[Key_Num];
static uint16_t max_jiange_cnt[Key_Num];
static uint16_t lianan_cnt[Key_Num];
static uint16_t chang_down_cnt[Key_Num];
static uint8_t key_Filt_all = 0;
static uint16_t old_v1 = 0;
for (i = 0; i < Key_Num; i++)
{
if (down_cnt[i])
down_cnt[i]--;
if (up_cnt[i])
up_cnt[i]--;
if (max_jiange_cnt[i])
max_jiange_cnt[i]--;
else
lianan_cnt[i] = 0, keyList[i].Bits.Double = 0;
}
#if Key_Filt_Cnt == 8
key_Filt_all = key_Filt_all << 1;
if (v1 == old_v1)
key_Filt_all |= 0x01;
#elif Key_Filt_Cnt == 4
key_Filt_all = key_Filt_all << 2;
if (v1 == old_v1)
key_Filt_all |= 0x03;
#elif Key_Filt_Cnt == 2
key_Filt_all = key_Filt_all << 4;
if (v1 == old_v1)
key_Filt_all |= 0x0F;
#elif Key_Filt_Cnt == 1
key_Filt_all = key_Filt_all << 8;
if (v1 == old_v1)
key_Filt_all |= 0xFF;
#endif
old_v1 = v1;
if (key_Filt_all != 0xFF)
{
return;
}
if ((v1 == 0x00) && (old_v1 == 0x00))
{
for (i = 0; i < Key_Num; i++)
{
keyList[i].Bits.State = 0;
keyList[i].Bits.Duan = 0;
keyList[i].Bits.Double = 0;
keyList[i].Bits.Long = 0;
}
}
if (v1 == 0x00)
{
if (auto_adj_cnt)
auto_adj_cnt--;
else
{
auto_adj_cnt = 100;
// TouchSensor_init_Fun();
}
}
else
{
auto_adj_cnt = 100;
}
for (i = 0; i < Key_Num; i++)
{
if (Read16Bit(v1, i))
{
up_cnt[i] = 100 / Key_Task_Time;
keyList[i].Bits.State = 1;
keyList[i].Bits.Rise = 0;
if (chang_down_cnt[i] < KeyState_LongTime / Key_Task_Time)
chang_down_cnt[i]++, keyList[i].Bits.Long = 0;
else
keyList[i].Bits.Duan = 0, keyList[i].Bits.Long = 1;
}
else
{
keyList[i].Bits.State = 0;
chang_down_cnt[i] = 0;
keyList[i].Bits.Fall = 0;
if (up_cnt[i] == 0) // 按键抬起超过100ms
{
keyList[i].Bits.Duan = 0; // 清掉短按标记位
keyList[i].Bits.Rise = 0;
}
}
if ((keyList[i].Bits.State == 0) && (oldKeyList[i].Bits.State == 1)) // 按键抬起
{
keyList[i].Bits.Rise = 1;
if (down_cnt[i])
{
keyList[i].Bits.Duan = 1;
}
keyList[i].Bits.Long = 0;
down_cnt[i] = KeyState_LongTime / Key_Task_Time;
chang_down_cnt[i] = 0;
for (j = 0; j < Key_Num; j++)
{
down_cnt[j] = KeyState_LongTime / Key_Task_Time;
chang_down_cnt[j] = 0;
keyList[j].Bits.Double = 0;
keyList[j].Bits.Long = 0;
}
}
if ((keyList[i].Bits.State == 1) && (oldKeyList[i].Bits.State == 0)) // 按键按下
{
keyList[i].Bits.Fall = 1;
max_jiange_cnt[i] = KeyState_DoubleTime / Key_Task_Time;
lianan_cnt[i]++;
if (lianan_cnt[i] > 1)
keyList[i].Bits.Double = 1;
chang_down_cnt[i] = 0;
for (j = 0; j < Key_Num; j++)
{
chang_down_cnt[j] = 0;
keyList[j].Bits.Duan = 0;
keyList[j].Bits.Long = 0;
}
}
oldKeyList[i] = keyList[i];
}
}
void Key_Deal(void)
{
ReadKeyState(&gKey.Value, gKey.key_value);
}
key.h:
#ifndef _H_Lib_TouchKey_
#define _H_Lib_TouchKey_
#include "mcu_library/mcu_library.h"
#define Key_Task_Time 10
enum Key_Name
{
TKey1, // 上左
TKey2, // 上右
Lock_Key, // 下左,童锁键
Out_Key, // 下中,出水键
Menu_Key, // 下右
Key_Num,
};
/**
* Key_Filt_Cnt 按键滤波次数
* 可选:
* 8:滤波8次,80ms
* 4:滤波4次,40ms
* 2:滤波2次,20ms
* 1:滤波1次,即不滤波,10ms
**/
#define Key_Filt_Cnt 2
#define KeyState_LongTime 2000 // 长按判定时间, 3秒
#define KeyState_DoubleTime 500 // 双击间隔时间,
union KeyState
{
struct
{
uint8_t State : 1; // 当前状态
uint8_t Duan : 1; // 单机
uint8_t Double : 1; // 双击
uint8_t Long : 1; // 长按
uint8_t Fall : 1; // 下降沿
uint8_t Rise : 1; // 上升沿
uint8_t NA1 : 1; // 占位
uint8_t NA2 : 1; // 占位
} Bits;
uint8_t All_Byte;
};
struct KEY
{
union KeyState Value[Key_Num];
uint8_t key_value;
};
extern struct KEY gKey;
extern void Key_Deal(void);
#endif
离线