VxWorks开发板驱动程序学习之IIC

来源:互联网 发布:cpu温度检测软件知乎 编辑:程序博客网 时间:2024/04/30 12:25

手上的一块S3C2410的开发板搭载VxWorks 5.5操作系统,今天开始继续学习BSP源码和驱动程序,争取在硕士毕业前能自己实现BSP的配置。

  • IIC接口
    • IIC总线的状态及信号
    • IIC总线数据传输格式
  • S3C2410 IIC接口
    • S3C2410 IIC串行总线编程
  • S3C2410 IIC 驱动程序
  • 验证结果

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

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

1 0