cancel
Showing results for 
Search instead for 
Did you mean: 

STM8L151G6' I2C: simple and robust communication needed

Alex T_2
Associate
Posted on June 20, 2017 at 21:29

Hello,

My apologize for poor English

I'm working on the'simple' (as I thought) task: reading thermo sensor's indication over I2C interface. The procedure is:

  • send command, 1 byte
  • read sensor's answer, 3 bytes

This first part of task works fine, until I try to manage its second part: make this communication 110% robust (make it work at ALL bus' conditions). I mean that 100% recovery from ANY state is needed.

The device is designed to work in very bad environment, frequent electrical noise, so the stress-test is to, for example, pull SDA line to VDD many times. The recovery procedure, in almost all cases, is to reset STM8's I2C peripheral, as I thought it should give me guaranteed switch to the known state. Later, it was expanded to do status registers reset sequence, data register readout and STOP condition generation first. But nope. It works, say, nine times of ten, but then stucks in some erroneous condition which I can't cure no matter what do I try. In that condition device's status registers (SR1,2,3) remain all zeroes forever. SDA and SCL lines are both at HI level, so I assume the master and the slave devices are idle. But there is no any change after the master generates START condition.

I've searched I2C related topics here, and found one probably useful hint in some of answers (for example

https://community.st.com/thread/39124-handle-i2c-bus-error

:( generate 9 STOP bits to flush slave's receiving sequence. But, even IF it is the case, it should mean only that the slave doesn't acks master's request, NOT that the master itself can't generate START condition and set therefore SB bit in status. Am I right? But of course I will try this hint once get to devices tomorrow.

So the question remains: how to switch (100% guaranteed) I2C peripheral to the known state from any other?

Here's what I try to do in code (last revision):

void reset_peripheral()
{
I2C1->SR2 = 0x00;
(void)I2C1->SR1;
I2C1->CR2 |= I2C_CR2_STOP; // STOP: stop generation
/*10 nops delay here */
(void)I2C1->DR; (void)I2C1->DR; // data register readout
I2C1->CR2 = 0x00;
I2C1->CR1 = 0x00; // clear PE and the entire control reg
DISABLE_CLOCK();
/* 100 nops delay here */
init_i2c();
ENABLE_CLOCK();
I2C1->CR1 |= (1<<0); // PE (periph enable)
}
uint16_t _v2_request_temp()
{
uint16_t S = 0xFFFF;
uint8_t s2;
uint16_t rval = 0;
uint8_t cmdSent = 0;
uint8_t buf[3], bufCtr = 0;
ENABLE_CLOCK();
I2C1->CR1 |= I2C_CR1_PE; // PE: peripheral enable
I2C1->CR2 |= I2C_CR2_ACK; // ACK: acknowledge enable
do
{
switch ( S )
{
case 0xFFFF: // just started
I2C1->CR2 |= I2C_CR2_START; // START
break;
case I2C_EVENT_MASTER_MODE_SELECT:
if ( cmdSent ) {
I2C1->DR = Si7050_addr | 0x01; // master-receiver
} else {
I2C1->DR = Si7050_addr | 0x00; // master-transmitter
cmdSent = 1;
}
break;
case I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED:
I2C1->DR = 0xE3; // t-sensor command: start measure
break;
case I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED:
bufCtr = 0;
break;
case I2C_EVENT_MASTER_BYTE_TRANSMITTING:
/* ... */
break;
case I2C_EVENT_MASTER_BYTE_TRANSMITTED:
I2C1->CR2 |= I2C_CR2_STOP;
while( I2C1->CR2 & I2C_CR2_STOP ) {}
I2C1->CR2 |= I2C_CR2_START; // START -> proceed to master-receiver
break;
case I2C_EVENT_MASTER_BYTE_RECEIVED | I2C_SR1_BTF: // both data and shift registers are full
if ( bufCtr == 0 ) {
I2C1->CR2 &= ~I2C_CR2_ACK; // ACK: acknowledge disable
buf[ bufCtr++ ] = I2C1->DR;
} else {
I2C1->CR2 |= I2C_CR2_STOP; // STOP: stop generation
buf[ bufCtr++ ] = I2C1->DR;
buf[ bufCtr++ ] = I2C1->DR;
uint8_t rcrc = buf[2]; // remote crc
uint8_t lcrc = calc_crc( buf, 2 ); // local crc
//!!!if ( rcrc == lcrc ) {
rval = ((uint16_t)buf[0] << 8) | ((uint16_t)buf[1]);
//}
}
break;
// ERRORS
case 0x0000: // this is the branch, which had to cure topic's problem, but doesn't...
reset_peripheral();
break;
case I2C_SR3_BUSY << 8:
case (I2C_SR3_BUSY << 8) | (I2C_SR3_TRA << 8) | (I2C_SR3_MSL << 8):
I2C1->CR2 |= I2C_CR2_SWRST;
asm('nop');asm('nop');asm('nop');asm('nop');asm('nop');
I2C1->CR2 &= ~I2C_CR2_SWRST;
rval = 0x0003;
break;
}
asm('nop');asm('nop');asm('nop');asm('nop');asm('nop');
s2 = I2C1->SR2; S = I2C1->SR1; S |= (uint16_t)I2C1->SR3 << 8;
} while ( !s2 && rval == 0x0000 );
if ( s2 ) {
reset_peripheral();
}
return rval;
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

https://community.st.com/tags#/?tags=i2c%20stm8

https://community.st.com/tags#/?tags=i2c%20stuck

‌ #i2c-stuck #i2c-stm8
0 REPLIES 0