CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

DMA Driver for PIC18F26J11 and others

 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
Eduardo__



Joined: 23 Nov 2011
Posts: 197
Location: Brazil

View user's profile Send private message

DMA Driver for PIC18F26J11 and others
PostPosted: Thu Feb 02, 2012 7:51 am     Reply with quote

I made a driver. I would like to share it with you.

I´ve not tested with interrupts yet.
I´ve not tested with full_duplex yet.


Code:
/*
                          PIC18F24J11 DMA driver

Arquivo     :  spi_dma_PIC18F24J11.c
Programador :  Eduardo Guilherme Brandt                   
Criação     :  31/01/2012 ( V 1.0 )
Contato     :  eduardogbrandt@yahoo.com.br

Histórico   :  Versão 1.0 - 31/01/2012
Last change :  01/02/2012
                             

               
Functions   :
      // DEFINED MACROS
      //
      DMA_TXADDR(x)                 //This address is byte reversed. **Use this macro for load data.
      DMA_RXADDR(x)                 //This address is byte reversed **Use this macro for load data.
      DMA_DMABC(x)                  //This address is byte reversed **Use this macro for load data.
      //
      //
      DMA_config(x);                //DMA Control 1 config options
      DMA_int_config(x);            //configs DMACON2 register, that contains control bits for controlling interrupt generation and inter-byte delay behavior.
      //     
      DMA_start();                  //Start DMA transfer
      DMA_is_busy();                //DMA is working
      DMA_ended();                  //DMA transfer ended
      DMA_SET_FULL_DUPLEX();        //SPI DMA operates in Full-Duplex mode, data is simultaneously transmitted and received
      DMA_SET_TX();                 //DMA operates in Half-Duplex mode, data is transmitted only
      DMA_SET_RX();                 //DMA operates in Half-Duplex mode, data is transmitted only
      DMA_write(DataSize, buffer);  //Simple DMA Write and start transfer
      DMA_read(DataSize, buffer);   //Simple DMA Read and start transfer
      //
      // DEFINED FUNCTIONS
      //
      void DMA_default_config();                            //Sets default DMA configuration
      void DMA_write_block(int16 DataSize, int8 *buffer);   //Write RAM data block using DMA
      void DMA_read_block(int16 DataSize, int8 *buffer);    //Read RAM data block using DMA
      void DMA_spi_isr(void);                               //DMA interrupt service control(If DMA_use_ints is defined)


Further information:
   Ps:   It can be used for SPI or Ram memory copy(see uC datasheet)

   #define DMA_use_ints //DMA interrupt service control enabled.
                        //Use and change the function "void DMA_spi_isr(void);" as needed

   Registers-set:       
         see spi_dma.h header file
         ps: For more details, see uC datasheet.

   Config example: See in DMA_default_config() function

   #define DMA_FULL_DUPLEX_MODE     //Set to Full duplex mode



Eduardo Guilherme Brandt  31/01/2012   */
//////////////////////////////////////////////////////////////////////////////////////////
//
//
#ifdef DMA_use_ints     
   #warning spi DMA is using interrupt! (INT_SSP2)
#endif
//
//
//
#include <spi_dma_PIC18F24J11.h>      //HEADER FILE
//
//
//
/*****************************************************************************************
******************************************************************************************
******************************************************************************************
*****************************************************************************************/
/*****************************************************************************************
******************************************************************************************
******************************************************************************************
*****************************************************************************************/
/*****************************************************************************************
******************************************************************************************
* DEFINED MACROS
* ex:   
*     DMA_start();   //Start DMA transfer
*/
#define DMA_TXADDR(x)   DMA_TXADDRL=make8((int16)x,0); DMA_TXADDRH=make8((int16)x,1)   //This address is byte reversed. **Use this macro for load data.
#define DMA_RXADDR(x)   DMA_RXADDRL=make8((int16)x,0); DMA_RXADDRH=make8((int16)x,1)   //This address is byte reversed **Use this macro for load data.
#define DMA_DMABC(x)    DMA_DMABCL=make8((int16)x,0); DMA_DMABCH=make8((int16)x,1)     //This address is byte reversed **Use this macro for load data.
//
#define  DMA_config(x)        DMA_DMACON1 = x      //DMA Control 1 config options
#define  DMA_int_config(x)    DMA_DMACON2 = x      //configs DMACON2 register, that contains control bits for controlling interrupt generation and inter-byte delay behavior.

#define  DMA_start()          DMA_DMAEN_=true      //Start DMA transfer
#define  DMA_is_busy()        DMA_DMAEN_           //DMA is working
#define  DMA_ended()          !DMA_DMAEN_          //DMA transfer ended
#define  DMA_SET_FULL_DUPLEX() DMA_FULL_DUPLEX_=true; DMA_TX_=false  //SPI DMA operates in Full-Duplex mode, data is simultaneously transmitted and received
#define  DMA_SET_TX()         DMA_FULL_DUPLEX_=false; DMA_TX_=true   //DMA operates in Half-Duplex mode, data is transmitted only
#define  DMA_SET_RX()         DMA_FULL_DUPLEX_=false; DMA_TX_=false  //DMA operates in Half-Duplex mode, data is transmitted only

