您现在的位置是:首页 >技术杂谈 >NRF24L01学习操作教程(二)——NRF实现一对一,一对多通讯网站首页技术杂谈

NRF24L01学习操作教程(二)——NRF实现一对一,一对多通讯

Joseph Wen 2024-06-17 10:48:36
简介NRF24L01学习操作教程(二)——NRF实现一对一,一对多通讯

上篇博客链接:https://blog.csdn.net/DIVIDADA/article/details/130599974?spm=1001.2014.3001.5501


以下单片机例程都是基于STM32 HAL库,在文档末尾,我会提供参考博客和源码程序的链接。

通讯实例与代码实现

在CubeMx中配置单片机时钟、SPI通讯接口、NRF24L01接口等,并生成Keil工程

image-20230514000842277

将NRF24L01的驱动程序的.c文件和.h文件添加到工程目录下,重新编译程序

image-20230514001231068

nrf24l01实现一对一单向通讯

1.流程分析:

A端:

  • 设置成发送模式(TX_Mode(0))
  • 每隔100ms发送一次数据(NRF24L01_TxPacket())

B端:

  • 设置成接收模式(RX_Mode(0),B端地址设置要同A端一致)
  • 循环判断是否接受到数据(NRF24L01_RxPacket())

2.源码程序

A端:

//....................
uint8_t tx_buf[8];
//....................
int main(void)
{
    //...................
    NRF24L01_Init(); //NRF24L01初始化
    while(NRF24L01_Check());  //检测NRF24L01模块是否在位
    TX_Mode(0);  //将NRF24L01模块设置为发送模式,传入参数0设置地址
    //....................
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        HAL_Delay(100);
        if(NRF24L01_TxPacket(tx_buf)==0X20)  //NRF24L01模块发送数据并判断是否发送成功
        {
            HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);  //若发送成功,LED电平反转
            tx_buf[0]++;  //若发送成功,tx_buf[0]自增加
        }

    }
    /* USER CODE END 3 */
}

B端:

//....................
uint8_t rx_buf[8];
//....................
int main(void)
{
    //...................
    NRF24L01_Init(); //NRF24L01初始化
    while(NRF24L01_Check());  //检测NRF24L01模块是否在位
    RX_Mode(0);  //将NRF24L01模块设置为发送模式,传入参数0设置地址(同发送模块地址相同)
    //....................
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        if(NRF24L01_RxPacket(rx_buf)==0X00)  //NRF24L01模块接收数据并判断是否接收成功
        {
            HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);  //若接收成功,LED电平反转
        }

    }
    /* USER CODE END 3 */
}

3.实验结果

在B端程序中进入debug模式,将rx_buf添加到watch窗口中,可以观察到rx_buf[0]值不断增加

image-20230514012007998

nrf24l01实现一对一双向通讯

1.流程分析:

A端:

  • 设置成发送模式(TX_Mode(0))
  • 发送一次数据(NRF24L01_TxPacket())
  • 发送完成立马转变成接收模式(RX_Mode(0))
  • 等待接收数据成功(while(READ_NRF24L01_IRQ!=0))
  • 接收数据成功后立马转换发送模式(TX_Mode(0))
  • 进入下一次收发数据,如此循环

B端:

  • 设置成接收模式(RX_Mode(0),B端地址设置要同A端一致)
  • 循环判断是否接受到数据(NRF24L01_RxPacket())
  • 接收到数据成功后立马转换成发送模式(TX_Mode(0))
  • 发送成后立马转换成接收模式(RX_Mode(0)),方便下一次接收

