环境监测预警防护系统

目录

一.项目概述

1.项目功能描述

2.物料清单

实物图

清单

3.硬件接线和软件代码

1.接线

2.完整代码

二.LCD1602

1.简介与功能描述

2.模块代码

三.烟雾传感器

1.简介与功能描述

2.模块代码

四.DHT11

1.简介与功能描述

2.模块代码

五.HC-05

1.简介与功能描述

2.模块代码(使用串口代码模块

六.SUBtoTTL

1.简介与功能描述

2.所用型号和实物图

七.防护模块

1.简介与功能描述

2.模块代码

八.报警模块

1.简介与功能描述

2.模块代码(与防护模块代码写在一起

九.显示及控制

1.简介与功能描述

2.模块代码(具体的数据信息需要调用其他模块的显示函数)

十.多线程

1.描述

2.代码

十一.调试

1.描述

2.方法

1.使用软件的自带调试工具

2.使用led灯或屏幕,串口打印显示

十二.项目总结

1.收获

2.疑难及解决

3.杂记点


一.项目概述

1.项目功能描述

本项目为基于STM32的环境监测预警防护系统,以STM32f103c8t6为控制芯片,通过DHT11温湿度传感器和MQ-2烟雾传感器实时监测环境的温湿度和烟雾值,将数据信息显示在LCD屏幕上,并通过蓝牙串口通信将信息传输给上位机。当环境中温湿度超过阈值时,系统将自动打开风扇进行通风,当烟雾值超过设定阈值时,将关闭风扇防止火灾蔓延,并打开警报器,同时在屏幕和上位机显示警报信息,用户可以修改温湿度阈值。

2.物料清单

实物图

清单

STM32F103C8T6开发板LCD1602烟雾传感器MQ-2DHT11温湿度传感器
HC05蓝牙模块SUB转TTL蜂鸣器继电器
电池直流电机和风扇面包板两个杜邦线若干
LED等若干按键三个安卓手机

3.硬件接线和软件代码

1.接线

硬件接线
STM32LCD1602MQ-2DHT11HC05SUB转TTL蜂鸣器继电器按键1按键2按键3按键4按键5
5VVCC5V
3.3VVCC,AVCCVCCVCC
GNDVSS,V0,KGNDGNDGNDGNDGND
A0D0
A1D1
A2D2
A3D3
A4D4
A5D5
A6D6
A7D7
A8
A9RXDRX
A10TXDTX
A11
A12DATA
A15
B0A0
B1RS
B3
B4
B5I/O
B6
B7
B8
B9
B10RW
B11E
B12
B13
B14
B15

2.完整代码

完整代码链接如下(百度网盘):
链接:https://pan.baidu.com/s/177G1yuDBALkuub9O0EpqMA 
提取码:5210 
 

二.LCD1602

1.简介与功能描述

在LCD显示屏上实时显示环境的温湿度,烟雾值,警报器,电机风扇的状态,以及修改温湿度和烟雾的阈值时的修改页面,包括提示信息和实时阈值。

2.模块代码

#include "dht11.h"
#include "delay.h"
#include "string.h"
#include "stdio.h"
#include "led.h"



char dht11_data[5] = {0};

uint8_t dht11_data_humidity_left = 0;
uint8_t dht11_data_humidity_after = 0;
uint8_t dht11_data_temperature_left = 0;
uint8_t dht11_data_temperature_after = 0;

float dht11_data_humidity = 0.0;
float dht11_data_temperature = 0.0;


extern float limit_value;           //串口打印烟雾阈值时使用

void dht11_gpio_input(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    DHT11_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = DHT11_PIN;
    gpio_initstruct.Mode = GPIO_MODE_INPUT;                     //输入
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}

void dht11_gpio_output(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    DHT11_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = DHT11_PIN;         
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;                 //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}

void dht11_start(void)
{
    dht11_gpio_output();
    DHT11_DQ_OUT(1);
    DHT11_DQ_OUT(0);
    delay_ms(20);
    DHT11_DQ_OUT(1);
    
    dht11_gpio_input();
    while(DHT11_DQ_IN);     //等待DHT11拉低电平
    while(!DHT11_DQ_IN);    //等待DHT11拉高电平
    while(DHT11_DQ_IN);     //等待DHT11拉低电平
}

uint8_t dht11_read_byte(void)
{
    
    uint8_t temp = 0;
    uint8_t i = 0;
    uint8_t read_data = 0;
    for(i = 0;i < 8;i ++ )
    {
        while(!DHT11_DQ_IN);
        delay_us(50);
        if(DHT11_DQ_IN == 1)
        {
            temp = 1;
            while(DHT11_DQ_IN);
        }
        else
            temp = 0;
    
        read_data = read_data << 1;
        read_data |= temp;
    }
    return read_data;
}

void dht11_read(uint8_t *result)
{
    //printf("hhh");
    
    uint8_t i = 0;
    //printf("aaa\r\n");
    dht11_start();
    //printf("bbb\r\n");
    dht11_gpio_input();
    //printf("ccc\r\n");
    for(i = 0;i < 5; i++)
        dht11_data[i] = dht11_read_byte();
    //printf("ddd\r\n");
    if(dht11_data[0] + dht11_data[1] + dht11_data[2] + dht11_data[3] == dht11_data[4])
    {
        memcpy(result, dht11_data, 4);
        dht11_data_humidity_left = 0;
        dht11_data_humidity_after = 0;
        dht11_data_temperature_left = 0;
        dht11_data_temperature_after = 0;
        
        //将温湿度信息传给4个变量,在main函数中调用对应get函数
        dht11_data_humidity_left = dht11_data[0];       
        dht11_data_humidity_after = dht11_data[1];
        dht11_data_temperature_left = dht11_data[2];
        dht11_data_temperature_after = dht11_data[3];
        
        dht11_data_humidity = dht11_data_humidity_left + dht11_data_humidity_after / 10.0;
        dht11_data_temperature = dht11_data_temperature_left + dht11_data_temperature_after / 10.0;
    }
    
}

uint8_t dht11_data_humidity_left_get(void)
{
    return dht11_data_humidity_left;
}

uint8_t dht11_data_humidity_after_get(void)
{
    return dht11_data_humidity_after;
}

uint8_t dht11_data_temperature_left_get(void)
{
    return dht11_data_temperature_left;
}

uint8_t dht11_data_temperature_after_get(void)
{
    return dht11_data_temperature_after;
}

float dht11_data_humidity_get(void)
{
    return dht11_data_humidity;
}

float dht11_data_temperature_get(void)
{
    return dht11_data_temperature;
}

三.烟雾传感器

1.简介与功能描述

通过ADC获取环境中烟雾传感器的值,并在模块代码中给出调用函数,抛出标志位。

2.模块代码

#include "adc.h"

ADC_HandleTypeDef adc_handle = {0};
DMA_HandleTypeDef dma_handle = {0};

uint16_t adc_value = 0;
void adc_config(void)
{
    adc_handle.Instance = ADC1;
    adc_handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
    adc_handle.Init.ScanConvMode = ADC_SCAN_DISABLE;
    adc_handle.Init.ContinuousConvMode = ENABLE;
    adc_handle.Init.NbrOfConversion = 1;
    adc_handle.Init.DiscontinuousConvMode = DISABLE;
    adc_handle.Init.NbrOfDiscConversion = 0;
    adc_handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
    HAL_ADC_Init(&adc_handle);
    
    HAL_ADCEx_Calibration_Start(&adc_handle);
}

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    if(hadc->Instance == ADC1)
    {
        RCC_PeriphCLKInitTypeDef adc_clk_init = {0};
        GPIO_InitTypeDef gpio_init_struct = {0};
        
        __HAL_RCC_ADC1_CLK_ENABLE();
        __HAL_RCC_GPIOB_CLK_ENABLE();
        
        gpio_init_struct.Pin = GPIO_PIN_0;
        gpio_init_struct.Mode = GPIO_MODE_ANALOG;
        HAL_GPIO_Init(GPIOB, &gpio_init_struct);
        
        adc_clk_init.PeriphClockSelection = RCC_PERIPHCLK_ADC;
        adc_clk_init.AdcClockSelection = RCC_ADCPCLK2_DIV6;
        HAL_RCCEx_PeriphCLKConfig(&adc_clk_init);
    }
}

void dma_config(void)
{
    __HAL_RCC_DMA1_CLK_ENABLE();
    dma_handle.Instance = DMA1_Channel1;
    dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;
    
    //内存相关配置
    dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    dma_handle.Init.MemInc = DMA_MINC_ENABLE;
    
    //外设相关配置
    dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;
    
    dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;
    dma_handle.Init.Mode = DMA_CIRCULAR;
    HAL_DMA_Init(&dma_handle);
    
    __HAL_LINKDMA(&adc_handle, DMA_Handle, dma_handle);
}

void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch, uint32_t rank, uint32_t stime)
{
    ADC_ChannelConfTypeDef adc_ch_config = {0};
    
    adc_ch_config.Channel = ch;
    adc_ch_config.Rank = rank;
    adc_ch_config.SamplingTime = stime;
    HAL_ADC_ConfigChannel(hadc, &adc_ch_config);
}

void adc_dma_init(void)
{
    adc_config();
    adc_channel_config(&adc_handle, ADC_CHANNEL_8, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
    dma_config();
    
    HAL_ADC_Start_DMA(&adc_handle, (uint32_t *)&adc_value, 1);
}

float adc_get_smoke(void)
{
    return (float)adc_value / 4096 * 3.3;
}

四.DHT11

1.简介与功能描述

通过DHT11模块,获取环境中的温湿度值

2.模块代码

#include "dht11.h"
#include "delay.h"
#include "string.h"
#include "stdio.h"
#include "led.h"



char dht11_data[5] = {0};

uint8_t dht11_data_humidity_left = 0;
uint8_t dht11_data_humidity_after = 0;
uint8_t dht11_data_temperature_left = 0;
uint8_t dht11_data_temperature_after = 0;

float dht11_data_humidity = 0.0;
float dht11_data_temperature = 0.0;


extern float limit_value;           //串口打印烟雾阈值时使用

void dht11_gpio_input(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    DHT11_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = DHT11_PIN;
    gpio_initstruct.Mode = GPIO_MODE_INPUT;                     //输入
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}

void dht11_gpio_output(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    DHT11_CLK_ENABLE();
    
    //调用GPIO初始化函数
    gpio_initstruct.Pin = DHT11_PIN;         
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;                 //推挽输出
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}

void dht11_start(void)
{
    dht11_gpio_output();
    DHT11_DQ_OUT(1);
    DHT11_DQ_OUT(0);
    delay_ms(20);
    DHT11_DQ_OUT(1);
    
    dht11_gpio_input();
    while(DHT11_DQ_IN);     //等待DHT11拉低电平
    while(!DHT11_DQ_IN);    //等待DHT11拉高电平
    while(DHT11_DQ_IN);     //等待DHT11拉低电平
}

uint8_t dht11_read_byte(void)
{
    
    uint8_t temp = 0;
    uint8_t i = 0;
    uint8_t read_data = 0;
    for(i = 0;i < 8;i ++ )
    {
        while(!DHT11_DQ_IN);
        delay_us(50);
        if(DHT11_DQ_IN == 1)
        {
            temp = 1;
            while(DHT11_DQ_IN);
        }
        else
            temp = 0;
    
        read_data = read_data << 1;
        read_data |= temp;
    }
    return read_data;
}

void dht11_read(uint8_t *result)
{
    //printf("hhh");
    
    uint8_t i = 0;
    //printf("aaa\r\n");
    dht11_start();
    //printf("bbb\r\n");
    dht11_gpio_input();
    //printf("ccc\r\n");
    for(i = 0;i < 5; i++)
        dht11_data[i] = dht11_read_byte();
    //printf("ddd\r\n");
    if(dht11_data[0] + dht11_data[1] + dht11_data[2] + dht11_data[3] == dht11_data[4])
    {
        memcpy(result, dht11_data, 4);
        dht11_data_humidity_left = 0;
        dht11_data_humidity_after = 0;
        dht11_data_temperature_left = 0;
        dht11_data_temperature_after = 0;
        
        //将温湿度信息传给4个变量,在main函数中调用对应get函数
        dht11_data_humidity_left = dht11_data[0];       
        dht11_data_humidity_after = dht11_data[1];
        dht11_data_temperature_left = dht11_data[2];
        dht11_data_temperature_after = dht11_data[3];
        
        dht11_data_humidity = dht11_data_humidity_left + dht11_data_humidity_after / 10.0;
        dht11_data_temperature = dht11_data_temperature_left + dht11_data_temperature_after / 10.0;
    }
    
}

uint8_t dht11_data_humidity_left_get(void)
{
    return dht11_data_humidity_left;
}

uint8_t dht11_data_humidity_after_get(void)
{
    return dht11_data_humidity_after;
}

uint8_t dht11_data_temperature_left_get(void)
{
    return dht11_data_temperature_left;
}

uint8_t dht11_data_temperature_after_get(void)
{
    return dht11_data_temperature_after;
}

float dht11_data_humidity_get(void)
{
    return dht11_data_humidity;
}

float dht11_data_temperature_get(void)
{
    return dht11_data_temperature;
}

五.HC-05

1.简介与功能描述

使用蓝牙模块进行串口通信,打印实时状态信息,并可以通过串口透传,将信息上传至上位机。

2.模块代码(使用串口代码模块

#include "sys.h"
#include "uart1.h"
#include "string.h"

UART_HandleTypeDef uart1_handle;                                            /* UART1句柄 */

uint8_t uart1_rx_buf[UART1_RX_BUF_SIZE];                                    /* UART1接收缓冲区 */
uint16_t uart1_rx_len = 0;                                                  /* UART1接收字符长度 */

/**
 * @brief       重定义fputc函数
 * @note        printf函数最终会通过调用fputc输出字符串到串口
 */
int fputc(int ch, FILE *f)
{
    while ((USART1->SR & 0X40) == 0);                                       /* 等待上一个字符发送完成 */

    USART1->DR = (uint8_t)ch;                                               /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}

/**
 * @brief       串口1初始化函数
 * @param       baudrate: 波特率, 根据自己需要设置波特率值
 * @retval      无
 */
void uart1_init(uint32_t baudrate)
{
    /*UART1 初始化设置*/
    uart1_handle.Instance = USART1;                                         /* USART1 */
    uart1_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
    uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
    uart1_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
    uart1_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
    uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
    uart1_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
    HAL_UART_Init(&uart1_handle);                                           /* HAL_UART_Init()会使能UART1 */
}

/**
 * @brief       UART底层初始化函数
 * @param       huart: UART句柄类型指针
 * @note        此函数会被HAL_UART_Init()调用
 *              完成时钟使能,引脚配置,中断配置
 * @retval      无
 */
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef gpio_init_struct;

    if (huart->Instance == USART1)                                          /* 如果是串口1,进行串口1 MSP初始化 */
    {
        __HAL_RCC_GPIOA_CLK_ENABLE();                                       /* 使能串口TX脚时钟 */
        __HAL_RCC_USART1_CLK_ENABLE();                                      /* 使能串口时钟 */

        gpio_init_struct.Pin = GPIO_PIN_9;                                  /* 串口发送引脚号 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                            /* 复用推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                                /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;                      /* IO速度设置为高速 */
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);
                
        gpio_init_struct.Pin = GPIO_PIN_10;                                 /* 串口RX脚 模式设置 */
        gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;    
        HAL_GPIO_Init(GPIOA, &gpio_init_struct);                            /* 串口RX脚 必须设置成输入模式 */
        
        HAL_NVIC_EnableIRQ(USART1_IRQn);                                    /* 使能USART1中断通道 */
        HAL_NVIC_SetPriority(USART1_IRQn, 3, 3);                            /* 组2,最低优先级:抢占优先级3,子优先级3 */

        __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);                          /* 使能UART1接收中断 */
        __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);                          /* 使能UART1总线空闲中断 */
    }
}

