This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430FR5738 I2C slave mode DMA 问题

Other Parts Discussed in Thread: MSP430FR5738, MSP430FR5739

MSP430FR5738 I2C DMA 模式调试时碰到了一个问题,貌似是430单片机的bug,问题描述如下

1、P1.6/1.7配置成I2C Slave 通信模式,I2C Slave RX 和Slave TX配置成DMA模式,配置代码如下

      由于I2C 接受数据时,事先并不知道接收长度,在DMA接收时,当STOP信号到来时,表示一帧数据的结束。

      当STOP信号到来时,结束当前DMA传输

      1. I2C Config

void I2C_Slave_Init(unsigned char address)
{
UCB0CTLW0 |= UCSWRST; //reset I2C Module
UCB0CTLW0 |= UCMODE_3 + UCSYNC; //I2C Slave
UCB0CTLW1 &= ~USCI_I2C_UCCLTOIFG;
UCB0CTLW1 |= (0x01 << 6);
UCB0I2COA0 = address + UCOAEN; //I2C ADDRESS

//Set P1.6 & P1.7 on I2C MODE
P1SEL1 |= BIT6 + BIT7;
P1SEL0 &= ~BIT6;
P1SEL0 &= ~BIT7;

UCB0CTLW0 &= ~UCSWRST; //release reset I2C Module
UCB0IE__I2C |= UCSTPIE;//Enable I2C INTERRUPT

}

2、I2C Slave RX DMA config

void I2C_Slave_RX_DMA_Config()

{

DMA0CTL = 

(0x00 << 12) //Single Transfer
|(0x03 << 10) //Dest Inc
|(0x00 << 8) //Src Unchange
|(0x01 << 7)
|(0x01 << 6);


DMA0SA = (unsigned int)&UCB0RXBUF;
DMA0DA = (unsigned int)buf;
DMA0SZ = 40;
DMACTL0 |= 18;//MSP430FR5738 I2C Slave RX Trigger

DMA0CTL |= DMAIE;
DMA0CTL |= DMAEN;

}

3、中断

switch(__even_in_range(UCB0IV,0x1E))
{

case 0x08:
if(UCB0CTLW0_bit.UCTR == 0) //Receive
{

      DMA I2C Slave RX ReConfig

}
else //Transmitter
{
     DMA I2C Slave TX ReConfig
}
UCB0IFG = 0;
break;

default:
break;
}

main()

{

   .....

   I2C_Slave_Init();

   I2C_Slave_RX_DMA_Config();

  _EINT();

  while(1)

{
...

....

//P2DIR_bit.P2DIR2 = 1;

}

}

问题

如果在while(1)的循环里涉及到单片机IO寄存器的配置,操作,则 I2C Slave RX DMA接收就会出问题,表现目前监测到两种情况,一种就是没有识别STOP信号,无中断产生,另一种是中断IFG标志已产生,但没有进中断服务程序,如果此时在仿真器中暂停一次,再全速运行,则可以进中断服务程序

如果while(1)里没有对IO端口的操作,则整个I2C Slave RX DMA 没有任何问题

