350 lines
8.9 KiB
C
350 lines
8.9 KiB
C
/*
|
|
* Copyright 2017 Oleg Borodin <onborodin@gmail.com>
|
|
*
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include <avr/io.h>
|
|
#include <util/delay.h>
|
|
#include <avr/pgmspace.h>
|
|
#include <avr/interrupt.h>
|
|
|
|
#include <tools.h>
|
|
#include <spi.h>
|
|
#include <lcd.h>
|
|
|
|
#define MADCTL_MY 0x80
|
|
#define MADCTL_MX 0x40
|
|
#define MADCTL_MV 0x20
|
|
#define MADCTL_ML 0x10
|
|
#define MADCTL_RGB 0x00
|
|
#define MADCTL_BGR 0x08
|
|
#define MADCTL_MH 0x04
|
|
|
|
#define LCD_DELTA_X 1
|
|
#define LCD_DELTA_Y 0
|
|
|
|
void lcd_write_command(uint8_t c) {
|
|
REG_SETDOWN_BIT(PORTB, PIN_A0);
|
|
REG_SETDOWN_BIT(PORTB, PIN_SS);
|
|
|
|
spi_write_byte(c);
|
|
|
|
REG_SETUP_BIT(PORTB, PIN_A0);
|
|
}
|
|
|
|
void lcd_write_byte(uint8_t c) {
|
|
REG_SETUP_BIT(PORTB, PIN_A0);
|
|
REG_SETDOWN_BIT(PORTB, PIN_SS);
|
|
|
|
spi_write_byte(c);
|
|
|
|
REG_SETUP_BIT(PORTB, PIN_A0);
|
|
}
|
|
|
|
void lcd_write_word(uint16_t w) {
|
|
REG_SETUP_BIT(PORTB, PIN_A0);
|
|
REG_SETDOWN_BIT(PORTB, PIN_SS);
|
|
|
|
spi_write_word(w);
|
|
|
|
REG_SETUP_BIT(PORTB, PIN_A0);
|
|
}
|
|
|
|
void lcd_write_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t color) {
|
|
|
|
lcd_addr_window(x, y, (x + w), (y + h));
|
|
|
|
REG_SETUP_BIT(PORTB, PIN_A0);
|
|
REG_SETDOWN_BIT(PORTB, PIN_SS);
|
|
|
|
spi_write_word_array(color, (w + 1) * (h + 1));
|
|
|
|
REG_SETUP_BIT(PORTB, PIN_A0);
|
|
}
|
|
|
|
void lcd_reset(void) {
|
|
REG_SETUP_BIT(PORTB, PIN_RESET);
|
|
_delay_ms(100);
|
|
REG_SETDOWN_BIT(PORTB, PIN_RESET);
|
|
_delay_ms(100);
|
|
REG_SETUP_BIT(PORTB, PIN_RESET);
|
|
_delay_ms(100);
|
|
}
|
|
|
|
void lcd_init(void) {
|
|
|
|
REG_SETUP_BIT(PORTB, PIN_RESET);
|
|
|
|
/* 1: Software reset, 0 args, w/delay */
|
|
lcd_write_command(ST7735_SWRESET);
|
|
/* 150 ms delay */
|
|
_delay_ms(150);
|
|
|
|
/* 2: Out of sleep mode, 0 args, w/delay */
|
|
lcd_write_command(ST7735_SLPOUT);
|
|
/* 500 ms delay */
|
|
_delay_ms(500);
|
|
/* 3: Frame rate ctrl - normal mode, 3 args: */
|
|
lcd_write_command(ST7735_FRMCTR1);
|
|
/* Rate = fosc/(1x2+40) * (LINE+2C+2D) */
|
|
lcd_write_byte(0x01);
|
|
lcd_write_byte(0x2C);
|
|
lcd_write_byte(0x2D);
|
|
/* 4: Frame rate control - idle mode, 3 args: */
|
|
lcd_write_command(ST7735_FRMCTR2);
|
|
/* Rate = fosc/(1x2+40) * (LINE+2C+2D) */
|
|
lcd_write_byte(0x01);
|
|
lcd_write_byte(0x2C);
|
|
lcd_write_byte(0x2D);
|
|
/* 5: Frame rate ctrl - partial mode, 6 args: */
|
|
lcd_write_command(ST7735_FRMCTR3);
|
|
/* Dot inversion mode */
|
|
lcd_write_byte(0x01);
|
|
lcd_write_byte(0x2C);
|
|
lcd_write_byte(0x2D);
|
|
/* Line inversion mode */
|
|
lcd_write_byte(0x01);
|
|
lcd_write_byte(0x2C);
|
|
lcd_write_byte(0x2D);
|
|
|
|
/* 6: Display inversion ctrl, 1 arg, no delay: */
|
|
lcd_write_command(ST7735_INVCTR);
|
|
/* No inversion */
|
|
lcd_write_byte(0x07);
|
|
|
|
/* 7: Power control, 3 args, no delay: */
|
|
lcd_write_command(ST7735_PWCTR1);
|
|
lcd_write_byte(0xA2);
|
|
/* -4.6V */
|
|
lcd_write_byte(0x02);
|
|
/* AUTO mode */
|
|
lcd_write_byte(0x84);
|
|
|
|
/* 8: Power control, 1 arg, no delay: */
|
|
lcd_write_command(ST7735_PWCTR2);
|
|
/* VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD */
|
|
lcd_write_byte(0xC5);
|
|
|
|
/* 9: Power control, 2 args, no delay: */
|
|
lcd_write_command(ST7735_PWCTR3);
|
|
/* Opamp current small */
|
|
lcd_write_byte(0x0A);
|
|
/* Boost frequency */
|
|
lcd_write_byte(0x00);
|
|
|
|
/* 10: Power control, 2 args, no delay: */
|
|
lcd_write_command(ST7735_PWCTR4);
|
|
/* BCLK/2, Opamp current small & Medium low */
|
|
lcd_write_byte(0x8A);
|
|
lcd_write_byte(0x2A);
|
|
|
|
/* 11: Power control, 2 args, no delay: */
|
|
lcd_write_command(ST7735_PWCTR5);
|
|
lcd_write_byte(0x8A);
|
|
lcd_write_byte(0xEE);
|
|
|
|
/* 12: Power control, 1 arg, no delay: */
|
|
lcd_write_command(ST7735_VMCTR1);
|
|
lcd_write_byte(0x0E);
|
|
|
|
/* 13: Don't invert display, no args, no delay */
|
|
lcd_write_command(ST7735_INVOFF);
|
|
|
|
/* 14: Memory access control (directions), 1 arg: */
|
|
lcd_write_command(ST7735_MADCTL);
|
|
/* row addr/col addr, bottom to top refresh, RGB order */
|
|
lcd_write_byte(0xC0);
|
|
|
|
/* 15: Set color mode, 1 arg + delay: */
|
|
lcd_write_command(ST7735_COLMOD);
|
|
/* 16-bit color 5-6-5 color format */
|
|
lcd_write_byte(0x05);
|
|
/* 10 ms delay */
|
|
_delay_ms(10);
|
|
|
|
/* 1: Column addr set, 4 args, no delay */
|
|
lcd_write_command(ST7735_CASET);
|
|
/* XSTART = 0 */
|
|
lcd_write_byte(0x00);
|
|
lcd_write_byte(0x00);
|
|
/* XEND = 127 */
|
|
lcd_write_byte(0x00);
|
|
lcd_write_byte(ST7735_TFTWIDTH /* 0x7F */);
|
|
|
|
/* 2: Row addr set, 4 args, no delay: */
|
|
lcd_write_command(ST7735_RASET);
|
|
/* XSTART = 0 */
|
|
lcd_write_byte(0x00);
|
|
lcd_write_byte(0x00);
|
|
/* XEND = 127 */
|
|
lcd_write_byte(0x00);
|
|
lcd_write_byte(ST7735_TFTHEIGHT /* 0x7F */);
|
|
|
|
/* 1: Magical unicorn dust, 16 args, no delay: */
|
|
lcd_write_command(ST7735_GMCTRP1);
|
|
lcd_write_byte(0x02);
|
|
lcd_write_byte(0x1c);
|
|
lcd_write_byte(0x07);
|
|
lcd_write_byte(0x12);
|
|
lcd_write_byte(0x37);
|
|
lcd_write_byte(0x32);
|
|
lcd_write_byte(0x29);
|
|
lcd_write_byte(0x2d);
|
|
lcd_write_byte(0x29);
|
|
lcd_write_byte(0x25);
|
|
lcd_write_byte(0x2B);
|
|
lcd_write_byte(0x39);
|
|
lcd_write_byte(0x00);
|
|
lcd_write_byte(0x01);
|
|
lcd_write_byte(0x03);
|
|
lcd_write_byte(0x10);
|
|
|
|
/* 2: Sparkles and rainbows, 16 args, no delay: */
|
|
lcd_write_command(ST7735_GMCTRN1);
|
|
lcd_write_byte(0x03);
|
|
lcd_write_byte(0x1d);
|
|
lcd_write_byte(0x07);
|
|
lcd_write_byte(0x06);
|
|
lcd_write_byte(0x2E);
|
|
lcd_write_byte(0x2C);
|
|
lcd_write_byte(0x29);
|
|
lcd_write_byte(0x2D);
|
|
lcd_write_byte(0x2E);
|
|
lcd_write_byte(0x2E);
|
|
lcd_write_byte(0x37);
|
|
lcd_write_byte(0x3F);
|
|
lcd_write_byte(0x00);
|
|
lcd_write_byte(0x00);
|
|
lcd_write_byte(0x02);
|
|
lcd_write_byte(0x10);
|
|
|
|
/* 3: Normal display on, no args, w/delay */
|
|
lcd_write_command(ST7735_NORON);
|
|
_delay_ms(10);
|
|
|
|
/* 4: Main screen turn on, no args w/delay */
|
|
lcd_write_command(ST7735_DISPON);
|
|
_delay_ms(100);
|
|
|
|
}
|
|
|
|
|
|
void lcd_addr_window(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1) {
|
|
|
|
lcd_write_command(ST7735_CASET);
|
|
lcd_write_word(x0 + LCD_DELTA_X);
|
|
lcd_write_word(x1 + LCD_DELTA_X);
|
|
|
|
lcd_write_command(ST7735_RASET);
|
|
lcd_write_word(y0 + LCD_DELTA_Y);
|
|
lcd_write_word(y1 + LCD_DELTA_Y);
|
|
|
|
lcd_write_command(ST7735_RAMWR);
|
|
}
|
|
|
|
void lcd_draw_pixel(uint8_t x, uint8_t y, uint16_t color) {
|
|
//if (x > lcd_screen.height || y > lcd_screen.width)
|
|
// return;
|
|
lcd_addr_window(x, y, x, y);
|
|
lcd_write_word(color);
|
|
}
|
|
|
|
#define swap(a, b) { int16_t t = a; a = b; b = t; }
|
|
|
|
void lcd_draw_line(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color) {
|
|
int16_t steep = abs(y1 - y0) > abs(x1 - x0);
|
|
if (steep) {
|
|
swap(x0, y0);
|
|
swap(x1, y1);
|
|
}
|
|
if (x0 > x1) {
|
|
swap(x0, x1);
|
|
swap(y0, y1);
|
|
}
|
|
int16_t dx, dy;
|
|
dx = x1 - x0;
|
|
dy = abs(y1 - y0);
|
|
int16_t err = dx / 2;
|
|
int16_t ystep;
|
|
if (y0 < y1) {
|
|
ystep = 1;
|
|
} else {
|
|
ystep = -1;
|
|
}
|
|
while (x0 <= x1) {
|
|
if (steep) {
|
|
lcd_draw_pixel(y0, x0, color);
|
|
} else {
|
|
lcd_draw_pixel(x0, y0, color);
|
|
}
|
|
err -= dy;
|
|
if (err < 0) {
|
|
y0 += ystep;
|
|
err += dx;
|
|
}
|
|
x0++;
|
|
}
|
|
}
|
|
|
|
void lcd_draw_vline(int16_t x, int16_t y, int16_t l, uint16_t color) {
|
|
lcd_write_rect(x, y, (l - 1), 0, color);
|
|
}
|
|
|
|
void lcd_draw_hline(int16_t x, int16_t y, int16_t l, uint16_t color) {
|
|
lcd_write_rect(x, y, 0, (l - 1), color);
|
|
}
|
|
|
|
void lcd_draw_rest(uint16_t x1, uint16_t y1, uint16_t w, uint16_t h, uint16_t color) {
|
|
lcd_draw_hline(x1, y1, w, color);
|
|
lcd_draw_vline(x1, y1, h, color);
|
|
lcd_draw_hline((x1 + h) - 0, y1, w, color);
|
|
lcd_draw_vline(x1, (y1 + w) - 0, h, color);
|
|
|
|
}
|
|
|
|
uint16_t lcd_rgb2color(uint8_t r, uint8_t g, uint8_t b) {
|
|
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
|
|
}
|
|
|
|
void lcd_orient0(void) {
|
|
lcd_write_command(ST7735_MADCTL);
|
|
lcd_write_byte(MADCTL_RGB);
|
|
}
|
|
|
|
void lcd_draw_char(uint16_t xbase, uint16_t ybase, font_t *font, uint8_t c) {
|
|
if (c < font->start || c > (font->start + font->length))
|
|
c = ' ';
|
|
c = c - font->start;
|
|
#if 0
|
|
//ybase += font->width;
|
|
//xbase += font->height;
|
|
for (uint8_t h = 0; h < font->height; h++) {
|
|
for (uint8_t w = 0; w < font->width; w++) {
|
|
|
|
if (pgm_read_byte(&(font->bitmap[(c) * font->height + h])) & (1 << w))
|
|
lcd_draw_pixel((xbase - h), (ybase - w), 0xffff);
|
|
else
|
|
lcd_draw_pixel((xbase - h), (ybase - w), 0x0000);
|
|
}
|
|
}
|
|
#else
|
|
lcd_addr_window(xbase, ybase, xbase + font->height - 1, ybase + font->width - 1);
|
|
for (uint8_t w = font->width; w > 0; w--) {
|
|
for (uint8_t h = font->height; h > 0; h--) {
|
|
if (pgm_read_byte(&(font->bitmap[(c) * font->height + (h - 1)])) & (1 << (w - 1)))
|
|
lcd_write_word(0xffff);
|
|
else
|
|
lcd_write_word(0x0000);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void lcd_clear(void) {
|
|
_delay_ms(100);
|
|
lcd_write_rect(0, 0, ST7735_TFTWIDTH, ST7735_TFTHEIGHT, 0x0000);
|
|
_delay_ms(100);
|
|
}
|