论坛元老
- 积分
- 4562
- 金钱
- 4562
- 注册时间
- 2010-12-14
- 在线时间
- 32 小时
|
<h1 style="text-align:center;">
<a name="_Toc402209403"></a>第三十八章 无线通信实验<span></span>
</h1>
<p class="a" style="text-indent:21.1pt;">
<b><span>
<div style="background-color:#E8E8E8;">
[mw_shl_code=c,true]
1.硬件平台:正点原子探索者STM32F407开发板
2.软件平台:MDK5.1
3.固件库版本:V1.4.0
[/mw_shl_code]
</div>
<br />
</span></b>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>ALIENTKE</span>探索者<span>STM32F4</span>开发板带有一个<span>2.4G</span>无线模块(<span>NRF24L01</span>模块)通信接口,采用<span>8</span>脚插针方式与开发板连接。本章我们将以<span>NRF24L01</span>模块为例向大家介绍如何在<span>ALIENTEK</span>探索者<span>STM32F4</span>开发板上实现无线通信。在本章中,我们将使用两块探索者<span>STM32F4</span>开发板,一块用于发送收据,另外一块用于接收,从而实现无线数据传输。本章分为如下几个部分:<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>38.1 NRF24L01</span>无线模块简介<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>38.2 </span>硬件设计<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>38.3 </span>软件设计<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>38.4 </span>下载验证<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span> </span>
</p>
<h2 style="margin-left:0cm;">
<a name="_Toc402209404"></a><span>38.1 NRF</span><span>24L</span><span>01</span>无线模块简介<span></span>
</h2>
<p class="a" style="text-indent:21.0pt;">
<span>NRF24L01</span>无线模块,采用的芯片是<span>NRF24L01</span>,该芯片的主要特点如下:<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>1</span>)<span>2.4G</span>全球开放的<span>ISM</span>频段,免许可证使用。<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>2</span>)最高工作速率<span>2Mbps</span>,高校的<span>GFSK</span>调制,抗干扰能力强。<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>3</span>)<span>125</span>个可选的频道,满足多点通信和调频通信的需要。<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>4</span>)内置<span>CRC</span>检错和点对多点的通信地址控制。<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>5</span>)低工作电压(<span>1.9~3.6V</span>)。<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>6</span>)可设置自动应答,确保数据可靠传输。<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
该芯片通过<span>SPI</span>与外部<span>MCU</span>通信,最大的<span>SPI</span>速度可以达到<span>10Mhz</span>。本章我们用到的模块是深圳云佳科技生产的<span>NRF24L01</span>,该模块已经被很多公司大量使用,成熟度和稳定性都是相当不错的。该模块的外形和引脚图如图<span>38.1.1</span>所示:<br />
<div style="text-align:center;">
<span style="line-height:1.5;"></span>
</div>
<span></span>
</p>
<p class="a" align="center" style="text-align:center;text-indent:0cm;">
<span></span>
</p>
<p class="a" align="center" style="text-align:center;text-indent:9.95pt;">
图<span>38.1.1
NRF24L01</span>无线模块外观引脚图<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
模块<span>VCC</span>脚的电压范围为<span>1.9~3.6V</span>,建议不要超过<span>3.6V</span>,否则可能烧坏模块,一般用<span>3.3V</span>电压比较合适。除了<span>VCC</span>和<span>GND</span>脚,其他引脚都可以和<span>5V</span>单片机的<span>IO</span>口直连,正是因为其兼容<span>5V</span>单片机的<span>IO</span>,故使用上具有很大优势。<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
关于<span>NRF24L01</span>的详细介绍,请参考<span>NRF24L01</span>的技术手册。<span></span>
</p>
<h2 style="margin-left:0cm;">
<a name="_Toc402209405"></a><span>38.2 </span>硬件设计<span> </span><span></span>
</h2>
<p class="a" style="text-indent:21.0pt;">
本章实验功能简介:开机的时候先检测NRF24L01模块是否存在,在检测到NRF24L01模块之后,根据KEY0和KEY1的设置来决定模块的工作模式,在设定好工作模式之后,就会不停的发送/接收数据,同样用DS0来指示程序正在运行。<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
所要用到的硬件资源如下:<span></span>
</p>
<p class="a" style="margin-left:39.0pt;text-indent:-18.0pt;">
<span>1) </span>指示灯<span>DS0 </span>
</p>
<p class="a" style="margin-left:39.0pt;text-indent:-18.0pt;">
<span>2) </span><span>KEY0</span>和<span>KEY1</span>按键<span></span>
</p>
<p class="a" style="margin-left:39.0pt;text-indent:-18.0pt;">
3) <span>TFTLCD</span>模块
</p>
<p class="a" style="margin-left:39.0pt;text-indent:-18.0pt;">
<span>4) </span><span>NRF24L01</span>模块<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
<span>NRF24L01</span>模块属于外部模块,这里我们仅介绍开发板上<span>NRF24L01</span>模块接口和<span>STM32F4</span>的连接情况,他们的连接关系如图<span>38.2.1</span>所示:<span></span>
</p>
<p class="a" style="text-align:center;text-indent:0cm;">
<span>
</span>
</p>
<p class="a" align="center" style="text-align:center;text-indent:0cm;">
图<span>38.2.1
NRF24L01</span>模块接口与<span>STM32F4</span>连接原理图 <span></span>
</p>
<p class="Default">
这里NRF24L01也是使用的SPI1,和W25Q128共用一个SPI接口,所以在使用的时候,他们分时复用SPI1。本章我们需要把W25Q128的片选信号置高,以防止这个器件对NRF24L01的通信造成干扰。另外,NRF_IRQ和RS485_RE共用了PG8,所以,他们不能同时使用,不过我们一般用不到NRF_IRQ这个信号,因此,RS485和NRF一般也可以同时使用。
</p>
<p class="Default" style="text-indent:21.0pt;">
由于无线通信实验是双向的,所以至少要有两个模块同时能工作,这里我们使用2套ALIENTEK探索者STM32F4开发板来向大家演示。
</p>
<h2 style="margin-left:0cm;">
<a name="_Toc402209406"></a><span>38.3 </span>软件设计<span></span>
</h2>
<p class="a" style="text-indent:21.0pt;">
打开本章实验工程可以看到,我们在工程中添加了<span>spi</span>底层驱动函数,因为<span>NRF24L01</span>是<span>SPI</span>通信接口。同时,我们增加了<span>24l01.c</span>源文件以及包含了对应的头文件用来编写<span>NRF24L01</span>底层驱动函数。<span></span>
</p>
<p class="a0">
打开<span>24l01.c</span>文件,代码如下:<span></span>
</p>
<p class="a0">
const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01};
//发送地址<span></span>
</p>
<p class="a0">
const u8
RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址<span></span>
</p>
<p class="a0">
void NRF24L01_SPI_Init(void)
</p>
<p class="a0">
{
</p>
<p class="a0">
</p>
<p class="a0">
SPI_InitTypeDef SPI_InitStructure;
</p>
<p class="a0">
SPI_Cmd(SPI1,
DISABLE); //失能<span>SPI</span>外设<span></span>
</p>
<p class="a0">
SPI_InitStructure.SPI_Direction
= SPI_Direction_2Lines_FullDuplex; //全双工<span></span>
</p>
<p class="a0">
SPI_InitStructure.SPI_Mode
= SPI_Mode_Master; //工作模式:主<span>SPI</span>
</p>
<p class="a0">
SPI_InitStructure.SPI_DataSize
= SPI_DataSize_8b;// 8位帧结构<span></span>
</p>
<p class="a0">
SPI_InitStructure.SPI_CPOL
= SPI_CPOL_Low; //空闲状态为低电平<span></span>
</p>
<p class="a0">
SPI_InitStructure.SPI_CPHA
= SPI_CPHA_1Edge;//第<span>1</span>个跳变沿数据被采样<span></span>
</p>
<p class="a0">
SPI_InitStructure.SPI_NSS
= SPI_NSS_Soft; //NSS信号软件管理<span></span>
</p>
<p class="a0">
SPI_InitStructure.SPI_BaudRatePrescaler
= SPI_BaudRatePrescaler_256;
</p>
<p class="a0" style="text-indent:273.0pt;">
//预分频值为<span>256</span>
</p>
<p class="a0">
SPI_InitStructure.SPI_FirstBit
= SPI_FirstBit_MSB;//数据传输从<span>MSB</span>位开始<span></span>
</p>
<p class="a0">
SPI_InitStructure.SPI_CRCPolynomial
= 7; //CRC值计算的多项式<span></span>
</p>
<p class="a0">
SPI_Init(SPI1,
&SPI_InitStructure); //初始化外设<span>SPIx</span>寄存器<span></span>
</p>
<p class="a0">
</p>
<p class="a0">
SPI_Cmd(SPI1,
ENABLE); //使能<span>SPI</span>外设<span></span>
</p>
<p class="a0">
}
</p>
<p class="a0">
</p>
<p class="a0">
//初始化<span>24L01</span>的<span>IO</span>口<span></span>
</p>
<p class="a0">
void NRF24L01_Init(void)
</p>
<p class="a0">
{
</p>
<p class="a0" style="text-indent:31.5pt;">
GPIO_InitTypeDef
GPIO_InitStructure;
</p>
<p class="a0" style="text-indent:31.5pt;">
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOG,
</p>
<p class="a0" style="text-indent:231.0pt;">
ENABLE);//使能<span>GPIOB,G</span>时钟<span></span>
</p>
<p class="a0">
//GPIOB14初始化设置<span>:</span>推挽输出<span></span>
</p>
<p class="a0">
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
</p>
<p class="a0">
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式<span></span>
</p>
<p class="a0">
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出<span></span>
</p>
<p class="a0">
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
</p>
<p class="a0">
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉<span></span>
</p>
<p class="a0">
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化<span>B14</span>
</p>
<p class="a0" style="text-indent:26.25pt;">
//GPIOG6,7推挽输出<span></span>
</p>
<p class="a0">
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
</p>
<p class="a0">
GPIO_InitStructure.GPIO_Mode
= GPIO_Mode_OUT;//普通输出模式<span></span>
</p>
<p class="a0">
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出<span></span>
</p>
<p class="a0">
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
</p>
<p class="a0">
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉<span></span>
</p>
<p class="a0">
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化<span>G6,7</span>
</p>
<p class="a0" style="text-indent:26.25pt;">
//GPIOG.8上拉输入<span></span>
</p>
<p class="a0" style="text-indent:31.5pt;">
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
</p>
<p class="a0">
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入<span></span>
</p>
<p class="a0">
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉<span></span>
</p>
<p class="a0">
GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化<span>G8</span>
</p>
<p class="a0">
</p>
<p class="a0">
GPIO_SetBits(GPIOB,GPIO_Pin_14);//PB14输出<span>1,</span>防止<span>SPI FLASH</span>干扰<span>NRF</span>的通信 <span></span>
</p>
<p class="a0">
</p>
<p class="a0">
SPI1_Init(); //初始化<span>SPI1 </span>
</p>
<p class="a0" style="text-indent:31.5pt;">
NRF24L01_SPI_Init();//针对<span>NRF</span>的特点修改<span>SPI</span>的设置<span></span>
</p>
<p class="a0" style="text-indent:31.5pt;">
NRF24L01_CE=0; //使能<span>24L01</span>
</p>
<p class="a0" style="text-indent:31.5pt;">
NRF24L01_CSN=1; //SPI片选取消<span> </span>
</p>
<p class="a0">
}
</p>
<p class="a0">
//检测<span>24L01</span>是否存在<span></span>
</p>
<p class="a0">
//返回值<span>:0</span>,成功<span>;1</span>,失败<span> </span>
</p>
<p class="a0">
u8 NRF24L01_Check(void)
</p>
<p class="a0">
{
</p>
<p class="a0">
u8
buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
</p>
<p class="a0">
u8 i;
</p>
<p class="a0">
SPI1_SetSpeed(SPI_BaudRatePrescaler_8);
</p>
<p class="a0">
NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,buf,5);//写入<span>5</span>个字节的地址<span>. </span>
</p>
<p class="a0">
NRF24L01_Read_Buf(TX_ADDR,buf,5);
//读出写入的地址<span> </span>
</p>
<p class="a0">
for(i=0;i<5;i++)if(buf!=0XA5)break;
</p>
<p class="a0">
if(i!=5)return
1;//检测<span>24L01</span>错误<span> </span>
</p>
<p class="a0">
return 0; //检测到<span>24L01</span>
</p>
<p class="a0">
}
</p>
<p class="a0">
//SPI写寄存器<span></span>
</p>
<p class="a0">
//reg:指定寄存器地址<span></span>
</p>
<p class="a0">
//value:写入的值<span></span>
</p>
<p class="a0">
u8 NRF24L01_Write_Reg(u8 reg,u8 value)
</p>
<p class="a0">
{
</p>
<p class="a0">
u8 status;
</p>
<p class="a0">
NRF24L01_CSN=0; //使能<span>SPI</span>传输<span></span>
</p>
<p class="a0">
status
=SPI1_ReadWriteByte(reg);//发送寄存器号 <span></span>
</p>
<p class="a0">
SPI1_ReadWriteByte(value); //写入寄存器的值<span></span>
</p>
<p class="a0">
NRF24L01_CSN=1; //禁止<span>SPI</span>传输<span> </span>
</p>
<p class="a0">
return(status); //返回状态值<span></span>
</p>
<p class="a0">
}
</p>
<p class="a0">
//读取<span>SPI</span>寄存器值<span></span>
</p>
<p class="a0">
//reg:要读的寄存器<span></span>
</p>
<p class="a0">
u8 NRF24L01_Read_Reg(u8 reg)
</p>
<p class="a0">
{
</p>
<p class="a0">
u8 reg_val;
</p>
<p class="a0">
NRF24L01_CSN =
0; //使能<span>SPI</span>传输<span> </span>
</p>
<p class="a0">
SPI1_ReadWriteByte(reg); //发送寄存器号<span></span>
</p>
<p class="a0">
reg_val=SPI1_ReadWriteByte(0XFF);//读取寄存器内容<span></span>
</p>
<p class="a0">
NRF24L01_CSN =
1; //禁止<span>SPI</span>传输<span> </span>
</p>
<p class="a0">
return(reg_val); //返回状态值<span></span>
</p>
<p class="a0">
}
</p>
<p class="a0">
//在指定位置读出指定长度的数据<span></span>
</p>
<p class="a0">
//reg:寄存器<span>(</span>位置<span>)</span>
</p>
<p class="a0">
//*pBuf:数据指针<span></span>
</p>
<p class="a0">
//len:数据长度<span></span>
</p>
<p class="a0">
//返回值<span>,</span>此次读到的状态寄存器值 <span></span>
</p>
<p class="a0">
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len)
</p>
<p class="a0">
{
</p>
<p class="a0">
u8
status,u8_ctr;
</p>
<p class="a0">
NRF24L01_CSN =
0; //使能<span>SPI</span>传输<span></span>
</p>
<p class="a0">
status=SPI1_ReadWriteByte(reg);//发送寄存器值<span>(</span>位置<span>),</span>并读取状态值<span> </span>
</p>
<p class="a0">
for(u8_ctr=0;u8_ctr<len;u8_ctr++)pBuf[u8_ctr]=SPI1_ReadWriteByte(0XFF);
</p>
<p class="a0">
NRF24L01_CSN=1; //关闭<span>SPI</span>传输<span></span>
</p>
<p class="a0">
return
status; //返回读到的状态值<span></span>
</p>
<p class="a0">
}
</p>
<p class="a0">
//在指定位置写指定长度的数据<span></span>
</p>
<p class="a0">
//reg:寄存器<span>(</span>位置<span>)</span>
</p>
<p class="a0">
//*pBuf:数据指针<span></span>
</p>
<p class="a0">
//len:数据长度<span></span>
</p>
<p class="a0">
//返回值<span>,</span>此次读到的状态寄存器值<span></span>
</p>
<p class="a0">
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len)
</p>
<p class="a0">
{
</p>
<p class="a0">
u8
status,u8_ctr;
</p>
<p class="a0">
NRF24L01_CSN =
0; //使能<span>SPI</span>传输<span></span>
</p>
<p class="a0">
status =
SPI1_ReadWriteByte(reg);//发送寄存器值<span>(</span>位置<span>),</span>并读取状态值<span></span>
</p>
<p class="a0">
for(u8_ctr=0;
u8_ctr<len; u8_ctr++)SPI1_ReadWriteByte(*pBuf++); //写入数据<span></span>
</p>
<p class="a0">
NRF24L01_CSN =
1; //关闭<span>SPI</span>传输<span></span>
</p>
<p class="a0">
return
status; //返回读到的状态值<span></span>
</p>
<p class="a0">
}
</p>
<p class="a0">
//启动<span>NRF24L01</span>发送一次数据<span></span>
</p>
<p class="a0">
//txbuf:待发送数据首地址<span></span>
</p>
<p class="a0">
//返回值<span>:</span>发送完成状况<span></span>
</p>
<p class="a0">
u8 NRF24L01_TxPacket(u8 *txbuf)
</p>
<p class="a0">
{
</p>
<p class="a0">
u8 sta;
</p>
<p class="a0">
SPI1_SetSpeed(SPI_BaudRatePrescaler_8);//s24L01的最大<span>SPI</span>时钟为<span>10Mhz </span>
</p>
<p class="a0">
NRF24L01_CE=0;
</p>
<p class="a0">
NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);
</p>
<p class="a0" style="text-indent:246.75pt;">
//写数据到<span>TX BUF 32</span>个字节<span></span>
</p>
<p class="a0">
NRF24L01_CE=1;//启动发送<span> </span>
</p>
<p class="a0">
while(NRF24L01_IRQ!=0);//等待发送完成<span></span>
</p>
<p class="a0">
sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值<span> </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta);
//清除<span>TX_DS</span>或<span>MAX_RT</span>中断标志<span></span>
</p>
<p class="a0">
if(sta&MAX_TX)//达到最大重发次数<span></span>
</p>
<p class="a0">
{ NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除<span>TX FIFO</span>寄存器 <span></span>
</p>
<p class="a0">
return
MAX_TX;
</p>
<p class="a0">
}
</p>
<p class="a0">
if(sta&TX_OK)//发送完成<span></span>
</p>
<p class="a0">
{ return
TX_OK;
</p>
<p class="a0">
}
</p>
<p class="a0">
return
0xff;//其他原因发送失败<span></span>
</p>
<p class="a0">
}
</p>
<p class="a0">
//启动<span>NRF24L01</span>发送一次数据<span></span>
</p>
<p class="a0">
//txbuf:待发送数据首地址<span></span>
</p>
<p class="a0">
//返回值<span>:0</span>,接收完成;其他,错误代码<span></span>
</p>
<p class="a0">
u8 NRF24L01_RxPacket(u8 *rxbuf)
</p>
<p class="a0">
{
</p>
<p class="a0">
u8 sta;
</p>
<p class="a0">
SPI1_SetSpeed(SPI_BaudRatePrescaler_8);
//24L01的最大<span>SPI</span>时钟为<span>10Mhz </span>
</p>
<p class="a0">
sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值<span> </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+STATUS,sta);
//清除<span>TX_DS</span>或<span>MAX_RT</span>中断标志<span></span>
</p>
<p class="a0">
if(sta&RX_OK)//接收到数据<span></span>
</p>
<p class="a0">
{
</p>
<p class="a0">
NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据<span></span>
</p>
<p class="a0">
NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除<span>RX FIFO</span>寄存器 <span></span>
</p>
<p class="a0">
return 0;
</p>
<p class="a0">
}
</p>
<p class="a0">
return 1;//没收到任何数据<span></span>
</p>
<p class="a0">
}
</p>
<p class="a0">
//该函数初始化<span>NRF24L01</span>到<span>RX</span>模式<span></span>
</p>
<p class="a0">
//设置<span>RX</span>地址<span>,</span>写<span>RX</span>数据宽度<span>,</span>选择<span>RF</span>频道<span>,</span>波特率和<span>LNA HCURR</span>
</p>
<p class="a0">
//当<span>CE</span>变高后<span>,</span>即进入<span>RX</span>模式<span>,</span>并可以接收数据了<span> </span>
</p>
<p class="a0">
void NRF24L01_RX_Mode(void)
</p>
<p class="a0">
{
</p>
<p class="a0">
NRF24L01_CE=0;
</p>
<p class="a0">
NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);
</p>
<p class="a0" style="text-indent:304.5pt;">
//写<span>RX</span>节点地址<span></span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道<span>0</span>的自动应答<span> </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);//使能通道<span>0</span>的接收地址<span> </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40); //设置<span>RF</span>通信频率<span> </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);
</p>
<p class="a0" style="text-indent:262.5pt;">
//选择通道<span>0</span>的有效数据宽度<span> </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);
</p>
<p class="a0" style="text-indent:189.0pt;">
//设置<span>TX</span>发射参数<span>,0db</span>增益<span>,2Mbps,</span>低噪声增益开启<span> </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG, 0x0f);
</p>
<p class="a0" style="text-indent:126.0pt;">
//配置基本工作模式的参数<span>WR_UP,EN_CRC,16BIT_CRC,</span>接收模式 <span></span>
</p>
<p class="a0">
NRF24L01_CE =
1; //CE为高<span>,</span>进入接收模式 <span></span>
</p>
<p class="a0">
}
</p>
<p class="a0">
//该函数初始化<span>NRF24L01</span>到<span>TX</span>模式<span></span>
</p>
<p class="a0">
//设置<span>TX</span>地址<span>,</span>写<span>TX</span>数据宽度<span>,</span>设置<span>RX</span>自动应答的地址<span>,</span>填充<span>TX</span>发送数据<span>,</span>
</p>
<p class="a0">
//选择<span>RF</span>频道<span>,</span>波特率和<span>LNA HCURR</span>
</p>
<p class="a0">
//PWR_UP,CRC使能<span></span>
</p>
<p class="a0">
//当<span>CE</span>变高后<span>,</span>即进入<span>RX</span>模式<span>,</span>并可以接收数据了<span> </span>
</p>
<p class="a0">
//CE为高大于<span>10us,</span>则启动发送<span>. </span>
</p>
<p class="a0">
void NRF24L01_TX_Mode(void)
</p>
<p class="a0">
{
</p>
<p class="a0" style="text-indent:31.5pt;">
NRF24L01_CE=0;
</p>
<p class="a0">
NRF24L01_Write_Buf(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);
</p>
<p class="a0" style="text-indent:309.75pt;">
//写<span>TX</span>节点地址 <span></span>
</p>
<p class="a0">
NRF24L01_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);
</p>
<p class="a0" style="text-indent:241.5pt;">
//设置<span>TX</span>节点地址<span>,</span>主要为了使能<span>ACK </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); //使能通道<span>0</span>的自动应答<span> </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道<span>0</span>的接收地址<span> </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);
</p>
<p class="a0" style="text-indent:99.75pt;">
//设置自动重发间隔时间<span>:500us +
86us;</span>最大自动重发次数<span>:10</span>次<span></span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+RF_CH,40); //设置<span>RF</span>通道为<span>40</span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f);
</p>
<p class="a0" style="text-indent:152.25pt;">
//设置<span>TX</span>发射参数<span>,0db</span>增益<span>,2Mbps,</span>低噪声增益开启<span> </span>
</p>
<p class="a0">
NRF24L01_Write_Reg(NRF_WRITE_REG+CONFIG,0x0e);
</p>
<p class="a0" style="text-indent:57.75pt;">
//配置基本工作模式的参数<span>WR_UP,EN_CRC,16BIT_CRC,</span>接收模式<span>,</span>开启所有中断<span></span>
</p>
<p class="a0">
NRF24L01_CE=1;//CE为高<span>,10us</span>后启动发送<span></span>
</p>
<p class="a0">
}
</p>
<p class="a" style="text-indent:21.0pt;">
此部分代码我们不多介绍,在这里强调一个要注意的地方,在<span>NRF24L01_Init</span>函数里面,我们调用了<span>SPI1_Init()</span>函数,该函数我们在第三十章曾有提到,在第三十章的设置里面,<span>SCK</span>空闲时为高,但是<span>NRF24L01</span>的<span>SPI</span>通信时序如图<span>38.3.1</span>所示: <span></span>
</p>
<p class="a" style="text-align:center;text-indent:9.95pt;">
</p>
<p class="a" align="center" style="text-align:center;text-indent:0cm;">
图<span>38.3.1 NRF24L01</span>读写操作时序<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
上图中<span>Cn</span>代表指令位,<span>Sn</span>代表状态寄存器位,<span>Dn</span>代表数据位。从图中可以看出,<span>SCK</span>空闲的时候是低电平的,而数据在<span>SCK</span>的上升沿被读写。所以,我们需要设置<span>SPI</span>的<span>CPOL</span>和<span>CPHA</span>均为<span>0</span>,来满足<span>NRF24L01</span>对<span>SPI</span>操作的要求。所以,我们在<span>NRF24L01_Init</span>函数里面又单独添加了将<span>CPOL</span>和<span>CPHA</span>设置为<span>0</span>的函数<span>NRF24L01_SPI_Init</span>。这里主要是修改了下面两行代码;<span></span>
</p>
<p class="a0">
<span>SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //</span>空闲状态为低电平<span></span>
</p>
<p class="a0">
<span>SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;//</span>第<span>1</span>个跳变沿数据被采样<span></span>
</p>
<p class="a" style="text-indent:0cm;">
接下来我们看看<span>24l01.h</span>头文件部分内容:<span></span>
</p>
<p class="a0">
<span>#ifndef __24L01_H</span>
</p>
<p class="a0">
<span>#define __24L01_H </span>
</p>
<p class="a0">
<span>#include "sys.h" </span>
</p>
<p class="a0">
<span>//NRF24L01</span>寄存器操作命令<span></span>
</p>
<p class="a0">
<span>#define READ_REG
0x00 //</span>读配置寄存器<span>,</span>低<span>5</span>位为寄存器地址<span></span>
</p>
<p class="a0">
<span>......//</span>省略部分定义<span></span>
</p>
<p class="a0">
<span>#define FIFO_STATUS 0x17
//FIFO</span>状态寄存器<span>;bit0,RX FIFO</span>寄存器空标志<span>;</span>
</p>
<p class="a0">
<span>//bit1,RX FIFO</span>满标志<span>;bit2,3,</span>保留 <span>bit4,TX FIFO</span>空标志<span>;bit5,TX FIFO</span>满标志<span>;</span>
</p>
<p class="a0">
<span>//bit6,1, </span>循环发送上一数据包<span>.0,</span>不循环<span>;</span>
</p>
<p class="a0">
<span>//24L01</span>操作线<span></span>
</p>
<p class="a0">
<span>#define NRF24L01_CE PGout(6)
//24L01</span>片选信号<span></span>
</p>
<p class="a0">
<span>#define NRF24L01_CSN PGout(7)
//SPI</span>片选信号<span> </span>
</p>
<p class="a0">
<span>#define NRF24L01_IRQ PGin(8) //IRQ</span>主机数据输入<span></span>
</p>
<p class="a0">
<span>//24L01</span>发送接收数据宽度定义<span></span>
</p>
<p class="a0">
<span>#define TX_ADR_WIDTH 5 //5</span>字节的地址宽度<span></span>
</p>
<p class="a0">
<span>#define RX_ADR_WIDTH 5 //5</span>字节的地址宽度<span></span>
</p>
<p class="a0">
<span>#define TX_PLOAD_WIDTH 32 //32</span>字节的用户数据宽度<span></span>
</p>
<p class="a0">
<span>#define RX_PLOAD_WIDTH 32 //32</span>字节的用户数据宽度<span> </span>
</p>
<p class="a0">
<span> </span>
</p>
<p class="a0">
<span>void NRF24L01_Init(void); //</span>初始化<span></span>
</p>
<p class="a0" style="text-indent:15.75pt;">
<span> ......//</span>省略部分函数申明<span></span>
</p>
<p class="a0">
<span>u8 NRF24L01_RxPacket(u8 *rxbuf); //</span>接收一个包的数据<span></span>
</p>
<p class="a0">
<span>#endif</span>
</p>
<p class="a" style="text-indent:21.0pt;">
部分代码,主要定义了一些<span>24L</span><span>01</span>的命令字(这里我们省略了一部分),以及函数声明,这里还通过<span>TX_PLOAD_WIDTH</span>和<span>RX_PLOAD_WIDTH</span>决定了发射和接收的数据宽度,也就是我们每次发射和接受的有效字节数。<span>NRF24L01</span>每次最多传输<span>32</span>个字节,再多的字节传输则需要多次传送。<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
最后我们看看主函数:<span></span>
</p>
<p class="a0">
<span>//</span>要写入到<span>W25Q16</span>的字符串数组<span></span>
</p>
<p class="a0">
<span>const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI
TEST"};</span>
</p>
<p class="a0">
<span>#define SIZE sizeof(TEXT_Buffer) </span>
</p>
<p class="a0">
<span>int main(void)</span>
</p>
<p class="a0">
<span>{ </span>
</p>
<p class="a0">
<span> u8
key,mode, tmp_buf[33];</span>
</p>
<p class="a0">
<span> u16
t=0; </span>
</p>
<p class="a0">
<span> NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//</span>设置系统中断优先级分组<span>2</span>
</p>
<p class="a0">
<span> delay_init(168); //</span>初始化延时函数<span></span>
</p>
<p class="a0">
<span> uart_init(115200); //</span>初始化串口波特率为<span>115200</span>
</p>
<p class="a0">
<span> LED_Init(); //</span>初始化<span>LED </span>
</p>
<p class="a0">
<span> LCD_Init(); //LCD</span>初始化<span> </span>
</p>
<p class="a0">
<span> KEY_Init(); //</span>按键初始化<span></span>
</p>
<p class="a0">
<span> NRF24L01_Init(); //</span>初始化<span>NRF24L01 </span>
</p>
<p class="a0">
<span> POINT_COLOR=RED;//</span>设置字体为红色 <span></span>
</p>
<p class="a0">
<span> LCD_ShowString(30,50,200,16,16,"Explorer
STM32F4"); </span>
</p>
<p class="a0">
<span> LCD_ShowString(30,70,200,16,16,"NRF24L01
TEST"); </span>
</p>
<p class="a0">
<span> LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");</span>
</p>
<p class="a0">
<span> LCD_ShowString(30,110,200,16,16,"2014/5/9");</span>
</p>
<p class="a0">
<span> while(NRF24L01_Check())</span>
</p>
<p class="a0">
<span> {</span>
</p>
<p class="a0">
<span> LCD_ShowString(30,130,200,16,16,"NRF24L01
Error");</span>
</p>
<p class="a0">
<span> delay_ms(200);</span>
</p>
<p class="a0">
<span> LCD_Fill(30,130,239,130+16,WHITE);</span>
</p>
<p class="a0">
<span> delay_ms(200);</span>
</p>
<p class="a0">
<span> }</span>
</p>
<p class="a0">
<span> LCD_ShowString(30,130,200,16,16,"NRF24L01
OK"); </span>
</p>
<p class="a0">
<span> while(1)</span>
</p>
<p class="a0">
<span> { </span>
</p>
<p class="a0">
<span> key=KEY_Scan(0);</span>
</p>
<p class="a0">
<span> if(key==KEY0_PRES)</span>
</p>
<p class="a0">
<span> { </span>
</p>
<p class="a0" style="text-indent:73.5pt;">
<span>mode=0; break;</span>
</p>
<p class="a0">
<span> }else if(key==KEY1_PRES)</span>
</p>
<p class="a0">
<span> {</span>
</p>
<p class="a0">
<span> mode=1;break;</span>
</p>
<p class="a0">
<span> }</span>
</p>
<p class="a0">
<span> t++;</span>
</p>
<p class="a0">
<span> if(t==100)LCD_ShowString(10,150,230,16,16,</span>
</p>
<p class="a0" style="text-indent:136.5pt;">
<span>"KEY0:RX_Mode
KEY1:TX_Mode"); //</span>闪烁显示提示信息<span></span>
</p>
<p class="a0">
<span> if(t==200)</span>
</p>
<p class="a0">
<span> { </span>
</p>
<p class="a0">
<span> LCD_Fill(10,150,230,150+16,WHITE);
t=0; </span>
</p>
<p class="a0">
<span> }</span>
</p>
<p class="a0">
<span> delay_ms(5); </span>
</p>
<p class="a0">
<span> } </span>
</p>
<p class="a0">
<span> LCD_Fill(10,150,240,166,WHITE);//</span>清空上面的显示<span> </span>
</p>
<p class="a0">
<span> POINT_COLOR=BLUE;//</span>设置字体为蓝色<span> </span>
</p>
<p class="a0">
<span> if(mode==0)//RX</span>模式<span></span>
</p>
<p class="a0">
<span> {</span>
</p>
<p class="a0">
<span> LCD_ShowString(30,150,200,16,16,"NRF24L01
RX_Mode"); </span>
</p>
<p class="a0">
<span> LCD_ShowString(30,170,200,16,16,"Received
DATA:"); </span>
</p>
<p class="a0">
<span> NRF24L01_RX_Mode(); </span>
</p>
<p class="a0">
<span> while(1)</span>
</p>
<p class="a0">
<span> { </span>
</p>
<p class="a0">
<span> if(NRF24L01_RxPacket(tmp_buf)==0)//</span>一旦接收到信息<span>,</span>则显示出来<span>.</span>
</p>
<p class="a0">
<span> {</span>
</p>
<p class="a0">
<span> tmp_buf[32]=0;//</span>加入字符串结束符<span></span>
</p>
<p class="a0">
<span> LCD_ShowString(0,190,lcddev.width-1,32,16,tmp_buf); </span>
</p>
<p class="a0">
<span> }else
delay_us(100); </span>
</p>
<p class="a0">
<span> t++;</span>
</p>
<p class="a0">
<span> if(t==10000)//</span>大约<span>1s</span>钟改变一次状态<span></span>
</p>
<p class="a0">
<span> {</span>
</p>
<p class="a0">
<span> t=0;LED0=!LED0;</span>
</p>
<p class="a0">
<span> }
</span>
</p>
<p class="a0">
<span> }; </span>
</p>
<p class="a0">
<span> }else//TX</span>模式<span></span>
</p>
<p class="a0">
<span> { </span>
</p>
<p class="a0">
<span> LCD_ShowString(30,150,200,16,16,"NRF24L01
TX_Mode"); </span>
</p>
<p class="a0">
<span> NRF24L01_TX_Mode();</span>
</p>
<p class="a0">
<span> mode='
';//</span>从空格键开始<span> </span>
</p>
<p class="a0">
<span> while(1)</span>
</p>
<p class="a0">
<span> { </span>
</p>
<p class="a0">
<span> if(NRF24L01_TxPacket(tmp_buf)==TX_OK)</span>
</p>
<p class="a0">
<span> {</span>
</p>
<p class="a0">
<span> LCD_ShowString(30,170,239,32,16,"Sended
DATA:"); </span>
</p>
<p class="a0">
<span> LCD_ShowString(0,190,lcddev.width-1,32,16,tmp_buf); </span>
</p>
<p class="a0">
<span> key=mode;</span>
</p>
<p class="a0">
<span> for(t=0;t<32;t++)</span>
</p>
<p class="a0">
<span> {</span>
</p>
<p class="a0">
<span> key++;</span>
</p>
<p class="a0">
<span> if(key>('~'))key='
';</span>
</p>
<p class="a0">
<span> tmp_buf[t]=key; </span>
</p>
<p class="a0">
<span> }</span>
</p>
<p class="a0">
<span> mode++; </span>
</p>
<p class="a0">
<span> if(mode>'~')mode='
'; </span>
</p>
<p class="a0">
<span> tmp_buf[32]=0;//</span>加入结束符<span> </span>
</p>
<p class="a0">
<span> }else</span>
</p>
<p class="a0">
<span> { </span>
</p>
<p class="a0">
<span> LCD_Fill(0,170,lcddev.width,170+16*3,WHITE);//</span>清空显示<span> </span>
</p>
<p class="a0">
<span> LCD_ShowString(30,170,lcddev.width-1,32,16,"Send
Failed "); </span>
</p>
<p class="a0">
<span> };</span>
</p>
<p class="a0">
<span> LED0=!LED0;delay_ms(1500); </span>
</p>
<p class="a0">
<span> };</span>
</p>
<p class="a0">
<span> } </span>
</p>
<p class="a0">
<span>}</span>
</p>
<p class="a" style="text-indent:21.0pt;">
以上代码,我们就实现了<span>38.2</span>节所介绍的功能,程序运行时先通过<span>NRF24L01_Check</span>函数检测<span>NRF24L01</span>是否存在,如果存在,则让用户选择发送模式<span>(KEY1)</span>还是接收模式(<span>KEY0</span>),在确定模式之后,设置<span>NRF24L01</span>的工作模式,然后执行相应的数据发送<span>/</span>接收处理。<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
至此,我们整个实验的软件设计就完成了。<span></span>
</p>
<h2 style="margin-left:0cm;">
<a name="_Toc402209407"></a><span>38.4 </span>下载验证<span></span>
</h2>
<p class="a" style="text-indent:21.0pt;">
在代码编译成功之后,我们通过下载代码到<span>ALIENTEK</span>探索者<span>STM32F4</span>开发板上,可以看到<span>LCD</span>显示如图<span>38.4.1</span>所示的内容(默认<span>NRF24L01</span>已经接上了):<span></span>
</p>
<div style="text-align:center;">
</div>
<p class="a" align="center" style="text-align:center;text-indent:0cm;">
图<span>38.4.1 </span>选择工作模式界面<span></span>
</p>
<p class="a" style="text-indent:21.0pt;">
通过<span>KEY0</span>和<span>KEY1</span>来选择<span>NRF24L01</span>模块所要进入的工作模式,我们两个开发板一个选择发送,一个选择接收就可以了。设置好后通信界面如图<span>38.4.2</span>和图<span>38.4.3</span>所示:<br />
<br />
<br />
<br />
<br />
<div class="WordSection1" style="font-family:Simsun;background-color:#D1D9E2;">
<p class="a" style="text-indent:21pt;">
<span style="line-height:1.5;"><strong><span style="background-color:#E53333;color:#FFFFFF;font-size:16px;">实验详细手册和源码下载地址:</span></strong><a href="http://www.openedv.com/posts/list/41586.htm" target="_blank">http://www.openedv.com/posts/list/41586.htm </a></span>
</p>
</div>
<b>
<p class="a" style="text-indent:21pt;">
<b></b>
</p>
<p class="a" style="text-indent:21pt;">
<span style="line-height:1.5;"><strong><span style="color:#FFFFFF;font-size:18px;background-color:#E53333;">正点原子探索者STM32F407开发板购买地址</span>:</strong><a href="http://item.taobao.com/item.htm?id=41855882779" target="_blank">http://item.taobao.com/item.htm?id=41855882779<br />
</a></span>
</p>
<p class="a" style="text-indent:21pt;">
<span style="line-height:1.5;"> </span><strong><span style="color:#CC33E5;font-size:32px;"><strong><strong><strong><strong><strong><b><a href="http://item.taobao.com/item.htm?id=41855882779" target="_blank"><img src="http://img01.taobaocdn.com/imgextra/i1/413302608/TB217icaVXXXXbIXXXXXXXXXXXX_!!413302608.png" alt="" /></a></b></strong></strong></strong></strong></strong></span></strong>
</p>
</b><span></span>
</p> |
|