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;
}
我在 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;
}