Objective

This tutorial will guide you through connecting an ILI9341 TFT display to an STM32F103RB microcontroller on a Nucleo-64 board, displaying text, and rotating the display orientation using a push button.

ILI8341 TFT Display test

 

Components Required

  • STM32F103RB Nucleo-64 Board
  • ILI9341 TFT Display
  • Push Button
  • Connecting Wires

Step 1: Connecting the ILI9341 TFT Display

Connect the ILI9341 TFT display to the Nucleo-64 board as follows:

ILI9341 Pin Nucleo-64 Pin
VCC 3.3V
GND GND
CS PA8
RESET PA10
DC/RS PA9
MOSI PB15
SCK PB13
LED 3.3V
MISO PB14
T_CLK Not connected
T_CS Not connected
T_DIN Not connected
T_DO Not connected
T_IRQ Not connected

Step 2: Initialize SPI2 and GPIO Pins

Use STM32CubeMX to configure the SPI2 peripheral and the GPIO pins. Here’s the configuration:

  • SPI2 Configuration:
    • Mode: Master
    • Direction: 2 Lines
    • Data Size: 8 Bits
    • Clock Polarity (CPOL): Low
    • Clock Phase (CPHA): 1 Edge
    • NSS: Software
    • Baud Rate Prescaler: 8
    • First Bit: MSB First
  • GPIO Configuration:
    • PA8, PA9, PA10 as Output
    • PB13, PB14, PB15 as Alternate Function Push Pull

Step 3: Configure the Button

Configure PC13 as an input with an external interrupt.

Step 4: Implement the Code

main.h

Update your main.h file as follows:

#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

#include "stm32f1xx_hal.h"

void Error_Handler(void);

#define ILI9341_RESET_PIN GPIO_PIN_10
#define ILI9341_DC_PIN GPIO_PIN_9
#define ILI9341_CS_PIN GPIO_PIN_8
#define ILI9341_RESET_PORT GPIOA
#define ILI9341_DC_PORT GPIOA
#define ILI9341_CS_PORT GPIOA
#define BUTTON_PIN GPIO_PIN_13
#define BUTTON_PORT GPIOC

extern SPI_HandleTypeDef hspi2;
extern volatile uint8_t display_orientation;

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */
main.h

main.c

Here is the main.c file:

#include "main.h"
#include "fonts.h"

SPI_HandleTypeDef hspi2;
volatile uint8_t display_orientation = 0; // 0: 0 degrees, 1: 90 degrees, 2: 180 degrees, 3: 270 degrees

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI2_Init(void);
void ILI9341_WriteCommand(uint8_t cmd);
void ILI9341_WriteData(uint8_t data);
void ILI9341_Init(void);
void ILI9341_SetAddressWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
void ILI9341_FillScreen(uint16_t color);
void ILI9341_DrawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bgcolor, uint8_t size);
void ILI9341_DrawString(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bgcolor, uint8_t size);
void ILI9341_SetOrientation(uint8_t orientation);
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);

int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI2_Init();
ILI9341_Init();

uint16_t colors[] = {0xF800, 0x07E0, 0x001F}; // Red, Green, Blue
int colorIndex = 0;

while (1) {
ILI9341_FillScreen(colors[colorIndex]);
ILI9341_DrawString(10, 10, "Hello, World!", 0xFFFF, colors[colorIndex], 2); // size 2 for larger text
ILI9341_DrawString(10, 30, "This is GCLD test!", 0xFFFF, colors[colorIndex], 2); // size 2 for larger text

colorIndex = (colorIndex + 1) % 3;
HAL_Delay(3000); // Delay 3 seconds
}
}

void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
Error_Handler();
}
}

static void MX_SPI2_Init(void) {
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK) {
Error_Handler();
}
}

static void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10, GPIO_PIN_RESET);

GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_13 | GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

// Configure PC13 as input with external interrupt
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

// Enable and set EXTI line 15 to 10 Interrupt to the lowest priority
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}

