aboutsummaryrefslogtreecommitdiff
path: root/main/InovaLedDisplay.cpp
blob: 0d8d7e8974598158b7b52e2717bb31a2b7103c7c (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
#include "InovaLedDisplay.h"

#include <cstring>

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


InovaLedDisplay::InovaLedDisplay(uint8_t r0, uint8_t g0, uint8_t clk, uint8_t cs, 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.sclk_io_num=clk;
    buscfg.quadwp_io_num=-1;
    buscfg.quadhd_io_num=-1;
    buscfg.max_transfer_sz=3*600*2*8;
    buscfg.flags=SPICOMMON_BUSFLAG_DUAL;

    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=cs;                       //CS pin
    devcfg.queue_size=1;                          //We want to be able to queue 7 transactions 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;
    r = color >> 11; // RRRRRggggggbbbbb
    g = (color >> 5) & 0x3F; // rrrrrGGGGGGbbbbb

    color = 0;
    if(g > 0) color += 1;
    if(r > 0) color += 2;

    uint8_t i = backBufferIndex;
    displayBuffer[i][y][x / 8] = displayBuffer[i][y][x / 8] & ~(0b0000000000000011 << (14 - (2 * (x % 8)))); // clear this column (note the bitwise inversion)
    displayBuffer[i][y][x / 8] = displayBuffer[i][y][x / 8] | (color << (14 - (2 * (x % 8)))); // write color to column
}


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();

    uint8_t current_row = 0;
    uint8_t i = 0;
    uint16_t data;
    esp_err_t ret;
    run = true;
    while(run) {
        vTaskDelayUntil(&xLastWakeTime, xFrequency);

        for(i = 0; i < (NUM_COL / 8); i++) {
            spi_transaction_t t;
            memset(&t, 0, sizeof(t));
            t.length = 16;
            data = SPI_SWAP_DATA_TX(displayBuffer[1 - backBufferIndex][current_row][i], 16);
            t.tx_buffer = &data;
            t.flags = SPI_TRANS_MODE_DIO;  // output on two data lines. 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 == NUM_ROW) {
            current_row = 0;

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


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