LPC11U6x - DMA and SSP

cancel
Showing results for 
Search instead for 
Did you mean: 

LPC11U6x - DMA and SSP

Jump to solution
478 Views
Contributor IV

I've been banging my head on this for a couple weeks now...

I've followed Jeremy's Ping Pong ADC code, and that's what I want - just read in all the words from the MISO and store into to buffers. I don't care what is transmitted, it just needs to be continuous.

I actually got it transmitting from the buffers, but after the first eight words, it goes off the rails and transmits out of order (which is fine).

I can't get it to receive - if I call the SW trigger on the receiver, it transmits nine words, then seems to get stuck in the ISR with a IRQ's that won't clear. It fills buffer B with "6101" (tracer of the second word transmitted)

What am I doing wrong?

#define I2S_BUFFER_SIZE                     16U

#define DMA_PDM_TX                          DMAREQ_SSP0_TX
#define DMA_PDM_RX                          SSP0_RX_DMA

#define PDM_SPI                             LPC_SSP0

/*
 * Local Data
 */
static uint16_t incoming_buffer_a[I2S_BUFFER_SIZE];
static uint16_t incoming_buffer_b[I2S_BUFFER_SIZE];


// we're going to set them both up to ping-pong.
static DMA_CHDESC_T dmaTXDesc[2] __attribute__ ((aligned(16)));
static DMA_CHDESC_T dmaRXDesc[2] __attribute__ ((aligned(16)));


callback_pointer_t callback_ptr;

/*
 * Global Function Definitions
 */
void DMA_IRQHandler(void)
{
    Chip_DMA_ClearActiveIntAChannel(LPC_DMA, DMA_PDM_TX);
    Chip_DMA_ClearActiveIntAChannel(LPC_DMA, DMA_PDM_RX);
    if (callback_ptr)
    {
        callback_ptr(incoming_buffer_a);
    }

     /* Rrror interrupt on channel 0? */
     if ((Chip_DMA_GetIntStatus(LPC_DMA) & DMA_INTSTAT_ACTIVEERRINT) != 0)
     {
          /* This shouldn't happen for this simple DMA example, so set the LED
             to indicate an error occurred. This is the correct method to clear
             an abort. */
          Chip_DMA_DisableChannel(LPC_DMA, DMA_PDM_TX);
          Chip_DMA_AbortChannel(LPC_DMA, DMA_PDM_TX);
          Chip_DMA_ClearErrorIntChannel(LPC_DMA, DMA_PDM_TX);
          Chip_DMA_EnableChannel(LPC_DMA, DMA_PDM_TX);

     }
}


