历史上的今天
返回首页

历史上的今天

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

正在发生

2020年03月03日 | 【STM库应用】stm32 之 IIC应用

2020-03-03 来源:eefocus

iic协议是比较简单的双线协议,时钟线CLK和数据线SDA。


一般我们常见的还有spi总线,这种总线可以可以根据需要扩展,还有单总线等等


这次还以at240c2为例进行操作!

PS:这就是传说中的iic时序图


硬件构造我们不过多的分析,今天用到库了!我们先从库函数硬件iic初始化说起!


PB6   --   CLK


PB7   --   SDA


void i2c_init(u8 addr,u32 clock)

{

I2C_InitTypeDef i2c;

RCC->APB2ENR |= 1<<3;

GPIOB->CRL |= (u32)0xff<<(6*4);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);

i2c.I2C_Ack = I2C_Ack_Enable;

i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

i2c.I2C_ClockSpeed = clock*1000;

i2c.I2C_DutyCycle = I2C_DutyCycle_2;

i2c.I2C_Mode = I2C_Mode_I2C;

i2c.I2C_OwnAddress1 = addr;

 

I2C_Cmd(I2C1,ENABLE);

I2C_Init(I2C1,&i2c);

}

在配置管脚方面,我还是喜欢用寄存器配置,因为我的两行代码可以解决库函数的N多行代码的问题!

还有在结构体变量命名方面也是属于我自己的独创吧,这样反正我觉得是既容易识别,也少打几个字!


typedef struct

{

  uint32_t I2C_ClockSpeed; //I2C时钟频率设置

  uint16_t I2C_Mode;             //I2C模式设置

  uint16_t I2C_DutyCycle;     //高低电平时间之比

  uint16_t I2C_OwnAddress1;      //主设备地址设置,也就是自己的地址

  uint16_t I2C_Ack;                 //Check

  uint16_t I2C_AcknowledgedAddress; //地址长度,可以为7bit的也可以为10bit的

}I2C_InitTypeDef;


IIC初始化完之后,我们开始来研究eeprom

看完这个写一个字节的协议之后,我们应该对这个写已经没有什么问题了,很简单的。

这个是写一个page


注:在eeprom里面写数据时,一次最多只能写一个page,一个page为8byte,同时这个也有字节对齐的要求!

比如我们从Address = 4开始写,那么我们最多一次性可写4个byte,如果我们从8开始写的话,我们就可以8个byte,最后偏移到15。


void eeprom_write_byte(u8 wt_addr,u8 data)

{

 

I2C_GenerateSTART(I2C1,ENABLE);delay(5);

I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); 

I2C_SendData(I2C1,wt_addr);delay(5);

I2C_SendData(I2C1,data);delay(5);

I2C_GenerateSTOP(I2C1, ENABLE);

delay(20);

}


由于stm32的i2c确实做的不怎么样,标着寄存器太多,也不容易识别,我们就不要检测这些标志寄存器,用延时了把他们隔离了。不过在把地址发送出去之后,要检测设备是否被选中,这个在我们的模拟的i2c里面也是必须检测的!可以认为是必不可少的!

void eeprom_write_page(u8 wt_addr,u8 *buff,u32 length)

{

int i = 0;

I2C_GenerateSTART(I2C1,ENABLE);delay(5);

I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); 

I2C_SendData(I2C1,wt_addr);delay(5);

for(i=0; i {

I2C_SendData(I2C1,buff[i]);delay(5);

}

I2C_GenerateSTOP(I2C1, ENABLE);

}


这个是写一个page的函数,如果大家想看比较灵活的写函数,去我看我前面发表的博客里面找找,那个无论你写几个都无所谓,只要不超过eeprom的大小!

读数据是稍稍复杂一点点的,我们首先要选中设备,然后选择我们要操作的地址,这时候不要stop,如果stop信号一发出,总线就被释放掉了,设备也就跟处理器断开,所以这里需要一个RSTART,跟START不一样,多了个R,这个可以理解为重新开始,这个信号不会选中其他设备,也不会丢失当前设备。


