【无线通信开发应用】nRF905数据手册深度解读

希望通过两个stm32、两个nRF905无线通信模块、串口来实现两机通信。具体功能为:

板子A、B分别包含一个stm32单片机和一个nRF905无线模块,欲实现板子A、B之间的通信。
其中,PC端串口助手可向板子A的stm32发送字符‘A’控制板子B上的LED亮灯,发送字符‘B’控制板B的LED熄灭;
同样地,可通过按下板B的按键向板A发送一段字符。

但是网络上鲜有NRF905模块的深层解读,故引出此文,主要解读NRF905的官方英文文档,以及部分重要的库函数封装,不涉及功能逻辑,不上传整个工程。
以下为文档的Git地址:NRF905相关文档下载地址


文章目录

  • 一、功能原理
  • 二、一些函数的封装

一、功能原理

在这里插入图片描述

首先看引脚定义,作为一个集成化的模块,我们需要像关注黑盒模型那样关注它的输入、输出的格式即可。
(1) 需要重点关注的是图中红框的部分:

TRX_CE:芯片收/发的使能
CD:	载波检测
AM:	地址匹配
TX_EN:	发送/接收使能

其中,TRX_CE 和 TX_EN 都为输入,需要 MCU 对其写入高低电平来控制 NRF905 的发送、接收模式的启动与否。CD 和 AM 都为输出,用来告诉 MCU “我检测到了空中跟我有相同载波频率的发送器了,并且它发过来的数据包也是发给我的(通过地址判断)”,主要是在接收模式下使用。
关于它们四个的具体解释如下:

  • NRF905 的工作模式
    在这里插入图片描述
    总的来说,在Power使能状态下,TRX_CE 和 TX_EN 共同控制 NRF905 的工作模式。
    在模块发送之前,需要将数据包写入到模块中,这需要通过SPI编程,故此需要将 TRX_CE 拉低;
    将 TRX_CE 和 TX_EN 都置1则设置为发送模式;
    将 TX_EN 拉低,设置为接收模式。

另外,工作模式的转换还需要遵守规定。 从待机模式切换到接收或发送模式都需要延迟一个650us的时间,这样才能够保证数据包被成功接收/发送。
在这里插入图片描述

(2)需要关注绿框中的几个引脚,它们是模块与 MCU通信的桥梁。
在这里插入图片描述
模块通过

MOSI:	主机发送从机接收
MISO:	从机发送主机接收
SCK:	SPI 通信的时钟线
SCN:	SPI的使能位,置0为使能

与 MCU 进行通信。置于 SPI 的使用时序,数据手册里也有所提及,在Figure 6. 到 Figure 8,不再展示。

那么 MCU 具体是如何通过 SPI 总线与 NRF905 进行通信的呢? 发送的频率、功率、目标设备的地址、数据信息都是写入到 NRF905 的哪里呢?

模块包含有五个内部寄存器,每个寄存器有各自的功能。
在这里插入图片描述
在这里插入图片描述
五个寄存器具体的作用:

  • Status – RegisterDR 标志位,当发送完一个完整的数据包(数据包的格式见下图)时或者接收到一个有效的数据包时,DR标志位置一;AM 标志位,接收模式下,当CD标志位置一后检查数据包中的地址是否和寄存器初始化时设置的本设备地址号一致,如果一致则AM标志位置一。(具体的描述见 数据手册-12 nRF905 features)
    在这里插入图片描述
    数据包中的 ADDR 和 PAYLOAD 是我们需要写入的,但是在真正发送之前,前导码和循环冗余校验会自动生成一并发送。

  • RF – Configuration Register :收发器的初始化配置,包括频率、输出功率、设备ID等。(具体的配置见下表)
    配置时需按照下表来写 RF配置寄存器的这10个字节,至于每位代表的含义需要搭配表15使用。在这里插入图片描述在这里插入图片描述
    先来大概看一下,我们需要结合Byte0和Byte1来设置发送频率、输出功率,需要Byte2来设置发送地址、接收地址的宽度,Byte5-8用来设置设备ID,最后的Byte9用来设置CRC模式等等。具体的配置待下文书写。

  • TX – Address:设置目标设备的的地址,也就是数据包中的ADDR。具体为多少个字节要在RF配置寄存器中设置。

  • TX – Payload:用来存放具体的发送信息了,具体为多少个字节要在RF配置寄存器中设置。

  • RX – Payload:用来存放具体的接收信息了,具体为多少个字节要在RF配置寄存器中设置。

现在我们知道了MCU通过SPI总线与模块的具体哪些寄存器进行读/写操作。在对具体的寄存器进行读/写操作之前,需要先发送SPI指令。

