您的位置:首页 > 产品设计 > UI/UE

51单片机与Arduino通过nrf24l01无线模块通信

2016-05-04 20:28 821 查看
本人大三学生一名,因为要做一个小项目而第一次接触arduino,简单学习了一下就开始动手做了。要通过nrf24l01实现arduino与51单片机之间的无线通信,在网上查了很多资料都没有响应的文章,所以之间想写一篇,包括之间遇到的一些问题,跟大家分享一下,也算是前车之鉴吧。废话不多说,开工。。。。。

首先是实现两个51单片机之间的通信,这个网上很多相应的程序,我就不多说了。现在呈上51的完整代码:

(这里nrf24l01的vcc接的是5v的电压,网上看到很多人说不能5v,会把24l01烧坏。反正我是这样接的,没什么事,如果有。。。那就再买个吧。)

51发送

#include <reg52.h>

#include <intrins.h>

typedef unsigned int uint;

typedef unsigned char uchar;

#define TX_ADDR_WITDH 5//发送地址宽度设置为5个字节

#define RX_ADDR_WITDH 5//接收地址宽度设置为5个字节

#define TX_DATA_WITDH 8//

#define RX_DATA_WITDH 8

/******************************************************************

// nRF24L01指令格式:

*******************************************************************/

#define R_REGISTER 0x00 // 读寄存器

#define W_REGISTER 0x20 // 写寄存器

#define R_RX_PLOAD 0x61 // 读RX FIFO有效数据,1-32字节,当读数据完成后,数据被清除,应用于接收模式

#define W_TX_PLOAD 0xA0 // 写TX FIFO有效数据,1-32字节,写操作从字节0开始,应用于发射模式

#define FLUSH_TX 0xE1 // 清除TX FIFO寄存器,应用于发射模式

#define FLUSH_RX 0xE2 // 清除RX FIFO寄存器,应用于接收模式

#define REUSE_TX_PL 0xE3 // 重新使用上一包有效数据,当CE为高过程中,数据包被不断的重新发射

#define NOP 0xFF // 空操作,可以用来读状态寄存器

/******************************************************************

// nRF24L01寄存器地址

*******************************************************************/

#define CONFIG 0x00 // 配置寄存器

#define EN_AA 0x01 // “自动应答”功能寄存

#define EN_RX_ADDR 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 // 数据通道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 sta; // 状态变量

#define RX_DR (sta & 0x40) // 接收成功中断标志

#define TX_DS (sta & 0x20) // 发射成功中断标志

#define MAX_RT (sta & 0x10) // 重发溢出中断标志

sbit CE=P2^0;

sbit IRQ=P1^0;

sbit CSN=P2^1;

sbit MOSI=P3^3;

sbit MISO=P2^4;

sbit SCK=P2^2;

sbit Key=P3^4;

uchar code TX_Addr[]={0x34,0x43,0x10,0x10,0x01};

//uchar TX_Addr[]="Rec01";

uchar code TX_Buffer[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x00};

uchar RX_Buffer[RX_DATA_WITDH];

void _delay_us(uint x)

{

uint i,j;

for (j=0;j<x;j++)

for (i=0;i<12;i++);

}

void _delay_ms(uint x)

{

uint i,j;

for (j=0;j<x;j++)

for (i=0;i<120;i++);

}

/*nRF24L01初始化*/

void nRF24L01_Init(void)

{

CE=0;//待机模式Ⅰ

CSN=1;

SCK=0;

IRQ=1;

}

/*SPI时序函数*/

uchar SPI_RW(uchar byte)

{

uchar i;

for(i=0;i<8;i++)//一字节8位循环8次写入

{

if(byte&0x80)//如果数据最高位是1

MOSI=1;//向NRF24L01写1

else //否则写0

MOSI=0;

byte<<=1;//低一位移到最高位

SCK=1;//SCK拉高,写入一位数据,同时读取一位数据

if(MISO)

byte|=0x01;

SCK=0;//SCK拉低

}

return byte;//返回读取一字节

}

/*SPI写寄存器一字节函数*/

/*reg:寄存器地址*/

/*value:一字节(值)*/

uchar SPI_W_Reg(uchar reg,uchar value)

