dma_example.c - Uses DMA in ping-pong mode to capture data from the UART, write it to the 24LC515 EEPROM

 
#include "pic24_all.h"

#define EEPROM (0xA0)   //LC515 address assuming both address pins tied low.
#define BLKSIZE (64)

//Assumes WDT is configured for longer than EEPROM write time
void waitForWriteCompletion(uint8_t u8_i2cAddr) {
  uint8_t u8_ack, u8_savedSWDTEN;
  u8_savedSWDTEN = _SWDTEN;
  _SWDTEN = 1; //enable WDT so that do not get stuck in infinite loop!
  u8_i2cAddr = I2C_WADDR(u8_i2cAddr);  //write operation, R/W# = 0;
  do {
    startI2C1();
    u8_ack = putNoAckCheckI2C1(u8_i2cAddr);
    stopI2C1();
  } while (u8_ack == I2C_NAK);
  _SWDTEN = u8_savedSWDTEN;  //restore WDT to original state
}

//this version just expects a block of 64 data bytes
void memWriteLC515(uint8_t u8_i2cAddr,  uint16_t u16_MemAddr, uint8_t *pu8_buf) {
  uint8_t u8_AddrLo, u8_AddrHi;
  uint16_t u16_i;
  u8_AddrLo = u16_MemAddr & 0x00FF;
  u8_AddrHi = (u16_MemAddr >> 8);
  if (u16_MemAddr & 0x8000) {

if MSB set , set block select bit

    u8_i2cAddr = u8_i2cAddr | 0x08;
  }
  waitForWriteCompletion(u8_i2cAddr);
  //write the data to the EEPROM
  startI2C1();
  putI2C1(I2C_WADDR(u8_i2cAddr));
  putI2C1(u8_AddrHi);
  putI2C1(u8_AddrLo);
  for (u16_i=0; u16_i < BLKSIZE; u16_i++) {
    putI2C1(*pu8_buf);
    pu8_buf++;
  }
  stopI2C1();
}

void memReadLC515(uint8_t u8_i2cAddr,  uint16_t u16_MemAddr, uint8_t *pu8_buf) {

  uint8_t u8_AddrLo, u8_AddrHi;

  u8_AddrLo = u16_MemAddr & 0x00FF;
  u8_AddrHi = (u16_MemAddr >> 8);

  if (u16_MemAddr & 0x8000) {

if MSB set , set block select bit

    u8_i2cAddr = u8_i2cAddr | 0x08;
  }
  waitForWriteCompletion(u8_i2cAddr);
  //set address counter
  write2I2C1(u8_i2cAddr,u8_AddrHi, u8_AddrLo);
  //read data
  readNI2C1(u8_i2cAddr,pu8_buf, BLKSIZE);
}

#define DMA_TRANSFER_SIZE  BLKSIZE

//define DMA buffers
#if defined(__PIC24H__) || defined (__dsPIC33F__)
uint8_t au8_bufferA[DMA_TRANSFER_SIZE] __attribute__((space(dma)));
uint8_t au8_bufferB[DMA_TRANSFER_SIZE] __attribute__((space(dma)));
#elif defined(__dsPIC33E__)
uint8_t au8_bufferA[DMA_TRANSFER_SIZE] __attribute__((space(xmemory),aligned(DMA_TRANSFER_SIZE)));
uint8_t au8_bufferB[DMA_TRANSFER_SIZE] __attribute__((space(xmemory),aligned(DMA_TRANSFER_SIZE)));
#else
#error "DMA memory not properly defined for this processor."
#endif

//some one-bit flags
typedef struct tagFLAGBITS {
unsigned u1_activeBuffer:
  1;
unsigned u1_writeFlag:
  1;
} FLAGBITS;
volatile FLAGBITS flags;

