|
|
View previous topic :: View next topic |
Author |
Message |
guy
Joined: 21 Oct 2005 Posts: 297
|
DSPIC33 DMA+SPI only sends one byte [SOLVED] |
Posted: Mon Sep 23, 2024 4:48 pm |
|
|
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: 19513
|
|
Posted: Mon Sep 23, 2024 11:04 pm |
|
|
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
|
|
Posted: Mon Sep 23, 2024 11:53 pm |
|
|
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: 19513
|
|
Posted: Tue Sep 24, 2024 3:39 am |
|
|
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
|
|
Posted: Tue Sep 24, 2024 6:07 am |
|
|
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: 19513
|
|
Posted: Tue Sep 24, 2024 7:55 am |
|
|
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
|
|
Posted: Tue Sep 24, 2024 9:19 am |
|
|
Thanks, I tried your code, no change...
I also tried lowering the SPI rate, still doesn't work |
|
|
guy
Joined: 21 Oct 2005 Posts: 297
|
|
Posted: Thu Sep 26, 2024 2:20 pm |
|
|
Any help would be appreciated |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Fri Sep 27, 2024 10:41 pm |
|
|
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......
SCK2OUT and SCK2IN both need to be mapped to the pin being used for
the clock. |
|
|
guy
Joined: 21 Oct 2005 Posts: 297
|
|
Posted: Sat Sep 28, 2024 5:45 am |
|
|
Works perfectly!!! Thank you Ttelmah.
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: 9226 Location: Greensville,Ontario
|
|
Posted: Sun Sep 29, 2024 11:52 am |
|
|
Ok, please educate the dinosaur...
what is a 'virtual_pin' and how is it used ?? |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19513
|
|
Posted: Mon Sep 30, 2024 3:22 am |
|
|
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!...
This is mentioned in the PIN_SELECT 'sticky'. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9226 Location: Greensville,Ontario
|
|
Posted: Tue Oct 01, 2024 2:55 pm |
|
|
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: 19513
|
|
Posted: Wed Oct 02, 2024 3:45 am |
|
|
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: 9226 Location: Greensville,Ontario
|
|
Posted: Thu Oct 03, 2024 5:47 am |
|
|
'wasting a virtual pin' ??
really ??
I mean, 'it's 'virtual', it doesn't even exist !!
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 |
|
|
|
|
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
|