嘉兴市南湖区城乡规划建设局网站,企业云,韩国网站域名,北京seo营销培训前言#xff1a;本文为手把手教学NRF24L01 2.4G通讯模块的驱动实验#xff0c;本教程的 MCU 采用STM32F103ZET6与STM32F103C8T6#xff0c;彼此进行互相通讯。通过 CubeMX 软件配置 SPI 协议驱动NRF24L01 2.4G通讯模块#xff08;HAL库#xff09;。NRF24L01 2.4G是嵌入式…前言本文为手把手教学NRF24L01 2.4G通讯模块的驱动实验本教程的 MCU 采用STM32F103ZET6与STM32F103C8T6彼此进行互相通讯。通过 CubeMX 软件配置 SPI 协议驱动NRF24L01 2.4G通讯模块HAL库。NRF24L01 2.4G是嵌入式较为常见的模块希望这篇博文能给读者朋友的工程项目给予些许帮助。文末代码开源
硬件设备STM32F103ZET6STM32F103C8T6NRF24L01 2.4G(2个)DHT11OLED
硬件实物图 效果图 引脚连接 NRF24L01 2.4G引脚ZET6 CSN -- PB3 CE -- PB4 IRQ -- PB5 SCK -- PA5 MISO -- PA6 MOSI -- PA7 Vcc -- 3.3v GND -- GND DHT11引脚ZET6 PA1 -- DATA Vcc -- 3.3v GND -- GND 0.96寸OLED引脚C8T6 SDA -- PB7 SCL -- PB6 Vcc -- 3.3v GND -- GND 一、NRF24L01 2.4G模块简介
NRF24L01 是北欧芯片巨头 Nordic 公司生产的一款无线通信芯片可以工作在免费开放的 2.4GHz 频段通信速率可以达到最高 2Mbps。采用 FSK 调制内部集成 Nordic 自己的 Enhanced Short Burst 协议可以实现 点对点 或者 1对6 的无线通信。 NRF24L01 2.4G 通讯模块采用 SPI 通信引脚如下 1、CSN芯片的片选线低电平芯片工作 2、SCK芯片控制的时钟线SPI的时钟 3、MISO芯片控制数据线SPI的MISO 4、MOSI芯片控制数据线SPI的MOSI 5、IRQ中断信号NRF24L01芯片收到数据、或者发送完数据等等一些情况会产生下降沿中断 6、CE芯片的模式控制线决定了芯片的工作状态。 NRF24L01 的引脚情况如下图所示上图仅供参考实际情况以手中模块为准。 NRF24L01 的涉及领域 无线鼠标、键盘、游戏机操纵杆无线门禁无线数据通讯无线数据通讯遥控装置遥感勘测智能运动设备工业传感器玩具。 二、模块详解
2.1 工作模式
NRF24L01 共有 6 种工作模式工作模式由 PWR_UP 寄存器、PRIM_RX 寄存器 和 CE 决定详见下表 收发模式 其中收发模式又有 Enhanced ShockBurstTM 收发模式和 ShockBurstTM 收发模式只有 Enhanced ShockBurstTM 收发模式支持自动 ACK 和自动重发。开启自动 ACK则默认选择 Enhanced ShockBurstTM 模式 待机模式 待机模式 I 在保证快速启动的同时减少系统平均消耗电流。在待机模式 I 下晶振正常工作。在待机模式 II 下部分时钟缓冲器处在工作模式。当发送端 TX FIFO 寄存器为空并且 CE 为高电平时进入待机模式 II。在待机模式期间寄存器配置字内容保持不变。 掉电模式 在掉电模式下nRF20L01 各功能关闭保持电流消耗最小。进入掉电模式后nRF24L01 停止工作但寄存器内容保持不变。掉电模式由寄存器 PWR_UP 位来控制。 ★在 Enhanced ShockBurstTM 收发模式下
1、NRF24L01 自动处理字头和 CRC 校验码。在接收数据时自动把字头和 CRC 校验码移去。在发送数据时自动加上字头和 CRC 校验码在发送模式下置 CE 为高至少 10us将使能发送过程。
2、在接收模式下最多可以接收 6 路不通的数据。每一个数据通道使用不同的地址但是共用相同的频道。也就是说 6 个不同的 NRF24L01 置为发送模式后可以与同一个设置为接收模式的 NRF24L01 进行通讯而设置为接收模式的 NRF24L01 可以对这 6 个发射端进行识别。 2.2 NRF24L01固件编程 NRF24L01 2.4G 的固件编程的基本思路如下 置 CSN 为低使能芯片配置芯片各个参数关键RX和TX配置配置参数在PowerDown状态中完成。如果是Tx模式装载接收端地址填充TxFIFO。配置完成以后通过CONFIG中的PWR_UP、PRIM_RX与CE参数确定24L01要切换到的状态。Tx ModePWR_UP1PRIM_RX0CE1 (保持超过10us就可以)Rx ModePWR_UP1PRIM_RX1CE1;将IRQ 接到外部中断输入引脚通过中断程序进行处理。IRQ引脚会在以下三种情况变低Tx FIFO发完并且收到ACK使能 ACK 情况下Rx FIFO收到数据达到最大重发次数 ★Tx 与Rx 的配置过程
本节只是叙述了采用 Enhanced ShockBurstTM 通信方式的Tx 与Rx 的配置及通信过程熟悉了NRF24L01 以后可以采用别的通信方式。
2.2.1 Tx 模式初始化过程 (1写Tx 节点的地址 TX_ADDR (2写Rx 节点的地址主要是为了使能Auto Ack RX_ADDR_P0 (3使能AUTO ACK EN_AA (4使能PIPE 0 EN_RXADDR (5配置自动重发次数 SETUP_RETR (6选择通信频率 RF_CH (7配置发射参数低噪放大器增益、发射功率、无线速率 RF_SETUP (8 ) 选择通道0 有效数据宽度 Rx_Pw_P0 (9配置24L01 的基本参数以及切换工作模式 CONFIG。 2.2.2 Rx 模式初始化过程 (1写Rx 节点的地址 RX_ADDR_P0 (2使能AUTO ACK EN_AA (3使能PIPE 0 EN_RXADDR (4选择通信频率 RF_CH (5) 选择通道0 有效数据宽度 Rx_Pw_P0 (6配置发射参数低噪放大器增益、发射功率、无线速率 RF_SETUP (7配置24L01 的基本参数以及切换工作模式 CONFIG。 三、项目详解
3.1 项目概述 实验目标实时进行 STM32F103ZET6 驱动 DHT11 进行环境测温然后借助 NRF24L01 2.4G 模块将温度信息传输给 STM32F103C8T6并在0.96寸OLED上进行显示。具体工程图如下 3.2 项目模块 本项目除了 NRF24L01 2.4G 模块外其余模块均为过往博客项目使用的模块这里仅给读者朋友提供参考博客。 博客地址http://t.csdn.cn/suwlV 四、CubeMX配置 ★CubeMX 项目工程给出 NRF24L01 2.4G 的配置过程2个板套件的 NRF24L01 2.4G 驱动方式一致以STM32F103ZET6的CubeMX配置为例。 1、RCC配置外部高速晶振精度更高——HSE 2、SYS配置Debug设置成Serial Wire否则可能导致芯片自锁 3、TIM2配置由上面可知DHT11的使用需要us级的延迟函数,HAL库自带只有ms的所以需要自己设计一个定时器 4、SPI1配置NRF24L01 2.4G模块采用SPI通讯Mode设置Full Duplex MasterBaud Rate不可超10MBits/s速度 5、UART1配置通过串口1将DHT11测得温度实时打印与NRF24L01通讯后的数据进行验证 6、GPIO配置PB3和PB4设置为output与PB5设置为inputPA1设置为output即DHT11数据输出引脚 7、时钟树配置 8、工程配置 五、代码与解析
5.1 STM32F103ZET6程序
STM32F103ZET6 通过 DHT11 模块测量温湿度之后将测得的温度通过 NRF24L01 2.4G 发送给同 “地址” 并处于 “接受” 状态的 NRF24L01 2.4G 模块之后再将接收到的温湿度数据显示在 OLED 上。 特别强调 根据无线通讯原理NRF24L01 发送数据给对端模块的时候要发射电磁波这本质上是一个像四周空间 广播 的过程。只要是有效距离范围内的任意一个处于接收状态的 NRF24L01 模块都能收到这个信号。辨别数据的前提就是 “地址”。 总结一定要保证接收端与发送端的地址一致并处于各自应该处于的模式下。 5.1.1 NRF24L01代码
根据上述解析可以得知需要将程序中的 TX_ADDRESS[TX_ADR_WIDTH] 和 RX_ADDRESS[RX_ADR_WIDTH] 变量保持一致。同时根据 NRF24L01 2.4G 模块的 Tx 和 Rx 的配置要求编写寄存器的配置代码。代码框架为HAL库的API接口函数编写具体参考如下 nrf24l01.h
#ifndef __nrf24L01_H
#define __nrf24L01_H
#include stdint.h/* 宏定义 --------------------------------------------------------------------*/#define NRF24L01_SPI_CS_ENABLE() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_3,GPIO_PIN_RESET) //PB3
#define NRF24L01_SPI_CS_DISABLE() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_3,GPIO_PIN_SET)#define NRF24L01_CE_LOW() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_RESET) //PB4
#define NRF24L01_CE_HIGH() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_4,GPIO_PIN_SET)#define NRF24L01_IRQ_PIN_READ() HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_5) //PB5// NRF24L01发送接收数据宽度定义
#define TX_ADR_WIDTH 5 //5字节的地址宽度
#define RX_ADR_WIDTH 5 //5字节的地址宽度
#define TX_PLOAD_WIDTH 32 //32字节的用户数据宽度
#define RX_PLOAD_WIDTH 32 //32字节的用户数据宽度//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*x86us
#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,不循环;/* 函数声明 ------------------------------------------------------------------*/
void NRF24L01_RX_Mode(void); //配置为接收模式
void NRF24L01_TX_Mode(void); //配置为发送模式
uint8_t NRF24L01_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uint8_ts);//写数据区
uint8_t NRF24L01_Read_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uint8_ts); //读数据区
uint8_t NRF24L01_Read_Reg(uint8_t reg); //读寄存器
uint8_t NRF24L01_Write_Reg(uint8_t reg, uint8_t value); //写寄存器
uint8_t NRF24L01_Check(void); //检查24L01是否存在
uint8_t NRF24L01_TxPacket(uint8_t *txbuf); //发送一个包的数据
uint8_t NRF24L01_RxPacket(uint8_t *rxbuf); //接收一个包的数据
void NRF_LowPower_Mode(void); #endif
nrf24l01.c
#include nrf24L01.h
#include spi.hconst uint8_t TX_ADDRESS[TX_ADR_WIDTH]{0x34,0x43,0x10,0x10,0x01}; //发送地址
const uint8_t RX_ADDRESS[RX_ADR_WIDTH]{0x34,0x43,0x10,0x10,0x01}; //接收地址/*** 函数功能: 往串行Flash读取写入一个字节数据并接收一个字节数据* 输入参数: byte待发送数据* 返 回 值: uint8_t接收到的数据* 说 明无*/
uint8_t SPIx_ReadWriteByte(SPI_HandleTypeDef* hspi,uint8_t byte)
{uint8_t d_read,d_sendbyte;if(HAL_SPI_TransmitReceive(hspi,d_send,d_read,1,0xFF)!HAL_OK){d_read0xFF;}return d_read;
}/*** 函数功能: 检测24L01是否存在* 输入参数: 无* 返 回 值: 0成功;1失败* 说 明无 */
uint8_t NRF24L01_Check(void)
{uint8_t buf[5]{0XA5,0XA5,0XA5,0XA5,0XA5};uint8_t i;NRF24L01_Write_Buf(NRF_WRITE_REGTX_ADDR,buf,5);//写入5个字节的地址. NRF24L01_Read_Buf(TX_ADDR,buf,5); //读出写入的地址 for(i0;i5;i)if(buf[i]!0XA5)break; if(i!5)return 1; //检测24L01错误 return 0; //检测到24L01
} /*** 函数功能: SPI写寄存器* 输入参数: 无* 返 回 值: 无* 说 明reg:指定寄存器地址* */
uint8_t NRF24L01_Write_Reg(uint8_t reg,uint8_t value)
{uint8_t status; NRF24L01_SPI_CS_ENABLE(); //使能SPI传输status SPIx_ReadWriteByte(hspi1,reg); //发送寄存器号 SPIx_ReadWriteByte(hspi1,value); //写入寄存器的值NRF24L01_SPI_CS_DISABLE(); //禁止SPI传输 return(status); //返回状态值
}/*** 函数功能: 读取SPI寄存器值* 输入参数: 无* 返 回 值: 无* 说 明reg:要读的寄存器* */
uint8_t NRF24L01_Read_Reg(uint8_t reg)
{uint8_t reg_val; NRF24L01_SPI_CS_ENABLE(); //使能SPI传输 SPIx_ReadWriteByte(hspi1,reg); //发送寄存器号reg_valSPIx_ReadWriteByte(hspi1,0XFF);//读取寄存器内容NRF24L01_SPI_CS_DISABLE(); //禁止SPI传输 return(reg_val); //返回状态值
} /*** 函数功能: 在指定位置读出指定长度的数据* 输入参数: 无* 返 回 值: 此次读到的状态寄存器值 * 说 明无* */
uint8_t NRF24L01_Read_Buf(uint8_t reg,uint8_t *pBuf,uint8_t len)
{uint8_t status,uint8_t_ctr; NRF24L01_SPI_CS_ENABLE(); //使能SPI传输statusSPIx_ReadWriteByte(hspi1,reg);//发送寄存器值(位置),并读取状态值 for(uint8_t_ctr0;uint8_t_ctrlen;uint8_t_ctr){pBuf[uint8_t_ctr]SPIx_ReadWriteByte(hspi1,0XFF);//读出数据}NRF24L01_SPI_CS_DISABLE(); //关闭SPI传输return status; //返回读到的状态值
}/*** 函数功能: 在指定位置写指定长度的数据* 输入参数: 无* 返 回 值: 无* 说 明reg:寄存器(位置) *pBuf:数据指针 len:数据长度* */
uint8_t NRF24L01_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t len)
{uint8_t status,uint8_t_ctr; NRF24L01_SPI_CS_ENABLE(); //使能SPI传输status SPIx_ReadWriteByte(hspi1,reg);//发送寄存器值(位置),并读取状态值for(uint8_t_ctr0; uint8_t_ctrlen; uint8_t_ctr){SPIx_ReadWriteByte(hspi1,*pBuf); //写入数据 }NRF24L01_SPI_CS_DISABLE(); //关闭SPI传输return status; //返回读到的状态值
} /*** 函数功能: 启动NRF24L01发送一次数据* 输入参数: 无* 返 回 值: 发送完成状况* 说 明txbuf:待发送数据首地址* */
uint8_t NRF24L01_TxPacket(uint8_t *txbuf)
{uint8_t sta; NRF24L01_CE_LOW();NRF24L01_Write_Buf(WR_TX_PLOAD,txbuf,TX_PLOAD_WIDTH);//写数据到TX BUF 32个字节NRF24L01_CE_HIGH();//启动发送 while(NRF24L01_IRQ_PIN_READ()!0);//等待发送完成staNRF24L01_Read_Reg(STATUS); //读取状态寄存器的值 NRF24L01_Write_Reg(NRF_WRITE_REGSTATUS,sta); //清除TX_DS或MAX_RT中断标志if(staMAX_TX)//达到最大重发次数{NRF24L01_Write_Reg(FLUSH_TX,0xff);//清除TX FIFO寄存器 return MAX_TX; }if(staTX_OK)//发送完成{return TX_OK;}return 0xff;//其他原因发送失败
}/*** 函数功能:启动NRF24L01接收一次数据* 输入参数: 无* 返 回 值: 无* 说 明无* */
uint8_t NRF24L01_RxPacket(uint8_t *rxbuf)
{uint8_t sta; staNRF24L01_Read_Reg(STATUS); //读取状态寄存器的值 NRF24L01_Write_Reg(NRF_WRITE_REGSTATUS,sta); //清除TX_DS或MAX_RT中断标志if(staRX_OK)//接收到数据{NRF24L01_Read_Buf(RD_RX_PLOAD,rxbuf,RX_PLOAD_WIDTH);//读取数据NRF24L01_Write_Reg(FLUSH_RX,0xff);//清除RX FIFO寄存器 return 0; } return 1;//没收到任何数据
} /*** 函数功能: 该函数初始化NRF24L01到RX模式* 输入参数: 无* 返 回 值: 无* 说 明无* */
void NRF24L01_RX_Mode(void)
{NRF24L01_CE_LOW(); NRF24L01_Write_Reg(NRF_WRITE_REGCONFIG, 0x0F);//配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC NRF24L01_Write_Reg(NRF_WRITE_REGEN_AA,0x01); //使能通道0的自动应答 NRF24L01_Write_Reg(NRF_WRITE_REGEN_RXADDR,0x01);//使能通道0的接收地址 NRF24L01_Write_Reg(NRF_WRITE_REGRF_CH,40); //设置RF通信频率 NRF24L01_Write_Reg(NRF_WRITE_REGRF_SETUP,0x0f);//设置TX发射参数,0db增益,2Mbps,低噪声增益开启 NRF24L01_Write_Reg(NRF_WRITE_REGRX_PW_P0,RX_PLOAD_WIDTH);//选择通道0的有效数据宽度 NRF24L01_Write_Buf(NRF_WRITE_REGRX_ADDR_P0,(uint8_t*)RX_ADDRESS,RX_ADR_WIDTH);//写RX节点地址NRF24L01_CE_HIGH(); //CE为高,进入接收模式 HAL_Delay(1);
} /*** 函数功能: 该函数初始化NRF24L01到TX模式* 输入参数: 无* 返 回 值: 无* 说 明无* */
void NRF24L01_TX_Mode(void)
{ NRF24L01_CE_LOW(); NRF24L01_Write_Buf(NRF_WRITE_REGTX_ADDR,(uint8_t*)TX_ADDRESS,TX_ADR_WIDTH);//写TX节点地址 NRF24L01_Write_Buf(NRF_WRITE_REGRX_ADDR_P0,(uint8_t*)RX_ADDRESS,RX_ADR_WIDTH); //设置TX节点地址,主要为了使能ACK NRF24L01_Write_Reg(NRF_WRITE_REGEN_AA,0x01); //使能通道0的自动应答 NRF24L01_Write_Reg(NRF_WRITE_REGEN_RXADDR,0x01); //使能通道0的接收地址 NRF24L01_Write_Reg(NRF_WRITE_REGSETUP_RETR,0xff);//设置自动重发间隔时间:4000us 86us;最大自动重发次数:15次NRF24L01_Write_Reg(NRF_WRITE_REGRF_CH,40); //设置RF通道为40NRF24L01_Write_Reg(NRF_WRITE_REGRF_SETUP,0x0f); //设置TX发射参数,0db增益,2Mbps,低噪声增益开启 NRF24L01_Write_Reg(NRF_WRITE_REGCONFIG,0x0e); //配置基本工作模式的参数;PWR_UP,EN_CRC,16BIT_CRC,接收模式,开启所有中断NRF24L01_CE_HIGH();//CE为高,10us后启动发送HAL_Delay(1);
}/*** 函数功能: 该函数NRF24L01进入低功耗模式* 输入参数: 无* 返 回 值: 无* 说 明无* */
void NRF_LowPower_Mode(void)
{NRF24L01_CE_LOW(); NRF24L01_Write_Reg(NRF_WRITE_REGCONFIG, 0x00); //配置工作模式:掉电模式
}
5.1.2 DHT11代码
dht11.h
#ifndef __DHT11_H__
#define __DHT11_H__/* Private includes ----------------------------------------------------------*/
#include main.h
#include gpio.h
#include stdio.h
#include tim.h
#include stm32f1xx.h/* Private define ------------------------------------------------------------*/
#define DHT11_PIN_SET HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET) // 拉高GPIO
#define DHT11_PIN_RESET HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET) // 拉低GPIO
#define DHT11_READ_IO HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) // DHT11 GPIO数据引脚#define DLY_TIM_Handle (htim2) // 定时器/* Private variables ---------------------------------------------------------*//* Private typedef -----------------------------------------------------------*//* Private function prototypes -----------------------------------------------*/
void DHT11(void);
void DHT11_START(void);
unsigned char DHT11_READ_BIT(void);
unsigned char DHT11_READ_BYTE(void);
unsigned char DHT11_READ_DATA(void);
unsigned char DHT11_Check(void);
static void DHT11_GPIO_MODE_SET(uint8_t mode);
void Tims_delay_us(uint16_t nus);
void delay_us(uint16_t nus);#endif
dht11.c
#include dht11.h
#include stdio.h
#include nrf24l01.h/*** brief 温湿度传感器主函数* param void* retval None*/
void DHT11(void)
{DHT11_READ_DATA();HAL_Delay(50); // 预设一定缓冲
}/*** brief 温湿度传感器启动信号发送* param void* retval None*/
void DHT11_START(void)
{DHT11_GPIO_MODE_SET(0); // 主机设置为输出模式DHT11_PIN_RESET; // 主机拉低电平HAL_Delay(20); // 主机等待 18 ms 30DHT11_GPIO_MODE_SET(1); // 主机设置为输入模式等待DHT11答应
} // 因为设置了上拉输入GPIO - 1/*** brief 读取一位数据 1bit* param void* retval 0/1*/
unsigned char DHT11_READ_BIT(void)
{while(!DHT11_READ_IO); // 过度数据的低电平 Tims_delay_us(40); // 过度数据的高电平if(DHT11_READ_IO) // 此时如果还为高电平则数据为 1{while(DHT11_READ_IO); // 过度数据的高电平return 1;} else // 若此时为低则为 0{return 0;}
}/*** brief 读取一个字节数据 1byte / 8bit* param void* retval temp*/
unsigned char DHT11_READ_BYTE(void)
{uint8_t i,temp 0; // 暂时存储数据for(i0; i8 ;i){temp 1; if(DHT11_READ_BIT()) // 1byte - 8bit{temp | 1; // 0000 0001}}return temp;
}/*** brief 读取温湿度传感器数据 5byte / 40bit* param void* retval 0/1/2*/
unsigned char DHT11_READ_DATA(void)
{uint8_t i;uint8_t data[5] {0};DHT11_START(); // 主机发送启动信号if(DHT11_Check()) // 如果DHT11应答 { while(!DHT11_READ_IO); // 过度DHT11答复信号的低电平while(DHT11_READ_IO); // 过度DHT11答复信号的高电平for(i0; i5; i){ data[i] DHT11_READ_BYTE(); // 读取 5byte}if(data[0] data[1] data[2] data[3] data[4]){//显示温度printf(TEMP:%d\r\n,data[2]);//显示湿度printf(HUM:%d\r\n,data[0]);NRF24L01_TxPacket(data);return 1; // 数据校验通过}else{return 0; // 数据校验失败}}else // 如果DHT11不应答{return 2;}
}/*** brief 检测温湿度传感器是否存在(检测DHT11的应答信号)* param void* retval 0/1*/
unsigned char DHT11_Check(void)
{Tims_delay_us(40);if(DHT11_READ_IO 0) // 检测到DHT11应答{return 1;}else // 检测到DHT11不应答{return 0;}
}/*** brief 设置引脚模式* param mode: 0-out, 1-in* retval None*/
static void DHT11_GPIO_MODE_SET(uint8_t mode)
{if(mode){/* 输入 */GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.Pin GPIO_PIN_1; // 9号引脚GPIO_InitStruct.Mode GPIO_MODE_INPUT; // 输入模式GPIO_InitStruct.Pull GPIO_PULLUP; // 上拉输入HAL_GPIO_Init(GPIOA, GPIO_InitStruct);}else {/* 输出 */GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.Pin GPIO_PIN_1; // 9号引脚GPIO_InitStructure.Mode GPIO_MODE_OUTPUT_PP; // Push Pull 推挽输出模式GPIO_InitStructure.Pull GPIO_PULLUP; // 上拉输出GPIO_InitStructure.Speed GPIO_SPEED_FREQ_HIGH; // 高速HAL_GPIO_Init(GPIOA,GPIO_InitStructure);}
}/*** brief 定时器延时usPrescaler - 32-1* param us: 65535* retval None*/
void Tims_delay_us(uint16_t nus)
{__HAL_TIM_SET_COUNTER(DLY_TIM_Handle, 0);__HAL_TIM_ENABLE(DLY_TIM_Handle);while (__HAL_TIM_GET_COUNTER(DLY_TIM_Handle) nus){}__HAL_TIM_DISABLE(DLY_TIM_Handle);
}
上述代码核心如下代码 if(data[0] data[1] data[2] data[3] data[4])
{//显示温度printf(TEMP:%d\r\n,data[2]);//显示湿度printf(HUM:%d\r\n,data[0]);NRF24L01_TxPacket(data);return 1; // 数据校验通过
} 当 DHT11 测得数据满足 DHT11 的数据验证格式则串口打印出温湿度信息并用 NRF24L01_TxPacket(data) 函数将温湿度信息进行发送广播。
5.2 STM32F103C8T6程序
STM32F103C8T6 通过设置 NRF24L01 2.4G 模块为接收模式并且保证收发地址一致来接收发送过来的温湿度数据。将接收到的温湿度数据通过 OLED 屏幕进行显示。
5.2.1 NRF24L01代码
nrf24l01.h
#ifndef __nrf24L01_H
#define __nrf24L01_H
#include stdint.h/* 宏定义 --------------------------------------------------------------------*/#define NRF24L01_SPI_CS_ENABLE() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET) //PB0
#define NRF24L01_SPI_CS_DISABLE() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET)#define NRF24L01_CE_LOW() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET) //PB1
#define NRF24L01_CE_HIGH() HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET)#define NRF24L01_IRQ_PIN_READ() HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_10) //PB10// NRF24L01发送接收数据宽度定义
#define TX_ADR_WIDTH 5 //5字节的地址宽度
#define RX_ADR_WIDTH 5 //5字节的地址宽度
#define TX_PLOAD_WIDTH 32 //32字节的用户数据宽度
#define RX_PLOAD_WIDTH 32 //32字节的用户数据宽度//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*x86us
#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,不循环;/* 函数声明 ------------------------------------------------------------------*/
void NRF24L01_RX_Mode(void); //配置为接收模式
void NRF24L01_TX_Mode(void); //配置为发送模式
uint8_t NRF24L01_Write_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uint8_ts);//写数据区
uint8_t NRF24L01_Read_Buf(uint8_t reg, uint8_t *pBuf, uint8_t uint8_ts); //读数据区
uint8_t NRF24L01_Read_Reg(uint8_t reg); //读寄存器
uint8_t NRF24L01_Write_Reg(uint8_t reg, uint8_t value); //写寄存器
uint8_t NRF24L01_Check(void); //检查24L01是否存在
uint8_t NRF24L01_TxPacket(uint8_t *txbuf); //发送一个包的数据
uint8_t NRF24L01_RxPacket(uint8_t *rxbuf); //接收一个包的数据
void NRF_LowPower_Mode(void); #endif
nrf24l01.c同上STM32F103ZET6的nrf24l01.c
5.2.2 OLED代码 OLED参考博客http://t.csdn.cn/heTcD 5.3 主程序
主函数中分别通过 NRF24L01_TX_Mode() 和 NRF24L01_RX_Mode() 将各自设置成发送和接收模式然后通过 NRF24L01_TxPacket(data) 和 NRF24L01_RxPacket(Receive) 函数分别发送和接收 data 数组data数组和Receive数组一致。
STM32F103ZET6的主程序
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_SPI1_Init();MX_TIM2_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */while(NRF24L01_Check()){printf(检测不到NRF24L01无线模块请检查硬件连接\r\n); HAL_Delay(1000);}printf(NRF24L01无线模块硬件连接正常\r\n);NRF24L01_TX_Mode();//设置为发送模式/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */DHT11(); /* DHT11()程序中包含了NRF24L01的数据发送代码 */ }/* USER CODE END 3 */
}STM32F103C8T6的主程序
int main(void)
{/* USER CODE BEGIN 1 */uint8_t Receive[6];/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_I2C1_Init();MX_SPI1_Init();/* USER CODE BEGIN 2 */OLED_Init();OLED_CLS();while(NRF24L01_Check()){printf(硬件查寻不到NRF24L01无线模块\n); HAL_Delay(1000);}printf(NRF24L01无线模块硬件连接正常\n); NRF24L01_RX_Mode();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */if(NRF24L01_RxPacket(Receive)0){Receive[5]0; OLED_ShowStr(0,0,Temperature:,2);OLED_ShowStr(0,2,Humidity:,2);OLED_ShowNum(100,0,Receive[2],2,16);OLED_ShowNum(75,2,Receive[0],2,16); }HAL_Delay(500);}/* USER CODE END 3 */
}
5.4 补充程序 上述是通过NRF传输数据其实也可以通过 NRF24L01 传输字符串等提供如下代码示例 5.4.1 发送端main.c部分代码
//---------------------------添加头文件------------------------------
#include stdio.h
#include nrf24L01.h//---------------------------main函数部分----------------------------
int main(void)
{/* USER CODE BEGIN 1 */
uint8_t Send_Out[33]本段数据来自NRF24L01发送端 ; //要发送的内容(单次最多32个字节)/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_SPI1_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */while(NRF24L01_Check()){printf(检测不到NRF24L01无线模块请检查硬件连接\r\n); HAL_Delay(1000);}printf(NRF24L01无线模块硬件连接正常\r\n);NRF24L01_TX_Mode();//设置为发送模式printf(进入数据发送模式每1s发送一次数据\r\n);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){HAL_Delay(1000);if(NRF24L01_TxPacket(Send_Out)TX_OK){printf(NRF24L01无线模块数据发送成功%s\r\n,Send_Out);}else{printf(NRF24L01无线模块数据发送失败\r\n);} /* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
5.4.2 接受端main.c部分代码
//---------------------------添加头文件-------------------------------
#include stdio.h
#include nrf24L01.h//---------------------------main函数部分-------------------------------
int main(void)
{/* USER CODE BEGIN 1 */uint8_t Receive[33];/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_SPI1_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */while(NRF24L01_Check()){printf(硬件查寻不到NRF24L01无线模块\n); HAL_Delay(1000);}printf(NRF24L01无线模块硬件连接正常\n);NRF24L01_RX_Mode();printf(进入数据接收模式\n);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){if(NRF24L01_RxPacket(Receive)0){Receive[32]0;//加入字符串结束符 printf(NRF24L01无线模块数据接收成功%s\r\n,Receive);}HAL_Delay(500);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
六、实验效果 NRF24L01 七、代码开源
代码地址基于STM32的NRF24L012.4G通讯模块的驱动实验代码HAL库资源-CSDN文库
如果积分不够的朋友点波关注评论区留下邮箱作者无偿提供源码和后续问题解答。求求啦关注一波吧