NRF24L01模块----6通道通讯测试

2023-10-27

一、MCU与NRF24L01通讯

       采用SPI通讯协议,速率最大为10M,与普通SPI元器件稍有不同的是,多了一个CE引脚,用来开启接收、发送,以及使器件进入待机模式。具体看IC DATASHEET说明。

       写寄存器指令格式为:1、SPI写寄存器地址 + 0x20

                                           2、SPI写参数

       读寄存器指令格式为:1、SPI写寄存器地址

                                            2、SPI读参数      

       但少数几个指令,只需写入一个参数,如:

       

       读STATUS寄存器: 不能使用NOP指令,读取出来参数不正确。

                                        应使用读普通寄存器的方式,STATUS寄存器地址为0x07;

 

二、数据的发送

       当设置芯片的寄存器,“CE”引脚需要为低电平。

       用到以下几个寄存器:

       1、将接收端地址写入发送地址寄存器“TX_ADDR

       2、将接收端地址写入PIPE0通道地址寄存器“RX_ADDR_P0”,开启自动应答后,PIPE0将接收接收端的应答信号。

       3、使能“EN_AA”寄存器开启自动应答,使能“EN_RXADDR”中PIPE0对应的bit。

       4、设置重发寄存器“SETUP_RETR”,设置重发次数以及时间间隔。

       5、设置发送频道的频率“RF_CH”,以及发送功率、速率“RF_SETUP

       6、设置配置寄存器“CONFIG”,开启中断以及设置发送。

       如果以上寄存器设置完毕,拉低“CE”将需要发送的数据通过“WR_TX_PLOAD”指令写入TX_FIFO,"CE"拉高“10us”以上即可

       开启发送。以上寄存器设置完后,不必每次设置,接下来的发送只要不断通过“WR_TX_PLOAD”写数据至TX_FIFO即可。

 

       发送完毕后,根据发送情况产生2种中断:“TX_DS”中断以及“MAX_RT”中断

       1、对于“TX_DS”中断,可在处理标志位后,开启下一帧传输或进行其他处理。

       2、对于“MAX_RT”中断,需清除TX_FIFO。

三、数据的接收       

       1、设置接收通道,以及通道的地址“RX_ADDR_P0”~“RX_ADDR_P5”

       2、设置接收通道接收数据的字节数“RX_PW_P0”~“RX_PW_P5”

       3、设置“EN_AA”、“EN_RXADDR”、“RF_CH”、“RF_SETUP”、“CONFIG”等寄存器

       4、拉高“CE”开启接收。

 

注意:1、假设需要开启PIPE3,接收数据。那么PIPE0~2也必须开启。即,不能单独开启后面的通道,而关闭前面的通道。

           2、地址传入芯片是低字节先行。例如:

                 设定P0地址为:0x01,0x02,0x03,0x04,0x05

                 写入地址的顺序为:0x05,0x04,0x03,0x02,0x01

           3、单对单传输,在“RX_DR”中断中接收数据一般不会有问题,如果是6发送,1接收。可能会出现,中断中数据尚

                 未接收完毕,再次产生RX_DR中断。第二次产生的中断MCU是检测不到的。此时应将接收数据的操作放于中

                 断外。

 

四、程序

       1、FREERTOS模块

/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether 
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * Copyright (c) 2018 STMicroelectronics International N.V. 
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without 
  * modification, are permitted, provided that the following conditions are met:
  *
  * 1. Redistribution of source code must retain the above copyright notice, 
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of STMicroelectronics nor the names of other 
  *    contributors to this software may be used to endorse or promote products 
  *    derived from this software without specific written permission.
  * 4. This software, including modifications and/or derivative works of this 
  *    software, must execute solely and exclusively on microcontroller or
  *    microprocessor devices manufactured by or for STMicroelectronics.
  * 5. Redistribution and use of this software other than as permitted under 
  *    this license is void and will automatically terminate your rights under 
  *    this license. 
  *
  * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" 
  * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
  * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
  * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 
  * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os.h"

/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "usart.h"
#include "bsp_NRF24L01.h"
#include "bsp_key.h"
/* USER CODE END Includes */

/* Variables -----------------------------------------------------------------*/
osThreadId defaultTaskHandle;
osThreadId Task_KEYSCANHandle;
osThreadId Task_NRF2401Handle;
osMutexId Mutex_USARTHandle;

/* USER CODE BEGIN Variables */

/* USER CODE END Variables */

/* Function prototypes -------------------------------------------------------*/
void StartDefaultTask(void const * argument);
void Task_KEYSCAN(void const * argument);
void Task_NRF2401(void const * argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */


int fputc(int c, FILE * f)
{
    HAL_UART_Transmit(&huart1,(uint8_t *)&c,1,1000);//发送串口
    return c;
}



void MX_FREERTOS_Init(void) {

  osMutexDef(Mutex_USART);
  Mutex_USARTHandle = osMutexCreate(osMutex(Mutex_USART));



  /* Create the thread(s) */
  /* definition and creation of defaultTask */
  osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
  defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);

  /* definition and creation of Task_KEYSCAN */
  osThreadDef(Task_KEYSCAN, Task_KEYSCAN, osPriorityLow, 0, 128);
  Task_KEYSCANHandle = osThreadCreate(osThread(Task_KEYSCAN), NULL);

  /* definition and creation of Task_NRF2401 */
  osThreadDef(Task_NRF2401, Task_NRF2401, osPriorityBelowNormal, 0, 256);
  Task_NRF2401Handle = osThreadCreate(osThread(Task_NRF2401), NULL);


}




