Microchip
直播中

陈思远

7年用户 207经验值
私信 关注
[问答]

PIC24FJ256GB406,为什么CN中断暂停SPI DMA传输?

[解决方案] 2011/2018:问题不存在如所描述的那样。DMA传输未暂停。由于逻辑分析仪的错误触发器,我看了一个错误的数据传输。]用逻辑分析仪查看我的PIC的两个不同SPI外设的信号,我惊讶地看到,我的DMA SPI传输(写入512个字节到闪存)被CN(IOC)中断/暂停。中断服务例程,它访问另一个SPI外围设备。只需注意:传输和中断工作得很好,这只是我想优化的时机。在我看来,DMA传输的想法是在没有MPU的情况下工作(使用SPI重复的连续模式),所以我没有想到MPU中断对DMA传输有影响。它。我的意图是,DMA传输应该继续而不是等待另一个(无关的)中断。虽然我有一个模糊的理解,DMA SPI传输确实依赖于内部的中断逻辑,但我还没有找到文档中的细节的解释,尤其是我如何才能解释。观察到的行为。我必须注意中断优先级还是应该继续搜索哪个方向…这里是一些背景信息。前4个频道显示了512字节DMA SPI传输(SPI 2)的摘录,它被CN中断(底部4通道)打断。CN中断例程读取来自另一SPI外围设备(SPI 1)的数据的七个16位字,即在最后4个通道中看到的数据传输也反映中断的长度。在中断退出时,DMA继续工作。在ButtoMod中看到的图像是中断功能。这是对DMA SPI传输的SPI 2的一次初始化,这是一个函数,它实际上对512字节的附加信息进行DMA传输:嵌套中断被启用(而不是DI)。中断优先级是所有4(这是复位后的默认值,例如,πSPI2TXIP,πSPI2IP)(设置为3,对于SPI2不改变行为)在这个PIC的中断向量表中。CN中断位于γ-SPI2中断和γ-SPI2TX中断之前,即“自然优先级”F。或CN高于SPI2(第8.1章FRM)

以上来自于百度翻译


      以下为原文

    ["solution" 11.Jan.2018:  the problem does not exist as described. The DMA transfer is NOT paused. Due to wrong trigger for the Logic Analyzer, I looked at a wrong data transfer.]
Looking at the signals of two different SPI peripherals of my PIC with a logic analyzer, I was a bit astonished to see that my DMA SPI transfer (writing 512 bytes into a flash memory) is interrupted/paused by a CN (IOC) interrupt service routine, which accesses another SPI peripheral.  Just to note: the transfers and interrupts work perfectly,- it's just the timing which I would like to optimize.
In my mind the idea of a DMA transfer is to work without the MPU (using SPI repeated continous mode), so I did not expect an MPU interrupt to have influence on the DMA transfer, by delaying it. My intention is that the DMA transfer should continue and not wait for another (unrelated) interrupt.
While I have a vague understanding that the DMA SPI transfer DOES depend on interrupt logic internally, I did not yet find an explanation of the details in the documentation, especially how can I change the observed behaviour.  Do I have to pay attention to interrupt priorities or in which direction should I continue to search ...

Here is some background information

The top 4 channels shows an excerpt of the 512 byte DMA SPI transfer (SPI 2), which is interrupted by a CN interrupt (bottom 4 channels). The CN interrupt routine reads seven 16-bit words of data from another SPI peripheral  (SPI 1), i.e. the data transfer seen in the last 4 channels also reflects the length of the interrupt. On exit of interrupt, the DMA continues to work.

See image at bottom
This is the interrupt function


void __attribute__((interrupt, auto_psv)) _CNInterrupt(void)
{
if (_IOCFB10)
  {
    spiReadData16(ICM_ACCEL_XOUT_H, (WORD *) dataICM.iData, ICM_DATA_COUNT);
    bIcmDataReady = 1;
    _IOCFB10 = 0;
  }

  _CNIF = 0;              // clear interrupt flag
}


This is some one-time initialization of SPI #2 for the DMA SPI transfer


  SPI2CON1bits.MODE16 = 0; // byte mode
  SPI2CON1bits.MSTEN = 1;  //SPI Master

  SPI2CON1bits.CKP = 0;    // Idle state for clock is a low level
  SPI2CON1bits.CKE = 1;   
  SPI2CON1bits.ENHBUF = 0;  
   
  SPI2STATLbits.SPIROV = 0; // clear the Receive overflow flag
  SPI2BRGLbits.BRG = 0;
  SPI2CON1bits.MCLKEN = 0;
   
  // SPIEN enabled; DISSDO disabled; MCLKEN FOSC/2; CKP Idle:Low, Active:High;
  //  SSEN disabled;MSTEN Master; MODE16 disabled; SMP Middle; DISSCK disabled;
  // SPIFE Frame Sync pulse precedes;CKE Idle to Active; MODE32 disabled;
  // SPISIDL disabled; ENHBUF enabled; DISSDI disabled;

  SPI2CON1Hbits.IGNROV = 1;  
  SPI2CON1Lbits.SPIEN = 1;  // enable SPI2 peripheral