/***************************************************************************
****************************************************************************
 2008 CODE for PIC24H
 ***************************************************************************
void configDMA0() {
  DMA0PAD = (unsigned int) &U1RXREG;      //peripheral address to read
  DMA0REQ = DMA_IRQ_U1RX;  //source from UART1 RX
  _U1RXIF = 0;             //clear the UART RX IF flag
//set up ping pong buffer registers
#if defined(__PIC24H__) || defined (__dsPIC33F__)
  DMA0STA = __builtin_dmaoffset(au8_bufferA);
  DMA0STB = __builtin_dmaoffset(au8_bufferB);
#elif defined(__PIC24E__)
  DMA0STAH = __builtin_dmaoffset(au8_bufferA);
  DMA0STAL = __builtin_dmaoffset(au8_bufferA);
  DMA0STBH = __builtin_dmaoffset(au8_bufferB);
  DMA0STBL = __builtin_dmaoffset(au8_bufferB);
#endif
//setup transfer size
  DMA0CNT =   DMA_TRANSFER_SIZE -1;
  DMA0CON =             //configure and enable the module Module
    (DMA_MODULE_ON |
     DMA_SIZE_BYTE |
     DMA_DIR_READ_PERIPHERAL |
     DMA_INTERRUPT_FULL |
     DMA_NULLW_OFF |
     DMA_AMODE_REGISTER_POSTINC |
     DMA_MODE_CONTINUOUS_PING_PONG);
//enable the UART1RX Error interrupt
  _U1EIF = 0;
  _U1EIP = 1;
  _U1EIE = 1;
//enable DMA channel 0 interrupt
  _DMA0IF = 0;
  _DMA0IP = 2;
  _DMA0IE = 1;
}

***************************************************************************/


/***************************************************************************
** Configure DMA engine
**
** There are slight differences in the dsPIC33E/PIC24E devices from
**  the previous devices (F,H, etc.)
**
***************************************************************************/
#if defined(__PIC24H__) || defined (__dsPIC33F__)

ECANMSG msgBuf[NUM_BUFS] __attribute__((space(dma),aligned(NUM_BUFS*16)));

//define DMA buffers
uint8_t au8_bufferA[DMA_TRANSFER_SIZE] __attribute__((space(dma)));
uint8_t au8_bufferB[DMA_TRANSFER_SIZE] __attribute__((space(dma)));

//configure DMA transmit buffer
void configDMA0(void) {
  DMACS0 = 0;
  _DMA0IF = 0;                            // clear DMA0 IF
  _U1RXIF = 0;                            // clear the UART RX IF
  DMA0PAD = (unsigned int) &U1RXREG;      // read from UART peripheral RX register
  DMA0REQ = DMA_IRQ_U1RX;                 // DMA requests will come from UART1 RX

address of two ping-pong buffers (target mem where DMA will xfer data to)

  DMA0STA = __builtin_dmaoffset(au8_bufferA);
  DMA0STB = __builtin_dmaoffset(au8_bufferB);
  DMA0CNT =   DMA_TRANSFER_SIZE -1;       // number of byte/words to transfer each DMA request
 

configure and enable the DMA0 module DMA0 will “READ” “BYTES” giving interrupts when buffer is full, “POSTINCREMENT” the addresses to set thru buffer, and will “PING_PONG” next transfer into a second buffer

  DMA0CON =
    (DMA_MODULE_ON |
     DMA_SIZE_BYTE |
     DMA_DIR_READ_PERIPHERAL |
     DMA_INTERRUPT_FULL |
     DMA_NULLW_OFF |
     DMA_AMODE_REGISTER_POSTINC |
     DMA_MODE_CONTINUOUS_PING_PONG);

  //enable the UART1RX Error interrupt
  _U1EIF = 0;
  _U1EIP = 1;
  _U1EIE = 1;
  //enable DMA channel 0 interrupt
  _DMA0IF = 0;
  _DMA0IP = 2;
  _DMA0IE = 1;
}

#elif defined(__dsPIC33E__)

//ECANMSG msgBuf[NUM_BUFS] __attribute__((space(xmemory),aligned(NUM_BUFS*16)));
uint8_t au8_bufferA[DMA_TRANSFER_SIZE] __attribute__((space(xmemory),aligned(DMA_TRANSFER_SIZE)));
uint8_t au8_bufferB[DMA_TRANSFER_SIZE] __attribute__((space(xmemory),aligned(DMA_TRANSFER_SIZE)));

