基于nRF905 模块的AT89S 单片机无线收发系统设计 管脚 名称 管脚功能 说明 1 VCC 电源 电源+3.3~3.6V DC 2 TX_EN 数字输入 工作模式选择 3 TRX_CE 数字输入 使能芯片发射或接收 4 PWR_UP 数字输入 芯片上电 5 uCLK 时钟输出 (未使用) 6 CD 数字输出 载波检测 7 AM 数字输出 地址匹配 8 DR 数字输出 接收或发射数据完成 9 MISO SPI 接口 SPI 输出 10 MOSI SPI 接口 SPI 输入 11 SCK SPI 时钟 SPI 时钟 12 CSN SPI 使能 SPI 使能 13、14 GND 地 接地 下面为典型的 nRF905 模块数据发送流程[3]: (1)当微控制器要发送数据时,将接收机的地址和发 送数据通过SPI 接口传输给nRF905 模块; (2)微控制器设置TRX_CE 和TX_EN 管脚同时置为 高电平,启动发送端的nRF905 模块为发送模式; (3)发送端的nRF905 模块发送过程处理: a)射频寄存器开启; b)数据打包(加字头和CRC 校验码); c)数据包发送; d)当数据包发送结束,将数据发送完成管脚(DR 管脚) 置为高电平; (4)如果AUTO_RETRAN 被设置为高,nRF905 模块 将连续地发送数据包,直到TRX_CE 被设置为低; (5)TRX_CE 被设置为低时,nRF905 模块数据包发送 过程结束并回到待机模式。 AT89S单片机控制nRF905 模块数据发送流程图如图3 所示。 下面为典型的 nRF905 模块数据接收流程[4]: (1)微控制器控制TRX_CE 为高电平、TX_EN 为低电 平,nRF905 模块进入接收模式; (2)650us 后,nRF905 模块监测空中的信息,等待接 收数据; (3)当nRF905 模块检测到与接收频率相同的载波时, 设置载波检测管脚(CD 管脚)为高电平; (4)当nRF905 模块接收到有效的地址时,设置地址匹 配管脚(AM 管脚)为高电平; (5)当一个正确的数据包接收完毕后,nRF905 模块自 动去掉数据包的字头、地址和CRC 校验码,然后将数据接 受完成管脚置为高电平; (6)微控制器将TRX_CE 设置为低电平; (7)微控制器通过SPI 接口以一定的速率提取数据包 中的有效接收数据; (8)当所有的有效数据接收完毕,微控制器控制nRF905 模块数据接收完成管脚(DR 管脚)和地址匹配管脚(AM 管脚)为低电平; (9)nRF905 进入待机模式。 说明:(1)VCC电压范围为DC 3.3V~3.6V之间,不能超过3.6V否则会烧坏模块。 (2)模块 附加更加详细的收发程序,包括解释: ////////////////////////////////////////////整体参数//////////////////////////////////////////////////// //NewMsg-RF905-共有四种工作模式,其中有两种活动RX/TX模式和两种节电模式。 //活动模式 // ShockBurst RX //ShockBurst TX //节电模式 //掉电和SPI编程 //工作模式: //┏━━━━┳━━━━┳━━━━┳━━━━━━━━━━━━━━━━┓ //┃PWR UP ┃ TRX CE ┃ TX_EN ┃工作模式 ┃ //┣━━━━╋━━━━╋━━━━╋━━━━━━━━━━━━━━━━┫ //┃0 ┃ x ┃ x ┃掉电和SPI编程 ┃ //┣━━━━╋━━━━╋━━━━╋━━━━━━━━━━━━━━━━┫ //┃1 ┃ 0 ┃ x ┃ Standby和SPI编程 ┃ //┣━━━━╋━━━━╋━━━━╋━━━━━━━━━━━━━━━━┫ //┃1 ┃ 1 ┃ O ┃ShockB urst RX ┃ //┣━━━━╋━━━━╋━━━━╋━━━━━━━━━━━━━━━━┫ //┃ 1 ┃ l ┃ 1 ┃ShockBurst T X ┃ //┗━━━━┻━━━━┻━━━━┻━━━━━━━━━━━━━━━━? //ShockBurst TX发送流程: //典型的RF905发送流程分以下几步: //A.当微控制器有数据要发送时,通过SPI接口,按时序把接收机的地址和要发送的数据送传给RF905, //SPI接口的速率在通信协议和器件配置时确定; //B.微控制器置高TRX_CE和TX_EN,激发RF905的ShockBurs发送模式; //C.RF905的ShockBurs tTMI发送: //(1)射频寄存器自动开启; //(2)数据打包(加字头和CRC校验码); //(3)发送数据包; //(4)当数据发送完成,数据准备好引脚被置高; //D.AUTO_REI'RAN被置高,RF905不断重发,直到TRX_CE被置低; //E.当TRX-CE被置低,RF905发送过程完成,自动进入空闲模式。 //注意:ShockBurs tTM工作模式保证,一旦发送数据的过程开始,无 // 论TRX_EN和TX-EN引脚是高或低,发送过程都会被处理完。只有 // 在前一个数据包被发送完毕,RF905才能接受下一个发送数据包 //ShockBurst RX接收流程 // 接收流程 //A.当TRX_CE为高、TX_EN为低时,RF905进入ShockBurs tTM接收模式; //B.650us后,RF905不断监测,等待接收数据; //C.当RF905检测到同一频段的载波时,载波检测引脚被置高; //D.当接收到一个相匹配的地址,AM引脚被置高; //E.当一个正确的数据包接收完毕,RF905自动穆去字头、地址和CRC // 校验位,然后把DR引脚置高 //F.微控制器把TRX_CE置低,nRF905进入空闲模式; //G.微控制器通过SPI口,以一定的速率把数据穆到微控制器内; //H.彼?械氖?萁邮胀瓯希琻RF905把DR引脚和AM引脚置低; //当正在接收一个数据包时,TRX_CE或TX_EN引脚的状态发生改变, //RF905立即把其工作模式改变,数据包则丢失。当微处理器接到AM //引脚的信号之后, 其就知道RF905正在接收数据包,其可以决定是 //让RF905继续接收该数据包还是进入另一个工作模式。 ///////节能模式 //RF905的节能模式包括关机模式和节能模式。 //在关机模式,RF905的工作电流最小,一般为2.SuA。进入关机模 //式后,RF905保持配置字中的内容,但不会接收或发送任何数据。空 //闲模式有利于减小工作电流,其从空闲模式到发送模式或接收模式的 //启动时间也比较短。在空阑模式下,RF905内部的部分晶体振荡器处 //于工作状态? //五、配置NeWMsg-RF905模块 //所有配置字都是通过SPlI接口送给RF905。SIP接口的工作方式可 //通过SPlI指令进行设置。当RF905处于空闲模式或关机模式时,SPI //按口可以保持在工作状? //SPI寄存器配置 //SPI接口由5个内部寄存器组成。执行寄存器的回读模式来确认寄存器的内容。 //状态寄存器(Status-Register) //寄存器包含数据就绪(DR)和地址匹配(AM)状态。 //RF配置寄存器(RF-Configuration Register) //寄存器包含收发器的频率,输出功率等配置信息。 //发送地址(IX-Address) //寄存器包含目标器件地址,字节长度由配置寄存器设置。 //发送有效数据( IX-Payload) //寄存器包含发送的有效ShockBurst数据包数据,字节长度由配置寄存器设置。 //接收有效数据( IX-Payload) //寄存器包含接收到的有效ShockBurst数据包数据,字节长度由配置寄存器设置。在寄存器中的有效数据由 //数据准备就绪(DR)指荆 //SPI指令设置 //用于SPI接口的有用命令见下表。当CSN为低时,SPI接口开始等待一条指令,任何一条新指令均由CSN //的由高到低的转换开始。 //┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ //┃ SPI串行接口指令 ┃ //┣━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ //┃指令名称 ┃指令格式 ┃操作 ┃ //┣━━━━━━╋━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ //┃W CONFIG ┃ OOOOAAAA ┃写配置寄存器。AAAA指出写操作的开始字节,字节数量取决于 ┃ //┃(WC) ┃ ┃AAAA指出的开始地址。 ┃ //┣━━━━━━╋━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ //┃R CONFIG ┃ OOOIAAAA ┃读配置寄存器。AAAA指出读操作的开始字节,字节数量取决于 ┃ //┃(RC) ┃ ┃AAAA指出的开始地址。 ┃ //┣━━━━━━╋━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ //┃W TX PAYLOA ┃ 00100000 ┃写TX有效数据:1-32字节。写操作全部从字节o开始。 ┃ //┃D ┃ ┃ ┃ //┃(WTP) ┃ ┃ ┃ //┣━━━━━━╋━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ //┃R TX PAYLOA ┃ 00100001 ┃读TX有效数据:1-32字节。读操作全部从字节o开始。 ┃ //┃D ┃ ┃ ┃ //┃(RTP) ┃ ┃ ┃ //┣━━━━━━╋━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ //┃W TX ADDRES ┃00100010 ┃写TX地址:1-4字节。写操作全部从字节o开始 ┃ //┃S ┃ ┃ ┃ //┃(WTA) ┃ ┃ ┃ //┣━━━━━━╋━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ //┃R TX ADDRES ┃0010001 1 ┃读TX地址:1-4字节。读操作全部从字节o开始。 ┃ //┃S ┃ ┃ ┃ //┃(RTA) ┃ ┃ ┃ //┣━━━━━━╋━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ //┃R RX PAYLOA ┃ 001 001 00 ┃读RX有效数据:1-32字节。读操作全部从字节o开始。 ┃ //┃D ┃ ┃ ┃ //┃(RRP) ┃ ┃ ┃ //┣━━━━━━╋━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ //┃CHANNEL CON ┃lOOOpphc ┃快速设置配置寄存器中CH NO,HFREQ_PLL和PA PWR的专用 ┃ //┃FIG ┃cccccccc ┃命令_ CH NO=ccccccccc: HFREQ_PLL=h: PA_PWR=pp ┃ //┃(CC) ┃ ┃ ┃ //┗━━━━━━┻━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━タ #include <reg52.h> //#include <ABSACC.h> //#include <intrins.h> //#include <stdio.h> ////---------------------------------------------------------------------------------------------------------------- #define uint unsigned int #define uchar unsigned char //---------------------------------------------------------------------------------------------------------------- #define BYTE_BIT00x01 #define BYTE_BIT1 0x02 #define BYTE_BIT2 0x04 #define BYTE_BIT3 0x08 #define BYTE_BIT4 0x10 #define BYTE_BIT5 0x20 #define BYTE_BIT6 0x40 #define BYTE_BIT70x80 //---------------------------------------------------------------------------------------------------------------- bdata unsigned char DATA_BUF;//可位寻址的片内RAN #define DATA7((DATA_BUF&BYTE_BIT7) != 0) #define DATA0 ((DATA_BUF&BYTE_BIT0) != 0) sbitflag=DATA_BUF^7; sbitflag1=DATA_BUF^0; //------------------------------------ 发送数据缓冲区------------------------------------------------- #define TxRxBuf_Len 4 unsigned char TxRxBuf[TxRxBuf_Len]={0x29,0x30,0x31,0x32,}; code TxAddress[4]={0xcc,0xcc,0xcc,0xcc}; char tf; //----------------------------------------NRF905工作模式控制端口------------------------------------------------------ sbitTXEN=P2^4;//发射使能 sbitTRX_CE=P3^2;//发射接收使能 sbitPWR=P2^3; //----------------------------------------LED显示端口--------------------------------------------------- sbit LED=P1^0; //----------------------------------------NRF905 数据交换端口(SPI)--------------------------------------------------- sbitMISO=P2^6;//输出 sbitMOSI=P2^1;//输入 sbitSCK=P2^5;//时钟 sbitCSN=P2^0;//使能 //----------------------------------------nrf905状态端口--------------------------------------------------------- sbitAM=P2^7; sbitDR=P3^3; sbitCD=P2^2; //--------------------------------------------------------------------------------------------------------------- //----------------------------------------按键端口------------------------------------------------------- sbitKEY=P3^7; //---------------------nrf905控制指令------------------------------------------- #define WC0x00 //写配置寄存器 #define RC0x10 //读配置寄存器 #define WTP0x20 //向TX-Payload寄存器写入发送有效数据 #define RTP0x21 //向TX-Payload寄存器读取发送有效数据 #define WTA0x22 //向TX-Addtess寄存器写入发送地址 #define RTA0x23 //向TX-Addtess寄存器读取发送地址 #define RRP0x24 //从RX-Payload寄存器读取接收到的有效数据 //------------------------------------------NRF905寄存器配置------------------------------------------------ unsigned char idata RFConf[11]= { 0x00, //配置命令// 0x4c, //CH_NO,配置频段在430MHZ字节0,配置频段 0x0c, //输出功率为10db,不重发,节电为正常模式 字节1,000 1100 0x44, //地址宽度设置,为4字节字节2,6:4 是TX地址宽度, 2:0是RX地址宽度 0x04,0x04, //接收发送有效数据长度为4字节字节3(RX),字节(TX):可设置为1,2,4,8,16,32 字节,其中6,7 两位为空,写00,则4字节为:0000 0100 : 0x04 依次类推 0xCC,0xCC,0xCC,0xCC, //接收地址字节5到字节8 0x58, //CRC充许,8位CRC校验,外部时钟信号不使能,16M晶振 字节9, }; //================================================延时=========================================================== void nrf905_Delay(int n) { uint i; while(n--) for(i=0;i<80;i++); } //=================================================SPI读函数======================================================= //步骤一:MISO线准备好需要发送的数据位 //步骤二:SCK置高,主机读取MISO线上的数据 //步骤三:SCK置低,准备接收数据的下一位 // 以上步骤循环执行8次,通过SPI从器件上读取数据完成! //数据传送时候。高位在前,低位在后。 unsigned char SpiRead(void) { unsigned char j; for (j=0;j<8;j++) { DATA_BUF=DATA_BUF<<1; SCK=1; if (MISO)//读取最高位,保存至最末尾,通过左移位完成整个字节 { DATA_BUF|=BYTE_BIT0; } else { DATA_BUF&=~BYTE_BIT0; } SCK=0; } return DATA_BUF; } //===========================================SPI写函数=============================================================== //步骤一:MOSI线准备好需要发送的数据位 //步骤二:SCK置高,器件读取MOSI线上的数据 //步骤三:SCK置低,准备发送数据的下一位 // 以上步骤循环执行8次,通过SPI从器件上发送数据完成! //数据传送时候。低位在前,高位在后。 void SpiWrite(unsigned char send) { unsigned char i; DATA_BUF=send; for (i=0;i<8;i++) { if (DATA7)//总是发送最高位 { MOSI=1;//SPI输入,主机写操作 } else { MOSI=0; } SCK=1; DATA_BUF=DATA_BUF<<1; SCK=0; } } //--------------------------------------初始化nRF905--------------------------------------------- void nRF905Init(void) { CSN=1;// Spi disable SCK=0;// Spi clock line init low DR=1;// Init DR for input AM=1;// Init AM for input CD=1;// Init CD for input PWR=1;// nRF905 power on TRX_CE=0;// Set nRF905 in standby mode TXEN=0;// set radio in Rx mode } //-----------------------------------------------------初始化寄存器----------------------------------------------- //步骤一:CSN置低电平,SPI接口开始等待第一条命令 //步骤二:调用SpiWrite函数,向nrf905发送WC指令,准备写入配置信息 //步骤三:反复调用SpiWrite函数,向器件配置寄存器写入配置信息 //步骤四:CSN置高电平,结束SPI通讯。即nrf905配置完成! void Config905(void) { uchar i; CSN=0;// CSN片选信号,SPI使能 //SpiWrite(WC);// 向905芯片写配置命令 for (i=0;i<11;i++)// 循环写入配置信息 { SpiWrite(RFConf[i]); //RxTxConf保存预先设置好的配置信息 } CSN=1;// 结束SPI数据传输 } //-------------------------------发送数据打包--------------------------------------------------- //步骤一:通过SpiWrite函数发送WTP命令,准备写入TX有效数据 //步骤二:循环调用SpiWrite向TX-Payload寄存器写入有效数据(中间必须夹有CSN电平变化) //步骤三:延时 //步骤四: 通过SpiWrite函数发送WTA命令,准备写入TX地址 //步骤五:循环调用SpiWrite向TX-Address寄存器写入TX地址 //步骤六:TRC_CE=1;开始发送数据,延时,nrf905数据发送完成, //当nrf905接收到一条完成的信息时,会将DR引脚置高。 void TxPacket(uchar *TxRxBuf) { uchar i; //Config905(); CSN=0; SpiWrite(WTP);// Write payload command for (i=0;i<4;i++) { SpiWrite(TxRxBuf[i]);// 写入32直接发送数据 } CSN=1; nrf905_Delay(1);// 关闭SPI,保存写入的数据 CSN=0;// SPI使能,保存写入的数据 SpiWrite(WTA);// 写数据至地址寄存器 for (i=0;i<4;i++)// 写入四字节地址 写入与对方地址一样的地址 { SpiWrite(TxAddress[i]); } CSN=1;// 关闭SPI TRX_CE=1;// 进入发送模式,启动射频发送 nrf905_Delay(1);//进入ShockBurst发送模式后,芯片保存数据 TRX_CE=0;// 发送完成后返回ATANDBY模式 while (DR!=1); } //----------------------------------------------设置发送初始状态--------------------------------------------- void SetTxMode(void) { TRX_CE=0; TXEN=1; nrf905_Delay(1); // nrf905_Delay for mode change(>=650us) } //步骤一:TRX_ce=0;必须将次引脚置低,使905进入standby模式 //步骤二:发送RRP指令 //步骤三:循环调用SpiWrite函数,读取接收到的数据 //步骤四:等待DR和AM引脚复位为低电平 // AM 地址匹配,接收到有效地址,被置高 // DR 接收到有效数据包,并解码后,被置高,当所有有效数据被读取后, // nrf905降AM和DR置低,最后需要注意的是,必须首先设置器件的 // 发送/接收模式才能保证有效的数据发生接收 //-----------------------------------------------设置nrf905进入接收模式--------------------------------------------------- void SetRxMode(void) { TXEN=0; TRX_CE=1; nrf905_Delay(1); // nrf905_Delay for mode change(>=650us) } //-------------------------------------判断数据接收状态----------------------------------------------------- unsigned char CheckDR(void)//检查是否有新数据传入 Data Ready { DR=1; //通过对端口写1,可以使端口为输入状态,这51的 特性。不熟悉者可以参阅51相关书籍作证(将DR端口设置为输入状态。) if (DR==1) { DR=0; return 1; } else { return 0; } } //----------------------------NRF905接收到数据后读取保存------------------------------------------------------------ void RxPacket(void) { uchar i; nrf905_Delay(1); //TRX_CE=0;// 设置905进入待机模式 nrf905_Delay(100); TRX_CE=0; CSN=0;// 使能SPI nrf905_Delay(1); SpiWrite(RRP); //准备读取接收到的数据 for (i = 0 ;i < 4 ;i++) { TxRxBuf[i]=SpiRead();// 通过SPI接口从905芯片读取数据 } CSN=1;//禁用SPI nrf905_Delay(10); TRX_CE=1; } //--------------------------------------------------------数据接收------------------------------------------------ void RX(void) { SetRxMode(); // while (CheckDR()==0); 为了实现双向通信,就不能一直处于接收等待状态,所以注释掉 nrf905_Delay(10); RxPacket(); if(TxRxBuf[0]==0x29) { LED=0; nrf905_Delay(300); LED=1; nrf905_Delay(300);//接收到数据 后闪烁 } } //----------------------------------------------------------------------------------------------------------------- void main(void) { nRF905Init(); Config905(); LED=1; while(1) { RX(); if(KEY ==0 ) { while(KEY==0); tf = 1 ; TxRxBuf[0]=0x29; } if (tf==1) { SetTxMode(); TxPacket(TxRxBuf);// 发送命令数据 LED=0; nrf905_Delay(300); LED=1; nrf905_Delay(300);//发送后LED闪烁 tf = 0; } } } |
|