适用场景
在STM32的使用中,经常被当作从机来使用负责特定模块的控制以及数据传输,IIC作为一种全双工的通讯方式适合进行指令传输,所以能够通过IIC进行指令的接收、寄存器的寻址,是IIC应用的一个重要的方面。
目录
1.IIC通讯的定义以及时序
2.基于HAL库的IIC如何配置为从机
3.如何进行寄存器寻址以及指令的解析
一、IIC的定义以及时序
IIC的定义比较容易理解,IIC(Inter-Integrated Circuit)中文可以叫集成电路总线,是一种串行通信总线,使用多主从架构(意思是可以使用IIC总线的方式,实现多个从机的访问)。
I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。
- 起始条件(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, ®isterAddress, 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);
}
下期再继续详细介绍~