Componentes · 17 min de leitura

Guia do ILI9341 com Arduino: ligações do display TFT, toque e imagens do SD

Como ligar um display TFT ILI9341 ao Arduino: conversão de nível para 3,3 V no Uno, Adafruit GFX, toque XPT2046, BMP do cartão SD, TFT_eSPI no ESP32 e solução para a tela branca.

Uma montagem com ILI9341 e Arduino é o jeito mais barato e popular de acrescentar ao seu projeto um display de 240×320 pixels com 65 mil cores. Esses módulos TFT de 2,4 e 2,8 polegadas, em placa vermelha, se ligam por SPI com poucos fios, e as versões com toque também trazem um controlador de toque XPT2046 e um slot para cartão microSD. Com um único módulo, você mostra gráficos coloridos, botões de toque e imagens lidas de um cartão. É ideal para menus, painéis, jogos e porta-retratos em que um OLED ou um LCD de caracteres já não basta.

Neste guia você vai aprender passo a passo como o módulo funciona, como ligar com segurança as saídas de 5 V do Arduino Uno às entradas de 3,3 V do display, como desenhar textos, formas e cores com as bibliotecas Adafruit_ILI9341 e Adafruit GFX, como ler a tela de toque e como mostrar imagens BMP de um cartão SD. Depois veremos o desenho muito mais rápido no ESP32 com a TFT_eSPI, o uso de MicroPython no Raspberry Pi Pico e todas as causas do problema da “tela branca” que quase todo mundo enfrenta pelo menos uma vez.

Módulo display TFT colorido ILI9341
Módulo display TFT colorido ILI9341: 240×320 pixels, interface SPI e 65 mil cores. As versões com toque também têm na placa um chip XPT2046 e um slot microSD.

O que é o display TFT ILI9341 e como ele funciona?

O ILI9341 é o chip driver atrás do vidro. Ele tem cerca de 172 KB de memória de vídeo (GRAM), que guarda todos os 240×320 pixels. O Arduino manda comandos e dados de cor para essa memória por SPI, e o chip atualiza a imagem sozinho dezenas de vezes por segundo. Por isso a imagem não precisa ficar na RAM do Arduino: até um Uno com 2 KB de RAM consegue acionar uma tela colorida grande. Para desenhar algo, você primeiro seleciona uma janela (x, y, largura, altura) e depois manda os pixels dessa janela um atrás do outro. O pino DC diz ao chip se o byte enviado é um comando (LOW) ou um dado (HIGH). As cores costumam ser enviadas no formato RGB565, ou seja, 16 bits por pixel: 5 bits de vermelho, 6 de verde e 5 de azul. Você encontra em detalhes como o barramento SPI funciona em O que é SPI.

CaracterísticaValor
Chip driverILI9341
Resolução240×320 pixels (320×240 em paisagem)
Cor65.536 cores com RGB565 (o chip suporta 262 mil)
Tamanhos de tela2,2", 2,4", 2,8" e 3,2" (todos 240×320)
InterfaceSPI de 4 fios: CS, DC, SCK, MOSI (MISO para leitura)
Alimentação (VCC)3,3–5 V (há um regulador de 3,3 V na placa)
Nível lógico3,3 V (não tolera 5 V)
ConsumoCerca de 0,3–0,6 W, a maior parte na luz de fundo
ToqueResistivo, controlador XPT2046 (nos modelos com toque)
Hardware extraSlot para cartão microSD (pinos SPI separados)

2,4 ou 2,8 polegadas? Módulo SPI ou shield para Uno

CaracterísticaMódulo SPI de 2,4"Módulo SPI de 2,8"Shield para Uno de 2,4"
Resolução240×320240×320240×320
LigaçãoSPI, com jumpersSPI, com jumpersParalela de 8 bits, encaixa em cima do Uno
Pinos usados5 para o display, 8–9 com toque e SD5 para o display, 8–9 com toque e SDQuase todos (D2–D9, A0–A4, D10–D13 para o SD)
DriverILI9341ILI9341Varia: ILI9341, ILI9325, ST7789 e outros
BibliotecaAdafruit_ILI9341, TFT_eSPIAdafruit_ILI9341, TFT_eSPIMCUFRIEND_kbv
ToqueXPT2046 (SPI)XPT2046 (SPI)Analógico de 4 fios (biblioteca TouchScreen)
ESP32 e PicoAdequadoAdequadoFeito para o Uno e o Mega

Este artigo trata do módulo SPI, porque ele também funciona com o ESP32 e o Pico e deixa livre a maioria dos pinos do Uno. Os dois módulos SPI têm exatamente os mesmos pinos e o mesmo código; a tela de 2,8 polegadas tem pixels um pouco maiores, então fica mais fácil acertar os botões de toque. Se o seu display encaixa direto em cima do Uno como um shield, as bibliotecas SPI não conseguem acioná-lo, e o resultado costuma ser uma tela branca. Para um shield assim, instale a biblioteca MCUFRIEND_kbv, leia o ID do driver com tft.readID() (0x9341 para o ILI9341) e inicie com tft.begin(ID). Para uma alternativa menor, mas mais nítida, veja display TFT ST7789 e, para comparar todas as opções, leia tipos de display para Arduino.

Pinagem do ILI9341 e tabela de ligações

Uma borda do módulo tem o conector principal de 14 pinos: os 9 primeiros pinos são do display e os 5 últimos, que começam com T_, são do toque. Os 4 pinos da borda oposta (SD_CS, SD_MOSI, SD_MISO, SD_SCK) vão para o slot do cartão. Os três dispositivos podem dividir o mesmo barramento SPI (SCK, MOSI, MISO); só os pinos CS precisam ser separados. A tabela abaixo corresponde a todos os sketches deste artigo; no Pico, o toque vai em um barramento SPI separado por causa da diferença de velocidade.

