stm32-模拟IIC读写EEPROM实验(寄存器版)

IIC驱动编写

单片机:STM32F103VET6

IIC引脚:SCL:PB6 SDA:PB7

USART串口部分略过,参照:https://blog.csdn.net/staypt/article/details/129131822?spm=1001.2014.3001.5502

本文部分代码参考野火STM32模拟IIC例程进行修改

SCL与SDA引脚的高低电平由BSRR和BRR寄存器控制:

#define SCL_1     SET_BIT(GPIOB->BSRR,1<<6)
#define SCL_0     SET_BIT(GPIOB->BRR,1<<6)
#define SDA_1     SET_BIT(GPIOB->BSRR,1<<7)
#define SDA_0     SET_BIT(GPIOB->BRR,1<<7)

延迟函数

延迟函数由逻辑分析仪分析计算获得,这里使用野火例程中计算好的延迟函数循环:

/****延迟函数*******/
static void i2c_Delay(void)
{
    uint8_t i;

    /* 
         下面的时间是通过逻辑分析仪测试得到的。
    工作条件:CPU主频72MHz ,MDK编译环境,1级优化
  
        循环次数为10时,SCL频率 = 205KHz 
        循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 
         循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us 
    */
    for (i = 0; i < 10; i++);
}

开始与停止

由IIC时序图,获得开始信号与停止信号时序:

/******起始信号******/
void i2c_start(void)
{
    SDA_1;
    SCL_1;
    i2c_Delay();
    SDA_0;
    i2c_Delay();
    SCL_0;
    i2c_Delay();
}


/******停止信号******/
void i2c_stop(void)
{
    SDA_0;
    SCL_1;
    i2c_Delay();
    SDA_1;
}

读取与发送数据

读取与发送数据:

/******发送数据******/
void i2c_sendbyte(uint8_t Sbyte)
{
    uint8_t i;
    for(i=0;i<8;i++)//每个字节八位,每次发送一个字节,高位到低位
    {
        if(Sbyte & 0x80)//发送从左到右,使用0x80判断最高位
        {
            SDA_1;
        }
        else
        {
            SDA_0;
        }
        i2c_Delay();
        SCL_1;
        i2c_Delay();
        SCL_0;
        if(i==7)
        {
            SDA_1;//释放总线给从机设置ack应答
        }
        Sbyte<<=1;
        i2c_Delay();
    }
}


/******读取数据******/
uint8_t i2c_readbyte(void)
{
    uint8_t values;
    uint8_t i;
    values=0;
    for(i=0;i<8;i++)
    {
        values<<=1;//第一位为读写位,略过。
        SCL_1;
        i2c_Delay();
        if(READ_BIT(GPIOB->IDR,GPIO_Pin_7))//这里读取IDR寄存器来读取SDA口线
        {
         values++;
        }
        SCL_0;
        i2c_Delay();
    }
    return values;
}

ACK应答信号

应答信号:


/******产生应答信号*******/
void i2c_ack(void)
{
    SDA_0;
    
    i2c_Delay();
    SCL_1;
    i2c_Delay();
    SCL_0;
    i2c_Delay();
    
    SDA_1;//释放总线
    
    
}


/******产生NOACK应答信号******/
void i2c_noack(void)
{
    SDA_1;
    i2c_Delay();
    
    SCL_1;
    i2c_Delay();
    
    SCL_0;
    i2c_Delay();
    
}


/*****等待ack应答信号******/
uint8_t waitack(void)
{
    uint8_t re;
    SDA_1;//释放总线信号给从机
    i2c_Delay();
    
    SCL_1;
    i2c_Delay();
    
    if(READ_BIT(GPIOB->IDR,GPIO_Pin_7))
    {
        re=1;
    }
    else
    {
        re=0;
    }
    SCL_0;
    i2c_Delay();
    return re;
}

引脚初始化

引脚初始化:

/****引脚初始化*****/
void i2c_gpio_init(void)
{
    SET_BIT(RCC->APB2ENR,RCC_APB2ENR_IOPBEN);//开GPIOB时钟
    /***IIC总线使用开漏输出,低电平时输出高阻态,保护电路*****/
  GPIOB->CRL=0x00;
    
    SET_BIT(GPIOB->CRL,(7<<28));//SDA,开漏输出
    SET_BIT(GPIOB->CRL,(7<<24));//SCL,开漏输出
    i2c_stop();//复位
}

到这里,IIC驱动已经完成,下面是使用模拟IIC协议对EEPROM进行读写。

读写EEPROM

在IIC总线上的每个设备都有一个独立的地址,当主机发起IIC通讯的时候,会通过发送设备地址来查找从机,当发送设备地址+读/写信号后,从机回应一个ACK信号,表示搜索到从机,可以进行通讯。

