嵌入式学习笔记——IIC通信

news2024/11/17 17:29:56

IIC通信

  • 前言
  • IIC概述
    • 通信特征
    • 物理拓扑结构
    • IIC通信的流程
    • IIC的特点:
  • STM32的IIC通信
    • GPIO模拟IIC
      • IIC的时序组成(主机对从机写入数据)
        • 1.起始信号
        • 2.器件地址与读写位
        • 3.从机应答信号
        • 5.传输的数据与结束信号
      • IIC的时序组成(主机对从从机获取数据)
        • 1.数据接收
        • 2.主机发送应答信号
  • IIC初始化代码
  • 总结
  • M4系列目录
  • 写在后面的话

前言

本文接着之前的M4系列介绍,对另外一个十分常见的通信总线进行一个介绍,就是IIC总线。

IIC概述

通信特征

首先,还是找个免费劳动力来做一个官方的介绍,下面这一段话非常全面的介绍了IIC的各个特征,用之前提到的通信特征来总结,IIC是一种串行,同步,半双工,板级有线通信。与SPI对比,其少了一个数据线,只有一个数据线,因此只能实现半双工通信。
在这里插入图片描述

物理拓扑结构

第二段是描述了IIC的拓扑结构,IIC一共有两个线,一根时钟线SCL,用来提供主从通信的时钟基准,另外一根是SDA数据线,用来传输数据用;之所以称作为总线,与前面的SPI一样,表明其可以同时挂接多个设备,设备之间可以进行通信。一主多从的物理拓扑图如下图所示:
在这里插入图片描述

在这里插入图片描述
与上一篇的SPI对比可以发现少了CS片选线,那么主机是怎么选择通信的对象的呢,注意上面的简介中提到了一个地址的概念,IIC的通信就是通过地址来确定通信的对象的,根据原理图和芯片手册获取到对应器件的信息后,主机将地址发送到总线上,从设备接收地址信息进行判断,如果地址与自己的地址一致,则给主机一个反馈信号,进而二者建立通信;而地址不对应的从器件是不会有反馈的。具体的地址信息放到后面做介绍,这里做个了解即可。
除了上面的一主多从模式,IIC还有多主多从的拓扑结构,结构图如下图所示:
在这里插入图片描述
这样的连接方式可以实现多个主机操作同一组IIC从设备。

IIC通信的流程

这里对IIC的通信流程做个大致的介绍,如下图所示,假设此时A为主设备,也就是我们的单片机,B设备就是常用的IIC从机AT24cxx。
第一个通信目标是:A将数据写入B
其通信流程如下:
1.微控制器 A 主机 寻址微控制器 B 从机
2.微控制器 A 主机 发送器 发送数据到微控制器 B 从机 接收器
3.微控制器 A 终止传输,总线空闲,两条线路都恢复高电平
在这里插入图片描述
第二个通信的目标是: A从B中读取数据,其具体的通信流程如下:这里需要补充一点,在IIC总线中,主机才有对时钟线的操作权利,从机是没有的,因此,从机并不能主动给主机传输数据,必须要接收到主机给从机相应的获取指令后,才可以将数据传输到数据线上,进而被主机接收。因此A从B中获取数据的流程如下:
1.微控制器 A 主机 寻址微控制器 B 从机
2.微控制器 A 主机 接收器 从微控制器 B 从机 发送器 接收数据
3.微控制器 A 终止传输总线空闲,两条线路都恢复高电平

IIC的特点:

•1. 只要求两条总线线路 一条串行数据线 SDA 一条串行时钟线 SCL
• 2.每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机 从机关系软件设定地址 主机可以作为主机发送器或主机接收器
• 3.它是一个真正的多主机总线 如果两个或更多主机同时初始化数据传输可以通过冲突检测和仲裁防止数据被破坏
4.串行的 8 位双向数据传输位速率在标准模式下可达 100kbit/s ;快速模式下可达 400kbit/s; 高速模式下可达 3.4Mbit/s,此处可以发现,IIC的最高传输速率是不如SPI的。
• 5.片上的滤波器可以滤去总线数据线上的毛刺波 保证数据完整
• 6. 连接到相同总线的 IC 数量只受到总线的最大电容 400pF 限制
以上描述来自——IIC总线规范
广州周立功单片机发展有限公司 Tel: (020)38730976 38730977 Fax:38730925 http://www.zlgmcu.com
关于IIC的一些更详细的介绍可以参考以下几篇文章,上面的图片也是来自这两篇博客:
关于为什么要加上拉电阻以及电阻阻值的计算在里面也有描述。
一文搞懂I2C通信总线——http://t.csdn.cn/RjJob
IIC原理超详细讲解—值得一看——http://t.csdn.cn/zkq43