{

uchar status;//返回状态

CSN=0;//SPI片选

status=SPI_RW(reg);//写入寄存器地址,同时读取状态

SPI_RW(value);//写入一字节

CSN=1;//

return status;//返回状态

}

/*SPI读一字节*/

uchar SPI_R_byte(uchar reg)

{

uchar reg_value;

CSN=0;//SPI片选

SPI_RW(reg);//写入地址

reg_value=SPI_RW(0);//读取寄存器的值

CSN=1;

return reg_value;//返回读取的值

}

/*SPI读取RXFIFO寄存器数据*/

/*reg:寄存器地址*/

/*Dat_Buffer:用来存读取的数据*/

/*DLen:数据长度*/

uchar SPI_R_DBuffer(uchar reg,uchar *Dat_Buffer,uchar Dlen)

{

uchar status,i;

CSN=0;//SPI片选

status=SPI_RW(reg);//写入寄存器地址,同时状态

for(i=0;i<Dlen;i++)

{

Dat_Buffer[i]=SPI_RW(0);//存储数据

}

CSN=1;

return status;

}

/*SPI向TXFIFO寄存器写入数据*/

/*reg:写入寄存器地址*/

/*TX_Dat_Buffer:存放需要发送的数据*/

/*Dlen:数据长度*/

uchar SPI_W_DBuffer(uchar reg,uchar *TX_Dat_Buffer,uchar Dlen)

{

uchar status,i;

CSN=0;//SPI片选,启动时序

status=SPI_RW(reg);

for(i=0;i<Dlen;i++)

{

SPI_RW(TX_Dat_Buffer[i]);//发送数据

}

CSN=1;

return status;

}

/*设置发送模式*/

void nRF24L01_Set_TX_Mode(uchar *TX_Data)

{

CE=0;//待机(写寄存器之前一定要进入待机模式或掉电模式)

SPI_W_DBuffer(W_REGISTER+TX_ADDR,TX_Addr,TX_ADDR_WITDH);/*写寄存器指令+接收节点地址+地址宽度*/

SPI_W_DBuffer(W_REGISTER+RX_ADDR_P0,TX_Addr,TX_ADDR_WITDH);/*为了接收设备应答信号,接收通道0地址与发送地址相同*/

SPI_W_DBuffer(W_TX_PLOAD,TX_Data,TX_DATA_WITDH);/*写有效数据地址+有效数据+有效数据宽度*/

SPI_W_Reg(W_REGISTER+EN_AA,0x01);/*接收通道0自动应答*/

SPI_W_Reg(W_REGISTER+EN_RX_ADDR,0x01);/*使能接收通道0*/

SPI_W_Reg(W_REGISTER+SETUP_RETR,0x0a);/*自动重发延时250US+86US,重发10次*/

SPI_W_Reg(W_REGISTER+RF_CH,0);/*(2400)MHZ射频通道*/

SPI_W_Reg(W_REGISTER+RF_SETUP,0x07);/*1Mbps速率,发射功率:0DBM,低噪声放大器增益*/

SPI_W_Reg(W_REGISTER+CONFIG,0x0e);/*发送模式,上电,16位CRC校验,CRC使能*/

CE=1;//启动发射

_delay_ms(5);/*CE高电平持续时间最少10US以上*/

}

uchar Check_Rec(void)

{

uchar status;

sta=SPI_R_byte(R_REGISTER+STATUS);

if(RX_DR)

{

CE=0;

SPI_R_DBuffer(R_RX_PLOAD,RX_Buffer,RX_DATA_WITDH);

status=1;

}

SPI_W_Reg(W_REGISTER+STATUS,0xff);

return status;

}

/*检测应答信号*/

uchar Check_Ack(void)

{

sta=SPI_R_byte(R_REGISTER+STATUS);/*读取寄存状态*/

if(TX_DS||MAX_RT)/*如果TX_DS或MAX_RT为1,则清除中断和清除TX_FIFO寄存器的值*/

{

SPI_W_Reg(W_REGISTER+STATUS,0xff);

CSN=0;

SPI_RW(FLUSH_TX);/*如果没有这一句只能发一次数据,大家要注意*/

CSN=1;

return 0;

}

else

return 1;

}

void main(void)

