STM32中nRF24L01的使用

最近在一个项目中用到了nRF24L01这个无线2.4G收发芯片,项目中有主机和分机,默认都是使用数据通道0,主机通过nRF24L01发送数据后,对应地址的分机在收到数据后会返回一个确认数据包给主机(注意:这个确认数据包并不是nRF24L01自动应答时的数据包,而是自定义的一个数据包,说明了就是双方都能进行收发),在经过长时间的摸索之后,终于将接收和发送都调通了,基本的SPI驱动我使用的是正点原子的教程,我是使用的中断法来处理相应的收发工作。

我的软件硬件环境大致如下:

服务端:STM32F103VET6+u/COS-III

从机端:STM32F103RBT6+u/COS-III

库函数是使用的3.5版本的。

在这里我只是列出主机部分的代码,从机上的都是差不多的。


下面是nRF24L01的驱动部分

头文件部分(24l01.h):

#ifndef __24L01_H
#define __24L01_H

#include "config.h"

#if nRF24L01_EN > 0u

#include "sys_temp.h"


/*---------------------------------------------------------------------------------------------
							NRF24L01寄存器操作命令及寄存器地址
-----------------------------------------------------------------------------------------------*/
#define SPI_READ_REG    	0x00  //读配置寄存器,低5位为寄存器地址
#define SPI_WRITE_REG   	0x20  //写配置寄存器,低5位为寄存器地址
#define RD_RX_PLOAD     	0x61  //读RX有效数据,1~32字节
#define WR_TX_PLOAD     	0xA0  //写TX有效数据,1~32字节
#define FLUSH_TX        	0xE1  //清除TX FIFO寄存器.发射模式下用
#define FLUSH_RX        	0xE2  //清除RX FIFO寄存器.接收模式下用
#define REUSE_TX_PL     	0xE3  //重新使用上一包数据,CE为高,数据包被不断发送.
#define NOP             	0xFF  //空操作,可以用来读状态寄存器	 

#define CONFIG          	0x00  //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能;
								  //bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能
#define EN_AA           	0x01  //使能自动应答功能  bit0~5,对应通道0~5
#define EN_RXADDR       	0x02  //接收地址允许,bit0~5,对应通道0~5
#define SETUP_AW        	0x03  //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节;
#define SETUP_RETR      	0x04  //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us
#define RF_CH           	0x05  //RF通道,bit6:0,工作通道频率;
#define RF_SETUP        	0x06  //RF寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益
#define STATUS          	0x07  //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,自动重发完成中断
								  //bit5:数据发送完成中断;bit6:接收数据完成中断;
#define MAX_TX  			0x10  //达到最大发送次数中断,即自动重发完成中断
#define TX_OK   			0x20  //TX发送完成中断,即数据发送完成中断
#define RX_OK   			0x40  //接收到数据中断,即数据接收完成中断

#define OBSERVE_TX      	0x08  //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器
#define CD              	0x09  //载波检测寄存器,bit0,载波检测;
#define RX_ADDR_P0      	0x0A  //数据通道0接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P1      	0x0B  //数据通道1接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P2      	0x0C  //数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P3      	0x0D  //数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P4      	0x0E  //数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P5      	0x0F  //数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define TX_ADDR         	0x10  //发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等
#define RX_PW_P0        	0x11  //接收数据通道0有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P1        	0x12  //接收数据通道1有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P2        	0x13  //接收数据通道2有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P3        	0x14  //接收数据通道3有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P4        	0x15  //接收数据通道4有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P5        	0x16  //接收数据通道5有效数据宽度(1~32字节),设置为0则非法
#define FIFO_STATUS     	0x17  //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留
								  //bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环;
								  

