VxWorks開發板驅動程序學習之IIC

手上的一塊S3C2410的開發板搭載VxWorks 5.5操作系統,今天開始繼續學習BSP源碼和驅動程序,爭取在碩士畢業前能自己實現BSP的配置。

IIC接口

這裏簡單複習一下IIC接口的相關知識。

IIC/I2C(Inter-Integrated Circuit)總線由Philips公司針對MCU需要研製的二線式串行總線,用於MCU及外圍設備通信。

IIC總線主要特點:

  • IIC總線長度可高達7.6m,並能夠以100kb/s的最大傳輸速率支持40個組件。
  • IIC總線支持多主控。主控能控制信息傳輸和時鐘頻率。任一時刻只有一個主控。

IIC總線要求:

  • 各個設備必須共地
  • 兩根信號線必須接上拉電阻

多IIC設備接口示意圖:
圖片來源-《嵌入式系統原理與應用技術(第2版)》

IIC總線的狀態及信號

由於上拉電阻的存在,SCL和SDA在總線處於空閒狀態均爲高電平。

主控器想使用總線就應當先拉低SCL,控制總線,傳送完成後應當釋放總線。

啓動信號:SCL爲高,SDA上產生下降沿
結束信號:SCL爲高,SDA上產生上升沿

應答/響應信號(A/NA) — 數據接收者接收到1字節後,應當主動向數據發出者發送一個應答信號。對應SCL的第9個應答時鐘脈衝,SDA爲低表示應答,繼續發送;SDA爲高表示非應答,結束髮送。

地址信號(尋址字節) —

bit D7 D6 D5 D4 D3 D2 D1 D0
DA3 DA2 DA1 DA0 A2 A1 A0 R/nW

其中,(DA3-DA0)爲器件的固有地址編碼,由器件生產廠家給定;(A2、A1、A0)爲器件地址引腳電平,高爲1,接地爲0;讀寫控制位(R/nW),爲1表示主機讀,爲0表示主機寫。

等待狀態: 從機接收完一個數據字節後,可以拉低SCL使總線系統進入等待狀態;當從機完成數據處理後,再釋放SCL線,主機方可繼續發送數據。

IIC總線數據傳輸格式

  • 一般格式
    這裏寫圖片描述
  • 主控制器寫操作格式
    這裏寫圖片描述
  • 主控制器讀操作格式
    這裏寫圖片描述
  • 主控制器讀/寫操作格式
    這裏寫圖片描述

好了,IIC總線接口相關知識就複習到此,現在來看看S3C2410的IIC接口

S3C2410 IIC接口

先來看看S3C2410下的IIC串行總線控制器框圖:
S3C2410-IIC

S3C2410的端口E的GPE15用作數據線SDA,GPE14用作連續時鐘線SCL。使用時要記得外部加上拉電阻。

發送數據寫入到 IICDS
接收數據從IICDS讀取

S3C2410的4個IIC專用寄存器:

Register Address Read or Write Description
IICCON 0x54000000 R/W IIC總線控制寄存器
IICSTAT 0x54000004 R/W IIC總線控制/狀態寄存器
IICADD 0x54000008 R/W IIC總線地址寄存器
IICDS 0x5400000C R/W IIC數據發送/接收寄存器

IICSTAT的常用控制字:

  • 啓動主設備發送的控制字爲 0xF0 (Tx)
  • 結束主設備發送的控制字爲 0xD0 (Tx)
  • 啓動主設備接收的控制字爲 0xB0 (Rx)
  • 結束主設備接收的控制字爲 0x90 (Rx)

IICADD 地址寄存器僅對從設備有意義。

S3C2410 IIC串行總線編程

直接看圖吧:

  • IIC主發送模式流程
    這裏寫圖片描述

  • IIC主接收模式流程

  • 這裏寫圖片描述

