KL25的I2C模块调试的两个注意事项(Two tips in the debugging of KL25's I2C)

Document created by Const Yu Employee on Mar 2, 2014
Version 1Show Document
  • View in full screen mode

中文版本:


    在KL25的官方Demo 源代码中只有I2C驱动的PE代码而没有I2C驱动的baremental代码,对于不习惯用PE生成代码的用户直接上手有难度,于是考虑将K60的 I2C baremental 驱动代码中移植到KL25上,以供大家参考。但在移植过程中遇到了两个比较典型的问题,所以这里分享出来,希望能帮助遇到同样问题的用户迅速定位并解决问题。

 

测试硬件:TWR-K60D100M开发板  K60+MMA8451(MMA8451为三轴加速传感器,与K60通过I2C总线连接。K60作为master,MMA8451作为slave)

                FRDM-KL25Z开发板       KL25+MMA8451

开发环境:IAR 6.6

 

1.问题描述: 配置I2Cx_F寄存器MULT位不为0时,Repeat start信号无法产生


问题提出:

K60示例代码(如附件1)中I2C demo的功能是通过I2C接口读取板载的加速度传感器MMA8451的数据,并且I2C数据控制采用查询ACK标志位的方式,在TWR-K60D100M开发板上运行该Demo一切正常。使用几乎相同的I2C驱动代码,在FRDM-KL25Z开发板上执行发现:程序总是停在如下Function 1的红色字体行i2c_wait(I2C0_B),进入这个函数内部,它实际上是停在while((p->S & I2C_S_IICIF_MASK)==0),一直等待传输完成的中断标志IICIF置位。

 

Function 1.

u8 hal_dev_mma8451_read_reg(u8 addr)

{

    u8 result;

 

    i2c_start(I2C0_B);

 

    i2c_write_byte(I2C0_B, I2C_ADDR_MMA8451 | I2C_WRITE);

    i2c_wait(I2C0_B);

    i2c_get_ack(I2C0_B);

 

    i2c_write_byte(I2C0_B, addr);

    i2c_wait(I2C0_B);

    i2c_get_ack(I2C0_B);

 

    i2c_repeated_start(I2C0_B);

    i2c_write_byte(I2C0_B, I2C_ADDR_MMA8451 | I2C_READ);

    i2c_wait(I2C0_B);

    i2c_get_ack(I2C0_B);

 

    i2c_set_rx_mode(I2C0_B);

 

    i2c_give_nack(I2C0_B);

    result = i2c_read_byte(I2C0_B);

    i2c_wait(I2C0_B);

 

    i2c_stop(I2C0_B);

    result = i2c_read_byte(I2C0_B);

    pause();

    return result;

}

Function 2.

void i2c_wait(I2C_MemMapPtr p)

{

    while((p->S & I2C_S_IICIF_MASK)==0)  ; // wait flag

    p->S |= I2C_S_IICIF_MASK;    // clear flag

}

 

原因分析:

     初步判断可能是上一步数据的传输 i2c_write_byte()没有完成,导致IICIF未能被置位。于是通过示波器去捕捉这个过程,发现在执行 i2c_repeated_start(I2C0_B)时,KL25并没有产生一个 Repeat start信号。经过一番谷哥和度娘,终于在Kinetis L的Errata中找到了答案Repeat start cannot be generated if the I2Cx_F[MULT] field is set to a non-zero value. 这也就意味着,当 I2Cx_F[MULT]位被设置为非0值时,I2C Master不能产生一个Repeat start信号。而在应用程序的I2C初始化I2C_init()代码中, 我恰好设置I2Cx_F[MULT]=01,这正好是符合了Errata描述的错误产生的条件。

 

解决方案:

     I2C的C1寄存器中MULT位是I2C SCL时钟的倍乘因子,用于控制I2C的波特率。为解决上面的问题,FSL官方提供了两种workaround的办法:

1)如果repeat start必须产生时,配置 I2Cx_F[MULT]为0;

2)在置位 I2Cx_F register (I2Cx_C1[RSTA]=1)的Repeat START产生位之前临时设置 I2Cx_F [MULT],然后再在repeated start信号产生后恢复I2Cx_F [MULT]位的设置。

