Using VS1010D as a spi slave

Designing hardware and software for systems that use the VS1010 MP3 Audio DSP Microcontroller.
goncalo
User
Posts: 13
Joined: Fri 2023-01-13 12:57

Using VS1010D as a spi slave

Post by goncalo »

Hello :D ,

Im trying to use the vs1010d devboard to comunicate to other microcontroller via SPI comunnication.

The other microcontroller is set as MASTER and im trying to set de vs1010d as slave.

Image

To make the comunnication between the two controllers work, first i disable de tv, configure the vs1010d as slave, and enable the SPI1 interrupts, i then tried using the spi interrupt as shown in the code bellow i learned from the forum. The code is meant to read the spi1 receiving data and storing it to a buffer.

Code: Select all

#include <vo_stdio.h>
#include <volink.h>     // Linker directives like DLLENTRY
#include <apploader.h>  // RunLibraryFunction etc
#include <vs1010dRom.h>
#include <vo_gpio.h>
#include <vs1010c.h>
#include <playerinfo.h>
#include <string.h>
#include <protocol.h>
#include <spitv.h>
#include <vs1010_pins.h>
#include <lcd.h>
#include <devboard.h>
#include <audio.h>
						
int cnt=0;						
#define SPI_BUF_SIZE 128
u_int16 spi_buf[SPI_BUF_SIZE] = {0};
u_int16 bufhead = 0;
u_int16 buftail = 0;
u_int16 rx_dat;

void InitSpiSlave()
{ 	
	GpioSetAsPeripheral(0x14);    //Set GPIO1.4 as MOSI1
	GpioSetAsPeripheral(0x15);
	GpioSetAsPeripheral(0x16);
	GpioSetAsPeripheral(0x17);
	PERIP(SPI1_CF) = SPI_CF_SRESET|SPI_CF_RXFIFO_ENA|SPI_CF_SLAVE|SPI_CF_DLEN16;
	PERIP(SPI1_DATA) = 0; 
	PERIP(INT_ENABLE0_HP)|= INTF_SPI1; //Enable interrupt
	PERIP(INT_ENABLE0_LP)|= INTF_SPI1; //Enable interrupt
}

#pragma interrupt y 0x24
void SPI1_INT(void) {
		while(SPI_ST_RXFULL & PERIP(SPI1_STATUS)){					
		rx_dat = PERIP(SPI1_DATA);
		cnt=cnt+2;	
		if ((buftail + 1) % SPI_BUF_SIZE != bufhead) {
			spi_buf[buftail] = rx_dat;
			buftail = (buftail + 1) % SPI_BUF_SIZE;
		}
	}
}

void print_spi_buf(void)
{
	while (buftail != bufhead) {		
		printf("SPI MOSI-->%x\n",spi_buf[bufhead]);
		bufhead = (bufhead + 1)% SPI_BUF_SIZE;// % SPI_BUF_SIZE;	
		printf("cnt-->%d\n",cnt);	
	}		
	cnt=0;
}

ioresult main (char *params) 			///Função main
{     	                 
	PERIP(INT_ENABLE0_HP)&= ~(INTF_SPI1);
	PERIP(INT_ENABLE0_LP)&= ~ (INTF_SPI1); 
	SetJmpiHookFunction((void*)102,VoidVoid);  
	lcd0.clipy2 = 0; 	// Disable tv buffer and tv-out	
	
	InitSpiSlave();
	while(1){
		DelayL(10);
		print_spi_buf();
	}
	return S_OK;///Return OK
}
When i try running the code the interrupt does not work at all. Not being able to make this code work i tried to develop a custom hardware driver for the SPI1 folowing the vs1010 handbook, using the vs1010d as slave. I tried to follow this thread viewtopic.php?f=15&t=2830&p=14441&hilit ... ver#p14441 but it is for vs1010d set as MASTER.

Did anybody came across with this problem, the spi1 interruption not working? Is it possible to set the vs1010 as SLAVE using the custom hardware driver for the SPI1?
Attachments
vs1010_spi1_slave_not_working.txt
(1.74 KiB) Downloaded 25 times
Capture.PNG
Capture.PNG (3.37 KiB) Viewed 705 times
Hannu
VLSI Staff
Posts: 435
Joined: Mon 2016-05-30 11:54
Location: Finland
Contact:

