项目1:STM32+DHT11+FreeRTOS+emwin+LCD

第一部分!!!!!!!************


【屏幕显示DHT11数据】

     面向对象的思想编写硬件驱动程序,DHT11采集环境中的温湿度数据。使用FreeRTOS提供的任务间通信、同步、互斥,将DHT11的数据传递给显示任务。显示任务中,使用emWin中间件,制作屏幕的各种界面,并将DHT11的数据显示到屏幕上。

项目开发流程:

第一个项目内容:屏幕显示DHT11数据

       面向对象的思想编写硬件驱动程序,DHT11采集环境中的温湿度数据。使用FreeRTOS提供的任务间通信、同步、互斥,将DHT11的数据传递给显示任务。显示任务中,使用emWin中间件,制作屏幕的各种界面,并将DHT11的数据显示到屏幕上。

项目框架:

TFT彩屏

TFT-LCD 即薄膜晶体管液晶显示器。其英文全称为:Thin Film Transistor-Liquid Crystal Display。TFT-LCD与无源 TN-LCD、STN-LCD 的简单矩阵不同,它在液晶显示屏的每一个象素上都设置有一个薄膜晶体管(TFT),可有效地克服非选通时的串扰,使显示液晶屏的静态特性与扫描线数无关。     

TFT-LCD具有:亮度好、对比度高、层次感强、颜色鲜艳等特点。应用于电视、手机、电脑、平板等各种电子产品。

分辨率:240*320 驱动IC:ILI9341 自带触摸屏(电阻触摸屏) 16位80并口驱动 16位真彩显示(65536色)

TFT彩屏引脚定义

TFT写数据

TFT读数据

TFT控制框图

DHT11模块简介

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。

 传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。

DHT11电路图

DHT11参数

测量分辨率分别为 8bit(温度)、8bit(湿度)。

DHT11传输数据:串行接口(单线双向)

DHT11采用单总线协议与stm32通信。DHT11只有在接收到开始信号后才会触发温湿度采集。数据采集完毕且无开始信号后,DHT11自动切换到低速模式(复位信号触发DHT11从低速到高速模式)

(1)一次完整的数据传输为40bit,高位先出。

(2)数据格式:
8bit湿度整数数据 + 8bit湿度小数数据 + 8bi温度整数数据 + 8bit温度小数数据 + 8bit校验和

(3)数据传送正确时,校验和数据等于
“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位

注:DHT11传输时,时序非常严格,不允许各种意外情况打断。

DHT11时序 (通信过程)

如下图所示,用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集,用户可选择读取部分数据。

从模式下,DHT11接收到开始信号触发一次温湿度采集,如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集。采集数据后转换到低速模式。

如下图所示,总线空闲状态为高电平,主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号。DHT11接收到主机的开始信号后,等待主机开始信号结束,然后发送80us低电平响应信号。主机发送开始信号结束后,延时等待20-40us后,读取DHT11的响应信号,主机发送开始信号后,可以切换到输入模式,或者输出高电平均可,总线由上拉电阻拉高。

如上图所示,总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高80us,准备发送数据。

DHT11数据位格式 

如下图所示每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1。如果读取响应信号为高电平,则DHT11没有响应,请检查线路是否连接正常。当最后一bit数据传送完毕后,DHT11拉低总线50us,随后总线由上拉电阻拉高进入空闲状态。
     数字0信号表示方法如下图所示:                             数字1信号表示方法如下图所示:

数字0信号与数字1信号的不同之处在于高电平的时间不同,利用这点,我么们可以通过设置电平时间阈值来判断信号的种类。

DHT11驱动程序

DHT11.c

 配置输入输出GPIO:

复位DHT11

复位DHT11就是发送DHT11起始信号,告诉传感器通讯开始。

 

检查DHT11是否正常

检查DHT11是否正常,正常的话会在单片机发送起始信号完成后,传感器返回80us低电平,然后发送80us高电平。即证明DHT11工作正常,该函数工作正常返回0,否则返回1,该函数中利用了while循环检测在一定时间内的电平变化,此类用法在后面也会经常用到。

读取一位数据(返回值0/1)

该函数采用两个while循环是等待每个周期的电平变化,先等待低电平到来,后等待高电平到来,延时40us后判断引脚电平,来判断该位数据为1或0。之所以是40微秒是因为传感器数字0的信号持续时间为26-28us,数字1的信号持续时间为70us,选择一个中值来区分两种信号,当然也可以选择其他值,但最好在40us附近,在while循环中选择循环100次也就是100us,是因为防止当单片机由于某些原因迟迟收不到传感器电平信号,造成死机。

读取一个字节(返回值:读到的数据)

循环读入一个字节的数据,并将每一步新加入的数据放置在最低位。

读取DHT11数据(读取成功返回0,失败返回1)

读取数据将数据存入数组,这里仅保留了温度数据的整数位,注意数据较验方法,校验和数据等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位

完整版.c文件
#include "dht11.h"
#include "delay.h"



#define    DHT11_DelayMs(t)                      Delay_Ms(t)
#define    DHT11_DelayUs(t)                      Delay_Us(t)


#define    DHT11_PIN_HIGH                        1
#define    DHT11_PIN_LOW                         0 


#if  defined  (STM32F40_41xxx)

#include "stm32f4xx.h"
//配置输入输出GPIO:

//温湿度模块输入函数
//浮空输入+设置IO口速度+选择端口
#define    __DHT11_CONFIG_IO_INPUT(DHT11)           {  GPIO_InitTypeDef GPIO_InitStructure; \ 
                                                       GPIO_InitStructure.GPIO_Mode     = GPIO_Mode_IN; \
                                                       GPIO_InitStructure.GPIO_PuPd     = GPIO_PuPd_UP; \
                                                       GPIO_InitStructure.GPIO_Speed    = GPIO_Speed_50MHz; \
                                                       GPIO_InitStructure.GPIO_Pin      = DHT11->pin; \
                                                       GPIO_Init(DHT11->port, &GPIO_InitStructure); \
                                                     }
//温湿度模块输入函数
//推挽输出+设置IO口速度+选择端口
#define    __DHT11_CONFIG_IO_OUTPUT(DHT11)          {  GPIO_InitTypeDef GPIO_InitStructure; \
                                                       GPIO_InitStructure.GPIO_Mode     = GPIO_Mode_OUT; \
                                                       GPIO_InitStructure.GPIO_OType    = GPIO_OType_PP; \  
                                                       GPIO_InitStructure.GPIO_PuPd     = GPIO_PuPd_UP; \
                                                       GPIO_InitStructure.GPIO_Speed    = GPIO_Speed_50MHz; \
                                                       GPIO_InitStructure.GPIO_Pin      = DHT11->pin; \
                                                       GPIO_Init(DHT11->port, &GPIO_InitStructure); \
                                                     }

#define    __DHT11_IO_SET(DHT11, value)             {  if (value == DHT11_PIN_HIGH) \
                                                            GPIO_SetBits(DHT11->port, DHT11->pin); \
                                                        else \
                                                            GPIO_ResetBits(DHT11->port, DHT11->pin); \
                                                     }

#define    DHT11_IO_H(DHT11)                        {__DHT11_IO_SET(DHT11, DHT11_PIN_HIGH)}
#define    DHT11_IO_L(DHT11)                        {__DHT11_IO_SET(DHT11, DHT11_PIN_LOW)}
#define    DHT11_IO_IN(DHT11)                       GPIO_ReadInputDataBit(DHT11->port, DHT11->pin)

#endif




/******************************************************************************
 * @brief      复位DHT11
 *
 * @param[in]  dht11  :  dht11结构体指针
 *
 * @return     0, 表示正常, 其他值表示失败
 *
 ******************************************************************************/
//根据DHT11时序图
//复位DHT11:发送DHT11起始信号,告诉传感器通讯开始
static int DHT11_Rst(DHT11_t *dht11)     //DHT端口复位,发出起始信号(IO发送)
{
    __DHT11_CONFIG_IO_OUTPUT(dht11);    //设置引脚为输出模式
    DHT11_IO_L(dht11);                  //拉低DQ
    DHT11_DelayMs(20);                  //拉低至少18ms  拉低20ms
    DHT11_IO_H(dht11);                  //DQ=1 
    DHT11_DelayUs(30);                  //主机拉高20~40us
    __DHT11_CONFIG_IO_INPUT(dht11);     //设置引脚为输入模式
   //随后主机开始读取
    return 0;
}

//复位DHT11可以理解为开始


/******************************************************************************
 * @brief      等待DHT11的回应
 *
 * @param[in]  dht11  :  dht11结构体指针
 *
 * @return     0, 存在, 返回1:未检测到DHT11的存在
 *
 ******************************************************************************/
//检查DHT11是否正常:检查DHT11是否正常,正常的话会在单片机发送起始信号完成后,传感器返回80us低电平,然后发送80us高电平。
static int DHT11_Check(DHT11_t *dht11)  //读取引脚的状态 retry临界值
{
    int retry = 0; 
    while (DHT11_IO_IN(dht11) && (retry < 100)) {   //DHT11会拉低40-80us
        retry++;
        DHT11_DelayUs(1);
    };

    if (retry >= 100) {       //超过100ms说明统计没有相应
        return -2;
    } else {
        retry = 0;
    }

    while (!DHT11_IO_IN(dht11) && (retry < 100)) {  //DHT11拉低后会再次拉高40-80us
        retry++;
        DHT11_DelayUs(1);
    };
    if (retry >= 100) {
        return -3;
    }

    return 0;
}