//configure DMA transmit buffer
void configDMA0(void) {
  DMAPWC = 0; // Reset the DMA Peripheral Write Collision Status Register
  _DMA0IF = 0;                            // clear DMA0 IF
  _U1RXIF = 0;                            // clear the UART RX IF
  DMA0PAD = (unsigned int) &U1RXREG;      // read from UART peripheral RX register
  DMA0REQ = DMA_IRQ_U1RX;                 // DMA requests will come from UART1 RX

address of two ping-pong buffers (target mem where DMA will xfer data to)

  DMA0STAL = __builtin_dmaoffset(au8_bufferA);
  DMA0STAH = 0;
  DMA0STBL = __builtin_dmaoffset(au8_bufferB);
  DMA0STBH = 0;
  DMA0CNT =   DMA_TRANSFER_SIZE -1;       // number of byte/words to transfer each DMA request

configure and enable the DMA0 module DMA0 will “READ” “BYTES” giving interrupts when buffer is full, “POSTINCREMENT” the addresses to set thru buffer, and will “PING_PONG” next transfer into a second buffer

  DMA0CON =
    (DMA_MODULE_ON |
     DMA_SIZE_BYTE |
     DMA_DIR_READ_PERIPHERAL |
     DMA_INTERRUPT_FULL |
     DMA_NULLW_OFF |
     DMA_AMODE_REGISTER_POSTINC |
     DMA_MODE_CONTINUOUS_PING_PONG);
  //enable the UART1RX Error interrupt
  _U1EIF = 0;
  _U1EIP = 1;
  _U1EIE = 1;
  //enable DMA channel 0 interrupt
  _DMA0IF = 0;
  _DMA0IP = 2;
  _DMA0IE = 1;

}

#else
#error "DMA has not been configured for this chip.  Edit chap13/dma_example.c."
#endif


//UART error interrupt, need this with DMA since DMA does not check for errors.
void _ISRFAST _U1ErrInterrupt(void) {
  _U1EIF = 0;      //clear the UART1 Error flag
  checkRxErrorUART1();
}

//interrupted when a ping-pong buffer is full
void _ISRFAST _DMA0Interrupt(void) {
  _DMA0IF = 0;
  flags.u1_activeBuffer = !flags.u1_activeBuffer;
  flags.u1_writeFlag = 1;
}

int main (void) {
  uint8_t au8_eepromBuf[BLKSIZE];  //holds read data from EEPROM
  uint16_t u16_MemAddr;
  uint8_t u8_mode, u8_pause;
  configBasic(HELLO_MSG);
  configI2C1(400);            //configure I2C for 400 KHz
  outString("\nEnter 'w' for write mode, anything else reads: ");
  u8_mode = inCharEcho();
  outString("\n");
  u16_MemAddr = 0;     //start at location 0 in memory
  u8_pause = 0;
  while (1) {
    uint8_t u8_i;
    if (u8_mode == 'w') {
      configDMA0();
      while (1) {
        if (flags.u1_writeFlag) reportError("DMA overflow!\n");
        while (!flags.u1_writeFlag) doHeartbeat();
        flags.u1_writeFlag = 0;
        if (flags.u1_activeBuffer)
          memWriteLC515(EEPROM,u16_MemAddr,au8_bufferA);
        else
          memWriteLC515(EEPROM,u16_MemAddr,au8_bufferB);
        u16_MemAddr = u16_MemAddr +64;
        outString("*");
      }//end while(1), press reset to escape
    } else {
      while (1) {
        if (u8_pause) {
          inChar();  //get a character
          u8_pause = 0;
        }
        memReadLC515(EEPROM,u16_MemAddr,au8_eepromBuf); // do read
        for (u8_i = 0; u8_i< 64; u8_i++) outChar(au8_eepromBuf[u8_i]);
        if (isCharReady()) {
          inChar();
          u8_pause = 1;
        }
        u16_MemAddr = u16_MemAddr + 64;
      }//end while(1) read mode, press reset to escape
    }//end else
  }//end while(1) main
}//end main