Last active
October 30, 2018 21:16
-
-
Save vsch/4c173dfc92772ac2d11de16fe1c2d94c to your computer and use it in GitHub Desktop.
Text version of Bruce E. Hall TFT 1.8 Driver from http://w8bh.net/avr/AvrTFT.pdf for ATmega328P microcontroller
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//----------------------------------------------------------------------------- // TFT: Experiments interfacing ATmega328 to an ST7735 1.8" LCD TFT display // | |
// Author : Bruce E. Hall <bhall66@gmail.com> | |
// Website : w8bh.net | |
// Version : 1.0 | |
// Date : 04 May 2014 | |
// Target : ATmega328P microcontroller | |
// Language : C, using AVR studio 6 | |
// Size : 3622 bytes | |
// | |
// Fuse settings: 8 MHz osc with 65 ms Delay, SPI enable; *NO* clock/8 // | |
// Connections from LCD to DC Boarduino: | |
// | |
// TFT pin 1 (backlight) +5V | |
// TFT pin 2 (MISO) n/c | |
// TFT pin 3 (SCK) digital13, PB5(SCK) | |
// TFT pin 4 (MOSI) digital11, PB3(MOSI) | |
// TFT pin 5 (TFT_Select) gnd | |
// TFT pin 7 (DC) digital8, PB0 | |
// TFT pin 8 (Reset) digital9, PB1 | |
// TFT pin 9 (Vcc) +5V | |
// TFT pin 10 (gnd) gnd | |
// | |
//--------------------------------------------------------------------------- | |
// GLOBAL DEFINES | |
#define F_CPU 16000000L // run CPU at 16 MHz | |
#define LED 5 // Boarduino LED on PB5 | |
#define ClearBit(x,y) x &= ~_BV(y) // equivalent to cbi(x,y) | |
#define SetBit(x,y) x |= _BV(y) // equivalent to sbi(x,y) | |
#define TFT_DC 0 // DC 8 | |
#define TFT_RST 1 // DC 9 | |
#define TFT_CS 2 // DC 10 | |
// --------------------------------------------------------------------------- | |
// INCLUDES | |
#include <avr/io.h> // deal with port registers | |
#include <avr/interrupt.h> // deal with interrupt calls | |
#include <avr/pgmspace.h> // put character data into progmem | |
#include <util/delay.h> // used for _delay_ms function | |
#include <string.h> // string manipulation routines | |
#include <avr/sleep.h> // used for sleep functions | |
#include <stdlib.h> | |
// --------------------------------------------------------------------------- | |
// TYPEDEFS | |
typedef uint8_t byte; // I just like byte & sbyte better | |
typedef int8_t sbyte; | |
// --------------------------------------------------------------------------- | |
// GLOBAL VARIABLES | |
const byte FONT_CHARS[96][5] PROGMEM = { | |
{0x00, 0x00, 0x00, 0x00, 0x00}, // (space) | |
{0x00, 0x00, 0x5F, 0x00, 0x00}, // ! | |
{0x00, 0x07, 0x00, 0x07, 0x00}, // " | |
{0x14, 0x7F, 0x14, 0x7F, 0x14}, // # | |
{0x24, 0x2A, 0x7F, 0x2A, 0x12}, // $ | |
{0x23, 0x13, 0x08, 0x64, 0x62}, // % | |
{0x36, 0x49, 0x55, 0x22, 0x50}, // & | |
{0x00, 0x05, 0x03, 0x00, 0x00}, // ' | |
{0x00, 0x1C, 0x22, 0x41, 0x00}, // ( | |
{0x00, 0x41, 0x22, 0x1C, 0x00}, // ) | |
{0x08, 0x2A, 0x1C, 0x2A, 0x08}, // * | |
{0x08, 0x08, 0x3E, 0x08, 0x08}, // + | |
{0x00, 0x50, 0x30, 0x00, 0x00}, // , | |
{0x08, 0x08, 0x08, 0x08, 0x08}, // - | |
{0x00, 0x60, 0x60, 0x00, 0x00}, // . | |
{0x20, 0x10, 0x08, 0x04, 0x02}, // / | |
{0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0 | |
{0x00, 0x42, 0x7F, 0x40, 0x00}, // 1 | |
{0x42, 0x61, 0x51, 0x49, 0x46}, // 2 | |
{0x21, 0x41, 0x45, 0x4B, 0x31}, // 3 | |
{0x18, 0x14, 0x12, 0x7F, 0x10}, // 4 | |
{0x27, 0x45, 0x45, 0x45, 0x39}, // 5 | |
{0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6 | |
{0x01, 0x71, 0x09, 0x05, 0x03}, // 7 | |
{0x36, 0x49, 0x49, 0x49, 0x36}, // 8 | |
{0x06, 0x49, 0x49, 0x29, 0x1E}, // 9 | |
{0x00, 0x36, 0x36, 0x00, 0x00}, // : | |
{0x00, 0x56, 0x36, 0x00, 0x00}, // ; | |
{0x00, 0x08, 0x14, 0x22, 0x41}, // < | |
{0x14, 0x14, 0x14, 0x14, 0x14}, // = | |
{0x41, 0x22, 0x14, 0x08, 0x00}, // > | |
{0x02, 0x01, 0x51, 0x09, 0x06}, // ? | |
{0x32, 0x49, 0x79, 0x41, 0x3E}, // @ | |
{0x7E, 0x11, 0x11, 0x11, 0x7E}, // A | |
{0x7F, 0x49, 0x49, 0x49, 0x36}, // B | |
{0x3E, 0x41, 0x41, 0x41, 0x22}, // C | |
{0x7F, 0x41, 0x41, 0x22, 0x1C}, // D | |
{0x7F, 0x49, 0x49, 0x49, 0x41}, // E | |
{0x7F, 0x09, 0x09, 0x01, 0x01}, // F | |
{0x3E, 0x41, 0x41, 0x51, 0x32}, // G | |
{0x7F, 0x08, 0x08, 0x08, 0x7F}, // H | |
{0x00, 0x41, 0x7F, 0x41, 0x00}, // I | |
{0x20, 0x40, 0x41, 0x3F, 0x01}, // J | |
{0x7F, 0x08, 0x14, 0x22, 0x41}, // K | |
{0x7F, 0x40, 0x40, 0x40, 0x40}, // L | |
{0x7F, 0x02, 0x04, 0x02, 0x7F}, // M | |
{0x7F, 0x04, 0x08, 0x10, 0x7F}, // N | |
{0x3E, 0x41, 0x41, 0x41, 0x3E}, // O | |
{0x7F, 0x09, 0x09, 0x09, 0x06}, // P | |
{0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q | |
{0x7F, 0x09, 0x19, 0x29, 0x46}, // R | |
{0x46, 0x49, 0x49, 0x49, 0x31}, // S | |
{0x01, 0x01, 0x7F, 0x01, 0x01}, // T | |
{0x3F, 0x40, 0x40, 0x40, 0x3F}, // U | |
{0x1F, 0x20, 0x40, 0x20, 0x1F}, // V | |
{0x7F, 0x20, 0x18, 0x20, 0x7F}, // W | |
{0x63, 0x14, 0x08, 0x14, 0x63}, // X | |
{0x03, 0x04, 0x78, 0x04, 0x03}, // Y | |
{0x61, 0x51, 0x49, 0x45, 0x43}, // Z | |
{0x00, 0x00, 0x7F, 0x41, 0x41}, // [ | |
{0x02, 0x04, 0x08, 0x10, 0x20}, // "\" | |
{0x41, 0x41, 0x7F, 0x00, 0x00}, // ] | |
{0x04, 0x02, 0x01, 0x02, 0x04}, // ^ | |
{0x40, 0x40, 0x40, 0x40, 0x40}, // _ | |
{0x00, 0x01, 0x02, 0x04, 0x00}, // ` | |
{0x20, 0x54, 0x54, 0x54, 0x78}, // a | |
{0x7F, 0x48, 0x44, 0x44, 0x38}, // b | |
{0x38, 0x44, 0x44, 0x44, 0x20}, // c | |
{0x38, 0x44, 0x44, 0x48, 0x7F}, // d | |
{0x38, 0x54, 0x54, 0x54, 0x18}, // e | |
{0x08, 0x7E, 0x09, 0x01, 0x02}, // f | |
{0x08, 0x14, 0x54, 0x54, 0x3C}, // g | |
{0x7F, 0x08, 0x04, 0x04, 0x78}, // h | |
{0x00, 0x44, 0x7D, 0x40, 0x00}, // i | |
{0x20, 0x40, 0x44, 0x3D, 0x00}, // j | |
{0x00, 0x7F, 0x10, 0x28, 0x44}, // k | |
{0x00, 0x41, 0x7F, 0x40, 0x00}, // l | |
{0x7C, 0x04, 0x18, 0x04, 0x78}, // m | |
{0x7C, 0x08, 0x04, 0x04, 0x78}, // n | |
{0x38, 0x44, 0x44, 0x44, 0x38}, // o | |
{0x7C, 0x14, 0x14, 0x14, 0x08}, // p | |
{0x08, 0x14, 0x14, 0x18, 0x7C}, // q | |
{0x7C, 0x08, 0x04, 0x04, 0x08}, // r | |
{0x48, 0x54, 0x54, 0x54, 0x20}, // s | |
{0x04, 0x3F, 0x44, 0x40, 0x20}, // t | |
{0x3C, 0x40, 0x40, 0x20, 0x7C}, // u | |
{0x1C, 0x20, 0x40, 0x20, 0x1C}, // v | |
{0x3C, 0x40, 0x30, 0x40, 0x3C}, // w | |
{0x44, 0x28, 0x10, 0x28, 0x44}, // x | |
{0x0C, 0x50, 0x50, 0x50, 0x3C}, // y | |
{0x44, 0x64, 0x54, 0x4C, 0x44}, // z | |
{0x00, 0x08, 0x36, 0x41, 0x00}, // { | |
{0x00, 0x00, 0x7F, 0x00, 0x00}, // | | |
{0x00, 0x41, 0x36, 0x08, 0x00}, // } | |
{0x08, 0x08, 0x2A, 0x1C, 0x08}, // -> | |
{0x08, 0x1C, 0x2A, 0x08, 0x08}, // <- | |
}; | |
// --------------------------------------------------------------------------- | |
// MISC ROUTINES | |
void SetupPorts() | |
{ | |
DDRB = 0x2F; // 0010.1111; set B0-B3, B5 as outputs | |
DDRC = 0x00; // 0000.0000; set PORTC as inputs | |
SetBit(PORTB,TFT_RST); // start with TFT reset line inactive high | |
SetBit(PORTB, TFT_CS); // deselect TFT CS | |
} | |
// put into a routine to remove code inlining at cost of timing accuracy | |
void msDelay(int delay) | |
{ | |
for (int i=0;i<delay;i++) _delay_ms(1); | |
} | |
// flash the on-board LED at ~ 3 Hz | |
void FlashLED(byte count) | |
{ | |
for (; count > 0; count--) { | |
SetBit(PORTB, LED); // turn LED on | |
msDelay(150); // wait | |
ClearBit(PORTB, LED); // turn LED off | |
msDelay(150); // wait | |
} | |
} | |
// calculate integer value of square root | |
unsigned long intsqrt(unsigned long val) { | |
unsigned long mulMask = 0x0008000; | |
unsigned long retVal = 0; | |
if (val > 0) { | |
while (mulMask != 0) { | |
retVal |= mulMask; | |
if ((retVal * retVal) > val) | |
retVal &= ~mulMask; | |
mulMask >>= 1; | |
} | |
} | |
return retVal; | |
} | |
// --------------------------------------------------------------------------- | |
// SPI ROUTINES | |
// | |
// b7 b6 b5 b4 b3 b2 b1 b0 | |
// SPCR: SPIE SPE DORD MSTR CPOL CPHA SPR1 SPR0 | |
// 0 1 0 1 . 0 0 0 1 | |
// | |
// SPIE - enable SPI interrupt | |
// SPE - enable SPI | |
// DORD - 0=MSB first, 1=LSB first | |
// MSTR - 0=slave, 1=master | |
// CPOL - 0=clock starts low, 1=clock starts high | |
// CPHA - 0=read on rising-edge, 1=read on falling-edge | |
// SPRx - 00=osc/4, 01=osc/16, 10=osc/64, 11=osc/128 // | |
// SPCR = 0x50: SPI enabled as Master, mode 0, at 16/4 = 4 MHz | |
void OpenSPI() { | |
SPCR = 0x50; // SPI enabled as Master, Mode0 at 4 MHz | |
SetBit(SPSR, SPI2X); // double the SPI rate: 4-->8 MHz | |
ClearBit(PORTB, TFT_CS); // select TFT CS | |
} | |
void CloseSPI() { | |
SPCR = 0x00; // clear SPI enable bit | |
SetBit(PORTB, TFT_CS); // deselect TFT CS | |
} | |
byte Xfer(byte data) { | |
SPDR = data; // initiate transfer | |
while (!(SPSR & 0x80)); // wait for transfer to complete | |
return SPDR; | |
} | |
// --------------------------------------------------------------------------- | |
// ST7735 ROUTINES | |
#define SWRESET 0x01 // software reset | |
#define SLPOUT 0x11 // sleep out | |
#define DISPOFF 0x28 // display off | |
#define DISPON 0x29 // display on | |
#define CASET 0x2A // column address set | |
#define RASET 0x2B // row address set | |
#define RAMWR 0x2C // RAM write | |
#define MADCTL 0x36 // axis control | |
#define COLMOD 0x3A // color mode | |
// 1.8" TFT display constants | |
#define XSIZE 128 | |
#define YSIZE 160 | |
#define XMAX XSIZE-1 | |
#define YMAX YSIZE-1 | |
// Color constants | |
#define BLACK 0x0000 | |
#define BLUE 0x001F | |
#define RED 0xF800 | |
#define GREEN 0x0400 | |
#define LIME 0x07E0 | |
#define CYAN 0x07FF | |
#define MAGENTA 0xF81F | |
#define YELLOW 0xFFE0 | |
#define WHITE 0xFFFF | |
void WriteCmd(byte cmd) { | |
ClearBit(PORTB, TFT_DC); // B1=DC; 0=command, 1=data | |
Xfer(cmd); | |
SetBit(PORTB, TFT_DC); // return DC high | |
} | |
void WriteByte(byte b) { | |
Xfer(b); | |
} | |
void WriteWord(int w) { | |
Xfer(w >> 8); // write upper 8 bits | |
Xfer(w & 0xFF); // write lower 8 bits | |
} | |
void Write888(long data, int count) { | |
byte red = data >> 16; // red = upper 8 bits | |
byte green = (data >> 8) & 0xFF; // green = middle 8 bits | |
byte blue = data & 0xFF; // blue = lower 8 bits | |
for (; count > 0; count--) { | |
WriteByte(red); | |
WriteByte(green); | |
WriteByte(blue); | |
} | |
} | |
// send 16-bit pixel data to the controller | |
// note: inlined spi xfer for optimization | |
void Write565(int data, unsigned int count) { | |
WriteCmd(RAMWR); | |
for (; count > 0; count--) { | |
SPDR = (data >> 8); // write hi byte | |
while (!(SPSR & 0x80)); // wait for transfer to complete | |
SPDR = (data & 0xFF); // write lo byte | |
while (!(SPSR & 0x80)); // wait for transfer to complete | |
} | |
} | |
void HardwareReset() { | |
ClearBit(PORTB, TFT_RST); // pull PB0 (digital 8) low | |
msDelay(1); // 1mS is enough | |
SetBit(PORTB, TFT_RST); // return PB0 high | |
msDelay(150); // wait 150mS for reset to finish | |
} | |
void InitDisplay() | |
{ | |
HardwareReset(); // initialize display controller | |
WriteCmd(SLPOUT); // take display out of sleep mode | |
msDelay(150); // wait 150mS for TFT driver circuits | |
WriteCmd(COLMOD); // select color mode: | |
WriteByte(0x05); // mode 5 = 16bit pixels (RGB565) | |
WriteCmd(DISPON); // turn display on! | |
} | |
void SetAddrWindow(byte x0, byte y0, byte x1, byte y1) { | |
WriteCmd(CASET); // set column range (x0,x1) | |
WriteWord(x0); | |
WriteWord(x1); | |
WriteCmd(RASET); // set row range (y0,y1) | |
WriteWord(y0); | |
WriteWord(y1); | |
} | |
void ClearScreen() { | |
SetAddrWindow(0, 0, XMAX, YMAX); // set window to entire display | |
WriteCmd(RAMWR); | |
for (unsigned int i = 40960; i > 0; --i) // byte count = 128*160*2 | |
{ | |
SPDR = 0; // initiate transfer of 0x00 | |
while (!(SPSR & 0x80)); // wait for xfer to finish | |
} | |
} | |
// --------------------------------------------------------------------------- | |
// SIMPLE GRAPHICS ROUTINES | |
// | |
// note: many routines have byte parameters, to save space, | |
// but these can easily be changed to int params for larger displays. | |
void DrawPixel (byte x, byte y, int color) { | |
SetAddrWindow(x,y,x,y); | |
Write565(color,1); | |
} | |
// draws a horizontal line in given color | |
void HLine (byte x0, byte x1, byte y, int color) | |
{ | |
byte width = x1-x0+1; | |
SetAddrWindow(x0,y,x1,y); | |
Write565(color,width); | |
} | |
// draws a vertical line in given color | |
void VLine (byte x, byte y0, byte y1, int color) | |
{ | |
byte height = y1-y0+1; | |
SetAddrWindow(x,y0,x,y1); | |
Write565(color,height); | |
} | |
// an elegant implementation of the Bresenham algorithm | |
void Line(int x0, int y0, int x1, int y1, int color) { | |
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1; | |
int dy = abs(y1 - y0), sy = y0 < y1 ? 1 : -1; | |
int err = (dx > dy ? dx : -dy) / 2, e2; | |
for (;;) { | |
DrawPixel(x0, y0, color); | |
if (x0 == x1 && y0 == y1) break; | |
e2 = err; | |
if (e2 > -dx) { | |
err -= dy; | |
x0 += sx; | |
} | |
if (e2 < dy) { | |
err += dx; | |
y0 += sy; | |
} | |
} | |
} | |
// draws a rectangle in given color | |
void DrawRect(byte x0, byte y0, byte x1, byte y1, int color) { | |
HLine(x0, x1, y0, color); | |
HLine(x0, x1, y1, color); | |
VLine(x0, y0, y1, color); | |
VLine(x1, y0, y1, color); | |
} | |
void FillRect(byte x0, byte y0, byte x1, byte y1, int color) { | |
byte width = x1 - x0 + 1; | |
byte height = y1 - y0 + 1; | |
SetAddrWindow(x0, y0, x1, y1); | |
Write565(color, width * height); | |
} | |
// draws circle quadrant(s) centered at x,y with given radius & color | |
// quad is a bit-encoded representation of which cartesian quadrant(s) to draw. // Remember that the y axis on our display is 'upside down': | |
// bit 0: draw quadrant I (lower right) | |
// bit 1: draw quadrant IV (upper right) | |
// bit 2: draw quadrant II (lower left) | |
// bit 3: draw quadrant III (upper left) | |
void CircleQuadrant(byte xPos, byte yPos, byte radius, byte quad, int color) { | |
int x, xEnd = (707 * radius) / 1000 + 1; | |
for (x = 0; x < xEnd; x++) { | |
byte y = intsqrt(radius * radius - x * x); | |
if (quad & 0x01) { | |
DrawPixel(xPos + x, yPos + y, color); // lower right | |
DrawPixel(xPos + y, yPos + x, color); | |
} | |
if (quad & 0x02) { | |
DrawPixel(xPos + x, yPos - y, color); // upper right | |
DrawPixel(xPos + y, yPos - x, color); | |
} | |
if (quad & 0x04) { | |
DrawPixel(xPos - x, yPos + y, color); // lower left | |
DrawPixel(xPos - y, yPos + x, color); | |
} | |
if (quad & 0x08) { | |
DrawPixel(xPos - x, yPos - y, color); // upper left | |
DrawPixel(xPos - y, yPos - x, color); | |
} | |
} | |
} | |
// draws circle at x,y with given radius & color | |
void Circle (byte xPos, byte yPos, byte radius, int color) | |
{ | |
CircleQuadrant(xPos, yPos, radius, 0x0F, color); // do all 4 quadrants | |
} | |
// coordinates: top left = x0,y0; bottom right = x1,y1 | |
void RoundRect(byte x0, byte y0, byte x1, byte y1, byte r, int color) // draws a rounded rectangle with corner radius r. | |
{ | |
HLine(x0 + r, x1 - r, y0, color); // top side | |
HLine(x0 + r, x1 - r, y1, color); // bottom side | |
VLine(x0, y0 + r, y1 - r, color); // left side | |
VLine(x1, y0 + r, y1 - r, color); // right side | |
CircleQuadrant(x0 + r, y0 + r, r, 8, color); // upper left corner | |
CircleQuadrant(x1 - r, y0 + r, r, 2, color); // upper right corner | |
CircleQuadrant(x0 + r, y1 - r, r, 4, color); // lower left corner | |
CircleQuadrant(x1 - r, y1 - r, r, 1, color); // lower right corner | |
} | |
// draws filled circle at x,y with given radius & color | |
void FillCircle(byte xPos, byte yPos, byte radius, int color) { | |
long r2 = radius * radius; | |
for (int x = 0; x <= radius; x++) { | |
byte y = intsqrt(r2 - x * x); | |
byte y0 = yPos - y; | |
byte y1 = yPos + y; | |
VLine(xPos + x, y0, y1, color); | |
VLine(xPos - x, y0, y1, color); | |
} | |
} | |
// draws an ellipse of given width & height | |
// two-part Bresenham method | |
// note: slight discontinuity between parts on some (narrow) ellipses. | |
void Ellipse(int x0, int y0, int width, int height, int color) { | |
int a = width / 2, b = height / 2; | |
int x = 0, y = b; | |
long a2 = (long) a * a * 2; | |
long b2 = (long) b * b * 2; | |
long error = (long) a * a * b; | |
long stopY = 0, stopX = a2 * b; | |
while (stopY <= stopX) { | |
DrawPixel(x0 + x, y0 + y, color); | |
DrawPixel(x0 + x, y0 - y, color); | |
DrawPixel(x0 - x, y0 + y, color); | |
DrawPixel(x0 - x, y0 - y, color); | |
x++; | |
error -= b2 * (x - 1); | |
stopY += b2; | |
if (error < 0) { | |
error += a2 * (y - 1); | |
y--; | |
stopX -= a2; | |
} | |
} | |
x = a; | |
y = 0; | |
error = b * b * a; | |
stopY = a * b2; | |
stopX = 0; | |
while (stopY >= stopX) { | |
DrawPixel(x0 + x, y0 + y, color); | |
DrawPixel(x0 + x, y0 - y, color); | |
DrawPixel(x0 - x, y0 + y, color); | |
DrawPixel(x0 - x, y0 - y, color); | |
y++; | |
error -= a2 * (y - 1); | |
stopX += a2; | |
if (error < 0) { | |
error += b2 * (x - 1); | |
x--; | |
stopY -= b2; | |
} | |
} | |
} | |
// draws a filled ellipse of given width & height | |
void FillEllipse(int xPos, int yPos, int width, int height, int color) { | |
int a = width / 2, b = height / 2; // get x & y radii | |
int x1, x0 = a, y = 1, dx = 0; | |
long a2 = a * a, b2 = b * b; | |
long a2b2 = a2 * b2; // need longs: big numbers! | |
HLine(xPos - a, xPos + a, yPos, color); // draw centerline | |
while (y <= b) { // draw horizontal lines... | |
for (x1 = x0 - (dx - 1); x1 > 0; x1--) | |
if (b2 * x1 * x1 + a2 * y * y <= a2b2) break; | |
dx = x0 - x1; | |
x0 = x1; | |
HLine(xPos - x0, xPos + x0, yPos + y, color); | |
HLine(xPos - x0, xPos + x0, yPos - y, color); | |
y += 1; | |
} | |
} | |
// --------------------------------------------------------------------------- | |
// TEXT ROUTINES | |
// | |
// Each ASCII character is 5x7, with one pixel space between characters | |
// So, character width = 6 pixels & character height = 8 pixels. // | |
// In portrait mode: | |
// Display width = 128 pixels, so there are 21 chars/row (21x6 = 126). | |
// Display height = 160 pixels, so there are 20 rows (20x8 = 160). | |
// Total number of characters in portait mode = 21 x 20 = 420. // | |
// In landscape mode: | |
// Display width is 160, so there are 26 chars/row (26x6 = 156). | |
// Display height is 128, so there are 16 rows (16x8 = 128). | |
// Total number of characters in landscape mode = 26x16 = 416. | |
byte curX,curY; // current x & y cursor position | |
// position cursor on character x,y grid, where 0<x<20, 0<y<19. | |
void GotoXY(byte x, byte y) { | |
curX = x; | |
curY = y; | |
} | |
// position character cursor to start of line y, where 0<y<19. | |
void GotoLine(byte y) { | |
curX = 0; | |
curY = y; | |
} | |
// moves character cursor to next position, assuming portrait orientation | |
void AdvanceCursor() { | |
curX++; // advance x position | |
if (curX > 20) { // beyond right margin? | |
curY++; // go to next line | |
curX = 0; // at left margin | |
} | |
if (curY > 19) // beyond bottom margin? | |
curY = 0; // start at top again | |
} | |
// Set the display orientation to 0,90,180,or 270 degrees | |
void SetOrientation(int degrees) | |
{ | |
byte arg; | |
switch (degrees) | |
{ | |
case 90: arg = 0x60; break; | |
case 180: arg = 0xC0; break; | |
case 270: arg = 0xA0; break; | |
default: arg = 0x00; break; | |
} | |
WriteCmd(MADCTL); | |
WriteByte(arg); | |
} | |
// write ch to display X,Y coordinates using ASCII 5x7 font | |
void PutCh(char ch, byte x, byte y, int color) { | |
int pixel; | |
byte row, col, bit, data, mask = 0x01; | |
SetAddrWindow(x, y, x + 4, y + 6); | |
WriteCmd(RAMWR); | |
for (row = 0; row < 7; row++) { | |
for (col = 0; col < 5; col++) { | |
data = pgm_read_byte(&(FONT_CHARS[ch - 32][col])); | |
bit = data & mask; | |
if (bit == 0) pixel = BLACK; | |
else pixel = color; | |
WriteWord(pixel); | |
} | |
mask <<= 1; | |
} | |
} | |
// writes character to display at current cursor position. | |
void WriteChar(char ch, int color) { | |
PutCh(ch, curX * 6, curY * 8, color); | |
AdvanceCursor(); | |
} | |
// writes string to display at current cursor position. | |
void WriteString(const char *text, int color) { | |
for (; *text; text++) // for all non-nul chars | |
WriteChar(*text, color); // write the char | |
} | |
// writes integer i at current cursor position | |
void WriteInt(int i) { | |
char str[8]; // buffer for string result | |
itoa(i, str, 10); // convert to string, base 10 | |
WriteString(str, WHITE); | |
} | |
// writes hexadecimal value of integer i at current cursor position | |
void WriteHex(int i) { | |
char str[8]; // buffer for string result | |
itoa(i, str, 16); // convert to base 16 (hex) | |
WriteString(str, WHITE); | |
} | |
// --------------------------------------------------------------------------- // TEST ROUTINES | |
// draws 4000 pixels on the screen | |
void PixelTest() { | |
for (int i = 4000; i > 0; i--) // do a whole bunch: | |
{ | |
int x = rand() % XMAX; // random x coordinate | |
int y = rand() % YMAX; // random y coordinate | |
DrawPixel(x, y, YELLOW); // draw pixel at x,y | |
} | |
} | |
// sweeps Line routine through all four quadrants. | |
void LineTest() { | |
ClearScreen(); | |
int x, y, x0 = 64, y0 = 80; | |
for (x = 0; x < XMAX; x += 2) Line(x0, y0, x, 0, YELLOW); | |
for (y = 0; y < YMAX; y += 2) Line(x0, y0, XMAX, y, CYAN); | |
for (x = XMAX; x > 0; x -= 2) Line(x0, y0, x, YMAX, YELLOW); | |
for (y = YMAX; y > 0; y -= 2) Line(x0, y0, 0, y, CYAN); | |
msDelay(2000); | |
} | |
// draw series of concentric circles | |
void CircleTest() { | |
for (int radius = 6; radius < 60; radius += 2) | |
Circle(60, 80, radius, YELLOW); | |
} | |
// Writes 420 characters (5x7) to screen in portrait mode | |
void PortraitChars() { | |
ClearScreen(); | |
for (int i = 420; i > 0; i--) { | |
byte x = i % 21; | |
byte y = i / 21; | |
char ascii = (i % 96) + 32; | |
PutCh(ascii, x * 6, y * 8, CYAN); | |
} | |
msDelay(2000); | |
} | |
// --------------------------------------------------------------------------- // MAIN PROGRAM | |
int main() { | |
SetupPorts(); // use PortB for LCD interface | |
FlashLED(1); // indicate program start | |
OpenSPI(); // start communication to TFT | |
InitDisplay(); // initialize TFT controller | |
PortraitChars(); // show full screen of ASCII chars | |
LineTest(); // paint background of lines | |
FillEllipse(60, 75, 100, 50, BLACK); // erase an oval in center | |
Ellipse(60, 75, 100, 50, LIME); // outline the oval in green | |
const char *str = "Hello, World!"; // text to display | |
GotoXY(4, 9); // position text cursor | |
WriteString(str, YELLOW); // display text inside oval | |
CloseSPI(); // close communication with TFT | |
FlashLED(3); // indicate program end | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Revision 2 has RST and DC pin definitions reversed to match pins used by Adafruit graphicstest sample for ST7735 library.