自制单片机之十八……无线通讯模块NRF24L01+

时间:2022-08-28 14:30:45

(一)基础知识篇

自制单片机之十八……无线通讯模块NRF24L01+
自制单片机之十八……无线通讯模块NRF24L01+
自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+
今天刚调试好,先看图吧!

这张是AT89C2051控制NRF24L01+做发射调试。

自制单片机之十八……无线通讯模块NRF24L01+

看看NRF24L01细节吧!

自制单片机之十八……无线通讯模块NRF24L01+

这是LCD屏显示:

自制单片机之十八……无线通讯模块NRF24L01+

AT89S52做接收测试:

自制单片机之十八……无线通讯模块NRF24L01+

正在接收时的显示:

自制单片机之十八……无线通讯模块NRF24L01+

接收到数据后显示32个数据值:

自制单片机之十八……无线通讯模块NRF24L01+

无线模块NRF24L01+应用上篇结束,敬请期待NRF24L01+下篇的调试部分。。。。

(二)模块调试篇

自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+

三)发送与接收模块的联调

自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+

(四)举例应用

自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+自制单片机之十八……无线通讯模块NRF24L01+

LED调试篇

写了前面四篇关于NRF24L01通讯调试的文章,看来大家还是很喜欢,有帮助的。有很多大学生朋友问我说,我们没有两个LCD来显示调试状态,连一个也没有,能不能用几个LED来显示调试状态呢?因此我就写这篇补充调试的文章,就用P0口的8个LED来显示调试NRF24L01到成功进行数据通讯。

先把51单片机的最小系统准备好,还有8个LED的小电路板,如果你的LED就在系统板上那省了这一步。

自制单片机之十八……无线通讯模块NRF24L01+

8个LED的小板子电路很简单,但你焊接要可靠,不然电路本身都不稳定,后面对判断故障会产生很大影响。

自制单片机之十八……无线通讯模块NRF24L01+

自制单片机之十八……无线通讯模块NRF24L01+

自制单片机之十八……无线通讯模块NRF24L01+

NRF24L01+模块电路还是前面说过的那样:

自制单片机之十八……无线通讯模块NRF24L01+

相同的两个模块的板子。

自制单片机之十八……无线通讯模块NRF24L01+

好!假设我们用P0口来作LED显示、用P1口来作模块接口,下面我们先写一段最简单的程序,来确认LED电路,和P0、P1口的完好!
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
typedef unsigned char uchar;

//*********************************
//        延时函数
//  在晶振为12MHz时,延时count毫秒
//*********************************
void Delayms(uint count)
{
  uint i;
  while(count--)
   { for(i=0;i<80;i++){}
   }
  _nop_();
  _nop_(); 
  _nop_();
  _nop_(); 
  _nop_();
}

//*********************************
//           主函数
//*********************************
void main()

  P0=0x00;  //P0口LED点亮
  P1=0x00;  //P1口LED点亮
  P2=0x00;
  P3=0x00;
  Delayms(2000);  //延时2秒
  while(1)
   {
     P0=~P0;//将P0口数据取反,原来亮的就熄灭
     P1=~P1;//将P1口数据取反,原来亮的就熄灭
     P2=~P2;
     P3=~P3;
     Delayms(500);  //延时半秒
   }
}
这是段极简单的程序,用来检测单片机电路连接的正确性,和IO口的工作状态是否正常,为后面调试NRF24L01做好准备。

它的工作状态如下:

同样的,把LED的接口再接到P1口,看看它是否一样的在全部闪烁。做好了这步,准备工作就算完成了。
 接下来我们把NRF24L01+的模块插上,要注意,接口要对清楚,电源要连接正确:

自制单片机之十八……无线通讯模块NRF24L01+

接下来我们写发送程序:

//**********************************
//   NRF24L01+模块发射程序
//      用8个LED调试
//   Txz001 2012.05.16
//**********************************
#include <reg52.h> 
typedef unsigned char uchar; //将无符号字节类型重定义为uchar
typedef unsigned int uint;  //将无符号整数类型重定义为Uint

//*********************NRF24L01函数定义**************************** 
void delayms(uint t);//毫秒延时
void init_NRF24L01(void);   //模块初始化函数
uchar SPI_RW(uchar reg);    //基本SPI读写时序
uchar SPI_Read(uchar reg);  //从寄存器reg读一个字节
void SetRX_Mode(void);      //设置接收模式
uchar SPI_RW_Reg(uchar reg, uchar value);  //向寄存器写一个字节
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar uchars); // 从缓冲器读出uchars字节的数据
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars); //向缓冲器写进uchars字节的数据
void nRF24L01_TxPacket(uchar * tx_buf); //启动一次发送
uchar nRF24L01_RxPacket(uchar * rx_buf);//读取接收的数据,放入rx_buf数组

