项目案例:基于单片机设计植物、花卉养殖温室控温系统

举报
DS小龙哥 发表于 2024/02/29 22:23:59 2024/02/29
【摘要】 本项目设计一款基于单片机设计温室控温系统。该系统以STM32F103C8T6作为核心处理器,通过与DS18B20数字温度传感器和0.96英寸OLED显示屏等硬件组件的紧密配合,实现对温室内温度的实时监测和精准控制。

一、前言

随着现代园艺业的发展和人们对美化生活的追求,花卉养殖已成为越来越多人的爱好和职业。花卉温室作为花卉生长的重要场所,其内部环境尤其是温度条件对花卉的生长和开花具有至关重要的影响。因此,如何为花卉提供一个稳定、适宜的生长环境成为了花卉温室管理的核心任务。

本项目设计一款基于单片机设计温室控温系统。该系统以STM32F103C8T6作为核心处理器,通过与DS18B20数字温度传感器和0.96英寸OLED显示屏等硬件组件的紧密配合,实现对温室内温度的实时监测和精准控制。

DS18B20温度传感器以其高精度和快速响应的特点,能够准确捕捉温室内的温度变化,并将数据传输给STM32微控制器进行处理。同时,0.96英寸OLED显示屏通过SPI协议与微控制器通信,直观展示当前温度值以及用户设定的温度阈值。用户可以通过两个独立按键轻松设置和调整温度阈值,以满足不同花卉的生长需求。

当系统检测到温室内温度低于设定的阈值时,将自动触发继电器控制热风机启动,通过吹出热风来提升室内温度。这一闭环控制系统确保了花卉温室能够在任何天气条件下,为花卉提供一个恒定的、适宜的生长环境。

本项目的实施不仅提高了花卉温室的自动化水平,降低了人工干预的频率和强度,而且通过精确的温度控制,促进了花卉的生长速度和品质提升。这一创新解决方案不仅适用于个人花卉爱好者,也广泛适用于商业花卉种植基地和园艺企业,为现代园艺业的发展注入了新的活力。


二、硬件选型介绍

以下是基于STM32的花卉温室控温系统的硬件选型:

【1】主控芯片:STM32F103C8T6

  • STM32F103系列具有良好的性能和丰富的外设,适合嵌入式应用。

  • STM32F103C8T6是一款32位ARM Cortex-M3内核的微控制器,具有64KB的Flash存储器和20KB的RAM。

【2】温度传感器:DS18B20

  • DS18B20是一款数字温度传感器,采用单总线接口进行通信。

  • 具有高精度、防水防尘等特点,非常适合测量温室内的温度。

  • 通过引脚连接到STM32的GPIO口,并使用OneWire协议进行数据通信。

【3】显示屏:0.96寸OLED显示屏

  • 选择支持SPI协议的0.96寸OLED显示屏作为显示设备,可以方便地显示环境温度和温度阀值。

  • OLED显示屏具有低功耗、高对比度、视角广等优点,适合嵌入式应用。

【4】按键:两个独立按键

  • 选择两个独立按键用于设置温度阀值,可以通过按下按钮增加或减小温度阀值。

【5】继电器:用于控制热风机加热

  • 根据温度阀值和实时温度数据,通过STM32的GPIO口控制继电器的开关,从而控制热风机的加热。

  • 继电器的选型要根据热风机的额定电流和电压来确定,确保能够正常工作。


三、设计思路

软件逻辑设计思路:

【1】初始化STM32外设,包括GPIO、SPI、USART等。 在项目的开始阶段,需要对STM32的外设进行初始化设置,这包括GPIO(通用输入输出)、SPI(串行外设接口)和USART(通用同步异步收发器)等。

【2】设置温度阈值的初始值,并通过按键调整阈值。 在系统中,需要设定一个温度阈值作为参考点,这个阈值可以通过按键来进行调节。

【3】循环读取DS18B20温度传感器的数据,并将读取到的温度值与阈值进行比较。 系统会持续不断地读取DS18B20温度传感器的数据,然后将这些数据与设定的阈值进行比较。

