【GD32/STM32】IIC高级实战应用-IIC作为从机实现与主机交互(一)

适用场景

​ 在STM32的使用中,经常被当作从机来使用负责特定模块的控制以及数据传输,IIC作为一种全双工的通讯方式适合进行指令传输,所以能够通过IIC进行指令的接收、寄存器的寻址,是IIC应用的一个重要的方面。

目录

​ 1.IIC通讯的定义以及时序

​ 2.基于HAL库的IIC如何配置为从机

​ 3.如何进行寄存器寻址以及指令的解析

一、IIC的定义以及时序

​ IIC的定义比较容易理解,IICInter-Integrated Circuit)中文可以叫集成电路总线,是一种串行通信总线,使用多主从架构(意思是可以使用IIC总线的方式,实现多个从机的访问)。

​ I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。

在这里插入图片描述

  1. 起始条件(Start Condition)
    • 主设备(通常是微控制器)发出一个低电平的起始信号,即SCL为高电平的时候,SDA由高电平向低电平跳变
    • 这个起始信号标志着总线上即将开始一个新的传输过程。

在这里插入图片描述

2.地址和读/写位(Address and Read/Write Bit)

  • 主设备发送从设备的地址,这个地址通常是7位长。
  • 接着发送一个读/写位(R/W位),指示是读操作(1)还是写操作(0)。

在这里插入图片描述
在这里插入图片描述

3.应答(Acknowledgement)

  • 主设备发送地址和R/W位后,等待从设备的应答。
  • 如果从设备存在且准备好进行通信,它会拉低SDA(串行数据线)来应答(ACK)主设备。

4.数据传输

  • 在地址和R/W位后,主设备或从设备可以开始传输数据。
  • 数据传输时钟由主设备产生,数据通过SDA线传输,每次时钟脉冲都会改变SDA上的数据位。

5.停止条件(Stop Condition)

  • 数据传输完成后,主设备会发出一个高电平的停止信号。
  • 这个停止信号表明当前通信过程已经结束,总线可以被其他设备使用。

6.重复起始条件(Repeated Start Condition)

  • 在一个传输过程中,主设备可以发送重复起始信号,而无需首先发送停止信号。
  • 这使得主设备可以与同一从设备进行多次数据传输,而不必重新选择地址。

二、HAL库配置为从机

static void InitI2C(void)
{
	hi2c1.Instance = I2C1;
	hi2c1.Init.ClockSpeed = 100000;								/*时钟速率主从要保持一致*/
	hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
	hi2c1.Init.OwnAddress1 = 4;									/*设置作为从机的设备地址*/
	hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
	hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
	hi2c1.Init.OwnAddress2 = 0;									/*双地址,设置成0就行*/
	hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
	hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
	
	if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
		Error_Handler();
	}
	HAL_I2C_EnableListen_IT(&hi2c1);							/*启动监听*/
}

void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{
  if(i2cHandle->Instance==I2C1)
  {
	GPIO_InitTypeDef GPIO_InitStruct = {0};
		
    __HAL_RCC_I2C1_CLK_ENABLE();	
    __HAL_RCC_GPIOB_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
	GPIO_InitStruct.Pull=GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
	
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);						/*将两个引脚拉高*/
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);	
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);

    HAL_NVIC_SetPriority(I2C1_EV_IRQn, IIC_INTERRUPT_POLARITY , 0);/*设置事件中断*/
    HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
    HAL_NVIC_SetPriority(I2C1_ER_IRQn, IIC_INTERRUPT_POLARITY , 0);/*设置错误中断*/
    HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);				
	if(HAL_I2C_EnableListen_IT(&hi2c1) != HAL_OK) {
	}
  }
}
static uint8_t registerAddress; /*用来保存当前操作的寄存器地址*/

/*判断是IIC是写操作还是读操作,可以使用这个回调函数来自动完成*/
void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
	__HAL_I2C_CLEAR_FLAG(hi2c, I2C_FLAG_ADDR);

	if(TransferDirection == I2C_DIRECTION_TRANSMIT) /*发送*/
	{
		HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, &registerAddress, 1, I2C_FIRST_FRAME);
		HAL_I2C_EnableListen_IT(hi2c);
	}
	else if(TransferDirection == I2C_DIRECTION_RECEIVE)/*接收*/
	{
		receiveTimes=0;
		transmitRegisterData(registerAddress);
		HAL_I2C_EnableListen_IT(hi2c);
	}
}