/*---------------------------------------------------------------------------------------------
							24L01的用到的单片机引脚
-----------------------------------------------------------------------------------------------*/
#define NRF24L01_SPI_Periph_CLK   	RCC_APB2Periph_GPIOB	//无线2.4G模块用到的引脚的外设时钟源
#define NRF24L01_SPI_GPIO_SRC     	GPIOB
#define NRF24L01_SPI_SCK_PIN		GPIO_Pin_13		//SCK时钟
#define NRF24L01_SPI_MISO_PIN		GPIO_Pin_14		//主机输入,从机输出
#define NRF24L01_SPI_MOSI_PIN		GPIO_Pin_15		//主机输出,从机输入
#define NRF24L01_CE_PIN				GPIO_Pin_10		//24L01芯片使能信号
#define NRF24L01_IRQ_PIN			GPIO_Pin_11		//IRQ主机数据输入
#define NRF24L01_CSN_PIN			GPIO_Pin_12		//SPI片选


/*---------------------------------------------------------------------------------------------
							24L01芯片使能信号和片选信号操作
-----------------------------------------------------------------------------------------------*/
#define NRF24L01_CE   				PBout(10) //24L01芯片使能信号
#define NRF24L01_CSN  				PBout(12) //SPI片选信号	   
#define NRF24L01_IRQ  				PBin(11)  //IRQ主机数据输入


/*---------------------------------------------------------------------------------------------
			24L01中断线配置,当接收到数据,发送数据,达到最大重发次数时都会触发中断
			相应的状态寄存器会置位1,注意需要手动清除中断,写1清零
-----------------------------------------------------------------------------------------------*/
#define	NRF24L01_INT_SOURCE_PORT    	GPIO_PortSourceGPIOB	//中断引脚组
#define	NRF24L01_INT_IRQ       			EXTI15_10_IRQn			//中断号
#define	NRF24L01_STATUS_INT_SOURCE   	GPIO_PinSource11		//中断源  	
#define	NRF24L01_STATUS_LINE         	EXTI_Line11				//中断线
#define	NRF24L01_STATUS_INT_MODE      	EXTI_Trigger_Falling	//中断触发方式


/*---------------------------------------------------------------------------------------------
							24L01发送接收数据宽度定义
-----------------------------------------------------------------------------------------------*/
#define TX_ADR_WIDTH    5   //5字节的地址宽度
#define RX_ADR_WIDTH    5   //5字节的地址宽度
#define TX_PLOAD_WIDTH  32  //32字节的用户数据宽度
#define RX_PLOAD_WIDTH  32  //32字节的用户数据宽度



/*---------------------------------------------------------------------------------------------
							24L01相关操作函数定义
-----------------------------------------------------------------------------------------------*/
void NRF24L01_Init(void);//初始化
void RX_Mode(void);//配置为接收模式

//void TX_Mode(void);//配置为发送模式(原始定义)
void TX_Mode(u8 * addr);//配置为发送模式

static u8 SPIx_ReadWriteByte(u8 TxData);
u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 u8s);//写数据区
u8 NRF24L01_Read_Buf(u8 reg, u8 *pBuf, u8 u8s);//读数据区		  
u8 NRF24L01_Read_Reg(u8 reg);			//读寄存器
u8 NRF24L01_Write_Reg(u8 reg, u8 value);//写寄存器
u8 NRF24L01_Check(void);//检查24L01是否存在
u8 NRF24L01_TxPacket(u8 *txbuf);//发送一个包的数据

//u8 NRF24L01_RxPacket(u8 *rxbuf);//接收一个包的数据(原始定义)

u8 NRF24L01_RxPacket(u8 *rxbuf, u8 *chl);//接收一个包的数据


#endif /* nRF24L01_EN */

#endif /* __24L01_H */


源文件部分(24l01.c):

/******************** (C) COPYRIGHT 2015 ASTO ***************************
 @* 文件名  :24l01.c
 @* 描述    :nRF24L01驱动程序         
 @* 开发平台:STM32F103VET6主控制MCU
 @* 硬件连接:SPI2
 @* 库版本  :ST3.5.0
 @* 作者    : 
 @* 公司网址:www.test.com
 @* 总部网址:www.test.com
**********************************************************************************/

#include "config.h"

#if nRF24L01_EN > 0u

#include "sys_temp.h"
#include "24l01.h"
#include "delay.h"

