51系列单片机之I2C协议

    /*---------------------------------------------------- 
    名称:IIC协议 PCF8591AD/DA转换 
    编写:付新 
    日期:2012/5/9 
    平台:Keil 4, Ly-51S学习板 
    引脚定义如下: 
    与51连接: 
    内容:函数是采用软件延时的方法产生SCL脉冲,固对高晶振频率要作 一定的修改....(本例是1us机器周期,即晶振频率要小于12MHZ) 
    -----------------------------------------------------*/  
      
    #include<reg52.h>  
    #include<intrins.h>  
    #include "uart.h"  
                                          
    #define AddWr 0x90   //写数据地址   
    #define AddRd 0x91   //读数据地址  
      
    sbit SDA=P2^1;  
    sbit SCL=P2^0;                           
    bit ack;                  //应答标志位  
      
    /*启动总线*/  
    void I2C_start() {  
        SDA=1;   //发送起始条件的数据信号  
         _nop_();  
        SCL=1;  
        _nop_();    //起始条件建立时间大于4.7us,延时  
        _nop_();  
        _nop_();  
        _nop_();  
        _nop_();      
        SDA=0;     //发送起始信号  
        _nop_();    //起始条件锁定时间大于4μ  
        _nop_();  
        _nop_();  
        _nop_();  
        _nop_();         
        SCL=0;    //钳住I2C总线,准备发送或接收数据  
        _nop_();  
        _nop_();  
    }  
      
    /*结束总线*/  
    void I2C_stop() {  
        SDA=0;    //发送结束条件的数据信号  
        _nop_();   //发送结束条件的时钟信号  
        SCL=1;    //结束条件建立时间大于4μ  
        _nop_();  
        _nop_();  
        _nop_();  
        _nop_();  
        _nop_();  
        SDA=1;    //发送I2C总线结束信号  
        _nop_();  
        _nop_();  
        _nop_();  
        _nop_();  
    }  
      
    /*字节数据传送函数                
    功能:将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对此状态位进行操作. 
    (不应答或非应答都使ack=0 假) 
         发送数据正常,ack=1; ack=0表示被控器无应答或损坏。 
    */  
    void I2C_send_byte(unsigned char c) {  
        unsigned char n;  
        for(n = 0; n < 8; n++) { //要传送的数据长度为8  
            if((c << n)&0x80) SDA=1;   //判断发送位  
            else  SDA=0;                  
            _nop_();  
            SCL=1;               //置时钟线为高,通知被控器开始接收数据位  
            _nop_();   
            _nop_();             //保证时钟高电平周期大于4μ  
            _nop_();  
            _nop_();  
            _nop_();           
            SCL=0;   
        }             
        _nop_();  
        _nop_();  
        SDA=1;               //8位发送完后释放数据线,准备接收应答位  
        _nop_();  
        _nop_();     
        SCL=1;  
        _nop_();  
        _nop_();  
        _nop_();  
        if(SDA == 1)ack=0;       
        else ack=1;        //判断是否接收到应答信号  
        SCL=0;  
        _nop_();  
        _nop_();  
    }  
                  
    /*字节数据传送函数                
    功能:  用来接收从器件传来的数据,并判断总线错误(不发应答信号),发完后请用应答函数。   
    */  
    unsigned char I2C_rcv_byte() {  
        unsigned char dat = 0;  
        unsigned char n;  
        SDA=1;             //置数据线为输入方式  
        for(n = 0; n < 8; n++) {  
            _nop_();             
            SCL=0;       //置时钟线为低,准备接收数据位  
            _nop_();  
            _nop_();      //时钟低电平周期大于4.7us  
            _nop_();  
            _nop_();  
            _nop_();  
            SCL=1;       //置时钟线为高使数据线上数据有效  
            _nop_();  
            _nop_();  
            dat = dat<<1;  
            if(SDA==1)dat = dat + 1; //读数据位,接收的数据位放入retc中  
            _nop_();  
            _nop_();   
        }  
        SCL=0;      
        _nop_();  
        _nop_();  
        return dat;  
    }  
          
    /*应答函数*/  
    /*void I2C_ack() {   
      SDA=0;      
      _nop_(); 
      _nop_(); 
      _nop_();       
      SCL=1; 
      _nop_(); 
      _nop_();              //时钟低电平周期大于4μ 
      _nop_(); 
      _nop_(); 
      _nop_();   
      SCL=0;               //清时钟线,钳住I2C总线以便继续接收 
      _nop_(); 
      _nop_();     
    }*/  
      
    /*非应答子函数*/  
    void I2C_noack() {  
        SDA=1;  
        _nop_();  
        _nop_();  
        _nop_();        
        SCL=1;  
        _nop_();  
        _nop_();              //时钟低电平周期大于4μ  
        _nop_();  
        _nop_();  
        _nop_();    
        SCL=0;                //清时钟线,钳住I2C总线以便继续接收  
        _nop_();  
        _nop_();      
    }  
      
    /*读AD转值程序 
    输入参数 Chl 表示需要转换的通道,范围从0-3 返回值范围0-255 
    */  
    unsigned char read_AD(unsigned char channel)  
     {  
       unsigned char val;  
       I2C_start();               //启动总线  
       I2C_send_byte(AddWr);             //发送器件地址  
         if(ack==0) return 0;  
       I2C_send_byte(0x40 | channel);            //发送器件子地址  
         if(ack==0)return 0;  
       I2C_start();  
       I2C_send_byte(AddWr+1);  
          if(ack==0)return 0;  
       val=I2C_rcv_byte();  
       I2C_noack();                 //发送非应位  
       I2C_stop();                  //结束总线  
      return val;  
    }  
      
    void main() {  
        unsigned char num=0;  
        UART_init();  
        while(1) {  
            num = read_AD(0);  
            UART_send_byte(num);  
        }     
    }  

 

posted @ 2013-03-09 08:47  顶顶顶顶  阅读(329)  评论(0)    收藏  举报