/**
 * @brief       UART1接收缓冲区清除
 * @param       无
 * @retval      无
 */
void uart1_rx_clear(void)
{
    memset(uart1_rx_buf, 0, sizeof(uart1_rx_buf));                          /* 清空接收缓冲区 */
    uart1_rx_len = 0;                                                       /* 接收计数器清零 */
}

/**
 * @brief       串口1中断服务函数
 * @note        在此使用接收中断及空闲中断,实现不定长数据收发
 * @param       无
 * @retval      无
 */
void USART1_IRQHandler(void)
{
    uint8_t receive_data = 0;   
    if(__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_RXNE) != RESET){        /* 获取接收RXNE标志位是否被置位 */
        if(uart1_rx_len >= sizeof(uart1_rx_buf))                            /* 如果接收的字符数大于接收缓冲区大小, */
            uart1_rx_len = 0;                                               /* 则将接收计数器清零 */
        HAL_UART_Receive(&uart1_handle, &receive_data, 1, 1000);            /* 接收一个字符 */
        uart1_rx_buf[uart1_rx_len++] = receive_data;                        /* 将接收到的字符保存在接收缓冲区 */
    }

    if (__HAL_UART_GET_FLAG(&uart1_handle, UART_FLAG_IDLE) != RESET)        /* 获取接收空闲中断标志位是否被置位 */
    {
        printf("recv: %s\r\n", uart1_rx_buf);                               /* 将接收到的数据打印出来 */
        uart1_rx_clear();
        __HAL_UART_CLEAR_IDLEFLAG(&uart1_handle);                           /* 清除UART总线空闲中断 */
    }
}

