又是因为比赛的需要,所以用起了这个无线模块,模块也是因为凑巧在同学那里搞来的,他说他现在不用,所以我就拿来主义,练练手嘛!昨天下午开始研究了,昨晚测试的时候发现有些问题,今天上午有课,没办法,值得下午来调试调试了。所幸调试通过了,谢天谢地~~~~好的,既然这样,那么就总结一下这个无线模块需要注意的地方吧,毕竟以后可是能够拿来做遥控汽车的 嘿嘿。。。。
首先给大家传一份技术参考手册,这是中文的,但是有些地方如果你觉得有问题,那么请对照这英文手册看,我就是这样干的!
地址在这里
一、硬件
nrf905这个芯片我们就不谈了,涉及到高频、射频是比较复杂,主要针对如何使用这个模块谈一谈
这是涉及到我们编程的引脚图, 其中uclk在这里不用,下面请看管脚图
总结一下这个管脚表的比较重要的信息如下:
1、nrf905和单片机通信使用的是SPI协议,我这里用的是软件模拟spi,硬件spi有其他用途
2、电源3.3V没有问题,IO口电压完全兼容匹配,输出电流也是没有问题的
3、CD是载波检测信号,意思是当我们的模块作为接收的时候,一旦它接收到发射模块同一个频段的信号时,该引脚会被nrf905置高,平常为低!
4、AM是地址匹配的意思,当作为接收模块的时候,当接收地址和发射地址匹配的时候,那么该引脚会被nrf905置高,平常为低!
5、DR表示数据接收或者发送成功!当一个正确的数据包接收完毕, RF905自动移去字校验位,然后把DR引脚置高,平常为低!
注意了:CD、AM、DR3个引脚的状态在我们调试的时候是非常重要的,所以充分利用这几个引脚的功能!
硬件方面需要注意的地方我们已经说完了,接下来分析我的程序!
二、软件
首先是发送流程:
1、当微控制器有数据要发送时,通过SPI协议将地址和要发送的数据送传给RF905,SPI接口的速率在通信协议和器件配置时确定;
2、微控制器置高TRX_CE和TX_EN,这就设置为了发送数据模式
3、RF905发送流程:
(1) 射频寄存器自动开启;
(2) 数据打包(加字头和CRC校验
(3) 发送数据包;
(4) 当数据发送完成,
(1)(2)俩步自动完成!
4、AUTO_RETRAN被置高,RF905不断重发,直到TRX_
5、当TRX_CE被置低,RF905发送过程完成,自动进入空闲模式。
注意:ShockBurstTM工作模式保证,一旦发送数据的过程开始,无论
TRX_EN和TX_EN引脚是高或低,发送过程都会被处理完。只有在前一
个数据包被发送完毕,RF905才能接受下一个发送数据包。
然后是接收流程:
1、当 TRX_CE 为高、TX_EN 为低时,RF905 进入 ShockBurstTM 接收模式;
2、650us 后,RF905 不断监测,等待接收数据;
3、当 RF905 检测到同一 频段的载波时,载波检测引脚被置高;
4、当接收到一个相匹配的地址,AM 引脚被置高;
5、当一个正确的数据包接收完毕, RF905 自动移去字头、地址和 CRC校验位,然后把 DR 引脚置高
6、微控制器把 TRX_CE 置低,nRF905 进入空闲模式;
7、 微控制器通过 SPI 口,以一定的速率把数据移到微控制器内;
8、 当所有的数据接收完毕,nRF905 把 DR 引脚和 AM 引脚置低;
9、nRF905 此时可以进入 ShockBu rstTM 接收模式、ShockBurstTM 发送模式或关机模式。 当正在接收一个数据包时,TRX_CE 或 TX_EN 引脚的状态发生改变,
RF905 立即把其工作模式改变,数据包则丢失。
需要注意的是:我们设置接收模式的时候,是需要先把数据写入nrf905模块,然后才使能发送引脚的!!!
接下来就是拿具体的程序来分析了
A.首先看我的主函数,很简单
int main(void) { //初始化系统定时器 SysTick_Init(); USART1_Config(); NRF905_Init(); NRF905_Config(); SetTxMode(); while(1) { TxPacket(TxBuf); Delay_ms(2000); } }注意这个模式
B.然后就是这个配置NRF905的函数,这个可是关键啊,具体数据的选择,最好是能看一下芯片手册,这里我把代码贴出来
/************************************************************************************** * 名 称: NRF905_Init * 功 能: 配置NRF905寄存器 * 参 数: 无 * 调用方式:NRF905_Init(); * 返 回 值:无 **************************************************************************************/ void NRF905_Config(void) { u8 i; NRF905_CSN_L; // Spi enable for write a spi command NRF905_SPI_Write_Byte(WC); // Write config command写放配置命令 for (i = 0;i < RxTxConf.n; i++) // Write configration words 写放配置字 { NRF905_SPI_Write_Byte(RxTxConf.buf[i]); } for(i = 0; i < 10; i ++) { printf(" %x \r",RxTxConf.buf[i]); } NRF905_CSN_H; // Disable Spi }其实简单来说给它发的内容就是:
/************************************************************************************** * 名 称: 配置寄存器中的内容! **************************************************************************************/ RFConfig RxTxConf ={10, 0x4c, //CH_NO[7:0] 连同字节 1 的 CH_NO[8]和 HFREQ_PLL 控制 905 的载波频段 433MHZ 0x0c, //7,6:no use;5:自动重发=0;4:接收灵敏度降低=0;3,2:发射功率=10dBm;1:433MHz;CH_NO[8]=0 0x44, //7:no use;6,5,4:发送地址宽度4;3:no use;2,1,0:接收地址宽度4 0x20, //7,6 no use;5-0:接收数据宽度32 0x20, //7,6 no use;5-0:发送数据宽度32 0xcc, //接收地址字节0(设备ID) 0xcc, //接收地址字节1(设备ID) 0xcc, //接收地址字节2(设备ID) 0xcc, //接收地址字节3(设备ID) 0x58 //7:CRC模式8位;6:CRC校验许可;5,4,3:011表示16MHz;2:UP_CLK_EN=0没有外部时钟;1,0:外部时钟频率 };这就是根据寄存器的格式含义来的,其实也没有什么好说的,但是我总是怀疑芯片手册上有些问题,那个应该是433.0MHZ
C.接下来看我们是怎么实现发送数据的吧,我把我的代码贴出来,然后对着分析就好了。
/************************************************************************************** * 名 称: TxPacket * 功 能: 发送数据包 * 参 数: 需要发送的内容 * 调用方式:TxPacket(); * 返 回 值: MISO的值 **************************************************************************************/ void TxPacket(u8 *TxBuf) { u8 i; NRF905_CSN_L; // 使能SPI NRF905_SPI_Write_Byte(WTP); // Write payload command for (i=0;i<32;i++) { NRF905_SPI_Write_Byte(TxBuf[i]); // Write 32 bytes Tx data } for(i = 0; i < 32;i ++) { printf("%x \r",TxBuf[i]); } NRF905_CSN_H; // Spi disable Delay_us(2); NRF905_CSN_L; // Spi enable for write a spi command NRF905_SPI_Write_Byte(WTA); // Write address command for (i=0;i<4;i++) // Write 4 bytes address { NRF905_SPI_Write_Byte(RxTxConf.buf[i+5]); } for(i = 0; i < 4;i ++) { printf("%x \r",RxTxConf.buf[i+5]); } NRF905_CSN_H; // Spi disable NRF905_TRX_CE_H; // Set TRX_CE high,start Tx data transmission 先写数据然后开启发送 Delay_ms(100); //记得给反应时间 while(!DR_read()) { printf("DATA Send failed!!!\n"); } printf("DATA send success !!!\n"); NRF905_TRX_CE_L; // Set TRX_CE low }
大家可能好奇之前的模式设置是不是出问题了,心想,当设置为发送的时候应该是两个都选择为高啊,但是这里要告诉你,传输数据的时候,我是这样理解的,选择模式的时候
先将EN失能。然后当我们通过SPI写入了数据之后才使用这句
NRF905_TRX_CE_H; // Set TRX_CE high,start Tx data transmission 先写数据然后开启发送这样就是为了消除干扰吧,我是这样想的。
D.最后贴上部分驱动函数
/**************************************************************************************
* 名 称: SetTxMode
* 功 能: 设置NRF905发送状态
* 参 数: 无
* 调用方式:SetTxMode();
* 返 回 值:无
**************************************************************************************/
void SetTxMode(void)
{
NRF905_TX_EN_H;
NRF905_TRX_CE_L; //为什么置低,先写数据后发送,所以要后拉高
Delay_us(650);
}
/**************************************************************************************
* 名 称: SetRxMode
* 功 能: 设置NRF905发送状态
* 参 数: 无
* 调用方式:SetRxMode();
* 返 回 值:无
**************************************************************************************/
void SetRxMode(void)
{
NRF905_TX_EN_L;
NRF905_TRX_CE_H;
Delay_us(650);
}
/**************************************************************************************
* 名 称: CD_read
* 功 能: 读取CD的值(载波检测)
* 参 数: 无
* 调用方式:CD_read();
* 返 回 值: CD的值
**************************************************************************************/
u8 CD_read(void)
{
u8 ReadValue;
ReadValue = NRF905_CD_DATA;
return ReadValue;
}
/**************************************************************************************
* 名 称: AM_read
* 功 能: 读取AM的值(地址匹配AM)
* 参 数: 无
* 调用方式:AM_read();
* 返 回 值: AM的值
**************************************************************************************/
u8 AM_read(void)
{
u8 ReadValue;
ReadValue = NRF905_AM_DATA;
return ReadValue;
}
/**************************************************************************************
* 名 称: DR_read
* 功 能: 读取CD的值(数据就绪DR)
* 参 数: 无
* 调用方式:DR_read();
* 返 回 值: DR的值
**************************************************************************************/
u8 DR_read(void)
{
u8 ReadValue;
ReadValue = NRF905_DR_DATA;
return ReadValue;
}
/**************************************************************************************
* 名 称: NRF905_SPI_Read_Bit
* 功 能: 读取MISO的值
* 参 数: 无
* 调用方式:NRF905_SPI_Read_Bit();
* 返 回 值: MISO的值,0或者1
**************************************************************************************/
static u8 NRF905_SPI_Read_Bit(void)
{
u8 ReadValue;
ReadValue = NRF905_MISO_DATA;
return ReadValue;
}
/**************************************************************************************
* 名 称: NRF905_SPI_Read_Byte
* 功 能: 读取MISO的值,字节的形式
* 参 数: 无
* 调用方式:NRF905_SPI_Read_Byte();
* 返 回 值: MISO的值
**************************************************************************************/
u8 NRF905_SPI_Read_Byte(void)
{
u8 i;
u8 Temp_data;
for (i = 0;i < 8;i ++) // Setup byte circulation bits
{
Temp_data = Temp_data << 1; // Right shift Temp_data
NRF905_SCK_H; // Set clock line high
if (NRF905_SPI_Read_Bit())
{
Temp_data |= 0x01; // Read data
}
NRF905_SCK_L; // Set clock line low
}
return Temp_data; // Return function parameter
}
/**************************************************************************************
* 名 称: NRF905_SPI_Write_Byte
* 功 能: NRF905写1字节函数
* 参 数: DATA: 需要写入的字节
* 调用方式:NRF905_SPI_Write_Byte(0xaa);
* 返 回 值:无
**************************************************************************************/
void NRF905_SPI_Write_Byte(u8 DATA)
{
u8 i,Temp_data;
Temp_data = DATA;
for (i=0;i<8;i++)
{
if (Temp_data & 0x80) //总是发送最高位
{
NRF905_MOSI_H;
}
else
{
NRF905_MOSI_L;
}
NRF905_SCK_H;
Temp_data=Temp_data<<1;
NRF905_SCK_L;
}
}
需要程序的可以留言.....