在这里插入图片描述
主要是用到三个指令:

  • WC指令。写RF配置寄存器,所以我们需要通过SPI总线写入0x00(从0位置开始)
  • WTA指令。向TX-ADDR寄存器写入目标设备的地址,需要SPI写入0x22.
  • RRP指令。读取RX-payload寄存器,接收到的有效数据包就存在这里面。需要SPI写入0x24.

二、一些函数的封装

  1. SPI写一个字节
    根据SPI时序来就好。
    在SCK低电平时,将一位数据通过MOSI放入,在SCK高电平时读取MOSI上的一位数据。经过循环移位,可以读取到一个字节的数据。
    void NRF905_SPI_Write_Byte(uint8_t data)
    {
    	for (uint8_t i=0; i<8; i++)
    	{
    		if (data & (0x80 << i))				
    		{
    			NRF905_MOSI_H; // 已宏定义,表示释放MOSI线。以下宏定义均同理
    		}
    		else
    		{
    			NRF905_MOSI_L; // 拉低MOSI线
    		}
    		NRF905_SCK_H; // 释放SCK线
    		NRF905_SCK_L; // 拉低SCK线
    	}
    }
    
  2. 写 RF – Configuration Register 配置寄存器
    上一章节中提到,配置寄存器总共有10个字节,我们一个个来看。
    在这里插入图片描述
    每一个字节都需要搭配着查找 Table 15. Configuration register description 来使用。
  • 写Byte0
    在这里插入图片描述
    查找表15,可以看到九位CH_NO加上一个HFREQ_PLL位共同才能决定工作频率。具体的频率需要查找表24:
    在这里插入图片描述
    可以看到,欲使得工作频率为433MHz,需CH_NO[7:0]=[01001100]=0x4c,CH_NO[8]=0,HFREQ_PLL=0,而这后两位都属于Byte1.
    总之,Byte0= 0x4c。
  • Byte1

在这里插入图片描述
bit[7:6]=00,AUTO_RETRAN=0(不自动重发),RX_RED_PWR=0(普通模式),PA_PWR=11(+10dBm),HFREQ_PLL=0,CH_NO[8]=0。因此,Byte1=00001100=0x0c。

  • Byte2
    在这里插入图片描述

bit[7]=0,TX_AFW[2:0]=100,bit[3]=0,RX_AFW[2:0]=100。因此,Byte2=01000100=0x44.

  • Byte3
    在这里插入图片描述
    bit[7:6]=0, RX_PW[5:0]=100000(32个字节的接收Buffer)。因此,Byte3=00100000=0x20.

  • Byte4
    在这里插入图片描述
    bit[7:6]=0, TX_PW[5:0]=100000(32字节的发送数据包)。因此,Byte4=00100000=0x20.

  • Byte5~Byte8
    4个字节从高到低来写设备ID。

  • Byte9
    CRC_MODE=0(8位CRC校验位),CRC_EN=1(开启CRC校验), XOF[2:0]=011(外部晶振频率16MHz), UP_CLK_EN=0,UP_CLK_FREQ[1:0]=00(输出时频)。因此,Byte9=0101 1000=0x58.

    /* 配置RF – Configuration Register */
    void NRF905_Config(void)
    {
    	NRF905_CSN_ENABLE; // 已宏定义,使能SPI
    	NRF905_SPI_Write_Byte(WC); // SPI指令
    	/*
    	* 将配置寄存器的10个字节依次写入,代码省略
    	*/
    	NRF905_CSN_DISABLE; // 失能SPI
    }
    
  1. 写要发送的数据包
    在这里插入图片描述
    根据这个流程图来,首先将TX_EN置一、TRX_CE置零,然后通过SPI总线向TX – Address写入目标地址、向TX – Payload写入32个字节的数据,随后将TRX_CE置一,设置NRF905为发送模式。之后会根据配置寄存器的设置自动生成CRC和前导码,从而组成一个完成发送数据包。
    /*
    *	NRF905在一般状态下长时间处于接收模式,当中断事件发生,方在回调函数中发送数据包。
    */
    void SendPacket(uint32_t TxBuffer)
    {
    	NRF905_TX_EN_H;
    	NRF905_TRX_CE_L;
    	delay_us(650); // 从待机模式切换到发送模式,需要650us
    	NRF905_CSN_ENABLE;
    	NRF905_SPI_Write_Byte(WTP); // Write TX-payload
    	for (uint8_t i = 0; i < 32; i++)
    	{
    		NRF905_SPI_Write_Byte(TxBuffer[i]); // 写入数据
    	}
    	NRF905_SPI_Write_Byte(WTA); 
    	for (uint8_t i = 0; i < 4; i++)
    	{
    		NRF905_SPI_Write_Byte(RF_Config[i + 5]); // 写入目标地址
    	}
    	NRF905_CSN_DISABLE;
    	NRF905_TRX_CE_H; //开启发送模式
    	
    	/*
    	*	延时至少10us(根据Data rate,见Figure 9.TX timing)
    	*/
    	NRF905_TRX_CE_L;
    }
    
  2. 接收一个数据包
    在这里插入图片描述
    根据流程图,保证TX_EN为低,将TRX_CE置一,接收器开始检测目标频率,一旦检测到则CD标志位置一,随后再检查数据包中的ADDR与自身设备ID是否一致,一致的话AM标志位置一,对接收的数据包进行循环冗余校验,如果是有效信息则DR数据位置一。
    int main(void)
    {
    /* 许多初始化,省略。 */
    	NRF905_Config(); // 写RF配置寄存器,对NRF905进行初始化
    	NRF905_TX_EN_L;
    	NRF905_TRX_CE_H;
    	delay_us(650);
    	
    	while(1)
    	{
    		if(!binarySignal) // 一个二值信号量,常为0,当中断事件发生时变为1
    		{
    			while(!(CD_ifH() & AM_ifH())) // CD标志位和AM标志位都置1
    			{
    				delay_ms(100);
    				NRF905_TX_EN_L;
    				NRF905_TRX_CE_H;
    			}
    			ReceivePacket(RxBuffer);
    			
    			/* 处理逻辑 */
    			
    		}
    	}
    }
    
    void ReceivePacket(uint32_t RxBuffer)
    {
    	NRF905_CSN_ENABLE;
    	NRF905_TRX_CE_L;
    	NRF905_SPI_Write_Byte(RRP); // Read RX-payload
    	for (uint8_t i = 0; i < 32; i++)
    	{
    		RxBuffer[i] = NRF905_SPI_Read_Byte();
    	}
    
    	NRF905_CSN_DISABLE;
    	NRF905_TRX_CE_L;
    }
    

