NRF24L01模块STM32通信-通信初始化
目录
前言
一、IO口初始化
二、模拟SPI的基础代码
1.一些代码的宏定义
2.起始信号
3.CS,SCK,MOSI操作
4.MISO,IRQ操作
三.中间层代码
1.字节的输入和读取
2.写操作
3.读操作
四.应用层代码
1.24L01的检测
2.在main函数进行简单验证
3.24L01宏定义的代码
总结
前言
环境:
芯片:STM32F103C8T6
Keil:V5.24.2.0
模块:NRF24L01
一、IO口初始化
根据:NRF24L01模块STM32通信-调试前言-CSDN博客
需要初始6个IO:4个输出,2个输入
我初始对应的IO口如下;
//输出
CSN ->PA3
CE ->PA4
MOSI ->PA7
SCK ->PA5
//输入
IRQ ->PB1
MISO ->PA6
IO口初始化,包含了OLED和其他的初始化代码.
void Gpio_Init(void)
{
/* 输出
CSN ->PA3//
CE ->PA4
MOSI ->PA7
SCK ->PA5 输入
IRQ ->PB1
MISO ->PA6
*/
//outputRCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure);//inputGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_15;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);}
二、模拟SPI的基础代码
1.一些代码的宏定义
#define SPI_CS_PROT GPIOA //CS接线引脚通道, CSN
#define SPI_CS_PIN GPIO_Pin_3#define SPI_DO_PROT GPIOA //D0接线引脚通道, MOSI
#define SPI_DO_PIN GPIO_Pin_7#define SPI_SLK_PROT GPIOA //CL接线引脚通道, SCK
#define SPI_SLK_PIN GPIO_Pin_5#define SPI_DI_PROT GPIOA //DI接线引脚通道, MISO
#define SPI_DI_PIN GPIO_Pin_6#define SPI_IRQ_PROT GPIOB //DI接线引脚通道, MISO
#define SPI_IRQ_PIN GPIO_Pin_1#define MYSPI_W_CS(x) GPIO_WriteBit(SPI_CS_PROT,SPI_CS_PIN,(BitAction)(x))//对CS线进行操作
#define MYSPI_W_DI(x) GPIO_WriteBit(SPI_DI_PROT,SPI_DI_PIN,(BitAction)(x))//对DI线进行操作
#define MYSPI_W_DO(x) GPIO_WriteBit(SPI_DO_PROT,SPI_DO_PIN,(BitAction)(x))//对DO线进行操作
#define MYSPI_W_SLK(x) GPIO_WriteBit(SPI_SLK_PROT,SPI_SLK_PIN,(BitAction)(x))//对SLK线进行操作
#define NRF24L01_CE(x) GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)(x))//对CE线进行操作
2.起始信号
void MySPI_Start(void)
{MYSPI_W_CS(0); //拉低为开始信号
}void MySPI_Stop(void)
{MYSPI_W_CS(1); //拉高为结束信号
}
3.CS,SCK,MOSI操作
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, SPI_CS_PIN, (BitAction)BitValue);
}void MySPI_W_SCK(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, SPI_SLK_PIN, (BitAction)BitValue);
}void MySPI_W_MOSI(uint8_t BitValue)
{GPIO_WriteBit(GPIOA, SPI_DO_PIN, (BitAction)BitValue);
}
4.MISO,IRQ操作
uint8_t MySPI_Read_MISO(void)
{return GPIO_ReadInputDataBit(SPI_DI_PROT, SPI_DI_PIN);
}uint8_t MySPI_Read_IRQ(void)
{return GPIO_ReadInputDataBit(SPI_IRQ_PROT, SPI_IRQ_PIN);
}
三.中间层代码
1.字节的输入和读取
uint8_t MySPI_SwapByte(uint8_t ByteSend) //字节读取和交换
{uint8_t i,ByteReceive = 0x00;for(i = 0;i < 8;i ++){MySPI_W_MOSI(ByteSend & (0x80 >> i));MySPI_W_SCK(1);if (MySPI_Read_MISO() == 1){ByteReceive |= (0x80 >> i);}MySPI_W_SCK(0);}return ByteReceive;
}
关于这段代码更详细的解说可以观看江科大的视频SPI部分.
2.写操作
uint8_t NRF24l01_write_buf(uint8_t reg, uint8_t *pbuf, uint8_t len)
{uint8_t status, i;MySPI_Start(); /* 使能SPI传输 */status = MySPI_SwapByte(reg); /* 发送寄存器值(位置),并读取状态值 */for (i = 0; i < len; i++){MySPI_SwapByte(*pbuf++); /* 写入数据 */}MySPI_Stop(); /* 关闭SPI传输 */ return status; /* 返回读到的状态值 */}
3.读操作
uint8_t NRF24l01_read_buf(uint8_t reg, uint8_t *pbuf, uint8_t len)
{uint8_t status, i; MySPI_Start(); /* 使能SPI传输 */status = MySPI_SwapByte(reg); /* 发送寄存器值(位置),并读取状态值 */for (i = 0; i < len; i++){pbuf[i] = MySPI_SwapByte(0X55); /* 读出数据 */ } return status; /* 返回读到的状态值 */
}
四.应用层代码
1.24L01的检测
/*** @brief 检测24L01是否存在* @param 无* @retval 0, 成功; 1, 失败;*/
uint8_t NRF24l01_check(void)
{uint8_t buf[5] = {0XA5, 0XA5, 0XA5, 0XA5, 0XA5};uint8_t i;NRF24l01_write_buf(NRF_WRITE_REG + TX_ADDR, buf, 5); /* 写入5个字节的地址. */NRF24l01_read_buf(TX_ADDR, buf, 5); /* 读出写入的地址 */for (i = 0; i < 5; i++){if (buf[i] != 0XA5) break;}if (i != 5) return 1; /* 检测24L01错误 */return 0; /* 检测到24L01 */
}
关于其中的一些宏定义,代码放在本文末.
2.在main函数进行简单验证
void MY24L01_Init(void)//对模块和通信线的前期操作
{NRF24L01_CE(0);
MYSPI_W_CS(1);
MYSPI_W_SLK(0);}while(1)
{while (NRF24l01_check()) /* 检查NRF24L01是否在线 */{OLED_ShowString(16, 18, "NRF24l01 NGNGNG", OLED_6X8);OLED_Update();}OLED_ShowString(32, 18, "GOOD", OLED_6X8);OLED_Update();
}
可以使用OLED进行显示,当然因为结果只有0或1,所以也可以采用其他方式进行验证,如LED的亮灭
如果代码正确则可以进行后面的代码书写.
3.24L01宏定义的代码
/******************************************************************************************/
/* NRF24L01寄存器操作命令 */
#define NRF_READ_REG 0x00 /* 读配置寄存器,低5位为寄存器地址 */
#define NRF_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 NRF_FIFO_STATUS 0x17 /* FIFO状态寄存器;bit0,RX FIFO寄存器空标志;bit1,RX FIFO满标志;bit2,3,保留 *//* bit4,TX FIFO空标志;bit5,TX FIFO满标志;bit6,1,循环发送上一数据包.0,不循环; *//******************************************************************************************/
总结
本文代码并未涉及很多的交互动作,只是验证基础代码和IO口的连接正确.操作相对简单,方便及时对代码进行验证,查缺补漏,方便后面代码的调试
相关文章:
NRF24L01模块STM32通信-通信初始化
目录 前言 一、IO口初始化 二、模拟SPI的基础代码 1.一些代码的宏定义 2.起始信号 3.CS,SCK,MOSI操作 4.MISO,IRQ操作 三.中间层代码 1.字节的输入和读取 2.写操作 3.读操作 四.应用层代码 1.24L01的检测 2.在main函数进行简单验证 3.24L01宏定义的代码 总结 前…...
高比例压缩:Linux 中的压缩命令与技巧
文章目录 高比例压缩:Linux 中的压缩命令与技巧1. 压缩格式的选择2. gzip 命令示例:压缩文件示例:解压文件 3. bzip2 命令示例:压缩文件示例:解压文件 4. xz 命令示例:压缩文件示例:解压文件 5.…...
LabVIEW软件Bug的定义与修改
在LabVIEW软件开发过程中,bug(程序错误或缺陷)指的是程序中导致不符合预期行为的任何问题。Bug可能是由于编码错误、逻辑漏洞、硬件兼容性问题、系统资源限制等因素引起的。它可能会导致程序崩溃、功能无法正常执行或输出结果不符合预期。理解…...
基于Springboot + vue实现的办公用品管理系统
🥂(❁◡❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞 💖📕🎉🔥 支持我:点赞👍收藏⭐️留言📝欢迎留言讨论 🔥🔥&…...
B+树的原理及实现
文章目录 B树的原理及实现一、引言二、B树的特性1、结构特点2、节点类型3、阶数 三、B树的Java实现1、节点实现2、B树操作2.1、搜索2.2、插入2.3、删除2.4、遍历 3、B树的Java实现示例 四、总结 B树的原理及实现 一、引言 B树是一种基于B树的树形数据结构,它在数据…...
(四)结合代码初步理解帧缓存(Frame Buffer)概念
帧缓存(Framebuffer)是图形渲染管线中的一个非常重要的概念,它用于存储渲染过程中产生的像素数据,并最终输出到显示器上。简单来说,帧缓存就是计算机图形中的“临时画布”,它储存渲染操作生成的图像数据&am…...
python注意事项:range遍历越索引现象、列表边遍历边修改出现的问题
文章目录 前言一、range遍历越索引现象QS1:遍历range(2,2)会发生什么?不会报错,但是也不会遍历到任何内容QS1:遍历range(3,2)会发生什么?不会报错,但是也不会遍历到任何内容 二、列表边遍历边修改注意事项(Java的List系…...
【C++】模板与泛型编程(三):重载与模板
16.3 重载与模板 函数模板可以被另一个模板或一个普通分模板函数重载。与往常一样,名字相同的函数必须具有不同数量或类型的参数(这样才可以完成重载)。 如果设计模板,则函数的匹配规则与普通函数的重载有所不同,具体…...
JavaScript字符串拓展:实用方法与示例全解析
一、引言:为什么要学习 JS 字符串拓展 在前端开发的世界里,JavaScript 如同基石般支撑着网页的交互与动态呈现。而字符串作为我们日常操作中最频繁接触的数据类型之一,其原生方法在面对复杂多变的业务需求时,有时难免显得捉襟见肘…...
基于html5实现音乐录音播放动画源码
源码介绍 基于html5实现音乐录音播放动画源码是一款类似Shazam的UI,点击按钮后,会变成为一个监听按钮。旁边会有音符飞入这个监听按钮,最后转换成一个音乐播放器。 效果预览 源码获取 基于html5实现音乐录音播放动画源码...
初学stm32 --- ADC模拟/数字转换器工作原理
目录 常见的ADC类型 并联比较型工作示意图 逐次逼近型工作示意图 ADC的特性参数 STM32各系列ADC的主要特性 ADC框图简介 参考电压/模拟部分电压 输入通道( F1为例) 转换序列(F1为例) 规则组和注入组执行优先级对比 规则…...
导航技术的分类
导航技术可以根据不同的分类标准进行划分,以下是从不同角度对导航技术的分类: 一、按导航信息获取原理分类 无线电导航:利用无线电波的传播特性来测定运动体的位置、速度等导航参数。常见的无线电导航系统包括罗兰-C、奥米加、台卡等。卫星…...
C++语言的函数实现
C语言中的函数实现详解 C是一种强大的编程语言,广泛应用于系统软件、游戏开发、实时物理模拟等多个领域。在C中,函数是组织和重用代码的重要工具。本文将深入探讨C中的函数实现,包括函数的定义、调用、重载、递归、作用域、内联函数和模板函…...
每日一题-两个链表的第一个公共结点
文章目录 两个链表的第一个公共结点问题描述示例说明示例 1示例 2 方法及实现方法描述代码实现 复杂度分析示例运行过程示例 1示例 2 总结备注 两个链表的第一个公共结点 问题描述 给定两个无环的单向链表,找到它们的第一个公共节点。如果没有公共节点,…...
细说STM32F407单片机以轮询方式读写外部SRAM的方法
目录 一、实例的功能 二、工程配置 1、KEYLED 2、时钟、DEBUG、USART6、NVIC、GPIO、CodeGenerator 3、FSMC (1) 模式设置 (2) Bank 1子区3参数设置 1) NOR/PSRAM control组,子区控制参数 2) NOR/PSRAM timi…...
【3】安装cyclictest和iperf
cyclictest 安装比较简单,我是直接使用命令行: apt-get install rt-tests 随后,运行 sudo cyclictest 但是这个程序会一直运行,直到你手动中断程序,而且每秒生成一行输出也很烦人,所以可以选择把结果…...
C语言将点分十进制的IP字符串转成4个整数
最近在做lldp的snmp返回值时需要做这样的转换处理:C语言将点分十进制的IP字符串转成4个整数。 这里用两种方式: sscanf格式化处理用 inet_aton函数将ip字符串转成32位的整形,然后再根据bit转成对应的4个整数。 man命令可以确认下sscanf和i…...
go语言学习 笔记 1(变量,语法,数据类型)
1,包管理 一个文件夹可以称为一个包 在一个包里面可以创建多个文件 包中可以创建包 同一个包内的同一级的包的名字要相同 如:包a中的包b.包b中的包得是同一个package,a中和包b同级的包名字也得是一个名字 必须要有一个main包,入口,就像是c必须有一个main函数 如果没有mai…...
无网络时自动切换备用网络环境
目录 背景目标为什么需要做自动网络切换网络切换手段 网络环境实现思路和代码部署脚本开机自动执行附录连接两个网络时的路由问题 背景 目标 学校实验室有两个网络环境,我电脑使用网线连接稳定但低速的网络A,使用WiFi连接高速但不稳定的网络B。因此&am…...
电脑32位和64位之区别(Difference between 32-Bit and 64 Bit Computers)
电脑32位和64位之区别 很多小伙伴还不知道电脑32位和64位是什么意思,今天小编就来普及一下。 32位和64位是指电脑处理器(CPU)和操作系统的架构,决定了电脑如何处理数据、存储信息、运行程序等。 32位和64位是指电脑系统中每个处…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...