//***********NRF24L01模块IO端口定义****************** 
sbit CE=P1^0;
sbit CSN =P1^1;
sbit SCK =P1^2;
sbit MOSI =P1^3;
sbit MISO =P1^4;
sbit IRQ =P1^5;

//*****************NRF24L01常量********************** 
#define TX_ADR_WIDTH    5    //发送地址宽度 5字节
#define RX_ADR_WIDTH    5    //接收地址宽度 5字节
#define TX_PLOAD_WIDTH  32   // 发送数据宽度 32字节
#define RX_PLOAD_WIDTH  32   //接收数据的宽度 32字节
uchar const TX_ADDRESS[TX_ADR_WIDTH]= {0x01,0x02,0x03,0x04,0x05}; //本地地址 
uchar const RX_ADDRESS[RX_ADR_WIDTH]= {0x01,0x02,0x03,0x04,0x05}; //接收地址

//*****************NRF24L01寄存器指令******************* 
#define READ_REG        0x00   // 读寄存器指令 
#define WRITE_REG       0x20  // 写寄存器指令 
#define RD_RX_PLOAD     0x61   // 读取接收数据指令 
#define WR_TX_PLOAD     0xA0   // 写待发数据指令 
#define FLUSH_TX        0xE1   //清空发送缓冲区

//**************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 STATUS          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  // 接收频道0接收数据长度 
#define RX_PW_P2        0x13  // 接收频道0接收数据长度 
#define RX_PW_P3        0x14  // 接收频道0接收数据长度 
#define RX_PW_P4        0x15  // 接收频道0接收数据长度 
#define RX_PW_P5        0x16  // 接收频道0接收数据长度 
#define FIFO_STATUS     0x17  // FIFO栈入栈出状态寄存器设置

/*****毫秒延时子程序*****/
void delayms(uint t)     //约延时t毫秒
{
  uint i;
  while(t--)
    {
     for(i=0;i<125;i++);
    }        
}

/********************************************** 
/*函数:uint SPI_RW(uint uchar) 
/*功能:NRF24L01的SPI写时序 
/**********************************************/ 
uchar SPI_RW(uchar uuchar) 

 uchar bit_ctr; 
    for(bit_ctr=0;bit_ctr<8;bit_ctr++) // 输出8个位 
    { 
  MOSI = (uuchar & 0x80);     //输出uuhar的最高位
  uuchar = (uuchar << 1);     //左移一位
  SCK = 1;                    // 将时钟线置‘1’ 
  uuchar |= MISO;             //同时读取STATUS
  SCK = 0;                //然后再将时钟线置‘0’ 
    } 
    return(uuchar);               //返回读取的值 

/*********************************************** 
/*函数:uchar SPI_Read(uchar reg) 
/*功能:NRF24L01的SPI读取一个字节时序 
/***********************************************/
uchar SPI_Read(uchar reg) 

 uchar reg_val; 
 CSN = 0;             //CSN置'0',允许指令操作
 SPI_RW(reg);            //写一条reg指令
 reg_val = SPI_RW(0);    //读取reg的值到reg_val
 CSN = 1;                //CSN置'1',禁示操作
 return(reg_val);        //返回读取的值
}
/*********************************************** 
/*功能:NRF24L01写一个字节到寄存器函数 
/***********************************************/ 
uchar SPI_RW_Reg(uchar reg, uchar value) 

 uchar status; 
  
 CSN = 0;                   // CSN置'0',允许操作 
 status = SPI_RW(reg);      //这指令,并读STATUS
 SPI_RW(value);             //写数据值到reg
 CSN = 1;                   // CSN置'1',禁止操作
 return(status);            // return nRF24L01 status uchar 
}

/***************************************************************** 
/*函数:uint SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars) 
/*功能: 用于写数据:reg:为寄存器地址,
/*                  pBuf:为待写入数据地址,
/*                  uchars:写入数据的个数 
/*****************************************************************/ 
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar uchars) 

 uchar status,uchar_ctr; 
 CSN = 0;            //SPI使能        
 status = SPI_RW(reg);    
 for(uchar_ctr=0; uchar_ctr<uchars; uchar_ctr++) // 
  SPI_RW(*pBuf++); 
 CSN = 1;           //关闭SPI 
 return(status);    //  
}

