stm32 smbus 上不可靠的 PEC

Unreliable PEC on stm32 smbus

我在 STM32 SMBUS 上遇到 PEC 问题,我用它从 MLX90614 IR 温度计读取数据。当我启动设备时,即使来自 IR 温度计的数据似乎是正确的,对于所有传输,PECERR 标志都会设置并继续设置。或者 PECERR 从未设置,红外温度计的所有数据仍然正确。

当我在示波器上研究数据时,我看不出设置或不设置 PECERR 时信号之间没有区别。如前所述,无论哪种方式,数据似乎都不错。

我当然可以忽略 PECERR 标志,但我希望能够过滤掉任何最终的乱码传输。有人知道我在这里做错了什么吗?

void I2C_SMBUS_Initialize(I2C_TypeDef *h) {
h->CR2 &= ~I2C_CR2_FREQ;                // Clear frequency part of register
h->CR2 |= 0x8;                          // Clock speed in Mhz
h->OAR1 = 0x4000;

h->CCR = 0;
h->CCR &= ~I2C_CCR_DUTY;
h->CCR |= 0x190;

h->TRISE &= ~I2C_TRISE_TRISE;           // Clear TRISE bits
h->TRISE |= 0x9;                        // Set TRISE

h->CR1 |= I2C_CR1_ENPEC;                // Enable packet error check

h->CR1 |= I2C_CR1_SMBTYPE;              // SMBUS host
h->CR1 |= I2C_CR1_SMBUS;                // SMBUS Mode

h->CR1 |= I2C_CR1_PE;  // Start i2c

}

uint16_t I2C_SMBUS_ReadWord(I2C_TypeDef* h, uint8_t 设备地址, uint8_t 命令) {

static const uint16_t ERROR_CODE = 0x3BFF;
//static const uint8_t deviceAddress = 0x5A;
static const uint8_t timeout = 100;

uint16_t temp = 0;

h->CR1 &= ~I2C_CR1_POS;

// Generate start bit 
sendStartBit(h);

// Wait for start bit set
if (!waitFlag((&h->SR1), I2C_SR1_SB, BIT_SET, timeout)) {                                          
    DEBUG_PUTS("Timeout while waiting for start bit set");
    return ERROR_CODE;
}

// Address byte. 7 bit. Shifted one lefet
sendAddress(h, deviceAddress, I2C_WRITE);

// Wait for address bit set
if (!waitFlag((&h->SR1), I2C_SR1_ADDR, BIT_SET, timeout)) {                                        
    DEBUG_PUTS("Timeout while waiting for address bit set");
    return ERROR_CODE;
}

// Clear ADDR bit
clearAddressFlag(h);

sendData(h, command);

// wait for tx buffer empty
if (!waitFlag((&h->SR1), I2C_SR1_TXE, BIT_SET, timeout)) {
    DEBUG_PUTS("Timeout while waiting for buffer empty");
    return ERROR_CODE;
}   
                      
uint8_t length = 3;
uint8_t tmpBuffer[length+1];
memset(tmpBuffer, 0x00, 4);

// Enable automatic ACK generation
enableAutomaticACK(h);

// Generate start bit
sendStartBit(h);

// Wait for start bit set
if (!waitFlag((&h->SR1), I2C_SR1_SB, BIT_SET, timeout)) {                                          
    DEBUG_PUTS("Timeout while waiting for repeted start bit set");
    return ERROR_CODE;
}

// Send the read command to the slave address
sendAddress(h, deviceAddress, I2C_READ);
                               
// Wait for address bit set
if (!waitFlag((&h->SR1), I2C_SR1_ADDR, BIT_SET, timeout)) {
    DEBUG_PUTS("Timeout while waiting for address bit set");
    return ERROR_CODE;
}

// Clear ADDR bit by reading status register 
clearAddressFlag(h);

// Now we must read the data from the slave 
if (length > 2) {
    // Receive the first n-2 bytes
    for (uint8_t i=0; i < length-2; i++) {                                             

        // Wait for Byte Transfer ready
        if (!waitFlag((&h->SR1), I2C_SR1_BTF, BIT_RESET, timeout))  {                                
            DEBUG_PUTS("Timeout while waiting for Byte Transfer ready");
            return ERROR_CODE;
        } 

        tmpBuffer[i] = h->DR;                                                 
      
        // Wait for Byte Transfer Finished
        if (!waitFlag((&h->SR1), I2C_SR1_BTF, BIT_SET, timeout))  {                                
            DEBUG_PUTS("Timeout while waiting for Byte Transfer Finished");
            return ERROR_CODE;
        }
    }                                                                           

    // Wait for Byte Transfer ready
    if (!waitFlag((&h->SR1), I2C_SR1_BTF, BIT_RESET, timeout))  {                                
        DEBUG_PUTS("Timeout while waiting for Byte Transfer ready");
        return ERROR_CODE;
    } 

    // Disable automatic ACK generation
    disableAutomaticACK(h);

    // Read the second last byte 
    tmpBuffer[length-1] = h->DR;                                             

    // Send stop bit
    sendStopBit(h);

    // Enable packet error check
    h->CR1 |= I2C_CR1_PEC;

    // Read the last byte
    tmpBuffer[length] = h->DR;                                                    
    temp = tmpBuffer[3]*256 + tmpBuffer[2]; 

    uint8_t pec = h->SR2 &= I2C_SR2_PEC_Msk;

    if ((h->SR1 & I2C_SR1_PECERR) != 0) {
        puts("PEC ERROR");
    }
}
return temp;

}

发现错误。 PECERR 必须由软件清零。开机后第一次传输偶尔会失败

    if ((h->SR1 & I2C_SR1_PECERR) != 0) {
        DEBUG_PUTS("PEC ERROR");
        h->SR1 &= ~I2C_SR1_PECERR;
        return ERROR_CODE;
    }