/******************************************************************************
以上没有问题就可以传输数据了
 * @brief      从DHT11读取一个位
 *
 * @param[in]  dht11  :  dht11结构体指针
 *
 * @return     0, 1 读取一位数据(返回值0/1)
 *该函数采用两个while循环是等待每个周期的电平变化,先等待低电平到来,
*等待高电平到来,延时40us后判断引脚电平,来判断该位数据为1或0。
 ******************************************************************************/
static uint8_t DHT11_ReadBit(DHT11_t *dht11)
{
    int retry = 0;
    while (DHT11_IO_IN(dht11) && (retry < 100)) {   //等待变为低电平
        retry++;
        DHT11_DelayUs(1);
    }

    retry = 0;
    while (!DHT11_IO_IN(dht11) && (retry < 100)) {  //等待变高电平
        retry++;
        DHT11_DelayUs(1);
    }

    DHT11_DelayUs(40);//等待40us
    if (DHT11_IO_IN(dht11)) {
        return 1;
    } else {
        return 0;
    }
}


/******************************************************************************
 * @brief      从DHT11读取一个字节
 *
 * @param[in]  dht11  :  dht11结构体指针
 *
 * @return     读到的数据
 *读取一个字节(返回值:读到的数据)
循环读入一个字节的数据,并将每一步新加入的数据放置在最低位。
 ******************************************************************************/
static uint8_t DHT11_ReadByte(DHT11_t *dht11)
{
    uint8_t i, dat;
    dat = 0;
    for (i = 0; i < 8; i++) {
        dat <<= 1;
        dat |= DHT11_ReadBit(dht11);
    }

    return dat;
}




/******************************************************************************
 * @brief      从DHT11读取一次数据    把协议全部放进去
 *                   temp:温度值(范围:0~50°)
 *                   humi:湿度值(范围:20%~90%)
 *
 * @param[in]  dht11  :  dht11结构体指针
 *
 * @return     0, 表示正常, 其他值表示失败
 *读取DHT11数据(读取成功返回0,失败返回1)
读取数据将数据存入数组,这里仅保留了温度数据的整数位,注意数据较验方法,校验和数据
等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位。
 ******************************************************************************/
int DHT11_ReadData(DHT11_t *dht11)
{
    if (!dht11 || !dht11->init) 
        return -1;  //初始化引脚之后

    uint8_t buf[5];
    uint8_t i;
    DHT11_Rst(dht11);       //复位传感器

    if (DHT11_Check(dht11) == 0) {
        for (i = 0; i < 5; i++) {   //读取40位数据   40位 五个字节 连续读五次
            buf[i] = DHT11_ReadByte(dht11);
        }
        if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4]) {     //校验  相等说明传输数据么有问题
            dht11->humidity = buf[0];
            dht11->temperature = buf[2];             //放入温湿度
        }
    } else {
        return -2;
    }

    return 0;
}




/******************************************************************************
 * @brief      初始化DHT11的IO口 DQ 同时检测DHT11的存在.
 *DHT11初始化
在上电后,对IO端口初始化,和检查DHT11状态。
 * @param[in]  dht11  :  dht11结构体指针
 *
 * @return     0, 表示正常, 其他值表示失败
 *
 ******************************************************************************/
int DHT11_Init(DHT11_t *dht11, DHT11_GPIO_Port_t port, uint32_t pin)    //对外提供两个接口 触发温湿度传感器  读取数据
{
    if(!dht11) 
        return -1;

    //配置引脚,默认输出
#if defined (STM32F40_41xxx)

    assert_param(IS_GPIO_ALL_PERIPH(port));

    if (port  == GPIOA)        { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); }
    else if (port  == GPIOB)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); }
    else if (port  == GPIOC)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); }
    else if (port  == GPIOD)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); }
    else if (port  == GPIOE)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); }
    else if (port  == GPIOF)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); }
    else if (port  == GPIOG)   { RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE); }
    else return -1;

    __DHT11_CONFIG_IO_OUTPUT(dht11);

#endif

    dht11->port = port;
    dht11->pin = pin;
    dht11->temperature = 0.0;
    dht11->humidity = 0.0;
    dht11->init = true;

    DHT11_IO_H(dht11);
    DHT11_Rst(dht11);

    return DHT11_Check(dht11);
}

DHT11.h

#ifndef __DHT11_H
#define __DHT11_H 

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>


#if defined (STM32F40_41xxx)

#include "stm32f4xx.h"

typedef    GPIO_TypeDef*  DHT11_GPIO_Port_t;

#else

    #error dht11.h: No processor defined!

#endif

typedef struct dht11_t
{
    DHT11_GPIO_Port_t     port;             //所使用的端口
    uint32_t              pin;              //所使用的引脚
    double                temperature;      //温度值
    double                humidity;         //湿度
    bool                  init;             //初始化标志
}DHT11_t;