STM32的IIC通信

关于STM32的IIC通信,与SPI类似,也是有两种方案来解决一种是使用控制器来实现,另一种是配置STM32内部的控制器来实现通信,实际使用过程中常用的反而是IO模拟的方式,STM32内部的IIC控制器数量是3个,数量不算多
在这里插入图片描述
虽然每个IIC控制器都对应有两组IO口,但是我们去数据手册的复用映射表查看就知道了,支持IIC控制器服用的IO口,大多都有着其他的复用功能,复用为IIC后其他的功能就需要找其他IO口,而且其他的复用功能,并不能很好地使用IO模拟来实现,而IIC可以通过IO模拟很好地实现,也就是说,只要是GPIO口理论上就可以实现IIC的时序模拟,因此大多是的IIC通信都是使用的IO模拟。
在这里插入图片描述
当然,使用控制器的IIC会有更高的传输速率以及稳定性,但是其配置过程也是十分复杂的,一不留神就会配置错误导致通信不上,这里我们后面做个对比即可。
由于常用的是GPIO模拟的IIC,所以本文就重点介绍一下IO模拟的IIC实现过程。

GPIO模拟IIC

使用GPIO模拟的IIC,参考之前的SPI模拟时序,第一件事就是要看IIC通信的时序图,然后根据时序图来实现IIC的模拟时序。

IIC的时序组成(主机对从机写入数据)

来看一下IIC通信的完整数据帧,这里用的是7位器件地址,一位读写位,八位数据位,一次传输两个数据的情况为例。
在这里插入图片描述
从上图可以看出完整的数据传输包含了1.开始信号;2.器件地址;3.读写位;4.应答信号;5.传输的数据;6.结束信号。而且观察上图可以发现:
SDA 线上的数据在时钟的高电平周期保持稳定; SDA线的高或低电平状态只有在 SCL 线的时钟信号是低电平时才能改变;也就是说,时钟线高电平期间,数据线是只读的不能操作,而时钟线低电平期间,数据线是可写的,可以写入对应的高低电平。
在这里插入图片描述
接下来及分别对这里面的各个部分进行介绍。

1.起始信号

起始条件一般由主机产生。
在 SCL 线是高电平时 SDA 线从高电平向低电平切换,表示起始信号;
在这里插入图片描述
根据这个时序图可以大致知道起始信号的样子,但是还不知道其具体的持续时间,这时就需要打开对应的芯片手册进行查看了,在芯片手册的读写周期范围里面会有具体的时间要求,根据以下表格,可以写出起始信号的伪代码。
在这里插入图片描述

起始条件函数
{
   时钟线拉低
   delay_us(5);//IIC的延时时间    时间只能大于表中的时间,不能小于表中时间
   数据线拉高     //为数据线的下降沿做准备
   
   时钟线拉高
    delay_us(5);//IIC的延时时间  空闲时间不得少于4.7us这里给个5us
   数据线拉低

   时钟线拉低    //安全模式  维持完整周期
}

上面的是手册给出的示意图,接下来来看一个实际的示波器抓取的波形,如下图所示:
原图链接——http://www.eepw.com.cn/article/234058.htm
橙色框内的就是起始信号,时钟线和数据线都是先被拉高,然后持续一段时间,随后,数据线拉低,这时一个起始信号就已经完成了,不过为了保证能够正确的接收数据,时钟线也会被拉低,因为只有在时钟线低电平的时候数据线才能被操作。
在这里插入图片描述

2.器件地址与读写位

