Newer
Older
avrtft / tft.c
@Torsten Römer Torsten Römer on 16 Feb 2024 6 KB Cosmetics
/* 
 * File:   tft.c
 * Author: torsten.roemer@luniks.net
 *
 * Created on 6. November 2023, 18:45
 */

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <util/delay.h>
#include "tft.h"
#include "pins.h"
#include "usart.h"
#include "spi.h"

#include "bitmaps.h"

/**
 * Converts the given 8 pixel in 1-Bit monochrome to 16-Bit RGB (5/6/5) color
 * stored in the given array of 16 bytes.
 * 
 * @param grey 8 pixel in 1-Bit monochrome
 * @param rgb 8 pixel in 16-Bit RGB (5/6/5) color
 */
static void mono1ToRGB16(uint8_t mono, uint8_t *rgb) {
    for (uint8_t i = 0; i < 16; i++) {
        rgb[i] = (mono & (1 << ((15 - i) >> 1))) ? 0x0 : 0xff;
    }
}

/*
 * Converts the given two pixel in 4-Bit greyscale to 16-Bit RGB (5/6/5) color
 * stored in the given array of four bytes.
 * 
 * @param grey two pixel in 4-Bit greyscale
 * @param rgb two pixel in 16-Bit RGB (5/6/5) color
 */
static void grey4ToRGB16(uint8_t grey, uint8_t *rgb) {
    uint8_t grey4 = ((grey >> 4) & 1);
    uint8_t grey0 = ((grey >> 0) & 1);
    
    rgb[0] = (grey & 0xf0);
    rgb[0] |= (grey4 << 3);
    rgb[0] |= (grey >> 5);

    rgb[1] = ((grey & 0xf0) << 3);
    rgb[1] |= ((grey & 0xf0) >> 3);
    rgb[1] |= (grey4 << 6) | (grey4 << 5) | (grey4 << 0);

    rgb[2] = (grey << 4);
    rgb[2] |= (grey0 << 3);
    rgb[2] |= ((grey & 0x0f) >> 1);

    rgb[3] = (grey << 7);
    rgb[3] |= ((grey & 0x0f) << 1);
    rgb[3] |= (grey0 << 6) | (grey0 << 5) | (grey0 << 0);    
}

/**
 * Does a hardware reset.
 */
static void hwReset(void) {
    PORT_DISP &= ~(1 << PIN_RST);
    _delay_ms(10);
    PORT_DISP |= (1 << PIN_RST);
}

/**
 * Sets display to send a command.
 */
static void displaySetCmd(void) {
    PORT_DSPI &= ~(1 << PIN_DC);
}

/**
 * Sets display to send data.
 */
static void displaySetData(void) {
    PORT_DSPI |= (1 << PIN_DC);
}

/**
 * Sends the given command to the display.
 * 
 * @param cmd
 */
static void displayCmd(uint8_t cmd) {
    displaySetCmd();
    transmit(cmd);
}

/**
 * Sends the given data to the display.
 * 
 * @param data
 */
static void displayData(uint8_t data) {
    displaySetData();
    transmit(data);
}

/**
 * Sets horizontal and/or vertical flip.
 * 
 * @param hflip
 * @param vflip
 */
static void madctl(bool hflip, bool vflip) {
    // Memory data access control
    uint8_t madctl = 0b00110110;
    madctl |= (VFLIP << 7);
    madctl |= (HFLIP << 6);
    madctl |= (BGR << 3);

    if (vflip) {
        // Row Address Order (MY)
        madctl ^= (1 << 7);
    }
    if (hflip) {
        // Column Address Order (MX)
        madctl ^= (1 << 6);
    }

    displaySel();
    displayCmd(MADCTL);
    displayData(madctl);
    displayDes();
}

/**
 * Sets the given column start and end address.
 * 
 * @param xs start address
 * @param xe end address
 */
static void caset(uint16_t xs, uint16_t xe) {
    displaySel();
    displayCmd(CASET);
    displayData(xs >> 8);
    displayData(xs);
    displayData(xe >> 8);
    displayData(xe);
    displayDes();
}

