CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

DSPIC33 DMA+SPI only sends one byte [SOLVED]
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
guy



Joined: 21 Oct 2005
Posts: 297

View user's profile Send private message Visit poster's website

DSPIC33 DMA+SPI only sends one byte [SOLVED]
PostPosted: Mon Sep 23, 2024 4:48 pm     Reply with quote

EDIT: Solution at the end of the thread

Hello everyone,
I went over a dozen threads, tried several things, still not solved...
I want to use DMA+SPI2 to control WS2812 LEDs in the background. Standard SPI code works well but when I change to DMA only one byte is sent (which represents one bit for the LEDS but this is irrelevant) . The byte is indeed the first byte in the buffer.
The compiler is outdated (5.078) but I can't afford to upgrade...

Any idea? Any bit/register I should check?

Code:
#include <33EV256GM002.h>
#FUSES BROWNOUT,NOWDT,XT,DMT_SW
#use delay(CLOCK=70MHz)
#pin_select SDO2=PIN_B10
#pin_select SDI2=PIN_B12    // UNUSED?

byte RGBdmaBuf[384];    // 16 LEDs = 384 bits
byte garbage[384];
int16 RGBdmaInd;

void sendRGBbyte(byte b) {
    byte c;
   
    for(c=0;c<8;c++) {
        if(bit_test(b,7)) {         // MSB 1
            RGBdmaBuf[RGBdmaInd++]=0b11111110;
        }
        else {                      // MSB 0
            RGBdmaBuf[RGBdmaInd++]=0b11100000;
        }
        b<<=1;
        if(RGBdmaInd>384) RGBdmaInd=384;
    }
}
////////////////////
void initRGB() {
    int16 i;
    RGBdmaInd=0;
    for(i=0;i<16;i++) {
        sendRGBbyte(0);
        sendRGBbyte(0);
        sendRGBbyte(0);
    }
}
////////////////////
void setRGB(byte b,rr,gg,bb) {
   
    RGBdmaInd=b*3*8;
    sendRGBbyte(gg);
    sendRGBbyte(rr);
    sendRGBbyte(bb);
}

////////////////////
void txRGB() {
    int16 i;
           
    dma_start(3,DMA_ONE_SHOT|DMA_FORCE_NOW,&garbage[0],RGBdmaInd-1);
    dma_start(2,DMA_ONE_SHOT|DMA_FORCE_NOW,&RGBdmaBuf[0],RGBdmaInd-1);

    setup_spi2(SPI_MASTER|SPI_SS_DISABLED|SPI_MODE_8B|SPI_CLK_DIV_3|SPI_XMIT_L_TO_H);
   
    while(1) {
        i++;
    }
}
    /////////////////
   
void main() {
    byte b;

    setup_oscillator(OSC_CRYSTAL,70000000,6000000);
   
    setup_dma(2,DMA_OUT_SPI2,DMA_BYTE);   
    setup_dma(3,DMA_IN_SPI2,DMA_BYTE);   
       
    while(1) {
        delay_ms(1);
        initRGB();
        RGBdmaInd=0;
        for(b=0;b<48;b++) {
            sendRGBbyte(b);
        }
        txRGB();
    }
}


Last edited by guy on Sat Sep 28, 2024 5:46 am; edited 1 time in total
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Mon Sep 23, 2024 11:04 pm     Reply with quote

