基于STM32设计的数显热水器

news2024/12/26 11:31:40

一、项目介绍

当前介绍的项目是基于 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 SET
    GPIO_ResetBits(GPIOB, GPIO_Pin_6); //RST RESET
    GPIO_SetBits(GPIOB, GPIO_Pin_6);   //RST SET

    write_command(0xAE); // 关闭显示
    write_command(0xD5); // 设置时钟分频因子,震荡频率
    write_command(0x80); // 分频因子=1 ,震荡频率(fosc)=8MHz
    write_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 通道5
    ADC_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.coloradmin.cn/o/827716.html

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

相关文章

虚拟机之间配置免密登录

目录 一、配置主机名映射 二、虚拟机配置SSH免密登录 三、验证 一、配置主机名映射 即修改/etc/hosts文件&#xff0c;将几台服务器和主机名进行映射。 注意每台服务器都要进行同样的配置。这样在各自服务器下&#xff0c;我们就可以通过主机名访问对应的ip地址了。 当然&…

拿捏--->打印菱形

文章目录 题目描述算法思路代码示例 题目描述 在屏幕上输出以下图案&#xff1a; 算法思路 代码示例 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() {int n;scanf("%d", &n);//上半部分菱形for (int i 0; i < n; i) //上半部分…

LinearAlgebraMIT_6_ColumnSpaceAndNullSpace

这节课的两个重点是column space列空间和null space零空间。 x.1 pre-multiply/left multiply and post-multiply/right multiply 对于pre-multiply/left multiply左乘和post-multiply/right multiply右乘&#xff0c;如果用英文的pre-和post-是比较容易理解的&#xff0c; A…

Promise用法

学习了promise之后&#xff0c;有点懂但让我说又说不出来&#xff0c;参考别人的记录一下。 1.什么是promise&#xff1f; 2.promise解决了什么问题 3.es6 promise语法 &#xff08;1&#xff09;then链式操作语法 &#xff08;2&#xff09;catch的语法 &#xff08;3&#xf…

DataX 异构数据贴源同步产品 - 技术分享篇(一)

DataX 是阿里开源的一个异构数据源离线同步工具&#xff0c;致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定高效的数据同步功能。 DataX设计理念 DataX本身作为数据同步框架&#xff0c;将不同数据源的同步抽象为从源头…

springboot自定义错误消息

为了提供自定义错误消息提示&#xff0c;springboot在resources目录下&#xff0c;有一个文件ValidationMessages.properties 用于存储 验证错误的消息提示&#xff1a; 比如&#xff1a; 这样一个ValidationMessage.properties username.notempty用户名不能为空 username.len…

【RTT驱动框架分析04】-I2C驱动框架分析

IIC RT-Thread IIC 应用编程 2.驱动分析 IIC总线设备继承自io设备驱动框架&#xff0c;RTT对IIC就只有2层的封装 IIC设备总线&#xff0c;在RTT内部有软件IIC和硬件IIC 设备驱动注册 rt_err_t rt_i2c_bus_device_register(struct rt_i2c_bus_device *bus,const char …

第六章:SpringMVC上

第六章&#xff1a;SpringMVC上 6.1&#xff1a;SpringMVC简介 什么是MVC MVC是一种软件架构的思想&#xff0c;将软件按照模型、视图、控制器来划分。 M&#xff1a;Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据。 一类称为实体类Bean&…

Hololens2二维码识别

配置 目前大部分Hololens进行二维码识别的开发都是基于ZXing的包完成&#xff0c;首先需要完成zxing.unity.dll&#xff0c;很多地方应该都能下载&#xff0c;也可以直接上github上下载&#xff08;下载点这里&#xff09;。 下载时注意一下版本就好&#xff0c;过老的zxing兼…

shell脚本中set -e的作用

set -e作用描述&#xff1a;shell中脚本运行中可能出现命令执行失败的情况&#xff0c;如果执行失败对后续有影响那么就应该退出脚本&#xff0c;不继续往下执行。set -e 命令就可以避免操作失败还继续往下执行的问题。 #!/bin/shset -eecho "make axp ..."VERSION$…

