基于freertos的温湿度蓝牙系统

news2024/9/29 11:40:30

前言:本项目主要是基于freertos的小项目,目的是为了巩固近期学习的知识,功能较简单,可自行扩充。

一、项目基本架构

项目基本功能:通过STM32单片机的freertos操作系统,将温湿度数据显示在oled屏幕上,并将数据通过蓝牙发送到手机上显示。

项目基本思路:使用freeRTOS创建两个任务,一个任务用于读取温湿度的数据,然后将读取的内容放入队列当中,另一个任务从队列中读取温湿度数据,然后显示在屏幕上并通过串口发给蓝牙。

项目调试难题:1、多任务进行时,需要考虑优先级的问题,防止优先级反转;2、创建队列时需要使用合适的队列深度和项目大小,防止队列满溢出;3、在任务中使用延时时,可能会导致高优先级任务阻塞,从而暂时执行就绪态的低优先级任务,而出现任务执行出现卡住错误。

DHT11电容式温湿度传感器模块 温湿度检测系统解决方案 温湿度传感器品牌 型号 报价

 

Buy HC 08 Bluetooth BLE Serial Port Module at Best Price | Robu

显示屏模块_0.96寸oled显示屏arduino12864oled模块iic0.96寸oled - 阿里巴巴

 

 

二、项目配置

时钟树

 

1、任务的创建(这里确保读取温湿度任务的优先级高于显示温湿度任务的优先级即可)

