一、 综述
I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备。
它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU和被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。但在STM8中,400kHZ已经是最快速度了。
在往后的模块调试中也经常涉及,是一个很常见并且很好用的协议。
二、STM8S103中手册对I2C简介
看完中文资料手册,个人觉得比较浅显,具体使用在后面我会贴出来。
三、 I2C详细解析
I2C总共由五个核心函数,分别为:
①起始信号
②停止信号
③应答信号
④发送数据
⑤接收数据
通过这五个核心基本函数就能于大多数的传感进行通信了。
以下对各个部分进行详细介绍,附上部分主要代码,各位可以参考一下。
3.1 起始信号
当SCL为高电平期间,SDA由高电平到低电平的跳变过程;起始信号是一种电平跳变时序信号,而不是一个电平信号,如上图虚线框所。
void Start_Signal_IIC_(void){ //起始信号: GPIO_WriteHigh(GPIOD, GPIO_PIN_2);//数据线 IIC_Delay_4us(); GPIO_WriteHigh(GPIOD, GPIO_PIN_3);//时钟线 IIC_Delay_4us(); GPIO_WriteLow(GPIOD, GPIO_PIN_2); //数据线 IIC_Delay_4us(); GPIO_WriteLow(GPIOD, GPIO_PIN_3); //时钟线 IIC_Delay_4us(); }
3.2 停止信号
当SCL为高电平期间,SDA由低电平到高电平的跳变过程;停止信号也是一种电平跳变时序信号,而不是一个电平信号,如上图虚线框所。
void End_Data_IIC_() { GPIO_WriteLow(GPIOD, GPIO_PIN_2); //数据线拉低 IIC_Delay_4us(); GPIO_WriteHigh(GPIOD, GPIO_PIN_3);//时钟线拉高 IIC_Delay_4us(); GPIO_WriteHigh(GPIOD, GPIO_PIN_2);//数据线拉高
}
3.3 应答信号
应答信号有两种:分别是主动应答信号和主动不应答信号
①Ack(主动拉低SDA形成应答信号)
I2C总线的数据都是以字节(8位)的方式传送的,发送器件每发送一个字节之后,在时钟的第9个脉冲期间释放数据总线,由接收器发送一个 ACK(把数据总线的电平拉低)来表示数据成功接收。
//主动应答信号
void vIIC_Ack() { GPIO_WriteLow(GPIOD, GPIO_PIN_2); IIC_Delay_4us(); GPIO_WriteHigh(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); GPIO_WriteLow(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); }
②NAck(主动不拉低SDA不形成应答信号)
在时钟的第9个脉冲期间发送器释放数据总线,接收器不拉低数据总线表示一个NACK,NACK有两种用途:
a. 一般表示接收器未成功接收数据字节;
b. 当接收器是主控器时,它收到最后一个字节后,应发送一个NACK信号,以通知被控发送器结束数据发送,并释放总线,以便主控接收器发送一个停止信号STOP。
//主动不应答 void vIIC_NAck() { GPIO_WriteHigh(GPIOD, GPIO_PIN_2); IIC_Delay_4us(); GPIO_WriteHigh(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); GPIO_WriteLow(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); }
③ReadAck(等待应答信号)
该信号在主机发送完数据后等待从机应答时候使用。
u8 bIIC_ReadACK() { GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_IN_PU_IT);//将SDA改为输入模式。 GPIO_WriteHigh(GPIOD, GPIO_PIN_3); //拉高时钟线。 IIC_Delay_4us(); if(IIC_SDA_R != 0) { //低 有应答 GPIO_WriteLow(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_OUT_PP_HIGH_FAST);//SDA return 1; } else //高 无应答 { GPIO_WriteLow(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_OUT_PP_HIGH_FAST);//SDA return 0; } }
3.4 发送数据
在发送起始信号后开始通信,主机发送一个8位数据。然后,主机释放SDA线并等待从从机发出得确认信号(ACK)。
void Send_Data_IIC_(uint8_t Data){ int i;
//拉低数据线和时钟线 GPIO_WriteLow(GPIOD, GPIO_PIN_3); GPIO_WriteLow(GPIOD, GPIO_PIN_2); for(i=0;i<8;i++) { if(Data&0x80) GPIO_WriteHigh(GPIOD, GPIO_PIN_2); else GPIO_WriteLow(GPIOD, GPIO_PIN_2); Data= Data<<1; IIC_Delay_2us(); GPIO_WriteHigh(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); GPIO_WriteLow(GPIOD, GPIO_PIN_3); IIC_Delay_2us(); } }
3.5 接收数据
在发送起始信号后开始通信,主机发送一个8位数据。然后,从机收到数据返回一个确认信号(ACK)给主机,这时候主机才开始接收数据,待主机接收数据完成后,发送一个NACK信号给从机,以通知接收端结束数据接收。
//接收函数 uint8_t uIIC_RecvByte() { uint8_t i,uReceiveByte = 0; GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_IN_PU_IT); for(i=0;i<8;i++) { uReceiveByte <<= 1; IIC_Delay_4us(); GPIO_WriteHigh(GPIOD, GPIO_PIN_3);//高时钟线时读取数据电平 IIC_Delay_4us(); if(IIC_SDA_R !=0 ) { uReceiveByte|=0x01; } IIC_Delay_4us(); GPIO_WriteLow(GPIOD, GPIO_PIN_3); IIC_Delay_4us(); } GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_OUT_PP_HIGH_FAST); return uReceiveByte; }
3.6 I2C通信总过程
以下图完美地诠释了iic从接收到发送全过程中,SDA和SCL线的变化线。综合上面的解释和过程代码,这张图可以帮助记忆和理解。
四、例程
4.1 编译环境:
这里用的是IAR进行编译,较为好用,后期使用STM32的开发板可以推荐使用CUBE直接生成初始化函数,与Keil5相互搭配使用,很是方便。
4.2 主芯片:
我的主芯片是STM8S系列中的103,其中STM8S的003、005、和103、105,配置一样(外设和CPU频率,FLASH),在代码相同的情况下均可进行烧写。
4.3 代码&解析
iic代码可以驱动几乎市面上的所有时钟模块,所以这里的代码可以与时钟模块的代码相互调用。每个函数我都有加以解释,可以详细了解一下。
看了上面的代码也可以知道,这个协议是由数据线和时钟线,数据发送接收要求拉高数据线或拉低时钟线。所以这里推荐直接使用库函数的拉高低,如果要方便的话,再加个宏定义可以更加直观方便。
五、结尾
以上是iic的核心函数,对于每个函数我已经写的很清楚,下一篇博客我会将基于iic的时钟模块的传感通信博客贴出来,各位可以继续阅读下一篇博客做一下参考。