1. 程式人生 > >s3c2440 IIC控制器裸板程式

s3c2440 IIC控制器裸板程式

        I2C匯流排只有兩根雙向訊號線。一根是資料線SDA,另一根是時鐘線SCL。


要點:
1、匯流排空閒:I2C匯流排空閒的時候,兩條線SDA和SCL都是高電平。
2、開始訊號 S 訊號:SCL 為高電平時,SDA由高電平向低電平跳變,開始傳送資料。
3、結束訊號 P 訊號:SCL 為高電平時,SDA由低電平向高電平跳變,結束傳送資料。
4、響應訊號 ACK:接收器在接收到8位資料後,在第9個時鐘週期,拉低 SDA 電平。
(注意:在第9個時鐘週期,傳送器保持SDA為高,如果有ACK,那麼第9個時鐘週期SDA為    低電平,如果沒有為高電平,傳送器根據電平高低分辨是否有ACK訊號。)
5、正常資料傳輸時:SDA 在 SCL 為低電平時改變,在 SCL 為高電平時保持穩定。
        如果使能了IIC中斷,傳送完8bit資料後,主機自動進入中斷處理函式,此時SCL被髮送器拉低,讓接收器被迫等待。恢復傳輸只需要清除中斷掛起。
        接收器件收到一個完整的資料位元組後,有可能需要完成一些其它工作,如處理內部中斷服務等,可能無法立刻接收下一個位元組,這時接收器件可以將SCL線拉成低電平,從而使主機處於等待狀態。直到接收器件準備好接收下一個位元組時,再釋放SCL線使之為高電平,從而使資料傳送可以繼續進行。  

        傳送模式中當傳送了資料時,在 IIC 匯流排資料移位(IICDS)暫存器收到新資料之前 IIC 匯流排介面將會一直等待。在新資料寫入到暫存器之前,SCL 線將會保持為低,然後在其寫入後釋放。S3C2440A 應該等待中斷來確定當前資料傳送的完成。在 CPU 收到中斷請求後,需要再次寫一個新資料到 IICDS 暫存器中。

        接收模式中當收到了資料時,在讀取 IICDS 暫存器前 IIC 介面將會一直等待。在新資料讀出前,SCL 線將會保持為低,然後在其讀取後釋放。S3C2440A 應該等待中斷來確定當前資料接收的完成。在 CPU 收到中斷請求後,需要從 IICDS 暫存器中讀取資料。

特別注意:
IIC 匯流排中斷髮生在:
1、當完成了 1 位元組傳送或接收操作;
2、當廣播呼叫或從地址匹配發生時;
3、如果匯流排仲裁失敗。

s3c2440的IIC匯流排控制器:
s3c2440提供4個暫存器來完成所有IIC的操作:
IICDS:
IICADD:
IICCON:ACK訊號使能,傳送模式時鐘源選擇,傳送、接收中斷使能,中斷標記,傳送模式時鐘分頻係數。
IICSTAT:工作模式,忙狀態,發出S訊號,P訊號等,序列輸出使能(IICDS輸出使能),最後一位的狀態(是否接收到ACK)。當發出S訊號後,IICDS暫存器中的資料被自動傳送。

S3C2440A 的 IIC 匯流排介面有 4 種工作模式:
–  主機發送模式
–  主機接收模式
–  從機發送模式
–  從機接收模式 

這裡只要介紹主機發送模式和主機接收模式流程:

主機發送模式流程圖:


主機接收模式流程圖:


結合at24cxx的讀寫來學習吧:

at24cxx.c:  

實現兩個函式at24cxx_read()和at24cxx_write(),這兩個函式直接提供給使用者操作at24cxx。 

#include <string.h>
#include "i2c.h"

unsigned char at24cxx_read(unsigned char address)
{
	unsigned char val;
	printf("at24cxx_read address = %d\r\n", address);
    i2c_write(0xA0, &address, 1);
	printf("at24cxx_read send address ok\r\n");
    i2c_read(0xA0, (unsigned char *)&val, 1);
	printf("at24cxx_read get data ok\r\n");
	return val;
}

void at24cxx_write(unsigned char address, unsigned char data)
{
	unsigned char val[2];
	val[0] = address;
	val[1] = data;
    i2c_write(0xA0, val, 2);
}

i2c.c:

注意:控制器讀/寫一位元組後,會自動更新下一個讀/寫的地址,所以傳送一次讀/寫地址,可連續讀寫多個數據。
同時注意,EEPROM 介面, Rx 模式中為了產生停止條件在讀取最後資料之前會禁止產生應答。
在次強調一下:IIC 匯流排中斷髮生的時機:1、當完成了 1 位元組傳送或接收操作;2、當廣播呼叫或從地址匹配發生時;3、如果匯流排仲裁失敗。

/*
 * FILE: i2c.c
 * 用於主機發送/接收
 */
#include <stdio.h>
#include "s3c24xx.h"
#include "i2c.h"

void Delay(int time);

#define WRDATA      (1)
#define RDDATA      (2)

typedef struct tI2C {
    unsigned char *pData;   /* 資料緩衝區 */
    volatile int DataCount; /* 等待傳輸的資料長度 */
    volatile int Status;    /* 狀態 */
    volatile int Mode;      /* 模式:讀/寫 */
    volatile int Pt;        /* pData中待傳輸資料的位置 */
}tS3C24xx_I2C, *ptS3C24xx_I2C;

static tS3C24xx_I2C g_tS3C24xx_I2C;

