Close

HOMER WP5: FPGA conf via USB

A project log for HOMER

2D GPU: HD resolution @ 60Hz, HDMI, double buff, hardware acceleration, USB config upgrade, ... For ARDUINO & other µC

papydoctorpapyDoctor 08/15/2015 at 20:500 Comments

I've managed to do a better video thanks to a tripod.

Progress:

- Now, it's possible to upload the FPGA configuration file via USB (VCOM port). It remains to store this file into the FLASH next to the LPC812, then the board will be ready at power on.

- I've made a little python application to do some tests with the board but also -and mainly- to upload image files to the embedded flash.


Here is the source C code for the animation. This code can easily be embedded in a small 8bit uC.

	//Init LisaGlyph
	for (uint32_t i=0; i<LISA_GLYPHS; i++)
		GlyphLisa[i] = LISA_FL_START_ADDRESS + i * LISA_FL_STEP_ADDRESS;

	//Init Lisa
	for (uint32_t i=0; i<N_LISA; i++) {
		Lisa[i].size = (SIZE_t){ LISA_SIZE_W, LISA_SIZE_H};
		Lisa[i].pos.X = rand() % 1000;
		Lisa[i].pos.Y = rand() % 500;
		Lisa[i].inc.X = rand() % 10 + 0.5;
		Lisa[i].inc.Y = i / 10.0 + 0.5;
		Lisa[i].idxGlyph = 0;
	}

	//Init HomerGlyph
	for (uint32_t i=0; i<HOMER_GLYPHS; i++)
		GlyphHomer[i] = HOMER_FL_START_ADDRESS + i * HOMER_FL_STEP_ADDRESS;

	//Init Homer
	for (uint32_t i=0; i<N_HOMER; i++) {
		Homer[i].size = (SIZE_t){ HOMER_SIZE_W, HOMER_SIZE_H};
		Homer[i].pos.X = rand() % 1000;
		Homer[i].pos.Y = rand() % 500;;
		Homer[i].inc.X = rand() % 10;
		Homer[i].inc.Y = i / 2.0;
		Homer[i].idxGlyph = 0;
	}

	//Init BallGlyph
	for (uint32_t i=0; i<BALL_GLYPHS; i++)
		GlyphBall[i] = BALL_FL_START_ADDRESS + i * BALL_FL_STEP_ADDRESS;

	Ball.size = (SIZE_t){ BALL_SIZE_W, BALL_SIZE_H};
	Ball.pos.X = 500;
	Ball.pos.Y = 100;
	Ball.vit.X = 7.0;
	Ball.acc.X = 0;
	Ball.vit.Y = 0;
	Ball.acc.Y = 1.5;
	Ball.idxGlyph = 0;


	GlyphMur = MUR_FL_START_ADDRESS;
	Mur.size = (SIZE_t){ MUR_SIZE_W, MUR_SIZE_H};
	Mur.pos.X = 500;
	Mur.pos.Y = 719-512;

	//Init display
	GPUstorePalette(0);
	GPUclipArea((COORD_t){0, 0}, (COORD_t){1279, 719});

	while (1) {

		GPUclearScreen(45);

		for (uint32_t i=0; i<N_HOMER/2; i++)
			GPUcopyBlob(
					GlyphHomer[ Homer[i].idxGlyph ],
					(COORD_t){ (int16_t)Homer[i].pos.X, (int16_t)Homer[i].pos.Y },
					Homer[i].size,
					0
					);

		for (uint32_t i=0; i<N_LISA/2; i++)
			GPUcopyBlob(
					GlyphLisa[ Lisa[i].idxGlyph ],
					(COORD_t){ (int16_t)Lisa[i].pos.X, (int16_t)Lisa[i].pos.Y },
					Lisa[i].size,
					0
					);

		GPUcopyBlob(
				GlyphMur,
				(COORD_t){ (int16_t)Mur.pos.X, (int16_t)Mur.pos.Y },
				Mur.size,
				0
				);

		for (uint32_t i=N_LISA/2; i<N_LISA; i++)
			GPUcopyBlob(
					GlyphLisa[ Lisa[i].idxGlyph ],
					(COORD_t){ (int16_t)Lisa[i].pos.X, (int16_t)Lisa[i].pos.Y },
					Lisa[i].size,
					0
					);

		for (uint32_t i=N_HOMER/2; i<N_HOMER; i++)
			GPUcopyBlob(
					GlyphHomer[ Homer[i].idxGlyph ],
					(COORD_t){ (int16_t)Homer[i].pos.X, (int16_t)Homer[i].pos.Y },
					Homer[i].size,
					0
					);


		GPUcopyBlob(
				GlyphBall[ Ball.idxGlyph ],
				(COORD_t){ (int16_t)Ball.pos.X, (int16_t)Ball.pos.Y },
				Ball.size,
				0
				);

		GPUdrawLine((COORD_t){0, 0}, (COORD_t){ 1279, 0}, 15, 0);
		GPUdrawLine((COORD_t){0, 0}, (COORD_t){ 0, 719}, 15, 0);
		GPUdrawLine((COORD_t){1279, 0}, (COORD_t){ 1279, 719}, 15, 0);
		GPUdrawLine((COORD_t){0, 719}, (COORD_t){ 1279, 719}, 15, 0);

		for (uint32_t i=0; i<N_LISA; i++) {
			Lisa[i].pos.X += Lisa[i].inc.X;
			Lisa[i].pos.Y += Lisa[i].inc.Y;

			if (Lisa[i].pos.X > SCR_W - Lisa[i].size.W  || Lisa[i].pos.X < 0 ) {
				Lisa[i].inc.X = - Lisa[i].inc.X;
				Lisa[i].pos.X += Lisa[i].inc.X;
			}
			if (Lisa[i].pos.Y > SCR_H - Lisa[i].size.H || Lisa[i].pos.Y < 0 ) {
				Lisa[i].inc.Y = - Lisa[i].inc.Y;
				Lisa[i].pos.Y += Lisa[i].inc.Y;
			}

			Lisa[i].idxGlyph = ((uint16_t)(Lisa[i].pos.X/5)) % LISA_GLYPHS;
		}

		for (uint32_t i=0; i<N_HOMER; i++) {
			Homer[i].pos.X += Homer[i].inc.X;
			Homer[i].pos.Y += Homer[i].inc.Y;

			if (Homer[i].pos.X > SCR_W - Homer[i].size.W  || Homer[i].pos.X < 0 ) {
				Homer[i].inc.X = - Homer[i].inc.X;
				Homer[i].pos.X += Homer[i].inc.X;
			}
			if (Homer[i].pos.Y > SCR_H - Homer[i].size.H || Homer[i].pos.Y < 0 ) {
				Homer[i].inc.Y = - Homer[i].inc.Y;
				Homer[i].pos.Y += Homer[i].inc.Y;
			}

			Homer[i].idxGlyph = ((uint16_t)(Homer[i].pos.X/20)) % HOMER_GLYPHS;
		}

			Ball.vit.X += Ball.acc.X;
			Ball.vit.Y += Ball.acc.Y;

			Ball.pos.X += Ball.vit.X;
			Ball.pos.Y += Ball.vit.Y;



			if (Ball.pos.X > SCR_W - Ball.size.W  || Ball.pos.X < 0 ) {
				Ball.vit.X = - Ball.vit.X;
				Ball.pos.X += Ball.vit.X;
			}
			if (Ball.pos.Y > SCR_H - Ball.size.H || Ball.pos.Y < 0 ) {
				Ball.vit.Y = - Ball.vit.Y;
				Ball.pos.Y += Ball.vit.Y;
			}

			Ball.idxGlyph = ((uint16_t)(Ball.pos.X/5)) % BALL_GLYPHS;


		//Display it!
		GPUswapBuffer();

	}

