CW32L012/F030灵眸X1智能小车——使用NRF24L01通信

一、NRF24L01模块介绍

引脚介绍

wKgZO2nLxH2AAWOnAABcaF83udk820.jpg

NRF24L01所使用的通讯协议为SPI,SPI又可分为软件SPI和硬件SPI。

硬件SPI与软件SPI相比,硬件SPI是靠硬件上面的SPI控制器,所有的时钟边缘采样,时钟发生,还有时序控制,都是由硬件完成的。它降低了CPU的使用率,提高了运行速度。软件SPI就是用代码控制IO输出高低电平,模拟SPI的时序,这种方法通信速度较慢,且不可靠。

想要使用硬件SPI驱动,需要确定使用的引脚是否有SPI外设功能。可以通过用户手册146页进行查看。

当前使用的是硬件SPI接口,而NRF24L01我们需要与它发送数据也需要接收数据,故使用的是4线的SPI,使用到了时钟线SCK、主机输出从机输入线MOSI、主机输入从机输出线MISO和软件控制的片选线NSS。所以除了这些引脚需要使用硬件SPI功能的引脚外,其他引脚都可以使用开发板上其他的GPIO。这里选择使用PB13/PB14/PB15的SPI复用功能 。其他对应接入的引脚请按照你的需要。这里选择的引脚见下表。

模块在开发板上的接线为:

GND ->GND

VCC ->3.3V

CSN ->PA10

CE ->PA9

MOSI->PB15

MISO->PB14

SCK ->PB13

IRQ ->PB8

模块接口原理图

wKgZO2nLxH6AKXHvAAAnaIrGWGI454.jpg

在我们小车主控板中,NRF24L01接口如上,注意不要插反。

二、工程代码

在NRF24L01.H文件中宏定义引脚以及寄存器

//通信引脚
#define IRQ_Pin  GPIO_PIN_8
#define CE_Pin   GPIO_PIN_9
#define CS_Pin   GPIO_PIN_10
#define SCK_Pin  GPIO_PIN_13
#define MISO_Pin GPIO_PIN_14
#define MOSI_Pin GPIO_PIN_15
//寄存器地址代码
#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 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状态寄存器
#define DYNPD       0x1C  // 动态有效数据长度寄存器
#define FEATURE     0x1D  // 特性寄存器
//操作指令代码
#define R_REGISTER    0x00  // 读寄存器命令
#define W_REGISTER    0x20  // 写寄存器命令
#define R_RX_PAYLOAD  0x61  // 读取接收有效数据命令
#define W_TX_PAYLOAD  0xA0  // 写入发送有效数据命令
#define FLUSH_TX      0xE1  // 清空发送FIFO命令
#define FLUSH_RX      0xE2  // 清空接收FIFO命令
#define NOP           0xFF  // 无操作命令
//状态
#define RX_OK         0X40
#define TX_OK         0X20
#define MAX_OK        0X10

然后在NRF24L01.C文件中先配置SPI,随后配置NRF24L01寄存器

