#include<reg52.h>
#include<intrins.h>
#define u8 unsigned char
#define u16 unsigned int
sfr IAP_DATA=0xc2;
sfr IAP_ADDRH=0xc3;
sfr IAP_ADDRL=0xc4;
sfr IAP_CMD=0xc5;
sfr IAP_TRIG=0xc6;
sfr IAP_CONTR=0xc7;
sbit LED=P3^6; //定义指示灯
sbit CH1=P1^7; //定义6个通道输出
sbit CH2=P1^6;
sbit CH3=P1^5;
sbit CH4=P1^4;
sbit CH5=P1^3;
sbit CH6=P1^2;
sbit MDO=P3^3; //定义无线模块的管脚
sbit SCK=P3^4;
sbit CE=P3^5;
sbit IRQ=P3^2;
sbit MDI=P1^1;
sbit CSN=P1^0;
sfr P1M0=0x92; //单片机IO口配置寄存器地址
sfr P3M0=0xb2;
u8 rx[17]; //接收的6字节数据
u8 tx[17]={0xc3,0x3c,12};
u8 k,n,w,tt,m;
u8 channel,connecting,hopping_turn;
bit cha,chb,chc,chd,che;
u8 missing_data[4];
u8 address[5];
u8 hopping[5];
bit first;
bit lose;
bit jump_1,jump_2;
bit restar;
void delay1ms() //@12.000MHz
{
unsigned char i, j;
_nop_();
_nop_();
i = 11;
j = 168;
do
{
while (--j);
} while (--i);
}
void delay4us() //@12.000MHz
{
unsigned char i;
i = 8;
while (--i);
}
void delay(u8 i)
{
while(i--)
delay4us();
}
void delay_ms(u8 i)
{
while(i--)
delay1ms();
}
u8 EEPROM_read(u8 address)
{
IAP_CMD=0x01;
IAP_ADDRH=0;
IAP_ADDRL=address;
IAP_TRIG=0x5a; //EEPROM读写密码,不同系列单片机不同
IAP_TRIG=0xa5;
return IAP_DATA;
}
void EEPROM_write(u8 address,u8 byte)
{
IAP_CMD=0x02;
IAP_DATA=byte;
IAP_ADDRH=0;
IAP_ADDRL=address;
IAP_TRIG=0x5a;
IAP_TRIG=0xa5;
}
void EEPROM_clean(u8 address)
{
IAP_CMD=0x03;
IAP_ADDRH=0;
IAP_ADDRL=address;
IAP_TRIG=0x5a;
IAP_TRIG=0xa5;
}
void DATA_read()
{
IAP_CONTR=0x82;
if(EEPROM_read(0)!=0xf5)
{
first=1;
missing_data[0]=127;
missing_data[1]=127;
missing_data[2]=0;
missing_data[3]=127;
}
else
{
hopping[0]=EEPROM_read(1);
hopping[1]=EEPROM_read(2);
hopping[2]=EEPROM_read(3);
hopping[3]=EEPROM_read(4);
hopping[4]=EEPROM_read(5);
address[0]=EEPROM_read(6);
address[1]=EEPROM_read(7);
address[2]=EEPROM_read(8);
address[3]=EEPROM_read(9);
address[4]=EEPROM_read(10);
missing_data[0]=EEPROM_read(11);
missing_data[1]=EEPROM_read(12);
missing_data[2]=EEPROM_read(13);
missing_data[3]=EEPROM_read(14);
}
IAP_CONTR=0;
}
void DATA_save()
{
IAP_CONTR=0x82;
EEPROM_clean(0);
EEPROM_write(0,0xf5);
EEPROM_write(1,hopping[0]);
EEPROM_write(2,hopping[1]);
EEPROM_write(3,hopping[2]);
EEPROM_write(4,hopping[3]);
EEPROM_write(5,hopping[4]);
EEPROM_write(6,address[0]);
EEPROM_write(7,address[1]);
EEPROM_write(8,address[2]);
EEPROM_write(9,address[3]);
EEPROM_write(10,address[4]);
EEPROM_write(11,missing_data[0]);
EEPROM_write(12,missing_data[1]);
EEPROM_write(13,missing_data[2]);
EEPROM_write(14,missing_data[3]);
IAP_CONTR=0;
}
u8 SPI(u8 byte)
{
u8 i;
for(i=0;i<8;i++)
{
MDI=(byte&0x80);
SCK=1;
byte<<=1;
byte|=MDO;
SCK=0;
}
return byte;
}
void REG_write(u8 address,u8 command)
{
CSN=0;
SPI(0x20+address);
SPI(command);
CSN=1;
}
/*
u8 REG_read(u8 address)
{
u8 m;
CSN=0;
SPI(address);
m=SPI(0);
CSN=1;
return m;
} */
void FIFO_write(u8 DATA_OUT[],u8 lengh)
{
u8 i;
CSN=0;
SPI(0xa0);
for(i=0;i<lengh;i++)
SPI(DATA_OUT[i]);
CSN=1;
}
void FIFO_read(u8 DATA_IN[],u8 lengh) //读取接收数据缓冲区
{
u8 i;
CSN=0;
SPI(0x61); //读取命令
for(i=0;i<lengh;i++)
DATA_IN[i]=SPI(0);
CSN=1;
}
/*
void TX_address(u8 DATA_IN[])
{
CE=0;
CSN=0;
SPI(0x20+0x10);
SPI(DATA_IN[0]);
SPI(DATA_IN[1]);
SPI(DATA_IN[2]);
SPI(DATA_IN[3]);
SPI(DATA_IN[4]);
CSN=1;
CE=1;
} */
void RX_address(u8 DATA_IN[])
{
CE=0;
CSN=0;
SPI(0x20+0x0a);
SPI(DATA_IN[0]);
SPI(DATA_IN[1]);
SPI(DATA_IN[2]);
SPI(DATA_IN[3]);
SPI(DATA_IN[4]);
CSN=1;
CE=1;
}
void RX_mode()
{
CE=0; //先关闭模块,才能写命令寄存器
REG_write(0x00,0x3b); //CRC,8 bit,Power on,RX
CE=1;
}
void TX_mode()
{
CE=0;
REG_write(0x00,0x0a);
CE=1;
}
void TX_power_set(u8 H)
{
CE=0;
if(H)REG_write(0x06,0x27);
else REG_write(0x06,0x21);
CE=1;
}
void TX_size_set(u8 l)
{
CE=0;
REG_write(0x11,l);
CE=1;
}
void channel_set(u8 c)
{
CE=0;
REG_write(0x05,c);
CE=1;
}
void NRF_init()
{
CE=0;
SCK=0;
REG_write(0x01,0x00); //unable ACK
REG_write(0x02,0x01); //enable P0_RX
REG_write(0x04,0x00); //unable ACK
CE=0;
RX_mode(); //CRC,8 bit,Power on,TX
channel_set(66);
TX_power_set(1);
TX_size_set(8);
// TX_address(address);
}
void initial()
{
u8 t;
CH5=0; //如果CH5与CH6被短接,重新对码
delay1ms();
if(CH6==0) //新增修正通道6插上舵机不能用的BUG
{
P1M0=0x08; //插上舵机也会使CH6为0,所以将CH5设为推挽
CH5=1;
delay1ms();
if(CH6)restar=1; //如果CH6被拉高,说明5/6通道短接,启动重新对码
}
else restar=0; //如果CH5=0,CH6仍为1,则5/6通道无连接
CH5=1;
LED=0; //点亮指示灯1毫秒再关闭,表示单片机正常工作
P1M0=0xfc; //将6通道输出IO口配置为大电流推挽模式,保证正常驱动电调与舵机
TMOD=0x11; //允许两个定时器中断
IE=0x8a;
if(restar)first=1;
DATA_read();
NRF_init();
delay1ms();connecting=1;
if(first)
{
TX_size_set(17);
while(connecting)
{
while(IRQ);
FIFO_read(rx,17); //读取接收数据
CE=0;
REG_write(0x07,0x40); //清除无线模块中断信号
CE=1;
if(rx[0]==0xaa&&rx[1]==0x55)
{
hopping[0]=rx[2];
hopping[1]=rx[3];
hopping[2]=rx[4];
hopping[3]=rx[5];
hopping[4]=rx[6];
address[0]=rx[7];
address[1]=rx[8];
address[2]=rx[9];
address[3]=rx[10];
address[4]=rx[11];
if(rx[12]==1)
{
missing_data[0]=rx[13];
missing_data[1]=rx[14];
missing_data[2]=rx[15];
missing_data[3]=rx[16];
}
connecting=0;
}
}
connecting=1;
while(connecting)
{
TX_mode();
channel_set(66);TX_size_set(17);
delay1ms();delay1ms();
FIFO_write(tx,17);
delay1ms();delay1ms();
RX_mode();
channel_set(hopping[0]);
while(1)
{
delay1ms();
if(IRQ==0)
{
FIFO_read(rx,17); //读取接收数据
CE=0;
REG_write(0x07,0x40); //清除无线模块中断信号
CE=1;
connecting=0;
}
t++;if(t>30){t=0;break;}
}
}
LED=1;
DATA_save();
TX_size_set(8);
RX_address(address);
}
else
{
RX_address(address);
}
// while(1);
}
main()
{
delay_ms(200);delay_ms(200);//开机延时以避过电源波动
initial();
TR0=1; //打开定时器0,开始统计信号
LED=1;
channel_set(hopping[0]);
lose=1;
while(1)
{
while(IRQ&&lose);TR1=1; //等待无线模块的中断信号,直到第一个有效信号再开始输出6通道信号
if(lose)
{
tt=0;m=0;jump_1=0;jump_2=0; //收到有效信号后刷新定时器0、跳频器
k++; //每收到一个有效信号自加一
FIFO_read(rx,8); //读取接收数据
CE=0;
REG_write(0x07,0x40); //清除无线模块中断信号
CE=1;
hopping_turn++;
if(hopping_turn>4)hopping_turn=0;
channel_set(hopping[hopping_turn]);
}
else
{
hopping_turn++;
if(hopping_turn>4)hopping_turn=0;
channel_set(hopping[hopping_turn]);
lose=1;
}
}
}
void et0()interrupt 1 //定时器0用作信号统计,定时周期65ms
{
tt++;w++;
if(w==15) //每秒统计接收到的有效信号数量,小于20个点亮LED示警,表示信号微弱
{
if(k<20)LED=0;
else LED=1;
k=0;w=0;
}
if(tt>30)//如果tt增长到30,表示已经有两秒未收到有效信号,执行失控保护舵量
{
tt=30;
rx[0]=missing_data[0];
rx[1]=missing_data[1];
rx[2]=missing_data[2];
rx[3]=missing_data[3];
}
}
void et1()interrupt 3 //定时器1用作信号输出,定时周期3ms
{
TH1=0xf4,TL1=0x47;
m++; //两个标志位循环跳频
if(jump_1==0)
{
if(m>7)lose=0,jump_1=1,jump_2=0; //未收到信号,开始第一跳
}
else
{
if(jump_2==0)
{
if(m>14)lose=0,jump_2=1; //第一跳之后仍无信号,开始第二跳
}
else
{
if(m>22)m=0,lose=0,jump_1=0; //第二跳之后仍无信号,复位重新跳。
}
}
n++;
if(n==6)n=0; //轮流输出6通道信号,整个周期18ms,输出频率55HZ
switch(n)
{
case 0:CH1=1,delay1ms(),delay(rx[0]),CH1=0;break;//1ms固定高电平,256级控制,每级4us
case 1:CH2=1,delay1ms(),delay(rx[1]),CH2=0;break;//舵机控制原理,0.5ms高电平对应最左端
case 2:CH3=1,delay1ms(),delay(rx[2]),CH3=0;break;//1.5ms高电平对应舵机中点
case 3:CH4=1,delay1ms(),delay(rx[3]),CH4=0;break;//2.5ms高电平对应舵机最右端
case 4:CH5=1,delay1ms(),delay(rx[4]),CH5=0;break;//实际控制取1至2ms
case 5:CH6=1,delay1ms(),delay(rx[5]),CH6=0;break;
}
//每次输出信号费时1至2ms,剩余时间空闲,跳回主程序接收新数据
}