/*如果是主机发送的指令,接收完成后会执行从机接收完成的回调函数,在这个函数中可以进行接收主机发送的数据*/
void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    /* 匹配不指令需要接收数据的长度 */
	if(getRegisterAddressFinished(receiveTimes))
		receiveLength = matchRegisterDataLength(registerAddress);

	if(receiveTimes==receiveLength)
	{
		recieveRegisterData(registerAddress);
		receiveTimes = 0;
	}
	else
	{
		HAL_I2C_Slave_Seq_Receive_IT(&hi2c1, &receiveDataBuff[receiveTimes], 1, I2C_NEXT_FRAME);	
		receiveTimes++;
	}
	HAL_I2C_EnableListen_IT(hi2c); 
}

​ 下期再继续详细介绍~

### Faiss 中的不同 IVF 类型及实现方式 Faiss 是种高效的相似度搜索库,广泛应用于大规模向量检索场景。以下是关于 Faiss 的不同 IVF(Inverted File Indexing)类型的详细介绍。 #### 1. **基本概念** IVF 方法的核心思想是通过聚类算法将数据划分为多个簇(cluster),并为每个簇分配个唯的 ID。查询时,仅需在目标最近的几个簇中查找即可减少计算复杂度[^1]。 ```python import faiss dim, measure = 64, faiss.METRIC_L2 index = faiss.IndexFlatL2(dim) print(index.is_trained) # 判断索引是否已经训练完成 ``` --- #### 2. **常见 IVF 类型** ##### (1) **IVFFlat** `IVFFlat` 是最基本的倒排索引结构之。它通过对输入向量进行 k-means 聚类来划分簇,并存储原始向量以便精确距离计算。 - 实现方式:先对数据集执行次全局 k-means 计算得到质心集合;之后,在查询阶段找到离查询点最近的若干个质心对应的簇,并逐比较这些簇中的向量。 - 参数说明: - `nlist`: 表示总的簇数量。 - `metric`: 使用的距离度量标准,默认为 L2 距离。 ```python quantizer = faiss.IndexFlatL2(dim) index_ivf_flat = faiss.IndexIVFFlat(quantizer, dim, nlist=100, metric=faiss.METRIC_L2) index_ivf_flat.train(xb) # 需要显式调用 train 函数 index_ivf_flat.add(xb) ``` --- ##### (2) **IVFPQ** `IVFPQ` 结合了乘积量化技术,进步压缩了向量表示的空间需求。其主要特点是将高维空间切分成更小的部分,每部分独立编码后再组合成最终结果。 - 特点:相比传统方法显著降低了内存占用率,但引入了定程度上的精度损失。 - 参数解释: - `M`: 向量被分割的数量。 - `nbits`: 每段使用的比特数决定码本大小 \(2^{nbits}\)。 ```python param_pq = f'PQ{m}x{nbits}' index_ivfpq = faiss.index_factory(dim, param_pq, measure) assert not index_ivfpq.is_trained index_ivfpq.train(xb) index_ivfpq.add(xb) ``` --- ##### (3) **IVFSQ** 平方根量化 (`Scalar Quantization`) 提供了个简单而有效的替代方案用于降低存储成本。尽管它的性能可能不如 PQ 复杂模型那么优越,但在某些特定条件下仍然非常实用。 - 主要优势在于易于理解和部署以及较低的时间开销。 ```python index_sq8 = faiss.IndexIVFScalarQuantizer( quantizer, dim, nlist=100, sq_type=faiss.ScalarQuantizer.QT_8bit_uniform ) index_sq8.train(xb) index_sq8.add(xb) ``` --- #### 3. **优劣对比分析** | 索引类型 | 存储效率 | 查询速度 | 数据准确性 | |----------|-----------|------------|--------------| | IVFFlat | 较低 | 快 | 高 | | IVFPQ | 极高 | 很快 | 中等偏下 | | IVFSQ | 高 | 快 | 中等 | 上述表格总结了几种主流 IVF 变体之间的权衡关系[^2]。 --- #### 4. **实际应用场景建议** 对于大多数常规任务而言,“IVFxPQy”的配置通常是首选解决方案因为它综合表现良好既兼顾了资源利用率又保持定水平的服务质量。 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值