The model is definitely bugged.
As in the datasheet, I2C SR2 MSL bit goes to 0 (Slave mode) when a stop is detected.
I have some code using that flag, it worked perfectly in real hardware.
MSL never goes back to 0 in Proteus.
Just use LL. Does exactly what you would do by hand. No overhead, no bloat. Just skip rewriting the wheel.
This is my function:
ErrorStatus I2C1_MasterTransmit(uint8_t DevAddress, uint8_t *pData, uint16_t Size)
{
ErrorStatus status = SUCCESS;
LL_I2C_GenerateStartCondition(I2C1);
while(LL_I2C_IsActiveFlag_SB(I2C1) != 1);
LL_I2C_TransmitData8(I2C1, DevAddress);
while(!LL_I2C_IsActiveFlag_ADDR(I2C1) || !LL_I2C_IsActiveFlag_TXE(I2C1)){
if(LL_I2C_IsActiveFlag_AF(I2C1))
break;
}
LL_I2C_ClearFlag_ADDR(I2C1);
if(LL_I2C_IsActiveFlag_AF(I2C1)){
status = ERROR;
LL_I2C_ClearFlag_AF(I2C1);
}
else{
while(Size--)
{
LL_I2C_TransmitData8(I2C1, *pData++);
while(!LL_I2C_IsActiveFlag_TXE(I2C1));
if(LL_I2C_IsActiveFlag_AF(I2C1)){
status = ERROR;
break;
}
}
}
while(!LL_I2C_IsActiveFlag_BTF(I2C1));
LL_I2C_GenerateStopCondition(I2C1);
while(LL_I2C_IsActiveFlag_MSL(I2C1));
return status;
}
__STATIC_INLINE void LL_I2C_GenerateStartCondition(I2C_TypeDef *I2Cx)
{
SET_BIT(I2Cx->CR1, I2C_CR1_START);
}
__STATIC_INLINE void LL_I2C_GenerateStopCondition(I2C_TypeDef *I2Cx)
{
SET_BIT(I2Cx->CR1, I2C_CR1_STOP);
}
__STATIC_INLINE void LL_I2C_TransmitData8(I2C_TypeDef *I2Cx, uint8_t Data)
{
MODIFY_REG(I2Cx->DR, I2C_DR_DR, Data);
}
__STATIC_INLINE uint32_t LL_I2C_IsActiveFlag_ADDR(I2C_TypeDef *I2Cx)
{
return (READ_BIT(I2Cx->SR1, I2C_SR1_ADDR) == (I2C_SR1_ADDR));
}
And so on. That's LL. Inlining avoids any slow calls. Simple functions and calls, nothing convoluted.