int   DHT11_Init(DHT11_t* dht11, DHT11_GPIO_Port_t port, uint32_t pin);         //初始化DHT11  使用哪个引脚
int   DHT11_ReadData(DHT11_t* dht11);                                           //获取DHT11的数据


#endif


main.c

#include "main.h"
DHT11_t gDht11;  //定义一个温湿度结构体
char show_buffer[100];
int main(void)
{
    Debug_Init(115200);
    Delay_Init();

    printf("DHT11 start\r\n");

    LCD_Init();          //初始化屏幕
    LCD_Clear(RED);      //屏幕显示红色
    Delay_Ms(1000);
    LCD_Clear(~RED);
    Delay_Ms(1000);

    /* 初始化DHT11 */
    int ret = DHT11_Init(&gDht11, GPIOA, GPIO_Pin_6);   //DHT11接到PA6引脚上去
    printf("ret: %d\r\n", ret);
    /* 每秒读取一次温湿度传感器,并通过串口发送 */
    while (1) {
        DHT11_ReadData(&gDht11);                               //读取温湿度传感器的数值
        memset(show_buffer, 0, sizeof(show_buffer));
        sprintf(show_buffer, "T: %0.2f   H: %0.2f\r\n", gDht11.temperature, gDht11.humidity);
        printf("%s", show_buffer);                             //显示
        LCD_ShowString(0, 0, 240, 30, 24, show_buffer);        //通过LCD屏幕显示
        Delay_Ms(1000);
        
    }
}


总结

介绍DHT11温湿度传感器,及STM32版本驱动函数的编写

后续将加入freertos和emwin

第二部分!!!!!!!************

先了解移植操作系统:

 

没有操作系统rtos的main.c代码:

#include "main.h"

DHT11_t gDht11;  //定义一个温湿度结构体
char show_buffer[100];
int main(void)
{
    Debug_Init(115200);
    Delay_Init();

    printf("DHT11 start\r\n");

    LCD_Init();          //初始化屏幕
    LCD_Clear(RED);      //屏幕显示红色
    Delay_Ms(1000);
    LCD_Clear(~RED);
    Delay_Ms(1000);

    /* 初始化DHT11 */
    int ret = DHT11_Init(&gDht11, GPIOA, GPIO_Pin_6);   //DHT11接到PA6引脚上去
    printf("ret: %d\r\n", ret);
    /* 每秒读取一次温湿度传感器,并通过串口发送 */
    while (1) {
        DHT11_ReadData(&gDht11);                               //读取温湿度传感器的数值
        memset(show_buffer, 0, sizeof(show_buffer));
        sprintf(show_buffer, "T: %0.2f   H: %0.2f\r\n", gDht11.temperature, gDht11.humidity);
        printf("%s", show_buffer);                             //显示
        LCD_ShowString(0, 0, 240, 30, 24, show_buffer);        //通过LCD屏幕显示
        Delay_Ms(1000);
        
    }
}


移植并创建操作系统rtos的main.c代码:=

#include "main.h"
TaskHandle_t DHT11Task_Handler;  /* 任务句柄 */ //可以理解为指针
TaskHandle_t LCDTask_Handler;    /* 任务句柄 */ 
DHT11_t gDht11;                  //结构体 用来存放
uint8_t show_buffer[100];




/*****************************************************
 * @brief       Task_DHT11
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 *
 ******************************************************/
void Task_DHT11(void *pvParameters)
{
    /* 初始化DHT11 */
    printf("Task_DHT11\r\n");
    int ret = DHT11_Init(&gDht11, GPIOA, GPIO_Pin_6);
    printf("ret: %d\r\n", ret);

    while (1) {

        /* 每秒读取一次温湿度传感器,并通过串口发送 */
        DHT11_ReadData(&gDht11);
        //显示出来
        printf("T: %0.2f   H: %0.2f\r\n", gDht11.temperature, gDht11.humidity);
        vTaskDelay(1000);
    }
}




/*****************************************************
 * @brief       Task_LCD
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 *
 ******************************************************/
void Task_LCD(void *pvParameters)
{

    while (1) {
        memset(show_buffer, 0, sizeof(show_buffer));
        sprintf((char *)show_buffer, "T: %0.2f   H: %0.2f\r\n", gDht11.temperature, gDht11.humidity);
        LCD_ShowString(0, 0, 240, 30, 24, show_buffer);
        vTaskDelay(1000);
    }
}


