历史上的今天
返回首页

历史上的今天

今天是:2025年07月21日(星期一)

正在发生

2018年07月21日 | STM32之利用I2C协议读写EEPROM

2018-07-21 来源:eefocus

/* 

名称:STM32之利用I2C协议读写EEPROM 

说明: 

1.利用STM32来读写EEPROM和C51最大的不同就是,前者是直接使用I2C控制器(硬件方式)来产生所需要的I2C时序,而后者是通过软件方式来产生I2C时序。相对来说,前者使用硬件电路简化了编程的复杂性,用户只要将数据发送到相应的数据寄存器,然后I2C控制器自动按照I2C协议把数据通过SCL和SDA总线发送出去。而后者,你必须按照I2C协议手动产生SCL和SDA的高低电平。当然对于EEPROM来说,它是感受不到发送来的数据是通过硬件电路还是软件方式产生的。


2.还有,STM32内部集成的I2C控制器不仅可以产生时序,还包括了一些其他的功能。比如说,相关的状态寄存器。当主设备或者从设备的相关状态发生改变的时候,就会自动把相关状态寄存器中相关的位置零或置一,这样我们就能通过检查状态寄存器的值来检查通信正处于什么状态。 

相反的,如果使用软件产生的I2C时序方式,必须自己来判断正处于通信的何种状态。比如说,当主设备发送给从设备一个数据后,需要等待从设备的相应。对于STM32来说,若有从机应答,则产生事件“EV6”及“EV8”,这时 SR1 寄存器的“ADDR”位及“TXE”位被置 1, ADDR 为 1 表示地址已经发送, TXE 为 1 表示数据寄存器为空。 我们通过检查寄存器即可得知从设备是否应答。对于C51来说,我们必须手动检测返回的数据是0还是1来判断从设备是否应答。从这个角度来说,有了硬件控制器的帮助,软件编程变得简单不少。


但是,对于I2C来说,STM32封装的固件库网上有很多说有问题的,尤其是无缘故的卡死,还不如直接使用软件方式直接模拟。在编写这个开始程序时,我也有这个问题。一开始,我也以为是这个问题,后来才发现I2C忘了初始化了。嘿嘿。。。


3.还有一点,就是以前在编写C51的I2C驱动AT24C16时,有个不确定的地方:在连续读的时候,会不会产生页回滚的问题?今天又用STM32的方式试了一下,确实是和我当时想的一样。只有页写的时候会产生回滚的问题,页读的时候不会产生这个问题,它会一直读下去,直到到达整个地址边界。


*/


//I2C端口配置

void I2C_GPIO_Config(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    I2C_InitTypeDef  I2C_InitStructure; 


    // 打开IIC GPIO的时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);



    // 打开IIC 外设的时钟 

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);


    // 将IIC SCL和SDL的GPIO配置为推挽复用模式

    GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_GPIO_PIN|EEPROM_I2C_SDA_GPIO_PIN;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(EEPROM_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);



    // 配置IIC的工作参数

    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable  ;//使能应答

    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit ;//使用7位地址模式

    I2C_InitStructure.I2C_ClockSpeed = EEPROM_I2_BAUDRATE; //配置SCL时钟频率

    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2 ;

    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C ;

    I2C_InitStructure.I2C_OwnAddress1 = STM32_I2C_OWN_ADDR; //这是STM32 IIC自身设备地址,只要是总线上唯一即可


    I2C_Init(EEPROM_I2C,&I2C_InitStructure);


    // 使能I2C

    I2C_Cmd (EEPROM_I2C, ENABLE);    


}



//主机向EEPROM写入一个字节

void EEPROM_Byte_Write(uint8_t addr,uint8_t data)

{

    //产生起始信号

    I2C_GenerateSTART(EEPROM_I2C,ENABLE);


    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);


    //EV5事件被检测到,发送设备地址

    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);


  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);


    //EV6事件被检测到,发送要操作的存储单元地址

    I2C_SendData (EEPROM_I2C,addr);


    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING ) == ERROR);


  //EV8事件被检测到,发送要存储的数据

    I2C_SendData (EEPROM_I2C,data);


    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED ) == ERROR);


    //数据传输完成

    I2C_GenerateSTOP(EEPROM_I2C,ENABLE);    


}



//向EEPROM写入多个字节(页写入),每次写入不能超过8个字节

void EEPROM_Page_Write(uint8_t addr,uint8_t *data,uint8_t numByteToWrite)

{

    //产生起始信号

    I2C_GenerateSTART(EEPROM_I2C,ENABLE);




    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);



    //EV5事件被检测到,发送设备地址

    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);


  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);




    //EV6事件被检测到,发送要操作的存储单元地址

    I2C_SendData (EEPROM_I2C,addr);


    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING ) == ERROR);




    while(numByteToWrite)

    {

        //EV8事件被检测到,发送要存储的数据

        I2C_SendData (EEPROM_I2C,*data);


        while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED ) == ERROR);



        data++;

        numByteToWrite--;


    }

    //数据传输完成

    I2C_GenerateSTOP(EEPROM_I2C,ENABLE);    


}



