#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,剩余时间空闲,跳回主程序接收新数据 }