//GPIO_9外部中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	 uint8_t STATUS_Temp;
	
	  if(GPIO_Pin == NRF2401_IRQ_Pin)  
	{

		STATUS_Temp = NRF24L01_Read_REG(NRF24_RREG + STATUS);
			
		if(NRF_Dev->Mode == NRF_RXMode)
		{						 
				if(STATUS_Temp&0x40)  //接收数据中断
				{
					RX_Handle( STATUS_Temp>>1&0x07 );
				}							 
		}
		else
		{
				if(STATUS_Temp&0x20)     //数据发送成功中断
				{
					TX_DSHandle();
				}
				else   //此次中断为发送失败中断
				{
		
					TX_RTHandle();	

				}			
					 
		}
                NRF24L01_Write_REG(NRF24_WREG + STATUS,STATUS_Temp);  //清除STATUS寄存器中
                                                                      //的中断位			
	}
}







/* StartDefaultTask function */
void StartDefaultTask(void const * argument)
{

  NRFStructInit();  //初始化NRF24L01
	
  for(;;)
  {

    osDelay(1);
  }
  /* USER CODE END StartDefaultTask */
}

/* StartTask02 function */
void Task_KEYSCAN(void const * argument)
{

  for(;;)
  {
     ScanKey();
     osDelay(50);
  }

}


/* NRF2401_TXRX function */
void Task_NRF2401(void const * argument)
{
  uint32_t i;
	
  for(;;)
  {
		
		if(KeySingle(KEY_0))  //KEY0被单次触发,发送数据
		{
				
			printf("\r\n发送模式\r\n");
				
			for(i=0;i<6;i++)
			{
				printf("发送数据至接收端通道:%d\r\n",i);
                                 NRF_Dev->TX_S->TXPIPE = i;
				TX_Mode();

                                 TX_Package();
						
				osDelay(10);  //等待printf函数输出完毕,实际发送数据可取消此延时
						
				while(NRF_Dev->IRQ_S->TxFinish == 0 && NRF_Dev->IRQ_S->TxMAXRT == 0);
				NRF_Dev->IRQ_S->TxMAXRT = 0;
				NRF_Dev->IRQ_S->TxFinish = 0;
				
			}
				   
		}
			
		if(KeySingle(KEY_1))  //KEY1被单次触发,切换为接收模式
		{
			printf("接收模式");
			RX_Mode();
		}
			
        osDelay(1);
  }

}

/* USER CODE BEGIN Application */
     
/* USER CODE END Application */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

 2、NRF24L01模块

  

/**************************************************************
*  NRF24L01程序模块
*  
*  测试板为原子战舰V3,使用外设为SPI1,SPI分频系数为8分频
*
**************************************************************/

#include "bsp_NRF24L01.h"

//收发端地址相反,否则由于地址问题无法通讯
//#define TX_Device

#ifdef  TX_Device

//接收端6通道地址
uint8_t NRF_TXAddr[TXADRR_NUM][5] = 
{
         {0x0,0x11,0x11,0x11,0x11},
         {0x1,0x11,0x11,0x11,0x11},
	 {0x2,0x11,0x11,0x11,0x11},
	 {0x3,0x11,0x11,0x11,0x11},
	 {0x4,0x11,0x11,0x11,0x11},
	 {0x5,0x11,0x11,0x11,0x11},
};

//发送端(本机)6通道接收地址
uint8_t NRF_RXAddr[6][5] = 
{

	 {0x0,0x22,0x22,0x22,0x22},
	 {0x1,0x22,0x22,0x22,0x22},
	 {0x2,0x22,0x22,0x22,0x22},
	 {0x3,0x22,0x22,0x22,0x22},
	 {0x4,0x22,0x22,0x22,0x22},
	 {0x5,0x22,0x22,0x22,0x22},

};

#else

uint8_t NRF_TXAddr[TXADRR_NUM][5] = 
{
	 {0x0,0x22,0x22,0x22,0x22},
	 {0x1,0x22,0x22,0x22,0x22},
	 {0x2,0x22,0x22,0x22,0x22},
	 {0x3,0x22,0x22,0x22,0x22},
	 {0x4,0x22,0x22,0x22,0x22},
	 {0x5,0x22,0x22,0x22,0x22},
};

uint8_t NRF_RXAddr[6][5] = 
{
         {0x0,0x11,0x11,0x11,0x11},
         {0x1,0x11,0x11,0x11,0x11},
	 {0x2,0x11,0x11,0x11,0x11},
	 {0x3,0x11,0x11,0x11,0x11},
	 {0x4,0x11,0x11,0x11,0x11},
	 {0x5,0x11,0x11,0x11,0x11},


};

#endif



//数据发送缓冲数组
uint8_t NRF_TXBuff[32] = "NRF24L01通讯测试";

//发送数据size管理数组
uint8_t NRF_TXPxSize[TXADRR_NUM] = {32,32,32,32,32,32};



//接收数据缓存数组
uint8_t NRF_RXBuff[6][32];

//接收数据size管理数组
uint8_t NRF_RXPxSize[6] = {32,32,32,32,32,32};


//NRF管理结构体
NRF_Struct     NRF_S, *NRF_Dev = &NRF_S;
NRF_TXStruct   NRF_TXS;
NRF_RXStruct   NRF_RXS;
NRF_IRQStruct  NRF_IRQS;



/**
  * @brief  NRF24L01管理结构体初始化
  *
  * @Note   默认开启6个通道, 
  *         发送字节数为32字节
  *         6个通道接收字节数位32字节
  *
  * @retval None
  */