Pino do móduloArduino Uno (pelo conversor de nível)ESP32Raspberry Pi Pico
VCC5V3V33V3(OUT)
GNDGNDGNDGND
CSD10GPIO 15GP17
RESETD8GPIO 4GP21
DCD9GPIO 2GP20
SDI (MOSI)D11GPIO 23GP19
SCKD13GPIO 18GP18
LED3.3V3V33V3(OUT)
SDO (MISO)D12 (direto)GPIO 19GP16
T_CLKD13GPIO 18GP10
T_CSD7GPIO 21GP13
T_DIND11GPIO 23GP11
T_DOD12 (direto)GPIO 19GP12
T_IRQD2 (direto)Não ligadoNão ligado
SD_CSD4GPIO 5Não ligado
SD_MOSI / SD_MISO / SD_SCKD11 / D12 / D13GPIO 23 / 19 / 18Não ligado
O pino VCC dos módulos ILI9341 aceita 5 V, mas os pinos de sinal são só de 3,3 V. O regulador da placa só baixa a alimentação; não há conversão de nível nas linhas CS, DC, RESET, SCK e MOSI. Ligar direto as saídas de 5 V do Uno muitas vezes resulta em tela branca e às vezes danifica o chip para sempre. O jumper J1 na parte de trás contorna o regulador: feche-o com solda só se você alimentar o VCC com 3,3 V. Pôr 5 V no VCC com o J1 fechado queima o display.

Conversão de nível para 3,3 V no Arduino Uno

O ESP32 e o Raspberry Pi Pico já funcionam com 3,3 V, então você pode ligar o display diretamente. Em placas de 5 V como o Uno, o Nano e o Mega, toda linha que vai do Arduino para o display precisa ser baixada para 3,3 V: CS, DC, RESET, SCK, MOSI e, se você usar toque e SD, T_CLK, T_DIN, T_CS, SD_CS, SD_MOSI e SD_SCK. As linhas SDO, T_DO, SD_MISO e T_IRQ, que vêm do display para o Arduino, não precisam de conversor: o Uno lê como HIGH qualquer coisa acima de 3 V, e uma saída de 3,3 V atende a isso. Na prática, há quatro jeitos de fazer:

  • Chip buffer 74HC4050 (a solução mais limpa): ele funciona com 3,3 V, aceita 5 V nas entradas e converte 6 linhas em um só chip. É rápido e funciona sem problemas com SPI a 8 MHz. O CD4050, mais antigo, também funciona, mas é lento; se a imagem ficar corrompida, você vai precisar baixar a velocidade do SPI. O display sozinho precisa de 5 linhas; com toque e SD, você acrescenta T_CS e SD_CS e chega a 7 linhas, então use um segundo chip ou um divisor de tensão para a sétima.
  • Divisores de tensão com resistores: para cada linha, ponha 1 kΩ entre o pino do Arduino e o pino do display e 2 kΩ entre o pino do display e o GND; isso dá 5 V × 2 / 3 ≈ 3,3 V. Você pode calcular outros pares de resistores com a calculadora de divisor de tensão. É barato, mas 10 resistores para 5 linhas lotam a protoboard.
  • Módulo conversor de nível pronto: módulos de 4 ou 8 canais com MOSFETs BSS138 são fáceis de ligar, mas os resistores de pull-up deles amolecem as bordas do sinal em velocidades altas. Se a imagem ficar corrompida, baixe o SPI para 4 MHz com tft.begin(4000000). Os métodos são comparados em conversor de nível lógico.
  • O método a evitar: o popular truque de “resistor de 10 kΩ em série em cada linha” joga a tensão excedente nos diodos de proteção internos do chip. Pode funcionar por um tempo, mas não é confiável e pode danificar o display a longo prazo.

Passo a passo: primeiro teste com a Adafruit_ILI9341

  1. Abra o Gerenciador de Bibliotecas no Arduino IDE, procure Adafruit ILI9341 e instale. Quando ele perguntar sobre as dependências, escolha Instalar tudo para que a Adafruit GFX e a Adafruit BusIO também sejam instaladas. Se esta for a sua primeira biblioteca, Como instalar uma biblioteca no Arduino vai ajudar.
  2. Para o toque, instale também a biblioteca XPT2046_Touchscreen, de Paul Stoffregen. A biblioteca SD vem com o Arduino IDE.
  3. Ligue o display de acordo com a tabela acima, pelo conversor de nível no Uno. No primeiro teste, você pode deixar os pinos de toque e de SD desligados.
  4. Carregue o sketch abaixo e abra o Monitor Serial em 9600 baud. Você deve ver textos e formas no display e os códigos de diagnóstico do display no Monitor Serial.
  5. Experimente também os exemplos que vêm com a biblioteca: Arquivo > Exemplos > Adafruit ILI9341 > graphicstest mede quantos microssegundos cada operação de desenho leva.
C++ (Arduino IDE)
1#include <SPI.h>
2#include <Adafruit_GFX.h>
3#include <Adafruit_ILI9341.h>
4
5#define TFT_CS  10   // ekran seçme / chip select
6#define TFT_DC  9    // komut/veri seçimi / command/data select
7#define TFT_RST 8    // donanım reset / hardware reset
8
9Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);   // donanım SPI: D11 MOSI, D13 SCK / hardware SPI
10uint16_t orange;                                  // kendi rengimiz / our own colour
11
12void setup() {
13  Serial.begin(9600);
14  tft.begin();                                    // Uno'da 8 MHz SPI / 8 MHz SPI on Uno
15
16  // Teşhis: çalışan ekranda 0x94 ve 0xC0 beklenir / Diagnostics: a working display reports 0x94 and 0xC0
17  Serial.print(F("Power Mode: 0x"));
18  Serial.println(tft.readcommand8(ILI9341_RDMODE), HEX);
19  Serial.print(F("Self Diagnostic: 0x"));
20  Serial.println(tft.readcommand8(ILI9341_RDSELFDIAG), HEX);
21
22  tft.setRotation(1);                             // yatay, 320x240 / landscape, 320x240
23  tft.fillScreen(ILI9341_BLACK);
24  orange = tft.color565(255, 128, 0);             // RGB'den RGB565'e / RGB to RGB565
25
26  tft.setTextColor(ILI9341_WHITE);
27  tft.setTextSize(3);                             // 18x24 piksel karakter / 18x24 px characters
28  tft.setCursor(10, 10);
29  tft.print(F("BlueGrays"));
30  tft.setTextColor(ILI9341_CYAN);
31  tft.setTextSize(2);
32  tft.setCursor(10, 45);
33  tft.print(F("ILI9341 320x240"));
34
35  tft.fillRect(10, 75, 90, 60, ILI9341_RED);                  // dolu dikdörtgen / filled rectangle
36  tft.drawRect(115, 75, 90, 60, ILI9341_GREEN);               // çerçeve / outline
37  tft.fillCircle(265, 105, 30, ILI9341_BLUE);                 // dolu daire / filled circle
38  tft.fillRoundRect(10, 150, 140, 50, 10, orange);            // yuvarlak köşeli düğme / rounded button
39  tft.drawTriangle(180, 200, 240, 150, 300, 200, ILI9341_YELLOW);
40  tft.drawFastHLine(0, 220, 320, ILI9341_MAGENTA);            // yatay çizgi / horizontal line
41}
42
43void loop() {
44  // Arka plan rengiyle yazınca eski rakam silinir, ekran titremez / writing with a background colour erases the old digits, no flicker
45  tft.setTextColor(ILI9341_WHITE, orange);
46  tft.setTextSize(2);
47  tft.setCursor(22, 167);
48  tft.print(millis() / 1000);
49  tft.print(F(" s"));
50  delay(200);
51}
FunçãoO que faz
tft.begin(frequência)Inicia o display; sem frequência, usa SPI de 8 MHz no Uno e de 40 MHz no ESP32
tft.setRotation(0–3) (orientação)Escolhe a orientação: 0 e 2 retrato (240×320), 1 e 3 paisagem (320×240)
tft.fillScreen(cor)Pinta a tela inteira de uma cor
tft.setTextColor(texto, fundo)Define a cor do texto; com uma cor de fundo, sobrescreve o texto antigo de forma limpa
tft.setTextSize(n) (tamanho do texto)Aumenta o texto n vezes; o tamanho 1 = caracteres de 6×8 pixels
tft.drawRect / fillRect(x, y, largura, altura, cor)Contorno de retângulo ou retângulo preenchido
tft.fillRoundRect(x, y, largura, altura, r, cor)Retângulo preenchido com cantos arredondados; ideal para botões
tft.drawCircle / fillCircle(x, y, r, cor)Círculo ou círculo preenchido
tft.color565(r, g, b) (conversão de cor)Converte valores RGB de 0–255 em uma cor de 16 bits
tft.readcommand8(comando)Lê um registrador do display (o MISO precisa estar ligado); para diagnóstico