S3C2410 IIC 驅動程序

    /* 文件模塊說明:
    * 2410Iic.c S3C2410內部驅動,初始化驅動器,外部EERPROM採用 
    * ATMEL AT24C128-128kbit
    * 文件版本:
    * 開發人員:
    * 創建時間:
    * Copyright(c) t0 - t1  CompanyName Limited Co.
    */
    #include "intLib.h"
    #include "2410addr.h"                  // 寄存器地址宏頭文件
    // 24C128IIC總線定義
    #define CN_IIC_VOLUME       (0x4000)   // IIC容量,16KB
    #define AT24128256_w        (0xa0 )    // 寫地址:原理圖中 A2A1A0 均接地
    #define AT24128256_r        (0xa1 )    // 讀地址
    #define IIC_mode_mask       (0x3f )    
    #define IIC_mtx             (0xc0 )    // 主機發送模式
    #define IIC_mrx             (0x80 )    // 主機接收模式
    #define pagelength          ( 64  )
    //------------------------------------------------------------------------------
    // 初始化IIC程序
    void IIC_init(void)
    {
        UINT32 dwValReg;

        // GPE14 ~ GPE15爲IIC接口
        // GPECON[31:28] = 0b 1010 = 配置GPE14爲IICSCL, GPE15爲IICSDA
        SNGS3C_REG_READ(  rGPECON,  dwValReg );
        dwValReg = (dwValReg & 0xAFFFFFFF);
        dwValReg = (dwValReg | 0xA0000000);
        SNGS3C_REG_WRITE( rGPECON,  dwValReg );     

        // IICCON[7] ENABLE
        // IICCON[6] Tx Clock Source, 0 = IICCLK = fPCLK / 16; 1 = IICCLK = fPCLK / 512
        // IICCON[5] Interrupt Enable
        // 50Mhz/16/(9+1) = 312.5Khz 
        // AT24C128 在 3.3V 時的最大工作頻率爲 400KHz
        *(volatile UINT32 *)rIICCON=(1<<7)|(0<<6)|(1<<5)|9; // = 0xa9 
        *(volatile UINT32 *)rIICSTAT=0xd0;    // 狀態控制字:0xd0 結束主設備發送
        *(volatile UINT32 *)rIICADD=0x00;     // 器件地址 0x00
    }
    // 函數名稱:IIC_WaitBus
    // 函數功能:等待IIC總線操作結束
    // 返回值: 若操作等待超時返回 false; 正確則返回 true
    char IIC_WaitBus(void)
    {
        UINT i=0;

        // IICCON[4] - Interrupt is depending
        while( !(  (*(volatile UINT32*)rIICCON) & 0x10  ) )  // no interruption
        {
            i++;
            if(i > 1000)  // 超時
                return (FALSE);
        }
        return (TRUE);    // 還未超時就有中斷產生
    }
    // 函數名稱:IIC_WaitAck
    // 函數功能:等待IIC總線應答信號
    // 返回值: 若操作等待超時或得不到應答信號,返回 false; 正確則返回 true
    // 說明: 先等待操作結束信號,然後判斷是否有應答信號
    char IIC_WaitAck(void)
    {
        UINT i=0;

        while( !( (*(volatile UINT32*)rIICCON) & 0x10) )   // 查詢中斷信號
        {
            i++;
            if(i>1000)
                return (FALSE);
        }

        if( (*(volatile UINT32*)rIICSTAT) & 0x1 )         // ACK was not received
            return (FALSE);

        return (TRUE);
    }
    // 函數名稱:IIC_WaitEnd
    // 函數功能:等待寫操作結束
    // 返回值:
    char IIC_WaitEnd(void)
    {
        int i, k;

        // 嘗試100次寫地址並等待應答,只要收到一次即認爲寫操作成功,否則寫操作失敗
        for(i=0; i<100; i++)
        {
            *(volatile UINT32*) rIICDS = AT24128256_w; // IIC器件AT24C128寫地址

            for(k=0; k<100; k++) ;                     // 延時

            *(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
            *(volatile UINT32*) rIICSTAT = 0xf0;       // 0XF0 啓動發送狀態字
            k = 0;
            if(TRUE == IIC_WaitAck())
                return (TRUE);
        }

        return (FALSE);
    }
    // 函數名稱:IIC_Read
    // 函數功能:讀芯片
    // 返回值: 0 讀成功;  1 讀失敗

    // 讀24C128
    UINT32  IIC_Read(UINT32 start, char  * buf, UINT length)
    {
        int i, iicbusy, k;

        // 讀出起始地址+數據長度大於芯片容量(字節)則退出
        if( (start+length) > CN_IIC_VOLUME ) return;

        iicbusy = IIC_WaitEnd();                      // 等待寫操作結束

        for(k=0; k<100; k++) ;
        *(volatile UINT32*) rIICDS = (UINT8) (start/256);   // 地址高字節
        for(k=0; k<100; k++) ;
        *(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
        if(FALSE == IIC_WaitAck())
            return 1;

        *(volatile UINT32*) rIICDS = (UINT8) (start%256);   // 地址低字節
        for(k=0; k<100; k++) ;
        *(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
        if(FALSE == IIC_WaitAck())
            return 1;

        *(volatile UINT32*) rIICSTAT = 0xb0;            // 狀態控制字: 0XB0 啓動主設備接收
        *(volatile UINT32*) rIICDS = AT24128256_r;      // IIC器件AT24C128讀地址
        *(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9; // = 0xa9
        if(FALSE == IIC_WaitAck())
            return 1;

        for(i=0; i<length; i++)
        {
            if(i == length - 1)
            {
                *(volatile UINT32*) rIICCON = 0xa9;     // (1<<7)|(0<<6)|(1<<5)|9
                *(volatile UINT32*) rIICCON = 0x29;     // 禁止應答
            }
            else
            {
                *(volatile UINT32) rIICCON = 0xa9;
            }

            if(FALSE == IIC_WaitBus())                  // 等待總線操作結束
                return ;
            buf[i] = *(volatile UINT32*) rIICDS;            
        }

        *(volatile UINT32*) rIICSTAT = 0x90;            // 狀態控制字: 0x90 停止主設備接收
        for(k=0; k<100; k++) ;
        *(volatile UINT32*) rIICCON = 0xa9;             // (1<<7)|(0<<6)|(1<<5)|9

        return 0;
    }
    // 函數名稱:IIC_Write
    // 函數功能:寫芯片
    // 返回值: 0 讀成功;  1 讀失敗

    // 寫24C128
    UINT32  IIC_Write(UINT32 start, char  * buf, UINT length)
    {
        int i, j, iicbusy, k, addr = start;

        // 寫入地址大於芯片容量(字節)則退出
        if( (addr+length) > CN_IIC_VOLUME ) return;

        iicbusy = IIC_WaitEnd();                      // 等待寫操作結束

        *(volatile UINT32*) rIICDS = (UINT8) (addr/256);   // 地址高字節
        for(k=0; k<100; k++) ;
        *(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
        if(FALSE == IIC_WaitAck())
            return 1;

        *(volatile UINT32*) rIICDS = (UINT8) (addr%256);   // 地址低字節
        for(k=0; k<100; k++) ;
        *(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
        if(FALSE == IIC_WaitAck())
            return 1;

        j = FALSE;
        for(i=0; i<length; i++)
        {
            if(j)
            {
                j = FALSE;
                iicbusy = IIC_WaitEnd();                 // 等待寫操作結束

                for(k=0; k<100; k++) ;
                *(volatile UINT32*) rIICDS = (UINT8) ((addr+i)/256); // 地址高字節
                for(k=0; k<100; k++) ;
                *(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
                if(FALSE == IIC_WaitAck())
                    return 1;

                *(volatile UINT32*) rIICDS = (UINT8) (addr+i);       // 地址低字節
                for(k=0; k<100; k++) ;
                *(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
                if(FALSE == IIC_WaitAck())
                    return 1;
            }

            *(volatile UINT32*) rIICDS = buf[i];                // 發送字節數據
            for(k=0; k<100; k++) ;
            *(volatile UINT32*) rIICCON = (1<<7)|(0<<6)|(1<<5)|9;
            if(FALSE == IIC_WaitAck())
                return 1;

            // 沒有寫完全部length字節長度的數據 並且還沒有寫滿一整頁,則繼續寫入
            if( (i<length) &&  (0 == ((addr+i+1) % pagelength)) )
            {
                *(volatile UINT32*) rIICSTAT = 0xd0;          // 狀態控制字:0xd0 主設備發送結束, 產生停止信號
                *(volatile UINT32*) rIICCON  = (1<<7)|(0<<6)|(1<<5)|9;
                taskDelay(1);
                j = TRUE;
            }
        }

        if(!j)
        {
            *(volatile UINT32*) rIICSTAT = 0xd0;          // 狀態控制字:0xd0 主設備發送結束, 產生停止信號
            *(volatile UINT32*) rIICCON  = (1<<7)|(0<<6)|(1<<5)|9;
        }
        taskDelay(1);

        return 0;
    }
    // 函數功能:向定值區寫入數據,先讀,再擦除,最後寫
    // 輸入參數:wAddress 寫入區地址 0-0x10 0000
    //         pbyBuff  爲待寫入字節流指針
    //         dwLen    爲字節流長度
    // 返回值: 0 成功; 1  失敗
    STATUS   Iic_Write_Data(UINT32 wAddress, UINT8* pbyBuf, UINT32 dwLen)
    {
        UINT32 ret;
        ret = IIC_Write(wAddress, pbyBuf, dwLen);
        return ret;
    }
    // 函數功能:從定值區讀取數據
    // 輸入參數:wAddress 爲讀取區地址
    //         pbyBuf   爲待讀取字節流指針
    //         dwLen    爲字節流長度
    // 返回值: 0 成功; 1  失敗
    STATUS    Iic_Read_Data(UINT32 wAddress, UINT8 *pbyBuf, UINT32 dwLen)
    {
        UINT32 ret;
        ret = IIC_Read(wAddress, pbyBuf, dwLen);
        return ret;
    }
    // 函數功能:向定值區寫入半字(16位)
    // 輸入參數:wAddress 爲寫入區地址, wBuf 爲待寫入數據
    // 返回值: 0 成功; 1  失敗
    STATUS    Iic_Write_Word(UINT32 wAddress, UINT16 wData)
    {
        UINT32 ret;
        UINT8  buf[2];
        buf[0] = wData & 0x00ff;
        buf[1] = (wData >> 8) & 0x00ff;
        ret = Iic_Write_Data(wAddress, &buf[0], 2);
        return ret;
    }
    // 函數功能:從定值區讀取半字(16位)
    // 輸入參數:wAddress 爲讀取區地址
    // 返回值: 待讀取數據,低16位有效
    UINT16    Iic_Read_Word(UINT32 wAddress)
    {
        UINT8      buf[2];
        UINT16     wData, wData1, wData2;

        Iic_Read_Data(wAddress, &buf[0], 2);

        wData1 = buf[0];
        wData2 = buf[1];
        wData = (wData1 & 0x00ff | (wData2&0x00ff) << 8);

        return (wData);
    }

今晚就把IIC驅動代碼分享到這裏,明天到系統上實際運行試試,看看能否成功寫入並讀取數據。

驗證結果

2016/09/28
這裏寫圖片描述

在這裏做了一個簡單的測試,申請一個長度爲50的字符數組並清空,向IIC器件寫入字符串 “I love China!”,短暫延時後,從IIC器件裏面讀回來存到 字符數組中,並打印出來。可以看到,結果正確。

證明以上驅動程序,完全正確。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章