基于STM32F103C8T6实现用2.4G模块(NRF2401)传输室内温湿度并显示OLED

基于STM32F103C8T6实现用2.4G模块(NRF2401)传输室内温湿度并显示OLED

对于2.4G模块上的应用,很多是将这个这个模块上的应用实现在51单片机上,我问了很多相关文章的博主,他们从最开始的程序几乎都是从51单片机上,进行开发,感谢这些赶路人,我们这些从事快速开发的爱好者做好的道路。
感谢999994博主绽放中的青春博主

好了,现在就进入主食阶段

一:对2.4G模块(NRF2401)的介绍

作为我们在开发应用的短距离传输信息的选择模块之一,作为在项目上实现互传的模块,可实现半双工和全双工,可以实现点对点或是 1 对 6 的无线通信。对于 NRF24L01 的固件编程工作主要是参照NRF24L01 的状态机。主要有以下几个状态:
Power Down Mode:掉电模式
Tx Mode:发射模式
Rx Mode:接收模式
Standby-1Mode:待机 1 模式
Standby-2 Mode:待机 2 模式

这里配上接线管脚
NRF2401管脚图示

PA1NRF- CSN
PA5-SPI1-SCKNRF -SCK
PA6-SPI1-MISONRF -MISO
PA7-SPI1-MOSINRF -MOSI
PA3NRF-IRQ
PA2NRF-CE

但是在我应用的项目上我没有使用全双工,项目设计是一个板子(STM32最小系统,以下皆是)上的一个作为主机,一个作为从机,其中主机是负责采集室内的温度,从机是为了负责接收主机主机上的数据,显示在OLED上面,同时为了让自己的项目更好的检测,我在从机上也实现了对主机数据的发送,主句实现接收。
主机上发送:

printf("\r\n 主机端 进入自应答发送模式\r\n"); 
	NRF_TX_Mode();

	txbuf[0]=temp;			
	txbuf[1]=humi;//将检测到的温度和湿度赋值给这个非空数组中,实现数据包的发送
	status = NRF_Tx_Dat(txbuf);//

		  switch(status)
		  	{
		  	  case MAX_RT:
			 	 	printf("\r\n 主机端 没接收到应答信号,发送次数超过限定值,发送失败。 \r\n");
				 break;
	
			  case ERROR:
			  	 	printf("\r\n 未知原因导致发送失败。 \r\n");
				 break;
	
			  case TX_DS:
			  		printf("\r\n 主机端 接收到 从机端 的应答信号,发送成功! \r\n");	 		
				 break;  								
		  	}			   	
	

从机上接收:

printf("\r\n 从机端 进入接收模式\r\n"); 
	NRF_RX_Mode();     //这边的是在收
	 
	/*等待接收数据*/
	status = NRF_Rx_Dat(rxbuf);

	   /*判断接收状态*/
		 if(status == RX_DR)
	{
		printf("\r\n 从机端 接收到 主机端 发送的数据 \r\n");
	  for(i=0;i<2;i++)
				{	
					if(i%2==0)
					{
						printf("\r\n 温度的数据为:%d \r\n",rxbuf[i]);
						OLED_ShowNum(45,2,rxbuf[i],3,16);//显示温度的码值
						txbuf[i] =1;
			        }
			        else
			       {
						printf("\r\n 湿度的数据为:%d \r\n",rxbuf[i]);
					    OLED_ShowNum(45,4,rxbuf[i],3,16);//显示湿度的码值
						txbuf[i] =1;
			       }									
				}		
	}   

主机上接收从机数据:

printf("\r\n 主机端 进入接收模式。 \r\n");	
		NRF_RX_Mode();
		status = NRF_Rx_Dat(rxbuf);	
			
		switch(status)
			{
			 case RX_DR:
			 	for(i=0;i<4;i++)
				{					
					printf("\r\n 主机端 接收到 从机端 发送的数据为:%d \r\n",rxbuf[i]);
					txbuf[i] =rxbuf[i];
														
				}
				  break;
			
			 case ERROR:
				  	printf("\r\n 主机端 接收出错。   \r\n");
				  break;  		
			}
		

从机上发送给主机数据:

printf("\r\n 温度的数据为:%d \r\n",rxbuf[i]);
						OLED_ShowNum(45,2,rxbuf[i],3,16);//显示温度的码值
						txbuf[i] =1;//返回发送给主机上的值为1

txbuf[i]就是发送个主机上面的数据