const u8 RX0_Address[RX_ADR_WIDTH]={0x01,0x01,0x01,0x01,0x01};	//接收方通道0地址

/*
const u8 RX1_Address[RX_ADR_WIDTH]={0x02,0x20,0x20,0x20,0x20};	//接收方通道1地址
const u8 RX2_Address[1] = {0x03}; 								//接收方通道2地址
const u8 RX3_Address[1] = {0x04}; 								//接收方通道3地址
const u8 RX4_Address[1] = {0x05}; 								//接收方通道4地址
const u8 RX5_Address[1] = {0x06}; 								//接收方通道5地址
*/

/**
	@函数名称:EXTI_Configuration()
	@函数功能:nRF24L01的IRQ中断配置(只响应接收数据的中断)
	@输入:无
	@输出:无
	@调用:内部调用
**/
static void EXTI_Configuration(void)
{
	EXTI_InitTypeDef EXTI_InitStructure;  
    NVIC_InitTypeDef NVIC_InitStructure;
  
	//中断优先级设置
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  
    NVIC_InitStructure.NVIC_IRQChannel = NRF24L01_INT_IRQ; 	//10-15的中断线共享一个中断处理程序 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优行级
    NVIC_Init(&NVIC_InitStructure); 
	
	//中断线配置EXTI_Line11-->PB11
    GPIO_EXTILineConfig(NRF24L01_INT_SOURCE_PORT, NRF24L01_STATUS_INT_SOURCE); //设置中断源引脚 
    EXTI_InitStructure.EXTI_Line = NRF24L01_STATUS_LINE; //中断线设置 
    EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中断线使能 
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断方式 
    EXTI_InitStructure.EXTI_Trigger = NRF24L01_STATUS_INT_MODE;//模式,这里设置为下降沿触发  
    EXTI_Init(&EXTI_InitStructure);
}

/**
 @* 函数名:NRF24L01_SPI_Init()
 @* 描述  :初始化SPI2端口及基模式,用于操作nRF24L01无线2.4G模块
 @* 输入  :无
 @* 输出  : 无
 @* 调用  :内部调用
 */
static void NRF24L01_SPI_Init(void) {	
	
	GPIO_InitTypeDef GPIO_InitStructure;	
	SPI_InitTypeDef SPI_InitStructure;

	//注意将普通IO作为中断线时必须开启AFIO时钟
	RCC_APB2PeriphClockCmd(NRF24L01_SPI_Periph_CLK|RCC_APB2Periph_AFIO, ENABLE);//开启GPIOB的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//开启SPI2的时钟
	
	/*配置 SPI的SCK, MISO, MOSI引脚,GPIOB^13,GPIOB^14,GPIOB^15 */
	GPIO_InitStructure.GPIO_Pin = NRF24L01_SPI_SCK_PIN| NRF24L01_SPI_MISO_PIN | NRF24L01_SPI_MOSI_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用输出功能	
	GPIO_Init(NRF24L01_SPI_GPIO_SRC, &GPIO_InitStructure);	

	/*GPIOB^10为nRF24L01的CE引脚,CSN引脚GPIOB^12 */
	GPIO_InitStructure.GPIO_Pin = NRF24L01_CE_PIN|NRF24L01_CSN_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//通用推挽输出
	GPIO_Init(NRF24L01_SPI_GPIO_SRC, &GPIO_InitStructure);
	
	/* GIOB^11为nRF24L01的IRQ中断输入引脚 */
	GPIO_InitStructure.GPIO_Pin = NRF24L01_IRQ_PIN;	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮动输入
	GPIO_Init(NRF24L01_SPI_GPIO_SRC, &GPIO_InitStructure);
			 
	SPI_Cmd(SPI2,DISABLE);/*先失能,然后再使能*/
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ; /* 全双工 */
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master ; /*当前的设备为主机模式*/
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low ;   /* 时钟极性为低,即SPI空闲时,SCK为低电平 */
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge ; /* 时钟相位,第一个时钟沿(也就是奇数边沿)捕捉数据 */
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;  /* 数据宽度 */	
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB ; /* 低地址存放最高有效字节 */	
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; /*配置片选为软件控制方式*/
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; /* 时钟分频为8分频,即72MHz/8=9MHz */		
	SPI_InitStructure.SPI_CRCPolynomial = 7;  /* 校验多项式,这个不起作用 */	
	SPI_Init(SPI2, &SPI_InitStructure);
	
	//使能SPI2
	SPI_Cmd(SPI2,ENABLE);

	EXTI_Configuration();//配置中断线
}