然后还有个注意点是,在读完第N个字节后,不要返回回应,直接stop,不然设备会以为你没有结束,会一直占据总线,等待下一个数据的发送,这样等你下一次来访问他的时候,他就不让你访问了,因为他还停留在给你传数据的状态,所以这里一定不要返回acK直接stop信号发出哦!


unsigned char eeprom_read_byte(u8 rd_addr)

{

u8 temp = 0;

 

I2C_GenerateSTART(I2C1,ENABLE);delay(5);

I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

I2C_Cmd(I2C1, ENABLE);

I2C_SendData(I2C1,rd_addr);delay(10);

I2C_GenerateSTART(I2C1,ENABLE);delay(5);

I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Receiver);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

I2C_AcknowledgeConfig(I2C1, DISABLE);

while(!(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)));

temp = I2C_ReceiveData(I2C1);delay(5);

I2C_GenerateSTOP(I2C1, ENABLE);

delay(20);

I2C_AcknowledgeConfig(I2C1, ENABLE);

 

return temp;

}


还是有几个信号是必须确认的,设备地址发送出去,看设备是否有回应!

这里最后一个NOACK必须在发送最后一个字节前使能,在stop信号发出后,记得吧ACK信号重新使能,因为我们刚刚开始是需要ack的,只是最后有时候不需要!

对于数据的读,在所读数据长度上,是没有要求的,也没有page限制,想读多少,读多少!


void eeprom_read(u8 rd_addr,u8 *buff,u32 length)

{

int i = 0;

I2C_GenerateSTART(I2C1,ENABLE);delay(5);

I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

I2C_Cmd(I2C1, ENABLE);

I2C_SendData(I2C1,rd_addr);delay(20);

I2C_GenerateSTART(I2C1,ENABLE);delay(5);

I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Receiver);

while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

for(i=0; i {

if(i == length-1)

I2C_AcknowledgeConfig(I2C1, DISABLE);

while(!(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)));

buff[i] = I2C_ReceiveData(I2C1);delay(5);

}

I2C_GenerateSTOP(I2C1, ENABLE);

I2C_AcknowledgeConfig(I2C1, ENABLE);

}


PS:在提一下,在接收最后一个数据之前,必须关闭ACK,不然,。。。后果很严重!


推荐阅读

史海拾趣

E-San Electronic Co Ltd公司的发展小趣事

随着市场的不断变化和消费者需求的升级,E-San Electronic Co Ltd意识到技术创新是企业持续发展的关键。公司投入大量资金和资源,建立了自己的研发团队,并与多所高校和研究机构建立了合作关系。经过数年的努力,公司成功研发出了一系列具有自主知识产权的核心技术,这些技术不仅提升了产品的性能和质量,也为企业赢得了更多的市场份额。

GeneSiC Semiconductor公司的发展小趣事

E-San Electronic Co Ltd的创始人李明,在电子科技行业摸爬滚打多年后,决定创立自己的公司。创业初期,公司面临着资金短缺、技术瓶颈和市场竞争激烈的挑战。李明凭借对电子技术的深刻理解和敏锐的市场洞察力,带领团队成功研发出了一款具有创新性的电子产品,并成功打入市场。这款产品以其高性价比和稳定的性能赢得了消费者的青睐,为公司带来了第一笔可观的收入,也为后续的发展奠定了坚实的基础。

CAROLCABLE公司的发展小趣事

CAROLCABLE公司的创立,标志着电子线缆行业迎来了一位新的竞争者。在电子科技飞速发展的时代背景下,创始人凭借对线缆技术的深刻理解和市场需求的敏锐洞察,决定创立CAROLCABLE公司。公司初期,面临着资金短缺、技术瓶颈和市场认知度低等多重挑战。然而,创始人凭借着坚定的信念和不懈的努力,带领团队克服了一个又一个困难。他们积极研发新产品,优化生产工艺,提高产品质量,逐渐在市场上站稳了脚跟。

