Quantcast
Channel: MSP low-power microcontroller forum - Recent Threads
Viewing all articles
Browse latest Browse all 22733

DMA SPI - Why can't I use DMA channels other than CH0 for Tx and CH1 for Rx

$
0
0

Hi all,

I'm been struggling with SPI over DMA.  
In my hardware I have EUSCI port B0 connected as my SPI port, and I'd like to use DMA channel 2 for SPI Tx and DMA channel 3 for SPI Rx.
(I can't use Ch0 for Tx and Ch1 for Rx in my final code because I'm using EUSCI A0 for a UART and A0 can only be mapped to CH0/1)

Now, in the following code, everything works fine if I map B0 to Ch0/1, but it I map to any other channels the code just doesn't work.

If I don't put the SPI Tx onto CH0, I never see any data come out the SPI port (and consequently there is no data recieved either)
If I don't put the SPI Rx onto CH1, the DMA Rx interrupt never fires.

Why can't I use different DMA channels for this port?  What do I need to do differently?



As an additional question, why does every DMA example I come across use the line

#pragma DATA_ALIGN(controlTable, 256)

but the MSP432 Peripheral Driver Library users guide says that controlTable needs to be aligned on a 1024 byte boundary??



// ---------------------------------------------------------------------------
// Don't forget to insert dma_[1,2,3]_interrupt() into your interrupt mapping
// (e.g. in your startup_mspXXX.c file)

// ---------------------------------------------------------------------------
// INCLUDES
// ---------------------------------------------------------------------------
#include "driverlib.h"

#include <stdint.h>
#include <string.h>
#include <stdbool.h>

// ---------------------------------------------------------------------------
// DEFINES
// ---------------------------------------------------------------------------
/* DMA Control Table */
#ifdef ewarm
#pragma data_alignment=256
#else
// QUESTION -> MSP432 Peripheral Driver Library User's Guide (4Nov2015)
//             Page 101 says controlTable should be aligned on 1024 byte
//             boundary, but all code examples I can find set it to 256.
#pragma DATA_ALIGN(controlTable, 256)
#endif


// QUESTION -> If using TX and RX, does the RX have to be on TX channel + 1???

// QUESTION -> Why does TX never start if not using DMA CH 0 ??
#define DMA_TX_MAPPING DMA_CH0_EUSCIB0TX0
//#define DMA_TX_MAPPING DMA_CH2_EUSCIB0TX1
//#define DMA_TX_MAPPING DMA_CH4_EUSCIB0TX2
//#define DMA_TX_MAPPING DMA_CH6_EUSCIB0TX3

#define DMA_TX_CHANNEL (DMA_TX_MAPPING & 0x0f)    // This matches what is done in many places in mspware dma.c.  LSNibble of channel mapping macro is the channel number

// QUESTION -> Why does RX never complete (interrupt doesn't fire) if not using DMA CH1 ???
#define DMA_RX_MAPPING  DMA_CH1_EUSCIB0RX0
//#define DMA_RX_MAPPING  DMA_CH3_EUSCIB0RX1
//#define DMA_RX_MAPPING  DMA_CH5_EUSCIB0RX2
//#define DMA_RX_MAPPING  DMA_CH6_EUSCIB0TX3

#define DMA_RX_CHANNEL (DMA_RX_MAPPING & 0x0f)

// Interrupt assignment seems to make no difference to above problems
#define DMA_INT_ASSIGNMENT  DMA_INT1   // DMA_INTx == INT_DMA_INTx

#define SPI_PORT      EUSCI_B0_BASE    // One of EUSCI_xx_BASE
#define SPI_nCS_PORT  GPIO_PORT_P4
#define SPI_nCS_PIN   GPIO_PIN6

// ---------------------------------------------------------------------------
// TYPEDEFS
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// GLOBALS
// ---------------------------------------------------------------------------
uint8_t controlTable[256];

