STM32
直播中

李丽华

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

如何去实现基于STM32F103C8T6的NRF24L01无线传输的代码呢

如何去实现基于STM32F103C8T6的NRF24L01无线传输的代码呢?其原理又是怎样的?

回帖(2)

刘东

2021-12-16 15:14:02
前言

STM32下NRF24L01实现无线传输
一、原理图

1.STM32F103C8T6






2.NRF24L01






NRF24L01是 nordic 的无线通信芯片,它具有以下特点:
1) 2.4G 全球开放的 ISM 频段(2.400 - 2.4835GHz),免许可证使用;
2)最高工作速率 2Mbps,高校的 GFSK 调制,抗干扰能力强;
3) 125 个可选的频道,满足多点通信和调频通信的需要;
4)内置 CRC 检错和点对多点的通信地址控制;
5)低工作电压(1.9~3.6V),待机模式下状态为 26uA;掉电模式下为 900nA;
6)可设置自动应答,确保数据可靠传输;
7)工作于EnhancedShockBurst 具有Automatic packet handling,Auto packet transaction handling ,可以实现点对点或是 1 对 6 的无线通信,速度可以达到 2M(bps),具有可选的内置包应答机制,极大的降低丢包率。
8)通过 SPI 总线与单片机进行交互,最大通信速率为10Mbps;
二、Keil代码

1.SPI_NRF2401.C


#include "Struct.h"


/******************************************************************************
                                                        宏定义
*******************************************************************************/
#define NRF_CE_GPIO                GPIOC               
#define NRF_CE_Pin                GPIO_Pin_14       
#define NRF_CSN_GPIO        GPIOC               
#define NRF_CSN_Pin                GPIO_Pin_13       
#define NRF_IRQ_GPIO        GPIOC
#define NRF_IRQ_Pin                GPIO_Pin_15


#define NRF_CE_H                   NRF_CE_GPIO ->BSRR = NRF_CE_Pin  //CE高电平
#define NRF_CE_L                   NRF_CE_GPIO ->BRR  = NRF_CE_Pin  //CE低电平
#define NRF_CSN_H                  NRF_CSN_GPIO->BSRR = NRF_CSN_Pin //CSN高电平
#define NRF_CSN_L                  NRF_CSN_GPIO->BRR  = NRF_CSN_Pin //CSN高电平
#define        NRF_IRQ_Read        NRF_IRQ_GPIO->IDR  & NRF_IRQ_Pin //IRQ读数据
/******************************************************************************
                                                        变量定义
*******************************************************************************/
uint8_t NRF24L01_RXDATA[32];//nrf24l01接收到的数据
uint8_t NRF24L01_TXDATA[32];//nrf24l01需要发送的数据
static uint8_t TX_ADDRESS[5]= {0x1A,0x2A,0x3A,0x4A,0x5A};//本地地址
static uint8_t RX_ADDRESS[5]= {0x1A,0x2A,0x3A,0x4A,0x5A};//接收地址
static uint16_t Nrf_Erro=0;


/******************************************************************************
函数原型:        void SPI2_Init(void)
功  能:        初始化SPI总线
*******************************************************************************/
void SPI2_Init(void)
{
        SPI_InitTypeDef SPI_InitStructure;
        GPIO_InitTypeDef GPIO_InitStructure;
        EXTI_InitTypeDef EXTI_InitStructure;
       
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
        //配置SCK,MISO,MOSI引脚  
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用功能
        GPIO_Init(GPIOB, &GPIO_InitStructure);
        //配置CE引脚
        GPIO_InitStructure.GPIO_Pin = NRF_CE_Pin;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
        GPIO_Init(NRF_CE_GPIO, &GPIO_InitStructure);
        //配置CSN引脚
        GPIO_InitStructure.GPIO_Pin = NRF_CSN_Pin;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
        GPIO_Init(NRF_CSN_GPIO, &GPIO_InitStructure);       
        //配置IRQ引脚
        GPIO_InitStructure.GPIO_Pin = NRF_IRQ_Pin;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
        GPIO_Init(NRF_IRQ_GPIO, &GPIO_InitStructure);       
       
        GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource15);
    EXTI_InitStructure.EXTI_Line=EXTI_Line15;
    EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;//外部中断
    EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发
    EXTI_InitStructure.EXTI_LineCmd=ENABLE;
    EXTI_Init(&EXTI_InitStructure);
       
        NRF_CSN_H;        //禁止NRF器件
        SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //双线全双工
        SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主模式
        SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //数据大小8位
        SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //时钟极性,空闲时为低
        SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //第1个边沿有效,上升沿为采样时刻
        SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;  //NSS信号由软件产生
        SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; //8分频,9MHz
        SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位在前
        SPI_InitStructure.SPI_CRCPolynomial = 7;
        SPI_Init(SPI2, &SPI_InitStructure);
          
        SPI_Cmd(SPI2, ENABLE);//使能 SPI1
       