int main(void)
{
    Debug_Init(115200);
    Delay_Init();

    printf("DHT11 start\r\n");

    LCD_Init();
    LCD_Clear(WHITE);
    //读取出来
    xTaskCreate((TaskFunction_t )Task_DHT11,            /* 任务函数 */
                (const char*    )"Task_DHT11",          /* 任务名称 */
                (uint16_t       )128,                   /* 任务堆栈大小 */
                (void*          )NULL,                  /* 传入给任务函数的参数 */
                (UBaseType_t    )2,                     /* 任务优先级 */
                (TaskHandle_t*  )&DHT11Task_Handler);   /* 任务句柄 */
    //显示出来
    xTaskCreate((TaskFunction_t )Task_LCD,              /* 任务函数 */
                (const char*    )"Task_LCD",            /* 任务名称 */
                (uint16_t       )512,                   /* 任务堆栈大小 */
                (void*          )NULL,                  /* 传入给任务函数的参数 */
                (UBaseType_t    )1,                     /* 任务优先级 */
                (TaskHandle_t*  )&LCDTask_Handler);     /* 任务句柄 */
    //启动任务调度器
    vTaskStartScheduler();

    while (1) {
        Delay_Ms(1000);
        
    }
}


第三部分!!!!!!!!!!!***********************************

emwin:

emwin是什么(并发)

Emwin特点

Emwin效果图

emWin支持平台

emWin支持的硬件平台非常广泛,支持几乎所有的16位或32位微控制器,从ARM7、ARM9到Cortex-M3、Cortex-M4、 Cortex-M7再到Cortex-A9都能运行,甚至在Cortex-M0上也能跑。

Emwin框架

emWin配套的软件工具

STemWin库内容

移植用到的文件

移植emwin

代码:

最终代码:

#include "main.h"


/**************************************************************************
    @版权说明:在不收取他人费用的情况下,可以自由的分发本软件,分发时,保留本版权说明。
    @author  :梅花七月香,公众号:梅花七月香
    @data    :2024-2-25
    @brief   :定时器实验
*****************************************************************************/




extern WM_HWIN CreateWindow(void);
TaskHandle_t DHT11Task_Handler;  /* 任务句柄 */
TaskHandle_t LCDTask_Handler;    /* 任务句柄 */
DHT11_t gDht11;       
char show_buffer[100];
WM_HWIN   hItemH, hItemT;




/*****************************************************
 * @brief       Task_DHT11
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 *
 ******************************************************/
void Task_DHT11(void *pvParameters)
{
    /* 初始化DHT11 */
    int ret = DHT11_Init(&gDht11, GPIOA, GPIO_Pin_6);
    printf("ret: %d\r\n", ret);

    while (1) {

        /* 每秒读取一次温湿度传感器,并通过串口发送 */
        DHT11_ReadData(&gDht11);

        printf("T: %0.2f   H: %0.2f\r\n", gDht11.temperature, gDht11.humidity);
        vTaskDelay(1000);
    }
}




/*****************************************************
 * @brief       Task_LCD
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 *
 ******************************************************/
void Task_LCD(void *pvParameters)
{
    int i = 0;

    GUI_Init();              //初始化总入口
    CreateWindow();          //

    while (1) {
        printf("Task_LCD:  %d\r\n", i++);
        memset(show_buffer, 0, sizeof(show_buffer));
        sprintf((char *)show_buffer, "%0.2f", gDht11.temperature);
        EDIT_SetText(hItemT, show_buffer);

        memset(show_buffer, 0, sizeof(show_buffer));
        sprintf((char *)show_buffer, "%0.2f", gDht11.humidity);
        EDIT_SetText(hItemH, show_buffer);
        GUI_Delay(1000);
    }
}




int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);      //中断分组配置
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE);  //开启CRC时钟
    Debug_Init(115200);
    Delay_Init();

    ILI9341_Init();                                      //初始化硬件

    xTaskCreate((TaskFunction_t )Task_DHT11,            /* 任务函数 */
                (const char*    )"Task_DHT11",          /* 任务名称 */
                (uint16_t       )128,                   /* 任务堆栈大小 */
                (void*          )NULL,                  /* 传入给任务函数的参数 */
                (UBaseType_t    )2,                     /* 任务优先级 */
                (TaskHandle_t*  )&DHT11Task_Handler);   /* 任务句柄 */

    xTaskCreate((TaskFunction_t )Task_LCD,              /* 任务函数 */
            (const char*    )"Task_LCD",            /* 任务名称 */
            (uint16_t       )512,                   /* 任务堆栈大小 */
            (void*          )NULL,                  /* 传入给任务函数的参数 */
            (UBaseType_t    )1,                     /* 任务优先级 */
            (TaskHandle_t*  )&LCDTask_Handler);     /* 任务句柄 */

    vTaskStartScheduler();

    while(1)
    {
    
    }
}

第四部分!!!!!!!!!!!***********************************

加入freertos队列,信号量,锁:

main.c源代码:

#include "main.h"


/**************************************************************************
    @版权说明:在不收取他人费用的情况下,可以自由的分发本软件,分发时,保留本版权说明。
    @author  :梅花七月香,公众号:梅花七月香
    @data    :2024-2-25
    @brief   :定时器实验
*****************************************************************************/




