首页资源分类应用技术射频与通信技术 > NRF905参考程序参考教程包含多个实例和解释 (1)

NRF905参考程序参考教程包含多个实例和解释 (1)

已有 445125个资源

下载专区

文档信息举报收藏

标    签:NR

分    享:

文档简介

NRF905参考程序参考教程包含多个实例和解释 (1)

文档预览

NRF905程序参考教程。 本资料主要是将程序中各部分子程序的功能与NRF905的手册相关联,使得各位同仁在每个子程序为什么这么写都在手册中找到具体的体现,特别是寄存器配置。 内涵完整参考程序,是100%可用程序。 硬件的连接方法在参考程序之后,并介绍原理。 本人最后只是为了提供给大家一个入门的资料或是引子罢了,至于如何应用的巧妙那是后期编程巧妙的结果,目前只是给刚进门的人士一个可以快速理解和掌握的浅显易懂的教程罢了。本人疏忽之处还请见谅。 按照惯例先展示作品抛砖引玉: 四路AD采集+温度采集 彩屏显示信息数据GSM手机电话 最近做的,音频功放四路电机控制大功率led控制,不解释。 以前做的,舵机和摄像头,不解释。 更早先的,VGA显示,不解释。 已发送为例子。 1.通过spi接口把寄存器相应的值写到905中 2.把要发的数据写到905中 3.把状态设置成发送 4.数据被发出 那么操作905就主要是前三步的问题,那么请带个这前三个问题深入理解下面的相关解释了。 无线通信模块的三个要素: Nrf905模式的配置 Nrf905通过寄存器配置 Nrf905需要spi通信配置寄存器 先看模式配置:程序加解释 PWR_UP TRX_CE TX_EN 操作模式 0 X X 断电和SPI编程 1 0 X 待机和SPI编程 1 1 0 射频接收模式 1 1 1 射频发送模式 根据这个图表,我们发觉有四种模式。捡重点的说实现收发功能有两种模式。 这两种模式在程序段中的实现是: 设置成接受模式,程序中没写PWR_UP,如果他是低电平就变成断电,所以个程序段默认PWR_UP为高电平。 void SetRxMode(void) { TXEN=0; TRX_CE=1; Delay(1); // delay for mode change(>=650us) } 设置发送模式,这里会有疑问,在于 TRX_CE=0;。这里给出的解释是,如果我们直接写 TRX_CE=1;这样模块立即将其内部所写好的数据发送出去。 而对于编程的人员来说编出的程序五花八门,就比如说这条,改程序员的意图并不想让设置发送模式时,数据就被立即发出,所以写了 TRX_CE=0;。如果看后面的完整程序,你会发现在发送时,有 TRX_CE=1;这一步。所以说,刚才那个图表没有问题。这里可以认为是准备发送模式,而不是发送模式,一旦 TRX_CE=1;那么数据立即被发送。 void SetTxMode(void) { TRX_CE=0; TXEN=1; Delay(1); // delay for mode change(>=650us) } 关于图表中前两种模式中,实例程序所应用的是第二种,即待机spi编程模式。不管应用两种的哪一种,都是为了spi编程(通过spi通信配置905寄存器)。 那么给出这个模式的应用程序段: 有这么做引脚赋予各种电平先不用管他,我们看到 PWR=1;TRX_CE=0;TXEN=0;这三个,在待机spi模式中TXEN=x;即可以为任何值。说明现在是待机且spi编程模式。 程序段中其他引脚功能罗列下: Csn:spi的有效与否的引脚,低电平有效。如果只是单纯的设置模式,该引脚并没用处,只是后期程序的编写,所以做下配置。 Sck:spi的时钟,现在只是设置模式,还没开始spi通信,所以付个低电平。 DR:数据是否准备好,现在没有什么可准备的。 AD,CD也是一样,等到spi通信的时候才需关系。这里做个引子吧。 void nRF905Init(void) { CSN=1; // Spi disable SCK=0; // Spi clock line init low DR=0; // Init DR for input AM=0; // Init AM for input CD=0; // Init CD for input PWR=1; // nRF905 power on TRX_CE=0; // Set nRF905 in standby mode TXEN=0; // set radio in Rx mode } Nrf905寄存器的配置 配置905寄存器的意思是,通过spi传输一个值,放入905的寄存器中,这个值可以让905传输数据时,产生各种你想要的效果,类似于你用手调节耳机音量,你的手就相当于配置耳机的寄存器。 那么我先给出主要需配置的寄存器然后再解释. 如下面这个程序段: unsigned char idata RFConf[11]= { 0x00, //配置命令// 0x4c, //CH_NO,配置频段在430MHZ 0x0c, //输出功率为10db,不重发,节电为正常模式 0x44, //地址宽度设置,为4字节 0x04,0x04, //接收发送有效数据长度为32字节 0xCC,0xCC,0xCC,0xCC, //接收地址 0x58, //CRC充许,8位CRC校验,外部时钟信号不使能,16M晶振 }; 0x00, //配置命令// 后面的讲解中会说,所以大家从第二个开始看。 CH_NO的意思如下,通过以下解释设置不同的值,可以让905工作在不同频段,这个需要的话再做详解,不需要,可以照搬默认值,或者程序。 CH_NO 9 和 HFREQ_PLL一起进行平率设置 (默认值= 001101100b = 108d). fRF = ( 422.4 + CH_NOd /10)*(1+HFREQ_PLLd) MHz 于是乎相关的就引出以下这个寄存器 HFREQ_ PLL 1 使 PLL工作于 433 或 868/915 MHz 模式 (默认值 = 0). '0' – 工作于 433MHz 频段 '1' –工作于868 or 915 MHz频段 在这里给出个表格,如需更改该值可以参照: 工作频率 HFREQ_PLL CH_NO 430.0 MHz [0] [001001100] 433.1 MHz [0] [001101011] 433.2 MHz [0] [001101100] 434.7 MHz [0] [001111011] 862.0 MHz [1] [001010110] 868.2 MHz [1] [001110101] 868.4 MHz [1] [001110110] 869.8 MHz [1] [001111101] 902.2 MHz [1] [100011111] 902.4 MHz [1] [100100000] 927.8 MHz [1] [110011111] 0x0c, //输出功率为10db,不重发,节电为正常模式 这里做下说明:我们拆分看看这段话。 @输出功率为10db @不重发 @节电为正常模式 输出功率为10db,这个对于的寄存器是: 如下表,二进制10db应该是 11 PA_PWR 2 输出功率(默认值 = 00). “00”-10dBm “01”-2dBm “10”+6dBm “11” +10dBm 不重发,针对的寄存器是: 不管怎么说,部分都不自动重发(一般情况),故 二进制是 0 AUTO_ RETRAN 1 如果TRX_CE 和 TXEN为高时,自动重发 (默认值 = 0). '0' – 不重发 '1' – 数据包重发 节电为正常模式,针对的寄存器是: 如下表,要是正常模式则二进制是 0 RX_RED_ PWR 1 接收方式节能,工作电流1.6mA.灵敏度降低 (默认值 = 0). '0' – 正常工作 '1' – 节能模式 那么如下结论: 输出功率为10db--------------------11 不重发-----------------------------0 节电为正常模式---------------------0 按顺序写则是:1100---》0000 1100———》0x0C 0x44, //地址宽度设置,为4字节 如下面两个表: 收地址宽度:4字节的2进制是 100 RX_AFW 3 接收地址宽度 (默认值 = 100). '001' – 1 byte RX 地址 '100' – 4 byte RX 地址 发地址宽度:4字节的2进制是 100 TX_AFW 3 发送地址宽度(default = 100) . '001' – 1 byte TX 地址 '100' – 4 byte TX地址 于是乎: 100并上100, 可认为是0100并上0100,可认为是4并上4,则可认为是0x44. 0x04,0x04, //接收发送有效数据长度为32字节 这条命令是我擅自更改的,更改前是2字节,如是0x04这是32字节。这样可以使905在一个数据包内传输更多信息。 那么我给出两个寄存器。 RX_PW 6 接收数据宽度(默认 = 100000). '000001' – 1 byte 接收数据宽度 '000010' – 2 byte 接收数据宽度 '100000' – 32 byte 接收数据宽度 TX_PW 6 发送数据宽度(默认 = 100000). '000001' – 1 byte 发送数据宽度 '000010' – 2 byte 发送数据宽度. '100000' – 32 byte 发送数据宽度 这里要把码补全,10 0000——》0010 0000——0x40这里实际是0x40一点没错但是程序中写的是0x04,仔细想想,也没什么特别的问题。这里我水平有限,不做说明了。 0xCC,0xCC,0xCC,0xCC, //接收地址 一看就知道,地址被从新改了下,默认地址是E7这种。 RX_ ADDRESS 32 发送地址标识,使用字节取决于 RX_AFW (默认值 = E7E7E7E7h). 0x58, //CRC充许,8位CRC校验,外部时钟信号不使能,16M晶振 CRC_EN 1 CRC 校验可用 (默认值 = 1). '0' – 不可用 '1' – 可用 CRC_ MODE 1 CRC 模式选择端 (默认值 = 1). '0' – 8 位 '1' – 16 位 UP_CLK_ EN 1 输出时钟可用 (默认值 = 1) '0' – 外面没有可用的时钟信号 '1' – 外面有可用的时钟信号 XOF 3 晶振频率端,必须与外部的晶振频率相对应(默认值 = 100). '000' – 4MHz '001' – 8MHz '010' – 12MHz '011' – 16MHz '100' – 20MHz 这块看着有点乱的话,请继续往后看。 我们既然把相关寄存器的配置解释了一边,但是如果对于一个编程序的人,或者程序开发来说,这样的罗列虽然我们能弄懂每个寄存器是咋回事,但是实际编程并自己配置寄存器的话,难度是很大的。幸好,开发手册解决一切问题,下面是一个表,表的后面我有解释。 寄存器内容 射频器配置寄存器(R/W) 字节 位内容[7:0],最高有效位[7] 初始值 0 CH_NO[7:0] 0110_1100 1 bit[7:6] not used, AUTO_RETRAN, RX_RED_PWR, PA_PWR[1:0], HFREQ_PLL, CH_NO[8] 0000_0000 2 bit[7] not used, TX_AFW[2:0] , bit[3] not used, RX_AFW[2:0] 0100_0100 3 bit[7:6] not used, RX_PW[5:0] 0010_0000 4 bit[7:6] not used, TX_PW[5:0] 0010_0000 5 RX_ADDRESS (device identity) byte 0 E7 6 RX_ADDRESS (device identity) byte 1 E7 7 RX_ADDRESS (device identity) byte 2 E7 8 RX_ADDRESS (device identity) byte 3 E7 9 CRC_MODE,CRC_EN, XOF[2:0], UP_CLK_EN, UP_CLK_FREQ[1:0] 1110_0111 解释:这是手册中的一张表,假设寄存器的配置值是如图给的这些。那么他的传输是从0字节开始到9字节截止,按顺序把16进制码传进去,你的工作就完成了。 而你需要对那个寄存器进行微小的改动,只需找到手册相关寄存器的说明进行改动就可以了。我们从上表中摘出一个小表看,小表如下: bit[7] not used, TX_AFW[2:0] , bit[3] not used, RX_AFW[2:0] 0100_0100 bit[7]就是该值得第七位,第七位没用上。 TX—AFW【2:0】意思是有三位被这个寄存器用了。 等等。。 通过这种字节的划分,将寄存器的配置变成了传多个2位十六进制数,使得寄存器的配置变得博大精深,新手上手困难。 不过对于驱动其他芯片也一样,配置寄存器就是这样配置的。像是某些器件如saa7113等芯片,配置寄存器时,前面还有地址,弄得更加复杂。 所以大家要通过学习nrf905了解芯片的驱动方法这才是关键。 spi通信: 如何实现spi通信,在这个问题上,如果说正常学习应该是,先知晓spi的协议,spi的时序,spi写和读的时序和协议。但是如果将其看成程序的话就比较方便。咱们用程序谈这件事情。 该程序段式spi的写程序: 从MOSI=(bit)(b & 0x80);我们分析下。假设b=abcdefgh 那b & 0x80就是abcdefgh&10000000可以想象a被提取了出来。 至于bit,其实可以没有,这里可以参考c51语言关于 ‘与‘ 有两个做法,一个是&,另一个是&&,即位与和整个值得与。 之后我们观察sck的变化,sck是spi的时钟,我们发现从0到1然后回到0,这是sck的变化。在sck变到1之前,mosi已经有了一个值,那么当sck=1;的时候,也就是所谓的上升沿,mosi被写入,这里可以认为是写入905的内部了。 b<<=1;这个语句等同于b=b《1;意思是b左移一位的新值付给b, 比如b=abcdefgh左移一位,那么b=bcdefgh0,再左移一位,那么b=cdefgh00,以此类推左移8次之后b=00000000. 因为每移出一位,就代表着移进一位,移进的是0。 那么观察一下0x80——》1000 0000 这个数1000 0000与上abcdefgh之后会提取出a 左移之后,再次进行与运算,就会提取出b,循环往复abcdefgh就都提取出来了。并在每一次都把这个值付给了mosi。 我想通过梳理,大家应该能看懂了,至于spi的通信协议,大家可以参考下网上资料,我想看懂了程序,再看看资料应该能彻底明白了。 void SpiWrite(uchar b) { uchar i=8; while(i--) { Delay(10); SCK=0; MOSI=(bit)(b & 0x80); b<<=1; Delay(10); SCK=1; Delay(10); SCK=0; } 读Spi程序段: _nop_();_nop_();是延时,延时是一个指令周期的时间。说白了,就是延时一段时间。 uchar ddata=0;这条语句意味着ddata是完全为0的。且注意一个事情ddata的定义是uchar,那么他的值最大能到0xff。 ddata|=MISO;等同于ddata=ddata|miso; miso是一个引脚的电平。经过这条语句后ddata的最低位就是miso当时的电平了,这时左移再次提取新的miso电平,当八个电平都提取之后,ddata的值就提取完成了。 同样sck是spi时钟,想提取下一个miso的值必须让时钟波动一次。uchar SpiRead(void) { uchar i=8; uchar ddata=0; while(i--) { ddata<<=1; SCK=0; _nop_();_nop_(); ddata|=MISO; SCK=1; _nop_();_nop_(); } SCK=0; return ddata; } 到此为止,905的基本问题讲完了。那么我们把它串联在一起。 我们先宏观的看下。以发送流程为例 905现处于待机spi编程状态——》向905中传送寄存器值 ————》@——》将905的状态设置为发送状态————》成功发出-----》待机状态 如果想再发个数据,那么他的流程将变成 @——》将905的状态设置为发送状态————》成功发出————》待机状态 由此看来对同一个对象进行发送,如果大家的设置都没改的话,寄存器的值只需设置一次。剩下的就是重复发送到待机这个环节了。 那么在@之前的问题,大家都了解了,剩下的就是@到把数据发送这一块了。这里我分为三个部分说这件事情。 向905传输一个命令 向905装入待发送的数据、 把数据发出去 向905传输命令: 这里定义了这些命令,先在语法上说下#define WC 0x00的意思等价于 wc=0x00; 那么我们先解释下这几个命令,大家理解下。 #define WC 0x00 #define RC 0x10 #define WTP 0x20 #define RTP 0x21 #define WTA 0x22 #define RTA 0x23 #define RRP 0x24 如下是wc的解释: 在讲解配置寄存器是有个值我没有讲解先在我告诉大家 指令名称 指令格式 操作 W_CONFIG (WC) 0000 AAAA 写配置寄存器 AAAA 指明哪个字节 。 写操作从哪个字节开始取决于地址AAAA unsigned char idata RFConf[11]= { 0x00, //配置命令// 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 0x00, //配置命令// 这就意味着wc=0x00,意思就是从0字节开始进行写操作。举个例子看下,0字节代表的是哪个寄存器, 寄存器内容 射频器配置寄存器(R/W) 字节 位内容[7:0],最高有效位[7] 初始值 0 CH_NO[7:0] 0110_1100 写之后的操作就是开始写1字节,这点上没什么问题最后写到第九字节。前面有这个 表的完整版,大家可以翻着看。 #define RC 0x10的解释如下:这个看表就知道不做解释了跟wc意思差不多。 R_CONFIG (RC) 0001 AAAA 读配置寄存器 AAAA 指明哪个字节 。 读操作从哪个字节开始取决于地址AAAA 后几个命令都好理解我就都列出来大家自己吸收下: 那么到此,命令部分就说完了,能用的就这几个命令。 向905装入待发送数据: 向905装入数据这件事情和刚才的命令结合着说。先看以下程序段: SpiWrite(WTP); // Write payload command for (i=0;i<4;i++) { SpiWrite(TxRxBuf[i]); // Write 32 bytes Tx data } 先传了wtp命令,之后把txrxbuf数组中的前4字节传了进去。 那么执行外这条之后,数据就被传到905中了(还没有进行发送)。观察此函数,发现调用了spiwrite这个函数,说明装入命令和装入数据都是通过spi通信进行传输的。 观察以下程序段: SpiWrite(WTA); // Write address command for (i=0;i<4;i++) // Write 4 bytes address { SpiWrite(TxAddress[i]); } TxAddress[i]和 SpiWrite(WTA); 是其中的要素,wta是写地址命令,那么TxAddress[i]就是地址咯,地址前面说过了,程序中除了那个config数组中有地址的说法,另外在程序段中被单独列出的地方是code TxAddress[4]={0xcc,0xcc,0xcc,0xcc}; 那么通过这个程序段,功能就是把地址写进去。 以上的两个程序段完成了数据和地址的写入,这时只要设置成发送状态,数据就可以被发出了。那么从现在开始是重点部分,就是以上两段程序的组合,并加以延伸。 仔细看下面这段程序,主要关注红字部分。程序之后有我的相关解释。 void TxPacket(uchar *TxRxBuf) { uchar i; //Config905(); CSN=0; SpiWrite(WTP); // Write payload command for (i=0;i<4;i++) { SpiWrite(TxRxBuf[i]); // Write 32 bytes Tx data }// Spi enable for write a spi command CSN=1; Delay(1); // Spi disable CSN=0; // Spi enable for write a spi command SpiWrite(WTA); // Write address command for (i=0;i<4;i++) // Write 4 bytes address { SpiWrite(TxAddress[i]); } CSN=1; // Spi disable TRX_CE=1; // Set TRX_CE high,start Tx data transmission Delay(1); // while (DR!=1); TRX_CE=0; // Set TRX_CE low } 上面这段程序,如果被main调用了之后,出现的效果是将txrxbuf数组中的数无线传输出去。也就是说这段程序看懂了,905就能发数了。我们看下,这个程序有两个大段的红色字体和两个小段。 大段的红色字体前面已经做过解释,小段的红色字体解释为。 在开头的几页提到过这个函数:void SetTxMode(void)其中有两个引脚的电平为:TRX_CE=0;TXEN=1;(PWR_UP默认高电平),且开头我给我一个表格,那么我把它再拿出来说下。 PWR_UP TRX_CE TX_EN 操作模式 0 X X 断电和SPI编程 1 0 X 待机和SPI编程 1 1 0 射频接收模式 1 1 1 射频发送模式 如此图发射模式,TRX_CE和TX_EN全为1是发送状态但是在void SetTxMode(void)中TRX_CE=0;所以他不属于发送也不属于接收状态,但只要TRX_CE=1;也就是全为1,那么就实现了发送状态,数据就被发送了。而在程序void TxPacket(uchar *TxRxBuf)就是上面那段大程序,中恰好两个小段红字阐明。所以小红字加上void SetTxMode(void)的完整功能就是将905从准备发射到发射的流程。 至于在大段程序中CSN的值,注释中已经写了spi是否有效的管脚。所以理解起来很容易,我就不解释了。 综上所述,如果把我上面所讲的所有东西全融合在一起,那么就是响当当的905发送数据的程序了。 而对于905接收程序而言也是一样,先要有相关命令,相关地址,并且用spi总线传输相关命令,并且用spi读取接收到的数据。这里我就不在多说什么了,因为跟发送的区别不大,下面我给出程序: 这个函数不像刚才的那个函数含有状态的设置,我后面会说的。那么程序的其他部分自己琢磨下。 void RxPacket(void) { uchar i; Delay(1); // TRX_CE=0; // Set nRF905 in standby mode Delay(100); TRX_CE=0; CSN=0; // Spi enable for write a spi command Delay(1); SpiWrite(RRP); for (i = 0 ;i < 4 ;i++) { TxRxBuf[i]=SpiRead(); // Read data and save to buffer } CSN=1; Delay(10); TRX_CE=1; } 那么这样就完成了发送和接收,但是我们目前面临着一个很有意义的问题,比如我现在发了一个数据,那么无线电波在空气中传播的速度是光速,我们可以认为对面的905瞬间得到了一个值,于是我们就将它读取。 但是当我们发送数据时,对面的905正由于mcu正在干别的事,或者用更通俗的话说,不是任何时候都能用接收函数接到数据,只有当数据被发送且传到另一个905上,另一个905接收到了,这时我们读接收到的数据才能读出来。 幸好存在一个功能,当接收到数据后,905的DR会产生电平变化。所以当dr产生变化的时候我们就开始提取数据,其他时候我们可以干别的。 所以就有以下这个函数: void RX(void) { SetRxMode(); // Set nRF905 in Rx mode while (CheckDR()==0); Delay(10); RxPacket(); } 我们看见三个子函数在其中,有两个我已经说过了,但是我还想再说一下,首先你想让905接收数据,那么必须先让其处于接收状态。 当接收到数据,那么dr会变化,当确实变化了我们才执行RxPacket();这个函数提取数据。 这个与发送函数的一个很大的区别在与状态设置,发送状态是一个很果断的状态,就像你想什么时候说话都可以,别人是否能听得见都可以说话。 但是接收状态就像你的耳朵一样,你不知道何时对方会说话,那么你就必须长期处于接收状态,否则你就听不到说话,除非你知道他什么时候会说话,听不到说话的含义就是数据丢失。 长期处于接收状态,并不是说单片机不能干别的事,而是说905不能干别的事。原因是单片机可以用中断的方式得到DR引脚的变化。 而这里为了程序的简单,我们直接用while等待也没什么问题,特此注明。如有中断需要,请自己改动。 那么如何使用这些函数,也就是宏观上程序如何控制905的,我们看下主函数: void main(void) { nRF905Init(); Config905(); while(1) { if(KEY0 ==0){TxRxBuf[0]=0x11;} if(KEY0 ==1){TxRxBuf[0]=0x22;} if(KEY1 ==0){TxRxBuf[1]=0x11;} if(KEY1 ==1){TxRxBuf[1]=0x22;} SetTxMode();// Set nRF905 in Tx mode TxPacket(TxRxBuf);// Send data by nRF905 } } 前面的两个红字子函数是905的初始化和寄存器配置。 而在while内部有两个红字子函数,这两个函数就实现了数据的发送。而while中的if语句中key是按键,当key的按下与不按都会使TXRXBUF数组的前两个字节产生一组值,这里你可以认为咱们向TXRXBUF中写入了数据。 那么这样,并在外面加了while语句就实现了循环发送按键值的功能。 那么我们再看下接收端的主函数是什么样子。接收部分只有三个子函数,且我都讲过了相关功能,这里就不赘述了。 void main(void) { nRF905Init(); Config905(); while(1) { RX(); if(TxRxBuf[0]==0x22){in1=1;in2=1;} if(TxRxBuf[0]==0x33){in1=1;in2=0;} if(TxRxBuf[1]==0x22){in3=1;in4=1;} if(TxRxBuf[1]==0x33){in3=1;in4=0;} } } 上面所讲的一切就能实现905的收发了,那我把示例给大家。 其中子函数在收发程序中都是编写的一模一样所以我就给出一份接收函数,如想得到接收函数,把刚才讲的发送主函数粘上就成了。 #include #include #include #include //---------------------------------------------------------------------------------------------------------------- #define uint unsigned int #define uchar unsigned char //---------------------------------------------------------------------------------------------------------------- #define BYTE_BIT0 0x01 #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_BIT7 0x80 //---------------------------------------------------------------------------------------------------------------- bdata unsigned char DATA_BUF; #define DATA7 ((DATA_BUF&BYTE_BIT7) != 0) #define DATA0 ((DATA_BUF&BYTE_BIT0) != 0) sbit flag =DATA_BUF^7; sbit flag1 =DATA_BUF^0; //---------------------------------------------------发送数据缓冲区------------------------------------------------- #define TxRxBuf_Len 4 unsigned char TxRxBuf[TxRxBuf_Len]= { 0x29,0x30,0x31,0x32, }; //----------------------------------------------NRF905控制IO------------------------------------------------------ sbit TXEN=P1^0; sbit TRX_CE=P3^2; sbit PWR=P1^1; //----------------------------------------------NRF905 SPI接口--------------------------------------------------- sbit MISO=P1^6; sbit MOSI=P1^5; sbit SCK=P1^7; sbit CSN=P1^3; //----------------------------------------nrf905状态标志--------------------------------------------------------- sbit AM=P1^4; sbit DR=P3^3; sbit CD=P1^2; //--------------------------------------------------------------------------------------------------------------- sbit in4=P3^4; sbit in3=P3^5; sbit in2=P3^6; sbit in1=P3^7; //-------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------nrf905控制指令------------------------------------------- #define WC 0x00 #define RC 0x10 #define WTP 0x20 #define RTP 0x21 #define WTA 0x22 #define RTA 0x23 #define RRP 0x24 //------------------------------------------------NRF905寄存器配置------------------------------------------------ unsigned char idata RFConf[11]= { 0x00, //配置命令// 0x4c, //CH_NO,配置频段在430MHZ 0x0c, //输出功率为10db,不重发,节电为正常模式 0x44, //地址宽度设置,为4字节 0x04,0x04, //接收发送有效数据长度为32字节 0xCC,0xCC,0xCC,0xCC, //接收地址 0x58, //CRC充许,8位CRC校验,外部时钟信号不使能,16M晶振 }; code TxAddress[4]={0xcc,0xcc,0xcc,0xcc}; char tf; //------------------------------------------------延时------------------------------------------------------------ static void Delay(uchar n) { uint i; while(n--) for(i=0;i<80;i++); } //---------------------------------------------------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写函数---------------------------------------------------------- void SpiWrite(unsigned char send) { unsigned char i; DATA_BUF=send; for (i=0;i<8;i++) { if (DATA7) //总是发送最高位 { MOSI=1; } 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=0; // Init DR for input AM=0; // Init AM for input CD=0; // Init CD for input PWR=1; // nRF905 power on TRX_CE=0; // Set nRF905 in standby mode TXEN=0; // set radio in Rx mode } //-----------------------------------------------------初始化寄存器----------------------------------------------- void Config905(void) { uchar i; CSN=0; // Spi enable for write a spi command //SpiWrite(WC); // Write config command写放配置命令 for (i=0;i<11;i++) // Write configration words 写放配置字 { SpiWrite(RFConf[i]); } CSN=1; // Disable Spi } //-----------------------------------------------------发送数据打包--------------------------------------------------- void TxPacket(uchar *TxRxBuf) { uchar i; //Config905(); CSN=0; SpiWrite(WTP); // Write payload command for (i=0;i<4;i++) { SpiWrite(TxRxBuf[i]); // Write 32 bytes Tx data }// Spi enable for write a spi command CSN=1; Delay(1); // Spi disable CSN=0; // Spi enable for write a spi command SpiWrite(WTA); // Write address command for (i=0;i<4;i++) // Write 4 bytes address { SpiWrite(TxAddress[i]); } CSN=1; // Spi disable TRX_CE=1; // Set TRX_CE high,start Tx data transmission Delay(1); // while (DR!=1); TRX_CE=0; // Set TRX_CE low } //----------------------------------------------------------设置发送状态--------------------------------------------- void SetTxMode(void) { TRX_CE=0; TXEN=1; Delay(1); // delay for mode change(>=650us) } //-----------------------------------------------设置发送状态--------------------------------------------------- void SetRxMode(void) { TXEN=0; TRX_CE=1; Delay(1); // delay for mode change(>=650us) } //-------------------------------------------------判断数据接收状态----------------------------------------------------- unsigned char CheckDR(void) //检查是否有新数据传入 Data Ready { if (DR=1&&TRX_CE==1 && TXEN==0) { // Delay(50) ; return 1; } else { return 0; } } //----------------------------------------------------读NRF905接收数据------------------------------------------------------------ void RxPacket(void) { uchar i; Delay(1); // TRX_CE=0; // Set nRF905 in standby mode Delay(100); TRX_CE=0; CSN=0; // Spi enable for write a spi command Delay(1); SpiWrite(RRP); for (i = 0 ;i < 4 ;i++) { TxRxBuf[i]=SpiRead(); // Read data and save to buffer } CSN=1; Delay(10); TRX_CE=1; } //--------------------------------------------------------数据接收------------------------------------------------ void RX(void) { SetRxMode(); // Set nRF905 in Rx mode while (CheckDR()==0); Delay(10); RxPacket(); } //----------------------------------------------------------------------------------------------------------------- void main(void) { nRF905Init(); Config905(); while(1) { RX(); if(TxRxBuf[0]==0x22){in1=1;in2=1;} if(TxRxBuf[0]==0x33){in1=1;in2=0;} if(TxRxBuf[1]==0x22){in3=1;in4=1;} if(TxRxBuf[1]==0x33){in3=1;in4=0;} } } --------------------------------------------- 硬件的连接方法: 对于芯片来说电平是有很多种的有5V也有3.3V也有别的比如2.85V。 对于单片机来说,单片机电平有5V的也有3.3V的。如果是3.3V的单片机那么就可以直接与NRf905模块相连接了。如果是5V的单片机一般我采用两种方式解决这个问题: 我们知道905是可以接受3,3V电平的,那么我们可以用在5V单片机io串联电阻接在905io的方式使得实现安全通信。 我们也可以在单片机容许范围内降低单片机供电电压比如用3.3V给单片机供电。比如STC89C52RC单片机,在进行下载程序时候,如果用3,3V供电则会造成下载中断或者不稳定的下载。而3.3V此单片机可以稳定工作,这在STC手册上有据可查,所以我们可以用降低供电电压的方式实现单片机io直接与905相连。通常我在调试FPGA时候也是采用直接用FPGA板上给的3.3V电源进行调试,明显要比在单片机IO焊接电阻,然后再接FPGA简单,更利于调试。 最有教育意义905连接方式: 我们经常连接一些模拟器件,但是我们不能用芯片直接接电机,不能用芯片接电灯,我们虽然见到单片机可以接LED,但这也是小功率led。如果是FPGA,可能容许这种灌电流的值更为严谨了,所以从通用性上说单片机接灯是不太好的。 同样5V直接跟3.3V连接也是不好的,假设你用的是一款电平为2.85V的FPGA想控制L298电机芯片,结果逻辑电平最少也要4.5V,于是乎你就会陷入尴尬。于是很多人想到光耦,但仔细想来还是很尴尬。 所以说,能在不同电压中灵活实现通信很有意义,单纯的串接电阻,降低电压只能是土办法,应该用电平转换芯片,像是MAX232这种芯片。那么5V和3.3V电平转换芯片其实是很多的。 那么我推荐一款芯片,74LVX4245.此芯片IO较少应用简单,美中不足是个贴片的,不过相信大家在得到此款芯片的帮助下,能让大家的设计更为合理。 串接电阻的905图: 如上图是905的接线图,这个图是完全正确的,大家可以放心。这里要说的是: 如上图3.3V电压如何得到,这里比较常用的是AMS1117系列的3.3V稳压器,该稳压器可以从大于4V的电压稳定得到3.3V,可以将小于20V的电压稳压到3.3V,大于20V,稳压器将处于闭锁(就是不工作的意思)。 那么此类稳压器的封装为: 这是贴片的封装,具体可以自己查阅资料得知。 稳压器是很有用的东西可以搭建稳压电路和恒流源可以实现各类功能,也可以做小功率变压器,我曾经拆过变压器,除了线圈和整流桥和电容就剩他了,所以稳压器是很好的东西。 那么ams1117稳压器的实验接法是: 该图引脚标号与上图pcb封装引脚标号一一对应。图中两个电容的大小具体可按0.3u和0.1u,不过一般情况就接两个小电容就可以,甚至不接也可以(不推荐)。 希望大家像牛蛙一样学习,虽然我不知道牛蛙是怎么学习的。

Top_arrow
回到顶部
EEWORLD下载中心所有资源均来自网友分享,如有侵权,请发送举报邮件到客服邮箱bbs_service@eeworld.com.cn 或通过站内短信息或QQ:273568022联系管理员 高进,我们会尽快处理。