Constantes de cor prontas (19 delas, como ILI9341_RED, ILI9341_NAVY e ILI9341_ORANGE) vêm com a biblioteca; para uma cor sua, use color565(). Ela junta lado a lado os 5 bits mais altos do vermelho, os 6 mais altos do verde e os 5 mais altos do azul: cor = (R >> 3) << 11 | (G >> 2) << 5 | (B >> 3). Para o laranja (255, 165, 0), por exemplo, 31 << 11 = 0xF800 e 41 << 5 = 0x0520 se combinam em 0xFD20, que é exatamente o valor ILI9341_ORANGE da biblioteca. O verde ganha um bit a mais porque o olho humano é mais sensível ao verde.

Apagar números que mudam com fillScreen() e redesenhá-los toda vez faz a tela piscar; no Uno, pintar a tela inteira leva pelo menos 0,15 segundo com SPI a 8 MHz. Em vez disso, use setTextColor(texto, fundo) como no sketch, ou apague com fillRect() só a pequena área que muda. Escrever textos fixos dentro de F("...") também economiza os 2 KB de RAM do Uno; para problemas de memória, veja memória insuficiente no Arduino.

Lendo a tela de toque XPT2046

Nos módulos com toque, há uma fina camada resistiva sobre o vidro. Onde o dedo ou uma caneta pressiona, duas camadas condutoras se tocam; o chip XPT2046 mede a posição X e Y desse ponto com um ADC de 12 bits como valores brutos de 0 a 4095 e também informa um valor de pressão (Z). A biblioteca considera sem toque um valor de Z abaixo de 400. O chip fica no mesmo barramento SPI do display, mas conversa em uma velocidade menor, por volta de 2 MHz; a biblioteca ajusta a velocidade sozinha a cada leitura. O pino T_IRQ vai a LOW quando a tela é tocada, o que poupa o processador de leituras inúteis. O sketch abaixo transforma a tela em uma pequena lousa de desenho: você escolhe uma cor nas caixas do topo e apaga a tela com o botão “SIL” (apagar, em turco).

C++ (Arduino IDE)
1#include <SPI.h>
2#include <Adafruit_GFX.h>
3#include <Adafruit_ILI9341.h>
4#include <XPT2046_Touchscreen.h>
5
6#define TFT_CS  10
7#define TFT_DC  9
8#define TFT_RST 8
9#define T_CS    7    // dokunmatik seçme / touch chip select
10#define T_IRQ   2    // dokununca LOW olur / goes LOW on touch
11#define SD_CS   4    // SD kullanılmıyor, HIGH tutulur / SD unused, kept HIGH
12
13// Kalibrasyon: Seri Monitör'deki ham değerlerle değiştir / Calibration: replace with the raw values from Serial Monitor
14#define TS_MINX 300
15#define TS_MAXX 3800
16#define TS_MINY 300
17#define TS_MAXY 3800
18
19Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
20XPT2046_Touchscreen ts(T_CS, T_IRQ);
21
22uint16_t brush = ILI9341_YELLOW;                  // fırça rengi / brush colour
23
24void drawPalette() {
25  tft.fillRect(0, 0, 40, 40, ILI9341_RED);
26  tft.fillRect(40, 0, 40, 40, ILI9341_GREEN);
27  tft.fillRect(80, 0, 40, 40, ILI9341_BLUE);
28  tft.fillRect(120, 0, 40, 40, ILI9341_YELLOW);
29  tft.drawRect(260, 0, 60, 40, ILI9341_WHITE);    // silme düğmesi / clear button
30  tft.setTextColor(ILI9341_WHITE);
31  tft.setTextSize(2);
32  tft.setCursor(273, 13);
33  tft.print(F("SIL"));
34}
35
36void setup() {
37  Serial.begin(9600);
38  pinMode(SD_CS, OUTPUT);
39  digitalWrite(SD_CS, HIGH);                      // SD kart hattı meşgul etmesin / keep the SD card off the bus
40  tft.begin();
41  tft.setRotation(1);                             // ekran yatay / display landscape
42  ts.begin();
43  ts.setRotation(1);                              // dokunmatik de aynı yönde / touch in the same orientation
44  tft.fillScreen(ILI9341_BLACK);
45  drawPalette();
46}
47
48void loop() {
49  if (!ts.touched()) return;                      // dokunma yok / no touch
50  TS_Point p = ts.getPoint();                     // ham x, y (0-4095) ve basınç z / raw x, y and pressure z
51  Serial.print(p.x); Serial.print(',');
52  Serial.print(p.y); Serial.print(',');
53  Serial.println(p.z);
54
55  int16_t x = constrain(map(p.x, TS_MINX, TS_MAXX, 0, 319), 0, 319);   // ekran koordinatı / screen coordinate
56  int16_t y = constrain(map(p.y, TS_MINY, TS_MAXY, 0, 239), 0, 239);
57
58  if (y < 40) {                                   // üst şerit: palet ve düğme / top strip: palette and button
59    if (x < 40)        brush = ILI9341_RED;
60    else if (x < 80)   brush = ILI9341_GREEN;
61    else if (x < 120)  brush = ILI9341_BLUE;
62    else if (x < 160)  brush = ILI9341_YELLOW;
63    else if (x >= 260) { tft.fillScreen(ILI9341_BLACK); drawPalette(); }
64  } else {
65    tft.fillCircle(x, y, 3, brush);               // parmağın altına nokta / dot under the finger
66  }
67}

