3 IIC总线

news2025/1/4 19:10:43

3 IIC总线

  • 1、基本概念
    • 1.1 IIC总线定义
    • 1.2 IIC总线协议概念
  • 2 以AT24C02为例说明时序
    • 2.1 基本特性
    • 2.2 利用GPIO模拟IIC
    • 2.3 对AT24C02的操作
    • 2.4 重定向printf

1、基本概念

1.1 IIC总线定义

定义:两线式串行总线

  • 两线式:说明处理器和外设之间只需两根信号线,分别是SCL时钟控制信号线和SDA数据线

  • SCL:时钟控制信号线,永远只能由CPU控制,用于实现数据的同步,就四个字:低放高取
    SCL为低电平的时候将数据放到SDA数据线上
    SCL为高电平的时候从数据线SDA上获取数据

  • SDA:数据线,用于传输数据,双方都可以控制
    如果处理器给外设发送数据,SDA由处理器控制
    如果外设给处理器发送数据,SDA由外设控制

  • 串行:由于数据线就一根SDA,必然是串行,又由于有时钟控制信号线SCL,所以数据传输是一个时钟周期传输一个bit位

  • 总线:说明SCL和SDA上可以连接多个外设(理论上也可以连接多个CPU,此场景极其少见,常见一个处理器连接多个外设)
    在这里插入图片描述

  • 切记:SCL和SDA必须要分别连接一个上拉电阻,所以他们默认的电平都是高电平,且I2C数据传输从高位开始,I2C数据传输一次传输一个字节,如果传输多个字节,需要分拆着来传
    在这里插入图片描述

  • 主-从架构
    一个设备作为主设备 - master - cpu
    发起通信控制总线的时序
    其他设备作为从设备 - slave - 外设
    在cpu的控制下进行响应和数据传输

  • 半双工通信
    允许双向传输,但是在同一个时刻只能单向传输

  • 通信速率
    标准模式 - 100kbps
    快速模式 - 400kbps
    高速模式 - 3.4Mbps

1.2 IIC总线协议概念

  • 寻址机制
    在同一条总线上,每个IIC设备都有一个唯一的7位作为设备地址,设备地址由原理图和芯片手册共同决定在这里插入图片描述

  • R/W读写位:用于表示CPU到底是向外设写入数据还是从外设读取数据,有效位数为1个bit位,CPU读取数据:R/W=1,CPU向外设写入数据:R/W=0

  • START信号:又称起始信号,此信号永远只能由CPU发起,表示CPU开始要访问外设时序为:SCL为高电平,SDA由高电平向低电平跳变产生START信号

  • STOP信号:又称结束信号,此信号永远只能由CPU发起,表示CPU结束对总线的访问时序为:SCL为高电平,SDA由低电平向高电平跳变产生STOP信号
    在这里插入图片描述

  • ACK信号:又称应答信号,表示双方数据传输的反馈,有效位数为1个bit位,低电平有效

  • nack/NAK信号:有效位是1个bit位,高电平有效,只在特定情况下使用

  • 片内寄存器:任何I2C外设芯片内部都集成了一堆的寄存器,此类寄存器又称片内寄存器
    这些寄存器同样也有地址,地址编号从0x00开始
    虽然这些寄存器都有唯一的地址,但是CPU不能直接以指针的形式访问,必须要严格按照,读写时序进行访问

  • 结论:CPU访问I2C外设本质就是访问I2C外设内部的寄存器!所以I2C外设本身只需关注三点:
    I2C外设片内寄存器的特性
    I2C外设片内寄存器的基地址
    I2C外设片内寄存器的读写时序

2 以AT24C02为例说明时序

2.1 基本特性

