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是否好坏,代码是否正确)
- 配置接受发送模式(通信必须)
- 配置相应的速率,接收发送地址等
- 实现通信**