void i2s_init(callback_pointer_t callback)
{
    DMA_CHDESC_T Initial_DMA_RX_Descriptor;
    DMA_CHDESC_T Initial_DMA_TX_Descriptor;
    unsigned int i;

    callback_ptr = callback;

    // we have no select line
    Chip_IOCON_PinMuxSet(LPC_IOCON, SPI_SCK_PORT,  SPI_SCK_PIN,   SPI_SCK_FUNCT  | IOCON_MODE_INACT | IOCON_DIGMODE_EN);
    Chip_IOCON_PinMuxSet(LPC_IOCON, SPI_MOSI_PORT, SPI_MOSI_PIN,  SPI_MOSI_FUNCT | IOCON_MODE_INACT | IOCON_DIGMODE_EN);
    Chip_IOCON_PinMuxSet(LPC_IOCON, SPI_MISO_PORT, SPI_MISO_PIN,  SPI_MISO_FUNCT | IOCON_MODE_INACT | IOCON_DIGMODE_EN);


    // setup some markers to see what gets transmitted, and if anything is received.
    for (i = 0; i < I2S_BUFFER_SIZE; i++)
    {
        incoming_buffer_a[i] = 0x6000 + 0x100 * i + i;
        incoming_buffer_b[i] = 0xB000 + 0x100 * i + i;
    }

    // setup SPI
    Chip_SSP_Init(PDM_SPI);
    Chip_SSP_SetFormat(PDM_SPI, SSP_BITS_16, SSP_FRAMEFORMAT_SPI, SSP_CLOCK_MODE0);
    Chip_SSP_Set_Mode(PDM_SPI, SSP_MODE_MASTER);
    Chip_SSP_SetBitRate(PDM_SPI, PDM_BITRATE);
    PDM_SPI->CR1 = 3;       // added to set some mysterious bits that are

                            // in the manual, but not in the driver code.
                            // it didn't help. (or hurt)
    Chip_SSP_Enable(PDM_SPI);

    // turn on the DMA
     Chip_DMA_Init(LPC_DMA);
    Chip_DMA_Enable(LPC_DMA);
    Chip_DMA_SetSRAMBase(LPC_DMA, DMA_ADDR(Chip_DMA_Table));
    NVIC_SetPriority(DMA_IRQ_NUM, DMA_INTERRUPT_PRIORITY);
    Chip_DMA_ClearActiveIntAChannel(LPC_DMA, DMA_PDM_TX);
    Chip_DMA_ClearActiveIntAChannel(LPC_DMA, DMA_PDM_RX);


    // first the receiver
    dmaRXDesc[0].source  = DMA_ADDR(&PDM_SPI->DR);
    dmaRXDesc[0].dest    = DMA_ADDR(incoming_buffer_a + sizeof(incoming_buffer_a) - 1);
    dmaRXDesc[0].next    = DMA_ADDR(&dmaRXDesc[1]);
    dmaRXDesc[0].xfercfg = DMA_XFERCFG_CFGVALID  | DMA_XFERCFG_SETINTA  |
                           DMA_XFERCFG_SRCINC_0  | DMA_XFERCFG_DSTINC_1 |
                           DMA_XFERCFG_WIDTH_16  | DMA_XFERCFG_RELOAD   |
                           DMA_CFG_CHPRIORITY(2) | DMA_XFERCFG_XFERCOUNT(I2S_BUFFER_SIZE);

    dmaRXDesc[1].source  = DMA_ADDR(&PDM_SPI->DR);
    dmaRXDesc[1].dest    = DMA_ADDR(incoming_buffer_b + sizeof(incoming_buffer_b) - 1);
    dmaRXDesc[1].next    = DMA_ADDR(&dmaRXDesc[0]);
    dmaRXDesc[1].xfercfg = dmaRXDesc[0].xfercfg;
    // I honestly don't understand the point of this, but I'm following the example.
    Initial_DMA_RX_Descriptor.source  = dmaRXDesc[0].source;
    Initial_DMA_RX_Descriptor.dest    = dmaRXDesc[0].dest;
    Initial_DMA_RX_Descriptor.next    = dmaRXDesc[0].next;
    Initial_DMA_RX_Descriptor.xfercfg = dmaRXDesc[0].xfercfg;

    Chip_DMA_EnableChannel(LPC_DMA, DMA_PDM_RX);
    Chip_DMA_EnableIntChannel(LPC_DMA, DMA_PDM_RX);

/* Chip_DMA_SetupChannelConfig() seems to either do nothing, or in some cases break the process */
    //Chip_DMA_SetupChannelConfig(LPC_DMA, DMA_PDM_RX,  DMA_CFG_HWTRIGEN      | DMA_CFG_TRIGBURST_BURST |
    //                                                  DMA_CFG_TRIGTYPE_EDGE | DMA_CFG_TRIGPOL_LOW     |
    //                                                  DMA_CFG_BURSTPOWER_1  | DMA_CFG_CHPRIORITY(0));
    Chip_DMA_SetupTranChannel(LPC_DMA, DMA_PDM_RX, &Initial_DMA_RX_Descriptor);
    Chip_DMA_SetupChannelTransfer(LPC_DMA, DMA_PDM_RX, dmaRXDesc[0].xfercfg);
    Chip_DMA_SetValidChannel(LPC_DMA, DMA_PDM_RX);


    // now the transmitter
    dmaTXDesc[0].source  = DMA_ADDR(incoming_buffer_a + I2S_BUFFER_SIZE - 1);
    dmaTXDesc[0].dest    = DMA_ADDR(&PDM_SPI->DR);
    dmaTXDesc[0].next    = DMA_ADDR(&dmaTXDesc[1]);
    dmaTXDesc[0].xfercfg = DMA_XFERCFG_CFGVALID  | DMA_XFERCFG_SETINTA  |
                           DMA_XFERCFG_SRCINC_1  | DMA_XFERCFG_DSTINC_0 |
                           DMA_XFERCFG_WIDTH_16  | DMA_XFERCFG_RELOAD   |
                           DMA_CFG_CHPRIORITY(1) | DMA_XFERCFG_XFERCOUNT(I2S_BUFFER_SIZE);

    dmaTXDesc[1].source  = DMA_ADDR(incoming_buffer_a + I2S_BUFFER_SIZE - 1);
    dmaTXDesc[1].dest    = DMA_ADDR(&PDM_SPI->DR);
    dmaTXDesc[1].next    = DMA_ADDR(&dmaTXDesc[0]);
    dmaTXDesc[1].xfercfg = dmaTXDesc[0].xfercfg;

    Initial_DMA_TX_Descriptor.source  = dmaTXDesc[0].source;
    Initial_DMA_TX_Descriptor.dest    = dmaTXDesc[0].dest;
    Initial_DMA_TX_Descriptor.next    = dmaTXDesc[0].next;
    Initial_DMA_TX_Descriptor.xfercfg = dmaTXDesc[0].xfercfg;

    Chip_DMA_EnableChannel(LPC_DMA, DMA_PDM_TX);
    Chip_DMA_DisableIntChannel(LPC_DMA, DMA_PDM_TX);    // We don't want a transmitter DMA interrupt.
    /* Enabling either of the following lines will break the transmitter. Why ? I don't care. */
    //Chip_DMA_SetupChannelConfig(LPC_DMA, DMA_PDM_TX, (DMA_XFERCFG_CFGVALID | DMA_XFERCFG_RELOAD | DMA_XFERCFG_SETINTA | DMA_XFERCFG_WIDTH_16 | DMA_XFERCFG_SRCINC_0 | DMA_XFERCFG_DSTINC_1 | DMA_XFERCFG_XFERCOUNT(128)));
    //Chip_DMA_SetupChannelConfig(LPC_DMA, DMA_PDM_TX, (DMA_CFG_PERIPHREQEN | DMA_CFG_TRIGBURST_SNGL | DMA_CFG_CHPRIORITY(3)));
    Chip_DMA_SetupTranChannel(LPC_DMA, DMA_PDM_TX, &Initial_DMA_TX_Descriptor);
    Chip_DMA_SetupChannelTransfer(LPC_DMA, DMA_PDM_TX, dmaTXDesc[0].xfercfg);
    Chip_DMA_SetValidChannel(LPC_DMA, DMA_PDM_TX);

    /* Enable the DMA IRQ */
    NVIC_EnableIRQ(DMA_IRQn);

    // making this call causes the tx to stop, and an endless IRQ loop.
    //Chip_DMA_SWTriggerChannel(LPC_DMA, DMA_PDM_RX);
    // This call is essential
    Chip_DMA_SetTrigChannel(LPC_DMA, DMA_PDM_TX);
}