void ILI9341_WriteCommand(uint8_t cmd) {
HAL_GPIO_WritePin(ILI9341_DC_PORT, ILI9341_DC_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(ILI9341_CS_PORT, ILI9341_CS_PIN, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi2, &cmd, 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(ILI9341_CS_PORT, ILI9341_CS_PIN, GPIO_PIN_SET);
}

void ILI9341_WriteData(uint8_t data) {
HAL_GPIO_WritePin(ILI9341_DC_PORT, ILI9341_DC_PIN, GPIO_PIN_SET);
HAL_GPIO_WritePin(ILI9341_CS_PORT, ILI9341_CS_PIN, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi2, &data, 1, HAL_MAX_DELAY);
HAL_GPIO_WritePin(ILI9341_CS_PORT, ILI9341_CS_PIN, GPIO_PIN_SET);
}

void ILI9341_Init(void) {
HAL_GPIO_WritePin(ILI9341_RESET_PORT, ILI9341_RESET_PIN, GPIO_PIN_RESET);
HAL_Delay(20);
HAL_GPIO_WritePin(ILI9341_RESET_PORT, ILI9341_RESET_PIN, GPIO_PIN_SET);
HAL_Delay(150);

ILI9341_WriteCommand(0xEF);
ILI9341_WriteData(0x03);
ILI9341_WriteData(0x80);
ILI9341_WriteData(0x02);

ILI9341_WriteCommand(0xCF);
ILI9341_WriteData(0x00);
ILI9341_WriteData(0xC1);
ILI9341_WriteData(0x30);

ILI9341_WriteCommand(0xED);
ILI9341_WriteData(0x64);
ILI9341_WriteData(0x03);
ILI9341_WriteData(0x12);
ILI9341_WriteData(0x81);

ILI9341_WriteCommand(0xE8);
ILI9341_WriteData(0x85);
ILI9341_WriteData(0x00);
ILI9341_WriteData(0x78);

ILI9341_WriteCommand(0xCB);
ILI9341_WriteData(0x39);
ILI9341_WriteData(0x2C);
ILI9341_WriteData(0x00);
ILI9341_WriteData(0x34);
ILI9341_WriteData(0x02);

ILI9341_WriteCommand(0xF7);
ILI9341_WriteData(0x20);

ILI9341_WriteCommand(0xEA);
ILI9341_WriteData(0x00);
ILI9341_WriteData(0x00);

ILI9341_WriteCommand(0xC0);
ILI9341_WriteData(0x23);

ILI9341_WriteCommand(0xC1);
ILI9341_WriteData(0x10);

ILI9341_WriteCommand(0xC5);
ILI9341_WriteData(0x3e);
ILI9341_WriteData(0x28);

ILI9341_WriteCommand(0xC7);
ILI9341_WriteData(0x86);

ILI9341_WriteCommand(0x36);
ILI9341_WriteData(0x48); // Default to 0 degrees

ILI9341_WriteCommand(0x3A);
ILI9341_WriteData(0x55);

ILI9341_WriteCommand(0xB1);
ILI9341_WriteData(0x00);
ILI9341_WriteData(0x18);

ILI9341_WriteCommand(0xB6);
ILI9341_WriteData(0x08);
ILI9341_WriteData(0x82);
ILI9341_WriteData(0x27);

ILI9341_WriteCommand(0xF2);
ILI9341_WriteData(0x00);

ILI9341_WriteCommand(0x26);
ILI9341_WriteData(0x01);

ILI9341_WriteCommand(0xE0);
ILI9341_WriteData(0x0F);
ILI9341_WriteData(0x31);
ILI9341_WriteData(0x2B);
ILI9341_WriteData(0x0C);
ILI9341_WriteData(0x0E);
ILI9341_WriteData(0x08);
ILI9341_WriteData(0x4E);
ILI9341_WriteData(0xF1);
ILI9341_WriteData(0x37);
ILI9341_WriteData(0x07);
ILI9341_WriteData(0x10);
ILI9341_WriteData(0x03);
ILI9341_WriteData(0x0E);
ILI9341_WriteData(0x09);
ILI9341_WriteData(0x00);

ILI9341_WriteCommand(0xE1);
ILI9341_WriteData(0x00);
ILI9341_WriteData(0x0E);
ILI9341_WriteData(0x14);
ILI9341_WriteData(0x03);
ILI9341_WriteData(0x11);
ILI9341_WriteData(0x07);
ILI9341_WriteData(0x31);
ILI9341_WriteData(0xC1);
ILI9341_WriteData(0x48);
ILI9341_WriteData(0x08);
ILI9341_WriteData(0x0F);
ILI9341_WriteData(0x0C);

ILI9341_WriteCommand(0x11);
HAL_Delay(120);

ILI9341_WriteCommand(0x29);
ILI9341_WriteCommand(0x2C);
}

void ILI9341_SetAddressWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
ILI9341_WriteCommand(0x2A); // Column address set
ILI9341_WriteData(x0 >> 8);
ILI9341_WriteData(x0 & 0xFF);
ILI9341_WriteData(x1 >> 8);
ILI9341_WriteData(x1 & 0xFF);

ILI9341_WriteCommand(0x2B); // Row address set
ILI9341_WriteData(y0 >> 8);
ILI9341_WriteData(y0 & 0xFF);
ILI9341_WriteData(y1 >> 8);
ILI9341_WriteData(y1 & 0xFF);

ILI9341_WriteCommand(0x2C); // Write to RAM
}

void ILI9341_FillScreen(uint16_t color) {
uint16_t i, j;
if (display_orientation % 2 == 0) { // 0 or 180 degrees
ILI9341_SetAddressWindow(0, 0, 239, 319);
for (i = 0; i < 320; i++) {
for (j = 0; j < 240; j++) {
ILI9341_WriteData(color >> 8);
ILI9341_WriteData(color & 0xFF);
}
}
} else { // 90 or 270 degrees
ILI9341_SetAddressWindow(0, 0, 319, 239);
for (i = 0; i < 240; i++) {
for (j = 0; j < 320; j++) {
ILI9341_WriteData(color >> 8);
ILI9341_WriteData(color & 0xFF);
}
}
}
}

void ILI9341_SetOrientation(uint8_t orientation) {
switch (orientation) {
case 0:
ILI9341_WriteCommand(0x36);
ILI9341_WriteData(0x48);
break;
case 1:
ILI9341_WriteCommand(0x36);
ILI9341_WriteData(0x28);
break;
case 2:
ILI9341_WriteCommand(0x36);
ILI9341_WriteData(0x88);
break;
case 3:
ILI9341_WriteCommand(0x36);
ILI9341_WriteData(0xE8);
break;
}
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == GPIO_PIN_13) {
display_orientation = (display_orientation + 1) % 4; // Cycle through 0, 90, 180, 270 degrees
ILI9341_SetOrientation(display_orientation);
ILI9341_FillScreen(0x0000); // Clear the screen after rotation
}
}