printf("\r\n 湿度的数据为:%d \r\n",rxbuf[i]);
					    OLED_ShowNum(45,4,rxbuf[i],3,16);//显示湿度的码值
						txbuf[i] =1;//返回发送给主机上的值为1

二.温湿度传感器简介

DHT11是一款价格便宜,易于使用的温度湿度测量二合一传感器。它使用单根总线与单片机进行双向的串行数据传输,信号传输距离可达20米以上。非常适用于对精度和实时性要求不高的温湿度测量场合。DHT117温湿度传感器
接口设置:

DATAPB11

这个模块也是有相应的代码格式,我这里就提几个注意事项:
1.DHT11使用单一总线通信,即DATA引脚和单片机连接的线。总线总是处于空闲状态和通信状态这个2个状态之间。
2.DHT11上电后,要等待 约 1秒 以越过不稳定状态,在此期间不能发送任何指令。
3.DHT11属于低速传感器,两次通信请求之间的间隔时间不能太短,一般来说要不能低于1秒。
4.当前DHT11通信帧的小数部分默认都是0,厂商预留给以后实现。所以一般只读取整数值部分即可。校验和定义为:前4个Byte的总和的低8位。

三.OLED显示屏

我所用的OLED屏有白色,屏的大小为0.96寸,像素点为128*64,也称之为0.96OLED屏或者12864屏。通时我选择的是4脚,运用的协议是IIC协议,

void IIC_Start(void)
{

	OLED_SCLK_Set() ;
	OLED_SDIN_Set();
	OLED_SDIN_Clr();
	OLED_SCLK_Clr();
}

理解了OLED的显示方式,汉字取模也是很有必要的,我选择列行式的取模方式,设置为C51原则:
PLC取模软件

想要更加详细的理解OLED上面的知识我推荐这位博主的博文OLED详细解剖

下面是主机项目工程中的main.c文件:

#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
#include "DTH117.h"
#include "sys.h"
#include "SPI_NRF.h"
#include <stdarg.h>
#include <string.h>
#include <stdio.h>

u8 status;	//用于判断接收/发送状态
u8 txbuf[2];	 //发送缓冲
u8 rxbuf[4];//接收缓冲
int i=0;
/*
*读取温湿度传感器DHT11的值,并用串口打印出来
*/

void clock_init(void);

u8 temp = 0,humi = 0;


/**************************************************************************
函数名:int main(void)
参数说明:无
返回值:无
函数作用:主函数
***************************************************************************/
 int main(void)
 {	
	 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	SPI_NRF_Init();
	delay_init();
    DHT11_Init();	 
    clock_init();	
    usart_init(115200);		//初始化串口
	printf("\r\n 这是一个 NRF24L01 无线传输实验 \r\n");
	printf("\r\n 这是无线传输 主机端 的反馈信息\r\n");
	printf("\r\n   正在检测NRF与MCU是否正常连接。。。\r\n");
	
	 /*检测NRF模块与MCU的连接*/
   	status = NRF_Check(); 

	/*判断连接状态*/  
   if(status == SUCCESS)	   
   		 printf("\r\n      NRF与MCU连接成功!\r\n");  
   else	  
   	     printf("\r\n  NRF与MCU连接失败,请重新检查接线。\r\n"); 
	 
	 
	 while(1)
	 {	
	DHT11_Read_Data(&temp,&humi);
	printf("\r\n 主机端 进入自应答发送模式\r\n"); 
	NRF_TX_Mode();

	txbuf[0]=temp;			
	txbuf[1]=humi;//将检测到的温度和湿度赋值给这个非空数组中,实现数据包的发送
	status = NRF_Tx_Dat(txbuf);//这里是发送
		  switch(status)		  	{
		  	  case MAX_RT:
			 	 	printf("\r\n 主机端 没接收到应答信号,发送次数超过限定值,发送失败。 \r\n");
				 break;
	
			  case ERROR:
			  	 	printf("\r\n 未知原因导致发送失败。 \r\n");
				 break;
	
			  case TX_DS:
			  		printf("\r\n 主机端 接收到 从机端 的应答信号,发送成功! \r\n");	 		
				 break;  								
		  	}			   	
	
	 	printf("\r\n 主机端 进入接收模式。 \r\n");	
		NRF_RX_Mode();
		status = NRF_Rx_Dat(rxbuf);	
			
		switch(status)
			{
			 case RX_DR:
			 	for(i=0;i<4;i++)
				{					
					printf("\r\n 主机端 接收到 从机端 发送的数据为:%d \r\n",rxbuf[i]);
					txbuf[i] =rxbuf[i];
														
				}
				  break;
			
			 case ERROR:
				  	printf("\r\n 主机端 接收出错。   \r\n");
				  break;  		
			}
		
	 } 	  
	
 } 
