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

news2024/11/15 10:10:07

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


【屏幕显示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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1666363.html

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

相关文章

【Linux】AlmaLinux 9.4版本发布

AlmaLinux 9.4 正式版发布&#xff0c;该版本基于 Redhat Enterprise 9.4&#xff0c;内核版本号&#xff1a; 5.14.0-427.13.1.el9_4.x86_64 相对于Rocky Linux&#xff0c; AlmaLinux更加的稳定&#xff0c;生产环境建议使用AlmaLinux来替代CentOS 7.x AlmaLinux 9.4版本系统…

MIPI DPHY HS传输模式SoT和EoT的传输值

目录 1. 高速传输模式的传输序列 2. SoT传输序列 3. EoT传输序列 1. 高速传输模式的传输序列 Mipi DPHY的高速数据传输&#xff08;HST&#xff1a;High Speed Transmission&#xff09;以突发&#xff08;Burst&#xff09;方式发生。 为了帮助接收机同步&#xff1a; (1) …

Python-VBA函数之旅-str函数

目录 一、str函数的常见应用场景 二、str函数使用注意事项 三、如何用好str函数&#xff1f; 1、str函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a; https://myelsa1024.blog.csdn.net/ 一、str函数的常…

Redis-配置文件详解

Redis配置文件详解 units单位 配置大小单位&#xff0c;开头定义基本度量单位&#xff0c;只支持bytes&#xff0c;大小写不敏感。 INCLUDES Redis只有一个配置文件&#xff0c;如果多个人进行开发维护&#xff0c;那么就需要多个这样的配置文件&#xff0c;这时候多个配置 文…

指针(5)加油吧

指针&#xff08;5&#xff09; 拿冒泡排序来举例&#xff1a; 1 .qsort void qsort (void* base,//base指向待排序数组的首元素的指针size_t num,//base指向数组中元素的个数size_t size,//base指向的数组中的一个元素的大小&#xff0c;单位是字节int(*cmp)(const void*,co…

前端工程化,前端监控,工作流,部署,性能

开发规范 创建项目的时候&#xff0c;配置下 ESlint&#xff0c;stylelint&#xff0c; prettier&#xff0c; commitlint 等; ESLint 主要功能&#xff1a; ESLint 是一个静态代码检查工具&#xff0c;用于在 JavaScript 代码中识别和报告模式。它的目标是提供一个插件化的 …

LeetCode 138. 随机链表的复制

目录 1.原题链接&#xff1a; 2.结点拆分&#xff1a; 代码实现&#xff1a; 3.提交结果&#xff1a; 4.读书分享&#xff1a; 1.原题链接&#xff1a; 138. 随机链表的复制 2.结点拆分&#xff1a; ①.拷贝各个结点&#xff0c;连接在原结点后面&#xff1b; ②.处…

Imitation Learning学习记录(理论例程)

前言 最近还是衔接着之前的学习记录&#xff0c;这次打算开始学习模仿学习的相关原理&#xff0c;参考的开源资料为 TeaPearce/Counter-Strike_Behavioural_Cloning: IEEE CoG & NeurIPS workshop paper ‘Counter-Strike Deathmatch with Large-Scale Behavioural Clonin…

【源头开发】运营级竞拍商城源码/抢拍转拍/竞拍源码/转卖寄售/拆分/溢价商城转拍溢价php源码uniapp源码

大家好啊&#xff0c;欢迎来到web测评&#xff0c;我是年哥&#xff0c;我们有个小伙伴又开发了一款竞拍商城的源码&#xff0c;是此系统的源头开发者&#xff0c;本系统是前后端分离的架构&#xff0c;前端php&#xff0c;后端uniapp&#xff0c;系统现在是持续的在更新中&…

libcity笔记: HSTLSTMEncoder

1 __init__ 2 encode 得到的内容如下&#xff1a; data_feature的内容&#xff1a; 一共有多少个location1【包括pad的一个】最长的时间间隔&#xff08;秒&#xff09;最长的距离间隔&#xff08;千米&#xff09;多少个useer idpadding 的locationidpad_item的内容 location…

