温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

arm9 IIC接口有什么用

发布时间:2021-12-20 10:46:42 来源:亿速云 阅读:85 作者:iii 栏目:互联网科技

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

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

主机发送模式使用流程:

arm9 IIC接口有什么用

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

arm9 IIC接口有什么用

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信号发送, 标记各种状态

arm9 IIC接口有什么用

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<<oft;
	INTPND = INTPND;

    /* 调用中断服务程序 */
    isr_handle_array[oft]();
}

以下为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接口有什么用”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI