参考——温湿度传感器DHT11驱动_STM32

news2024/11/27 22:37:33

设备stm32f407ZGT6

环境FreeRTOS        HAL

一、简介

        到网上找DHT11的驱动,但是都无法使用。原因是RTOS环境中,由于多线程,使用循环计数阻塞式的delay_us延时函数就没那么准,且不同设备中delay_us的计数值不一样。而DHT11对时序要求得又十分严格,这就会导致读取数据异常,甚至无法读取。

/**********************************************************************************************************
函数名称:us延时函数,最小延时为1us
输入参数:时间
输出参数:无
**********************************************************************************************************/
void delay_us(unsigned int time)
{
    unsigned short i = 0;

    while (time--)
    {
        i = 8;
        while (i--)
            ;
    }
}

        而且这个DHT11间隔若干秒连续读取一段时间后,会在某次读取第一个完整字节后就会莫名其妙卡住,不知道是不是硬件的问题。这个没有什么好办法解决,在读取的时候加个超时判断语句,超时就返回false        

        虽然考虑过使用其他方法达到更精准的微秒级延时,但还是想尝试一下用不那么精准的方法来完成。于是下面就简单的使用计数值来模拟延时,不需要考虑延时多少微秒,只需要比较高电平持续时间的相对长短。

二、代码

1,简略分析

        时序很好分析。分为主机部分DHT部分。通信的整个过程是主机先向DHT发送开始信号,如果DHT回应了(大多数情况都会回应,除非延时不准),那么就读取数据,读取结束后再拉高(实测过程中,这个加不加都会间歇性卡住。这里没有加)

发送开始信号

inline void DHT11_Rst()
{
    DHT11_OUT();
    DHT11_Low();
    osDelay(30);//根据时序图可知,需要至少拉低18ms。换成自己的延时函数,不需要太准
    DHT11_High();
    std_delay_25us();//20-40us,不需要太准,它与它上面的那行代码也可以不加
}

检测DHT响应

inline void DHT11_Check()
{
    DHT11_IN();
    while (DHT11_Read())
        ;

    while (!DHT11_Read())
        ;
}

读取一位数据

        当DHT输出响应后会拉高一段时间,然后才会拉低,进入一个50us的低电平间隙,等再次拉高时就是读取数据的时候

此后在读取上一位数据结束后,都会拉低50us,然后再次拉高来读取数据

        根据上面两种情况,所以统一先等待拉低,以便进入50us的低电平间隙。然后再等待拉高,以便读取数据,最后再等待拉低,在这两个操作中用计数值来记录时间长短。

0:26-28us

1:70us

            while (DHT11_Read()) {} // 等待低电平
            while (!DHT11_Read()) {}// 等待变高电平

            // 开始读数据
            uint16_t time_count = 0;
            for (; DHT11_Read() && time_count < DHT11_MAX_DELAY_COUNT; ++time_count) {}
            if (time_count >= DHT11_MAX_DELAY_COUNT)
            {
                return false;
            }
            timeBuf[timeBufIndex++] = time_count >> 4;// 存储计数值,由于事先已经知道一个为

        上面这个读取一位数据是可以简化的,因为按照下面读取的规则,其实是不需要每次都要在开头等待低电平。但是检测DHT输出响应后需要等待低电平 

        简化后是这样的,读取一位数据不需要考虑很多,只需要等待变为高电平就行了。但是检测DHT11响应考虑的就比较多了,需要再次等待低电平。

inline void DHT11_Check()
{
    DHT11_IN();
    while (DHT11_Read())    //等待低电平
        ;

    while (!DHT11_Read())   //等待高电平
        ;//也可以在这里计数,用于测量80us所需的计数值,从而测算出20us、40us等对应的计数值

    while (DHT11_Read()) {} // 等待低电平
}

            while (!DHT11_Read()) {}// 等待变高电平

            // 开始读数据
            uint16_t time_count = 0;
            for (; DHT11_Read() && time_count < DHT11_MAX_DELAY_COUNT; ++time_count) {}
            if (time_count >= DHT11_MAX_DELAY_COUNT)
            {
                return false;
            }
            timeBuf[timeBufIndex++] = time_count >> 4;// 存储计数值,由于事先已经知道一个为

