基于STM32设计的数显热水器

一、项目介绍

当前介绍的项目是基于 STM32F103ZET6 系列 MCU 设计的数显热水器,通过显示屏来显示热水器的温度及其工作状态,通过 PT100 传感器来检测热水器的温度变化,并通过电加热片实现加热过程,以达到控制热水器温度的目的。

image-20230618142512933

image-20230618142039119

二、设计流程

2.1 硬件选型

  • STM32F103ZET6 系列 MCU
  • OLED 显示屏
  • PT100 温度传感器
  • 电加热片
  • 继电器

2.2 软件设计

(1)显示屏

使用 OLED 显示屏来显示热水器的温度及其工作状态,通过 SPI 接口与 STM32 芯片进行通讯。设计温度值及其单位、热水器工作状态等。

(2)温度传感器

使用 PT100 温度传感器来检测热水器内部温度的变化,并将数据通过 ADC 转换后,传输给 STM32 芯片,以实现对热水器加热过程的控制。

(3)电加热片

使用电加热片模拟热水器加热过程,通过继电器控制电加热片的通断,以调节热水器的温度。

(4)控制系统

通过 STM32 芯片来实现对热水器的控制,读取温度传感器的数据。

三、代码设计

3.1 OLED显示屏

(1)SPI 接口初始化

需要对 STM32F103ZET6 的 SPI 接口进行初始化配置,设置相关的时钟和模式,使其能够与 OLED 显示屏进行通讯。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE); // 打开SPI3时钟
SPI_InitTypeDef spi_init_type;
spi_init_type.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
spi_init_type.SPI_Mode = SPI_Mode_Master;
spi_init_type.SPI_DataSize = SPI_DataSize_8b;
spi_init_type.SPI_CPOL = SPI_CPOL_Low;
spi_init_type.SPI_CPHA = SPI_CPHA_1Edge;
spi_init_type.SPI_NSS = SPI_NSS_Soft;
spi_init_type.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32; // 设置 SPI 时钟频率为 72 MHz / 32 = 2.25MHz
spi_init_type.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI3, &spi_init_type);
SPI_Cmd(SPI3, ENABLE);

(2)OLED 显示屏初始化

以下是 OLED 显示屏的初始化代码:

void OLED_Init(void) {GPIO_SetBits(GPIOB, GPIO_Pin_6);   //RST SETGPIO_ResetBits(GPIOB, GPIO_Pin_6); //RST RESETGPIO_SetBits(GPIOB, GPIO_Pin_6);   //RST SETwrite_command(0xAE); // 关闭显示write_command(0xD5); // 设置时钟分频因子,震荡频率write_command(0x80); // 分频因子=1 ,震荡频率(fosc)=8MHzwrite_command(0xA8); // 设置驱动路数:MUX(复用方式)write_command(0x1F); // 1/32 duty (0x0F~0x3F)write_command(0xD3); // 设置显示偏移write_command(0x00); // 不偏移write_command(0x40); // 设置显示开始行[5:0], 对于设置了32行的液晶,// 这里的值为0表示从0行开始显示write_command(0x8D); // 对比度设置write_command(0x14); // AHB参考电压256等分 移位[3:0]100[n,1/256]write_command(0x20); // 水平方向上的寻址模式write_command(0x00); // 垂直方向上的寻址模式write_command(0xA1); // 设置段再映射write_command(0xC0); // 设置COM扫描方向write_command(0xDA); // 设置COM引脚硬件配置write_command(0x12);write_command(0x81); // 对比度设置write_command(0xBF); // 设置电荷泵电压write_command(0xD9); // 设置预充电周期write_command(0xF1);write_command(0xDB); // 设置VCOMH电压倍率write_command(0x40);write_command(0xAF); // 打开显示OLED_Clear(); // 清屏
}

(3)OLED 显示函数

接下来编写 OLED 显示函数,实现字符和数字的显示功能。