本文的目的在于掌握阅读数据手册的能力,张贴的代码中包含许多伪代码,故而不能直接运行。此次阅读完英文数据手册,只觉层层递进、环环相扣、逻辑清晰。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/587598.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux migrate_type初步探索

1、基础知识 我们都知道Linux内存组织管理结构架构&#xff0c;顶层是struct pglist_data&#xff0c;然后再到struct zone&#xff0c;最后是struct page。大概的管理结构是这样的&#xff1a; 根据物理内存的地址范围可划分不同的zone&#xff0c;每个zone里的内存由buddy…

【redis】Redis数据类型(三)List类型

目录 List类型介绍特点 List数据结构附&#xff1a;3.2以前的版本(介绍一下压缩列表和双向链表)压缩列表ZipList双向链表LinkedList 常用命令lpush示例 lpushx示例 rpush示例 rpushx示例 LPOP示例 RPOP示例 BLPOP非阻塞行为阻塞行为相同的 key 被多个客户端同时阻塞在 MULTI/EX…

【类型商店】字符字符串(上)

啊&#xff0c;哈喽&#xff0c;小伙伴们大家好。我是#Y清墨&#xff0c;今天呐&#xff0c;我要介绍的是字符与字符串。 导语 在我之前发的[算法材料包]C数字拆分里有提及到.size()和length()和strlen(),想看更多内容请别划走&#xff01; 一.论字符 &#xff08;1&#xff…

口袋实验室--使用AD2高效调试IIC、UART、SPI等低速接口

目录 1. 简介 2. 调试过程 2.1 简要步骤 2.2 Si5338 寄存器配置流程 2.3 AD2的基本配置 2.4 检查Si5338状态 2.5 配置Si5338寄存器 2.6 保存Si5338寄存器 3. 总结 1. 简介 使用Digilent Analog Discovery 2进行调试不仅提升了工作效率&#xff0c;而且极大地简化了常…

Golang | Leetcode Golang题解之第60题排列序列

题目&#xff1a; 题解&#xff1a; func getPermutation(n int, k int) string {factorial : make([]int, n)factorial[0] 1for i : 1; i < n; i {factorial[i] factorial[i - 1] * i}k--ans : ""valid : make([]int, n 1)for i : 0; i < len(valid); i {…

2024年第二十一届 五一杯 (C题)大学生数学建模挑战赛|数学建模完整代码+建模过程全解全析

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 CS团队倾注了大量时间和心血&#xff0c;深入挖掘解决方案。通…

pyinstaller打包pytorch和transformers程序

记录使用pyinstaller打包含有pytorch和transformers库的程序时遇到的问题和解决方法。 环境和版本信息 操作系统&#xff1a;Windows 11 Python&#xff1a;3.10.12 pyinstaller&#xff1a;5.13.0 torch&#xff1a;2.2.2 transformers&#xff1a;4.40.1 打包过程和问…

【进收藏夹吃灰系列】算法学习指南

文章目录 [toc]分治算法 个人主页&#xff1a;丷从心 系列专栏&#xff1a;进收藏夹吃灰系列 分治算法 博客标题博客url【分治算法】【Python实现】Hanoi塔问题https://blog.csdn.net/from__2024_04_11/article/details/138093461?spm1001.2014.3001.5502