#define  DMA_write(DataSize, buffer)      DMA_SET_TX(); DMA_TXADDR(buffer); DMA_DMABC(DataSize-1); DMA_start()      //Simple DMA Write and start transfer
#define  DMA_read(DataSize, buffer)       DMA_SET_RX(); DMA_RXADDR(buffer); DMA_DMABC(DataSize-1); DMA_start()      //Simple DMA Read and start transfer
/*****************************************************************************************
******************************************************************************************
******************************************************************************************
*****************************************************************************************/
/*****************************************************************************************
******************************************************************************************
******************************************************************************************
*****************************************************************************************/
/*****************************************************************************************
******************************************************************************************
* Inicializa configuracao padrao nRF24L01+ (Ititializes default configuration for DMA)
* ex:   DMA_default_config();
*/
void DMA_default_config() {  //Sets default DMA configuration

   #ifdef DMA_FULL_DUPLEX_MODE     //Set to Full duplex mode
      DMA_config(DMA_TXINC|DMA_RXINC|DMA_FULL_DUPLEX);   //DMA Control 1 config options
      DMA_int_config(0x00);                              //configs DMACON2 register, that contains control bits for controlling interrupt generation and inter-byte delay behavior.
   #else 
      DMA_config(DMA_TXINC|DMA_RXINC|DMA_TX);            //DMA Control 1 config options
      DMA_int_config(0x00);                              //configs DMACON2 register, that contains control bits for controlling interrupt generation and inter-byte delay behavior.
   #endif
}//
/*****************************************************************************************
******************************************************************************************
*
* DMA_write_block : Write RAM data block using DMA
* DMA_read_block  : Read RAM data block using DMA
*
*
* Parameters:
*
*     buffer   :  pointer to data array for sending or receiving using DMA
*     DataSize :  data size(number of bytes for writing or reading into "buffer" pointer)(Máx 1024 bytes)
*
* ex:   
*     int databuf[140];                //RAM DMA data pointer
*     DMA_read_block(140, databuf);    //reads 140 bytes using DMA
*     while(DMA_is_busy());            //waits till DMA ended databuf reading
*     DMA_write_block(140, databuf);    //writes back the 140 bytes to SPI bus
*     while(DMA_is_busy());            //waits till DMA ended databuf writing(sending)
*/

 void DMA_write_block(int16 DataSize, char *buffer) {    //Write RAM data block using DMA
   if (DataSize>1024) DataSize=1024;
   dma_write = true;
   dma_busy = true;

   #ifndef DMA_FULL_DUPLEX_MODE     //If not seted to Full duplex mode
      DMA_SET_TX();                 //DMA operates in Half-Duplex mode, data is transmitted only
   #endif

   DataSize--;
   DMA_TXADDR(buffer);     
   DMA_DMABC(DataSize);             //write datasize -1(1 to 1024 bytes)

   #ifdef DMA_use_ints     
      enable_interrupts(INT_SSP2);
   #endif
   DMA_start();            //Start DMA transfer
}
//
void DMA_read_block(int16 DataSize, char *buffer) {   //Read RAM data block using DMA
     
   if (DataSize>1024) DataSize=1024;
   dma_write = false;
   dma_busy = true;

   #ifndef DMA_FULL_DUPLEX_MODE     //If not seted to Full duplex mode
      DMA_SET_RX();                 //DMA operates in Half-Duplex mode, data is transmitted only
   #endif

   DataSize--;
   DMA_RXADDR(buffer);     
   DMA_DMABC(DataSize);             //write datasize -1(1 to 1024 bytes)
     
   #ifdef DMA_use_ints     
      enable_interrupts(INT_SSP2);
   #endif
   DMA_start();            //Start DMA transfer
}
//
/*****************************************************************************************
******************************************************************************************
******************************************************************************************
*****************************************************************************************/
/*****************************************************************************************
******************************************************************************************
*
* DMA interrupt service control
*
*/
#INT_SSP2
 void DMA_spi_isr(void) {
//   int timeout=10;
     
   if (!DMA_DMAEN_) {
      disable_interrupts(INT_SSP2);

      // If write mode
      if (dma_write) {
         delay_cycles(1); //do what you want to
         spi_write2(0xFF);      // 16 bit CRC MSB (ignored)
      }
      else {
         delay_cycles(1); //do what you want to
      }
       
      delay_cycles(1); //do what you want to
      dma_busy = false;
  }
 }//




And the header file:




Code:
// spi_dma.h for PIC18F24J11, by Eduardo Guilherme Brandt
//

 #ifndef SPI_DMA
 #define SPI_DMA
 
/***************************
Further information
 
- I/O PIN CONSIDERATIONS
   When enabled, the SPI DMA module uses the MSSP2 module. All SPI related input and output signals related
   to MSSP2 are routed through the Peripheral Pin Select module.

- RAM TO RAM COPY OPERATIONS
   Although the SPI DMA module is primarily intended to be used for SPI communication purposes, the module
   can also be used to perform RAM to RAM copy operations.
   To do this, configure the module for Full-Duplex Master mode operation, but assign the SDO2 output
   and SDI2 input functions onto the same RPn pin in the PPS module. This will allow the module to operate in
   Loopback mode, providing RAM copy capability.

- IDLE AND SLEEP CONSIDERATIONS
   The SPI DMA module remains fully functional when the microcontroller is in Idle mode.
   During normal sleep, the SPI DMA module is not functional and should not be used. To avoid corrupting a
   transfer, user firmware should be careful to make certain that pending DMA operations are complete by
   polling the DMAEN bit in the DMACON1 register prior to putting the microcontroller into Sleep.
   
   In SPI Slave modes, the MSSP2 module is capable of transmitting and/or receiving one byte of data while in
   Sleep mode. This allows the SSP2IF flag in the PIR3 register to be used as a wake-up source. When the
   DMAEN bit is cleared, the SPI DMA module is effectively disabled, and the MSSP2 module functions
   normally, but without DMA capabilities. If the DMAEN bit is clear prior to entering Sleep, it is still possible to
   use the SSP2IF as a wake-up source without any data loss.

   Neither MSSP2 nor the SPI DMA module will provide any functionality in Deep Sleep. Upon exiting from
   Deep Sleep, all of the I/O pins, MSSP2 and SPI DMA related registers will need to be fully reinitialized before
   the SPI DMA module can be used again.
*/

