IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32CubeIDE实现nRF24L01通信(C和C++混合编程) -> 正文阅读

[嵌入式]STM32CubeIDE实现nRF24L01通信(C和C++混合编程)

一、软硬件版本及技术点

  • STM32F103ZET6的开发板(正点原子战舰和精英板)

  • STM32CubeIDE

  • nRF24L01(云佳科技)

  • C与C++混编,c++仅使用了class以及string,比较好恢复为c程序

  • FreeRTOS

  • SPI

二、STM32CubeIDE的工程创建及软件配置

1.打开软件后,点击File→newSTM32Project

2.选择芯片类型
在这里插入图片描述
3.输入项目名、选择为C++目标语言,完成
在这里插入图片描述

4.添加头文件路径、源文件路径等

可以直接做第三节的内容,之后再添加头文件路径,源文件路径等,随着工程的增加要随时添加这部分内容

右击工程名,点击最后的属性propertise后得到如下图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gHwSYmxZ-1648863808076)(resource/image/image_2995vaonfBHG1gTaYvaSKy.png)]
点击Add增加头文件的路径,没有C++的头文件路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RWzmS2K4-1648863808083)(resource/image/image_h6qEBwCsDHufdXwJ2xQdK2.png)]
这里的C++头文件路径可以照着工程中的Includes添加,如下图,把框出的路径添加到头文件路径。

添加的头文件路径会在这个文件夹中显示**(但是不知道为什么上面的几个文件夹的路径虽然创建工程就显示但是还需要挨个手动添加??)**
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HCHlIE40-1648863808085)(resource/image/image_ETirALVcjj2MxfaPwuvvR.png)]
增加的源文件路径也要添加
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P2lrcEm3-1648863808087)(resource/image/image_shFWASbXNX4kTcxU8P5tAp.png)]

三、STM32CubeIDE配置系统参数

3.1系统时钟配置

采用外部高速晶振和低速外部晶振
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOwLKEHb-1648863808090)(resource/image/image_5dPf7cY2ToJi6nek4DqChn.png)]
在这里插入图片描述

3.2时基配置

debug选用串口线,时基由于使用了FreeRTOS,不建议使用systick
在这里插入图片描述

3.3配置SPI2

采用STM32的SPI2,全双工主机(master)模式

射频芯片的最高为10MHz,SPI速度设置为以下

根据射频芯片的时序图特征更改CPOL以及CPHA

配置如图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EnIDNasm-1648863808097)(resource/image/image_sbdGFkLALyzR3PJRVgrps8.png)]
需要增加射频芯片使用的CSN、CE和IRQ引脚,这里要做对应的设置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fOZqfQ2s-1648863808100)(resource/image/image_kVYD9Z5RroibPDpzPuTzMK.png)]

3.4配置串口1

配置重点是需要重定向printf函数,<stdio.h>中的printf默认是输出到终端,对于嵌入式设备需要对其重定向,此处重定向到串口1

//生成工程后在对应的uart.c文件中加入重定向代码
//代码增加的地方需要在如下类似的两个宏定义之间,否则重新生成代码将覆盖
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */

#include "stdio.h"//

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif

PUTCHAR_PROTOTYPE
{
  HAL_UART_Transmit(&huart1, (uint8_t*)&ch,1,HAL_MAX_DELAY);
    return ch;
}


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5fPMxh5n-1648864262939)(resource/image/image_4Nns7GQpyLnBYyJsJBgHmy.png)]

3.5配置FreeRTOS

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bLBJbxqP-1648863808104)(resource/image/image_eTfdy8gxQtZQET69opDeyd.png)]
在这里插入图片描述
在这里插入图片描述

3.6其他设置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eaYwR15x-1648863808110)(resource/image/image_dNB3yadfHa13c8zZWyomRT.png)]
每次修改.ioc文件后都要保存后重新生成代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r43Xog6k-1648863808112)(resource/image/image_dsxBvqTgh57mXJmhU5Uh7C.png)]

四、nRF24L01功能实现

  • 头文件 nRF24L01.h
/*
 * nRF24L01.h
 *
 *  Created on: Mar 28, 2022
 *      Author: MBW
 */

#ifndef NRF2401_NRF24L01_H_
#define NRF2401_NRF24L01_H_

#include <stdint.h>
#define nRF_ASSERT

