Decoding the Gameboy LCD output.

Post Reply
ZeroWalker
Posts: 57
Joined: Wed Nov 01, 2017 6:17 pm

Re: Decoding the Gameboy LCD output.

Post by ZeroWalker » Mon Nov 06, 2017 11:26 am

Okay did a test using digitalWrite(PB3,1) before sending via serial and digitalWrite(PB3,0) afterwards.

It seems to take about 18.88 ms, which is huge O.o, basically more than one entire frame at 60fps.

Image

EDIT:

okay thought it was weird, forgot to take it After reading the SPI lol xd.
Here's just the USB Serial, it takes about 3ms, which is 3x too slow basically.

Image

stevestrong
Posts: 1735
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Decoding the Gameboy LCD output.

Post by stevestrong » Mon Nov 06, 2017 10:48 pm

Which function do you use to send over USB serial? How many bytes? Byte by byte? Or buffered?

ZeroWalker
Posts: 57
Joined: Wed Nov 01, 2017 6:17 pm

Re: Decoding the Gameboy LCD output.

Post by ZeroWalker » Mon Nov 06, 2017 10:50 pm

uint8_t Data0Buffer[2880];
Serial.write(Data0Buffer, sizeof(Data0Buffer));

stevestrong
Posts: 1735
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Decoding the Gameboy LCD output.

Post by stevestrong » Mon Nov 06, 2017 11:42 pm

Well, this seems to be a very good throughput, ~960kBps!
You will not get it any faster than that.
You have to compress the data before you send it.

ZeroWalker
Posts: 57
Joined: Wed Nov 01, 2017 6:17 pm

Re: Decoding the Gameboy LCD output.

Post by ZeroWalker » Mon Nov 06, 2017 11:50 pm

Hmm, well then i am probably screwed, i think i need to send like 300 bytes max in order to not make it too slow,
and good luck compressing 2880 to 300bytes;p

ZeroWalker
Posts: 57
Joined: Wed Nov 01, 2017 6:17 pm

Re: Decoding the Gameboy LCD output.

Post by ZeroWalker » Tue Nov 07, 2017 12:36 am

hmm, wouldn't it be possible to double buffer it with DMA?
So you DMA it, get an interrupt, send the data, and while the data is sent a DMA will happen in a secondary buffer,
then when that interrupts it sends from that and the DMA will happen on the first buffer etc.

User avatar
RogerClark
Posts: 7136
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Decoding the Gameboy LCD output.

Post by RogerClark » Tue Nov 07, 2017 1:08 am

Yes. You can double buffer the DMA. I do that for output to addressable LED

So the USB serial can be sent while the next line of data is being received.

There is some increase in RAM access time, but hardly anything unless you are doing DMA to memory and running another memory to memory DMA

(There is a long thread about DMA RAM performance on the forum somewhere)

ZeroWalker
Posts: 57
Joined: Wed Nov 01, 2017 6:17 pm

Re: Decoding the Gameboy LCD output.

Post by ZeroWalker » Tue Nov 07, 2017 1:30 am

