历史上的今天
返回首页

历史上的今天

今天是:2024年12月30日(星期一)

正在发生

2021年12月30日 | STM32F103通过IIC总线读取EEPROM

2021-12-30 来源:eefocus

IIC总线是常用的串行总线,它只需要简单的两根线就可以实现数据的高速传输,同时还可以实现多机通信功能。


在单片机中用的比较多的就是通过IIC总线操作EEPROM芯片。比较常用的EEPROM芯片就是24Cxx系列的芯片,主要用来存储系统运行过程中的关键数据。

image.png?imageView2/2/w/550

要操作这个芯片的话,必须按照一定的时序去读写。这个时序通常被称为通信协议。24Cxx系列芯片通信协议如下。


I2 C 总线协议


I 2 C 总线协议定义如下:


只有在总线空闲时才允许启动数据传送

在数据传送过程中 当时钟线为高电平时 数据线必须保持稳定状态 不允许有跳变 时钟线为高电平时 数据线的任何电平变化将被看作总线的起始或停止信号。

image.png?imageView2/2/w/550

起始信号


时钟线保持高电平期间 数据线电平从高到低的跳变作为 I 2 C 总线的起始信号


停止信号


时钟线保持高电平期间 数据线电平从低到高的跳变作为 I 2 C 总线的停止信号

image.png?imageView2/2/w/550

image.png?imageView2/2/w/550

image.png?imageView2/2/w/550

image.png?imageView2/2/w/550

这里就使用最简单的几个通信时序起始、停止、读、写和应答时序。


为了IIC协议的通用性,将这几个协议封装为一个c文件,这样以后每个使用IIC协议的器件都能调用这个文件。


//IIC产生起始信号

void IIC_Start(void)

{

    //START:when CLK is high,DATA change form high to low

    SDA_OUT();

    IIC_SDA = 1;

    IIC_SCL = 1;

    delay_us(4);

    IIC_SDA = 0;

    delay_us(4);

    IIC_SCL = 0;

}

//产生停止信号

void IIC_Stop(void)

{

    //STOP:when CLK is high DATA change form low to high

    SDA_OUT();

    IIC_SCL = 0;

    IIC_SDA = 0;

    delay_us(4);

    IIC_SCL = 1;

    IIC_SDA = 1;

    delay_us(4);

}

//等待应答信号

//返回值: 1 应答失败

//        0 应答成功

u8 IIC_Wait_Ack(void)

{

    u8 errTime = 0;

    SDA_IN();

    IIC_SDA = 1;

    delay_us(1);

    IIC_SCL = 1;

    delay_us(1);

    while(READ_SDA)

    {

        errTime++;

        if(errTime > 250)

        {

            IIC_Stop();

            return 1;

        }

    }

    IIC_SCL = 0;

    return 0;

}

//产生ACK应答

void IIC_Ack(void)

{

    IIC_SCL = 0;

    SDA_OUT();

    IIC_SDA = 0;

    delay_us(2);

    IIC_SCL = 1;

    delay_us(2);

    IIC_SCL = 0;

}

//不产生应答

void IIC_NAck(void)

{

    IIC_SCL = 0;

    SDA_OUT();

    IIC_SDA = 1;

    delay_us(2);

    IIC_SCL = 1;

    delay_us(2);

    IIC_SCL = 0;

}

//IIC发送一个字节

void IIC_Send_Byte(u8 txd)

{

    u8 t;

    SDA_OUT();

    IIC_SCL = 0;

    for(t = 0; t < 8; t++)

    {

        if((txd & 0x80) >> 7)

            IIC_SDA = 1;

        else

            IIC_SDA = 0;

        txd <<= 1;

        delay_us(2);

        IIC_SCL = 1;

        delay_us(2);

        IIC_SCL = 0;

        delay_us(2);

    }

}

//读1个字节,ack=1时,发送ACK,ack=0,发送NACK

u8 IIC_Read_Byte(unsigned char ack)

