STM32
直播中

刘强

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

怎样通过STM32F103C8T6模拟SPI去实现NRF24L01通信模块呢

怎样通过STM32F103C8T6模拟SPI去实现NRF24L01通信模块呢?

回帖(2)

施敏

2021-12-16 14:04:00
MCU:stm32f103c8t6
通信模块:nrf24l01
连线:CE->PA2,SCK->PA3,MISO->PA4,CSN->PA5,MOSI->PA6,IRQ->PA7
模拟SPI,可以更改任意引脚。把源文件或者头文件,全选替换。

遇到的问题,硬件连线。连接错误,发送接收不正常。debug 到 错误点,检查错误点相应的硬件及软件。

源文件:NRF24L01.c

#include "NRF24L01.h"

const u8 TX_ADDRESS[TX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //发送地址
const u8 RX_ADDRESS[RX_ADR_WIDTH]={0x34,0x43,0x10,0x10,0x01}; //接收地址                                            


void Delay(vu32 nCount)
{
  for(; nCount != 0; nCount--);
}

//初始化NRF24L01IO口

//CE->PA2,CSN->PA5,SCK->PA3,MOSI->PA6,MISO->PA4,IRQ->PA7
void Init_NRF24L01(void)
{
        //CE->PD2,CSN->PD5,SCK->PD3,MOSI->PD6
        GPIO_InitTypeDef GPIO_InitStructure;                       
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);        //使能GPIO 的时钟
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_5|GPIO_Pin_3|GPIO_Pin_6;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;            //推挽输出
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);

        CE_H;           //初始化时先拉高
  CSN_H;                                        //初始化时先拉高
       
        //MISO->PD4,IRQ->PD7
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_7;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;     //上拉输入
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
               
        IRQ_H;                                                                                 //IRQ置高
        CE_L;                           //使能NRF24L01
        CSN_H;                    //SPI片选取消
}

//模拟SPI读写数据函数
u8 SPI_ReadWriteByte(u8 TxData)                                       
{               
        u16 bit_ctr;
           for(bit_ctr=0;bit_ctr<8;bit_ctr++)
           {
                if(TxData & 0x80)
                MOSI_H;         
                else
                MOSI_L;
                TxData = (TxData << 1);           
                SCK_H;
                Delay(0xff);
                if(READ_MISO)                     
                TxData |= 0x01;                         
                SCK_L;
                Delay(0xff);                            
           }
    return(TxData);                                                 
}

