• AVR Freaks

Helpful ReplyPIC32 DMA SPI QUESTION

Author
vport
New Member
  • Total Posts : 17
  • Reward points : 0
  • Joined: 2010/12/09 22:33:11
  • Location: 0
  • Status: offline
2012/06/04 22:12:01 (permalink)
0

PIC32 DMA SPI QUESTION

I am communicating two PIC32 with SPI CHANNEL some time SPI data is available at slave microcontroler some time no data available i am using spi_txfer.c example from PLIB library dma i am continuously sending data from master pic32 to slave pic32 should i call DmaChnStartTxfer(dmaTxChn, DMA_WAIT_NOT, 0); in while loop gain after DmaTxIntFlag reset by DMA  interrupt controller code for master and slave pic32 is given below 
 
 
Master side code
#include <stdlib.h>
#include <plib.h>
// prototypes

// some local data
volatile int        DmaTxIntFlag;            // flag used in interrupts, signal that DMA transfer ended
volatile int        DmaRxIntFlag;            // flag used in interrupts, signal that DMA transfer ended
unsigned char     txferTxBuff[256];    // the buffer containing the data to be transmitted
// Configuration Bit settings
// SYSCLK = 80 MHz (8MHz Crystal/ FPLLIDIV * FPLLMUL / FPLLODIV)
// PBCLK = 40 MHz
// Primary Osc w/PLL (XT+,HS+,EC+PLL)
// WDT OFF
// Other options are don't care
//
#pragma config FPLLMUL = MUL_20, FPLLIDIV = DIV_2, FPLLODIV = DIV_1, FWDTEN = OFF
#pragma config POSCMOD = HS, FNOSC = PRIPLL, FPBDIV = DIV_2

#define SYS_FREQ (80000000L)


/*********************************************************************
 * Function:        int main(void)
 *
 * PreCondition:    None
 *
 * Input:            None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Examples for the usage of the DMA Peripheral Lib
 *
 * Note:            None.
 ********************************************************************/
int    main(void)
{
    int    res;
TRISD = 0;
    PORTD = 0 ;
    // Configure the device for maximum performance but do not change the PBDIV
    // Given the options, this function will change the flash wait states, RAM
    // wait state and enable prefetch cache but will not change the PBDIV.
    // The PBDIV value is already set via the pragma FPBDIV option above..
    SYSTEMConfig(SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);

    srand(ReadCoreTimer());        // init the random generator

    // send some data from memory to an SPI channel
unsigned char int            ix;

//    static

                            // less than DmaGetMaxTxferSize() bytes per transfer
    DmaChannel        dmaTxChn=DMA_CHANNEL1;    // DMA channel to use for our example
                            // NOTE: the DMA ISR setting has to match the channel number
    SpiChannel        spiTxChn= SPI_CHANNEL2;    // the transmitting SPI channel to use in our example
 
   

    // fill the transmit buffer with some random data
    for(ix=0; ix<256  ; ix++)
    {
        txferTxBuff[ix]= ix;
    }


    // open and configure the SPI channel to use: master, no frame mode, 8 bit mode.
    // won't use SS for communicating with the slave
    // we'll be using 40MHz/4=10MHz SPI clock
    SpiChnOpen(SPI_CHANNEL2, SPI_OPEN_MSTEN|SPI_OPEN_SMP_END|SPI_OPEN_MODE8, 4);
    mPORTGClearBits(BIT_9 );

    // open and configure the DMA channel.
//    DmaChnOpen(SPI1_TX_DMA_CHN, DMA_CHN_PRI3, DMA_OPEN_DEFAULT);

    DmaChnOpen(dmaTxChn, DMA_CHN_PRI2, DMA_OPEN_DEFAULT);

    // set the events: we want the SPI transmit buffer empty interrupt to start our transfer
    DmaChnSetEventControl(dmaTxChn, DMA_EV_START_IRQ_EN|DMA_EV_START_IRQ(_SPI2_TX_IRQ));

    // set the transfer:
    // source is our buffer, dest is the SPI transmit buffer
    // source size is the whole buffer, destination size is one byte
    // cell size is one byte: we want one byte to be sent per each SPI TXBE event
    DmaChnSetTxfer(dmaTxChn, txferTxBuff, (void*)&SPI2BUF, sizeof(txferTxBuff), 1, 1);

    DmaChnSetEvEnableFlags(dmaTxChn, DMA_EV_BLOCK_DONE);    // enable the transfer done interrupt, when all buffer transferred

    INTConfigureSystem(INT_SYSTEM_CONFIG_MULT_VECTOR);
    INTEnableInterrupts();

    INTSetVectorPriority(INT_VECTOR_DMA(dmaTxChn), INT_PRIORITY_LEVEL_5);        // set INT controller priority
    INTSetVectorSubPriority(INT_VECTOR_DMA(dmaTxChn), INT_SUB_PRIORITY_LEVEL_3);        // set INT controller sub-priority

    INTEnable(INT_SOURCE_DMA(dmaTxChn), INT_ENABLED);        // enable the chn interrupt in the INT controller
   
    DmaTxIntFlag=0;            // clear the interrupt flag we're  waiting on

        // force the DMA transfer: the SPI TBE flag it's already been active
DmaChnStartTxfer(dmaTxChn, DMA_WAIT_NOT, 0);
    // wait for the transfer to complete
    // In a real application you can do some other stuff while the DMA transfer is taking place

do
{
   


while(!DmaTxIntFlag);
//DmaChnStartTxfer(dmaTxChn, DMA_WAIT_NOT, 0); //is to call this function here or outside loop only onece
//mPORTGSetBits(BIT_9 );
//PORTD = 0xFF;
DmaTxIntFlag=0;   
}while(1);
PORTD = 0XFF;


    return res;
}