Here is for example the C definition of a GPU function, Tx_Buff is a buffer whose the byte elements are sent to the SPI. Address is the blob address in the 8MBytes Flash connected to the FPGA.

Here I use DMA but simple transfert work also.

void GPUcopyBlob(uint32_t Address, COORD_t P1, SIZE_t Size, uint8_t operation)
{
	Tx_Buf[0] = CMD_COPY_BLOB;
	Tx_Buf[1] = 12; //DATA LENGTH
	Tx_Buf[2] = Address >> 16;	// FlashAddress MSB
	Tx_Buf[3] = Address >> 8;	// FlashAddress mSB
	Tx_Buf[4] = Address;		// FlashAddress LSB
	Tx_Buf[5] = P1.X >> 8;
	Tx_Buf[6] = P1.X;
	Tx_Buf[7] = P1.Y >> 8;
	Tx_Buf[8] = P1.Y;
	Tx_Buf[9] = Size.W >> 8;
	Tx_Buf[10] = Size.W;
	Tx_Buf[11] = Size.H >> 8;
	Tx_Buf[12] = Size.H;
	Tx_Buf[13] = operation;

	// Start transmission
	SSP_DMA_Start(14, Tx_Buf, Rx_Buf);
}

I've made a SPI speed test, it's possible to go up to 32Mbits/s. That's well within the need. SPI is not the bottleneck here since the receive only commands, not big blobs of data.

Discussions