AT24C02介绍

本次实验使用的EEPROM为AT24C02,其固定地址为1010A2A1A0,其中前四位1010是固定的,A2,A1,A0由EEPROM的1,2,3引脚决定,这里将1,2,3管脚全部设置为低电平,因此EEPROM的地址就为:1010 000,转换成16进制为0X50,此时加上读写位:0(写方向),1(读方向)。这里使用写方向,因此发送的八位地址为1010 0000,即0XA0

EEPROM读取数据

读取数据主要流程:开始信号->发送设备地址+写信号(用于写EEPROM需要读取的内部存储位置)->发送EEPROM需要读取位置的地址->重新发送开始信号->发送设备地址+读信号->读取数据->结束信号

void ee_readbytes(uint8_t *rbuf,uint16_t ee_adderss,uint16_t ee_size)
{

    uint16_t i;
    
    i2c_start();//向iic总线发出启动信号
    
    i2c_sendbyte(0xa0 | 0);//发送设备地址和写信号

        if (waitack() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }
    
    i2c_sendbyte(ee_adderss);//发送eeprom内部子地址
    

            if (waitack() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }
    
    
    i2c_start();//重新发送启动信号,开始读数据
    
  i2c_sendbyte(0xa0 | 1);//发送设备地址和读信号
    

            if (waitack() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }
    
    
    for(i=0;i<ee_size;i++)
    {
        rbuf[i]=i2c_readbyte();//循环读取eeprom数据
        
        if(i != ee_size - 1)
        {
            i2c_ack();//每读取一个字节,发送一次ack应答信号
        }
        else
        {
            i2c_noack();//读取完毕后产生非应答信号
        }
    }
    
    i2c_stop();//读取完成后关闭iic
    cmd_fail: /* 命令执行失败 发送I2C总线停止信号 */
    i2c_stop();
    
}

EEPROM写入数据

过程:开始信号->设备地址+写信号->EEPROM内部地址->开始信号->写入数据->结束信号

/******从eeprom中写入数据*******/
void ee_writebyte(uint8_t *wbuf,uint16_t ee_adderss,uint16_t ee_size)
{
    

    uint16_t i;

        if (i == 0)
        {
            /* 第0步:发停止信号,启动内部写操作 */
            i2c_stop();
        }
        
    i2c_start();//发送开始信号

    i2c_sendbyte(0xa0 | 0);//寻找设备地址和写操作
    
    if (waitack() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }
    
    i2c_sendbyte(ee_adderss);//发送eeprom内部地址
    
  if (waitack() != 0)
    {
        goto cmd_fail;    /* EEPROM器件无应答 */
    }
    
        i2c_start();//重新发送启动信号
    
    
    for(i=0;i<ee_size;i++)
    {
        i2c_sendbyte(wbuf[i]);//开始写入数据
//        

        if(i != ee_size-1)
        {
            if (waitack() != 0)
                {
                    goto cmd_fail;    /* EEPROM器件无应答 */
                }
        }
        
    }


    i2c_stop();//写入完成后发送I2C总线停止信号
    
    
    
        cmd_fail: 
    i2c_stop();
}

填充写入缓存数组

    for(i=0;i<ee_sizeof;i++)
    {
        wbuf[i]=i;
    }

简单延迟函数

EEPROM写入后需要等待一定时间的延迟,让EEPROM完成内部操作

static void ee_Delay(uint32_t count)     //¼òµ¥µÄÑÓʱº¯Êý
{
    for(; count != 0; count--);
}

主函数

#define ee_sizeof   256

int main(void)
{
    uint8_t wbuf[ee_sizeof];
    uint8_t rbuf[ee_sizeof];
    uint16_t i;

    i2c_gpio_init();
    USART_GPIO_INIT();
 
    for(i=0;i<ee_sizeof;i++)
    {
        wbuf[i]=i;
    }

        ee_writebyte(wbuf,0,ee_sizeof);
        ee_Delay(0x0fffff);
        ee_readbytes(rbuf,0,ee_sizeof);
   
        for(i=0;i<ee_sizeof;i++)
        {
            printf(" %02X", rbuf[i]);
        if ((i & 15) == 15)
        {
            printf("\r\n");    
        }        
        }
    while(1);
        
}

实验结果

实验能够正常收发数据,实验通过。

实验过程中遇到的一些问题和需要注意的地方:

  • IIC的引脚必须配置为开漏输出,当输出低电平时,需要为高阻态,否则会损坏设备,具体原理参考IIC协议硬件部分

  • 读写EEPROM过程中需要理解好IIC总线工作模式,每发送一次数据需要等待一次ACK应答信号

  • 需要明确不同EEPROM的空间大小。

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值