I2C組件(新)



/**
 *  文件名:I2C.h
 */

#ifndef _I2C_H
#define _I2C_H

#define I2Cdelay(); { \
    _nop_(); _nop_(); _nop_(); _nop_(); \
}

typedef enum ackType {
    ACK = 0, NAK = 1
} ack_t;

sbit I2C_SCL = P3^7;    //檢查引腳是否正確!
sbit I2C_SDA = P3^6;

void I2Cstart();
void I2Cstop();
bit I2Cwrite(uint8_t dat);
uint8_t I2Cread(ack_t ackbit);

#endif // _I2C_H



/**
 * 文件名:I2C.c
 * 描  述:I2C總線驅動模塊(新)
 * 備  注:基於IO口模擬實現,總線時序延時等皆由軟件方式實現
 *         時鐘信號SCL的產生和延時很重要
 */
#include <reg52.h>
#include <intrins.h>
#include "stdint.h"
#include "I2C.h"

/* 產生總線起始信號 */
void I2Cstart() {
    I2C_SDA = 1;    //首先確保sda、scl都是高電平
    I2C_SCL = 1;
    I2Cdelay();     //保持狀態一段時間
    I2C_SDA = 0;    //先拉低sda(在scl爲高電平期間,sda出現下降沿)
    I2Cdelay();     //保持狀態一段時間
    I2C_SCL = 0;    //再拉低scl(使得可以往數據線sda上寫數據)
}
/* 產生總線停止信號 */
void I2Cstop() {
    //I2C_SCL = 0;    //首先確保sda、scl都是低電平(時鐘信號scl之前已經置0了)
    I2C_SDA = 0;
    I2Cdelay();     //之前i2c已經工作過,是以時鐘信號scl爲0結束的,此時需要把該時鐘狀態保持一段時間
    I2C_SCL = 1;    //先拉高scl
    I2Cdelay();     //保持狀態一段時間
    I2C_SDA = 1;    //再拉高sda(在scl爲高電平期間,sda出現上升沿)
    I2Cdelay();     //保持狀態一段時間
}
/* i2c總線寫操作,dat-待寫入字節,返回值-從機應答位的值 */
bit I2Cwrite(uint8_t dat) {
    bit ack;        //用於暫存應答位的值
    uint8_t mask;   //用於探測字節內某一位值的掩碼變量
    //從高位到低位依次進行
    for (mask = 0x80; mask != 0; mask >>= 1) {
        if ((mask & dat) == 0)  //該位的值輸出到sda上(之前已經保證scl爲低電平了,此時可以寫數據)
            I2C_SDA = 0;
        else
            I2C_SDA = 1;
        I2Cdelay();             //保持狀態一段時間
        I2C_SCL = 1;            //拉高scl(使得從機可以從sda上讀數據)
        I2Cdelay();             //保持狀態一段時間
        I2C_SCL = 0;          //再拉低scl,完成一個位週期,爲下一次寫數據做好準備
    }
    I2C_SDA = 1;    //8位數據發送完後,主機釋放sda,以檢測從機應答
    I2Cdelay();     //保持狀態一段時間,此時從機將應答值寫到sda上
    I2C_SCL = 1;    //拉高scl
    ack = I2C_SDA;  //讀取此時的sda值,即爲從機的應答值
    I2Cdelay();     //保持狀態一段時間
    I2C_SCL = 0;    //再拉低scl完成應答位,並保持住總線,爲下一次寫數據做好準備

    return (~ack);  //應答值取反以符合通常的邏輯:
                    //0=不存在或忙或寫入失敗,1=存在且空閒或寫入成功
}
/**
 * I2C總線讀操作,併發送非應答(1)/應答(0)信號。
 * 參數:非應答(1)/應答(0)
 * 返回值:讀到的字節
 */
uint8_t I2Cread(ack_t ackbit) {
    uint8_t mask, dat;

    I2C_SDA = 1;    //首先確保主機釋放sda,之前已確保scl爲0
    //從高位到低位依次進行
    for (mask = 0x80; mask != 0; mask >>= 1) {
        I2Cdelay();         //延時一段時間,此時從機將數據位寫到sda上
        I2C_SCL = 1;        //拉高scl,主機開始讀
        if (I2C_SDA == 0)   //讀取sda的值
            dat &= ~mask;   //爲0時,dat中對應位清零
        else
            dat |= mask;    //爲1時,dat中對應位置1
        I2Cdelay();         //保持狀態一段時間
        I2C_SCL = 0;        //再拉低scl,以使從機發送出下一位
    }
    I2C_SDA = ackbit;     //8位數據發送完後,主機發送非應答/應答信號
    I2Cdelay();     //保持狀態一段時間
    I2C_SCL = 1;    //拉高scl,使得從機可以讀這個應答信號
    I2Cdelay();     //保持狀態一段時間
    I2C_SCL = 0;    //再拉低scl完成應答位,並保持住總線

    return dat;
}



發佈了108 篇原創文章 · 獲贊 28 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章