/* Exported macro ------------------------------------------------------------*/
#ifdef  nRF_ASSERT
/**
  * @brief  The assert_param macro is used for function's parameters check.
  * @param  expr If expr is false, it calls assert_failed function
  *         which reports the name of the source file and the source
  *         line number of the call that failed.
  *         If expr is true, it returns no value.
  * @retval None
  */
#define nRF_assert(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(uint8_t* file, uint32_t line);

#else
#define nRF_assert(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */

//use to call C++ function in C file
extern "C" void nRF24L01_wrapper();
extern "C" void nRF24L01_receive();
extern "C" void call_onIRQ();
extern "C" void nRF24L01_callRXmode();


namespace nRF {
//settings
#define nRF_SINGLE_MAXLEN 32
#define ADDRESS_LENGTH 5
#define MAX_TX      0x10
#define TX_OK       0x20
#define RX_OK       0x40
#define TX_ERROR        0x01
#define RX_ERROR        0x02
#define NO_PACKET       0x04
#define NOT_EXIST       0x08

//commands macro
#define NRF_READ_REG    0x00 // Define read command to register
#define NRF_WRITE_REG   0x20 // Define write command to register
#define RD_RX_PLOAD 0x61 // Define RX payload register address 
#define WR_TX_PLOAD 0xA0 // Define TX payload register address 
#define FLUSH_TX    0xE1 // Define flush TX register command 
#define FLUSH_RX   0xE2 // Define flush RX register command 
#define REUSE_TX_PL 0xE3 // Define reuse TX payload register command 
#define NOP     0xFF // Define No Operation, might be used to read status register

//register address
#define CONFIG       0x00 // 'Config' register address
#define EN_AA       0x01 // 'Enable Auto Acknowledgment' register address 
#define EN_RXADDR   0x02 // 'Enabled RX addresses' register address 
#define SETUP_AW    0x03 // 'Setup address width' register address 
#define SETUP_RETR  0x04 // 'Setup Auto. Retrans' register address 
#define RF_CH     0x05 // 'RF channel' register address 
#define RF_SETUP   0x06 // 'RF setup' register address 
#define STATUS     0x07 // 'Status' register address 
#define OBSERVE_TX  0x08 // 'Observe TX' register address 
#define CD       0x09 // 'Carrier Detect' register address 
#define RX_ADDR_P0  0x0A // 'RX address pipe0' register address 
#define RX_ADDR_P1  0x0B // 'RX address pipe1' register address 
#define RX_ADDR_P2  0x0C // 'RX address pipe2' register address 
#define RX_ADDR_P3  0x0D // 'RX address pipe3' register address 
#define RX_ADDR_P4  0x0E // 'RX address pipe4' register address 
#define RX_ADDR_P5  0x0F // 'RX address pipe5' register address 
#define TX_ADDR     0x10 // 'TX address' register address 
#define RX_PW_P0   0x11 // 'RX payload width, pipe0' register address 
#define RX_PW_P1   0x12 // 'RX payload width, pipe1' register address 
#define RX_PW_P2   0x13 // 'RX payload width, pipe2' register address 
#define RX_PW_P3   0x14 // 'RX payload width, pipe3' register address 
#define RX_PW_P4   0x15 // 'RX payload width, pipe4' register address 
#define RX_PW_P5   0x16 // 'RX payload width, pipe5' register address 
#define FIFO_STATUS 0x17 // 'FIFO Status Register' register address 

class nRF24L01 final {
#define RX_ADDRESS 0
#define TX_ADDRESS 1
public:
  nRF24L01();
  virtual ~nRF24L01();

  void onIRQ();         //callback function when enable interrupt
  uint8_t nRFSendMsg(uint8_t* msg, uint8_t msglen); //send or receive message
  uint8_t nRFRecvMsg(uint8_t* msg, uint8_t msglen, uint32_t timeout);
  uint8_t nRFexistDev();   //check if nRF is exist
  void setEnableCE(void(*func)()); //set GPIO control function
  void setDisableCE(void(*func)());
  void setEnableCSN(void(*func)());
  void setDisableCSN(void(*func)());
  void setIrq(uint8_t (*func)());
  void setAddress(uint8_t txorrx, uint8_t* addr, uint8_t len);
  void getAddress(uint8_t txorrx, uint8_t* addr, uint8_t len);
  void setRxMode();
  void setTxMode();
  
private:
  uint8_t nRFWriteReg(uint8_t reg, uint8_t val); //write or read register
  uint8_t nRFReadReg(uint8_t reg);