Re: Using VS1010D as a spi slave

Post by Hannu »

Hi,

I gave your code some love. I haven't used SPI in slave mode, at least with interrupts so this is just some hints.

I believe you aren't using ridiculous speeds? The SPI slave has to be below DSP clock /2 which is 30.720MHz by default.

First issue is that when you loaded your code to memory it wrote the interrupt as interrupt service. In that time the SPI-tv was still running before the InitSpiSlave was run. Execution should jump to Lunar orbit and trash memory everywhere. That's why the 0x7e trick.

Then I took it out of reset.
If the HW is running, it will give you 0x1234 back. That's the debug part.

Also enabling LP and HP, will put your interrupt to highest priority. You probably want to keep rather low it and gicve DAC max priority. So audio doesn't stutter.

Totally untested, may compile and work, probably contains bugs. Otherwise your code was quite nice and clean.


Code: Select all

#include <vo_stdio.h>
#include <volink.h>     // Linker directives like DLLENTRY
#include <apploader.h>  // RunLibraryFunction etc
#include <vs1010dRom.h>
#include <vo_gpio.h>
#include <vs1010c.h>
#include <playerinfo.h>
#include <string.h>
#include <protocol.h>
#include <spitv.h>
#include <vs1010_pins.h>
#include <lcd.h>
#include <devboard.h>
#include <audio.h>
						
int cnt=0;						
#define SPI_BUF_SIZE 128
u_int16 spi_buf[SPI_BUF_SIZE] = {0};
u_int16 bufhead = 0;
u_int16 buftail = 0;
u_int16 rx_dat;

void InitSpiSlave()
{ 	
	GpioSetAsPeripheral(0x14);    //Set GPIO1.4 as MOSI1
	GpioSetAsPeripheral(0x15);
	GpioSetAsPeripheral(0x16);
	GpioSetAsPeripheral(0x17);
	PERIP(SPI1_CF) = SPI_CF_SRESET|SPI_CF_RXFIFO_ENA|SPI_CF_SLAVE|SPI_CF_DLEN16;
	/* Please come out reset */
	PERIP(SPI1_CF) = SPI_CF_RXFIFO_ENA|SPI_CF_SLAVE|SPI_CF_DLEN16;
	/* Debug */
	PERIP(SPI1_DEFAULT) = 0x1234;
	PERIP(SPI1_DATA) = 0; 
	WriteToProgramRam(0x20 + INTV_SPI1, ReadFromProgramRam((u_int16)0x7e));
	PERIP(INT_ENABLE0_HP)|= INTF_SPI1; //Enable interrupt
	PERIP(INT_ENABLE0_LP)|= INTF_SPI1; //Enable interrupt
}

#pragma interrupt y 0x7e
void SPI1_INT(void) {
	while((SPI_ST_RXFULL | SPI_ST_RXFIFOFULL) & PERIP(SPI1_STATUS)){					
		rx_dat = PERIP(SPI1_DATA);
		
		if ((buftail + 1) % SPI_BUF_SIZE != bufhead) {
			spi_buf[buftail] = rx_dat;
			buftail = (buftail + 1) % SPI_BUF_SIZE;
		}
		cnt=cnt+2;	
	}
}

void print_spi_buf(void)
{
	while (buftail != bufhead) {		
		printf("SPI MOSI-->%x\n",spi_buf[bufhead]);
		bufhead = (bufhead + 1)% SPI_BUF_SIZE;// % SPI_BUF_SIZE;	
		printf("cnt-->%d\n",cnt);	
	}		
	cnt=0; /* This will be problematic. Luckily it is decorative. What if interrupt comes after loop but before this line. cnt is off by two. */
}

ioresult main (char *params) 			///Função main
{     	                 
	PERIP(INT_ENABLE0_HP)&= ~(INTF_SPI1);
	PERIP(INT_ENABLE0_LP)&= ~ (INTF_SPI1); 
	SetJmpiHookFunction((void*)102,VoidVoid);  
	lcd0.clipy2 = 0; 	// Disable tv buffer and tv-out	
	
	InitSpiSlave();
	while(1){
		DelayL(10);
		print_spi_buf();
	}
	return S_OK;///Return OK
}
goncalo
User
Posts: 13
Joined: Fri 2023-01-13 12:57