传输过程中是高位先发的MSB。
在这里插入图片描述
在起始条件 S 后,发送了一个从机地址 这个地址共有 7 位(下图中红色框内的器件地址),紧接着的第 8 位是数据方向位 R/ W 0 表示主机对从机写入数据,写 1 表示主机需要从从机中读取数据。
在这里插入图片描述
IIC每次传输的都是一个八位数据,起始信号发送完毕后需要紧跟一个8位数据,这个八位数据的高七位是从器件的地址,最低位是读写位,为了方便理解这里还是直接看个实例,假设我们使用AT24C02作为从器件,那么它的器件地址就是如下图所示的,其中前四位是厂商规定的,1010,而后面的三位A0-A2是由硬件电路来确定的。
在这里插入图片描述
如下图,硬件连接中A0-A2都是被拉低了,所以此时的7位器件地址位:101 0000(0x60)
而其对其写入数据的时的写地址为:1010 0000 (0xA0)
读地址为1010 0001(0XA1)
在这里插入图片描述
在这里插入图片描述
同样的,可以来看一下实际的示波器抓取的波形,下图中的器件地址是011 1000 也就是0x38,最后一位的读写位是0,标明是写入数据,因此最终发送的八位数据是0111 0000(0x70)。
在这里插入图片描述
通过上面的两个图,可以模拟出发送八位数据的时序,这里还需要再提示一下,前面说过,IIC通信过程中,只有在时钟线拉低的情况下,数据线才能被操作,因此,我们在写入每一位之前都需要保证,时钟线是拉低的状态才可以。伪代码如下:

发送数据:时钟低电平状态发送   发送8次
发送数据(地址)函数(数据)
{
  for(u8 i=0;i<8;i++)//发8次
{
  时钟线拉低
  If(data & 0x80 >>i)//高位先发
  {
     数据线拉高
  }
  Else
  {
    数据线拉低
  }
  拉高时钟线
}
调用接收应答函数
拉低时钟线  //安全模式
}

在介绍应答信号之前还需要插播一个小知识,注意如果被问到IIC总线可以挂接多少个从器件时,可以参考如下的描述作答。
在这里插入图片描述

3.从机应答信号

接下来就是应答信号,关于应答位,首先它只有一位,也只有两个状态,要么是0,要么是1;其中0表示应答成功,1表示应答失败。
在这里插入图片描述

数据传输必须带响应;相关的响应时钟脉冲由主机产生,在响应的时钟脉冲期间,发送器(主机)释放 SDA 线(拉高);
在响应的时钟脉冲期间 接收器必须将 SDA 线拉低(0)使它在这个时钟脉冲的高电平期间保持稳定的低电平,此时表示应答成功;否则,也就是数据线没有保持在低电平,说明应答失败。
在这里插入图片描述
在使用IO模拟的时候,我们需要获取SDA管脚的高低电平来判断是否应答成功,这里又涉及到了一个知识点,前面发送数据以及起始信号时,SDA一直是作为数据脚的,此时需要判断高低电平,既要做输入又要做输出,这时,根据之前GPIO的介绍,有两种方案,一是发送时配置为推挽输出,到了要接收的时候再配置一遍IO口,将其转换为无上下拉的输入模式,也就是需要我们不断地切换模式;
另外一个方案是配置为开漏输出,此时GPIO的高端MOS被屏蔽了,无法输出高电平,当GPIO的ODR配置为1时,SDA数据线不再受此GPIO的控制,而是有上拉电阻拉成空闲状态,由从机控制SDA的高低电平。此时直接使用IDR进行读取即可。
而且还需要注意,前面提到过,时钟线是交给主机控制的(也有例外,但是大部分是这样),而数据线的写操作,必须在时钟线拉低的情况下才可以,因此,为了从机能够操作数据线,还需要使用主机为其提供一个低电平的时钟信号。
在这里插入图片描述
同样的,注意示波器的波形,此时从机也是正常将SDA拉低了,说明从机应答正常,接下来可以开始传输数据了。
伪代码如下:

u8 主机接收从机应答函数
{
  //数据线模式  输入模式
  拉低时钟线
  拉高数据线     //让数据线处于空闲  可以接收数据
  
  拉低时钟线     //让从机发送应答
  拉高时钟线     //主机接收应答
  If(数据线状态)//检测到高表示应答失败
  return 1
  else
  return 0
  
  拉低时钟线    //安全模式
}

5.传输的数据与结束信号