Mysql-黑马

Mysql-黑马 编写规范&#xff1a;## 一级1. 二级三级 1.Mysql概述 数据库概念mysql数据仓库 cmd启动和停止 net start mysql180 net stop mysql180备注&#xff1a;其中的mysql180是服务名 客户端连接 远程连接数据仓库 -h 主机号 -P端口号 mysql [-h 127.0.0.1] [-P 33…

CSDN如何在个人主页开启自定义模块|微信公众号

目前只有下面三种身份才具有这个功能。 VIP博客专家企业博客 栏目内容不知道怎么写HTML的&#xff0c;可以联系我帮你添加

15.Blender Eevee和Cycles渲染引擎对比

初步介绍 Eevee是实时渲染的引擎&#xff0c;会省略一些解算方式&#xff0c;尤其对光线和阴影 Cycles会考虑这些因素&#xff0c;所以会对光线和阴影的表达更加真实&#xff0c;有一个实时光线追踪的功能 Cycles渲染完之后&#xff0c;每移动一次画面&#xff0c;都会重新渲染…

【工程记录】Python爬虫入门记录(Requests BeautifulSoup)

目录 写在前面1. 环境配置2. 获取网页数据3. 解析网页数据4. 提取所需数据4.1 简单提取4.2 多级索引提取 5. 常见问题 写在前面 仅作个人学习与记录用。主要整理使用Requests和BeautifulSoup库的简单爬虫方法。在进行数据爬取时&#xff0c;请确保遵守相关法律法规和网站的服务…

git revert的使用

由于某种原因我们需要撤销掉之前某一次的修改&#xff0c;但是这个修改已经提交&#xff0c;并且后面又经历了好几轮的提交。可能如下这种情况&#xff1a; 那么此时使用git revert再合适不过啦。git revert ${commit_id}就可以将指定commit id的修改撤销&#xff0c;然后提交…

Java从菜鸟到高手①

目录 1.数据类型 2.定义变量 2.1.编码方式 2.2.布尔型变量boolean 2.3.隐式类型转化和强制类型转化 2.4类型提升 3.字符串类型 4.运算符 4.1.取余 4.2. &#xff0c;- 4.3逻辑运算&& || &#xff01; 4.4.位运算 4.5.条件运算符 1.数据类型 Java中&#…

Linux:使用匿名管道对进程池的模拟实现

目录 一、Makefile 二、processpool.cc 2.1创建通信管道和子进程 2.2控制子进程 2.3回收进程 三、task.hpp 四、完整代码 接下来我们将模拟实现一个进程池&#xff0c;进程池广泛应用与各个领域和方向&#xff0c;比如我们打开电脑后同时打开很多个进程&#xff08;也就是软…

SpringBoot对接前端传递的base64编码的图片信息,转成图片以Get请求进行浏览器文件下载,不下载到本地。

一、问题描述 1.1需求描述。 前端将浏览器展示的图片以base64编码的形式传递给后端&#xff0c;以此实现文件下载的功能&#xff0c;在浏览器弹出文件下载框。效果如下 1.2实现思路 将前端传递的base64进行解码&#xff0c;设置响应头返回响应体&#xff0c;代码如下。 pu…

如何安全的使用密码登录账号(在不知道密码的情况下)

首先&#xff0c;需要用到的这个工具&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 1、打开工具&#xff0c;进入账号密码模块&#xff0c;如图 2、看到鼠标移动到密码那一栏有提示&#xff0c;按住Ctrl或者Alt点击或者双击就能复制内容&…

【大前端】ECharts 绘制立体柱状图

立体柱状图分为&#xff1a; 纯色立体柱状图渐变立体柱状图 常用实现方式 纯色立体柱状图 纯色立体柱状图&#xff0c;使用MarkPoint和颜色渐变就实现&#xff0c;如下代码 import * as echarts from "echarts";var chartDom document.getElementById("main&…

Window(Qt/Vs)软件添加版本信息

Window&#xff08;Qt/Vs&#xff09;软件添加版本信息 文章目录 Window&#xff08;Qt/Vs&#xff09;软件添加版本信息VS添加版本信息添加资源文件添加版本定义头自动更新版本添加批处理脚本设置生成事件 Qt添加版本信息添加资源文件文件信息修改自动更新版本 CMake添加版本信…

rust疑难杂症

rust疑难杂症解决 边碰到边记录&#xff0c;后续可能会逐步增加&#xff0c;备查 cargo build时碰到 Blocking waiting for file lock on package cache 原因是Cargo 无法获取对包缓存的文件锁&#xff0c; 有时vscode中项目比较多&#xff0c;如果其中某些库应用有问题&…
最新文章