实际上这种双向收发的方式对A、B端的时序要求非常高,通俗点说就是A端处于发送模式并发送数据的时候,B端一定要处于接收模式,否则就是两个哑巴在通讯。所以为了保证通讯的稳定性,需要对上述的通讯流程进行一定的优化,具体优化的地方就是:A端在等待接收数据成功的时候(while(READ_NRF24L01_IRQ!=0)计数等待,若计数值超过阈值,再发送一次,最多发送10次;B端在发送数据时候计数,若计数值超过阈值还未发送成功,立马转变成接收模式。具体实现方法可以见下面的源程序。

经过测试,这种双向收发机制,通讯十分稳定(NICE)

2.源码程序

A端:

//....................
uint8_t tx_buf[8];
uint8_t rx_buf[8];

void NRF_Intercommunication(uint8_t Seq_Nrf,uint8_t *Txbuf, uint8_t *Rxbuf);
//....................
int main(void)
{
    //...................
    NRF24L01_Init(); //NRF24L01初始化
    while(NRF24L01_Check());  //检测NRF24L01模块是否在位
    TX_Mode(0);  //将NRF24L01模块设置为发送模式,传入参数0设置地址
    //....................
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
		HAL_Delay(100);
		HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
		NRF_Intercommunication(0,tx_buf,rx_buf);  //进行一次双向通讯
    }
    /* USER CODE END 3 */
}
//.........................
void NRF_Intercommunication(uint8_t Seq_Nrf,uint8_t *Txbuf, uint8_t *Rxbuf)
{
	uint8_t j=1;
    uint16_t cnt_01=0,cnt_02=0;
    while(j--)
    {
        TX_Mode(Seq_Nrf);   //设置为发送模式
        NRF24L01_TxPacket(Txbuf);//将数据发送出去
        RX_Mode(Seq_Nrf);               //将数据发送出去后立马转换成接收模式
        while(READ_NRF24L01_IRQ!=0)  //等待接收成功
        {
            if(++cnt_01>0XAFFF)       //如果计数值超过阈值(0XAFFF),则跳出接收等待
                break;
        }

        if(cnt_01>0XAFFF)  //若计数值超过阈值,j赋值1,再次进行一次数据收发
        {
            cnt_01=0;
            if(++cnt_02<10)  //最多重复进行十次数据收发
            {
                j=1;
                continue;
            }
        }
        NRF24L01_RxPacket(Rxbuf);    //读取接收缓存区数据
        cnt_01=0;cnt_02=0;
    }
}

B端:

//....................
uint8_t tx_buf[8] = {0};
uint8_t rx_buf[8] = {0};
uint8_t Mode=0;//1代表发送模式,0代表接收模式
uint8_t Tx_cnt; //发送次数计数
//....................
int main(void)
{
    //...................
    NRF24L01_Init(); //NRF24L01初始化
    while(NRF24L01_Check());  //检测NRF24L01模块是否在位
    RX_Mode(0);  //将NRF24L01模块设置为发送模式,传入参数0设置地址(同发送模块地址相同)
    //....................
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
		if(Mode==1)
		{
			Tx_cnt++;
			if(NRF24L01_TxPacket(tx_buf)==0x20)//发送数据成功
			{
                tx_buf[0]++;
                Tx_cnt=0;
				Mode=0;    //转变为接收模式
				RX_Mode(0); //一但发送成功则变成接收模式;
			} 
			
			if(Tx_cnt==3)  //如果连续发送3次都失败,则转换为接收模式
			{
				Tx_cnt=0;
				Mode=0;     //转变为接收模式
				RX_Mode(0);   //一但达到最大发送次数则变成接收模式;
			}
		}
		
		if(Mode==0)
		{
			if(NRF24L01_RxPacket(rx_buf)==0X00)//一旦接收成功则变成发送模式;
			{
				HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
				Mode=1;//转变为发送模式
                TX_Mode(0);
			}
		}

    }
    /* USER CODE END 3 */
}

3.实验结果

在A端程序中进入debug模式,将rx_buf添加到watch窗口中,可以观察到rx_buf[0]值不断增加

image-20230514025835277

nrf24l01实现一对多双向通讯

以1对2双向通讯为例,给B端、C端设置不同的地址(Mode(0)和Mode(1)),A端地址设为Mode(0)先与B端进行双向通讯,然后设为Mode(1)先与C端进行双向通讯,如此循环就可实现1对2双向通讯

1.流程分析:

A端:

  • 设置成发送模式,先与B端通讯(TX_Mode(0))
  • 发送一次数据(NRF24L01_TxPacket())
  • 发送完成立马转变成接收模式(RX_Mode(0))
  • 等待接收数据成功(while(READ_NRF24L01_IRQ!=0))
  • 接收数据成功后立马转换发送模式,与C端通讯(TX_Mode(1))
  • 发送一次数据(NRF24L01_TxPacket())
  • 发送完成立马转变成接收模式(RX_Mode(1))
  • 等待接收数据成功(while(READ_NRF24L01_IRQ!=0))
  • 接收数据成功后立马转换发送模式,如此循环与B端、C端通讯

B端:

  • 设置成接收模式(RX_Mode(0),B端地址设置要同A端一致)
  • 循环判断是否接受到数据(NRF24L01_RxPacket())
  • 接收到数据成功后立马转换成发送模式(TX_Mode(0))
  • 发送成后立马转换成接收模式(RX_Mode(0)),方便下一次接收

C端:

  • 设置成接收模式(RX_Mode(1),C端地址设置要同A端一致)
  • 循环判断是否接受到数据(NRF24L01_RxPacket())
  • 接收到数据成功后立马转换成发送模式(TX_Mode(1))
  • 发送成后立马转换成接收模式(RX_Mode(1)),方便下一次接收

2.源码程序

A端:

