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.
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.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
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.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
}
}
Explanation
- Global Variable: A global variable
display_orientation
is used to keep track of the current display orientation. - GPIO Initialization: Configured PC13 as input with an external interrupt.
- 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. - Set Orientation: The
ILI9341_SetOrientation
function sets the MADCTL register to switch between the four orientations. - Draw Functions: The
ILI9341_DrawChar
andILI9341_DrawString
functions are updated to allow scaling the text by a given size factor. The text size can be adjusted by changing thesize
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
- Compile the project using your preferred IDE (STM32CubeIDE, Keil, etc.).
- Upload the compiled binary to the Nucleo-64 board.
- 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.