【STM32】HAL库HAL_I2C_Receive详解
STM32 Master Receive diagram
Diagram中,上方为硬件自动执行,下方为软件控制的事件(EVx)
需要确保一定能执行的操作,都需要关中断运行,防止中断打断操作
N=1 ⇒ 1 byte
1.In the ADDR event, clear the ACK bit.
2. Clear ADDR
3. Program the STOP/START bit.
4. Read the data after the RxNE flag is set.
I2C_CR1→Bit10 ACK
为0,这样子在接收到Data1后主机不会回传ACK,对应上图的NA
I2C_CR1→Bit10 ACK
为0 ⇒ 清空ADDR ⇒ 发送StopI2C_SR1→Bit6 RXNE
为1N=2 ⇒ 2 byte
- Set POS and ACK
- Wait for the ADDR flag to be set
- Clear ADDR
- Clear ACK
- Wait for BTF to be set
- Program STOP
- Read DR twice
EV6_1 事件,紧跟在EV6事件中,故需要注意的是此处和N=1的情况不同,是先清空ADDR,再置I2C_CR1→Bit10 ACK
为0。此外,为何此处是在完整接收Data1之前disable ACK?具体可见代码,此处用到了I2C_CR1→Bit11 POS
:
当在接收数据前,使能该标志位,并在清空ADDR后置I2C_CR1→Bit10 ACK
为0,则会在Data2(第二个byte)被移入Data shift register后给出一个NACK
EV7_3 事件,此处我们采用的方法是连续读两次Data register的方式在最后一起读出两个I2C的byte,故会出现Data register和Data shift register同时存在数据的情况,此时I2C_SR1→Bit6 RXNE
和I2C_SR1→Bit2 BTF
均为1
N>2 ⇒ >2 byte
When 3 bytes remain to be read:
- RxNE = 1 ⇒ Nothing (DataN-2 not read).
- DataN-1 received
- BTF = 1 because both shift and data registers are full: DataN-2 in DR and DataN-1 in
the shift register ⇒ SCL tied low: no other data will be received on the bus. - Clear ACK bit
- Read DataN-2 in DR ⇒ This will launch the DataN reception in the shift register
- DataN received (with a NACK)
- Program START/STOP
- Read DataN-1
- RxNE = 1
- Read DataN
当通过I2C接收的字节数大于2的情况,实际上需要注意的是其最后三个字节的接收情况,对于非最后三个字节:
I2C_SR1→Bit6 RXNE
拉高为1,此时从DR(Data register)中读出数据清空该标志位对于最后三个字节,其大致逻辑可参考step 1~10,简要概括为:当DataN-2被移入DR,此时I2C_SR1→Bit6 RXNE
拉高为1,但我们不去读DR,无EV7;继续接收DataN-1,当DataN-1被移入DSR,由于DR和DSR均有数据,此时I2C_SR1→Bit2 BTF
被拉高为1,此时SCL会被拉低,不再接收数据;由于下一个字节DataN为最后一个字节,我们需要在接收DataN之前,置I2C_CR1→Bit10 ACK
为0,以便接收到DATAN后回传NA,与此同时我们读DR中的DataN-2,则SCL会重新工作,DataN-1移入DR,DataN移入DSR,并且发送Stop信号;
I2C_CR1→Bit10 ACK
为0的事件,直至发送Stop信号这一段发送Stop信号后,我们读取DR中的DataN-1,此时DataN会从DSR移入DR,则由于DR中有数据而DSR中无数据,对应为I2C_SR1→Bit6 RXNE
拉高为1,I2C_SR1→Bit2 BTF
被拉低为0;此时我们再读取DataN,这里对应又为EV7 事件。
HAL_I2C_Master_Receive
REF: 6.HAL_I2C_Master_Receive函数解析
代码解析
和HAL_I2C_Master_Transmitter
类似,无论是接收多少个字节,开始的步骤都为发送START → EV5事件 → 发送从机7bit地址,这三个步骤执行的代码和HAL_I2C_Master_Transmitter
中是类似的;
当完成从机7bit地址的发送,会进入一个if~else if的判断,主要根据传入的参数hi2c->XferCount=Size
判断待接收的字节数量。
当hi2c->XferCount==0U
,对应接收0 byte。清零ADDR,并发送Stop信号。
if (hi2c->XferSize == 0U) // transfer size=0 byte,就清空ADDR并且发送停止信号
{
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Generate Stop */
SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);
}
当hi2c->XferCount==1U
,对应接收1 byte。置I2C_CR1→Bit10 ACK
为0,清ADDR,并发送Stop信号。(需要注意的是在清空ADDR和发送Stop的时候要关闭中断,确保正确完成上述步骤)
else if (hi2c->XferSize == 1U) // transfer size=1 byte
{
/* Disable Acknowledge */
CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_ACK); // ACK=0, 对应为发送NACK,ACK置零需要发生在接收Data1之前
/* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
software sequence must complete before the current byte end of transfer */
__disable_irq();
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Generate Stop */
SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP); // STOP信号生成,需要发生在接收Data1之前
/* Re-enable IRQs */
__enable_irq();
}
当hi2c->XferCount==2U
,对应接收2 byte。置位I2C_CR1→Bit11 POS
,清ADDR,置I2C_CR1→Bit10 ACK
为0。此处不需要发送Stop信号,因为Stop是在EV7_3事件中发送,此处仅包含EV6和EV6_1事件。
else if (hi2c->XferSize == 2U)
{
/* Enable Pos */
SET_BIT(hi2c->Instance->CR1, I2C_CR1_POS); // POS对应的为接收二个字节时,在接收DATA1之前拉低ACK,则在接收DATA2后会发送一个NACK
/* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
software sequence must complete before the current byte end of transfer */
__disable_irq();
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
/* Disable Acknowledge */
CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_ACK);
/* Re-enable IRQs */
__enable_irq();
}
当hi2c->XferCount>2U
,对应为接收大于2个byte的情况,此时无论是接收3个byte或是大于3个byte,首先都是EV6 事件,即清空ADDR,并且成功接收下一个字节时回传ACK。
else
{
/* Enable Acknowledge */
SET_BIT(hi2c->Instance->CR1, I2C_CR1_ACK);
/* Clear ADDR flag */
__HAL_I2C_CLEAR_ADDRFLAG(hi2c);
}
后续继续通过一个while循环持续地判断hi2c->XferCoun
t的个数,当hi2c->XferCount
大于零,则一直运行该循环,在循环内首先判断:
hi2c->XferCount <= 3U
hi2c->XferCount == 1U
,则继续N=1 ⇒ 1 byte的后续流程,即EV7,读DR寄存器获得接收的1个byte
/* One byte */
if (hi2c->XferSize == 1U)
{
/* Wait until RXNE flag is set */
if (I2C_WaitOnRXNEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Read data from DR */
*hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR;
/* Increment Buffer pointer */
hi2c->pBuffPtr++;
/* Update counter */
hi2c->XferSize--;
hi2c->XferCount--;
}
hi2c->XferCount == 2U
,则继续N=2 ⇒ 2 byte的后续流程,即EV7_3,故在该部分代码中,首先判断I2C_SR1→Bit2 BTF
是否拉高为1,当拉高为1,代表此时DR和DSR分别存有Data1和Data2;则首先发送Stop信号,然后连续读取两次DR,读出所有的数据。(此处关中断其实只需要确保Stop信号能在读取两次DR之前正确发送,否则若发送Stop信号被中断打断,导致未能正确发送Stop信号,可能会导致出错)
/* Two bytes */
else if (hi2c->XferSize == 2U)
{
/* Wait until BTF flag is set */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BTF, RESET, Timeout, tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
software sequence must complete before the current byte end of transfer */
__disable_irq();
/* Generate Stop */
SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);
/* Read data from DR */
*hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; // 第一次读取
/* Increment Buffer pointer */
hi2c->pBuffPtr++;
/* Update counter */
hi2c->XferSize--;
hi2c->XferCount--;
/* Re-enable IRQs */
__enable_irq();
/* Read data from DR */
*hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; // 第二次读取
/* Increment Buffer pointer */
hi2c->pBuffPtr++;
/* Update counter */
hi2c->XferSize--;
hi2c->XferCount--;
}
hi2c->XferCount == 3U
,则继续N>2 ⇒ >2 byte中读取最后3个byte的后续流程;首先不读取DR,等待I2C_SR1→Bit2 BTF
拉高为1,此时DataN-2位于DR,DataN-1位于DSR;当I2C_SR1→Bit2 BTF
拉高为1,进入EV7_2 事件,此时首先置I2C_CR1→Bit10 ACK
为0,因为后续接收DataN后需要回传NACK,然后读DR获得DataN-2,然后代码中还经过一个等待I2C_SR1→Bit2 BTF
拉高为1(?没看懂),发送停止信号,从DR中读取DataN-1;从DR中读取DataN
/* 3 Last bytes */
else
{
/* Wait until BTF flag is set */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_BTF, RESET, Timeout, tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Disable Acknowledge */
CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_ACK);
/* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
software sequence must complete before the current byte end of transfer */
__disable_irq();
/* Read data from DR */
*hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; // Read DataN
/* Increment Buffer pointer */
hi2c->pBuffPtr++;
/* Update counter */
hi2c->XferSize--;
hi2c->XferCount--;
/* Wait until BTF flag is set */ // 此处没看懂?
count = I2C_TIMEOUT_FLAG * (SystemCoreClock / 25U / 1000U);
do
{
count--;
if (count == 0U)
{
hi2c->PreviousState = I2C_STATE_NONE;
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT;
/* Re-enable IRQs */
__enable_irq();
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
}
}
while (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BTF) == RESET);
/* Generate Stop */
SET_BIT(hi2c->Instance->CR1, I2C_CR1_STOP);
/* Read data from DR */
*hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; // Read DataN-1
/* Increment Buffer pointer */
hi2c->pBuffPtr++;
/* Update counter */
hi2c->XferSize--;
hi2c->XferCount--;
/* Re-enable IRQs */
__enable_irq();
/* Read data from DR */
*hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR; // Read DataN
/* Increment Buffer pointer */
hi2c->pBuffPtr++;
/* Update counter */
hi2c->XferSize--;
hi2c->XferCount--;
}
hi2c->XferCount > 3U
I2C_SR1→Bit6 RXNE
是否拉高为1,若是,代表有数据被移入DR,则读取DR;
/* Wait until RXNE flag is set */
if (I2C_WaitOnRXNEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Read data from DR */
*hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR;
/* Increment Buffer pointer */
hi2c->pBuffPtr++;
/* Update counter */
hi2c->XferSize--;
hi2c->XferCount--;
I2C_SR1→Bit2 BTF
拉高为1,此时SCL被拉低,暂停从从机接收数据;接着会判断此时hi2c->XferCount
hi2c->XferCount == 3U
则对应回到了前文中N>2 ⇒ >2 byte且只剩最后3个byte待读取的情况,则会首先置I2C_CR1→Bit10 ACK
为0,并读取DR获得DataN-2;当下一次重新进入循环,则会进入前文while循环中的hi2c->XferCoun == 2U
,由于此时当读取DataN-2后,DataN-1会进入DR,并且释放SCL,DataN自动进入DSR,故此时I2C_SR1→Bit2 BTF
仍为1,故对应为EV7_3事件,首先发送Stop信号,然后读取两次DR获得DataN-1和DataN,完成读取。hi2c->XferCount != 3U
,实际就为hi2c->XferCount > 3U
;此时非最后3个byte的情景,就正常的读取DR中的数据获得Data;后续理论上就是DR和DSR一直有数据,故需要一直判断I2C_SR1→Bit2 BTF
,然后读取DR,直到剩余3个byte。
else // > 3bytes
{
/* Wait until RXNE flag is set */
if (I2C_WaitOnRXNEFlagUntilTimeout(hi2c, Timeout, tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Read data from DR */
*hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR;
/* Increment Buffer pointer */
hi2c->pBuffPtr++;
/* Update counter */
hi2c->XferSize--;
hi2c->XferCount--;
if (__HAL_I2C_GET_FLAG(hi2c, I2C_FLAG_BTF) == SET)
{
if (hi2c->XferSize == 3U) // 此处if走完,就相当于N=2的情景
{
/* Disable Acknowledge */
CLEAR_BIT(hi2c->Instance->CR1, I2C_CR1_ACK);
}
/* Read data from DR */
*hi2c->pBuffPtr = (uint8_t)hi2c->Instance->DR;
/* Increment Buffer pointer */
hi2c->pBuffPtr++;
/* Update counter */
hi2c->XferSize--;
hi2c->XferCount--;
}
}
作者:康