  uint8_t nRFWriteBuf(uint8_t reg, uint8_t* msg, uint8_t msglen); //send or receive message
  uint8_t nRFReadBuf(uint8_t reg, uint8_t* msg, uint8_t msglen); //send or receive message
  void nRFSPIInit();

  uint8_t nRFSendPacket(uint8_t* packet, uint8_t len);
  uint8_t nRFRecvPacket(uint8_t* packet, uint8_t len);
  void (*enableCE)();
  void (*disableCE)();
  void (*enableCSN)();
  void (*disableCSN)();
  uint8_t (*getIRQ)();
  uint8_t rx_address[5]; 
  uint8_t tx_address[5]; 

};

extern nRF24L01* g_nrf;

} /* namespace nRF */

#endif /* NRF2401_NRF24L01_H_ */

/*
 * nRF24L01.h
 *
 *  Created on: Mar 28, 2022
 *      Author: MBW
 */

#ifndef NRF2401_NRF24L01_H_
#define NRF2401_NRF24L01_H_

#include <stdint.h>
#define nRF_ASSERT

/* Exported macro ------------------------------------------------------------*/
#ifdef  nRF_ASSERT
/**
  * @brief  The assert_param macro is used for function's parameters check.
  * @param  expr If expr is false, it calls assert_failed function
  *         which reports the name of the source file and the source
  *         line number of the call that failed.
  *         If expr is true, it returns no value.
  * @retval None
  */
#define nRF_assert(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(uint8_t* file, uint32_t line);

#else
#define nRF_assert(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */

//use to call C++ function in C file
extern "C" void nRF24L01_wrapper();
extern "C" void nRF24L01_receive();
extern "C" void call_onIRQ();
extern "C" void nRF24L01_callRXmode();


namespace nRF {
//settings
#define nRF_SINGLE_MAXLEN 32
#define ADDRESS_LENGTH 5
#define MAX_TX      0x10
#define TX_OK       0x20
#define RX_OK       0x40
#define TX_ERROR        0x01
#define RX_ERROR        0x02
#define NO_PACKET       0x04
#define NOT_EXIST       0x08

//commands macro
#define NRF_READ_REG    0x00 // Define read command to register
#define NRF_WRITE_REG   0x20 // Define write command to register
#define RD_RX_PLOAD 0x61 // Define RX payload register address 
#define WR_TX_PLOAD 0xA0 // Define TX payload register address 
#define FLUSH_TX    0xE1 // Define flush TX register command 
#define FLUSH_RX   0xE2 // Define flush RX register command 
#define REUSE_TX_PL 0xE3 // Define reuse TX payload register command 
#define NOP     0xFF // Define No Operation, might be used to read status register

//register address
#define CONFIG       0x00 // 'Config' register address
#define EN_AA       0x01 // 'Enable Auto Acknowledgment' register address 
#define EN_RXADDR   0x02 // 'Enabled RX addresses' register address 
#define SETUP_AW    0x03 // 'Setup address width' register address 
#define SETUP_RETR  0x04 // 'Setup Auto. Retrans' register address 
#define RF_CH     0x05 // 'RF channel' register address 
#define RF_SETUP   0x06 // 'RF setup' register address 
#define STATUS     0x07 // 'Status' register address 
#define OBSERVE_TX  0x08 // 'Observe TX' register address 
#define CD       0x09 // 'Carrier Detect' register address 
#define RX_ADDR_P0  0x0A // 'RX address pipe0' register address 
#define RX_ADDR_P1  0x0B // 'RX address pipe1' register address 
#define RX_ADDR_P2  0x0C // 'RX address pipe2' register address 
#define RX_ADDR_P3  0x0D // 'RX address pipe3' register address 
#define RX_ADDR_P4  0x0E // 'RX address pipe4' register address 
#define RX_ADDR_P5  0x0F // 'RX address pipe5' register address 
#define TX_ADDR     0x10 // 'TX address' register address 
#define RX_PW_P0   0x11 // 'RX payload width, pipe0' register address 
#define RX_PW_P1   0x12 // 'RX payload width, pipe1' register address 
#define RX_PW_P2   0x13 // 'RX payload width, pipe2' register address 
#define RX_PW_P3   0x14 // 'RX payload width, pipe3' register address 
#define RX_PW_P4   0x15 // 'RX payload width, pipe4' register address 
#define RX_PW_P5   0x16 // 'RX payload width, pipe5' register address 
#define FIFO_STATUS 0x17 // 'FIFO Status Register' register address 

class nRF24L01 final {
#define RX_ADDRESS 0
#define TX_ADDRESS 1
public:
  nRF24L01();
  virtual ~nRF24L01();

