STM32
直播中

李娜

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

如何去使用STM32中nRF24L01这个无线2.4G收发芯片呢

nRF24L01是什么?
如何去使用STM32中nRF24L01这个无线2.4G收发芯片呢?

回帖(1)

李纪生

2021-11-1 09:55:51
  最近在一个项目中用到了nRF24L01这个无线2.4G收发芯片,项目中有主机和分机,默认都是使用数据通道0,主机通过nRF24L01发送数据后,对应地址的分机在收到数据后会返回一个确认数据包给主机(注意:这个确认数据包并不是nRF24L01自动应答时的数据包,而是自定义的一个数据包,说明了就是双方都能进行收发),在经过长时间的摸索之后,终于将接收和发送都调通了,基本的SPI驱动我使用的是正点原子的教程,我是使用的中断法来处理相应的收发工作。
  我的软件硬件环境大致如下:
  服务端:STM32F103VET6+u/COS-III
  从机端:STM32F103RBT6+u/COS-III
  库函数是使用的3.5版本的。
  在这里我只是列出主机部分的代码,从机上的都是差不多的。
  下面是nRF24L01的驱动部分
  头文件部分(24l01.h):
  #ifndef __24L01_H
  #define __24L01_H
  #include “config.h”
  #if nRF24L01_EN 》 0u
  #include “sys_temp.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 //空操作,可以用来读状态寄存器
  #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,不循环;
  /*---------------------------------------------------------------------------------------------
  24L01的用到的单片机引脚
  -----------------------------------------------------------------------------------------------*/
  #define NRF24L01_SPI_Periph_CLK RCC_APB2Periph_GPIOB //无线2.4G模块用到的引脚的外设时钟源
  #define NRF24L01_SPI_GPIO_SRC GPIOB
  #define NRF24L01_SPI_SCK_PIN GPIO_Pin_13 //SCK时钟
  #define NRF24L01_SPI_MISO_PIN GPIO_Pin_14 //主机输入,从机输出
  #define NRF24L01_SPI_MOSI_PIN GPIO_Pin_15 //主机输出,从机输入
  #define NRF24L01_CE_PIN GPIO_Pin_10 //24L01芯片使能信号
  #define NRF24L01_IRQ_PIN GPIO_Pin_11 //IRQ主机数据输入
  #define NRF24L01_CSN_PIN GPIO_Pin_12 //SPI片选
  /*---------------------------------------------------------------------------------------------
  24L01芯片使能信号和片选信号操作
  -----------------------------------------------------------------------------------------------*/
  #define NRF24L01_CE PBout(10) //24L01芯片使能信号
  #define NRF24L01_CSN PBout(12) //SPI片选信号
  #define NRF24L01_IRQ PBin(11) //IRQ主机数据输入
  /*---------------------------------------------------------------------------------------------
  24L01中断线配置,当接收到数据,发送数据,达到最大重发次数时都会触发中断
  相应的状态寄存器会置位1,注意需要手动清除中断,写1清零
  -----------------------------------------------------------------------------------------------*/
  #define NRF24L01_INT_SOURCE_PORT GPIO_PortSourceGPIOB //中断引脚组
  #define NRF24L01_INT_IRQ EXTI15_10_IRQn //中断号
  #define NRF24L01_STATUS_INT_SOURCE GPIO_PinSource11 //中断源
  #define NRF24L01_STATUS_LINE EXTI_Line11 //中断线
  #define NRF24L01_STATUS_INT_MODE EXTI_Trigger_Falling //中断触发方式
  /*---------------------------------------------------------------------------------------------
  24L01发送接收数据宽度定义
  -----------------------------------------------------------------------------------------------*/
  #define TX_ADR_WIDTH 5 //5字节的地址宽度
  #define RX_ADR_WIDTH 5 //5字节的地址宽度
  #define TX_PLOAD_WIDTH 32 //32字节的用户数据宽度
  #define RX_PLOAD_WIDTH 32 //32字节的用户数据宽度
  /*---------------------------------------------------------------------------------------------
  24L01相关操作函数定义
  -----------------------------------------------------------------------------------------------*/
  void NRF24L01_Init(void);//初始化
  void RX_Mode(void);//配置为接收模式
  //void TX_Mode(void);//配置为发送模式(原始定义)
  void TX_Mode(u8 * addr);//配置为发送模式
  static u8 SPIx_ReadWriteByte(u8 TxData);
  u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 u8s);//写数据区
  u8 NRF24L01_Read_Buf(u8 reg, u8 *pBuf, u8 u8s);//读数据区
  u8 NRF24L01_Read_Reg(u8 reg); //读寄存器
  u8 NRF24L01_Write_Reg(u8 reg, u8 value);//写寄存器
  u8 NRF24L01_Check(void);//检查24L01是否存在
  u8 NRF24L01_TxPacket(u8 *txbuf);//发送一个包的数据
  //u8 NRF24L01_RxPacket(u8 *rxbuf);//接收一个包的数据(原始定义)
  u8 NRF24L01_RxPacket(u8 *rxbuf, u8 *chl);//接收一个包的数据
  #endif /* nRF24L01_EN */
  #endif /* __24L01_H */
  源文件部分(24l01.c):
  /******************** (C) COPYRIGHT 2015 ASTO ***************************
  @* 文件名 :24l01.c
  @* 描述 :nRF24L01驱动程序
  @* 开发平台:STM32F103VET6主控制MCU
  @* 硬件连接:SPI2
  @* 库版本 :ST3.5.0
  @* 作者 :
  @* 公司网址:《a target=_blank href=“http://www.test.com”》www.test.com《/a》
  @* 总部网址:《a target=_blank href=“http://www.test.com”》www.test.com《/a》
  **********************************************************************************/
  #include “config.h”
  #if nRF24L01_EN 》 0u
  #include “sys_temp.h”
  #include “24l01.h”
  #include “delay.h”
  const u8 RX0_Address[RX_ADR_WIDTH]={0x01,0x01,0x01,0x01,0x01}; //接收方通道0地址
  /*
  const u8 RX1_Address[RX_ADR_WIDTH]={0x02,0x20,0x20,0x20,0x20}; //接收方通道1地址
  const u8 RX2_Address[1] = {0x03}; //接收方通道2地址
  const u8 RX3_Address[1] = {0x04}; //接收方通道3地址
  const u8 RX4_Address[1] = {0x05}; //接收方通道4地址
  const u8 RX5_Address[1] = {0x06}; //接收方通道5地址
  */
  /**
  @函数名称:EXTI_Configuration()
  @函数功能:nRF24L01的IRQ中断配置(只响应接收数据的中断)
  @输入:无
  @输出:无
  @调用:内部调用
  **/
  static void EXTI_Configuration(void)
  {
  EXTI_InitTypeDef EXTI_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  //中断优先级设置
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  NVIC_InitStructure.NVIC_IRQChannel = NRF24L01_INT_IRQ; //10-15的中断线共享一个中断处理程序
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优行级
  NVIC_Init(&NVIC_InitStructure);
  //中断线配置EXTI_Line11--》PB11
  GPIO_EXTILineConfig(NRF24L01_INT_SOURCE_PORT, NRF24L01_STATUS_INT_SOURCE); //设置中断源引脚
  EXTI_InitStructure.EXTI_Line = NRF24L01_STATUS_LINE; //中断线设置
  EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中断线使能
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断方式
  EXTI_InitStructure.EXTI_Trigger = NRF24L01_STATUS_INT_MODE;//模式,这里设置为下降沿触发
  EXTI_Init(&EXTI_InitStructure);
  }
  /**
  @* 函数名:NRF24L01_SPI_Init()
  @* 描述 :初始化SPI2端口及基模式,用于操作nRF24L01无线2.4G模块
  @* 输入 :无
  @* 输出 : 无
  @* 调用 :内部调用
  */
  static void NRF24L01_SPI_Init(void) {
  GPIO_InitTypeDef GPIO_InitStructure;
  SPI_InitTypeDef SPI_InitStructure;
  //注意将普通IO作为中断线时必须开启AFIO时钟
  RCC_APB2PeriphClockCmd(NRF24L01_SPI_Periph_CLK|RCC_APB2Periph_AFIO, ENABLE);//开启GPIOB的时钟
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);//开启SPI2的时钟
  /*配置 SPI的SCK, MISO, MOSI引脚,GPIOB^13,GPIOB^14,GPIOB^15 */
  GPIO_InitStructure.GPIO_Pin = NRF24L01_SPI_SCK_PIN| NRF24L01_SPI_MISO_PIN | NRF24L01_SPI_MOSI_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用输出功能
  GPIO_Init(NRF24L01_SPI_GPIO_SRC, &GPIO_InitStructure);
  /*GPIOB^10为nRF24L01的CE引脚,CSN引脚GPIOB^12 */
  GPIO_InitStructure.GPIO_Pin = NRF24L01_CE_PIN|NRF24L01_CSN_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//通用推挽输出
  GPIO_Init(NRF24L01_SPI_GPIO_SRC, &GPIO_InitStructure);
  /* GIOB^11为nRF24L01的IRQ中断输入引脚 */
  GPIO_InitStructure.GPIO_Pin = NRF24L01_IRQ_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮动输入
  GPIO_Init(NRF24L01_SPI_GPIO_SRC, &GPIO_InitStructure);
  SPI_Cmd(SPI2,DISABLE);/*先失能,然后再使能*/
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex ; /* 全双工 */
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master ; /*当前的设备为主机模式*/
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low ; /* 时钟极性为低,即SPI空闲时,SCK为低电平 */
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge ; /* 时钟相位,第一个时钟沿(也就是奇数边沿)捕捉数据 */
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; /* 数据宽度 */
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB ; /* 低地址存放最高有效字节 */
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; /*配置片选为软件控制方式*/
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; /* 时钟分频为8分频,即72MHz/8=9MHz */
  SPI_InitStructure.SPI_CRCPolynomial = 7; /* 校验多项式,这个不起作用 */
  SPI_Init(SPI2, &SPI_InitStructure);
  //使能SPI2
  SPI_Cmd(SPI2,ENABLE);
  EXTI_Configuration();//配置中断线
  }
  /**
  @* 函数名:SPIx_ReadWriteByte()
  @* 描述 :SPI2 读写一个字节
  @* 输入 :TxData:要写入的字节
  @* 输出 : 读取到的字节
  @* 调用 :内部调用
  */
  static u8 SPIx_ReadWriteByte(u8 TxData) {
  /** 原来的实现方式
  u8 retry=0;
  while((SPI2-》SR&1《《1)==0) {//等待发送区空
  retry++;
  if(retry》200)return 0;
  }
  SPI2-》DR=TxData; //发送一个byte
  retry=0;
  while((SPI2-》SR&1《《0)==0) { //等待接收完一个byte
  retry++;
  if(retry》200)return 0;
  }
  return SPI2-》DR; //返回收到的数据 */
  /* Loop while DR register in not empty */
  while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
  /* Send byte through the SPI2 peripheral */
  SPI_I2S_SendData(SPI2, TxData);
  /* Wait to receive a byte */
  while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
  /* Return the byte read from the SPI bus */
  return SPI_I2S_ReceiveData(SPI2);
  }
  /**
  @* 函数名:NRF24L01_Init()
  @* 描述 :初始化24L01的相关IO口
  @* 输入 :无
  @* 输出 : 无
  @* 调用 :外部板级支持包调用
  */
  void NRF24L01_Init(void) {
  NRF24L01_SPI_Init(); //初始化SPI2
  NRF24L01_CE=0; //使能24L01
  NRF24L01_CSN=1; //SPI片选取消
  }
  /**
  @* 函数名:NRF24L01_Check()
  @* 描述 :检测24L01是否存在
  @* 输入 :无
  @* 输出 : 0:成功,1:失败
  @* 调用 :外部板级支持包调用
  */
  u8 NRF24L01_Check(void) {
  u8 buf[5]={0x21,0x21,0x21,0x21,0x21};
  u8 buf1[5];
  u8 i;
  //SPIx_SetSpeed(SPI_SPEED_8); //spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)
  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[i]!=0x21)break; //判断读出的数据和写入的数据是否完全一致
  if(i!=5)return 1;//检测24L01错误
  return 0; //执行到这里,表示成功检测到24L01
  }
  /**
  @* 函数名:NRF24L01_Write_Reg()
  @* 描述 :SPI写寄存器
  @* 输入 :reg: 指定的寄存器地址
  @* value: 要写入的值
  @* 输出 : 返回寄存器的状态值
  @* 调用 :内部调用
  */
  u8 NRF24L01_Write_Reg(u8 reg,u8 value) {
  u8 status;
  NRF24L01_CSN=0; //使能SPI传输
  status =SPIx_ReadWriteByte(reg); //发送寄存器号
  SPIx_ReadWriteByte(value); //写入寄存器的值
  NRF24L01_CSN=1; //禁止SPI传输
  return(status); //返回状态值
  }
  /**
  @* 函数名:NRF24L01_Read_Reg()
  @* 描述 :读取SPI寄存器值
  @* 输入 :reg: 指定的寄存器地址
  @* 输出 : 返回寄存器的状态值
  @* 调用 :内部调用
  */
  u8 NRF24L01_Read_Reg(u8 reg) {
  u8 reg_val;
  NRF24L01_CSN = 0; //使能SPI传输
  SPIx_ReadWriteByte(reg); //发送寄存器号
  reg_val=SPIx_ReadWriteByte(0XFF); //读取寄存器内容
  NRF24L01_CSN = 1; //禁止SPI传输
  return(reg_val); //返回状态值
  }
  /**
  @* 函数名:NRF24L01_Read_Buf()
  @* 描述 :在指定位置读出指定长度的数据
  @* 输入 :reg:指定的寄存器位置
  @* pBuf:数据指针用来存储读到的数据,一般是数组
  @* len:数据长度
  @* 输出 : 此次读到的状态寄存器值
  @* 调用 :内部调用
  */
  u8 NRF24L01_Read_Buf(u8 reg,u8 *pBuf,u8 len) {
  u8 status,u8_ctr;
  NRF24L01_CSN = 0; //使能SPI传输
  status=SPIx_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值
  for(u8_ctr=0;u8_ctr《len;u8_ctr++)pBuf[u8_ctr]=SPIx_ReadWriteByte(0xFF);//读出数据
  NRF24L01_CSN=1; //关闭SPI传输
  return status; //返回读到的状态值
  }
  /**
  @* 函数名:NRF24L01_Write_Buf()
  @* 描述 :在指定位置写指定长度的数据
  @* 输入 :reg:寄存器(位置)
  @* pBuf:数据指针
  @* len:数据长度
  @* 输出 : 此次读到的状态寄存器值
  @* 调用 :内部调用
  */
  u8 NRF24L01_Write_Buf(u8 reg, u8 *pBuf, u8 len) {
  u8 status,u8_ctr;
  NRF24L01_CSN = 0; //使能SPI传输
  status = SPIx_ReadWriteByte(reg);//发送寄存器值(位置),并读取状态值
  for(u8_ctr=0; u8_ctr《len; u8_ctr++)SPIx_ReadWriteByte(*pBuf++); //写入数据
  NRF24L01_CSN = 1; //关闭SPI传输
  return status; //返回读到的状态值
  }
  /**
  @* 函数名:NRF24L01_TxPacket()
  @* 描述 :启动NRF24L01发送一次数据
  @* 输入 :txbuf:待发送数据首地址
  @* 输出 : 发送完成状况
  @* 调用 :外部板级支持包调用
  */
  u8 NRF24L01_TxPacket(u8 *txbuf) {
  //u8 sta;
  //SPIx_SetSpeed(SPI_SPEED_8);//spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)
  NRF24L01_CE=0;
  NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF 最长32个字节
  NRF24L01_CE=1;//启动发送,10微秒后启动发送
  return 0xff;//其他原因发送失败
  }
  /**
  @* 函数名:NRF24L01_RxPacket()
  @* 描述 :启动NRF24L01接收数据
  @* 输入 :rxbuf:用来接收数据的buffer
  @* chl:接收到数据的通道号
  @* 输出 : 0,接收完成;其他,错误代码
  @* 调用 :外部板级支持包调用
  */
  u8 NRF24L01_RxPacket(u8 *rxbuf, u8 *chl) {
  u8 sta;
  //SPIx_SetSpeed(SPI_SPEED_8); //spi速度为9Mhz(24L01的最大SPI时钟为10Mhz)
  sta=NRF24L01_Read_Reg(STATUS); //读取状态寄存器的值
  //状态寄存器的bit1-bit3是表示接收到数据的通道号,最大值为6
  *chl=(0x0e&sta)》》1;
  //清除TX_DS或MAX_RT中断标志,注意这里是写1清零,只有清零后设备才能正常通讯
  NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,sta);
  if(sta&RX_OK) { //接收到数据
  NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据
  NRF24L01_Write_Reg(FLUSH_RX, 0xff);//清除RX FIFO寄存器
  return 0;
  }
  return 1;//没收到任何数据
  }
  /**
  @* 函数名:RX_Mode()
  @* 描述 :该函数初始化NRF24L01到RX模式
  @* 设置RX地址,写RX数据宽度,选择RF频道,波特率和LNA HCURR
  @* 当CE变高后,即进入RX模式,并可以接收数据了
  @* 输入 :无
  @* 输出 : 无
  @* 调用 :外部板级支持包调用
  */
  void RX_Mode(void) {
  NRF24L01_CE=0; //CE为0进入待机模式
  //配置通道的接收
  NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(u8*)RX0_Address,RX_ADR_WIDTH);//写RX节点地址(通道0),也就是表示用哪个通道接收数据
  //======================注意以下被注释掉的内容可作参考和=========================================
  //NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P1,(u8*)RX1_Address,RX_ADR_WIDTH);//写RX节点地址(通道1),也就是表示用哪个通道接收数据
  //NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P2,(u8*)RX2_Address,1);//写RX节点地址(通道2),也就是表示用哪个通道接收数据
  //NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P3,(u8*)RX3_Address,1);//写RX节点地址(通道3),也就是表示用哪个通道接收数据
  //NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P4,(u8*)RX4_Address,1);//写RX节点地址(通道4),也就是表示用哪个通道接收数据
  //NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P5,(u8*)RX5_Address,1);//写RX节点地址(通道5),也就是表示用哪个通道接收数据
  NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA, 0x01); //使能通道0的自动应答
  NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址,共有6个通道,高两位保留固定为00
  NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,0); //设置RF通信频率
  NRF24L01_Write_Reg(SPI_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度
  NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x07);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启
  NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG, 0x0f);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式
  NRF24L01_CE = 1; //CE为高,进入接收模式
  }
  /**
  @* 函数名:TX_Mode()
  @* 描述 :该函数初始化NRF24L01到TX模式
  @* 设置TX地址,写TX数据宽度,设置RX自动应答的地址,
  @* 填充TX发送数据,选择RF频道,波特率和LNA HCURR,PWR_UP,CRC使能
  @* 当CE变高后,即进入RX模式,并可以接收数据了
  @* CE为高大于10us,则启动发送
  @* 输入 :addr,要发送数据的目标地址
  @* 输出 : 无
  @* 调用 :外部板级支持包调用
  */
  void TX_Mode(u8 * addr) {
  NRF24L01_CE=0; //CE为0进入待机模式
  NRF24L01_Write_Buf(SPI_WRITE_REG+TX_ADDR, addr, TX_ADR_WIDTH);//写TX节点地址,也就是接收方的地址(目标地址)
  //在发送端,数据通道0被用作接收应答信号,因此数据通道0的接收地址必须要与发送端的地址相同以确保收到正确的应答信号
  NRF24L01_Write_Buf(SPI_WRITE_REG+RX_ADDR_P0,(u8*)addr, RX_ADR_WIDTH);
  //先单独测试发送方,看发送是否正常
  NRF24L01_Write_Reg(SPI_WRITE_REG+EN_AA,0x01); //使能通道0的自动应答
  NRF24L01_Write_Reg(SPI_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址
  NRF24L01_Write_Reg(SPI_WRITE_REG+SETUP_RETR, 0x1a); //使能自动重发,设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次
  NRF24L01_Write_Reg(SPI_WRITE_REG+RF_CH,0); //设置RF通道为40
  NRF24L01_Write_Reg(SPI_WRITE_REG+RF_SETUP,0x07); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启
  NRF24L01_Write_Reg(SPI_WRITE_REG+CONFIG,0x0e); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断
  NRF24L01_CE=1;//CE为高,10us后启动发送
  }
  #endif
  /******************************************END OF FILE*************************************************/
  中断处理函数(stm32f10x_it.c)
  #if nRF24L01_EN 》 0u
  #include “24l01.h”
  /**
  @*功能简介:I/O线中断处理函数,处理nRF24L01的数据接收中断,IO口是PB11
  @*参数:None
  @*返回值:None
  */
  void EXTI15_10_IRQHandler(void)
  {
  OSIntEnter(); //用于统计中断的嵌套层数,对嵌套层数+1,请注意:这适用于有内核参与的中断
  if (EXTI_GetITStatus(NRF24L01_STATUS_LINE)!=RESET) {
  EXTI_ClearITPendingBit(NRF24L01_STATUS_LINE);//清除中断标志位
  if (!GPIO_ReadInputDataBit(NRF24L01_SPI_GPIO_SRC, NRF24L01_IRQ_PIN)) {//如果为低电平表示产生中断
  u8 sta; u8 sta2;
  sta=NRF24L01_Read_Reg(STATUS); //读取nRF24L01状态寄存器的值
  sta2=NRF24L01_Read_Reg(FIFO_STATUS);//读取FIFO状态寄存器
  //状态寄存器的bit1-bit3是表示接收到数据的通道号,最大值为6
  //*chl=(0x0e&sta)》》1;
  NRF24L01_Write_Reg(SPI_WRITE_REG+STATUS,sta); //清除TX_DS或MAX_RT中断标志,注意这里是写1清零,只有清零后设备才能正常通讯
  if(sta & RX_OK) { //此次中断表示是接收到了数据
  OS_ERR err;
  USART1_SendData((u8*)“Feedback received!”, 19);
  //直接发信号量给任务,这种方式比使用中介信号量更快,更节省资源
  //第二个参数:1. OS_OPT_POST_NONE表明在发布任务信号量之后调用任务调度程序
  // 2. OS_OPT_POST_NO_SCHED则OSTaskSemPost后不会调用调度程序
  OSTaskSemPost(&nRF24L01_TCB,
  OS_OPT_POST_NONE,
  &err);
  }else if (sta & MAX_TX) { //此次中断表示达到最大重发次数后必须手动清除TX FIFO寄存器
  USART1_SendData((u8 *)“Max retransmission reached!”, 28);
  NRF24L01_Write_Reg(FLUSH_TX,0xff);
  }
  //如果数据发送成功,则相应的寄存器值为下列值,可以调试用:
  //FIFO_STATUS: 0x11
  //STATUS: 0x2E
  else if (sta & TX_OK) { //此次中断表示发送数据成功,无需手动清除TX FIFO寄存器
  USART1_SendData((u8 *)“Send data OK!”, 14);
  RX_Mode(); //数据成功发送后直接转为接收模式
  }
  }
  }
  OSIntExit();//对嵌套层数减1,在退出中断前启动任务调度(适用于有u/COS-III内核参与的中断)
  }
  #endi
  在中断处理函数中,当收到数据的中断产生时,也就是RX_DR位置高时,我会将接收数据的工作交给某个任务去做,这里我使用的是发布信号量的方法,这样可以尽量减少中断处理的时间。这里我把RX_Mode();这句放在中断处理函数中,也就是数据发送完成后将nRF24L01转换为接收模式,也是出于无奈,因为我在函数NRF24L01_TxPacket中如果使用以下语句,就会出现问题:
  while(NRF24L01_IRQ!=0);//等待发送完成,IRQ变为0后表示发送完成或者达到最大重发次数 后来我就不用这一句,然后让主程序执行其他的语句,当产生中断后(无论怎样都会产生一种中断,要么发送成功,要么达到最大重传次数)再将nRF24L01的模式变为接收模式,不知这样做是否合理,还望看过的朋友指点一下。
  nRF24L01_TCB任务控制块的任务代码主函数(接收数据):
  #if nRF24L01_EN 》 0u
  /**
  @* 函数名:Task_nRF24L01_Sendback_Process()
  @* 描述 : 无线2.4G模块nRF24L01接收反馈数据包处理任务,优先级为4
  @* 当Main_Task或USART1_Task或Wi-Fi模块确定是要以无线2.4G方式发送数据给客户端后,会等待客户端设备的回应
  @* 以确定此次操作是否成功并同时会启动一个定时器,此任务采用等待信号量方式确认是否有客户端的回复
  @* 如果收到回复,则会根据上位机发送数据给主控制MCU的方式将回应数据包回发给上位机。
  @*
  @* 发送数据给上位机的方式有以下几种:
  @* *****************************
  @* * 1. 网络方式 *
  @* * 2. 串口方式 *
  @* * 3. Wi-Fi方式 *
  @* *****************************
  @* 输入 :p_arg: 创建任务时赋给任务的参数,该参数可以是任意类型的
  @* 输出 : 无
  */
  void Task_nRF24L01_Sendback_Process(void *p_arg) {
  OS_ERR err; //用于记录错误代号
  //u8 chl=0; //接收到数据的通道号
  CPU_TS ts;
  u8 n24l01_buf[32]; //接收2.4G数据缓冲区
  (void)p_arg; //保存创建任务控制块时传递的任务参数
  while(NRF24L01_Check()) { //如果检测不到24L01模块则打印出错信息
  printf(“No nRF24L01 device is found!rn”);
  }
  RX_Mode(); //默认为接收模式
  OSTimeDlyHMSM(0, 0, 0, 2, OS_OPT_TIME_HMSM_STRICT,&err); //至少延时130微秒后,nRF24L01模块进入接收状态
  while(DEF_ON) {
  IWDG_Feed(); //每个任务都会调用看门狗程序
  //*************************使用中断方式*************************************
  OSTaskSemPend(0,
  OS_OPT_PEND_BLOCKING,
  &ts, //信号量等待的时间
  &err);
  switch(err) { //处理Pend的结果
  case OS_ERR_NONE:
  OSTmrDel(&ClientDeviceTimeout_Tmr,&err); //正确接收到反馈数据包则删除定时器
  《strong》《span style=“color:#990000;”》NRF24L01_Read_Buf(RD_RX_PLOAD, n24l01_buf, RX_PLOAD_WIDTH); //从nRF24L01中读取接收到的数据
  NRF24L01_Write_Reg(FLUSH_RX, 0xff); //清除RX FIFO接收数据寄存器《/span》《/strong》
  switch(snd_type){ //根据上位机发送指令的方式分别处理
  #if ETHNET_EN 》 0u
  case Ethernet: //以太网方式
  if (custom_udp_send.cus_pbuf!=NULL) {
  custom_udp_send.cus_pbuf-》payload=n24l01_buf; //得到反馈数据包
  custom_udp_send.cus_pbuf-》tot_len=custom_udp_send.cus_pbuf-》len=sizeof(n24l01_buf);
  udp_sendto(custom_udp_send.cus_udp_pcb, //使用当前pbuf发送反馈数据包到客户端
  custom_udp_send.cus_pbuf,
  custom_udp_send.cus_ip_addr,
  custom_udp_send.cus_udp_port);
  pbuf_free(custom_udp_send.cus_pbuf); //释放缓冲池中的当前pbuf对象
  custom_udp_send.cus_pbuf=NULL;
  }
  break;
  #endif
  #if RS232_EN 》 0u
  case RS232: //RS232串口方式
  USART1_SendData(n24l01_buf, sizeof(n24l01_buf));
  break;
  #endif
  #if HF_LPB100_EN 》 0u
  case WiFi: //Wi-Fi方式(需要Wi-Fi模块作为服务端)
  USART3_SendData(n24l01_buf, sizeof(n24l01_buf));
  break;
  #endif
  default:
  break;
  }
  break;
  case OS_ERR_PEND_ABORT: //等待(挂起)状态被其他任务打断
  break;
  case OS_ERR_OBJ_DEL: //内核对象已被删除
  break;
  default:
  break;
  }
  // //*************************原来的方式,使用查询法****************************
  //
  // if(NRF24L01_RxPacket(n24l01_buf, &chl)==0) { //如果正确接收到数据
  // //nRF24L01_BUF[31]=chl; //最后一个字节表示模块接收到数据的通道号(暂时不用)
  // USART1_SendData(n24l01_buf,sizeof(n24l01_buf)); //测试接收到的数据
  // OSTmrDel(&ClientDeviceTimeout_Tmr,&err); //正确接收到反馈数据包则删除定时器
  //
  // switch(snd_type){ //根据上位机发送指令的方式分别处理
  // #if ETHNET_EN 》 0u
  // case Ethernet: //以太网方式
  // if (custom_udp_send.cus_pbuf!=NULL) {
  // custom_udp_send.cus_pbuf-》payload=n24l01_buf; //得到反馈数据包
  // custom_udp_send.cus_pbuf-》tot_len=custom_udp_send.cus_pbuf-》len=sizeof(n24l01_buf);
  // udp_sendto(custom_udp_send.cus_udp_pcb, //使用当前pbuf发送反馈数据包到客户端
  // custom_udp_send.cus_pbuf,
  // custom_udp_send.cus_ip_addr,
  // custom_udp_send.cus_udp_port);
  // pbuf_free(custom_udp_send.cus_pbuf); //释放缓冲池中的当前pbuf对象
  // custom_udp_send.cus_pbuf=NULL;
  // }
  // break;
  // #endif
  //
  // #if RS232_EN 》 0u
  // case RS232: //RS232串口方式
  // USART1_SendData(n24l01_buf, sizeof(n24l01_buf));
  // break;
  // #endif
  //
  // #if HF_LPB100_EN 》 0u
  // case WiFi: //Wi-Fi方式(需要Wi-Fi模块作为服务端)
  // USART3_SendData(n24l01_buf, sizeof(n24l01_buf));
  // break;
  // #endif
  //
  // default:
  // break;
  // }
  // }
  //
  // OSTimeDlyHMSM(0, 0, 0, 1, OS_OPT_TIME_HMSM_NON_STRICT,&err); //阻塞1毫秒,即延时1个时钟节拍
  }
  }
  #endif
  发送数据的代码就比较简单了,这里只列出部分代码:
  ##if nRF24L01_EN 》 0u
  /**
  @* 函数名:Task_nRF24L01_Sendback_Process()
  @* 描述 : 无线2.4G模块nRF24L01接收反馈数据包处理任务,优先级为4
  @* 当Main_Task或USART1_Task或Wi-Fi模块确定是要以无线2.4G方式发送数据给客户端后,会等待客户端设备的回应
  @* 以确定此次操作是否成功并同时会启动一个定时器,此任务采用等待信号量方式确认是否有客户端的回复
  @* 如果收到回复,则会根据上位机发送数据给主控制MCU的方式将回应数据包回发给上位机。
  @*
  @* 发送数据给上位机的方式有以下几种:
  @* *****************************
  @* * 1. 网络方式 *
  @* * 2. 串口方式 *
  @* * 3. Wi-Fi方式 *
  @* *****************************
  @* 输入 :p_arg: 创建任务时赋给任务的参数,该参数可以是任意类型的
  @* 输出 : 无
  */
  void Task_nRF24L01_Sendback_Process(void *p_arg) {
  OS_ERR err; //用于记录错误代号
  //u8 chl=0; //接收到数据的通道号
  CPU_TS ts;
  u8 n24l01_buf[32]; //接收2.4G数据缓冲区
  (void)p_arg; //保存创建任务控制块时传递的任务参数
  while(NRF24L01_Check()) { //如果检测不到24L01模块则打印出错信息
  printf(“No nRF24L01 device is found!rn”);
  }
  RX_Mode(); //默认为接收模式
  OSTimeDlyHMSM(0, 0, 0, 2, OS_OPT_TIME_HMSM_STRICT,&err); //至少延时130微秒后,nRF24L01模块进入接收状态
  while(DEF_ON) {
  IWDG_Feed(); //每个任务都会调用看门狗程序
  //*************************使用中断方式*************************************
  OSTaskSemPend(0,
  OS_OPT_PEND_BLOCKING,
  &ts, //信号量等待的时间
  &err);
  switch(err) { //处理Pend的结果
  case OS_ERR_NONE:
  OSTmrDel(&ClientDeviceTimeout_Tmr,&err); //正确接收到反馈数据包则删除定时器
  《strong》《span style=“color:#990000;”》NRF24L01_Read_Buf(RD_RX_PLOAD, n24l01_buf, RX_PLOAD_WIDTH); //从nRF24L01中读取接收到的数据
  NRF24L01_Write_Reg(FLUSH_RX, 0xff); //清除RX FIFO接收数据寄存器《/span》《/strong》
  switch(snd_type){ //根据上位机发送指令的方式分别处理
  #if ETHNET_EN 》 0u
  case Ethernet: //以太网方式
  if (custom_udp_send.cus_pbuf!=NULL) {
  custom_udp_send.cus_pbuf-》payload=n24l01_buf; //得到反馈数据包
  custom_udp_send.cus_pbuf-》tot_len=custom_udp_send.cus_pbuf-》len=sizeof(n24l01_buf);
  udp_sendto(custom_udp_send.cus_udp_pcb, //使用当前pbuf发送反馈数据包到客户端
  custom_udp_send.cus_pbuf,
  custom_udp_send.cus_ip_addr,
  custom_udp_send.cus_udp_port);
  pbuf_free(custom_udp_send.cus_pbuf); //释放缓冲池中的当前pbuf对象
  custom_udp_send.cus_pbuf=NULL;
  }
  break;
  #endif
  #if RS232_EN 》 0u
  case RS232: //RS232串口方式
  USART1_SendData(n24l01_buf, sizeof(n24l01_buf));
  break;
  #endif
  #if HF_LPB100_EN 》 0u
  case WiFi: //Wi-Fi方式(需要Wi-Fi模块作为服务端)
  USART3_SendData(n24l01_buf, sizeof(n24l01_buf));
  break;
  #endif
  default:
  break;
  }
  break;
  case OS_ERR_PEND_ABORT: //等待(挂起)状态被其他任务打断
  break;
  case OS_ERR_OBJ_DEL: //内核对象已被删除
  break;
  default:
  break;
  }
  // //*************************原来的方式,使用查询法****************************
  //
  // if(NRF24L01_RxPacket(n24l01_buf, &chl)==0) { //如果正确接收到数据
  // //nRF24L01_BUF[31]=chl; //最后一个字节表示模块接收到数据的通道号(暂时不用)
  // USART1_SendData(n24l01_buf,sizeof(n24l01_buf)); //测试接收到的数据
  // OSTmrDel(&ClientDeviceTimeout_Tmr,&err); //正确接收到反馈数据包则删除定时器
  //
  // switch(snd_type){ //根据上位机发送指令的方式分别处理
  // #if ETHNET_EN 》 0u
  // case Ethernet: //以太网方式
  // if (custom_udp_send.cus_pbuf!=NULL) {
  // custom_udp_send.cus_pbuf-》payload=n24l01_buf; //得到反馈数据包
  // custom_udp_send.cus_pbuf-》tot_len=custom_udp_send.cus_pbuf-》len=sizeof(n24l01_buf);
  // udp_sendto(custom_udp_send.cus_udp_pcb, //使用当前pbuf发送反馈数据包到客户端
  // custom_udp_send.cus_pbuf,
  // custom_udp_send.cus_ip_addr,
  // custom_udp_send.cus_udp_port);
  // pbuf_free(custom_udp_send.cus_pbuf); //释放缓冲池中的当前pbuf对象
  // custom_udp_send.cus_pbuf=NULL;
  // }
  // break;
  // #endif
  //
  // #if RS232_EN 》 0u
  // case RS232: //RS232串口方式
  // USART1_SendData(n24l01_buf, sizeof(n24l01_buf));
  // break;
  // #endif
  //
  // #if HF_LPB100_EN 》 0u
  // case WiFi: //Wi-Fi方式(需要Wi-Fi模块作为服务端)
  // USART3_SendData(n24l01_buf, sizeof(n24l01_buf));
  // break;
  // #endif
  //
  // default:
  // break;
  // }
  // }
  //
  // OSTimeDlyHMSM(0, 0, 0, 1, OS_OPT_TIME_HMSM_NON_STRICT,&err); //阻塞1毫秒,即延时1个时钟节拍
  }
  }
  #endif
  
举报

更多回帖

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