1.存储介质
EEPROM - Electrically Eraseable Programmable ROM
无限次擦除和重写数据
大约10万次擦除
2.存储容量 - 256字节 - 2K位
3.数据保持时间
断电保存数据 - 十年以上
4.通信接口
I2C接口 - SDA / SCL
5.工作电压
1.8V - 5.5V之间
6.物理特性
SCL / SDA - 数据传输
WP - 写保护
如果将WP拉高, 将保护引脚WP拉高, 防止芯片被写入/擦除.
7.分页机制
共256字节
256个寄存器, 每个寄存器1字节, 寻址从0x00寻址 0x00 - 0xff
每8个字节一页, 分页管理 共32页
第1页 第2页 第3页 第4页 第5页 第6页 …
|--------|--------|--------|--------|--------|--------|--------
0 8 16 24 32 40 每一页的首地址
8.可以跨页读,不可跨页写
例如:
读取6 7 8 9地址数据, 正常读取
发生跨页写的操作, 会回滚到当前页首
例如:
写入6 7 8 9地址数据
6 7 - 正常写入
0 1 - 写入
6 7 0 1 - 写入的寄存器
9.读写时序

  • 写入单字节
    在这里插入图片描述
[CPU]发送开始信号 
[CPU]发送写设备地址 
[外设]回复ack
[CPU]发送要写入的寄存器地址 
[外设]回复ack
[CPU]发送要写入的数据(1字节)
[外设]回复ack
[CPU]发送停止信号
  • 写多字节
    在这里插入图片描述
[CPU]发送开始信号 
[CPU]发送写设备地址 
[外设]回复ack
[CPU]发送要写入的寄存器地址n
[外设]回复ack 
[CPU]发送数据1 -> 地址为n寄存器中 
[外设]回复ack 
[CPU]发送数据2 -> 地址为n+1寄存器中
[外设]回复ack 
[CPU]发送数据3 -> 地址为n+2寄存器中
[外设]回复ack
....
[CPU]发送数据 -> 地址为n+x寄存器中
[外设]回复ack
[CPU]发送停止信号
  • 读取单字节:
    在这里插入图片描述
[CPU]发送开始信号 
[CPU]发送写设备地址 
[外设]回复ack
[CPU]发送要读取的寄存器地址n 
[外设]回复ack
[CPU]发送开始信号 
[CPU]发送读设备地址 
[外设]回复ack
[外设]发送寄存器n的数据给CPU
[CPU]回复nack 
[CPU]发送停止信号 
  • 读取多字节
    在这里插入图片描述
[CPU]发送开始信号 
[CPU]发送写设备地址 
[外设]回复ack
[CPU]发送要读取的寄存器地址n 
[外设]回复ack
[CPU]发送开始信号 
[CPU]发送读设备地址 
[外设]回复ack
[外设]发送寄存器n的数据给CPU
[CPU]回复ack
[外设]发送寄存器n+1的数据给CPU
[CPU]回复ack
[外设]发送寄存器n+2的数据给CPU
[CPU]回复ack
...
[外设]发送寄存器n+x的数据给CPU
[CPU]回复nack 
[CPU]发送停止信号 

2.2 利用GPIO模拟IIC

在system目录下,新建IIC目录,打开keil工程,新建iic.c和iic.h文件
编辑iic.h

#ifndef __IIC_H_
#define __IIC_H_
#include "stm32f10x.h"
#include "system.h"
// 定义SCL和SDA 引脚信息
#define IIC_SCL_PORT GPIOB
#define IIC_SCL_PIN GPIO_Pin_6
#define IIC_SCL PBout(6)
// SDA GPIOB7
#define IIC_SDA_PORT GPIOB
#define IIC_SDA_PIN GPIO_Pin_7
#define IIC_SDA PBout(7)
#define READ_SDA PBin(7)
// 函数声明
void 	IIC_Init(void);// IIC初始化
void 	IIC_Start(void); // 开始信号
void 	IIC_Stop(void); // 结束信号
u8 		IIC_Wait_Ack(void); // 等待响应
void    IIC_NAck(void); // NAck信号
void	IIC_Ack(void); // 应答信号
void 	IIC_Send_Byte(u8 TxByte); // 发送数据
u8 		IIC_Read_Byte(u8 ack); // 读取数据
#endif

编辑iic.h

