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.
Greetings,
Prior to posting I have searched the forums for a solution to my problem and tried everything but was unable to get the GSPI module working with DMA transfer enable on the TX channel only.
For my application I need to send data over SPI to an OLED/LCD display module.
I have authored similar drivers for both msp430 and cc2538 platform and it's working fine, so now I needed to port to the cc3200 platform.
However, I am unable to get anything on the SPI pins, there is no Clock output and no data outpot on MOSI pin.
This happens only when I use DMA, when I use simple SPI transfers the code works fine.
For the record I need to control the CS manually, that's why I don't set it as a peripheral pin, same goes for the MISO/RX pin.
This is my SPI/uDMA initialization code.
#define SPI_BIT_RATE 8000000
#define OLED_SYSCTL_PERIPH_SPI PRCM_GSPI
#define OLED_SPI_BASE GSPI_BASE
#define OLED_SPI_CLK_PIN PIN_05
#define OLED_SPI_TX_PIN PIN_07
#define OLED_CS_PORT_BASE GPIOA1_BASE
#define OLED_SPI_CS_PIN_NUM GPIO_PIN_4
#define OLED_SPI_CS_PIN PIN_03
#define OLED_RS_PORT_BASE GPIOA1_BASE
#define OLED_SPI_RS_PIN_NUM GPIO_PIN_5
#define OLED_SPI_RS_PIN PIN_04
#define OLED_RST_PORT_BASE GPIOA1_BASE
#define OLED_SPI_RST_PIN_NUM GPIO_PIN_7
#define OLED_SPI_RST_PIN PIN_06
#define OLED_SPI_UDMA_TX_CHANNEL UDMA_CH31_GSPI_TX
#define OLED_SPI_UDMA_STRUCT UDMA_PRI_SELECT
#define DMA_BLOCK 1024
void OLED_SPI_INIT()
{
MAP_PRCMPeripheralClkEnable(OLED_SYSCTL_PERIPH_SPI, PRCM_RUN_MODE_CLK);
MAP_PinTypeSPI(OLED_SPI_CLK_PIN, PIN_MODE_7);
MAP_PinTypeSPI(OLED_SPI_TX_PIN, PIN_MODE_7);
MAP_PinTypeGPIO(OLED_SPI_CS_PIN, PIN_MODE_0, false);
MAP_GPIODirModeSet(OLED_CS_PORT_BASE, OLED_SPI_CS_PIN_NUM, GPIO_DIR_MODE_OUT);
MAP_GPIOPinWrite(OLED_CS_PORT_BASE, OLED_SPI_CS_PIN_NUM, OLED_SPI_CS_PIN_NUM);
MAP_PinTypeGPIO(OLED_SPI_RS_PIN, PIN_MODE_0, false);
MAP_GPIODirModeSet(OLED_RS_PORT_BASE, OLED_SPI_RS_PIN_NUM, GPIO_DIR_MODE_OUT);
MAP_GPIOPinWrite(OLED_RS_PORT_BASE, OLED_SPI_RS_PIN_NUM, OLED_SPI_RS_PIN_NUM);
MAP_PinTypeGPIO(OLED_SPI_RST_PIN, PIN_MODE_0, false);
MAP_GPIODirModeSet(OLED_RST_PORT_BASE, OLED_SPI_RST_PIN_NUM, GPIO_DIR_MODE_OUT);
MAP_GPIOPinWrite(OLED_RST_PORT_BASE, OLED_SPI_RST_PIN_NUM, OLED_SPI_RST_PIN_NUM);
MAP_UtilsDelay(PRCMPeripheralClockGet(PRCM_GPIOA0)/3000);
MAP_PRCMPeripheralReset(OLED_SYSCTL_PERIPH_SPI);
MAP_SPIReset(OLED_SPI_BASE);
UDMAInit();
MAP_SPIConfigSetExpClk(OLED_SPI_BASE,MAP_PRCMPeripheralClockGet(OLED_SYSCTL_PERIPH_SPI),
SPI_BIT_RATE,SPI_MODE_MASTER,SPI_SUB_MODE_0,(SPI_3PIN_MODE | SPI_TURBO_OFF | SPI_WL_8));
MAP_SPIEnable(OLED_SPI_BASE);
}
When transferring a single byte I use the function below. The StartAndCompleteTransfer is take from the uDMA example of the cc3200 SDK as is.
void OLED_SPI_WRITE(unsigned char out)
{
MAP_SPIDmaDisable(OLED_SPI_BASE, SPI_TX_DMA);
MAP_uDMAChannelDisable((OLED_SPI_UDMA_TX_CHANNEL|OLED_SPI_UDMA_STRUCT));
SetupTransfer((OLED_SPI_UDMA_TX_CHANNEL|OLED_SPI_UDMA_STRUCT), UDMA_MODE_BASIC, 1, UDMA_SIZE_8, UDMA_ARB_1,&out, UDMA_SRC_INC_8, (void *)(GSPI_BASE + MCSPI_O_RX0), UDMA_DST_INC_NONE);
MAP_SPIWordCountSet(OLED_SPI_BASE, 1);
MAP_SPIDmaEnable(OLED_SPI_BASE, SPI_TX_DMA);
StartAndCompleteSWTransfer((OLED_SPI_UDMA_TX_CHANNEL|OLED_SPI_UDMA_STRUCT));
MAP_uDMAChannelDisable((OLED_SPI_UDMA_TX_CHANNEL|OLED_SPI_UDMA_STRUCT));
}
This is the function I use when I want to transfer large data blocks (over DMA_BLOCK size which is 1024 bytes)
void OLED_SPI_WRITE_MORE_CHUNK(unsigned char* data, unsigned long len)
{
unsigned long sbytes = 0;
unsigned long ind = 0;
if(len > DMA_BLOCK)
sbytes = DMA_BLOCK;
else
sbytes = len;
while (len > 0)
{
memcpy(&oled_tx_buffer[0],&data[ind],sbytes);
MAP_SPIDmaDisable(OLED_SPI_BASE, SPI_TX_DMA);
MAP_uDMAChannelDisable((OLED_SPI_UDMA_TX_CHANNEL|OLED_SPI_UDMA_STRUCT));
SetupTransfer((OLED_SPI_UDMA_TX_CHANNEL|OLED_SPI_UDMA_STRUCT), UDMA_MODE_BASIC, sbytes,
UDMA_SIZE_8, UDMA_ARB_1,(void *)&oled_tx_buffer[0], UDMA_SRC_INC_8,
(void *)(GSPI_BASE + MCSPI_O_RX0), UDMA_DST_INC_NONE);
MAP_SPIWordCountSet(OLED_SPI_BASE, sbytes);
MAP_SPIDmaEnable(OLED_SPI_BASE, SPI_TX_DMA);
StartAndCompleteSWTransfer((OLED_SPI_UDMA_TX_CHANNEL|OLED_SPI_UDMA_STRUCT));
len-=sbytes;
ind+=sbytes;
if(len >= DMA_BLOCK)
sbytes = DMA_BLOCK;
else
sbytes = len;
}
}
What am I doing wrong and get no outpun on CLK and TX pin ?
Thank you in advance for any assistance.
Regards,
Pavlos
Hi Pavlos,
You may refer to the cc3200-sdk\simplelink\cc_pal.c or cc3200-sdk\middleware\driver\spi_drv.c for some working references of the SPI with DMA. These are checked with the internal SPI but since the IP is same should be a good reference for GSPI as well.
Best regards,
Naveen
Hello again,
I checked the suggested source files.
The spi_drv.c does not help much as I am not using an RTOS. The cc_pal.c file as far as I can see does the uDMA/GSPI setup similar to my code.
I attempted to use the same order in function calls but no luck. I have checked the documentation on the uDMA peripheral and the UART - uDMA example and tried everything I can think of but still nothing.
I there a specific order on how to setup uDMA for SPI transfers in terms of function calls ?
What I have noticed in my code , when I enable the SPI_DMA_TX interrupt, is that the interrupt is asserted even before I use the MAP_uDMAChannelRequest() to start the transfer. Once the channel request is sent I set a break-point to the interrupt handler and it seems like even though I clear the interrupt it gets asserted continuously, without request the next transfer.
In my example I have enable WiFi using simplelink and after that I attempt to initialize the OLED driver and display something on the screen. Could there be a conflict in the uDMA setup when using simplelink ?
Nevertheless, as far as you can see in my code, is there something wrong in the order functions are called ?
P.S. I have added the below calls to my init function and use the MAP_SPIWordCountSet(OLED_SPI_BASE,1); function prior to requesting a 1 byte channel transfer.
MAP_SPIFIFOEnable(OLED_SPI_BASE, SPI_TX_FIFO);
MAP_SPIFIFOLevelSet(OLED_SPI_BASE,1,1);
Ok....I found one problem.
I missed the call where I put the transmitted data in the GSPI RX buffer instead of TX...
Once I changed MCSPI_O_RX0 to MCSPI_O_TX0 it works but it only transmits one byte and then nothing.
What am I missing ?
Working a bit more with the SPI interrupt handler and uDMA this is what I cannot figure out.
When using SPI with uDMA, what is the interrupt that tells me that my transfer is complete ?
The SPI_INT_DMATX interrupt when enable gets triggered non-stop and my application does not run even if I clear it.
When I poll the uDMA channel after I request a transfer, the UDMA_MODE_STOP condition is set before the SPI transfer is complete.
When using SPIWordCountSet(GSPI_BASE,size); I never get the SPI_INT_EOW interrupt asserted.
Which approach should I use for my application and which interrupts (if any) should I use ?
Just a follow up.
I tested the code given at this thread : http://e2e.ti.com/support/wireless_connectivity/f/968/t/353790.aspx
Instead of using the uDMA channel on the Slave, I used on Master side TX.
Still getting only one byte transmitted.
And another question, when using DMA and SPI should we transfer the data to the (GSPI_BASE + MCSPI_O_TX0) register or the (GSPI_BASE + MCSPI_O_DAFTX) register ?
Here is the code I use for the Master side uDMA test, teh rest of the code is unchanged with respect to the posted at the above thread.
void MasterMain()
{
unsigned long ulUserData;
unsigned long ulDummy;
//
// Initialize the message
//
memcpy(g_ucTxBuff,MASTER_MSG,sizeof(MASTER_MSG));
//
// Set Tx buffer index
//
ucTxBuffNdx = 0;
ucRxBuffNdx = 0;
//
// Reset SPI
//
MAP_SPIReset(GSPI_BASE);
//
// uDMA Initialization
//
UDMAInit();
Message("Completed DMA Initialization \n\r\n\r");
//
// Configure SPI interface
//
MAP_SPIConfigSetExpClk(GSPI_BASE,MAP_PRCMPeripheralClockGet(PRCM_GSPI),
SPI_IF_BIT_RATE,SPI_MODE_MASTER,SPI_SUB_MODE_0,
(SPI_HW_CTRL_CS |
SPI_4PIN_MODE |
SPI_TURBO_OFF |
SPI_CS_ACTIVELOW |
SPI_WL_8));
MAP_SPIIntRegister(GSPI_BASE,SPIIntHandler);
SetupTransfer(UDMA_CH31_GSPI_TX,UDMA_MODE_BASIC,50,
UDMA_SIZE_8,UDMA_ARB_1,
g_ucTxBuff,UDMA_SRC_INC_8,(void *)(GSPI_BASE + MCSPI_O_TX0),
UDMA_DST_INC_NONE);
SPIDmaEnable(GSPI_BASE, SPI_TX_DMA);
//
// Print mode on uart
//
Message("Enabled SPI Interface in Master Mode\n\r");
//
// User input
//
Report("Press any key to transmit data....");
//
// Read a character from UART terminal
//
ulUserData = MAP_UARTCharGet(UARTA0_BASE);
//
// Enable SPI for communication
//
MAP_SPIIntEnable(GSPI_BASE,SPI_INT_DMATX);
MAP_SPIEnable(GSPI_BASE);
}
void SPIIntHandler()
{
unsigned long ulStatus;
ulStatus = MAP_SPIIntStatus(GSPI_BASE, true);
MAP_SPIIntClear(GSPI_BASE, ulStatus);
}
I would really appreciate your feedback here cause this thing is driving me crazy....
Hi, is there any progress with this?
We are considering using CC3200 for our IoT platform but efficient SPI is absolutely necessary, so if the problems reported above are caused by a hardware bug it will mean we cannot use it.
Let's hope that the problems so far are due to immature datasheet and/or driverlib.
Any new information will be appreciated.
Best,
Giannis
I've successfully used SPI w/ DMA. Here are some portions of my code which may help. I know initially I did have the same issue as you that I'd only get one byte to come out. IIRC the fix was correct interrupt mode selection and transfer length setup.
void spi_init( void ) { MAP_SPIDisable( GSPI_BASE ); MAP_SPIReset( GSPI_BASE ); procClkRate = MAP_PRCMPeripheralClockGet( PRCM_GSPI ); /* configure the SPI port */ MAP_SPIConfigSetExpClk( GSPI_BASE, procClkRate, SPI_CLOCK_HZ, SPI_MODE_MASTER, SPI_SUB_MODE_0, (SPI_WL_8 | SPI_CS_ACTIVELOW | SPI_SW_CTRL_CS | SPI_4PIN_MODE | SPI_TURBO_OFF) ); MAP_uDMAChannelAssign( UDMA_CH6_GSPI_RX ); MAP_uDMAChannelAssign( UDMA_CH7_GSPI_TX ); MAP_uDMAChannelAttributeDisable( UDMA_CH6_GSPI_RX, UDMA_ATTR_ALTSELECT ); MAP_uDMAChannelAttributeDisable( UDMA_CH7_GSPI_TX, UDMA_ATTR_ALTSELECT ); MAP_SPIFIFOEnable( GSPI_BASE, SPI_RX_FIFO ); MAP_SPIFIFOEnable( GSPI_BASE, SPI_TX_FIFO ); MAP_SPIDmaEnable( GSPI_BASE, SPI_RX_DMA ); MAP_SPIDmaEnable( GSPI_BASE, SPI_TX_DMA ); MAP_SPIFIFOLevelSet( GSPI_BASE, 1, 1 ); MAP_IntRegister( INT_GSPI, (void(*)(void))spi_isr ); MAP_IntPrioritySet( INT_GSPI, SPI_ISR_PRIORITY ); MAP_IntEnable( INT_GSPI ); MAP_SPIIntEnable( GSPI_BASE, SPI_INT_EOW ); MAP_SPIEnable( GSPI_BASE ); } void spi_transfer ( const uint8_t* pTxBuff, uint8_t* pRxBuff, uint16_t nNumBytes ) { uint32_t rxIncrement; if( pTxBuff == NULL ) { /* the RX buffer can be null... the TX buffer cannot */ return ; } if( pRxBuff == NULL ) { /* we don't care about the receive data, so dump it to a temp variable */ pRxBuff = &dumpByte; rxIncrement = UDMA_DST_INC_NONE; } else { rxIncrement = UDMA_DST_INC_8; } /* configure the TX DMA channel */ MAP_uDMAChannelControlSet( UDMA_CH7_GSPI_TX, (UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_1) ); MAP_uDMAChannelAttributeEnable( UDMA_CH7_GSPI_TX, UDMA_ATTR_USEBURST ); MAP_uDMAChannelTransferSet( UDMA_CH7_GSPI_TX, UDMA_MODE_BASIC, (void *)pTxBuff, (void *)(GSPI_BASE+MCSPI_O_TX0), nNumBytes ); MAP_uDMAChannelEnable( UDMA_CH7_GSPI_TX ); /* configure the RX DMA channel */ MAP_uDMAChannelControlSet( UDMA_CH6_GSPI_RX, (UDMA_SIZE_8 | UDMA_SRC_INC_NONE | rxIncrement | UDMA_ARB_1) ); MAP_uDMAChannelAttributeEnable( UDMA_CH6_GSPI_RX, UDMA_ATTR_USEBURST ); MAP_uDMAChannelTransferSet( UDMA_CH6_GSPI_RX, UDMA_MODE_BASIC, (void *)(GSPI_BASE+MCSPI_O_RX0), pRxBuff, nNumBytes ); MAP_uDMAChannelEnable( UDMA_CH6_GSPI_RX ); /* * make sure the semaphore is taken * before issuing the transfer */ xSemaphoreTake( spiSema, 0 ); /* prime the FIFO with our transfer size */ MAP_SPIWordCountSet( GSPI_BASE, nNumBytes ); /* asserting chip select triggers the transfer process */ MAP_SPICSEnable( GSPI_BASE ); /* block for the SPI completion */ xSemaphoreTake( spiSema, CONVERT_MS_TO_TICKS(SPI_TRANSFER_TIMEOUT_MS) ); MAP_uDMAChannelDisable( UDMA_CH7_GSPI_TX ); MAP_uDMAChannelDisable( UDMA_CH6_GSPI_RX ); return ; } void spi_isr ( void ) { portBASE_TYPE xYieldRequired = pdFALSE; MAP_SPIIntClear( GSPI_BASE, SPI_INT_EOW ); MAP_SPICSDisable( GSPI_BASE ); if( spiSema ) { /* the DMA transfer is complete */ xSemaphoreGiveFromISR( spiSema, &xYieldRequired ); } portYIELD_FROM_ISR( xYieldRequired ); }
(This is modified a bit to strip out my application specific stuff). I was using an OS (hence the semaphore), but that should be easy to adopt. I was also using the SW controlled chip select. Note that enabling the CS line started the transfer.
Hi Pavlos,
Apologies for delayed response.
You need to use (GSPI_BASE + MCSPI_O_TX0) for DMA transfer.
From the code you posted above, the DMA is configured only for TX side. Since the SPI is full duplex when you do a TX, there is always an RX associated and SPI module expects the host (or DMA) to read out the received data before allowing next transaction.
In your case SPI is waiting for CPU/DMA to read out the received data after transferring first byte before issuing next TX request to the DMA.
Can you pls configure DMA for RX side to read into a dummy variable and try
Thanks and regards,
Praveen