{

uchar i;

P0=0xff;//初始化IO口

P1=0xff;

P2=0xff;

P3=0xff;

_delay_us(100);

nRF24L01_Init();//NRF24L01初始化

while(1)

{

if(Key==0)

{

_delay_ms(10);

if(Key==0)

{

for(i=0;i<TX_DATA_WITDH;i++)//发送7次数据

{

nRF24L01_Set_TX_Mode(&TX_Buffer[i]);//发送数据

while(Check_Ack());//等待发送完成

}

}

}

}

}

接下来是arduino的完整代码:

arduino接收

VCC ---3.3V

GND ----GND

CE ---D9

CSN ---D10

MOSI ---D11

MISO ---D12

SCK ---D13

IRQ ---D8

#define uint unsigned int

#define uchar unsigned char

#define TX_ADDR_WITDH 5//发送地址宽度设置为5个字节

#define RX_ADDR_WITDH 5

#define TX_DATA_WITDH 8

#define RX_DATA_WITDH 8

/******************************************************************

// nRF24L01指令格式:

*******************************************************************/

#define R_REGISTER 0x00 // 读寄存器

#define W_REGISTER 0x20 // 写寄存器

#define R_RX_PLOAD 0x61 // 读RX FIFO有效数据,1-32字节,当读数据完成后,数据被清除,应用于接收模式

#define W_TX_PLOAD 0xA0 // 写TX FIFO有效数据,1-32字节,写操作从字节0开始,应用于发射模式

#define FLUSH_TX 0xE1 // 清除TX FIFO寄存器,应用于发射模式

#define FLUSH_RX 0xE2 // 清除RX FIFO寄存器,应用于接收模式

#define REUSE_TX_PL 0xE3 // 重新使用上一包有效数据,当CE为高过程中,数据包被不断的重新发射

#define NOP 0xFF // 空操作,可以用来读状态寄存器

/******************************************************************

// nRF24L01寄存器地址

*******************************************************************/

#define CONFIG 0x00 // 配置寄存器

#define EN_AA 0x01 // “自动应答”功能寄存器

#define EN_RX_ADDR 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 // 数据通道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 sta; // 状态变量

#define RX_DR (sta & 0x40) // 接收成功中断标志

#define TX_DS (sta & 0x20) // 发射成功中断标志

#define MAX_RT (sta & 0x10) // 重发溢出中断标志

int CE = 9;

int CSN = 10;

int IRQ = 8;

//int SCK = 13;

//int MOSI = 11;

//int MISO = 12; 这几个引脚arduino自己就已经定义好了

uchar TX_Addr[]={0x34,0x43,0x10,0x10,0x01};

uchar TX_Buffer[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};

uchar RX_Buffer[RX_DATA_WITDH];

void nRF24L01_Init(void)

{

delay(2);

digitalWrite(CE,LOW);

digitalWrite(CSN,HIGH);

digitalWrite(SCK,LOW);

// digitalWrite(MOSI,LOW);

// digitalWrite(MISO,LOW);

digitalWrite(IRQ,HIGH);

}

uchar SPI_RW(uchar byte)

{

uchar i;

for(i=0;i<8;i++)

{

if(byte&0x80)

digitalWrite(MOSI,HIGH);

else

digitalWrite(MOSI,LOW);

byte<<=1;

digitalWrite(SCK,HIGH);

if(digitalRead(MISO))

// Serial.println(10);

byte|=0x01;

digitalWrite(SCK,LOW);

}

return byte;

}

uchar SPI_W_Reg(uchar reg,uchar value)

{

uchar status;

digitalWrite(CSN,LOW);

status=SPI_RW(reg);

SPI_RW(value);

digitalWrite(CSN,HIGH);

return status;

}

uchar SPI_R_byte(uchar reg)

{

uchar status;

digitalWrite(CSN,LOW);

SPI_RW(reg);

status=SPI_RW(0);

digitalWrite(CSN,HIGH);

return status;

}

uchar SPI_R_DBuffer(uchar reg,uchar *Dat_Buffer,uchar Dlen)

{

uchar reg_value,i;

digitalWrite(CSN,LOW);

reg_value=SPI_RW(reg);

for(i=0;i<Dlen;i++)

{

Dat_Buffer[i]=SPI_RW(0);

}

digitalWrite(CSN,HIGH);

return reg_value;

}

uchar SPI_W_DBuffer(uchar reg,uchar *TX_Dat_Buffer,uchar Dlen)