and this is the function which actually does DMA transfers of 512 bytes



static void dmaWrite(BYTE *pBuf, WORD wCount, WORD wMode)
{
    DMACON = 0x0001;    // DMACONbits.DMAEN = 0;
    DMAH= DMA_UPPER_LIMIT;
    DMAL= 0x0000;
    // CHEN enabled; DAMODE Unchanged; TRMODE One-Shot; CHREQ disabled;

    // RELOAD disabled; SIZE 8 bit; NULLW disabled; SAMODE Incremented;
    // HALFIF disabled; LOWIF disabled; HALFEN disabled; DONEIF disabled;

    // OVRUNIF disabled; CHSEL SPI2 Event; HIGHIF disabled;
    DMACH0 = 0;   
    // SPI  "repeated continous mode"
    DMACH0bits.TRMODE=0;  // Transfer mode   
    DMACH0bits.SAMODE=1;  // Source address increment mode
    DMACH0bits.DAMODE=0;  // dest
    DMACH0bits.SIZE=1;  // byte
    DMACH0bits.NULLW=0;  // null write disable
   
    DMAINT0= 0x00;  

    IFS0bits.DMA0IF = false;
    _DMA0IE = 1;   // enable interrupt
   
    // for CHSEL see FRM Table 5-1
    DMAINT0bits.CHSEL = DMA_TRIG_SRC_SPI2_TRANSMIT;    //Trig on SPI2 TX
   
    DMASRC0= (WORD) pBuf;
   
    DMADST0= (unsigned int)&SPI2BUFL;
    DMACNT0= wCount;

    _SPI2TXIF = 0;
    SPI2IMSKLbits.SPITBEN = 1;  // interrupt if tx empty
    // Clearing Channel 0 Interrupt Flag;
    IFS0bits.DMA0IF = 0;
   
    //Enable DMA
    DMACONbits.DMAEN = 1;
   
    //Enable DMA Channel 0
    DMACH0bits.CHEN = 1;
    DMACH0bits.CHREQ = 1;  

// note, actually the end of the DMA transfer is signalled end handled
// by a DMA interrupt, not shown here
}

Additional information:
Nested interrupts are enabled  (not disabled)
Interrupt priorities are all 4 (which is the default after reset, e.g. _SPI2TXIP, _SPI2IP)
(setting them to 3 for SPI2 does not change the behaviour)
In the interrupt vectors table for this PIC the CNInterrupt comes BEFORE the __SPI2Interrupt and __SPI2TXInterrupt,
i.e. the 'natural priority' for CN is higher than for SPI2 (chapter 8.1 FRM)

   Attached Image(s)

回帖(9)

李椰潭

2018-11-22 15:26:18
我不认为CN中断对DMA传输有任何直接影响。但是在中断例程中,您调用SPICADATA1616()来从SPI1传输一些数据。这将暂停DMA,因为SPI1和SPI2寄存器通过相同的数据总线访问,并且CPU访问总是优先于DMA。如果修改代码,也可以使用DMA从SPI1读取数据(原因是使用另一个DMA通道)。然后,您可以使用固定的DMA优先级来提供SPI2比SPI1更高的优先级,然后SPI2传输将不间断地运行。如果有足够的时间,SPI1数据将在SPI2数据之间传输。否则SPI1数据将在SPI2传输结束后被转移。您还可以使用循环DMA优先级,然后两个传输将以“并行”方式运行。如果CPU/DMA速度不够高,SPI2传输速率可能会下降,而SPI1传输是活跃的。另一种方法是优化SpReDATA16()。例如,如果代码连续轮询SPI1寄存器,以检查数据是否可用,这也将使用DMA数据总线,并且暂停SPI2 DMA传输。因此,在SpReDATA16()程序中的一些NOP可以允许SPI2 DMA传输继续。最好的问候,Michael Brinks。

以上来自于百度翻译


      以下为原文

    I don't think the CN interrupt has any direct influence on the DMA transfer.