JVM-运行时数据区

目录 什么是运行时数据区&#xff1f; 方法区 堆 程序计数器 虚拟机栈 局部变量表 操作数栈 动态连接 运行时常量池 方法返回地址 附加信息 本地方法栈 总结&#xff1a; 什么是运行时数据区&#xff1f; Java虚拟机在执行Java程序时&#xff0c;将它管…

BI报表工具有哪些作用?奥威BI全面剖析数据

BI报表工具有哪些作用&#xff1f;主要的作用是通过整合多业务来源数据&#xff0c;全面分析挖掘数据&#xff0c;来帮助企业实现数据化运营、支持智能决策、实现数据资产沉淀和增值、进行数据挖掘和预测分析、提高数据可读性和数据可视化程度等&#xff0c;从而提高企业的竞争…

目标用户特征分析常见4大方法

用户特征分析直接影响需求分析、用户体验设计等软件开发的关键环节&#xff0c;如果不对用户特征进行科学分析&#xff0c;不能获得用户真实意图&#xff0c;这直接影响需求分析质量&#xff0c;对整个项目影响较大。 因此我们需要用科学的方法对目标用户进行特征分析。而常见的…

腾讯云-宝塔添加MySQL数据库

1. 数据库菜单 2. 添加数据库 3. 数据库添加成功 4. 上传数据库文件 5. 导入数据库文件 6. 开启数据库权限 7. 添加安全组 (宝塔/腾讯云) 8. Navicat 连接成功

深入了解PostgreSQL:高级查询和性能优化技巧

在当今数据驱动的世界中&#xff0c;数据库的性能和查询优化变得尤为重要。 POSTGRESQL作为一种开源的关系型数据库管理系统&#xff0c;在处理大规模数据和复杂查询时表现出色。 但随着数据量和查询复杂性的增加&#xff0c;性能问题可能会显现出来。 本文将深入探讨POSTGR…

机器学习深入浅出

机器学习是一种人工智能的分支&#xff0c;它使用算法和数学模型来让计算机自主学习数据并做出预测和决策。这种技术正在被广泛应用于各种领域&#xff0c;包括自然语言处理、计算机视觉、语音识别、医学诊断和金融预测等。在本篇博客中&#xff0c;我们将介绍机器学习的基本概…

python的decimal或者叫Decimal,BigDecimal

前言 在python中进行小数计算时&#xff0c;很容易发生精度错误问题&#xff01;&#xff01;&#xff01;&#xff01;一定要注意&#xff01;&#xff01;&#xff01;或者说&#xff0c;只要进行小数的运算都要用decimal。如&#xff1a;银企对账&#xff1b;工程计算等等在…

(十一)大数据实战——hadoop高可用之HDFS手动模式高可用

前言 本节内容我们介绍一下hadoop在手动模式下如何实现HDFS的高可用&#xff0c;HDFS的高可用功能是通过配置多个 NameNodes(Active/Standby)实现在集群中对 NameNode 的热备来解决上述问题。如果出现故障&#xff0c;如机器崩溃或机器需要升级维护&#xff0c;这时可通过此种…

Amazon Aurora Serverless v2 正式发布:针对要求苛刻的工作负载的即时扩展

我们非常兴奋地宣布&#xff0c;Amazon Aurora Serverless v2 现已面向 Aurora PostgreSQL 和 MySQL 正式发布。Aurora Serverless 是一种面向 Amazon Aurora 的按需自动扩展配置&#xff0c;可让您的数据库根据应用程序的需求扩展或缩减容量。 亚马逊云科技开发者社区为开发者…

4 Promethues监控主机和容器

目录 目录 1. 监控节点 1.1 安装Node exporter 解压包 拷贝至目标目录 查看版本 1.2 配置Node exporter 1.3 配置textfile收集器 1.4 启动systemd收集器 1.5 基于Docker节点启动node_exporter 1.6 抓取Node Exporter 1.7 过滤收集器 2. 监控Docker容器 2.1 运行cAdviso…