关于数据传输,与前面提到的地址写入是一致的,在操作的时候根据芯片手册的指令表,写入对应功能的数据即可。
接下来就是结束信号了,当数据传输完成后,需要一个结束信号,来告诉从机通信结束。当 SCL 是高电平时,SDA 线由低电平向高电平切换表示停止条件;结束信号的时序图如下图所示:
在这里插入图片描述
实际的示波器图形如下图所示:

在这里插入图片描述
根据时序图,以及上面的时间要求,可以写出结束信号的伪代码:

停止条件函数
{
  拉低时钟线
  数据线拉低     //为上升沿做准备
  
  拉高时钟线
  delay_us(5);
  拉高数据线
	delay_us(5);
}

至此,关于主机对从机写入数据的时序模拟就结束了,实际使用过程中,根据对应的时序图调用对应的函数即可,例如对于AT24C02写入数据
在这里插入图片描述
代码流程如下:
①发送一个起始信号
②器件写地址
③接收应答
④器件的存储上的绝对地址
⑤接收应答
⑥写入数据
⑦接收应答
⑧停止信号

IIC的时序组成(主机对从从机获取数据)

上面介绍了主机对从机写入数据的过程以及所需要函数逻辑,也就是主机 发送器发送到从机 接收器 的传输;方向不会改变,一直是主机发送数据,从机接收数据后做应答,主机检测应答,然后继续发送数据,直到数据发送完成后,主机发送结束信号。
在这里插入图片描述
实际使用过程中,对于IIC的从设备,往往是一些传感器和存储器,不仅需要对其写入对应的数据,还需要进行获取数据,获取的具体流程与上面的发送大致一样,但是数据接收和应答这个需要修改一下。
在第一个字节(读地址)后;主机立即读从机 。
此时的第一次响应还是由从机产生,之后就变成了从机发送数据,主机接收数据,每次接受一个数据后主机发送个应答位。直到数据接收完成,最后一个数据接收完成后,主机会发送一个非应答信号,告诉从机接收结束,紧接着主机产生结束信号,接收此次通信。
在这里插入图片描述
可以注意到,在读取数据时,起始信号,读地址发送,从机应答,结束信号都是与前面一样的,只是多了一个数据接收以及一个主机应答的操作,根据IIC的通信需求,每次接收完一个八位数据位后接收端就需要返回给发送端一个应答,只有应答信号正常时才会进行下一步操作,前面的数据发送是主机MCU的发送器发送给从机的接收器的,从机接收八位数据后会返回一个应答位,主机检测应答正常后继续发送。而此时的读取数据是从机发送器发送数据,主机的接收器接收数据当主机接收了八位数据后,需要给从机返回一个应答位,以便于从机下一位的数据发送,当接收最后一位数据后,主机需要产生一个非应答信号告诉从机接收完成。

1.数据接收

前面提到过,只有在时钟线拉高的情况下才可以进行数据的接收,而且一次接收就需要接收八位,且是高位在前,因此,接收数据的伪代码如下:

u8 接收数据函数
{
  u8 data
  //数据线切换成输入模式
  拉低时钟线
  拉高数据线     //让数据线处于空闲  可以接收数据

  for(u8 i=0;i<8;i++)
  {
	拉低时钟线    //从机发送数据
	拉高时钟线    //主句可以开始接受数据
	data = data << 1;  //先进行左移再接收
	if(数据线)
	{
 	  data |= 1;
	}
  }
 //调用发送应答函数
 拉低时钟线   ///安全模式
}

2.主机发送应答信号

除了接收八位数据以外,就是需要给从机发送应答信号,来告诉它是否传输成功,其实思路与之前的接收应答位差不多,就是在时钟拉低的时候将数据线对应拉高或者拉低就可以了,其中拉低表示应答成功,拉高表示应答失败。
伪代码如下:

发送应答函数(ack)
{
  拉低时钟线
  delay_us(5);  //此时间需要从参考对应的从设备的数据手册 
  if(ack==0)
  拉低数据线      //主机发送应答信号
  else
  拉高数据线	//主机发送非应答信号
  delay_us(1);  
  拉高时钟线      //主机帮从机拉高时钟线从而 从机获取应答
  delay_us(5);   
  拉低时钟线     //安全模式
}