void NRFStructInit(void)
{

	  NRF_Dev->RX_S = &NRF_RXS;
	  NRF_Dev->TX_S = &NRF_TXS;
	  NRF_Dev->IRQ_S = &NRF_IRQS;
	
          NRF_Dev->Mode = NRF_RXMode;


	  NRF_Dev->RX_S->PIPEx = 5;
	  NRF_Dev->RX_S->Size = NRF_RXPxSize;		 
	  NRF_Dev->RX_S->ADDR = (uint8_t *)&NRF_RXAddr;
	  NRF_Dev->RX_S->Buff = (uint8_t *)&NRF_RXBuff;
	  

	  NRF_Dev->TX_S->TXPIPE = 0;
	  NRF_Dev->TX_S->ReCount = 15;
	  NRF_Dev->TX_S->ReTime  = 1;
	  NRF_Dev->TX_S->Size = NRF_TXPxSize;		
          NRF_Dev->TX_S->RF_Ch = 40;
          NRF_Dev->TX_S->RF_DR = DR_2Mbps;		
          NRF_Dev->TX_S->RF_PWR = PWR_0dBm;				
          NRF_Dev->TX_S->ADDR = (uint8_t *)&NRF_TXAddr;
          NRF_Dev->TX_S->Buff = NRF_TXBuff;	  
	 
	
	  NRF_Dev->IRQ_S->TxFinish = 0;
	  NRF_Dev->IRQ_S->TxMAXRT = 0;
	
	  RX_Mode();  //初始化为接收模式

}


//SPI与NRF2401读写通信
uint8_t NRF24L01_RW(uint8_t d_send)
{
	uint8_t d_read;
	
  if(HAL_SPI_TransmitReceive(&hspi1,&d_send,&d_read,1,0xFFFFFF)!=HAL_OK)
    d_read=0xff;
  
  return d_read;    
}


//  NRF24L01写SPI指令
void SPI_WReg(uint8_t reg)
{
	NRF24L01_CE_L
	NRF24L01_Write_REG(reg, 0);      
	NRF24L01_CE_H
}


//SPI读取NRF2401寄存器数据
uint8_t NRF24L01_Read_REG(uint8_t reg)
{
	uint8_t status;
	
	NRF24L01_CS_L

	NRF24L01_RW(reg);
	
	status = NRF24L01_RW(0);
	
	NRF24L01_CS_H	
       return status;    
}

//SPI写NRF2401寄存器数据
uint8_t NRF24L01_Write_REG(uint8_t reg,uint8_t value)
{
	uint8_t status;
	
	NRF24L01_CS_L

	status = NRF24L01_RW(reg);
	
	NRF24L01_RW(value);
	
	NRF24L01_CS_H
	
       return status;    
}


//SPI连续读取NRF2401寄存器数据
uint8_t NRF24L01_Read_Buff(uint8_t reg,uint8_t *pBuf,uint8_t Count)
{
	uint8_t status,i;
	
	NRF24L01_CS_L

	status = NRF24L01_RW(reg);
	
      for(i=0;i<Count;i++)
	{
	    pBuf[i] = NRF24L01_RW(0); 
	}
	
	NRF24L01_CS_H	
      return status;    
}

//SPI连续写NRF2401寄存器数据
uint8_t NRF24L01_Write_Buff(uint8_t reg,uint8_t *pBuf,uint8_t Count)
{
	uint16_t j = 720;
	uint8_t status,i;
	
	NRF24L01_CS_L

	status = NRF24L01_RW(reg);
	
	while(j--);  //短暂延时
	
      for(i=0;i<Count;i++)
	{
	    status = NRF24L01_RW(*pBuf++);
	}

	NRF24L01_CS_H
	
  return status;    
}


//SPI与NRF2401通讯自检
//通过则代表MCU SPI通讯NRF2401成功
//可用于测试器件之间的连接
uint8_t NRF24L01_Check(void)
{
	uint8_t buf[5]={0x01,0x02,0x03,0x04,0x05};
	uint8_t buf1[5];
	uint8_t i; 
	
	NRF24L01_Write_Buff(NRF24_WREG+TX_ADDRReg,buf,5); //开机第一次写入不成功
	
	NRF24L01_Write_Buff(NRF24_WREG+TX_ADDRReg,buf,5); /*写入5个字节的地址.  */  
	NRF24L01_Read_Buff(TX_ADDRReg,buf1,5); /*读出写入的地址 */	   	
	
	
	for(i=0;i<5;i++)   	/*比较*/ 
	{
		if(buf1[i] != i+1)
		break;
	}   
	
	if(i==5)
		return SUCCESS ;        //MCU与NRF成功连接 
	else
		return ERROR ;        //MCU与NRF不正常连接
}



/**
  * @brief  NRF24L01设置为发送模式
  *
  * @retval None
  */