//****************************************** 
/*NRF24L01初始化 
//******************************************/ 
void init_NRF24L01(void) 

  delayms(1); 
  CE=0;    //  射频停止工作 
  CSN=1;   // 停止寄存器读写 
  SCK=0;   //时种信号停止读写
  IRQ=1;//中断复位
  SPI_RW_Reg(WRITE_REG + EN_AA, 0x00);      //  频道0自动 ACK应答禁止  
  SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x00);      //禁止自动发送 
  SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);  //  允许接收地址只有频道0,   
  SPI_RW_Reg(WRITE_REG + RF_CH, 1);        //   设置信道工作为2.4GHZ,收发必须一致 
  SPI_RW_Reg(WRITE_REG + RX_PW_P0, RX_PLOAD_WIDTH); //设置接收数据长度,本次设置为32字节 
  SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);     //设置发射速率为2MHZ,发射功率为最大值0dB


/****************************************************** 
/*函数:void nRF24L01_TxPacket(unsigned char * tx_buf) 
/*功能:发送 tx_buf中数据 
/*******************************************************/ 
void nRF24L01_TxPacket(unsigned char * tx_buf) 

 CE=0;   //StandBy I模式  
 SPI_Write_Buf(WRITE_REG + TX_ADDR, TX_ADDRESS, TX_ADR_WIDTH);    // 写本地地址  
 SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH); // 装载接收端地址 
 SPI_Write_Buf(WR_TX_PLOAD, tx_buf, TX_PLOAD_WIDTH);// 装载数据  
 SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);      // IRQ收发完成中断响应,16位CRC,主发送 
 CE=1;   //置高CE,激发数据发送 
 delayms(1); 
}

//************************************
//              主函数
//************************************
void main() 

  uchar  TxBuf[32];
  uchar status;  //定义一个变量用来装读取到的STATUS数值
  init_NRF24L01();//NRF24L01初始化
  SPI_RW_Reg(WRITE_REG+STATUS,0XFF);   //清状态寄存器
  status=SPI_Read(STATUS); //读取状态
  P0=~status;//P0口显示读取的状态
  delayms(4000);//显示延时4秒,以便从容看清楚
  P0=0xff;//清除显示
  delayms(600);
  TxBuf[0]=1;  //我们设置个初值1在想要发送的数组的第1个里变量里。

while(1) 
   { TxBuf[0]=~TxBuf[0];   //这句把要发送的第1个变量的值取反,如果原来是1,现再就为0
     nRF24L01_TxPacket(TxBuf);//装载数据并进行一次发送操作
     status=SPI_Read(STATUS); //发送完后再读取状态
     P0=~status;  //显示发送完后的状态
     delayms(500);  //显示发送后的信息停留1秒
     P0=0xFF;   //清除显示
     delayms(500); 
   }  
}

程序看上去挺长,其实大部分都是常量的定义。主要的几句就在主函数里,要注意的是接口的定义跟你插在板子上接口要一致。电源不能接错哦!这个程序很简单,开始对NRF24L01初始化,然后读取它的状态值显示在P0口,正确的状态应为00001110,然后停顿4秒,让我可以从容看清状态。然后进入循环发送状态,先将要发送的数据取反,就是说,这次发送0,下次就发送1,这样交替进行,以便后面接收时,我们可以看到变化。接下来就是进行发送,发送完后,再读取状态并显示。

正确的显示是00101110。如是上面的视频。5位上的值为1说明模块发送成功,产生了中断信号。

。。。。。待续

