STM32
直播中

张艳

7年用户 1590经验值
私信 关注
[问答]

如何利用STM32软件SPI去实现NRF24L01通信呢

如何利用STM32软件SPI去实现NRF24L01通信呢?求解

回帖(1)

于敏

2021-12-16 15:47:19
STM32软件SPI实现NRF24L01

NRF一共是八个引脚,除去VCC和GND还有六个引脚。
所以我们只需要配置这六个引脚就可以了。
这六个引脚分别是SCK,MOSI,MISO,CSN,CE,IRQ
除了MISO和IRQ配置成输入,其他的都配置为输出即可,其实如果不配IRQ也没事,如果有需要的话可以配置,不配的也是可以的,不会影响单片机的通信。
配置输入和输出就和配置LED和按键输入是一样的,代码可以参考LED和按键输入的代码。
以上的代码是NRF的初始化
接下来我们需要几个宏定义,具体的作用是方面我们随时改变管教的高低电平

/* SCK PA0 */
#define NRF_SCK(x) GPIOA->ODR &=~(1<<0);GPIOA->ODR |= x<<0
/* MOSI PA1*/
#define NRF_MOSI(x) GPIOA->ODR &=~(1<<1);GPIOA->ODR |= x<<1
/* MISO PA2*/
#define NRF_MISO(x) GPIOA->ODR &=~(1<<2);GPIOA->ODR |= x<<2
#define NRF_MISO_VALUE  (u8)((GPIOA->IDR&0x04)>>2)
/* CSN PA3 */
#define NRF_CSN(x) GPIOA->ODR &=~(1<<3);GPIOA->ODR |= x<<3
/* CE  PA4 */
#define NRF_CE(x) GPIOA->ODR &=~(1<<4);GPIOA->ODR |= x<<4
/* IRQ PA5 */
#define NRF_IRQ_VALUE  (u8)((GPIOA->IDR&0x20)>>5)
这里我写的代码是基于寄存器的操作,这个是简单的宏定义,例如当我们写NRF_SCK(1)时PA0也就是接到NRF的SCK引脚配置为高电平。
我们还需要相应的寄存器的地址宏定义

#define NRF_READ_REG                        0X00
#define NRF_WRITE_REG                 0X20
#define RD_RX_PLOAD         0X61
#define WR_TX_PLOAD         0XA0
#define FLUSH_TX                        0XE1
#define        FLUSH_RX                        0XE2
#define REUSE_TX_PL                0XE3
#define NOP                                                0XFF
#define CONFIG          0x00  
#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 MAX_TX                  0x10
#define TX_OK                   0x20  
#define RX_OK                   0x40
#define OBSERVE_TX      0x08  
#define CD              0x09  
#define RX_ADDR_P0      0x0A
#define RX_ADDR_P1      0x0B  
#define RX_ADDR_P2      0x0C
#define RX_ADDR_P3      0x0D  
#define RX_ADDR_P4      0x0E  
#define RX_ADDR_P5      0x0F  
#define TX_ADDR         0x10  
#define RX_PW_P0        0x11  
#define RX_PW_P1        0x12
#define RX_PW_P2        0x13  
#define RX_PW_P3        0x14
#define RX_PW_P4        0x15
#define RX_PW_P5        0x16
#define NRF_FIFO_STATUS 0x17
#define TX_ADR_WIDTH    5          
#define RX_ADR_WIDTH    5   
#define TX_PLOAD_WIDTH  32       
#define RX_PLOAD_WIDTH  32       
这个时候我们基本已经完成了,现在我们可以写SPI了,简单的操作,具体几个函数如下

/* 写入的时候并读取一个字节 */
u8 NRF_WR(u8 byte)
{
        u8 i;
        for(i = 0;i<8; i++)
        {
                /* 先发送高位 */
                NRF_MOSI((byte & 0X80)>>7);
                byte = (byte << 1);
                NRF_SCK(1);
                byte |= NRF_MISO_VALUE;
                NRF_SCK(0);
        }
        return byte;
}


/* 向一个寄存器写入一个值 */
u8 NRF_WR_REG(u8 reg,u8 value)
{
        u8 status;
        NRF_CSN(0);
        status = NRF_WR(reg);
        NRF_WR(value);
        NRF_CSN(1);
        return status;
}


/* 读取寄存器 */
u8 NRF_READ(u8 reg)
{
        u8 reg_val;
        NRF_CSN(0);
        NRF_WR(reg);
        reg_val = NRF_WR(0XFF);
        NRF_CSN(1);
        return reg_val;
}