/**
 @* 函数名:SPIx_ReadWriteByte()
 @* 描述  :SPI2 读写一个字节
 @* 输入  :TxData:要写入的字节
 @* 输出  : 读取到的字节
 @* 调用  :内部调用
 */
static u8 SPIx_ReadWriteByte(u8 TxData) {		
	
	/** 原来的实现方式
	
	u8 retry=0;				 
	while((SPI2->SR&1<<1)==0) {//等待发送区空
		
		retry++;
		if(retry>200)return 0;
	}
	
	SPI2->DR=TxData;	 	  //发送一个byte 
	retry=0;
	
	while((SPI2->SR&1<<0)==0) { //等待接收完一个byte
		
		retry++;
		if(retry>200)return 0;
	}	  						    
	return SPI2->DR;          //返回收到的数据	*/	

	/* Loop while DR register in not empty */
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);

	/* Send byte through the SPI2 peripheral */
	SPI_I2S_SendData(SPI2, TxData);

	/* Wait to receive a byte */
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);

	/* Return the byte read from the SPI bus */
	return SPI_I2S_ReceiveData(SPI2);
}

/**
 @* 函数名:NRF24L01_Init()
 @* 描述  :初始化24L01的相关IO口
 @* 输入  :无
 @* 输出  : 无
 @* 调用  :外部板级支持包调用
 */
void NRF24L01_Init(void) { 
	
	NRF24L01_SPI_Init();    //初始化SPI2
	NRF24L01_CE=0; 			//使能24L01
	NRF24L01_CSN=1;			//SPI片选取消	
}

/**
 @* 函数名:NRF24L01_Check()
 @* 描述  :检测24L01是否存在
 @* 输入  :无
 @* 输出  : 0:成功,1:失败
 @* 调用  :外部板级支持包调用
 */	
u8 NRF24L01_Check(void) {
	
	u8 buf[5]={0x21,0x21,0x21,0x21,0x21};
	u8 buf1[5];
	u8 i;
	//SPIx_SetSpeed(SPI_SPEED_8); //spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)   	 
	NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址.	
	NRF24L01_Read_Buf(TX_ADDR,buf1, 5); //读出写入的地址  	
	for(i=0;i<5;i++)if(buf1[i]!=0x21)break;	//判断读出的数据和写入的数据是否完全一致				   
	if(i!=5)return 1;//检测24L01错误	
	return 0;		 //执行到这里,表示成功检测到24L01
}

/**
 @* 函数名:NRF24L01_Write_Reg()
 @* 描述  :SPI写寄存器
 @* 输入  :reg: 指定的寄存器地址
 @*		    value: 要写入的值
 @* 输出  : 返回寄存器的状态值
 @* 调用  :内部调用
 */
u8 NRF24L01_Write_Reg(u8 reg,u8 value) {
	
	u8 status;
   	NRF24L01_CSN=0;                 	//使能SPI传输
  	status =SPIx_ReadWriteByte(reg);	//发送寄存器号 
  	SPIx_ReadWriteByte(value);      	//写入寄存器的值
  	NRF24L01_CSN=1;                 	//禁止SPI传输	   
  	return(status);       				//返回状态值
}

/**
 @* 函数名:NRF24L01_Read_Reg()
 @* 描述  :读取SPI寄存器值
 @* 输入  :reg: 指定的寄存器地址		   
 @* 输出  : 返回寄存器的状态值
 @* 调用  :内部调用
 */