Haven't looked a the code, but glaring thing near the start, your DMA
buffer is not located in the DMA memory space. Normally you have to
use #bank DMA to locate the buffer in the area DMA can be done from.
Possibly does not apply to your chip (haven't got the datasheet handy),
but every PIC DMA I've used needs this.
guy



Joined: 21 Oct 2005
Posts: 297

View user's profile Send private message Visit poster's website

PostPosted: Mon Sep 23, 2024 11:53 pm     Reply with quote

Hi Ttelmah, as always thank you for the support.
Re your idea to use DMA memory space,
a. To me, it is not completely clear from the DS whether DMA can access SRAM directly on the specific chip
b. I have another DMA setup for the UART without #BANK_DMA and it works well

Anyway, I added the following and the problem persists. Did I do it correctly? Other suggestions?
Code:

#bank_dma
byte RGBdmaBuf[384];    // 16 LEDs = 384 bits
#bank_dma
byte garbage[384];
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Tue Sep 24, 2024 3:39 am     Reply with quote

Your chip is one that allows DMA on the whole of memory. So no DMA
bank needed. It is slightly more efficient if you use the second memory
space for DMA, and the first for your variables, otherwise there will be
slight delays when both CPU and DMA want to access at the same time.
However small.

I'm wondering if you are creating a conflict by having the FORCE_NOW on
the read channel.
Think about it. There is nothing for this channel to read, until a byte
has been sent by the transmitting channel. I think this should be left just
to trigger on the interrupt.

On an older DMA controller than this I setup both channels to just
trigger on the interrupt, and then wrote the first byte to the SPI. This
then sends the whole sequence for me, and reads the reply bytes.
guy



Joined: 21 Oct 2005
Posts: 297

View user's profile Send private message Visit poster's website

PostPosted: Tue Sep 24, 2024 6:07 am     Reply with quote

I tried removing FORCE_NOW from the read channel. I tried a manual SPI read, a manual SPI write. I tried to remove the Read DMA altogether. I constantly see only a single byte on the output.

Any other ideas? Can you send me an example code that worked for you? I will do the adjustments myself.
Also, is there a way to debug the DMA operation?

Thank you.
Guy
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Tue Sep 24, 2024 7:55 am     Reply with quote

Problem is my code is for a very different chip.
However I'd suggest:

Code:

#include <33EV256GM002.h>
#FUSES BROWNOUT,NOWDT,XT,DMT_SW
#use delay(CLOCK=70MHz)
#pin_select SDO2=PIN_B10
#pin_select SDI2=PIN_B12    // UNUSED?

byte RGBdmaBuf[384];    // 16 LEDs = 384 bits
byte garbage[384];
int16 RGBdmaInd;

void sendRGBbyte(byte b) {
    byte c;
   
    for(c=0;c<8;c++) {
        if(bit_test(b,7)) {         // MSB 1
            RGBdmaBuf[RGBdmaInd++]=0b11111110;
        }
        else {                      // MSB 0
            RGBdmaBuf[RGBdmaInd++]=0b11100000;
        }
        b<<=1;
        if(RGBdmaInd>384) RGBdmaInd=384;
    }
}
////////////////////
void initRGB() {
    int16 i;
    RGBdmaInd=0;
    for(i=0;i<16;i++) {
        sendRGBbyte(0);
        sendRGBbyte(0);
        sendRGBbyte(0);
    }
}

////////////////////
void txRGB() {
    int16 i;
    setup_spi2(SPI_MASTER|SPI_SS_DISABLED|SPI_MODE_8B|SPI_CLK_DIV_3|SPI_XMIT_L_TO_H);
   
    dma_start(3,DMA_CONTINOUS,&garbage[0],RGBdmaInd-1);
    dma_start(2,DMA_ONE_SHOT|DMA_FORCE_NOW,&RGBdmaBuf[0],RGBdmaInd); //Using force_now, count has to increase by one   
   
    while(TRUE) {
        i++;
    }
}
    /////////////////
   
void main(void)
{
    byte b;

    setup_oscillator(OSC_CRYSTAL,70000000,6000000);
   
    setup_dma(2,DMA_OUT_SPI2,DMA_BYTE);   
    setup_dma(3,DMA_IN_SPI2,DMA_BYTE);   
       
    while(TRUE) {
        delay_ms(1);
        initRGB();
        RGBdmaInd=0;
        for(b=0;b<48;b++) {
            sendRGBbyte(b);
        }
        txRGB();
    }
}


If you read the manual on the force mode, you have to add one to the
byte count since iit decrements for this.

If that doesn't work I'll have to see if I can find this version, and look if
it does anything differently to the current compilers.
guy



Joined: 21 Oct 2005
Posts: 297

View user's profile Send private message Visit poster's website

PostPosted: Tue Sep 24, 2024 9:19 am     Reply with quote

Thanks, I tried your code, no change...
I also tried lowering the SPI rate, still doesn't work Crying or Very sad
guy



Joined: 21 Oct 2005
Posts: 297

View user's profile Send private message Visit poster's website

PostPosted: Thu Sep 26, 2024 2:20 pm     Reply with quote

Any help would be appreciated
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Fri Sep 27, 2024 10:41 pm     Reply with quote

One simple thing. Your comment explains all!.
Where is the clock connection for the SPI in the PPS settings?.

Your chip is one where the CLKIN and CLKOUT connections are separate.
What is happening is the CLKOUT is coming out on the default pin, but
you have not made the CLKIN connection. So this is not being clocked.
The SPI therefore does not 'see' the clock happening, so locks after the
first byte waiting for the clocks...... Sad

SCK2OUT and SCK2IN both need to be mapped to the pin being used for
the clock.
guy



Joined: 21 Oct 2005
Posts: 297

View user's profile Send private message Visit poster's website

PostPosted: Sat Sep 28, 2024 5:45 am     Reply with quote

Works perfectly!!! Thank you Ttelmah. Smile
I used virtual pins to save on IOs.

Code:

// WS2812 RGB led code with SPI and DMA, PIC33EV256GM002

#include <33EV256GM002.h>
#device ADC=10
#FUSES BROWNOUT,NOWDT,XT,DMT_SW
#use delay(CLOCK=70MHz)
#pin_select SDO2=PIN_B10
#pin_select SDI2=VIRTUAL_PIN2
#pin_select SCK2IN=VIRTUAL_PIN1
#pin_select SCK2OUT=VIRTUAL_PIN1

byte RGBdmaBuf[384];    // 16 LEDs = 384 bits
byte garbage[384];
int16 RGBdmaInd;

void storeRGBbyte(byte b) {
    byte c;
   
    for(c=0;c<8;c++) {
        if(bit_test(b,7)) {         // MSB 1
            RGBdmaBuf[RGBdmaInd++]=0b11111110;
        }
        else {                      // MSB 0
            RGBdmaBuf[RGBdmaInd++]=0b11100000;
        }
        b<<=1;
        if(RGBdmaInd>384) RGBdmaInd=384;
    }
}
////////////////////
void clearRGBbuf() {
    int16 i;
   
    memset(RGBdmaBuf,0b11100000,sizeOf(RGBdmaBuf)); // optimized version
    RGBdmaInd=sizeOf(RGBdmaBuf);
    /*
     // non optimized:
    RGBdmaInd=0;
    for(i=0;i<16;i++) {
        storeRGBbyte(0);
        storeRGBbyte(0);
        storeRGBbyte(0);
    }
     */
}
////////////////////
void setRGB(byte b,rr,gg,bb) {
   
    RGBdmaInd=b*3*8;
    storeRGBbyte(gg);
    storeRGBbyte(rr);
    storeRGBbyte(bb);
}

////////////////////
void txRGB() {
    int16 i;

    dma_start(3,DMA_CONTINOUS,&garbage[0],RGBdmaInd-1);
    dma_start(2,DMA_ONE_SHOT|DMA_FORCE_NOW,&RGBdmaBuf[0],RGBdmaInd-1);
}
    /////////////////
   
void main() {
    int16 i;

    setup_oscillator(OSC_CRYSTAL,70000000,6000000);
   
    setup_dma(2,DMA_OUT_SPI2,DMA_BYTE);   
    setup_dma(3,DMA_IN_SPI2,DMA_BYTE);   
    setup_spi2(SPI_MASTER|SPI_SS_DISABLED|SPI_MODE_8B|SPI_CLK_DIV_3|SPI_XMIT_L_TO_H|SPI_SCK_IDLE_LOW);
       
    delay_ms(1);
    clearRGBbuf();
    txRGB();
   
    while(1) {
        delay_ms(1);
        RGBdmaInd=0;        // reset pointer
        for(i=0;i<16;i++) {
            setRGB(i,i*3,i*3+1,i*3+2);  // set RGB pixel
        }
        txRGB();
    }
}
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sun Sep 29, 2024 11:52 am     Reply with quote

Ok, please educate the dinosaur...
what is a 'virtual_pin' and how is it used ??
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Mon Sep 30, 2024 3:22 am     Reply with quote

Some of the later chips have internal connections that can be used as
if they are pins. These are the 'virtual pins'.
He doesn't actually need to do this. Just connect bot the SC2IN and SCK2OUT
to the same single default physical pin.
This is a 'classic' on these later chips the input SPI clock is not automatically
connected to the output clock. Great if you do need to have separate clocks,
but annoying when you are setting this up!... Very Happy

This is mentioned in the PIN_SELECT 'sticky'.
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Oct 01, 2024 2:55 pm     Reply with quote

Thanks Mr. T. !
great 'virtual-pins'.....
waay too much new stuff going on 'under the quartz' !

I assume that if virtual_pins are used the physical pin can still be used for other purposes ?

Hopefully the compiler warns you about 'misconnections ' ?
Ttelmah



Joined: 11 Mar 2010
Posts: 19495

View user's profile Send private message

PostPosted: Wed Oct 02, 2024 3:45 am     Reply with quote

Yes.
As I said he doesn't really want to use his (wasting a virtual pin). He is
using the default SPI clock out (or it wouldn't work!), so just needs to
connect the clock in to this pin as well.
Where they are really useful, is things like using some of the programmable
logic gates, or routing clocks between different peripherals.
temtronic



Joined: 01 Jul 2010
Posts: 9221
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Thu Oct 03, 2024 5:47 am     Reply with quote

'wasting a virtual pin' ??
really ??
I mean, 'it's 'virtual', it doesn't even exist !!

Laughing

OK, I understand, there's probably only a few ' internal, bypass connections' inside the PIC......

It just seemed 'funny' to me

Proteus probably would allow 100s of them though Very Happy
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group