Os valores brutos não são 0 e 4095 bem nos cantos; na maioria dos módulos, as bordas ficam mais ou menos entre 200–400 e 3700–3900. Para calibrar, abra o Monitor Serial, toque com uma caneta os cantos superior esquerdo e inferior direito e ponha os valores de x e y que aparecerem nas constantes TS_MINX, TS_MAXX, TS_MINY e TS_MAXY. Se o ponto aparecer espelhado em relação ao dedo, troque no map() os valores mínimo e máximo daquele eixo; se X e Y estiverem totalmente trocados, experimente valores de ts.setRotation() de 0 a 3. Um menu de verdade funciona do mesmo jeito: você desenha botões e confere em qual retângulo cai o ponto tocado. A camada resistiva também funciona com o dedo, mas uma ponta parecida com uma caneta é mais precisa para botões pequenos.

Mostrando imagens BMP do cartão SD

O slot microSD atrás do módulo permite guardar logotipos, fundos e fotos em um cartão e mostrá-los na tela. Para preparar uma imagem, redimensione uma foto para 320×240 pixels e salve no Paint como Bitmap de 24 bits (.bmp). Como a biblioteca SD do Arduino trabalha com nomes de arquivo curtos, use um nome de até 8 caracteres, como RESIM.BMP, e formate o cartão em FAT32. Os detalhes do cartão em si e a solução para o erro initialization failed estão em módulo de cartão SD no Arduino. Lembre que esse slot também é de 3,3 V: no Uno, as linhas SD_CS, SD_MOSI e SD_SCK também precisam passar pelo conversor de nível.

C++ (Arduino IDE)
1#include <SPI.h>
2#include <SD.h>
3#include <Adafruit_GFX.h>
4#include <Adafruit_ILI9341.h>
5
6#define TFT_CS  10
7#define TFT_DC  9
8#define TFT_RST 8
9#define SD_CS   4
10#define T_CS    7
11#define CHUNK   20   // tek okumada 20 piksel (60 bayt) / 20 pixels (60 bytes) per read
12
13Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
14
15uint16_t read16(File &f) {                        // BMP sayıları küçük uçlu / BMP numbers are little-endian
16  uint16_t lo = f.read();
17  uint16_t hi = f.read();
18  return lo | (hi << 8);
19}
20
21uint32_t read32(File &f) {
22  uint32_t lo = read16(f);
23  uint32_t hi = read16(f);
24  return lo | (hi << 16);
25}
26
27bool drawBmp(const char *name, int16_t x, int16_t y) {
28  File f = SD.open(name);
29  if (!f) return false;                                   // dosya yok / file not found
30  if (read16(f) != 0x4D42) { f.close(); return false; }  // "BM" imzası / "BM" signature
31  read32(f);                                              // dosya boyutu / file size
32  read32(f);                                              // ayrılmış alan / reserved
33  uint32_t offset = read32(f);                            // piksel verisinin başı / start of pixel data
34  read32(f);                                              // başlık boyutu / header size
35  int32_t w = (int32_t)read32(f);
36  int32_t h = (int32_t)read32(f);
37  read16(f);                                              // düzlem sayısı / colour planes
38  uint16_t depth = read16(f);
39  uint32_t comp = read32(f);
40  if (depth != 24 || comp != 0) { f.close(); return false; }   // yalnız 24 bit, sıkıştırmasız / 24-bit uncompressed only
41
42  bool bottomUp = true;                                   // BMP satırları alttan üste / rows are stored bottom-up
43  if (h < 0) { h = -h; bottomUp = false; }
44  uint32_t rowSize = (w * 3 + 3) & ~3UL;                  // satırlar 4 baytın katı / rows padded to 4 bytes
45  int32_t drawW = w, drawH = h;
46  if (x + drawW > tft.width())  drawW = tft.width() - x;   // ekrana sığmayanı kırp / clip to the screen
47  if (y + drawH > tft.height()) drawH = tft.height() - y;
48
49  uint8_t sdBuf[CHUNK * 3];
50  uint16_t line[CHUNK];
51  for (int32_t row = 0; row < drawH; row++) {
52    uint32_t pos = offset + (bottomUp ? (h - 1 - row) : row) * rowSize;
53    f.seek(pos);                                          // satırın başına git / jump to the start of the row
54    for (int32_t col = 0; col < drawW; col += CHUNK) {
55      int16_t n = drawW - col;
56      if (n > CHUNK) n = CHUNK;
57      f.read(sdBuf, n * 3);
58      for (int16_t i = 0; i < n; i++) {                   // BGR'den RGB565'e / BGR to RGB565
59        line[i] = tft.color565(sdBuf[i * 3 + 2], sdBuf[i * 3 + 1], sdBuf[i * 3]);
60      }
61      tft.drawRGBBitmap(x + col, y + row, line, n, 1);    // parçayı ekrana bas / push the chunk to the screen
62    }
63  }
64  f.close();
65  return true;
66}
67
68void setup() {
69  Serial.begin(9600);
70  pinMode(T_CS, OUTPUT);
71  digitalWrite(T_CS, HIGH);                       // dokunmatik hattı meşgul etmesin / keep touch off the bus
72  tft.begin();
73  tft.setRotation(1);                             // 320x240 yatay / landscape
74  tft.fillScreen(ILI9341_BLACK);
75  tft.setTextSize(2);
76
77  if (!SD.begin(SD_CS)) {                         // kart başlatılamadı / card init failed
78    tft.setTextColor(ILI9341_RED);
79    tft.setCursor(10, 10);
80    tft.print(F("SD kart bulunamadi"));
81    return;
82  }
83  uint32_t t = millis();
84  if (!drawBmp("RESIM.BMP", 0, 0)) {
85    tft.setTextColor(ILI9341_YELLOW);
86    tft.setCursor(10, 10);
87    tft.print(F("RESIM.BMP okunamadi"));
88  }
89  Serial.print(F("Cizim suresi / Draw time (ms): "));
90  Serial.println(millis() - t);
91}
92
93void loop() {
94}