  void onIRQ();         //callback function when enable interrupt
  uint8_t nRFSendMsg(uint8_t* msg, uint8_t msglen); //send or receive message
  uint8_t nRFRecvMsg(uint8_t* msg, uint8_t msglen, uint32_t timeout);
  uint8_t nRFexistDev();   //check if nRF is exist
  void setEnableCE(void(*func)()); //set GPIO control function
  void setDisableCE(void(*func)());
  void setEnableCSN(void(*func)());
  void setDisableCSN(void(*func)());
  void setIrq(uint8_t (*func)());
  void setAddress(uint8_t txorrx, uint8_t* addr, uint8_t len);
  void getAddress(uint8_t txorrx, uint8_t* addr, uint8_t len);
  void setRxMode();
  void setTxMode();
  
private:
  uint8_t nRFWriteReg(uint8_t reg, uint8_t val); //write or read register
  uint8_t nRFReadReg(uint8_t reg);


  uint8_t nRFWriteBuf(uint8_t reg, uint8_t* msg, uint8_t msglen); //send or receive message
  uint8_t nRFReadBuf(uint8_t reg, uint8_t* msg, uint8_t msglen); //send or receive message
  void nRFSPIInit();

  uint8_t nRFSendPacket(uint8_t* packet, uint8_t len);
  uint8_t nRFRecvPacket(uint8_t* packet, uint8_t len);
  void (*enableCE)();
  void (*disableCE)();
  void (*enableCSN)();
  void (*disableCSN)();
  uint8_t (*getIRQ)();
  uint8_t rx_address[5]; 
  uint8_t tx_address[5]; 

};

extern nRF24L01* g_nrf;

} /* namespace nRF */

#endif /* NRF2401_NRF24L01_H_ */

  • nRF24L01.cpp 源文件
/*
 * nRF24L01.cpp
 *
 *  Created on: Mar 28, 2022
 *      Author: MBW
 */

#include <nRF24L01.h>
#include "spi.h"
#include "gpio.h"
#include <string>
#include <stdio.h>