还是举个例子吧,假设需要从AT24C02中读取一个字节其时序流程如下图所示:
在这里插入图片描述
具体的流程如下:
①开始信号
②发送写地址
③接收应答
④发送绝对地址//需要读取的数据的地址
⑤接收应答
⑥开始信号
⑦发送读地址
⑧接收应答
⑨读取一个字节
⑩发送非应答
⑪停止信号

IIC初始化代码

由于笔者手头没有IIC的器件了,所以此处就只留一个IIC的初始化代码了。
根据前面的介绍,一个IIC的模拟时序需要有以下的函数:
1.GPIO口的初始化,SCL初始化为推挽输出,SDA初始化为开漏输出,这里笔者用开漏输出,如果想要推挽输出,然后通信过程中切换GPIO模式的,可以去看一下正点原子的代码,他们的就是使用的推挽输出,然后接收时再配置为输入模式。
2.编写起始信号代码,结束信号代码,发送数据代码,接受数据代码,接受应答的代码,发送应答的代码。
IIC的代码如下:

#include "stm32f4xx.h"                  // Device header

#include "iic.h"

static void delay_us(u32 us)
{
	u32 i = 168 / 4 * us;
	while(i)
	{
		i--;
	}
}


/*******************************************
*函数名    :iic_IO_init
*函数功能  :IIC管脚初始化配置
*函数参数  :无
*函数返回值:无
*函数描述  :
						SCL-----PB8   通用推挽输出
						SDA-----PB9   通用开漏输出
*********************************************/
void iic_IO_init(void)
{
	//端口时钟使能
	RCC->AHB1ENR |= (1<<1);
	//端口模式配置
	GPIOB->MODER &= ~((3<<16) | (3<<18));   //清零
	GPIOB->MODER |= ((1<<16) | (1<<18));    //通用输出
	//端口输出类型配置
	GPIOB->OTYPER &= ~(1<<8);     //推挽
	GPIOB->OTYPER |= (1<<9);      //开漏
	//端口输出速度配置
	GPIOB->OSPEEDR &= ~((3<<16) | (3<<18)); //2M
	//IO口保持电平
	GPIOB->ODR |= (1<<8);
	GPIOB->ODR |= (1<<9);
}


/*******************************************
*函数名    :iic_star
*函数功能  :IIC其实信号函数
*函数参数  :无
*函数返回值:无
*函数描述  :
*********************************************/
void iic_star(void)
{
	//时钟线拉低
	SCL_L;
	//数据线高电平
	SDA_OUT_H;
	
	//时钟线拉高
	SCL_H;
	delay_us(5);   
	//数据线拉低
	SDA_OUT_L;
	delay_us(5);   
	
	//安全
	SCL_L;
}


/*******************************************
*函数名    :iic_stop
*函数功能  :IIC停止信号函数
*函数参数  :无
*函数返回值:无
*函数描述  :
*********************************************/
void iic_stop(void)
{
	SCL_L;
	SDA_OUT_L;
	
	SCL_H;
	delay_us(5);   
	SDA_OUT_H;
	delay_us(5);   
}




/*******************************************
*函数名    :iic_send_ack
*函数功能  :IIC发送应答函数
*函数参数  :u8 ack
*函数返回值:无
*函数描述  :
						ack  0    应答
            ack  1    不应答
						主机发送应答表示继续接收数据
            主机发送不应答表示不再接收数据
*********************************************/
void iic_send_ack(u8 ack)
{
	SCL_L;
	delay_us(5);   
	if(ack == 0)
	{
		SDA_OUT_L;       //应答信号
	}
	else
	{
		SDA_OUT_H;       //不应答信号
	}
	delay_us(1);   
	
	SCL_H;
	delay_us(5);   
	
	
	SCL_L;     //安全
}


/*******************************************
*函数名    :iic_get_ack
*函数功能  :主机检测应答函数
*函数参数  :无
*函数返回值:u8
*函数描述  :
						应答返回值是0
            不应答返回值是1
*********************************************/
u8 iic_get_ack(void)
{
	u8 ack = 0;
	
	/*将IO口切换为输入*/
	SCL_L;
	SDA_OUT_H;          //切换输入
	
	/*检测应答*/
	SCL_L;              //主机帮助从机拉低时钟线从机才自动发送应答信号
	delay_us(5);   
	SCL_H;              //主机可以读数据线
	delay_us(5);   
	if(SDA_INT )
	{
		ack = 1;
	}
	else
	{
		ack = 0;
	}
	
	//安全
	SCL_L;
	
	return ack;
}