随着公司业务的不断拓展,CAROLCABLE开始与一些知名的电子企业建立合作关系,为其提供高质量的线缆产品。这些合作不仅为公司带来了稳定的订单和收入,也进一步提升了CAROLCABLE在行业内的知名度和影响力。

Dantona Industries公司的发展小趣事

在公司的成长过程中,Dantona Industries始终将技术创新作为核心竞争力。他们投入大量资源研发新产品,不断提高产品的性能和质量。一次,公司面临了一项重大的技术难题,需要开发一款具有更高集成度和更低功耗的芯片。Daniel Tona带领团队日夜兼程,经过数月的努力,终于成功突破了技术瓶颈,推出了这款具有划时代意义的芯片。这一创新不仅为公司赢得了市场的认可,也为整个电子行业树立了新的标杆。

驰芯微(CHIPWISE)公司的发展小趣事

驰芯微公司一直注重技术研发和产品创新。公司拥有一支高素质的研发团队,不断在芯片设计、制造和封装等领域取得突破。其中,车规级霍尔传感器芯片XL3600系列和车规级32位微控制器MCU芯片XL6600系列是公司的明星产品。这些产品不仅性能卓越,而且可靠性高,赢得了客户的广泛认可。此外,驰芯微还不断推出新产品,如新能源动力多节电池组监控器BMS AFE芯片XL8812/XL8820系列等,进一步丰富了公司的产品线。

Cembre SPA公司的发展小趣事

Cembre SPA一直秉持品质至上的原则,不断追求产品质量的提升。公司建立了严格的质量管理体系,从原材料采购到生产制造,再到产品出厂,每一个环节都严格把控。同时,公司还注重持续改进,通过引入先进的生产设备和技术,不断提高生产效率和产品质量。

问答坊 | AI 解惑

电子万年历我成功调试过

http://blog.163.com/li_j920/edit/ 还的硬件电路,你们要我会给你们的。…

查看全部问答>

求助IIC总线

在网上看了IIC总线的资料但是还是不懂如何用C语言写个程序出来,看了人家的程序也不懂,请哪些高手帮助我下解释程序的含义,或者进一步讲解下思路。 我看了下资料,不知道这样可以实现IIC总线的启停吗? #include <reg51.h>#include \"intrin ...…

查看全部问答>

WinCE中如何调用ARM汇编指令?

RT,我在做GBA模拟器,想直接调用ARM汇编指令,利用ARM9模拟ARM7-TDMI,但是如果可以直接调用ARM汇编指令模拟就很简单了,请问需要什么平台和设备?因为主要是在Windows Mobile上面用,所以希望能简单一点的…

查看全部问答>

请教 关于上层过滤器驱动程序的问题

请问如何在上层过滤驱动实现指定的IRP拦截,比如,禁止C:\\helloworld.exe以外的一切程序写G:\\盘? 我现在用DriverStudio生成了一个upper filter,那么应该如何修改代码呢?…

查看全部问答>

想找个师傅带一下【上海,武汉均可】

要毕业了没有工作经验,想找一个师傅教一下。毕业课题是无线点菜机,辅导我做一下。…

查看全部问答>

ARM的IO脚为什么不像51那样可以直接操作

arm是先要打开指定的IO口才可以读写,感觉不如51的方便arm的IO脚为什么不像51那样可以直接操作?这样有什么好处?…

查看全部问答>

GBT 20234-2006电动汽车传导充电用插头、插座、车辆耦合器和车辆插孔通用要求

GBT 20234-2006电动汽车传导充电用插头、插座、车辆耦合器和车辆插孔通用要求…

查看全部问答>

zigbeePro实际最多能接多少个节点?

zigbeePro实际最多能接多少个节点?想将一个网络的节点数布置为1000个,分5个楼层,不知延时会有多少?…

查看全部问答>