×
嵌入式 > 技术百科 > 详情

基于STM8的IIC协议通信

发布时间:2021-05-13 发布时间:
|

一、 综述

I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备。


它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU和被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。但在STM8中,400kHZ已经是最快速度了。


在往后的模块调试中也经常涉及,是一个很常见并且很好用的协议。


二、STM8S103中手册对I2C简介

看完中文资料手册,个人觉得比较浅显,具体使用在后面我会贴出来。

三、 I2C详细解析

I2C总共由五个核心函数,分别为:

①起始信号

②停止信号

③应答信号

④发送数据

⑤接收数据

通过这五个核心基本函数就能于大多数的传感进行通信了。

以下对各个部分进行详细介绍,附上部分主要代码,各位可以参考一下。

3.1 起始信号

当SCL为高电平期间,SDA由高电平到低电平的跳变过程;起始信号是一种电平跳变时序信号,而不是一个电平信号,如上图虚线框所。

1
2
3
4
5
6
7
8
9
10
11
12
voidStart_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由低电平到高电平的跳变过程;停止信号也是一种电平跳变时序信号,而不是一个电平信号,如上图虚线框所。

1
2
3
4
5
6
7
voidEnd_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);//数据线拉高
1
}

3.3 应答信号

应答信号有两种:分别是主动应答信号和主动不应答信号

①Ack(主动拉低SDA形成应答信号)

I2C总线的数据都是以字节(8位)的方式传送的,发送器件每发送一个字节之后,在时钟的第9个脉冲期间释放数据总线,由接收器发送一个 ACK(把数据总线的电平拉低)来表示数据成功接收。

1
2
3
4
5
6
7
8
9
//主动应答信号
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。

1
2
3
4
5
6
7
8
9
10
//主动不应答
voidvIIC_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(等待应答信号)

该信号在主机发送完数据后等待从机应答时候使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
return1;
}
else
//高 无应答
{
GPIO_WriteLow(GPIOD, GPIO_PIN_3);
IIC_Delay_4us();
GPIO_Init(GPIOD, GPIO_PIN_2, GPIO_MODE_OUT_PP_HIGH_FAST);//SDA
return0;
}
}

3.4 发送数据

在发送起始信号后开始通信,主机发送一个8位数据。然后,主机释放SDA线并等待从从机发出得确认信号(ACK)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
voidSend_Data_IIC_(uint8_t Data){
inti;
//拉低数据线和时钟线
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信号给从机,以通知接收端结束数据接收。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//接收函数
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);
returnuReceiveByte;
}


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的核心函数,对于每个函数我已经写的很清楚。


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
家庭网络:从带宽共享走向内容共享