// Definitions for SPI DMA transfer
#byte DMA_TXADDRH = 0x0F6A    //SPI DMA Tranmsit Data Pointer High Byte ---- 0000 73
#byte DMA_TXADDRL = 0x0F6B    //SPI DMA Tranmsit Data Pointer Low Byte 0000 0000 73
#byte DMA_RXADDRH = 0x0F68    //SPI DMA Receive Data Pointer High Byte ---- 0000 73
#byte DMA_RXADDRL = 0x0F69    //SPI DMA Receive Data Pointer Low Byte 0000 0000 73
#byte DMA_DMABCH  = 0x0F66    //SPI DMA Receive Data Pointer High Byte
#byte DMA_DMABCL  = 0x0F67    //SPI DMA Byte Count Low Byte 0000 0000 73

// Definitions for SPI DMA transfer
#byte DMA_DMACON1 = 0x0F88    //see DMACON1. DMA Control1 register options
#byte DMA_DMACON2 = 0x0F86    //see DMACON2. Contains control bits for controlling interrupt generation and inter-byte delay behavior. The INTLVL<3:0> bits are used to select when an SSP2IF interrupt should be generated.The function of the DLYCYC<3:0> bits depends on the SPI operating mode (Master/Slave), as well as the DLYINTEN setting.                         
#word DMA_TXADDR  = 0x0F6A    //**use DMA_TXADDR(x) for load data(byte reversed). SPI DMA Tranmsit Data Pointer(word)
#word DMA_RXADDR  = 0x0F68    //**use DMA_RXADDR(x) for load data(byte reversed). SPI DMA Receive Data Pointer(word)
#word DMA_DMABC   = 0x0F66    //**use DMA_DMABC(x) for load data(byte reversed). SPI DMA Data Size(word). (set value 0 to 1023 = 1 to 1024 bytes)

 // DMACON1 bits
 #bit DMA_SSCON1_    = DMA_DMACON1.7
 #bit DMA_SSCON0_    = DMA_DMACON1.6                       
 #bit DMA_TXINC_     = DMA_DMACON1.5
 #bit DMA_RXINC_     = DMA_DMACON1.4
 #bit DMA_FULL_DUPLEX_=DMA_DMACON1.3   //SPI DMA operates in Full-Duplex mode, data is simultaneously transmitted and received
 #bit DMA_TX_        = DMA_DMACON1.2   //DMA operates in Half-Duplex mode, data is transmitted only
 #bit DMA_DUPLEX1_   = DMA_DMACON1.3                         
 #bit DMA_DUPLEX0_   = DMA_DMACON1.2
 #bit DMA_DLYINTEN_  = DMA_DMACON1.1
 #bit DMA_DMAEN_     = DMA_DMACON1.0
 
  // Definitions for SPI2(not used)
 #byte SSP2STAT      = 0x0F73
 #byte SSP2BUF       = 0x0F75
 #byte SSP2CON1      = 0x0F72

enum  DMA_DMACON1_enum {   //DMA Control1 register options
   DMA_SSCON_32bits=0b11000000,  //Used to control the SS(slave select pin). SS_DMA is asserted each 4 bytes transmitted; DLYINTEN is always reset low
   DMA_SSCON_16bits=0b10000000,  //Used to control the SS(slave select pin). SS_DMA is asserted each 2 bytes transmitted; DLYINTEN is always reset low
   DMA_SSCON_8bits= 0b01000000,  //Used to control the SS(slave select pin). SS_DMA is asserted each 1 byte transmitted; DLYINTEN is always reset low
   DMA_TXINC      = 0b00100000,  //Allows the transmit address to increment as the transfer progresses. The transmit address is to be incremented from the initial value of TXADDR<11:0>
   DMA_RXINC      = 0b00010000,  //Allows the receive address to increment as the transfer progresses. The received address is to be incremented from the initial value of RXADDR<11:0>
   DMA_FULL_DUPLEX= 0b00001000,  //SPI DMA operates in Full-Duplex mode, data is simultaneously transmitted and received
   DMA_TX         = 0b00000100,  //DMA operates in Half-Duplex mode, data is transmitted only
   DMA_RX         = 0b00000000,  //DMA operates in Half-Duplex mode, data is transmitted only
   DMA_DLYINTEN   = 0b00000010,  //Delay Interrupt Enable bit. Enables the interrupt to be invoked after the number of SCK cycles specified in DLYCYC<2:0> has elapsed from the latest completed transfer. If interrupt is enabled, SSCON<1:0> must be set to ‘00’
   DMA_DMAEN      = 0b00000001}; //This bit is set by the users’ software to start the DMA operation. It is reset back to zero by the DMA engine when the DMA operation is completed or aborted. **To avoid possible data corruption, once the DMAEN bit is set, user firmware should not attempt to modify any of the MSSP2 or SPI DMA related registers, with the exception of the INTLVL bits in the DMACON2 register.                   