void OLED_show_string(uint8_t x, uint8_t y, char *str) {uint8_t i = 0;while (str[i] != '\0') {OLED_show_char(x, y + i * 8, str[i]);++i;}
}void OLED_show_char(uint8_t x, uint8_t y, char ch) {uint8_t c = ch - 32;if (c >= 96) return;uint8_t* buffer = (uint8_t*)oled_buffer;uint8_t cx, cy;for (cy = 0; cy < 8; cy++) {uint8_t line = font[c][cy];for (cx = 0; cx < 6; cx++) {if (line & 0x1) {buffer[(y + cy) * OLEDWIDTH + x + cx] = 1;} else {buffer[(y + cy) * OLEDWIDTH + x + cx] = 0;}line >>= 1;}}OLED_Draw_Pixel(x + 6, y, 0);OLED_Draw_Pixel(x + 6, y + 1, 0);OLED_Draw_Pixel(x + 6, y + 6, 0);OLED_Draw_Pixel(x + 6, y + 7, 0);
}

(4)结果显示

在代码中调用 OLED_show_string 函数和 OLED_show_char 函数显示数值和字符。

OLED_Init();
OLED_Clear();
OLED_show_string(0, 0, "HELLO WORLD!");
OLED_show_string(0, 16, "TEMP:20 C");

3.2 测温代码

(1)引脚配置

需要对 STM32F103ZET6 的 IO 口进行配置,将用于连接 PT100 温度传感器的引脚设置为输入模式。

这里以 PA0 引脚作为 PT100 传感器的连接口(即 PT100 三线连接中的 R3 端),代码如下:

GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);

(2)ADC 配置

接下来需要对 STM32F103ZET6 的 ADC 进行初始化配置,使其能够读取 PT100 温度传感器输出的电压信号。

这里以 ADC1 通道5 作为读取口,代码如下:

ADC_InitTypeDef ADC_InitStructure;
RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 设置 ADC 时钟为 PCLK2 的 1/6
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // 打开 ADC1 时钟
ADC_DeInit(ADC1); // 初始化 ADC1
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // 连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Cmd(ADC1, ENABLE); // 开启 ADC1

(3)温度转换函数

根据 PT100 温度传感器输出电压与温度的关系,可使用线性函数计算出温度值。

转换公式如下:

Rt = (Vref - Vpt) / Ipt // Rt 为 PT100 的阻值,Vref 为基准电压,Vpt 为 PT100 输出电压,Ipt 为 PT100 驱动电流
Temp = a * Rt + b // Temp 为温度值,a 和 b 为经过拟合后的系数

其中 Rt 的计算需要使用差分运算放大器进行转换,这里不再赘述。假设已经得到 Rt 值,则温度转换函数代码如下:

float PT100_Get_Temperature(float Rt)
{float a = 3.9083e-3f, b = -5.775e-7f, R0 = 100.0f; // 根据实际数据进行拟合得到 a、b 和 R0 的值float Tem, delta;delta = pow(Rt / R0, 2) + a * (Rt / R0) + b;Tem = (delta > 0) ? (-R0*a + sqrt(delta)) / (2 * b) : 0;return Tem;
}

(4)数据采集

根据差分放大器输出的电压值得到 PT100 温度传感器的阻值,再根据阻值计算出实际温度,最后将温度值通过串口打印出来。以下是数据采集代码:

