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

(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; &…...

STL简介 + string【上】

一 . STL简介 1.1 什么是STL STL&#xff08;standard template libaray - 标准模板库) : 是C标准库的重要组成部分 &#xff0c; 不仅是一个可复用的组件库 &#xff0c; 而且是一个包罗 数据结构 与 算法 的软件框架 。 注意 &#xff1a; 是标准库的一部分 &#xff…...

【Bluedroid】A2DP Sink播放流程源码分析(二)

接上一篇继续分析:【Bluedroid】A2DP Sink播放流程源码分析(一)_安卓a2dp sink播放流程-CSDN博客 AVDTP接收端(Sink)流事件处理 bta_av_sink_data_cback 是 Bluedroid 中 A2DP Sink 角色的 AVDTP 数据回调函数,负责处理接收端的音频数据事件,将底层接收到的音频数据传递…...

redis利用备忘录

fofa: icon_hash"864611937" 防护&#xff1a; redis的安全设置&#xff1a;设置完毕&#xff0c;需要重加载配置文件启动redis 1.绑定内网ip地址进行访问 2. requirepass设置redis密码 3.保护模式开启protected-mode开启&#xff08;默认开启&#xff09; 4.最好把…...

SAP系统中MD01与MD02区别

知识点普及&#xff0d;MD01与MD02区别 1、从日常业务中&#xff0c;我们都容易知道MD01是运行全部物料&#xff0c;MD02是运行单个物料 2、在做配置测试中&#xff0c;也出现过MD02可以跑出物料&#xff0c;但是MD01跑不出的情况。 3、MD01与MD02的差异: 3.1、只要在物料主数…...

企业官网nodejs mySQL数据库安装及使用

以下是企业官网的MySQL数据库设计、本地安装指南&#xff0c;以及基于Node.js的增删改查&#xff08;CRUD&#xff09;实现方案&#xff1a; 一、MySQL数据库设计&#xff08;企业官网基础表&#xff09; 1. 核心表结构 -- 1. 用户表&#xff08;管理员&#xff09; CREATE T…...

Spring Boot自动配置原理深度解析:从条件注解到spring.factories

大家好&#xff01;今天我们来深入探讨Spring Boot最神奇的特性之一——自动配置(Auto-configuration)。这个功能让Spring Boot如此受欢迎&#xff0c;因为它大大简化了我们的开发工作。让我们一起来揭开它的神秘面纱吧&#xff01;&#x1f440; &#x1f31f; 什么是自动配置…...

ubuntu学习day3

3 编译与调试 3.1 gcc/g编译器 当我们进行编译的时候&#xff0c;要使用一系列的工具&#xff0c;我们称之为工具链。SDK就是编译工具链的简写&#xff0c;我们所使用的是gcc系列编译工具链。使用-v参数来查看gcc的版本&#xff0c;从而确定某些语法特性是否可用&#xff0c;…...

C++数据结构与二叉树详解

前言&#xff1a; 在C编程的世界里&#xff0c;数据结构是构建高效程序的基石&#xff0c;而二叉树则是其中最优雅且应用广泛的数据结构之一。本文将带你深入理解二叉树的本质、实现与应用&#xff0c;助你在算法设计中游刃有余。 一、二叉树的基本概念 1. 什么是二叉树 二叉树…...

论文阅读:2023 arxiv Safe RLHF: Safe Reinforcement Learning from Human Feedback

总目录 大模型安全相关研究&#xff1a;https://blog.csdn.net/WhiffeYF/article/details/142132328 Safe RLHF: Safe Reinforcement Learning from Human Feedback https://arxiv.org/pdf/2310.12773 https://github.com/PKU-Alignment/safe-rlhf 速览 研究动机&#xff…...

C++11中的std::condition_variable

一、什么是条件变量&#xff1f; std::condition_variable 是C11标准库中提供的线程同步工具&#xff0c;用于在多线程环境中实现“等待-通知”机制。它的核心作用是让线程能够高效地等待某个条件成立&#xff0c;避免“忙等待”对CPU资源的浪费。 条件变量必须与std::mutex配…...

6.8.最小生成树

一.复习&#xff1a; 1.生成树&#xff1a; 对于一个连通的无向图&#xff0c;假设图中有n个顶点&#xff0c;如果能找到一个符合以下要求的子图&#xff1a; 子图中包含图中所有的顶点&#xff0c;同时各个顶点保持连通&#xff0c; 而且子图的边的数量只有n-1条&#xff0…...

QT中栅格模式探索

1、Qt中选择了栅格模式&#xff0c;如下图所示&#xff1a; 2、在进行整个大的UI界面布局时&#xff0c;需了解每个控件所需要选择的属性sizePolicy。 sizePolicy包含如下几种选择&#xff1a; 3、举个例子&#xff1a;此时整个UI界面&#xff0c;我采用了栅格模式&#xf…...

SLAM | 激光SLAM中的退化问题

在激光SLAM中,判断退化环境的核心是通过数学建模分析环境特征对位姿估计的约束能力。除了LOAM中提出的退化因子D外,还存在多种基于表达式和阈值设定的方法。以下是几种典型方法及其实现原理: 1. 协方差矩阵特征值分析 原理:通过分析点云协方差矩阵的特征值分布,判断环境中…...

C++入门基础:命名空间,缺省参数,函数重载,输入输出

命名空间&#xff1a; C语言是基于C语言的&#xff0c;融入了面向对象编程思想&#xff0c;有了很多有用的库&#xff0c;所以接下来我们将学习C如何优化C语言的不足的。 在C/C语言实践中&#xff0c;在全局作用域中变量&#xff0c;函数&#xff0c;类会有很多&#xff0c;这…...

tomcat 的安装与启动

文章目录 tomcat 服务器安装启动本地Tomcat服务器 tomcat 服务器安装 https://tomcat.apache.org/下载 Tomcat 10.0.X 启动本地Tomcat服务器 进入 Tomcat 的 bin...

C 语言中经典的数据结构

在 C 语言中&#xff0c;经典的数据结构通常包括以下几种&#xff0c;每种都有其特定的应用场景和实现方式&#xff1a; 1. 数组&#xff08;Array&#xff09; 定义&#xff1a;连续内存空间存储相同类型的数据。 特点&#xff1a;随机访问快&#xff08;O(1)&#xff09;&am…...

算法-堆+单调栈

堆 首先堆在我们的Java中我们的是一个优先队列类 PriorityQueue 然后我们要弄最大堆和最小堆 最大堆&#xff1a; PriorityQueue<Integer> pq new PriorityQueue<Integer>((a, b) -> b - a); 最小堆&#xff1a; PriorityQueue<Integer> pq new P…...

物联网平台管理系统

物联网平台管理系统概述 物联网平台管理系统是物联网架构中的核心枢纽&#xff0c;承担着承上启下的关键作用。它向下连接各类物联网设备&#xff0c;实现设备的接入、管理与控制&#xff1b;向上为应用开发提供统一的数据接口和共性模块工具&#xff0c;支撑起各种丰富多彩的…...

STM32CubeMX-H7-15-SPI通信协议读写W25Q64

前言 SPI&#xff08;Serial Peripheral Interface&#xff09;通信协议是一种高速、全双工、同步的串行通信协议 本篇文章就使用W25Q64模块来学习SPI,包括软件SPI代码的编写&#xff0c;硬件SPI&#xff0c;中断SPI和DMASPI SPI的应用场景和模块 &#xff01;这里是抄AI的&a…...

【软考】论devops在企业信息系统开发中的应用

摘要&#xff1a; 随着互联网的不断发展&#xff0c;各行各业都在建设自己的企业信息系统&#xff0c;而随着业务的不断升级和复杂化&#xff0c;系统的更新迭代速度越来越快&#xff0c;系统也越来越复杂。对于信息系统开发者&#xff0c;架构师&#xff0c;管理者&#xff0c…...

生物化学笔记:医学免疫学原理22 肿瘤及肿瘤治疗

肿瘤及肿瘤治疗 免疫疗法 CAR-T细胞介绍...

JVM考古现场(二十二):降维打击·用二向箔优化内存模型

"警报&#xff01;三维堆内存正在经历二维化坍缩&#xff01;" 我腰间的玄铁令突然震动&#xff0c;在蜀山剑派的量子剑阵中投射出诡异的曼德博分形——这是三体文明发动降维打击的铁证&#xff01; 楔子&#xff1a;二向箔奇点降临 昆仑镜监控日志&#xff1a; // …...

第三阶段面试题

Nginx nginx常用模块以及其功能 proxy模块&#xff0c;进行代理功能 ssl模块&#xff0c;进行HTTPS协议的使用 gzip模块&#xff0c;进行传输数据的压缩 upstream模块&#xff0c;进行反向代理时使用 static模块&#xff0c;静态资源进行访问的模块 cache模块&#xff0…...

操作系统-PV

&#x1f9e0; 背景&#xff1a;为什么会有 PV&#xff1f; 类比&#xff1a;内存&#xff08;生产者&#xff09; 和 CPU&#xff08;消费者&#xff09; 内存 / IO / 磁盘 / 网络下载 → 不断“生产数据” 例如&#xff1a;读取文件、下载视频、从数据库加载信息 CPU → 负…...

nuxt3路由切换页面出不来,刷新可以

nuxt3遇到一个奇怪的现象&#xff1a; 不管是router.push()跳转还是navigateTo()跳转&#xff0c;浏览器url变了&#xff0c;但是页面是空白的&#xff0c;没加载出来&#xff0c;刷新之后页面正常。 解决方案&#xff1a; <template>下的所有内容必须套在一个div里面...

Spring Boot配置文件优先级全解析:如何优雅覆盖默认配置?

&#x1f4da; 一、为什么需要了解配置文件优先级&#xff1f; 想象一下&#xff0c;你正在玩一个游戏&#x1f3ae;&#xff0c;游戏里有默认设置&#xff0c;但你可以通过不同的方式修改这些设置&#xff1a; 游戏内置的默认设置&#xff08;就像Spring Boot的默认配置&…...

医院数据中心智能化数据上报与调数机制设计

针对医院数据中心的智能化数据上报与调数机制设计,需兼顾数据安全性、效率性、合规性及智能化能力。以下为系统性设计方案,分为核心模块、技术架构和关键流程三部分: 一、核心模块设计 1. 数据上报模块 子模块功能描述多源接入层对接HIS/LIS/PACS/EMR等异构系统,支持API/E…...

Linux之基础命令

Linux作为开源操作系统的代表&#xff0c;以其高效、灵活和强大的命令行工具闻名。无论是系统管理、开发调试还是日常使用&#xff0c;掌握基础命令都是与Linux系统交互的必备技能。本文整理了20个最常用的Linux基础命令&#xff0c;帮助新手快速入门。 目录 目录与文件导航文…...

【MATLAB代码例程】AOA与TOA结合的高精度平面地位,适用于四个基站的情况,附完整的代码

本代码实现了一种基于到达角(AOA) 和到达时间(TOA) 的混合定位算法,适用于二维平面内移动或静止目标的定位。通过4个基站的协同测量,结合最小二乘法和几何解算,能够有效估计目标位置,并支持噪声模拟、误差分析和可视化输出。适用于室内定位、无人机导航、工业监测等场景…...