CS5536上的I2C总线的应用
2008-05-21 13:28:41
以前我们使用AMD 的GX1搭配CS5530做主板,用的还比较顺手,后来AMD的GX1停产了,加上欧洲无铅化的要求,只好在AMD的LX CPU搭配CS5536开新主板,在这片新主板上,我们为了能有一个较好的AV输出,放上了一颗AIT2138的芯片,专门负责把VGA输出转换成AV输出,AIT2138这颗芯片也可以不用软件去控制,但是如果能用软件控制,当然更好,AIT2138上有一个I2C总线可以实现软件控制,我们看到CS5536上也有一个I2C总线的接口,于是我们把他们接到了一起,实践证明,非常有效,本文将重点介绍CS5536上的这个I2C总线接口的操作方法。
1、I2C总线介绍
I2C(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。I2C总线产生于在80年代,最初为音频和视频设备开发,如今主要在服务器管理中使用,其中包括单个组件状态的通信。
I2C总线最主要的优点是其简单性和有效性;另一个优点是它支持多主控, 其中任何能够进行发送和接收的设备都可以成为主控。一个主控能够控制信号的传输和时钟频率,当然,在任何时间点上只能有一个主控。
I2C总线只有两条线,数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,最高传送速率100kbps。
上图截自I2C总线的规范,它清楚地告诉我们,I2C总线,只有两条线,所有的设备(IC)都并联接在这两条线上,图中有两个MICRO CONTROLLER,A和B,告诉我们在I2C总线上可以连接多个主控。下面我们尽可能简单地说明它的通信原理。
上图同样截自I2C总线的规范,它清楚地告诉我们,I2C总线在空闲状态由两个上拉电阻把总线拉到高电平,器件对I2C总线的控制只有释放和拉低这两种方式,这一点对后面理解信号很有帮助。
I2C总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。
-
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
-
结束信号:SCL为低电平时,SDA由低电平向高电平跳变,结束传送数据。
-
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,可以判断为受控单元出现故障。
先看开始和结束信号,空闲状态,SDA、SCL高,当某个主控器件要传输或接收数据时,它要将SDA拉低,这便产生一个开始信号,之后,主控机要控制SCL,开始在SCL上发送时钟信号,当传输完毕后,主控机首先释放SCL,然后再释放SDA,经过一定时延后,认为传输结束。
在开始信号后,主控期间开始发送时钟信号,根据规范,SDA上的数据要在SCL为低时改变,在SCL为高时保持稳定,这样数据位就可以在总线上传输,数据必须以8位为一组传输,高位在前,低位再后,8位之后要有一个确认位,在确认位的时钟周期,主控机释放SDA,此时SDA为高,从机要把SDA拉低,同时保证在SCL为高电平时保持信号稳定,如果从机由于某些原因不能马上响应下一位的数据传输,可以一直保持SDA为低,直到可以响应为止,此时,主控机将处于等待状态。
根据规范,主控机在I2C上传输的第一个字节(8位)中,bit0–bit6为地址,bit7位读/写位,0表示要向从机写信息,即由主控机向从机发送数据,1表示读数据,即表示准备从从机接收数据。很显然,接在总线上的所有器件均可以收到这个信息,但应该只有与地址码相符的从机响应,否则将引起混乱。
基本上I2C总线的传输原理就是这样,并不复杂,可以很容易地使用单片机实现,下面是I2C总线规范的下载地址:
- 英文原版I2C总线规范:http://blog.whowin.net/specification/i2c-en.pdf
- 中文翻译版I2C总线规范:http://blog.whowin.net/specification/i2c-cn.pdf
个人建议尽量阅读原文文献,但中文版可以做参考,对概念的理解会有帮助。
2、CS5536上的I2C总线
在CS5536这颗芯片上集成了一个I2C总线,在芯片的DATASHEET中被称作为System Management Bus Controller,简称SMB,有7个内部寄存器控制总线,使操作变得十分方便。
SMBCTL2和SMBCTL3用来指定在SCL上发送的时钟频率,其中SMBCTL2的bit0是一个使能位,1–SMB使能,0–SMB禁止,bit1–bit7是时钟频率的低7位,SMBCTL3的bit0–bit7是时钟频率的高8位,一共15位(抱歉,这些与上图中标注不符,上图尽管截自规范,但很遗憾,其中的SMBCTL2和SMBCTL3是错的),始终频率的结果符合下面公式:
$ t_{SCL} = 2 \times SCLFRQ \times t_{clk} $
下面我们仅介绍工作在主控状态时,信号的产生方法,但CS5536也可以工作在从机模式。
-
START信号的产生
- 把SMBCTL1的INTEN置为0,采用轮询方式,如果使用中断方式,可以设为1
- 把SMBCTL1的START置为1,表示要产生START信号
这将使CS5536发出START信号,如果总线发生冲突,SMBST的BER将置为1;如果没有冲突,SMBST的MASTER和SDAST将被置为1,其中MASTER=1表明CS5536工作在主控方式,SDAST=1表明准备好发送数据位。
-
发送地址字节
发送的地址字节不能是自己的地址,另外,在SMB的寄存器中,有一个SMBADDR寄存器,这个寄存器是CS5536做从机时用的,里面放本机地址,用于比较总线上发出的地址信息,要特别说明的是,这个寄存器不是用于放地址字节的。实际发送地址字节与发送数据基本无异。
- 把7位地址位和一位数据传送方向位按照前面介绍I2C总线时介绍的顺序放到SMBSDA中
- SMBSDA中的数据发送到总线上,地址字节发送完毕。
- 在发送地址字节时,如果发生冲突,SMBST的BER将置位,同时SMBST的MASTER将被清0
- 发送完毕,将把收到的确认位放到SMBST的NEGACK中
-
发送数据
先说明一下SMBCTL1的STASTRE的作用,如果这位置1,当发送完一个字节并收到确认信号后,CS5536会把SDA拉低,使总线进入等待状态(前面有介绍),此时SMBST的STASTR会置位,表明总线处于等待状态,要清除此状态需要读取SMBST寄存器,如果下一字节不需进入等待,请记得把SMBCTL1的STASTRE清0。
- 检查SMBST的BER和NEGACK,均应该是0,BER为1说明出错(一般是总线冲突),NEGACK为1说明没有收到相应的确认信号,所以在这种情况下再发数据没有意义。
- 在SMBST的BER和NEGACK均为0的情况下,检查SMBST的SDAST,如果为1,表示可以发送数据
- 把要发送的数据字节放到SMBSDA中
- 数据发送到总线上
-
STOP信号的产生
- 在发送最后一个字节后不要去读SMBST
- 将SMBCTL1的STOP置1
-
如何操作CS5536的SMB?
- 说了半天,我们一直说SMB的寄存器,而且一直用的是偏移地址,那么到底SMB的基地址是多少?应该用I/O方式还是访问存储器的方式访问SMB呢?
- CS5536的所有器件均以虚拟PCI的形式挂在PCI总线上,SMB挂接在其中的ISA上,ISA的基地址就是SMB的基地址,CS5536的Vendor ID是0x1022,这个在以前的文章中说过,ISA的Device ID是0x2090,通过这两个条件就可以在PCI总线上找到这个设备,然后读取它的基地址即可。SMB的寄存器是用I/O端口映射的,所以要用in和out指令操作SMB的寄存器。
- 有关CS5536的datasheet在下面地址下可以下载:http://blog.hengch.com/datasheet/cs5536.pdf
3、一个简单的应用
下面是一个类,我实际用在系统中用CS5536的SMB连接AIT2138的应用中,类中有产生各种信号的源代码,我想对读者理解CS5536和I2C总线均会有帮助。
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
|
/***********************************************************
* class : AIT2138
* int getStatus(void) : return class status
* 0--exist AIT2138
* -1--No PCI BIOS -2--NO AIT2138
* int getData(int regNo) : get Data from AIT2138
* return : see function note
* int setData(int regNo, char value) : set data to ait2138
* return : see function note
************************************************************/
001 #define CS5536_VENDOR_ID 0X1022
002 #define CS5536_ISA_DEVICE 0X2090
// SMB NATIVE REGISTER
003 #define SMBSDA 0X00
004 #define SMBST 0X01
005 #define SMBCST 0X02
006 #define SMBCTL1 0X03
007 #define SMBADDR 0X04
008 #define SMBCTL2 0X05
009 #define SMBCTL3 0X06
// BIT DEFINE
010 #define SMBSDA_DIRECTION 0X80 // BIT 7
011 #define SMBST_XMIT 0X01 // BIT 0
012 #define SMBST_MASTER 0X02 // BIT 1
013 #define SMBST_NMATCH 0X04 // BIT 2
014 #define SMBST_STASTR 0X08 // BIT 3
015 #define SMBST_NEGACK 0X10 // BIT 4
016 #define SMBST_BER 0X20 // BIT 5
017 #define SMBST_SDAST 0X40 // BIT 6
018 #define SMBST_SLVSTP 0X80 // BIT 7
019 #define SMBCST_BUSY 0X01 // BIT 0
020 #define SMBCST_BB 0X02 // BIT 1
021 #define SMBCST_MATCH 0X04 // BIT 2
022 #define SMBCST_GCMTCH 0X08 // BIT 3
023 #define SMBCST_TSDA 0X10 // BIT 4
024 #define SMBCST_TGSCL 0X20 // BIT 5
025 #define SMBCTL1_START 0X01 // BIT 0
026 #define SMBCTL1_STOP 0X02 // BIT 1
027 #define SMBCTL1_INTEN 0X04 // BIT 2
028 #define SMBCTL1_ACK 0X10 // BIT 4
029 #define SMBCTL1_GCMEN 0X20 // BIT 5
030 #define SMBCTL1_NMINTE 0X40 // BIT 6
031 #define SMBCTL1_STASTRE 0X80 // BIT 7
032 #define SMBADDR_SAEN 0X80 // BIT 7
033 #define SMBCTL2_SCLFRQ 0X80 // BIT 7
034 #define SMBCTL3_SCLFRQ 0X80 // BIT 7
035 class SMBCLASS {
036 protected:
037 __dpmi_regs r;
038 int i, j;
039 int status; // 0--DS3231 exist -1--No PCI BIOS -2--No DS3231
040 int retValue;
041 unsigned int busNo; // Bus Number
042 unsigned int devNo; // Device Number
043 unsigned int funcNo; // Function Number
044 unsigned int devFunc; //
045 unsigned long int baseAddr; // Base address of mixer
046 struct SMB {
047 char sda;
048 char st;
049 char cst;
050 char ctl1;
051 char addr;
052 char ctl2;
053 char ctl3;
054 }smb;
/**************************************
* CheckPCIBios
**************************************/
055 unsigned int CheckPCIBios(void) {
056 unsigned int i;
// Check BIOS Support PCI or not
057 r.x.ax = 0xb101;
058 __dpmi_int(0x1a, &r);
059 i = r.x.flags;
060 if ((i & 0x01) == 0) return 1;
061 else return 0;
062 }
/****************************************
* Find CS5536's PCI Configuration space
****************************************/
063 unsigned int FindCS5536(void) {
064 r.x.ax = 0xb102;
065 r.x.cx = CS5536_ISA_DEVICE; // Device ID
066 r.x.dx = CS5536_VENDOR; // Vendor ID
067 r.x.si = 0; // Device index 0--n
068 __dpmi_int(0x1a, &r); // Find PCI device
069 busNo = r.h.bh; // Bus nnmber
070 devFunc = r.h.bl; // device/function no(bits 7-3 device, bits 2-0 function)
071 return r.h.ah;
072 }
/**********************************************
* Read a dword fromPCI Configuration register
**********************************************/
073 unsigned long int ReadConfigDword(unsigned int reg) {
// Read Configure Byte
074 r.x.ax = 0xb10a;
075 r.h.bl = devFunc;
076 r.h.bh = busNo; // Bus number
077 r.x.di = reg; // register no
078 __dpmi_int(0x1a, &r);
079 return r.d.ecx;
080 }
/*******************************************
* Read a byte from SMB register
*******************************************/
081 char getI2cReg(int regOffset) {
082 return inportb(baseAddr + regOffset);
083 }
/*******************************************
* Write a byte to SMB register
*******************************************/
084 void setI2cReg(int regOffset, char value) {
085 outportb(baseAddr + regOffset, value);
086 }
/*********************************************
* Get content of all SMB registers
*********************************************/
087 void getAllRegs(void) {
088 smb.sda = getI2cReg(SMBSDA);
089 smb.st = getI2cReg(SMBST);
090 smb.cst = getI2cReg(SMBCST);
091 smb.ctl1 = getI2cReg(SMBCTL1);
092 smb.addr = getI2cReg(SMBADDR);
093 smb.ctl2 = getI2cReg(SMBCTL2);
094 smb.ctl3 = getI2cReg(SMBCTL3);
095 }
/****************************************
* Generate a START condition
* return : 0--OK
* -1--FAIL
* 1--OTHERS
****************************************/
096 int start(void) {
097 smb.ctl1 = getI2cReg(SMBCTL1);
098 smb.ctl1 &= (~SMBCTL1_INTEN);
099 smb.ctl1 |= SMBCTL1_START;
100 setI2cReg(SMBCTL1, smb.ctl1);
101 delay(2);
102 smb.st = getI2cReg(SMBST);
103 if ((smb.st & SMBST_BER) != 0)
104 return -1;
105 if ((smb.st & SMBST_SDAST) != 0 && (smb.st & SMBST_MASTER) != 0)
106 return 0;
107 return 1;
108 }
/*****************************************
* Clear STASTR
*****************************************/
109 void clearSTASTR(void) {
110 smb.st = getI2cReg(SMBST);
111 smb.st |= SMBST_STASTR;
112 setI2cReg(SMBST, smb.st);
113 }
/*****************************************
* Clear NEGACK
*****************************************/
114 void clearNEGACK(void) {
115 smb.st = getI2cReg(SMBST);
116 smb.st |= SMBST_NEGACK;
117 setI2cReg(SMBST, smb.st);
118 }
/*****************************************
* clear STASTRE
*****************************************/
119 void clearSTASTRE(void) {
120 smb.ctl1 = getI2cReg(SMBCTL1);
121 smb.ctl1 &= (~SMBCTL1_STASTRE);
122 setI2cReg(SMBCTL1, smb.ctl1);
123 }
/*****************************************
* set ACK
*****************************************/
124 void setACK(void) {
125 smb.ctl1 = getI2cReg(SMBCTL1);
126 smb.ctl1 |= SMBCTL1_ACK;
127 setI2cReg(SMBCTL1, smb.ctl1);
128 }
/*****************************************
* Sending a byte to bus
* return: 0--ok
* 1--NO ACK
* 2--NO ACK and SDAST = 0
* 3--OTHER ERROR
* 4--UNKNOWN
*****************************************/
129 int sendData(unsigned char aByte) {
130 clearSTASTR();
131 clearSTASTRE();
132 setI2cReg(SMBSDA, aByte);
133 delay(2);
134 smb.st = getI2cReg(SMBST);
135 if ((smb.st & SMBST_NEGACK) != 0) {
136 if ((smb.st & SMBST_SDAST) == 0)
137 return 2;
138 else return 1;
139 } else {
140 if ((smb.st & SMBST_BER) != 0)
141 return 3;
142 else if ((smb.st & SMBST_SDAST) != 0 && (smb.st & SMBST_MASTER) != 0)
143 return 0;
144 else return 4;
145 }
146 }
/*************************************
* generate a STOP condition
*************************************/
147 int stop(void) {
148 clearNEGACK();
149 clearSTASTR();
150 smb.sda = getI2cReg(SMBSDA);
151 smb.ctl1 = getI2cReg(SMBCTL1);
152 smb.ctl1 |= SMBCTL1_STOP;
153 setI2cReg(SMBCTL1, smb.ctl1);
154 delay(2);
155 smb.st = getI2cReg(SMBST);
156 if ((smb.st & SMBST_BER) != 0)
157 return -1;
158 if ((smb.st & SMBST_SDAST) != 0 && (smb.st & SMBST_MASTER) != 0)
159 return 0;
160 return 1;
161 }
161 public:
162 AIT2138(void) {
// Check BIOS Support PCI or not
163 if (CheckPCIBios() == 0) {
164 status = -1;
165 return;
166 }
// Find CS5536. After this Step I will get Bus Number, Device Number
// and Function Number
167 if (FindCS5536() != 0) {
168 status = -2;
169 return;
170 }
171 devNo = devFunc >> 3; // Device Number
172 funcNo = devFunc & 07; // Function Number
// Get CS5536 Base address from PCI configuration space
// Configuration register index is 0x10
173 baseAddr = ReadConfigDword(CS5536_BASEADDR_REG);
174 baseAddr = baseAddr & 0Xfffffff0;
175 status = 0;
176 }
/**********************************************
* Get Data From AIT2138
* return 0 : ok
* <0 : fail
* -1 : START fail
* -2 : Sending ADDRESS for write fail
* -3 : Sending pointer of ait2138 fail
* -4 : Repeated START fail
* -5 : Sending Address for read fail
* -6 : Other fault
************************************************/
177 int getData(int regNo) {
178 int i;
179 if (start() != 0) {
180 return -1;
181 }
// Send ADDRESS
182 if (sendData(0x88) != 0) {
183 return -2;
184 }
// initial pointer od ait2138
185 setACK();
186 if (sendData(regNo) != 0) {
187 return -3;
188 }
// Repeated START
189 if (start() != 0) {
190 return -4;
191 }
192 setACK();
193 if (sendData(0x89) != 0) {
194 return -5;
195 }
196 smb.st = getI2cReg(SMBST);
197 if ((smb.st & SMBST_SDAST) != 0 && (smb.st & SMBST_BER) == 0) {
198 smb.sda = getI2cReg(SMBSDA);
199 } else return -1;
200 delay(2);
201 i = stop();
202 return 0;
203 }
/***************************************************
* Set Date To AIT2138
* return : 0 : OK
* <1 : fail
* -1 : START fail
* -2 : Sending Address for write fail
* -3 : Sending pointer of ait2138 fail
* -4 : Sending data fail
***************************************************/
204 int setData(int regNo, char value) {
205 int i, j;
206 if (start() != 0) {
207 return -1;
208 }
// Send ADDRESS
209 if (sendData(0x88) != 0) {
210 return -2;
211 }
// initial pointer od ait2138
//setACK();
212 if (sendData(regNo) != 0) {
213 return -3;
214 }
215 smb.sda = value;
216 setACK();
217 if (sendData(smb.sda) != 0) {
218 return -3;
219 }
220 delay(2);
221 i = stop();
222 return 0;
223 }
224 };
|