六.SUBtoTTL

1.简介与功能描述

用于单片机与电脑连接,方便将程序下载到单片机。

2.所用型号和实物图

本项目中我使用的是CH340模块,实物图如下:

七.防护模块

1.简介与功能描述

当环境中的温湿度值超过设定的阈值时,系统会自动打开风扇通风,以降低温湿度。

2.模块代码

if(smoke_value >= smoke_limit_value)                //电机驱动程序开启前提下,超过烟雾传感器阈值,则警报并关闭电机                                                         ,抛出标志位
                    {
                        fan_off();
                        lcd1602_display_fan_off();
                        fan_on_off_flag_main = OFF;
                        
                        beep_on();
                        lcd1602_display_beep_on();
                        lcd1602_show_line(2, 10 ," ");
                        beep_on_off_flag_main = ON;
                        
                    }
                    if(smoke_value < smoke_limit_value)                //电机驱动程序开启前提下,未超过烟雾传感器阈值,则不警报,并抛出标志位
                    {
                        fan_on();
                        lcd1602_display_fan_on();
                        lcd1602_show_line(2, 15 ," ");
                        fan_on_off_flag_main = ON;
                        
                        beep_off();
                        lcd1602_display_beep_off();
                        beep_on_off_flag_main = OFF;
                    }

