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。

CS5536上的I2C总线的应用

上图截自I2C总线的规范,它清楚地告诉我们,I2C总线,只有两条线,所有的设备(IC)都并联接在这两条线上,图中有两个MICRO CONTROLLER,A和B,告诉我们在I2C总线上可以连接多个主控。下面我们尽可能简单地说明它的通信原理。

CS5536上的I2C总线的应用

上图同样截自I2C总线的规范,它清楚地告诉我们,I2C总线在空闲状态由两个上拉电阻把总线拉到高电平,器件对I2C总线的控制只有释放和拉低这两种方式,这一点对后面理解信号很有帮助。

I2C总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。

  • 开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。

  • 结束信号:SCL为低电平时,SDA由低电平向高电平跳变,结束传送数据。

  • 应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,可以判断为受控单元出现故障。

    CS5536上的I2C总线的应用

先看开始和结束信号,空闲状态,SDA、SCL高,当某个主控器件要传输或接收数据时,它要将SDA拉低,这便产生一个开始信号,之后,主控机要控制SCL,开始在SCL上发送时钟信号,当传输完毕后,主控机首先释放SCL,然后再释放SDA,经过一定时延后,认为传输结束。

CS5536上的I2C总线的应用

在开始信号后,主控期间开始发送时钟信号,根据规范,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个内部寄存器控制总线,使操作变得十分方便。

CS5536上的I2C总线的应用

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  };