/*等待EEPROM内部时序完成:其基本的原理是重新发送一个起始信号和设备地址,

如果从设备响应了,那么说明写操作完成*/

void EEPROM_WaitForWriteEnd(void)

{


    do

    {

        //产生起始信号

        I2C_GenerateSTART(EEPROM_I2C,ENABLE);


        while(I2C_GetFlagStatus (EEPROM_I2C,I2C_FLAG_SB) == RESET);


        //EV5事件被检测到,发送设备地址

        I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);

    } while(I2C_GetFlagStatus (EEPROM_I2C,I2C_FLAG_ADDR) == RESET );


    //EEPROM内部时序完成传输完成

    I2C_GenerateSTOP(EEPROM_I2C,ENABLE);    

}



//从EEPROM读取数据

void EEPROM_Read(uint8_t addr,uint8_t *data,uint8_t numByteToRead)

{

    //产生起始信号

    I2C_GenerateSTART(EEPROM_I2C,ENABLE);


    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);


    //EV5事件被检测到,发送设备地址

    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);


  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);


    //EV6事件被检测到,发送要操作的存储单元地址

    I2C_SendData (EEPROM_I2C,addr);


    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTING ) == ERROR);



    //第二次起始信号

    //产生起始信号

    I2C_GenerateSTART(EEPROM_I2C,ENABLE);


    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);


    //EV5事件被检测到,发送设备地址

    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Receiver);


  while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) == ERROR);


    while(numByteToRead)

    {


        //EV7事件被检测到 

        while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED ) == ERROR);


        //EV7事件被检测到,即数据寄存器有新的有效数据   

        *data = I2C_ReceiveData(EEPROM_I2C);


        data++;


        numByteToRead--;


        if(numByteToRead == 0)      //如果为最后一个字节

            I2C_AcknowledgeConfig(EEPROM_I2C,DISABLE);          //发送非响应位

        else

            I2C_AcknowledgeConfig (EEPROM_I2C,ENABLE);          //发送响应位



    }


    //数据传输完成

    I2C_GenerateSTOP(EEPROM_I2C,ENABLE);    


//  //重新配置ACK使能,以便下次通讯

    I2C_AcknowledgeConfig (EEPROM_I2C,ENABLE);


}


推荐阅读

史海拾趣

Central Semiconductor公司的发展小趣事

Central Semiconductor一直致力于创新分立元器件的研发和生产。公司凭借其深厚的技术积累和市场洞察力,成功开发出一系列具有节省空间、电气规格灵活等特点的创新产品。这些产品不仅满足了客户对高性能、高可靠性的需求,也为Central在竞争激烈的市场中赢得了良好的声誉。

此外,Central还注重根据客户的特殊需求进行定制开发。无论是筛选电气规格、特殊晶圆扩散还是开发定制元器件,Central都能迅速响应并提供满足客户需求的解决方案。这种以客户为中心的服务理念也为Central赢得了众多客户的信任和合作。

硕颉(BITEK)公司的发展小趣事

尽管硕颉科技在知识产权方面做出了积极努力,但仍难免面临专利诉讼的挑战。在某次与凹凸科技的专利侵权诉讼中,公司虽然一度面临败诉和永久禁制令的风险,但硕颉科技迅速应诉,积极应对。最终,美国联邦巡回上诉法院废除了原判决,公司得以自由销售被诉产品,不受任何限制。这次诉讼的胜利,不仅展示了硕颉科技在应对法律挑战方面的决心和能力,也为公司的长远发展奠定了坚实基础。

3E SECURITY公司的发展小趣事

在追求经济效益的同时,3E SECURITY公司始终关注社会责任。公司积极参与社会公益活动,支持教育事业和环境保护事业。同时,公司还加强了对员工的人文关怀和职业发展支持,为员工提供了良好的工作环境和成长空间。这些举措不仅提升了公司的社会形象,还增强了员工的归属感和忠诚度,为公司的长期发展奠定了坚实的基础。


请注意,以上故事纯属虚构,仅用于展示电子行业中安全公司可能的发展路径和经历。实际情况可能因公司策略、市场环境等因素而有所不同。如果您需要关于3E SECURITY公司的真实发展故事,建议您查阅相关资料或联系该公司进行了解。

Analogix Semiconductor公司的发展小趣事

为了进一步提升竞争力,3E SECURITY公司开始实施产业链整合战略。公司通过收购、兼并等方式,整合了上下游产业链资源,形成了从研发、生产到销售、服务的完整产业链体系。这一举措不仅降低了公司的运营成本,还提高了产品的质量和服务的效率,为公司的发展注入了新的动力。

AZM [Arizona Microtek, Inc]公司的发展小趣事