namespace nRF {

nRF24L01* g_nrf = nullptr; //global nrf pointer

nRF24L01::nRF24L01() {

  tx_address[0] = 0xE1; //default address when user not set address
  tx_address[1] = 0xE2;
  tx_address[2] = 0xE3;
  tx_address[3] = 0xE4;
  tx_address[4] = 0xE5;

  rx_address[0] = 0xE1;
  rx_address[1] = 0xE2;
  rx_address[2] = 0xE3;
  rx_address[3] = 0xE4;
  rx_address[4] = 0xE5;

  nRFSPIInit();       //special setting for nRF24L01 device about spi 

}

nRF24L01::~nRF24L01() {
  //do nothing
}

void nRF24L01::setAddress(uint8_t txorrx, uint8_t* addr, uint8_t len) {
  nRF_assert(len >= ADDRESS_LENGTH + 1); // limit len <= 5
  if (txorrx == TX_ADDRESS) {
    tx_address[0] = addr[0];
    tx_address[1] = addr[1];
    tx_address[2] = addr[2];
    tx_address[3] = addr[3];
    tx_address[4] = addr[4];
  } else {
    rx_address[0] = addr[0];
    rx_address[1] = addr[1];
    rx_address[2] = addr[2];
    rx_address[3] = addr[3];
    rx_address[4] = addr[4];
  }
}

void nRF24L01::getAddress(uint8_t txorrx, uint8_t* addr, uint8_t len) {
  nRF_assert(len >= ADDRESS_LENGTH + 1);
  if (txorrx == TX_ADDRESS) {
    addr[0] = tx_address[0];
    addr[1] = tx_address[1];
    addr[2] = tx_address[2];
    addr[3] = tx_address[3];
    addr[4] = tx_address[4];
    addr[5] = 0;
  } else {
    addr[0] = rx_address[0];
    addr[1] = rx_address[1];
    addr[2] = rx_address[2];
    addr[3] = rx_address[3];
    addr[4] = rx_address[4];
    addr[5] = 0;
  }
}

void nRF24L01::setRxMode() {
  disableCE(); //power down state or standby state to configure nRF24L01
  nRFWriteBuf(NRF_WRITE_REG + RX_ADDR_P0, rx_address, ADDRESS_LENGTH);
  nRFWriteReg(NRF_WRITE_REG + EN_AA, 0x01);
  nRFWriteReg(NRF_WRITE_REG + EN_RXADDR, 0x01);
  nRFWriteReg(NRF_WRITE_REG + RF_CH, 40);
  nRFWriteReg(NRF_WRITE_REG + RX_PW_P0, nRF_SINGLE_MAXLEN);
  nRFWriteReg(NRF_WRITE_REG + RF_SETUP, 0x0F);
  nRFWriteReg(NRF_WRITE_REG + CONFIG, 0x0F);
  nRFWriteReg(FLUSH_TX, 0XAA);
  nRFWriteReg(FLUSH_RX, 0XAA);
  enableCE();
  HAL_Delay(1);
}

void nRF24L01::setTxMode() {
  disableCE(); //configure
  nRFWriteBuf(NRF_WRITE_REG + TX_ADDR, tx_address, ADDRESS_LENGTH);
  nRFWriteBuf(NRF_WRITE_REG + RX_ADDR_P0, rx_address, ADDRESS_LENGTH);
  nRFWriteReg(NRF_WRITE_REG + EN_AA, 0x01);
  nRFWriteReg(NRF_WRITE_REG + EN_RXADDR, 0x01);
  nRFWriteReg(NRF_WRITE_REG + SETUP_RETR, 0x1a);
  nRFWriteReg(NRF_WRITE_REG + RF_CH, 40);
  nRFWriteReg(NRF_WRITE_REG + RF_SETUP, 0x0F);
  nRFWriteReg(NRF_WRITE_REG + CONFIG, 0x0E);
  nRFWriteReg(FLUSH_TX, 0XAA);
  nRFWriteReg(FLUSH_RX, 0XAA);
  enableCE();
  HAL_Delay(1);
}

//when interrupt happen, call this function
void nRF24L01::onIRQ() {
  uint8_t msg[33] = {0};
  nRF::g_nrf->nRFRecvMsg(msg, 32 , 60*1000);
  msg[32] = 0;
  printf("irq get msg: %s\r\n", msg);
}

//wrapper C++ function for using in C file
extern "C" void call_onIRQ() {
  nRF::g_nrf->onIRQ();
}

//set irq GPIO
void nRF24L01::setIrq(uint8_t (*func)()) {
  getIRQ = func;
}

//send msg longer than 32 bytes
uint8_t nRF24L01::nRFSendMsg(uint8_t* msg, uint8_t msglen) {
  uint8_t packetnum = msglen / nRF_SINGLE_MAXLEN;
  uint8_t txresult;
  for (int i = 0; i < packetnum; i++) {
    txresult = nRFSendPacket(msg + i * nRF_SINGLE_MAXLEN, nRF_SINGLE_MAXLEN);
    if (txresult != TX_OK) return txresult;
  }
  if (msglen % nRF_SINGLE_MAXLEN) {
    txresult = nRFSendPacket(msg + packetnum * nRF_SINGLE_MAXLEN, nRF_SINGLE_MAXLEN);
    if (txresult != TX_OK) return txresult;
  }
  return SUCCESS;
}

//send a packet less than 32 bytes
uint8_t nRF24L01::nRFSendPacket(uint8_t* packet, uint8_t len) {
  disableCE();
  nRFWriteBuf(WR_TX_PLOAD , packet, len);    //actually , len must be 32 if send packet
  nRFWriteReg(NRF_WRITE_REG + CONFIG, 0x0E); //set to txmode
  enableCE();  //enable TX
  HAL_Delay(1);
  while(getIRQ() != DISABLE); //wait for TX_OK or MAX_TX
  disableCE();
  HAL_Delay(1);
  uint8_t status = nRFReadReg(NRF_READ_REG + STATUS); //get STATUS 
  nRFWriteReg(NRF_WRITE_REG + STATUS, status); //clear interrupt
  nRFWriteReg(FLUSH_TX, 0xff); //clear TX FIFO
  if (status & MAX_TX) {
    return MAX_TX;
  }
  if (status & TX_OK) {
    return TX_OK;
  }
  return TX_ERROR;
}

uint8_t nRF24L01::nRFRecvPacket(uint8_t* packet, uint8_t len) {
  uint8_t status = nRFReadReg(NRF_READ_REG + STATUS);
  nRFWriteReg(NRF_WRITE_REG + STATUS, status);  //clear interrupt
  //auto clear FIFO after read RX FIFO
  if (status & RX_OK) {
    nRFReadBuf(RD_RX_PLOAD, packet, len);
    return SUCCESS;
  }
  return NO_PACKET;
}

//receive the designated length msg in timeout
uint8_t nRF24L01::nRFRecvMsg(uint8_t* msg, uint8_t msglen, uint32_t timeout) {
  uint8_t packetnum = msglen / nRF_SINGLE_MAXLEN;
  uint8_t cnt = 0;
  uint32_t tickstart = HAL_GetTick();

  while (HAL_GetTick()-tickstart < timeout) {
    if (cnt < packetnum) {
      int ret = nRFRecvPacket(msg + cnt * nRF_SINGLE_MAXLEN, nRF_SINGLE_MAXLEN);
      if (ret == SUCCESS)  cnt++;
    } else {
      if (msglen % nRF_SINGLE_MAXLEN == 0) return SUCCESS;
      int ret = nRFRecvPacket(msg + packetnum * nRF_SINGLE_MAXLEN, msglen % nRF_SINGLE_MAXLEN);
      if (ret == SUCCESS)  return SUCCESS;
    }
  }
  return RX_ERROR;
}

uint8_t nRF24L01::nRFWriteBuf(uint8_t reg, uint8_t* msg, uint8_t msglen) {
  uint8_t status;
  uint8_t nouse;
  nRF_assert(msglen <= nRF_SINGLE_MAXLEN);
  disableCSN();
  HAL_SPI_TransmitReceive(&hspi2, &reg, &status,1, 10);
  for (int i = 0; i < msglen; i++) HAL_SPI_TransmitReceive(&hspi2, msg+i, &nouse , 1, 10);
  enableCSN();
  return status;
}

uint8_t nRF24L01::nRFReadBuf(uint8_t reg, uint8_t* msg, uint8_t msglen) {
  uint8_t status;
  uint8_t nouse;
  nRF_assert(msglen <= nRF_SINGLE_MAXLEN);
  disableCSN();
  HAL_SPI_TransmitReceive(&hspi2, &reg, &status, 1, 10);
  for (int i = 0; i < msglen; i++) HAL_SPI_TransmitReceive(&hspi2, &nouse, msg+i, 1, 10);
  enableCSN();
  return status;
}

uint8_t nRF24L01::nRFWriteReg(uint8_t reg, uint8_t val) {
  uint8_t status;
  uint8_t nouse;
  disableCSN();
  HAL_SPI_TransmitReceive(&hspi2, &reg, &status, 1, 10);
  HAL_SPI_TransmitReceive(&hspi2, &val, &nouse, 1, 10);
  enableCSN();
  return status;
}

uint8_t nRF24L01::nRFReadReg(uint8_t reg) {
  uint8_t regval;
  uint8_t nouse;
  disableCSN();
  HAL_SPI_TransmitReceive(&hspi2, &reg, &nouse, 1, 10);
  HAL_SPI_TransmitReceive(&hspi2, &nouse, &regval, 1, 10);
  enableCSN();
  return regval;
}

//check if connect the device
uint8_t nRF24L01::nRFexist() {
  uint8_t buf[5] = {0,0,0,0,0};
  disableCE();
  nRFWriteBuf(NRF_WRITE_REG+TX_ADDR, tx_address, ADDRESS_LENGTH);
  nRFReadBuf(NRF_READ_REG+TX_ADDR, buf, ADDRESS_LENGTH);
  for (int i = 0; i < ADDRESS_LENGTH; i++) {
    if (buf[i] != tx_address[i]) return NOT_EXIST;
  }
  return SUCCESS;
}

//special for SPI setting
void nRF24L01::nRFSPIInit() {
  __HAL_SPI_DISABLE(&hspi2);
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
    HAL_SPI_Init(&hspi2);
  __HAL_SPI_ENABLE(&hspi2);
}

void nRF24L01::setEnableCE(void(*func)()) {
  enableCE = func;
}

void nRF24L01::setDisableCE(void(*func)()) {
  disableCE = func;
  disableCE();
}

void nRF24L01::setEnableCSN(void(*func)()) {
  enableCSN = func;
  enableCSN();
}

void nRF24L01::setDisableCSN(void(*func)()) {
  disableCSN = func;
}

} /* namespace nRF */