Re: Using VS1010D as a spi slave

Post by goncalo »

Hi :D ,

Many thanks, the interruption works very well and i can receive data from the master just fine :D .

The VS1010d slave sends first the 2 default bytes and then other 2 bytes and just then sends the actual data.

I tried commenting the lines that send the default bytes but no luck, is there any other way that i can disenable this first 4 first bytes so that the VS1010d slave only sends the actual data, i think im missing something here :( ?

I made some changes to the spi configurations and added some code so that i can send a datagram to the master controller.


Best regards, sincerely
Gonçalo

Code: Select all

#include <vo_stdio.h>
#include <volink.h>     // Linker directives like DLLENTRY
#include <apploader.h>  // RunLibraryFunction etc
#include <vs1010dRom.h>
#include <vo_gpio.h>
#include <vs1010c.h>
#include <playerinfo.h>
#include <string.h>
#include <protocol.h>
#include <spitv.h>
#include <vs1010_pins.h>
#include <lcd.h>
#include <devboard.h>
#include <audio.h>
												
#define SPI_BUF_SIZE 128
u_int16 spi_buf[SPI_BUF_SIZE] = {0};
u_int16 bufhead = 0;
u_int16 buftail = 0;
u_int16 rx_dat;
int i=0;

void InitSpiSlave()
{ 	
	//disable tv
	PERIP(INT_ENABLE0_HP)&= ~(INTF_SPI1);
	PERIP(INT_ENABLE0_LP)&= ~ (INTF_SPI1); 
	SetJmpiHookFunction((void*)102,VoidVoid);  
	lcd0.clipy2 = 0; 

	//configure spi
	GpioSetAsPeripheral(0x14);  
	GpioSetAsPeripheral(0x15);
	GpioSetAsPeripheral(0x16);
	GpioSetAsPeripheral(0x17);

	PERIP(SPI1_CF) = SPI_CF_SRESET //changed line
	/* Please come out reset */

	PERIP(SPI1_CF) = SPI_CF_RXFIFO_ENA|SPI_CF_SLAVE|SPI_CF_DLEN16|SPI_CF_TXFIFO_ENA|SPI_CF_XCSMODE; //changed line
	/* Debug */

	//PERIP(SPI1_DEFAULT) = 0x1234;
	//PERIP(SPI1_DATA) = 0; 

	WriteToProgramRam(0x20 + INTV_SPI1, ReadFromProgramRam((u_int16)0x7e));
	PERIP(INT_ENABLE0_HP)|= INTF_SPI1; //Enable interrupt
	PERIP(INT_ENABLE0_LP)|= INTF_SPI1; //Enable interrupt
}

//datagram
struct data_to_send_spi
{
	u_int16 begin[2];
	u_int16 data[4];
	u_int16 end[2];
}d;

struct data_to_send_spi d = {0xaabb,0xccdd,0xeeff,0x1111,0x2222,0x3333,0x4444,0x5555};
struct data_to_send_spi* p1 = &d;


#pragma interrupt y 0x7e
void SPI1_INT(void) {
	while((SPI_ST_RXFULL | SPI_ST_RXFIFOFULL) & PERIP(SPI1_STATUS)){					
		rx_dat = PERIP(SPI1_DATA);  
		if ((buftail + 1) % SPI_BUF_SIZE != bufhead) {
			spi_buf[buftail] = rx_dat;
			buftail = (buftail + 1) % SPI_BUF_SIZE;
		}
		PERIP(SPI1_DATA) = (p1+i)->begin; //added line
		i++;			
	}
	i=0;
}

void print_spi_buf(void)
{
	while (buftail != bufhead) {		
		printf("SPI MOSI-->%x\n",spi_buf[bufhead]);
		bufhead = (bufhead + 1)% SPI_BUF_SIZE;// % SPI_BUF_SIZE;		
	}		
}

ioresult main (char *params) 			
{     	                 
	InitSpiSlave();
	
	while(1)
	{
		DelayL(10);
		print_spi_buf();
	}
	
	return S_OK;///Return OK
}
Hannu
VLSI Staff
Posts: 435
Joined: Mon 2016-05-30 11:54
Location: Finland
Contact:

Re: Using VS1010D as a spi slave

Post by Hannu »

Fidling with SPI_CF_EARLYINT and SPI_CF_XCSMODE could help.

The the value of SPI_DEFAULT is used when the TX side of the SPI is empty, so that shouldn't make any difference, except if you put it to some distinct value, it's easier to debug.

I think what happens is you get some extra interrupt or interrupt comes when you don't expect it it come.

And two bytes is just one word. And you are using SPI in 16 bit mode. so just one interrupt seems to be wrong.

Code: Select all

/* TOTALLY UNTESTED, MAYBE even NON-COMPILABLE  */
struct debug {
  u_int16 gpio1_I_data;
  u_int16 rx_data;
  u_int16 spi_st;
};
struct debug debugArr[64];
u_int16 ints = 0;

/* And then in interrupt: */
if (ints < 64) {
  debugArr[ints].gpio1_I_data = PERIP(GPIO1_IDATA);
  debugArr[ints].spi_st = PERIP(SPI1_STATUS);
  debugArr[ints].rx_data = rx_dat;
  PERIP(SPI1_DATA) = ints;
  ints++;
}
/* And in main loop over and print. */
With those, you can observe from the master side when interrupts are executed and on VS1010 side you see some info.
goncalo
User
Posts: 13
Joined: Fri 2023-01-13 12:57

Re: Using VS1010D as a spi slave

Post by goncalo »

Hi again :D


Testing of my code, i found that i couldn't send more than 10 bytes before it "explodes".

When i was researching how to develop a spi comunication solution for the DSP, as slave, i found some developers with the same question but with the difference that they wanted to use the DSP as master, so they develop a custom driver similar to the one used in the manual, viewtopic.php?f=15&t=2830&p=14441&hilit ... ver#p14441.

I tried some of those solutions and they work fine, but as master. I was thinking, if my problem is the interruption, i should try to develop a solution using a spi slave custom driver for the DSP, that can communicate to my other micro-controller. Is it even possible doing it this way?

Bellow is a portion of code from the thread i mentioned above, i can see that it is set as master because it sets the cs pin to high when it wants to send data.

Code: Select all

MEDIUM_DEVICE spiBus1;
#define __HWSPI_WAIT_UNTIL_TX_IDLE() {  while (hw->regs->status & SPI_ST_TXRUNNING); } 
	
ioresult DevHwSpiIoctl_fixed(register __i0 MEDIUM_DEVICE *dev, s_int16 request, char *argp) {
	if (request == IOCTL_END_FRAME) {
		devHwSpiHwInfo *hw=(devHwSpiHwInfo*)dev->hardwareInfo;
		__HWSPI_WAIT_UNTIL_TX_IDLE();
		hw->regs->config |= SPI_CF_FSIDLE;
		GpioSetPin(hw->csPin, 1); //cs pin high
		return S_OK;
	} else {
		return DevHwSpiIoctl(dev, request, argp);
	}
}
Im new to this community and i want to explore the capabilities of the VS1010, since I'm not having luck with the spi comunication, i´m currently using the UART for the communication but spi is the right solution for this project, so i want to make it work.





Best regards, thanks
Goncalo
Hannu
VLSI Staff
Posts: 435
Joined: Mon 2016-05-30 11:54
Location: Finland
Contact:

Re: Using VS1010D as a spi slave

Post by Hannu »

Hi,

I gave some thought and wrote blindly how SPI could work in interrupted slave mode. Could this help? And does it even work?

Youi'll get zero answer if there is overrun and otherwise you'll receive ramp.

And words are printed out in mainloop.
Attachments
spislave.zip
Totally untested idea how SPI slave might work.
(66.17 KiB) Downloaded 15 times
goncalo
User
Posts: 13
Joined: Fri 2023-01-13 12:57

Re: Using VS1010D as a spi slave

Post by goncalo »

Hi :D

I made some changes to the configuration, many thanks for the improved FIFO, i'm sending 1byte data to the DSP so i configured to 8 bit length and commented the SPI_CF_RXFIFOMODE | SPI_CF_RXFIFO_ENA | SPI_CF_TXFIFO_ENA , i also inverted the clk polarity, now im able to see in the DSP 1 byte data arriving from the master :D .

The problem is that the data bytes are reversed, i need to change the DSP SPI1 to big-endian if im not mistaken. In the datasheet i only see the SPI2_CFG_RX_BE configuration for the SPI2. How can i do it for the SPI1?

Code: Select all

#include <vo_stdio.h>
#include <vs1010dRom.h>
#include <vo_gpio.h>
#include <vs1010c.h>
#include <lcd.h>
#include <fifoy.h>
#include <vs1010_pins.h>

#define FIFO_SIZE 64
#define SPI_DIVIDER 0
__mem_y spiBuf[FIFO_SIZE];
__mem_y struct FIFOY spiFifo;

__mem_y u_int32 fifoFulls = 0;
u_int16 answer = 1;
void IntSpi1C(void) {
	while (PERIP(SPI1_STATUS) & (SPI_ST_RXFULL | SPI_ST_RXFIFOFULL)) {
		u_int16 data = PERIP(SPI1_DATA);
		if (FIFOYSpace(&spiFifo)) {
			FIFOYPut(&spiFifo, data);
			//PERIP(SPI1_DATA) = answer++;
		} else {
			fifoFulls++;
		}
	}
}


extern u_int16 IntSpi1Vector;

void DisableTV() {
	//Disable SPI1 TV-Out // disable tvout
	PERIP(INT_ENABLE0_LP) &= ~(INTF_SPI1); //Disable TV interrupt
	PERIP(INT_ENABLE0_HP) &= ~(INTF_SPI1); //Disable TV interrupt
	lcd0.clipy2 = 0; 	// Disable tv buffer and tv-out
	SetJmpiHookFunction((void*)102,VoidVoid);  //102=ScreenOutputFunction
}
void SetupSpi() {
	/* This configuration can be changed to master by flipping SPI1_CF_MASTER bit */
	//PERIP(SPI2_CFG)= SPI2_CFG_RX_BE;
	PERIP(SPI1_CF) =
		// SPI_CF_SRESET | 
		//SPI_CF_RXFIFOMODE |
		//SPI_CF_RXFIFO_ENA |
		//SPI_CF_TXFIFO_ENA |

	   //SPI_CF_XCSMODE | 
		//SPI_CF_INTXCS | 
		//SPI_CF_FALLXCS | 
		//SPI_CF_RISEXCS | 
		
		/* SPI_CF_MASTER | */
		SPI_CF_SLAVE |
		
		 //SPI_CF_DLEN | 
		SPI_CF_DLEN8 | 
		//SPI_CF_DLEN16 |
		 //SPI_CF_FSIDLE | 
		 //SPI_CF_FSIDLE1 | 
		/* SPI_CF_FSIDLE0 | */
		
		0; /* For bitwise OR */
	PERIP(SPI1_DEFAULT) = 0;

	/* Divider not really needed. */
	PERIP(SPI1_CLKCF) = (/*SPI_CC_CLKDIV * SPI_DIVIDER*/ SPI_CC_INV_CLKPOL  ) /* | SPI_CC_INV_CLKPOL | SPI_CC_INV_CLKPHASE */ ;
	WriteToProgramRam(0x20 + INTV_SPI1, ReadFromProgramRam((u_int16)&IntSpi1Vector));
	GpioSetAsPeripheral(PIN_MOSI1);
	GpioSetAsPeripheral(PIN_MISO1);
	GpioSetAsPeripheral(PIN_SCK1);
	GpioSetAsPeripheral(PIN_XCS1);
	PERIP(INT_ENABLE0_LP) |= INTF_SPI1;
}
ioresult main (char *params) {
	FIFOYInit(&spiFifo, spiBuf, sizeof(spiBuf));
	DisableTV();
	SetupSpi();
	while (1) {//((FIFOYGet(&spiFifo) & 0x0F) << 4 | (FIFOYGet(&spiFifo) & 0xF0) >> 4)>>1)
		if (FIFOYFill(&spiFifo)) {
		
			printf("Recv: 0x%x \n",FIFOYGet(&spiFifo ));
		}
	}
	PERIP(INT_ENABLE0_LP) &= ~INTF_SPI1;
	PERIP(INT_ENABLE0_HP) &= ~INTF_SPI1;
	return S_OK;
}

Many thanks and best regards, sincerely
Gonçalo
Hannu
VLSI Staff
Posts: 435
Joined: Mon 2016-05-30 11:54
Location: Finland
Contact:

Re: Using VS1010D as a spi slave

Post by Hannu »

So your master is sending 16-bit words? And are you on the charge of it?

like 0x1234 and the print loop prints 0x34, 0x12? - Little endian
Or do you get 0x27 0x48? - LSb first LE, broken SPI master. SPI is not UART with clock.

And if you transmit from your master which would work in 8-bit mode.

Code: Select all

/* MCU master mode code */
XCS_low();
spi_transfer(word >>8);
spi_transfer(word &0xff);
XCS_high();
the data should come correctly to VSDSP. And actually you can set DLEN to 15 and read it as one word.

There are also some tricks.

If there is time between XCS going down and SCK starting, you can get interrupt and check if TX is running. And set some variable to do the LE->BE conversion (SPI_CF_XCSMODE) The correct way would be send sane data as it is much easier to construct the messages on transmitter side than do hard parsing work on reception. At least I think the HW should work like that, but I have no evidence to one way or another. Yet.

Or if it is always 16 bits, Check RXFIFOFULL, shift to high 8-bit, RXFULL low 8-bit. Or other way around. You need to have RXFIFOMODE and FIFOs enabled. That way you even cut the interrupt load to half.

And even if you send some 8-bit opcode first and then 16-bit data, you can change the DLEN and it takes effect after the previous word is received. This might need some rethinking of the datastructure, if more than one transaction needs to be cached because the XCS infor mation is lost after reception. But if the YFIFO is always empty when a new transaction starts, then it isn't a problem.

Another thing is that now you use only half of the data, you can put information to high bits of the received words.

For a sneak preview, I've attached future datasheet draft version of SPI chapter of VSDSP SPI. SPI slave isn't documented as thoroughly as master. The slave mode should work quite much similar way.

There are new timing diagrams and explanation of operation of the peripheral in master mode. I hope it helps. And the differences between VS1005g and VS1010 SPI are that VS1010 has multibit support and SPI_CF_EARLYINT.

The SPI2 slave is an interesting perip. It lives in the XPERIP (common data interface) and it has some other limitations like getting just a few words out of it and quick answer etc. But for transferring constant fast data stream, it suits very well even it is limited to CLKI/4.

So what kind of protocol you have designed? Makes my life easier if I don't have to think all possibilities how to break code which could solve this problem.
Attachments
vs1005ds_081_spi_wip.pdf
SPI chapter of VS1005. Draft.
(215.6 KiB) Downloaded 15 times
goncalo
User
Posts: 13
Joined: Fri 2023-01-13 12:57

Re: Using VS1010D as a spi slave

Post by goncalo »

I´m sending from my master 1 word 0x1234, then i receive in the VSDSP 0x2416. I enabled SPI_CF_RXFIFO_ENA and changed the DLEN TO 16, the print in the VSDSP side is print(%04x).
Hannu
VLSI Staff
Posts: 435
Joined: Mon 2016-05-30 11:54
Location: Finland
Contact:

Re: Using VS1010D as a spi slave

Post by Hannu »

Your transmission has very little sense.

Code: Select all

0001 0010 0011 0100 0x1234 Should be
0010 0100 0001 0110 0x2416 Really is
The two first nibles would look like sampling happens early. On the other hand the the two last nibles could be going other direction.
Does the transfer work to other direction? How fast you are sending the data?

This looks like bad clock configuration. Or you have JP4 installed on dev board. Or power/ground problem in your setup.

Do you have logic analyzer or oscilloscope which can use to capture the signals if you are sure that the MCU and VSDSP have same idea how the SPI should work? I'm just curious because last time I almost touched polarity and phase configuration of SPI, I was actually hunting electrical problem. And that was many years ago.
Post Reply