IIC工作模式时序分析
此处利用IO口模拟IIC通信过程中的时序。
通信过程
在IIC通信过程SDA存在两种模式(接收模式和发送模式),发送或接受一个字节(器件的7个bit+1个bit方向(1 - 读方向,0 - 写方向))
模式配置
- 当SDA为接入模式接收了1字节数据后在第九个时钟脉冲期间就要变成输出模式发送ask
- 当SDA为输出模式发送了1字节数据后在第九个时钟脉冲期间就要变成输入模式检测是否收到ask
- 因此SDA需配置两个模式而SCL模拟时序只需配置输出模式
#define SDA_SET(n) (n) ? GPIO_SetBits(GPIO?,GPIO_Pin_?) : GPIO_ResetBits(GPIO?,GPIO_Pin_?)
#define SCL_SET(n) (n) ? GPIO_SetBits(GPIO?,GPIO_Pin_?) : GPIO_ResetBits(GPIO?,GPIO_Pin_?)
#define SDA_READ GPIO_ReadInputDataBit(GPIO?,GPIO_Pin_?)
IIC的SCL配置
/*** @name: IIC_SCLConfig* @brief IIC的SCL配置* @param void* @retval void* @date 2024/09/10* @note 补充 注意 说明*/
void IIC_SCLConfig()
{GPIO_InitTypeDef GPIO_InitStructure;//打开GPIO端口的时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIO?,ENABLE);//SCLGPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //输出模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_?;GPIO_Init(GPIO?, &GPIO_InitStructure);
}
IIC的SDA配置
/*** @name: IIC_SDAConfig* @brief IIC的SCL配置* @param @GPIO_Mode* @retval void* @date 2024/09/10* @note 补充 注意 说明*/
void IIC_SDAConfig(GPIOMode_TypeDef GPIO_Mode)
{GPIO_InitTypeDef GPIO_InitStructure;//打开GPIO端口的时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIO?,ENABLE);//SCLGPIO_InitStructure.GPIO_Mode = GPIO_Mode; //输出模式GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_?;GPIO_Init(GPIO?, &GPIO_InitStructure);
}
IIC的空闲状态
/*** @name: IIC_Config* @brief IIC的初始化* @param @GPIO_Mode* @retval void* @date 2024/09/10* @note 补充 注意 说明*/
void IIC_Config(void)
{//1.设置SDA和SCL为输出模式IIC_SCLConfig();IIC_SDAConfig(GPIO_Mode_OUT);//2.确保SDA和SCL处于空闲状态SDA_SET(1);SCL_SET(1);delay_us(5);
}
IIC的开始信号
/*** @name: IIC_Start* @brief IIC的开始信号* @param void* @retval void* @date 2024/09/10* @note 补充 注意 说明*/
void IIC_Start(void)
{//1.设置SDA为输出模式IIC_SDAConfig(GPIO_Mode_OUT);//2.确保SDA和SCL处于空闲状态SDA_SET(1);SCL_SET(1);delay_us(5);//3.设置SDA为低电平SDA_SET(0);delay_us(5);
}
IIC的发送字节
/*** @name: IIC_SendByte* @brief IIC的发送字节* @param @Byte* @retval void* @date 2024/09/10* @note 补充 注意 说明*/
void IIC_SendByte(uint8_t Byte) //1011 0110
{//1.设置SDA为输出模式IIC_SDAConfig(GPIO_Mode_OUT);//2.设置SDA为低电平,SCL为低电平SDA_SET(0);delay_us(5);SCL_SET(0);delay_us(5);//2.循环发送8次,每次发送一个bit 遵循MSB 高位先出for(int i=0;i<8;i++){SDA_SET(1);delay_us(5);//3.判断待发送的字节的最高位 ???? ???? & 1000 0000if( Byte & 0x80 ){SDA_SET(1);}elseSDA_SET(0);Byte <<= 1;delay_us(5);SDA_SET(0);delay_us(5);}
}
IIC的接收字节
/*** @name: IIC_RecvByte* @brief IIC的接受字节* @param void* @retval uint8_t * @date 2024/09/10* @note 补充 注意 说明*/
uint8_t IIC_RecvByte(void)
{uint8_t i=0;uint8_t data =0;//保证SDA引脚为输入模式IIC_SDAConfig(GPIO_Mode_IN);SCL_SET(0);delay_us(5);for(i=0;i<8;i++){//时钟线拉高,数据有效SCL_SET(1);delay_us(5);data <<=1;data |= SDA_READ;delay_us(5);//时钟线拉低,保持占用总线,总线是忙状态SCL_SET(0);delay_us(5);}
}
IIC的判断从机是否应答
/*** @name: IIC_IsSlaveACK* @brief IIC的判断从机是否应答* @param void* @retval bool* @date 2024/09/10* @note 补充 注意 说明*/
bool IIC_IsSlaveACK(void)
{bool ack;//1.设置SDA引脚为输入模式IIC_SDAConfig(GPIO_Mode_IN);//3.把SCL引脚电平拉高,此时为第9个脉冲的高电平,主机读取状态SCL_SET(1);delay_us(5);//4.主机读取状态 1 表示未应答 0 表示已应答if(SDA_READ)ack = false;elseack = true;//5.把SCL引脚电平拉低SCL_SET(0);delay_us(5);return ack;
}
IIC的收到从机数据发送应答
/*** @name: IIC_MasterACK* @brief IIC的收到从机数据发送应答* @param void* @retval void* @date 2024/09/10* @note 补充 注意 说明*/
void IIC_MasterACK(void)
{// 1.设置SDA引脚为输出模式IIC_SDAConfig(GPIO_Mode_OUT);// 2.把SCL引脚电平拉低,此时主机准备SCL_SET(0);delay_us(5);// 3.设置SDA为高电平,表示应答SDA_SET(1);delay_us(5);// 4.把SCL电平拉高,此时从机读取bitSCL_SET(1);delay_us(5);
}
IIC的停止信号
/*** @name: IIC_Stop* @brief IIC的停止信号* @param void* @retval void* @date 2024/09/10* @note 补充 注意 说明*/
void IIC_Stop(void)
{//1.设置SDA引脚为输出模式IIC_SDAConfig(GPIO_Mode_OUT);//2.设置SDA和SCL均为低电平SDA_SET(0);SCL_SET(0);delay_us(5);//3.把SCL电平拉高SCL_SET(1);delay_us(5);//4.把SDA电平拉高SDA_SET(1);delay_us(5);}