/*******************************************
*函数名    :iic_send_byte
*函数功能  :主机通过IIC发送一个字节函数
*函数参数  :u8 data
*函数返回值:无
*函数描述  :
*********************************************/
void iic_send_byte(u8 data)
{
	u8 i;
	
	for(i=0;i<8;i++)
	{
		SCL_L;            //可以写数据
		delay_us(5);   
		if(data & 0x80)  
			SDA_OUT_H;
		else             
			SDA_OUT_L;
		delay_us(1);   
		SCL_H;            //主机帮助从机拉高时钟线,从机才可以读这一位数据
		delay_us(5);   
		
		data = data << 1; //下一位数据
	}

	//安全
	SCL_L;
}



/*******************************************
*函数名    :iic_read_byte
*函数功能  :主机通过IIC接收一个字节函数
*函数参数  :无
*函数返回值:u8
*函数描述  :
*********************************************/
u8 iic_read_byte(void)
{
	u8 data;    
	u8 i;
	
	/*将IO口切换为输入*/
	SCL_L;
	SDA_OUT_H;
	
	/*读数据*/
	for(i=0;i<8;i++)
	{
		SCL_L;             //主机帮助从机拉低时钟线
		delay_us(5);   
		SCL_H;             //主机可以读数据
		delay_us(5);   
		data = data << 1;
		if(SDA_INT) 
			data= data | 0x01;		
	}
	
	//安全
	SCL_L;
	
	return data;
}


需要特别提醒的是IIC的延时一定要准确,一定一定要准确,大部分的软件模拟IIC通信异常都是延时的问题。

总结

关于IIC的介绍就先记录到此,关于控制器实现IIC的大家可以自己搜索一下,下面两篇文章中也有详细介绍,这路笔者不再做介绍了,文中如有不足,欢迎大家批评指正,关于IIC和SPI的实际使用,后面有机会再做补充,因为手上的最小系统板实在是外设有限,目前只能介绍这么多了。

STM32硬件I2C与软件模拟I2C超详解http://t.csdn.cn/8ljI9
【STM32】入门(七):I2C硬件控制方式http://t.csdn.cn/1zZHP

M4系列目录

1.嵌入式学习笔记——概述
2.嵌入式学习笔记——基于Cortex-M的单片机介绍
3.嵌入式学习笔记——STM32单片机开发前的准备
4.嵌入式学习笔记——STM32硬件基础知识
5.嵌入式学习笔记——认识STM32的 GPIO口
6.嵌入式学习笔记——使用寄存器编程操作GPIO
7.嵌入式学习笔记——寄存器实现控制LED小灯
8.嵌入式学习笔记——使用寄存器编程实现按键输入功能
9.嵌入式学习笔记——STM32的USART通信概述
10.嵌入式学习笔记——STM32的USART相关寄存器介绍及其配置
11.嵌入式学习笔记——STM32的USART收发字符串及串口中断
12.嵌入式学习笔记——STM32的中断控制体系
13.嵌入式学习笔记——STM32寄存器编程实现外部中断
14.嵌入式学习笔记——STM32的时钟树
15.嵌入式学习笔记——SysTick(系统滴答)
16.嵌入式学习笔记——M4的基本定时器
17.嵌入式学习笔记——通用定时器
18.嵌入式学习笔记——PWM与输入捕获(上)
19.嵌入式学习笔记——PWM与输入捕获(下)
20.嵌入式学习笔记——ADC模数转换器
21.嵌入式学习笔记——DMA
22.嵌入式学习笔记——SPI通信
23.嵌入式学习笔记——SPI通信的应用
24嵌入式学习笔记——IIC通信

写在后面的话

整个M4的介绍基本上结束了,还有一个看门狗和一个位带操作,这个我是打算鸽了,笔者最近正在准备一个板子,因为这个系列主要使用的寄存器来编程控制,而且用的是M4系列的;其实在实际使用过程中M3系列的还是占比比较大的,所以下一个系列可能使用标准库,围绕这个板子进行一个介绍,然后插入一点FreeRTOS或者是用STM32CubeMX配置,用HAL库来进行。当然,前提是这个板子能够正常调通。而且最近立创那边好像也开了一个GD32的小车项目,这个大家有兴趣的也可以去试试水。
在这里插入图片描述
欢迎大家提供意见和建议哟,感谢大家的支持和点赞。

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

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