#include "iic.h"
#include "systick.h"
/*
	时钟线和数据线的初始化
*/
void IIC_Init(void){
	// 打开时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	// SCL PB6
	GPIO_InitTypeDef gpio_init;
	gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
	gpio_init.GPIO_Pin = IIC_SCL_PIN;
	gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(IIC_SCL_PORT,&gpio_init);
	
	// SDA PB7
	gpio_init.GPIO_Pin = IIC_SDA_PIN;
	gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(IIC_SDA_PORT,&gpio_init);
	
	// 将SCL和SDA拉高
	IIC_SCL =1;
	IIC_SDA =1;
}

// 配置SDA为输入模式
void SDA_IN(void){
	// SDA PB7
	GPIO_InitTypeDef gpio_init;
	gpio_init.GPIO_Mode = GPIO_Mode_IPU;
	gpio_init.GPIO_Pin = IIC_SDA_PIN;
	gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(IIC_SDA_PORT,&gpio_init);
}

// 配置SDA为输出模式
void SDA_OUT(void){
	// SDA PB7
	GPIO_InitTypeDef gpio_init;
	gpio_init.GPIO_Mode = GPIO_Mode_Out_PP;
	gpio_init.GPIO_Pin = IIC_SDA_PIN;
	gpio_init.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(IIC_SDA_PORT,&gpio_init);
}

根据开始信号时序,书写开始信号函数
在这里插入图片描述

在这里插入图片描述

编辑iic.c

/**
	IIC的操作函数
*/
// 开始信号
void IIC_Start(void){
	IIC_SCL = 0;
	SDA_OUT();
	IIC_SCL=1;
	IIC_SDA=1;
	// 1、时钟和数据线拉高,保持至少4.7us
	delay_us(6);
	IIC_SDA=0;
	// 2、时钟线拉高,数据线拉低,至少保持5us
	delay_us(6);
}

// 结束信号
void IIC_Stop(){

	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	IIC_SCL=1;
	// 时钟线拉高,数据线拉低,至少保持4us
	delay_us(6);
	IIC_SDA = 1;
	// 时钟线拉高,数据线拉高,至少保持4.7us
	delay_us(6);
}

// 等待ACK信号
// @return 如果收到了ack,返回0,没有收到ack,返回-1 
// 外设发ack,cpu收ack   时序为低放高取
u8 IIC_Wait_Ack(void){
	int tempTime = 0;
	// 1 将SCL拉低,让让外设将数据放入SDA上
	IIC_SCL = 0;
	// 1.1 让时钟线拉低保持至少4.7us
	delay_us(6); 
	// 1.2 配置SDA为输入
	SDA_IN();// 从外设读取ACK信号
	IIC_SCL=1;
	// 2 将SCL拉高,用于读取ACK信号
	delay_us(6);
	// 2.1 检查ACK信号,收到了ACK,值为0,没收到ACK信号,则READ_SDA=1;循环等待
	while(READ_SDA){
		tempTime++;
		if(tempTime>250){
			IIC_Stop();
			return -1;
		}
	}
	IIC_SCL = 0; // 收到了ack,这里拉低,是为了方便下一次的数据传输(这里必须要拉低)
	return 0;
}
// 发送nack信号  低放高取
void  IIC_NAck(void){
	// 将scl拉低,将数据放入sda上
	IIC_SCL = 0;
	SDA_OUT();
	IIC_SDA = 1; // 将SDA拉高,发送nack信号
	// 保持6us
	delay_us(6);
	// 将SCL拉高,用于外设读取数据
	IIC_SCL =1;
	delay_us(6);
	IIC_SCL = 0; // 继续后续的数据传输
}
// 发送Ack信号
void IIC_Ack(void){
	// 将scl拉低,将数据放入sda上
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA = 0; // 将SDA拉低,发送ack信号
	// 保存6us
	delay_us(6);
	// 将SCL拉高,用于外设读取数据
	IIC_SCL =1;
	delay_us(6);
	IIC_SCL =0;
}

// 发送单字节 IIC是先发送高位后发送低位
void IIC_Send_Byte(u8 TxByte){
	int i=0;
	IIC_SCL=0;// 拉低,为了将数据放入SDA
	SDA_OUT();
	for(i=0;i<8;i++){
		IIC_SCL=0;// 拉低,为了将数据放入SDA
		IIC_SDA = (TxByte>>(7-i))&0x01;// 将数据放到SDA上 
		delay_us(6);
		// 将SCL拉高
		IIC_SCL = 1;
		delay_us(6);
	}
}