为了进一步提升公司的国际竞争力,AZM公司开始实施国际化战略。公司积极寻求与国际知名企业的合作机会,通过技术合作、市场合作等方式,共同开拓全球市场。同时,AZM公司还在海外设立了研发中心和生产基地,以便更好地了解当地市场需求和技术发展趋势,实现全球布局和资源整合。

请注意,这些故事是基于假设和推测构建的,并非AZM公司的真实发展历程。如果需要了解AZM公司的具体发展历程和故事,建议查阅该公司的官方网站、新闻报道或相关文献资料。

Anderson Electronics Inc公司的发展小趣事

在取得一系列辉煌成就的同时,Anderson Electronics Inc公司始终关注可持续发展和环境保护。公司积极推行绿色生产理念,采用环保材料和节能技术,努力降低生产过程中的能耗和排放。同时,公司还关注社会责任和公益事业,积极参与社会公益活动,回馈社会。展望未来,公司将继续坚持创新驱动、质量为本的发展理念,不断推动技术创新和产业升级,为电子行业的发展贡献更多的力量。


请注意,以上内容仅为一个通用的框架,具体的故事内容需要根据Anderson Electronics Inc公司的实际情况进行创作。在撰写时,您可以结合公司的历史背景、发展历程、技术创新、市场拓展、国际合作、产业升级等方面的信息进行详细描述,以展现公司在电子行业中的发展历程和成就。

问答坊 | AI 解惑

对开关电源寿命的思考

今 天碰到一个客服的同事问我电源寿命的问题,当时很轻易的把她搪塞过去了。事后仔细一想,有以下几点疑问,想请大侠们探讨下,请不吝赐教:   1;任何物体都是有寿命的!只是长短的区别!对于电源这块,寿命最长的,我认为是磁性材料, ...…

查看全部问答>

八木天线的工作原理

八木天线的工作原理是这样的(以三单元天线接收为例):引向器略短于二分之一波长,主振子等于二分之一波长,反射器略长于二分之一波长,两振子间距四分之一波长。此时,引向器对感应信号呈“容性”,电流超前电压90°;引向器感应的电磁波会向主振 ...…

查看全部问答>

怎么让家用电表不走

让你省电 !教 你 怎 么 免 费 用 电~~shuzikejiwangye〖数字科技〗Q Q:1229781074//T E L:~~1 5 1 6 0 0 4 8 1 8 4  聯 系 人:小 王Q Q:1229781074//T E L:~~1 5 1 6 0 0 4 8 1 8 4  (僅 供 電 工 使 用,嚴 禁 違 ...…

查看全部问答>

OpenGL ES 1.1 透视模式设置

先说一下我的平台,硬件是s3c6410,系统wince6.0,OpenGL ES版本1.1,三星提供的dll。 现在说一下问题,我画了几张图,发现远的和近的一样大,没有\"近大远小\"的感觉。查了些资料说是应该要采用\"透视模式\"的投影方式,需要用到这样的一个API ...…

查看全部问答>

关于9054的I/O,内存问题以及地址映射的问题

RT 小弟最近要把9054的datasheet吃透,但是有些问题不明白,希望各位大侠帮忙回答一下 1.9054的基地址0~1用于内存映射和I/O映射,基地址2~5用作本地地址空间0~3,后面Local寄存器又说明本地空间可以映射到I/O或者内存中,用I/O活内存操作,纳秒这 ...…

查看全部问答>

TI LED照明知识(转) 中英文版

现阶段节能已经成为一种硬性的要求,不是一种选择,而其中一部分需要采用环保绿色的方式去实现节能。对于照明来说,我们可以很容易的设想全球照明提高10%的效率后所带来的影响,但是提高100%呢?最近高效率发光二极管(LED)可能实现这种效率的改进, ...…

查看全部问答>

TI推出业界首款支持集成型MOSFET 的100V同步降压稳压器LM5017

最新系列高集成引脚兼容型 IC 可缩小 PCB 面积,提高高电压应用的可靠性日前,德州仪器 (TI) 宣布推出业界首款支持集成型 MOSFET 的100 V 同步降压稳压器,进一步壮大其高电压负载点产品阵营。该600 mA LM5017是最新系列降压开关稳压器中 ...…

查看全部问答>

刚接触啊,各种不会,求喷。。。

任务由stm32 ADC2采集数据后存入sd卡里面(不用DMA),我修改了下两个程序,第一:3ADCs_DMA删除部分程序只用ADC2,第二:Ex013-SDIO+FatFS删除部分程序。然后合并两程序。。。可是不对。好像ADC转换的数据存入的变量要申明成全局变量,不懂怎么设 ...…

查看全部问答>

ADuC7060的GND_SW作为低速IO的问题

ADuC7060的IO引脚确实太少了,想多带个东西就很麻烦 突然想到GND_SW在手册上说是用于和内部模拟地相连的,对于模拟地和数字地相接的电路来说,如果将它作为象电源开关用途的、低速控制,应该没有问题吧?…

查看全部问答>