打印
[牛人杂谈]

I2C 初始化

[复制链接]
4267|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
I2c, AC, ck, ip
I2C相信大家都不陌生,很多人都用GPIO模拟过I2C。但是模拟的太占CPU资源,并且一般只能模拟Master,模拟slave还是挺困难的。
下面介绍一下I2C IP。
这个是I2CON寄存器,I2C的控制寄存器
从左往右依次为:中断使能位,状态变化指示位,发送START信号,发送STOP信号,回ACK,使能I2C IP
²  I2C_STS:I2C 的状态发生变化该位就会置 1
²  START:请求 I2C IP 发送 START 信号。一旦发送成功,该 I2C就作为 I2CMaster
²  STOP:请求 I2C IP 发送 STOP 信号
²  ACK:如果收到数据的时候该位为 1,I2C IP 将回 ACK 给对方,否则回 NACK

其实从I2C的波形就可以看出,I2C协议完全是状态驱动的,从START信号开始,发送/接收地址字节之后收到ACK/NACK状态,发送/接收数据之后收到ACK/NACK状态,发送STOP
这个就是状态寄存器,状态一旦发生改变,就会发生I2C中断,然后读该寄存器就知道发生了何事。

沙发
zhuomuniao110|  楼主 | 2016-5-15 11:56 | 只看该作者
下图就是I2C作为Master和Slave时各个状态的含义表格:
0xF8是总线空闲的状态值,也是STATUS寄存器的缺省值。
下面详细介绍一下上表的各个状态。
Master状态介绍:
1)      发送 START 信号成功,发生 I2C 中断,STATUS 寄存器的值=0x08
2)      作为 I2C Master 没有发送 STOP 又发送 START 信号成功,发生 I2C 中断,STATUS 寄存器的值=0x10
3)      发送地址+W 成功并收到 ACK,发生 I2C 中断,STATUS 寄存器的值=0x18
4)      发送地址+W 成功并收到 NACK,发生 I2C 中断,STATUS 寄存器的值=0x20
5)      发送数据成功并收到 ACK,发生 I2C 中断,STATUS 寄存器的值=0x28
6)      发送数据成功并收到 NACK,发生 I2C 中断,STATUS 寄存器的值=0x30
7)      Master发生仲裁失败,发生 I2C中断,STATUS寄存器的值=0x38
8)      发送地址+R 成功并收到 ACK,发生 I2C 中断,STATUS 寄存器的值=0x40
9)      发送地址+R 成功并收到 NACK,发生 I2C 中断,STATUS 寄存器的值=0x48
10)   收到数据并返回 ACK,发生 I2C 中断,STATUS 寄存器的值=0x50
11)   收到数据并返回 NACK,发生 I2C 中断,STATUS 寄存器的值=0x58
12)   总线错误,发生 I2C 中断,STATUS 寄存器的值=0x00

使用特权