void TX_Mode(void)
{
	//发送地址不能大于发送地址二维数组个数上限,发送size不能大于32
	if(  NRF_Dev->TX_S->TXPIPE > TXADRR_NUM -1 || \
		   NRF_Dev->TX_S->Size[NRF_Dev->TX_S->TXPIPE] > 32 ) return ;
	
	NRF_Dev->Mode = NRF_TXMode;
	
	NRF24L01_CE_L
	
	NRF24L01_Write_Buff(NRF24_WREG + TX_ADDRReg, \
	                    (uint8_t *)NRF_Dev->TX_S->ADDR + NRF_Dev->TX_S->TXPIPE*5, 5);  //设置发送地址
	
	NRF24L01_Write_Buff(NRF24_WREG + RX_ADDR_P0, \
	                    (uint8_t *)NRF_Dev->TX_S->ADDR + NRF_Dev->TX_S->TXPIPE*5, 5);  //设置本机地址,P0接收应答
	
	NRF24L01_Write_REG(NRF24_WREG + EN_AA, 0x01);      // Enable Auto.Ack:Pipe0
	NRF24L01_Write_REG(NRF24_WREG + EN_RXADDR, 0x01);  // Enable Pipe0
	
	NRF24L01_Write_REG(NRF24_WREG + SETUP_RETR,  \
	                   NRF_Dev->TX_S->ReCount + (NRF_Dev->TX_S->ReTime<<4)); // 500us + 86us, 10 retrans...
	
	NRF24L01_Write_REG(NRF24_WREG + RF_CH, NRF_Dev->TX_S->RF_Ch);        // Select RF channel 
	
	NRF24L01_Write_REG(NRF24_WREG + RF_SETUP,    \
	                   NRF_Dev->TX_S->RF_DR<<3 | NRF_Dev->TX_S->RF_PWR<<1);   // TX_PWR:0dBm, Datarate:2Mbps,
										 
	NRF24L01_Write_REG(NRF24_WREG + CONFIG, 0x0e);     // Set PWR_UP bit, enable CRC(2 bytes)

}


void TX_Package(void)
{
	NRF_Dev->Mode = NRF_TXMode;
	
	NRF24L01_CE_L
	
	NRF24L01_Write_Buff(WR_TX_PLOAD,
						(uint8_t *)NRF_Dev->TX_S->Buff , \
						NRF_Dev->TX_S->Size[NRF_Dev->TX_S->TXPIPE]);  // Writes data to TX payload
	
	
	NRF24L01_CE_H
}



/**
  * @brief  NRF24L01设置为接收模式 
  *
  * @retval None
  */
void RX_Mode(void)
{
	uint8_t i,j = 5;

  NRF_Dev->Mode = NRF_RXMode;	
	
	NRF24L01_CE_L
	
	for(i=0;i<NRF_Dev->RX_S->PIPEx+1;i++)
	{
		  if(i>1) j = 1;

		NRF24L01_Write_Buff(NRF24_WREG + RX_ADDR_P0 + i, \
							(uint8_t *)NRF_Dev->RX_S->ADDR + 5*i, j);  //设置接收通道以及通道的地址	

		NRF24L01_Write_REG(NRF24_WREG + RX_PW_P0 + i, \
						   NRF_Dev->RX_S->Size[i]);               //设置接收通道接收数据的长度	
		
	}
	
	
	NRF24L01_Write_REG(NRF24_WREG + EN_AA, ( 2<<NRF_Dev->RX_S->PIPEx) - 1 );     // Enable Auto.Ack:PipeX
	NRF24L01_Write_REG(NRF24_WREG + EN_RXADDR, ( 2<<NRF_Dev->RX_S->PIPEx) - 1  ); // Enable PipeX

	NRF24L01_Write_REG(NRF24_WREG + RF_CH, NRF_Dev->TX_S->RF_Ch);    // Select RF channel 
	
	NRF24L01_Write_REG(NRF24_WREG + RF_SETUP,  \
	                   NRF_Dev->TX_S->RF_DR<<3 | NRF_Dev->TX_S->RF_PWR<<1);   // TX_PWR:0dBm, Datarate:2Mbps,
	
	NRF24L01_Write_REG(NRF24_WREG + CONFIG, 0x0f);    // Set PWR_UP bit, enable CRC(2 bytes)
		
	NRF24L01_CE_H
}


/**
  * @brief  NRF24L01接收处理函数
  *
  * @param  reg: NRF2401状态寄存器的值
  *         @arg 0 - 5
  * @retval None
  */
void RX_Handle(uint8_t reg)
{
	
	if( reg < 6 )  //获取接收数据pipe编号,溢出则不进行接收处理
	{
		 NRF24L01_Read_Buff(NRF24_RREG + RD_RX_PLOAD, \
		                   (uint8_t *)NRF_Dev->RX_S->Buff + reg*32, \
		                   NRF_Dev->RX_S->Size[reg]);  //接收数据	
		
		SPI_WReg(FLUSH_RX); //清除RX FIFO

		printf("\r\n数据来自通道%d\r\n",reg);
		printf("%s\r\n",(uint8_t *)NRF_Dev->RX_S->Buff + reg*32);		
		
		
	}
	 
}


/**
  * @brief  NRF24L01发送成功处理函数
  *
  * @retval None
  */
void TX_DSHandle(void)
{
	  NRF_Dev->IRQ_S->TxFinish = 1;
}


/**
  * @brief  NRF24L01达到最大重发数处理函数
  *
  * @retval None
  */
void TX_RTHandle(void)
{
	  SPI_WReg(FLUSH_TX);  //重发送溢出,需清除TX FIFO,否则再次触发发送
	  NRF_Dev->IRQ_S->TxMAXRT = 1;
}











 3、NRF24L01头文件定义

#ifndef __bsp_NRF24L01_H__
#define __bsp_NRF24L01_H__

#include "stm32f1xx_hal.h"
#include "main.h"
#include "spi.h"


#define  TXADRR_NUM   6


#define  vu8   volatile uint8_t
#define  vu16  volatile uint16_t
#define  vu32  volatile uint32_t


#define  DR_1Mbps     0
#define  DR_2Mbps     1

#define  PWR_0dBm     3
#define  PWR_6dBm     2
#define  PWR_16dBm    1
#define  PWR_18dBm    0