[单机]成吉思汗3_GM工具_VM虚拟机

稀有端游成吉思汗1,2,3单机版虚拟机一键端完整版 本教程仅限学习使用&#xff0c;禁止商用&#xff0c;一切后果与本人无关&#xff0c;此声明具有法律效应&#xff01;&#xff01;&#xff01;&#xff01; 教程是本人亲自搭建成功的&#xff0c;绝对是完整可运行的&#x…

React 第三十一章 前端框架的分类

现代前端框架&#xff0c;有一个非常重要的特点&#xff0c;那就是基于状态的声明式渲染。如果要概括的话&#xff0c;可以使用一个公式&#xff1a; UI f&#xff08;state&#xff09; state&#xff1a;当前视图的一个状态f&#xff1a;框架内部的一个运行机制UI&#xff1…

计算机视觉——基于改进UNet图像增强算法实现

1. 引言 在低光照条件下进行成像非常具有挑战性&#xff0c;因为光子计数低且存在噪声。高ISO可以用来增加亮度&#xff0c;但它也会放大噪声。后处理&#xff0c;如缩放或直方图拉伸可以应用&#xff0c;但这并不能解决由于光子计数低导致的低信噪比&#xff08;SNR&#xff…

从头理解transformer,注意力机制(下)

交叉注意力 交叉注意力里面q和KV生成的数据不一样 自注意力机制就是闷头自学 解码器里面的每一层都会拿着编码器结果进行参考&#xff0c;然后比较相互之间的差异。每做一次注意力计算都需要校准一次 编码器和解码器是可以并行进行训练的 训练过程 好久不见输入到编码器&…

【图论 回溯 广度优先搜索】126. 单词接龙 II

本文涉及知识点 图论 回溯 深度优先搜索 广度优先搜索 图论知识汇总 LeetCode 126. 单词接龙 II 按字典 wordList 完成从单词 beginWord 到单词 endWord 转化&#xff0c;一个表示此过程的 转换序列 是形式上像 beginWord -> s1 -> s2 -> … -> sk 这样的单词序…

机器学习入门到放弃2:朴素贝叶斯

1. 算法介绍 1.1 算法定义 朴素贝叶斯分类&#xff08;NBC&#xff09;是以贝叶斯定理为基础并且假设特征条件之间相互独立的方法&#xff0c;先通过已给定的训练集&#xff0c;以特征词之间独立作为前提假设&#xff0c;学习从输入到输出的联合概率分布&#xff0c;再基于学习…

oracle 数据库与服务、实例与SID、表空间、用户与表模式

一、数据库与数据库服务: 概念:就是一个数据库的标识,在安装时就要想好,以后一般不修改,修改起来也麻烦,因为数据库一旦安装,数据库名就写进了控制文件,数据库表,很多地方都会用到这个数据库名。是数据库系统的入口,它会内置一些高级权限的用户如SYS,SYSTEM等。我们…

Xilinx 千兆以太网TEMAC IP核 MDIO 配置及物理接口

基于AXI4-Lite接口可以访问MDIO(Management Data Input/Output)接口&#xff0c;而MDIO接口连接MAC外部的PHY芯片&#xff0c;用户可通过AXI4-Lite接口实现对PHY芯片的配置。 1 MDIO接口简介 开放系统互连模型OSI的最低两层分别是数据链路层和物理层&#xff0c;数据链路层的…

探讨欧盟就人工智能监管达成协议

《人工智能法案》是一项具有里程碑意义的立法&#xff0c;它可以创造一个有利的环境&#xff0c;在这种环境中&#xff0c;人工智能的使用将成为一种更优秀的安全和信任的工具&#xff0c;确保整个欧盟的公共和私人机构利益相关者的参与。 历时3天的“马拉松式”谈判圆满结束&…

数据可视化训练第四天(模拟投掷筛子并且统计频次)

投掷一个筛子 import matplotlib.pyplot as plt from random import randint import numpy as npclass Die:"""模拟投掷筛子"""def __init__(self,num_sides6):self.num_sidesnum_sidesdef roll(self):return randint(1,self.num_sides)num1000…