千家信息网

arm9 IIC接口有什么用

发表于:2024-11-19 作者:千家信息网编辑
千家信息网最后更新 2024年11月19日,本篇内容主要讲解"arm9 IIC接口有什么用",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"arm9 IIC接口有什么用"吧!在2440的使用中其iic接
千家信息网最后更新 2024年11月19日arm9 IIC接口有什么用

本篇内容主要讲解"arm9 IIC接口有什么用",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"arm9 IIC接口有什么用"吧!

在2440的使用中其iic接口一般用来读取外围芯片的数据, 这种情况下2440处于主机模式, 本例用中断的方式来讲述 主机发送/主机接收 模式.

主机发送模式使用流程:

IICCON: 是否返回ack, 总线时钟选择, 中断标志

IICCON[7] 在发送模式该位没有意义, 因为发送模式下主机只接收ACK信号, 并不主动发出ACK.

在接收模式中当从机返回了数据之后, 如果主机需要从机继续返回数据就必须发送一个ACK, 否则数据发送方不会继续发送数据.

综上: 无论是主/从, 只要接收数据的一方想要继续通信就必须要发送一个ACK信号, 所以ACK信号绝对是由接收数据的一方来发出的.

IICCON[5] 必须为1, 否则IICCON[4]无法使用

IICCON[4] 0: 无中断发生 1: 有中断发生, 这时总线传输中止. 如果要继续传输则需将该位清0

发生中断的条件: 1. 总线仲裁失败 2. 发送/接收完一个字节 3. 当广播或从地址匹配成功

    GPEUP  |= 0xc000;       // 禁止内部上拉    GPECON |= 0xa0000000;   // 选择引脚功能:GPE15:IICSDA, GPE14:IICSCL    INTMSK &= ~(BIT_IIC);     //清除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    IICSTAT = 0x10;     // IICSTAT[4]:I2C串行输出使能(Rx/Tx), 这里可以不必配置该寄存器, 因为真正开始发送/接收的时候会重新配置IICSTAT(见下边的I2C_Write)

IICADD: 当2440作为从机的时候, 本身的iic地址可以由该寄存器来设置.

IICSTAT : 主从模式选择, S/P信号发送, 标记各种状态

IICSTAT[7:6] 主从模式选择

IICSTAT[5] 读取此位时 0 : 总线空闲, 1 : 总线忙 ; 写入0: 发出 P 信号, 写入 1: 发出 S 信号

IICSTAT[4] 开启收发

IICSTAT[3] 总线仲裁成功标志位

IICSTAT[2] 当2440作为从机时, 接收到的地址和IICADD匹配则该位置一, 在检测到 S/P 信号时自动清0

IICSTAT[1] 当接收到 0x00 地址时置一, 当检测到 S/P 信号时自动清0

IICSTAT[0] 0: 接收到ACK; 1: 没有接收到ACK

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *以下为中断方式操作I2C接口的主要代码, 从机芯片为 m41t11* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

在启动文件中设定好I2C中断的异常向量, 在ISR中清除异常标志

//head.S中设置异常向量@ 0x1c: 快中断模式的向量地址HandleFIQ:    b   HandleFIQ    //interrupt.c中的ISR, 该ISR总查询到了IIC中断, 调取了IIC异常处理函数void IRQ_Handle(void){        unsigned long oft = INTOFFSET;        //清中断        if (oft == 4)        EINTPEND = 1<<7;    //EINT4-7合用IRQ4,注意EINTPEND[3:0]保留未用,向这些位写入1可能导致未知结果        SRCPND = 1<

以下为IIC的主要操作:

