51 单片机 2.4G 模块通信教程

使用STC89C52单片机实现硬件模块2.4G(NRF24L01+)的相互通信

背景介绍

关于nRF24L01这个模块,网上可以说是资料非常多了,我参考的是云佳科技的pdf以及官方的datasheet英文数据手册。 另外,关于这个模块的介绍以及能够用来做什么也不过多的介绍了,数据手册及说明书都有。

硬件介绍

1
2.4G nRF24L01 模块
1
STC89C52RC 单片机
1

说明:

  1. VCC脚接电压范围为 1.9 V-3.6 V 之间,不能在这个区间之外,超过 3.6 V 将会烧毀模块。推荐电压 3.3 V 左右
  2. 除电源VCC和接地端,其余脚都可以直接和普通的 5V 单片机 IO 口直接相连,无需电平转换。当然对 3V 左右的单片机更加适用了
  3. 硬件上面没有 SPI 的单片机也可以控制本模块,用普通单片机 IO 口模拟 SPI 不需要单片机真正的串口介入,只需要普通的单片机 IO 口就可以了,当然用串口也可以了

由于我是用STC89C52RC的单片机,上面没有 3.3 V 的供电接口,所以直接找了一个 3.3 V 的电源供电,下面是51单片机的原理图。

软件部分

变量设置及宏定义 接收端和发射端一样

#define     uchar       unsigned char 
#define     uint        unsigned int 
#define     ulong        unsigned long
#define     TX_ADR_WIDTH    5
#define     TX_PLOAD_WIDTH  4

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

uchar RX_BUF[TX_PLOAD_WIDTH];
uchar TX_BUF[TX_PLOAD_WIDTH];
uchar flag;
uchar DATA = 0x01;
uchar bdata sta;          
sbit  RX_DR     = sta^6;
sbit  TX_DS     = sta^5;
sbit  MAX_RT 	= sta^4;

定义LED与信号灯

sbit SIG = P1^6;
sbit LED = P1^7;

NRF24L01 模块引脚位定义

sbit	CE	        =P1^0;
sbit	CSN		=P1^1;
sbit	SCK	        =P1^2;
sbit 	MOSI    	=P1^3;
sbit 	MISO	        =P1^4;
sbit	IRQ		=P1^5;

寄存器设置

/*  
    SPI(nRF24L01) 指令设置
    指令格式
    <命令字  : 由高位到低位(每字节)>
    <数据字节: 低字节到高字节,每一字节高位在前>
 */
#define READ_REG    0x00  // Define read command to register
#define WRITE_REG   0x20  // Define write command to register
#define RD_RX_PLOAD 0x61  // Define RX payload register address
#define WR_TX_PLOAD 0xA0  // Define TX payload register address
#define FLUSH_TX    0xE1  // 清除 TX FIFO寄存器  应用于发射模式下
#define FLUSH_RX    0xE2  // 清除 RX FIFO寄存器  应用于接收模式下。
#define REUSE_TX_PL 0xE3  // 重新使用上一包有效数据。 当CE=1,数据包被不断重新发射 发射过程中必须禁止数据包重利用功能
#define NOP         0xFF  // 空操作。可以用来读状态寄存器

/*  
    SPI(nRF24L01) registers(addresses)
    一下寄存器都是默认只使用 数据通道0 不开启其他通道
 */