//        PrintString("rn SPI2     初始化完成!");
}
/******************************************************************************
函数原型:        uint8_t SPI_RW(uint8_t data)
功  能:        SPI总线读写
返 回 值:        返回SPI总线读取数据
*******************************************************************************/
uint8_t SPI_RW(uint8_t data)
{
        while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);//当SPI发送缓冲器非空时等待  
        SPI_I2S_SendData(SPI2, data);//通过SPI总线发送一字节数据
        while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);//当SPI接收缓冲器为空时等待
        return SPI_I2S_ReceiveData(SPI2);
}


/******************************************************************************
函数原型:        uint8_t NRF_Write_Reg(uint8_t reg, uint8_t value)
功    能:        NRF写寄存器
返 回 值:        NRF写寄存器返回值
*******************************************************************************/
uint8_t NRF_Write_Reg(uint8_t reg, uint8_t value)
{
        uint8_t status;
        NRF_CSN_L;                //选通NRF器件
        status = SPI_RW(reg);//写寄存器地址
        SPI_RW(value);        //写数据
        NRF_CSN_H;                //禁止NRF器件
        return         status;
}


/******************************************************************************
函数原型:        uint8_t NRF_Read_Reg(uint8_t reg)
功    能:        NRF读寄存器
返 回 值:        寄存器数据
*******************************************************************************/
uint8_t NRF_Read_Reg(uint8_t reg)
{
        uint8_t reg_val;
        NRF_CSN_L;                //选通NRF器件
        SPI_RW(reg);        //写寄存器地址
        reg_val = SPI_RW(0);//读取该寄存器返回数据
        NRF_CSN_H;                //禁止NRF器件
    return         reg_val;
}