u8 NRF24L01_Read_Reg(u8 reg) {
	
	u8 reg_val;	
 	NRF24L01_CSN = 0;          			//使能SPI传输		
  	SPIx_ReadWriteByte(reg);   			//发送寄存器号
  	reg_val=SPIx_ReadWriteByte(0XFF);	//读取寄存器内容
  	NRF24L01_CSN = 1;          			//禁止SPI传输		    
  	return(reg_val);           			//返回状态值
}

/**
 @* 函数名:NRF24L01_Read_Buf()
 @* 描述  :在指定位置读出指定长度的数据
 @* 输入  :reg:指定的寄存器位置
 @*         pBuf:数据指针用来存储读到的数据,一般是数组	
 @*		    len:数据长度
 @* 输出  : 此次读到的状态寄存器值
 @* 调用  :内部调用
 */ 
u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len) {
	
	u8 status,u8_ctr;
  	NRF24L01_CSN = 0;     //使能SPI传输
  	status=SPIx_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值   	   
 	for(u8_ctr=0;u8_ctr>1;
	
	//清除TX_DS或MAX_RT中断标志,注意这里是写1清零,只有清零后设备才能正常通讯
	NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,sta);
	
	if(sta&RX_OK) { //接收到数据
		
		NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
		NRF24L01_Write_Reg(FLUSH_RX, 0xff);//清除RX FIFO寄存器 
		return 0; 
	}	   
	return 1;//没收到任何数据
}

/**
 @* 函数名:RX_Mode()
 @* 描述  :该函数初始化NRF24L01到RX模式
 @*         设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
 @*         当CE变高后,即进入RX模式,并可以接收数据了
 @* 输入  :无 
 @* 输出  : 无
 @* 调用  :外部板级支持包调用
 */	   
void RX_Mode(void) {
	
	NRF24L01_CE=0;	//CE为0进入待机模式
	
	//配置通道的接收
	NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(u8*)RX0_Address,RX_ADR_WIDTH);//写RX节点地址(通道0),也就是表示用哪个通道接收数据	  
	
	//======================注意以下被注释掉的内容可作参考和=========================================
	//NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P1,(u8*)RX1_Address,RX_ADR_WIDTH);//写RX节点地址(通道1),也就是表示用哪个通道接收数据	  
	//NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P2,(u8*)RX2_Address,1);//写RX节点地址(通道2),也就是表示用哪个通道接收数据
	//NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P3,(u8*)RX3_Address,1);//写RX节点地址(通道3),也就是表示用哪个通道接收数据	  	
	//NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P4,(u8*)RX4_Address,1);//写RX节点地址(通道4),也就是表示用哪个通道接收数据
	//NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P5,(u8*)RX5_Address,1);//写RX节点地址(通道5),也就是表示用哪个通道接收数据	  
  	
	NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA, 0x01);    			//使能通道0的自动应答  
	NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01);			//使能通道0的接收地址,共有6个通道,高两位保留固定为00
  	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,0);	     		//设置RF通信频率		  
  	NRF24L01_Write_Reg(SPI_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度	
  	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x07);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启     	
	NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式 	
	
  	NRF24L01_CE = 1; //CE为高,进入接收模式 
}


/**
 @* 函数名:TX_Mode()
 @* 描述  :该函数初始化NRF24L01到TX模式
 @*         设置TX地址,写TX数据宽度,设置RX自动应答的地址,
 @*         填充TX发送数据,选择RF频道,波特率和LNA HCURR,PWR_UP,CRC使能
 @*         当CE变高后,即进入RX模式,并可以接收数据了
 @*		    CE为高大于10us,则启动发送
 @* 输入  :addr,要发送数据的目标地址 
 @* 输出  : 无
 @* 调用  :外部板级支持包调用
 */	 