extern WM_HWIN CreateWindow(void);
TaskHandle_t DHT11Task_Handler;    /* 任务句柄 */
TaskHandle_t LCDTask_Handler;      /* 任务句柄 */
TaskHandle_t TouchTask_Handler;    /* 任务句柄 */
char show_buffer[100];
DHT11_t gDht11;
LED_t gLed[3];
WM_HWIN   hItemH, hItemT;
QueueHandle_t  gDHT112EmWin_Queue;
xSemaphoreHandle gDHT112EmWin_Queue_Mutex;




/*****************************************************
 * @brief       Task_DHT11
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 *
 ******************************************************/
void Task_DHT11(void *pvParameters)
{

    float   dht11_data[2] = {0};
    /* 初始化DHT11 */
    int ret = DHT11_Init(&gDht11, GPIOA, GPIO_Pin_6);
    printf("ret: %d\r\n", ret);
    //初始化队列  队列深度为5 
    gDHT112EmWin_Queue = xQueueCreate(5, sizeof(float) * 2);
    if (!gDHT112EmWin_Queue) {
        printf("create emwin queue fail\r\n");
    }
    //
    gDHT112EmWin_Queue_Mutex = xSemaphoreCreateMutex(); //队列
    if (!gDHT112EmWin_Queue_Mutex) {
        printf("create emwin queue mutex  fail\r\n");
    }

    while (1) {

        /* 每秒读取一次温湿度传感器,并通过串口发送 */
        DHT11_ReadData(&gDht11);

        dht11_data[0] = gDht11.temperature;
        dht11_data[1] = gDht11.humidity;

        xSemaphoreTake(gDHT112EmWin_Queue_Mutex, portMAX_DELAY);     //上锁
        xQueueSend(gDHT112EmWin_Queue, dht11_data, portMAX_DELAY);   //向队列中发送数据
        xSemaphoreGive(gDHT112EmWin_Queue_Mutex);  //解锁
        
        printf("T: %0.2f   H: %0.2f\r\n", gDht11.temperature, gDht11.humidity);
        vTaskDelay(1000);
    }
}




/*****************************************************
 * @brief       Task_LCD
 * @param       pvParameters : 传入参数(未用到)
 * @retval      无
 *
 ******************************************************/
void Task_LCD(void *pvParameters)
{
    int i = 0;
    float dht11_data[2] = {0};
    float temperature;      //温度值
    float humidity;         //湿度
    CreateWindow();
    while (1) {
        xSemaphoreTake(gDHT112EmWin_Queue_Mutex, portMAX_DELAY);   //上锁

        BaseType_t  wait_cnt = uxQueueMessagesWaiting(gDHT112EmWin_Queue);
        if (wait_cnt <= 0){
            
            xSemaphoreGive(gDHT112EmWin_Queue_Mutex);  //解锁
            vTaskDelay(10);
            continue;  
        }

        xQueueReceive(gDHT112EmWin_Queue, dht11_data, portMAX_DELAY);   //从队列中接受数据
        xSemaphoreGive(gDHT112EmWin_Queue_Mutex);  //解锁
        
        temperature  =  dht11_data[0];
        humidity     =  dht11_data[1];

        memset(show_buffer, 0, sizeof(show_buffer));
        sprintf((char *)show_buffer, "%0.2f", temperature);
        EDIT_SetText(hItemT, show_buffer);

        memset(show_buffer, 0, sizeof(show_buffer));
        sprintf((char *)show_buffer, "%0.2f", humidity);
        EDIT_SetText(hItemH, show_buffer);

        GUI_Delay(50);
    }
}





void Task_Touch(void *pvParameters)
{
    pvParameters = pvParameters;
    GUI_Init();
    while (1)
    {
        GUI_TOUCH_Exec();
        GUI_Delay(5);
    }
}


