本篇内容主要讲解“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<<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接口有什么用”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。