#define CONFIG      0x00  // 'Config' register address
#define EN_AA       0x01  // 'Enable Auto Acknowledgment' register address
#define EN_RXADDR   0x02  // 'Enabled RX addresses' register address
#define SETUP_AW    0x03  // 'Setup address width' register address
#define SETUP_RETR  0x04  // 'Setup Auto. Retrans' register address
#define RF_CH       0x05  // 'RF channel' register address
#define RF_SETUP    0x06  // 'RF setup' register address
#define STATUS      0x07  // 'Status' register address
#define OBSERVE_TX  0x08  // 'Observe TX' register address
#define CD          0x09  // 'Carrier Detect' register address
#define RX_ADDR_P0  0x0A  // 'RX address pipe0' register address
#define RX_ADDR_P1  0x0B  // 'RX address pipe1' register address
#define RX_ADDR_P2  0x0C  // 'RX address pipe2' register address
#define RX_ADDR_P3  0x0D  // 'RX address pipe3' register address
#define RX_ADDR_P4  0x0E  // 'RX address pipe4' register address
#define RX_ADDR_P5  0x0F  // 'RX address pipe5' register address
#define TX_ADDR     0x10  // 'TX address' register address
#define RX_PW_P0    0x11  // 'RX payload width, pipe0' register address
#define RX_PW_P1    0x12  // 'RX payload width, pipe1' register address
#define RX_PW_P2    0x13  // 'RX payload width, pipe2' register address
#define RX_PW_P3    0x14  // 'RX payload width, pipe3' register address
#define RX_PW_P4    0x15  // 'RX payload width, pipe4' register address
#define RX_PW_P5    0x16  // 'RX payload width, pipe5' register address
#define FIFO_STATUS 0x17  // 'FIFO Status Register' register address


发射端代码

初始化IO口

// 初始化IO
void init_io(void)
{
    CE  = 0;        // 待机
    CSN = 1;        // SPI禁止
    SCK = 0;        // SPI时钟置低
    IRQ = 1;        // 中断复位
}

写一个字节到24L01 同时读出一个字节

uchar SPI_RW(uchar byte)
{
    uchar   bit_ctr;
    // output 8-bits
    for (bit_ctr = 0; bit_ctr < 8; bit_ctr++)
    {
        MOSI = (byte & 0x80);   // output ‘byte’  MSB to MOSI
        byte = (byte << 1);     // shift next bit into MSB..
        SCK = 1;                // Set SCK high.. 24L01 read 1-bit from MOSI and output 1-bit to MISO 
        byte |= MISO;           // capture current MISO bit 
        SCK = 0;                // ..then set SCK low again
    }

    return (byte);  // return read byte
}

向寄存器reg写一个字节,同时返回状态字节

uchar SPI_RW_Reg(uchar reg, uchar value)
{
    uchar status;
    CSN = 0;                // CSN low, init SPI transaction, start transmitting data
    status = SPI_RW(reg);   // select register and return status byte
    SPI_RW(value);          // ..and write value to it..
    CSN = 1;                // CSN high again, transmission end
    return(status);         // return nRF24L01 status byte
}

往reg寄存器写入 bytes 个字节 通常用来写入发射通道或接收/发送地址

uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar bytes)
{
    uchar status,byte_ctr;
    CSN = 0;                // Set CSN low, init SPI tranaction
    status = SPI_RW(reg);   // Select register to write to & return status byte
    
    for (byte_ctr = 0; byte_ctr < bytes; byte_ctr++) 
        SPI_RW(*pBuf++);    // 逐个字节写入nRF24L01
    CSN = 1;                // Set CSN high again 结束数据传输
    return(status);         // 返回状态寄存器
}

设置nRF24L01为发送模式

void TX_Mode(uchar *BUF)
{
    CE = 0;
    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, BUF, TX_PLOAD_WIDTH); 
    SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);  
    SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);
    SPI_RW_Reg(WRITE_REG + SETUP_RETR, 0x0a); 
    SPI_RW_Reg(WRITE_REG + RF_CH, 40);  
    SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);
    SPI_RW_Reg(WRITE_REG + CONFIG, 0x0e);
    delay_ms(150);
    CE = 1;
}

检查接收设备有无接收到数据包

uchar Check_ACK(bit clear)
{
    delay_ms(200);
    while(IRQ);             // 等待数据接收完成
    sta = SPI_RW(NOP);      // 返回状态寄存器
    if(TX_DS)
    {
		SIG = 0 ;
    }
    if(MAX_RT)
        if(clear)                         // 是否清除TX FIFO,没有清除在复位MAX_RT中断标志后重发
            SPI_RW(FLUSH_TX);
    SPI_RW_Reg(WRITE_REG + STATUS, sta);  // 清除TX_DS或MAX_RT中断标志
    IRQ = 1;
    if(TX_DS)
        return(0x00);
    else
        return(0xff);
}