读取一个字节

        这个只要一个for循环即可,循环8次。这里是未简化的版本。读取5个字节就是再套个for循环,循环5次即可

        //读取一字节
        for (uint8_t j = 0; j < 8; j++)
        {
            while (DHT11_Read()) {} // 等待低电平
            while (!DHT11_Read()) {}// 等待变高电平

            // 开始读数据
            uint16_t time_count = 0;
            for (; DHT11_Read() && time_count < DHT11_MAX_DELAY_COUNT; ++time_count) {}
            if (time_count >= DHT11_MAX_DELAY_COUNT)
            {
                return false;
            }
            timeBuf[timeBufIndex++] = time_count >> 4;// 存储计数值,由于事先已经知道一个为875,一个为275左右,所以除以16
        }

数据处理

使用中位数来区分高电平持续的时间是长是短,以便判断是1还是0。正常情况下,这个持续时间差别很明显。

//找出最大计数值和最小计数值,用以判断是1还是0
    uint16_t timeMax = 0;
    uint16_t timeMin = 0xFFFF;
    for (unsigned short i: timeBuf)
    {
        if (i > timeMax) timeMax = i;
        if (i < timeMin) timeMin = i;
    }

    uint16_t timeMed = (timeMax + timeMin) >> 1;// 取中位数,以区分时间长短

// 转为二进制数据,进而转为字节数据
    for (uint8_t i = 0; i < 5; ++i)
    {
        uint8_t data = 0;
        for (uint8_t j = 0; j < 8; j++)
        {
            data <<= 1;
            data |= (timeBuf[i * 8 + j] > timeMed);
        }
        buf[i] = data;// 存储数据
    }

2,完整代码

需要注意的是,下面代码使用的是C++,如果你使用的是C,那么用相应功能的代码替换掉即可。bool类型需要包含stdbool头文件

DHT11.h

        头文件里未被注释的接口,决定了哪些函数需要重点关注

#ifndef DHT11_H
#define DHT11_H



void DHT11_Init();
//bool DHT11_Read_Data(float &temp,float &humi);
bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi);//自定义的快速读取函数
#endif//DHT11_H

DHT11.cpp

        里面的这个std_delay_us是用于调试中用的,在void DHT11_Rst()函数里,你把延时换成你自带的即可,这个不需要多精准。

        下面的函数是与时序图直接对应的,连续读取温度时,最好间隔1s以上。

        后面还有一个版本

#include "DHT11.h"
#include "cmsis_os2.h"
#include "stm32f4xx_hal.h"

#define DHT11_Pin GPIO_PIN_6
#define DHT11_Pin_Location 6    //第几个引脚就写几
#define DHT11_GPIO_Port GPIOE
#define DHT11_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE()
#define DHT11_MAX_DELAY_COUNT 7000//防止卡死

#define DHT11_Read() (DHT11_GPIO_Port->IDR & DHT11_Pin) /*HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin)*/

#define DHT11_High() DHT11_GPIO_Port->ODR |= (0x01 << DHT11_Pin_Location)
#define DHT11_Low() DHT11_GPIO_Port->ODR &= ~(0x01 << DHT11_Pin_Location) /*HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_RESET)*/

#define DHT11_IN()                                                  \
    {                                                               \
        DHT11_GPIO_Port->MODER &= ~(3 << (2 * DHT11_Pin_Location)); \
        DHT11_GPIO_Port->MODER |= 0 << 2 * DHT11_Pin_Location;      \
    }
#define DHT11_OUT()                                                 \
    {                                                               \
        DHT11_GPIO_Port->MODER &= ~(3 << (2 * DHT11_Pin_Location)); \
        DHT11_GPIO_Port->MODER |= 1 << 2 * DHT11_Pin_Location;      \
    }

//static uint16_t std_delay_80us = 875;//事先测试过
//static uint16_t std_delay_50us = 566;

/**动态计算延时,以确保任何情况下都可以得到较为准确的延时*/
//根据80us的那段时序,通过计数值得到80us需要的计数值,然后以此测算不同us的延时,在Check函数里,这里省略了
//void std_delay_us(uint8_t us)
//{
//    //   uint16_t count = std_delay_80us * us / 80;//测试得到的,但乘法会有很大的延时,所以只能作为计算公式
//    uint16_t count = 11 * us;
//    for (uint16_t i = 0; i < count; ++i)
//        ;
//}

//这个延时函数可以换成自己的延时,因为设备不同、代码环境不同,延时的计数就不同
//可以通过上面那个方法来计算,测算的是Check函数里的80s延时
inline void std_delay_25us()
{
    for (uint16_t i = 0; i < 20; ++i)//单个任务时为273
        ;
}