Well trying to do it with double buffering, sadly it freezes here as well, it should swap buffers,
and i should have like 15ms between the two (haven't tested though, but if it's grabbing one frame at least that's what it should be).

Code: Select all

#include <SPI.h>
#define BUFFER_SIZE 2880 // 1 bit per pixel (144*160/8)
uint8_t Data0_Buffer[BUFFER_SIZE];
uint8_t Data1_Buffer[BUFFER_SIZE];

dma_tube_config setDMABuffer(void* buffer, voidFuncPtr function)
{

	dma_disable(DMA1, DMA_CH2);	// Disable the DMA tube.
	dma_detach_interrupt(DMA1, DMA_CH2);

	// DMA tube configuration for SPI1 Rx - channel 2
	dma_tube_config my_tube_cfg = {
		&SPI1->regs->DR,	// data source address
		DMA_SIZE_32BITS,	// source transfer size
		buffer,		// data destination address 
		DMA_SIZE_32BITS,	// destination transfer size
		BUFFER_SIZE,	// nr. of data to transfer
								// tube flags: auto increment dest addr, circular buffer, set tube full IRQ, very high prio:
								(DMA_CFG_CMPLT_IE | DMA_CCR_PL_VERY_HIGH),
								0,	// unused
								DMA_REQ_SRC_SPI1_RX,	// Hardware DMA request source
	};
	int ret = dma_tube_cfg(DMA1, DMA_CH2, &my_tube_cfg);	// SPI1 Rx channel is nr. 2
	//dma_set_num_transfers(DMA1, DMA_CH2, 2880);
	dma_attach_interrupt(DMA1, DMA_CH2, function);	// attach an interrupt handler.

	return my_tube_cfg;
}

// Buffer 1 has been filled
void DMACompleted1()
{
	// Change DMA Buffer
	setDMABuffer(Data0_Buffer, DMACompleted0);

	//Serial.write(Data1_Buffer, 400);

	// Enable DMA
	dma_enable(DMA1, DMA_CH2);
	// delay test (Causes hard lock)
	delay(1);

	Serial.print("Buffer1\r\n");
}

// Buffer 0 has been filled
void DMACompleted0()
{
	// Change DMA Buffer
	setDMABuffer(Data1_Buffer, DMACompleted1);
	// Enable DMA
	dma_enable(DMA1, DMA_CH2);
	//Serial.write(Data0Buffer, sizeof(Data0Buffer));
	Serial.print("Buffer0\r\n");
}

// setup DMA to tunnel data into a buffer and execute an interruption when it's filled
void setupDMA()
{
	dma_init(DMA1);	// init DMA clock domain
	dma_tube_config my_tube_cfg = setDMABuffer(Data0_Buffer, DMACompleted0);

}


// Start DMA on first VSYNC rise (to sync up the frame)
void VsyncOccur()
{
	// disable the interrupt
	exti_detach_interrupt(EXTI0);
	// enable DMA
	dma_enable(DMA1, DMA_CH2);	// Enable the DMA tube. It will now begin serving requests.
}

// Setup SPI 1 to begin receiving data via DMA
void setupSPI()
{
	// Set to SPI 1
	SPI.setModule(1);

	// initialize SPI 1
	spi_init(SPI1);

	// start SPI 1 receiving
	spi_slave_enable(SPI1, SPI_MODE1, SPI_FRAME_LSB | SPI_SW_SLAVE | SPI_DFF_8_BIT | SPI_RX_ONLY);
	// enable SPI 1 RX DMA
	spi_rx_dma_enable(SPI1);
}

// the setup function runs once when you press reset or power the board
void setup()
{
	// Wait for USB Serial
	while (!Serial) {}

	delay(400);


	setupSPI();

	exti_attach_interrupt(EXTI0, EXTI_PB, VsyncOccur, EXTI_RISING);

	pinMode(PB3, OUTPUT);
	pinMode(PB1, INPUT);
	pinMode(PB0, INPUT);
	pinMode(PB12, INPUT);
	pinMode(PB15, INPUT);

	setupDMA();
}





// the loop function runs over and over again until power down or reset
void loop() {

	while (1) delay(1000);
}

ZeroWalker
Posts: 57
Joined: Wed Nov 01, 2017 6:17 pm

Re: Decoding the Gameboy LCD output.

Post by ZeroWalker » Tue Nov 07, 2017 5:49 am

Okay it seems that i can get DMA to work and do the sending, If the interruption is fast,
so basically i have to use flags that's set in the interruption that then gets checked in "loop".
Not sure if this is how it's supposed to be or if it's a bug?

stevestrong
Posts: 1735
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany

Re: Decoding the Gameboy LCD output.

Post by stevestrong » Wed Nov 08, 2017 10:12 am

I am usually using only one buffer but double sized (to contain two lines data) instead of two distinct buffers.
The advantage is that you can leave the DMA to run in circular mode (continuously), and you must not switch the DMA address from one buffer to other.
The ISR is much shorter and is only used to signalize a flag to the main loop.
Actually it can run without using the interrupts at all, just checking in the main loop for the complete/half complete flag in the DMA status reg. These flags must be reset by software afterwards.

So when the first half Rx is done, you process the first half in the main loop, and when the lower half Rx is done, then you process the lower half.
During you process (compress and send to USB serial) one half, the other half is received in the background.
The compress and send process should take less time then a line period, otherwise Rx data will overflow.

Post Reply