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

       本人大三学生一名,因为要做一个小项目而第一次接触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接好,在串口助手下能看到接收到的数据。这只是一个最基础的版本,如果想改进还有很多功能可以加。
第一次写博客,不喜勿喷。谢谢。


猜你喜欢

转载自blog.csdn.net/qq_26226375/article/details/51317862
今日推荐