按照第一种方法,我修改程序中I2Cx_F[MULT]的设置从01到00,然后程序在FRDM-KL25Z 开发板上运行正常,能正常读取板载的加速度传感器MMA8451的数据。

 

2.问题描述: I2C单字节读取时序问题

 

问题提出:

在上面的Function 1中, KL25读取MMA8451的基本过程是:发送要访问的从机地址及对从机的写命令->发送要访问的从机的寄存器地址->发送Repeat Start信号到从机->发送要访问的从机地址及读命令->读取从机返回的数据,如下Figure1 MMA8451的单周期读时序图所示,其过程和上面代码的描述一致。但是有一点值得注意的是Figure 1中红色方框部分,按照Figure 1的表述,Master是在从Slave从机读取DATA[7:0]之后返回NAK信号的,用于指示本数据是Master要接收的最后一个DATA,最后发送stop signal终止数据的传送。按照这个思路得到的KL25的程序代码如下Section 2,它首先去读取从机返回的数据 i2c_read_byte(I2C0_B),然后发送NACK信号到从机i2c_give_nack(I2C0_B)。然而从KL25实际的物理时序的角度看,这个顺序是错误的,正确的应该是如下Section 1,应该在读取从机返回的数据 i2c_read_byte(I2C0_B)之前,首先发送NACK信号到从机i2c_give_nack(I2C0_B)。

Section 1.

  i2c_set_rx_mode(I2C0_B);

  i2c_give_nack(I2C0_B);----line1

  result = i2c_read_byte(I2C0_B);----line2

  i2c_wait(I2C0_B);----line3

  i2c_stop(I2C0_B);----line4

  result = i2c_read_byte(I2C0_B);----line5

Section 2.

  i2c_set_rx_mode(I2C0_B);

  result = i2c_read_byte(I2C0_B);-

  i2c_wait(I2C0_B);

  i2c_give_nack(I2C0_B);-

  i2c_stop(I2C0_B);

原因:

主机发送的NACK信号只有在下一个数据接收之后才会被push到总线上,KL25的RM手册中的描述为the No acknowledge signal is sent to the bus after the following receiving data byte (if FACK is cleared)。

 

具体分析:

按照两个时序分别做了一个测试,并用示波器捕捉了相应的波形:执行Section 1的代码得到的波形如下Figure 2所示,NACK(1)信号刚好在第9个pluse脉冲上升沿被push总线上,然后在Stop信号后总线处于idle状态(SCL和SDA均为高)。执行Section 2的代码得到的波形如下Figure 3所示,ACK(0)信号在第9个pluse脉冲上升沿被push总线上,说明后面还有数据要传输,一直处于等待MMA8451数据的再次传送中,这明显违背了读取单字节数据的原本意图。总之,KL的I2C应用中Section 1的代码操作顺序是正确的,实际的物理时序和 Figure 1的示意图时序是不一样的,这点需要特别注意。

 


Figure 1. MMA8451's 单周期读时序示意图

8451.png

Figuire 2. Section 1 代码对应的时序

IMG_20130816_140822.jpg

Figure 3. Section 2 代码对应的时序

IMG_20130816_141117.jpg


为方便大家验证这些问题,我这里在附件中一并上传了K60的I2C的示例代码,KL25的示例代码,以及Kinetis L关于I2C的Errata。

——————————————————————————————————————————————————————————————————————

English Version:


     Recently, I migrate the K60’s I2C demo code to the KL25, but found it can't works when the same demo code runs on FRDM-KL25Z board while it runs well on the K60 board. After a painful struggling, I finally get the cause, so here I make a record, wish it could be helpful when other users happen to meet same problem.

  1. Repeat start can't be generated when configure I2Cx_F[MULT] to non-zero

     The K60’s demo( the attached 1) is to communicate with the onboard accelerometer MMA8451 by I2C, and in the demo it finish a data transmission by quering I2C’s flag bit. With almost same code, it always stops at below Function 1's red line i2c_wait(I2C0_B), also this function's defination is shown as below Function 2, it stops at while((p->S & I2C_S_IICIF_MASK)==0) to wait IICIF flag.


Function 1.

u8 hal_dev_mma8451_read_reg(u8 addr)

