【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); 
}

​ 下期再继续详细介绍~

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值