#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);   //允许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]  这句可以不需要的, 该寄存器只在2440作为从机才有用    IICSTAT = 0x10;     // I2C串行输出使能(Rx/Tx)    这句也可以不用, 因为真正写的时候又重新配置了IICSTAT}/* * 主机发送 * 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;         // 主机发送,启动        /* 等待直至数据传输完毕 */        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 != -1);}//  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:        {            //检测是否数据发送完毕, 若完毕: 发送P信号, 数据长度=-1            if((g_tS3C24xx_I2C.DataCount--) == 0)            {                // 下面两行用来恢复I2C操作,发出P信号                IICSTAT = 0xd0;                IICCON  = 0xaf;                Delay(10000);  // 等待一段时间以便P信号已经发出                break;         //注意这里的break, 发送P信号完毕直接返回.            }            //若数据未发送完毕直接发送, 缓冲区索引值++            IICDS = g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++];            // 将数据写入IICDS后,需要一段时间才能出现在SDA线上            for (i = 0; i < 10; i++);            IICCON = 0xaf;      // 恢复I2C传输            break;        }        case RDDATA:        {            // 这次中断是发送I2C设备地址后发生的,没有数据, 第一次发送完地址之后(发送完一个字节会产生中断)产生的中断, 接收数据之后2440是需要发出ACK信号的            if (g_tS3C24xx_I2C.Pt == -1)            {                // 只接收一个数据时,不要发出ACK信号                g_tS3C24xx_I2C.Pt = 0;//准备从数据缓存区的0开始发送数据                if(g_tS3C24xx_I2C.DataCount == 1)                   IICCON = 0x2f;   // 恢复I2C传输,开始接收数据,关闭ACK, 接收到数据时不发出ACK, 最后一个数据要先关闭ack, 因为最后一个数据要发送no ack, 其实就是不发送ack                else                   IICCON = 0xaf;   // 恢复I2C传输,开始接收数据, 0x2f与0xaf的区别就是前者不开ack                break;            }            if ((g_tS3C24xx_I2C.DataCount--) == 0)            {                    //如果要读取的剩余数据长度为0, 就发送P信号                g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++] = IICDS;                // 下面两行恢复I2C操作,发出P信号                IICSTAT = 0x90;                IICCON  = 0xaf;                Delay(10000);  // 等待一段时间以便P信号已经发出                break;            }           g_tS3C24xx_I2C.pData[g_tS3C24xx_I2C.Pt++] = IICDS;           // 接收最后一个数据时,不要发出ACK信号           if(g_tS3C24xx_I2C.DataCount == 0)               IICCON = 0x2f;   // 恢复I2C传输,接收到下一数据时无ACK           else               IICCON = 0xaf;   // 恢复I2C传输,接收到下一数据时发出ACK           break;        }        default:            break;    }}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *以上为中断方式操作I2C总线, 以下使用轮询方式来操作* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

以下以 LM75(数字温度传感器) 来说明 轮询方式 读取温度值的方法.

控制过程: 1. 读取温度值之前需要先指定 LM75 的寄存器, LM75 的寄存器 0x0 是温度寄存器器(READ ONLY). 所以先发送S信号再发送 LM75 的地址, 然后发送 0x0 就可以选中温度寄存器

2. 选中温度寄存器之后, LM75 会将 高/低 字节依次发送到I2C总线. 所以, 再次发送 LM75 地址, 读取 高/低 字节 发送P信号结束传输.

int set_pointer_and_read_2byte(int mode){               //主机发送模式, 选中温度寄存器        I2C0.I2CDS0 = 0x90;                // 发送LM75地址         I2C0.I2CCON0 = 0xe0;                // 使能, PRESCALER:512 ,RX/TX 中断使能        I2C0.I2CSTAT0 =0xf0;                // 主发送模式, 启动, 使能 RX/TX         while(!(I2C0.I2CCON0&(1<<4)));    // 等待直至发送完成                 I2C0.I2CDS0 = mode;                // READ TEMPERATURE ONLY 读取温度寄存器        I2C0.I2CCON0 &= ~(1<<4);            // 清除挂起标志 & 恢复总线操作         while(!(I2C0.I2CCON0&(1<<4)));    // 等待直至发送完成                 //主机接收模式, 开始接收温度数据        I2C0.I2CDS0 = 0x91;                // 重新发送LM75地址         I2C0.I2CSTAT0 =0xb0;                // 主机接收模式, 启动, 使能 RX/TX         I2C0.I2CCON0  &= ~(1<<4);            // 清除挂起标志 & 恢复总线操作         while(!(I2C0.I2CCON0&(1<<4)));        // 等待直至发送完成         I2C0.I2CCON0 &= ~(1<<4);             // 清除挂起标志 & 恢复总线操作         while(!(I2C0.I2CCON0&(1<<4)));        // 等待直至读取完成         high = I2C0.I2CDS0;                // 读取低8位数据         I2C0.I2CCON0 &= ~((1<<7)|(1<<4));// 清除挂起标志 & 恢复总线操作 & 禁止发送 ACK          while(!(I2C0.I2CCON0&(1<<4)));        // 等待直至读取完成          low = I2C0.I2CDS0;                    // 读取更低字节数据(小数部分 ?)          I2C0.I2CSTAT0 &= ~(1<<5);        // 发送 P信号, 释放总线          I2C0.I2CCON0 &= ~(1<<4);        // 清除中断标志位          return ((high << 8) | low);}

到此,相信大家对"arm9 IIC接口有什么用"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0