/**函数*/
inline void DHT11_Rst();


void DHT11_Init()
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    DHT11_GPIO_CLK_ENABLE();

    GPIO_InitStruct.Pin = DHT11_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    //    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
    DHT11_High();
}


inline void DHT11_Rst()
{
    DHT11_OUT();
    DHT11_Low();
    osDelay(30);//根据时序图可知,需要至少拉低18ms
    DHT11_High();
    std_delay_25us();//20-40us
}

inline void DHT11_Check()
{
    DHT11_IN();
    while (DHT11_Read())
        ;

    while (!DHT11_Read())
        ;
}

// 全局变量
static uint8_t timeBuf[40];// 存储计数值
static uint8_t timeBufIndex = 0;

bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
{
    static uint8_t buf[5];

    DHT11_Rst();  // 设置输出模式
    DHT11_Check();// 设置输入模式

    timeBufIndex = 0;          // 重置计数值索引
    for (unsigned char &i: buf)// 读取40位数据
    {
        //        DHT11_Read_Byte_Fast_Pro();
        //读取一字节
        for (uint8_t j = 0; j < 8; j++)
        {
            while (DHT11_Read()) {} // 等待低电平
            while (!DHT11_Read()) {}// 等待变高电平

            // 开始读数据
            uint16_t time_count = 0;
            for (; DHT11_Read() && time_count < DHT11_MAX_DELAY_COUNT; ++time_count) {}
            if (time_count >= DHT11_MAX_DELAY_COUNT)
            {
                return false;
            }
            timeBuf[timeBufIndex++] = time_count >> 4;// 存储计数值,由于事先已经知道一个为875,一个为275左右,所以除以16
        }
    }


//找出最大计数值和最小计数值,用以判断是1还是0
    uint16_t timeMax = 0;
    uint16_t timeMin = 0xFFFF;
    for (unsigned short i: timeBuf)
    {
        if (i > timeMax) timeMax = i;
        if (i < timeMin) timeMin = i;
    }

    uint16_t timeMed = (timeMax + timeMin) >> 1;// 取中位数
    for (uint8_t i = 0; i < 5; ++i)
    {
        uint8_t data = 0;
        for (uint8_t j = 0; j < 8; j++)
        {
            data <<= 1;
            data |= (timeBuf[i * 8 + j] > timeMed);
        }
        buf[i] = data;// 存储数据
    }

    //校验数据

    if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
    {
        humi = (buf[0] * 10 + buf[1]) / 10.0f;
        temp = (buf[2] * 10 + buf[3]) / 10.0f;
        return true;
    }
    else
    {
        return false;
    }
}

里面有多个版本,第0个是特化版本,其下愈为通用。只有第1个版本(没有注释的)加了防卡死的判断语句

#include "DHT11.h"
#include "cmsis_os2.h"
#include "stm32f4xx_hal.h"

#define DHT11_Pin GPIO_PIN_6
#define DHT11_Pin_Location 6
#define DHT11_GPIO_Port GPIOE
#define DHT11_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE()
#define DHT11_MAX_DELAY_COUNT 4000//防止卡死
#define USE_YZHX 1                //优化等级,分为0,1,2,3

#define DHT11_Read() (DHT11_GPIO_Port->IDR & DHT11_Pin) /*HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin)*/

#define DHT11_High() DHT11_GPIO_Port->ODR |= (0x01 << DHT11_Pin_Location)
#define DHT11_Low() DHT11_GPIO_Port->ODR &= ~(0x01 << DHT11_Pin_Location) /*HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_RESET)*/

#define DHT11_Wait_Low() while (DHT11_Read())
#define DHT11_Wait_High() while (!DHT11_Read())

#define DHT11_IN()                                                  \
    {                                                               \
        DHT11_GPIO_Port->MODER &= ~(3 << (2 * DHT11_Pin_Location)); \
        DHT11_GPIO_Port->MODER |= 0 << 2 * DHT11_Pin_Location;      \
    }
#define DHT11_OUT()                                                 \
    {                                                               \
        DHT11_GPIO_Port->MODER &= ~(3 << (2 * DHT11_Pin_Location)); \
        DHT11_GPIO_Port->MODER |= 1 << 2 * DHT11_Pin_Location;      \
    }