//用于C文件调用C++,作为对应C++代码的wrapper
//函数前需要加上 extern "C" ,表示按照C方式编译
//可新建位置或者放在源文件中

extern "C" void nRF24L01_callSend() {

  std::string msg = "hello nrf24 hello nrf24 hello nrf24 hello nrf24";
  printf("send msg begin\r\n");
  nRF::nRF24L01 nrf1;
  nrf1.setEnableCE(setCE);
  nrf1.setDisableCE(resetCE);
  nrf1.setEnableCSN(setCSN);
  nrf1.setDisableCSN(resetCSN);
  nrf1.setIrq(getIrq);

  HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);

  if (nrf1.nRFexistDev() == 0) {
    nrf1.setTxMode();
    uint8_t ret = nrf1.nRFSendMsg((uint8_t*)msg.c_str(), msg.length());
    if (ret == SUCCESS) printf("send result = %d\r\n", ret);
    else {
      printf("send failed [errorcode:0X%X]\r\n", ret);
    }
  } else {
    printf("no device!\r\n");
  }
  printf("send msg end\r\n");
}

extern "C" void nRF24L01_callRXmode() {
  nRF::g_nrf = new nRF::nRF24L01;
  nRF::g_nrf->setEnableCE(setCE);
  nRF::g_nrf->setDisableCE(resetCE);
  nRF::g_nrf->setEnableCSN(setCSN);
  nRF::g_nrf->setDisableCSN(resetCSN);
  printf("rx mode wait interrupt\r\n");

  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);

  nRF::g_nrf->setRxMode();

}