void ILI9341_DrawPixel(uint16_t x, uint16_t y, uint16_t color) {
ILI9341_SetAddressWindow(x, y, x, y);
ILI9341_WriteData(color >> 8);
ILI9341_WriteData(color & 0xFF);
}

void ILI9341_DrawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bgcolor, uint8_t size) {
if ((x >= 240) || (y >= 320) || ((x + 5 * size - 1) < 0) || ((y + 7 * size - 1) < 0))
return;

for (int8_t i = 0; i < 5; i++) {
uint8_t line = Font5x7[(c - 32) * 5 + i];
for (int8_t j = 0; j < 8; j++) {
if (line & 0x01) {
for (uint8_t dx = 0; dx < size; dx++) {
for (uint8_t dy = 0; dy < size; dy++) {
ILI9341_DrawPixel(x + i * size + dx, y + j * size + dy, color);
}
}
} else {
for (uint8_t dx = 0; dx < size; dx++) {
for (uint8_t dy = 0; dy < size; dy++) {
ILI9341_DrawPixel(x + i * size + dx, y + j * size + dy, bgcolor);
}
}
}
line >>= 1;
}
}
}

void ILI9341_DrawString(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bgcolor, uint8_t size) {
while (*str) {
ILI9341_DrawChar(x, y, *str++, color, bgcolor, size);
x += 6 * size; // Move to the next character position (5 pixels wide + 1 pixel spacing) * size
}
}