typedef  struct  
{
	vu8 RF_PWR;		  //发射功率
	vu8 RF_DR;		  //发射速率
	vu8 RF_Ch;	    //发射通道
	vu8 TXPIPE;     //发送地址
	vu8 ReCount;    //重发送次数
	vu8 ReTime;     //重发送时间
	vu8 *Size;	    //发送数据字节数
	vu8 *Buff;	    //发送数据指针
	vu8 *ADDR;		  //发送地址指针
}NRF_TXStruct;

typedef  struct  
{
	vu8 PIPEx;	    //接收通道开启 0~5
    vu8 *Size;      //接收字节数
	vu8 *Buff;      //接收缓存指针
	vu8 *ADDR;			//接收通道地址指针
}NRF_RXStruct;


typedef  struct  
{
	vu8 TxMAXRT;    //重发送中断标志位
	vu8 TxFinish;		//发送完成中断标志位
}NRF_IRQStruct;



typedef  struct  
{
    vu8 Mode;
    NRF_IRQStruct *IRQ_S; 	
    NRF_TXStruct  *TX_S;
    NRF_RXStruct  *RX_S;
	
}NRF_Struct;

extern   NRF_Struct  *NRF_Dev;


#define  NRF_TXMode     0
#define  NRF_RXMode     1


/************************NRF引脚定义*********************/
#define NRF24L01_CS_L    HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_RESET);
#define NRF24L01_CS_H    HAL_GPIO_WritePin(SPI1_CS_GPIO_Port, SPI1_CS_Pin, GPIO_PIN_SET);

#define NRF24L01_CE_L    HAL_GPIO_WritePin(NRF2401_CSN_GPIO_Port, NRF2401_CSN_Pin, GPIO_PIN_RESET);
#define NRF24L01_CE_H    HAL_GPIO_WritePin(NRF2401_CSN_GPIO_Port, NRF2401_CSN_Pin, GPIO_PIN_SET);



/************************SPI 接口寄存器定义*********************/
#define NRF24_RREG 0x00 // Define read command to register
#define NRF24_WREG 0x20 // Define write command to register
#define RD_RX_PLOAD 0x61 // Define RX payload register address
#define WR_TX_PLOAD 0xA0 // Define TX payload register address
#define FLUSH_TX 0xE1 // Define flush TX register command
#define FLUSH_RX 0xE2 // Define flush RX register command
#define REUSE_TX_PL 0xE3 // Define reuse TX payload register command
#define NOP 0xFF // Define No Operation, might be used to read status


/**********************NRF2401功能寄存器定义********************/
#define CONFIG 0x00 // 'Config' register address

#define EN_AA 0x01 // 'Enable Auto Acknowledgment' register address
#define EN_RXADDR 0x02 // 'Enabled RX addresses' register address
#define SETUP_AW 0x03 // 'Setup address width' register address
#define SETUP_RETR 0x04 // 'Setup Auto. Retrans' register address
#define RF_CH 0x05 // 'RF channel' register address
#define RF_SETUP 0x06 // 'RF setup' register address
#define STATUS 0x07 // 'Status' register address
#define OBSERVE_TX 0x08 // 'Observe TX' register address
#define CD 0x09 // 'Carrier Detect' register address
#define RX_ADDR_P0 0x0A // 'RX address pipe0' register address
#define RX_ADDR_P1 0x0B // 'RX address pipe1' register address
#define RX_ADDR_P2 0x0C // 'RX address pipe2' register address
#define RX_ADDR_P3 0x0D // 'RX address pipe3' register address
#define RX_ADDR_P4 0x0E // 'RX address pipe4' register address
#define RX_ADDR_P5 0x0F // 'RX address pipe5' register address
#define TX_ADDRReg 0x10 // 'TX address' register address
#define RX_PW_P0 0x11 // 'RX payload width, pipe0' register address
#define RX_PW_P1 0x12 // 'RX payload width, pipe1' register address
#define RX_PW_P2 0x13 // 'RX payload width, pipe2' register address
#define RX_PW_P3 0x14 // 'RX payload width, pipe3' register address
#define RX_PW_P4 0x15 // 'RX payload width, pipe4' register address
#define RX_PW_P5 0x16 // 'RX payload width, pipe5' register address
#define FIFO_STATUS 0x17 // 'FIFO Status Register' register address



void NRFStructInit(void);

uint8_t NRF24L01_RW(uint8_t d_send);

uint8_t NRF24L01_Read_REG(uint8_t reg);

uint8_t NRF24L01_Write_REG(uint8_t reg,uint8_t value);

uint8_t NRF24L01_Read_Buff(uint8_t reg,uint8_t *pBuf,uint8_t Count);

uint8_t NRF24L01_Write_Buff(uint8_t reg,uint8_t *pBuf,uint8_t Count);

uint8_t NRF24L01_Check(void);

void RX_Mode(void);

void TX_Mode(void);

void SPI_WReg(uint8_t reg);

void RX_Handle(uint8_t reg);

void TX_DSHandle(void);

void TX_RTHandle(void);

void TX_Package(void);

#endif

  注意:1、程序基于STM32CUBEMX生成,编译环境为MDK,硬件环境为F1原子战舰V3。

             2、使用程序,接收与发送端地址不同,具体使用宏定义“#define TX_Device”即可切换地址。

             3、两块板子烧好程序,上电后,先按下KEY1,将其中一个板子转换为接收模式,另一个板子开启发送。

开启两个串口助手,观察到现象为:

 

五、速度测试

       器件本身选定的是2Mbps的速度,那么我们能做的就是,将MCU的SPI通讯速率提升上去。所以程序作了如下改动。