/******************************************************************************
函数原型:        uint8_t NRF_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
功    能:        NRF写缓冲区
返 回 值:        NRF写缓冲区返回值
*******************************************************************************/
uint8_t NRF_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
{
        uint8_t i;
        uint8_t status;
        NRF_CSN_L;        //选通NRF器件
        status = SPI_RW(reg);//写寄存器地址
        for(i=0; i         {
                SPI_RW(pBuf);//写数据
        }
        NRF_CSN_H;        //禁止NRF器件
    return         status;       
}


/******************************************************************************
函数原型:        uint8_t NRF_Read_Buff(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
功    能:        NRF读缓冲区
返 回 值:        缓冲区数据
*******************************************************************************/
uint8_t NRF_Read_Buff(uint8_t reg, uint8_t *pBuf, uint8_t uchars)
{
        uint8_t i;
        uint8_t status;
        NRF_CSN_L;        //选通NRF器件
        status = SPI_RW(reg);//写寄存器地址
        for(i=0; i         {
                pBuf = SPI_RW(0);//读取返回数据       
        }
        NRF_CSN_H;        //禁止NRF器件
    return         status;
}


/******************************************************************************
函数原型:        void NRF24L01_Check(void)
功    能:        检查NRF器件是否正常
*******************************************************************************/
void NRF24L01_Check(void)
{
        uint8_t buf[5];
        uint8_t i;
        //写入5个字节的地址
        NRF_Write_Buf(NRF_WRITE_REG+TX_ADDR,TX_ADDRESS,5);
        //读出写入的地址
        NRF_Read_Buff(TX_ADDR,buf,5);
        //比较
        for(i=0;i<5;i++)
        {
                if(buf!=TX_ADDRESS)
                        break;
        }
//        if(i==5)
//                PrintString("rn NRF24L01 初始化成功!");
//        else
//                PrintString("rn NRF24L01 初始化失败!");
}


/******************************************************************************
函数原型:        static void NRF24L01_Set_TX(void)
功    能:        将NRF24L01设置为发送模式
*******************************************************************************/
static void NRF24L01_Set_TX(void)
{
        NRF_CE_L;
        NRF_Write_Reg(NRF_WRITE_REG + CONFIG,0x0E);//发送
        NRF_CE_H;
}


/******************************************************************************
函数原型:        static void NRF24L01_Set_RX(void)
功    能:        将NRF24L01设置为接收模式
*******************************************************************************/
static void NRF24L01_Set_RX(void)
{
        NRF_CE_L;
        NRF_Write_Reg(NRF_WRITE_REG + CONFIG,0x0F);//接收
        NRF_CE_H;
}


/******************************************************************************
函数原型:        void NRF_Send_TX(uint8_t * tx_buf, uint8_t len)
功    能:        NRF2401发送数据包
*******************************************************************************/
void NRF_Send_TX(uint8_t * tx_buf, uint8_t len)
{       
        NRF24L01_Set_TX();
        NRF_CE_L;//进入待机模式1       
        NRF_Write_Buf(WR_TX_PLOAD, tx_buf, len);//装载数据
        NRF_CE_H;//设置CE为高,启动发射。CE高电平持续时间最小为10us
}
/******************************************************************************
函数原型:        void NRF24L01_Init(uint8_t Chanal,uint8_t Mode)
功    能:        NRF24L01初始化
参    数:        Chanal,RF通道
*******************************************************************************/
void NRF24L01_Init(uint8_t Chanal,uint8_t Mode)
{
        NRF_CE_L;
               
        NRF_Write_Reg(FLUSH_TX,0xff);//清空发送缓冲区
        NRF_Write_Reg(FLUSH_RX,0xff);//清空接收缓冲区
        NRF_Write_Buf(NRF_WRITE_REG + TX_ADDR,   TX_ADDRESS,5); //写TX节点地址         
        NRF_Write_Buf(NRF_WRITE_REG + RX_ADDR_P0,RX_ADDRESS,5);        //写RX节点地址


        NRF_Write_Reg(NRF_WRITE_REG + EN_AA,     0x01); //使能通道0的自动应答
        NRF_Write_Reg(NRF_WRITE_REG + EN_RXADDR, 0x01);        //使能通道0的接收地址
        NRF_Write_Reg(NRF_WRITE_REG + SETUP_RETR,0x1a);        //设置自动重发间隔时间:500us;最大自动重发次数:10次
        NRF_Write_Reg(NRF_WRITE_REG + RF_CH,   Chanal);        //设置RF通道为CHANAL
        NRF_Write_Reg(NRF_WRITE_REG + RX_PW_P0,    32);        //设置通道0的有效数据宽度
        NRF_Write_Reg(NRF_WRITE_REG + RF_SETUP,  0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启
       
        if(Mode==TX)
                NRF_Write_Reg(NRF_WRITE_REG + CONFIG,0x0E);//发送
        else if(Mode==RX)
                NRF_Write_Reg(NRF_WRITE_REG + CONFIG,0x0F);//接收
       
        NRF_CE_H;
}
举报

翁靠庭

2021-12-16 15:14:09
/******************************************************************************
函数原型:        static void NRF24L01_Analyse(void)
功    能:        分析NRF24L01收到的数据帧
*******************************************************************************/
static void NRF24L01_Analyse(void)
{
        uint8_t sum = 0,i;
        uint8_t len = NRF24L01_RXDATA[3] + 5;
        //uint8_t i=0;
        for(i=3;i                 sum ^= NRF24L01_RXDATA;
        if( sum!=NRF24L01_RXDATA[len] )        return;        //数据校验
        if( NRF24L01_RXDATA[0] != '$' )        return;        //数据校验
        if( NRF24L01_RXDATA[1] != 'M' )        return;        //数据校验
        if( NRF24L01_RXDATA[2] != '>' )        return;        //MWC发送给上位机的标志
//        LEDGreen_ON;
//        if( NRF24L01_RXDATA[4] == MSP_FLY_DATA )//功能桢标志
//        {
//                Battery_Fly =( (uint16_t)(NRF24L01_RXDATA[6])  << 8 ) | NRF24L01_RXDATA[5];       
//                THROTTLE1  = ( (uint16_t)(NRF24L01_RXDATA[8])  << 8 ) | NRF24L01_RXDATA[7];
//                THROTTLE2  = ( (uint16_t)(NRF24L01_RXDATA[10]) << 8 ) | NRF24L01_RXDATA[9];       
//                THROTTLE3  = ( (uint16_t)(NRF24L01_RXDATA[12]) << 8 ) | NRF24L01_RXDATA[11];       
//                THROTTLE4  = ( (uint16_t)(NRF24L01_RXDATA[14]) << 8 ) | NRF24L01_RXDATA[13];       
//                pid[0].kp = NRF24L01_RXDATA[15];
//                pid[0].ki = NRF24L01_RXDATA[16];
//                pid[0].kd = NRF24L01_RXDATA[17];
//               
//                pid[1].kp = NRF24L01_RXDATA[18];
//                pid[1].ki = NRF24L01_RXDATA[19];
//                pid[1].kd = NRF24L01_RXDATA[20];


//                pid[2].kp = NRF24L01_RXDATA[21];
//                pid[2].ki = NRF24L01_RXDATA[22];
//                pid[2].kd = NRF24L01_RXDATA[23];
//               
//                for(i=3;i<10;i++)
//                {
//                        pid.kp=0;
//                        pid.ki=0;
//                        pid.kd=0;
//                }
//        }
//        else if( NRF24L01_RXDATA[4] == MSP_RAW_IMU || NRF24L01_RXDATA[4] == MSP_ATTITUDE )//功能桢标志
//                Uart_Send(NRF24L01_RXDATA,len+1);
}


/******************************************************************************
函数原型:        void NRF24L01_IRQ(void)
功    能:        NRF24L01中断
*******************************************************************************/
void NRF24L01_IRQ(void)
{
        uint8_t status = NRF_Read_Reg(NRF_READ_REG + NRFRegSTATUS);
       
        if(status & (1<         {
                uint8_t rx_len = NRF_Read_Reg(R_RX_PL_WID);//收到数据长度
                if(rx_len==32)
                {
                        NRF_Read_Buff(RD_RX_PLOAD,NRF24L01_RXDATA,rx_len);//读取接收FIFO数据
                        Nrf_Erro = 0;
                }
                else
                {
                        NRF_Write_Reg(FLUSH_RX,0xff);//清空接收缓冲区
                }
        }
        if(status & (1<         {
                if(status & (1<                 {
                        NRF_Write_Reg(FLUSH_TX,0xff);//清空发送缓冲区
                }
        }
        if(status & (1<         {
                NRF24L01_Set_RX();//设置Nrf2401为接收模式
        }
        NRF_Write_Reg(NRF_WRITE_REG + NRFRegSTATUS, status);//清除中断标志位
}


/******************************************************************************
函数原型:        void Nrf_Connect(void)
功    能:        NRF24L01连接函数
*******************************************************************************/
void Nrf_Connect(void)//500HZ
{
        Nrf_Erro++;
        if(Nrf_Erro==1)
        {
                NRF24L01_Analyse();//分析NRF24L01收到的数据帧
        }
        if(Nrf_Erro%50==0)//0.1s未接收nrf数据 ,试图连接飞控
        {       
                NRF24L01_IRQ();//清除中断标志位
        }
        if(Nrf_Erro>=500)//1s未接收nrf数据 ,关闭绿色LED指示灯
        {       
//                LEDGreen_OFF;
                Nrf_Erro = 1;
//                Battery_Fly = 0;
        }
}
2.SPI_NRF24L01.h


#ifndef _SPI_NRF24L01_H_
#define _SPI_NRF24L01_H_
#include "stm32f10x.h"


/******************************************************************************
                                                        宏定义
*******************************************************************************/
#define TX        1
#define RX        2


#define RX_DR        6        //接收数据中断.当接收到有效数据后置一。写‘1’清除中断。
#define TX_DS        5        //数据发送完成中断。当数据发送完成后产生中断。如果工作在自动应答模式下,只有当接收到应答信号后此位置一。写‘1’清除中断。
#define MAX_RT        4        //达到最多次重发中断。写‘1’清除中断。如果MAX_RT中断产生则必须清除后系统才能进行通讯。
#define TX_FULL 0        //TX FIFO寄存器满标志。   1:TX FIFO  寄存器满   0: TX FIFO 寄存器未满, 有可用空间。


/******************************************************************************
                                                        全局变量声明
*******************************************************************************/
extern         uint8_t NRF24L01_RXDATA[32];//nrf24l01接收到的数据
extern         uint8_t NRF24L01_TXDATA[32];//nrf24l01需要发送的数据


/******************************************************************************
                                                        全局函数声明
*******************************************************************************/
void SPI2_Init(void);
void NRF24L01_IRQ(void);
void NRF24L01_Check(void);
void NRF24L01_Init(uint8_t Chanal,uint8_t Mode);
void NRF_Send_TX(uint8_t * tx_buf, uint8_t len);
void Nrf_Connect(void);


uint8_t NRF_Read_Reg(uint8_t reg);
uint8_t NRF_Write_Reg(uint8_t reg, uint8_t value);
uint8_t NRF_Read_Buff(uint8_t reg, uint8_t *pBuf, uint8_t uchars);


/******************************************************************************
                                                        NRF24L01寄存器指令
*******************************************************************************/
#define NRF_READ_REG    0x00          // 读寄存器指令
#define NRF_WRITE_REG   0x20         // 写寄存器指令
#define ACTIVATE                0x50          // follow with 0x73 to activate feature register
#define R_RX_PL_WID           0x60        // 读接收缓冲区的长度
#define RD_RX_PLOAD     0x61          // 读取接收数据指令
#define WR_TX_PLOAD     0xA0          // 写待发数据指令
#define W_ACK_PAYLOAD        0xA8        // Used in RX mode.
#define FLUSH_TX        0xE1         // 冲洗发送 FIFO指令
#define FLUSH_RX        0xE2          // 冲洗接收 FIFO指令
#define REUSE_TX_PL     0xE3          // 定义重复装载数据指令
#define NOP             0xFF          // 保留
/******************************************************************************
                                                        NRF24L01寄存器地址
*******************************************************************************/
#define CONFIG          0x00          // 配置收发状态,CRC校验模式以及收发状态响应方式
#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 NRFRegSTATUS    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          // per pipe DPL control
#define FEATURE                    0x1D          // “Feature” register address
       
#endif
3.Struct.h


#ifndef _STRUCT_H_
#define _STRUCT_H_


#include "stm32f10x.h"
#include "Led.h"
#include "SPI_NRF24L01.h"
#include "Uart.h"


#endif


4.main.c


#include "Struct.h"








int main(void)
{




  SPI2_Init();  //SPI2初始化
        NRF24L01_Init(35,TX);//2401选择35通道,发送模式
        NRF24L01_Check();//检测2401是否正常
        PrintString("rn HEELO QST! rn");












        }






总结

以上就是今天要讲的内容,本文仅仅简单介绍了基于STM32F103C8T6在Keil下编程实现NRF24L01实现无线传输的原理图和代码。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分