Os primeiros 54 bytes de um arquivo BMP são o cabeçalho: ali ficam a assinatura “BM”, onde começam os dados dos pixels, a largura, a altura e a profundidade de cor. Os pixels ficam na ordem azul, verde, vermelho (BGR) e normalmente de baixo para cima, e cada linha é completada até um múltiplo de 4 bytes. Por isso o sketch salta para a posição certa do arquivo para cada linha da tela, lê 20 pixels (60 bytes) por vez, converte-os para RGB565 e os envia com drawRGBBitmap(). Trabalhar em pedaços pequenos mantém tudo dentro da RAM do Uno e deixa o cartão SD e o display se revezarem no mesmo barramento SPI. O tempo de desenho aparece no Monitor Serial; no Uno, uma imagem de tela inteira aparece linha por linha ao longo de alguns segundos. Se quiser mostrar arquivos PNG ou JPEG e desenhar muito mais rápido, passe para um ESP32 com as bibliotecas TFT_eSPI e TJpg_Decoder.

Desenho rápido no ESP32 com a TFT_eSPI

Placa de desenvolvimento ESP32
Graças à lógica de 3,3 V, o ESP32 se liga direto ao ILI9341 e aciona o SPI a 40 MHz, desenhando muitas vezes mais rápido que o Uno.

O ESP32 funciona com 3,3 V e consegue acionar o SPI a até 40 MHz, então você não precisa de conversor de nível e preencher a tela inteira cai, na teoria, para uns 31 milissegundos. Para usar de verdade essa velocidade, a biblioteca TFT_eSPI, de Bodmer, é a escolha habitual. Ela acessa diretamente o hardware SPI do ESP32 e suporta fontes suavizadas (anti-aliased) e, principalmente, buffers fora da tela chamados sprites. Você desenha à vontade em um sprite na RAM e manda o resultado para a tela de uma vez, então nem gráficos que mudam rápido piscam. O que torna a TFT_eSPI diferente é que os pinos não são definidos no sketch, e sim no arquivo User_Setup.h dentro da pasta da biblioteca. Abra-o, comente as linhas de pinos do ESP8266 (NodeMCU), que vêm ativas por padrão, e ative estas:

C++ (Arduino IDE)
1// TFT_eSPI/User_Setup.h içinde yalnız bu satırlar açık kalsın / keep only these lines active in TFT_eSPI/User_Setup.h
2#define ILI9341_DRIVER
3
4#define TFT_MISO 19
5#define TFT_MOSI 23
6#define TFT_SCLK 18
7#define TFT_CS   15   // ekran seçme / chip select
8#define TFT_DC    2   // komut/veri / data/command
9#define TFT_RST   4   // sıfırlama / reset
10#define TOUCH_CS 21   // dokunmatik seçme (T_CS) / touch chip select (T_CS)
11
12#define LOAD_GLCD     // 1: 8 piksel yazı tipi / 8 px font
13#define LOAD_FONT2    // 2: 16 piksel / 16 px
14#define LOAD_FONT4    // 4: 26 piksel / 26 px
15#define LOAD_FONT7    // 7: 7 segment rakamlar / 7-segment digits
16#define LOAD_GFXFF    // Adafruit FreeFont'lar / Adafruit FreeFonts
17#define SMOOTH_FONT
18
19#define SPI_FREQUENCY       40000000   // kablolar uzunsa 27000000 / 27 MHz with long wires
20#define SPI_READ_FREQUENCY  20000000
21#define SPI_TOUCH_FREQUENCY  2500000
C++ (Arduino IDE)
1#include <SPI.h>
2#include <TFT_eSPI.h>                     // pinler User_Setup.h içinde / pins are set in User_Setup.h
3
4TFT_eSPI tft = TFT_eSPI();
5TFT_eSprite graph = TFT_eSprite(&tft);    // ekran dışı çizim alanı / off-screen canvas
6
7// Touch_calibrate örneğinin verdiği 5 sayıyla değiştir / replace with the 5 numbers from the Touch_calibrate example
8uint16_t calData[5] = {286, 3534, 283, 3600, 6};
9
10const int GW = 300, GH = 100;             // grafik boyutu / graph size
11uint8_t samples[GW];                      // son 300 ölçüm / last 300 readings
12uint32_t frames = 0, fps = 0, lastFps = 0;
13
14void setup() {
15  tft.init();
16  tft.setRotation(1);                     // yatay, 320x240 / landscape, 320x240
17  tft.setTouch(calData);
18  tft.fillScreen(TFT_BLACK);
19  tft.setTextColor(TFT_WHITE, TFT_BLACK);
20  tft.drawString("ESP32 + TFT_eSPI", 10, 8, 4);   // 4 numaralı yazı tipi, 26 px / font 4, 26 px
21  graph.createSprite(GW, GH);             // 300x100x2 bayt = 60 KB RAM / 60 KB of RAM
22}
23
24void loop() {
25  memmove(samples, samples + 1, GW - 1);                          // grafiği sola kaydır / scroll the graph left
26  samples[GW - 1] = map(analogRead(34), 0, 4095, 0, GH - 1);       // GPIO 34'teki potansiyometre / pot on GPIO 34
27
28  graph.fillSprite(TFT_BLACK);
29  for (int y = 0; y < GH; y += 25) graph.drawFastHLine(0, y, GW, TFT_DARKGREY);   // ızgara / grid
30  for (int x = 1; x < GW; x++) {
31    graph.drawLine(x - 1, GH - 1 - samples[x - 1], x, GH - 1 - samples[x], TFT_GREEN);
32  }
33  graph.setTextColor(TFT_YELLOW);
34  graph.drawString(String(fps) + " FPS", 4, 4, 2);
35  graph.pushSprite(10, 50);               // tek seferde ekrana gönder, titreme yok / push in one go, no flicker
36
37  uint16_t tx, ty;
38  if (tft.getTouch(&tx, &ty)) {           // dokunulduysa / if touched
39    tft.fillRect(10, 170, 300, 30, TFT_BLACK);
40    tft.setTextColor(TFT_CYAN, TFT_BLACK);
41    tft.drawString("Dokunma / Touch: " + String(tx) + ", " + String(ty), 10, 175, 2);
42  }
43
44  frames++;
45  if (millis() - lastFps >= 1000) {       // saniyede bir FPS hesapla / compute FPS once per second
46    fps = frames;
47    frames = 0;
48    lastFps = millis();
49  }
50}

