两个STM32单片机通过nfr模块无线通讯

目录

一、原理

1.nRF24L01模块简介

2.通信说明

2.SPI协议简介

SPI物理层

协议层

3.模块使用方法

使用方法:

发送数据:


一、原理

1.nRF24L01模块简介

nRF24L01是由NORDIC生产的工作在2.4GHz~2.5GHz的ISM 频段的单片无线收发器芯片。无线收发器包括:频率发生器、增强型“SchockBurst”模式控制器、功率放大器晶体振荡器调制器解调器

输出功率频道选择和协议的设置可以通过SPI 接口进行设置。几乎可以连接到各种单片机芯片,并完成无线数据传送工作。

极低的电流消耗:当工作在发射模式下发射功率为0dBm 时电流消耗为11.3mA ,接收模式时为12.3mA,掉电模式和待机模式下电流消耗更低。

nRF24L01原理图

nRF24L01引脚定义

2.通信说明

每个nRF24L01模块都可以设置发送地址和接收地址,地址长度均为40位,两个模块间的通信是依靠地址来识别的,通信地址与接收地址相匹配,才能进行数据收发。

例如:

A模块的发送地址是A5A5A5A5,其意思就是向A5A5A5A5这个地址发送数据,所以B模块的接收地址必须是A5A5A5A5,这样才能接收到A模块发送的数据。因为存在应答机制,B模块会向给它发送数据的地址回复应答数据(也就是A5A5A5A5),所以A模块的接收地址也要是A5A5A5A5。

如果两个模块要互相发送数据,都将发送地址和接收地址设置为同一个即可

    nRF24L01 在接收模式下可以接收6路不同通道的数据,见上图。每个数据通道使用不同的地址,但是共用相同的频道。也就是说6个不同的 nRF24L01 设置为发送模式后可以与用一个设置为接收模式的 nRF24L01 进行通讯,而设置为接收模式的 nRF24L01 可以对这个6个发送端进行识别。数据通道0是唯一的一个可以配置为 40 位自身地址的数据通道。1~5数据通道都为8位自身地址和32位公用地址。所有的数据通道都可以设置为增强型 ShockBurst 模式。

    nRF24L01 在确认收到数据后记录地址,并以此地址为目标地址发送应答信号。在发送端,数据通道0被用作接收应答信号,因此,数据通道0的接收地址要与发送端地址相等以确保接收到正确的应到信号。见下图选地址举例。

模块在同一时间内只能是发送模式或者接收模式,为了确保模块能及时接收到数据,一般都是让模块处于接收状态,要发送数据时再切换到发送状态,数据发送完后立即切换到接收状态。

当模块接收到数据后,IRQ引脚会输出低电平,所以我们在程序中检测到IRQ变为低电平后,读取模块的接收区即可。   

2.SPI协议简介

SPI 协议是由摩托罗拉公司提出的通讯协议 (Serial Peripheral Interface) ,即串行外围设备接口,是 一种高速全双工的通信总线。它被广泛地使用在 ADC LCD 等设备与 MCU 间,要求通讯速率
较高的场合。

SPI物理层

SPI 通讯使用 3 条总线及片选线, 3 条总线分别为 SCK MOSI MISO ,片选线为,它们的作用介
绍如下:
(1) ( Slave Select) :从设备选择信号线,常称为片选信号线,也称为 NSS CS ,以下用 NSS 表示。
当有多个 SPI 从设备与 SPI 主机相连时,设备的其它信号线 SCK MOSI MISO 同时并联到相
同的 SPI 总线上,即无论有多少个从设备,都共同只使用这 3 条总线;而每个从设备都有独立的
这一条 NSS 信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号
线。 I2C 协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而 SPI 协议中没
有设备地址,它使用 NSS 信号线来寻址,当主机要选择从设备时,把该从设备的 NSS 信号线设
置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI 通讯。
所以 SPI 通讯以 NSS 线置低电平为开始信号,以 NSS 线被拉高作为结束信号。
(2) SCK (Serial Clock) :时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,
不同的设备支持的最高时钟频率不一样,如 STM32 SPI 时钟频率最大为 f pclk /2 ,两个设备之间
通讯时,通讯速率受限于低速设备。
(3) MOSI (Master Output Slave Input) :主设备输出 / 从设备输入引脚。主机的数据从这条信号线输
出,从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。
(4) MISO(Master Input, Slave Output) :主设备输入 / 从设备输出引脚。主机从这条信号线读入数
据,从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。