对IO端口寄存器配置操作,不会影响I2C Slave TX DMA

  • 请您先看一下 www.ti.com/.../slaz391ae.pdf 的 DMA9

    我手边没有这个型号的板子,我会找同系列的来测试一下,请您等待,谢谢
  • 用TI自己的MSP430FR5739的试验板测试就可以
  • 嗯 但是我手边没有这个板子,只有5969的
  • 你好

    首先有一些代码上的建议
    1. 清除USCI_I2C_UCCLTOIFG 标志位应该是在UCB0IFG 寄存器中
    2. UCB0CTLW1 |= (0x01 << 6); 你是打算使能 Clock low timeout 对吧?
    3. 对于DMA 源地址和目的地址的配置请参照我们的参考历程dev.ti.com/.../node

    为了我们能更快的定位问题, 可以提供一个完整的CCS 或IAR 的可以复现问题的最简工程吗? 最好附上Host 端的代码如果Host 也是MSP430

  • 1. 清除USCI_I2C_UCCLTOIFG 标志位应该是在UCB0IFG 寄存器中
    2. UCB0CTLW1 |= (0x01 << 6); 你是打算使能 Clock low timeout 对吧?
    -----1.2两个问题,这个功能可以去掉的,就是因为我发现这个问题本身后,启用这个功能看能不能自恢复的,这个功能的开和关不影响这个问题的复现。
    3. 对于DMA 源地址和目的地址的配置请参照我们的参考历程dev.ti.com/.../node
    ----这个我会去看一下
    4、完整的功能,和HOST端问题
    ----完整的IAR工程可以的,怎么提供呢?I2C的Host端,我们为了调试方便,是采购了一批USB转I2C的模块的,型号是Ginkgo的USB转I2C,我们自己写的Host端,不灵活,所以调试I2C功能的时候,我们直接使用的这种模块
  • 你好

    代码上传可以在这里使用高级编辑器编辑文本 如下图里面就可以上传附件

  • 为什么有两个工程,有什么区别吗?
  • 一样的,我不小心传了两次,一次测试,我以为没传上

  • 你好

    感觉你的通信逻辑有些问题
    1. HAL_I2C_Slave_Transfer_DMA(tx_buf,100);
    HAL_I2C_Slave_Receive_DMA(rx_buf,100);
    我在不连接Host 端的时候这两个函数都能执行通过。从逻辑上应该先实现100字节的发送再执行100字节接收。如果你没有发送完100字节就需要等待host. 解决方案建议你可以使用DMA完成中断里设一个标志变量然后等待这个标志变量。
    2. 你只使能了I2C 的stop中断,但在stop 中断中你判断UCB0CTLW0_bit.UCTR 来判断接收或发送。Stop 代表通信结束,你为什么在通信结束后再去判断发送还是接收,有点奇怪。

    你可以把你想要实现的最简单的通信流程描述一下,我可以给你一下实现上的建议。

    Gary
  • 因为这个程序,I2C的工作模式是Slave 模式,所以所有的读写发起都是由另外的Master发起的,
    Slave TX DMA和RX DMA都是配置好了,如果外部有Write操作,则Slave RX会接收数据,Stop来的时候,表示一包数据结束,而外部有Read操作的时候,Slave TxDMA会工作,将txbuf里的数据发出去,Stop到的时候,表示一包数据读操作结束。
    因此不管是TX还是RX,最终都会产生Stop信号,但两者在程序中的处理是不一样的,所以需要在Stop中断里判断是READ还是WRITE的中断,以做出不同的处理。

    我想做的就是一个I2C Slave 模式,可以正常收发数据。以前是用的非DMA模式操作的,这个对I2C通信来说,没有问题。但由于我需要处理的一部分工作对时间特别敏感,因此在I2C读写操作时,非DMA模式,会频繁的进中断,每收到或发出一个字节就要进一次中断,这个时间会影响程序的部分功能,所以我就想切换成DMA模式,只在Stop到来时集中处理一次中断,那我在程序中可以把这个因素处理掉,所以我才想换成DMA模式的

    现在的问题是,主程序循环中只要不涉及到IO端口的操作,I2C的所有功能都是正常的,但主循环中如果涉及到对端口寄存器的配置,就会影响I2C的通信(主要影响I2C slave RX模式,即主机WRITE的时候会出错,主机READ,即Slave DMA TX模式目前没有错误),对IO端口操作越频繁就越容易报错,主要的I2C错误目前发现两种,一种就是识别不到STOP中断,所以I2C 模块的状态机就在Busy态出不来,还有一种状态从仿真看,是识别到了STOP中断的,但就是不进中断服务程序,如果此时在仿真器按暂停,就发现IFG是置位了的,中断后再全速运行,又是可以进中断服务程序的。这个我在主贴里已经描述过了。
    上面就是我目前发现的问题。

  • 你好

    我已经把代码跑了一下。好像有时候是会进不了stop 中断。 我会做进一步测试。等有结论了会通知你。谢谢
  • 你好

    这个问题是由于DMA7 引起的。

    指令

    P2DIR_bit.P2DIR0 = 1;
    P2OUT_bit.P2OUT0 = 1;
    P2OUT_bit.P2OUT0 = 0;

    经编译后均为 BIS.B 或BIC.B 这些都是 read-modify-write instruction,当CPU 执行这些指令时如何产生DMA请求,CPU 会miss 下一个中断。

    解决方法也很简单

    避免使用read-modify-write instruction。 比如要将P2OUT 的BIT0 置位可以这样操作:定义一个中间变量buffer,  buffer=P2OUT; 然后 P2OUT=buffer|BIT0;

  • ok,我这两天试一下你的这个方案,有结果我在这里回复
  • 你的说法也许是对的,但这个限制条件在工程实践中无法合理规避,我们正常程序都是用C语言写的,在编译过程中没法完全规避这类指令。。。 P2OUT = buffer | BIT0;这个指令编译后还是有BIS.B指令。

    所以这个问题,貌似是无解的。TI有计划在以后的版本中修复这个bug吗
  • IAR 和 CCS 都有反汇编功能你可以检查一下。目前木有修复计划。
  • 嗯,我知道的,我就是看了编译出来的汇编代码之后,发现有很多这种指令的
  • 不好意思更正一下:
    BIS.B和BIC.B 的操作对象不能是状态或中断寄存器。P2OUT = buffer | BIT0;中 BIS.B 操作的是 CPU 寄存器。
    这个BUG 已经在新的产品中修复掉了但是一些旧的的产品只能使用上面的方法进行检查了。