// 接收单字节
// @return 返回读到的数据
// @param ack 1回复ack ack 0 回复nack
u8 IIC_Read_Byte(u8 ack){
	u8 i =0,data =0;
	IIC_SCL = 0;// 拉低SCL 让外设放数据
	SDA_IN();
	for(i=0;i<8;i++){
		IIC_SCL = 0;// 拉低SCL 让外设放数据
		delay_us(6);
		IIC_SCL = 1;// 拉高SCL cpu读取数据
		data |= READ_SDA <<(7-i);
	}
	// 判断读取了1字节的数据后, 回复ack还是nack
	if(!ack)
		IIC_NAck();
	else
		IIC_Ack();
	return data; // 返回读取到的数据
}	

2.3 对AT24C02的操作

在system目录下,新建AT24C02目录,打开keil工程,新建at24c02.c和at24c02.h文件
编辑at24c02.h

#ifndef __AT24C02_H_
#define __AT24C02_H_
#include "stm32f10x.h"

// 外设地址
#define AT24C02_ID (0x50)
// 功能函数
void 	AT24C02_Init(void);
u8 		AT24C02_ReadByte(u16 ReadAddr);
void 	AT24C02_WriteByte(u16 WriteAddr,u8 data);
void 	AT24C02_ReadBlockData(u16 ReadAddr,u8* pBuffer,u16 Len);
void 	AT24C02_WriteBlockData(u16 WriteAddr,u8* pBuffer,u16 Len);

// 测试函数
void 	AT24C02_ReadOne(void);
void 	AT24C02_WriteOne(void);
void 	AT24C02_ReadMul(void);
void 	AT24C02_WriteMul(void);
#endif

编辑at24c02.c

#include "at24c02.h"
#include "iic.h"

#include "systick.h"
#include "stdio.h"

/** 功能函数 */
// 初始化函数
void 	AT24C02_Init(void){
	IIC_Init();
}

根据上文提到了AT24C02的时序图,完成单字节、多字节数据的读取与写入

// 定义读取一个字节的函数
u8 AT24C02_ReadByte(u16 ReadAddr){
	u8 temp;
	// 1 发送开始信号
	IIC_Start();
	// 2 发送写设备地址
	IIC_Send_Byte(AT24C02_ID<<1|0);
	// 3 等待ACK信号
	IIC_Wait_Ack();
	// 4 发送要读取的寄存器地址
	IIC_Send_Byte(ReadAddr);
	// 5 等待ACK信号
	IIC_Wait_Ack();
	// 6 发送开始信号
	IIC_Start();
	// 7 发送读设备地址
	IIC_Send_Byte(AT24C02_ID<<1|1);
	// 8 等待ack信号
	IIC_Wait_Ack();
	// 9 读取单字节数据+回复nack信号
	temp = IIC_Read_Byte(0);
	// 10 发送stop信号
	IIC_Stop();
	// 11 返回数据
	return temp;
}
// @brief 写入的寄存器地址
// @param 写入的数据
void AT24C02_WriteByte(u16 WriteAddr,u8 data){
	// 1 发送开始信号
	IIC_Start();
	// 2 发送写设备地址
	IIC_Send_Byte(AT24C02_ID << 1 | 0);
	// 3 等待ACK信号
	IIC_Wait_Ack();
	// 4 发送要写入的寄存器地址
	IIC_Send_Byte(WriteAddr);
	// 5 等待ACK信号
	IIC_Wait_Ack();
	// 6 发送要写入的数据
	IIC_Send_Byte(data);
	// 7 等待ACK
	IIC_Wait_Ack();
	// 8 发送结束信号
	IIC_Stop();
}

