#include "systick.h" #include "bitband.h" #include "gpio_iic.h" static GPIO_InitTypeDef GPIO_InitStructSDAOut; static GPIO_InitTypeDef GPIO_InitStructSDAIn; // 位段定义 #define IIC_SCL_OUT PBOut(6) #define IIC_SDA_OUT PBOut(7) #define IIC_SDA_IN PBIn(7) // 初始化 IIC 的IO 端口:SDA:PB7,SCL:PB6 void iic_init(void) { GPIO_InitTypeDef GPIO_InitStructSCL; // 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 初始化 PB6 为开漏输出 GPIO_InitStructSCL.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStructSCL.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructSCL.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructSCL); // 初始化 PB7 为开漏输出 GPIO_InitStructSDAOut.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_InitStructSDAOut.GPIO_Pin = GPIO_Pin_7; GPIO_InitStructSDAOut.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructSDAOut); // 设置 SCL和SDA 都为高电平 IIC_SCL_OUT = 1; IIC_SDA_OUT = 1; // 填充 PB7 为上拉输入状态的结构体(后续备用) GPIO_InitStructSDAIn.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructSDAIn.GPIO_Pin = GPIO_Pin_7; } // 设置 SDA 为输入模式 static void iic_set_sda_in(void) { GPIO_Init(GPIOB, &GPIO_InitStructSDAIn); } // 设置 SDA 为输出模式 static void iic_set_sda_out(void) { GPIO_Init(GPIOB, &GPIO_InitStructSDAOut); } // 主机发送起始信号 void iic_start(void) { iic_set_sda_out(); IIC_SDA_OUT = 1; IIC_SCL_OUT = 1; delay_us(2); IIC_SDA_OUT = 0; delay_us(2); IIC_SCL_OUT = 0; delay_us(1); } //主机发送停止信号 void iic_stop(void) { iic_set_sda_out(); IIC_SCL_OUT = 0; IIC_SDA_OUT = 0; delay_us(2); IIC_SCL_OUT = 1; delay_us(1); IIC_SDA_OUT = 1; delay_us(2); } // 主机发送一个字节 void iic_send_byte(u8 txd) { int bit; iic_set_sda_out(); for (bit = 7; bit >= 0; bit--) { // 拉低时钟线 IIC_SCL_OUT = 0; delay_us(1); // 变化数据线的数据 IIC_SDA_OUT = (txd >> bit) & 0x01; delay_us(1); IIC_SCL_OUT = 1; delay_us(2); } IIC_SCL_OUT = 0; // 发送完毕拉低时钟线。 } // 主机接收一个字节并回复ack,参数ack:1表示应答,0表示非应答。 u8 iic_recv_byte(u8 ack) { int bit; u8 value = 0; // 用于保存最终的返回值 iic_set_sda_in(); // 设置为输入模式 for (bit = 7; bit >= 0; bit--) { IIC_SCL_OUT = 0; // 拉低时钟线 delay_us(2); IIC_SCL_OUT = 1; delay_us(2); if (IIC_SDA_IN) { value |= (0x01 << bit); } } IIC_SCL_OUT = 0; // 接收8为数据,根据参数ack 回复应答或非应答。 if (ack) iic_send_ack(); else iic_send_nack(); return value; } // 主机等待从机,如果从机未响应(收到NACK),则发送停止 // 信号结束本次通信,非应答返回1(NACK),应答返回0,ACK); u8 iic_wait_ack(void) { u8 error_timeout = 0; IIC_SCL_OUT = 0; // 拉低时钟线 iic_set_sda_in(); // 设置为输入模式 delay_us(1); IIC_SCL_OUT = 1; // 拉高时钟线。 // 等待从机拉低的引号 while (IIC_SDA_IN) { error_timeout++; if (error_timeout > 250) { iic_stop(); // 发送停止信号,然后返回1 return 1; } } IIC_SCL_OUT = 0; // 拉低时钟线 return 0; } // 主机发送 ACK void iic_send_ack(void) { IIC_SCL_OUT = 0; // 拉低时钟线 iic_set_sda_out(); // 变为输出模式 IIC_SDA_OUT = 0; // 低电平为应答ACK delay_us(2); IIC_SCL_OUT = 1; // 拉高时钟线 delay_us(2); IIC_SCL_OUT = 0; } // 主机发送 NACK void iic_send_nack(void) { IIC_SCL_OUT = 0; // 拉低时钟线 iic_set_sda_out(); // 变为输出模式 IIC_SDA_OUT = 1; // 高电平为非应答NACK delay_us(2); IIC_SCL_OUT = 1; // 拉高时钟线 delay_us(2); IIC_SCL_OUT = 0; }