@[toc] lib_i2c_simulation
/*
* @Author: Haiyichen
* @Date: 2023-09-21 16:16:16
* @LastEditors: Haiyichen
* @LastEditTime: 2023-10-31 18:01:10
* @Description: Personal notes of i2c-simulation
*/
除了文字解释,有用wavedrom简单画了一些各种信号的电平变化过程,但需要支持MPE才能看到,虽然CSDN上的在线编辑器无法渲染出图,但还是放出来了,如果有条件可以在自己的VSCode中配置MPE(markdown preview enhenced)
{signal:[
{ name:'SCL', wave:'p.Pp'},
{ name:'data', wave:"x.=x",
data:["Start" ]},
{ name:'SDA', wave:"1.0"}
]}
{signal:[
{ name:'SCL', wave:'p.PPp'},
{ name:'data', wave:"x.=x",
data:["Stop" ]},
{ name:'SDA', wave:"0.10"}
]}
{signal:[
{ name:'SCL', wave:'p.Pp..|Pp'},
{ name:'data', wave:"x.==..|=x",
data:["Start", "SlaveAddress","Ack"]},
{ name:'SDA', wave:"0.10.1|0."}
]}
{signal:[
{ name:'SCL', wave:'p.Pp..|Pp'},
{ name:'data', wave:"x.==..|=x",
data:["Start", "SlaveAddress","Nack"]},
{ name:'SDA', wave:"0.10.0|1."}
]}
/* IO definition of i2c simulation */
#define I2C_SIMULATION_GPIO_PORT GPIOA
#define I2C_SIMULATION_SCL_PIN GPIO_PINS_0
#define I2C_SIMULATION_SDA_PIN GPIO_PINS_1
/* driver definition of i2c simulation for M1 */
#define I2C_SDA_H() I2C_SIMULATION_GPIO_PORT->scr = I2C_SIMULATION_SDA_PIN;
#define I2C_SDA_L() I2C_SIMULATION_GPIO_PORT->clr = I2C_SIMULATION_SDA_PIN;
#define I2C_SCL_H() I2C_SIMULATION_GPIO_PORT->scr = I2C_SIMULATION_SCL_PIN;
#define I2C_SCL_L() I2C_SIMULATION_GPIO_PORT->clr = I2C_SIMULATION_SCL_PIN;
关于SDA引脚是配置成开漏还是推挽,
//需要根据主控MCU频率和i的取值,调整i2c_Delay的时长,进而调整SCL的脉宽。(也受编译器“优化等级”影响)
/**
* @name i2c_Delay
* @brief soft delay for i2c clock
* @param none
* @retval none
*/
static void i2c_Delay(void)
{
uint8_t i;
/*
*AT32F425F6P7,
*i = 100,SCL = 163.4KHZ,6.1us
*i = 75, SCL = 243.9KHZ,4.1us
*i = 50, SCL = 312.5kHZ,3.2us
*/
for(i=0;i<100;i++);
}
/**
* @name hw_i2c_START
* @brief start signal for i2c simulation
* @param none
* @retval none
*/
void hw_i2c_START(void)
{
I2C_SDA_H();
I2C_SCL_H();
i2c_Delay();
I2C_SDA_L();
i2c_Delay();
I2C_SCL_L();
i2c_Delay();
}
/**
* @name hw_i2c_ACK
* @brief ACK signal for i2c simulation
* @param none
* @retval none
*/
void hw_i2c_ACK(void)
{
I2C_SDA_L();
i2c_Delay();
I2C_SCL_H();
i2c_Delay();
I2C_SCL_L();
i2c_Delay();
I2C_SDA_H();
}
/**
* @name hw_i2c_WaitAck
* @brief Wait ACK signal for i2c simulation
* @param none
* @retval uint8_t tempRe:Get slave ack signal or not
*/
uint8_t hw_i2c_WaitAck(void)
{
uint8_t tempRe;
I2C_SDA_H(); //MCU(master) set SDA High
i2c_Delay();
I2C_SCL_H(); //MCU(master) send a new SCL signal, slave device should return an Ack signal
i2c_Delay();
tempRe = I2C_SDA_READ(); //MCU(master) read SDA state(1 or 0)
I2C_SCL_L();
i2c_Delay();
return tempRe;
}
/**
* @name hw_i2c_NACK
* @brief Send NACK signal to slave for i2c simulation
* @param none
* @retval none
*/
void hw_i2c_NACK(void)
{
I2C_SDA_L();
I2C_SCL_H();
i2c_Delay();
I2C_SDA_H();
}
/**
* @name hw_i2c_STOP
* @brief Send STOP signal to slave for i2c simulation
* @param none
* @retval none
*/
void hw_i2c_STOP(void)
{
I2C_SDA_L();
I2C_SCL_H();
i2c_Delay();
I2C_SDA_H();
i2c_Delay();
}
/**
* @name lib_i2c_SendByte
* @brief Send Byte from master by simulation of i2c
* @param DataByte:data
* @retval none
*/
void lib_i2c_SendByte(uint8_t DataByte)
{
uint8_t i;
for ( i = 0; i < 8; i++)
{
if (DataByte & 0x80)
{
I2C_SDA_H();
}
else
{
I2C_SDA_L();
}
i2c_Delay();
I2C_SCL_H();
i2c_Delay();
I2C_SCL_L();
if (i == 7)
{
I2C_SDA_H(); //MCU(master) set SDA high
}
DataByte <<= 1;
i2c_Delay();
}
}
/**
* @name lib_i2c_ReadByte
* @brief Read Byte from slave device by simulation of i2c
* @param none
* @retval tempData:data
*/
uint8_t lib_i2c_ReadByte(void)
{
uint8_t i;
uint8_t tempData = 0;
uint8_t tempRe = 0;
for(i = 0; i < 8; i++)
{
tempData <<=1;
I2C_SCL_H();
i2c_Delay();
tempRe = I2C_SDA_READ();
if(tempRe)
{
tempData++;
}
I2C_SCL_L();
i2c_Delay();
}
return tempData;
}
/**
* @name lib_i2c_ReadMutiBytes
* @brief Read Muti Bytes data from slave device
* @param slave_address
* @param reg_address
* @param pdatabuf
* @param len
* @retval tempRe:whether read data successfully or not
*/
uint8_t lib_i2c_ReadMutiBytes(uint8_t slave_address, uint8_t reg_address, uint8_t* pdatabuf, uint8_t len)
{
uint8_t tempData;
uint8_t tempRe = 0;
uint8_t cnt = 0;
uint8_t tempaddr_W = slave_address<<1;
uint8_t tempaddr_R = tempaddr_W + 1;
do
{
/* 1st:i2c start signal */
hw_i2c_START();
/* 2nd:write slave device address, bit0 is a read-write control bit, 0 for write, and 1 for read */
lib_i2c_SendByte(tempaddr_W);
/* 3rd:wait ack from slave device */
tempRe = hw_i2c_WaitAck();
if (tempRe){ // the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ack
break;
}
/* 4th:send target register address */
lib_i2c_SendByte(reg_address);
/* 5th:wait Ack from slave device */s
tempRe = hw_i2c_WaitAck();
if (tempRe){ // the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ack
break;
}
/* 6th:send a start signal to reset i2c bus, and then start to read data from slave device */
hw_i2c_START();
/* 7th:Send a read command(bit0) for the slave address */
lib_i2c_SendByte(tempaddr_R);
/* 8th:wait Ack from slave device */
tempRe = hw_i2c_WaitAck();
if (tempRe){ // the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ack
break;
}
//9th:read data by loop
for(cnt = 0; cnt < len; cnt++)
{
pdatabuf[cnt] = lib_i2c_ReadByte(); //read 1 byte
/* After reading each byte, an Ack needs to be sent, except for the last byte, which requires a Nack */
if(cnt != len - 1)
{
/* After the middle byte is read, the CPU generates the ACK signal (Drive SDA = 0) */
hw_i2c_ACK();
}
else
{
/* After reading the last byte, the CPU generates the NACK signal (drive SDA = 1) */
hw_i2c_NACK();
}
}
}while(0);
/* Send I2C bus stop signal */
hw_i2c_STOP();
if(tempRe){
tempRe = 0;
}else{
tempRe = 1;
}
return tempRe;
}
/**
* @brief write single byte through the i2c1(For M1) by sofyware simulation.
* @param slave_address: address of tagert slave device
* @param reg_address: address of tagert register
* @param pdata: pointer to the data
* @retval tempRe: Write data successfully or not
*/
uint8_t lib_i2c_WriteSingleByte(uint16_t slave_address, uint16_t reg_address, uint8_t pdata)
{
uint8_t tempRe = 0;
uint8_t tempData = pdata;
uint16_t tempaddr = slave_address<<1;
do
{
hw_i2c_START();
lib_i2c_SendByte(tempaddr);
tempRe = hw_i2c_WaitAck();
if (tempRe){ // the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ack
break;
}
lib_i2c_SendByte(reg_address);
tempRe = hw_i2c_WaitAck();
if (tempRe){ // the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ack
break;
}
lib_i2c_SendByte(tempData);
tempRe = hw_i2c_WaitAck();
if (tempRe){ // the return value is 1 ,that is to say SDA is not lowwed. the target ist8310 doesn't Ack
break;
}
hw_i2c_STOP();
} while (0);
return tempRe;
}
因为用的芯片硬件IIC的底层官方函数一直卡死跑不通,于是干脆自己整理了一套软件模拟IIC的相关流程和函数,已经在项目中顺利调通了,项目换芯片也经历过不同芯片的移植,也很方便。