2、队列的创建(这里项目的大小我设置为uint64_t,因为温湿度以及校验位有5个字节,共40位bit数据

 3、IIC设置(这里MOde选择iic即可,下面涉及到主从模式,这里并没有用到)

 4、串口(串口选择串口一,MODE选择异步通信)

 5、gpio设置(细心会发现并没有DHT11的数据引脚,因为该引脚既需要当输入引脚又需要当输出引脚,这里需要我们手动在代码设置即可)

 三、代码实现

1、温湿度读取任务

/* USER CODE END Header_StartDht11Task */
void StartDht11Task(void const * argument)
{
  /* USER CODE BEGIN StartDht11Task */
	BaseType_t status;
	
  /* Infinite loop */
  for(;;)
  {
		//不断读取温湿度数据
		Read_Data_From_DHT();
		//这里需要加段延时等待数据读取稳定,否则可能会出现卡住的现象。
		osDelay(500);
		//将读取到的数据丢到队列里面去
		status = xQueueSend(Dht11QueueHandle,datas,0);
		//将该任务暂时挂起,防止该任务一直执行。
		osThreadSuspend(dht11TaskHandle);
		//判断写入队列状态
		if(status == pdTRUE){
			printf("写入成功\r\n");
		}else{
			printf("写入失败\r\n");
		}
		osDelay(1000);
	}
  /* USER CODE END StartDht11Task */
}

2、温湿度显示任务

void StartReadDht11Task(void const * argument)
{
  /* USER CODE BEGIN StartReadDht11Task */
	uint8_t dht[5] = {0};//队列数据缓冲区
	BaseType_t status;
	char message[32] = {'\0'};
	
  /* Infinite loop */
	
  for(;;)
  {
		//高优先级任务调用osDelay函数会使任务阻塞,从而执行低优先级任务,加入延时可保证队列的写入先执行。
		osDelay(1000);
		//将队列里面的数据读取出来
		status = xQueueReceive(Dht11QueueHandle,dht,100);
		//将接收温湿度数据的任务恢复,方便下次接收。
		osThreadResume(dht11TaskHandle);
    if(status == pdTRUE){
			printf("读取成功\r\n");
			printf("Temp: %d.%d ", dht[2], dht[3]);//温度
			printf("Humi: %d.%d\r\n", dht[0], dht[1]);//湿度
			memset(message,'\0',sizeof(message));
			sprintf(message,"%d.%d",dht[0], dht[1]);
			Oled_Show_Str(2,40,message);
			memset(message,'\0',sizeof(message));
			sprintf(message,"%d.%d",dht[2], dht[3]);
			Oled_Show_Str(1,40,message);
		}
		else{
			printf("读取失败\r\n");
		}
		
		osDelay(2000);
  }
		
  /* USER CODE END StartReadDht11Task */
}

3、DHT11

void delay_us(uint16_t cnt)
{
	uint8_t i;
	while(cnt)
	{
		for (i = 0; i < 10; i++)
	{
	}
		cnt--;
	}
}
//设置引脚模式
void DHT_GPIO_Init(uint32_t Mode)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	__HAL_RCC_GPIOB_CLK_ENABLE();//用外设需要打开对应时钟
	GPIO_InitStruct.Pin = GPIO_PIN_5;
	GPIO_InitStruct.Mode = Mode;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
//DHT11初始化
void DHT11_Start(void)
{
	DHT_GPIO_Init(GPIO_MODE_OUTPUT_PP);//配置为输出引脚
	DHT_HIGHT;
	DHT_LOW;
	HAL_Delay(30);
	DHT_HIGHT;
	
	DHT_GPIO_Init(GPIO_MODE_INPUT);//配置为输入引脚
	while(DHT_VALUE);
	while(!DHT_VALUE);
	while(DHT_VALUE);
}
//读取DHT11数据
void Read_Data_From_DHT(void)
{
	int i;//轮
	int j;//每一轮读多少次
	char tmp;
	char flag;
	DHT11_Start();//判断硬件是否存在
	DHT_GPIO_Init(GPIO_MODE_INPUT);配置为输入引脚
	for(i= 0;i < 5;i++){
		for(j=0;j<8;j++){
		while(!DHT_VALUE);//等待卡g点
		delay_us(40);
		if(DHT_VALUE == 1){
			flag = 1;
			while(DHT_VALUE);
		}else{
	  flag = 0;
	}
		tmp = tmp << 1;
		tmp |= flag;
	}
	datas[i] = tmp;
	}
}

4、串口重定向

int fputc(int ch, FILE *f){
	unsigned char temp[1] = {ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);
	return ch;
}

打开USB MicroLIB选项

5、OLED

#include "oled.h"
#include "oledfont.h"
#include "i2c.h"

int i =0;
char w1[16] = {0x10,0x60,0x02,0x8C,0x00,0x00,0xFE,0x92,0x92,0x92,0x92,0x92,0xFE,0x00,0x00,0x00};//温上半部分
char w2[16] = {0x04,0x04,0x7E,0x01,0x40,0x7E,0x42,0x42,0x7E,0x42,0x7E,0x42,0x42,0x7E,0x40,0x00};//温下办部分

char d1[16] = {0x00,0x00,0xFC,0x24,0x24,0x24,0xFC,0x25,0x26,0x24,0xFC,0x24,0x24,0x24,0x04,0x00};//度上半部分
char d2[16] = {0x40,0x30,0x8F,0x80,0x84,0x4C,0x55,0x25,0x25,0x25,0x55,0x4C,0x80,0x80,0x80,0x00};//度下办部分

char s1[16] = {0x10,0x60,0x02,0x8C,0x00,0xFE,0x92,0x92,0x92,0x92,0x92,0x92,0xFE,0x00,0x00,0x00};//湿上半部分
char s2[16] = {0x04,0x04,0x7E,0x01,0x44,0x48,0x50,0x7F,0x40,0x40,0x7F,0x50,0x48,0x44,0x40,0x00};//湿下办部分
char c1[8]  = {0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00};//冒号上半部分
char c2[8]  = {0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00};//冒号下半部分


void Oled_Write_Cmd(uint8_t dataCmd)//写命令
{
	HAL_I2C_Mem_Write(&hi2c1,0x78,0x00,I2C_MEMADD_SIZE_8BIT,
										&dataCmd,1,0xff);
}
void Oled_Write_Data(uint8_t dataData)//写数据
{
	HAL_I2C_Mem_Write(&hi2c1, 0x78, 0x40, I2C_MEMADD_SIZE_8BIT,
										&dataData, 1, 0xff);
}
//oled屏初始化
void Oled_Init(void){
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128
	Oled_Write_Cmd(0xA1);//set segment remap
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	Oled_Write_Cmd(0xAF);//--turn on oled panel
	
}
//温湿度初始化
void Oled_Meg_Slow(void)
{
	//page0
	Oled_Write_Cmd(0xB0);
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
	for(i=0;i<16;i++){
		Oled_Write_Data(w1[i]);
	}
	for(i=0;i<16;i++){
		Oled_Write_Data(d1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(c1[i]);
	}
	//page1
	Oled_Write_Cmd(0xB1);
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
	for(i=0;i<16;i++){
		Oled_Write_Data(w2[i]);
	}
	for(i=0;i<16;i++){
		Oled_Write_Data(d2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(c2[i]);
	}
	//page2
	Oled_Write_Cmd(0xB2);
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
	for(i=0;i<16;i++){
		Oled_Write_Data(s1[i]);
	}
	for(i=0;i<16;i++){
		Oled_Write_Data(d1[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(c1[i]);
	}
	//page3
	Oled_Write_Cmd(0xB3);
	Oled_Write_Cmd(0x00);
	Oled_Write_Cmd(0x10);
	for(i=0;i<16;i++){
		Oled_Write_Data(s2[i]);
	}
	for(i=0;i<16;i++){
		Oled_Write_Data(d2[i]);
	}
	for(i=0;i<8;i++){
		Oled_Write_Data(c2[i]);
	}

}
void Oled_Show_Char(char row,char col,char oledChar){ //row*2-2
    unsigned int  i;
    Oled_Write_Cmd(0xb0+(row*2-2));                           //page 0
    Oled_Write_Cmd(0x00+(col&0x0f));                          //low
    Oled_Write_Cmd(0x10+(col>>4));                            //high  
    for(i=((oledChar-32)*16);i<((oledChar-32)*16+8);i++){
        Oled_Write_Data(F8X16[i]);                            //写数据oledTable1
    }
 
    Oled_Write_Cmd(0xb0+(row*2-1));                           //page 1
    Oled_Write_Cmd(0x00+(col&0x0f));                          //low
    Oled_Write_Cmd(0x10+(col>>4));                            //high
    for(i=((oledChar-32)*16+8);i<((oledChar-32)*16+8+8);i++){
        Oled_Write_Data(F8X16[i]);                            //写数据oledTable1
    }       
}
 
 
/******************************************************************************/
// 函数名称:Oled_Show_Char 
// 输入参数:oledChar 
// 输出参数:无 
// 函数功能:OLED显示单个字符
/******************************************************************************/
//指定行列显示字符串
void Oled_Show_Str(char row,char col,char *str){
    while(*str!=0){
        Oled_Show_Char(row,col,*str);
        str++;
        col += 8;   
    }       
}
//清屏函数
void Oled_Screen_Clear(void){
	int i,n;
	Oled_Write_Cmd (0x20); //set memory addressing mode
	Oled_Write_Cmd (0x02); //page addressing mode
	for(i=0;i<8;i++){
		Oled_Write_Cmd(0xb0+i); //éè??ò3μ??·£¨0~7£?
		Oled_Write_Cmd(0x00); //éè????ê??????aáDμíμ??·
		Oled_Write_Cmd(0x10); //éè????ê??????aáD??μ??·
	for(n=0;n<128;n++)Oled_Write_Data(0x00);
	}
}

四、效果实现

 四、总结

最近也是断断续续的在学习freertos知识,有了STM32的HAL库开发经历及Linux的系统编程知识,对freertos的学习相对没有那么吃力,虽然对其中的任务、信号量、任务标志位的理解还是有点欠缺,但是以后会通过动手做项目来巩固加深理解。

对于该小项目、其实还有优化的空间、比如可以通过任务标志位来进行任务的通信或者信号量互斥量来进行同步互斥,也可以自行进行功能的拓展,比如检测数据加入烟雾传感器(ADC)、二氧化碳模块等传感器,通信模块换成wifi模块、4g模块,屏幕换成lcd屏幕、串口屏等,更高级就造个手机APP或者使用mqtt与云平台交互显示数据,妥妥的一个设计就出来了哈哈。

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

单片机复位电路分析

来分析一下这个电路&#xff1a; 首先这里面有电容&#xff0c;所以是一个动态电路。哈哈哈 假设左上角的电压源是5V的代号为VOLT。 可以知道电容capacitor C1左边的电压也是5V&#xff0c;电容中间隔着一个绝缘体&#xff0c;所以不导电&#xff0c; 这个时候电流无法通过…

【深度学习_TensorFlow】激活函数

写在前面 上篇文章我们了解到感知机使用的阶跃函数和符号函数&#xff0c;它们都是非连续&#xff0c;导数为0的函数&#xff1a; 建议回顾上篇文章&#xff0c;本篇文章将介绍神经网络中的常见激活函数&#xff0c;这些函数都是平滑可导的&#xff0c;适合于梯度下降算法。 写…

【Spring Boot】Thymeleaf模板引擎 — 表达式的语法

表达式的语法 模板的主要作用是将后台返回的数据渲染到HTML中。那么Thymeleaf是如何解析后台数据的呢&#xff1f;接下来从变量、方法、条件判断、循环、运算&#xff08;逻辑运算、布尔运算、比较运算、条件运算&#xff09;方面学习Thymeleaf表达式支持的语法。 1.赋值和拼…

eclipse Java Editor Templates

​ Window - Preferences - Java - Editor - Templates ​ date ${currentDate:date(yyyy.MM.dd)}

KubeSphere 社区双周报 | 本周六上海站 Meetup 准时开启 | 2023.7.21-08.03

KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者&#xff0c;并对近期重要的 PR 进行解析&#xff0c;同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为&#xff1a;2023.07.21-2023.…

逆向时如何找到MingGW(GNU)编译程序的main函数

编译器是MingGW生成的可执行文件的显著特点是, 最终运行ZwContinue后程序就莫名其妙启动了, 也找不到main函数。 为了探究里面究竟怎么回事, 我找到了wrk-v1.2的源码, 其中包含了ZwContinue的实现, 首先先看一下注释, API界面包含了2个参数, 其中让人感兴趣的是PCONTEXT, 这是…

git merge 和rebase区别

Merge the incoming changes into the current branch 找到两个分支的祖先 commit&#xff0c;然后将公共分支最新版合并到自己的分支&#xff0c;形成一个新的 commit 提交&#xff0c;用图表示如下。 Rebase the current branch on top of the incoming Rebase 则是重新基于…

蓝桥杯上岸每日N题 第八期 (全球变暖)!!!

蓝桥杯上岸每日N题第八期(全球变暖)&#xff01;&#xff01;&#xff01; 同步收录 &#x1f447; 蓝桥杯上岸必背&#xff01;&#xff01;&#xff01;(第五期BFS) 大家好 我是寸铁&#x1f4aa; 冲刺蓝桥杯省一模板大全来啦 &#x1f525; 蓝桥杯4月8号就要开始了 &am…

JVM内存模型结构

什么是JVM JVM是Java Virtual Machine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;JVM是一个虚构出来的计算机&#xff0c;有着自己完善的硬件架构&#xff0c;如处理器、堆栈等。 为什么需要JVM&#xff1f; Java语言使用Java虚拟机屏蔽了与具体平台相关的信息&…

9 多进程编程

文章目录 进程以及状态进程进程的状态linux下的进程管理pstopbgkillcrontab 进程的创建两个while同时运行示例获取进程pidProcess 结构给子进程指定的函数传递参数进程间是否共享全局变量补充 进程间通信-Queue常用操作Queue 实例 管道通信&#xff08;了解&#xff09;样例 进…

PDM系统的项目跟踪功效

在现代企业管理中&#xff0c;项目跟踪是一个至关重要的环节。PDM系统&#xff08;Product Data Management&#xff0c;产品数据管理&#xff09;作为一款强大的数字化工具&#xff0c;为企业提供了高效的项目跟踪优势。让我们一同深入探讨&#xff0c;看看PDM系统是如何通过项…

JavaWeb 手写Tomcat底层机制

目录 一、Tomcat底层整体架构 1.简介 : 2.分析图 : 3.基于Socket开发服务端的流程 : 4.打通服务器端和客户端的数据通道 : 二、多线程模型的实现 1.思路分析 : 2.处理HTTP请求 : 3.自定义Tomcat : 三、自定义Servlet规范 1. HTTP请求和响应 : 1 CyanServletRequest …

提高测试用例质量的6大注意事项

在软件测试中&#xff0c;经常会遇到测试用例设计不完整&#xff0c;用例没有完全覆盖需求等问题&#xff0c;这样往往容易造成测试工作效率低下&#xff0c;不能及时发现项目问题&#xff0c;无形中增加了项目风险。 因此提高测试用例质量&#xff0c;就显得尤为重要。一般来说…

SQL基础复习与进阶

SQL进阶 文章目录 SQL进阶关键字复习ALLANYEXISTS 内置函数ROUND&#xff08;四舍五入&#xff09;TRUNCATE&#xff08;截断函数&#xff09;SEILING&#xff08;向上取整&#xff09;FLOOR&#xff08;向下取整&#xff09;ABS&#xff08;获取绝对值&#xff09;RAND&#x…

采用 SpringDoc、javadoc 无注解零入侵基于java注释

今天在gitee上学习到一个&#xff0c;个人觉得非常使用的功能。就是采用 SpringDoc、javadoc 无注解零入侵基于java注释&#xff0c;只需把注释写好 无需再写一大堆的文档注解了。 下面是源码的地址 RuoYi-Vue-Plus: 后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-TokenMybati…

【数模】预测模型

一、灰色系统 白色系统&#xff1a;系统信息完全明确灰色系统&#xff1a;系统部分信息已知&#xff0c;部分信息未知 对在一定范围内变化的、与时间有关的灰色过程进行预测。过程&#xff1a;原始数据找规律→生成强规律性的数据序列→建立微分方程来预测未来趋势 黑色系统&a…

企业上云实施路线图

企业上云步骤主要分为规划、设计、实施、验证、运维五个阶段。https://articles.e-works.net.cn/cloud/article144684.htm

idea 2023 新版ui中git的相关操作

前两个月换了新电脑&#xff0c;下了最新版的idea发现可以切换一套新的ui了 切换新ui肯定不太习惯&#xff0c;很多操作都得重新摸索一下 在这里记录几个git相关的操作 忽略我下面截图中当前项目是js的后端项目…… 切换ui 首先说一下怎么切换新旧版ui&#xff0c;我这里就…

【前端实习生备战秋招】—CSS面试题汇总,建议收藏系列

【前端实习生备战秋招】—CSS面试题汇总&#xff0c;建议收藏系列 文章目录 【前端实习生备战秋招】—CSS面试题汇总&#xff0c;建议收藏系列一、CSS基础1. CSS选择器及其优先级2. CSS中可继承与不可继承属性有哪些3. display的属性值及其作用4. display的block、inline和inli…

2023年电赛A题报告模板--可直接使用

任务 图1 任务内容 要求 图2 基本要求内容 图3 发挥部分内容 说明 图4 说明内容 评分标准 图5 评分内容 正文 &#xff08;部分&#xff09; 摘要 本实验旨在设计和制作一个由两个单相逆变器组成的并联系统&#xff0c;用于为电阻负载供电或并入220V电网。采用基于STM…