//上电检测NRF24L01是否在位
//写5个数据然后再读回来进行比较,
//相同时返回值0,表示在位;
//否则返回1,表示不在位.       
u8 NRF24L01_Check(void)
{
        u8 buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};
        u8 buf1[5];
        u8 i;            
        NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址.       
        NRF24L01_Read_Buf(TX_ADDR,buf1,5);              //读出写入的地址         
        for(i=0;i<5;i++)
                if(buf1!=0XA5) break;                                          
        if(i!=5) return 1;                               //NRF24L01不在位
               
        return 0;                                                //NRF24L01在位
}                  
//通过SPI写寄存器
u8 NRF24L01_Write_Reg(u8 reg_addr,u8 data)
{
        u8 status;       
    CSN_L;                    //使能SPI传输
          status =SPI_ReadWriteByte(reg_addr); //发送寄存器号
          SPI_ReadWriteByte(data);            //写入寄存器的值
          CSN_H;                    //禁止SPI传输          
          return(status);                                //返回状态值
}
//读取SPI寄存器值 ,regaddr:要读的寄存器
u8 NRF24L01_Read_Reg(u8 reg_addr)
{
        u8 reg_val;            
        CSN_L;                //使能SPI传输               
          SPI_ReadWriteByte(reg_addr);     //发送寄存器号
          reg_val=SPI_ReadWriteByte(0);//读取寄存器内容

          CSN_H;                //禁止SPI传输                    
          return(reg_val);                 //返回状态值
}       
//在指定位置读出指定长度的数据
//*pBuf:数据指针
//返回值,此次读到的状态寄存器值
u8 NRF24L01_Read_Buf(u8 reg_addr,u8 *pBuf,u8 data_len)
{
        u8 status,i;               
          CSN_L;                     //使能SPI传输
          status=SPI_ReadWriteByte(reg_addr);   //发送寄存器值(位置),并读取状态值             
        for(i=0;i                 pBuf=SPI_ReadWriteByte(0);//读出数据

          CSN_H;                     //关闭SPI传输
          return status;                        //返回读到的状态值
}
//在指定位置写指定长度的数据
//*pBuf:数据指针
//返回值,此次读到的状态寄存器值
u8 NRF24L01_Write_Buf(u8 reg_addr, u8 *pBuf, u8 data_len)
{
        u8 status,i;            
        CSN_L;                                    //使能SPI传输
          status = SPI_ReadWriteByte(reg_addr);                //发送寄存器值(位置),并读取状态值
          for(i=0; i                 SPI_ReadWriteByte(*pBuf++); //写入数据         
          CSN_H;                                    //关闭SPI传输
          return status;                                       //返回读到的状态值
}                                  
//启动NRF24L01发送一次数据
//txbuf:待发送数据首地址
//返回值:发送完成状况
u8 NRF24L01_TxPacket(u8 *tx_buf)
{
        u8 state;   
        CE_L;
          NRF24L01_Write_Buf(WR_TX_PLOAD,tx_buf,TX_PLOAD_WIDTH);//写数据到TX BUF  32个字节
        CE_H;                                     //启动发送          
        while(READ_IRQ != 0);                         //等待发送完成
        state=NRF24L01_Read_Reg(STATUS);                     //读取状态寄存器的值          
        NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,state);      //清除TX_DS或MAX_RT中断标志
        if(state&MAX_TX)                                     //达到最大重发次数
        {
                NRF24L01_Write_Reg(FLUSH_TX,0xff);               //清除TX FIFO寄存器
                return MAX_TX;
        }
        if(state&TX_OK)                                      //发送完成
        {
                return TX_OK;
        }
        return 0xff;                                         //其他原因发送失败
}

//启动NRF24L01发送一次数据
//txbuf:待发送数据首地址
//返回值:0,接收完成;其他,错误代码
u8 NRF24L01_RxPacket(u8 *rx_buf)
{
        u8 state;                                                                                 
        state=NRF24L01_Read_Reg(STATUS);                //读取状态寄存器的值             
        NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志
        if(state&RX_OK)                                 //接收到数据
        {
                NRF24L01_Read_Buf(RD_RX_PLOAD,rx_buf,RX_PLOAD_WIDTH);//读取数据
                NRF24L01_Write_Reg(FLUSH_RX,0xff);          //清除RX FIFO寄存器
                return 0;
        }          
        return 1;                                      //没收到任何数据
}

//该函数初始化NRF24L01到RX模式
//设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
//当CE变高后,即进入RX模式,并可以接收数据了                  
void RX_Mode(void)
{
        CE_L;          
    //写RX节点地址
          NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);

    //使能通道0的自动应答   
          NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01);   
    //使能通道0的接收地址           
          NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01);
    //设置RF通信频率                  
          NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,40);             
    //选择通道0的有效数据宽度             
          NRF24L01_Write_Reg(SPI_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);
    //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   
          NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x0f);
    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,PRIM_RX接收模式
          NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG, 0x0f);
    //CE为高,进入接收模式
          CE_H;                                
}                       

//该函数初始化NRF24L01到TX模式
//设置TX地址,写TX数据宽度,设置RX自动应答的地址,填充TX发送数据,
//选择RF频道,波特率和LNA HCURR PWR_UP,CRC使能
//当CE变高后,即进入RX模式,并可以接收数据了                  
//CE为高大于10us,则启动发送.         
void TX_Mode(void)
{                                                                                                                 
        CE_L;            
    //写TX节点地址
          NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR,(u8*)TX_ADDRESS,TX_ADR_WIDTH);   
    //设置TX节点地址,主要为了使能ACK          
          NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(u8*)RX_ADDRESS,RX_ADR_WIDTH);

    //使能通道0的自动应答   
          NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01);     
    //使能通道0的接收地址  
          NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01);
    //设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
          NRF24L01_Write_Reg(SPI_WRITE_REG+SETUP_RETR,0x1a);
    //设置RF通道为40
          NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,40);      
    //设置TX发射参数,0db增益,2Mbps,低噪声增益开启   
          NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x0f);  
    //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,PRIM_RX发送模式,开启所有中断
          NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG,0x0e);   
    // CE为高,10us后启动发送
        CE_H;                                 
}                  

                  
举报