{

    unsigned char i, receive = 0;

    SDA_IN();

    for(i = 0; i < 8; i++)

    {

        IIC_SCL = 0;

        delay_us(2);

        IIC_SCL = 1;

        receive <<= 1;

        if(READ_SDA)

            receive++;

        delay_us(1);

    }

    if(!ack)

        IIC_NAck();

    else

        IIC_Ack();

    return receive;

}


这里将最常用的几个信号封装为函数,当操作24Cxx芯片的时候,直接调用这几个函数。


#include "24Cxx.h"

#include "delay.h"


void AT24Cxx_Init(void)

{

    IIC_Init();

}

//在AT24CXX指定地址读出一个数据

//ReadAddr:开始读数的地址

//返回值  :读到的数据

u8 AT24Cxx_ReadOneByte(u16 ReadAddr)

{

    u8 temp = 0;

    IIC_Start();

    if(EE_TYPE > AT24C16)

    {

        IIC_Send_Byte(0xA0); //发送写命令

        IIC_Wait_Ack();

        IIC_Send_Byte(ReadAddr >> 8); //发送高地址

        IIC_Wait_Ack();

    }

    else

        IIC_Send_Byte(0xA0 + ((ReadAddr / 256) << 1)); //发送器件地址0XA0,写数据


    IIC_Wait_Ack();

    IIC_Send_Byte(ReadAddr % 256); //发送低地址

    IIC_Wait_Ack();

    IIC_Start();

    IIC_Send_Byte(0xA1); //进入接收模式

    IIC_Wait_Ack();

    temp = IIC_Read_Byte(0);

    IIC_Stop(); //产生一个停止条件

    return temp;

}

//在AT24CXX指定地址写入一个数据

//WriteAddr  :写入数据的目的地址

//DataToWrite:要写入的数据

void AT24Cxx_WriteOneByte(u16 WriteAddr, u8 DataToWrite)

{

    IIC_Start();

    if(EE_TYPE > AT24C16)

    {

        IIC_Send_Byte(0xA0); //发送写命令

        IIC_Wait_Ack();

        IIC_Send_Byte(WriteAddr >> 8); //发送高地址

    }

    else

        IIC_Send_Byte(0xA0 + ((WriteAddr / 256) << 1)); //发送器件地址0XA0,写数据

    IIC_Wait_Ack();

    IIC_Send_Byte(WriteAddr % 256);

    IIC_Wait_Ack();

    IIC_Send_Byte(DataToWrite);

    IIC_Wait_Ack();

    IIC_Stop();

    delay_ms(10);

}

//在AT24CXX里面的指定地址开始写入长度为Len的数据

//该函数用于写入16bit或者32bit的数据.

//WriteAddr  :开始写入的地址

//DataToWrite:数据数组首地址

//Len        :要写入数据的长度2,4

void AT24Cxx_WriteLenByte(u16 WriteAddr, u32 DataToWrite, u8 Len)

{

    u8 t;

    for(t = 0; t < Len; t++)

    {

        AT24Cxx_WriteOneByte(WriteAddr + t, (DataToWrite >> (8 * t)) & 0xff);

    }

}

//在AT24CXX里面的指定地址开始读出长度为Len的数据

//该函数用于读出16bit或者32bit的数据.

//ReadAddr   :开始读出的地址

//返回值     :数据

//Len        :要读出数据的长度2,4

u32 AT24Cxx_ReadLenByte(u16 ReadAddr, u8 Len)

{

    u8 t;

    u32 temp = 0;

    for(t = 0; t < Len; t++)

    {

        temp <<= 8;

        temp += AT24Cxx_ReadOneByte(ReadAddr + Len - t - 1);

    }

    return temp;

}

//检查AT24CXX是否正常

//这里用了24XX的最后一个地址(255)来存储标志字.

//如果用其他24C系列,这个地址要修改

//返回1:检测失败

//返回0:检测成功

u8 AT24Cxx_Check(void)