/* 读取指定长度的值 */
u8 NRF_READ_BUF(u8 reg,u8 *pBuf,u8 bytes)
{
        u8 status,byte;
        NRF_CSN(0);
        status = NRF_WR(reg);
        for(byte = 0;byte < bytes;byte ++)
        {
                pBuf[byte] = NRF_WR(0);
        }
        NRF_CSN(1);
        return status;
}
/* 写入指定长度的值 */
u8 NRF_WRITE_BUF(u8 reg,u8 *pBuf,u8 bytes)
{
        u8 status,byte;
        NRF_CSN(0);
        status = NRF_WR(reg);
        for(byte = 0;byte < bytes;byte ++)
        {
                NRF_WR(*pBuf++);
        }
        NRF_CSN(1);
        return status;
}


好大致的操作已经完成了,我们现在向NRF寄存器中写入一个值,然后我们再去读这个NRF寄存器的值,应该就是我们写的值。这一步就是我们所说的NRF自检。

/* NRF自检 */
/* 就是先写入一个值 然后再读出一个值 看一下自己读出来的是否和自己写入的是否一样*/
/* 一样自检通过 不一样自检不通过 */
u8 NRF24L01_Check(void)
{
        u8 i;
        u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
        NRF_WRITE_BUF((NRF_WRITE_REG+TX_ADDR),buf,5);
        NRF_READ_BUF(TX_ADDR,buf,5);
        for(i=0;i<5;i++)if(buf!=0XA5)break;                                                                   
        if(i!=5)return 1;
        return 0;                 
}


这个时候该配置的都配置了接下来就是两个单片机之间的通信了。
主机我们负责发送数据,我们需要配置发送地址和发送的速率
从机负责接受数据,我们需要配置接收地址和接受速率等

/* 配置接受和发送的地址 */
const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x09,0x25,0x57,0x11,0x01}; //发送地址
const u8 RX_ADDRESS[RX_ADR_WIDTH]={0x09,0x25,0x57,0x11,0x01}; //发送地址
地址可以自己随意设定,但是主机和从机的地址应该一样,否则连接不上


/* RX模式 */
void RX_MODE(void)
{
        NRF_CE(0);
        /* 配置接收的地址 */
        NRF_WRITE_BUF(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);
        /* 通道0自动应答 */
        NRF_WR_REG(NRF_WRITE_REG+EN_AA,0x01);      
        /* 接收数据通道0允许 */
        NRF_WR_REG(NRF_WRITE_REG+EN_RXADDR,0x01);
        /* 设置工作频率 */       
        NRF_WR_REG(NRF_WRITE_REG+RF_CH,40);       
        /* 接收数据有效宽度为32字节 */       
        NRF_WR_REG(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);       
        /* 低噪声放大 发射功率 数据传输率2M */
        NRF_WR_REG(NRF_WRITE_REG+RF_SETUP,0x0f);
        /* 配置为接收 */
        NRF_WR_REG(NRF_WRITE_REG+CONFIG, 0x0f);
        NRF_CE(1);
}


/* TX模式 */
void TX_MODE(void)
{
        NRF_CE(0);
        /* 发送的地址 */
        NRF_WRITE_BUF(NRF_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);
        /* 有效的字节宽度为32字节 */
        NRF_WRITE_BUF(NRF_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);
        /* 通道0自动应答 */
        NRF_WR_REG(NRF_WRITE_REG+EN_AA,0x01);
        /* 数据接收通道0允许 */
        NRF_WR_REG(NRF_WRITE_REG+EN_RXADDR,0x01);          
        /* 等待500+86us 自动重发次数为10次*/
        NRF_WR_REG(NRF_WRITE_REG+SETUP_RETR,0x1a);
        /* 设置工作频率 */
        NRF_WR_REG(NRF_WRITE_REG+RF_CH,40);
        /* 低噪声放大 发射功率 数据传输率2M */
        NRF_WR_REG(NRF_WRITE_REG+RF_SETUP,0x0f);
        /* 配置为发送 */
        NRF_WR_REG(NRF_WRITE_REG+CONFIG, 0x0e);
        NRF_CE(1);
}


**好 至此我们配置已经全部完成了大致分为


  • SPI初始化(必须)
  • 引脚高低电平的宏定义(不是必须)
  • 地址宏定义(不写的话需要查NRF数据手册很麻烦)
  • 自检(检验NRF是否好坏,代码是否正确)
  • 配置接受发送模式(通信必须)
  • 配置相应的速率,接收发送地址等
  • 实现通信**


举报

更多回帖

×
20
完善资料,
赚取积分