AnsweredHot![SOLVED] PIC32MX250F128D: DMA SPI transfer only with RX buffer's length = 1

Author
molrse
Starting Member
  • Total Posts : 45
  • Reward points : 0
  • Joined: 2016/05/15 11:16:16
  • Location: 0
  • Status: offline
2017/12/06 13:06:50 (permalink)
0

[SOLVED] PIC32MX250F128D: DMA SPI transfer only with RX buffer's length = 1

Hi,

I'm trying to understand the DMA.

The following code works for a SPI using DMA with two channels. It focuses on “TX-only” (OLED-panel) and uses a buffer for the RX-Channel, having the same size as TX-Buffer. (The RX buffer content is actually functional irrelevant)

I take the legacy examples as base of my tests, where DmaTxIntFlag is used as waiting-loop until interrupt is called and set it to “1”.

I focused, in this first example, on the RX block-done event because is needed to wait until the RX channel is done (releasing the SPI) before the loop could start again

This code works:
void init(){
 
// Interrupts
INTCONbits.MVEC = 1;
__builtin_mtc0(12,0,(__builtin_mfc0(12,0) | 0x0001));
 

// SPI
SPI1BRG = 0x3;
// FRMERR disabled;
SPI1STAT = 0x0;
// no audio mode, etc
SPI1CON2 = 0x0;
 
// SPI1CON
SPI1CONbits.SRXISEL=0b01; // Interrupt is generated when the buffer is not empty
SPI1CONbits.STXISEL=0b11; // Interrupt is generated when the buffer is not full
SPI1CONbits.DISSDI=0; // SDI pin is controlled by the SPI module
SPI1CONbits.MSTEN=1; // Master
SPI1CONbits.CKP=0; // Idle state for clock is a low level; active state is a high level
SPI1CONbits.SSEN=0; // SSx pin not used for Slave mode, pin controlled by port function.
SPI1CONbits.CKE=1; // from active clock state to Idle clock state
SPI1CONbits.SMP=0; // Input data sampled at middle of data output time
SPI1CONbits.MODE16=0; // AUDEN=0 & 8-bit
SPI1CONbits.MODE32=0; // AUDEN=0 & 8-bit
SPI1CONbits.DISSDO=0; // SDOx pin is controlled by the module
SPI1CONbits.SIDL=0; // Continue module operation when the device enters Idle mode
SPI1CONbits.ON=1;
SPI1CONbits.ENHBUF=1; // Enhanced Buffer mode is enabled
SPI1CONbits.SPIFE=1; // Frame synchronization pulse precedes the first bit clock
SPI1CONbits.MCLKSEL=0; //PBCLK
SPI1CONbits.FRMCNT= 0b000;
SPI1CONbits.FRMSYPW= 0;
SPI1CONbits.MSSEN=0;
SPI1CONbits.FRMPOL=0;
SPI1CONbits.FRMSYNC=0;
SPI1CONbits.FRMEN = 0;

//Dma
DmaChnOpen(SPI1_TX_DMA_CHN, DMA_CHN_PRI3, DMA_OPEN_DEFAULT);
DmaChnOpen(SPI1_RX_DMA_CHN, DMA_CHN_PRI3, DMA_OPEN_DEFAULT);

//set interrupt priorities
INTSetVectorPriority(INT_VECTOR_DMA(SPI1_TX_DMA_CHN), 6);
INTSetVectorPriority(INT_VECTOR_DMA(SPI1_RX_DMA_CHN), 6);
INTSetVectorSubPriority(INT_VECTOR_DMA(SPI1_TX_DMA_CHN), 3);
INTSetVectorSubPriority(INT_VECTOR_DMA(SPI1_RX_DMA_CHN), 3);
 
DmaChnClrEvFlags(SPI1_TX_DMA_CHN, DMA_EV_ALL_EVNTS);
DmaChnClrEvFlags(SPI1_RX_DMA_CHN, DMA_EV_ALL_EVNTS);
 
// set the events
DmaChnSetEventControl(SPI1_TX_DMA_CHN, DMA_EV_START_IRQ_EN | DMA_EV_START_IRQ(_SPI1_TX_IRQ));
DmaChnSetEventControl(SPI1_RX_DMA_CHN, DMA_EV_START_IRQ_EN | DMA_EV_START_IRQ(_SPI1_RX_IRQ));
 

// set txs
DmaChnSetTxfer(SPI1_TX_DMA_CHN, buffer , (void*) &SPI1BUF, sizeof (buffer ), 1, 1);
DmaChnSetTxfer(SPI1_RX_DMA_CHN, (void*) &SPI1BUF, rxDummy , 1, sizeof (rxDummy), 1);
 
//enable the transfer done interrupt, when all buffer has been completely transferred
DmaChnSetEvEnableFlags(SPI1_RX_DMA_CHN, DMA_EV_BLOCK_DONE);
 
//enable the DMA channel interrupt controller
INTEnable(INT_SOURCE_DMA(SPI1_RX_DMA_CHN), INT_ENABLED);

// DMA will be activated after SPI commands
DmaChnDisable(SPI1_RX_DMA_CHN);
DmaChnDisable(SPI1_TX_DMA_CHN);
}
 