{

    u8 temp;

    temp = AT24Cxx_ReadOneByte(255);

    if(temp == 0x55)

        return 0;

    else

    {

        AT24Cxx_WriteOneByte(1023, 0x55);

        temp = AT24Cxx_ReadOneByte(255);

        if(temp == 0x55)

            return 0;

    }

    return 1;

}


//在AT24CXX里面的指定地址开始读出指定个数的数据

//ReadAddr :开始读出的地址 对24c02为0~255

//pBuffer  :数据数组首地址

//NumToRead:要读出数据的个数

void AT24Cxx_Read(u16 ReadAddr, u8 *pBuffer, u16 NumToRead)

{

    while(NumToRead)

    {

        *pBuffer++ = AT24Cxx_ReadOneByte(ReadAddr++);

        NumToRead--;

    }

}

//在AT24CXX里面的指定地址开始写入指定个数的数据

//WriteAddr :开始写入的地址 对24c02为0~255

//pBuffer   :数据数组首地址

//NumToWrite:要写入数据的个数

void AT24Cxx_Write(u16 WriteAddr, u8 *pBuffer, u16 NumToWrite)

{

    while(NumToWrite--)

    {

        AT24Cxx_WriteOneByte(WriteAddr, *pBuffer);

        WriteAddr++;

        pBuffer++;

    }

}


这里就是对具体的芯片操作函数,在主函数中通过这几个函数就可读写EEPROM存储芯片的内容了。


int main(void)

{


    u8 key;

    u16 i = 0, j = 0;

    u8 datatemp[SIZE];


    delay_init();

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    uart_init(115200);

    LED_Init();

    KEY_Init();

   

    AT24Cxx_Init();


    printf("IIC test!!!rn");

    while(AT24Cxx_Check())

    {

        printf("The chip is not detected, please check whether the hardware connection is normal!!!rn");

        delay_ms(300);

        LED1 = !LED1;

    }


    while(1)

    {


        key = KEY_Sacn(0);

        if(key == WKUP_PRES)

        {

            printf("rnStart Write 24C02....rn");

            AT24Cxx_Write(0, (u8 *)TEXT_Buffer, SIZE);

            printf("24C02 Write Finished!rn");

        }

        else if(key == KEY1_PRES)

        {

            printf("rnStart Read 24C02....rn");

            AT24Cxx_Read(0, datatemp, SIZE);

            printf("The Data Readed Is:  ");

            for(j = 0; j < SIZE; j++)

            {

                printf("%c", datatemp[j]);

            }

        }


        i++;

        if(i == 20)

        {

            i = 0;

            LED0 = !LED0;

        }

        delay_ms(5);

    }

}


这里通过按键来测试存储芯片,一个按键按下后向芯片内写入数据,另一个按键按下后从芯片中读取刚才写入的内容。

推荐阅读

史海拾趣

Curtis Instruments Inc公司的发展小趣事

1995年,Curtis在中国建立了独资企业科蒂斯仪器(中国)有限公司,其制造工厂于1997年投入生产。经过近20年的发展,Curtis在中国已经建立了完善的生产、研发和销售体系。2012年,Curtis在苏州工业园区成立了独立的科蒂斯技术(苏州)有限公司,进一步提升了在中国的研发和生产能力。如今,Curtis中国已成为Curtis在全球的第6个研发中心,也是中国最现代化的生产电动车辆驱动控制系统的厂商之一。

Greenlee公司的发展小趣事

面对电子行业的多元化需求,GREEGOO公司选择了智能家居作为其主要发展方向。公司深入调研市场需求,精准定位目标客户群体,推出了一系列具有创新性和实用性的智能家居产品。通过不断优化产品设计和用户体验,GREEGOO在智能家居领域逐渐建立了良好的口碑和品牌形象。随着智能家居市场的不断扩大,GREEGOO的销售额和市场份额也实现了稳步增长。

DLG Hanbit公司的发展小趣事

面对数字化转型的浪潮,DLG Hanbit公司积极拥抱变革。公司加大了对云计算、大数据、人工智能等技术的研发投入,推出了一系列智能电子产品和解决方案。同时,公司还加强了与互联网企业、电信运营商等合作伙伴的合作,共同打造智能生态圈。未来,DLG Hanbit公司将继续秉承创新、品质、环保的理念,致力于成为全球领先的半导体设计和制造企业。

Elpaq公司的发展小趣事
Barkston Plastics Engineering Ltd.公司的发展小趣事

随着电子行业的快速发展,Barkston Plastics Engineering Ltd.意识到单一产品线的局限性,于是开始扩展其产品线,涵盖更多种类的塑料电子元件。同时,公司积极寻求与行业内其他企业的战略合作,通过技术共享和市场资源互补,共同开拓更广阔的市场。这一战略转型不仅提升了Barkston的市场竞争力,也为其后续发展奠定了坚实的基础。

Eurotechnique公司的发展小趣事

随着全球对环保和可持续发展的关注度不断提高,Eurotechnique也开始注重环保和可持续发展。公司采用环保材料和生产工艺,降低产品对环境的影响。同时,Eurotechnique还积极参与环保公益活动,推动电子行业的绿色发展。此外,公司还致力于研发更加节能、环保的产品,以满足市场的需求。

问答坊 | AI 解惑

阻抗匹配资料包

本帖最后由 paulhyde 于 2014-9-15 03:43 编辑 不多说了,有需要的拿    …

查看全部问答>

单片机C语言编程与实例

这是一本比较经典的电子书,希望对大家的学习有所帮助 两个部分需要一起下载…

查看全部问答>

Altera白皮书-保护 FPGA 设计不受常见的入侵威胁

2009年,全球由于盗版带来的损失大约为1.5万亿美元。盗版影响了所有商业市场,从Gucci手包到计算机芯片,直至专用算法等。全球供应链越来越复杂,对企业知识产权 (IP) 的剽窃也日益增多。全球供应链的安全和保护问题是保持竞争优势的关键,需要认真 ...…

查看全部问答>

大学生就业面试多“隐恋”

大学生就业面试多“隐恋”     越来越多关于明星“隐婚”、“隐恋”近来纷纷被曝光,有些人觉得偶像欺骗了他们而深感不爽,对此,广东的大学生们却说:“我们在求职时,也常常会\'\'隐恋\'\',表现出要专心工作的样子以博得面 ...…

查看全部问答>

中文为默认语言的问题

WinCE5.0选中文为默认语言 把图片\\SHELL\\OAK\\HPC\\CESHELL\\UI\\RES\\mycomputer.ico换成另一张图片。 选中clean before building 然后build and sysgen 编完开机后发现“我的设备”图标还是原来的 选英文为默认语言那么开机后“我的设备” ...…

查看全部问答>

关于Target Shell的问题,急!

现在使用的是vxworks_rom这种映像,烧在flash里启动; 在符号表那块,     如果选择build-in的,其他命令可用,但在使用ld命令下载应用模块时报\"打不开相应的文件\";     如果选择downloadable的,则可以使用ld顺利下载应用模块; ...…

查看全部问答>

请教关于SM32 USART 串口中断的问题

最近两天在STM32的开发板调试USART的串口程序,遇到了一些问题: 采用中断的方式发送和接收数据 使用USART1时从 开发板 --> PC 发送数据成功 但是当只要 PC 发送数据给 开发板,开发板发送到PC的数据就变成乱码了 即使把USART1的接收中断关 ...…

查看全部问答>

题目来了,供下载

本帖最后由 paulhyde 于 2014-9-15 03:01 编辑 题目下载    …

查看全部问答>

使用zstack进行组网时终端无法加入网络的问题!!!

本帖最后由 kelaosi 于 2014-10-29 17:36 编辑 在进行zigbee通信实验过程中,发现组网成功后终端怎么也无法加入网络,后经逐步调试,发现在ZDO_NetworkDiscoveryConfirmCB函数中,所搜寻到的网络参数“pNwkDesc->deviceCapacity”等于0,结果导致 ...…

查看全部问答>