参考——温湿度传感器DHT11驱动_STM32
设备: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--);}
}
需要注意的是,下面代码使用的是C++,如果你使用的是C,那么用相应功能的代码替换掉即可。bool类型需要包含stdbool头文件
DHT11.h
头文件里未被注释的接口,决定了哪些函数需要重点关注
#ifndef DHT11_H
#define DHT11_Hvoid 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以上
同时说明一下,下面将作为反面例子!!,找了许久才发现是数组越界引发的硬件中断,下面代码忘了对timeBufIndex清零。后面我会发一个简化版
#include "DHT11.h"
#include "cmsis_os2.h"
#include "stm32f4xx_hal.h"#define DHT11_Pin GPIO_PIN_6
#define DHT11_GPIO_Port GPIOE
#define DHT11_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE()#define DHT11_Read() (DHT11_GPIO_Port->IDR & DHT11_Pin) /*HAL_GPIO_ReadPin(DHT11_GPIO_Port, DHT11_Pin)*/
#define DHT11_Write(x) HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, (GPIO_PinState) (x))#define DHT11_High() HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_SET)
#define DHT11_Low() HAL_GPIO_WritePin(DHT11_GPIO_Port, DHT11_Pin, GPIO_PIN_RESET)static GPIO_InitTypeDef GPIO_InitStruct = {0};
static uint16_t std_delay_80us=875;//事先测试过
static uint16_t std_delay_50us=566;
static uint16_t time_count;/**动态计算延时,以确保任何情况下都可以得到较为准确的延时*/
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);
}/**函数*/
void DHT11_Input();
void DHT11_Output();
void DHT11_Rst();void DHT11_Init()
{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_HIGH;HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);DHT11_High();
}void DHT11_Input()
{GPIO_InitStruct.Mode = GPIO_MODE_INPUT;HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
}void DHT11_Output()
{GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;HAL_GPIO_Init(DHT11_GPIO_Port, &GPIO_InitStruct);
}void DHT11_Rst()
{DHT11_Output();DHT11_Low();osDelay(25);//根据时序图可知,需要至少拉低18msDHT11_High();std_delay_us(20);//20-40us
}void DHT11_Check()
{DHT11_Input();for (time_count =0;DHT11_Read();++time_count);/* 等待DHT11数据线拉低 */for (time_count =0;!DHT11_Read();++time_count); /* 等待DHT11数据线拉高 *///持续80usstd_delay_80us = time_count;//获取80us需要的计数值
}//从DHT11读取一个位
//返回值:1/0
uint8_t DHT11_Read_Bit()
{for (time_count =0;DHT11_Read();++time_count);//等待低电平//变低了说明上一次数据位读取结束for (time_count =0;!DHT11_Read();++time_count); //等待变高电平// std_delay_50us = time_count;//测试用的//变高了说明数据位读取开始/**开始读数据*///低电平:26-28us//高电平:70usstd_delay_us(40);// 等待40us,如果延时准确的话if (DHT11_Read())return 1;elsereturn 0;
}//从DHT11读取一个字节
//返回值:读到的数据
uint8_t DHT11_Read_Byte()
{uint8_t dat = 0;for (uint8_t i = 0; i < 8; i++){dat <<= 1;dat |= DHT11_Read_Bit();}return dat;
}//uint16_t timeBuf[40];
//uint8_t timeBufIndex=0;
//uint16_t timeMax=0;
//uint16_t timeMin=10000;
//uint8_t DHT11_Read_Byte_Fast()
//{
// uint8_t dat=0;
// for (uint8_t i = 0; i < 8; i++)
// {
// for (time_count =0;DHT11_Read();++time_count);//等待低电平
// //变低了说明上一次数据位读取结束
//
// for (time_count =0;!DHT11_Read();++time_count); //等待变高电平
// // std_delay_50us = time_count;//测试用的
// //变高了说明数据位读取开始
//
// /**开始读数据*/
// //低电平:26-28us 高电平:70us
// for (time_count =0;DHT11_Read();++time_count);//等待低电平
// timeBuf[timeBufIndex++]=time_count;
// // std_delay_us(30);// 等待40us,如果延时准确的话
// dat <<= 1;
// dat |= DHT11_Read();
// }
// return dat;
//}uint16_t timeBuf[40];//存储计数值
uint8_t timeBufIndex=0;//存储计数值索引
uint16_t timeMax=0;
uint16_t timeMin=0xFFFF;
void DHT11_Read_Byte_Fast_Pro()
{for (uint8_t i = 0; i < 8; i++){for (time_count =0;DHT11_Read();++time_count);//等待低电平//变低了说明上一次数据位读取结束for (time_count =0;!DHT11_Read();++time_count); //等待变高电平//变高了说明数据位读取开始/**开始读数据*///低电平:26-28us 高电平:70usfor (time_count =0;DHT11_Read();++time_count);//等待低电平timeBuf[timeBufIndex++]=time_count;//存储计数值}
}bool DHT11_Read_Data_Fast_Pro(float &temp, float &humi)
{uint8_t buf[5];DHT11_Rst();DHT11_Check();for (unsigned char & i : buf)//读取40位数据{DHT11_Read_Byte_Fast_Pro();}for (unsigned short i : timeBuf){if(i>timeMax){timeMax=i;}else if(i<timeMin){timeMin=i;}}/**把计数值转为二进制数据*/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;}
}//bool DHT11_Read_Data(float &temp, float &humi)
//{
// uint8_t buf[5];
// DHT11_Rst();
// DHT11_Check();
// for (unsigned char & i : buf)//读取40位数据
// {
// i = DHT11_Read_Byte_Fast();
// }
//
// for (unsigned short i : timeBuf)
// {
// if(i>timeMax)
// {
// timeMax=i;
// }
// else if(i<timeMin)
// {
// timeMin=i;
// }
// }
//
// uint16_t timeMed=(timeMax+timeMin)>>1;
// uint8_t data;
// uint8_t tempBin;
// uint8_t tempBuf[5];
// 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=1;
// }
// else
// {
// tempBin=0;
// }
// data|=tempBin;
// }
// tempBuf[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;
// }
//}
修正版。里面有三个版本,没被注释的是特化版本,其下愈为通用
#include "DHT11.h"
#include "cmsis_os2.h"
#include "delay.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;/**动态计算延时,以确保任何情况下都可以得到较为准确的延时*/
//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 < 273; ++i);
}/**函数*/
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);//根据时序图可知,需要至少拉低18msDHT11_High();std_delay_25us();//20-40us
}inline void DHT11_Check()
{DHT11_IN();while (DHT11_Read());while (!DHT11_Read());
}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;}
}/********************下面为次优级优化********************/全局变量
//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();
// }
//
// 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;
// }
//}//
//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;
// }
//}/********************下面为原版优化********************/
//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;
// }
//}
相关文章:
参考——温湿度传感器DHT11驱动_STM32
设备:stm32f407ZGT6 环境:FreeRTOS HAL 到网上找DHT11的驱动,但是都无法使用。原因是RTOS环境中,由于多线程,使用循环计数阻塞式的delay_us延时函数就没那么准,且不同设备中delay_us的计数值不一样…...
架构每日一学 14:架构师如何进行可行性探索?
架构活动中,如果不进行可行性探索可能会导致重大失误,为企业发展带来风险。 可行性探索是架构活动的最后一个节点,在这之后的架构活动就像是离弦之箭,即便发现重大风险也很难再回头了。 互联网公司之间的竞争非常激烈࿰…...
多线程知识-13
为什么应该在循环中检查等待条件 为了实现多线程的同步和协调,通常使用等待和唤醒机制。在等待和唤醒机制中,等待条件是指一个线程等待某个条件的满足,当条件满足时,线程被唤醒继续执行。 在循环中检查等待条件的目的是为了避免虚…...
vue3+cli-service配置代理,跨域请求
一、配置代理端口和代理转发 在vue.config.js文件中 const {defineConfig} require(vue/cli-service)module.exports defineConfig({devServer: {host: 0.0.0.0,port: 8088, // 启动端口号proxy: {/api: { // 请求接口中要替换的标识target: , // 代理地址,后…...
git介绍、安装、配置
文章目录 1. GIT介绍2. 使用GIT的好处3. GIT 安装4. GIT 配置4.1 GIT 初始化设置、命令别名设置4.2 如果终端安装了oh-my-zsh,会带一堆git命令别名4.3 GIT配置文件介绍4.3.1 Linux、Mac OS系统4.3.2 windows系统 5. git设置远程仓库账号密码(拉取、上传代码不用输入…...
打开flutter调试
debugPaintSizeEnabled true; debugPaintBaselinesEnabled true;...
【前端 - Vue】Vuex基础入门,创建仓库的详细步骤
🚀 个人简介:6年开发经验,现任职某国企前端负责人,分享前端相关技术与工作常见问题~ 💟 作 者:前端菜鸟的自我修养❣️ 📝 专 栏:vue从基础到起飞 🌈 若有帮助&…...
#01 Stable Diffusion基础入门:了解AI图像生成
文章目录 前言什么是Stable Diffusion?Stable Diffusion的工作原理如何使用Stable Diffusion?Stable Diffusion的应用场景结论 前言 在当今迅速发展的人工智能领域,AI图像生成技术以其独特的魅力吸引了广泛的关注。Stable Diffusion作为其中的一项前沿技术&#…...
Knife4j使用
Knife4j使用 文章目录 Knife4j使用1、Knife4j介绍2、SpringBoot集成Knife4j3、基本使用 1、Knife4j介绍 Knife4j是一个用于生成和展示API文档的工具,同时它还提供了在线调试的功能,可以看作是Swagger的升级版,界面也比Swagger更好看…...
一文读懂银行承兑汇票:从申请到使用全攻略
银行承兑汇票(Banks Acceptance Bill,BA)是商业汇票的一种。它是由在承兑银行开立存款账户的存款人出票,向开户银行申请并经银行审查同意承兑的,保证在指定日期无条件支付确定的金额给收款人或持票人的票据。银行承兑汇…...
唯众智联网(AIoT)应用开发教学实训解决方案
一、引言 随着信息技术的飞速发展,物联网(IoT)和人工智能(AI)技术逐渐融合,形成了智联网(AIoT)这一新兴领域。智联网通过智能化设备、传感器、云计算等技术手段,实现了数…...
归纳跨域几种解决方案
什么是跨域? **说起跨域,就要知道什么是浏览器同源策略 **浏览器同源策略:必须是协议、域名、端口完全一致的才符合同源策略 **如果以上三项,有一项不同都涉及到跨域问题 为什么浏览器要设置同源策略呢? 没有同源策…...
LeetCode刷题第3题(C#)
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串的长度。 法一: 这道题用到的其实是滑动窗口。 滑动窗口算法是在特定窗口大小的数组或字符串上执行要求的操作。它可以将一部分问题中的嵌套循环转变为一个单循环,以此减少时间复…...
了解一下Ubuntu Linux
1.3.1 什么是Ubuntu Ubuntu这个名字非常神奇,它取自非洲南部祖鲁语的ubuntu,是一个哲学名称,其意思为“人性”或者“我的存在是因为大家的存在”。对于中国人来说,一般称呼它为乌班图。 Ubuntu是在Debian的基础上开发出来的&am…...
单一原则+干湿分离,让你的架构能力起飞
# 概念 软件单一原则(Single Responsibility Principle,SRP)是面向对象编程中五大基本设计原则之一。它指每个软件模块或类都应该只负责一个单一的功能或责任。 高内聚低耦合 实现代码可维护性 干湿分离是一种建筑设计和室内装修的方法,主…...
如何恢复永久删除的照片?
“嗨,我永久删除了电脑上的很多照片。回收站被清空,照片会永久丢失吗?有什么方法可以恢复这些已删除的照片吗? 我们所有人都经历过同样的事情:我们的硬盘上存储了文件、视频或照片,但不小心删除了它。这个…...
一文看懂llama2(原理模型训练)
自从Transformer架构问世以来,大型语言模型(Large Language Models, LLMs)以及AIGC技术的发展速度惊人,它们不仅在技术层面取得了重大突破,还在商业应用、社会影响等多个层面展现出巨大潜力。随着ChatGPT的推出&#x…...
Sui基金会公布2024年3–4月资助项目名单
Sui基金会宣布3月和4月的资助项目名单,在这两个月中,共有10个项目获得了资助,以加速Sui的整合和发展。其中有八个项目专注于为开发者创造更好的体验,从开发强大的集成开发环境(IDE)到使用零知识证明保护用户…...
Spring Security3.0.1版本
前言: 抽象Spring Security3.0上一篇 在上一篇中,我们完成了对Security导入,快速入门,和对自动配置的简单验证 对登录流程的分析和Security基本原理 补充: 先解决上一篇留下的问题,端口和端点的区别 端…...
网络报文协议头学习
vxlan:就是通过Vxlan_header头在原始报文前面套了一层UDPIP(4/6)Eth_hdr 需求背景:VXLAN:简述VXLAN的概念,网络模型及报文格式_vxlan报文格式-CSDN博客 如果服务器作为VTEP,那从服务器发送到接…...
XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
