2.4G无线模块(NRF24L01)学习(1)——串口实现两个模块之间信息交互

先看模块,如下图:

2.4G无线模块(NRF24L01)学习(1)——串口实现两个模块之间信息交互_第1张图片

2.4G无线模块(NRF24L01)学习(1)——串口实现两个模块之间信息交互_第2张图片

 

2.4G无线模块(NRF24L01)学习(1)——串口实现两个模块之间信息交互_第3张图片

        一个模块的使用,必须先阅读产品文档,我为了学习这个模块,特地将资料文档下载下来,逐一阅读理解,这样以后才能灵活使用其模块。

        NRF24L01+模块的使用还是有一定的复杂度的,复杂度和我之前写的红外通信模块相比,不可相提并论。为什么要使用这个模块,主要还是因为它相对红外通信模块传输距离较远,数据传输较为稳定,这样以后对小车的控制将比较灵活,也是以后转移到物联网上的一个方向。

        话不多说,进入正题吧,NRF24L01模块内部不含单片机,所以需要单片机或者微控制器驱动控制才可以使用该模块,本文章,我是使用的51系列单片机(12C5A60S2)进行对NRF24L01模块进行控制,下面来叙述一下本文所要完成的功能。

功能实现:

  1. 使用两个单片机控制两个NRF24L01模块,每个模块都可以接受或发送数据。
  2. 一个单片机需要通过串口与电脑相连(单片机A),为了显示接受数据,或发送数据;另外一个单片机我们称为单片机B。
  3. 需要完成:单片机A下发命令控制单片机B上的灯闪烁;按单片机B上的按键触发给单片机A发送数据

如何实现这样的功能呢?其实这个功能实现也是在网上参照别人写的博客学习的,该博客网址:http://www.51hei.com/bbs/dpj-92127-1.html              基于51单片机的nrf24l01无线的接受和发射程序

代码很全,我只是在原有基础上稍作改动。先看一下我的硬件实施,如图:

2.4G无线模块(NRF24L01)学习(1)——串口实现两个模块之间信息交互_第4张图片

代码如下:

1.单片机A的代码

主函数文件部分(test.c):

#include"2401.h"

#define uint unsigned int
#define uchar unsigned char

//1//sbit KEY1=P0^0;	 		//发送按键 
//1//sbit KEY2=P0^1;
//1//sbit KEY3=P0^2;
//1//sbit KEY4=P0^3;
sbit beep=P2^3;					//喇叭 
sbit LED6=P1^6;	 				//发送数据时显示灯+接收到数据后的功能实现灯