//static uint16_t std_delay_80us = 875;//事先测试过
//static uint16_t std_delay_50us = 566;

/**动态计算延时,以确保任何情况下都可以得到较为准确的延时*/
//void std_delay_us(uint8_t us)
//{
//    //   uint16_t count = std_delay_80us * us / 80;//测试得到的
//    uint16_t count = 11 * us;
//    for (uint16_t i = 0; i < count; ++i)
//        ;
//}
inline void std_delay_25us()
{
    for (uint16_t i = 0; i < 20; ++i)//单个任务时,大概为273
        ;
}


void DHT11_Init()
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    DHT11_GPIO_CLK_ENABLE();

    GPIO_InitStruct.Pin = DHT11_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    //    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//输入模式下,最好不要配置速度,所以为了兼容输入就不配置了,即默认2MHz
    HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
    DHT11_High();
}


inline void DHT11_Rst()
{
    DHT11_OUT();
    DHT11_Low();
    osDelay(25);//根据时序图可知,需要至少拉低18ms
    DHT11_High();
    std_delay_25us();//20-40us
}

inline void DHT11_Check()
{
    DHT11_IN();
    //等待低电平
    DHT11_Wait_Low();
    //等待高电平
    DHT11_Wait_High();
    // 等待低电平
    DHT11_Wait_Low();
}

#if USE_YZHX == 0
bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
{
    static uint8_t buf[5];

    DHT11_Rst();  // 设置输出模式
    DHT11_Check();// 设置输入模式

    for (unsigned char &i: buf)// 读取40位数据
    {
        uint8_t data = 0;
        for (uint8_t j = 0; j < 8; j++)
        {
            data <<= 1;
            while (DHT11_Read()) {} // 等待低电平
            while (!DHT11_Read()) {}// 等待变高电平

            // 开始读数据
            uint16_t time_count;
            for (time_count = 0; DHT11_Read(); ++time_count) {}
            data |= time_count >> 10;// 由于事先已经知道一个为1194,一个为406左右
        }
        i = data;// 存储数据
    }

    if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
    {
        humi = (buf[0] * 10 + buf[1]) / 10.0f;
        temp = (buf[2] * 10 + buf[3]) / 10.0f;
        return true;
    }
    else
    {
        return false;
    }
}
#endif

/********************下面为次优级优化********************/
#if USE_YZHX == 1
// 全局变量
static uint8_t timeBuf[40];// 存储计数值
static uint8_t timeBufIndex = 0;

//void DHT11_Read_Byte_Fast_Pro()
//{
//    for (uint8_t i = 0; i < 8; i++)
//    {
//        while (DHT11_Read()) {} // 等待低电平
//        while (!DHT11_Read()) {}// 等待变高电平
//
//        // 开始读数据
//        uint16_t time_count;
//        for (time_count = 0; DHT11_Read(); ++time_count) {}
//        timeBuf[timeBufIndex++] = time_count>>4;// 存储计数值,由于事先已经知道一个为875,一个为275左右,所以除以16
//    }
//}

bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
{
    static uint8_t buf[5];

    DHT11_Rst();  // 设置输出模式
    DHT11_Check();// 设置输入模式

    timeBufIndex = 0;          // 重置计数值索引
    for (unsigned char &i: buf)// 读取40位数据
    {
        //        DHT11_Read_Byte_Fast_Pro();
        //读取一字节
        for (uint8_t j = 0; j < 8; j++)
        {
            DHT11_Wait_High();// 等待变高电平

            // 开始读数据
            uint16_t time_count = 0;
            for (; DHT11_Read() && time_count < DHT11_MAX_DELAY_COUNT; ++time_count) {}
            if (time_count >= DHT11_MAX_DELAY_COUNT)
            {
                return false;
            }

            timeBuf[timeBufIndex++] = time_count >> 4;// 存储计数值,由于事先已经知道一个为875,一个为275左右,所以除以16
        }
    }

    //    std_delay_25us();
    //    std_delay_25us();
    //    DHT11_OUT();
    //    DHT11_High();

    uint16_t timeMax = 0;
    uint16_t timeMin = 0xFFFF;
    for (unsigned short i: timeBuf)
    {
        if (i > timeMax) timeMax = i;
        if (i < timeMin) timeMin = i;
    }

    uint16_t timeMed = (timeMax + timeMin) >> 1;// 取中位数
    for (uint8_t i = 0; i < 5; ++i)
    {
        uint8_t data = 0;
        for (uint8_t j = 0; j < 8; j++)
        {
            data <<= 1;
            data |= (timeBuf[i * 8 + j] > timeMed);
        }
        buf[i] = data;// 存储数据
    }

    if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
    {
        humi = (buf[0] * 10 + buf[1]) / 10.0f;
        temp = (buf[2] * 10 + buf[3]) / 10.0f;
        return true;
    }
    else
    {
        return false;
    }
}
#endif

