/************************************************************
利用IO口模拟IIC时序,需要使用2个IO口(SDA和SCL)
SCL时钟线只能由主器件进行控制,所以SCL引脚必须为输出模式
SDA数据线,在主器件发送数据时,SDA引脚为输出模式
SDA数据线,在主器件接收数据时,SDA引脚为输入模式
************************************************************/
//配置SCL——》PB6,PB7的引脚设置
#define SDA_SET(n) (n)?(GPIO_SetBits(GPIOB,GPIO_Pin_7)):(GPIO_ResetBits(GPIOB,GPIO_Pin_7))
#define SCL_SET(n) (n)?(GPIO_SetBits(GPIOB,GPIO_Pin_6)) : (GPIO_ResetBits(GPIOB,GPIO_Pin_6))
#define SDA_READ GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_7)
void IIC_SCLConfig(void)
{
//打开GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure = {0};//定义结构体GPIO外设结构体变量
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //配置引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //推挽模式
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//配置IIC_Config初始化
void IIC_Config(void)
{
IIC_SCLConfig();
//开始期间SDA 与SCL都空闲状态是高电平
SDA_SET(1);
SCL_SET(1);
Delay_nus(5);
}
//IIC的开始信号
void IIC_Start(void)
{
//2.确保SDA与SCL空闲处于高电平
SDA_SET(1);
SCL_SET(1);
Delay_nus(5);
//3.SDA电平拉低
SDA_SET(0);
Delay_nus(5);
//4.把SCL引脚电平拉底此时,此时准备数据
SCL_SET(0);
Delay_nus(5);
}
//IIC的发送字节
//由于:开始-->发送字节-->等待应答-->发送字节所以第一步不能少
void IIC_SendByte(uint8_t Byte)
{
//2.把SCL引脚电平拉底此时,此时准备数据
SCL_SET(0);
Delay_nus(5);
//3.此时准备数据
for(uint8_t i = 0;i < 8;i++)
{
//4.判断最高位是0或者是1
if(Byte & 0x80)
{
SDA_SET(1);
}
else
{
SDA_SET(0);
}
Byte <<= 1;
Delay_nus(5);
//5.此时SCL引脚电平拉高,从机接收数据
SCL_SET(1);
Delay_nus(5);
//6.把SCL引脚电平拉底此时,此时准备数据
SCL_SET(0);
Delay_nus(5);
}
}
//判断是否从机应答
uint8_t IIC_SlaveACK(void)
{
uint8_t data = 0;
SDA_SET(1);//主机释放SDA,让从机对SDA进行控制
//2.把SCL引脚电平拉低,此时第九个脉冲的低电平,此时从机准备Bit
SCL_SET(0);
Delay_nus(5);
//3.把SCL引脚电平拉高,此时第九个脉冲高电平到来
SCL_SET(1);
Delay_nus(5);
//4.主机读取状态 1:表示未应答 0:表示已应答
if(SDA_READ)
{
data = 1;
}
else
{
data = 0;
}
//5.把SCL引脚电平拉低,此时第九个脉冲的结束
SCL_SET(0);
Delay_nus(5);
return data;
}
//IIC读取数据
uint8_t IIC_ReadData(void)
{
uint8_t data = 0;
SCL_SET(0);
//主机释放SDA,让从机对SDA进行控制
SDA_SET(1);
//3.主机循环八次读取数据
for(uint8_t i = 0;i < 8;i++)
{
//2.把SCL引脚拉低,此时从机准备数据
SCL_SET(0);
Delay_nus(5);
//4.把SCL电平拉高8bit,此时主机读取bit
SCL_SET(1);
Delay_nus(5);
//5.主机读取bit
data <<= 1;
data |= SDA_READ;
//Delay_nus(5);
}
// //6.把SCL引脚电平拉低,此时准备数据
// SCL_SET(0);
// Delay_nus(5);
return data;
}
//主机应答 ACK = 1 表示不应答 ACK = 0表示应答
void IIC_MasterACK(uint8_t Ack)
{
//2.把SCL引脚电平拉低,此时主机准备应答
SCL_SET(0);
Delay_nus(5);
//3.判断主机是否应答 1:表示未应答 0:表示已应答
if(Ack)
{
SDA_SET(1);
}
else
{
SDA_SET(0);
}
//4.把SCL引脚电平拉高,从机读取应答
SCL_SET(1);
Delay_nus(5);
//5.把SCL引脚电平拉低
SCL_SET(0);
Delay_nus(5);
}
//停止位
void IIC_Stop(void)
{
//1.SDA配置为输出模式
// IIC_SDAConfig(GPIO_Mode_Out_OD);
//2.确保SDA与SCL都为低电平
SDA_SET(0);
SCL_SET(0);
Delay_nus(5);
//3.SCL电平拉高
SCL_SET(1);
Delay_nus(5);
//4.SDA电平拉高
SDA_SET(1);
Delay_nus(5);
}