/*********************************************************************
 * Function:        void DmaDoM2Spi(void)
 *
 * PreCondition:    None
 *
 * Input:            None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Example for sending a memory buffer to an SPI channel using the DMA and SPI Peripheral Lib.
 *                     The DMA interrupts will  be enabled and will signal when the transfer is completed.
 *
 * Note:            None.
 ********************************************************************/

// handler for the DMA channel 1 interrupt
void __ISR(_DMA1_VECTOR, IPL5SOFT) DmaHandler1(void)
{
    int    evFlags;                // event flags when getting the interrupt

    INTClearFlag(INT_SOURCE_DMA(DMA_CHANNEL1));    // acknowledge the INT controller, we're servicing int

    evFlags=DmaChnGetEvFlags(DMA_CHANNEL1);    // get the event flags

    if(evFlags&DMA_EV_BLOCK_DONE)
    { // just a sanity check. we enabled just the DMA_EV_BLOCK_DONE transfer done interrupt
        DmaTxIntFlag=1;
        DmaChnClrEvFlags(DMA_CHANNEL1, DMA_EV_BLOCK_DONE);
    }
}
SLAVE CODE

 * FileName:        spi_txfer.c
 * Dependencies:    plib.h
 *
 * Processor:       PIC32MX
 *
 * Complier:        MPLAB C32 v1or higher
 *                  MPLAB IDE v8 or higher
 

unsigned short int    txferRxBuff[256];
#include <stdlib.h>

#include <plib.h>



// prototypes


// some local data
 
volatile int        DmaRxIntFlag;            // flag used in interrupts, signal that DMA transfer ended

// Configuration Bit settings
// SYSCLK = 80 MHz (8MHz Crystal/ FPLLIDIV * FPLLMUL / FPLLODIV)
// PBCLK = 40 MHz
// Primary Osc w/PLL (XT+,HS+,EC+PLL)
// WDT OFF
// Other options are don't care
//
#pragma config FPLLMUL = MUL_20, FPLLIDIV = DIV_2, FPLLODIV = DIV_1, FWDTEN = OFF
#pragma config POSCMOD = HS, FNOSC = PRIPLL, FPBDIV = DIV_2

#define SYS_FREQ (80000000L)


/*********************************************************************
 * Function:        int main(void)
 *
 * PreCondition:    None
 *
 * Input:            None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Examples for the usage of the DMA Peripheral Lib
 *
 * Note:            None.
 ********************************************************************/
