嵌入式stm32学习:I2C-读写EEPROM

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;
}

你可能感兴趣的:(嵌入式-stm32)