int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);      //中断分组配置
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE);  //开启CRC时钟
    Debug_Init(115200);
    Delay_Init();

    LED_Init(&gLed[0], GPIOE, GPIO_Pin_5);
    LED_Init(&gLed[1], GPIOE, GPIO_Pin_6);
    LED_Init(&gLed[2], GPIOC, GPIO_Pin_13);
    ILI9341_Init();
    tp_dev.init();                                       /* 触摸屏初始化 */
    xTaskCreate((TaskFunction_t )Task_DHT11,             /* 任务函数 */
                (const char*    )"Task_DHT11",           /* 任务名称 */
                (uint16_t       )512,                    /* 任务堆栈大小 */
                (void*          )NULL,                   /* 传入给任务函数的参数 */
                (UBaseType_t    )2,                      /* 任务优先级 */
                (TaskHandle_t*  )&DHT11Task_Handler);    /* 任务句柄 */

    xTaskCreate((TaskFunction_t )Task_Touch,             /* 任务函数 */
            (const char*    )"Task_Touch",               /* 任务名称 */
            (uint16_t       )512,                        /* 任务堆栈大小 */
            (void*          )NULL,                       /* 传入给任务函数的参数 */
            (UBaseType_t    )3,                          /* 任务优先级 */
            (TaskHandle_t*  )&TouchTask_Handler);        /* 任务句柄 */

            
    xTaskCreate((TaskFunction_t )Task_LCD,               /* 任务函数 */
            (const char*    )"Task_LCD",                 /* 任务名称 */
            (uint16_t       )512,                        /* 任务堆栈大小 */
            (void*          )NULL,                       /* 传入给任务函数的参数 */
            (UBaseType_t    )1,                          /* 任务优先级 */
            (TaskHandle_t*  )&LCDTask_Handler);          /* 任务句柄 */

    vTaskStartScheduler();

    while(1)
    {
    
    }
}


TOUCH

#include "touch.h" 
#include "ILI9341.h"
#include "delay.h"
#include "stdlib.h"
#include "math.h"
 
 
// PCin(5)      //T_PEN
// PBin(14)       //T_MISO
// PBout(15)      //T_MOSI
// PBout(13)      //T_SCK
// PBout(12)      //T_CS  
 
//#define      PEN_H()       GPIO_WriteBit(GPIOC, GPIO_Pin_5, 1)
//#define      PEN_L()       GPIO_WriteBit(GPIOC, GPIO_Pin_5, 1)
#define      PEN()      GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_5)
//#define      DOUT_H()      GPIO_WriteBit(GPIOB, GPIO_Pin_14, 1)
//#define      DOUT_L()      GPIO_WriteBit(GPIOB, GPIO_Pin_14, 0)
#define      DOUT_R()      GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14)

#define      TDIN_H()      GPIO_WriteBit(GPIOB, GPIO_Pin_15, 1)
#define      TDIN_L()      GPIO_WriteBit(GPIOB, GPIO_Pin_15, 0)

#define      TCLK_H()      GPIO_WriteBit(GPIOB, GPIO_Pin_13, 1)
#define      TCLK_L()      GPIO_WriteBit(GPIOB, GPIO_Pin_13, 0)

#define      TCS_H()       GPIO_WriteBit(GPIOB, GPIO_Pin_12, 1)
#define      TCS_L()       GPIO_WriteBit(GPIOB, GPIO_Pin_12, 0)


_m_tp_dev tp_dev=
{
    TP_Init,
    TP_Scan,
//    TP_Adjust,
    NULL,
    0,
    0, 
    0,
    0,
    0,
    0,                   
    0,
    0,                   
};                    
//默认为touchtype=0的数据.
u8 CMD_RDX=0XD0;
u8 CMD_RDY=0X90;
                                             
//SPI写数据
//向触摸屏IC写入1byte数据    
//num:要写入的数据
void TP_Write_Byte(u8 num)    
{
    u8 count=0;   
    for(count=0;count<8;count++)  
    {
        if(num&0x80)TDIN_H();
        else TDIN_L();
        num<<=1;
        TCLK_L();
        delay_us(1);
        TCLK_H();        //上升沿有效
    }
}