O sketch desenha o valor de um potenciômetro no GPIO 34 como um gráfico rolando e mostra os quadros por segundo (FPS) no canto do sprite. Um sprite de 300×100 pixels com cor de 16 bits ocupa 60 KB de RAM; isso é cerca de 30 vezes toda a memória do Uno, mas não é problema para o ESP32. Para o toque, os pinos T_CLK, T_DIN e T_DO se juntam às linhas SCK, MOSI e MISO do display, e só o T_CS (GPIO 21) fica separado. Para obter os seus próprios valores de calibração, rode uma vez o exemplo Generic > Touch_calibrate da TFT_eSPI, toque nas setas que aparecem nos cantos e copie para o array calData os 5 números mostrados no Monitor Serial. A linha pronta Setup42_ILI9341_ESP32.h em User_Setup_Select.h escolhe os mesmos pinos do display, mas você precisa acrescentar a linha TOUCH_CS a ela você mesmo. Se você é novo no ESP32, leia antes O que é o ESP32.

Quando você atualiza a TFT_eSPI pelo Arduino IDE, a pasta da biblioteca é regravada e as suas configurações no User_Setup.h se perdem; se um display que funcionava ontem fica branco hoje, este é o primeiro lugar para olhar, então guarde um backup do seu arquivo de configuração. Se o vermelho e o azul estiverem trocados, ative TFT_RGB_ORDER TFT_BGR; se as cores parecerem um negativo, ative TFT_INVERSION_ON; alguns painéis funcionam melhor com ILI9341_2_DRIVER. A TFT_eSPI 2.5.43 pode travar e reiniciar na partida em algumas placas com as versões 3.x do pacote de placas esp32; se isso acontecer, volte para uma versão 2.0.x do pacote esp32 no Gerenciador de Placas.

ILI9341 com Raspberry Pi Pico e MicroPython

O Pico também é uma placa de 3,3 V, então o display se liga diretamente. O driver de MicroPython mais usado é o par ili9341.py e xpt2046.py do repositório micropython-ili9341, de rdagger; basta copiar os dois arquivos para o Pico com o Thonny. No código abaixo, o display fica no SPI0 a 40 MHz e o controlador de toque em um barramento SPI1 separado a 1 MHz, então a diferença de velocidade entre os dois chips não é problema. O código desenha textos e formas e depois põe pontos amarelos onde você tocar, formando uma lousa de desenho simples. Se é a primeira vez que você usa o Pico e o Thonny, comece por O que é o Raspberry Pi Pico.

MicroPython
1from machine import Pin, SPI
2from time import sleep_ms
3from ili9341 import Display, color565      # ili9341.py Pico'da olmalı / must be on the Pico
4from xpt2046 import Touch                  # xpt2046.py Pico'da olmalı / must be on the Pico
5
6# Ekran: SPI0, 40 MHz / Display: SPI0, 40 MHz
7spi_tft = SPI(0, baudrate=40000000, sck=Pin(18), mosi=Pin(19), miso=Pin(16))
8tft = Display(spi_tft, cs=Pin(17), dc=Pin(20), rst=Pin(21), width=240, height=320, rotation=0)
9
10# Dokunmatik: ayrı SPI1, 1 MHz / Touch: separate SPI1, 1 MHz
11spi_touch = SPI(1, baudrate=1000000, sck=Pin(10), mosi=Pin(11), miso=Pin(12))
12touch = Touch(spi_touch, cs=Pin(13), width=240, height=320)
13
14WHITE = color565(255, 255, 255)
15YELLOW = color565(255, 255, 0)
16
17tft.clear(color565(0, 0, 0))                                   # ekranı temizle / clear the screen
18tft.draw_text8x8(10, 10, "BlueGrays + Pico", WHITE)
19tft.fill_rectangle(10, 30, 100, 50, color565(255, 0, 0))       # dolu dikdörtgen / filled rectangle
20tft.draw_rectangle(130, 30, 100, 50, color565(0, 255, 0))      # çerçeve / outline
21tft.fill_circle(60, 130, 30, color565(0, 128, 255))            # dolu daire / filled circle
22tft.draw_line(0, 175, 239, 175, color565(255, 0, 255))         # çizgi / line
23tft.draw_text8x8(10, 185, "Ekrana dokun / Touch me", YELLOW)
24
25while True:
26    raw = touch.raw_touch()                # dokunma yoksa None / None when not touched
27    if raw is not None:
28        x, y = touch.normalize(*raw)       # ham değeri piksele çevir / raw value to pixels
29        y = 319 - y                        # eksen ters ise / if the axis is inverted
30        x = min(max(x, 3), 236)            # ekran dışına taşma / keep inside the screen
31        y = min(max(y, 3), 316)
32        tft.fill_circle(x, y, 3, YELLOW)
33    sleep_ms(20)

No modo retrato (rotation=0), a largura é 240 e a altura 320; para usar a tela em paisagem, passe width=320 e height=240 junto com rotation=90. A calibração padrão do driver de toque (x_min=100, x_max=1962) pode não servir para todo módulo; se o ponto sair deslocado, mostre os valores de raw_touch() e passe ao Touch() os seus próprios x_min, x_max, y_min e y_max. Para mostrar imagens, a função draw_image() do driver lê arquivos .raw convertidos para RGB565; o script utils/img2rgb565.py do repositório faz essa conversão no computador.

O problema da tela branca e como resolver

