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.

Configuration of SPI and DMA modules for byte by byte transfer

Other Parts Discussed in Thread: HALCOGEN

Hi,

I have a TMS570LS20216 USB stick and running the SPI slave demo code (example_mibspi_halcogen_ccs_c_project.zip) obtained from the TMS570 wiki (http://processors.wiki.ti.com/index.php/Category:TMS570) in the 'Training & Videos' section.  This zip contains an SPI slave project that I am running with no changes to the code.

I am modifying the example so as to receive data (more than 1 kB) streamed in from the master.  My intention is to DMA this incoming data straight into RAM on a byte by byte basis.  Currently my SPI is configured to use multi buffers, but my intention is not to use multi-buffers but DMA each byte from the SPIBUF as it arrives.  This is becasue the messages form the master can be of variable byte length.

I change to single buffer using:

SPIRegAdd->MSPIENA = 0;  // MSPIENA - Multi-buffer mode dis-able

but am at a loss as how to configure the SPI's DMA interrupt to signal the DMA module to DMA the SPIBUF to RAM.

I am also unsure how to configure the incrementing of the DMA destination pointer.  At present I have:

        SPICtrlPktData.Frame_Cnt = 1;                // FRM_CNT 1 Frame transfer
        SPICtrlPktData.Elmnt_Cnt = NUMB_ELEMENTS;    // ELM_CNT 10 Element per Frame
           
        SPICtrlPktData.Read_Size  = ACCESS_8_BIT;   // RD_SIZE Size of source element
        SPICtrlPktData.Write_Size = ACCESS_8_BIT;   // WR_SIZE Size of destination element
   
        SPICtrlPktData.Trans_Type = FRAME_TRANSFER;  // TRANSFER_TYPE
   
        SPICtrlPktData.Addr_Mode_Read  = ADDR_INC1;  // RD_ADDR_MODE Auto increment src (read) address by size of read element
        SPICtrlPktData.Addr_Mode_Write = ADDR_INC1;  // WR_ADDR_MODE Auto increment dest (write) address by size of write element
   
        SPICtrlPktData.Autoinit = AUTOINIT_OFF;      // AUTOINIT Disable
       
        SPICtrlPktData.channel_Ctrl = 0x00;          // channel Ctrl info by default NO CHanning is Enabled.
       
        SPICtrlPktData.Elmnt_Dst_Offst = 0;          // EIDXD (for Index mode) 
        SPICtrlPktData.Elmnt_Src_Offst = 0;          // EIDXS (for Index mode)
        SPICtrlPktData.Frame_Dst_Offst = 0;          // FIDXD (for Index mode)
        SPICtrlPktData.Frame_Src_Offst = 0;          // FIDXS (for Index mode)

Although I believe I have the length correct (8 bits) I am uncertain with respect to the element, frame and block sizes.  I am intending to use 1 element per frame and 1 frame per block.  Is this along the correct lines?

Can you clarify the above points for me please?

Steve

 

 

 

  • Steve,

     

    If I understand you want to read all incoming data from your MIBSPI (used as regular SPI) to a buffer in memory.
    The MIBSPI will have to generate a DMA request on every incoming data.
    This DMA Request will trigger the DMA module to start a FRAME transfer.

    In your code example, the number of element per frame is set to 10. That means, the DMA will receive 10 Elements from the source before doing the transfer to RAM.
    This is valid. An alternate option is to set the number of element per frame to 1. In this case, for every incoming data, the DMA will transfer this data to RAM.

    Concerning the addressing mode for READ an WRITE, I think you have a problem.

    The options are:

    Constant: source and/or destination address do not change. In your case you want that for the READ. You want the DMA to read over and over the SPIBUF register.
    Post Incremented: source and/or destination are post-incremented by the element size. This is what you want for the destination side. The DMA will copy to your destination buffer one 8 bits data at the time. (you have defined READ and WRITE size as 8bits.
    Indexed: Source and/or destination are post-incremented as defined in the element index offset register.

    SPICtrlPktData.Addr_Mode_Read  = CONSTANT;  // RD_ADDR_MODE CONSTANT src (read)

    NOTE:
    The DMA can do data packing. This is a great option to minimize DMA access.
    Using your example, the DMA will always read from the SPI element of 8bits.
    You can define the DESTINATION element size as 32bits. In this configuration, the DMA will read 4 8bits element from your SPIBUF before doing 1 32 bits write to the destination.

    Please let me know if this is helpful.

    You can also send me your project with your modification and I can have a look.

     

    Regards,

     

    Jean-Marc

  • Jean Marc,

    thank you for you reply and sorry for my delay in replying.

    I will give your suggestions a try and get back to you shortly.

    Thanks,

    Steve

  • Jean-Marc,

     

    I have made some alterations to my project, but still have some questions.

    I have made the DMA read addressing mode constant and the number of element = 1

    The read/write sizes I have left a 8-bit since I want to read the incoming data on a byte by byte basis.

    I  have used the TI DMA and SPI (which used multi-buffers and transfer groups) examples as the basis of my code and still have references to the transfer groups.

    I am uncertain that I am configuring the SPI/DMA link correctly: see function SPI_DMAenable(...).

    Will the setting of SPEIN to 1 inmmediatley transfer the first byte?  Or do I have to set SPEIN to 1, then the writing of data to SPIDAT1 causes the data to be clocked out?

    Attached is my project:

    To summarise, it is an attempt to send data (byte by byte) out on SPI1 and receive it on SPI1 (LOOPBACK = 1) then DMA it to RAM.

    ***** Edit: Removed zipped project - it has been suspenseeded by the next post *****

    Any help apreciated.

    Steve

  • Jean-Marc,

     

    I now have a project that does byte by byte loopback SPI and DMA:

    Here is the project:

    0118.08 - SPI1_nonmulti_buff_labview slave_DMA.zip

    It still uses transfer group which I would like to avoid but I will deal with this in due course.

    I have the following problems:

    I don't seem to be able to do the DMA on a byte basis i.e. changing ACCESS_32_BIT to ACCESS_8_BIT cause the program to fail.

    You can see that the DMA is reinitialliesed after every send/recevie of data, however when I only do the initialisation once the program fails.

    Steve

  • Hi Steve

    Please Find some Sample code for SPI TX_RX with DMA ( 16 bit data)... and for MIBSPI DMA

    Hope it helps for you.. 3554.SPI_MIBSPI_DMA.zip

    Best Regards
    Prathap

     

     

  • Hi Prathap,

    thanks for the sample code, looks Like it will be helpful to me.

    For the SPI_DMA demo which only has the file SPI61_TXRXDMA.c

    Could you give me the following header files:

    • pcr.h
    • module.h
    • mibspi_p.h
    • system.h
    • device.h
    • dma.h

    Thanks,

    Steve

     

  • Hi Steve,

    FInd the header files in 3583.SPI_DMA_Headerfiles.zip

     

    Best Regards
    Prathap

     

  • Hi Prathap,

    thanks, I will let you know how I get on,

    Steve

     

     

  • Hi Prathap,

    have now got my project to SPI (in loop back mode) and DMA.

    However, the problem still remains in the fact that I cannot get the DMA to increment its destination address:

    I have set:

    DMACtrlPkt.Addr_Mode_Write = ADDR_INC1;

    Which I means each DMA transfer should be in to the next memory location, but every SPI receipt and DMA operation alwasy wrties to the same memory address:

    I am configruing the SPI for DMA with:

    void SPI_enableDMA(spiREGISTER_t* SPIRegAdd, unsigned DMAChannel, int numbElements)
    {
        // The SPI DMA Channel Control Register (DMAxCTRL)
        SPIRegAdd->DMAxCTRL_UN[DMAChannel].BIT_FIELDS.ONESHOT   = 1;                // ONESHOT   - Auto-disable of DMA channel after ICOUNT+1 transfers
        SPIRegAdd->DMAxCTRL_UN[DMAChannel].BIT_FIELDS.RXDMAENAx = 1;                // RXDMAENAx - Receive data DMA channel enable
        SPIRegAdd->DMAxCTRL_UN[DMAChannel].BIT_FIELDS.NOBRKx    = 1;                // NOBRKx    - Non-interleaved DMA block transfer. This bit is available in master mode only
        SPIRegAdd->DMAxCTRL_UN[DMAChannel].BIT_FIELDS.ICOUNTx   = numbElements - 1; // ICOUNTx   - Initial count of DMA transfers
        SPIRegAdd->DMAxCTRL_UN[DMAChannel].BIT_FIELDS.BUFIDx    = numbElements - 1; // BUFIDx    - Buffer utilized for DMA transfer
    }

    and my DMA configuration is as follows:

            CP_config_ST  DMACtrlPkt;  // DMA control packet    
           
            // Configure SPI module for DMA...
            SPI_enableDMA( ptrSPIRegAdd, DMA_CHANNEL_1, NUMB_ELEMENTS );  // Enable the DMA Functionality
                                                                          // for the specified channel
        
            // Populate the DMA control packet...
            DMACtrlPkt.Src_Addr        = (unsigned int)(&(ptrSPIRegAdd->SPIBUF_UN.WORDS) + 16);  // SRCADDR is the address to DMA from
            DMACtrlPkt.Dst_Addr        = (unsigned int)(&dataRx_pre_processed);             // DSTADDR is the address to DMA to
       
            DMACtrlPkt.Frame_Cnt       = 1;                 // FRM_CNT 1 Frame transfer
            DMACtrlPkt.Elmnt_Cnt       = NUMB_ELEMENTS;     // ELM_CNT Element per Frame
               
            DMACtrlPkt.Read_Size       = ACCESS_16_BIT;     // RD_SIZE Size of source element
            DMACtrlPkt.Write_Size      = ACCESS_16_BIT;     // WR_SIZE Size of destination element
       
            DMACtrlPkt.Trans_Type      = FRAME_TRANSFER;    // TRANSFER_TYPE
       
            DMACtrlPkt.Addr_Mode_Read  = ADDR_FIXED;        // RD_ADDR_MODE Constant src (read) address by size of read element
            DMACtrlPkt.Addr_Mode_Write = ADDR_INC1;         // WR_ADDR_MODE Auto increment dest (write) address by size of write element
       
            DMACtrlPkt.Autoinit        = AUTOINIT_OFF;      // AUTOINIT Disable
           
            DMACtrlPkt.channel_Ctrl    = 0x00;              // channel Ctrl info by default NO CHanning is Enabled.
           
            DMACtrlPkt.Elmnt_Dst_Offst = 0;                 // EIDXD (for Index mode)  SKCN - was 0
            DMACtrlPkt.Elmnt_Src_Offst = 0;                 // EIDXS (for Index mode)
            DMACtrlPkt.Frame_Dst_Offst = 0;                 // FIDXD (for Index mode)
            DMACtrlPkt.Frame_Src_Offst = 0;                 // FIDXS (for Index mode)
       
            DMACtrlPkt.Port_Assign     = PORTB;             // PORT = PORTB for channel 0 (only 1 port on TMS570LS series)
       
            // Initialise/configure DMA Module... (TRM 15.6.2 Configure DMA Module p734)
            DMA_Reset();                                          // 1. Reset DMA
            DMA_CPClear();                                        // 2. Clear DMA control packet RAM
            DMA_Enable();                                         // 3. Enable DMA
            DMA_SetCtrlPacket( DMA_CHANNEL_1, &DMACtrlPkt );      // 4. Set up DMA control packet
            DMA_REQConfig( DMA_CHANNEL_1, MIBSPI1_TG0_DMA_REQ );  // 5. Assign the selected DMA channel
                                                                  //    for to the SPI DMA request line
            DMA_SetChEnable( DMA_CHANNEL_1, DMA_HW );             // 6. Enable the hardware DMA request
                                                                  //    for the channel

    I am writing data to the SPIDAT1 and checking the SPI and DMA operation with:

     

     

        while( 1 )  // Forever...
        {       
            // User must wait for TXFULL to reset or TXINT before writting next data to SPIDAT1 register.
            // Wait while the Tx data is moved from the TXBUF to the TX shift register
            while( ptrSPIRegAdd->SPIBUF_UN.BIT_FIELDS.TXFULL == 1 ){};   // Wait if TXBUF is full...

            // TXBUF is empty; so load the Tx data in to the SPI
            ptrSPIRegAdd->SPIDAT1_UN.BIT_FIELDS.TXDATA = data;

            // Wait for DMA transfer to happen...
            while( !DMA_IsDMATransferComplete( DMA_CHANNEL_1 ) ){};
       
            // Get the data and reset...
            DMA_GetData( (unsigned short *)dataRx_pre_processed, dataRx_post_processed, NUMB_ELEMENTS );

            // SPIBUF is full; so copy the Rx data out of the SPI
            dataRx_pre_processed[0] = ptrSPIRegAdd->SPIBUF_UN.BIT_FIELDS.RXDATA;  // This seems to be neede to reset the SPI
           
            // Note:  Reading the data from RXDATA sets the RXEMPTY flag and clears RXINTFLG

            // At this point data in dataRx_post_processed should be all the data sent.
                      
            data++;
        }

     

     

    Any ideas why the DMA Current Destination Address Register (CDADDR) address is not incrementing?

    Thanks,

    Steve

     

     

  • Steve

    Looks like you are using DMA in MIBSPI mode... not compatibility mode.

    Few things I want you to be clear if you are using Module in SPI mode( Compatibility Mode) with DMA

    1) Do not touch any register after offset 0x64(INTVECT1). ( Except if you are using I/O-Loopback Test Control Register (IOLPBKTSTCR) [offset = 134h])
    2) DMAReq bit in SPIINT0 register has to be enabled to use TX and RX DMA request. DMAREQ bit is common for TX and RX DMA req,
    3) To use only for RX DMA,
                    a) Ignore TX req,
                    b) Do not configure any DMA channel for TX req,
                    c) User software should fill the SPIDAT1 register after waiting for TXINTFLG in status register.
                    d) For receiving say 10 - 8-Bit data configure DMA control packets as below 
                                 1) Source = SPIBUF ( Byte address - offset 0x43, for 16 bit data - offset 0x42) 
                                 2) Destination = RAM address 
                                 3) Frame count = 10
                                 4) Element count = 1
                                 5) Trans type  = Frame Transfer
                                 6) Read = Addr Fixed
                                 7) Write = Inc1
                                 8) Read Size = 8 bit 
                                 9) Write Size = 8 bit 
                    e) For every 8 bit data received one Frame( 1 frame = 1 element as per our setting) will be received, So after 10 data received you will get Frame transfer complete Interrupt if enabled. It is good not to enable RX int in SPI, instead using DMA transfer complete interrupt.

    -----------------------------------------------------------------------------------------------------------------------------------------------

    Only for using MIBSPI with DMA, register DMA channel Control register has to be used ( DMAxCTRL - offset 0xD8 to 0x114 in Mibspi)..

    Best Regards
    Prathap

     

  • Normal 0 false false false EN-GB X-NONE X-NONE MicrosoftInternetExplorer4

    Hi Haixiao,

    thanks for your reply.

    I have made the changes as per your suggestion and also set up an interrupt to signal block transfer complete and it is now running as I had planned.

    That is, SPI received data DMA'd to RAM each time new SPI data is received and the BTC flag getting raised after x number of DMAs to RAM.

    As you suspected my error was in the configuration of the element, frame and block sizes.

    Thank you very much for your assistance with this problem, consider it the matter now closed.

    Regards,

    Steve