自制单片机之十八……无线通讯模块NRF24L01+
(一)基础知识篇








今天刚调试好,先看图吧!
这张是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+的更多相关文章
- 自制单片机之十五……可串行驱动LCD12864的应用
在网上搜了一下,ST7920控制器的LCD产品可以提供8位,4位并行和串行接口可选,并行的控制接口的LCD较多,前面的贴子也介绍过,我们在这儿不说了,这儿我们讲的是串口控制LCD12864. 买了块S ...
- 自制单片机之十二……AT89C2051烧写器的制做与调试
现在都用S52了,还用C2051干嘛!价格也差不多.但是C2051的体积要比S51.S52小很多,而且引脚只有20只,在一些简单的控制中,这些引脚已足够了,小的体积更具有优势些.但目前好像还没有支持在 ...
- 自制单片机之十……AT89S51的上拉电阻问题
很多网友都问我AT89S51的P0口为什么要接一个上拉电阻.我就用一个篇幅来说一说 P0口和其它三个口的内部电路是不同的,如下图 P0口是接在两个三极管D0和D1之间的,而P1-P3口的上部是接一个电 ...
- 自制单片机之十六……将文字或图形转成LCD上使用的C51字模数据
这一讲说说如何用取模软件将图形转成数据吧,有很多人反复问我这个问题,我就再罗嗦下吧! 取字模的软件有很多款.有的只能将文字转成字模数据,有的既可将文本文字转字模也能将图片转成点阵数据.在这里我就介绍一 ...
- [Micropython]TPYBoard v10x NRF24L01无线通讯模块使用教程
1.实验目的: • 学习使用NRF24L01无线通讯模块 2.所需原器件: • TPYBoard v10X开发板两块 • NRF24L01无线通讯模块两个 • ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十八):Web代理功能
在Senparc.Weixin.dll v4.5.7版本开始,我们提供了Web代理功能,以方便在受限制的局域网内的应用可以顺利调用接口. 有关的修改都在Senparc.Weixin/Utilities ...
- Bootstrap <基础二十八>列表组
列表组.列表组件用于以列表形式呈现复杂的和自定义的内容.创建一个基本的列表组的步骤如下: 向元素 <ul> 添加 class .list-group. 向 <li> 添加 cl ...
- Bootstrap <基础十八>面包屑导航(Breadcrumbs)
面包屑导航(Breadcrumbs)是一种基于网站层次信息的显示方式.以博客为例,面包屑导航可以显示发布日期.类别或标签.它们表示当前页面在导航层次结构内的位置. Bootstrap 中的面包屑导航( ...
- 最全的MySQL基础【燕十八传世】
1.课前准备! 开启mysql服务:1).配置环境变量;2).net start mysql 将该sql文件导入到你的数据库中,以下所有操作都是基于该数据库表操作的!!! [此笔记是本人看着视频加上自 ...
随机推荐
- COJ 0995 WZJ的数据结构(负五)区间操作
WZJ的数据结构(负五) 难度级别:C: 运行时间限制:1000ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 请你设计一个数据结构,完成以下功能: 给定一个大小为 ...
- POJ 2135 Farm Tour
题目大意:有一个无向图..农夫从1号点出发..要到达N号点..然后回到1号点..来回不能走相同的路径..问最短的距离是多少. 题解:又是不能走重复路径!基本图论算法直接扔掉上网络流.不能相同就边限1, ...
- js 数组引用 发现的问题
最近做项目时,要对返回的数据[保存在json数组中]做一次修改,但原数据要保留一次做备用.首先想到,原数据不动,用一个临时的变量来修改,大致模型就是这样: // 原始: a=[1,2,3,4,5,.. ...
- css——基础样式总结
颜色和单位的使用 颜色 用颜色的名字表示颜色,比如:red 用16进制表示演示 比如:#FF0000 用rgb数值表示颜色,rgb(红,绿,蓝),每个值都在0-255之间一般都用16进制表示颜色 单位 ...
- css链接
css code: a:link{ color:#FF0000; } a:visited{ color:#00FF00; } a:hover { color:#0000FF; } a:active{ ...
- Jetty监控线程使用情况的配置
Jetty监控线程使用情况配置 第一步,配置xml文件 jetty-monitor.xml 参数说明: threads: 线程池中的线程 busyThreads: 使用中的线程 idleThreads ...
- oracle中 connect by prior 递归算法
Oracle中start with...connect by prior子句用法 connect by 是结构化查询中用到的,其基本语法是: select ... from tablename sta ...
- DLNA介绍(包含UPnP,2011/6/20 更新)
这部分的内容大多来源于网络及官方文档,依照自己的翻译理解整理所成.东西比較多,从头慢慢看还是能够懂个大概的. 文件夹: 一.DNLA的建立 二.DLNA的成员 三.DLNA标准的制定 四.DLNA的设 ...
- mvc中使用jsonp进行跨域请求详细说明
在web开发中,如果你要在不同域下进行数据异步请求,会出现一个No ‘Access-Control-Allow-Origin’ header is present on the requested r ...
- C# typeof Gettype is as &拆箱 装箱
有时候,我们不想用值类型的值,就是想用一个引用..Net提供了一个名为装箱(boxing)的机制,它允许根据值类型来创建一个对象,然后使用对这个新对象的一个引用. 首先,回顾两个重要的事实,1.对于引 ...