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器件里面读回来存到 字符数组中,并打印出来。可以看到,结果正确。

证明以上驱动程序,完全正确。

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