先看模块,如下图:
一个模块的使用,必须先阅读产品文档,我为了学习这个模块,特地将资料文档下载下来,逐一阅读理解,这样以后才能灵活使用其模块。
NRF24L01+模块的使用还是有一定的复杂度的,复杂度和我之前写的红外通信模块相比,不可相提并论。为什么要使用这个模块,主要还是因为它相对红外通信模块传输距离较远,数据传输较为稳定,这样以后对小车的控制将比较灵活,也是以后转移到物联网上的一个方向。
话不多说,进入正题吧,NRF24L01模块内部不含单片机,所以需要单片机或者微控制器驱动控制才可以使用该模块,本文章,我是使用的51系列单片机(12C5A60S2)进行对NRF24L01模块进行控制,下面来叙述一下本文所要完成的功能。
功能实现:
如何实现这样的功能呢?其实这个功能实现也是在网上参照别人写的博客学习的,该博客网址:http://www.51hei.com/bbs/dpj-92127-1.html 基于51单片机的nrf24l01无线的接受和发射程序
代码很全,我只是在原有基础上稍作改动。先看一下我的硬件实施,如图:
代码如下:
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的数据了。
下面的博客将不使用串口,而是通过两个单片机上的按键进行互相控制对方的LED灯,这样就为以后小车的开发更近一步了,加油。