按键扫描

void CheckButtons()
{
    if(KEY1 == 0)
    {
        delay_ms(10);
        if(KEY1 == 0)
        {
			while(!KEY1);
            TX_BUF[0] = 1;          // 数据送到缓存
            TX_Mode(TX_BUF);        // 把nRF24L01设置为发送模式并发送数据
            Check_ACK(0);           // 等待发送完毕,清除TX FIFO
            delay_ms(250);
            delay_ms(250);
        }
    }

    if(KEY2 == 0)
    {
        delay_ms(10);
        if(KEY2 == 0)
        {
			while(!KEY2);
            TX_BUF[0] = 2;          // 数据送到缓存
            TX_Mode(TX_BUF);            // 把nRF24L01设置为发送模式并发送数据 
            Check_ACK(0);               // 等待发送完毕,清除TX FIFO
            delay_ms(250);
            delay_ms(250);
        }
    }
}

主函数

void main(void)
{
    init_io();                      // 初始化IO
    while(1)
    {
        CheckButtons();           // 按键扫描
		SIG = 1;
    }
}

接收端代码

从reg寄存器读一字节

uchar SPI_Read(uchar reg)
{
	uchar reg_val;
    CSN = 0;                    // CSN置低,开始传输数据
	SPI_RW(reg);                // 选择寄存器
	reg_val = SPI_RW(0);        // 然后从该寄存器读数据
	CSN = 1;                    // CSN拉高,结束数据传输
	return(reg_val);            // 返回寄存器数据

}

从reg寄存器读 bytes 个字节

uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar bytes)
{
    uchar status, byte_ctr;
    CSN = 0;                    // Set CSN low, init SPI tranaction
    status = SPI_RW(reg);       // Select register to read & return status byte
    
    for (byte_ctr = 0; byte_ctr < bytes; byte_ctr++)
        pBuf[byte_ctr] = SPI_RW(0); //逐个字节从nRF24L01读出
    CSN = 1;                    // set CSN high, stop transaction
    return(status);             // return nRF24L01 status byte
}

设置nRF24L01为接收模式

void RX_Mode(void)
{
    CE = 0;
    SPI_Write_Buf(WRITE_REG + RX_ADDR_P0, TX_ADDRESS, TX_ADR_WIDTH);   
    SPI_RW_Reg(WRITE_REG + EN_AA, 0x01);      
    SPI_RW_Reg(WRITE_REG + EN_RXADDR, 0x01);   
    SPI_RW_Reg(WRITE_REG + RF_CH, 40);   
    SPI_RW_Reg(WRITE_REG + RX_PW_P0, TX_PLOAD_WIDTH); 
    SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);   
    SPI_RW_Reg(WRITE_REG + CONFIG, 0x0f); 
    delay_ms(150);
    CE = 1;                                    
}

判断是否接收数据包

void RX_FLAG()
{
    sta = SPI_Read(STATUS);      // 读状态寄存器
    if(RX_DR)                    // 判断是否接受到数据
    {
        SPI_Read_Buf(RD_RX_PLOAD, RX_BUF, TX_PLOAD_WIDTH);  // 从RX FIFO读出数据
        flag = 1;
    }
    SPI_RW_Reg(WRITE_REG + STATUS, sta);  // 清除RX_DS中断标志
}

接收数据(标志判断)

void RX_action()
{
	if(flag)                   // 接受完成
    {
		Send(RX_BUF[0]);	   //串口发送数值
		SIG = 0 ;
		ACTION_RX();
        flag = 0;               // 清标志
		SIG = 1;
        delay_ms(250);
    }
}

主函数

void main(void)
{
    init_io();                      // 初始化IO
	RX_Mode();						// 设置为接收模式
	USART();						// 串口初始化(9600并用16进制接收)
    while(1)
    {
        RX_FLAG();
		RX_action();
    }
}

工程文件下载