void Error_Handler(void) {
while (1) {
// Stay in this loop in case of error
}
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line) {
// Report the file name and line number
}
#endif
main.c

Step 5: Implement Font File

fonts.h

Create the fonts.h file:

#ifndef FONTS_H
#define FONTS_H

#include "stm32f1xx_hal.h"

// 5x7 font for alphanumeric characters
extern const uint8_t Font5x7[];

void ILI9341_DrawPixel(uint16_t x, uint16_t y, uint16_t color);
void ILI9341_DrawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bgcolor, uint8_t size);
void ILI9341_DrawString(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bgcolor, uint8_t size);

#endif // FONTS_H
fonts.h

fonts.c

Create the fonts.c file:

#include "fonts.h"

const uint8_t Font5x7[] = {
// Define 5x7 font for ASCII characters 32-127
0x00, 0x00, 0x00, 0x00, 0x00, // 32: Space
0x00, 0x00, 0x5F, 0x00, 0x00, // 33: !
0x00, 0x07, 0x00, 0x07, 0x00, // 34: "
0x14, 0x7F, 0x14, 0x7F, 0x14, // 35: #
0x24, 0x2A, 0x7F, 0x2A, 0x12, // 36: $
0x23, 0x13, 0x08, 0x64, 0x62, // 37: %
0x36, 0x49, 0x55, 0x22, 0x50, // 38: &
0x00, 0x05, 0x03, 0x00, 0x00, // 39: '
0x00, 0x1C, 0x22, 0x41, 0x00, // 40: (
0x00, 0x41, 0x22, 0x1C, 0x00, // 41: )
0x14, 0x08, 0x3E, 0x08, 0x14, // 42: *
0x08, 0x08, 0x3E, 0x08, 0x08, // 43: +
0x00, 0x50, 0x30, 0x00, 0x00, // 44: ,
0x08, 0x08, 0x08, 0x08, 0x08, // 45: -
0x00, 0x60, 0x60, 0x00, 0x00, // 46: .
0x20, 0x10, 0x08, 0x04, 0x02, // 47: /
0x3E, 0x51, 0x49, 0x45, 0x3E, // 48: 0
0x00, 0x42, 0x7F, 0x40, 0x00, // 49: 1
0x42, 0x61, 0x51, 0x49, 0x46, // 50: 2
0x21, 0x41, 0x45, 0x4B, 0x31, // 51: 3
0x18, 0x14, 0x12, 0x7F, 0x10, // 52: 4
0x27, 0x45, 0x45, 0x45, 0x39, // 53: 5
0x3C, 0x4A, 0x49, 0x49, 0x30, // 54: 6
0x01, 0x71, 0x09, 0x05, 0x03, // 55: 7
0x36, 0x49, 0x49, 0x49, 0x36, // 56: 8
0x06, 0x49, 0x49, 0x29, 0x1E, // 57: 9
0x00, 0x36, 0x36, 0x00, 0x00, // 58: :
0x00, 0x56, 0x36, 0x00, 0x00, // 59: ;
0x08, 0x14, 0x22, 0x41, 0x00, // 60: <
0x14, 0x14, 0x14, 0x14, 0x14, // 61: =
0x00, 0x41, 0x22, 0x14, 0x08, // 62: >
0x02, 0x01, 0x51, 0x09, 0x06, // 63: ?
0x32, 0x49, 0x79, 0x41, 0x3E, // 64: @
0x7E, 0x11, 0x11, 0x11, 0x7E, // 65: A
0x7F, 0x49, 0x49, 0x49, 0x36, // 66: B
0x3E, 0x41, 0x41, 0x41, 0x22, // 67: C
0x7F, 0x41, 0x41, 0x22, 0x1C, // 68: D
0x7F, 0x49, 0x49, 0x49, 0x41, // 69: E
0x7F, 0x09, 0x09, 0x09, 0x01, // 70: F
0x3E, 0x41, 0x49, 0x49, 0x7A, // 71: G
0x7F, 0x08, 0x08, 0x08, 0x7F, // 72: H
0x00, 0x41, 0x7F, 0x41, 0x00, // 73: I
0x20, 0x40, 0x41, 0x3F, 0x01, // 74: J
0x7F, 0x08, 0x14, 0x22, 0x41, // 75: K
0x7F, 0x40, 0x40, 0x40, 0x40, // 76: L
0x7F, 0x02, 0x0C, 0x02, 0x7F, // 77: M
0x7F, 0x04, 0x08, 0x10, 0x7F, // 78: N
0x3E, 0x41, 0x41, 0x41, 0x3E, // 79: O
0x7F, 0x09, 0x09, 0x09, 0x06, // 80: P
0x3E, 0x41, 0x51, 0x21, 0x5E, // 81: Q
0x7F, 0x09, 0x19, 0x29, 0x46, // 82: R
0x46, 0x49, 0x49, 0x49, 0x31, // 83: S
0x01, 0x01, 0x7F, 0x01, 0x01, // 84: T
0x3F, 0x40, 0x40, 0x40, 0x3F, // 85: U
0x1F, 0x20, 0x40, 0x20, 0x1F, // 86: V
0x3F, 0x40, 0x30, 0x40, 0x3F, // 87: W
0x63, 0x14, 0x08, 0x14, 0x63, // 88: X
0x07, 0x08, 0x70, 0x08, 0x07, // 89: Y
0x61, 0x51, 0x49, 0x45, 0x43, // 90: Z
0x00, 0x7F, 0x41, 0x41, 0x00, // 91: [
0x02, 0x04, 0x08, 0x10, 0x20, // 92: \
0x00, 0x41, 0x41, 0x7F, 0x00, // 93: ]
0x04, 0x02, 0x01, 0x02, 0x04, // 94: ^
0x40, 0x40, 0x40, 0x40, 0x40, // 95: _
0x00, 0x03, 0x07, 0x00, 0x00, // 96: `
0x20, 0x54, 0x54, 0x54, 0x78, // 97: a
0x7C, 0x54, 0x54, 0x54, 0x28, // 98: b
0x38, 0x44, 0x44, 0x44, 0x28, // 99: c
0x38, 0x44, 0x44, 0x44, 0x7C, // 100: d
0x38, 0x54, 0x54, 0x54, 0x08, // 101: e
0x08, 0x7E, 0x09, 0x01, 0x02, // 102: f
0x0C, 0x52, 0x52, 0x52, 0x3E, // 103: g
0x7E, 0x08, 0x04, 0x04, 0x78, // 104: h
0x00, 0x44, 0x7D, 0x40, 0x00, // 105: i
0x20, 0x40, 0x44, 0x3D, 0x00, // 106: j
0x7E, 0x10, 0x28, 0x44, 0x00, // 107: k
0x00, 0x41, 0x7F, 0x40, 0x00, // 108: l
0x78, 0x04, 0x18, 0x04, 0x78, // 109: m
0x78, 0x08, 0x04, 0x04, 0x78, // 110: n
0x38, 0x44, 0x44, 0x44, 0x38, // 111: o
0x7C, 0x14, 0x14, 0x14, 0x08, // 112: p
0x08, 0x14, 0x14, 0x18, 0x7C, // 113: q
0x7C, 0x08, 0x04, 0x04, 0x08, // 114: r
0x48, 0x54, 0x54, 0x54, 0x20, // 115: s
0x04, 0x3F, 0x44, 0x40, 0x20, // 116: t
0x3C, 0x40, 0x40, 0x20, 0x7C, // 117: u
0x1C, 0x20, 0x40, 0x20, 0x1C, // 118: v
0x3C, 0x40, 0x30, 0x40, 0x3C, // 119: w
0x44, 0x28, 0x10, 0x28, 0x44, // 120: x
0x0C, 0x50, 0x50, 0x50, 0x3C, // 121: y
0x44, 0x64, 0x54, 0x4C, 0x44, // 122: z
0x00, 0x08, 0x36, 0x41, 0x00, // 123: {
0x00, 0x00, 0x7F, 0x00, 0x00, // 124: |
0x00, 0x41, 0x36, 0x08, 0x00, // 125: }
0x02, 0x01, 0x02, 0x04, 0x02, // 126: ~
0x3E, 0x55, 0x55, 0x41, 0x22, // 127: house
};

