STM8S I2C Slave模式錯誤解決

ZHONGCAI0901發表於2020-10-20

1.問題背景

STM8S平臺使用I2C Slave模式,參考STM8S的標準庫的Sample Code,但是隻要Master Controller Read/Write多個Bytes時,就會存在中斷一直被觸發的問題。因為中斷對應的I2C State Register沒有被正確的清除,所以中斷一直被觸發導致I2C通訊失敗。

參考STM8S的標準庫的Sample Code路徑如下:
STM8S_StdPeriph_Lib\Project\STM8S_StdPeriph_Examples\I2C\I2C_TwoBoards\I2C_DataExchange\Slave\

2.I2C Slave模式

下面是STM8S I2C Slave模式的參考程式碼,我這邊驗證目前沒有遇到問題。

#define I2C_DATA_SIZE  8

#if ((I2C_DATA_SIZE > 0) && ((I2C_DATA_SIZE & (I2C_DATA_SIZE - 1)) != 0))
#error I2C_DATA_SIZE isnt a power of 2.
#endif


#define uint8_bits_def(x) union \
{\
	unsigned char x; \
	struct \
	{\
		unsigned char x##_Bit0:1, \
					  x##_Bit1:1, \
					  x##_Bit2:1, \
					  x##_Bit3:1, \
					  x##_Bit4:1, \
					  x##_Bit5:1, \
					  x##_Bit6:1, \
					  x##_Bit7:1; \
	};\
}

uint8_bits_def(tagI2C);
uint8_t i2c_addr = 0;
uint8_t i2c_data[I2C_DATA_SIZE] = {0x00};

void I2C_Slave_IRQHandler(void)
{
	volatile I2C_Event_TypeDef Event = I2C_GetLastEvent();

	/* Slave address matched with the address sent by master */
	/* EV1: ADDR = 1, 順序讀SR1,SR3暫存器清除此標誌位 */
	if(Event == (I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED))
	{
		tagI2C_Bit0 = 1;
	}
	/* Slave received the data byte from master */
	/* EV2: RxNE = 1, 讀DR暫存器清除此位 */
	else if(Event == (I2C_EVENT_SLAVE_BYTE_RECEIVED))
	{
		uint8_t ch = I2C_ReceiveData();
		if(tagI2C_Bit0){
			tagI2C_Bit0 = 0;
			i2c_addr = ch;
		}else{
			i2c_data[i2c_addr++ & (I2C_DATA_SIZE - 1)] = ch;
		}
	}
	/* Slave received STOP byte from master */
	/* EV4: STOPF = 1,讀SR1暫存器後再寫CR2清除 */
	else if(Event == (I2C_EVENT_SLAVE_STOP_DETECTED))
	{
		I2C_GetFlagStatus(I2C_FLAG_STOPDETECTION);
		I2C_GenerateSTOP(DISABLE);
	}
	/* SLAVE TRANSMITTER */
	/* Data transmited from slave to master */
	/* EV3: TxE = 1, 寫DR暫存器清除DR */
	else if((Event == (I2C_EVENT_SLAVE_BYTE_TRANSMITTED)) ||
		     (Event == (I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED)))
	{
		uint8_t ch = i2c_data[i2c_addr++ & (I2C_DATA_SIZE - 1)];
		I2C_SendData(ch);
	}
	/* Acknowledge failure */
	/* EV3-2: AF = 1, AF is cleared by writing '0' in AF bit of SR2 register */
	else if(Event == (I2C_EVENT_SLAVE_ACK_FAILURE))
	{
		I2C_ClearFlag(I2C_FLAG_ACKNOWLEDGEFAILURE);
	}
}


void I2C_Slave_Init(void)
{
	I2C_DeInit();

	/* Initialize I2C peripheral */
	I2C_Init(400000, SLAVE_ADDRESS, I2C_DUTYCYCLE_2, I2C_ACK_CURR, I2C_ADDMODE_7BIT, 16);
	
	/* Enable Error Interrupt*/
	I2C_ITConfig((I2C_IT_TypeDef)(I2C_IT_ERR | I2C_IT_EVT | I2C_IT_BUF), ENABLE);
}

【備註】:I2C_Slave_IRQHandler是在stm8s_it.c檔案中的INTERRUPT_HANDLER(I2C_IRQHandler, 19)中被呼叫的。

3.分析驗證

為了方便驗證問題,在I2C_Slave_IRQHandler函式中儲存了所有的Event,如下:

+uint16_t event_rx_buf[50] = {0x00};
+uint16_t event_count = 0;

void I2C_Slave_IRQHandler(void)
{
	volatile I2C_Event_TypeDef Event = I2C_GetLastEvent();

+	if(event_count < 50){
+		event_rx_buf[event_count++] = Event;
+	}

	...
}
  • Master端write 2個byte產生的Event,如下:
    在這裡插入圖片描述

    【備註】上面截圖出現了3次0x240,因為是1個Register addr + 2個data,所以其實是write3個資料。

  • Master端read 2個byte產生的Event,如下:
    在這裡插入圖片描述

    【備註】0x680是在傳送資料中,中斷函式中不做任何處理,等待傳送完成。

相關文章