Labels (2)
Tags (2)
0 Kudos
1 Solution
77 Views
Contributor IV

Just in case some someone trying to do I2S on these chips ever stumbles across this... here's the code I got working.

View solution in original post

0 Kudos
9 Replies
78 Views
Contributor IV

Just in case some someone trying to do I2S on these chips ever stumbles across this... here's the code I got working.

View solution in original post

0 Kudos
333 Views
Contributor IV

OK, I've actually got this running, through a lot of trial and error.

It seems there's two essential things:

    Chip_DMA_SetupChannelConfig(LPC_DMA, DMAREQ_SSP0_TX, (DMA_CFG_PERIPHREQEN |DMA_CFG_SRCBURSTWRAP));

then

    Chip_DMA_SWTriggerChannel(LPC_DMA, DMAREQ_SSP0_TX);

I'm not sure about the xfercfg portion, but it seems to work.

There's also some errors in my original code that could cause hard faults, so in case someone else has this problem, I'm posting the receive setup portion.

// setup SPI

Chip_SSP_Init(PDM_SPI);

Chip_SSP_SetFormat(PDM_SPI, SSP_BITS_16, SSP_FRAMEFORMAT_SPI, SSP_CLOCK_MODE0);

Chip_SSP_Set_Mode(PDM_SPI, SSP_MODE_MASTER);