void TX_Mode(u8 * addr) {
	
	NRF24L01_CE=0;	 //CE为0进入待机模式
	
  	NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR, addr, TX_ADR_WIDTH);//写TX节点地址,也就是接收方的地址(目标地址)
	
	//在发送端,数据通道0被用作接收应答信号,因此数据通道0的接收地址必须要与发送端的地址相同以确保收到正确的应答信号
	NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(u8*)addr, RX_ADR_WIDTH);

	//先单独测试发送方,看发送是否正常
	NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01);     //使能通道0的自动应答
  	NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址  
	NRF24L01_Write_Reg(SPI_WRITE_REG+SETUP_RETR, 0x1a); //使能自动重发,设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
  	  	
	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,0);       //设置RF通道为40
  	NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x07);  //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   
  	NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG,0x0e);    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
	
	NRF24L01_CE=1;//CE为高,10us后启动发送
}

#endif

/******************************************END OF FILE*************************************************/

中断处理函数(stm32f10x_it.c)

#if nRF24L01_EN > 0u

#include "24l01.h"

/**
	@*功能简介:I/O线中断处理函数,处理nRF24L01的数据接收中断,IO口是PB11
	@*参数:None
	@*返回值:None
*/
void EXTI15_10_IRQHandler(void)
{
	OSIntEnter(); 	 //用于统计中断的嵌套层数,对嵌套层数+1,请注意:这适用于有内核参与的中断
	
	if (EXTI_GetITStatus(NRF24L01_STATUS_LINE)!=RESET) {
		
		EXTI_ClearITPendingBit(NRF24L01_STATUS_LINE);//清除中断标志位
		
		if (!GPIO_ReadInputDataBit(NRF24L01_SPI_GPIO_SRC, NRF24L01_IRQ_PIN)) {//如果为低电平表示产生中断
			
			u8 sta; u8 sta2;
			sta=NRF24L01_Read_Reg(STATUS);  //读取nRF24L01状态寄存器的值
			sta2=NRF24L01_Read_Reg(FIFO_STATUS);//读取FIFO状态寄存器
			
			//状态寄存器的bit1-bit3是表示接收到数据的通道号,最大值为6
			//*chl=(0x0e&sta)>>1;
			NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志,注意这里是写1清零,只有清零后设备才能正常通讯

			if(sta & RX_OK) {	//此次中断表示是接收到了数据
				
				OS_ERR err;				
				USART1_SendData((u8*)"Feedback received!", 19);
				
				//直接发信号量给任务,这种方式比使用中介信号量更快,更节省资源
				//第二个参数:1. OS_OPT_POST_NONE表明在发布任务信号量之后调用任务调度程序
				//			  2. OS_OPT_POST_NO_SCHED则OSTaskSemPost后不会调用调度程序
				OSTaskSemPost(&nRF24L01_TCB,
							OS_OPT_POST_NONE,
							&err);
				
			}else if (sta & MAX_TX) {		//此次中断表示达到最大重发次数后必须手动清除TX FIFO寄存器
				USART1_SendData((u8 *)"Max retransmission reached!", 28);
				NRF24L01_Write_Reg(FLUSH_TX,0xff);
			}
			
			//如果数据发送成功,则相应的寄存器值为下列值,可以调试用:
			//FIFO_STATUS: 	0x11
			//STATUS: 		0x2E
			else if (sta & TX_OK) {		//此次中断表示发送数据成功,无需手动清除TX FIFO寄存器
				USART1_SendData((u8 *)"Send data OK!", 14);
				RX_Mode();	//数据成功发送后直接转为接收模式
			}
		}
	}
	
	OSIntExit();//对嵌套层数减1,在退出中断前启动任务调度(适用于有u/COS-III内核参与的中断)
}
#endi


在中断处理函数中,当收到数据的中断产生时,也就是RX_DR位置高时,我会将接收数据的工作交给某个任务去做,这里我使用的是发布信号量的方法,这样可以尽量减少中断处理的时间。这里我把RX_Mode();这句放在中断处理函数中,也就是数据发送完成后将nRF24L01转换为接收模式,也是出于无奈,因为我在函数NRF24L01_TxPacket中如果使用以下语句,就会出现问题:

while(NRF24L01_IRQ!=0);//等待发送完成,IRQ变为0后表示发送完成或者达到最大重发次数

后来我就不用这一句,然后让主程序执行其他的语句,当产生中断后(无论怎样都会产生一种中断,要么发送成功,要么达到最大重传次数)再将nRF24L01的模式变为接收模式,不知这样做是否合理,还望看过的朋友指点一下。


nRF24L01_TCB任务控制块的任务代码主函数(接收数据):

#if nRF24L01_EN > 0u
/** 
 @* 函数名:Task_nRF24L01_Sendback_Process()
 @* 描述  : 无线2.4G模块nRF24L01接收反馈数据包处理任务,优先级为4	
 @*		    当Main_Task或USART1_Task或Wi-Fi模块确定是要以无线2.4G方式发送数据给客户端后,会等待客户端设备的回应
 @*		    以确定此次操作是否成功并同时会启动一个定时器,此任务采用等待信号量方式确认是否有客户端的回复
 @*		    如果收到回复,则会根据上位机发送数据给主控制MCU的方式将回应数据包回发给上位机。
 @*
 @*		    发送数据给上位机的方式有以下几种:
 @*		    *****************************
 @*         *		   1. 网络方式      *
 @*	        *	       2. 串口方式      *
 @*		    *	       3. Wi-Fi方式     *
 @*         *****************************
 @* 输入  :p_arg: 创建任务时赋给任务的参数,该参数可以是任意类型的
 @* 输出  : 无
 */
