基于stm32单片机的模拟IIC时序(附源码)

我下面要说的是基于stm32单片机的模拟IIC时序,以及是一些要注意的事项;结合自己所做的MMA7455加速度传感器,我把模拟IIC的源代码贴了出来,大家可以参考一下。

1.因为在IIC协议中,当总线空闲的时候,SDA和SCL都为高电平,所以硬件电路中SDA和SCL引脚都要接上拉电阻。

2.注意开始信号,停止信号,响应信号,非响应信号的时序,特别是要留意高低电平时间的延时:

3.应答信号分为主机应答和从机应答:
    主机应答是在主机从从机中读取数据时每次读取完一个字节的数据后主机给从机的一个应答信号,表示主机已收到数据了。
    从机应答是指主机给从机发送数据时从机给主机的应答,给一个应答就代表从机已经收到了数据,为主机接下来的工作做个判断。主机在核查从机的应答信号的时候,必须先将SDA总线拉高,释放总线。
    非应答信号是主机给从机的,当读取完一字节数据以后,主机不再去读取数据就给从机一个非应答信号,接着一个停止信号,直接给停止信号也是可以结束此次读操作,但是会对后面的操作带来影响。

基于stm32单片机的模拟IIC时序(附源码)_第1张图片

主机发送一个字节的数据,从高位开始发送。SCL位高电平的时候,数据必须保持稳定,所以可以在SCL为低电平时组织数据;同理读取数据也是类似的。
基于stm32单片机的模拟IIC时序(附源码)_第2张图片


4.SCL是单向的,由master控制。SDA是双向的,master可以控制,slaver也可以控制。
主机给从机写入数据时,SDA在SCL低电平的时候变化,SDA在SCL高电平的时候保持不变,也就是说在SCL上升沿时写入数据
主机从从机读取数据时,SDA在SCL高电平的时候变化,SDA在SCL低电平的时候保持不变也就是说在SCL下升沿时读出数据
需要注意的是:写入数据时就是要先改变SDA的值再去制造一个SCL的上升沿,读取数据时就是要先改变SDA的值再去制造一个SCL的下降沿,先后顺序必须把握好。
下面是我在用MMA7455加速度传感器时,写的IIC时序;程序已验证过可使用,大家可以参考一下

 C Code 
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#define  mma7455addree  0x3a                                           //每个设备有还会有自己的设备地址
#define sdah GPIO_SetBits(GPIOA,GPIO_Pin_0)
#define sclh GPIO_SetBits(GPIOA,GPIO_Pin_1)

#define sdal GPIO_ResetBits(GPIOA,GPIO_Pin_0)
#define  scll GPIO_ResetBits(GPIOA,GPIO_Pin_1)


void  sdain()
{
    GPIO_InitTypeDef i2csdain;
    i2csdain.GPIO_Pin = GPIO_Pin_0;

    i2csdain.GPIO_Mode = GPIO_Mode_IN_FLOATING;        //设置为浮空输入
    GPIO_Init(GPIOA, &i2csdain);
}


void  sdaout()
{
    GPIO_InitTypeDef i2csdaout;    
    i2csdaout.GPIO_Pin = GPIO_Pin_0;
    i2csdaout.GPIO_Mode = GPIO_Mode_Out_PP;
    i2csdaout.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &i2csdaout);
}


void  sclout()
{
    GPIO_InitTypeDef i2csclout;
    SystemInit();
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    i2csclout.GPIO_Pin = GPIO_Pin_1;
    i2csclout.GPIO_Mode = GPIO_Mode_Out_PP;
    i2csclout.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &i2csclout);
}


void  i2cst()                     //启动信号的函数
{

    sdaout();

    sclh;
    sdah;
    Delay_us(
10 );

    sdal;
    Delay_us(
5 );
    scll;
}

void  i2ced()               //停止信号的函数
{
    sdaout();

    sdal;
    sclh;
    Delay_us(
10 );

    sdah;
    Delay_us(
5 );
}


void  i2cack()              //等待信号,当发送器发送一个数据之后,需要等待从接收端发过来的应答信号,主机等待从机应答
{
     unsigned    char  i;
    sdain();

    sclh;
    Delay_us(
5 );
    
while ((GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) ==  1 ) && (i <  250 ))  i++;     //检测到SDA为低,也就是从机产生了应答信号或者 从机没有应答信号,导致 超时,都会跳出该函数。
    scll;
    Delay_us(
10 );
}


void  i2cwrda( unsigned   char  da)                       //写入一个字节的函数
{
     unsigned   char  i, temp, temp1;
    sdaout();
    scll;
    temp1 = da;

    
for (i =  0 ; i <  8 ; i++)
    {
        scll;
        temp = temp1;
        temp = temp & 0x80; 
        
if (temp == 0x80)
        {
            sdah;
        }
        
else
        {
            sdal;
        }
        Delay_us(
15 );
        sclh;
        Delay_us(
15 );
        temp1 = temp1 << 
1 ;

    }

}


unsigned   char  i2creda()                              //读取 一个字节的函数
{
     unsigned   char  i, j;
    sdain();
    
for (i =  0 ; i <  8 ; i++)
    {
        sclh;
        Delay_us(
5 );
        j = (j << 
1 ) | GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
        scll;
        Delay_us(
5 );
    }
    
return  j;
}


void  mma7455Write( unsigned   char  addr,  unsigned   char  dat)     //往 mma7455的addr地址写入addr数据
{

    i2cst();
    i2cwrda(0x3a);
    i2cack();
    i2cwrda(addr);
    i2cack();
    i2cwrda(dat);
    i2cack();
    i2ced();


}


unsigned   char  mma7455 Read( unsigned   char  addr)         //读取mma7455的addr地址的数据
{
    
unsigned   char  num1;
    i2cst();
    i2cwrda(0x3a);
    i2cack();
    i2cwrda(addr);
    i2cack();

    i2cst();
    i2cwrda(0x3b);
    i2cack();
    num1 = i2creda();
    i2ced();
    
return  (num1);
}

 
其实多机通信可以用一个简单的办法解决,只要在只要在上面mma7455Write,mma7455Read函数中用if语句根据不同的标志位选择相应的设备地址,要读取地址即可。大家可以去试一下!

这是我个人的一些总结,有什么不对的望指正!大家一起进步!

你可能感兴趣的:(stm32)