enum  DMA_DMACON2_enum {   //DMACON2 register contains control bits for controlling interrupt generation and inter-byte delay behavior. The INTLVL<3:0> bits are used to select when an SSP2IF interrupt should be generated.The function of the DLYCYC<3:0> bits depends on the SPI operating mode (Master/Slave), as well as the DLYINTEN setting.                         
   DMA_DLYCYC3  = 0b10000000,    //
   DMA_DLYCYC2  = 0b01000000,    //
   DMA_DLYCYC1  = 0b00100000,    //
   DMA_DLYCYC0  = 0b00010000,    //
   DMA_INTLVL3  = 0b00001000,    //
   DMA_INTLVL2  = 0b00000100,    //
   DMA_INTLVL1  = 0b00000010,    //
   DMA_INTLVL0  = 0b00000001};   //


/*
In SPI Master mode, the DLYCYC<3:0> bits can be usedto control how much time the module will Idle between
bytes in a transfer. By default, the hardware requires a minimum delay of: 8 TCY for FOSC/4, 9 TCY for FOSC/16
and 15 TCY for FOSC/64. Additional delays can be added with the DLYCYC bits. In SPI Slave modes, the
DLYCYC<3:0> bits may optionally be used to trigger an additional time-out based interrupt.

DMA_DLYCYCLES(bits 3 to 0):
1111 = Delay time in number of instruction cycles is 2,048 cycles
1110 = Delay time in number of instruction cycles is 1,024 cycles
1101 = Delay time in number of instruction cycles is 896 cycles
1100 = Delay time in number of instruction cycles is 768 cycles
1011 = Delay time in number of instruction cycles is 640 cycles
1010 = Delay time in number of instruction cycles is 512 cycles
1001 = Delay time in number of instruction cycles is 384 cycles
1000 = Delay time in number of instruction cycles is 256 cycles
0111 = Delay time in number of instruction cycles is 128 cycles
0110 = Delay time in number of instruction cycles is 64 cycles
0101 = Delay time in number of instruction cycles is 32 cycles
0100 = Delay time in number of instruction cycles is 16 cycles
0011 = Delay time in number of instruction cycles is 8 cycles
0010 = Delay time in number of instruction cycles is 4 cycles
0001 = Delay time in number of instruction cycles is 2 cycles
0000 = Delay time in number of instruction cycles is 1 cycle



INTLVL<3:0>: Watermark Interrupt Enable bits
These bits specify the amount of remaining data yet to be transferred (transmitted and/or received)
upon which an interrupt is generated.
1111 = Amount of remaining data to be transferred is 576 bytes
1110 = Amount of remaining data to be transferred is 512 bytes
1101 = Amount of remaining data to be transferred is 448 bytes
1100 = Amount of remaining data to be transferred is 384 bytes
1011 = Amount of remaining data to be transferred is 320 bytes
1010 = Amount of remaining data to be transferred is 256 bytes
1001 = Amount of remaining data to be transferred is 192 bytes
1000 = Amount of remaining data to be transferred is 128 bytes
0111 = Amount of remaining data to be transferred is 67 bytes
0110 = Amount of remaining data to be transferred is 32 bytes
0101 = Amount of remaining data to be transferred is 16 bytes
0100 = Amount of remaining data to be transferred is 8 bytes
0011 = Amount of remaining data to be transferred is 4 bytes
0010 = Amount of remaining data to be transferred is 2 bytes
0001 = Amount of remaining data to be transferred is 1 byte
0000 = Transfer complete

For example, if DMACON2<3:0> = 0101(16 bytes remaining), the SSP2IF interrupt flag will
become set once DMABC reaches 00Fh. If user firmware then clears the SSP2IF interrupt flag, the flag
will not be set again by the hardware until after all bytes have been fully transmitted and the DMA
transaction is complete.
*/

 int1 dma_write = false;
 int1 dma_busy = false;

// int8 res1, res2;


 






[/code]
_________________
Eduardo Guilherme Brandt
Eduardo__



Joined: 23 Nov 2011
Posts: 197
Location: Brazil

View user's profile Send private message

PostPosted: Wed Feb 08, 2012 11:28 am     Reply with quote

Archere wrote:
Thanks for sharing this driver I'll try this driver if you have any more such drivers then share these with me.


Happy for helping!

You can change others SPI drivers to accept DMA transfers.
I reached almost 740KBytes/S (PIC at 48MHz) reading from a FLASH memory.
It´s pretty good for a PIC uC!! Smile It´s easy to use, but it´s a penny that PIC has just one DMA. But it´s good enough for small projects.

++++++++++++++++++++
Archere post deleted.

Reason: Spambot selling insurance in sigline link

Sorry.

- Forum Moderator
++++++++++++++++++++

_________________
Eduardo Guilherme Brandt
hugo_br



Joined: 01 Aug 2011
Posts: 26
Location: BRAZIL

View user's profile Send private message Visit poster's website

DMA com AD?
PostPosted: Fri Feb 10, 2012 10:17 am     Reply with quote

Good afternoon Eduardo.


I'm trying to read 4 channels of AD DSPIC33FJ256GP506 simultaneously via DMA.
Or you need to make measurements of each channel in 1024 AD by a total of four channels of DMA read and play these on a SD card in an I2C memory.
Through the Example of DMA 4120 CCS could do a reading at a time, but I can not make 4 simultaneous readings. Do you think it would be possible? Would help with something? For I am crude in DMA!

I really appreciate the attention and help.

I look forward to a return.

Thank you, Hugo.

Now in Portguese:

Boa tarde Eduardo.

Vou escrever em Português mesmo!