void test() {
 
while (1) {
// OLED commands

 
//DMA
DmaTxIntFlag = 0; // clear the interrupt flag we're waiting on
DmaChnEnable(SPI1_RX_DMA_CHN);
DmaChnEnable(SPI1_TX_DMA_CHN);
DmaChnStartTxfer(SPI1_TX_DMA_CHN, DMA_WAIT_NOT, 0); // force the DMA transfer
 
// wait for the transfer to complete
while (!DmaTxIntFlag);
DmaChnDisable(SPI1_TX_DMA_CHN);
DmaChnDisable(SPI1_RX_DMA_CHN);
}
}
 

void __ISR(_DMA1_VECTOR, IPL6SOFT) DmaHandler1(void) {
int evFlags; // event flags when getting the interrupt
INTClearFlag(INT_SOURCE_DMA(SPI1_RX_DMA_CHN)); // acknowledge the INT controller, we're servicing int
evFlags = DmaChnGetEvFlags(SPI1_RX_DMA_CHN); // get the event flags
if (evFlags & DMA_EV_BLOCK_DONE) { // just a sanity check. we enabled just the DMA_EV_BLOCK_DONE transfer done interrupt
DmaTxIntFlag = 1;
DmaChnClrEvFlags(SPI1_RX_DMA_CHN, DMA_EV_BLOCK_DONE);
}
}


But I would like to use a one-char-length buffer for the RX-Channel. I tried a couple of modifications of the above code, f.e.:


 
DmaChnOpen(SPI1_RX_DMA_CHN, DMA_CHN_PRI3, DMA_OPEN_AUTO);
 
....
 
DmaChnSetTxfer(SPI1_RX_DMA_CHN, (void*) &SPI1BUF, rxOneChar , 1, 1, 1);
 

without success: The process stops, in this example, on the RX-interrupt method after 200 interrupts. This code is only for test-purpose, because one interrupt per byte RX is possible not as CPU-less as a DMA-solution is looking for.

I tried to use the interrupt on TX-Channel's block-done event:

 
//enable the transfer done interrupt, when all buffer has been completely transferred
DmaChnSetEvEnableFlags(SPI1_TX_DMA_CHN, DMA_EV_BLOCK_DONE);
//enable the DMA channel interrupt controller
INTEnable(INT_SOURCE_DMA(SPI1_TX_DMA_CHN), INT_ENABLED);
 

but the OLED shows distortion on data. I suppose that the DMA RX was still running and collide with the command mode/ command routine.

Is a "one char RX buffer" possible?
How could/should the DMA be configured for a "functional correct" one char-length RX mode?
 
(code new formatted)
post edited by molrse - 2017/12/13 14:52:47
#1
molrse
Starting Member
  • Total Posts : 45
  • Reward points : 0
  • Joined: 2016/05/15 11:16:16
  • Location: 0
  • Status: offline
Re: PIC32MX250F128D: DMA SPI transfer only with RX buffer's length = 1 2017/12/07 12:40:17 (permalink)
0
I would like to add that I tried to do the same with Harmony but I didn't find any example on how to configure the SPI and DMA for my case and integrate it under the OLED-Driver with Harmony.
 
I would be very thankful if someone could give an example for configurable DMA SPI under Harmony as well.
#2
radikn
Starting Member
  • Total Posts : 12
  • Reward points : 0
  • Joined: 2015/07/31 23:58:01
  • Location: Czech Republic
  • Status: offline
Re: PIC32MX250F128D: DMA SPI transfer only with RX buffer's length = 1 2017/12/08 05:41:56 (permalink) ☼ Best Answerby molrse 2017/12/13 14:52:06
5 (1)
I worked on connection of OLED display with PIC32MX205F128B two weeks ago. Display has SPI interface. I wanted to transfer data from memory frame buffer into display via DMA, of course. DMA terminates transfer by writing the last byte to the SPIxBUF registry. But SPI module is still transmitting last characters which are in shift register. So real end of transmission is not know and switch display chip select into inactive level in DMA interrupt service routine when DMA transfer is finished is bad. Last bytes of video data was lost. So it is important to wait to end of SPI transfer. No DMA. It is possible to use one DMA channel for sending data into display via SDO and simultaneously second DMA channel for receiving dummy data via SDI. I, like you, didn't want waste RAM with dummy buffer of 1024 bytes length. I wanted to write received data into one byte. But I didn't able to set up DMA to not automatically increment source and destination address. I was looking for a solution on internet but without success. At the end I resolved setting chip select with using a timer. Using peripheral pin selection I connected SPI CLK signal to timer input TxCK pin. Timer counted CLK pulses. For transfer 1024 bytes I need 8192 pulses. After it timer generate interrupt request and in service routine I set up display chip select into inactive level.

