STM32总线实验

STM32总线实验

项目介绍

通过IIC总线,向EEPROM存储器24c02进行读写操作。在51单片机中,由于没有IIC接口,所以需要通过两个管脚模拟IIC协议的时序,从而进行IIC总线通信。而在STM32中,是具有IIC总线接口的,但是比较复杂,所以这里依然通过GPIO口模拟IIC时序。通过IIC总线与24C02进行通信,并通过串口返回数据。

知识点

  • 1、IIC是飞利浦公司推出的一种串行总线,可以连接多个设备。IIC总线仅包含两根信号线。一根为SDA(数据线),另一根为SCL(时钟线)。
  • 2、每一个连接到IIC总线都具有唯一的地址,每一个设备之间为与的关系。
  • 3、由主机发送起始s和终止p信号。起始信号后紧跟着的数据为寻址信号字节。通过这个字节可以选择主机想要与之通信的从机。这个字节的高7位表示地址,其中包括固定地址和可编程地址,可编程地址的位数决定了这种类型的芯片最多可以连接到IIC总线的数量。例如24c02芯片的7为地址为1010,后三位可编程为为A2,A1,A0。这三位为外接口,可通过连接高电平或者接地进行编程。寻址字节的最后一位为数据的传送方向,若为0,则表示主机向从机写数据;若为1,则表示主机需要向从机度数据。
  • 4、一问一答式。每次数据传送都需要进行应答。每个数据位均为8位,后面会跟一个从机或者主机的应答位,因此每帧数据为9位。
  • 5、每次数据传送都是由主机产生终止信号来结束,但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发送起始信号对另从机进行寻址。
  • 6、起始信号s;终止信号p;0/应答;1/非应答这四种信号的表示方法如下图。
    image

部分代码

  • 1、由于还是使用STM32 的两个GPIO口进行模拟IIC协议,所以这里的程序主要包括两层,首先就是需用通过GPIO口模拟IIC协议,因此需要进行模拟操作。这部分程序包括了基本的GPIO初始化部分,输入设置部分,输出设置部分,发送四种信号部分,等待从机应答部分,发送一个字节部分,读取一个字节部分。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//产生起始信号
void I2C_Start(void)
{
I2C_SDA_OUT();
I2C_SDA_H;
I2C_SCL_H;
delay_us(5);
I2C_SDA_L;
delay_us(6);
I2C_SCL_L;
}
//产生停止信号
void I2C_Stop(void)
{
I2C_SDA_OUT();
I2C_SCL_L;
I2C_SDA_L;
I2C_SCL_H;
delay_us(6);
I2C_SDA_H;
delay_us(6);
}
//主机产生应答信号ACK
void I2C_Ack(void)
{
I2C_SCL_L;
I2C_SDA_OUT();
I2C_SDA_L;
delay_us(2);
I2C_SCL_H;
delay_us(5);
I2C_SCL_L;
}
//主机不产生应答信号NACK
void I2C_NAck(void)
{
I2C_SCL_L;
I2C_SDA_OUT();
I2C_SDA_H;
delay_us(2);
I2C_SCL_H;
delay_us(5);
I2C_SCL_L;
}
//等待从机应答信号
//返回值:1 接收应答失败
// 0 接收应答成功
u8 I2C_Wait_Ack(void)
{
u8 tempTime=0;
I2C_SDA_IN();
I2C_SDA_H;
delay_us(1);
I2C_SCL_H;
delay_us(1);
while(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA))
{
tempTime++;
if(tempTime>250)
{
I2C_Stop();
return 1;
}
}
I2C_SCL_L;
return 0;
}
//I2C 发送一个字节
void I2C_Send_Byte(u8 txd)
{
u8 i=0;
I2C_SDA_OUT();
I2C_SCL_L;//拉低时钟开始数据传输
for(i=0;i<8;i++)
{
if((txd&0x80)>0) //0x80 1000 0000
I2C_SDA_H;
else
I2C_SDA_L;
txd<<=1;
I2C_SCL_H;
delay_us(2); //发送数据
I2C_SCL_L;
delay_us(2);
}
}
//I2C 读取一个字节
u8 I2C_Read_Byte(u8 ack)
{
u8 i=0,receive=0;
I2C_SDA_IN();
for(i=0;i<8;i++)
{
I2C_SCL_L;
delay_us(2);
I2C_SCL_H;
receive<<=1;
if(GPIO_ReadInputDataBit(GPIO_I2C,I2C_SDA))
receive++;
delay_us(1);
}
if(ack==0)
I2C_NAck();
else
I2C_Ack();
return receive;
}
  • 2、第二部分包括对24C02的基本读写函数。而基本的读写函数都需要通过IIC来控制24c02。因此这部分的函数是需要调用上面IIC操作函数的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include "AT24CXX.h"
//24c02读一个字节地址 数据
u8 AT24Cxx_ReadOneByte(u16 addr)
{
u8 temp=0;
I2C_Start();
if(EE_TYPE>AT24C16)
{
I2C_Send_Byte(0xA0);
I2C_Wait_Ack();
I2C_Send_Byte(addr>>8); //发送数据地址高位
}
else
{
I2C_Send_Byte(0xA0+((addr/256)<<1));//器件地址+数据地址
}
I2C_Wait_Ack();
I2C_Send_Byte(addr%256);//双字节是数据地址低位
//单字节是数据地址低位
I2C_Wait_Ack();
I2C_Start();
I2C_Send_Byte(0xA1);
I2C_Wait_Ack();
temp=I2C_Read_Byte(0); // 0 代表 NACK
I2C_NAck();
I2C_Stop();
return temp;
}
//24c02读2个字节地址 数据
u16 AT24Cxx_ReadTwoByte(u16 addr)
{
u16 temp=0;
I2C_Start();
if(EE_TYPE>AT24C16)
{
I2C_Send_Byte(0xA0);
I2C_Wait_Ack();
I2C_Send_Byte(addr>>8); //发送数据地址高位
}
else
{
I2C_Send_Byte(0xA0+((addr/256)<<1));//器件地址+数据地址
}
I2C_Wait_Ack();
I2C_Send_Byte(addr%256);//双字节是数据地址低位
//单字节是数据地址低位
I2C_Wait_Ack();
I2C_Start();
I2C_Send_Byte(0xA1);
I2C_Wait_Ack();
temp=I2C_Read_Byte(1); // 1 代表 ACK
temp<<=8;
temp|=I2C_Read_Byte(0); // 0 代表 NACK
I2C_Stop();
return temp;
}
/*******************************************************************************
* 函 数 名 : AT24Cxx_WriteOneByte
* 函数功能 : 24c02写一个字节地址 数据
* 输 入 : addr dt
* 输 出 : 无
*******************************************************************************/
void AT24Cxx_WriteOneByte(u16 addr,u8 dt)
{
I2C_Start();
if(EE_TYPE>AT24C16)
{
I2C_Send_Byte(0xA0);
I2C_Wait_Ack();
I2C_Send_Byte(addr>>8); //发送数据地址高位
}
else
{
I2C_Send_Byte(0xA0+((addr/256)<<1));//器件地址+数据地址
}
I2C_Wait_Ack();
I2C_Send_Byte(addr%256);//双字节是数据地址低位
//单字节是数据地址低位
I2C_Wait_Ack();
I2C_Send_Byte(dt);
I2C_Wait_Ack();
I2C_Stop();
delay_ms(10);
}
/*******************************************************************************
* 函 数 名 : AT24Cxx_WriteTwoByte
* 函数功能 : 24c02写2个字节地址 数据
* 输 入 : addr dt
* 输 出 : 无
*******************************************************************************/
void AT24Cxx_WriteTwoByte(u16 addr,u16 dt)
{
I2C_Start();
if(EE_TYPE>AT24C16)
{
I2C_Send_Byte(0xA0);
I2C_Wait_Ack();
I2C_Send_Byte(addr>>8); //发送数据地址高位
}
else
{
I2C_Send_Byte(0xA0+((addr/256)<<1));//器件地址+数据地址
}
I2C_Wait_Ack();
I2C_Send_Byte(addr%256);//双字节是数据地址低位
//单字节是数据地址低位
I2C_Wait_Ack();
I2C_Send_Byte(dt>>8);
I2C_Wait_Ack();
I2C_Send_Byte(dt&0xFF);
I2C_Wait_Ack();
I2C_Stop();
delay_ms(10);
}