相关文章

MySQL调优系列(二)——数据类型优化

一、总则 1、更小的数据类型更好 应该尽量使用可以正确存储数据的最小数据类型&#xff0c;更小的数据类型通常更快&#xff0c;因为它们占用更少的磁盘、内存和CPU缓存&#xff0c;并且处理时需要的CPU周期更少&#xff0c;但是要确保没有低估需要存储的值的范围&#xff0c…

OpenCV中的图像处理3.1-3.3(三)色彩空间、几何变换、阈值处理

目录 3.1 改变色彩空间目标改变色彩空间对象跟踪如何找到HSV值来追踪&#xff1f;练习 3.2 图像的几何变换目标变换缩放平移旋转仿射变换透视变换其他资源 3.3 图像阈值处理目标简单的阈值处理自适应阈值处理Otsu的二值化Otsu的二值化是如何工作的&#xff1f;其他资源练习 翻译…

Netty基础(二)

1.Netty高性能架构设计 1.1.线程模型基本介绍 1>.不同的线程模式,对程序的性能有很大影响,为了搞清Netty线程模式,我们来系统的讲解下各个线程模式,最后看看Netty线程模型有什么优越性; 2>.目前存在的线程模型有: ①.传统阻塞I/O服务模型; ②.Reactor(反应器)模式; 3…

The Annotated Diffusion Model(翻译)

The Annotated Diffusion Model&#xff08;翻译&#xff09; 来源&#xff1a; https://huggingface.co/blog/annotated-diffusion code: 在这篇博文中&#xff0c;我们将更深入地研究去噪扩散概率模型&#xff08;也称为 DDPM、扩散模型、基于分数的生成模型或简称为自动编…

一、PEMFC基础之热力学

一、PEMFC基础之热力学 1.内能U、焓H、熵S、吉布斯自由能G、赫姆霍兹自由能F关系图2.可逆电压与温度和压力的关系3.能斯特方程4.燃料电池效率 1.内能U、焓H、熵S、吉布斯自由能G、赫姆霍兹自由能F关系图 2.可逆电压与温度和压力的关系 标准状态可逆电压Er计算&#xff1a; E …

JAVA15新特性

JAVA15新特性 概述 2020年9月15日,java15正式发布,(风平浪静的一个版本)共有14个JEP,是时间驱动形式发布的第六个版本.相关文档: https://openjdk.java.net/projects/jdk/15/ 一个孵化器阶段,三个预览,两个废弃和两个移除 JAVA15参与企业 java各版本主要特征 JDK5 :enum 泛型…

【面试】面试官:说一下线程池7个参数的含义?

文章目录 前言一、参数1&#xff1a;corePoolSize二、参数2&#xff1a;maximumPoolSize三、参数3&#xff1a;keepAliveTime四、参数4&#xff1a;TimeUnit五、参数5&#xff1a;BlockingQueue六、参数6&#xff1a;ThreadFactory七、参数7&#xff1a;RejectedExecutionHandl…

5月1日启动|2023年CSC西部/地方合作项目即将申报

2023年国家留学基金委&#xff08;以下简称CSC&#xff09;西部地区人才培养特别项目&#xff08;以下简称“西部项目”&#xff09;及地方合作项目选派工作即将开始&#xff0c;为此知识人网小编整理出以下申请要点&#xff0c;提醒申请者注意。 一、可以申报的省份 1.西部项…

hive sql实战案例-访问开始结束时间

问题描述 现在我们有一张用户访问区域的记录表&#xff0c;有三个字段&#xff1a;user_id表示用户ID&#xff0c;area表示用户访问的区域&#xff0c;visit_time是访问时间。求用户对某一区域访问的开始和结束时间&#xff0c;结果如右表所示。 分析问题 101访问了上午和晚…

InnoDB 与MyISAM 的区别