int    main(void)
{
    int    res;
    unsigned short int  i =0;
    TRISD = 0x00;
    PORTD = 00;
// Configure the device for maximum performance but do not change the PBDIV
    // Given the options, this function will change the flash wait states, RAM
    // wait state and enable prefetch cache but will not change the PBDIV.
    // The PBDIV value is already set via the pragma FPBDIV option above..
   
   
SYSTEMConfig(SYS_FREQ, SYS_CFG_WAIT_STATES | SYS_CFG_PCACHE);


DmaChannel        dmaRxChn=DMA_CHANNEL2;    // DMA channel to use for our example
                            // NOTE: the DMA ISR setting has to match the channel number
SpiChannel        spiRxChn=SPI_CHANNEL2;    // the receiving SPI channel to use in our example
 


    // open and configure the SPI channel to use: slave, no frame mode, 8 bit mode.
    // won't use SS for communicating with the master
    // SPI clock is irrelevant in slve mode
    SpiChnOpen(SPI_CHANNEL2, SPI_OPEN_SLVEN|SPI_OPEN_MODE16, 4);

    // open and configure the DMA channel.
    DmaChnOpen(dmaRxChn, DMA_CHN_PRI3, DMA_OPEN_DEFAULT);

    // set the events: we want the SPI receive buffer full interrupt to start our transfer
    DmaChnSetEventControl(dmaRxChn, DMA_EV_START_IRQ_EN|DMA_EV_START_IRQ(_SPI2_RX_IRQ));

    // set the transfer:
    // source is the SPI buffer, dest is our memory buffer
    // source size is one byte, destination size is the whole buffer
    // cell size is one byte: we want one byte to be sent per each SPI RXBF event
    DmaChnSetTxfer(dmaRxChn, (void*)&SPI2BUF, txferRxBuff, 1, sizeof(txferRxBuff), 1);

    DmaChnSetEvEnableFlags(dmaRxChn, DMA_EV_BLOCK_DONE);    // enable the transfer done interrupt, when all buffer transferred

    INTEnableSystemMultiVectoredInt();            // enable system wide multi vectored interrupts

    INTSetPriorityAndSubPriority(INT_SOURCE_DMA(dmaRxChn), INT_PRIORITY_LEVEL_5, INT_SUB_PRIORITY_LEVEL_3);        // set INT controller priorities

    INTEnable(INT_SOURCE_DMA(dmaRxChn), INT_ENABLED);        // enable the chn interrupt in the INT controller

    DmaRxIntFlag=0;            // clear the interrupt flag we're  waiting on
 for(i =0; i<255;i++)
txferRxBuff = 0;
 
    DmaChnEnable(dmaRxChn);    // enable the DMA channel
   

    // receive some data from an SPI channel and store it in the memory
 do
{
    while(!DmaRxIntFlag);
    DmaRxIntFlag = 0;

  

   
}while(1);
    return int;
}
// handler for the DMA channel 2 interrupt
void __ISR(_DMA2_VECTOR, IPL5SOFT) DmaHandler2(void)
{
    int    evFlags;                // event flags when getting the interrupt

    INTClearFlag(INT_SOURCE_DMA(DMA_CHANNEL2));    // acknowledge the INT controller, we're servicing int

    evFlags=DmaChnGetEvFlags(DMA_CHANNEL2);    // get the event flags

    if(evFlags&DMA_EV_BLOCK_DONE)
    { // just a sanity check. we enabled just the DMA_EV_BLOCK_DONE transfer done interrupt
        DmaRxIntFlag=1;
        DmaChnClrEvFlags(DMA_CHANNEL2, DMA_EV_BLOCK_DONE);
    }
}





#1
threedog
Super Member
  • Total Posts : 998
  • Reward points : 0
  • Joined: 2009/12/04 12:28:11
  • Location: Boise
  • Status: offline
