/*
* Bare-Metal SPI TFT Heart Demo for Arduino Uno R3
* --------------------------------------------------
* No Arduino libraries used for SPI, GPIO, or USART.
* Direct ATmega328P register manipulation only.
*
* Target display: ILI9341 240x320 SPI TFT
*
* Pin Mapping (Arduino Uno R3):
* TFT CS -> D10 (PB2)
* TFT DC -> D9 (PB1)
* TFT RST -> D8 (PB0)
* TFT MOSI -> D11 (PB3) [SPI hardware]
* TFT SCK -> D13 (PB5) [SPI hardware]
* TFT MISO -> D12 (PB4) [unused here]
*
* Every SPI command/data byte is echoed to the serial monitor at 9600 baud.
*/
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
/* ---------- Pin macros (Port B) ---------- */
#define CS_PIN PB2 /* D10 */
#define DC_PIN PB1 /* D9 */
#define RST_PIN PB0 /* D8 */
#define MOSI_PIN PB3 /* D11 */
#define SCK_PIN PB5 /* D13 */
#define SS_PIN PB2 /* same as CS here */
#define CS_LOW() (PORTB &= ~(1 << CS_PIN))
#define CS_HIGH() (PORTB |= (1 << CS_PIN))
#define DC_LOW() (PORTB &= ~(1 << DC_PIN)) /* command */
#define DC_HIGH() (PORTB |= (1 << DC_PIN)) /* data */
#define RST_LOW() (PORTB &= ~(1 << RST_PIN))
#define RST_HIGH() (PORTB |= (1 << RST_PIN))
/* ---------- ILI9341 commands ---------- */
#define CMD_SOFTWARE_RESET 0x01
#define CMD_SLEEP_OUT 0x11
#define CMD_DISPLAY_ON 0x29
#define CMD_COLUMN_ADDR_SET 0x2A
#define CMD_PAGE_ADDR_SET 0x2B
#define CMD_MEMORY_WRITE 0x2C
#define CMD_MEMORY_ACCESS 0x36
#define CMD_PIXEL_FORMAT 0x3A
/* ---------- RGB565 colors ---------- */
#define COLOR_BLACK 0x0000
#define COLOR_WHITE 0xFFFF
#define COLOR_RED 0xF800
#define COLOR_PINK 0xFB56
#define SCREEN_W 240
#define SCREEN_H 320
/* =========================================================
* USART (serial) - bare metal at 9600 baud, 16 MHz clock
* ========================================================= */
static void usart_init(void) {
/* UBRR = (F_CPU / (16 * baud)) - 1 = (16000000 / 153600) - 1 = 103 */
UBRR0H = 0;
UBRR0L = 103;
UCSR0B = (1 << TXEN0); /* TX enable */
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); /* 8 data bits, 1 stop, no parity */
}
static void usart_tx(char c) {
while (!(UCSR0A & (1 << UDRE0))) { } /* wait for empty TX buffer */
UDR0 = c;
}
static void usart_print(const char *s) {
while (*s) usart_tx(*s++);
}
static void usart_print_hex(uint8_t b) {
const char hex[] = "0123456789ABCDEF";
usart_print("0x");
usart_tx(hex[(b >> 4) & 0x0F]);
usart_tx(hex[b & 0x0F]);
}
static void usart_print_dec(uint16_t v) {
char buf[6];
int8_t i = 0;
if (v == 0) { usart_tx('0'); return; }
while (v > 0 && i < 6) { buf[i++] = '0' + (v % 10); v /= 10; }
while (i--) usart_tx(buf[i]);
}
/* =========================================================
* SPI - bare metal, master mode, fosc/2 = 8 MHz
* ========================================================= */
static void spi_init(void) {
/* Set MOSI, SCK, SS(CS), DC, RST as outputs */
DDRB |= (1 << MOSI_PIN) | (1 << SCK_PIN) | (1 << CS_PIN)
| (1 << DC_PIN) | (1 << RST_PIN);
/* SS must be OUTPUT HIGH before enabling SPI master, else HW slips into slave */
CS_HIGH();
/* SPCR: SPE=1 enable, MSTR=1 master, CPOL=0, CPHA=0 (Mode 0), SPR1:0=00 -> fosc/4
SPSR: SPI2X=1 -> doubles speed -> fosc/2 = 8 MHz */
SPCR = (1 << SPE) | (1 << MSTR);
SPSR = (1 << SPI2X);
}
static uint8_t spi_transfer(uint8_t b) {
SPDR = b;
while (!(SPSR & (1 << SPIF))) { }
return SPDR;
}
/* =========================================================
* TFT helpers - every call logs to serial
* ========================================================= */
static void tft_write_cmd(uint8_t cmd) {
usart_print("CMD -> DC=LOW CS=LOW byte=");
usart_print_hex(cmd);
usart_print(" CS=HIGH\r\n");
DC_LOW();
CS_LOW();
spi_transfer(cmd);
CS_HIGH();
}
static void tft_write_data(uint8_t d) {
usart_print("DATA -> DC=HIGH CS=LOW byte=");
usart_print_hex(d);
usart_print(" CS=HIGH\r\n");
DC_HIGH();
CS_LOW();
spi_transfer(d);
CS_HIGH();
}
static void tft_write_data16(uint16_t v) {
tft_write_data((uint8_t)(v >> 8));
tft_write_data((uint8_t)(v & 0xFF));
}
/* For pixel streaming, we hold CS low across many bytes and skip per-byte logging. */
static void tft_begin_pixel_stream(void) {
usart_print(">>> begin pixel stream (DC=HIGH, CS held LOW)\r\n");
DC_HIGH();
CS_LOW();
}
static void tft_end_pixel_stream(void) {
CS_HIGH();
usart_print("<<< end pixel stream (CS=HIGH)\r\n");
}
static void tft_stream_pixel(uint16_t color) {
spi_transfer((uint8_t)(color >> 8));
spi_transfer((uint8_t)(color & 0xFF));
}
/* =========================================================
* TFT init / drawing
* ========================================================= */
static void tft_hw_reset(void) {
usart_print("\r\n--- HARDWARE RESET ---\r\n");
RST_HIGH(); _delay_ms(5);
RST_LOW(); _delay_ms(20);
RST_HIGH(); _delay_ms(150);
}
static void tft_init(void) {
tft_hw_reset();
usart_print("--- INIT SEQUENCE ---\r\n");
tft_write_cmd(CMD_SOFTWARE_RESET);
_delay_ms(150);
tft_write_cmd(CMD_SLEEP_OUT);
_delay_ms(120);
tft_write_cmd(CMD_PIXEL_FORMAT);
tft_write_data(0x55); /* 16-bit RGB565 */
tft_write_cmd(CMD_MEMORY_ACCESS);
tft_write_data(0x48); /* MX, BGR */
tft_write_cmd(CMD_DISPLAY_ON);
_delay_ms(100);
usart_print("--- INIT DONE ---\r\n\r\n");
}
static void tft_set_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
usart_print("setWindow(");
usart_print_dec(x0); usart_print(",");
usart_print_dec(y0); usart_print(",");
usart_print_dec(x1); usart_print(",");
usart_print_dec(y1); usart_print(")\r\n");
tft_write_cmd(CMD_COLUMN_ADDR_SET);
tft_write_data16(x0);
tft_write_data16(x1);
tft_write_cmd(CMD_PAGE_ADDR_SET);
tft_write_data16(y0);
tft_write_data16(y1);
tft_write_cmd(CMD_MEMORY_WRITE);
}
static void tft_fill_screen(uint16_t color) {
usart_print("fillScreen(");
usart_print_hex((uint8_t)(color >> 8));
usart_print_hex((uint8_t)(color & 0xFF));
usart_print(")\r\n");
tft_set_window(0, 0, SCREEN_W - 1, SCREEN_H - 1);
tft_begin_pixel_stream();
uint32_t n = (uint32_t)SCREEN_W * SCREEN_H;
while (n--) tft_stream_pixel(color);
tft_end_pixel_stream();
}
/* Draw heart using a centered 16x16 bitmap, scaled by `scale` pixels per cell.
* Heart pattern (1 = filled, 0 = background). */
static const uint16_t heart_bitmap[16] = {
0b0000000000000000,
0b0001100000110000,
0b0011110001111000,
0b0111111011111100,
0b0111111111111100,
0b1111111111111110,
0b1111111111111110,
0b1111111111111110,
0b1111111111111110,
0b0111111111111100,
0b0111111111111100,
0b0011111111111000,
0b0001111111110000,
0b0000111111100000,
0b0000011111000000,
0b0000001110000000,
};
static void draw_heart(uint16_t cx, uint16_t cy, uint8_t scale,
uint16_t fg, uint16_t bg) {
usart_print("\r\n--- DRAWING HEART ---\r\n");
uint16_t side = 16 * scale;
uint16_t x0 = cx - side / 2;
uint16_t y0 = cy - side / 2;
tft_set_window(x0, y0, x0 + side - 1, y0 + side - 1);
tft_begin_pixel_stream();
for (uint8_t row = 0; row < 16; row++) {
for (uint8_t sy = 0; sy < scale; sy++) {
uint16_t bits = heart_bitmap[row];
for (uint8_t col = 0; col < 16; col++) {
uint16_t color = (bits & (1 << (15 - col))) ? fg : bg;
for (uint8_t sx = 0; sx < scale; sx++) {
tft_stream_pixel(color);
}
}
}
}
tft_end_pixel_stream();
usart_print("--- HEART DONE ---\r\n");
}
/* =========================================================
* Main
* ========================================================= */
int main(void) {
usart_init();
usart_print("\r\n=== Bare-Metal SPI TFT Heart Demo ===\r\n");
usart_print("F_CPU=16MHz, SPI=8MHz, Mode 0, MSB first\r\n\r\n");
spi_init();
tft_init();
tft_fill_screen(COLOR_BLACK);
draw_heart(SCREEN_W / 2, SCREEN_H / 2, 10, COLOR_RED, COLOR_BLACK);
usart_print("\r\n=== DONE - heart is on screen ===\r\n");
for (;;) { } /* idle forever */
return 0;
}
Loading
ili9341-cap-touch
ili9341-cap-touch