自制单片机之十八……无线通讯模块NRF24L01+的更多相关文章

  1. 自制单片机之十五……可串行驱动LCD12864的应用

    在网上搜了一下,ST7920控制器的LCD产品可以提供8位,4位并行和串行接口可选,并行的控制接口的LCD较多,前面的贴子也介绍过,我们在这儿不说了,这儿我们讲的是串口控制LCD12864. 买了块S ...

  2. 自制单片机之十二……AT89C2051烧写器的制做与调试

    现在都用S52了,还用C2051干嘛!价格也差不多.但是C2051的体积要比S51.S52小很多,而且引脚只有20只,在一些简单的控制中,这些引脚已足够了,小的体积更具有优势些.但目前好像还没有支持在 ...

  3. 自制单片机之十……AT89S51的上拉电阻问题

    很多网友都问我AT89S51的P0口为什么要接一个上拉电阻.我就用一个篇幅来说一说 P0口和其它三个口的内部电路是不同的,如下图 P0口是接在两个三极管D0和D1之间的,而P1-P3口的上部是接一个电 ...

  4. 自制单片机之十六……将文字或图形转成LCD上使用的C51字模数据

    这一讲说说如何用取模软件将图形转成数据吧,有很多人反复问我这个问题,我就再罗嗦下吧! 取字模的软件有很多款.有的只能将文字转成字模数据,有的既可将文本文字转字模也能将图片转成点阵数据.在这里我就介绍一 ...

  5. &lbrack;Micropython&rsqb;TPYBoard v10x NRF24L01无线通讯模块使用教程

    1.实验目的: •       学习使用NRF24L01无线通讯模块 2.所需原器件: •       TPYBoard v10X开发板两块 •       NRF24L01无线通讯模块两个 •    ...

  6. Senparc&period;Weixin&period;MP SDK 微信公众平台开发教程(十八):Web代理功能

    在Senparc.Weixin.dll v4.5.7版本开始,我们提供了Web代理功能,以方便在受限制的局域网内的应用可以顺利调用接口. 有关的修改都在Senparc.Weixin/Utilities ...

  7. Bootstrap &lt&semi;基础二十八&gt&semi;列表组

    列表组.列表组件用于以列表形式呈现复杂的和自定义的内容.创建一个基本的列表组的步骤如下: 向元素 <ul> 添加 class .list-group. 向 <li> 添加 cl ...

  8. Bootstrap &lt&semi;基础十八&gt&semi;面包屑导航(Breadcrumbs)

    面包屑导航(Breadcrumbs)是一种基于网站层次信息的显示方式.以博客为例,面包屑导航可以显示发布日期.类别或标签.它们表示当前页面在导航层次结构内的位置. Bootstrap 中的面包屑导航( ...

  9. 最全的MySQL基础【燕十八传世】

    1.课前准备! 开启mysql服务:1).配置环境变量;2).net start mysql 将该sql文件导入到你的数据库中,以下所有操作都是基于该数据库表操作的!!! [此笔记是本人看着视频加上自 ...

随机推荐

  1. python3 模拟登录网站

    最近学习python,因经常登录公积金网站查看公积金缴存还款情况,so网上找了写脚本,修改了一下,方便获取网页中的数据. 使用谷歌浏览器F12查看登录请求内容 1.request header需要参数 ...

  2. AM335x&lpar;TQ335x&rpar;学习笔记——u-boot-2014&period;10移植

    根据最近移植u-boot-2014.10至TQ335x,基于这样的假设am335x evm移植.不是很多地方需要改变. 因为TI的am335x evm开发了使用eeprom船上保存配置信息.它使用不同 ...

  3. JIRA API 对接

    系统要跟JIRA对接,将本系统数据发送给jira. 开始一头雾水怎么让数据传过去已什么形式存在,是存数据库呢还是怎么显示呢.研究半天发现其实只要将原数据作为json数据提供给jira接口,jira接口 ...

  4. Phone List HDU1671

    字典树的包含与不包含关系 #include<bits/stdc++.h> using namespace std; ][]; ]; ; bool insert1( char *word ) ...

  5. 关于 LD&lowbar;LIBRARY&lowbar;PATH 这个环境变量

    这个变量中可以保存linux寻找库时搜索的路径,按照一篇文章中的介绍,不应该设置这个变量.文章的重点如下: 1. 不要设置这个变量. 2. Solaris中,在编译时,使用 -L 选项指定编译时库的搜 ...

  6. C&num; HttpWebRequest 错误总结

    1.form data 需要编码!!! byte[] data = new ASCIIEncoding().GetBytes("pattern=0&wwid=古兴越&good ...

  7. 计算两个集合的差集——第六期 Power8 算法挑战赛

    第六期Power8大赛 1.1 比赛题目 题目: 计算两个集合的差集: 详细说明: 分别有集合A和B两个大数集合,求解集合A与B的差集(A中有,但B中无的元素),并将结果保存在集合C中,要求集合C中的 ...

  8. python基础之单例模式

    单例模式: 什么是单例模式? 基于某种方法实例化多次得到实例是同一个 实现方法: ip = '1.1.1.1' port = 3306 # 假装来自配置文件 #方法一:定义类方法进行判断 class ...

  9. Use default arguments instead of short circuiting or conditionals使用默认实参代替短路和条件

  10. Luogu【P1880】石子合并&lpar;环形DP&rpar;

    先放上luogu的石子合并题目链接 这是一道环形DP题,思想和能量项链很像,在预处理过程中的手法跟乘积最大相像. 用一个m[][]数组来存储石子数量,m[i][j]表示从第 i 堆石子到第 j 堆石子 ...