extern "C" void nRF24L01_receiveTimout() {
  uint8_t msg[50] = {0};
  nRF::nRF24L01 nrf1;
  nrf1.setEnableCE(setCE);
  nrf1.setDisableCE(resetCE);
  nrf1.setEnableCSN(setCSN);
  nrf1.setDisableCSN(resetCSN);
  nrf1.setIrq(getIrq);


  printf("waiting msg...\r\n");


  nrf1.setRxMode();

  nrf1.nRFRecvMsg(msg, 32 , 60*1000); //waiting 60s

  printf("msg : %s\r\n", msg);

}

//wrapper C++ function for using in C file
extern "C" void call_onIRQ() {
  nRF::g_nrf->onIRQ();
}

五、代码的使用方式

  1. 生成的main.c文件要更名为main.cpp (每次更改ioc重新生成代码之前先改回main.c,生成后再改为main.cpp,如果不改,则会另外生成一个main.c)

    main.c改为main.cpp 后要把 void MX_FREERTOS_Init(void) 前加上 extern "C" ,因为这个函数本身是freertos.c文件中定义的,main.c更名为main.cpp找不到对应的符号,因为C和C++编译为不同的符号

  2. 无论发送还是接收都要进行如下的函数的处理,即绑定使用的引脚对应的GPIO
    nRF::g_nrf->setEnableCE(setCE);
    nRF::g_nrf->setDisableCE(resetCE);
    nRF::g_nrf->setEnableCSN(setCSN);
    nRF::g_nrf->setDisableCSN(resetCSN);

//gpio.c中设置了这几个函数,也可以直接传入HAL_GPIO_WritePIN()
void setCE() {HAL_GPIO_WritePin(GPIOG, GPIO_PIN_8, GPIO_PIN_SET);}
void resetCE() {HAL_GPIO_WritePin(GPIOG, GPIO_PIN_8, GPIO_PIN_RESET);}
void setCSN() {HAL_GPIO_WritePin(GPIOG, GPIO_PIN_7, GPIO_PIN_SET);}
void resetCSN() {HAL_GPIO_WritePin(GPIOG, GPIO_PIN_7, GPIO_PIN_RESET);}
uint8_t getIrq() { return HAL_GPIO_ReadPin(GPIOG, GPIO_PIN_6);}

3. C和C++混合编程,采用的FreeRTOS创建任务,可以在对应的任务中增加Cwrapper后的代码

作为发送:

可以开辟一个缓冲区,用于填入发送的数据,然后调用nRF24L01_callSend 函数,进行发送。

作为接收:

  1. nRF24L01_receiveTimout 这个可以设置为在一定时间内接收一定大小的数据

  2. 或者采用中断的方式,收到数据后存储到指定的缓冲区,收集一定的数据后进行数据的处理

    中断函数尽量处理不高耗时的任务,中断可只做数据接收后放到指定的开辟的缓冲区

    处理缓冲区的工作在工作线程中执行,中断只负责转移数据

六、遇到的问题