void NRF24L01_GPIO_Init(void)
{
        RCC_APBPeriphClk_Enable1(RCC_APB1_PERIPH_SPI2,ENABLE);

        __RCC_GPIOA_CLK_ENABLE();
        __RCC_GPIOB_CLK_ENABLE();

        PB13_AFx_SPI2SCK();//开启引脚复用功能
        PB14_AFx_SPI2MISO();
        PB15_AFx_SPI2MOSI();

        GPIO_InitTypeDef GPIO_InitStruct; 
        GPIO_InitStruct.IT=GPIO_IT_NONE;
        GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pins=MOSI_Pin | SCK_Pin;
        GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;
        GPIO_Init(CW_GPIOB, &GPIO_InitStruct);

        GPIO_InitStruct.IT=GPIO_IT_NONE;
        GPIO_InitStruct.Mode=GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pins=CS_Pin | CE_Pin;
        GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;
        GPIO_Init(CW_GPIOA, &GPIO_InitStruct);

        GPIO_InitStruct.IT=GPIO_IT_NONE;
        GPIO_InitStruct.Mode=GPIO_MODE_INPUT_PULLUP;
        GPIO_InitStruct.Pins=MISO_Pin | IRQ_Pin;
        GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;
        GPIO_Init(CW_GPIOB, &GPIO_InitStruct);

        W_CS(1);

        SPI_InitTypeDef  SPI_InitStructure; // SPI 初始化结构体
        SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;    // 双线全双工
        SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                         // 主机模式
        SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                     // 帧数据长度为8bit
        SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                            // 时钟空闲电平为低
        SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                          // 第1个边沿采样
        SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                             // 片选信号由SSI寄存器控制
        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;    // 波特率为PCLK的8分频
        SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                    // 最高有效位 MSB 收发在前
        SPI_InitStructure.SPI_Speed = SPI_Speed_Low;                          // 低速SPI

        SPI_Init(CW_SPI2, &SPI_InitStructure);   // 初始化
        SPI_Cmd(CW_SPI2, ENABLE);   // 使能SPI1
}
uint8_t SPI_SwapByte(uint8_t TxByte)
{
        uint16_t l_Data = 0;
        while(SPI_GetFlagStatus(CW_SPI2, SPI_FLAG_TXE) == RESET);
  SPI_SendData(CW_SPI2, TxByte);
  while(SPI_GetFlagStatus(CW_SPI2, SPI_FLAG_RXNE) == RESET);
  l_Data = SPI_ReceiveData(CW_SPI2);//读取接收数据    
        return l_Data;                //返回
}
void W_Reg(uint8_t Reg,uint8_t value)//写字节
{
        W_CS(0);
        SPI_SwapByte(Reg);
        SPI_SwapByte(value);
        W_CS(1);
}
uint8_t R_Reg(uint8_t Reg)//读字节
{
        uint8_t value;
        W_CS(0);
        SPI_SwapByte(Reg);
        value=SPI_SwapByte(NOP);
        W_CS(1);

        return value;
}
void W_Buf(uint8_t Reg, uint8_t* Buf, uint8_t Len)//连续写
{
        uint8_t i;
        W_CS(0);
        SPI_SwapByte(Reg);
        for(i=0;i< Len;i++)
        {
                SPI_SwapByte(Buf[i]);
        }
        W_CS(1);
}
void R_Buf(uint8_t Reg, uint8_t* Buf, uint8_t Len)//连续读
{
        uint8_t i;
        W_CS(0);
        SPI_SwapByte(Reg);
        for(i=0;i< Len;i++)
        {
                Buf[i]=SPI_SwapByte(NOP);
        }
        W_CS(1);
}
void NRF24L01_Init(void)//初始化
{
        NRF24L01_GPIO_Init();

        W_CE(0);
        W_Buf(W_REGISTER+TX_ADDR,T_ADDR,5);
        W_Buf(W_REGISTER+RX_ADDR_P0,R_ADDR,5);
        W_Reg(W_REGISTER+CONFIG,0x0F);
        W_Reg(W_REGISTER+EN_AA,0x01);
        W_Reg(W_REGISTER+RF_CH,0x00);
        W_Reg(W_REGISTER+RX_PW_P0,32);
        W_Reg(W_REGISTER+EN_RXADDR,0X01);
        W_Reg(W_REGISTER+SETUP_RETR,0X1A);
        W_Reg(FLUSH_RX,NOP);
        W_CE(1);

}
void Receive(uint8_t *Buf)//数据接收
{
        uint8_t Status;
        Status=R_Reg(R_REGISTER+STATUS);
        if(Status & RX_OK)
        {
                R_Buf(R_RX_PAYLOAD,Buf,32);
                W_Reg(FLUSH_RX,NOP);
                W_Reg(W_REGISTER+STATUS,Status);
                RX_Flag=1;
                Delay_us(150);
        }
}
uint8_t Send(uint8_t *Buf)//数据发送
{
        uint8_t Status;
        W_Buf(W_TX_PAYLOAD,Buf,32);

        W_CE(0);
        W_Reg(W_REGISTER+CONFIG,0x0E);
        W_CE(1);

        while(R_IRQ==1);
        Status=R_Reg(R_REGISTER+STATUS);
        if(Status & MAX_OK)
        {
                W_Reg(FLUSH_TX,NOP);
                W_Reg(W_REGISTER+STATUS,Status);
                return MAX_OK;
        }
        if(Status & TX_OK)
        {
                W_Reg(W_REGISTER+STATUS,Status);
                return TX_OK;
        }
}
uint8_t Receive_Flag(void)//接收标志位
{
        if(RX_Flag==1)
        {
                RX_Flag=0;
                return 1;
        }
        else{
                return 0;
        }
}

三、实验现象

在发送端main函数中写入如下代码

int main(void)
{
        uint8_t Buf[32]={0x00,0x67,0x68,0x69,0x70},num=0;
        OLED_Init();//初始化
        NRF24L01_Init();
        OLED_ShowString(1,1,"Send ok");
        while(1)
        {
                num++;
                Buf[0]=num;
                Send(Buf);
                Delay_s(1);
        }
}

在接收端main函数中写入如下代码

int main(void)
{
        uint8_t Buf[32]={0};
        OLED_Init();//初始化
        NRF24L01_Init();
        OLED_ShowString(1,1,"Receive ok");
        while(1)
        {
                Receive(Buf);
                if(Receive_Flag()==1)
                {
                        OLED_ShowNum(3,1,Buf[0],3);
                        OLED_ShowHexNum(2,4,Buf[1],2);
                        OLED_ShowHexNum(2,7,Buf[2],2);
                        OLED_ShowHexNum(2,10,Buf[3],2);
                        OLED_ShowHexNum(2,13,Buf[4],2);
                }
        }
}

然后现象如下

发送端发送四个十六进制数据以及一个递增的十进制数据,OLED屏幕显示 Send ok

接收端第一行显示Receive OK,第二行显示接收到的固定十六进制数据,第三行显示接收到的十进制数据

wKgZO2nLxICABrnXAA1T1MDbySs891.jpg

推荐阅读:

时间跨度长达13年 A股又一百亿级财务造假案曝光!5家上市公司造假细节浮出水面

一周牛熊股 | 沪指守住2900点,本周最牛股五天翻倍

徽商银行分红之争再起!中静系125亿补偿要求再遭否决,A股IPO进程受阻

最高2000万元支持 深圳前海《科技创新办法》发布

唤醒沉睡海底的历史瑰宝,唤醒沉睡海底的历史瑰宝

2024中国(张家界)国际新杂技戏剧周将于8月23日启动

您可以还会对下面的文章感兴趣:

暂无相关文章

使用微信扫描二维码后

点击右上角发送给好友