aboutsummaryrefslogtreecommitdiff
path: root/main/main.c
blob: f2822a8e25e42d3faf030ca79803a1806e210b7f (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <stdio.h>
#include <string.h>

#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "atascii.h"
#include "esp_log.h"


#define NUM_COL              160
#define NUM_ROW              8
#define CHAR_HEIGHT          8
#define CHAR_WIDTH           8
#define RED                  1
#define GREEN                2
#define ORANGE               3

#define PIN_NUM_MISO         12
#define PIN_NUM_MOSI         13
#define PIN_NUM_CLK          14
#define PIN_NUM_CS           15
#define PIN_NUM_R_LATCH      16
#define PIN_NUM_R_CLK        17
#define PIN_NUM_R_ADDR_0     5
#define PIN_NUM_R_ADDR_1     18
#define PIN_NUM_R_ADDR_2     19

#define TAG                  "led_display"
#define STACK_SIZE           2000

static uint16_t pattern[NUM_ROW][NUM_COL / 8]; // each column is two bits, so 8 can fit in 16-bit integer


void draw_char_at_position(char character, uint16_t col_start, uint16_t row_start, uint16_t color) {
    uint8_t cur_char_row;
    for(int row = row_start; (row - row_start) < CHAR_HEIGHT && row < NUM_ROW; row++) {
        cur_char_row = atascii_font[(uint8_t) character][row - row_start];
        for(int col = col_start; (col - col_start) < CHAR_WIDTH && col < NUM_COL; col++) {
            pattern[row][col / 8] = (pattern[row][col / 8] & ~(0b0000000000000011 << (2 * (col - col_start)))); // clear this column (note the bitwise inversion)
            if((1 << (col - col_start)) & cur_char_row) {
                pattern[row][col / 8] = ((pattern[row][col / 8]) | color << (2 * (col - col_start))); // write color to column
            }
        }
    }
}


void updateDisplay(void* params)
{
    esp_err_t ret;
    spi_device_handle_t spi;

    spi_bus_config_t buscfg={
        .miso_io_num=PIN_NUM_MISO,
        .mosi_io_num=PIN_NUM_MOSI,
        .sclk_io_num=PIN_NUM_CLK,
        .quadwp_io_num=-1,
        .quadhd_io_num=-1,
        .max_transfer_sz=3*600*2*8,
        .flags=SPICOMMON_BUSFLAG_DUAL
    };

    spi_device_interface_config_t devcfg={
        .clock_speed_hz=1*1000*1000,            //Clock out at 1 MHz
        .mode=0,                                //SPI mode 0
        .spics_io_num=PIN_NUM_CS,               //CS pin
        .queue_size=1,                          //We want to be able to queue 7 transactions at a time
        .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);

    ESP_LOGI(TAG, "Initialized SPI host.");

    gpio_set_direction(PIN_NUM_R_LATCH, GPIO_MODE_OUTPUT);
    gpio_set_direction(PIN_NUM_R_CLK, GPIO_MODE_OUTPUT);
    gpio_set_direction(PIN_NUM_R_ADDR_0, GPIO_MODE_OUTPUT);
    gpio_set_direction(PIN_NUM_R_ADDR_1, GPIO_MODE_OUTPUT);
    gpio_set_direction(PIN_NUM_R_ADDR_2, GPIO_MODE_OUTPUT);

    ESP_LOGI(TAG, "Setup GPIO.");

    TickType_t xLastWakeTime;
    const TickType_t xFrequency = 1 / portTICK_PERIOD_MS;
    xLastWakeTime = xTaskGetTickCount();

    uint8_t current_row = 0;
    uint16_t refresh_count = 0;
    TickType_t startMillis = xTaskGetTickCount();
    TickType_t endMillis;
    uint8_t i = 0;
    uint16_t data;
    while(true) {
        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(pattern[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(PIN_NUM_R_LATCH, 0);
        gpio_set_level(PIN_NUM_R_CLK, 1);

        gpio_set_level(PIN_NUM_R_ADDR_0, 1 & current_row);
        gpio_set_level(PIN_NUM_R_ADDR_1, 1 & (current_row >> 1));
        gpio_set_level(PIN_NUM_R_ADDR_2, 1 & (current_row >> 2));

        gpio_set_level(PIN_NUM_R_LATCH, 1);
        gpio_set_level(PIN_NUM_R_CLK, 0);

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

            if(++refresh_count == 1000) {
                endMillis = xTaskGetTickCount();
                ESP_LOGI(TAG, "Refresh Rate: %f Hz", (float) refresh_count * 1000 / (endMillis - startMillis));
                refresh_count = 0;
                startMillis = xTaskGetTickCount();
            }
        }
    }
}


void app_main(void)
{
    TaskHandle_t taskHandle;
    xTaskCreate(updateDisplay, "updateDisplay", STACK_SIZE, NULL, tskIDLE_PRIORITY, &taskHandle);

    int j = 0;
    while(true) {
        ESP_LOGI(TAG, "Writing words...");

        char* hello = "Hello World!!";
        for(int i = 0; i < strlen(hello); i++) {
            draw_char_at_position(hello[i], i * CHAR_WIDTH, 0, j % 2 + 1);
        }

        char* numbers = "0123456789";
        int offset = strlen(hello) + 1;
        for(int i = 0; i < strlen(numbers); i++) {
            draw_char_at_position(numbers[i], (i + offset) * CHAR_WIDTH, 0, j % 2 + 2);
        }

        j++;
        vTaskDelay(5000 / portTICK_PERIOD_MS);
    }
}