协议层

I2C 的类似, SPI 协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。
SPI 通讯的通讯时序
这是一个主机的通讯时序。 NSS SCK MOSI 信号都由主机控制产生,而 MISO 的信号由从机
产生,主机通过该信号线读取从机的数据。 MOSI MISO 的信号只在 NSS 为低电平的时候才有
效,在 SCK 的每个时钟周期 MOSI MISO 传输一位数据。
以上通讯流程中包含的各个信号分解如下:
1.通讯的起始和停止信号
在图 SPI 通讯时序 中的标号处, NSS 信号线由高变低,是 SPI 通讯的起始信号。 NSS 是每个从机
各自独占的信号线,当从机在自己的 NSS 线检测到起始信号后,就知道自己被主机选中了,开
始准备与主机通讯。在图中的标号处, NSS 信号由低变高,是 SPI 通讯的停止信号,表示本次通
讯结束,从机的选中状态被取消。
2. 数据有效性
SPI 使用 MOSI MISO 信号线来传输数据,使用 SCK 信号线进行数据同步。 MOSI MISO
据线在 SCK 的每个时钟周期传输一位数据,且数据输入输出是同时进行的。数据传输时, MSB
先行或 LSB 先行并没有作硬性规定,但要保证两个 SPI 通讯设备之间使用同样的协定,一般都
会采用图 SPI 通讯时序 中的 MSB 先行模式。
观察图中的标号处, MOSI MISO 的数据在 SCK 的上升沿期间变化输出,在 SCK 的下降沿时
被采样。即在 SCK 的下降沿时刻, MOSI MISO 的数据有效,高电平时表示数据“ 1 ”,为低电
平时表示数据“ 0 ”。在其它时刻,数据无效, MOSI MISO 为下一次表示数据做准备。
SPI 每次数据传输可以 8 位或 16 位为单位,每次传输的单位数不受限制。
3. CPOL/CPHA 及通讯模式
上面讲述的图 SPI 通讯时序 中的时序只是 SPI 中的其中一种通讯模式, SPI 一共有四种通讯模式,
它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻。为方便说明,在此引入“时
钟极性 CPOL ”和“时钟相位 CPHA ”的概念。
时钟极性 CPOL 是指 SPI 通讯设备处于空闲状态时, SCK 信号线的电平信号 ( SPI 通讯开始前、
NSS 线为高电平时 SCK 的状态 ) CPOL=0 时, SCK 在空闲状态时为低电平, CPOL=1 时,则相
反。
时钟相位 CPHA 是指数据的采样的时刻,当 CPHA=0 时, MOSI MISO 数据线上的信号将会在
SCK 时钟线的“奇数边沿”被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿”采样。见图
CPHA = 0 时的 SPI 通讯模式 及图 CPHA = 1 时的 SPI 通讯模式
我们来分析这个 CPHA=0 的时序图。首先,根据 SCK 在空闲状态时的电平,分为两种情况。 SCK
信号线在空闲状态为低电平时, CPOL=0 ;空闲状态为高电平时, CPOL=1
无论 CPOL=0 还是 =1 ,因为我们配置的时钟相位 CPHA=0 ,在图中可以看到,采样时刻都是在
SCK 的奇数边沿。注意当 CPOL=0 的时候,时钟的奇数边沿是上升沿,而 CPOL=1 的时候,时钟
的奇数边沿是下降沿。所以 SPI 的采样时刻不是由上升 / 下降沿决定的。 MOSI MISO 数据线的
有效信号在 SCK 的奇数边沿保持不变,数据信号将在 SCK 奇数边沿时被采样,在非采样时刻,
MOSI MISO 的有效信号才发生切换。
类似地,当 CPHA=1 时,不受 CPOL 的影响,数据信号在 SCK 的偶数边沿被采样,见图 CPHA=1
时的 SPI 通讯模式 _

3.模块使用方法