/**************************************************************************
函数名:void clock_init(void)
参数说明:无
返回值:无
函数作用:开启高速外部时钟,
ADCCLK设置为12MHZ,	SYSCLK设置为72Mhz,PCLK1设置为36MHZ,PKLC2设置为72mhz
***************************************************************************/
 void clock_init(void)
 {
	RCC->CR = 0x1010000;
	RCC->CFGR = 0x1DC402;
}

这里从机项目的main.c文件:

#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
#include "oled.h"
#include "sys.h"
#include "SPI_NRF.h"
#include <stdarg.h>
#include <string.h>
#include <stdio.h>

u8 status;	//用于判断接收/发送状态
u8 txbuf[4];	 //发送缓冲
u8 rxbuf[4];//接收缓冲
u8 i;
/*
*读取温湿度传感器DHT11的值,并用串口打印出来
*/

void clock_init(void);
/**************************************************************************
函数名:int main(void)
参数说明:无
返回值:无
函数作用:主函数
***************************************************************************/
 int main(void)
 {	
	 
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	SPI_NRF_Init();
	delay_init();
    OLED_Init();	 
    clock_init();	
    usart_init(115200);		//初始化串口
	 OLED_Clear();
	OLED_Display_On();
	OLED_ShowCHinese(40,0,0);
	OLED_ShowCHinese(60,0,1);
	OLED_ShowCHinese(80,0,2);
	OLED_ShowCHinese(100,0,3);
		 //
		 //显示各个参数的中文
    OLED_ShowCHinese(0,2,4);
	OLED_ShowCHinese(18,2,5);	 
	OLED_ShowString(35,2, ":",16);//显示温度中文
	
	OLED_ShowCHinese(0,4,6);
	OLED_ShowCHinese(18,4,7);	 
	OLED_ShowString(35,4, ":",16);//显示温度中文	
	printf("\r\n 这是一个 NRF24L01 无线传输实验 \r\n");
	printf("\r\n 这是无线传输 主机端 的反馈信息\r\n");
	printf("\r\n   正在检测NRF与MCU是否正常连接。。。\r\n");
	
	 /*检测NRF模块与MCU的连接*/
   	status = NRF_Check(); 

	/*判断连接状态*/  
   if(status == SUCCESS)	   
   		 printf("\r\n      NRF与MCU连接成功!\r\n");  
   else	  
   	     printf("\r\n  正在检测NRF与MCU是否正常连接。。。\r\n"); 
	 
	 
	 while(1)
	 {	
		 
	printf("\r\n 从机端 进入接收模式\r\n"); 
	NRF_RX_Mode();     //这边的是在收
	 
	/*等待接收数据*/
	status = NRF_Rx_Dat(rxbuf);

	   /*判断接收状态*/
		 if(status == RX_DR)
	{
		printf("\r\n 从机端 接收到 主机端 发送的数据 \r\n");
	  for(i=0;i<2;i++)
				{	
					if(i%2==0)
					{
						printf("\r\n 温度的数据为:%d \r\n",rxbuf[i]);
						OLED_ShowNum(45,2,rxbuf[i],3,16);//显示温度的码值
						txbuf[i] =1;//返回发送给主机上的值为1
			        }
			        else
			       {
						printf("\r\n 湿度的数据为:%d \r\n",rxbuf[i]);
					    OLED_ShowNum(45,4,rxbuf[i],3,16);//显示湿度的码值
						txbuf[i] =1;//返回发送给主机上的值为1
			       }					
					
				}	
	}   
	 
		printf("\r\n 从机端 进入自应答发送模式"); 
	  	NRF_TX_Mode();
		
		/*不断重发,直至发送成功*/	  
	 do
	   { 	  
		status = NRF_Tx_Dat(txbuf);
		   
		}while(status == MAX_RT);
	} 

}
/**************************************************************************
函数名:void clock_init(void)
参数说明:无
返回值:无
函数作用:开启高速外部时钟,
ADCCLK设置为12MHZ,	SYSCLK设置为72Mhz,PCLK1设置为36MHZ,PKLC2设置为72mhz
***************************************************************************/
 void clock_init(void)
 {
	RCC->CR = 0x1010000;
	RCC->CFGR = 0x1DC402;
}