八.报警模块

1.简介与功能描述

在打开风扇时,如果监测到环境中烟雾值超过设定的烟雾阈值时,为防止火灾蔓延,系统会自动关闭电机驱动的风扇,并打开报警器持续鸣响,直至烟雾值低于设定的阈值或者温湿度降低到设定的阈值以下。

2.模块代码(与防护模块代码写在一起

if(smoke_value >= smoke_limit_value)                //电机驱动程序开启前提下,超过烟雾传感器阈值,则警报并关闭电机                                                         ,抛出标志位
                    {
                        fan_off();
                        lcd1602_display_fan_off();
                        fan_on_off_flag_main = OFF;
                        
                        beep_on();
                        lcd1602_display_beep_on();
                        lcd1602_show_line(2, 10 ," ");
                        beep_on_off_flag_main = ON;
                        
                    }
                    if(smoke_value < smoke_limit_value)                //电机驱动程序开启前提下,未超过烟雾传感器阈值,则不警报,并抛出标志位
                    {
                        fan_on();
                        lcd1602_display_fan_on();
                        lcd1602_show_line(2, 15 ," ");
                        fan_on_off_flag_main = ON;
                        
                        beep_off();
                        lcd1602_display_beep_off();
                        beep_on_off_flag_main = OFF;
                    }

九.显示及控制

1.简介与功能描述

在LCD1602上实时显示环境中的温湿度值,烟雾值,电机风扇和报警器的开关状态。实时发送给串口助手显示,并通过蓝牙串口透传实时发送给上位机APP显示。当修改温湿度阈值和烟雾阈值时,也会在LCD,串口助手,上位机APP显示相关信息。当风扇或报警器打开时,也同样会在三者显示风扇和警报的状态信息。

2.模块代码(具体的数据信息需要调用其他模块的显示函数)

#include "lcd1602.h"
#include "delay.h"
#include "stdio.h"
#include "led.h"



//RS引脚定义
#define RS_GPIO_Port    GPIOB
#define RS_GPIO_Pin     GPIO_PIN_1
#define RS_HIGH     HAL_GPIO_WritePin(RS_GPIO_Port, RS_GPIO_Pin, GPIO_PIN_SET);
#define RS_LOW      HAL_GPIO_WritePin(RS_GPIO_Port, RS_GPIO_Pin, GPIO_PIN_RESET);

//RW引脚定义
#define RW_GPIO_Port    GPIOB
#define RW_GPIO_Pin     GPIO_PIN_10
#define RW_HIGH     HAL_GPIO_WritePin(RW_GPIO_Port, RW_GPIO_Pin, GPIO_PIN_SET);
#define RW_LOW      HAL_GPIO_WritePin(RW_GPIO_Port, RW_GPIO_Pin, GPIO_PIN_RESET);

//EN引脚定义
#define EN_GPIO_Port    GPIOB
#define EN_GPIO_Pin     GPIO_PIN_11
#define EN_HIGH     HAL_GPIO_WritePin(EN_GPIO_Port, EN_GPIO_Pin, GPIO_PIN_SET);
#define EN_LOW      HAL_GPIO_WritePin(EN_GPIO_Port, EN_GPIO_Pin, GPIO_PIN_RESET);

void lcd1602_init(void)
{
    //初始化GPIO
    lcd1602_gpio_init();
    //上电初始化
    lcd1602_start();
}

void lcd1602_gpio_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    
    gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4
                         | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7;          // 两个LED对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;             // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP;                     // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;           // 高速
    HAL_GPIO_Init(GPIOA, &gpio_initstruct);
    
    gpio_initstruct.Pin = GPIO_PIN_1 | GPIO_PIN_10 | GPIO_PIN_11;          // 两个LED对应的引脚
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;             // 推挽输出
    gpio_initstruct.Pull = GPIO_PULLUP;                     // 上拉
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;           // 高速
    HAL_GPIO_Init(GPIOB, &gpio_initstruct);
}

