文档章节

arm9 IIC

季风_落地窗
 季风_落地窗
发布于 2014/05/14 15:25
字数 2155
阅读 423
收藏 4

在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 lcd controller
下一篇: arm9 uart
季风_落地窗
粉丝 4
博文 16
码字总数 16829
作品 0
杨浦
程序员
私信 提问
开源BOOT方案 - U-Boot

UBoot 是由开源项目PPCBoot发展起来的,ARMboot并入了PPCBoot,和其他一些arch的Loader合称U-Boot。2002年12月17日第一个版本U-Boot-0.2.0发布,同时PPCBoot和ARMboot停止维护。 U-Boot支持的...

匿名
2009/06/01
4.3K
0
Yocto tips (20): Yocto中qemu模拟器的使用,以zynq Cortex-A9为例

在以前的文章《使用Buildroot与Qemu学习ARM linux IIC驱动》中,写到过使用qemu来模拟使用ARM Linux,那个时候借助的是buildroot,这次我们使用Yocto来做同样的事情。 优点 使用Qemu的好处在...

sy373466062
2016/05/16
0
0
MSP430F5529LP(一)IIC与OLED的HELLOWRLD

简单理解下什么是IIC I2C(Inter-Integrated Circuit BUS) 集成电路总线,该总线由NXP(原PHILIPS)公司设计,多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻...

Pulsar-V
2018/04/16
1K
0
嵌入式定制开发选ARM7还是ARM9

ARM公司设计了许多处理器,它们可以根据使用的不同内核划分到各个系列中。系列划分是基于ARM7、ARM9、ARM10、ARM11和Cortex内核。后缀数字7、9、10和11表示不同的内核设计。数字的升序说明性...

gunser
2018/06/01
0
0
pcDuino/Arduino IIC LCD

IIC LCD在传统的LCD1602基础上改变了通讯方式,由并行数据传输改为IIC通讯。传统的LCD1602总共需要8根数据线和3根控制线(使能控制、读写选择和寄存器选择),而IIC LCD只要2根数据线(数据线...

酸酸苹果汁
2014/01/28
1K
1

没有更多内容

加载失败,请刷新页面

加载更多

springboot 403 问题

添加WebAppConfigurer 配置 @Configuration@EnableAutoConfigurationpublic class WebAppConfigurer extends WebMvcConfigurerAdapter { public WebAppConfigurer() { } ......

布袋和尚_爱吃鱼
9分钟前
2
0
Python自动更换壁纸爬虫与tkinter结合

直接上代码 import ctypesimport timeimport requestsimport osfrom threading import Threadfrom tkinter import Tk, Label, Button,Entry,StringVar,messagebox# '放到AppData\Roami......

物种起源-达尔文
10分钟前
1
0
Postgresql Study 笔记

Postgresql 安装 Windows, MAC Install Postgresql 下载地址: https://www.enterprisedb.com/downloads/postgres-postgresql-downloads Linux Install sudo apt-get update sudo apt-get in......

slagga
11分钟前
2
0
layer.open 打开新页面传参问题

如图所示,点击出售,把A页面的数据传到弹框上面,因为弹框比较复杂,所以使用引入一个新页面。 A.html a.js B.html b.js 1、第一种方案 sellInte: function (){ var obj = document.g...

木九天
14分钟前
2
0
沙龙报名 | 区块链数据服务技术应用实践

京东云是国内首家提供区块链数据在线分析服务产品的公司,也是行业内首家对区块链数据服务进行开源的公司。 本次沙龙是京东云BDS开源后,首次在深圳举办线下沙龙,我们将邀请京东云BDS团队核...

京东云技术新知
15分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部