bsp_i2c_gpio.h
#ifndef _BSP_I2C_GPIO_H
#define _BSP_I2C_GPIO_H
#include "stm32f4xx.h"
#include <inttypes.h>
#define EEPROM_I2C_WR 0 /* 写控制bit */
#define EEPROM_I2C_RD 1 /* 读控制bit */
/* 定义I2C总线连接的GPIO端口,用户只需要修改下面四行代码即可任意改变SCL和SDA的引脚 */
#define EEPROM_I2C_GPIO_PORT GPIOB
#define EEPROM_I2C_GPIO_CLK RCC_AHB1Periph_GPIOB
#define EEPROM_I2C_SCL_PIN GPIO_Pin_6 /* 连接到SCL时钟线的GPIO */
#define EEPROM_I2C_SDA_PIN GPIO_Pin_7 /* 连接到SDA数据线的GPIO */
/* 定义读写SCL和SDA的宏,增加代码可读性和可移植性 */
#if 1 /* 条件编译:1 选择GPIO库函数实现IO读写 */
#define EEPROM_I2C_SCL_1() GPIO_SetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SCL_PIN) /* SCL = 1 */
#define EEPROM_I2C_SCL_0() GPIO_ResetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SCL_PIN) /* SCL = 0 */
#define EEPROM_I2C_SDA_1() GPIO_SetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN) /* SDA = 1 */
#define EEPROM_I2C_SDA_0() GPIO_ResetBits(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN) /* SDA = 0 */
#define EEPROM_I2C_SDA_READ() GPIO_ReadInputDataBit(EEPROM_I2C_GPIO_PORT, EEPROM_I2C_SDA_PIN) /* 读SDA口线状态 */
#else /* 这个分支选择直接寄存器操作实现IO读写 */
/*注意: 如下写法,在IAR最高级别优化时会被编译器错误优化 */
#define EEPROM_I2C_SCL_1() EEPROM_I2C_GPIO_PORT->BSRRL = EEPROM_I2C_SCL_PIN /* SCL = 1 */
#define EEPROM_I2C_SCL_0() EEPROM_I2C_GPIO_PORT->BSRRH = EEPROM_I2C_SCL_PIN /* SCL = 0 */
#define EEPROM_I2C_SDA_1() EEPROM_I2C_GPIO_PORT->BSRRL = EEPROM_I2C_SDA_PIN /* SDA = 1 */
#define EEPROM_I2C_SDA_0() EEPROM_I2C_GPIO_PORT->BSRRH = EEPROM_I2C_SDA_PIN /* SDA = 0 */
#define EEPROM_I2C_SDA_READ() ((EEPROM_I2C_GPIO_PORT->IDR & EEPROM_I2C_SDA_PIN) != 0) /* 读SDA口线状态 */
#endif
void i2c_Start(void);
void i2c_Stop(void);
void i2c_SendByte(uint8_t _ucByte);
uint8_t i2c_ReadByte(void);
uint8_t i2c_WaitAck(void);
void i2c_Ack(void);
void i2c_NAck(void);
uint8_t i2c_CheckDevice(uint8_t _Address);
#endif
bsp_i2c_gpio.c
/*
应用说明:
在访问I2C设备前,需要先调用i2c_CheckDevice()检测I2C设备是否正常,该函数会配置GPIO
*/
#include "./i2c/bsp_i2c_gpio.h"
static void i2c_CfgGpio(void);
/*
*********************************************************************************************************
* I2C总线位延迟,最快400KHz
*********************************************************************************************************
*/
static void i2c_Delay(void)
{
uint8_t i;
for (i = 0; i < 30; i++);
}
/*
*********************************************************************************************************
* CPU发起I2C总线启动信号
*********************************************************************************************************
*/
void i2c_Start(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C启动信号 */
EEPROM_I2C_SDA_1();
EEPROM_I2C_SCL_1();
i2c_Delay();
EEPROM_I2C_SDA_0();
i2c_Delay();
EEPROM_I2C_SCL_0();
i2c_Delay();
}
/*
*********************************************************************************************************
* CPU发起I2C总线停止信号
*********************************************************************************************************
*/
void i2c_Stop(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C停止信号 */
EEPROM_I2C_SDA_0();
EEPROM_I2C_SCL_1();
i2c_Delay();
EEPROM_I2C_SDA_1();
}
/*
*********************************************************************************************************
* CPU向I2C总线设备发送8bit数据
* _ucByte:等待发送的字节
*********************************************************************************************************
*/
void i2c_SendByte(uint8_t _ucByte)
{
uint8_t i;
/* ÏÈ·¢ËÍ×ֽڵĸßλbit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
EEPROM_I2C_SDA_1();
}
else
{
EEPROM_I2C_SDA_0();
}
i2c_Delay();
EEPROM_I2C_SCL_1();
i2c_Delay();
EEPROM_I2C_SCL_0();
if (i == 7)
{
EEPROM_I2C_SDA_1(); // 释放总线
}
_ucByte <<= 1; /* 左移一个bit */
i2c_Delay();
}
}
/*
*********************************************************************************************************
* CPU向I2C总线设备读写8bit数据
* 返回值为读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(void)
{
uint8_t i;
uint8_t value;
value = 0;
for (i = 0; i < 8; i++)
{
value <<= 1;
EEPROM_I2C_SCL_1();
i2c_Delay();
if (EEPROM_I2C_SDA_READ())
{
value++;
}
EEPROM_I2C_SCL_0();
i2c_Delay();
}
return value;
}
/*
*********************************************************************************************************
* CPU产生一个时钟,并读取器件的ACK应答信号
* 返回0表示正确应答,返回1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{
uint8_t re;
EEPROM_I2C_SDA_1(); /* CPU释放SDA总线 */
i2c_Delay();
EEPROM_I2C_SCL_1(); /* CPU驱动SCL = 1,此时器件会返回ACK应答 */
i2c_Delay();
if (EEPROM_I2C_SDA_READ()) /* CPU访问SDA口线状态 */
{
re = 1;
}
else
{
re = 0;
}
EEPROM_I2C_SCL_0();
i2c_Delay();
return re;
}
/*
*********************************************************************************************************
* CPU产生一个ACK信号
*********************************************************************************************************
*/
void i2c_Ack(void)
{
EEPROM_I2C_SDA_0(); /* CPU驱动SDA = 0 */
i2c_Delay();
EEPROM_I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
EEPROM_I2C_SCL_0();
i2c_Delay();
EEPROM_I2C_SDA_1(); /* CPU释放SDA总线 */
}
/*
*********************************************************************************************************
* CPU产生一个NACK信号
*********************************************************************************************************
*/
void i2c_NAck(void)
{
EEPROM_I2C_SDA_1(); /* CPU驱动SDA = 1 */
i2c_Delay();
EEPROM_I2C_SCL_1(); /* CPU产生1个时钟 */
i2c_Delay();
EEPROM_I2C_SCL_0();
i2c_Delay();
}
/*
*********************************************************************************************************
* 配置I2C总线的GPIO,采用模拟IO的方式实现
*********************************************************************************************************
*/
static void i2c_CfgGpio(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(EEPROM_I2C_GPIO_CLK, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN | EEPROM_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; /* 开漏输出 */
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(EEPROM_I2C_GPIO_PORT, &GPIO_InitStructure);
/* 给一个停止信号,复位I2C总线上的所有设备到待机模式 */
i2c_Stop();
}
/*
*********************************************************************************************************
* 检测I2C总线设备,CUP发送设备地址,读取设备应答判断设备是否存在
* 返回0表示设备存在,返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(uint8_t _Address)
{
uint8_t ucAck;
i2c_CfgGpio(); /* 配置GPIO */
i2c_Start(); /* 发送启动信号 */
/* 发送设备地址+读写控制bit(0=w,1=r)bit7先传 */
i2c_SendByte(_Address | EEPROM_I2C_WR);
ucAck = i2c_WaitAck(); /* 检测设备的ACK应答 */
i2c_Stop(); /* 发送停止信号 */
return ucAck;
}