Re:PIC32 DMA SPI QUESTION 2012/06/05 07:40:37 (permalink)
0
SPI is a bidirectional transfer bus.  When the master sends out a bit, the slave MUST return a bit.
The slave can return dummy data if no real data is available, but it must send something.
If the master wants 255 bytes of data it must send 256 bytes to receive 255 bytes.
If it does not receive 255 bytes, then the DMA will stall until it does receive 255 bytes.
if slave wants to send data, well, it cannot.  The master must send dummy data to receive data
 
 
you may want a two buffers in both master and slave.  One is the send buffer, the other the receive buffer.
if no data is available on slave, then the slave may have to send dummy data, or nothing.
if nothing, then the master should use a timer to provide a timeout on SPI DMA transfer or just wait forever.
you want to start the DMA outside of the loop.
 
there are Microchip example programs, start with those.
 
 
#2
vport
New Member
  • Total Posts : 17
  • Reward points : 0
  • Joined: 2010/12/09 22:33:11
  • Location: 0
  • Status: offline
Re:PIC32 DMA SPI QUESTION 2012/06/08 00:57:21 (permalink)
0
Thanks for reply overall i want to modify spi_txfer.c for SPI communication between two PIC32 Microcontrollers  My goal is to send continuously data from master pic32 to slave PIC32 i have modified spi_txfer.c by changing DMA opened in auto mode and
DmaChnStartTxfer, DmaChnEnable functions from outside the main while loop in both slave and master DMA files and make both buffers as global variable in both files . i am using SPI channel no 2 so i modified files Chanel 2 instead of 1 . but notting happens.
#3
cgiordan
Super Member
  • Total Posts : 1364
  • Reward points : 0
  • Status: offline
Re:PIC32 DMA SPI QUESTION 2012/06/08 18:39:04 (permalink)
0
Curious if you know that you need to have 2 DMA channels in each micro-controller active?  You need a channel linked to receiving and another linked to transmitting.  This is true in the master device.  I have never set up a slave device with DMA, but I have done it with a master device successfully.  I would imagine that 2 channels are needed for the slave device as well.  Anyhow, look at the DMA example for the PIC32 that is in the C32 installation directory.  Notice that there are 2 DMA channels being used in the example application. 
 
The reason for this is that once you transmit a byte (from the master device) let's say out the SPI SDO pin, there is data coming in "nearly" simultaneously on the SDI pin, and this will fill a SPIRXBUF.  You need to read this data or the SPI module will not transmit any more data via DMA.  So, when you have DMA linked to transmitting a byte "automatically" and receiving "automatically" in this setup, you need not worry about the receiver stopping the whole transmission process - to avoid an overflow condition - because it is being handled to "ship" the received bytes in the SPIRXBUF into some RAM location you designate - like a buffering array of some sort. 
#4
vport
New Member
  • Total Posts : 17
  • Reward points : 0
  • Joined: 2010/12/09 22:33:11
  • Location: 0
  • Status: offline
Re:PIC32 DMA SPI QUESTION 2012/06/09 00:38:44 (permalink)
0
Again lot of thanks i would not make two dma channels enable in each device at time but only master channel sending SPI through DMA  data to slave pic32 which receives data via SPI SLAVE DMA . I will try example u have mentioned . Is it possible to setup one Master PIC32 with DMA transmit and slave PIC32 with RECEIVE data through DMA .
#5
vl
Super Member
  • Total Posts : 222
  • Reward points : 0
  • Joined: 2012/05/15 22:29:27
  • Location: 0
  • Status: offline
Re:PIC32 DMA SPI QUESTION 2012/06/09 03:05:37 (permalink)
0
vport but only master channel sending SPI through DMA  data to slave pic32 which receives data via SPI SLAVE DMA

There's no such thing for the master as "send only". The master HAS TO receive as well. Even if there's no useful data coming in, the master will put the input into the register, and you HAVE TO read those bytes, otherwise sending stops. Therefore if you want to send from the master using DMA, you HAVE TO read the bytes received by the master using DMA. So you need 2 DMA channels in your master for this, one for sending your useful data, and one to drop the useless incoming data bytes.
#6
cgiordan
Super Member
  • Total Posts : 1364
  • Reward points : 0
  • Status: offline