// @brief 读取多个字节
// @param 
//		readAddr 地址
// 		pBuffer 读取数据存储的存储区,第一个寄存器地址
// 		Len 读取的字节个数
void AT24C02_ReadBlockData(u16 ReadAddr,u8* pBuffer,u16 Len){
	// 方法1
	/*
	while(Len){
		*pBuffer++ = AT24C02_ReadByte(ReadAddr++);
		Len--;
	}
	*/
	
	// 方法2
	// 发送开始信号
	IIC_Start();
	// 发送写设备地址
	IIC_Send_Byte(AT24C02_ID<<1|0);
	// 等待ACK信号
	IIC_Wait_Ack();
	// 发送要读取的寄存器地址
	IIC_Send_Byte(ReadAddr);
	// 等待ACK信号
	IIC_Wait_Ack();
	// 发送开始信号
	IIC_Start();
	// 发送读设备地址
	IIC_Send_Byte(AT24C02_ID<<1|1);
	// 等待ack信号
	IIC_Wait_Ack();
	while(Len){
		if(Len==1){
			*pBuffer = IIC_Read_Byte(0);
		}else{
			*pBuffer = IIC_Read_Byte(1);
		}
		pBuffer++;
		Len--;
	}
	IIC_Stop();
}

// @brief 写入多个字节
// @param 
//		WriteAddr 地址
// 		pBuffer 写入数据存储的存储区,第一个寄存器地址
// 		Len 写入的字节个数
void AT24C02_WriteBlockData(u16 WriteAddr,u8* pBuffer,u16 Len){
	// 方法1
	/*
	while(Len){
		AT24C02_WriteByte(WriteAddr,*pBuffer);
		pBuffer++;
		WriteAddr++;
		Len--;
	}
	*/
	
	// 方法2
	IIC_Start();
	// 发送写设备地址
	IIC_Send_Byte(AT24C02_ID<<1|0);
	// 等待ACK信号
	IIC_Wait_Ack();
	// 发送要写入的寄存器地址
	IIC_Send_Byte(WriteAddr);
	// 等待ACK信号
	IIC_Wait_Ack();
	printf("1");
	while(Len--){
		IIC_Send_Byte(*pBuffer);
		IIC_Wait_Ack();
		WriteAddr++;
		pBuffer++;
		printf("1");
		if(WriteAddr % 8 ==0){
			IIC_Stop();// 结束传输
			delay_ms(5);//写页需要5ms
			// 重新传输
			IIC_Start();
			// 发送写设备地址
			IIC_Send_Byte(AT24C02_ID<<1|0);
			// 等待ACK信号
			IIC_Wait_Ack();
			// 发送要写入的寄存器地址
			IIC_Send_Byte(WriteAddr);
			// 等待ACK信号
			IIC_Wait_Ack();
		}
	}
	IIC_Stop();
	delay_ms(5);
}

// 测试函数
void AT24C02_ReadOne(void){
	// 读取0x00寄存器,显示
	printf("Read data : %#x\r\n",AT24C02_ReadByte(0x10));
}
void AT24C02_WriteOne(void){
	// 将数据0xff写入0x00寄存器中
	AT24C02_WriteByte(0x10,0X68);
	printf("Write data : \r\n");
}
void AT24C02_ReadMul(void){
	u8 data[5]={0};
	AT24C02_ReadBlockData(0x00,data,5);
	for(int i=0;i<5;i++){
		printf("adde[%d]:data[%#x]\r\n",i,data[i]);
	}
}
void AT24C02_WriteMul(void){
	// 将数据1 2 3 4 5分别写入地址00 01 02 03 04寄存器中
	u8 data[5] = {0x44,0x45,0x46,0x47,0x48};
	AT24C02_WriteBlockData(0x00,data,5);
}

这里需要重定向printf,使printf中的内容重定向到串口中

2.4 重定向printf

在printf函数中会调用fputc函数,在串口uart.c中重写fputs函数

#include <stdio.h>
// 重定向printf函数,后续printf函数自动调用该函数
// @param c:要发送的字符
int fputc(int c,FILE* fp){
	// 判断发送缓冲区是否为空
	while(USART_GetFlagStatus(USART1,USART_FLAG_TC)== RESET); 
	// 获取要发送的字符,就将其发送到串口1中
	USART_SendData(USART1,(u8)c);
	return c;
}

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

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