Estou tentando ler 4 Canais do AD de um DSPIC33FJ256GP506 simultaneamente através de DMA.
Ou seja preciso fazer 1024 medições de cada canal AD totalizando 4 Canais através de DMA e jogar estas leitura em um SD CARD em um memória I2C.
Através do Exemplo de DMA do CCS 4.120 consegui fazer uma leitura por vez, mas não consigo fazer 4 leituras simultâneas. Você acha que isso seria possível? Teria como ajudar em algo? Pois sou crú em DMA!

Agradeço muito a atenção e ajuda.

Aguardo um retorno.

Grato, Hugo.
Eduardo__



Joined: 23 Nov 2011
Posts: 197
Location: Brazil

View user's profile Send private message

PostPosted: Fri Feb 10, 2012 12:15 pm     Reply with quote

Dear Hugo,

I´ve not used DSPIC DMA yet*(Only PIC18F DMA). But you should see DSPIC33FJ256GP506 datasheet, because there are differences between part resources.

For example: PIC18F26J50 has no AD-DMA. Only SPI-DMA. But even SPI-DMA could be used for making RAM-to-RAM copy.
Another thing, PIC18F26J50 has only one DMA .

You should check all these parameters.

Another suggestion:
1 - You should post your questions in General CCS C Discussion Forum, not here. This is Code Library Forum.
2 - Post in English, so the community will help you!
3 - Use a debugger like "ICD2BR - Mosaico". Just look at it in google. It will help you very much! You can use it with MPLAB and CCS language. It works very well, even for DSPICs. CCS has also a very cheap debugger named "ICD-U64 In-Circuit Programmer/Debugger", but I´m not used yet. I´m going to buy it later.


Quote:
General CCS C Discussion Forum
http://www.ccsinfo.com/forum/viewforum.php?f=1

Good luck!! Razz
_________________
Eduardo Guilherme Brandt
hugo_br



Joined: 01 Aug 2011
Posts: 26
Location: BRAZIL

View user's profile Send private message Visit poster's website

PostPosted: Fri Feb 10, 2012 1:07 pm     Reply with quote

Eduardo thank you for your help.
I posted several posts here at CCS Forum and getting no help yet. But I'm always trying.
Even so thank very much the support and attention.

Maybe one day I'm lucky.

Thank you, Hugo.
zspikes



Joined: 10 Jan 2013
Posts: 7

View user's profile Send private message

PostPosted: Wed May 14, 2014 7:01 am     Reply with quote

Hi!! First of all, thanks a lot for sharing. This is very useful!
I have one question. How do you use it in a full duplex communication? You only execute the "write" command and receive it automatically?

thanks!!!
Eduardo__



Joined: 23 Nov 2011
Posts: 197
Location: Brazil

View user's profile Send private message

PostPosted: Wed May 14, 2014 6:30 pm     Reply with quote

yes, you can use dma_write_block() and dma_read_block() functions, but, first #define DMA_FULL_DUPLEX_MODE


I´m sorry it was a lot of time I not used this driver anymore, and now I´ve not too much time for checking it. You´ll have to read by yourself the driver instructions.

good luck friend!!
_________________
Eduardo Guilherme Brandt
zspikes



Joined: 10 Jan 2013
Posts: 7

View user's profile Send private message

PostPosted: Thu May 15, 2014 7:27 am     Reply with quote

Ok, great! I'll study a little the code. Then I tell you how it went ;)
Thank you very much!
zspikes



Joined: 10 Jan 2013
Posts: 7

View user's profile Send private message

PostPosted: Wed May 21, 2014 7:53 pm     Reply with quote

Hi Eduardo! I've read the whole chapter "SPI DMA Module" from the datasheet, and your code as well. I've made some modifications to adapt it to my needs. I hope you don't mind.
I post it in case someone needs it.
Cheers!

Here's the code:

Code:

/*
                          PIC18F24J11 DMA driver

File        :  spi_dma_PIC18F24J11.c
Created by  :  Eduardo Guilherme Brandt
Modified by :  Gerardo Daniel Cibeira                   
Created     :  31/01/2012 ( V 1.0 )
Contact     :  eduardogbrandt@yahoo.com.br

Historic    :  Version 1.0 - 31/01/2012 Eduardo Guilherme Brandt
               Version 1.1 - 19/05/2014 Gerardo Daniel Cibeira
*/


#include <dma.h>      //HEADER FILE



/***************************************************************************************** 
*
* DEFINED MACROS   
*
******************************************************************************************/

#define DMA_TXADDR(x)   DMA_TXADDRL=make8((int16)x,0); DMA_TXADDRH=make8((int16)x,1)   //This address is byte reversed. **Use this macro for load data.
#define DMA_RXADDR(x)   DMA_RXADDRL=make8((int16)x,0); DMA_RXADDRH=make8((int16)x,1)   //This address is byte reversed **Use this macro for load data.
#define DMA_DMABC(x)    DMA_DMABCL=make8((int16)x,0); DMA_DMABCH=make8((int16)x,1)     //This address is byte reversed **Use this macro for load data.
 
#define  setup_DMA(x)        DMA_DMACON1 = x
#define  setup_DMA_int(x)    DMA_DMACON2 = x

#define  DMA_start()           DMA_DMAEN_ = 1       //Start DMA transfer
#define  DMA_is_busy()         DMA_DMAEN_           //DMA is working
#define  DMA_ended()           !DMA_DMAEN_          //DMA transfer ended
#define  DMA_SET_FULL_DUPLEX() DMA_DUPLEX1_ = 1; DMA_DUPLEX0_ = 0
#define  DMA_SET_TX()          DMA_DUPLEX1_ = 0; DMA_DUPLEX0_ = 1
#define  DMA_SET_RX()          DMA_DUPLEX1_ = 0; DMA_DUPLEX0_ = 0



/*****************************************************************************************
*
* DMA_write       : Write RAM data block using DMA
* DMA_read        : Read RAM data block using DMA
* DMA_transfer    : Does a fullduplex transfer using DMA
*
* Parameters:
*
*     buffer   :  pointer to data array for sending or receiving using DMA
*     DataSize :  data size(number of bytes for writing or reading into "buffer" pointer)(Máx 1024 bytes)
*
******************************************************************************************/

void DMA_write(int16 DataSize, char *buffer) {
   if (DataSize>1024) DataSize=1024;
   
   DMA_SET_TX();
   DMA_TXADDR(buffer);     
   DMA_DMABC(DataSize-1);
   DMA_start();
}

 
void DMA_read(int16 DataSize, char *buffer) {     
   if (DataSize>1024) DataSize=1024; 
   
   DMA_SET_RX();
   DMA_RXADDR(buffer);     
   DMA_DMABC(DataSize-1);
   DMA_start();
}


void DMA_transfer(int16 DataSize, char *txbuffer, char *rxbuffer) {       
   if (DataSize>1024) DataSize=1024;
   
   DMA_SET_FULL_DUPLEX();
   DMA_RXADDR(rxbuffer);
   DMA_TXADDR(txbuffer);     
   DMA_DMABC(DataSize-1);
   DMA_start();
}


and the header file:

Code:

/*
                          PIC18F24J11 DMA driver

File        :  spi_dma_PIC18F24J11.h
Created by  :  Eduardo Guilherme Brandt
Modified by :  Gerardo Daniel Cibeira                   
Created     :  31/01/2012 (v1.0)
Contact     :  eduardogbrandt@yahoo.com.br

Historic    :  Version 1.0 - 31/01/2012 Eduardo Guilherme Brandt
               Version 1.1 - 19/05/2014 Gerardo Daniel Cibeira
*/ 

#ifndef SPI_DMA
#define SPI_DMA
 
/***************************
Further information
 
- I/O PIN CONSIDERATIONS
   When enabled, the SPI DMA module uses the MSSP2 module. All SPI related input and output signals related
   to MSSP2 are routed through the Peripheral Pin Select module.

- RAM TO RAM COPY OPERATIONS
   Although the SPI DMA module is primarily intended to be used for SPI communication purposes, the module
   can also be used to perform RAM to RAM copy operations.
   To do this, configure the module for Full-Duplex Master mode operation, but assign the SDO2 output
   and SDI2 input functions onto the same RPn pin in the PPS module. This will allow the module to operate in
   Loopback mode, providing RAM copy capability.

- IDLE AND SLEEP CONSIDERATIONS
   The SPI DMA module remains fully functional when the microcontroller is in Idle mode.
   During normal sleep, the SPI DMA module is not functional and should not be used. To avoid corrupting a
   transfer, user firmware should be careful to make certain that pending DMA operations are complete by
   polling the DMAEN bit in the DMACON1 register prior to putting the microcontroller into Sleep.
   
   In SPI Slave modes, the MSSP2 module is capable of transmitting and/or receiving one byte of data while in
   Sleep mode. This allows the SSP2IF flag in the PIR3 register to be used as a wake-up source. When the
   DMAEN bit is cleared, the SPI DMA module is effectively disabled, and the MSSP2 module functions
   normally, but without DMA capabilities. If the DMAEN bit is clear prior to entering Sleep, it is still possible to
   use the SSP2IF as a wake-up source without any data loss.

   Neither MSSP2 nor the SPI DMA module will provide any functionality in Deep Sleep. Upon exiting from
   Deep Sleep, all of the I/O pins, MSSP2 and SPI DMA related registers will need to be fully reinitialized before
   the SPI DMA module can be used again.
*/

// Definitions for SPI DMA transfer
#byte DMA_TXADDRH = 0x0F6A    //SPI DMA Tranmsit Data Pointer High Byte ---- 0000 73
#byte DMA_TXADDRL = 0x0F6B    //SPI DMA Tranmsit Data Pointer Low Byte 0000 0000 73
#byte DMA_RXADDRH = 0x0F68    //SPI DMA Receive Data Pointer High Byte ---- 0000 73
#byte DMA_RXADDRL = 0x0F69    //SPI DMA Receive Data Pointer Low Byte 0000 0000 73
#byte DMA_DMABCH  = 0x0F66    //SPI DMA Receive Data Pointer High Byte
#byte DMA_DMABCL  = 0x0F67    //SPI DMA Byte Count Low Byte 0000 0000 73

// Definitions for SPI DMA transfer
#byte DMA_DMACON1 = 0x0F88    //see DMACON1. DMA Control1 register options
#byte DMA_DMACON2 = 0x0F86    //see DMACON2. Contains control bits for controlling interrupt generation and inter-byte delay behavior. The INTLVL<3:0> bits are used to select when an SSP2IF interrupt should be generated.The function of the DLYCYC<3:0> bits depends on the SPI operating mode (Master/Slave), as well as the DLYINTEN setting.                         
#word DMA_TXADDR  = 0x0F6A    //**use DMA_TXADDR(x) for load data(byte reversed). SPI DMA Tranmsit Data Pointer(word)
#word DMA_RXADDR  = 0x0F68    //**use DMA_RXADDR(x) for load data(byte reversed). SPI DMA Receive Data Pointer(word)
#word DMA_DMABC   = 0x0F66    //**use DMA_DMABC(x) for load data(byte reversed). SPI DMA Data Size(word). (set value 0 to 1023 = 1 to 1024 bytes)

 // DMACON1 bits
 #bit DMA_SSCON1_    = DMA_DMACON1.7
 #bit DMA_SSCON0_    = DMA_DMACON1.6                       
 #bit DMA_TXINC_     = DMA_DMACON1.5
 #bit DMA_RXINC_     = DMA_DMACON1.4
 #bit DMA_DUPLEX1_   = DMA_DMACON1.3                         
 #bit DMA_DUPLEX0_   = DMA_DMACON1.2
 #bit DMA_DLYINTEN_  = DMA_DMACON1.1
 #bit DMA_DMAEN_     = DMA_DMACON1.0
 