{

uchar reg_value,i;

digitalWrite(CSN,LOW);

reg_value=SPI_RW(reg);

for(i=0;i<Dlen;i++)

{

SPI_RW(TX_Dat_Buffer[i]);

}

digitalWrite(CSN,HIGH);

return reg_value;

}

void nRF24L01_Set_RX_Mode(void)

{

digitalWrite(CE,LOW);//待机

SPI_W_DBuffer(W_REGISTER+TX_ADDR,TX_Addr,TX_ADDR_WITDH);

SPI_W_DBuffer(W_REGISTER+RX_ADDR_P0,TX_Addr,TX_ADDR_WITDH);

SPI_W_Reg(W_REGISTER+EN_AA,0x01);//auot ack

SPI_W_Reg(W_REGISTER+EN_RX_ADDR,0x01);

SPI_W_Reg(W_REGISTER+SETUP_RETR,0x0a);

SPI_W_Reg(W_REGISTER+RX_PW_P0,RX_DATA_WITDH);

SPI_W_Reg(W_REGISTER+RF_CH,0);

SPI_W_Reg(W_REGISTER+RF_SETUP,0x07);//0db,lna

SPI_W_Reg(W_REGISTER+CONFIG,0x0f);

digitalWrite(CE,HIGH);

delay(5);

}

uchar nRF24L01_RX_Data(void)

{

// uchar i,status;

sta=SPI_R_byte(R_REGISTER+STATUS);

if(RX_DR)

{

digitalWrite(CE,LOW);

SPI_R_DBuffer(R_RX_PLOAD,RX_Buffer,RX_DATA_WITDH);

SPI_W_Reg(W_REGISTER+STATUS,0xff);

digitalWrite(CSN,LOW);

SPI_RW(FLUSH_RX);

digitalWrite(CSN,HIGH);

return 1;

}

else

return 0;

}

void setup() {

// put your setup code here, to run once:

Serial.begin(9600);

pinMode(CE,OUTPUT);

pinMode(CSN,OUTPUT);

pinMode(SCK,OUTPUT);

pinMode(MOSI,OUTPUT);

pinMode(MISO,INPUT);

}

void loop() {

// put your main code here, to run repeatedly:

//Serial.println(10);

unsigned char i = 0;

delay(1);

nRF24L01_Init();

while(1)

{

nRF24L01_Set_RX_Mode();

delay(100);

if(nRF24L01_RX_Data())

{

// Serial.println(12345678);

for(i = 0;i < RX_DATA_WITDH;i++)

{

Serial.println(RX_Buffer[i]);

delay(1000);

}

}

}

}

arduino 的代码和51的代码大致相同,只是把它改成arduino的代码风格,也可以见到修改之后拿去51上使用。这里要注意的是MISO要设置为INPUT模式。不能是OUTPUT否则接收失败。具体原因如下

输出模式:对于Arduino,用pinMode将IO口设为OUTPUT的时候,其实IO的状态为“强推挽”,也就是说设为高电平时,IO口对电源正极的电阻比较小(强上拉),设为低电平时IO口对地的电阻也比较小(强下拉),这样IO口就具备了较强的驱动能力。其实也没有强到哪里去,大概几十毫安,能点亮LED而已。

输入模式:
对于Arduino,用pinMode将IO口设为INPUT的时候,其实IO的状态为浮空输入,浮空输入也称高阻输入,也就是说输入阻抗非常高。理想状态下,可以认为输入阻抗是无穷大的,大到就像这个引脚断路了一样。就像一个浮在空中的金属丝一样,没有连上任何电路,你让它的电压是多少,它的电压就是多少。这样做是有意义的,因为只有输入阻抗足够大,才能接收到微弱的信号。如果输入阻抗不够大,比如输入端跟地之间有一个1kΩ的电阻,那微弱的输入信号很可能就被直接拉到0V,检测不出来了。

这里自己也是刚接触arduino,所以这些东西也不熟,弄了一天才弄好。实在是惭愧。

主要内容就是这些,功能是在51单片机上按一下键盘上的按键会发送数据,把arduino接好,在串口助手下能看到接收到的数据。这只是一个最基础的版本,如果想改进还有很多功能可以加。

第一次写博客,不喜勿喷。谢谢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: