当前位置: 首页 > news >正文

(51单片机)LCD显示数据存储(DS1302时钟模块教学)(LCD1602教程)(独立按键教程)(延时函数教程)(I2C总线认识)(AT24C02认识)

目录

 演示视频:

 源代码

 main.c

LCD1602.c

LCD1602.h

AT24C02.c

AT24C02.h

Key.c

 Key.h

I2C.c

I2C.h

Delay.c 

 Delay.h

 代码解析与教程:

 Dealy模块

 LCD1602模块

 Key模块

I2C总线模块

AT24C02模块 /E2PROM模块

 main模块


 演示视频:

(正在审核,通过给大家发出来)

 源代码

如上图将11个文放在Keli5 中即可,然后烧录在单片机中就行了

烧录软件用的是STC-ISP,不知道怎么安装的可以去看江科大的视频:

【51单片机入门教程-2020版 程序全程纯手打 从零开始入门】https://www.bilibili.com/video/BV1Mb411e7re?p=2&vd_source=ada7b122ae16cc583b4add52ad89fd5e

源代码:

头文件要记得宏定义和重定义,避免重复调用:

#ifndef _Timer0_h_//名字根据文件名定义即可
#define _Timer0_h_//声明函数……#endif

 main.c

#include <STC89C5xRC.H>
#include "LCD1602.h"
#include "Key.h"
#include "Delay.h"
#include "AT24C02.h"//获取按键值
unsigned char KeyNum;
//16位的int类型的数
unsigned int Num;//主函数
void main()
{LCD_Init();//初始化LCDLCD_ShowNum(1,1,Num,5);//初始化显示while(1){KeyNum=Key();//获取按键值if(KeyNum==1)	//K1按键,Num自增{Num++;LCD_ShowNum(1,1,Num,5);//LCD显示当前Num}if(KeyNum==2)	//K2按键,Num自减{Num--;LCD_ShowNum(1,1,Num,5);//LCD显示当前Num}if(KeyNum==3)	//K3按键,向AT24C02写入数据{AT24C02_WriteByte(0,Num%256);//获取高8位,存到0字节地址Delay(5);//注意写周期5msAT24C02_WriteByte(1,Num/256);//获取低8位,存到1字节地址Delay(5);LCD_ShowString(2,1,"Write OK");//LCD显示Delay(1000);//显示1秒后消失LCD_ShowString(2,1,"        ");//显示1秒后消失}if(KeyNum==4)	//K4按键,从AT24C02读取数据{Num=AT24C02_ReadByte(0);//返回值Data赋值给Num,获取0字节地址的高8位Num|=AT24C02_ReadByte(1)<<8;//返回值Data赋值给Num,获取1字节地址的低8位,因为Num是16字节,先左移8将前8位变成0,再|(有1即1)将低8位获取出来LCD_ShowNum(1,1,Num,5);//LCD显示当前Num(Data)LCD_ShowString(2,1,"Read OK ");//LCD显示Delay(1000);//显示1秒后消失LCD_ShowString(2,1,"        ");//显示1秒后消失}}
}

LCD1602.c

#include <STC89C5xRC.H>//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0//函数定义:
/*** @brief  LCD1602延时函数,12MHz调用可延时1ms* @param  无* @retval 无*/
void LCD_Delay()		//@11.0592MHz
{unsigned char i, j;i = 11;j = 190;do{while (--j);} while (--i);
}/*** @brief  LCD1602写命令* @param  Command 要写入的命令* @retval 无*/
void LCD_WriteCommand(unsigned char Command)
{LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief  LCD1602写数据* @param  Data 要写入的数据* @retval 无*/
void LCD_WriteData(unsigned char Data)
{LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();
}/*** @brief  LCD1602设置光标位置* @param  Line 行位置,范围:1~2* @param  Column 列位置,范围:1~16* @retval 无*/
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));}
}/*** @brief  LCD1602初始化函数* @param  无* @retval 无*/
void LCD_Init()
{LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动LCD_WriteCommand(0x01);//光标复位,清屏
}/*** @brief  在LCD1602指定位置上显示一个字符* @param  Line 行位置,范围:1~2* @param  Column 列位置,范围:1~16* @param  Char 要显示的字符* @retval 无*/
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{LCD_SetCursor(Line,Column);LCD_WriteData(Char);
}/*** @brief  在LCD1602指定位置开始显示所给字符串* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  String 要显示的字符串* @retval 无*/
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='\0';i++){LCD_WriteData(String[i]);}
}/*** @brief  返回值=X的Y次方*/
int LCD_Pow(int X,int Y)
{unsigned char i;int Result=1;for(i=0;i<Y;i++){Result*=X;}return Result;
}/*** @brief  在LCD1602指定位置开始显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~65535* @param  Length 要显示数字的长度,范围:1~5* @retval 无*/
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief  在LCD1602指定位置开始以有符号十进制显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:-32768~32767* @param  Length 要显示数字的长度,范围:1~5* @retval 无*/
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');}
}/*** @brief  在LCD1602指定位置开始以十六进制显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~0xFFFF* @param  Length 要显示数字的长度,范围:1~4* @retval 无*/
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber<10){LCD_WriteData(SingleNumber+'0');}else{LCD_WriteData(SingleNumber-10+'A');}}
}/*** @brief  在LCD1602指定位置开始以二进制显示所给数字* @param  Line 起始行位置,范围:1~2* @param  Column 起始列位置,范围:1~16* @param  Number 要显示的数字,范围:0~1111 1111 1111 1111* @param  Length 要显示数字的长度,范围:1~16* @retval 无*/
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');}
}

LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__//用户调用函数:
void LCD_Init();//初始化
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);//显示单个字符
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);//显示字符串
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//显示数字
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);//显示带符号数字
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//显示十进制数字
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);//显示二进制数字#endif

AT24C02.c

//AT24C02.c#include <STC89C5xRC.H>
#include "I2C.h"#define AT24C02_ADDRESS 0xA0/*** @brief  AT24C02写入一个字节* @param  WordAddress 要写入字节的地址* @param  Data 要写入的数据* @retval 无*/void AT24C02_WriteByte(unsigned char WordAddress,Data){I2C_Start();I2C_SendByte(AT24C02_ADDRESS);I2C_ReceiveAck();I2C_SendByte(WordAddress);I2C_ReceiveAck();I2C_SendByte(Data);I2C_ReceiveAck();I2C_Shop();
}/*** @brief  AT24C02读取一个字节* @param  WordAddress 要读出字节的地址* @retval 读出的数据*/unsigned char AT24C02_ReadByte(unsigned char WordAddress){unsigned char Data;I2C_Start();I2C_SendByte(AT24C02_ADDRESS);I2C_ReceiveAck();I2C_SendByte(WordAddress);I2C_ReceiveAck();I2C_Start();I2C_SendByte(AT24C02_ADDRESS|0x01);I2C_ReceiveAck();Data=I2C_ReceiveByte();I2C_SendAck(1);I2C_Shop();return Data;
}

AT24C02.h

//AT24C02.h#ifndef __AT24C02_H__
#define __AT24C02_H__void AT24C02_WriteByte(unsigned char WordAddress,Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);
#endif

Key.c

#include <STC89C5xRC.H>
#include "Delay.h"unsigned char Key()//获取独立按键
{unsigned char KeyNumber=0;//进行判断是否摁下按键和防抖操作if(P31==0){Delay(20);while(P31==0);Delay(20);KeyNumber=1;}if(P30==0){Delay(20);while(P30==0);Delay(20);KeyNumber=2;}if(P32==0){Delay(20);while(P32==0);Delay(20);KeyNumber=3;}if(P33==0){Delay(20);while(P33==0);Delay(20);KeyNumber=4;}return KeyNumber;
}

 Key.h

#ifndef _Key_h_
#define _Key_h_unsigned char Key();#endif

I2C.c

//I2C.c#include <STC89C5xRC.H>sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;//I2C开始
void I2C_Start(){I2C_SDA=1;I2C_SCL=1;I2C_SDA=0;I2C_SCL=0;
}//I2C停止
void I2C_Shop(){I2C_SDA=0;I2C_SCL=1;I2C_SDA=1;
}//I2C发送一个字节
void I2C_SendByte(unsigned char Byte){unsigned char i;for(i=0;i<8;i++){I2C_SDA=Byte&(0x80>>i);I2C_SCL=1;I2C_SCL=0;}
}//I2C接收一个字节
unsigned char I2C_ReceiveByte(){unsigned char i,Byte=0x00;I2C_SDA=1;for(i=0;i<8;i++){I2C_SCL=1;if(I2C_SDA){Byte|=(0x80>>i);}I2C_SCL=0;}return Byte;
}//I2C发送应答
void I2C_SendAck(unsigned char AckBit){I2C_SDA=AckBit;I2C_SCL=1;I2C_SCL=0;
}//I2C接收应答
unsigned char I2C_ReceiveAck(){unsigned char AckBit;I2C_SDA=1;I2C_SCL=1;AckBit=I2C_SDA;I2C_SCL=0;return AckBit;
}

I2C.h

//I2C.h#ifndef __I2C_H__
#define __I2C_H__unsigned char I2C_ReceiveAck();
void I2C_SendAck(unsigned char AckBit);
unsigned char I2C_ReceiveByte();
void I2C_SendByte(unsigned char Byte);
void I2C_Shop();
void I2C_Start();#endif

Delay.c 

//Delay.c#include <STC89C5xRC.H>
#include <INTRINS.H>//延时函数
void Delay(unsigned int xms)		//@11.0592MHz
{unsigned char i, j;while(xms){i = 2;j = 199;do{while (--j);} while (--i);xms--;}
}

 Delay.h

//Delay.h#ifndef __Delay_H__
#define __Delay_H__//延时函数头文件
void Delay(unsigned int xms);
#endif

 代码解析与教程:

 Dealy模块
  • 包含源代码与头文件,不需要知道怎么实现的会用即可,后续使用,直接将头文件和源代码拿过来用即可;

xms是定义的毫秒,1000毫秒就是1秒;模版生成的是1毫秒的,因此xms等于1000

 LCD1602模块
  • 包含源代码与头文件,不需要知道怎么实现的会用即可,后续使用,直接将头文件和源代码拿过来用即可;使用格式:(参考江科大的视频素材)

  • LCD1602相关重要知识:
    • LCD1602有两上下两行显示屏,每行各有16个小显示屏,如上图中的LCD_ShowString(1,3,"Hello"),第一个参数是第一行还是第二行,第2个参数是对应第几行的第几个小显示屏,最后一个是输出的东西,同理,到LCD_ShowNum(1,9,123,3)里,前三个和前面一样,最后一个参数是显示的位数,不够就在前面补0,例如输入1,参数为4,显示就是0001,输入23,参数为3,显示就是023

  • 上图是LCD1602的开发原理模块图,由图可知,P10-P17是控制显示屏的重点;像矩阵那样,S1,由P17和P13控制,同理,其他按键一样,由于开发板限制,我们一般使用列遍历来控制按键,例如:S1,S5,S9,S13他们四个共用P13,所以将其为1列,将P13设定为1,P17设定为0,S1就会亮,其他按键同理。
 Key模块
  • 包含源代码与头文件,不需要知道怎么实现的会用即可,后续使用,直接将头文件和源代码拿过来用即可;

    序号1是按键的防抖操作,不需要理解,有按键的地方直接用即可
    序号2是独立按键控制变量。
    KeyNumber就是返回值,按键K1就返回1,其他同理
I2C总线模块
  • 包含源代码与头文件,需要知道怎么实现的,会用,后续使用,直接将头文件和源代码拿过来用即可;因为这部分比较难,轻度认识理解就行,越深越难
  • 在此之前,我们要认识一下存储器:

  • 本篇博客,以及博主的板子型号(STC89c52rc),用的储存器就是E2PROM储存器,简单介绍一下,RAM就是可读写储存器,SRAM是静态的,容量较大,成本高,类似于我们手机内存,DRAM是动态的,容量较小,成本较低,类似于我们手机的运行内存;ROM按常理来说是不可写的,只能读,但是他比RAM不易丢失,有断电保护,断电后数据不易丢失,因此非常受大家的喜欢,所以在后续的更新中,好多ROM也可以读写了,例如E2PROM。
  • 了解上面之后,I2C总线可以理解成一个传输协议,或者传输方式,规则;作用是将一个机器(主机)的数据传输到另一个机器(从机);来看E2PROM原理图

  • 可以看到SCL,SDA是I2C接口,A0,A1,A2是I2C地址,通过这些东西,将主机(单片机)数据传递到从机(E2PROM);下面来看传输是怎么实现的
  • I2C总线传输时有两大部分,时序结构和数据帧,时序结构就是SCL,SDA这种东西的先后顺序或者说他俩的一些重要关系
  • 时序结构有6个小模块,看图:

这是开始和结束两部分中间框起来的部分是数据确认阶段,高电平是1(高一点的部分),低电平是0(低一点的部分);图中可以看到在数据确认阶段SCL始终为1,SDA也会变化,因此有


//定义SCL,SDA的引脚
sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;//I2C开始
void I2C_Start(){I2C_SDA=1;I2C_SCL=1;I2C_SDA=0;I2C_SCL=0;
}//I2C停止
void I2C_Shop(){
//因为开始后SCL=0了,所有这里不需要重复定义了I2C_SDA=0;I2C_SCL=1;I2C_SDA=1;
}

接下来是发送数据,发送一个字节(8个bit),在数据确认期间,SCL先0后1再0,SDA是数据,有可能是1,有可能是0,也会变化,即可发送一个bit,然后循环8次就是一个字节:

//I2C发送一个字节
void I2C_SendByte(unsigned char Byte){unsigned char i;for(i=0;i<8;i++){I2C_SDA=Byte&(0x80>>i);I2C_SCL=1;I2C_SCL=0;}
}

发送字节是参数Byte,i是循环次数,也是右移几个单位,重点来了:注意看图中的先后顺序,SDA先变化,数据是高位在前,0x80是1000 0000,Byte&(有0即0)0x80就是保留Byte的第一位(最高位),其他全变0;然后变化SCL0,1,0;循环八次

接下来是接收数据,接收一个字节(8个bit),在数据确认期间,SCL先0后1再0,SDA是数据,有可能是1,有可能是0,也会变化,即可发送一个bit,然后循环8次就是一个字节:

//I2C接收一个字节
unsigned char I2C_ReceiveByte(){unsigned char i,Byte=0x00;I2C_SDA=1;for(i=0;i<8;i++){if(I2C_SDA){Byte|=(0x80>>i);}I2C_SCL=1;I2C_SCL=0;}return Byte;
}

接收字节是无参数,返回值是Data,i是循环次数,也是右移几个单位,重点来了:注意,主机接收之前需要释放SDA(使SDA=1);注意看图中的先后顺序,SDA先变化,数据是高位在前,0x80是1000 0000,Byte&(有0即0)0x80就是保留Byte的第一位(最高位),其他全变0;然后变化SCL0,1,0;循环八次


接下来是发送应答和就收应答,作用就是在发送和接收时进行一个反馈,告诉你发送或者接收成功。
 
//I2C发送应答
void I2C_SendAck(unsigned char AckBit){I2C_SDA=AckBit;I2C_SCL=1;I2C_SCL=0;
}//I2C接收应答
unsigned char I2C_ReceiveAck(){unsigned char AckBit;I2C_SDA=1;AckBit=I2C_SDA;I2C_SCL=1;I2C_SCL=0;return AckBit;
}
发送应答参数AckBit是应答数据,0就是表示应答,1是不应答; 注意看图中的先后顺序,SDA先变化,将应答数据赋值给SDA;然后变化SCL0,1,0;
接收应答无参数,返回值是AckBit是应答数据,0就是表示应答,1是不应答; 注意,主机接收之前需要释放SDA(使SDA=1);注意看图中的先后顺序,SDA先变化将SDA赋值给AckBit;然后变化SCL0,1,0;
  • 了解时序结构之后,来看数据帧,也就是数据是怎么在I2C中传输的:

发送一帧数据是有I2C时序结构的6个小模块组成的,图中都标注了;需要注意的是要告诉I2C总线向谁发送,也就是要先发送从机地址,从机地址最后一个是告诉你是读还是写;

接收一帧数据是有I2C时序结构的6个小模块组成的,图中都标注了;需要注意的是要告诉I2C总线向谁接收,也就是要先发送从机地址,从机地址最后一个是告诉你是读还是写;注意最后一个发送应答是1,表示非应答

复合格式数据是有I2C时序结构的6个小模块组成的,图中都标注了;需要注意的是要告诉I2C总线向谁发送并接收,也就是要先发送从机地址,从机地址最后一个是告诉你是读还是写;注意最后一个发送应答是1,表示非应答

AT24C02模块 /E2PROM模块
  • 包含源代码与头文件,需要知道怎么实现的,会用,后续使用,直接将头文件和源代码拿过来用即可;因为这部分比较难,轻度认识理解就行,越深越难
  • AT24C02是E2PROM模块中的一小块,可以理解上述中所提到的从机主机就是单片机,因此从机地址就是AT24C02地址;

字节写,随机读数据帧是有I2C时序结构的6个小模块组成的,图中不再标注了;需要注意的是要告诉I2C总线读还是写,也就是要发送从机地址SLAVE ADDRESS,从机地址最后一个是告诉你是读还是写;还有一个就是发送存储字节地址WORD ADDRESS;注意最后一个发送应答是1,表示非应答

//AT24C02.c#include <STC89C5xRC.H>
#include "I2C.h"#define AT24C02_ADDRESS 0xA0/*** @brief  AT24C02写入一个字节* @param  WordAddress 要写入字节的地址* @param  Data 要写入的数据* @retval 无*/void AT24C02_WriteByte(unsigned char WordAddress,Data){I2C_Start();I2C_SendByte(AT24C02_ADDRESS);I2C_ReceiveAck();I2C_SendByte(WordAddress);I2C_ReceiveAck();I2C_SendByte(Data);I2C_ReceiveAck();I2C_Shop();
}/*** @brief  AT24C02读取一个字节* @param  WordAddress 要读出字节的地址* @retval 读出的数据*/unsigned char AT24C02_ReadByte(unsigned char WordAddress){unsigned char Data;I2C_Start();I2C_SendByte(AT24C02_ADDRESS);I2C_ReceiveAck();I2C_SendByte(WordAddress);I2C_ReceiveAck();I2C_Start();I2C_SendByte(AT24C02_ADDRESS|0x01);I2C_ReceiveAck();Data=I2C_ReceiveByte();I2C_SendAck(1);I2C_Shop();return Data;
}

按照图中的I2C总线时序结构,依次调用函数,即可实现

注意写周期是5ms,意味着每次写完后要延时5ms;

 main模块
  • 注释写的很清楚,这里不做解释了
#include <STC89C5xRC.H>
#include "LCD1602.h"
#include "Key.h"
#include "Delay.h"
#include "AT24C02.h"//获取按键值
unsigned char KeyNum;
//16位的int类型的数
unsigned int Num;//主函数
void main()
{LCD_Init();//初始化LCDLCD_ShowNum(1,1,Num,5);//初始化显示while(1){KeyNum=Key();//获取按键值if(KeyNum==1)	//K1按键,Num自增{Num++;LCD_ShowNum(1,1,Num,5);//LCD显示当前Num}if(KeyNum==2)	//K2按键,Num自减{Num--;LCD_ShowNum(1,1,Num,5);//LCD显示当前Num}if(KeyNum==3)	//K3按键,向AT24C02写入数据{AT24C02_WriteByte(0,Num%256);//获取高8位,存到0字节地址Delay(5);//注意写周期5msAT24C02_WriteByte(1,Num/256);//获取低8位,存到1字节地址Delay(5);LCD_ShowString(2,1,"Write OK");//LCD显示Delay(1000);//显示1秒后消失LCD_ShowString(2,1,"        ");//显示1秒后消失}if(KeyNum==4)	//K4按键,从AT24C02读取数据{Num=AT24C02_ReadByte(0);//返回值Data赋值给Num,获取0字节地址的高8位Num|=AT24C02_ReadByte(1)<<8;//返回值Data赋值给Num,获取1字节地址的低8位,因为Num是16字节,先左移8将前8位变成0,再|(有1即1)将低8位获取出来LCD_ShowNum(1,1,Num,5);//LCD显示当前Num(Data)LCD_ShowString(2,1,"Read OK ");//LCD显示Delay(1000);//显示1秒后消失LCD_ShowString(2,1,"        ");//显示1秒后消失}}
}

 注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!

相关文章:

(51单片机)LCD显示数据存储(DS1302时钟模块教学)(LCD1602教程)(独立按键教程)(延时函数教程)(I2C总线认识)(AT24C02认识)

目录 演示视频&#xff1a; 源代码 main.c LCD1602.c LCD1602.h AT24C02.c AT24C02.h Key.c Key.h I2C.c I2C.h Delay.c Delay.h 代码解析与教程&#xff1a; Dealy模块 LCD1602模块 Key模块 I2C总线模块 AT24C02模块 /E2PROM模块 main模块 演示视频&#xff1a; &…...

2d深度预测

Depth anything v1 相对深度估计&#xff0c;要用绝对深度估计需要微调 概要&#xff1a; 1 使用大量的未标注图像信息 2 采用优化策略—数据增强工具(作用在未标注图像) 3 进行辅助监督—继承语义分割知识&#xff08;作用在未标注图像&#xff09; 数据层面&#xff1a; …...

Android12 ServiceManager::addService源码解读

源码 Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {auto ctx mAccess->getCallingContext();// apps cannot add servicesif (multiuser_get_app_id(ctx.uid) >…...

【HDFS入门】HDFS性能调优实战:从基准测试到优化策略

目录 引言 1 HDFS性能评估体系 1.1 性能评估体系架构 1.2 基准测试工具对比 2 TestDFSIO基准测试实战 2.1 TestDFSIO工作原理 2.2 测试执行步骤 2.3 结果分析指标 3 TeraSort基准测试实战 3.1 TeraSort测试流程 3.2 测试执行命令 3.3 关键性能指标 4 HDFS性能调优…...

Linux 内核开发/测试工具对比 Windows 驱动验证工具 (Driver Verifier)

Windows 的 Driver Verifier 是一个用于测试和验证驱动程序的强大工具。在 Linux 内核开发中&#xff0c;虽然没有一个完全等价的单一工具&#xff0c;但有多种工具和框架可以提供类似或更专业的功能。 Linux 内核开发和测试工具 1. KASAN (Kernel Address Sanitizer) 功能&…...

通信算法之269 : OFDM信号的循环自相关特性用于无人机图传信号识别

OFDM信号的循环自相关特性是其循环平稳性的核心体现,如下: [相关仿真代码,联系,提供] 一、循环自相关特性来源 ‌循环前缀引入周期性‌ OFDM符号通过添加循环前缀(CP)形成符号周期结构,导致信号具有循环平稳性‌26。每个符号的CP与尾部数据重复,在时延等于FFT长度(N…...

【无人机】电子速度控制器 (ESC) 驱动电机,常见的电调协议,PWM协议,Oneshot协议,DShot协议

目录 1、ESCs & 电机 #1.1、ESC 协议 --ESC Protocols #1.1.1、PWM协议&#xff0c;需要校准 #1.1.2、One Shot 125协议&#xff0c;速率更快 #1.1.3、DShot &#xff0c;减少延迟&#xff0c;无需校准 #1.1.4、无人机CAN 2、PWM 伺服系统和 ESC&#xff08;电机控…...

LeadeRobot具身智能应用标杆:无人机X柔韧具身智能,空中精准作业游刃有余

当前,具身智能已成为全球科技领域的前沿焦点,更受到国家战略级重视,吸引科技产业巨头抢滩布局。但同时,具身智能的商业化路径、规模化应用场景、技术成本等难题也开始在资本界与产业圈引起广泛讨论。 目前,万勋科技基于Pliabot 柔韧技术已推出多款具身智能柔韧机器人产品,在柔…...

WebSocket:实现实时双向通信的技术

WebSocket是一种网络通信协议&#xff0c;它在单个TCP连接上提供全双工通信。WebSocket协议在2011年被IETF&#xff08;互联网工程任务组&#xff09;标准化为RFC 6455&#xff0c;并由W3C&#xff08;万维网联盟&#xff09;制定了WebSocket API标准&#xff0c;使得客户端&am…...

探索 HumanoidBench:类人机器人学习的新平台

在科技飞速发展的当下&#xff0c;类人机器人逐渐走进我们的视野&#xff0c;它们有着和人类相似的外形&#xff0c;看起来能像人类一样在各种环境里完成复杂任务&#xff0c;潜力巨大。但实际上&#xff0c;让类人机器人真正发挥出实力&#xff0c;还面临着重重挑战。 这篇文…...

「数据可视化 D3系列」入门第十一章:力导向图深度解析与实现

D3.js 力导向图深度解析与实现 力导向图核心概念 力导向图是一种通过物理模拟来展示复杂关系网络的图表类型&#xff0c;特别适合表现社交网络、知识图谱、系统拓扑等关系型数据。其核心原理是通过模拟粒子间的物理作用力&#xff08;电荷斥力、弹簧引力等&#xff09;自动计…...

「数据可视化 D3系列」入门第八章:动画效果详解(让图表动起来)

动画效果详解 一、D3.js动画核心API1. d3.transition()2. transition.duration()3. transition.delay()4. 其他重要API 二、动画实现原理三、完整动画示例解析1. 柱状图生长动画2. 文本跟随动画 四、动画效果优化技巧1. 缓动函数选择&#xff1a;2. 组合动画&#xff1a;3. 动画…...

index: 自动化浏览器智能体

GitHub&#xff1a;https://github.com/lmnr-ai/index 更多AI开源软件&#xff1a;https://www.aiinn.cn/ 在做浏览器自动化脚本时&#xff0c;我们常常需要编写大量代码来处理复杂的网页交互&#xff0c;不仅耗时耗力&#xff0c;还难以调试和优化&#xff0c;要是出错更是难以…...

网页端调用本地应用打开本地文件(PDF、Word、excel、PPT)

一、背景原因 根据浏览器的安全策略&#xff0c;在网页端无法直接打开本地文件&#xff0c;所以需要开发者曲线救国。 二、实现步骤 前期准备&#xff1a; 确保已安装好可以打开文件的应用软件&#xff0c;如&#xff0c;WPS&#xff1b; 把要打开的文件统一放在一个文件夹&am…...

如何批量在多个 Word 文档末尾添加广告页面

Word是我们日常使用非常频繁的文档编辑软件&#xff0c;凭借其强大的文本处理功能&#xff0c;如文字输入、格式设置、段落排版、图片插入等&#xff0c;可以帮助我们轻松创建专业且美观的文档。不过呢当我们需要将这些文档分享给他人时&#xff0c;往往需要在每个文档的末尾添…...

JavaScript原生实现简单虚拟列表

旧笔记&#xff0c;最近使用时做了点新优化&#xff0c;之前只发在了个人博客上 地址&#xff1a;JavaScript原生实现简单虚拟列表 背景 在公司项目中&#xff0c;需要给商品配置大量的属性值&#xff0c;可能其中一个属性的值数量就有成百上千条。 一个商品会有很多属性&…...

安心联车辆管理平台应用前景分析

安心联车辆管理平台凭借其技术创新与行业适配能力&#xff0c;展现出广阔的应用前景。以下从技术驱动、行业覆盖、实际效益、市场策略及未来潜力五个维度进行分析&#xff1a; 一、技术驱动的核心竞争力 高精度定位与多传感器融合 安心联采用北斗/GPS双模定位技术&#xff0c;实…...

力扣每日打卡 2176. 统计数组中相等且可以被整除的数对(简单)

力扣 2176. 统计数组中相等且可以被整除的数对 简单 前言一、题目内容二、解题方法1. 暴力解法2.官方题解官方也是暴力解法 前言 这是刷算法题的第十三天&#xff0c;用到的语言是JS 题目&#xff1a;力扣 2176. 统计数组中相等且可以被整除的数对(简单) 一、题目内容 给你一…...

OpenStack Yoga版安装笔记(22)Swift笔记20250418

一、官方文档 https://docs.openstack.org/swift/yoga/admin/objectstorage-components.html#https://docs.openstack.org/swift/yoga/admin/objectstorage-components.html# 二、对象存储简介&#xff08;Introduction to Object Storage&#xff09; OpenStack 对象存储&a…...

Linux 线程互斥

目录 Linux线程互斥 进程线程间的互斥相关背景概念 互斥量的接口 初始化互斥量 互斥量加锁和解锁 锁的封装 ​编辑 互斥量加锁的非阻塞版本 互斥量实现原理探究 可重入VS线程安全 概念 常见的线程不安全的情况 常见的线程安全的情况 常见的不可重入的情况 常见的…...

webgl入门实例-12WebGL 投影矩阵 (Projection Matrix)基本概念

WebGL 投影矩阵 (Projection Matrix) 投影矩阵是将3D场景从视图空间(相机空间)转换到裁剪空间的关键矩阵&#xff0c;它决定了3D物体如何投影到2D屏幕上。 投影矩阵的作用 定义3D空间到2D平面的映射方式确定视景体(view frustum)的形状和范围实现透视效果(近大远小)或正交投…...

《实战AI智能体》——邮件转工单的AI自动化

💡 为什么使用 邮件转工单(AI自动化)模板 想象一下,您的邮箱像是一个繁忙的宇宙港口,每封邮件都是一艘载着信息的飞船。当这些飞船抵达时,我们的自动化系统就像是一位高效的宇宙调度员,迅速将每艘飞船(邮件)转化为一艘艘探索号(工单),并为它们分配勇敢的宇航员(…...

YOLOv8 Bug 及解决方案汇总 【2024.1.24更新】【环境安装】【训练 断点续训】OMPError / KeyError

YOLOv8 Bug 及解决方案汇总&#xff1a;深入解析与应对 引言 YOLOv8作为一款高性能的目标检测算法&#xff0c;在实际应用中难免会遇到各种各样的问题。本文将对YOLOv8常见的Bug进行汇总&#xff0c;并提供相应的解决方案&#xff0c;旨在帮助开发者更好地使用和优化YOLOv8。…...

健康养生:开启活力生活新篇章

在当代社会&#xff0c;熬夜加班、久坐不动、外卖快餐成为许多人的生活常态&#xff0c;随之而来的是各种亚健康问题。想要摆脱身体的疲惫与不适&#xff0c;健康养生迫在眉睫&#xff0c;它是重获活力、拥抱美好生活的关键。​ 应对不良饮食习惯带来的健康隐患&#xff0c;饮…...

Yocto项目实战教程 · 第4章:4.3小节-层

&#x1f50d; B站相应的视频教程&#xff1a; &#x1f4cc; Yocto项目实战教程-第4章-4.3小节-层 记得三连&#xff0c;标为原始粉丝。 在 Yocto 项目中&#xff0c;元数据&#xff08;Metadata&#xff09;不仅是构建系统的核心驱动力&#xff0c;更是实现高度定制化、可移植…...

小红书爬虫,小红书api,小红书数据挖掘

背景&#xff1a; 小红书&#xff08;Xiaohongshu&#xff09;是一款结合社交、购物和内容分享的移动应用&#xff0c;近年来在中国以及全球范围内拥有大量的用户群体。小红书上的内容包括用户的消费体验、生活方式、旅行分享、时尚搭配等。通过这些内容&#xff0c;用户可以了…...

选择 iOS 按键精灵无根有根越狱辅助工具的理由

节省成本​ 使用 iOS 按键精灵&#xff0c;每台设备可为你减少 5 - 10 元的签名成本。对于需要使用大量脚本或者多设备操作的用户来说&#xff0c;长期下来能够节省一笔可观的费用。​ 不断更新优化​ 按键精灵团队始终致力于产品的更新与优化。新版本不断增加新功能&#x…...

电脑里的AI帮手:Open Interpreter智能助手食用指南

Open Interpreter简介如下&#xff1a; interpreter是一个使用python开发的命令行工具&#xff0c;可以让你在终端中使用类似AI对话的方式&#xff0c;只需简单输入指令要求&#xff0c;即可自动编写程序、执行代码&#xff0c;实现各种自动化操作interpreter有自动检测输出结…...

Windows软件界面分析软件-控件识别工具

Inspect.exe 这是微软提供的一款 UI 自动化检查工具&#xff0c;主要用于开发和测试应用程序的辅助功能&#xff08;Accessibility&#xff09;及 UI 自动化。 主要功能&#xff1a; 查看界面元素的属性&#xff08;如名称、角色、状态、位置等&#xff09;。 支持 UIA&…...

iOS 冷启动时间监控:启动起点有哪些选择?

⏱️ iOS 冷启动时间监控&#xff1a;启动起点有哪些选择&#xff1f; 作者&#xff1a;侯仕奇 来源&#xff1a;sqi.io 在监控 iOS 冷启动性能时&#xff0c;一个关键问题是&#xff1a;如何精确记录 App 冷启动的开始时间&#xff1f; 本文将对不同的“冷启动起点”监控方式…...

MacOS怎么显示隐藏文件

现象描述&#xff1a; 有些文件比如git的配置文件会作为隐藏文件存在。 Mac os默认是不显示隐藏文件的。 但是很多场合下我们需要查看或者编辑这些隐藏文件。 解决方法&#xff1a; 如下图所示&#xff0c;在Finder中使用Shift⇧Command⌘.快捷键 显示和隐藏都是同样的按…...

苹果紧急修复两个已被利用的iOS漏洞,用于针对特定目标的复杂攻击

苹果公司已发布iOS 18.4.1和iPadOS 18.4.1更新&#xff0c;修复两个被用于针对特定iPhone用户实施高度定向、复杂攻击的关键零日漏洞。 这两个漏洞存在于CoreAudio和RPAC组件中&#xff0c;攻击者可利用它们在受影响设备上执行任意代码或绕过安全保护机制。 两个正被活跃利用…...

8.观察者模式:思考与解读

原文地址:观察者模式&#xff1a;思考与解读 更多内容请关注&#xff1a;7.深入思考与解读设计模式 引言 在开发软件时&#xff0c;系统的某些状态可能会发生变化&#xff0c;而你希望这些变化能够自动通知到依赖它们的其他模块。你是否曾经遇到过&#xff0c;系统中某个对象…...

13.编码器的结构

从入门AI到手写Transformer-13.编码器的结构 13.编码器的结构代码 整理自视频 老袁不说话 。 13.编码器的结构 T r a n s f o r m e r E n c o d e r : 输入 [ b , n ] TransformerEncoder:输入[b,n] TransformerEncoder:输入[b,n] E m b e d d i n g : − > [ b , n , d ]…...

java 设计模式之模板方法模式

简介 模板方法模式&#xff1a;定义一个算法的基本流程&#xff0c;将一些步骤延迟到子类中实现。模板方法模式可以提高代码的复用性&#xff0c; 模板方法中包含的角色&#xff1a; 抽象类&#xff1a;负责给出一个算法的基本流程&#xff0c;它由一个模板方法和若干个基本…...

C++面向对象

面向对象的思想 面向过程&#xff1a; 根据程序的执行过程&#xff0c;来设计软件的所有细节。面向过程的缺点&#xff1a;开发大型项目时&#xff0c;越来越难以把控&#xff0c;甚至失去控制。后期维护、更新成本很大。解决方案&#xff1a;使用面向对象。 什么是面向对象…...

守护进程编程

目录 一、守护进程 1.1 守护进程概述 1.2 守护进程的功能及特点 1.2.1 守护进程的功能 1.2.2 守护进程的特点 1.3 主要过程 1.4 阿里云服务器编程实现守护进程 1.4.1 daemon 命令 1.4.2 nohup命令 1.4.3 fork()编程实现 1.5 在树莓派中通过三种方式创建守护进程 1.5…...

【Spring Boot 源码学习】深入 ConfigurableEnvironment 的初始化过程

《Spring Boot 源码学习系列》 深入 ConfigurableEnvironment 的初始化过程 一、引言二、配置环境的初始化2.1 源码总览2.2 prepareEnvironment 方法2.2.1 获取或创建可配置环境2.2.2 配置环境并设置参数2.2.3 将配置属性源附加到环境中2.2.4 触发环境准备事件2.2.5 将DefaultP…...

若依集成BladeX单点登录的令牌管理与api请求流程

目录 概述系统架构单点登录流程令牌管理机制接口调用流程关键代码实现数据结构安全性考虑常见问题与解决 概述 本文档详细说明若依系统如何实现与BladeX的单点登录集成&#xff0c;包括令牌管理和接口调用的完整流程。整个集成采用基于OAuth2的授权码流程&#xff0c;允许用…...

54常用控件_QLCDNumber的属性

目录 代码示例: 倒计时 QLCDNumer 是一个专门用来显示数字的控件.类似于“老式计算器”的效果 核心属性 属性 说明 intValue QLCDNumber显示的数字值(int). value QLCDNumber 显示的数字值(double). 和intValue是联动的. 例如给value设为1.5, intValue的值就是2. 另外&a…...

IcePlayer音乐播放器项目分析及学习指南

IcePlayer音乐播放器项目分析及学习指南 项目概述 IcePlayer是一个基于Qt5框架开发的音乐播放器应用程序&#xff0c;使用Visual Studio 2013作为开发环境。该项目实现了音乐播放、歌词显示、专辑图片获取等功能&#xff0c;展现了桌面应用程序开发的核心技术和设计思想。 技…...

【ELF2学习板】Ne10进行FFT测试

目录 引言 Ne10简介 交叉编译Ne10 测试 测试程序 测试结果 结语 引言 在上一篇博文介绍了FFTW在ELF2开发板的测试。其中我们提到--enable-neon选项在aarch64平台下无法启用。接下来测试一个专门用NEON指令优化的FFT库Ne10。 Ne10简介 NE10 是一个面向 ARM 架构的开源数…...

Android device PCO (protocol configuration options) intro

术语 英文缩写英文全称中文PCOprotocol configuration options协议配置选项RILradio interface layer 无线电接口层PCO介绍 PCO(Protocol Configuration Options) 是 3GPP 标准协议(TS 24.008)中定义的核心概念,用于在 LTE/5G 网络建立 PDN 连接时传递动态配置参数(如 D…...

HAL库通过FATFS和SDIO+DMA写入SD卡数据错误

HAL库F4版本 1.28.1 最近在使用HAL库配置SDIODMA并通过FATFS向SD卡写入数据&#xff0c;但是发现写入的数据经常有错误&#xff0c;不是少了一部分就是多了一部分&#xff0c;写入的数据为csv格式&#xff0c;通过循环向缓冲区写入"100100,12.345678\r\n"数据来观察问…...

RK Android11 修改默认语言为法语及时区为巴黎时间

文章目录 1、需求2、解决 1、需求 客户要求将系统默认语言改为法语&#xff0c;系统默认时区改为巴黎时间&#xff08;也称为欧洲中部时间&#xff09;2、解决 --- a/build/make/tools/buildinfo.shb/build/make/tools/buildinfo.sh-46,7 46,7 echo "ro.product.cpu.ab…...

文件上传Ⅰ

文件上传--前后端验证 不让上传php,所以要绕过它 遇到网站可能不是php语言&#xff0c;会是java或者python语言等&#xff0c;它只能解析网站本身的语言&#xff0c;那我们就上传符合网站语言识别的格式&#xff08;它能解析什么后缀&#xff0c;就上传什么后缀&#xff09;&…...

IntelliJ IDEA clean git password

IntelliJ IDEA clean git password 清除git密码 方法一&#xff1a;&#xff08;这个要特别注意啊&#xff0c;恢复默认设置&#xff0c;你的插件什么要重新下载了&#xff09; File->Manage IDE Settings->Restore Default Settings以恢复IDEA的默认设置(可选); 清空…...

【C++指南】哈希驱动的封装:如何让unordered_map/set飞得更快更稳?【上】

&#x1f31f; 各位看官好&#xff0c;我是egoist2023&#xff01; &#x1f30d; 种一棵树最好是十年前&#xff0c;其次是现在&#xff01; &#x1f4ac; 注意&#xff1a;本文在哈希函数中主讲除法散列法&#xff0c;乘法散列法、全域散列法、双重散列等自行了解。 &#x…...

论坛测试报告

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…...

人脸扫描黑科技:多相机人脸扫描设备,打造你的专属数字分身

随着科技的迅猛发展&#xff0c;人脸扫描这个词已经并不陌生&#xff0c;通过人脸扫描设备制作超写实人脸可以为影视制作打造逼真角色、提升游戏沉浸感&#xff0c;还能助力教育机构等领域生产数字人以丰富教学资源&#xff0c;还在安防、身份识别等领域发挥关键作用&#xff0…...