Files
stm32-iot-sensor-node/mylib/gpio_iic.c
T
2026-10-19 15:47:18 +08:00

158 lines
4.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
}