/**
 * Sets the given row start and end address.
 * 
 * @param ys start address
 * @param ye end address
 */
static void raset(uint16_t ys, uint16_t ye) {
    displaySel();
    displayCmd(RASET);
    displayData(ys >> 8);
    displayData(ys);
    displayData(ye >> 8);
    displayData(ye);
    displayDes();
}

void initDisplay(void) {
    // Hardware reset
    hwReset();

    // TODO necessary?
    _delay_ms(10);

    // Software reset
    displaySel();
    displayCmd(SWRESET);
    displayDes();

    // TODO necessary?
    _delay_ms(10);

    // Sleep out & booster on
    displaySel();
    displayCmd(SLPOUT);
    displayDes();

    // Partial off (Normal)
    displaySel();
    displayCmd(NORON);
    displayDes();

    // Display Inversion on/off
    displaySel();
    displayCmd(INVOFF + INVERT); // INVOFF + 1 = INVON
    displayDes();

    // Interface pixel format
    displaySel();
    displayCmd(COLMOD);
    displayData(0b00111101);
    displayDes();
    
    // Display on
    displaySel();
    displayCmd(DISPON);
    displayDes();
    
    // Sleep in & booster off
    // displaySel();
    // displayCmd(SLPIN);
    // displayDes();
    
    printString("done initializing display\r\n");
}

void writeStart(void) {
    // Memory write
    displaySel();
    displayCmd(RAMWR);
    displaySetData();
}

void writeByte(uint8_t byte) {
    // Memory write
    transmit(byte);
}

void writeEnd(void) {
    // Memory write
    displayDes();
}

void fillArea(row_t row, col_t col,
              width_t width, height_t height,
              uint16_t color) {

    madctl(false, false);

    // X address start/end
    uint16_t xs = col;
    uint16_t xe = col + width - 1;
    caset(xs, xe);

    // Y address start/end
    uint16_t ys = row;
    uint16_t ye = row + height - 1;
    raset(ys, ye);

    writeStart();

    bytes_t pixels = (bytes_t)width * (bytes_t)height;
    for (bytes_t i = 0; i < pixels; i++) {
        transmit(color >> 8);
        transmit(color);
    }

    writeEnd();
}

void setArea(row_t row, col_t col, 
             width_t width, height_t height, 
             bool hflip, bool vflip) {

    madctl(hflip, vflip);

    // X address start/end
    uint16_t xs = col;
    uint16_t xe = col + width - 1;
    if (vflip) {
        xs = DISPLAY_WIDTH - col - width;
        xe = DISPLAY_WIDTH - col - 1;
    }
    caset(xs, xe);

    // Y address start/end
    uint16_t ys = row;
    uint16_t ye = row + height - 1;
    if (hflip) {
        ys = DISPLAY_HEIGHT - row - height;
        ye = DISPLAY_HEIGHT - row - 1;
    }
    raset(ys, ye);
}

void writeData(const __flash uint8_t *bitmap,
               width_t width, height_t height,
               space_t space) {
    writeStart();
    
    switch (space) {
        case SPACE_MONO1: {
            bytes_t bytes = width * height / 8;
            for (uint16_t i = 0; i < bytes; i++) {
                uint8_t rgb[16];
                mono1ToRGB16(bitmap[i], rgb);
                for (uint8_t j = 0; j < 16; j++) {
                    transmit(rgb[j]);
                }
            }            
        }; break;
        case SPACE_GREY4: {
            bytes_t bytes = width * height / 2;
            for (uint16_t i = 0; i < bytes; i++) {
                uint8_t rgb[4];
                grey4ToRGB16(bitmap[i], rgb);
                for (uint8_t j = 0; j < 4; j++) {
                    transmit(rgb[j]);
                }
            }
        }; break;
        default: {
            // SPACE_RGB16
            bytes_t bytes = width * height * 2;
            for (uint16_t i = 0; i < bytes; i++) {
                transmit(bitmap[i]);
            }
        }
    }

    writeEnd();
}