Chip_SSP_SetBitRate(PDM_SPI, PDM_BITRATE);

PDM_SPI->DMACR = 3; // added to set some mysterious bits that are in the manual,

// but not in the driver code.  it didn't help.

Chip_SSP_Enable(PDM_SPI);

// turn on the DMA

Chip_DMA_Init(LPC_DMA);

Chip_DMA_Enable(LPC_DMA);

Chip_DMA_SetSRAMBase(LPC_DMA, DMA_ADDR(Chip_DMA_Table));

NVIC_SetPriority(DMA_IRQ_NUM, DMA_INTERRUPT_PRIORITY); Chip_DMA_ClearActiveIntAChannel(LPC_DMA, DMA_PDM_TX); Chip_DMA_ClearActiveIntAChannel(LPC_DMA, DMA_PDM_RX);

// first the receiver

dmaRXDesc[0].source = DMA_ADDR(&PDM_SPI->DR);

dmaRXDesc[0].dest = DMA_ADDR(incoming_buffer_a + I2S_BUFFER_SIZE - 1);

dmaRXDesc[0].next = DMA_ADDR(&dmaRXDesc[1]);

dmaRXDesc[0].xfercfg = DMA_XFERCFG_CFGVALID | DMA_XFERCFG_SETINTA | DMA_CFG_BURSTPOWER_1  |

                       DMA_XFERCFG_SRCINC_0 | DMA_XFERCFG_DSTINC_1 | DMA_CFG_SRCBURSTWRAP |

                       DMA_XFERCFG_WIDTH_16 | DMA_XFERCFG_RELOAD | DMA_CFG_PERIPHREQEN    |

                       DMA_CFG_CHPRIORITY(2)| DMA_XFERCFG_XFERCOUNT(I2S_BUFFER_SIZE);

dmaRXDesc[1].source = DMA_ADDR(&PDM_SPI->DR);

dmaRXDesc[1].dest = DMA_ADDR(incoming_buffer_b + I2S_BUFFER_SIZE - 1);

dmaRXDesc[1].next = DMA_ADDR(&dmaRXDesc[0]);

dmaRXDesc[1].xfercfg = dmaRXDesc[0].xfercfg;

// I honestly don't understand the point of this, but I'm following the example.

Initial_DMA_RX_Descriptor.source = dmaRXDesc[0].source;

Initial_DMA_RX_Descriptor.dest = dmaRXDesc[0].dest; Initial_DMA_RX_Descriptor.next = dmaRXDesc[0].next;

Initial_DMA_RX_Descriptor.xfercfg = dmaRXDesc[0].xfercfg;

Chip_DMA_EnableChannel(LPC_DMA, DMA_PDM_RX); Chip_DMA_EnableIntChannel(LPC_DMA, DMA_PDM_RX);

Chip_DMA_SetupChannelConfig(LPC_DMA, DMA_PDM_RX, (DMA_CFG_PERIPHREQEN |DMA_CFG_SRCBURSTWRAP));

