一、NRF2401无线通信模块使用记录
一、电路引脚图

1、引脚说明:

2、引脚标号:
找到1号引脚,与原理图对号入座。

3、cubemx初始化配置:


5、驱动文件
配置spi,并构建发送与接收函数接口
.h
#define TX_ADR_WIDTH 5 //发射地址宽度
#define TX_PLOAD_WIDTH 32 //发射数据通道有效数据宽度0~32Byte
#define RX_ADR_WIDTH 5
#define RX_PLOAD_WIDTH 32#define CHANAL 40 //频道选择 // SPI(nRF24L01) commands , NRF的SPI命令宏定义,详见NRF功能使用文档
#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// SPI(nRF24L01) registers(addresses) ,NRF24L01 相关寄存器地址的宏定义
#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#define MAX_RT 0x10 //达到最大重发次数中断标志位
#define TX_DS 0x20 //发送完成中断标志位 // #define RX_DR 0x40 //接收到数据中断标志位uint8_t SPI_NRF_WriteBuf(uint8_t reg ,uint8_t *pBuf,uint8_t bytes);
uint8_t SPI_NRF_ReadBuf(uint8_t reg,uint8_t *pBuf,uint8_t bytes);
uint8_t SPI_NRF_ReadReg(uint8_t reg);
uint8_t SPI_NRF_WriteReg(uint8_t reg,uint8_t dat);
extern uint8_t RX_BUF[RX_PLOAD_WIDTH]; //接收数据缓存
extern uint8_t TX_BUF[TX_PLOAD_WIDTH]; //发射数据缓存
extern uint8_t TX_ADDRESS[TX_ADR_WIDTH]; // 定义一个静态发送地址
extern uint8_t RX_ADDRESS[RX_ADR_WIDTH];
void NRF_RX_Mode(void);
void NRF_TX_Mode(void);
uint8_t NRF_Check(void);
uint8_t NRF_Tx_Dat(uint8_t *txbuf);
uint8_t NRF_Rx_Dat(uint8_t *rxbuf);
.c
uint8_t SPIx_ReadWriteByte(SPI_HandleTypeDef* hspi,uint8_t byte)
{uint8_t d_read,d_send=byte;if(HAL_SPI_TransmitReceive(hspi,&d_send,&d_read,1,0xFF)!=HAL_OK){d_read=0xFF;}return d_read;
}
/*** @brief 用于向NRF特定的寄存器写入数据* @param * @arg reg:NRF的命令+寄存器地址* @arg dat:将要向寄存器写入的数据* @retval NRF的status寄存器的状态*/
uint8_t SPI_NRF_WriteReg(uint8_t reg,uint8_t dat)
{uint8_t status;CS_L();status =SPIx_ReadWriteByte(&hspi1,reg);SPIx_ReadWriteByte(&hspi1,dat);CS_H();return status;
}/*** @brief 用于从NRF特定的寄存器读出数据* @param * @arg reg:NRF的命令+寄存器地址* @retval 寄存器中的数据*/
uint8_t SPI_NRF_ReadReg(uint8_t reg)
{uint8_t val;CS_L();SPIx_ReadWriteByte(&hspi1,reg);val=SPIx_ReadWriteByte(&hspi1,0xff);CS_H();return val;
} /*** @brief 用于向NRF的寄存器中写入一串数据* @param * @arg reg : NRF的命令+寄存器地址* @arg pBuf:用于存储将被读出的寄存器数据的数组,外部定义* @arg bytes: pBuf的数据长度* @retval NRF的status寄存器的状态*/
uint8_t SPI_NRF_ReadBuf(uint8_t reg,uint8_t *pBuf,uint8_t bytes)
{uint8_t status,u8_ctr; CS_L(); //使能SPI传输status=SPIx_ReadWriteByte(&hspi1,reg);//发送寄存器值(位置),并读取状态值 for(u8_ctr=0;u8_ctr<bytes;u8_ctr++)//读出数据pBuf[u8_ctr]=SPIx_ReadWriteByte(&hspi1,0xff);CS_H(); //关闭SPI传输return status; //返回读到的状态值
}/*** @brief 用于向NRF的寄存器中写入一串数据* @param * @arg reg : NRF的命令+寄存器地址* @arg pBuf:存储了将要写入写寄存器数据的数组,外部定义* @arg bytes: pBuf的数据长度* @retval NRF的status寄存器的状态*/
uint8_t SPI_NRF_WriteBuf(uint8_t reg ,uint8_t *pBuf,uint8_t bytes)
{
uint8_t status,u8_ctr; CS_L(); //使能SPI传输status= SPIx_ReadWriteByte(&hspi1,reg);//发送寄存器值(位置),并读取状态值for(u8_ctr=0; u8_ctr<bytes; u8_ctr++){SPIx_ReadWriteByte(&hspi1,(*pBuf++)); //写入数据 } CS_H(); //关闭SPI传输return status; //返回读到的状态值}uint8_t RX_BUF[RX_PLOAD_WIDTH]; //接收数据缓存uint8_t TX_BUF[TX_PLOAD_WIDTH]; //发射数据缓存uint8_t TX_ADDRESS[TX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01}; // 定义一个静态发送地址uint8_t RX_ADDRESS[RX_ADR_WIDTH] = {0x34,0x43,0x10,0x10,0x01};
/*** @brief 配置并进入接收模式* @param 无* @retval 无*/
void NRF_RX_Mode(void)
{CE_L(); SPI_NRF_WriteBuf(NRF_WRITE_REG+RX_ADDR_P0,RX_ADDRESS,RX_ADR_WIDTH);//写RX节点地址,在配置时有RX_ADDR_P0~RX_ADDR_P5个通道,每个通道的RX_ADDRESS都一样根据需求自己设定SPI_NRF_WriteReg(NRF_WRITE_REG+EN_AA,0x01); //使能通道0的自动应答 ,b‘0 ,0 0 0 1’ ,为0通道 SPI_NRF_WriteReg(NRF_WRITE_REG+EN_RXADDR,0x01);//使能通道0的接收地址 SPI_NRF_WriteReg(NRF_WRITE_REG+RF_CH,CHANAL); //设置RF通信频率 SPI_NRF_WriteReg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度 SPI_NRF_WriteReg(NRF_WRITE_REG+RF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启 SPI_NRF_WriteReg(NRF_WRITE_REG+CONFIG, 0x0f); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式 /*CE拉高,进入接收模式*/ CE_H();HAL_Delay(1);
} /*** @brief 配置发送模式* @param 无* @retval 无*/
void NRF_TX_Mode(void)
{ CE_L(); SPI_NRF_WriteBuf(NRF_WRITE_REG+TX_ADDR,TX_ADDRESS,TX_ADR_WIDTH); //写TX节点地址 SPI_NRF_WriteBuf(NRF_WRITE_REG+RX_ADDR_P0,RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK SPI_NRF_WriteReg(NRF_WRITE_REG+EN_AA,0x01); //使能通道0的自动应答 SPI_NRF_WriteReg(NRF_WRITE_REG+EN_RXADDR,0x01); //使能通道0的接收地址 SPI_NRF_WriteReg(NRF_WRITE_REG+SETUP_RETR,0x1a);//设置自动重发间隔时间:500us + 86us;最大自动重发次数:10次SPI_NRF_WriteReg(NRF_WRITE_REG+RF_CH,CHANAL); //设置RF通道为CHANALSPI_NRF_WriteReg(NRF_WRITE_REG+RF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启 SPI_NRF_WriteReg(NRF_WRITE_REG+CONFIG,0x0e); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,发射模式,开启所有中断/*CE拉高,进入发送模式*/ CE_H();HAL_Delay(1);// HAL_Delay(0xffff); //CE要拉高一段时间才进入发送模式
}/*** @brief 主要用于NRF与MCU是否正常连接* @param 无* @retval SUCCESS/ERROR 连接正常/连接失败*/
static uint8_t buf[5]={0xC2,0xC2,0xC2,0xC2,0xC2};
uint8_t buf1[5]={0};
uint8_t NRF_Check(void)
{uint8_t buf[5]={0XA5,0XA5,0XA5,0XA5,0XA5};uint8_t i;SPI_NRF_WriteBuf(NRF_WRITE_REG+TX_ADDR,buf,5);//写入5个字节的地址. memset(buf,0,5);SPI_NRF_ReadBuf(TX_ADDR,buf,5); //读出写入的地址 for(i=0;i<5;i++)if(buf[i]!=0XA5)break; if(i!=5)return 1;//检测24L01错误 return 0; //检测到24L01}/*** @brief 用于向NRF的发送缓冲区中写入数据* @param * @arg txBuf:存储了将要发送的数据的数组,外部定义 * @retval 发送结果,成功返回TXDS,失败返回MAXRT或ERROR*/
uint8_t NRF_Tx_Dat(uint8_t *txbuf)
{uint8_t state; CE_L();SPI_NRF_WriteBuf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF 32个字节CE_H();//启动发送 while(READ_IRQ()!=0);//等待发送完成state=SPI_NRF_ReadReg(STATUS); //读取状态寄存器的值 SPI_NRF_WriteReg(NRF_WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志if(state&MAX_RT)//达到最大重发次数{SPI_NRF_WriteReg(FLUSH_TX,0xff);//清除TX FIFO寄存器 printf("state&(MAX_RT) %0x \n ",state&(MAX_RT) );printf("state %0x \n",state);return MAX_RT; }if(state&TX_DS)//发送完成{return TX_DS;}return 0xff;//其他原因发送失败} /*** @brief 用于从NRF的接收缓冲区中读出数据* @param * @arg rxBuf :用于接收该数据的数组,外部定义 * @retval * @arg 接收结果*/
uint8_t NRF_Rx_Dat(uint8_t *rxbuf)
{uint8_t state; /*等待接收中断*/state=SPI_NRF_ReadReg(STATUS); //读取状态寄存器的值 SPI_NRF_WriteReg(NRF_WRITE_REG+STATUS,state); //清除TX_DS或MAX_RT中断标志if(state&RX_DR)//接收到数据{SPI_NRF_ReadBuf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据SPI_NRF_WriteReg(FLUSH_RX,0xff);//清除RX FIFO寄存器 return 0; } return 1;//没收到任何数据}
自测调用
/*检测 NRF 模块与 MCU 的连接*/
void Self_Test(void)
{/*判断连接状态*/ if(NRF_Check() == SUCCESS) printf("\r\n NRF1与MCU连接成功! \r\n"); else printf("\r\n NRF1与MCU连接失败,请重新检查接线。\r\n");NRF_RX_Mode(); // NRF1 进入接收模式while(1){/* 等待 NRF1 接收数据 *//* 判断接收状态 */if(NRF_Rx_Dat(rxbuf) == 0){printf("\r\n NRF1 接收数据为: \r\n"); // for(i=0;i<32;i++)// { // printf(" %d ",rxbuf[i]); // }printf("\n %s ",rxbuf); }/* NRF1 发送数据 */ if ( get_date(rs_buf) == 1) // 获取发送数据,开始送数据{ /* 发送数据 */NRF_TX_Mode(); status = NRF_Tx_Dat(rs_buf);/* 发送数据的状态 */if(status == TX_DS){printf("\r\nNRF1 发送数据成功\r\n");}else{printf("\r\nNRF1 发送数据失败 %d\r\n", status);}memset(rs_buf,0,u_size);//再次开启接收,并等候接收。printf("\r\nNRF1 进入接收模式\r\n"); NRF_RX_Mode();}}相关文章:
一、NRF2401无线通信模块使用记录
一、电路引脚图 1、引脚说明: 2、引脚标号: 找到1号引脚,与原理图对号入座。 3、cubemx初始化配置: 5、驱动文件 配置spi,并构建发送与接收函数接口 .h #define TX_ADR_WIDTH 5 //发射地址宽度 #define TX_PLO…...
NVIDIA GPU 架构详解:Pascal、Volta、Turing、Ampere、Ada、Hopper、Blackwell
目录 1. Pascal(帕斯卡)架构(2016)关键技术性能特性代表产品应用场景 2. Volta(伏特)架构(2017)关键技术性能特性代表产品应用场景 3.Turing(图灵)架构&#…...
初阶数据结构(C语言实现)——3顺序表和链表(2)
2.3 数组相关面试题 原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。OJ链接 力扣OJ链接-移除元素删除排序数组中的重复项。力扣OJ链接-删除有序数组中的重复项合并两个有序数组。力扣OJ链接-合并两个有序数组 2.3.1 移除元素 1…...
DeepSeek R1模型性能瓶颈分析与解决方案
摘要 研究发现,DeepSeek R1模型在处理复杂问题时可能遭遇性能瓶颈。当模型过度思考时,其性能会下降。通过减少不必要的思考过程,计算成本可降低43%。研究团队分析了4018条轨迹数据,并创建了一个开源数据集,以推动智能体…...
IP-----BGP协议
7.BGP协议 1.BGP的所属分类 2.BGP的特性 3.BGP的数据包 4.BGP的6种状态机 5.BGP的工作过程 6.BGP的路由黑洞 1.BGP路由黑洞 2.解决方法 7.BGP的防环 1.EBGP水平分割 2.IBGP水平分割 1.解决IBGP环路的规则 2.解决IBGP水平分割问题 3.作用 8.BGP的基础配置 1.查看…...
【String】917. 仅仅反转字母
917. 仅仅反转字母 - 力扣(LeetCode) 使用双指针,一个指针指向s的开始,一个指向s的末尾,同时遍历即可。...
python3使用selenium打开火狐并全屏
序言 本来桌面端全屏这种东西现在用electron或者tauri来做软件的全屏,但是奈何今天拿到了一块早些年的nx板子,arm架构的,系统有点老,装node只能到16版本,装了半天终于搞好了,发现这个系统没法隐藏系统的顶…...
探秘基带算法:从原理到5G时代的通信变革【二】Viterbi解码
文章目录 二、关键算法原理剖析2.1 Viterbi 解码2.1.1 卷积码与网格图基础**卷积码****网格图****生成多项式****理想情况下解码过程** 2.1.2 Viterbi 算法核心思想2.1.3 路径度量与状态转移机制2.1.4 算法流程与关键步骤详解2.1.5 译码算法举例与复杂度分析2.1.6 算法代码示例…...
Spring Bean 作用域设置为prototype在并发场景下是否是线程安全的
在并发场景下,将 Spring Bean 作用域设置为 prototype 通常能在一定程度上保证线程安全,但这并不意味着绝对的线程安全 1. prototype 作用域的特点 在 Spring 中,Bean 的作用域定义了 Bean 的生命周期和可见性。prototype 作用域表示每次从…...
金融项目实战
测试流程 测试流程 功能测试流程 功能测试流程 需求评审制定测试计划编写测试用例和评审用例执行缺陷管理测试报告 接口测试流程 接口测试流程 需求评审制定测试计划分析api文档编写测试用例搭建测试环境编写脚本执行脚本缺陷管理测试报告 测试步骤 测试步骤 需求评审 需求评…...
mybatis热点面试题第五弹
1. MyBatis与Hibernate的区别 答案: MyBatis: 不完全是一个ORM框架,需要手动编写SQL语句,灵活性高,适合对数据库操作有高性能要求的场景。缺点是无法做到数据库无关性,如果需要支持多种数据库,…...
c++头文件和命名空间
在 C 中,头文件和命名空间是两个重要的概念,它们分别用于代码组织和作用域管理。 一、头文件(Header Files) 1. 作用 声明接口:存放函数、类、变量的声明(而非定义)。代码复用:通…...
命令行参数和环境变量 ─── linux第13课
目录 命令行参数 命令行参数列表: 如何实现命令行参数传递到此进程 环境变量 基本概念 常见环境变量 查看环境变量方法 编辑 环境变量如何写入 总结: 测试PATH 命令行参数 同一个程序,可以根据命令行参数的不同,表现不同功能 比如:指令中的选项的实现. ls -al…...
基于 openEuler 22.09 的 OpenStack Yoga 部署
openEuler 虚拟化环境部署 使用 VMWare Workstation 创建三台 2 CPU、8G内存、100 GB硬盘 的虚拟机 主机 IP 作用 Controller 192.168.184.110 控制节点 Compute 192.168.184.111 计算节点 Storage 192.168.184.112 存储节 一 基础配置 1.1 配置 yum 源 由于 op…...
【Linux实践系列】:用c语言实现一个shell外壳程序
🔥本文专栏:Linux Linux实践项目 🌸博主主页:努力努力再努力wz 那么今天我们就要进入Linux的实践环节,那么我们之前学习了进程控制相关的几个知识点,比如进程的终止以及进程的等待和进程的替换,…...
通俗易懂的分类算法之K近邻详解
通俗易懂的分类算法之K近邻详解 用最通俗的语言和例子,来彻底理解 K近邻(K-Nearest Neighbors,简称 KNN) 这个分类算法。不用担心复杂的数学公式,我会用生活中的例子来解释,保证你一听就懂! 1.…...
NO.19十六届蓝桥杯模拟赛第三期上
1 如果一个数 p 是个质数,同时又是整数 a 的约数,则 p 称为 a 的一个质因数。 请问, 2024 的最大的质因数是多少? 答:23 #include <bits/stdc.h> using namespace std;int main() {ios::sync_with_stdio(false)…...
基于 Rust 与 GBT32960 规范的编解码层
根据架构设计,实现编解码层的代码设计 Cargo.toml 加入二进制序列化支持 # 序列化支持 ... bincode "1.3" # 添加二进制序列化支持 bytes-utils "0.1" # 添加字节处理工具 开始编码 错误处理(error.rs&#x…...
conda安装及超详细避坑实战
1. Anaconda介绍。 Anaconda是一站式数据科学与机器学习平台,专为开发者、数据分析师设计,并带有python中超过180个科学包及其依赖项。通过 Anaconda,您可以轻松管理数据环境、安装依赖包,快速启动数据分析、机器学习项目。 Anaconda集成了…...
LM studio 加载ollama的模型
1.LM 下载: https://lmstudio.ai/ 2.ollama下载: https://ollama.com/download 3.打开ollama,下载deepseek-r1。 本机设备资源有限,选择7B的,执行ollama run deepseek-r1:7b 4.windows chocolatey下载: P…...
【图论】判断图中有环的两种方法及实现
判断图中有环的两种方法及实现 在图论中,检测有向图是否存在环是常见问题。本文将介绍两种主流方法:DFS三色标记法和拓扑排序(Kahn算法),并提供对应的C代码实现。 方法一:DFS三色标记法 核心思想 通过深…...
深入探索像ChatGPT这样的大语言模型-02-POST training supervised finetuning
参考 【必看珍藏】2月6日,安德烈卡帕西最新AI普及课:深入探索像ChatGPT这样的大语言模型|Andrej Karpathy fineweb知乎翻译介绍 fineweb-v1原始连接 fineweb中文翻译版本 Chinese Fineweb Edu数据集 查看网络的内部结果,可以参…...
Kaldi环境配置与Aishell训练
一、项目来源 代码来源:kaldi-asr/kaldi: kaldi-asr/kaldi is the official location of the Kaldi project. (github.com) 官网文档:Kaldi: The build process (how Kaldi is compiled) (kaldi-asr.org) 踩着我的同门李思成-CSDN博客填上的坑kaldi环境…...
数据集/API 笔记:新加坡PSI(空气污染指数)API
data.gov.sg 数据范围:2016年2月 - 2025年3月 1 获取API方式 curl --request GET \--url https://api-open.data.gov.sg/v2/real-time/api/psi 2 返回数据 API 的数据结构可以分为 3 大部分: 区域元数据(regionMetadata) →…...
【GPU使用】如何在物理机和Docker中指定GPU进行推理和训练
我的机器上有4张H100卡,我现在只想用某一张卡跑程序,该如何设置。 代码里面设置 import os # 记住要写在impot torch前 os.environ[CUDA_VISIBLE_DEVICES] "0, 1"命令行设置 export CUDA_VISIBLE_DEVICES0,2 # Linux 环境 python test.py …...
【Java项目】基于SpringBoot的CSGO赛事管理系统
【Java项目】基于SpringBoot的CSGO赛事管理系统 技术简介:采用SpringBoot框架、Java语言、MySQL数据库等技术实现。 系统简介:CSGO赛事管理系统是一个基于B/S架构的管理系统,主要功能包括前台和后台管理模块。前台系统功能模块分为…...
MIPI接口:(4)MIPI CSI-2协议详解(上)
1. 什么是CSI? CSI(Camera Serial Interface)是MIPI联盟早期制定的摄像头接口标准,主要用于连接摄像头和处理器。 CSI-2是CSI的第二代版本,在原有基础上进行了全面优化: (1)分层架…...
防火墙旁挂组网双机热备负载均衡
一,二层交换网络: 使用MSTPVRRP组网形式 VLAN 2--->SW3为主,SW4 作为备份 VLAN 3--->SW4为主,SW3 作为备份 MSTP 设计 --->SW3 、 4 、 5 运行 实例 1 : VLAN 2 实例 2 : VLAN 3 SW3 是实例 1 的主根,实…...
JMeter 实战项目脚本录制最佳实践(含 BadBoy 录制方式)
JMeter 实战项目脚本录制最佳实践(含 BadBoy 录制方式) 一、项目背景 在软件测试过程中,使用 JMeter 进行性能测试和功能测试是常见的操作。本实战项目将详细介绍如何使用 JMeter 自带工具以及 BadBoy 进行脚本录制,并完善脚本以…...
硅基流动nodejs流式输出
使用JavaScript的api直接在前端问答速度虽然快但是有token直接暴露的风险。 现在使用nodejs也可以快速进行流式输出并且可以隐藏用户敏感信息。 const express require(express); const axios require(axios); const app express(); const port 3000;//启动服务node index…...