问题1

  1. 不能够正确的写入到nRF24L01的寄存器,或者部分写入

  2. 读取数据超时

  3. 每次读取的错误数据是相同的,问题可复现

//第一块模块

//STM32CUBEIde读出的内容
send msg begin
txbuf : 0xE5 0xB5 0xE2 0xA0 0xB2 
rxbuf : 0xE5 0xB5 0xE2 0xA0 0xB2 
enaa :0x7
enrxaddr :0x7
setup :0x46
rfch :0x88
rfsetup :0x1
config = 0x2A
status = 0xE
send msg end



//正点原子代码读出的寄存器内容
txbuf : 0xb2 0xe5 0xb5 0xe2 0xa0   //0xb2数据位置错了,其他没问题
rxbuf : 0xb2 0xe5 0xb5 0xe2 0xa0 
EN_AA = 7
EN_RXADDR = 7
SETUP RETRY = 46
RF_CH = 88
RF_SETUP = F   //不同
CONFIG = 2A
STAUTS = E

//第二块模块

//STM32CUBEIde读出的内容
send msg begin
txbuf : 0xE7 0xE7 0xE7 0xE7 0xE7 
rxbuf : 0xE7 0xE7 0xE7 0xE7 0xE7 
enaa :0x3F
enrxaddr :0x3
setup :0x3
rfch :0x2
rfsetup :0x0
config = 0x8
status = 0xE
send msg end

//正点原子代码读出的寄存器内容
txbuf : 0xe7 0xe7 0xe7 0xe7 0xe7  //不具代表性
rxbuf : 0xe7 0xe7 0xe7 0xe7 0xe7 
EN_AA = 3F
EN_RXADDR = 3
SETUP RETRY = 3
RF_CH = 2
RF_SETUP = F   //不同
CONFIG = 8
STAUTS = E


//第二块第第二次测试

//STM32CUBEIde读出的内容
send msg begin
txbuf : 0x43 0x10 0x10 0x1 0x34 
rxbuf : 0x43 0x10 0x10 0x1 0x34 
enaa :0x1
enrxaddr :0x1
setup :0x1A
rfch :0x40
rfsetup :0x0
config = 0xE
status = 0xE
send msg end

//正点原子代码读出的寄存器内容
txbuf : 0x34 0x43 0x10 0x10 0x1  //有区别
rxbuf : 0x34 0x43 0x10 0x10 0x1 
EN_AA = 1
EN_RXADDR = 1
SETUP RETRY = 1A
RF_CH = 40
RF_SETUP = F //不同
CONFIG = E
STAUTS = E

解决方法

  1. 更改了disable CRC

  2. 项目中更改了HAL_SPI_TransmitHAL_SPI_TransmitReceive,可能是由于前者仅发送处理接收区,导致SPI同步收到的数据未被清除(仅猜测)

问题2

硬件错误,按下按键后发送第一次之后即发生Hard_Fault

解决方式:

增加线程栈空间为512

问题3

接收不到数据,发送端表示已经重发引起中断

解决方式:

  1. 经过测试发现,每次写给TX_FIFO必须写入32字节,否则nRF24L0不会发送!!

  2. 每次进入发送或者接收状态要清除缓冲区,防止错误导致缓冲区存在数据

七、建议

没事干的话改成纯c吧,C++没啥必要哈哈哈

参考资料

STM32—cubeMX+HAL库的SPI接口使用_夜风~的博客-CSDN博客_cubemx spi

正点原子库函数手册

nRF24L01的手册

STM32CubeIDE使用技巧(FreeRTOS点亮一盏灯)_重拾十年梦的博客-CSDN博客_cubeide自动补全

STM32L051C8T6 HAL库 + nRF24L01 收发案例(硬件SPI通讯)_Ch_champion的博客-CSDN博客_hal spi实例

使用STM32CubeMX开发三:按键中断实验 - 无网不进 - 博客园 (cnblogs.com)

attribute((weak)) 简介及作用_侵蚀昨天的博客-CSDN博客___attribute__((weak))

使用stm32cubeIDE git代码(gitee)_violet1714的博客-CSDN博客

stm32 FreeRTOS 某个任务一直不被运行_zhuimeng_ruili的博客-CSDN博客_freertos任务不运行

STM32CubeMX FreeRTOS堆栈分配、调试技巧 - 云+社区 - 腾讯云 (tencent.com)

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:27:28  更:2022-04-04 12:28:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 4:19:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码