成果展示:
双板接线图
串口打印
室内检测

项目总结:
总项目实现最大的难点是网上找到了相应的NRF2401的传输格式,只是这个格式并不能实现对你想要的数据的传输,所以这个实现的核心代码还是自己一遍又一遍根据模块实现的逻辑不断完善的,同时在数据传输的过程,OLED的格式我也是将实现数据的传输,让其在室温显示下更加的稳定,同时这个传输有个小小的瑕疵就是,设置了一个阈值,传输的阈值一到就会切换到主机变从机,从机变主机的状态,钻了全双工的空子(假的(✿◡‿◡))

未来可期
资料打包可用51黑子网
同时也可在本CSDN上下载:资料下载

备注:1. 本文章是个人总结,如有不足之处欢迎指正;
2.文章资料格式是网上找的也是从上面两个博主的,但是核心代码是自己写的,文章资料如有侵权,请联系我。

  • 12
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
### 回答1: stm32f103c8t6是一款单片机,具有丰富的外设资源和较强的处理能力。而2.4G NRF24L01无线模块可以实现低功耗、高速、长距离的无线通信。下面是一份包含发送和接收代码的示例: 发送端代码: 1. 首先,需要包含stm32f10x.h和nrf24l01.h头文件。 2. 初始化SPI接口和NRF24L01模块。 3. 设置NRF24L01工作模式为发送模式。 4. 通过SPI接口写入要发送的数据到NRF24L01发送缓冲区。 5. 发送数据。 接收端代码: 1. 首先,需要包含stm32f10x.h和nrf24l01.h头文件。 2. 初始化SPI接口和NRF24L01模块。 3. 设置NRF24L01工作模式为接收模式。 4. 在一个循环中检查NRF24L01接收缓冲区是否有数据可读。 5. 如果有数据可读,通过SPI接口从NRF24L01接收缓冲区读取数据。 在这份代码中,发送端和接收端的初始化和设置过程是相同的,只有在发送和接收数据的时候稍有不同。 这只是一份简单的示例代码,实际上,还需要进行一些参数配置,如频道选择、数据速率、发送接收地址等。同时,还需要根据具体需求对代码进行修改和完善。 希望以上回答能够满足您的需求,如果还有其他问题,欢迎继续提问! ### 回答2: 下面是一个基本的STM32F103C8T6和NRF24L01无线模块的通信代码示例,包含发送和接收代码。 发送端代码: #include "stm32f10x.h" #include "nrf24l01.h" void NRF24L01_CE(uint8_t level) { if(level) GPIO_SetBits(GPIOB, GPIO_Pin_0); // 设置CE端口为高 else GPIO_ResetBits(GPIOB, GPIO_Pin_0); // 设置CE端口为低 } uint8_t NRF24L01_RW(uint8_t data) { while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) ; // 等待发送完成 USART_SendData(USART1, data); // 发送数据 while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET) ; // 等待接收完成 return USART_ReceiveData(USART1); // 返回接收到的数据 } void NRF24L01_Delay_us(uint32_t n) { uint32_t i; for(i=0; i<n; i++); } int main() { uint8_t txData[32] = "Hello, NRF24L01!"; // 初始化USART1和PB0 USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); NRF24L01_Init(); while(1) { NRF24L01_SendData(txData, 32); // 发送数据 NRF24L01_Delay_us(1000); // 延时1ms } } 接收端代码: #include "stm32f10x.h" #include "nrf24l01.h" void NRF24L01_CE(uint8_t level) { if(level) GPIO_SetBits(GPIOB, GPIO_Pin_0); // 设置CE端口为高 else GPIO_ResetBits(GPIOB, GPIO_Pin_0); // 设置CE端口为低 } uint8_t NRF24L01_RW(uint8_t data) { while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) ; // 等待发送完成 USART_SendData(USART1, data); // 发送数据 while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET) ; // 等待接收完成 return USART_ReceiveData(USART1); // 返回接收到的数据 } void NRF24L01_Delay_us(uint32_t n) { uint32_t i; for(i=0; i<n; i++); } int main() { uint8_t rxData[32]; // 初始化USART1和PB0 USART_InitTypeDef USART_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); NRF24L01_Init(); while(1) { NRF24L01_ReceiveData(rxData, 32); // 接收数据 } } 请注意,以上仅为代码示例,具体使用时还要根据自己的需求进行修改和适配。还需要在此代码的基础上完成NRF24L01的初始化、数据发送和接收等函数的定义和实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值