1、不使用HAL的SPI库进行通讯,将SPI通讯函数改为:

//SPI与NRF2401读写通信
uint8_t NRF24L01_RW(uint8_t d_send)
{
	uint8_t d_read;

	for(d_read = 0; d_read<8; d_read++);	
	
    SPI1->DR = d_send;
	
	for(d_read = 0; d_read<8; d_read++);

    return SPI1->DR;    
}

2、FreeRTOS中发送任务,改为使用PIPE0连续发送10,000个32byte的数据包,数据总量为:312.5Kb

      需要注意的是,HAL库是在SPI通讯函数中使能SPI1外设,所以平时SPI1外设时没有使能的,需要我们手动使能

/* NRF2401_TXRX function */
void Task_NRF2401(void const * argument)
{
  uint32_t i;
	
  SPI1->CR1 |= 0x0040;  //使能SPI1
	
  SPI1->DR = 0xff;      //dummy write
	
  for(;;)
  {
		
			if(KeySingle(KEY_0))  //KEY0被单次触发,发送数据
			{
				
				printf("\r\n发送模式\r\n");

				TX_Mode();	
			
				for(i=0;i<10000;i++)
				{
                                       TX_Package();
						
					while(NRF_Dev->IRQ_S->TxFinish == 0 && NRF_Dev->IRQ_S->TxMAXRT == 0);
					NRF_Dev->IRQ_S->TxMAXRT = 0;
					NRF_Dev->IRQ_S->TxFinish = 0;
				}
				   
			}
			
			if(KeySingle(KEY_1))  //KEY1被单次触发,切换为接收模式
			{
			         printf("接收模式");
				 RX_Mode();
			}
			
      osDelay(1);
  }

}

3、接收中断处理函数中,加一个缓存标志“i”记录接收数据包的个数

void RX_Handle(uint8_t reg)
{
	volatile static uint32_t i=0;
	
	i++;
	
	if( reg < 6 )  //获取接收数据pipe编号,溢出则不进行接收处理
	{
		 NRF24L01_Read_Buff(NRF24_RREG + RD_RX_PLOAD, \
		                   (uint8_t *)NRF_Dev->RX_S->Buff + reg*32, \
		                    NRF_Dev->RX_S->Size[reg]);  //接收数据	
		
		SPI_WReg(FLUSH_RX); //清除RX FIFO
		
	}
	 
}

测试结果:

完整地接收到10000个数据包,按下输出按键同时,手机进行计时。。。别问我为什么不开个定时器,我懒。。。。

测量了几次,时间约为6.5S,据此计算,通信速率为:10000*32/1024/6.5 = 48Kb/s

听说网上有人不开启自动应答可以达到65Kb/s的,反正我是开了自动应答。。。不开启的就懒得测了。

 

六、距离测试

        跟距离有关的因素很多,我们能通过程序调整的有:1、发射功率

                                                                                           2、发射速率

                                                                                           3、频道

        所以将初始化参数更改如下:

void NRFStructInit(void)
{

	  NRF_Dev->RX_S = &NRF_RXS;
	  NRF_Dev->TX_S = &NRF_TXS;
	  NRF_Dev->IRQ_S = &NRF_IRQS;
	
          NRF_Dev->Mode = NRF_RXMode;


	  NRF_Dev->RX_S->PIPEx = 5;
	  NRF_Dev->RX_S->Size = NRF_RXPxSize;		 
	  NRF_Dev->RX_S->ADDR = (uint8_t *)&NRF_RXAddr;
	  NRF_Dev->RX_S->Buff = (uint8_t *)&NRF_RXBuff;
	  

	  NRF_Dev->TX_S->TXPIPE = 0;
	  NRF_Dev->TX_S->ReCount = 15;
	  NRF_Dev->TX_S->ReTime  = 15;
	  NRF_Dev->TX_S->Size = NRF_TXPxSize;		
          NRF_Dev->TX_S->RF_Ch = 80;
          NRF_Dev->TX_S->RF_DR = DR_1Mbps;		
          NRF_Dev->TX_S->RF_PWR = PWR_0dBm;				
          NRF_Dev->TX_S->ADDR = (uint8_t *)&NRF_TXAddr;
          NRF_Dev->TX_S->Buff = NRF_TXBuff;	  
	 
	
	  NRF_Dev->IRQ_S->TxFinish = 0;
	  NRF_Dev->IRQ_S->TxMAXRT = 0;
	
	  RX_Mode();  //初始化为接收模式

}

  未更改参数前,超过2米通讯会出现中断。

  参数更改后,无障碍下7米距离通讯正常。更远则由于环境限制未测试

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