//....................
uint8_t tx_buf[8];
uint8_t rx_buf[8];

void NRF_Intercommunication(uint8_t Seq_Nrf,uint8_t *Txbuf, uint8_t *Rxbuf);
//....................
int main(void)
{
    //...................
    NRF24L01_Init(); //NRF24L01初始化
    while(NRF24L01_Check());  //检测NRF24L01模块是否在位
    TX_Mode(0);  //将NRF24L01模块设置为发送模式,传入参数0设置地址
    //....................
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        for(int i=0;i<2;i++)
        {
            HAL_Delay(100);
            HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
            NRF_Intercommunication(i,tx_buf,rx_buf);  //与B端、C端各进行一次双向通讯
        }
    }
    /* USER CODE END 3 */
}
//.........................
void NRF_Intercommunication(uint8_t Seq_Nrf,uint8_t *Txbuf, uint8_t *Rxbuf)
{
	uint8_t j=1;
    uint16_t cnt_01=0,cnt_02=0;
    while(j--)
    {
        TX_Mode(Seq_Nrf);   //设置为发送模式
        NRF24L01_TxPacket(Txbuf);//将数据发送出去
        RX_Mode(Seq_Nrf);               //将数据发送出去后立马转换成接收模式
        while(READ_NRF24L01_IRQ!=0)  //等待接收成功
        {
            if(++cnt_01>0XAFFF)       //如果计数值超过阈值(0XAFFF),则跳出接收等待
                break;
        }

        if(cnt_01>0XAFFF)  //若计数值超过阈值,j赋值1,再次进行一次数据收发
        {
            cnt_01=0;
            if(++cnt_02<10)  //最多重复进行十次数据收发
            {
                j=1;
                continue;
            }
        }
        NRF24L01_RxPacket(Rxbuf);    //读取接收缓存区数据
        cnt_01=0;cnt_02=0;
    }
}

B端:

//....................
uint8_t tx_buf[8] = {0};
uint8_t rx_buf[8] = {0};
uint8_t Mode=0;//1代表发送模式,0代表接收模式
uint8_t Tx_cnt; //发送次数计数
//....................
int main(void)
{
    //...................
    NRF24L01_Init(); //NRF24L01初始化
    while(NRF24L01_Check());  //检测NRF24L01模块是否在位
    RX_Mode(0);  //将NRF24L01模块设置为发送模式,传入参数0设置地址(同发送模块地址相同)
    //....................
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
		if(Mode==1)
		{
			Tx_cnt++;
			if(NRF24L01_TxPacket(tx_buf)==0x20)//发送数据成功
			{
                tx_buf[0]++;
                Tx_cnt=0;
				Mode=0;    //转变为接收模式
				RX_Mode(0); //一但发送成功则变成接收模式;
			} 
			
			if(Tx_cnt==3)  //如果连续发送3次都失败,则转换为接收模式
			{
				Tx_cnt=0;
				Mode=0;     //转变为接收模式
				RX_Mode(0);   //一但达到最大发送次数则变成接收模式;
			}
		}
		
		if(Mode==0)
		{
			if(NRF24L01_RxPacket(rx_buf)==0X00)//一旦接收成功则变成发送模式;
			{
				HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
				Mode=1;//转变为发送模式
                TX_Mode(0);
			}
		}

    }
    /* USER CODE END 3 */
}

C端:

C端代码除了地址设置与B端不同外,其余均相同

//....................

//....................
int main(void)
{
    //...................
    NRF24L01_Init(); //NRF24L01初始化
    while(NRF24L01_Check());  //检测NRF24L01模块是否在位
    RX_Mode(1);  //将NRF24L01模块设置为发送模式,传入参数1设置地址(同发送模块地址相同)
    //....................
    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
		if(Mode==1)
		{
            //....................
            RX_Mode(1);
            
		}
		if(Mode==0)
		{
        	//....................
            TX_Mode(1);
		}
    }
    /* USER CODE END 3 */
}

3.实验结果

同样通过debug模式,可以观察到接收数据。

总结

以上内容详细介绍了,使用NRF24L01实现1对1单向通讯、1对1双向通讯、1对多双向通讯;想要程序源码的可以进我的github仓库自取:https://github.com/HaoJosephWen/Code-of-blog,或评论区留下邮箱。

参考博客

NRF24L01 的双向通信_nrf24l01怎么配对_努力学习cs的博客-CSDN博客

NRF24L01一对多通信方法_noting_to_talk的博客-CSDN博客

nrf24l01中文资料_工作原理_教程_程序 – 瑞生网 (rationmcu.com)

新手如何快速搞通NRF24L01通信 – 瑞生网 (rationmcu.com)

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。