【4】如果当前温度低于阈值,则控制继电器闭合,热风机开始加热;否则,打开继电器,停止加热。 根据温度传感器读取的温度和阈值的比较结果,如果当前温度低于阈值,系统会控制继电器闭合,使热风机开始工作进行加热;如果当前温度高于或等于阈值,系统会控制继电器打开,停止加热。

【5】将温度值和阈值显示在OLED屏幕上,通过USART串口输出给用户。 系统会将读取到的温度值和设定的阈值显示在OLED屏幕上,同时通过USART串口将这些信息输出给用户。

【6】不断循环执行以上步骤,实现温室的自动控温功能。 系统会不断地循环执行以上步骤,以实现对温室的自动控温功能。

伪代码:

// 定义变量
float temperature;  // 当前温度值
float threshold;    // 温度阀值
​
// 初始化硬件和外设
void initialize() {
    initialize_GPIO();     // 初始化GPIO
    initialize_SPI();      // 初始化SPI
    initialize_USART();    // 初始化USART
    initialize_DS18B20();  // 初始化DS18B20
    initialize_OLED();     // 初始化OLED显示屏
    initialize_Button();   // 初始化按键
    initialize_Relay();    // 初始化继电器
}
​
// 读取温度值
float readTemperature() {
    // 通过DS18B20读取温度值
    // 返回温度值
}
​
// 读取阀值
float readThreshold() {
    // 读取按键的状态,并调节阀值
    // 返回阀值
}
​
// 控制加热器
void controlHeater(float currTemperature, float currThreshold) {
    if (currTemperature < currThreshold) {
        // 温度低于阀值,控制继电器闭合,热风机加热
    } else {
        // 温度高于或等于阀值,打开继电器,停止加热
    }
}
​
// 显示温度和阀值
void displayTemperature(float currTemperature, float currThreshold) {
    // 在OLED屏幕上显示温度值和阀值
    // 通过USART串口输出温度值和阀值
}
​
// 主函数
int main() {
    initialize();  // 初始化
    
    while (1) {
        temperature = readTemperature();          // 读取温度值
        threshold = readThreshold();              // 读取阀值
        controlHeater(temperature, threshold);     // 控制加热器
        displayTemperature(temperature, threshold);// 显示温度和阀值
    }
​
    return 0;
}

以上是基本的软件逻辑设计思路和伪代码。


四、代码实现

4.1 读取温度显示

下面是使用STM32F103C8T6读取DS18B20温度传感器数据,并将温度显示到OLED显示屏上的实现代码:

#include "stm32f10x.h"
#include "delay.h"
#include "onewire.h"
#include "ds18b20.h"
#include "ssd1306.h"
​
int main(void)
{
    // 初始化延迟函数
    delay_init();
    
    // 初始化OLED显示屏
    SSD1306_Init();
    
    // 初始化DS18B20温度传感器
    DS18B20_Init();
    
    float temperature = 0.0;
    char tempStr[10];
    
    while (1)
    {
        // 读取DS18B20温度传感器数据
        temperature = DS18B20_GetTemp();
        
        // 将温度转换为字符串
        sprintf(tempStr, "%.2f C", temperature);
        
        // 清空OLED显示屏
        SSD1306_Clear();
        
        // 在OLED显示屏上显示温度
        SSD1306_GotoXY(0, 0);
        SSD1306_Puts("Temperature:", &Font_7x10, SSD1306_COLOR_WHITE);
        SSD1306_GotoXY(0, 20);
        SSD1306_Puts(tempStr, &Font_11x18, SSD1306_COLOR_WHITE);
        
        // 刷新OLED显示屏
        SSD1306_UpdateScreen();
        
        // 延时一段时间
        delay_ms(1000);
    }
}

代码中,使用了封装好库文件,包括延迟函数(delay.h)、OneWire总线(onewire.h)、DS18B20温度传感器(ds18b20.h)和SSD1306 OLED显示屏(ssd1306.h)的库文件。

在主函数中,初始化延迟函数和OLED显示屏,初始化DS18B20温度传感器。然后进入无限循环,在循环中读取DS18B20温度传感器的温度数据,将温度显示到OLED显示屏上。温度数据通过sprintf函数转换为字符串,使用SSD1306库函数在OLED显示屏上进行显示。通过延时函数延时一段时间,实现温度的定时更新。