/* SPI Master Configuration Parameter */
const eUSCI_SPI_MasterConfig spiMasterConfig =
{
        EUSCI_SPI_CLOCKSOURCE_SMCLK,
        12000000,
        2000000,
        EUSCI_A_SPI_MSB_FIRST,
        EUSCI_A_SPI_PHASE_DATA_CHANGED_ONFIRST_CAPTURED_ON_NEXT,
        EUSCI_A_SPI_CLOCKPOLARITY_INACTIVITY_HIGH,
        EUSCI_A_SPI_3PIN
};

// ---------------------------------------------------------------------------
// PROTOTYPES
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// IMPLEMENTATION
// ---------------------------------------------------------------------------
int main(void)
{
  uint8_t tx_data_array[6] = {3,4,5,6,7,8};
  uint8_t rx_data_array[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

  /* Halting Watchdog */
  MAP_WDT_A_holdTimer();
  MAP_Interrupt_enableMaster();

  // Setup up clocks
  MAP_CS_setReferenceOscillatorFrequency(CS_REFO_32KHZ);
  MAP_CS_initClockSignal(CS_MCLK,   CS_MODOSC_SELECT,  CS_CLOCK_DIVIDER_1);  //24MHz
  MAP_CS_initClockSignal(CS_HSMCLK, CS_MODOSC_SELECT,  CS_CLOCK_DIVIDER_1);  //24MHz
  MAP_CS_initClockSignal(CS_SMCLK,  CS_MODOSC_SELECT,  CS_CLOCK_DIVIDER_2);  //12MHz
  MAP_CS_initClockSignal(CS_ACLK,   CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1);  //32kHz
  MAP_CS_initClockSignal(CS_BCLK,   CS_REFOCLK_SELECT, CS_CLOCK_DIVIDER_1);  //32kHz

  // Setup pins for SPI port B0
  MAP_GPIO_setAsPeripheralModuleFunctionOutputPin(GPIO_PORT_P1,
                                                  GPIO_PIN5 | GPIO_PIN6 | GPIO_PIN7,
                                                  GPIO_PRIMARY_MODULE_FUNCTION);

  // Setup my nCS pin
  MAP_GPIO_setOutputHighOnPin(SPI_nCS_PORT, SPI_nCS_PIN);
  MAP_GPIO_setDriveStrengthLow(SPI_nCS_PORT, SPI_nCS_PIN);
  MAP_GPIO_setAsOutputPin(SPI_nCS_PORT, SPI_nCS_PIN);

  // Setup SPI
  MAP_SPI_initMaster(SPI_PORT, &spiMasterConfig);
  MAP_SPI_enableModule(SPI_PORT);

  // Setup DMA
  MAP_DMA_enableModule();
  MAP_DMA_setControlBase(controlTable);

  MAP_DMA_assignChannel(DMA_TX_MAPPING);
  MAP_DMA_assignChannel(DMA_RX_MAPPING);

  MAP_DMA_disableChannelAttribute(DMA_TX_CHANNEL,
                                    UDMA_ATTR_ALTSELECT     |
                                    UDMA_ATTR_USEBURST      |
                                    UDMA_ATTR_HIGH_PRIORITY |
                                    UDMA_ATTR_REQMASK);

  MAP_DMA_disableChannelAttribute(DMA_RX_CHANNEL,
                                    UDMA_ATTR_ALTSELECT     |
                                    UDMA_ATTR_USEBURST      |
                                    UDMA_ATTR_HIGH_PRIORITY |
                                    UDMA_ATTR_REQMASK);

  MAP_DMA_setChannelControl(UDMA_PRI_SELECT | DMA_TX_CHANNEL,
                              UDMA_SIZE_8       |
                              UDMA_SRC_INC_8    |
                              UDMA_DST_INC_NONE |
                              UDMA_ARB_1);

  MAP_DMA_setChannelControl(UDMA_PRI_SELECT | DMA_RX_CHANNEL,
                              UDMA_SIZE_8       |
                              UDMA_SRC_INC_NONE |
                              UDMA_DST_INC_8    |
                              UDMA_ARB_1);

  MAP_DMA_setChannelTransfer(UDMA_PRI_SELECT | DMA_TX_CHANNEL,
                              UDMA_MODE_BASIC,
                              tx_data_array,
                              (void*) MAP_SPI_getTransmitBufferAddressForDMA(SPI_PORT),
                              6);

  MAP_DMA_setChannelTransfer(UDMA_PRI_SELECT | DMA_RX_CHANNEL,
                              UDMA_MODE_BASIC,
                              (void*) MAP_SPI_getReceiveBufferAddressForDMA(SPI_PORT),
                              rx_data_array,
                              6);

  // We want to get an interrupt when RX is done, which is when the SPI
  // operation is fully complete
  if (DMA_INT_ASSIGNMENT != DMA_INT0) // DMA channels are mapped to Int0 by default
  {
    MAP_DMA_assignInterrupt(DMA_INT_ASSIGNMENT, DMA_RX_CHANNEL);
  }

  // DMA is ready to go

  // Check SPI is ready to go
  // SPI TX DMA triggers when it sees the transmit interrupt for the SPI Port high
  // (i.e. ready to transmit)
  while (!(MAP_SPI_getInterruptStatus(SPI_PORT, EUSCI_SPI_TRANSMIT_INTERRUPT)));

  // Pull nCS low.
  MAP_GPIO_setOutputLowOnPin(SPI_nCS_PORT, SPI_nCS_PIN);

  // Enable the RX channel first so that is ready to receive chars once we start TXing
  MAP_DMA_enableChannel(DMA_RX_CHANNEL);    // This also does a DMA_enableInterrupt()
  MAP_DMA_enableChannel(DMA_TX_CHANNEL);    // This also does a DMA_enableInterrupt()
  MAP_Interrupt_enableInterrupt(DMA_INT_ASSIGNMENT);

  MAP_PCM_gotoLPM0InterruptSafe();

  while(1);
}

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void dma_main_interrupt(uint32_t interrupt_u32)
{
  // No flags to clear!

  uint32_t dma_mode_u32;

  dma_mode_u32 = MAP_DMA_getChannelMode(DMA_TX_CHANNEL | UDMA_PRI_SELECT);
  if (dma_mode_u32 == UDMA_MODE_STOP)
  {
    MAP_DMA_disableChannel(DMA_TX_CHANNEL);
    MAP_DMA_disableChannel(DMA_RX_CHANNEL);
    MAP_DMA_disableInterrupt(interrupt_u32);
    GPIO_setOutputHighOnPin(SPI_nCS_PORT, SPI_nCS_PIN);
  }
}

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void dma_0_interrupt(void)
{
  // We are going to get both Rx and Tx interrupts because
  // all DMA channels are mapped to DMA0 by default.

  // We really only want to handle RX interrupts

  uint32_t status_u32;
  uint8_t  dma_ch_u8;
  uint32_t dma_ch_mask_u32 = 1;

  status_u32 = MAP_DMA_getInterruptStatus();

  for (dma_ch_u8 = DMA_CHANNEL_0; dma_ch_u8 <= DMA_CHANNEL_7; dma_ch_u8++)
  {
    // status_u32 is a bit mask
    if (status_u32 & dma_ch_mask_u32)
    {
      // clearInterruptFlag is a single channel number
      MAP_DMA_clearInterruptFlag(dma_ch_u8);
      if (dma_ch_u8 == DMA_RX_CHANNEL) dma_main_interrupt(INT_DMA_INT0);
    }
    dma_ch_mask_u32 = dma_ch_mask_u32 << 1;
  }
}

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void dma_1_interrupt(void)
{
  // No flags to clear!
  dma_main_interrupt(INT_DMA_INT1);
}

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void dma_2_interrupt(void)
{
  // No flags to clear!
  dma_main_interrupt(INT_DMA_INT2);
}

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
void dma_3_interrupt(void)
{
  // No flags to clear!
  dma_main_interrupt(INT_DMA_INT3);
}


Viewing all articles
Browse latest Browse all 22733

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>