#if USE_YZHX == 2
static uint16_t timeBuf[40];//存储计数值
static uint8_t timeBufIndex = 0;
void DHT11_Read_Byte_Fast_Pro()
{
    for (uint8_t i = 0; i < 8; i++)
    {
        while (DHT11_Read())
            ;//等待低电平
        //变低了说明上一次数据位读取结束

        while (!DHT11_Read())
            ;//等待变高电平
        //变高了说明数据位读取开始

        /**开始读数据*/
        //低电平:26-28us   高电平:70us
        uint16_t time_count;
        for (time_count = 0; DHT11_Read(); ++time_count)
            ;                                //等待低电平
        timeBuf[timeBufIndex++] = time_count;//存储计数值//存储计数值
    }
}


bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
{
    static uint8_t buf[5];

    DHT11_Rst();               //在里面设置了输出模式
    DHT11_Check();             //在里面设置了输入模式
                               //  return false;//如果超时,则退出
    timeBufIndex = 0;          //存储计数值索引
    for (unsigned char &i: buf)//读取40位数据
    {
        DHT11_Read_Byte_Fast_Pro();
    }

    uint16_t timeMax = 0;
    uint16_t timeMin = 0xFFFF;
    for (unsigned short i: timeBuf)
    {
        if (i > timeMax)
        {
            timeMax = i;
        }
        else if (i < timeMin)
        {
            timeMin = i;
        }
    }

    /**把计数值转为二进制数据*/
    uint8_t data;                               //临时数据
    uint16_t timeMed = (timeMax + timeMin) >> 1;//整除2,取中位数
    bool tempBin;
    for (uint8_t i = 0; i < 5; ++i)
    {
        data = 0;
        for (uint8_t j = 0; j < 8; j++)
        {
            data <<= 1;
            //比较计数值,读取二进制数据
            if (timeBuf[i * 8 + j] > timeMed)
            {
                tempBin = true;
            }
            else
            {
                tempBin = false;
            }
            data |= tempBin;
        }
        buf[i] = data;//存储数据
    }

    /**检验**/
    if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
    {
        humi = (float) (buf[0] + buf[1] * 0.1);
        temp = (float) (buf[2] + buf[3] * 0.1);
        return true;
    }
    else
    {
        return false;
    }
}
#endif


/********************下面为原版优化********************/
#if USE_YZHX == 3
static uint16_t timeBuf[40];//存储计数值

void DHT11_Read_Byte_Fast_Pro()
{
    static uint8_t timeBufIndex = 0;//存储计数值索引
    for (uint8_t i = 0; i < 8; i++)
    {
        while (DHT11_Read())
            ;//等待低电平
        //变低了说明上一次数据位读取结束

        while (!DHT11_Read())
            ;//等待变高电平
        //变高了说明数据位读取开始

        /**开始读数据*/
        //低电平:26-28us   高电平:70us
        uint16_t time_count;
        for (time_count = 0; DHT11_Read(); ++time_count)
            ;                                //等待低电平
        timeBuf[timeBufIndex++] = time_count;//存储计数值
    }
}


bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
{
    static uint8_t buf[5];
    static uint16_t timeMax = 0;
    static uint16_t timeMin = 0xFFFF;

    DHT11_Rst();               //在里面设置了输出模式
    DHT11_Check();             //在里面设置了输入模式
                               //  return false;//如果超时,则退出
    for (unsigned char &i: buf)//读取40位数据
    {
        DHT11_Read_Byte_Fast_Pro();
    }

    for (unsigned short i: timeBuf)
    {
        if (i > timeMax)
        {
            timeMax = i;
        }
        else
        {
            timeMin = i;
        }
    }
    std_delay_25us();
    std_delay_25us();
    DHT11_OUT();
    DHT11_High();


    /**把计数值转为二进制数据*/
    uint16_t timeMed = (timeMax + timeMin) >> 1;//整除2,取中位数
    uint8_t data;                               //临时数据
    bool tempBin;                               //临时二进制数据
    for (int i = 0; i < 5; ++i)
    {
        data = 0;//重置
        for (int j = 0; j < 8; ++j)
        {
            data <<= 1;
            //比较计数值,读取二进制数据
            if (timeBuf[i * 8 + j] > timeMed)
            {
                tempBin = true;
            }
            else
            {
                tempBin = false;
            }
            data |= tempBin;
        }
        buf[i] = data;//存储数据
    }

    /**检验**/
    if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
    {
        humi = (float) (buf[0] + buf[1] * 0.1);
        temp = (float) (buf[2] + buf[3] * 0.1);
        return true;
    }
    else
    {
        return false;
    }
}
#endif

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

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

相关文章

ChatGPT Prompt技术全攻略-精通篇:Prompt工程技术的高级应用

系列篇章&#x1f4a5; No.文章1ChatGPT Prompt技术全攻略-入门篇&#xff1a;AI提示工程基础2ChatGPT Prompt技术全攻略-进阶篇&#xff1a;深入Prompt工程技术3ChatGPT Prompt技术全攻略-高级篇&#xff1a;掌握高级Prompt工程技术4ChatGPT Prompt技术全攻略-应用篇&#xf…

USB (2)

USB transaction 以2.0的枚举过程为例。 首先是TOKEN TRANSACTION&#xff0c;其次是DATA TRANSACTION&#xff0c;再次是Handshake Transaction。 上面的SETUP TRANSACTION是TOKEN TRANSACTION的一种。另外三种是OUT, IN, SOF。 在每个TRANSACTION中又包含了3个STAGE&#x…

层次聚类分析星

clc,clear a [73,40,7;60,15,5;61,19,2;34,18,6;67,126,10;91,40,4;101,40,13;81,40,6;88,40,8;122,40,17;102,50,17;87,50,12;110,50,14;164,50,17;40,30,1;76,40,17;118,50,9;160,50,15];[m,n] size(a);d zeros(m); d mandist(a); % mandist 求矩阵列向量组之间的两两…

pxe批量部署linux介绍

1、PXE批量部署的作用及必要性&#xff1a; 1&#xff09;智能实现操作系统的批量安装&#xff08;无人值守安装&#xff09;2&#xff09;减少管理员工作&#xff0c;提高工作效率3&#xff09;可以定制操作系统的安装流程a.标准流程定制(ks.cfg)b.自定义流程定制(ks.cfg(%pos…

linux系统——telnet,ssh命令

telent命令用于登录远程主机&#xff0c;监测远程主机端口是否打开&#xff0c;明文传输&#xff0c;安全性较低&#xff0c;后被弃用&#xff0c;改为ssh

『大模型笔记』Transformer的几种高效自注意力(降低计算复杂度的方法)!