评论回复
板凳
zhuomuniao110|  楼主 | 2016-5-15 11:57 | 只看该作者
Slave状态介绍:
1)      收到 RE-START 信号或者 STOP 信号,发生 I2C 中断,STATUS 寄存器的值=0xA0
2)      收到 SLA+R 信号并返回 ACK,发生 I2C 中断,STATUS 寄存器的值=0xA8
3)      作为 Master 仲裁失败 HW 会自动转为 Slave,之后收到 SLA+R 信号,发生 I2C 中断, STATUS 寄存器的值=0xB0
4)      发送数据并收到 ACK,发生 I2C 中断,STATUS 寄存器的值=0xB8
5)      发送数据并收到 NACK,发生 I2C 中断,STATUS 寄存器的值=0xC0
6)      从接发送最后一个数据,但是居然收到的是 ACK,发生 I2C 中断,STATUS 寄存器的值
=0xC8
7)      从接收到 SLA+W 并返回 ACK,发生 I2C 中断,STATUS 寄存器的值=0x60
8)      作为 Master 仲裁失败 HW 会自动转为 Slave,之后收到 SLA+W 信号,发生 I2C 中断, STATUS 寄存器的值=0x68
9)      收到数据并返回 ACK,发生 I2C 中断,STATUS 寄存器的值=0x80
10)   收到数据并返回 NACK,发生 I2C 中断,STATUS 寄存器的值=0x88
11)   广播模式收到 SLA+W 并返回 ACK,发生 I2C 中断,STATUS 寄存器的值=0x70
12)   广播模式仲裁失败,发生 I2C中断,STATUS寄存器的值=0x78
13)   广播模式收到数据并返回 ACK,发生 I2C 中断,STATUS 寄存器的值=0x90
知道所有的状态之后,配置好I2C,然后在中断里面查看STATUS寄存器,处理状态就可以了。超时中断是用户设定时间内没有发生状态改变,就会发生超时中断,这个可以防止软件被hold住。
下面是一个读/EEPROM的例子。照样是先选择用到的IP的时钟源,使能各个IP的时钟,然后配置多功能引脚,然后是I2C IP功能初始化。另外是I2C 的中断处理函数。中断处理函数中根据 STATUS寄存器的值,进行状态处理。只要调用函数I2C_SET_CONTROL_REG(I2C0, I2C_SI);清除状态改变标志,I2CIP就会自动开始下一个状态。


void I2C0_IRQHandler(void) 
{

    uint32_t u32Status;

    /* 清除中断标志 */
    I2C0->INTSTS |= I2C_INTSTS_INTSTS_Msk;
                 /*取得STATUS寄存器的值*/
    u32Status = I2C_GET_STATUS(I2C0);
         
                 /*检查中断标志*/
    if (I2C_GET_TIMEOUT_FLAG(I2C0)) {/*发生超时中断*/
        /* 清除超时中断标志 */
        I2C_ClearTimeoutFlag(I2C0);     } else {/*状态改变中断*/
        if (s_I2C0HandlerFn != NULL)             s_I2C0HandlerFn(u32Status);
    }
}