void lcd1602_start(void)
{
//(1) 延时 15ms
    delay_ms(15);
//(2) 写指令 38H(不检测忙信号)
    lcd1602_write_cmd(0x38);
//(3) 延时 5ms
    delay_ms(5);
//(4) 以后每次写指令,读/写数据操作均需要检测忙信号
//(5) 写指令 38H:显示模式设置
    lcd1602_write_cmd(0x38);
//(6) 写指令 08H:显示关闭
    lcd1602_write_cmd(0x08);
//(7) 写指令 01H:显示清屏
    lcd1602_write_cmd(0x01);
//(8) 写指令 06H:显示光标移动设置
    lcd1602_write_cmd(0x06);
//(9) 写指令 0CH:显示开及光标设置
    lcd1602_write_cmd(0x0C);
}

void lcd1602_write_cmd(char cmd)
{
    RS_LOW;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = cmd;
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;
}

void lcd1602_write_data(char dataShow)
{
    RS_HIGH;
    RW_LOW;
    EN_LOW;
    GPIOA->ODR = dataShow;
    delay_ms(5);
    EN_HIGH;
    delay_ms(5);
    EN_LOW;
}

void lcd1602_show_char()
{
    //在哪里显示?
    lcd1602_write_cmd(0x80 + 0x02);
    //显示什么?
    lcd1602_write_data('L');
}

void lcd1602_show_line(char row, char col, char *string)
{
    switch(row)
    {
        case 1:
            lcd1602_write_cmd(0x80 + col);
            while(*string)
            {
                lcd1602_write_data(*string);
                string++;
            }
            break;
        
        case 2:
            lcd1602_write_cmd(0x80 + 0x40 + col);
            while(*string)
            {
                lcd1602_write_data(*string);
                string++;
            }
            break;
    }
}

void lcd1602_display_smoke(float smoke_value)
{
    char msg[16] = {0};
    sprintf(msg, "S:%0.1fV",smoke_value);
    lcd1602_show_line(2, 0, msg);
}

void lcd1602_display_humidity(float dht11_data_humidity)
{
    
    char msg[16] = {0};
    sprintf(msg, "H:%0.1fRH", dht11_data_humidity);
    lcd1602_show_line(1, 0, msg);
    //   "湿度:%d.%dRH,",dht11_data[0],dht11_data[1]
    
    
    
}

void lcd1602_display_temperature(float dht11_data_temperature)
{
    char msg[16] = {0};
    sprintf(msg, "T:%0.1fC", dht11_data_temperature);
    lcd1602_show_line(1, 9, msg);
}

void lcd1602_display_smoke_limit(float limit_value)
{
    char msg[16] = {0};
    sprintf(msg, "Limit:   %0.1f V",limit_value);
    lcd1602_show_line(2, 1, msg);
    sprintf(msg, "Smoke");
    lcd1602_show_line(1, 5, msg);
}