Re:PIC32 DMA SPI QUESTION 2012/06/09 04:16:19 (permalink)
0
Exactly as 'vl' said, it is imperative that you have 2 DMA channels when doing DMA based SPI - one for TX, one for RX.  I imagine that the slave device would need the same type of DMA configuration, because it is transmitting while receiving like the master.  The slave can only NOT initiate transfers.  Also as 'vl' said is that whenever you transmit a byte from the master device, you will receive a byte.  Think of it as byte out-byte in.  There's no avoiding this.  SPI is a synchronous, full-duplexed method of communication, hence the SPI acronym.  Overall, 'vl' is 100% correct. 
#7
vport
New Member
  • Total Posts : 17
  • Reward points : 0
  • Joined: 2010/12/09 22:33:11
  • Location: 0
  • Status: offline
Re:PIC32 DMA SPI QUESTION 2012/06/09 05:05:23 (permalink)
0
Yes of course i setup SPI2 on PIC32 with two buffers one for transmit and one for receive as with DMA transfer i have no control over send individual byte as DMA do it it self .also i put both DMA in auto mode as i want to transmit data continuously at slave i have also two buffers one of transmit and one for receive I am only using one DMA channel to receive But how to send DATA to Master PIC32 while Receiving through DMA regards  
#8
Vasus
New Member
  • Total Posts : 20
  • Reward points : 0
  • Joined: 2011/05/22 21:59:17
  • Location: Russia
  • Status: offline
Re:PIC32 DMA SPI QUESTION 2013/03/12 01:08:27 (permalink)
0
Hi. I have the similar problem as topicstarter. And I need some help.
PIC32 have to receive small data packet through SPI. SPI slave 16bit mode. There are little time beetween SPI words in packet.  In this little time PIC32 have to get into the interrupt routine, to take data from SPIBUF, to put new data to SPIBUF, then exit from interrupt routine. I tried to use SPI interrupt with shadow registers, 80 MHz clk. But PIC32 uses about 600ns for changing data in SPIBUF. It is very low for me so i try to use 2 DMA channels to change data in SPIBUF.
I think, it should work like this:
1. SPI module receive first word and set SPIRXIF
2. DMA CH0 detect  SPIRXIF and start cell transmission (2byte) from SPIBUF   to  rxBuf[0] in RAM
3. Transmission finished DMA CH0 set DMA0IF
4.  DMA CH1 detect  DMA0IF and start cell transmission (2byte) from txBuf[0] in RAM to SPIBUF
5-... Receiving and transmission next data word of data packet.
I think it should work faster then SPI interrupt routine. But I can't to setup DMA channels correct.
 
I tried to setup without SPI. Just sending data between 3 memory buffers. External interrupt starts the chain.  After 10 events DMA should destroy data in rxBuf[]. And in spibuf[] we want to see last word from txBuf[].
 
IEC1CLR = 0x00010000;   // disable DMA channel 0 interrupts
IFS1CLR = 0x00010000;  // clear existing DMA channel 0 interrupt flag
DMACONSET = 0x00008000; // enable the DMA controller
DCH0CON = 0x3; // channel off, pri 3, no chaining
DCH0ECON = ( 32 << 8 ) | 0x10; //  start irq CN, no pattern match
DCH0SSA = &spiBuf[0]; // transfer source physical address
DCH0DSA = &rxBuf[0];  // transfer destination physical address
DCH0SSIZ = 2;      // source size 2 bytes
DCH0DSIZ = 20;   // destination size 20 bytes
DCH0CSIZ = 2;     // 2 bytes transferred per event
DCH0INTCLR = 0x00ff00ff;    // clear existing events, disable all interrupts
DCH0INTSET = 0x000C0000; // cell and block complete interrupts
DCH0CONSET = 0x90; // turn channel on, auto enable

DCH1CON = 0xF2;  
DCH1ECON = ( 48 << 8 ) | 0x10; // start irq DMA0, no pattern match
DCH1SSA = &txBuf[0];   // transfer source physical address
DCH1DSA = &spiBuf[0]; // transfer destination physical address
DCH1SSIZ = 20; // source size 200 bytes
DCH1DSIZ = 2; // destination size 200 bytes
DCH1CSIZ = 2; // 200 bytes transferred per event
DCH1INTCLR = 0x00ff00ff; // clear existing events, disable all interrupts
DCH1INTSET = 0x000C0000; // cell and block complete interrupts

 
It doesn't work. Maybe DMA module can't do what I want? I think source size, cell size and destination size incorrect. But i don't understand how to configure them. Help me please.
post edited by Vasus - 2013/03/12 01:13:03
#9
cgiordan
Super Member
  • Total Posts : 1364
  • Reward points : 0
  • Status: offline