{

    u8 result;

 

    i2c_start(I2C0_B);

 

    i2c_write_byte(I2C0_B, I2C_ADDR_MMA8451 | I2C_WRITE);

    i2c_wait(I2C0_B);

    i2c_get_ack(I2C0_B);

 

    i2c_write_byte(I2C0_B, addr);

   i2c_wait(I2C0_B);

    i2c_get_ack(I2C0_B);

 

    i2c_repeated_start(I2C0_B);

    i2c_write_byte(I2C0_B, I2C_ADDR_MMA8451 | I2C_READ);

    i2c_wait(I2C0_B);

    i2c_get_ack(I2C0_B);

 

    i2c_set_rx_mode(I2C0_B);

 

    i2c_give_nack(I2C0_B);

    result = i2c_read_byte(I2C0_B);

    i2c_wait(I2C0_B);

 

    i2c_stop(I2C0_B);

    result = i2c_read_byte(I2C0_B);

    pause();

    return result;

}

Function 2.

void i2c_wait(I2C_MemMapPtr p)

{

    while((p->S & I2C_S_IICIF_MASK)==0)  ; // wait flag

    p->S |= I2C_S_IICIF_MASK;    // clear flag

}

     Then what's the matter? when I capture the I2C's wave form, found it didn't generate a Repeat start signal when excute i2c_repeated_start(I2C0_B);  After a struggle, In the Kinetis L's Errata do I find the answer: Repeat start cannot be generated if the I2Cx_F[MULT] field is set to a non-zero value. That means there is a bug in KL's design, if the I2Cx_F[MULT] field is set to a non-zero value, the I2C master can't generate a Repeat start signal. Coincidentally, in the I2C_init function I happen to set theI2Cx_F[MULT]=01, so it just meets the I2C's Errata.

 

     Considering the MULT bits define the multiplier factor mul. and  used along with the SCL divider to generate the I2C baud rate. In the Errata, FSL gives two possible workarounds:

1) Configure I2Cx_F[MULT] to zero if a repeat start has to be generated.

2) Temporarily set I2Cx_F [MULT] to zero immediately before setting the Repeat START bit in the I2C C1 register (I2Cx_C1[RSTA]=1) and restore the I2Cx_F [MULT] field to the original value after the repeated start has occurred.

 

To verify it easily, I revise the I2Cx_F[MULT] from 01 to 00. After that the same code runs well on FRDM-KL25Z board.


   2. The Timing Sequence Of I2C's single byte Reading


     In the above Function 1, there are a MMA8451 data read section like below after  Write Device Address->Write Register Address->Repeat Start->Write Device Address, and these steps is same as MMA8451's single byte read Timing Sequence requirment which is shown as below Figure 1. But referring to Figure 1, it looks like Section2 we should first excute below line2 to read the data, and then line1 give a nack  to suggest it's the last data, at last excute line4 to send a I2C stop signal. But unfortunately the idea is wrong, because in the phasical timing sequence the No acknowledge signal is sent to the bus after the following receiving data byte (if FACK is cleared) ,which means we need to give NACK signal before a read. And the captured wave form is like below Figure 2, you can find the NACK in the Ninth pluse, while the captured wave form is like below Figure 3 if excute Section 2 code instesd of Section 1 code, you can find the ACK in the Ninth pluse. it means the master will read another data, but the original intention is to read only one byte, so the I2C bus blocks. In a word, the section 1 code is right, the physical timing is different from the Figure 1's sketch map.

Section 1.

    i2c_set_rx_mode(I2C0_B);

    i2c_give_nack(I2C0_B);----line1

    result = i2c_read_byte(I2C0_B);----line2

    i2c_wait(I2C0_B);----line3

    i2c_stop(I2C0_B);----line4

    result = i2c_read_byte(I2C0_B);----line5

Section 2.

   i2c_set_rx_mode(I2C0_B);

   result = i2c_read_byte(I2C0_B);-

   i2c_wait(I2C0_B);

   i2c_give_nack(I2C0_B);-

   i2c_stop(I2C0_B);

Figure 1. MMA8451's single byte read Timing sketch map

8451.png

Figuire 2. Section 1 code's Timing

IMG_20130816_140822.jpg

Figure 3. Section 2 code's Timing

IMG_20130816_141117.jpg

Attachments

    Outcomes