void Task_nRF24L01_Sendback_Process(void *p_arg) {
	
    OS_ERR err;													//用于记录错误代号
	//u8 chl=0;													//接收到数据的通道号	
	CPU_TS ts;
	
	u8 n24l01_buf[32];											//接收2.4G数据缓冲区
    
	(void)p_arg;												//保存创建任务控制块时传递的任务参数	
	
	while(NRF24L01_Check()) {									//如果检测不到24L01模块则打印出错信息
		printf("No nRF24L01 device is found!\r\n");
	}
	
	RX_Mode();													//默认为接收模式	
	OSTimeDlyHMSM(0, 0, 0, 2, OS_OPT_TIME_HMSM_STRICT,&err);	//至少延时130微秒后,nRF24L01模块进入接收状态
	
    while(DEF_ON) {
		
		IWDG_Feed();			//每个任务都会调用看门狗程序
		
		//*************************使用中断方式*************************************		
		OSTaskSemPend(0,
					  OS_OPT_PEND_BLOCKING,
					  &ts,		//信号量等待的时间
					  &err);
		
		switch(err)	{																				//处理Pend的结果		
			case OS_ERR_NONE:
				OSTmrDel(&ClientDeviceTimeout_Tmr,&err);					//正确接收到反馈数据包则删除定时器
				NRF24L01_Read_Buf(RD_RX_PLOAD, n24l01_buf, RX_PLOAD_WIDTH);	//从nRF24L01中读取接收到的数据
				NRF24L01_Write_Reg(FLUSH_RX, 0xff);							//清除RX FIFO接收数据寄存器
				switch(snd_type){											//根据上位机发送指令的方式分别处理
					#if ETHNET_EN > 0u
					case Ethernet:											//以太网方式
						if (custom_udp_send.cus_pbuf!=NULL) {
							custom_udp_send.cus_pbuf->payload=n24l01_buf;	//得到反馈数据包
							custom_udp_send.cus_pbuf->tot_len=custom_udp_send.cus_pbuf->len=sizeof(n24l01_buf);						
							udp_sendto(custom_udp_send.cus_udp_pcb, 		//使用当前pbuf发送反馈数据包到客户端
										custom_udp_send.cus_pbuf, 
										custom_udp_send.cus_ip_addr, 
										custom_udp_send.cus_udp_port);											
							pbuf_free(custom_udp_send.cus_pbuf);			//释放缓冲池中的当前pbuf对象
							custom_udp_send.cus_pbuf=NULL;
						}
						break;
					#endif
					
					#if RS232_EN > 0u
					case RS232:												//RS232串口方式
						USART1_SendData(n24l01_buf, sizeof(n24l01_buf));
						break;
					#endif
					
					#if HF_LPB100_EN > 0u
					case WiFi:												//Wi-Fi方式(需要Wi-Fi模块作为服务端)
						USART3_SendData(n24l01_buf, sizeof(n24l01_buf));
						break;
					#endif
					
					default:
						break;
				}
				break;
				
			case OS_ERR_PEND_ABORT:																	//等待(挂起)状态被其他任务打断
				break;
			
			case OS_ERR_OBJ_DEL:																	//内核对象已被删除
				break;
			
			default:
				break;
		}
		
//		//*************************原来的方式,使用查询法****************************
//		
//		if(NRF24L01_RxPacket(n24l01_buf, &chl)==0) {					//如果正确接收到数据			
//			//nRF24L01_BUF[31]=chl;										//最后一个字节表示模块接收到数据的通道号(暂时不用)
//			USART1_SendData(n24l01_buf,sizeof(n24l01_buf));				//测试接收到的数据
//			OSTmrDel(&ClientDeviceTimeout_Tmr,&err);					//正确接收到反馈数据包则删除定时器
//			
//			switch(snd_type){											//根据上位机发送指令的方式分别处理
//				#if ETHNET_EN > 0u
//				case Ethernet:											//以太网方式
//					if (custom_udp_send.cus_pbuf!=NULL) {
//						custom_udp_send.cus_pbuf->payload=n24l01_buf;	//得到反馈数据包
//						custom_udp_send.cus_pbuf->tot_len=custom_udp_send.cus_pbuf->len=sizeof(n24l01_buf);						
//						udp_sendto(custom_udp_send.cus_udp_pcb, 		//使用当前pbuf发送反馈数据包到客户端
//									custom_udp_send.cus_pbuf, 
//									custom_udp_send.cus_ip_addr, 
//									custom_udp_send.cus_udp_port);											
//						pbuf_free(custom_udp_send.cus_pbuf);			//释放缓冲池中的当前pbuf对象
//						custom_udp_send.cus_pbuf=NULL;
//					}
//					break;
//				#endif
//				
//				#if RS232_EN > 0u
//				case RS232:												//RS232串口方式
//					USART1_SendData(n24l01_buf, sizeof(n24l01_buf));
//					break;
//				#endif
//				
//				#if HF_LPB100_EN > 0u
//				case WiFi:												//Wi-Fi方式(需要Wi-Fi模块作为服务端)
//					USART3_SendData(n24l01_buf, sizeof(n24l01_buf));
//					break;
//				#endif
//				
//				default:
//					break;
//			}			
//		}
//		
//		OSTimeDlyHMSM(0, 0, 0, 1, OS_OPT_TIME_HMSM_NON_STRICT,&err);	//阻塞1毫秒,即延时1个时钟节拍	
    }
}
#endif


发送数据的代码就比较简单了,这里只列出部分代码:

#if nRF24L01_EN > 0u
								TX_Mode(addr_t);												//设置nRF24L01模块为发送模式								
								NRF24L01_TxPacket(arg);								
								OSTimeDlyHMSM(0, 0, 0, 1, OS_OPT_TIME_HMSM_STRICT,&err);		//根据手册说明,至少延时10微秒后开始发送,这里延时长了一些
								
								//******其他的方式发送数据******								
								//NRF24L01_CE=0;
								//NRF24L01_Write_Buf(WR_TX_PLOAD, arg, TX_PLOAD_WIDTH);			//写数据到TX BUF  最长32个字节
								//NRF24L01_CE=1;													//启动发送,10微秒后会发送数据
								//OSTimeDlyHMSM(0, 0, 0, 20, OS_OPT_TIME_HMSM_STRICT,&err);		//延时以等待数据发送完成并产生中断,至少拉高CE10微秒才开始发送															
								//RX_Mode();														//设置nRF24L01为接收模式,至少延时130微秒
								//OSTimeDlyHMSM(0, 0, 0, 2, OS_OPT_TIME_HMSM_STRICT,&err);
							#endif




你可能感兴趣的:(嵌入式开发,STM32)