因为I2C通讯非常重要,因此要详细的分析每一个库函数
IIC初始化结构体
/**
* @brief I2C Init structure definition
*/
typedef struct
{
// 指定时钟频率,这个值不能超过400kHz
uint32_t I2C_ClockSpeed; /*!< Specifies the clock frequency.
This parameter must be set to a value lower than 400kHz */
// IIC模式:可以有3种选择:I2C_Mode_I2C I2C_Mode_SMBusDevice I2C_Mode_SMBusHost
uint16_t I2C_Mode; /*!< Specifies the I2C mode.
This parameter can be a value of @ref I2C_mode */
// 占空比 I2C_DutyCycle_2 I2C_DutyCycle_16_9
uint16_t I2C_DutyCycle; /*!< Specifies the I2C fast mode duty cycle.
This parameter can be a value of @ref I2C_duty_cycle_in_fast_mode */
// 自身地址,这个可以任意给一个与其他IIC从机设备不同的地址,如0X0A
uint16_t I2C_OwnAddress1; /*!< Specifies the first device own address.
This parameter can be a 7-bit or 10-bit address. */
// 应答Ack使能 I2C_Ack_Enable I2C_Ack_Disable
uint16_t I2C_Ack; /*!< Enables or disables the acknowledgement.
This parameter can be a value of @ref I2C_acknowledgement */
// IIC应答地址,一般7位 I2C_AcknowledgedAddress_7bit I2C_AcknowledgedAddress_10bit
uint16_t I2C_AcknowledgedAddress; /*!< Specifies if 7-bit or 10-bit address is acknowledged.
This parameter can be a value of @ref I2C_acknowledged_address */
}I2C_InitTypeDef;
IIC初始化函数,这里有一些值得注意的地方:
1 IIC模块时钟频率
2 分频系数
/**
* @brief Initializes the I2Cx peripheral according to the specified
* parameters in the I2C_InitStruct.
* @param I2Cx: where x can be 1 or 2 to select the I2C peripheral.
* @param I2C_InitStruct: pointer to a I2C_InitTypeDef structure that
* contains the configuration information for the specified I2C peripheral.
* @retval None
*/
void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct)
{
uint16_t tmpreg = 0, freqrange = 0;
uint16_t result = 0x04;
uint32_t pclk1 = 8000000; // 只是初始化时给的值,没有意义,给0也可以
RCC_ClocksTypeDef rcc_clocks;
/*---------------------------- I2Cx CR2 Configuration ------------------------*/
// 配置IIC模块输入时钟频率,标准模式至少为2MHz,快速模式至少为4MHz,最大为36MHz
/* Get the I2Cx CR2 value */
// 先暂存CR2,只处理FREQ[5:0]位,其他位不变
tmpreg = I2Cx->CR2;
/* Clear frequency FREQ[5:0] bits */
// 清除FREQ[5:0]位
tmpreg &= CR2_FREQ_Reset;
/* Get pclk1 frequency value */
RCC_GetClocksFreq(&rcc_clocks);
// 获得APB1总线的时钟频率(36MHz)
pclk1 = rcc_clocks.PCLK1_Frequency;
/* Set frequency bits depending on pclk1 value */
// 计算FREQ[5:0]位写入值(36,已经是最大值)
freqrange = (uint16_t)(pclk1 / 1000000);
tmpreg |= freqrange;
/* Write to I2Cx CR2 */
// 写入到CR2寄存器
I2Cx->CR2 = tmpreg;
/*---------------------------- I2Cx CCR Configuration ------------------------*/
// 配置CCR寄存器(设置标准/快速模式、占空比、时钟控制分频系数等)
/* Disable the selected I2C peripheral to configure TRISE */
I2Cx->CR1 &= CR1_PE_Reset;
/* Reset tmpreg value */
/* Clear F/S, DUTY and CCR[11:0] bits */
tmpreg = 0;
/* Configure speed in standard mode */
// I2C_ClockSpeed <= 100k时,配置为标准模式
if (I2C_InitStruct->I2C_ClockSpeed <= 100000)
{
/* Standard mode speed calculate */
// APB1总线时钟/2/I2C_ClockSpeed = 分频系数
result = (uint16_t)(pclk1 / (I2C_InitStruct->I2C_ClockSpeed << 1));
// 允许设定的最小值为0x04,在快速DUTY模式下允许的最小值为0x01;
/* Test if CCR value is under 0x4*/
if (result < 0x04)
{
/* Set minimum allowed value */
result = 0x04;
}
/* Set speed value for standard mode */
tmpreg |= result;
/* Set Maximum Rise Time for standard mode */
I2Cx->TRISE = freqrange + 1;
}
/* Configure speed in fast mode */
// 快速模式下,分频系数的计算
else /*(I2C_InitStruct->I2C_ClockSpeed <= 400000)*/
{
if (I2C_InitStruct->I2C_DutyCycle == I2C_DutyCycle_2)
{
/* Fast mode speed calculate: Tlow/Thigh = 2 */
result = (uint16_t)(pclk1 / (I2C_InitStruct->I2C_ClockSpeed * 3));
}
else /*I2C_InitStruct->I2C_DutyCycle == I2C_DutyCycle_16_9*/
{
/* Fast mode speed calculate: Tlow/Thigh = 16/9 */
result = (uint16_t)(pclk1 / (I2C_InitStruct->I2C_ClockSpeed * 25));
/* Set DUTY bit */
result |= I2C_DutyCycle_16_9;
}
/* Test if CCR value is under 0x1*/
if ((result & CCR_CCR_Set) == 0)
{
/* Set minimum allowed value */
result |= (uint16_t)0x0001;
}
/* Set speed value and set F/S bit for fast mode */
tmpreg |= (uint16_t)(result | CCR_FS_Set);
/* Set Maximum Rise Time for fast mode */
I2Cx->TRISE = (uint16_t)(((freqrange * (uint16_t)300) / (uint16_t)1000) + (uint16_t)1);
}
/* Write to I2Cx CCR */
// 写入到CCR寄存器,并使能IIC
I2Cx->CCR = tmpreg;
/* Enable the selected I2C peripheral */
I2Cx->CR1 |= CR1_PE_Set;
/*---------------------------- I2Cx CR1 Configuration ------------------------*/
// 配置IIC模式、应答
/* Get the I2Cx CR1 value */
tmpreg = I2Cx->CR1;
/* Clear ACK, SMBTYPE and SMBUS bits */
tmpreg &= CR1_CLEAR_Mask;
/* Configure I2Cx: mode and acknowledgement */
/* Set SMBTYPE and SMBUS bits according to I2C_Mode value */
/* Set ACK bit according to I2C_Ack value */
tmpreg |= (uint16_t)((uint32_t)I2C_InitStruct->I2C_Mode | I2C_InitStruct->I2C_Ack);
/* Write to I2Cx CR1 */
I2Cx->CR1 = tmpreg;
/*---------------------------- I2Cx OAR1 Configuration -----------------------*/
// 配置IIC应答地址位数
/* Set I2Cx Own Address1 and acknowledged address */
I2Cx->OAR1 = (I2C_InitStruct->I2C_AcknowledgedAddress | I2C_InitStruct->I2C_OwnAddress1);
}
在上一节中提到,IIC发送/接收过程会产生相应的事件,我们通过检测事件来判断当前运行的状态,以下是检测函数
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);
/**
* @brief Checks whether the last I2Cx Event is equal to the one passed
* as parameter.
* @param I2Cx: where x can be 1 or 2 to select the I2C peripheral.
* @param I2C_EVENT: specifies the event to be checked.
* This parameter can be one of the following values:
* @arg I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED : EV1
* @arg I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED : EV1
* @arg I2C_EVENT_SLAVE_TRANSMITTER_SECONDADDRESS_MATCHED : EV1
* @arg I2C_EVENT_SLAVE_RECEIVER_SECONDADDRESS_MATCHED : EV1
* @arg I2C_EVENT_SLAVE_GENERALCALLADDRESS_MATCHED : EV1
* @arg I2C_EVENT_SLAVE_BYTE_RECEIVED : EV2
* @arg (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_DUALF) : EV2
* @arg (I2C_EVENT_SLAVE_BYTE_RECEIVED | I2C_FLAG_GENCALL) : EV2
* @arg I2C_EVENT_SLAVE_BYTE_TRANSMITTED : EV3
* @arg (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_DUALF) : EV3
* @arg (I2C_EVENT_SLAVE_BYTE_TRANSMITTED | I2C_FLAG_GENCALL) : EV3
* @arg I2C_EVENT_SLAVE_ACK_FAILURE : EV3_2
* @arg I2C_EVENT_SLAVE_STOP_DETECTED : EV4
* @arg I2C_EVENT_MASTER_MODE_SELECT : EV5
* @arg I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED : EV6
* @arg I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED : EV6
* @arg I2C_EVENT_MASTER_BYTE_RECEIVED : EV7
* @arg I2C_EVENT_MASTER_BYTE_TRANSMITTING : EV8
* @arg I2C_EVENT_MASTER_BYTE_TRANSMITTED : EV8_2
* @arg I2C_EVENT_MASTER_MODE_ADDRESS10 : EV9
*
* @note: For detailed description of Events, please refer to section
* I2C_Events in stm32f10x_i2c.h file.
*
* @retval An ErrorStatus enumeration value:
* - SUCCESS: Last event is equal to the I2C_EVENT
* - ERROR: Last event is different from the I2C_EVENT
*/
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t lastevent = 0;
uint32_t flag1 = 0, flag2 = 0;
ErrorStatus status = ERROR;
/* Check the parameters */
assert_param(IS_I2C_ALL_PERIPH(I2Cx));
assert_param(IS_I2C_EVENT(I2C_EVENT));
/* Read the I2Cx status register */
// 分别读取SR1 SR2两个状态寄存器(16位),然后把状态合并到32位数据中
flag1 = I2Cx->SR1;
flag2 = I2Cx->SR2;
flag2 = flag2 << 16;
/* Get the last event value from I2C status register */
lastevent = (flag1 | flag2) & FLAG_Mask;
/* Check whether the last event contains the I2C_EVENT */
if ((lastevent & I2C_EVENT) == I2C_EVENT)
{
// 判断事件是否正确发生
/* SUCCESS: last event is equal to I2C_EVENT */
status = SUCCESS;
}
else
{
/* ERROR: last event is different from I2C_EVENT */
status = ERROR;
}
/* Return status */
return status;
}
检测标志位的函数,其实也是读取SR1 SR2的相应位
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);
/**
* @brief Checks whether the specified I2C flag is set or not.
* @param I2Cx: where x can be 1 or 2 to select the I2C peripheral.
* @param I2C_FLAG: specifies the flag to check.
* This parameter can be one of the following values:
* @arg I2C_FLAG_DUALF: Dual flag (Slave mode)
* @arg I2C_FLAG_SMBHOST: SMBus host header (Slave mode)
* @arg I2C_FLAG_SMBDEFAULT: SMBus default header (Slave mode)
* @arg I2C_FLAG_GENCALL: General call header flag (Slave mode)
* @arg I2C_FLAG_TRA: Transmitter/Receiver flag
* @arg I2C_FLAG_BUSY: Bus busy flag
* @arg I2C_FLAG_MSL: Master/Slave flag
* @arg I2C_FLAG_SMBALERT: SMBus Alert flag
* @arg I2C_FLAG_TIMEOUT: Timeout or Tlow error flag
* @arg I2C_FLAG_PECERR: PEC error in reception flag
* @arg I2C_FLAG_OVR: Overrun/Underrun flag (Slave mode)
* @arg I2C_FLAG_AF: Acknowledge failure flag
* @arg I2C_FLAG_ARLO: Arbitration lost flag (Master mode)
* @arg I2C_FLAG_BERR: Bus error flag
* @arg I2C_FLAG_TXE: Data register empty flag (Transmitter)
* @arg I2C_FLAG_RXNE: Data register not empty (Receiver) flag
* @arg I2C_FLAG_STOPF: Stop detection flag (Slave mode)
* @arg I2C_FLAG_ADD10: 10-bit header sent flag (Master mode)
* @arg I2C_FLAG_BTF: Byte transfer finished flag
* @arg I2C_FLAG_ADDR: Address sent flag (Master mode) "ADSL"
* Address matched flag (Slave mode)"ENDA"
* @arg I2C_FLAG_SB: Start bit flag (Master mode)
* @retval The new state of I2C_FLAG (SET or RESET).
*/
FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG)
{
FlagStatus bitstatus = RESET;
__IO uint32_t i2creg = 0, i2cxbase = 0;
/* Check the parameters */
assert_param(IS_I2C_ALL_PERIPH(I2Cx));
assert_param(IS_I2C_GET_FLAG(I2C_FLAG));
/* Get the I2Cx peripheral base address */
i2cxbase = (uint32_t)I2Cx;
/* Read flag register index */
i2creg = I2C_FLAG >> 28;
/* Get bit[23:0] of the flag */
I2C_FLAG &= FLAG_Mask;
if(i2creg != 0)
{
/* Get the I2Cx SR1 register address */
i2cxbase += 0x14;
}
else
{
/* Flag in I2Cx SR2 Register */
I2C_FLAG = (uint32_t)(I2C_FLAG >> 16);
/* Get the I2Cx SR2 register address */
i2cxbase += 0x18;
}
if(((*(__IO uint32_t *)i2cxbase) & I2C_FLAG) != (uint32_t)RESET)
{
/* I2C_FLAG is set */
bitstatus = SET;
}
else
{
/* I2C_FLAG is reset */
bitstatus = RESET;
}
/* Return the I2C_FLAG status */
return bitstatus;
}
发送7位地址的函数(这里就有一个问题,到底是7位还是8位),可以直接看源码。从源码中可以很清楚的看到,我们需要提供的就是8位地址,而且不论是读地址或者是写地址都可以,该函数会自动修改最后一位。
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
/**
* @brief Transmits the address byte to select the slave device.
* @param I2Cx: where x can be 1 or 2 to select the I2C peripheral.
* @param Address: specifies the slave address which will be transmitted
* @param I2C_Direction: specifies whether the I2C device will be a
* Transmitter or a Receiver. This parameter can be one of the following values
* @arg I2C_Direction_Transmitter: Transmitter mode
* @arg I2C_Direction_Receiver: Receiver mode
* @retval None.
*/
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction)
{
/* Check the parameters */
assert_param(IS_I2C_ALL_PERIPH(I2Cx));
assert_param(IS_I2C_DIRECTION(I2C_Direction));
/* Test on the direction to set/reset the read/write bit */
// 如果是写方向
if (I2C_Direction != I2C_Direction_Transmitter)
{
/* Set the address bit0 for read */
// 把最后一位置0,这里可以看到,我们实际上需要提供的是8位地址
Address |= OAR1_ADD0_Set;
}
// 如果是读方向,最后一位清0
else
{
/* Reset the address bit0 for write */
Address &= OAR1_ADD0_Reset;
}
// 然后把数据写入到DR寄存器
/* Send the address */
I2Cx->DR = Address;
}
发送数据函数(直接把数据写入到DR寄存器)
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data)
{
/* Check the parameters */
assert_param(IS_I2C_ALL_PERIPH(I2Cx));
/* Write in the DR register the data to be sent */
I2Cx->DR = Data;
}
接收数据函数(返回DR寄存器的值)
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx)
{
/* Check the parameters */
assert_param(IS_I2C_ALL_PERIPH(I2Cx));
/* Return the data in the DR register */
return (uint8_t)I2Cx->DR;
}