A simple game on STM32F429I Discovery board

In this past two weeks I spent some time learning how to create a simple graphic application for my STM32F429I-DISCOVERY board. Since it has a QVGA display with a ILI9341 display controller on board and a STM32F429ZI microcontroller I thought I should give it a try. I’ve been interfacing displays with SPI and 8080 parallel interfaces but since the ILI9341 offers the opportunity to drive the panel using a parallel RGB interface I felt that was the way to go. The STM32 micro has a LTDC display controller that provides all the signals needed to use the RGB interface. I wrote a post on how to configure and use it. It has its own DMA and manages to send all RGB data from a framebuffer in memory to the TFT display generating all the necessary signals to refresh correctly the LCD, leaving the CPU free to do other jobs (for example updating the back buffer). Since I wanted to animate sprites I used double buffering and a single LTDC layer so I needed some extra RAM.  Luckyly the board has a 64Mbit (8MB) external SDRAM and the micro has a Flexible Memory Controller that makes accessing the external memory a breeze. So I implemented a little graphic library with some functions to draw on the framebuffer and to swap the buffers once finished:

static volatile uint8_t *framebuffer;

void graphics_init(void)
{
	LTDC_init();
	ILI9341_init();
}

void graphics_draw_pixel(int x,int y,int red,int green,int blue)
{
	framebuffer = LTDC_get_backbuffer_address();
	framebuffer[(x + y * DISPLAY_WIDTH) * 3] = (red & 0xFF);
	framebuffer[(x + y * DISPLAY_WIDTH) * 3 + 1] = (green & 0xFF);
	framebuffer[(x + y * DISPLAY_WIDTH) * 3 + 2] = (blue & 0xFF);
}

void graphics_draw_background(const uint8_t *background)
{
	framebuffer = LTDC_get_backbuffer_address();
	for (int i = 0 ; i < DISPLAY_WIDTH * DISPLAY_HEIGHT * BPP ; i++)
			framebuffer[i] = background[i];
}

void graphics_restore_background(const uint8_t *background,int x,int y,int width,int height)
{
	framebuffer = LTDC_get_backbuffer_address();
	for (int h = 0 ; h < height ; h++)
		for (int w = 0 ; w < width ; w++)
		{
			framebuffer[((x + w) + (y + h) * DISPLAY_WIDTH) * 3] = background[((x + w) + (y + h) * DISPLAY_WIDTH) * 3];
			framebuffer[((x + w) + (y + h) * DISPLAY_WIDTH) * 3 + 1] = background[((x + w) + (y + h) * DISPLAY_WIDTH) * 3 + 1];
			framebuffer[((x + w) + (y + h) * DISPLAY_WIDTH) * 3 + 2] = background[((x + w) + (y + h) * DISPLAY_WIDTH) * 3 + 2];
		}
}

void graphics_draw_image(const uint8_t *image,int x,int y,int width,int height)
{
	framebuffer = LTDC_get_backbuffer_address();
	for (int h = 0 ; h < height ; h++)
		for (int w = 0 ; w < width ; w++)
		{
			framebuffer[(x + w) * 3 + (y + h) * DISPLAY_WIDTH * 3] = image[(w + h * width) * 3];
			framebuffer[(x + w) * 3 + (y + h) * DISPLAY_WIDTH * 3 + 1] = image[(w + h * width) * 3 + 1];
			framebuffer[(x + w) * 3 + (y + h) * DISPLAY_WIDTH * 3 + 2] = image[(w + h * width) * 3 + 2];
		}
}

void graphics_draw_transparent_image(const uint8_t *image,int x,int y,int width,int height)
{
	framebuffer = LTDC_get_backbuffer_address();
	uint8_t transparent1 = *image;
	uint8_t transparent2 = *(image + 1);
	uint8_t transparent3 = *(image + 2);
	for (int h = 0 ; h < height ; h++)
		for (int w = 0 ; w < width ; w++)
			if (!(image[(w + h * width) * 3] == transparent1 && image[(w + h * width) * 3 + 1] == transparent2 && image[(w + h * width) * 3 + 2] == transparent3))
			{
				framebuffer[(x + w) * 3 + (y + h) * DISPLAY_WIDTH * 3] = image[(w + h * width) * 3];
				framebuffer[(x + w) * 3 + (y + h) * DISPLAY_WIDTH * 3 + 1] = image[(w + h * width) * 3 + 1];
				framebuffer[(x + w) * 3 + (y + h) * DISPLAY_WIDTH * 3 + 2] = image[(w + h * width) * 3 + 2];
			}
}

void graphics_draw_texture(int x,int y,const uint8_t *texture_atlas,int atlas_width,int atlas_height,int texture_width,int texture_height,int index)
{
	framebuffer = LTDC_get_backbuffer_address();
	int start_pixel = (index % atlas_width) * texture_width + (index / atlas_width) * texture_width * texture_height * atlas_width;
	for (int h = 0 ; h < texture_height ; h++)
		for (int w = 0 ; w < texture_width ; w++)
		{
			framebuffer[((x + w) + (y + h) * DISPLAY_WIDTH) * 3] = texture_atlas[(start_pixel + w + h * texture_width * atlas_width) * 3];
			framebuffer[((x + w) + (y + h) * DISPLAY_WIDTH) * 3 + 1] = texture_atlas[(start_pixel + w + h * texture_width * atlas_width) * 3 + 1];
			framebuffer[((x + w) + (y + h) * DISPLAY_WIDTH) * 3 + 2] = texture_atlas[(start_pixel + w + h * texture_width * atlas_width) * 3 + 2];
		}
}

void graphics_draw_transparent_texture(int x,int y,const uint8_t *texture_atlas,int atlas_width,int atlas_height,int texture_width,int texture_height,int index)
{
	framebuffer = LTDC_get_backbuffer_address();
	int start_pixel = (index % atlas_width) * texture_width + (index / atlas_width) * texture_width * texture_height * atlas_width;
	uint8_t transparent1 = texture_atlas[start_pixel];
	uint8_t transparent2 = texture_atlas[start_pixel + 1];
	uint8_t transparent3 = texture_atlas[start_pixel + 2];
	for (int h = 0 ; h < texture_height ; h++)
		for (int w = 0 ; w < texture_width ; w++)
			if (!(texture_atlas[(start_pixel + w + h * texture_width * atlas_width) * 3] == transparent1 && texture_atlas[(start_pixel + w + h * texture_width * atlas_width) * 3 + 1] == transparent2 && texture_atlas[(start_pixel + w + h * texture_width * atlas_width) * 3 + 2] == transparent3))
			{
				framebuffer[((x + w) + (y + h) * DISPLAY_WIDTH) * 3] = texture_atlas[(start_pixel + w + h * texture_width * atlas_width) * 3];
				framebuffer[((x + w) + (y + h) * DISPLAY_WIDTH) * 3 + 1] = texture_atlas[(start_pixel + w + h * texture_width * atlas_width) * 3 + 1];
				framebuffer[((x + w) + (y + h) * DISPLAY_WIDTH) * 3 + 2] = texture_atlas[(start_pixel + w + h * texture_width * atlas_width) * 3 + 2];
			}
}

void graphics_swap_window(void)
{
	LTDC_switch_framebuffer();
}

It initializes the ILI9341 in SPI mode and then configures the ILI9341 display controller to use the RGB interface. Then the LTDC kicks in, initializes the SDRAM controller (FMC) and sets up two frame buffers in external memory. Then I configure the LTDC, one of the layers and everything is good to go.

// to be continued

Leave a Reply

Your email address will not be published. Required fields are marked *