NRF24L01模块----6通道通讯测试 的相关文章

  • 处理器指令周期执行时间

    我的猜测是 no operation 内在 ARM 指令应花费 1 168 MHz 来执行 前提是每个NOP在一个时钟周期内执行 我想通过文档验证这一点 有关处理器指令周期执行时间的信息是否有标准位置 我试图确定 STM32f407IGh6
  • c项目makefile多重定义错误

    这个问题是一个对应于创建的repexthis问题 在我的嵌入式 C 项目中 我有两个独立的板 我想为每个板创建两个 c 文件 master c 和 Slave c 其中包含自己的特定main 功能 我使用 stm32cumbemx 生成带有
  • 在没有 IDE 的情况下如何使用 CMSIS?

    我正在使用 STM32F103C8T6 并想使用 CMSIS 这本质上只是寄存器定义 没有代码 让我的生活更轻松 同时仍保持在较低水平 问题是我不知道如何安装该库以便在命令行上使用 Makefile 使用 所有文档似乎都与特定于供应商的 I
  • 以字符串形式接收数字(uart)

    我正在尝试通过 uart 接收一个包装为字符串的数字 我发送数字 1000 所以我得到 4 个字节 空字符 但是 当我使用 atoi 将数组转换为数字并将整数与 1000 进行比较时 我并不总是得到正确的数字 这是我用于接收号码的中断处理函
  • HAL库STM32常用外设教程(二)—— GPIO输入\输出

    HAL库STM32常用外设教程 二 GPIO输入 输出 文章目录 HAL库STM32常用外设教程 二 GPIO输入 输出 前言 一 GPIO功能概述 二 GPIO的HAl库驱动 三 GPIO使用示例 1 示例功能 四 代码讲解 五 总结
  • rt-thread studio中新建5.0不能用

    文章目录 一 版本对比 二 文件和文件夹打斜杠 在使用RT Thread studio创建新工程5 0版本的时候 结果发现新建完成之后程序不能正常运行 但是创建4 10版本的时候却能运行 那肯定是新版本出现了BUG 一 版本对比 首先对比了
  • 匹配 STM32F0 和 zlib 中的 CRC32

    我正在研究运行 Linux 的计算机和 STM32F0 之间的通信链路 我想对我的数据包使用某种错误检测 并且由于 STM32F0 有 CRC32 硬件 并且我在 Linux 上有带有 CRC32 的 zlib 所以我认为在我的项目中使用
  • 物联网网关

    物联网网关是 连接物联网设备和互联网的重要桥梁 它负责将物联网设备采集到的数据进行处理 存储和转发 使其能够与云端或其它设备进行通信 物联网网关的作用是实现物联网设备与云端的无缝连接和数据交互 物联网网关功能 数据采集 物联网网关可以从物联
  • SHT10温湿度传感器——STM32驱动

    实验效果 硬件外观 接线 3 3V供电 IIC通讯 代码获取 查看下方 END
  • [MM32硬件]搭建灵动微MM32G0001A6T的简易开发环境

    作为学习单片机的经典 自然是通过GPIO点亮LED 或者是响应按钮的外部中断例程 这我们看看SOP8封装的芯片MM32G0001A6T得引脚 除了VDD和GND固定外 我们可以使用PA14 PA1 PA13 PA15 PA2 PA3这六个G
  • 串口通讯第一次发送数据多了一字节

    先初始化IO再初始化串口 导致第一次发送时 多出一个字节数据 优化方案 先初始化串口再初始化IO 即可正常通讯
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • for循环延时时间计算

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 之前做led点亮的实验 好像是被delay函数影响了 因为delay参数设置的不对
  • STM32H5 Nucleo-144 board开箱

    文章目录 开发板资料下载 目标 点亮LD1 绿 LD2 黄 和LD3 红 三个LED灯 开箱过程 博主使用的是STM32CubeMX配置生成代码 具体操作如下 打开STM32CubeMX File gt New project 选择开发板型
  • 特殊寄存器

    特殊寄存器 文章目录 前言 一 背景 二 2 1 2 2 总结 前言 前期疑问 STM32特殊寄存器到底是什么 特殊寄存器怎么查看和调试代码 本文目标 记录和理解特殊寄存器 一 背景 最近在看ucosIII文章是 里面提到特殊寄存器 这就进
  • 核心耦合内存在 STM32F4xx 上可执行吗?

    尝试从 STM32F429s CCM 运行代码 但每当我命中 CCM 中的第一条指令时 我总是会遇到硬故障 并且 IBUSERR 标志被设置 该指令有效且一致 STM32F4xx 是否可能不允许从 CCM 执行 数据访问效果良好 alios
  • STM32 Nucleo 上的上升沿中断多次触发

    我正在使用 STM32 NUCLEO F401RE 微控制器板 我有一个扬声器 经过编程 当向上 向下推操纵杆时 可以按设定的量改变频率 我的问题是 有时 通常 当向上 向下推动操纵杆时 频率会增加 减少多次 这意味着 ISR 正在执行多次
  • 通过JTAG恢复STM32 MCU磨掉的标记

    我有一块可能带有 STM32 MCU 的板 我想为该板制作定制固件 因为库存板有很多问题 不幸的是 电路板制造商很友善地磨掉了所有标记 有没有办法通过 jtag 获取设备 系列 ID 并将其交叉引用到型号 我能找到的一切都是关于获取芯片的唯
  • 当端点和 PMA 地址均更改时,CubeMX 生成的 USB HID 设备发送错误数据

    我正在调试我正在创建的复合设备的问题 并在新生成的仅 CubeMX 代码中重新创建了该问题 以使其更容易解决 我添加了少量代码main 让我发送 USB HID 鼠标点击 并在按下蓝色按钮时使 LED 闪烁 uint8 t click re
  • 移动数组中的元素

    我需要一点帮助 我想将数组中的元素向上移动一个元素 以便新位置 1 包含位置 1 中的旧值 new 2 包含 old 1 依此类推 旧的最后一个值被丢弃 第一个位置的新值是我每秒给出的新值 我使用大小为 10 的数组 uint32 t TE