陈静

2021-12-16 14:04:04
头文件:NRF24L01.h

#ifndef _NRF24L01_H
#define _NRF24L01_H

#include "stm32f10x.h"

/****************************************************************************************************/
//NRF24L01寄存器操作命令
#define SPI_READ_REG    0x00  //读配置寄存器,低5位为寄存器地址
#define SPI_WRITE_REG   0x20  //写配置寄存器,低5位为寄存器地址
#define RD_RX_PLOAD     0x61  //读RX有效数据,1~32字节
#define WR_TX_PLOAD     0xA0  //写TX有效数据,1~32字节
#define FLUSH_TX        0xE1  //清除TX FIFO寄存器.发射模式下用
#define FLUSH_RX        0xE2  //清除RX FIFO寄存器.接收模式下用
#define REUSE_TX_PL     0xE3  //重新使用上一包数据,CE为高,数据包被不断发送.
#define NOP             0xFF  //空操作,可以用来读状态寄存器       

//SPI(NRF24L01)寄存器地址
#define CONFIG          0x00  //配置寄存器地址;bit0:1接收模式,0发射模式;bit1:电选择;bit2:CRC模式;bit3:CRC使能;
                              //bit4:中断MAX_RT(达到最大重发次数中断)使能;bit5:中断TX_DS使能;bit6:中断RX_DR使能
#define EN_AA           0x01  //使能自动应答功能  bit0~5,对应通道0~5
#define EN_RXADDR       0x02  //接收地址允许,bit0~5,对应通道0~5
#define SETUP_AW        0x03  //设置地址宽度(所有数据通道):bit1,0:00,3字节;01,4字节;02,5字节;
#define SETUP_RETR      0x04  //建立自动重发;bit3:0,自动重发计数器;bit7:4,自动重发延时 250*x+86us
#define RF_CH           0x05  //RF通道,bit6:0,工作通道频率;
#define RF_SETUP        0x06  //RF寄存器;bit3:传输速率(0:1Mbps,1:2Mbps);bit2:1,发射功率;bit0:低噪声放大器增益
#define STATUS          0x07  //状态寄存器;bit0:TX FIFO满标志;bit3:1,接收数据通道号(最大:6);bit4,达到最多次重发
                              //bit5:数据发送完成中断;bit6:接收数据中断;
#define MAX_TX              0x10  //达到最大发送次数中断
#define TX_OK               0x20  //TX发送完成中断
#define RX_OK               0x40  //接收到数据中断

#define OBSERVE_TX      0x08  //发送检测寄存器,bit7:4,数据包丢失计数器;bit3:0,重发计数器
#define CD              0x09  //载波检测寄存器,bit0,载波检测;
#define RX_ADDR_P0      0x0A  //数据通道0接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P1      0x0B  //数据通道1接收地址,最大长度5个字节,低字节在前
#define RX_ADDR_P2      0x0C  //数据通道2接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P3      0x0D  //数据通道3接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P4      0x0E  //数据通道4接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define RX_ADDR_P5      0x0F  //数据通道5接收地址,最低字节可设置,高字节,必须同RX_ADDR_P1[39:8]相等;
#define TX_ADDR         0x10  //发送地址(低字节在前),ShockBurstTM模式下,RX_ADDR_P0与此地址相等
#define RX_PW_P0        0x11  //接收数据通道0有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P1        0x12  //接收数据通道1有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P2        0x13  //接收数据通道2有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P3        0x14  //接收数据通道3有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P4        0x15  //接收数据通道4有效数据宽度(1~32字节),设置为0则非法
#define RX_PW_P5        0x16  //接收数据通道5有效数据宽度(1~32字节),设置为0则非法
#define FIFO_STATUS     0x17  //FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留
                              //bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环;
/**********************************************************************************************************/

//NRF2401片选信号
#define                 CE_L      GPIO_ResetBits(GPIOA, GPIO_Pin_2)
#define                 CE_H      GPIO_SetBits(GPIOA, GPIO_Pin_2)