//setup_DMA() defines
#define DMA_SSCON_4     0xC0  //Used to control the SS(slave select pin). SS_DMA is asserted each 4 bytes transmitted; DLYINTEN is always reset low
#define DMA_SSCON_2     0x80  //Used to control the SS(slave select pin). SS_DMA is asserted each 2 bytes transmitted; DLYINTEN is always reset low
#define DMA_SSCON_1     0x40  //Used to control the SS(slave select pin). SS_DMA is asserted each 1 byte transmitted; DLYINTEN is always reset low
#define DMA_TXINC       0x20  //Allows the transmit address to increment as the transfer progresses. The transmit address is to be incremented from the initial value of TXADDR<11:0>
#define DMA_RXINC       0x10  //Allows the receive address to increment as the transfer progresses. The received address is to be incremented from the initial value of RXADDR<11:0>
#define DMA_FULL_DUPLEX 0x08  //SPI DMA operates in Full-Duplex mode, data is simultaneously transmitted and received
#define DMA_TX          0x04  //DMA operates in Half-Duplex mode, data is transmitted only
#define DMA_RX          0x00  //DMA operates in Half-Duplex mode, data is transmitted only
#define DMA_DLYINTEN    0x02  //Delay Interrupt Enable bit. Enables the interrupt to be invoked after the number of SCK cycles specified in DLYCYC<2:0> has elapsed from the latest completed transfer. If interrupt is enabled, SSCON<1:0> must be set to ‘00’
#define DMA_DMAEN       0x01

//setup_DMA_int() defines
#define DMA_DLYCYC_2048   0xF0
#define DMA_DLYCYC_1024   0xE0
#define DMA_DLYCYC_896    0xD0
#define DMA_DLYCYC_768    0xC0
#define DMA_DLYCYC_640    0xB0
#define DMA_DLYCYC_512    0xA0
#define DMA_DLYCYC_384    0x90
#define DMA_DLYCYC_256    0x80
#define DMA_DLYCYC_128    0x70
#define DMA_DLYCYC_64     0x60
#define DMA_DLYCYC_32     0x50
#define DMA_DLYCYC_16     0x40
#define DMA_DLYCYC_8      0x30
#define DMA_DLYCYC_4      0x20
#define DMA_DLYCYC_2      0x10
#define DMA_DLYCYC_1      0x00
#define DMA_INTLVL_576    0x0F
#define DMA_INTLVL_512    0x0E
#define DMA_INTLVL_448    0x0D
#define DMA_INTLVL_384    0x0C
#define DMA_INTLVL_320    0x0B
#define DMA_INTLVL_256    0x0A
#define DMA_INTLVL_192    0x09
#define DMA_INTLVL_128    0x08
#define DMA_INTLVL_67     0x07
#define DMA_INTLVL_32     0x06
#define DMA_INTLVL_16     0x05
#define DMA_INTLVL_8      0x04
#define DMA_INTLVL_4      0x03
#define DMA_INTLVL_2      0x02
#define DMA_INTLVL_1      0x01
#define DMA_INTLVL_0      0x00


/*
In SPI Master mode, the DLYCYC<3:0> bits can be usedto control how much time the module will Idle between
bytes in a transfer. By default, the hardware requires a minimum delay of: 8 TCY for FOSC/4, 9 TCY for FOSC/16
and 15 TCY for FOSC/64. Additional delays can be added with the DLYCYC bits. In SPI Slave modes, the
DLYCYC<3:0> bits may optionally be used to trigger an additional time-out based interrupt.

DMA_DLYCYCLES(bits 3 to 0):
1111 = Delay time in number of instruction cycles is 2,048 cycles
1110 = Delay time in number of instruction cycles is 1,024 cycles
1101 = Delay time in number of instruction cycles is 896 cycles
1100 = Delay time in number of instruction cycles is 768 cycles
1011 = Delay time in number of instruction cycles is 640 cycles
1010 = Delay time in number of instruction cycles is 512 cycles
1001 = Delay time in number of instruction cycles is 384 cycles
1000 = Delay time in number of instruction cycles is 256 cycles
0111 = Delay time in number of instruction cycles is 128 cycles
0110 = Delay time in number of instruction cycles is 64 cycles
0101 = Delay time in number of instruction cycles is 32 cycles
0100 = Delay time in number of instruction cycles is 16 cycles
0011 = Delay time in number of instruction cycles is 8 cycles
0010 = Delay time in number of instruction cycles is 4 cycles
0001 = Delay time in number of instruction cycles is 2 cycles
0000 = Delay time in number of instruction cycles is 1 cycle



INTLVL<3:0>: Watermark Interrupt Enable bits
These bits specify the amount of remaining data yet to be transferred (transmitted and/or received)
upon which an interrupt is generated.
1111 = Amount of remaining data to be transferred is 576 bytes
1110 = Amount of remaining data to be transferred is 512 bytes
1101 = Amount of remaining data to be transferred is 448 bytes
1100 = Amount of remaining data to be transferred is 384 bytes
1011 = Amount of remaining data to be transferred is 320 bytes
1010 = Amount of remaining data to be transferred is 256 bytes
1001 = Amount of remaining data to be transferred is 192 bytes
1000 = Amount of remaining data to be transferred is 128 bytes
0111 = Amount of remaining data to be transferred is 67 bytes
0110 = Amount of remaining data to be transferred is 32 bytes
0101 = Amount of remaining data to be transferred is 16 bytes
0100 = Amount of remaining data to be transferred is 8 bytes
0011 = Amount of remaining data to be transferred is 4 bytes
0010 = Amount of remaining data to be transferred is 2 bytes
0001 = Amount of remaining data to be transferred is 1 byte
0000 = Transfer complete

For example, if DMACON2<3:0> = 0101(16 bytes remaining), the SSP2IF interrupt flag will
become set once DMABC reaches 00Fh. If user firmware then clears the SSP2IF interrupt flag, the flag
will not be set again by the hardware until after all bytes have been fully transmitted and the DMA
transaction is complete.
*/