随机推荐

  • ChatGPT 真的太强大了!

    如果你问我 2023年最大的机会是什么 那一定是 ChatGPT 从2月份GPT3 5版本发布 到现在的4 0PLUS升级版 ChatGPT的迭代速度已经完全超过了我们的想象 它已经可以替代很多行业的工作 比如说 文案 程序 客服 设计 律
  • redis集群环境搭建以及java中jedis客户端集群代码实现

    最近项目中用到了redis 所以就找了相关的资料学习了 然后搭建了redis集群 以及客户端的java代码实现 由于对linux不太熟悉 所以在Linux上搭建redis集群的时候碰到了很多问题 下面就大概总结下 redis相关网站 官网
  • 内核中关于启动条件的英文说明整理

    内核中关于启动条件的说明 Kernel startup entry point This is normally called from the decompressor code The requirements are MMU off
  • MiniNet的安装与使用

    直接下载Openflow Tutorial中已经安装好mininet的虚拟机镜像 http www openflow org wk index php OpenFlow Tutorial 放在VMWare中直接运行起来即可 VMware自身
  • Leetcode刷题日志2.0

    目录 前言 1 数字的补数 2 最大连续 1 的个数 3 下一个更大元素 I 4 斐波那契数 5 提莫攻击 6 557 反转字符串中的单词 III 前言 今天就分享一下最近刷到的leetcode习题吧 编程语言 Python3 废话不多说
  • 在Ubuntu上使用protobuf(C++)

    一 protobuf的下载和解压 protobuf提供了一些发布的版本 可以从里面下载指定语言的压缩包 例如 wget https github com protocolbuffers protobuf releases download
  • python从入门到放弃篇34(selenium库开启万花筒写轮眼)开启无界面模式,爬取豆瓣名著

    今天 突然发现 selenium库不开万花筒的话 有点脑热 因为 每爬一页就打开一个网页 除非我是撕裂者3990X的CPU 64核 128线的 但是 我的CPU不是 所以 怕到时候打开页面太多 直接卡机废了 我就去搜了开启万花筒模式 这样就
  • 【Python学习】Day-004 Day-005 if分支结构、三目运算符、for循环、while循环、循环关键字

    文章目录 1 分支结构 1 1 程序执行过程 1 2 if单分支结构 1 3 if双分支结构 1 4 if多分支结构 1 4 if嵌套结构 1 5 三目运算符 2 for循环 3 range函数 4 while循环 5 for循环和whil
  • 浏览器无法访问某个网站,其他网站都正常

    昨天在写完代码后 准备推送到远端配置的 Gitee 码云仓库 怎么也推不上去 一直报连接不到 然后在浏览器里试了下访问https gitee com 好吧 直接访问不到 但是访问其他网站都没有问题 当时和朋友连着语音 说到这个事情 他说应该
  • 小知识·Git常用命令

    1 强制推送 慎用 除非你认为其他冲突等可以丢弃 或者不是很重要 git push force 2 创建文件等小命令 touch a 创建一个a文件 echo 1234 gt gt a 把1234这个内容放入a文件 cat a 打开a文件
  • el-tree树设置懒加载以及设置默认勾选

    当需要对element ui组件懒加载进行拓展功能 如添加查看更多或者其他图标进行加载 可使用下面的方法进行调整使用 加载tree树时 要求能够通过点击查看更多进行懒加载 且可进行勾选复选框获取数据 由于界面存在多个Tree树 故命名多个r
  • 5. 一线大厂高并发缓存架构实战与性能优化

    分布式缓存技术Redis 1 冷热数据分离 2 缓存设计 2 1 缓存击穿 失效 2 2 缓存穿透 2 3 缓存雪崩 3 大V直播带货导致线上商品系统崩溃原因分析 4 突发性热点缓存重建导致系统压力暴增问题 5 缓存数据库双写不一致问题 6
  • 解类魔方算法(以C和JAVA为基础)

    这是15年前 环球科学杂志上的一篇文章 作者写了一个类似简单魔方的小游戏 M12 这是游戏说明 给大家翻译一下 游戏由两个按键控制 一个是 INVERT 另一个是 MERGE INVERT 英文的意思是 反向 能将 1 2 3 4 12 排
  • vscode 解决正在下载程序包“C/C++ language components (Linux / x86_64)” 失败引起的符号找不到问题

    1 问题 vscode ssh远程查看代码时 大纲无法罗列函数列表 根本原因是远程c c 插件没有安装成功 错误信息如下 正在下载程序包 C C language components Linux x86 64 失败 正在重试 失败 正在重
  • 前台-vue3使用animate.css

    cnpm install animate css save main ts import animated from animate css app use router use animated as any mount app 页面使用
  • BES固件烧录说明

    BES固件烧录说明 准备工具 烧录前准备 烧录步骤 单线烧录 单线升级的硬件要求 单线升级的软件要求 单线升级工具界面 单线升级步骤 详细演示了BES各芯片平台固件烧录的方法 包含串口烧录和单线烧录升级 适用于目前发布的BES2300系列
  • react + ts 配置项目路劲别名(import的时候使用@符号报错)

    前言 项目开发的过程中 tsx 页面中 import 引入项目的时候 ts 文件报错 找不到模块 xxx xxx 或其相对应的类型声明 其原因主要是因为目前 ts 对 指向 src 的目录的提示是不支持的 vite默认也不支持 所以需要手动
  • 小程序优惠券内圆角

  • Java技术——Java中的static关键字解析

    0 前言 static是Java中的重要的一个点 也是面试的时候经常被问到的点 如果理解不够很容易给面试官语言基础不扎实的印象 本文从static方法 static内部类 static变量 以及static代码块四个角度分别解析static
  • NRF24L01模块----6通道通讯测试

    一 MCU与NRF24L01通讯 采用SPI通讯协议 速率最大为10M 与普通SPI元器件稍有不同的是 多了一个CE引脚 用来开启接收 发送 以及使器件进入待机模式 具体看IC DATASHEET说明 写寄存器指令格式为 1 SPI写寄存器