/*  I2C 接收回调函数                                                                              
*/ void I2C_MasterRx(uint32_t u32Status)
{
    if (u32Status == 0x08) {        /* START 被发送,准备SLA+W */
        I2C_SET_DATA(I2C0, (g_u8DeviceAddr << 1)); /* 写 SLA+W 到数据寄存器 I2CDAT */
        I2C_SET_CONTROL_REG(I2C0, I2C_SI); /*清除状态改变标志,I2C IP开始发送SLA+W*/
    } else if (u32Status == 0x18) {             /* SLA+W 已经发送并且收到ACK */
        I2C_SET_DATA(I2C0, g_au8TxData[g_u8DataLen++]);/*将要发送的数据写到I2CDAT寄存器*/
        I2C_SET_CONTROL_REG(I2C0, I2C_SI); /* 清除状态改变标志, I2C IP开始发送数据 */
    } else if (u32Status == 0x20) {             /* SLA+W 已经被发送并且收到NACK */
        I2C_SET_CONTROL_REG(I2C0, I2C_STA | I2C_STO | I2C_SI); /*发送STOP并重新发送START信号*/
    } else if (u32Status == 0x28) {             /* DATA 已经被发送并且收到ACK */         if (g_u8DataLen != 2) {
            I2C_SET_DATA(I2C0, g_au8TxData[g_u8DataLen++]);/*继续写数据到I2CDAT寄存器*/
            I2C_SET_CONTROL_REG(I2C0, I2C_SI); /*清除状态改变标志,I2C IP开始发送数据*/         } else {
            I2C_SET_CONTROL_REG(I2C0, I2C_STA|I2C_SI);/*清除状态改变标志并再次发送START信号*/         }
    } else if (u32Status == 0x10) {             /* Repeat START 已经被发送,准备SLA+R */
        I2C_SET_DATA(I2C0, (g_u8DeviceAddr << 1) | 0x01);  /* 写 SLA+R 到I2CDAT寄存器 */
        I2C_SET_CONTROL_REG(I2C0, I2C_SI); /*清除状态改变标志,I2C IP开始发送SLA+R*/
    } else if (u32Status == 0x40) {             /* SLA+R 已经发送并且收到ACK信号 */


使用特权

评论回复
地板
zhuomuniao110|  楼主 | 2016-5-15 11:57 | 只看该作者
I2C是非常重要的一个总线,好多小型的存储芯片都是采用的I2C进行通信的。

使用特权

评论回复
5
lovecat2015| | 2016-5-15 22:39 | 只看该作者
我现在用GPIO模拟i2c很难达到400Khz,不知道楼主又什么好的方法提高速率

使用特权

评论回复
6
robter| | 2016-5-16 07:52 | 只看该作者
很好很好,学习学习

使用特权

评论回复
7
奥卡姆剃刀| | 2016-5-16 07:59 | 只看该作者
顶楼主!

使用特权

评论回复
8
ccw1986| | 2016-5-16 22:51 | 只看该作者
基本上i2c的初始化流程一样,就看硬件有没有bug

使用特权

评论回复
9
734774645| | 2016-5-17 00:03 | 只看该作者
中断处理函数中根据 STATUS寄存器的值,进行状态处理。

使用特权

评论回复
10
643757107| | 2016-5-17 14:40 | 只看该作者
I2C通信需要几根线啊?4根吗,两根数据,VCC和GND。?

使用特权

评论回复
11
稳稳の幸福| | 2016-5-17 20:42 | 只看该作者
I2C结构简单,但是逻辑复杂,因此这个比较难,不过由于可靠性高,因此和232,SPI,I2C是单片机现场总线使用最多的三种。

使用特权

评论回复
12
zhuomuniao110|  楼主 | 2016-5-22 23:15 | 只看该作者
ACK:如果收到数据的时候该位为 1,I2C IP 将回 ACK 给对方,否则回 NACK

使用特权

评论回复
13
598330983| | 2016-5-23 19:43 | 只看该作者
I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。

使用特权

评论回复
14
598330983| | 2016-5-23 19:44 | 只看该作者
它是同步通信的一种特殊形式,具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点。I2C 总线支持任何IC 生产过程(CMOS、双极性)。通过串行数据(SDA)线和串行时钟 (SCL)线在连接到总线的器件间传递信息。每个器件都有一个唯一的地址识别(无论是微控制器——MCU、LCD 驱动器、存储器或键盘接口),而且都可以作为一个发送器或接收器(由器件的功能决定)。LCD 驱动器只能作为接收器,而存储器则既可以接收又可以发送数据。除了发送器和接收器外,器件在执行数据传输时也可以被看作是主机或从机(见表1)。主机是初始化总线的数据传输并产生允许传输的时钟信号的器件。此时,任何被寻址的器件都被认为是从机。

使用特权

评论回复
15
玛尼玛尼哄| | 2016-5-24 11:18 | 只看该作者
感觉用起来好复杂,这么多配置。

使用特权

评论回复
16
643757107| | 2016-5-25 14:59 | 只看该作者
初始化过程是很重要的,能否工作都看着一步了。

使用特权

评论回复
17
rtmlw| | 2016-11-28 13:15 | 只看该作者
我将新唐的例子下载到硬件后,I2C也没有正常工作,这又是为什么呢?

使用特权

评论回复
18
zhuomuniao110|  楼主 | 2016-11-29 08:30 | 只看该作者
rtmlw 发表于 2016-11-28 13:15
我将新唐的例子下载到硬件后,I2C也没有正常工作,这又是为什么呢?

电路没有配置对吧。看看电路咋弄的。

使用特权

评论回复
19
wahahaheihei| | 2016-11-30 11:54 | 只看该作者
目前来说,我用的最多的是SPI。

使用特权

评论回复
20
zhuomuniao110|  楼主 | 2016-12-4 14:58 | 只看该作者
有时候不一定要查软件问题,多数时候容易忽略硬件问题。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

204

主题

3331

帖子

10

粉丝