However in the interrupt routine you call spiReadData16() to transfer some data from SPI1. This will pause the DMA because SPI1 and SPI2 registers are accessed through the same data bus, and CPU access always takes priority over DMA.
 
If you modify your code to also use DMA for reading data from SPI1 (of cause using another DMA channel). Then you can use fixed DMA priority to give SPI2 higher preference than SPI1, the SPI2 transfer will then run uninterrupted. The SPI1 data will be transferred in between the SPI2 data, if there is time enough. Otherwise the SPI1 data will be transferred after the SPI2 transfer ends.
You can also use round robin DMA priority, then the two transfers will run in "parallel". If the CPU/DMA speed isn't high enough, the SPI2 transfer rate may drop, while the SPI1 transfer is active.
 
An other approach is to optimize spiReadData16(). E.g. if the code continuously polls a SPI1 register, to check if data is available, this will also use the DMA data bus, and pause SPI2 DMA transfers. So a few nops in the spiReadData16() routine could allow the SPI2 DMA transfer to continue.
 
Best regards,
Michael Brinks
举报

李雨晨

2018-11-22 15:45:20
谢谢你的提示,我想我明白你的意思了。为了验证,我简单地注释了SPIDEL16函数,然后添加了一些虚拟延迟。因此,不再有SPI 1访问(也被逻辑分析器验证):DMA SPI 2的“暂停”仍然具有CN中断的长度,现在由被添加的延迟环路“主宰”。因此,即使您的想法是正确的,似乎也不是“暂停”DMA SPI传输的主要原因。

以上来自于百度翻译


      以下为原文

    Thanks for the tip, I think I got your idea. So for verification I simply commented out the spiRead16 function and then added some dummy delay. Thus there is no SPI#1 access any more (also verified with Logic Analyzer)
Result:  the DMA SPI#2 "pause" still has the length of the CN interrupt, now "dominated" by the added delay loop.
So even if your idea is correct, it seems not to be the primary reason for the 'paused' DMA SPI transfer.
 
void __attribute__((interrupt, auto_psv)) _CNInterrupt(void)
{
if (_IOCFB10)
  {
    // spiReadData16(ICM_ACCEL_XOUT_H, (WORD *) dataICM.iData, ICM_DATA_COUNT);
   volatile int k;
    for (k=0;k<250;++k)    ;   // approx.  100 us,  just to make interrupt call longer
    bIcmDataReady = 1;
    _IOCFB10 = 0;
  }
 
  _CNIF = 0;              // clear interrupt flag
}
举报

薄坤坤

2018-11-22 15:58:28
不相信米迦勒的论点。我同意CPU在访问内存时优先于DMA,但只有当它们同时尝试访问相同的SFR(在这种情况下)时!SPI读缓冲器的实际CPU读取需要一个指令周期,但DMA看起来要比它长很多,事实上,在另一个SPI外围设备上的完全交换,包括删除和提高Cs的行,作为一个主,需要由固件来完成。我怀疑我们需要看到(经常被请求和神秘的)“短而完整的代码示例”,它展示了这个问题。有东西告诉我,其他事情正在发生……RodiMs——查看你所提供的图像,你说ISR会导致7个16位的值被交换,但是(并且我承认在这张图片中不是很清楚),看起来这7个交换前面有一个短的E。Xchange或垃圾或某种类型的,这可能是重要的吗?也许我们需要看看你调用的函数是什么?苏珊

以上来自于百度翻译


      以下为原文

    Not convinced by Michael's argument. I agree that the CPU has priority over the DMA for accessing memory but only when they both try to access the same SFR (in this case) at the same time!
The actual CPU read of the SPI read buffer would take one instruction cycle but the DMA seems to be held up for a lot longer than that - in fact the complete exchange on the other SPI peripheral including the dropping and raising of the CS line which, as a master, needs to be done by the firmware.
I suspect that we need to see the (oft requested and mythical) "short but complete code example" that exhibits the problem. Something tells me that something else is happening to cause this....
Rodims - looking at the image you have provided, you said that the ISR causes 7 16-bit values to be exchanged, but (and I admit it is not very clear in that picture) it would appear that the 7 exchanges are preceded by a shorter exchange/garbage or some sort - could that be significant? Perhaps we need to see what is in the function you call?
Susan
举报

陈晨

2018-11-22 16:11:06
你有PIC港口双端口RAM,是你的缓冲区吗?

以上来自于百度翻译


      以下为原文

    Does you pic haven dual port ram and is your buffer there?
举报

更多回帖

发帖
×
20
完善资料,
赚取积分