aboutsummaryrefslogtreecommitdiff
path: root/main/InovaLedDisplay.cpp
blob: 3da8f220ca553c3afc87cf485cde415434b42294 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#include "InovaLedDisplay.h"

#include <cstring>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"


InovaLedDisplay::InovaLedDisplay(uint8_t r0, uint8_t g0, uint8_t r1, uint8_t g1, uint8_t r2, uint8_t g2, uint8_t clk, uint8_t latchParam, uint8_t oeParam, uint8_t addr0Param, uint8_t addr1Param, uint8_t addr2Param) : Adafruit_GFX(NUM_COL, NUM_ROW){
    latch = (gpio_num_t) latchParam;
    oe = (gpio_num_t) oeParam;
    addr0 = (gpio_num_t) addr0Param;
    addr1 = (gpio_num_t) addr1Param;
    addr2 = (gpio_num_t) addr2Param;

    esp_err_t ret;

    spi_bus_config_t buscfg;
    memset(&buscfg, 0, sizeof(spi_bus_config_t));
    buscfg.miso_io_num=g0;
    buscfg.mosi_io_num=r0;
    buscfg.quadwp_io_num=r1;
    buscfg.quadhd_io_num=g1;
    buscfg.data4_io_num=r2;
    buscfg.data5_io_num=g2;
    buscfg.data6_io_num=-1;
    buscfg.data7_io_num=-1;
    buscfg.sclk_io_num=clk;
    buscfg.max_transfer_sz=3*600*2*8;
    buscfg.flags=SPICOMMON_BUSFLAG_OCTAL;

    spi_device_interface_config_t devcfg;
    memset(&devcfg, 0, sizeof(spi_device_interface_config_t));
    devcfg.clock_speed_hz=1*1000*1000;            //Clock out at 1 MHz
    devcfg.mode=0;                                //SPI mode 0
    devcfg.spics_io_num=-1;                       //CS pin
    devcfg.queue_size=1;                          //Only one transaction at a time
    devcfg.flags=SPI_DEVICE_HALFDUPLEX;           //Needed to use both data lines for output

    //Initialize the SPI bus and attach our device handle
    ret=spi_bus_initialize(HSPI_HOST, &buscfg, SPI_DMA_CH_AUTO);
    ESP_ERROR_CHECK(ret);
    ret=spi_bus_add_device(HSPI_HOST, &devcfg, &spi);
    ESP_ERROR_CHECK(ret);

    gpio_set_direction(latch, GPIO_MODE_OUTPUT);
    gpio_set_direction(oe, GPIO_MODE_OUTPUT);
    gpio_set_direction(addr0, GPIO_MODE_OUTPUT);
    gpio_set_direction(addr1, GPIO_MODE_OUTPUT);
    gpio_set_direction(addr2, GPIO_MODE_OUTPUT);
}


void InovaLedDisplay::drawPixel(int16_t x, int16_t y, uint16_t color) {

    if(x < 0 || x >= _width || y < 0 || y >= _height) return;

    // Adafruit GFX color is 16-bit 5-6-5 rgb
    // We store only 2-bit rg, so we assume any amount of red or green wants that color
    uint8_t r, g, c;
    r = color >> 11; // RRRRRggggggbbbbb
    g = (color >> 5) & 0x3F; // rrrrrGGGGGGbbbbb

    c = 0;
    if(g > 0) c += 1;
    if(r > 0) c += 2;
    c = c << 6;

    // We store the 3 segments of the display 8-bit ints ready to transfer via 8-bit parallel SPI
    // So 2 bits are wasted per byte, but hopefully it's worth it for transfer speed

    //        b0      b1      b2      b3      b4      b5      b6     b7            b0      b1      b2      b3      b4      b5      b6     b7
    // [0][0] r(0,0)  g(0,0)  r(0,8)  g(0,8)  r(0,16) g(0,16) ---    ---    [0][1] r(1,0)  g(1,0)  r(1,8)  g(1,8)  r(1,16) g(1,16) ---    ---    [...]
    // [1][0] r(0,1)  g(0,1)  r(0,9)  g(0,9)  r(0,17) g(0,17) ---    ---    [1][1] r(1,1)  g(1,1)  r(1,9)  g(1,9)  r(1,17) g(1,17) ---    ---    [...]
    // [...]

    uint8_t bitwiseOffset = (y / 8) * 2;
    uint8_t i = backBufferIndex;
    displayBuffer[i][y % 8][x] = displayBuffer[i][y % 8][x] & ~(0b11000000 >> bitwiseOffset); // clear this pixel (note the bitwise inversion)
    displayBuffer[i][y % 8][x] = displayBuffer[i][y % 8][x] | (c >> bitwiseOffset); // write color to pixel
}


void InovaLedDisplay::swapBuffer(void) {
    shouldSwapBuffer = true;

    while(shouldSwapBuffer) {
        vTaskDelay(1 / portTICK_PERIOD_MS);
    }
}


void InovaLedDisplay::runDisplay(void) {
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = 1 / portTICK_PERIOD_MS;
    xLastWakeTime = xTaskGetTickCount();

    spi_transaction_t t;
    uint8_t current_row = 0;
    esp_err_t ret;
    run = true;
    while(run) {
        vTaskDelayUntil(&xLastWakeTime, xFrequency);

        memset(&t, 0, sizeof(t));
        t.length = _width;
        t.tx_buffer = &displayBuffer[1 - backBufferIndex][current_row];
        t.flags = SPI_TRANS_MODE_OCT;  // output on eight data lines (six utilized). Even bits go to GREEN, odd to RED
        ret = spi_device_polling_transmit(spi, &t);
        ESP_ERROR_CHECK(ret);

        gpio_set_level(latch, 0);
        gpio_set_level(oe, 1);

        gpio_set_level(addr0, 1 & current_row);
        gpio_set_level(addr1, 1 & (current_row >> 1));
        gpio_set_level(addr2, 1 & (current_row >> 2));

        gpio_set_level(latch, 1);
        gpio_set_level(oe, 0);

        if(++current_row == ROWS_PER_SEGMENT) {
            current_row = 0;

            if(shouldSwapBuffer) {
                backBufferIndex++;
                backBufferIndex = backBufferIndex % 2;
                shouldSwapBuffer = false;
            }
        }
    }
}


void InovaLedDisplay::stopDisplay(void) {
    run = false;
}