O problema mais famoso do ILI9341 é uma tela que fica totalmente branca com a luz de fundo acesa. A tela branca é na verdade uma pista: a alimentação e a luz de fundo estão funcionando, mas o chip driver nunca foi inicializado. Quando o painel não recebe comandos, os pixels deixam a luz passar e a tela parece branca. Então o problema é quase sempre de comunicação. Siga a tabela abaixo de cima para baixo:

SintomaCausa provávelSolução
Tela branca, ligado direto ao UnoSinais de 5 V, sem conversor de nívelUse um 74HC4050 ou divisores de tensão; troque o módulo se o chip estiver danificado
Tela branca, fios ligadosCS e DC trocados, ou os números dos pinos do código não batem com as ligaçõesCompare uma por uma as definições TFT_CS, TFT_DC e TFT_RST com a tabela
Tela branca, RESET soltoRESET desligado ou no pino erradoLigue o RESET ao pino do código ou a 3,3 V; nunca o deixe solto
Tela branca no ESP32 (TFT_eSPI)User_Setup.h não editado ou apagado por uma atualizaçãoDesative as linhas do ESP8266 e escreva de novo os pinos do ESP32 e o ILI9341_DRIVER
Tela branca de vez em quando, imagem deslocada ou embaralhadaJumpers longos, SPI rápido demais, contatos frouxos na protoboardMantenha os fios com menos de 10–15 cm; baixe o SPI para 20–27 MHz, ou 4 MHz no Uno
Tela branca só com o toque ou o SD ligadosO pino CS do outro dispositivo está solto e bloqueia o barramentoConfigure todo pino CS sem uso como OUTPUT e escreva HIGH nele no código
Não funciona com nenhum códigoO display não é um ILI9341 (ST7789, ILI9488) ou é um shield paraleloLeia a etiqueta atrás; uma tela de 3,5 polegadas e 320×480 é um ILI9488, e um shield precisa da MCUFRIEND_kbv
Tela branca, luz de fundo piscandoAlimentação insuficienteAlimente o VCC com 5 V e teste outro cabo USB e outra fonte
Para estreitar a falha, use as linhas de diagnóstico do primeiro sketch. Se o Monitor Serial mostrar Power Mode: 0x94 e Self Diagnostic: 0xC0, o display está recebendo comandos e o problema está no software (rotação, cores ou código de desenho). Se os valores forem 0x0 ou 0xFF, o Arduino não consegue falar com o display: confira os fios, o conversor de nível e os pinos CS/DC. Essa leitura só funciona com a linha SDO (MISO) ligada.

Outros erros comuns

  • Cores erradas: se o vermelho aparece como azul, o painel usa a ordem BGR; mude a configuração TFT_RGB_ORDER na TFT_eSPI. Se as cores parecerem um negativo, experimente tft.invertDisplay(true).
  • Imagem de cabeça para baixo ou espelhada: mude o valor de setRotation() entre 0 e 3 e ajuste os eixos do toque de acordo.
  • O display funciona, mas o toque ou o SD não: confira se T_CS e SD_CS batem com os pinos do código. Em alguns módulos, a saída SDO do display não solta o barramento; nesse caso, experimente desligar o pino SDO do display, e só a leitura de diagnóstico vai parar de funcionar.
  • O BMP não aparece: o cartão não está em FAT32, o nome do arquivo tem mais de 8 caracteres ou a imagem não é de 24 bits. Não esqueça de escolher 24 bits no Paint; BMPs de 16 e 32 bits não são desenhados por este sketch.
  • Faltam caracteres especiais: a fonte GFX padrão não tem letras acentuadas como ç, ã ou é. Na TFT_eSPI, você pode criar a sua própria fonte suavizada, ou usar uma fonte GFX que inclua os caracteres de que precisa.
  • O display está lento: uma atualização de tela inteira no Uno leva pelo menos 0,15 segundo. Para animações e jogos, passe para um ESP32 ou um Pico e redesenhe só a área que muda.
  • O módulo esquenta: pode haver 5 V no VCC com o jumper J1 fechado, ou o VCC e o GND podem estar invertidos; desligue a alimentação na hora.

Ideias de projetos com o ILI9341

  • Painel de casa inteligente com toque: controle luzes e relés com botões grandes na tela; para ideias, veja o projeto de casa inteligente com Arduino.
  • Display colorido do tempo: mostre com ícones e números grandes a previsão do tempo que você pega na internet com um ESP32 (estação meteorológica com ESP32).
  • Painel de sensores: desenhe a temperatura, a umidade e a pressão de um BME280 como mostradores, com um gráfico das últimas 24 horas.
  • Console de jogos retrô: jogue Snake, Pong ou Tetris em cores; acrescente um módulo joystick para o controle e veja projetos de jogos com Arduino para ideias.
  • Porta-retratos digital: passe pelas imagens BMP do cartão SD a cada poucos segundos.
  • Miniosciloscópio: desenhe os valores do analogRead como uma forma de onda suave com um sprite no ESP32.
  • Fechadura com teclado de toque: desenhe um teclado numérico na tela e abra um relé ou um servo quando o código certo for digitado.

O ILI9341 é o jeito mais acessível de dar aos seus projetos uma interface de toque colorida: comece com o Uno para aprender as funções básicas de desenho e passe para o ESP32 com a TFT_eSPI quando precisar de velocidade e de animação suave. Se preferir começar com uma tela menor, o display OLED (SSD1306) é um bom ponto de partida. No app BlueGrays você encontra os projetos de display do tempo com ESP32, registrador de temperatura, sensor de estacionamento e jogos com display, com esquemas de circuito e código passo a passo.

Perguntas frequentes

O display ILI9341 funciona com 5 V?

O pino VCC do módulo aceita 3,3–5 V porque há um regulador de 3,3 V na placa. Os pinos de sinal, como CS, DC, SCK e MOSI, porém, são só de 3,3 V. Ao usá-lo com um Arduino Uno, ponha nessas linhas um conversor de nível como o 74HC4050 ou divisores de tensão.

Por que o meu ILI9341 mostra só uma tela branca?

Uma tela branca significa que a luz de fundo está acesa, mas o chip driver nunca foi inicializado. As causas mais comuns são ligações erradas de CS, DC ou RESET, falta de conversor de nível no Uno, um User_Setup.h não editado na TFT_eSPI e um display que na verdade não é um ILI9341.

Qual biblioteca devo usar para o ILI9341?

