(一)基础知识篇




今天刚调试好,先看图吧!

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

看看NRF24L01细节吧!

这是LCD屏显示:

AT89S52做接收测试:

正在接收时的显示:

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

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

(二)模块调试篇

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

(四)举例应用

LED调试篇

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

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

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

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+模块发射程序
//      用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. [Micropython]TPYBoard v10x NRF24L01无线通讯模块使用教程

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

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

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

  7. Bootstrap <基础二十八>列表组

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

  8. Bootstrap <基础十八>面包屑导航(Breadcrumbs)

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

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

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

随机推荐

  1. BZOJ3398: [Usaco2009 Feb]Bullcow 牡牛和牝牛

    3398: [Usaco2009 Feb]Bullcow 牡牛和牝牛 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 30  Solved: 17[Sub ...

  2. 2013第38周日Java文件上传下载收集思考

    2013第38周日Java文件上传&下载收集思考 感觉文件上传及下载操作很常用,之前简单搜集过一些东西,没有及时学习总结,现在基本没啥印象了,今天就再次学习下,记录下自己目前知识背景下对该类问 ...

  3. linux串口驱动分析——发送数据

    一.应用程序中write函数到底层驱动历程 和前文提到的一样,首先先注册串口,使用uart_register_driver函数,依次分别为tty_register_driver,cdev_init函数 ...

  4. Remove Node in Binary Search Tree 解答

    从BST中移除一个节点是比较复杂的问题,需要分好几种情况讨论. 如这篇文章,就讨论了删除节点 1.有无左右子树 2.只有右子树 3.只有左子树 三种情况. 一种简单些的思维是只考虑删除节点是否有右子树 ...

  5. Dijkstra优先队列优化

    Dijkstra算法的核心思想就是两步排序,一个是对于一个点而言,他的最小边要经过所有其他点最小边的测试才能确认,也就是说要在这其中找一个最大的边出来:第二个是对于每次循环而言的,每次的更新d数组都是 ...

  6. [教程] 神器i9100刷基带与内核的方法!(兼带ROOT方法)

    http://bbs.hiapk.com/thread-2647905-1-1.html ------何为基带?何为内核? 为什么刷基带,为什么刷内核?!!! 基带:基带(Baseband)是手机中的 ...

  7. 【ArcGIS 10.2新特性】地理数据(Geodatabase 和database)10.2 新特性

    1. 大数据支持 ArcGIS与Hadoop集成,将提供一个开源的工具包用于大数据的空间分析,开发者将通过该工具包构建定制化的工作流并在ArcGIS当中执行.         支持更多的大数据平台数据 ...

  8. mysql Encryption and Compression Functions

    Name Description AES_DECRYPT() Decrypt using AES AES_ENCRYPT() Encrypt using AES COMPRESS() Return r ...

  9. [Git] Automatically running tests before commits with ghooks

    Wouldn't it be nice if everyone ran the tests before committing code? With ghooks, you can automatic ...

  10. Dialog( 对话框) 组件

    一. 加载方式//class 加载方式<div class="easyui-dialog" title="My Dialog"style="wi ...