float ADC_Get_Voltage(void)
{float voltage = 0;uint16_t adc_val = 0;ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_239Cycles5); // 配置 ADC 通道5ADC_SoftwareStartConvCmd(ADC1, ENABLE); // 使能软件触发 ADC 转换while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); // 等待转换结束adc_val = ADC_GetConversionValue(ADC1); // 读取 ADC 转换结果voltage = (float)adc_val * 3.3f / 4096; // 计算基准电压return voltage;
}float PT100_Get_Rt(float Vpt)
{float Rsource = 10e3f, Rpt = 100.0f; // Rsource 为差分放大器输出电阻,Rpt 为 PT100 阻值float Ipt = (3.3f - Vpt) / Rsource; // 计算 PT100 驱动电流float Rt = (3.3f - Vpt) / Ipt; // 根据欧姆定律计算出 PT100 阻值return Rt;
}void USART1_Send_Float(float f)
{char buf[32];sprintf(buf, "%.1f\r\n", f); // 转换为字符串while (*buf){USART_SendData(USART1, *buf);while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);buf++;}
}int main(void)
{...while (1){float Vpt = ADC_Get_Voltage(); // 获取差分放大器输出电压float Rt = PT100_Get_Rt(Vpt); // 计算 PT100 阻值float Temp = PT100_Get_Temperature(Rt); // 根据阻值计算温度USART1_Send_Float(Temp); // 将温度值打印到串口delay_ms(500);}...
}

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

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

相关文章

结构思考力-有效提升你的工作效率20倍以上

结构思考力是一种帮助人们察觉并改善自身思考结构的思考艺术&#xff0c;它将人们的思维表达以一种逻辑结构的形式来表示&#xff0c;从而简化人与人之间的沟通成本&#xff0c;提高工作效率。每个人思考问题的方式不同&#xff0c;不同的思考结构使人们的注意力的方向也不同&a…

机房环境、动力、网络、安防,帮您提高运维效率,确保机房安全

机房作为单位的核心部门&#xff0c;由计算机、服务器、网络设备、存储设备等关键设备组成&#xff0c;智能化计算机机房这个概念在各个领域中&#xff0c;已经占据了很重要的地位&#xff0c;伴随着国家大力倡导的&#xff0c;东数西算&#xff0c;数字经济、云计算、5G大数据…

IPC进程间通信探索——管道的原理与特点

&#x1f923; 爆笑教程 &#x1f449; 《看表情包学Linux》 &#x1f525; CSDN 累计订阅量破千的火爆 C/C 教程的 2023 重制版&#xff0c;C 语言入门到实践的精品级趣味教程。了解更多&#xff1a; &#x1f449; "不太正经" 的专栏介绍 ← 试读第一章订阅链接&am…

JVM基础篇-线程运行诊断

JVM基础篇-线程运行诊断 CPU占用过多定位 用top定位哪个进程对cpu的占用过高ps H -eo pid,tid,%cpu | grep 进程id &#xff08;用ps命令进一步定位是哪个线程引起的cpu占用过高&#xff09;jstack 进程id 可以根据线程id 找到有问题的线程&#xff0c;进一步定位到问题代码的…

SpringBoot整合SSMP小demo

创建项目 spring web&#xff0c;mybatis&#xff0c;mysql勾选 加入mp和druid&#xff0c;依赖见SpringBoot基础认识_阳光明媚UPUP的博客-CSDN博客 yml数据源 server:port: 81 spring:datasource:druid: #整合方式配置driver-class-name: com.mysql.jdbc.Driverurl: jdbc:m…

深度学习之tensorboard可视化工具

(1)什么是tensorboard tensorboard是TensorFlow 的一个可视化工具包&#xff0c;提供机器学习实验所需的可视化和工具&#xff0c;该工具的功能如下&#xff1a; 跟踪和可视化指标&#xff0c;例如损失和精度可视化模型图&#xff08;操作和层&#xff09;查看权重、偏差或其…

【动态规划】子序列系列

文章目录 动态规划&#xff08;子序列系列&#xff09;1. 最长递增子序列2. 摆动序列3. 最长递增子序列的个数4. 最长数对链5. 最长定差子序列6. 最长的斐波那契子序列的长度7. 最长等差数列8. 等差数列划分 || - 子序列 动态规划&#xff08;子序列系列&#xff09; 1. 最长递…

mac电脑访问windows共享文件夹连接不上(设置445端口)

前提&#xff1a;首先需要保证mac和windows都在同一局域网内&#xff0c;如果不在肯定是连不上的&#xff0c;就不用往下看了。 事情是这样的&#xff0c;公司入职发了mac电脑&#xff0c;但是我是window重度用户&#xff0c;在折腾mac的过程中&#xff0c;有许多文件需要从wi…

金融数学方法:蒙特卡洛模拟

1.方法介绍 蒙特卡洛模拟是一种基于概率和统计的数值计算方法&#xff0c;用于解决各种复杂问题。它以概率统计为基础&#xff0c;通过随机抽样和重复实验的方式进行模拟&#xff0c;从而得到问题的近似解。它的基本思想是通过大量的随机样本来近似计算问题的解&#xf…

搜索与图论(二)

最短路 单源最短路 所有边权都是正数 朴素Dijkstra算法 基本思路:从1号点到其他点的最短距离 步骤: 定义一个s集合包含当前已确定最短距离的点 1、初始化距离dis[1] 0,dis[其它] 正无穷 2、for i 0-n循环n次 2.1找到不在s中的距离最近的点 ->t 2.2把t加到s当中去…

【面试题】作用域面试题

作用域 全局作用域局部作用域&#xff08;函数里&#xff09;也称函数作用域块级作用域 {}包裹的 例如if for 括号&#xff08;&#xff09;也算 变量 全局变量 谁都能用&#xff0c;在函数内也可以局部变量&#xff0c;只能在该函数内用&#xff0c;如果这个函数嵌套了子函…

Arcgis 分区统计majority参数统计问题

利用Arcgis 进行分区统计时&#xff0c;需要统计不同矢量区域中栅格数据的众数&#xff08;majority&#xff09;&#xff0c;出现无法统计majority参数问题解决 解决&#xff1a;利用copy raster工具&#xff0c;将原始栅格数据 64bit转为16bit

kotlin 编写一个简单的天气预报app(三)broadcast换成eventbus

使用eventbus替换broadcast 将从Broadcast切换到EventBus有以下几个好处&#xff1a; 解耦性&#xff1a;通过使用EventBus&#xff0c;您可以实现组件之间的解耦。传统的Broadcast机制需要发送方和接收方明确知道对方的存在&#xff0c;并且需要在代码中设置Intent过滤器和广…

Vue中的插值表达式

Vue中的插值表达式&#xff08;Interpolation&#xff09;用于将数据动态绑定到HTML模板中。它的主要作用是在模板中直接显示变量的值&#xff0c;并实现数据的双向绑定。以下是插值表达式的一些作用&#xff1a; 1.变量展示&#xff1a;插值表达式允许将Vue实例中的数据直接显…

springboot项目如何自动重启(使用Devtools检测修改并自动重启springboot)

1. 问题&#xff1a; 我们在项目开发阶段&#xff0c;可能经常会修改代码&#xff0c;修改完后就要重启Spring Boot。经常手动停止再启动&#xff0c;比较麻烦。 所以我们引入一个Spring Boot提供的开发工具&#xff1b; 只要源码或配置文件发生修改&#xff0c;Spring Boot应用…

4年测试工程师,常用功能测试点总结,“我“不再走弯路...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 输入框测试 1、字…

如何把几个视频合并在一起?视频合并方法分享

当我们需要制作一个比较长的视频时&#xff0c;将多个视频进行合并可以使得整个过程更加高效。此外&#xff0c;合并视频还可以避免出现“剪辑断层”的情况&#xff0c;使得视频内容更加连贯&#xff0c;更加容易被观众理解和接受。再有&#xff0c;合并视频还可以减少视频文件…

【软件测试】说说你对TDD测试驱动开发的理解?

很多公司在面测试中高级岗时&#xff0c;都会不同程度地问到“有没有了解过TDD”“你认为TDD可以解决什么问题”或者“说说测试驱动开发的流程”等等&#xff0c;即使公司并不会用到此开发流程&#xff0c;面试官也会通过你对这个相对还比较“陌生”的概念的讲述来了解你对一些…

原型链污染,nodejs逃逸例子

文章目录 原型链污染原型链污染原理原型链污染小例子 原型链污染题目解析第一题第二题 Nodejs沙箱逃逸方法一方法二 原型链污染 原型链污染原理 原型链 function test(){this.a test; } b new test;可以看到b在实例化为test对象以后&#xff0c;就可以输出test类中的属性a…

“数智新应用”不再是口号,看汽车、医药、制造企业如何突出重围?

近日&#xff0c;以“释放数智生产力”为主题的 Kyligence 用户大会在上海前滩香格里拉大酒店成功举行。大会包含上午的主论坛和下午的 4 场平行论坛&#xff0c;并举办了闭门会议、Open Day 等活动。来自金融、零售、制造、医药等行业的客户及合作伙伴带来了超过 23 场主题演讲…