4.2 DS18B20的代码

头文件代码:

#ifndef DS18B20_H
#define DS18B20_H
​
#include "stm32f10x.h"
​
// DS18B20引脚定义
#define DS18B20_GPIO_PORT   GPIOA
#define DS18B20_GPIO_PIN    GPIO_Pin_0
​
// DS18B20函数声明
void DS18B20_Init(void);
void DS18B20_WriteByte(uint8_t data);
uint8_t DS18B20_ReadByte(void);
float DS18B20_GetTemp(void);
​
#endif
​

源文件代码:

#include "ds18b20.h"
#include "delay.h"
​
// 初始化DS18B20温度传感器
void DS18B20_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    // 配置GPIOA引脚为推挽输出
    GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStructure);
    
    // 将引脚拉低一段时间
    GPIO_ResetBits(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
    delay_us(500);
    
    // 将引脚拉高一段时间
    GPIO_SetBits(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
    delay_us(80);
    
    // 等待DS18B20的响应
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStructure);
    delay_us(80);
}
​
// 向DS18B20写入一个字节的数据
void DS18B20_WriteByte(uint8_t data)
{
    uint8_t i;
    
    // 将引脚设置为推挽输出
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStructure);
    
    // 写入数据
    for (i = 0; i < 8; i++)
    {
        GPIO_ResetBits(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
        delay_us(2);
        if (data & 0x01)
        {
            GPIO_SetBits(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
        }
        delay_us(60);
        GPIO_SetBits(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
        delay_us(2);
        data >>= 1;
    }
}
​
// 从DS18B20读取一个字节的数据
uint8_t DS18B20_ReadByte(void)
{
    uint8_t i, data = 0;
    
    // 将引脚设置为推挽输出
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStructure);
    
    // 读取数据
    for (i = 0; i < 8; i++)
    {
        GPIO_ResetBits(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
        delay_us(2);
        GPIO_SetBits(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN);
        delay_us(2);
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
        GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStructure);
        delay_us(2);
        data >>= 1;
        if (GPIO_ReadInputDataBit(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN))
        {
            data |= 0x80;
        }
        delay_us(60);
    }
    
    return data;
}
​
// 获取DS18B20温度数据
float DS18B20_GetTemp(void)
{
    uint8_t tempLSB, tempMSB;
    int16_t tempData;
    float temperature;
    
    // 发送温度转换命令
    DS18B20_WriteByte(0xCC);     // 跳过ROM操作
    DS18B20_WriteByte(0x44);     // 发送温度转换命令
    
    // 等待温度转换完成
    while (!GPIO_ReadInputDataBit(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN));
    
    // 发送读取温度命令
    DS18B20_WriteByte(0xCC);     // 跳过ROM操作
    DS18B20_WriteByte(0xBE);     // 发送读取温度命令
    
    // 读取温度数据
    tempLSB = DS18B20_ReadByte();
    tempMSB = DS18B20_ReadByte();
    
    // 计算温度值
    tempData = (tempMSB << 8) | tempLSB;
    if (tempData & 0x8000)      // 温度为负数
    {
        tempData = ~tempData + 1;
        temperature = -((float)tempData / 16.0);
    }
    else                        // 温度为正数
    {
        temperature = (float)tempData / 16.0;
    }
    
    return temperature;
}
​


4.3 OLED显示屏代码

头文件:

#ifndef SSD1306_H
#define SSD1306_H
​
#include "stm32f10x.h"
#include "fonts.h"
​
// SSD1306显示屏参数定义
#define SSD1306_I2C_ADDR      0x78    // I2C地址
#define SSD1306_WIDTH         128     // 显示屏宽度
#define SSD1306_HEIGHT        64      // 显示屏高度
​
// SSD1306函数声明
void SSD1306_Init(void);
void SSD1306_Clear(void);
void SSD1306_UpdateScreen(void);
void SSD1306_GotoXY(uint16_t x, uint16_t y);
void SSD1306_Puts(const char* str, FontDef_t* font, uint8_t color);
​
#endif
​


源文件:

#include "ssd1306.h"
#include "i2c.h"
​
static uint8_t SSD1306_Buffer[SSD1306_WIDTH * SSD1306_HEIGHT / 8];
​
void SSD1306_Init(void)
{
    // 初始化I2C总线
    I2C_Init();
    
    // 向SSD1306发送初始化命令
    uint8_t initCommands[] = {
        0xAE,           // 关闭显示
        0xD5, 0x80,     // 设置时钟分频因子
        0xA8, 0x3F,     // 设置驱动路数
        0xD3, 0x00,     // 设置显示偏移
        0x40,           // 设置显示开始行
        0x8D, 0x14,     // 设置电荷泵
        0x20, 0x00,     // 设置内存地址模式
        0xA1,           // 设置段重定义
        0xC8,           // 设置COM扫描方向
        0xDA, 0x12,     // 设置COM引脚配置
        0x81, 0xCF,     // 设置对比度控制
        0xD9, 0xF1,     // 设置预充电周期
        0xDB, 0x40,     // 设置VCOMH电压倍率
        0xA4,           // 全局显示开启
        0xA6,           // 设置显示方式
        0xAF            // 开启显示
    };
    
    for (uint8_t i = 0; i < sizeof(initCommands); i++)
    {
        I2C_WriteByte(SSD1306_I2C_ADDR, 0x00, initCommands[i]);
    }
    
    // 清空缓冲区
    SSD1306_Clear();
    
    // 更新显示屏
    SSD1306_UpdateScreen();
}
​
void SSD1306_Clear(void)
{
    memset(SSD1306_Buffer, 0x00, sizeof(SSD1306_Buffer));
}
​
void SSD1306_UpdateScreen(void)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        I2C_WriteBuffer(SSD1306_I2C_ADDR, 0x40, &SSD1306_Buffer[SSD1306_WIDTH * i], SSD1306_WIDTH);
    }
}
​
void SSD1306_GotoXY(uint16_t x, uint16_t y)
{
    if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT)
        return;
    
    SSD1306_Buffer[(x + (y / 8) * SSD1306_WIDTH)] |= (1 << (y % 8));
}
​
void SSD1306_Puts(const char* str, FontDef_t* font, uint8_t color)
{
    while (*str)
    {
        for (uint8_t i = 0; i < font->FontWidth; i++)
        {
            uint8_t temp = font->data[(*str - 32) * font->FontWidth + i];
            for (uint8_t j = 0; j < font->FontHeight; j++)
            {
                if (temp & (1 << j))
                {
                    SSD1306_GotoXY(font->FontWidth * i + j, font->FontHeight * i + j);
                    SSD1306_Buffer[(font->FontWidth * i + j + (font->FontHeight * i + j) / 8 * SSD1306_WIDTH)] |= (1 << ((font->FontHeight * i + j) % 8));
                }
                else
                {
                    SSD1306_GotoXY(font->FontWidth * i + j, font->FontHeight * i + j);
                    SSD1306_Buffer[(font->FontWidth * i + j + (font->FontHeight * i + j) / 8 * SSD1306_WIDTH)] &= ~(1 << ((font->FontHeight * i + j) % 8));
                }
            }
        }
        
        str++;
    }
}
​


五、总结

本项目设计了基于STM32的花卉温室控温系统,通过使用DS18B20温度传感器、OLED显示屏和继电器等硬件模块,实现了对温室内温度的监测和控制。该系统能够根据预设的温度阀值,自动控制热风机的加热,以维持温室内的适宜温度,从而保证花卉的生长环境。

在软件逻辑设计方面,采用了STM32的外设和中断机制,结合合适的算法和状态判断,实现了温度数据的获取和比较,并根据结果控制继电器的开关。通过OLED显示屏和USART串口,能够及时地将温度值和阀值反馈给用户,方便用户了解当前环境并进行调节。

本项目的设计和实现为温室控温系统提供了一个具体的解决方案,通过合理的硬件选型和软件逻辑设计,能够满足花卉种植对温度控制的需求。在未来的发展中,系统将在农业领域发挥重要作用,并为人们创造更舒适、高效的温控环境。

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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