And here is an example on how to use it

Code:

// SPI
setup_spi2(SPI_MASTER | SPI_SCK_IDLE_HIGH | SPI_CLK_DIV_4 | SPI_XMIT_H_TO_L | SPI_SAMPLE_AT_END);
   
//DMA
setup_DMA(DMA_TXINC | DMA_RXINC | DMA_FULL_DUPLEX );
setup_DMA_int(DMA_DLYCYC_1 | DMA_INTLVL_0);
...
...
...
char txbuffer[128], rxbuffer[128];
DMA_transfer(128, txbuffer, rxbuffer);
Eduardo__



Joined: 23 Nov 2011
Posts: 197
Location: Brazil

View user's profile Send private message

PostPosted: Thu May 22, 2014 7:56 am     Reply with quote

Thanks Mr. zspikes.

I Suggest you change the version in the header to V1.1 and put your name as a contributtor/remaker(write changes history from version 1 to 1.1).

Thanks a lot.
_________________
Eduardo Guilherme Brandt
zspikes



Joined: 10 Jan 2013
Posts: 7

View user's profile Send private message

PostPosted: Fri May 23, 2014 10:57 am     Reply with quote

Dear friend,
You mean something like this?

Code:
 
                          SPI DMA driver
 
File        :  spi_dma.c
Creator     :  Eduardo Guilherme Brandt
Contributor :  Gerardo Daniel Cibeira                               
Version     :  v1.1
Contact     :  eduardogbrandt@yahoo.com.br
 
Historic    :  Version 1.0 - 31/01/2012 Eduardo Guilherme Brandt
               Version 1.1 - 19/05/2014 Gerardo Daniel Cibeira


Also, a friend of mine wants to write an article in his blog http://www.micropic.es/mpblog/ about the SPI DMA module and the library you've made.
Do we have your permission to do this?
Thanks again!
Eduardo__



Joined: 23 Nov 2011
Posts: 197
Location: Brazil

View user's profile Send private message

PostPosted: Fri May 23, 2014 11:06 am     Reply with quote

Yes, the header is very good!

Of course he has the permition! I´m very happy you´re using this file for something! It´s very good to contribute with something to this world. The life is short!

Good luck and have a nice weekend!
=)
_________________
Eduardo Guilherme Brandt
zspikes



Joined: 10 Jan 2013
Posts: 7

View user's profile Send private message

PostPosted: Fri May 23, 2014 11:48 am     Reply with quote

Great! Thanks a lot. Have a good weekend you too ;)
Bye!
zspikes



Joined: 10 Jan 2013
Posts: 7

View user's profile Send private message

PostPosted: Tue Dec 16, 2014 10:38 am     Reply with quote

Hi!
I'm passing by to tell you I've just successfully tested the library in full duplex mode with interruptions :D

I'm using it to get readings via SPI from an ADC128S022. It's awesome, because while the CPU is busy processing the last sample, the DMA is getting the next sample, so I get a higher sample rate Smile (it's for a robot I'm working on)

I'm using the following code:
setup:
Code:
   setup_spi2(SPI_MASTER | SPI_CLK_DIV_4 | SPI_H_TO_L | SPI_XMIT_L_TO_H);
   setup_DMA(DMA_TXINC | DMA_RXINC | DMA_FULL_DUPLEX | DMA_DLYINTEN);
   setup_DMA_int(DMA_DLYCYC_1 | DMA_INTLVL_0);


main code:
Code:

//DMA buffers
int16 rxbuffer[8];
int16 txbuffer[8] = {0x0008,0x0010,0x0018,0x0020,0x0028,0x0030,0x0038,0x0000};

...

   if(DMA_ended()){
         //start next transaction
         output_low(SPI_CS);
         output_high(LEDS_ON);
         DMA_transfer(16, txbuffer, rxbuffer);
      }


interruption:
Code:
#INT_SSP2
void ssp2_isr(){
   output_high(SPI_CS);
   output_low(LEDS_ON);
}


I'm very glad it's working. Thank you again Eduardo for shearing.
Bye!
Eduardo__



Joined: 23 Nov 2011
Posts: 197
Location: Brazil

View user's profile Send private message

PostPosted: Mon Dec 22, 2014 10:05 am     Reply with quote

Trank you a lot for sharing your experience with us Mr. zspikes!!!
It´s very good to hear from you again!!!

I wish you a very Merry Xmas and a very good new year!!!!!!
Eduardo
_________________
Eduardo Guilherme Brandt
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group