MyISAM和InnoDB都是Mysql里面的两个存储引擎。 在Mysql里面&#xff0c;存储引擎是可以自己扩展的&#xff0c;它的本质其实是定义数据存储的方式以及数据读取的实现逻辑。 不同存储引擎本身的特性&#xff0c;使得我们可以针对性的选择合适的引擎来实现不同的业务场景。从而获…

大数据框架-Hadoop

大数据框架-Hadoop 1.什么是大数据 大数据是指由传统数据处理工具难以处理的规模极大、结构复杂或速度极快的数据集合。这些数据集合通常需要使用先进的计算和分析技术才能够处理和分析&#xff0c;因此大数据技术包括了大数据存储、大数据处理和大数据分析等方面的技术和工具…

工作日记NO.2

1. 安装局域网内网通&#xff1b; 2. 安装VS2017AutoCADObjectARX VS2017AutoCAD2020配置ObjectARX VS2017AutoCAD2020配置ObjectARX (dgrt.cn) 3. ObjectARX简介 ObjectARX简介 - CAD安装教程 - 土木工程网 (civilcn.com) CAD系统二次开发-ObjectARX中的实体造型技术 (r…

【LeetCode】9,回文数。 难度等级:简单。巧妙解法很多,值得推敲。

文章目录 一、题目二、我的解答&#xff1a;逐个判断对应位置的首末数字是否相同三、其他解答3.1 将数字x转为字符串进行处理3.2 反转一半数字进行比较 LeetCode 第9题&#xff0c;回文数&#xff1b;难度等级&#xff1a;简单 一、题目 二、我的解答&#xff1a;逐个判断对应…

【C++复习1】程序结构和C++的工作原理

如果你是一名newbird的话&#xff0c;建议先看如下的视频加深理解后&#xff0c;再看下面的内容&#xff1a; https://www.bilibili.com/video/BV1N24y1B7nQ?p6 声明 以下的内容均由chatGpt编写与对上面这个视频进行总结生成&#xff0c;我就只负责改改格式。 C的工作原理 …

Live800:在线客服系统对客服管理有哪些好处?

互联网的不断发展&#xff0c;让越来越多的企业开始使用在线客服系统来提高客户服务质量。但是&#xff0c;很多人并不了解在线客服系统对客服管理的好处。本文将介绍在线客服系统的一些能够提高客服管理效率和提升用户满意度的管理功能。 实时客服监控 在线客服系统可以实时监…

MPU6050详解(含源码)

前言&#xff1a;MPU6050是一款强大的六轴传感器&#xff0c;需要理解MPU6050首先得有IIC的基础&#xff0c;MPU6050 内部整合了 3 轴陀螺仪和 3 轴加速度传感器&#xff0c;并且含有一个第二 IIC 接口&#xff0c;可用于连接外部磁力传感器&#xff0c;内部有硬件算法支持. 1…

JumpServer Harbor

Jumpserver是一款开源的堡垒机&#xff0c;可使系统的管理员和开发人员安全的连接到企业内部服务器上执行操作&#xff0c;并且支持大部分操作系统&#xff0c;是一款非常安全的远程连接工具 安装JumpServer jumpserver.org官网去下载安装&#xff0c;有一键安装&#xff08;里…

【回溯篇(3)---最少城市数】

1、最少城市数 【题目】 下图表示的是从城市A到城市H的交通图。从图中可以看出&#xff0c;从城市A到城市H要经过若干个城市。现要找出一条经过城市最少的一条路线。 【算法分析】 看到这图很容易想到用邻接距阵来表示&#xff0c;0表示能走&#xff0c;1表示不能走。如图。…

BM14 链表的奇偶重排

链表的奇偶重排_牛客题霸_牛客网 (nowcoder.com) 双指针解决 /*** struct ListNode {* int val;* struct ListNode *next;* ListNode(int x) : val(x), next(nullptr) {}* };*/ class Solution { public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#x…

Android 9.0 系统systemui状态栏下拉左滑显示通知栏右滑显示控制中心模块的流程分析

1.前言 在android9.0的系统rom定制化开发中,在系统原生systemui进行自定义下拉状态栏布局的定制的时候,需要在systemui下拉状态栏下滑的时候,根据下滑坐标来 判断当前是滑出通知栏还是滑出控制中心模块,所以就需要根据屏幕宽度,来区分x坐标值为多少是左滑出通知栏或者右…