使用方法:

  1. 将 nfr.c 和 nfr.h 文件添加到工程

  2. 调用SPI_NRF_Init()对SPI接口进行初始化

  3. 调用函数NRF_Check()对模块进行通信检测,如果返回值为SUCCESS则表明开发板和模块通信正常

  4. 调用NRF_RX_Mode()函数,将模块设置为接收模式

  5. 在while循环中调用NRF_Rx_Dat()函数,如果函数返回值为RX_DR,则表明接收到数据

发送数据:

  1. 调用NRF_TX_Mode()函数,将模块切换为发送模式

  2. 调用NRF_Tx_Dat()函数来发送数据,如果返回值为TX_DS,则数据发送成功

  3. 调用NRF_RX_Mode()函数,将模块设置为接收模式

  4. 将这两个地址设置成一致(两台开发板以上地址要也要一模一样)

  5. 模块与开发板的连接:

    驱动代码中使用SPI5与模块连接,连接如下

    nRF24l01       开发板

        GND<--->GND

        3.3V<--->3.3V

        CE<--->PG9

        CSN<--->PG3

        SCK<--->PF7

        MOSI<--->PF9

        MISO<--->PF8

        IRQ<--->PC3

  6. int couter = 0;
    uint8_t key1;
    uint8_t key2;
    int8_t touch=0 ;
    int8_t touch2=0 ;
    uint32_t delay_time = 50000;
    uint32_t system_time = 0;
    
    //编写中断服务函数
    void EXTI0_IRQHandler()//中断服务函数名称
    {
        if(EXTI_GetITStatus(EXTI_Line0)!=RESET )//证明触发中断
    		{
    				//100ms = 100 000us
    				delay_time += 10000;
    				EXTI_ClearITPendingBit(EXTI_Line0);
    		}
    }
    void EXTI15_10_IRQHandler()
    {
    	   if(EXTI_GetITStatus(EXTI_Line13)!=RESET )//证明触发中断
    		{
    			delay_time-=10000;
    			EXTI_ClearITPendingBit(EXTI_Line13);
    		}
    }
    uint8_t chect_state ;
    uint8_t rec_state ;
    uint8_t rec_buf[32] = {0};
    int main(void)
    {
    		
    	//初始化串口 USART1
    	//因为要用PA9 和PA10所以先初始化
    		GPIO_InitTypeDef gpio_info;	
    
    		LED_init();
        key1_init();
        key2_init();
    		Delay_init();
    		ADC_init();
    		ADC_GetValue();
    		UART_Init(115200);
    		
    		SPI_NRF_Init();
    		chect_state = NRF_Check();	//通讯检测
    		if(chect_state == SUCCESS )
    		{	
    			printf ("init SUCCESS\r\n");
    		}
    		else 
    		{
    			printf ("init FAIL\r\n");
    		}
    	
        //初始化PI11蜂鸣器
    		//5.使能串口
    		USART_Cmd(USART1,ENABLE);	
    		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOI,ENABLE);
        
        gpio_info.GPIO_Mode = GPIO_Mode_OUT;
        gpio_info.GPIO_OType = GPIO_OType_PP;
        gpio_info.GPIO_Pin = GPIO_Pin_11;
        gpio_info.GPIO_PuPd = GPIO_PuPd_UP;
        gpio_info.GPIO_Speed = GPIO_Low_Speed;  
        GPIO_Init(GPIOI,&gpio_info);
    
    		//将模块切换到接收模式
    		NRF_RX_Mode();
    		LED_Green_Ctrl(LED_OFF);
    		LED_Blue_Ctrl(LED_OFF);
    		LED_Red_Ctrl(LED_OFF);
        while(1)
    		{
    			//轮询检测,是否收到数据,如果返回值为RX_DR,表明收到数据
    			rec_state = NRF_Rx_Dat(rec_buf);
    			if(rec_state == RX_DR)
    			{
    				//表明收到了数据,数据保存在rec_buf数组中
    				printf ("rec_state\r\n");
    
    					if(rec_buf[0]==0x33)
    				{
    					LED_Red_Ctrl( LED_ON);
    					LED_Green_Ctrl(LED_OFF );
    					LED_Blue_Ctrl(LED_OFF);
    
    				}
    				
    				}
    				if(rec_buf[0]==0x44)
    				{
    					LED_Red_Ctrl( LED_OFF);
    					LED_Green_Ctrl(LED_OFF );
    					LED_Blue_Ctrl(LED_OFF);
    				}
    				
    				if(rec_buf[1]==0x66)
    				{
    					LED_Red_Ctrl( LED_OFF);
    					LED_Green_Ctrl(LED_OFF );
    					LED_Blue_Ctrl(LED_ON);
    				}
    				if(rec_buf[1]==0x55)
    				{
    					LED_Red_Ctrl( LED_OFF);
    					LED_Green_Ctrl(LED_OFF );
    					LED_Blue_Ctrl(LED_OFF);
    				}
    			if(GPIO_ReadInputDataBit(GPIOA ,GPIO_Pin_0)==1)
    			{
    				Delay_ms(100);
    				if(GPIO_ReadInputDataBit(GPIOA ,GPIO_Pin_0)==1)
    			{
    				uint8_t send_buf[32] = {0};
    				uint8_t send_state;
    				touch++;
    				
    				NRF_TX_Mode();
    				if(touch==1)
    				{
    					send_buf [0] = 0x33;
    					send_buf [1] = 0x44;
    				}
    				if(touch==2)
    				{
    					send_buf [0] = 0x44;
    					send_buf [1] = 0x33;
    					touch=0;
    				}
    				
    			
    				send_state = NRF_Tx_Dat(send_buf);
    				if(send_state == TX_DS)
    				{
    					printf ("send SUCCESS\r\n");
    				}
    				else 
    				{
    					printf ("send FAIL\r\n");
    				}
    				//NRF_RX_Mode();
    				}			
    			}
    	//KEY2
    				if(GPIO_ReadInputDataBit(GPIOC ,GPIO_Pin_13)==1)
    			{
    				Delay_ms(100);
    				if(GPIO_ReadInputDataBit(GPIOC ,GPIO_Pin_13)==1)
    			{
    				uint8_t send_buf[32] = {0};
    				uint8_t send_state;
    				touch2++;
    				
    				NRF_TX_Mode();
    				if(touch2==1)
    				{
    					send_buf [0] = 0x55;
    					send_buf [1] = 0x66;
    				}
    				if(touch2==2)
    				{
    					send_buf [0] = 0x66;
    					send_buf [1] = 0x55;
    					touch2=0;
    				}			
    				send_state = NRF_Tx_Dat(send_buf);
    				if(send_state == TX_DS)
    				{
    					printf ("send SUCCESS\r\n");
    				}
    				else 
    				{
    					printf ("send FAIL\r\n");
    				}
    				NRF_RX_Mode();
    				}			
    			}
    		}
    	}
    

    以上代码可以实现在串口助手中显示两台开发板是否通讯成功,A按下KEY1,A发送成功B接收成功,A按下KEY2,B开发板的红色LED亮,再次按下熄灭,B同样可以控制A。