相关文章

【时间复杂度和空间复杂度】(内含超多实例练习)

【时间复杂度和空间复杂度】&#xff08;内含超多实例练习&#xff09; 1. 算法效率2. 时间复杂度2.1 时间复杂度的概念2.2 实例练习2.2.1 数组中搜索数据2.2.2 冒泡排序2.2.3 二分查找2.2.4 阶乘递归2.2.5 斐波那契递归 3. 空间复杂度3.1 空间复杂度的概念3.2 实例练习3.2.1 冒…

Set Hashset底层原理 LinkedSet底层原理 Treeset 31

不重复只执行一次&#xff0c;排序是小到大&#xff0c;色图是接口类 Hashset底层原理 LinkedSet底层原理 Treeset 总结

AI+云边端协同,EasyCVR视频汇聚技术赋能安防监控新生态

随着信息技术的飞速发展和数字化时代的到来&#xff0c;安防监控领域的技术也在不断创新和突破。EasyCVR平台的视频汇聚技术作为其中的佼佼者&#xff0c;以其强大的视频处理、汇聚与融合能力&#xff0c;在安防监控领域展现出了巨大的应用潜力和价值。本文将详细介绍EasyCVR视…

权限束缚术:windows工具自动化权限提升

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文整理利用工具全自动化提权的相关操作方法 因为我没有相关的环境复现&#xff0c;所以没有成功 不过复现的操作就是下面整理的操作 溢出漏洞提权原理 利用溢出漏洞提权&#xff0c;实际上就是一次判断过往的…

极简聊天室-websocket版

再写一个极简聊天室的websocket版&#xff0c;在本例中&#xff0c;websocket仅用于服务器向客户端传输信息&#xff0c;客户端向服务器发送信息是传统的http post方式&#xff0c;用axios来实现的&#xff0c;当然websocket本身是支持双向通信&#xff0c;主要是为了方便跟前面…

Selenium 无法定位元素的几种解决方案

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、frame/iframe表单嵌套 WebDriver只能在一个页面上对元素识别与定位&#xff0c;对于frame/iframe表单内嵌的页面元素无法直接定位。 解决方法&#xff1a; d…

LLM大模型实战项目--基于ChatGLM2的小书虫文档阅读助手

本文介绍如此从零开始编写一个基于ChatGLM2的文档阅读助手 一、项目介绍 二、获取API接口 三、环境配置 四、代码实现 五、效果展示 一、项目介绍 小书虫&#x1f4da; 文档阅读助手是一个基于百度千帆大模型平台的Web应用程序&#xff0c;核心是清华大学训练的ChatGLM2大模…

算法加项目

1.仅加号 很简单&#xff0c;找到每次添加后最小的即可&#xff0c;这里不做演示。 2.采药 dp&#xff0c;for1数目&#xff0c;for2时间&#xff0c;简单的dp模板题目。 #include<bits/stdc.h> using namespace std; int n,m; const int N105,M105; long long a[M],b[M…

TInyWebServer面试题

一、项目介绍 &#xff08;1&#xff09;为什么要做这样一个项目&#xff1f; &#xff08;2&#xff09;介绍一下你的项目 这个项⽬是我在学习计算机⽹络和Linux socket编程过程中独⽴开发的轻量级Web服务器&#xff0c;服务器的⽹络模型是主从reactor加线程池的模式&#xf…

VMware虚拟机网络模式配置详解【原理,功能,特点层面】

VMware虚拟机网络模式配置详解【原理,功能,特点层面】 文章目录 VMware虚拟机网络模式配置详解【原理,功能,特点层面】桥接模式&#xff08;Bridged&#xff09;原理功能点介绍虚拟网络编辑器配置虚拟机配置 配置教程编辑-虚拟网络编辑器虚拟机网络配置 特点 NAT模式概念功能点…

数学建模--智能算法之蚁群优化算法