//获取温度数值
void lcd1602_display_temperature_limit(float limit_value)
{
    char msg[16] = {0};
    sprintf(msg, "Limit:   %0.1f V",limit_value);
    lcd1602_show_line(2, 1, msg);
    sprintf(msg, "temperature");
    lcd1602_show_line(1, 2, msg);
}

//获取湿度数值
void lcd1602_display_humidity_limit(float limit_value)
{
    char msg[16] = {0};
    sprintf(msg, "Limit:   %0.1f V",limit_value);
    lcd1602_show_line(2, 1, msg);
    sprintf(msg, "Humidity");
    lcd1602_show_line(1, 4, msg);
}



void lcd1602_display_beep_on(void)
{
    lcd1602_show_line(2, 7, "bon");
}

void lcd1602_display_beep_off(void)
{
    lcd1602_show_line(2, 7, "boff");
}

void lcd1602_display_fan_on(void)
{
    lcd1602_show_line(2, 12, "fon");
}

void lcd1602_display_fan_off(void)
{
    lcd1602_show_line(2, 12, "foff");
}

十.多线程

1.描述

使用Systick系统滴答定时器模拟多线程,使得按键中断的引入不会破坏显示进程,实时获取数据进程的运行。本项目中设立了两条线程。

2.代码

#include "threads.h"
#include "led.h"


uint32_t thread1_cnt = 0;                  //每1ms进一次systic中断,计一次数,达到需要的ms数后清零并写操作
uint32_t thread2_cnt = 0;

uint8_t thread1_flag = 0;
uint8_t thread2_flag = 0;

void systick_threads(void)
{
    if( thread1_cnt < 1000 )
        thread1_cnt ++;
    else
    {
        thread1_cnt = 0;
        thread1_flag = 1;
        //led1_toggle();        //测试线程1计数
    }
    
    if( thread2_cnt < 2000 )
        thread2_cnt ++;
    else
    {
        thread2_cnt = 0;
        thread2_flag = 1;
        //led2_toggle();        //测试线程2计数
    }
}

uint8_t thread1_flag_get(void)
{
    uint8_t temp = thread1_flag;
    thread1_flag = 0;
    return temp;
}

uint8_t thread2_flag_get(void)
{
    uint8_t temp = thread2_flag;
    thread2_flag = 0;
    return temp;
}

十一.调试

1.描述

项目撰写调试时,难免会出现实验现象不如预期或出现错误,这是就需要找出程序哪里出了纰漏。

2.方法

我常用的检查方法有两种:

1.使用软件的自带调试工具

像keil5等代码编写软件都带有调试工具,通过打断点等方式控制程序运行,很方便得知道程序哪里出现纰漏。

2.使用led灯或屏幕,串口打印显示

通常情况下,程序现象与预期有偏差,只需在现象偏差模块使用显示信息就可以判断出哪写程序出现纰漏。

相比之下,第二种更直观,第一种更精确。大家根据自己习惯选择使用即可。

十二.项目总结

1.收获

对单片机运行原理的理解更加深刻,对相关模块的使用更熟练。建议初学者一定要亲手连接硬件、编写代码,这样才能对项目有更深刻的理解。

2.疑难及解决

不使用FreeRTOS的情况下,如果使用按键中断,但又不能破坏显示进程的正常运行,需要引入Systick模拟多线程,此项功能添加会导致按键中断产生时,显示模块的随机跳变,经过查找和思考,最后知问题出在中断优先级的考量和设定上,修改后也是终于把问题解决。

3.杂记点

通过项目的实操,我认为知识学习时一定要有全局观,不能各个知识点分别学习,而是要积极寻找各知识点之间的关系,这样在将知识运用于实践时,会有类似于清晰框架的胸有成竹的感觉。

当然,如果文章对你有帮助,记得给好兄弟点个赞,虽然没有收益,但却能给我大大的鼓励。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值