基于STM32F103C8T6实现用2.4G模块(NRF2401)传输室内温湿度并显示OLED
对于2.4G模块上的应用,很多是将这个这个模块上的应用实现在51单片机上,我问了很多相关文章的博主,他们从最开始的程序几乎都是从51单片机上,进行开发,感谢这些赶路人,我们这些从事快速开发的爱好者做好的道路。
感谢[999994博主](http://www.51hei.com/bbs/space-uid-683358.html),[绽放中的青春博主](http://www.51hei.com/bbs/space-uid-290170.html)
好了,现在就进入主食阶段
## 一:对2.4G模块(NRF2401)的介绍
作为我们在开发应用的短距离传输信息的选择模块之一,作为在项目上实现互传的模块,可实现半双工和全双工,可以实现点对点或是 1 对 6 的无线通信。对于 NRF24L01 的固件编程工作主要是参照NRF24L01 的状态机。主要有以下几个状态:
Power Down Mode:掉电模式
Tx Mode:发射模式
Rx Mode:接收模式
Standby-1Mode:待机 1 模式
Standby-2 Mode:待机 2 模式
这里配上接线管脚
|PA1 |NRF- CSN |
|--|--|
| PA5-SPI1-SCK | NRF -SCK
| PA6-SPI1-MISO |NRF -MISO |
|PA7-SPI1-MOSI|NRF -MOSI|
| PA3 | NRF-IRQ
| PA2 |NRF-CE |
但是在我应用的项目上我没有使用全双工,项目设计是一个板子(STM32最小系统,以下皆是)上的一个作为主机,一个作为从机,其中主机是负责采集室内的温度,从机是为了负责接收主机主机上的数据,显示在OLED上面,同时为了让自己的项目更好的检测,我在从机上也实现了对主机数据的发送,主句实现接收。
主机上发送:
```cpp
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;
}
```
从机上接收:
```cpp
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;
}
}
}
```
主机上接收从机数据:
```cpp
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;
}
```
从机上发送给主机数据:
```cpp
printf("\r\n 温度的数据为:%d \r\n",rxbuf[ i]);
OLED_ShowNum(45,2,rxbuf[ i],3,16);//显示温度的码值
txbuf[ i] =1;//返回发送给主机上的值为1
```
txbuf[ i]就是发送个主机上面的数据
```cpp
printf("\r\n 湿度的数据为:%d \r\n",rxbuf[ i]);
OLED_ShowNum(45,4,rxbuf[ i],3,16);//显示湿度的码值
txbuf[ i] =1;//返回发送给主机上的值为1
```
## 二.温湿度传感器简介
DHT11是一款价格便宜,易于使用的温度湿度测量二合一传感器。它使用单根总线与单片机进行双向的串行数据传输,信号传输距离可达20米以上。非常适用于对精度和实时性要求不高的温湿度测量场合。
接口设置:
|DATA | PB11 |
|--|--|
这个模块也是有相应的代码格式,我这里就提几个注意事项:
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协议,
```cpp
void IIC_Start(void)
{
OLED_SCLK_Set() ;
OLED_SDIN_Set();
OLED_SDIN_Clr();
OLED_SCLK_Clr();
}
理解了OLED的显示方式,汉字取模也是很有必要的,我选择列行式的取模方式,设置为C51原则:
[PLC取模软件]
想要更加详细的理解OLED上面的知识我推荐这位博主的博文[OLED详细解剖](https://blog.csdn.net/u010858987 ... 1018.2226.3001.4187)
下面是主机项目工程中的main.c文件:
```cpp
#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文件:
```cpp
#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的格式我也是将实现数据的传输,让其在室温显示下更加的稳定,同时这个传输有个小小的瑕疵就是,设置了一个阈值,传输的阈值一到就会切换到主机变从机,从机变主机的状态,钻了全双工的空子(假的(????))
代码:
基于STM32F103C8T6实现用2.4G模块(NRF2401)传输室内温湿度.7z
(157.63 KB, 下载次数: 227)
|