//SPI读数据 
//从触摸屏IC读取adc值
//CMD:指令
//返回值:读到的数据
u16 TP_Read_AD(u8 CMD)
{      
    u8 count=0;       
    u16 Num=0; 
    TCLK_L();        //先拉低时钟      
    TDIN_L();     //拉低数据线
    TCS_L();         //选中触摸屏IC
    TP_Write_Byte(CMD);//发送命令字
    delay_us(6);//ADS7846的转换时间最长为6us
    TCLK_L();                  
    delay_us(1);           
    TCLK_H();        //给1个时钟,清除BUSY
    delay_us(1);    
    TCLK_L();                  
    for(count=0;count<16;count++)//读出16位数据,只有高12位有效 
    {                   
        Num<<=1;      
        TCLK_L();    //下降沿有效                 
        delay_us(1);    
         TCLK_H();
         if(DOUT_R())Num++;          
    }      
    Num>>=4;       //只有高12位有效.
    TCS_H();        //释放片选     
    return(Num);   
}
//读取一个坐标值(x或者y)
//连续读取READ_TIMES次数据,对这些数据升序排列,
//然后去掉最低和最高LOST_VAL个数,取平均值 
//xy:指令(CMD_RDX/CMD_RDY)
//返回值:读到的数据
#define READ_TIMES 5     //读取次数
#define LOST_VAL 1          //丢弃值
u16 TP_Read_XOY(u8 xy)
{
    u16 i, j;
    u16 buf[READ_TIMES];
    u16 sum=0;
    u16 temp;
    for(i=0;i<READ_TIMES;i++)buf[i]=TP_Read_AD(xy);
    for(i=0;i<READ_TIMES-1; i++)//排序
    {
        for(j=i+1;j<READ_TIMES;j++)
        {
            if(buf[i]>buf[j])//升序排列
            {
                temp=buf[i];
                buf[i]=buf[j];
                buf[j]=temp;
            }
        }
    }      
    sum=0;
    for(i=LOST_VAL;i<READ_TIMES-LOST_VAL;i++)sum+=buf[i];
    temp=sum/(READ_TIMES-2*LOST_VAL);
    return temp;   
} 
//读取x,y坐标
//最小值不能少于100.
//x,y:读取到的坐标值
//返回值:0,失败;1,成功。
u8 TP_Read_XY(u16 *x,u16 *y)
{
    u16 xtemp,ytemp;                            
    xtemp=TP_Read_XOY(CMD_RDX);
    ytemp=TP_Read_XOY(CMD_RDY);                                                         
    //if(xtemp<100||ytemp<100)return 0;//读数失败
    *x=xtemp;
    *y=ytemp;
    return 1;//读数成功
}
//连续2次读取触摸屏IC,且这两次的偏差不能超过
//ERR_RANGE,满足条件,则认为读数正确,否则读数错误.       
//该函数能大大提高准确度
//x,y:读取到的坐标值
//返回值:0,失败;1,成功。
#define ERR_RANGE 50 //误差范围 
u8 TP_Read_XY2(u16 *x,u16 *y) 
{
    u16 x1,y1;
     u16 x2,y2;
     u8 flag;    
    flag=TP_Read_XY(&x1,&y1);   
    if(flag==0)return(0);
    flag=TP_Read_XY(&x2,&y2);       
    if(flag==0)return(0);   
    if(((x2<=x1&&x1<x2+ERR_RANGE)||(x1<=x2&&x2<x1+ERR_RANGE))//前后两次采样在+-50内
    &&((y2<=y1&&y1<y2+ERR_RANGE)||(y1<=y2&&y2<y1+ERR_RANGE)))
    {
        *x=(x1+x2)/2;
        *y=(y1+y2)/2;
        return 1;
    }else return 0;      
}  

//触摸按键扫描
//tp:0,屏幕坐标;1,物理坐标(校准等特殊场合用)
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
u8 TP_Scan(u8 tp)
{               
    if(PEN()==0)//有按键按下
    {
        if(tp)TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]);//读取物理坐标
        else if(TP_Read_XY2(&tp_dev.x[0],&tp_dev.y[0]))//读取屏幕坐标
        {
             tp_dev.x[0]=tp_dev.xfac*tp_dev.x[0]+tp_dev.xoff;//将结果转换为屏幕坐标
            tp_dev.y[0]=tp_dev.yfac*tp_dev.y[0]+tp_dev.yoff;  
         } 
        if((tp_dev.sta&TP_PRES_DOWN)==0)//之前没有被按下
        {         
            tp_dev.sta=TP_PRES_DOWN|TP_CATH_PRES;//按键按下  
            tp_dev.x[4]=tp_dev.x[0];//记录第一次按下时的坐标
            tp_dev.y[4]=tp_dev.y[0];                      
        }               
    }else
    {
        if(tp_dev.sta&TP_PRES_DOWN)//之前是被按下的
        {
            tp_dev.sta&=~(1<<7);//标记按键松开    
        }else//之前就没有被按下
        {
            tp_dev.x[4]=0;
            tp_dev.y[4]=0;
            tp_dev.x[0]=0xffff;
            tp_dev.y[0]=0xffff;
        }        
    }
    return tp_dev.sta&TP_PRES_DOWN;//返回当前的触屏状态
}      

//触摸屏初始化
//返回值:0,没有进行校准
//       1,进行过校准
u8 TP_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;    

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOB,C,F时钟

    //GPIOB14初始化设置
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;//PB14 设置为上拉输入
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化


    //GPIOC5初始化设置
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//PC5 设置为上拉输入
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12| GPIO_Pin_13 | GPIO_Pin_15;//PB0设置为推挽输出
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出模式
    GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化

    TP_Read_XY(&tp_dev.x[0],&tp_dev.y[0]);//第一次读取初始化     
//    AT24CXX_Init();        //初始化24CXX
//    if(TP_Get_Adjdata()) {
//        return 0;//已经校准
//    } else {
//        LCD_Clear(WHITE);//清屏
//        TP_Adjust();      //屏幕校准 
//        TP_Save_Adjdata();
        tp_dev.xfac = 0.064371;
        tp_dev.yfac = 0.088945;
        tp_dev.xoff = -11;
        tp_dev.yoff = -13;
        tp_dev.touchtype = 0;
//    }

//    TP_Get_Adjdata();

    return 1;
}

  • 25
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值