/*
 * I2C初始化
 */
void i2c_init(void)
{
    GPEUP  |= 0xc000;       // 禁止內部上拉
    GPECON |= 0xa0000000;   // 選擇引腳功能:GPE15:IICSDA, GPE14:IICSCL

    INTMSK &= ~(BIT_IIC);

    /* bit[7] = 1, 使能ACK
     * bit[6] = 0, IICCLK = PCLK/16
     * bit[5] = 1, 使能中斷
     * bit[3:0] = 0xf, Tx clock = IICCLK/16
     * PCLK = 50MHz, IICCLK = 3.125MHz, Tx Clock = 0.195MHz
     */
    IICCON = (1<<7) | (0<<6) | (1<<5) | (0xf);  // 0xaf

    IICADD  = 0x10;     // S3C24xx slave address = [7:1]
    IICSTAT = 0x10;     // I2C序列輸出使能(Rx/Tx)
}

/*
 * 主機發送
 * slvAddr : 從機地址,buf : 資料存放的緩衝區,len : 資料長度 
 */
void i2c_write(unsigned int slvAddr, unsigned char *buf, int len)
{
    g_tS3C24xx_I2C.Mode = WRDATA;   // 寫操作
    g_tS3C24xx_I2C.Pt   = 0;        // 索引值初始為0
    g_tS3C24xx_I2C.pData = buf;     // 儲存緩衝區地址
    g_tS3C24xx_I2C.DataCount = len; // 傳輸長度
    
    IICDS   = slvAddr;
    IICSTAT = 0xf0;         // 主機發送模式,傳送從機地址,使能序列傳輸,發出S訊號啟動
    
    /* 等待直至資料傳輸完畢 */    
    while (g_tS3C24xx_I2C.DataCount != -1);
}
        
/*
 * 主機接收
 * slvAddr : 從機地址,buf : 資料存放的緩衝區,len : 資料長度 
 */
void i2c_read(unsigned int slvAddr, unsigned char *buf, int len)
{
    g_tS3C24xx_I2C.Mode = RDDATA;   // 讀操作
    g_tS3C24xx_I2C.Pt   = -1;       // 索引值初始化為-1,表示第1箇中斷時不接收資料(地址中斷)
    g_tS3C24xx_I2C.pData = buf;     // 儲存緩衝區地址
    g_tS3C24xx_I2C.DataCount = len; // 傳輸長度
    
    IICDS        = slvAddr;
    IICSTAT      = 0xb0;    // 主機接收,啟動
    
    /* 等待直至資料傳輸完畢 */    
    while (g_tS3C24xx_I2C.DataCount != 0);
}

/*
 * I2C中斷服務程式
 * 根據剩餘的資料長度選擇繼續傳輸或者結束
 */
void I2CIntHandle(void)
{
    unsigned int iicSt,i;

    // 清中斷
    SRCPND = BIT_IIC;
    INTPND = BIT_IIC;
    
    iicSt  = IICSTAT; 

    if(iicSt & 0x8){ printf("Bus arbitration failed\n\r"); }

    switch (g_tS3C24xx_I2C.Mode)
    {    
        case WRDATA:
        {
            if((g_tS3C24xx_I2C.DataCount--) == 0)
            {
                // 下面兩行用來恢復I2C操作,發出P訊號
                IICSTAT = 0xd0;
                IICCON  = 0xaf;
                Delay(10000);  // 等待一段時間以便P訊號已經發出
                break;    
            }
            //資料的格式是,地址 資料1 資料2 資料3...,對於各個資料的寫地址,控制器會自動+1更新
            //想要改為讀模式,需要在發出P訊號之後,發出讀模式地址。
            IICDS = g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++];
            
            // 將資料寫入IICDS後,需要一段時間才能出現在SDA線上
            for (i = 0; i < 10; i++);   

            IICCON = 0xaf;      // 恢復I2C傳輸
            break;
        }

        case RDDATA:
        {
            if (g_tS3C24xx_I2C.Pt == -1)
            {
                // 這次中斷是傳送I2C裝置地址後發生的,沒有資料
                // 只接收一個數據時,不要發出ACK訊號
                g_tS3C24xx_I2C.Pt = 0;
                if(g_tS3C24xx_I2C.DataCount == 1)
                   IICCON = 0x2f;   // 恢復I2C傳輸,開始接收資料,接收到資料時不發出ACK
                else 
                   IICCON = 0xaf;   // 恢復I2C傳輸,開始接收資料
                break;
            }

			g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++] = IICDS;
			g_tS3C24xx_I2C.DataCount--;
            
            if (g_tS3C24xx_I2C.DataCount == 0)
            {

                // 下面兩行恢復I2C操作,發出P訊號
                IICSTAT = 0x90;
                IICCON  = 0xaf;
                Delay(10000);  // 等待一段時間以便P訊號已經發出
                break;    
            }      
			else
			{           
	           // 接收最後一個數據時,不要發出ACK訊號
	           if(g_tS3C24xx_I2C.DataCount == 1)
	               IICCON = 0x2f;   // 恢復I2C傳輸,接收到下一資料時無ACK
	           else 
	               IICCON = 0xaf;   // 恢復I2C傳輸,接收到下一資料時發出ACK
			}
           break;
        }
       
        default:
            break;      
    }
}

/*
 * 延時函式
 */
void Delay(int time)
{
    for (; time > 0; time--);
}

        本文到此為止,謝謝閱讀。