Streaming video on SPI LCD (updates)


I posted earlier a video and some details on this. I was not happy with the small LCD, limited resolution and limited viewing angles. I tied getting a IPS display but was very slow and could not get it working at anything above 2 Mhz. However, recently I found an amazing LCD from GeekPi (I guess is their name?): it is using a ILI9341 display, very bright and with pretty decent viewing angles but the interesting part is the fact that you can set it as parallel *OR* SPI LCD running at 8/16/18 bits. Just few resistors shaped and voila! It does have a SPI flash and SPI touch. A combination of possible connectors that can be populated later, which makes it really cool!

Anyway, with a bit of work and some tweaking I managed to get it stream a frame (320*240, 16 bit/pixel) in slightly over 27ms, technically can do slightly over 36 FPS.

The video below shows it in action. The board used is a custom built board using a LPC4320 MCU, a 32 Mbyte SDRAM and the code, for now, is running on the M4 core clocked at 196 Mhz. The camera used is a OV7670 with FIFO. Still working on the code for the non-FIFO version.

As capturing and processing the frames takes another 20+ ms for now, I managed to get 20 frames per second. The image looks actually better on the LCD in reality.

Anyway, the code is actually very simple I am using SSP1 as the SSP0 is used for the flash, it is configured with SPI frame format, 16 bits transfer, bitrate set to 50.000.000. The LCD initialisation bit is simple, however, using the default registries from Adafruit library shown strange colors so I had to tweak it a bit. the sequence used is:

write_command(0xed);		
write_data(0x64);
write_data(0x03);
write_data(0x12);
write_data(0x81);

write_command(0xf7);		
write_data(0x10);

write_command(0xc0);		// Power Control 1
write_data(0x23);

write_command(0xc1);		// Power Control 2
write_data(0x10);

write_command(0xc5);		
write_data(0x31);
write_data(0x3c);

write_command(0xc7);		
write_data(0xc0);

write_command(0x36);		// Memory Access Control
write_data(0x60);  			    // RGB + rotated

write_command(0x37);	
write_data(0x00);  			

write_command(0x3A);		// Interface Pixel Format
#if _16_BIT == 1
    write_data(0x55);  			// use 16 bits per pixel color
#else
    write_data(0x66);  			// use 18 bits per pixel color

#endif

write_command(0xb1);		
write_data(0x00);
write_data(0x18);

write_command(0xb6);		
write_data(0x08);
write_data(0x82);
write_data(0x27);

write_command(0xf2);		
write_data(0x00);

write_command(0x26);		
write_data(0x03);

write_command(0xE0);		// PGAMCTRL(Positive Gamma Control)
write_data(0x0F);
write_data(0x31);
write_data(0x28);
write_data(0x0C);
write_data(0x0e);
write_data(0x08);
write_data(0x4e);
write_data(0xf1);
write_data(0x37);
write_data(0x07);
write_data(0x10);
write_data(0x03);
write_data(0x0e);
write_data(0x09);
write_data(0x00);

write_command(0xE1);		// NGAMCTRL(Negative Gamma Control)
write_data(0x00);
write_data(0x0e);
write_data(0x14);
write_data(0x03);
write_data(0x11);
write_data(0x07);
write_data(0x31);
write_data(0xc1);
write_data(0x48);
write_data(0x08);
write_data(0x0f);
write_data(0x0c);
write_data(0x31);
write_data(0x36);
write_data(0x0f);

write_command(0x11);  		// Sleep OUT

delay_us(10000);   			// wait some time
write_command(0x29);  		// Display ON

delay_us(10000);   			// wait some time
write_command(0x13);  		// Display ON

The code I used for streaming is below. I set it to send the data in packets, however, will not make any difference in speed if you send it in one go or smaller packets. As the commands are sent as 8 bit, I switch to 16 bits when streaming data then back to 8 bit after.

__RAMFUNC(RAM) volatile uint8_t streamFrameToLCDC (uint32_t length, uint16_t *imageBuffer, uint16_t width, uint16_t height){
	uint32_t counter = 0;
	uint16_t packetSize = (length/2)/PACKETSIZE;

	setAddrWindow(0, 0, width-1, height-1);
	TFT_DC_HIGH;

	Chip_SSP_SetFormat(LPC_SSP1, SSP_BITS_16, SSP_FRAMEFORMAT_SPI, SSP_CLOCK_CPHA0_CPOL0);

	for (counter = 0; counter < packetSize; counter++){
		while (Chip_SSP_GetStatus(LPC_SSP1, SSP_STAT_RNE)) {
			Chip_SSP_ReceiveFrame(LPC_SSP1);
		}

		Chip_SSP_ClearIntPending(LPC_SSP1, SSP_INT_CLEAR_BITMASK);
		uint16_t *wdata16;
		uint32_t tx_cnt = 0;
		wdata16 = &imageBuffer[counter*PACKETSIZE];

		while (tx_cnt < PACKETSIZE) {
			if ((Chip_SSP_GetStatus(LPC_SSP1, SSP_STAT_TNF) == SET) && (tx_cnt < PACKETSIZE)) {
				Chip_SSP_SendFrame(LPC_SSP1, *wdata16);
				wdata16++;
				tx_cnt++;
			}

		}
	}

	Chip_SSP_SetFormat(LPC_SSP1, SSP_BITS_8, SSP_FRAMEFORMAT_SPI, SSP_CLOCK_CPHA0_CPOL0);

	return 1;
}

Leave a comment