本篇内容主要讲解“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接口有什么用”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/cxh1024/blog/264774