注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

DOS编程技术

讨论在纯DOS下的编程技术

 
 
 

日志

 
 
关于我

1984年大学毕业,1985年底有机会开始接触PC机,1986年开始在PC机上做开发工作,曾接触过MS-DOS、CP/M、UNIX、VMS、LINUX、iRMX等众多的操作系统并在上面从事技术开发,擅长做底层与硬件相关的软件开发,目前主要在DOS和LINUX平台下工作,主要从事软件,在硬件开发上也有一定造诣,亦有在8051系列、6502系列(凌阳)、z80系列、ARM、X86等各类平台下开发软硬件的经历。更详细情况可以参考http://resume.whowin.net

CS5536上的I2C总线的应用  

2008-05-21 13:28:41|  分类: 杂七杂八 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

    以前我们使用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总线的应用 - whowin - DOS编程技术

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

CS5536上的I2C总线的应用 - whowin - DOS编程技术

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

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

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

CS5536上的I2C总线的应用 - whowin - DOS编程技术

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

 CS5536上的I2C总线的应用 - whowin - DOS编程技术

    在开始信号后,主控期间开始发送时钟信号,根据规范,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(2017年3月22日注:链接已修复)

    中文翻译版I2C总线规范:http://blog.whowin.net/specification/i2c-cn.pdf(2017年3月22日注:链接已修复)

    个人建议尽量阅读原文文献,但中文版可以做参考,对概念的理解会有帮助。

2、CS5536上的I2C总线
    在CS5536这颗芯片上集成了一个I2C总线,在芯片的DATASHEET中被称作为System Management Bus Controller,简称SMB,有7个内部寄存器控制总线,使操作变得十分方便。

CS5536上的I2C总线的应用 - whowin - DOS编程技术

    SMBCTL2和SMBCTL3用来指定在SCL上发送的时钟频率,其中SMBCTL2的bit0是一个使能位,1--SMB使能,0--SMB禁止,bit1--bit7是时钟频率的低7位,SMBCTL3的bit0--bit7是时钟频率的高8位,一共15位(抱歉,这些与上图中标注不符,上图尽管截自规范,但很遗憾,其中的SMBCTL2和SMBCTL3是错的),始终频率的结果符合下面公式:

    tSCL = 2 x SCLFRQ x tclk

    下面我们仅介绍工作在主控状态时,信号的产生方法,但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总线均会有帮助。

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




  评论这张
 
阅读(2843)| 评论(2)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018