目录 基本原理 算法步骤 Python代码示例 应用领域 特点及改进 蚁群优化算法在解决哪些具体组合优化问题方面表现最为突出&#xff1f; 如何有效地改进蚁群优化算法以提高其收敛速度和避免陷入局部最优的问题&#xff1f; 蚁群优化算法与其他群体智能优化算法&#xff0…

三维旋转矩阵

前言 本文讲述三维旋转的矩阵推导&#xff0c;推导过程遵循下面的规则&#xff1a; 本文的坐标系是基于右手坐标系的逆时针旋转为正向旋转 围绕坐标轴的旋转 x x x轴 我们假设旋转的点为 P P P 假设旋转之前点 P P P的坐标为 ( x 0 , y 0 , z 0 ) (x_0,y_0,z_0) (x0​,y0​,…

CAN总线中注入拓展帧、远程帧,CAPL通用函数。

🍅 我是蚂蚁小兵,专注于车载诊断领域,尤其擅长于对CANoe工具的使用🍅 寻找组织 ,答疑解惑,摸鱼聊天,博客源码,点击加入👉【相亲相爱一家人】🍅 玩转CANoe,博客目录大全,点击跳转👉 【测试需求】:CAN总线要求DUT 接收到非预期的扩展帧、远程帧时,通信正常,…

vulhub:nginx解析漏洞nginx_parsing

这个解析漏洞其实是PHP CGI的漏洞&#xff0c;在PHP的配置文件中有一个关键的选项cgi.fix_pathinfo默认是开启的&#xff0c;当URL中有不存在的文件&#xff0c;PHP就会向前递归解析。在一个文件/xx.jpg后面加上/.php会将 /xx.jpg/xx.php 解析为 php 文件 条件&#xff1a; Ngi…

釉面陶瓷器皿和玻璃器皿 SOR/2016-175认证

釉面陶瓷制品和釉面玻璃制品设计用于盛装食品&#xff0c;包括扁平餐具、杯子或马克杯、小号或大号凹形器皿&#xff08;杯子或马克杯除外&#xff09;以及水罐。这些法规适用于以下釉面陶瓷制品和釉面玻璃制品&#xff1a; 完全或部分由陶瓷或玻璃制成完全或部分覆盖含铅或镉…

MATLAB(12)预测模型

一、前言 在MATLAB中创建一个预测模型通常涉及多个步骤&#xff0c;包括数据准备、模型选择、训练模型、评估模型以及使用模型进行预测。以下是一个简化的例子&#xff0c;说明如何使用MATLAB中的内置函数来创建一个基于线性回归的预测模型。这个例子将使用MATLAB的fitlm函数来…

C语言函数初识

文章目录 &#x1f34a;自我介绍&#x1f34a;函数初识前言概念具体实例解释函数的实用性 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以&#xff1a;点赞关注评论收藏&#xff08;一键四连&#xff09;哦~ &#x1f34a;自我介绍 Hello,大家好&#xff0c;我是小珑也…

院人全年无休计划背后,芒果把To C综艺玩明白了

《种地吧》掉粉闹得沸沸扬扬&#xff0c;不少禾伙人希望芒果把“十个勤天”挖走&#xff0c;毕竟芒果做综艺群像确实是一把好手&#xff0c;“院人”就是最好的例子。 在#种地吧粉丝让芒果把十个勤天挖走#的话题登上热搜之后&#xff0c;“学分们”&#xff08;院人粉丝&#…

JS等待所有方法执行完成在执行下一个方法,promise All

在工作中会遇到这样一个场景&#xff0c;前端需要拿到不同接口返回的结果在执行某个逻辑&#xff0c;当使用链式那样的方式去请求&#xff0c;等一个接口响应完在请求下一个接口&#xff0c;这种方法就会导致请求时间特别长。这个时候就可以使用promise all&#xff0c;同时请求…

国产麒麟操作系统下搞单机版

去年纪委单位的一个项目&#xff0c;因为单位保密性质&#xff0c;档案必须要保密&#xff0c;要求采用单机版&#xff0c; 要求跟EXE那样&#xff0c;双击打开&#xff0c;阿公单位信息人员电脑操作水平化滞后还是相当严重啊。 去年已经给他花了时间按他们的要求实现了。 上周…