(以上内容均来自野火STM32开发板资料及老师教学内容)

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
51单片机是一种常用的单片机芯片,而nrf24l01是一款常用的无线收发模块。编写51单片机nrf24l01的例程可以实现无线通信功能。 首先,需要在51单片机中配置相应的引脚与寄存器,以便与nrf24l01进行通信。然后,可以按照以下步骤编写例程: 1. 初始化nrf24l01模块:设置通信频率、数据速率、地址宽度等参数,并初始化寄存器。 2. 配置发送端与接收端:设置发送端与接收端的地址,以确保通信的正确性。 3. 发送数据:在发送端,将要发送的数据写入发送缓冲区,并启动发送。 4. 接收数据:在接收端,不断检测接收缓冲区,如果有数据接收到,则将接收到的数据存储到引脚,供单片机读取。 5. 处理数据:根据需要,可以在单片机中对接收到的数据进行处理和分析,比如判断命令、执行相应的操作等。 6. 控制流程:根据业务需求,可以通过单片机控制nrf24l01的工作方式,比如启动与停止发送与接收,切换发送与接收模式等。 需要注意的是,在编写例程过程中,需要特别关注nrf24l01单片机的通信协议,以及相应的时序要求。同时,为了保证通信的稳定性,还需要考虑一些干扰因素,比如信号强度、避免冲突等。 综上所述,编写51单片机nrf24l01的例程可以实现无线通信功能,将数据通过无线传输的方式发送与接收,从而实现远距离的数据传输与控制。这对于一些需要无线通信的应用场景,比如遥控、数据采集等,具有很高的实用价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值