单片机练习 - I2C总线协议
这次利用单片机通过软件模拟I2C总线协议, 并对基于I2C协议的AT24C02 EEPROM进行读写操作, 具体说明与功能见代码注释.
AT24C02与单片机的连接电路图如下:
单片机利用P2.0模拟SDA, P2.1模拟SCL.
程序代码:
经过两个星期的单片机学习, 对单片机有了一个深层次的了解. 而这次以后, 将不会连续关注单片机, 会将重心放回Asp.net开发上....
AT24C02与单片机的连接电路图如下:
单片机利用P2.0模拟SDA, P2.1模拟SCL.
程序代码:
I2C总线协议
1//用软件方法模拟I2C总线协议来读写AT24C02 EEPROM
2//每次上电后, 继续按照上次的计数结果计数, 每半秒计数一次
3#include <reg52.H>
4#include <intrins.H>
5
6#define uchar unsigned char
7sbit SDA = P2^0;
8sbit SCL = P2^1;
9
10//仿真延时5.43us
11void delay5us()
12{
13 _nop_();
14}
15
16//延时10ms, 仿真约11ms
17//实际测试中, 对于写一个字节, 只须2ms
18void delay10Ms()
19{
20 uchar a,b;
21 for(a=50;a>0;a--)
22 for(b=100;b>0;b--);
23}
24
25//延时2ms
26void delay2Ms()
27{
28 uchar a,b;
29 for(a=10;a>0;a--)
30 for(b=100;b>0;b--);
31}
32
33/**//***************************** I2C总线协议 ************************************/
34//起始信号
35void start()
36{
37 SDA = 1; //启动I2C总线
38 SCL = 1;
39 delay5us(); //延时
40 SDA = 0; //起始信号
41 delay5us(); //SDA拉低时间至少4us后, 才能拉低SCL
42 SCL = 0;
43}
44
45//终止信号
46void stop()
47{
48 SDA = 0;
49 SCL = 1; //SCL拉高至少4us后, 才能拉高SDA, 产生终止信号
50 delay5us();
51 SDA = 1;
52 delay5us(); //保持SDA拉高4.7us以上
53 //终止后, 总线处于空闲状态
54}
55
56//发送应答
57void ack()
58{
59 SDA = 0;
60 SCL = 1;
61 delay5us();
62 SCL = 0;
63 SDA = 1;
64}
65
66//发送非应答
67void nack()
68{
69 SDA = 1;
70 SCL = 1;
71 delay5us();
72 SCL = 0;
73}
74
75//获取应答, 有应答返回0, 非应答返回1
76bit getAck()
77{
78 bit flag;
79 SDA = 1;
80 SCL = 1;
81 flag = SDA;
82 SCL = 0;
83 return flag;
84}
85
86//发送一字节数据, 有应答返回0, 非应答返回1
87bit sendByte(uchar dat)
88{
89 uchar i;
90 for(i = 0; i < 8; i++)
91 {
92 dat = dat << 1; //左移, 最高位将移到CY中
93 SDA = CY;
94 SCL = 1;
95 delay5us();
96 SCL = 0;
97 }
98 return getAck();
99}
100
101//接收一字节数据
102uchar recvByte()
103{
104 uchar i, tmp, dat;
105 for(i = 0; i < 8; i++)
106 {
107 SCL = 1;
108 delay5us();
109 if(SDA == 1)
110 {
111 tmp = 1;
112 }
113 else
114 tmp = 0;
115 dat = (dat << 1) | tmp;
116 SCL = 0;
117 delay5us();
118 }
119 return dat;
120}
121
122/**//***************************** I2C总线协议 ************************************/
123
124
125/**//***************************** AT24C02 EEPROM的读写操作 ************************************/
126//AT24C02 EEPROM 在TX1-B实验板上的地址是 1010 000B
127
128//向EEPROM指定地址写一字节数据
129void writeByte(uchar dat, uchar add)
130{
131 start();
132 sendByte(0xa0); //找出EEPROM芯片, 写数据
133 sendByte(add); //先写地址
134 sendByte(dat); //读数据
135 stop(); //释放总线
136 delay2Ms(); //发送完写数据, stop()后, 需要延时10ms让芯片完成内部写周期
137 //实际测试中, 写一字节, 只须2ms
138}
139
140//连续写指定长度的字节流(Page Write)
141void writeBytes(uchar * dats, uchar length, uchar add)
142{
143 uchar i;
144 start();
145 sendByte(0xa0);
146 sendByte(add);
147 for(i = 0; i < length; i++)
148 {
149 sendByte(dats[i]);
150 }
151 stop();
152 delay10Ms();
153}
154
155//读取EEPROM指定地址的一字节数据(Random Read)
156uchar readByte(uchar add)
157{
158 uchar dat;
159 start();
160 sendByte(0xa0); //找出EEPROM芯片, 写数据
161 sendByte(add); //先写地址
162 start();
163 sendByte(0xa1); //读数据
164 dat = recvByte();
165 nack(); //非响应
166 stop(); //释放总线
167 return dat;
168}
169
170//连续读指定大小的字节数(Sequential Read)
171void readBytes(uchar * buffer, uchar size, uchar add)
172{
173 uchar i, count = size - 1;
174 start();
175 sendByte(0xa0);
176 sendByte(add);
177 start();
178 sendByte(0xa1);
179 for(i = 0; i < count; i++)
180 {
181 buffer[i] = recvByte();
182 ack(); //应答
183 }
184 buffer[count] = recvByte();
185 nack(); //非应答, 结束
186 stop();
187}
188
189/**//***************************** AT24C02 EEPROM的读写操作 ************************************/
190sbit wela = P2^7; //数码管位选
191sbit dula = P2^6; //数码管段选
192
193//0-F数码管的编码(共阴极)
194unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,
195 0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
196void display(uchar v)
197{
198 unsigned char count;
199 unsigned char datas[] = {0, 0, 0};
200 datas[0] = v / 100;
201 datas[1] = v % 100 / 10;
202 datas[2] = v % 10;
203 for(count = 0; count != 3; count++)
204 {
205 //关位选, 去除对上一位的影响
206 P0 = 0xff;
207 wela = 1; //打开锁存, 给它一个下降沿量
208 wela = 0;
209 //段选
210 P0 = table[datas[count]];
211 dula = 1; //打开锁存, 给它一个下降沿量
212 dula = 0;
213 //位选
214 P0 = _crol_(0xfe, count); //选择第(count + 1) 个数码管
215 wela = 1; //打开锁存, 给它一个下降沿量
216 wela = 0;
217 delay2Ms();
218 }
219}
220
221uchar value, th, tl, tCount;
222void main()
223{
224 value = readByte(0); //向EEPROM地址0读一字节数据
225 tCount = 0;
226 EA = 1; //开中断
227 ET1 = 1; //允许T1中断
228 TMOD = 0x10; //工作方式1
229 TH1 = th = 0x4b;//(65536 - 50000/1.085) / 256; //定时50ms
230 TL1 = tl = 0xfd;//(65536 - 50000/1.085) - th * 256;
231 TR1 = 1; //T1开始计时
232 while(1)
233 {
234 if(tCount > 10) //满500ms, 加1
235 {
236 value++;
237 writeByte(value, 0); //将当前值保存在EEPROM地址0中
238 tCount = 0;
239 }
240 display(value);
241 }
242}
243
244void time1() interrupt 3
245{
246 TH1 = th;
247 TL1 = tl;
248 tCount++;
249}
1//用软件方法模拟I2C总线协议来读写AT24C02 EEPROM
2//每次上电后, 继续按照上次的计数结果计数, 每半秒计数一次
3#include <reg52.H>
4#include <intrins.H>
5
6#define uchar unsigned char
7sbit SDA = P2^0;
8sbit SCL = P2^1;
9
10//仿真延时5.43us
11void delay5us()
12{
13 _nop_();
14}
15
16//延时10ms, 仿真约11ms
17//实际测试中, 对于写一个字节, 只须2ms
18void delay10Ms()
19{
20 uchar a,b;
21 for(a=50;a>0;a--)
22 for(b=100;b>0;b--);
23}
24
25//延时2ms
26void delay2Ms()
27{
28 uchar a,b;
29 for(a=10;a>0;a--)
30 for(b=100;b>0;b--);
31}
32
33/**//***************************** I2C总线协议 ************************************/
34//起始信号
35void start()
36{
37 SDA = 1; //启动I2C总线
38 SCL = 1;
39 delay5us(); //延时
40 SDA = 0; //起始信号
41 delay5us(); //SDA拉低时间至少4us后, 才能拉低SCL
42 SCL = 0;
43}
44
45//终止信号
46void stop()
47{
48 SDA = 0;
49 SCL = 1; //SCL拉高至少4us后, 才能拉高SDA, 产生终止信号
50 delay5us();
51 SDA = 1;
52 delay5us(); //保持SDA拉高4.7us以上
53 //终止后, 总线处于空闲状态
54}
55
56//发送应答
57void ack()
58{
59 SDA = 0;
60 SCL = 1;
61 delay5us();
62 SCL = 0;
63 SDA = 1;
64}
65
66//发送非应答
67void nack()
68{
69 SDA = 1;
70 SCL = 1;
71 delay5us();
72 SCL = 0;
73}
74
75//获取应答, 有应答返回0, 非应答返回1
76bit getAck()
77{
78 bit flag;
79 SDA = 1;
80 SCL = 1;
81 flag = SDA;
82 SCL = 0;
83 return flag;
84}
85
86//发送一字节数据, 有应答返回0, 非应答返回1
87bit sendByte(uchar dat)
88{
89 uchar i;
90 for(i = 0; i < 8; i++)
91 {
92 dat = dat << 1; //左移, 最高位将移到CY中
93 SDA = CY;
94 SCL = 1;
95 delay5us();
96 SCL = 0;
97 }
98 return getAck();
99}
100
101//接收一字节数据
102uchar recvByte()
103{
104 uchar i, tmp, dat;
105 for(i = 0; i < 8; i++)
106 {
107 SCL = 1;
108 delay5us();
109 if(SDA == 1)
110 {
111 tmp = 1;
112 }
113 else
114 tmp = 0;
115 dat = (dat << 1) | tmp;
116 SCL = 0;
117 delay5us();
118 }
119 return dat;
120}
121
122/**//***************************** I2C总线协议 ************************************/
123
124
125/**//***************************** AT24C02 EEPROM的读写操作 ************************************/
126//AT24C02 EEPROM 在TX1-B实验板上的地址是 1010 000B
127
128//向EEPROM指定地址写一字节数据
129void writeByte(uchar dat, uchar add)
130{
131 start();
132 sendByte(0xa0); //找出EEPROM芯片, 写数据
133 sendByte(add); //先写地址
134 sendByte(dat); //读数据
135 stop(); //释放总线
136 delay2Ms(); //发送完写数据, stop()后, 需要延时10ms让芯片完成内部写周期
137 //实际测试中, 写一字节, 只须2ms
138}
139
140//连续写指定长度的字节流(Page Write)
141void writeBytes(uchar * dats, uchar length, uchar add)
142{
143 uchar i;
144 start();
145 sendByte(0xa0);
146 sendByte(add);
147 for(i = 0; i < length; i++)
148 {
149 sendByte(dats[i]);
150 }
151 stop();
152 delay10Ms();
153}
154
155//读取EEPROM指定地址的一字节数据(Random Read)
156uchar readByte(uchar add)
157{
158 uchar dat;
159 start();
160 sendByte(0xa0); //找出EEPROM芯片, 写数据
161 sendByte(add); //先写地址
162 start();
163 sendByte(0xa1); //读数据
164 dat = recvByte();
165 nack(); //非响应
166 stop(); //释放总线
167 return dat;
168}
169
170//连续读指定大小的字节数(Sequential Read)
171void readBytes(uchar * buffer, uchar size, uchar add)
172{
173 uchar i, count = size - 1;
174 start();
175 sendByte(0xa0);
176 sendByte(add);
177 start();
178 sendByte(0xa1);
179 for(i = 0; i < count; i++)
180 {
181 buffer[i] = recvByte();
182 ack(); //应答
183 }
184 buffer[count] = recvByte();
185 nack(); //非应答, 结束
186 stop();
187}
188
189/**//***************************** AT24C02 EEPROM的读写操作 ************************************/
190sbit wela = P2^7; //数码管位选
191sbit dula = P2^6; //数码管段选
192
193//0-F数码管的编码(共阴极)
194unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,
195 0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
196void display(uchar v)
197{
198 unsigned char count;
199 unsigned char datas[] = {0, 0, 0};
200 datas[0] = v / 100;
201 datas[1] = v % 100 / 10;
202 datas[2] = v % 10;
203 for(count = 0; count != 3; count++)
204 {
205 //关位选, 去除对上一位的影响
206 P0 = 0xff;
207 wela = 1; //打开锁存, 给它一个下降沿量
208 wela = 0;
209 //段选
210 P0 = table[datas[count]];
211 dula = 1; //打开锁存, 给它一个下降沿量
212 dula = 0;
213 //位选
214 P0 = _crol_(0xfe, count); //选择第(count + 1) 个数码管
215 wela = 1; //打开锁存, 给它一个下降沿量
216 wela = 0;
217 delay2Ms();
218 }
219}
220
221uchar value, th, tl, tCount;
222void main()
223{
224 value = readByte(0); //向EEPROM地址0读一字节数据
225 tCount = 0;
226 EA = 1; //开中断
227 ET1 = 1; //允许T1中断
228 TMOD = 0x10; //工作方式1
229 TH1 = th = 0x4b;//(65536 - 50000/1.085) / 256; //定时50ms
230 TL1 = tl = 0xfd;//(65536 - 50000/1.085) - th * 256;
231 TR1 = 1; //T1开始计时
232 while(1)
233 {
234 if(tCount > 10) //满500ms, 加1
235 {
236 value++;
237 writeByte(value, 0); //将当前值保存在EEPROM地址0中
238 tCount = 0;
239 }
240 display(value);
241 }
242}
243
244void time1() interrupt 3
245{
246 TH1 = th;
247 TL1 = tl;
248 tCount++;
249}
经过两个星期的单片机学习, 对单片机有了一个深层次的了解. 而这次以后, 将不会连续关注单片机, 会将重心放回Asp.net开发上....