void ILI9341_DrawPixel(uint16_t x, uint16_t y, uint16_t color) {
ILI9341_SetAddressWindow(x, y, x, y);
ILI9341_WriteData(color >> 8);
ILI9341_WriteData(color & 0xFF);
}

void ILI9341_DrawChar(uint16_t x, uint16_t y, char c, uint16_t color, uint16_t bgcolor, uint8_t size) {
if ((x >= 240) || (y >= 320) || ((x + 5 * size - 1) < 0) || ((y + 7 * size - 1) < 0))
return;

for (int8_t i = 0; i < 5; i++) {
uint8_t line = Font5x7[(c - 32) * 5 + i];
for (int8_t j = 0; j < 8; j++) {
if (line & 0x01) {
for (uint8_t dx = 0; dx < size; dx++) {
for (uint8_t dy = 0; dy < size; dy++) {
ILI9341_DrawPixel(x + i * size + dx, y + j * size + dy, color);
}
}
} else {
for (uint8_t dx = 0; dx < size; dx++) {
for (uint8_t dy = 0; dy < size; dy++) {
ILI9341_DrawPixel(x + i * size + dx, y + j * size + dy, bgcolor);
}
}
}
line >>= 1;
}
}
}

void ILI9341_DrawString(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bgcolor, uint8_t size) {
while (*str) {
ILI9341_DrawChar(x, y, *str++, color, bgcolor, size);
x += 6 * size; // Move to the next character position (5 pixels wide + 1 pixel spacing) * size
}
}
fonts.c

Explanation

  1. Global Variable: A global variable display_orientation is used to keep track of the current display orientation.
  2. GPIO Initialization: Configured PC13 as input with an external interrupt.
  3. Interrupt Handler: HAL_GPIO_EXTI_Callback handles the button press, cycles through the four display orientations (0, 90, 180, 270 degrees), sets the new orientation, and clears the screen.
  4. Set Orientation: The ILI9341_SetOrientation function sets the MADCTL register to switch between the four orientations.
  5. Draw Functions: The ILI9341_DrawChar and ILI9341_DrawString functions are updated to allow scaling the text by a given size factor. The text size can be adjusted by changing the size parameter.

This code will toggle the display orientation between 0, 90, 180, and 270 degrees each time the button connected to PC13 is pressed. It also scales the text size to make it larger.

Step 6: Compiling and Uploading the Code

  1. Compile the project using your preferred IDE (STM32CubeIDE, Keil, etc.).
  2. Upload the compiled binary to the Nucleo-64 board.
  3. Power on the board and observe the display.

Conclusion

You’ve successfully connected an ILI9341 TFT display to an STM32F103RB microcontroller on a Nucleo-64 board. You can display text and rotate the display orientation using a button. This setup can be further expanded to include more complex graphics and interactive features.