Transformer的几种高效自注意力(降低计算复杂度的方法)! 文章目录 一. 快速回顾一下注意力机制二. 有哪些技术可以用来提高注意力的效率1. Sparse attention(1) 算法原理:Strided Attention & Fixed Attention(2) 复杂度分析: O ( N N p ) \mathscr{O}(N\sqrt[p]{N}) O(…

[学习笔记] VFX Silhouette

Part 1 : The interface of Silhouettte &#xff08;Silhouette的界面介绍&#xff09; Part 2: The shape divisions and manual roto&#xff08;形状分区和手动roto工作&#xff09;: roto过程一般使用 b 曲线roto工作需要逐帧工作按Alt可以修改锚点(拖动)和改变锚点平滑…

基于Gabor小波特征提取和PSO-SVM的胃溃疡分类(MATLAB R2018a)

Gabor滤波器是在测不准原则下能够在时域和频域中唯一能取得最佳的联合分辨率函数&#xff08;测不准原则&#xff1a;是指在时域与频域中都要获得任何的测量精度那是不可能同时实现的&#xff0c;要使时域分辨率有所提高&#xff0c;必须牺牲频域的分辨率&#xff0c;反之亦然&…

Hugging Face系列2:详细剖析Hugging Face网站资源——实战六类开源库

Hugging Face系列2&#xff1a;详细剖析Hugging Face网站资源——实战六类开源库 前言本篇摘要2. Hugging Face开源库2.1 transformers2.1.1 简介2.1.2 实战1. 文本分类2. 图像识别3. 在Pytorch和TensorFlow中使用pipeline 2.2 diffusers2.2.1 简介2.2.2 实战1. 管线2. 模型和调…

【AI大模型】Prompt Engineering

目录 什么是提示工程&#xff08;Prompt Engineering&#xff09; Prompt 调优 Prompt 的典型构成 「定义角色」为什么有效&#xff1f; 防止 Prompt 攻击 攻击方式 1&#xff1a;著名的「奶奶漏洞」 攻击方式 2&#xff1a;Prompt 注入 防范措施 1&#xff1a;Prompt 注…

如何从清空的回收站恢复照片

担心如何从清空的回收站中恢复已删除的照片&#xff1f;您删除的文件和文件夹暂时存储在 Windows 回收站中。它使 Windows 用户能够快速恢复意外或故意丢失的项目。但是&#xff0c;为了释放计算机驱动器上的某些空间&#xff0c;有时可能需要清理回收站。 然而&#xff0c;有…

车用柴油氧化安定性检测 GB 19147-2009全项检测

柴油分为轻柴油&#xff08;沸点范围约180-370℃&#xff09;和重柴油&#xff08;沸点范围约350-410℃&#xff09;两大类。柴油使用性能中最重要的是着火性和流动性&#xff0c;其技术指标分别为十六烷值和凝点&#xff0c;我国柴油现行规格中要求含硫量控制在0.5%-1.5%。 检…

增值税发票OCR识别功能介绍

OCR增值税发票识别功能介绍如下&#xff1a; 一、技术原理 OCR增值税发票识别系统基于光学字符识别&#xff08;OCR&#xff09;技术和人工智能的支持&#xff0c;将传统纸质发票的信息自动转换为计算机可以读取的数字信息。具体技术流程包括&#xff1a; 图像预处理&#x…

SAP ABAP 往数据库表里加数据

目录 方法一&#xff1a;SE16N SE11 方法二&#xff1a;创建维护VIEW&#xff1a;SE11 SM30 Error补充说明&#xff1a; 方法一&#xff1a;SE16N SE11 首先SE16N 进来。 进来之后在テーブル的位置输入表名&#xff0c;然后点击执行&#xff08;F8&#xff09; 如果第一次…

【复现】含能量路由器的交直流混合配电网潮流计算

目录 1 主要内容 2 理论及模型 3 程序结果 4 下载链接 1 主要内容 程序复现《含能量路由器的交直流混合配电网潮流计算》&#xff0c;主要是对算例4.1进行建模分析&#xff0c;理论和方法按照文献所述。能量路由器&#xff08;ER&#xff09;作为新兴的电力元器件&#xff…

八、【源码】细化XML语句构建器,完善静态SQL解析

源码地址&#xff1a;https://github.com/mybatis/mybatis-3/ 仓库地址&#xff1a;https://gitcode.net/qq_42665745/mybatis/-/tree/08-optimize-xml-parse 细化XML语句构建器&#xff0c;完善静态SQL解析 这一节主要是优化XML解析SQL部分&#xff0c;流程大概为&#xff…

SpringBoot+Vue企业客户管理系统(前后端分离)

技术栈 JavaSpringBootMavenMySQLMyBatisVueShiroElement-UI 角色对应功能 员工管理员 功能截图

饥荒云服务器卡顿情况如何解决

随着网络游戏的普及&#xff0c;云服务器逐渐成为游戏开发者和玩家们的首选。然而&#xff0c;在使用饥荒云服务器时&#xff0c;有时会遇到卡顿的问题&#xff0c;这给玩家带来了困扰。本文将探讨饥荒云服务器卡顿的原因&#xff0c;并提供一些可能的解决方法。 卡顿产生的原因…

Vyper重入漏洞解析

什么是重入攻击 Reentrancy攻击是以太坊智能合约中最具破坏性的攻击之一。当一个函数对另一个不可信合约进行外部调用时&#xff0c;就会发生重入攻击。然后&#xff0c;不可信合约会递归调用原始函数&#xff0c;试图耗尽资金。 当合约在发送资金之前未能更新其状态时&#…

分布式事务大揭秘:使用MQ实现最终一致性

本文作者:小米,一个热爱技术分享的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货! 大家好,我是小米,一个热爱分享技术的29岁程序员,今天我们来聊聊分布式事务中的一种经典实现方式——MQ最终一致性。这是一个在互联网公司中广…