Re:PIC32 DMA SPI QUESTION 2013/03/12 06:39:30 (permalink)
0
For SPI RX DMA Channel, your source is the SPIxBUF, your destination is your RAM variable (rxBuf[]).  Your source IRQ is SPI RX ISR.  When the SPI RX ISR flag is set, the DMA channel initiates transferring a cell which comprises a block eventually after the channel does this enough.  
For SPI TX DMA Channel, your source is your RAM variable (txBuf[]), and your destination is SPIxBUF.  Your source IRQ is SPI TX ISR.  
This should get some things straightened out.  Let's see how everything goes after these changes...

MCU's: PIC32MX795F512L, PIC24HJ64GP502
IDE: MPLAB X IDE v.1.70
Compilers: C32 v.2.02, C30 v.3.31
#10
Vasus
New Member
  • Total Posts : 20
  • Reward points : 0
  • Joined: 2011/05/22 21:59:17
  • Location: Russia
  • Status: offline
Re:PIC32 DMA SPI QUESTION 2013/03/14 00:42:57 (permalink) ☄ Helpfulby ok_vicinius 2014/04/11 07:17:59
5 (1)
I think SPIxRXIF and SPIxTXIF will come at the same time. So SPIxRXIF is source IRQ for both DMA channels. But DMA channel 0 has higher priority than channel 1. When SPIRX interrupt flag appears both DMA channels ready to transfer first cell. DMA0 transfer cell, then DMA1 transfer cell.
Now everything works. And DMA works faster then SPI interrupt routine. I put my code here. Maybe it will be useful for somebody.
 

IEC1CLR = 0x00010000; // disable DMA channel 0 interrupts
IFS1CLR = 0x00010000; // clear existing DMA channel 0 interrupt flag
DMACONSET = 0x00008000; // enable the DMA controller

DCH0CON = 0x03; // channel off, pri 3, no chaining
DCH0ECON = ( 39 << 8 ) | 0x10; // 39 - spi start irq, no pattern match
// program the transfer
DCH0SSA = 0x1F805A20; // &SPIBUF transfer source physical address
DCH0DSA = 0x00007F80; //&rxbuf[0]; // transfer destination physical address
DCH0SSIZ = 2; // source size 2 bytes
DCH0DSIZ = 20; // destination size 20 bytes
DCH0CSIZ = 2; // 2 bytes transferred per event
DCH0INTCLR = 0x00ff00ff; // clear existing events, disable all interrupts

DCH1CON = 0x02; // channel off, pri 2, no chaining
DCH1ECON = ( 39 << 8 ) | 0x10; // spi start irq, no pattern match
// program the transfer
DCH1SSA = 0x00007FA8; //&txbuf[0]; // transfer source physical address
DCH1DSA = 0x1F805A20; //&SPIBUF; // transfer destination physical address
DCH1SSIZ = 20; // source size 20 bytes
DCH1DSIZ = 2; // destination size 2 bytes
DCH1CSIZ = 2; // 2 bytes transferred per event
DCH1INTCLR = 0x00ff00ff; // clear existing events, disable all interrupts
DCH1INTSET = 0x00080000; // clear existing events, disable all interrupts

 
I enable DMA channels when my application ready to receive data packet. DMA receive/transmit  packets and set interrupt flag. When DMA transfered all cells it disable itself. But if incoming packet small (for example just 5 words), timeout appears in my application and I must abort current block transfer and reconfigure DMA channels for transmitting next packet. I CAN NOT ABORT BLOK TRANSMITTING.
I tried to abort transferring by interrupt, by setting CABORT flag, by clearing CHEN flag. But nothing can abort block transferring. Only disabling DMA module (DMACON.ON) or clearing irq sourse (DMAxECON = 0) can abort block transfer. I think it is incorrect.
 
#11
Jump to:
© 2019 APG vNext Commercial Version 4.5