uchar Tx_Buf1[]={1};			//发送的信息1 
uchar Rx_Buf[32];  				//接收到的数据暂存器,最多32字节数据  
uchar uart_flag,a;			//串口接收标志【收到数据就置1】,a为接收到的数据
/*------------------------------------------------
延时函数
------------------------------------------------*/
void delay_ms(uint z)			//延时函数 
{
	uint y;
	while(z--)
		for(y=110;y>0;y--);
}
/*------------------------------------------------
定义UART_Init函数
------------------------------------------------*/
void UART_Init(void)    // 波特率9600
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	AUXR &= 0xFB;		//独立波特率发生器时钟为Fosc/12,即12T
	BRT = 0xFD;		//设定独立波特率发生器重装值
	AUXR |= 0x01;		//串口1选择独立波特率发生器为波特率发生器
	AUXR |= 0x10;		//启动独立波特率发生器
	ES = 1;		 		//允许串口中断
	EA = 1;		 		//允许总中断
}
/*------------------------------------------------
定义UART_Send_Byte函数
------------------------------------------------*/
void UART_Send_Byte(uchar byte)
{
	SBUF=byte;	  		//缓冲区装载要发送的字节数据
	while(TI==0);		//等待发送完毕,TI标志位会置1
	TI=0;				//清零发送完成标志位
}
/*------------------------------------------------
串口接收中断服务程序
------------------------------------------------*/
void UART(void) interrupt 4
{
	if(RI)	   			//检测接收完成标志位置1
	{
		RI=0;			//清零接收完成标志位
		a=SBUF;			//读取接收到的数据
		uart_flag = 1;	//中断标志位置1
	}
}
/*------------------------------------------------
main函数
------------------------------------------------*/
void main()
{
	 LED6=1;				//初始灯6熄灭   
	 uart_flag=0;			//串口标志初始为0
	 init_NRF24L01();		//初始化24L01
	 UART_Init();	    	//初始化串口

	 while(NRF24L01_Check())					//检查不到24l01则报警 
	 {
		beep=0;
		delay_ms(200);
		beep=1;
		delay_ms(200);
	 }
	 while(1)
	 {	
		RX_Mode();							//接收模式  
		while(!nRF24L01_RxPacket(Rx_Buf)) 	//等待接收数据,返回1则接收到数据,在等待接收数据期间,可以随时变成发送模式  
	    {
			if(uart_flag==1)				//当串口接受标志为1表示有数据过来
			{
				ES=0;	   						//关串口中断
			
				TX_Mode();	 					//发送模式 
				Tx_Buf1[0]=a-'0';			// wantin modify 2018.11.16		//将串口数据给发送缓冲区
			    nRF24L01_TxPacket(Tx_Buf1);		//发送命令数据24L01
				UART_Send_Byte('O');			//向串口发送已经传送
				UART_Send_Byte('K');
				UART_Send_Byte(':');
				UART_Send_Byte(a);
				UART_Send_Byte('\n');
				LED6=0;
			    delay_ms(300);
				LED6=1;
			    delay_ms(300);					//发送后LED1闪一下 

				ES=1;							//允许串口中断
				uart_flag=0;  					//中断标志位置0
				break;							//退出最近的循环,从而变回接收模式,这句关键
			}
		 }
		 if(Rx_Buf[1]==1)	   						//若接收到对应的数据则实现对应功能 
		 {
		     Rx_Buf[1]=0;				//清空数据 
		     LED6=0;
		 	 delay_ms(300);
		 	 LED6=1;
		 	 delay_ms(300);				//接收到数据 后闪烁	
             UART_Send_Byte('W');			//向串口发送已经传送
			 UART_Send_Byte('A');  
             UART_Send_Byte('N');			//向串口发送已经传送
			 UART_Send_Byte('T');  
             UART_Send_Byte('I');			//向串口发送已经传送
			 UART_Send_Byte('N');  
             UART_Send_Byte('\n');
		 }
         if(Rx_Buf[0]==2)	   						//若接收到对应的数据则实现对应功能 
		 {
		     Rx_Buf[0]=0;				//清空数据 
		     LED6=0;
		 	 delay_ms(300);
		 	 LED6=1;
		 	 delay_ms(300);				//接收到数据 后闪烁	
             UART_Send_Byte('O');			//向串口发送已经传送
			 UART_Send_Byte('K');  
             UART_Send_Byte('\n');
		 }
	 }
}

因为想使用NRF24L01模块就需要访问模块内的地址,进行控制数据的收发等,因此还需要NRF24L01模块代码,这个代码是拷贝别人的,亲测可用,代码如下(2401.h):

#ifndef __NRF24L01_H__
#define __NRF24L01_H__
#include
#define uchar unsigned char
#define uint unsigned int

sbit	CE	    =P1^0;
sbit	CSN		=P1^1;
sbit	SCK	    =P1^2;
sbit 	MOSI	=P1^3;
sbit 	MISO	=P1^4;
sbit	IRQ		=P1^5;

//uchar TxBuf[20]={"1234567890abcdefghij"};
#define TX_ADR_WIDTH    5   	// 5 uints TX address width
#define RX_ADR_WIDTH    5   	// 5 uints RX address width
#define TX_PLOAD_WIDTH  32  	// 32 uints TX payload
#define RX_PLOAD_WIDTH  32  	// 32 uints TX payload
uchar  TX_ADDRESS[TX_ADR_WIDTH]= {0xE7,0xE7,0xE7,0xE7,0xE7};	//本地地址
uchar  RX_ADDRESS[RX_ADR_WIDTH]= {0xE7,0xE7,0xE7,0xE7,0xE7};	//接收地址
///***************************************NRF24L01寄存器指令*******************************************************
#define READ_REG        0x00  	// 读寄存器指令
#define WRITE_REG       0x20 	// 写寄存器指令
#define RD_RX_PLOAD     0x61  	// 读取接收数据指令
#define WR_TX_PLOAD     0xA0  	// 写待发数据指令
#define FLUSH_TX        0xE1 	// 冲洗发送 FIFO指令
#define FLUSH_RX        0xE2  	// 冲洗接收 FIFO指令
#define REUSE_TX_PL     0xE3  	// 定义重复装载数据指令
#define NOP             0xFF  	// 保留
///*************************************SPI(nRF24L01)寄存器地址****************************************************
#define CONFIG          0x00  // 配置收发状态,CRC校验模式以及收发状态响应方式
#define EN_AA           0x01  // 自动应答功能设置
#define EN_RXADDR       0x02  // 可用信道设置
#define SETUP_AW        0x03  // 收发地址宽度设置
#define SETUP_RETR      0x04  // 自动重发功能设置
#define RF_CH           0x05  // 工作频率设置
#define RF_SETUP        0x06  // 发射速率、功耗功能设置
#define NRFRegSTATUS    0x07  // 状态寄存器
#define OBSERVE_TX      0x08  // 发送监测功能
#define CD              0x09  // 地址检测           
#define RX_ADDR_P0      0x0A  // 频道0接收数据地址
#define RX_ADDR_P1      0x0B  // 频道1接收数据地址
#define RX_ADDR_P2      0x0C  // 频道2接收数据地址
#define RX_ADDR_P3      0x0D  // 频道3接收数据地址
#define RX_ADDR_P4      0x0E  // 频道4接收数据地址
#define RX_ADDR_P5      0x0F  // 频道5接收数据地址
#define TX_ADDR         0x10  // 发送地址寄存器
#define RX_PW_P0        0x11  // 接收频道0接收数据长度
#define RX_PW_P1        0x12  // 接收频道1接收数据长度
#define RX_PW_P2        0x13  // 接收频道2接收数据长度
#define RX_PW_P3        0x14  // 接收频道3接收数据长度
#define RX_PW_P4        0x15  // 接收频道4接收数据长度
#define RX_PW_P5        0x16  // 接收频道5接收数据长度
#define FIFO_STATUS     0x17  // FIFO栈入栈出状态寄存器设置
///*****************************子函数集*********************************************************
uchar NRF24SPI_Send_Byte(uchar dat);
uchar SPI_WR_Reg(uchar reg, uchar value);
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar Len);
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar Len);
uchar nRF24L01_RxPacket(unsigned char* rx_buf);
void nRF24L01_TxPacket(unsigned char * tx_buf);
uchar SPI_RD_Reg(uchar reg);
void init_NRF24L01(void);
void TX_Mode(void);
void RX_Mode(void);
void NRF_Send(void);
uchar NRF24L01_Check(void);
//----------------------------------------------
//发送一字节[MOSI和MISO数据传递]
//----------------------------------------------
uchar NRF24SPI_Send_Byte(uchar dat)
{
  	uchar i;
	for (i = 0; i < 8; i++) //output 8-bit
	{
		//写入1位数据
		MOSI=(dat & 0x80);	//output 'uchar', MSB to MOSI
		dat<<= 1;           // shift next bit into MSB..
		
		//读取1位数据
		SCK = 1;                   	// Set SCK high..
		if (MISO){
		 	dat|= 1;
		}else{             			// capture current MISO bit
		 	dat &= 0xFE;
		}
		SCK = 0;                    // ..then set SCK low again
	}
	return(dat);                	// return read uchar
}
//----------------------------------------------
//延时n个10us
//----------------------------------------------
void Delay_n10us(uint n)	
{
   for(;n>0;n--)
   {
   	 unsigned char a,b;
     for(b=1;b>0;b--)
         for(a=2;a>0;a--);
   }
}
//----------------------------------------------
//NRF24L01检测是否存在
//----------------------------------------------
uchar NRF24L01_Check(void)
{
	uchar bu[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
	uchar bu1[5];
	uchar i;   	 
	SPI_Write_Buf(WRITE_REG+TX_ADDR,bu,5);	  //写入5个字节的地址.	
	SPI_Read_Buf(TX_ADDR,bu1,5);              //读出写入的地址  	
	for(i=0;i<5;i++)if(bu1[i]!=0XA5)break;					   
	if(i!=5)return 1;                               //NRF24L01不在位	
	return 0;		                                //NRF24L01在位
}	
//----------------------------------------------
//NRF24L01初始化
//---------------------------------------------- 	 
void init_NRF24L01(void)
{
	uchar buf[5]={0};
	Delay_n10us(10);
	CE = 0;    		// chip enable
	CSN= 0;    		// Spi disable 
	
	SPI_Read_Buf(TX_ADDR, buf, TX_ADR_WIDTH);//debug 测试原来的本地地址:复位值是:0xE7 0xE7 0xE7 0xE7 0xE7
  
//	SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);    // 写本地地址	
//	SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, RX_ADDRESS, RX_ADR_WIDTH); // 写接收端地址

//	
//	SPI_WR_Reg(WRITE_REG + EN_AA, 0x01);      //  频道0自动	ACK应答允许	
//	SPI_WR_Reg(WRITE_REG + EN_RXADDR, 0x01);  //  允许接收地址只有频道0,如果需要多频道可以参考Page21  
//	SPI_WR_Reg(WRITE_REG + SETUP_RETR, 0x1a); // 设置自动重发时间和次数:500us + 86us, 10 retrans...
//	SPI_WR_Reg(WRITE_REG + RF_CH, 22);        //   设置信道工作为2.4GHZ,收发必须一致
//	SPI_WR_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收数据长度,本次设置为32字节
//	SPI_WR_Reg(WRITE_REG + RF_SETUP, 0x07);   		//设置发射速率为1MHZ,发射功率为最大值0dB
//  
//  SPI_RD_Reg(WRITE_REG + EN_AA);
//  SPI_RD_Reg(WRITE_REG + EN_RXADDR);
//  SPI_RD_Reg(WRITE_REG + RF_CH);
//  SPI_RD_Reg(WRITE_REG + RX_PW_P0);
//  SPI_RD_Reg(WRITE_REG + RF_SETUP);
}
//----------------------------------------------
//函数:uchar SPI_Read(uchar reg)
//功能:NRF24L01的SPI时序,读取指定寄存器的内容
//---------------------------------------------- 
uchar SPI_RD_Reg(uchar reg)
{
	uchar reg_val;
	CSN = 0;                			// CSN low, initialize SPI communication...
	NRF24SPI_Send_Byte(reg);            // Select register to read from..
	reg_val = NRF24SPI_Send_Byte(0);    // ..then read registervalue
	CSN = 1;                			// CSN high, terminate SPI communication	
	return(reg_val);        			// return register value
}
//----------------------------------------------
//函数:uchar SPI_Write(uchar reg)
//功能:NRF24L01的SPI时序,写入指定寄存器的内容
//---------------------------------------------- 
uchar SPI_WR_Reg(uchar reg, uchar value)
{
	uchar status;
	CSN = 0;                   			// CSN low, init SPI transaction
	status = NRF24SPI_Send_Byte(reg);	// select register
	NRF24SPI_Send_Byte(value);          // ..and write value to it..
	CSN = 1;                   			// CSN high again
	return(status);            			// return nRF24L01 status uchar
}
//---------------------------------------------- 
//函数:uint SPI_Read_Buf(uchar reg, uchar *pBuf, uchar Len)
//功能: 用于读数据
//参数:reg:为寄存器地址
//		pBuf:为待读出数据地址
//		uchars:读出数据的个数
//---------------------------------------------- 
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar Len)
{
	uint status,i;
	CSN = 0;                    		// Set CSN low, init SPI tranaction
	status = NRF24SPI_Send_Byte(reg);   // Select register to write to and read status uchar
	for(i=0;i

 

2.单片机B的代码和单片机A的代码基本上一致,只是在test.c中修改了部分,大家可以查看就知道了,2401.h中代码不需要修改

test.c

#include"2401.h"

#define uint unsigned int
#define uchar unsigned char

sbit KEY8=P3^5;	 				//发送按键 
sbit beep=P1^7;					//喇叭 
sbit LED6=P1^6;	 				//发送数据时显示灯
sbit LED1=P2^0;					//接收到数据后的功能实现灯
sbit LED2=P2^1;
sbit LED3=P2^2;
sbit LED4=P2^3;  

void delay_ms(uint z)			//延时函数 
{
	uint y;
	while(z--)
		for(y=110;y>0;y--);
}
void main()
{
	 uchar Tx_Buf1[]={2,1};	//发送的信息1 
	 uchar Rx_Buf[32];  	//接收到的数据暂存器,最多32字节数据  
	 init_NRF24L01();
	 LED6=1;				//初始灯6熄灭   

	while(NRF24L01_Check())					//检查不到24l01则报警 
	{
		beep=0;
		delay_ms(200);
		beep=1;
		delay_ms(200);
	}
	while(1)
	{	
		RX_Mode();							//接收模式  
		while(!nRF24L01_RxPacket(Rx_Buf)) 	//等待接收数据,返回1则接收到数据,在等待接收数据期间,可以随时变成发送模式  
	    {
			if(KEY8==0)	 					//按了按键8,则变成发送模式,发送对应数据,发送完后变成接收模式 
			{	
				delay_ms(5);//消抖动 
				if(KEY8==0)
				{
			 		while(!KEY8);
					TX_Mode();	 //发送模式 
			    	nRF24L01_TxPacket(Tx_Buf1);		//发送命令数据
					LED6=0;
			    	delay_ms(300);
					LED6=1;
			    	delay_ms(300);					//发送后LED6闪一下 
					break;							//退出最近的循环,从而变回接收模式,这句关键
				 }	
			 }
		 }
//		 if(Rx_Buf[0]==1)	   						//若接收到对应的数据则实现对应功能 
//		 {
//		    Rx_Buf[0]=0;				//清空数据 
//		    LED6=0;
//		 	delay_ms(300);
//		 	LED6=1;
//			delay_ms(300);				//接收到数据 后闪烁	  
//		 }
		switch(Rx_Buf[0]){//对数据进行分析来控制灯亮
		case 0:
			break;
		case 1:
			Rx_Buf[0]=0;				//清空数据 
		    LED1=0;
            LED6=0;
			delay_ms(300);
			LED1=1;
            LED6=1;
			delay_ms(300);	
			break;
		case 2:
			Rx_Buf[0]=0;				//清空数据 
		    LED2=0;
            LED6=0;
			delay_ms(300);
			LED2=1;
            LED6=1;
			delay_ms(300);	
			break;
		case 3:
			Rx_Buf[0]=0;				//清空数据 
		    LED3=0;
            LED6=0;
			delay_ms(300);
			LED3=1;
            LED6=1;
			delay_ms(300);	
			break;
		default:
			Rx_Buf[0]=0;				//清空数据 
		    LED4=0;
            LED6=0;
			delay_ms(300);
			LED4=1;
            LED6=1;
			delay_ms(300);	
			break;
		}	 	
	}
}

 

这样代码就基本粘贴完了,大家可以测试一下,效果如下:

单片机A发送2时,单片机B的第二个灯就会闪烁,3就对应第三个灯。。。

当按下单片机B的按键时,单片机会通过串口发一串字符串“WANTIN”字样,表示单片机A接收到单片机B的数据了。

2.4G无线模块(NRF24L01)学习(1)——串口实现两个模块之间信息交互_第5张图片

2.4G无线模块(NRF24L01)学习(1)——串口实现两个模块之间信息交互_第6张图片

 

下面的博客将不使用串口,而是通过两个单片机上的按键进行互相控制对方的LED灯,这样就为以后小车的开发更近一步了,加油。

 

你可能感兴趣的:(单片机c语言)