It is my experience :-). What you?
#3
arpananand
Super Member
  • Total Posts : 370
  • Reward points : 0
  • Joined: 2009/11/18 04:35:42
  • Location: 0
  • Status: offline
Re: PIC32MX250F128D: DMA SPI transfer only with RX buffer's length = 1 2017/12/08 07:21:33 (permalink) ☄ Helpfulby molrse 2017/12/08 12:20:23
5 (1)
in Harmony SPI driver with DMA example is present in "apps/driver/spi/spi_loopback" demo. you would need to select DMA configuration.
#4
NKurzman
A Guy on the Net
  • Total Posts : 15127
  • Reward points : 0
  • Joined: 2008/01/16 19:33:48
  • Location: 0
  • Status: offline
Re: PIC32MX250F128D: DMA SPI transfer only with RX buffer's length = 1 2017/12/08 07:54:22 (permalink) ☄ Helpfulby molrse 2017/12/08 12:20:26
5 (1)
I do not know if this applies but, on a pic24 write only DMA for SPI I wrote and read from the same buffer. This avoided having two buffers. The down side is the data is destroyed. This works because the byte is sent, then received.
#5
molrse
Starting Member
  • Total Posts : 45
  • Reward points : 0
  • Joined: 2016/05/15 11:16:16
  • Location: 0
  • Status: offline
Re: PIC32MX250F128D: DMA SPI transfer only with RX buffer's length = 1 2017/12/08 12:53:13 (permalink)
0
radikn
At the end I resolved setting chip select with using a timer. Using peripheral pin selection I connected SPI CLK signal to timer input TxCK pin. Timer counted CLK pulses. For transfer 1024 bytes I need 8192 pulses. After it timer generate interrupt request and in service routine I set up display chip select into inactive level.

 
a very clever solution for a buffered OLED!!! Very smart!
 
radikn
It is my experience :-). What you?

 
I suppose the RX-channel is still working after the TX-Channel's block-done even has been fired. So I'm opinion that the TX of the commands (DC to Low) before new transfer of data seems to interfere with the OLED driver. The result is random behavior of the OLED.
 
I tried to use IGNROV (Ignore Receive Overflow bit), despite in the datasheet is specified as "for Audio Data Transmissions". It didn't work. I was unable to implement a working solution with a RX.lenght=1.
#6
molrse
Starting Member
  • Total Posts : 45
  • Reward points : 0
  • Joined: 2016/05/15 11:16:16
  • Location: 0
  • Status: offline
Re: PIC32MX250F128D: DMA SPI transfer only with RX buffer's length = 1 2017/12/08 13:02:22 (permalink)
0
arpananand
in Harmony SPI driver with DMA example is present in "apps/driver/spi/spi_loopback" demo. you would need to select DMA configuration.

Which configuration should I select? I'm the opinion that I tried all of them and none of the configurations have got under Harmony's HFC->Drivers->SPI the check box "Use DMA?" checked/selected.
 
Configurations: Pic32mx795_pim_e16_freertos, Pic32mx795_pim_e16, Pic32mx795_usb_sk2, Pic32mx795_ef_sk, Pic32mx795_ef_sk_16b, Pic32mx795_sk_freertos
 
By the way: are there examples of "DMA configurations"?
#7
molrse
Starting Member
  • Total Posts : 45
  • Reward points : 0
  • Joined: 2016/05/15 11:16:16
  • Location: 0
  • Status: offline
Re: PIC32MX250F128D: DMA SPI transfer only with RX buffer's length = 1 2017/12/08 13:21:35 (permalink)
0
NKurzman
I do not know if this applies but, on a pic24 write only DMA for SPI I wrote and read from the same buffer. This avoided having two buffers. The down side is the data is destroyed. This works because the byte is sent, then received.

Yes, you're right. I'm thinking to re-implement my solution for use a page based transfer (transfer only from minimal to maximal changed pages). I would have a dynamic buffer and only transfer changed pages. The question is if the calculation of the page ranges is less CPU- intensive than updating a buffer and letting the DMA transfer the whole array.
 
For a buffered display unfortunately the solution wouldn't work, because the CPU would need to refill the whole array every time before transfer. This would decrease the performance achieved using the DMA if I want to repaint only changed components/regions.
#8
Jump to:
© 2017 APG vNext Commercial Version 4.5