Para o Arduino Uno, o Nano e o Mega, a Adafruit_ILI9341 com a Adafruit GFX é a escolha mais fácil. Em placas ESP32, ESP8266 e RP2040, prefere-se a TFT_eSPI, muito mais rápida. Shields paralelos de 2,4 polegadas que encaixam no Uno precisam, no lugar, da MCUFRIEND_kbv.

Como calibro a tela de toque do ILI9341?

O controlador de toque XPT2046 retorna valores brutos de 0 a 4095. Anote os menores e os maiores valores que aparecem no Monitor Serial quando você toca os cantos e converta-os em coordenadas da tela com map(). Se você usa a TFT_eSPI, o exemplo Touch_calibrate faz isso por você e mostra um array de calibração pronto.

Qual é a diferença entre o ILI9341 de 2,4 e o de 2,8 polegadas?

Os dois têm resolução de 240×320 pixels e usam os mesmos pinos e o mesmo código. A tela de 2,8 polegadas tem pixels um pouco maiores, então o texto fica mais fácil de ler e os botões de toque mais fáceis de apertar. O módulo de 2,4 polegadas cabe em gabinetes menores.

Posso usar o ILI9341 com um Arduino Nano?

Sim. Os pinos SPI do Nano são os mesmos do Uno: D13 SCK, D11 MOSI e D12 MISO. O Nano também usa lógica de 5 V, então você precisa de um conversor de nível. Os 2 KB de RAM dele bastam para acionar o display, mas não para guardar imagens grandes, então você precisa ler as imagens do cartão SD em pedaços pequenos.

Ver todos os projetos
ESP32 HTTPClient + API de clima (com display OLED SSD1306)
#134Avançado
Premium

ESP32 HTTPClient + API de clima (com display OLED SSD1306)

Neste projeto, usamos o ESP32 para fazer uma chamada REST à API de clima wttr.in pela internet com HTTPClient.h. Diferentemente da versão anterior com LCD I2C, aqui usamos um display OLED SSD1306 (128×64), que permite usar fontes grandes, ícones vetoriais e uma apresentação visual rica em uma única tela. Sete ícones vetoriais de condições do tempo (ensolarado, parcialmente nublado, nublado, chuvoso, com neve, com tempestade e com neblina) são desenhados durante a execução, sem arquivos bitmap.

4.3(10)
19
Registrador de temperatura com ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
#144Avançado
Premium

Registrador de temperatura com ESP32 (DHT22 + DS1307 RTC + microSD + OLED)

Monte um registrador de temperatura com data e hora usando ESP32! Leia a temperatura e a umidade do DHT22 a cada minuto, obtenha a hora real do DS1307 RTC e grave os dados em CSV no cartão microSD. O OLED exibe a temperatura atual em letras grandes, a porcentagem de umidade, a data e a hora e o contador de registros. O arquivo /temp_log.csv no cartão SD pode ser aberto no Excel, Google Sheets ou pandas do Python para análise — ideal para monitorar estufas, acompanhar a temperatura de casa ou…

4.7(7)
12
Luminária com mistura de cores e ESP32 (3 potenciômetros + LED RGB + OLED)
#138Avançado
Premium

Luminária com mistura de cores e ESP32 (3 potenciômetros + LED RGB + OLED)

Uma luminária inteligente que permite misturar a cor que você quiser girando três potenciômetros com o ESP32! Ao girar os potenciômetros de vermelho, verde e azul, o LED RGB de cátodo comum muda de cor em tempo real. O display OLED SSD1306 mostra os valores de cada cor na faixa de 0–255, barras de preenchimento e o código HEX da cor para a web, como #FF8040.

4.7(6)
10
Sensor de estacionamento com ESP32 (HC-SR04 + barra de 5 LEDs + buzzer + OLED)
#142Avançado
Premium

Sensor de estacionamento com ESP32 (HC-SR04 + barra de 5 LEDs + buzzer + OLED)

A versão com ESP32 do sensor de estacionamento de carro! Usamos o sensor ultrassônico HC-SR04 para medir a distância até o objeto à sua frente. Conforme a distância diminui, a barra de 5 LEDs vai se preenchendo (verde → amarelo → vermelho) e o intervalo entre os bipes do buzzer diminui. Quando o objeto chega muito perto (menos de 10 cm), o alarme soa continuamente e todos os LEDs acendem.

0.0(0)
14
Jogo de bola e raquetes - SSD1306
#86Intermediário
Premium

Jogo de bola e raquetes - SSD1306

Uma bola, duas raquetes e você: jogue tênis de mesa, um dos primeiros videogames, em um pequeno display OLED. Dois botões movem sua raquete à direita para cima e para baixo, enquanto o computador cont

4.5(47)
65
Corrida do T-Rex no OLED: jogo do dinossauro com Arduino
#87Avançado
Premium

Corrida do T-Rex no OLED: jogo do dinossauro com Arduino

O jogo do dinossauro que aparece quando a internet cai, agora na sua mesa: o dinossauro corre pelo pequeno display OLED, os cactos vêm na sua direção e você pula com um único botão. Sua pontuação aume

4.7(33)
53
Display LCD I2C com ESP32 (16x2)
#119Intermediário
Premium

Display LCD I2C com ESP32 (16x2)

Neste projeto, exibimos texto conectando um display LCD de 16x2 caracteres ao ESP32 pelo protocolo I2C. Com o I2C, controlamos o display usando apenas dois pinos de dados (SDA e SCL), dispensando o uso de seis ou mais pinos da conexão paralela tradicional. O código mostra uma mensagem fixa na primeira linha e um contador que aumenta a cada segundo na segunda linha. Na inicialização, também executamos um scanner I2C para mostrar o endereço do display no Monitor Serial (geralmente 0x27 ou 0x3F).

4.9(23)
40
Jogo de corrida no LCD com Arduino
#98Intermediário
Premium

Jogo de corrida no LCD com Arduino

Jogo de corrida infinita com rolagem lateral em um display LCD de 16x2 caracteres. O herói corre automaticamente, e o jogador pressiona o botão para pular os obstáculos. Obstáculos aleatórios aparecem nas linhas superior e inferior. O buzzer passivo toca a melodia de abertura, o som de pulo, o som de comemoração a cada 50 pontos e a melodia de fim de jogo. A tela de abertura "BlueGrays - Arduino Projects" aparece ao iniciar. A pontuação é atualizada em tempo real no canto superior direito.

4.6(32)
55