// 滚动码实验 //--------------------------------------------- // 文件名: receive bar.c // 版本: V1.0 // 日期: 13/12/06 // 功能: 接收滚动码 // 编译环境: HiTech PIC C compiler v.8.05 // 使用MCU: PIC16F876 //--------------------------------------------------------------------- // 数据格式 //--------------------------------------------------------------------- // | 固定码部分8bit*4=32bit | 滚动码部分8bit*4=32bit // B[0] B[1] B[2] B[3] Re[0] Re[1] Re[2] Re[3] // Function SNL SNM SNH KeyHop CountL CountH ID // RVOOKKKK.IIIIIIII.IIIIIIII.IIIIIIII.KKKKDDDD.SSSSSSSS.SSSSSSSS.DDDDDDDD // |功 |序列号24位 |功 暂时 |同步记数值16位 |识别码8位 ,SNL // 能 能 只用 // 键 键 8位 // I=S/N -> 序列号 (24 BIT) // K=KEY -> 按键 ( 4 BIT) // S=Sync -> 同步计数器 (16 BIT) // D=Disc -> 识别码 ( 8 BIT) // 暂时使用8位 // R=Rept -> 重复/首次 ( 1 BIT) // 暂时不使用 // V=Vlow -> 低电 ( 1 BIT) // 暂时不使用 // O= -> 保留位 //--------------------------------------------------------------------- //--------------------------------------------- #include //-----------------常量定义-------------------- #define RFin RC5 //数据输入端口 #define Key RC4 //学习,按键输入 #define Data0 RA0 //数据定义 #define Data1 RA1 #define Data2 RA2 #define Data3 RA3 #define LearnT RA5 //学习指示灯 #define PERIOD 133 //timer0 133us中断一次 #define Synch_shot 6 //同步头高电平计数范围 #define Synch_head 12 #define Syncl_shot 25 //同步头低电平计数范围 #define Syncl_head 35 #define High 10 #define Low -10 #define LWaitTime 65000 //学习模式无响应超限退出时间 #define Countcar 1024 //指针丢失极限 #define Setbit 64 //总共64位 #define Reset 0 //复位检测 #define Sync1 1 //同步头检测,检测高电平 #define Sync2 2 //同步头检测2, #define RFg 3 //数据高电平 #define RFl 4 //数据低电平 #define Work_Main0 0xa1 // 厂商ID定义 #define Work_Main1 0xb2 #define Work_Main2 0xc3 #define Work_Main3 0xd4 #define M_SNOld0L 0x00 // 定义EEPROM中的位置,控制器可存储两个序列号的手柄! #define M_SNOld0M 0x01 // #define M_SNOld0H 0x02 // #define M_CounOld0L 0x03 // #define M_CounOld0H 0x04 // #define M_UpdataFlag 0x10 // #define M_SNOld1L 0x05 // #define M_SNOld1M 0x06 // #define M_SNOld1H 0x07 // #define M_CounOld1L 0x08 // #define M_CounOld1H 0x09 // #define STATUSIT(avr,s) ((unsigned)(&avr)*8+(s)) //绝对寻址定义 static bit C @ STATUSIT(STATUS,0); //对进位位进行定义 //-----------------内存定义-------------------- volatile bit Learn; //学习标志 volatile bit RFfull; //接收数据满标志 volatile bit RFbit; //数据状态接收 volatile bit Syncflage; //同步头标志 volatile bit Sflage; //标志 volatile bit Test; //新学习的序列号等待测试标志 volatile bit VId; //ID校验标志 volatile bit VKey; //键值检验标志 volatile bit VSn; //序列号校验标志 volatile bit VCount; //指针校验标志 unsigned int Learncount; //学习等待时间 static signed char RFcount; //定义数据采样计数器,可为正负 unsigned char RFstate; //状态选择寄存器 unsigned char Synch; //同步头高电平计数器 unsigned char Syncl; //同步头低电平计数器 unsigned char Bitcount; //比特计数器 unsigned char Bytecount; //字节计数器 unsigned char Zsnl; //新序列号暂存 unsigned char Zsnm; unsigned char Zsnh; unsigned char Zcounterl; unsigned char Zcounterh; unsigned char TempSNL; unsigned char TempSNM; unsigned char TempSNH; unsigned char Etemp; //解码暂存 unsigned char Esave; unsigned char Decodebar[4]; //解码密码 unsigned char Receive[9]; //接收缓存 共8*8=64位,B[9]接收的是结束符无意义 #define VlowKey Receive[0] //前4个字节,固定码部分定义,后四个字节是跳码接收 #define SNH Receive[1] //即Roll0 Roll1 Roll2 Roll3(发送部分) #define SNM Receive[2] #define SNL Receive[3] unsigned char TrueBar[4]; //真实码,解密后的 #define KeyID TrueBar[0] //键值 #define CounterH TrueBar[1] //指针高8位 #define CounterL TrueBar[2] //指针低8位 #define ID TrueBar[3] //识别码,是序列号的最低8位,应与SNL相同 union { unsigned int Coase; unsigned char Coabuf[2]; }CountCur,CounUser; //当前使用指针/旧指针 #define CounUserL CounUser.Coabuf[0] #define CounUserH CounUser.Coabuf[1] #define CountCurL CountCur.Coabuf[0] #define CountCurH CountCur.Coabuf[1] //--------------------------------------------- //-----------------函数定义-------------------- void PicInt(void); //初始化函数 void Learnbar(void); //学习模式函数 void Commonbar(void); //普通模式函数 void Keyscan(void); //键盘扫描函数 void Delay(unsigned int asd); //延时函数 void Dcode(void); //解码密码产生函数 void Dscript(void); //解跳码函数 void JId(void); //校验ID函数 void JKey(void); //校验键值函数 void DefineL(void); //学习数据校验函数 void JSn(void); //校验序列号函数 void JZhen(void); //校验指针 void WorkID0(void); //序列号0动作 void WorkID1(void); //序列号1动作 void Work(void); //工作函数 unsigned char ReadEeprom(unsigned char E_Addr); void WriteEeprom(unsigned char E_Addr,unsigned char E_Data); //********************************************* // 延时函数 //********************************************* void Delay(unsigned int asd) { unsigned int i; for(i=0;i=LWaitTime) //学习超限,退出学习模式 { Learn=0; Test=0; Learncount=0; LearnT=0; //学习指示灯灭 } } if(RFfull) //如果数据满则返回 { return; } switch(RFstate) //查询接收状态 { //////////////////////////////////////////////////////////////// case RFl: if(RFbit) //证明一个数据位接收完毕 { RFstate=RFg; //指向下一数据位的接收 asm("bcf _STATUS,0"); Receive[Bytecount]>>=1; //左移1位,最高位写0 if(RFcount<0) //如果>0则证明是数据位10 { Receive[Bytecount]+=0x80; //最高位写逻辑1 } if(RFcount==0) //非法操作 { RFstate=Reset; //状态复位 } RFcount = 0; if((++Bitcount&7)==0) { Bytecount++; } if(Bitcount==Setbit) { RA0=1; RFfull=1; //接收满标志 RFstate=Reset; //状态复位 } } else //否则是低电平 { RFcount--; //计数器减操作 if(RFcountHigh) //高电平非法长度 { RFstate=Reset; //状态复位 } } else { RFstate=RFl; //更新状态机 } break; ///////////////////////////////////////////////////////////////// case Sync1: if(RFbit) //同步头的上升沿检测,当高电平到来后允许低电平进行判断 { Synch++; //同步头高电平计数器加操作 Syncflage=1; //允许低电平判断 } if(Syncflage) //是否允许低电平判断 { if(RFbit==0) { Sflage=1; if((SynchSynch_head)) //高电平不符合条件 { RFstate=Reset; } Syncl++; //低电平加加 } } if(Sflage) { if(RFbit) //在检测完低电平后,再判断高电平的到来 { if((SynclSyncl_head)) //低电平不符合条件 { RFstate=Reset; //低电平长度不合格,状态复位 } else { RFstate=RFg; //状态指向数据高接收 } } } break; //////////////////////////////////////////////////////////////// case Reset: //状态机复位 default: RFstate=Sync1; //指向同步头的上升沿检测 Synch=0; //同步头高电平计数清0 Syncl=0; //同步头低电平计数清0 Syncflage=0; //标志清0 Sflage=0; RFcount=0; //数据电平计数清0 Bitcount=0; Bytecount=0; } } } //********************************************* // 初始化 //********************************************* void PicInt() { INTCON=0; //清所有中断 GIE=1; //开总中断,以及外部中断 //PEIE=1; ADCON1=0X07; //设置为通用IO口 OPTION=0x8f; //与分频器分给WDT TRISA=0; //设置为输出端口,用来驱动LED指示 TRISC5=1; //设置为输入端口,一个按键输入,一个数据输入 TRISC4=1; PORTA=0; //初始化全为0 Data0=0; //数据指示灯灭 Data1=0; Data2=0; Data3=0; Learn=0; //学习标志清0 T0IF=0; T0IE=1; //T0中断使能 TMR0=256-PERIOD; //定时初始化 RFstate=Reset; //状态机初始化 Synch=0; RFcount=0; RFfull=0; Bitcount=0; Bytecount=0; Test=0; VId=0; VKey=0; Learncount=0; // WriteEeprom(0M_UpdataFlag,0);//写标志,是更新序列号顺序的标志,初始化为0 } //********************************************* // 解跳码函数 //********************************************* void Dscript() { CounterL=Receive[6]^Decodebar[0]; //首先进行异或操作,对CountL进行加密 Esave=CounterL; switch(Esave&=0x0F) //用CounterL的低四位对CounterL的高四位进行跳码(非线性)编码 { case 0: CounterL^=0xF0; break; case 1: CounterL^=0xE0; break; case 2: CounterL^=0xD0; break; case 3: CounterL^=0xC0; break; case 4: CounterL^=0xB0; break; case 5: CounterL^=0xA0; break; case 6: CounterL^=0x90; break; case 7: CounterL^=0x80; break; case 8: CounterL^=0x70; break; case 9: CounterL^=0x60; break; case 10: CounterL^=0x50; break; case 11: CounterL^=0x40; break; case 12: CounterL^=0x30; break; case 13: CounterL^=0x20; break; case 14: CounterL^=0x10; break; case 15: CounterL^=0x00; break; default: break; } Etemp=CounterL; asm("swapf _Etemp,f"); KeyID=Decodebar[2]^Receive[4]^CounterL^Etemp; CounterH=Decodebar[3]^Receive[5]^CounterL^KeyID^Etemp; >} //********************************************* // 解码密码产生 //********************************************* void Dcode() { Decodebar[0]=TempSNL^Work_Main0; //产生编码密码,异或运算 Decodebar[1]=TempSNM^Work_Main1; Decodebar[2]=TempSNH^Work_Main2; Decodebar[3]=TempSNL^Work_Main3; } //********************************************* // 学习函数 //********************************************* void Learnbar() { if((Test)&&(Zsnl==SNL)&&(Zsnm==SNM)&&(Zsnh==SNH))//新学习的码等待测试 { DefineL(); //测试学习的新手柄,如果合格将进行存储 } else //否则学习新的码 { TempSNL=SNL; //保存序列号,准备产生解码密码 TempSNM=SNM; TempSNH=SNH; Dcode(); //产生解码密码 Dscript(); //解跳码 JId(); //ID 校验 JKey(); //Key 校验 if(VId&VKey) //基本校验成功 { Test=1; //等待测试 Learncount=0; //计数器清0 Zsnl=SNL; //暂存序列号 Zsnm=SNM; Zsnh=SNH; Zcounterl= CounterL; //暂存指针 Zcounterh= CounterH; RA5=1; } } } //********************************************* // 测试新学序列号函数 //********************************************* void DefineL() { CounUserL=Zcounterl; //读出指针 CounUserH=Zcounterh; TempSNL=Zsnl; //保存序列号,准备产生解码密码 TempSNM=Zsnm; //从存储中读出序列号 TempSNH=Zsnh; Dcode(); //产生解码密码 Dscript(); //解码 JId(); //ID校验 JKey(); //键值校验 JSn(); //序列号校验 if(VId&VKey&VSn) //如果测试成功 { if(ReadEeprom(M_UpdataFlag)==0) //读更新标志,更新ID0 { WriteEeprom(M_CounOld0L,CounUserL); // 存新的计数器指针 WriteEeprom(M_CounOld0H,CounUserH); WriteEeprom(M_SNOld0L,TempSNL); // 存新的序列号 WriteEeprom(M_SNOld0M,TempSNM); // 存新的序列号 WriteEeprom(M_SNOld0H,TempSNH); // 存新的序列号 WriteEeprom(M_UpdataFlag,1); } else //否则,更新ID1 { WriteEeprom(M_CounOld1L,CounUserL); // 存新的计数器指针 WriteEeprom(M_CounOld1H,CounUserH); WriteEeprom(M_SNOld1L,TempSNL); // 存新的序列号 WriteEeprom(M_SNOld1M,TempSNM); // 存新的序列号 WriteEeprom(M_SNOld1H,TempSNH); // 存新的序列号 WriteEeprom(M_UpdataFlag,0); } Test=0; //新码测试成功 Learn=0; //学习标志清0 Learncount=0; RA5=0; } } //********************************************* // 普通解码模式 //********************************************* void Commonbar() { TempSNL=ReadEeprom(M_SNOld0L); //首先读出序列号0 TempSNM=ReadEeprom(M_SNOld0M); TempSNH=ReadEeprom(M_SNOld0H); if((TempSNL==SNL)&&(TempSNM==SNM)&&(TempSNH==SNH)) { WorkID0(); //ID0操作 } else { TempSNL=ReadEeprom(M_SNOld1L); //首先读出序列号1 TempSNM=ReadEeprom(M_SNOld1M); TempSNH=ReadEeprom(M_SNOld1H); if((TempSNL==SNL)&&(TempSNM==SNM)&&(TempSNH==SNH)) { WorkID1(); //ID1操作 } } } //********************************************* // ID0动作 //********************************************* void WorkID0() { CounUserL=ReadEeprom(M_CounOld0L); //加载旧指针 CounUserH=ReadEeprom(M_CounOld0H); Dcode(); //产生解码密码 Dscript(); //解码 JId(); //ID校验 JKey(); //键值校验 JZhen(); if(VId&VKey&VCount) { WriteEeprom(M_CounOld0L,CountCurL); //指针更新 WriteEeprom(M_CounOld0H,CountCurH); // Work(); } } //********************************************* // ID1动作 //********************************************* void WorkID1() { CounUserL=ReadEeprom(M_CounOld1L); //加载旧指针 CounUserH=ReadEeprom(M_CounOld1H); Dcode(); //产生解码密码 Dscript(); //解码 JId(); //ID校验 JKey(); //键值校验 JZhen(); if(VId&VKey&VCount) { WriteEeprom(M_CounOld1L,CountCurL); //指针更新 WriteEeprom(M_CounOld1H,CountCurH); // Work(); } } //********************************************* // 工作函数 //********************************************* void Work() { VlowKey&=0x0F; if(VlowKey==0x01) { Data0=1; Data1=0; Data2=0; Data3=0; } if(VlowKey==0x02) { Data0=0; Data1=1; Data2=0; Data3=0; } if(VlowKey==0x04) { Data0=0; Data1=0; Data2=1; Data3=0; } if(VlowKey==0x08) { Data0=0; Data1=0; Data2=0; Data3=1; } } //********************************************* // 指针校验标志 //********************************************* void JZhen() { unsigned int i; CountCurL=CounterL; //加载新接收的指针 CountCurH=CounterH; if(CountCur.Coase>CounUser.Coase) { i=CountCur.Coase-CounUser.Coase; //产生指针偏差 if(i>Countcar) { VCount=0; } else { VCount=1; } } if(CountCur.CoaseCountcar) { VCount=0; } else { VCount=1; } } } //********************************************* // 校验序列号 //********************************************* void JSn() { if((TempSNL==SNL)&&(TempSNM==SNM)&&(TempSNH==SNH)) { VSn=1; //校验成功 } else { VSn=0; //检验失败 } } //********************************************* // ID校验函数 //********************************************* void JId() { VId=0; //首先清0 if(ID==SNL) { VId=1; } else { VId=0; } } //********************************************* // 键值校验函数 //********************************************* void JKey() { VKey=0; //首先清0 if((VlowKey&&0x0F)==((KeyID>>4)&&0x0F)) { VKey=1; } else { VKey=0; } } //********************************************* // 键盘扫描函数 //********************************************* void Keyscan() { if(Key==1) { Delay(5000); //延时消抖 if(Key==1) //确认按键按下 { Learn=1; //进入学习状态 } else { Learn=0; //清学习状态 } } } //********************************************* // 读EEPROM函数 //********************************************* unsigned char ReadEeprom(unsigned char E_Addr) { unsigned char E_Data; EEADR=E_Addr; EEPGD=0; RD=1; E_Data=EEDATA; return E_Data; //返回值 } //********************************************* // 写EEPROM函数 //********************************************* void WriteEeprom(unsigned char E_Addr,unsigned char E_Data) { unsigned char GIE_flage; //GIE开关标志! EEADR=E_Addr; EEDATA=E_Data; if(GIE) { GIE=0; GIE_flage=1; } EEPGD=0; WREN=1; EECON2=0x55; //规定写入 EECON2=0xAA; WR=1; while(EEIF==0) { } EEIF=0; if(GIE_flage) { GIE=1; GIE_flage=0; } }