Chip_DMA_SetupTranChannel(LPC_DMA, DMA_PDM_RX, &Initial_DMA_RX_Descriptor);

Chip_DMA_SetupChannelTransfer(LPC_DMA, DMA_PDM_RX, dmaRXDesc[0].xfercfg);

Chip_DMA_SetValidChannel(LPC_DMA, DMA_PDM_RX);

/* Enable the DMA IRQ */

NVIC_EnableIRQ(DMA_IRQn);

Chip_DMA_SWTriggerChannel(LPC_DMA, DMA_PDM_RX);

0 Kudos
333 Views
Contributor IV

After running, I paused to check the registers.

BUSY0 is 0x2 (transmitting, not receiving)

No active interrupts.

INTA0 is 0x2 (set for receive)

ERRINT0 is 0

ACTIVE0 is 0x3

CFG0 is 0x4041

CLSTAT0 is 0x0001

No data was ever transferred into the buffer.

0 Kudos
333 Views
Contributor IV

Adding DMA_CFG_PERIPHREQEN doesn't seem to do anything. I get maybe one word stored. The only way to get it store anything and issue an interrupt is by calling Chip_DMA_SWTriggerChannel(), which isn't what I want.

EDIT: The LPCopen11U68 has no example for SSP and DMA. There's a UART & DMA, which is exactly what I based my code on.

0 Kudos
333 Views
NXP TechSupport
NXP TechSupport

Hello Chris,

There isn't a DMA+SSP demo exactly for LPC11u68, while you can refer to the demo under the thread:

https://community.nxp.com/message/960058?commentID=960058#comment-960058      

0 Kudos
333 Views
Contributor IV

So, I'm asking about a working with a low end M0+ MCU, but you're pointing me to an example for a high end M4?!

The drivers for these chips aren't even close.

So far, you've told me (1) it's easy, (2) read the manual, (3) look at a completely unrelated example.

0 Kudos
333 Views
NXP TechSupport
NXP TechSupport

Hello Chris Pflieger,

Do you meaning DMA Ping-Pong mode?

If yes,  recommend you first refer to Jeremy's demo make sure the DMA memory to memory  in Ping-Pong mode

can work well on your side. Then can easy add I2S transmition data.


Have a great day,
TIC

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos
333 Views
Contributor IV

"Then can easy add I2S transmition data."

If it was that easy, then I wouldn't still be working on it after two weeks.

It's running transfers, but it seems to me that I'm missing something - like how the SSP peripheral triggers the DMA to initiate a move. Perhaps the transmitter is just blasting data in the data register, regardless of whether it's ready or not. Perhaps the SSP receiver getting a byte isn't triggering the DMA to move that received byte.

Like, should DMA_CFG_PERIPHREQEN play a part in this?

I tried:

Chip_DMA_SetupChannelConfig(LPC_DMA, DMA_PDM_RX, DMA_CFG_PERIPHREQEN | DMA_CFG_TRIGBURST_SNGL);

But that doesn't seem to make a difference.

0 Kudos
333 Views
NXP TechSupport
NXP TechSupport

Hello Chris,

If you want to "it just needs to be continuous", how abut use Burst transfer mode:

pastedImage_1.png

The following example show how to configure the channel for peripheral
 *            DMA requests, burst transfer size of 1 (in 'transfers', not bytes),
 *            continuous reading of the same source address, incrementing destination
 *            address, and highest channel priority.<br>
 *            Example: Chip_DMA_SetupChannelConfig(pDMA, SSP0_RX_DMA,
 *                (DMA_CFG_PERIPHREQEN | DMA_CFG_TRIGBURST_BURST | DMA_CFG_BURSTPOWER_1 |
 *                DMA_CFG_SRCBURSTWRAP | DMA_CFG_CHPRIORITY(0)));

About detail you can check UM 10732 and DMA demo under LPCopen.

Regards,

TIC

0 Kudos