//SPI时钟
#define                 SCK_L        GPIO_ResetBits(GPIOA , GPIO_Pin_3)
#define                 SCK_H        GPIO_SetBits(GPIOA , GPIO_Pin_3)

//SPI输入
#define                 READ_MISO        GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_4)

//SPI片选信号       
#define                 CSN_L     GPIO_ResetBits(GPIOA, GPIO_Pin_5)
#define                 CSN_H     GPIO_SetBits(GPIOA, GPIO_Pin_5)

//SPI输出
#define                 MOSI_L        GPIO_ResetBits(GPIOA , GPIO_Pin_6)
#define                 MOSI_H        GPIO_SetBits(GPIOA , GPIO_Pin_6)
   
//IRQ中断脚
#define                 IRQ_L             GPIO_ResetBits(GPIOA,GPIO_Pin_7)
#define                 IRQ_H             GPIO_SetBits(GPIOA,GPIO_Pin_7)

#define                 READ_IRQ    GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7)


//NRF24L01发送接收数据宽度定义
#define TX_ADR_WIDTH    5                   //5字节的地址宽度
#define RX_ADR_WIDTH    5                   //5字节的地址宽度
#define TX_PLOAD_WIDTH  32                  //20字节的用户数据宽度
#define RX_PLOAD_WIDTH  32                  //20字节的用户数据宽度
                                                                                     

void Init_NRF24L01(void);                    //NRF24l01初始化
u8 SPI_ReadWriteByte(u8 TxData) ;                                                 //模拟SPI通讯函数
void RX_Mode(void);                          //配置为接收模式
void TX_Mode(void);                          //配置为发送模式
u8 NRF24L01_Write_Buf(u8 regaddr, u8 *pBuf, u8 datalen); //写数据区
u8 NRF24L01_Read_Buf(u8 regaddr, u8 *pBuf, u8 datalen);  //读数据区                  
u8 NRF24L01_Read_Reg(u8 regaddr);                                 //读寄存器
u8 NRF24L01_Write_Reg(u8 regaddr, u8 data);              //写寄存器
u8 NRF24L01_Check(void);                                 //检查NRF24L01是否在位
u8 NRF24L01_TxPacket(u8 *txbuf);                         //发送一个包的数据
u8 NRF24L01_RxPacket(u8 *rxbuf);                         //接收一个包的数据

void Delay(vu32 nCount);
#endif
主函数:main.c

/********************        (C) COPYRIGHT 2017        **************************
* 文件名  :main.c
* 描述    :NRF24L01+ 的 接收 测试程序  
* 实验平台:STM32F103VET6
* 库版本  :ST3.5.0
* 编写日期:2017-04-17
* 修改日期:2017-04-17
* 作者    :
* 2017-04-17
* (1)NRF24L01+  接收数据 测试正常!
**********************************************************************************/

#include "usart1.h"
#include "led.h"
#include "tim2.h"
#include "NRF24L01.h"
#include "SysTick.h"

/*
* 函数名:main
* 描述  : "主机"的主函数
* 输入  :无
* 输出  : 无
*/

int main(void)
{         
        uint8_t tmp_buf[6];

        SysTick_Init();
        USART1_Config(9600);                                                /* 初始化USART1 */
        LED_GPIO_Config();                                                          /* 运行LED初始化 */
        TIM2_Config();                                                                                 /* 定时器TIM2初始化 */       
        Init_NRF24L01();
        printf("STM32F103VET6 NRF24L01 RECV Test!rn");

        if(NRF24L01_Check())
        {
                printf("NRF24L01 is Not work!rn");
        }
        printf("NRF24L01 is working!rn");
       
        RX_Mode();                        //只收不发

        while(1)
        {
                if(NRF24L01_RxPacket(tmp_buf)==0)                //接收到数据
                {
                        printf("rn RECV Data is:%srn",tmp_buf);
                        LED1(ON);
                        Delay_ms(100);
                }
                LED1(OFF);
        }
}
/******************* (C) COPYRIGHT 2017 *****END OF FILE************/
举报

更多回帖

×
20
完善资料,
赚取积分