打印
[其他ST产品]

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

[复制链接]
355|18
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
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)

使用特权

评论回复
沙发
为你转身|  楼主 | 2023-11-20 22:42 | 只看该作者
延迟函数
延迟函数由逻辑分析仪分析计算获得,这里使用野火例程中计算好的延迟函数循环:

/****延迟函数*******/
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++);
}

使用特权

评论回复
板凳
为你转身|  楼主 | 2023-11-20 22:42 | 只看该作者
开始与停止
由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;
}





使用特权

评论回复
地板
为你转身|  楼主 | 2023-11-20 22:43 | 只看该作者
读取与发送数据
读取与发送数据:


/******发送数据******/
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;
}

使用特权

评论回复
5
为你转身|  楼主 | 2023-11-20 22:43 | 只看该作者
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;
}

使用特权

评论回复
6
为你转身|  楼主 | 2023-11-20 22:43 | 只看该作者
引脚初始化
引脚初始化:
/****引脚初始化*****/
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();//复位
}

使用特权

评论回复
7
为你转身|  楼主 | 2023-11-20 22:43 | 只看该作者
到这里,IIC驱动已经完成,下面是使用模拟IIC协议对EEPROM进行读写。

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

使用特权

评论回复
8
为你转身|  楼主 | 2023-11-20 22:44 | 只看该作者
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

使用特权

评论回复
9
为你转身|  楼主 | 2023-11-20 22:44 | 只看该作者
EEPROM读取数据
读取数据主要流程:开始信号->发送设备地址+写信号(用于写EEPROM需要读取的内部存储位置)->发送EEPROM需要读取位置的地址->重新发送开始信号->发送设备地址+读信号->读取数据->结束信号

使用特权

评论回复
10
为你转身|  楼主 | 2023-11-20 22:44 | 只看该作者
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();
   
}

使用特权

评论回复
11
为你转身|  楼主 | 2023-11-20 22:44 | 只看该作者
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();
}

使用特权

评论回复
12
为你转身|  楼主 | 2023-11-20 22:44 | 只看该作者
填充写入缓存数组
    for(i=0;i<ee_sizeof;i++)
    {
        wbuf[i]=i;
    }

使用特权

评论回复
13
为你转身|  楼主 | 2023-11-20 22:44 | 只看该作者
简单延迟函数
EEPROM写入后需要等待一定时间的延迟,让EEPROM完成内部操作

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

使用特权

评论回复
14
为你转身|  楼主 | 2023-11-20 22:44 | 只看该作者
主函数
#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);
        
}

使用特权

评论回复
15
为你转身|  楼主 | 2023-11-20 22:45 | 只看该作者
实验结果
实验能够正常收发数据,实验通过。

使用特权

评论回复
16
为你转身|  楼主 | 2023-11-20 22:45 | 只看该作者
实验过程中遇到的一些问题和需要注意的地方:

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

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

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

使用特权

评论回复
17
帛灿灿| | 2024-7-11 07:27 | 只看该作者

让电子产品电路免受瞬态雷击浪涌与ESD静电的损害。

使用特权

评论回复
18
周半梅| | 2024-7-11 10:26 | 只看该作者

不打坏仪器内部

使用特权

评论回复
19
Pulitzer| | 2024-7-11 11:29 | 只看该作者

具体采用灌封胶的种类的性能参数,主要看对电源模块的灌封用的胶的要求

使用特权

评论回复
20
童雨竹| | 2024-7-11 13:25 | 只看该作者

不影响线路正常工作

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

681

帖子

0

粉丝