基于STM32设计的数显热水器

举报
DS小龙哥 发表于 2023/07/27 18:42:18 2023/07/27
【摘要】 当前介绍的项目是基于 STM32F103ZET6 系列 MCU 设计的数显热水器,通过显示屏来显示热水器的温度及其工作状态,通过 PT100 传感器来检测热水器的温度变化,并通过电加热片实现加热过程,以达到控制热水器温度的目的。

一、项目介绍

当前介绍的项目是基于 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 温度传感器的阻值,再根据阻值计算出实际温度,最后将温度值通过串口打印出来。以下是数据采集代码:


cpp
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);
    }
    ...
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。