完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
IIC
1. IIC的定义及作用?与SPI,USART有何不同? STM32上有很多的通讯接口,主要用来连接MCU与IC(如下面将要介绍的EEPROM–AT24C02C),及IC与IC之间的通讯。因为对通讯的速度及功能要求不同,主要分为IIC,SPI,USART. 1).IIC (Inter-Integrated Circuit 意为IC之间总线):两线式串行总线,是由数据SDA线和时钟SCL线构成的串行总线,可发送和接收数据。IIC是多主控总线,所以任何一个设备都能像主控器一样工作,并控制总线。 总线上每一个设备都有一个独一无二的地址,根据设备它们自己的能力,它们可以作为发射器或接收器工作。非常适合在器件之间进行近距离、非经常性的数据通信。IIC不可以实现全双工(即异步)。 标准模式 高达 100kHz 快速模式 高达 400kHz 超快速模式 高达 1 MHz 2).SPI(Serial Peripheral Interface:串行外设接口) :主从设备间既可以实现同步通讯也可以异步通讯,SPI总线由三条信号线组成:串行时钟(SCLK)、串行数据输出(SDO)、串行数据输入(SDI),当有多个从设备时,还可以增加一条从设备(SS)选择线。SPI总线可以实现 多个SPI设备互相连接。提供SPI串行时钟的SPI设备为SPI主机或主设备(Master),其他设备为SPI从机或从设备(Slave)。 速度高达18M bit/s 如果用通用IO口模拟SPI总线,必须要有一个输出口(SDO)或者一个输入口(SDI),另一个口则视实现的设备类型而定,如果要实现主从设备,则需输入输出口,若只实现主设备,则需输出口即可,若只实现从设备,则只需输入口即可。数据(输入或者输出)在时钟上沿或下沿时改变,在紧接着的下沿或上沿被读取。这样至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。SCL信号线是由主设备控制,从设备不能控制SCL信号线,所以至少有一个主控设备。这样传输的特点:与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据。也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制。 SPI 通讯接口有:SS( Slave Select),SCK( Serial Clock),MOSI (Master Output, Slave Input),MISO (Master Input, Slave Output)。 3).USART(Universal Asynchronous Receiver Transmitter:通用异步收发器):是用于控制计算机与串行设备的芯片, USART总线是异步串口,因此一般比前两种同步串口的结构要复杂很多。硬件上有两根线,一根用于发送,一根用于接收。有一点要注意的是,它提供了RS-232C数据终端设备接口,这样计算机就可以和调制解调器或其它使用RS-232C接口的串行设备通信了。目前市场上有USB转USART(如CP21xx ),也有USB转RS-232C设备(如PL230x)。其中USART 是TTL 电平3.3v,RS-232C是232电平5v. 显然,如果用通用IO口模拟UART总线,则需一个输入口,一个输出口。 不同之处大概就是上面所示,SPI和UART可以实现全双工,但I2C不行。IIC为两条线(时钟线SCL 和数据线SDA),SPI 为至少3条线,可同步接收和发送,不过只有一条时钟线,如果SPI设备多的话,就还可加入CS片选线选择从机设备。USART 硬件IIC和模拟IIC的区别? 硬件IIC指的是MCU上面专有的IIC接口(SDA数据线和SCL时钟线)及在APB1上有IIC的指令寄存器,可以直接配置IIC指令寄存器及连接SDA线及SCL线至需要进行通讯的设备(IC或者EEPROM)进行读写通讯。(在STM32的数据手册上面写的需要配置的就是硬件IIC) 如下图所示: 模拟IIC与上面的硬件IIC有所不同,指通过通用的GPIO口进行配置与相应的IIC设备(如EEPROM—AT24C02)进行通讯,需要配置两个通用IO口,一个作为SDA线,一个作为SCL线与IC进行通讯,如同下面所示: 上面图片是模拟IIC使用GPIO口进行配置,在进行IIC的配置前,首先要配置GPIO口。 模拟IIC的配置&时序图& 协议 首先要配置GPIO口,现在以电路图的连接为主,PF6设置为SCL & PG7设置为SDA,则 首先需要使能GPIOF,PF6/SCL因为是时钟线,所以只要配置成输出就好了。 PF7因为是数据GPIO口,数据可以从MCU发送到IC(AT24C02),也可以接收IC(AT24C02)发送的数据。所以需要发送和接收数据,把PF7/SDA设置为输入输出口。 1)IIC总线协议的时序图: 1))。看上面的时序图,每个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一个应答位(即一帧共9位)。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断,若未收到应答信号,则判断为受控单元故障。 2)).IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。 3))。主控器向被控器发送的信息种类有:空闲状态,初始信号,结束信号,7位地址码,读/写控制位,10位地址码,数据字节,应答信号,脉冲时钟。 被控器向主控器发送的信息种类有:应答信号,数据字节,时钟低电平。 4))。空闲状态: 时钟线SCL与数据线SDA均为高电平信号。 初始信号:在空闲状态时,SCL与SDA均为高电平的状态下,SDA数据线由高电平变为低电平。 结束信号:在SDA数据线和SCL均为低电平的状态下,SDA数据线由低电平变为高电平。 读写控制位:IIC总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。 应答信号:在发送完8bit 位后,在第九位时钟的时候,由被控器发送一个应答位,主控器接收发送的应答位。当应答位为0时,表示应答。当应答位为1时,表示无应答。 2).EEPROM—AT24C02的配置 1))。目前我写的IC/受控器为AT24C02C,该芯片的总容量为256个字节(2kbit),IIC总线上启动信号后,需要发送一个8位设备地址,来使能IIC总线上与设备地址匹配的设备,匹配的设备会有一个“0”(ACK)应答,这样才能进行读写操作。一般所有的EEPROM设备地址前四位是固定的1010,接下来的三位是设备地址(不同容量规则不同),然后最后一位数是读写使能位。AT24C02的设备地址不需要设备地址,所以为000,所以这个芯片的读是0xA1,写是0xA0. 2)).EEPROM的读协议如下: MCU写入AT24C02的流程为:起始信号—-》MCU发送设备地址给AT24C02(0xA0)/写—–》MCU接收从AT24C02的应答信号—-》MCU发送字节地址给AT24C02(写)(这里有一点很重要,之前一直不明白,就是字节地址都是怎么样写的?一般的数据手册都没有写这个,所以一度很困惑。后来经由大神的解释弄清楚了,因为AT24C02的内存为256个字节,因而0x00~0xFF就代表的是这个范围,所以地址就是这个范围内的。例AT20C128的为16384个字节,为0x0000~0x3FFF. )—–》MCU接收从AT24C02的应答信号—-》MCU接收AT24C02发送的数据——》MCU接收从AT24C02的应答信号—–》结束信号 3)).EEPROM的写协议如下: MCU从AT23C02读的流程为:起始信号—-》MCU发送设备地址给AT24C02(0xA0)/写—–》MCU接收从AT24C02的应答信号—-》MCU发送字节地址给AT24C02(写)—–》MCU接收从AT24C02的应答信号—-》起始信号—–》MCU发送设备地址给AT24C02(0xA1)/读—–》MCU接收AT24C02发送的数据——》MCU接收从AT24C02的应答信号—–》结束信号 程序 IIC.C #include 《stm32f0xx.h》 #include “IIC.h” #include “System.h” void IIC_Init(void)/*initation the SDA=1 & SCL=1*/ { //I2C idle status SCLEnable; //SCL=1 SYS_DelayUs(2); SDAEnable; //SDA=1 SYS_DelayUs(2); SDA_OUT(); SCL_OUT(); } void IIC_Start(void)//Start the signal, when SCL&SDA in the Init status,pull down the SDA from 1 to 0 when SCL=1 to start the signal. { SDA_OUT(); SDAEnable; //SDA=1 SCLEnable; //SCL=1 SYS_DelayUs(2); SDAClean; //Pull down SDA from 1 to 0 when SCL=1 to start the signal transfer SYS_DelayUs(1); //Baud Rate SCLClean;//SCL=0. The SDA only could valid when SCL=0; } void IIC_Stop(void)//Stop the signal,to pull up the SDA from 0 to 1 when SCL=1 { SDA_OUT(); SCLClean; //SCL=0. The SDA only could change when SCL=0; SDAClean; //SDA=0 SYS_DelayUs(2); SCLEnable; //SCL=1 SYS_DelayUs(1); SDAEnable; //Pull up the SDA from 0 to 1 when SCL=1 to stop the signal.I2C idle status when SCL=1&SDA=1 SYS_DelayUs(2); } /*TO write the string from MCU to the corresponding IC ,such as 24C02, The first step to search the Device address(0xa0 write,0xa1 read),wait for the ACK/NoAck, then search the Word address want to write in the address,wait for ACK/NoAck, then write the string*/ //I2C when transfer the data.The data in SDA must stable when SCL=1. The data in SDA could change only when SCL=0. void IIC_Write(unsigned char String) { unsigned char i; SDA_OUT(); SCLClean;//The data in SDA could change only when SCL=0 SYS_DelayUs(1); for(i=0;i《8;i++) { if((String&0x80)==0) SDAClean; else SDAEnable; String=String《《1; SYS_DelayUs(1); SCLEnable;//The SDA must stble when SCL=1 SYS_DelayUs(2); SCLClean; SYS_DelayUs(1); } } /*TO Read the string from IC(Such as 24C02) to MCU, The first step to search the Device address(0xa0 write,0xa1 read),wait for the ACK/NoAck, then search the Word address want to write in the address,wait for ACK/NoAck, then receive the string*/ //I2C when receive the data from IC. MCU can read it.The data in SDA must stable when SCL=1. The data in SDA could change only when SCL=0. unsigned char IIC_Read(unsigned char ack) { unsigned char i,receive=0; SDA_IN(); for(i=0;i《8;i++) { SCLClean; SYS_DelayUs(2); SCLEnable; receive 《《=1; if((GPIOF-》IDR&GPIOF_IDR7)==0x80) receive++; SYS_DelayUs(2); } if(!ack) IIC_Ack(); else IIC_NoAck(); return receive; } void IIC_Ack(void)//Response signal,Make sure that when SCL=1,SDA=0. { SCLClean;//SCL=0 SDA_OUT(); SDAClean;//SDA=0,The data in SDA must stable when SCL=1. The data in SDA could change only when SCL=0. SYS_DelayUs(2); SCLEnable;//SCL=1 SYS_DelayUs(2); SCLClean;//SCL=0 } void IIC_NoAck(void)//No Response signal,Make sure that when SCL=1,SDA=1. { SCLClean;//SCL=0 SDA_OUT(); SDAEnable;//SDA=1,The data in SDA must stable when SCL=1. The data in SDA could change only when SCL=0. SYS_DelayUs(2); SCLEnable;//SCL=1 SYS_DelayUs(2); SCLClean;//SCL=0 } unsigned char IIC_Wait_Ack(void) { unsigned char i=0; SDA_IN(); SYS_DelayUs(1); SCLEnable; SYS_DelayUs(1); while((GPIOF-》IDR&GPIOF_IDR7)==0X80) { i++; if(i》250) { IIC_Stop(); return 1; } } SYS_DelayUs(1); SCLClean; return 0; } IIC.h #if !defined(IIC_H) #define IIC_H #define SDAClean GPIOF-》BSRR=1《《(16+7) //SDA=0 #define SDAEnable GPIOF-》BSRR=1《《(0+7) //SDA=1 #define SCLClean GPIOF-》BSRR=1《《(16+6) //SCL=0 #define SCLEnable GPIOF-》BSRR=1《《(0+6) //SCL=1 #define SDA_IN() GPIOF-》MODER=(GPIOF-》MODER&~(3《《(7*2)))|(0《《(7*2)) #define SDA_OUT() GPIOF-》MODER=(GPIOF-》MODER&~(3《《(7*2)))|(1《《(7*2)) #define SCL_OUT() GPIOF-》MODER=(GPIOF-》MODER&~(3《《(6*2)))|(1《《(6*2)) #define GPIOF_IDR7 ((uint32_t)0x00000080U) void SystemClock(void); void IIC_Init(void); void IIC_Start(void); void IIC_Stop(void); void IIC_Write(unsigned char String); unsigned char IIC_Read(unsigned char ack); void IIC_Ack(void); void IIC_NoAck(void); unsigned char IIC_Wait_Ack(void); #endif EEPROM.c #include 《stm32f0xx.h》 #include 《String.h》 #include “IIC.h” #include “EEPROM.h” #include “System.h” void EEPROM_Init(void) { IIC_Init(); return; } unsigned long AT24C02_ReadOneByte(unsigned long ReadAddr) { unsigned long temp=0; IIC_Start(); IIC_Write(0XA0); IIC_Wait_Ack(); IIC_Write(ReadAddr); IIC_Wait_Ack(); SYS_DelayUs(2); IIC_Start(); IIC_Write(0XA1); IIC_Wait_Ack(); temp=IIC_Read(0); IIC_Stop(); return temp; } void AT24C02_WriteOneByte(unsigned long WriteAddr,unsigned long DataToWrite) { IIC_Start(); IIC_Write(0XA0); IIC_Wait_Ack(); IIC_Write(WriteAddr); IIC_Wait_Ack(); IIC_Write(DataToWrite); IIC_Wait_Ack(); } void AT24C02_Read(unsigned long ReadAddr,unsigned long *pBuffer,unsigned long NumToRead) { while(NumToRead) { *pBuffer++=AT24C02_ReadOneByte(ReadAddr++); NumToRead--; } } void AT24C02_Write(unsigned long WriteAddr,unsigned long *pBuffer,unsigned long NumToWrite) { while(NumToWrite--) { AT24C02_WriteOneByte(WriteAddr,*pBuffer); WriteAddr++; pBuffer++; } } EEPROM.h #if !defined(EEPROM_H) #define EEPROM_H #define EEPROM_ADDRESS 0xa0 void EEPROM_Init(void); void EEPROM_Write(unsigned long Address,const void *pData,unsigned long Length); void EEPROM_Read(unsigned long Address,unsigned char *ReadData,unsigned long Length); unsigned long AT24C02_ReadOneByte(unsigned long ReadAddr); void AT24C02_WriteOneByte(unsigned long WriteAddr,unsigned long DataToWrite); void AT24C02_Read(unsigned long ReadAddr,unsigned long *pBuffer,unsigned long NumToRead); void AT24C02_Write(unsigned long WriteAddr,unsigned long *pBuffer,unsigned long NumToWrite); #endif 在这一定注意SCL时钟线,就像有生命的心脏一样,一定要保持它的跳动,不能有停止中断的时候,因此Delay语句就很重要了,要是SCL时钟的delay恰到好处,所以当有示波器就可以看出写的程序是否是遵循这个道理。如果没有,完成可以根据程序手绘一个,同时根据绘制的时序图可以反映出写的程序是否正确。这个下面是我手绘的版本。这是比较笨的方法,但是很有效果。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1614 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1541 浏览 1 评论
970 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
682 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1592 浏览 2 评论
1863浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
644浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
515浏览 3评论
531浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
504浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 00:57 , Processed in 0.744077 second(s), Total 76, Slave 60 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号