aboutsummaryrefslogtreecommitdiff
path: root/src/ESP32-HUB75-MatrixPanel-leddrivers.cpp
blob: b4d768b12ba27185cfae7a29ddf4834903bcdfcf (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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*
  Various LED Driver chips might need some specific code for initialisation/control logic 

*/

#include <driver/gpio.h>

#ifdef ARDUINO_ARCH_ESP32
  #include <Arduino.h>
#else
    #define LOW 0
    #define HIGH 1
#endif
#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h"

#define CLK_PULSE          gpio_set_level((gpio_num_t) _cfg.gpio.clk, HIGH); gpio_set_level((gpio_num_t) _cfg.gpio.clk, LOW);

/**
 * @brief - pre-init procedures for specific led-drivers
 * this method is called before DMA/I2S setup while GPIOs
 * aint yet assigned for DMA operation
 * 
 */
void MatrixPanel_I2S_DMA::shiftDriver(const HUB75_I2S_CFG& _cfg){
    switch (_cfg.driver){
    case HUB75_I2S_CFG::ICN2038S:
    case HUB75_I2S_CFG::FM6124:
    case HUB75_I2S_CFG::FM6126A:
        fm6124init(_cfg);
        break;
    case HUB75_I2S_CFG::DP3246_SM5368:
        dp3246init(_cfg);
        break;
    case HUB75_I2S_CFG::MBI5124:
        /* MBI5124 chips must be clocked with positive-edge, since it's LAT signal
        * resets on clock's rising edge while high
        * https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA/files/5952216/5a542453754da.pdf
        */
        m_cfg.clkphase=true;
        break;
    case HUB75_I2S_CFG::SHIFTREG:
    default:
        break;
    }
}


void MatrixPanel_I2S_DMA::fm6124init(const HUB75_I2S_CFG& _cfg) {

    ESP_LOGI("LEDdrivers", "MatrixPanel_I2S_DMA - initializing FM6124 driver...");

    bool REG1[16] = {0,0,0,0,0, 1,1,1,1,1,1, 0,0,0,0,0};    // this sets global matrix brightness power
    bool REG2[16] = {0,0,0,0,0, 0,0,0,0,1,0, 0,0,0,0,0};    // a single bit enables the matrix output

    for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2, _cfg.gpio.clk, _cfg.gpio.lat, _cfg.gpio.oe}){
        gpio_reset_pin((gpio_num_t)_pin);                        // some pins are not in gpio mode after reset => https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html#gpio-summary
        gpio_set_direction((gpio_num_t) _pin, GPIO_MODE_OUTPUT);
        gpio_set_level((gpio_num_t) _pin, LOW);
    }

    gpio_set_level((gpio_num_t) _cfg.gpio.oe, HIGH); // Disable Display

    // Send Data to control register REG1
    // this sets the matrix brightness actually
    for (int l = 0; l < PIXELS_PER_ROW; l++){
        for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
          gpio_set_level((gpio_num_t) _pin, REG1[l%16]);   // we have 16 bits shifters and write the same value all over the matrix array

        if (l > PIXELS_PER_ROW - 12){         // pull the latch 11 clocks before the end of matrix so that REG1 starts counting to save the value
            gpio_set_level((gpio_num_t) _cfg.gpio.lat, HIGH);
        }
        CLK_PULSE
    }

    // drop the latch and save data to the REG1 all over the FM6124 chips
    gpio_set_level((gpio_num_t) _cfg.gpio.lat, LOW);

    // Send Data to control register REG2 (enable LED output)
    for (int l = 0; l < PIXELS_PER_ROW; l++){
        for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
          gpio_set_level((gpio_num_t) _pin, REG2[l%16]);   // we have 16 bits shifters and we write the same value all over the matrix array

        if (l > PIXELS_PER_ROW - 13){       // pull the latch 12 clocks before the end of matrix so that reg2 stars counting to save the value
            gpio_set_level((gpio_num_t) _cfg.gpio.lat, HIGH);
        }
        CLK_PULSE
    }

    // drop the latch and save data to the REG1 all over the FM6126 chips
    gpio_set_level((gpio_num_t) _cfg.gpio.lat, LOW);

    // blank data regs to keep matrix clear after manipulations
    for (uint8_t _pin:{_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
       gpio_set_level((gpio_num_t) _pin, LOW);

    for (int l = 0; l < PIXELS_PER_ROW; ++l){
        CLK_PULSE
    }

    gpio_set_level((gpio_num_t) _cfg.gpio.lat, HIGH);
    CLK_PULSE
    gpio_set_level((gpio_num_t) _cfg.gpio.lat, LOW);
    gpio_set_level((gpio_num_t) _cfg.gpio.oe, LOW); // Enable Display
    CLK_PULSE
}

void MatrixPanel_I2S_DMA::dp3246init(const HUB75_I2S_CFG& _cfg) {

    ESP_LOGI("LEDdrivers", "MatrixPanel_I2S_DMA - initializing DP3246 driver...");

    // DP3246 needs positive clock edge
    m_cfg.clkphase = true;

    // 15:13   3   000        reserved
    // 12:9    4   0000       OE widening (= OE_ADD * 6ns)
    // 8       1   0          reserved
    // 7:0     8   11111111   Iout = (Igain+1)/256 * 17.6 / Rext
    bool REG1[16] = { 0,0,0, 0,0,0,0, 0, 1,1,1,1,1,1,1,1 };  // MSB first

    // 15:11   5   11111      Blanking potential selection, step 77mV, 00000: VDD-0.8V
    // 10:8    3   111        Constant current source output inflection point selection
    // 7       1   0          Disable dead pixel removel, 1: Enable
    // 6       1   0          0->1: (OPEN_DET rising edge) start detection, 0: reset to ready-to-detect state
    // 5       1   0          0: Enable black screen power saving, 1: Turn off the black screen to save energy
    // 4       1   0          0: Do not enable the fading function, 1: Enable the fade function
    // 3       1   0          Reserved
    // 2:0     3   000        000: single edge pass, others: double edge transfer
    bool REG2[16] = { 1,1,1,1,1, 1,1,1, 0, 0, 0, 0, 0, 0,0,0 };  // MSB first

    for (uint8_t _pin : {_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2, _cfg.gpio.clk, _cfg.gpio.lat, _cfg.gpio.oe}) {
        gpio_reset_pin((gpio_num_t)_pin);                        // some pins are not in gpio mode after reset => https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html#gpio-summary
        gpio_set_direction((gpio_num_t)_pin, GPIO_MODE_OUTPUT);
        gpio_set_level((gpio_num_t)_pin, LOW);
    }

    gpio_set_level((gpio_num_t)_cfg.gpio.oe, HIGH); // disable Display

    // clear registers - this seems to help with reliability
    for (int l = 0; l < PIXELS_PER_ROW; ++l) {
        if (l == PIXELS_PER_ROW - 3) {       // DP3246 wants the latch dropped for 3 clk cycles
            gpio_set_level((gpio_num_t)_cfg.gpio.lat, HIGH);
        }
        CLK_PULSE
    }

    gpio_set_level((gpio_num_t)_cfg.gpio.lat, LOW);

    // Send Data to control register REG1
    for (int l = 0; l < PIXELS_PER_ROW; l++) {
        for (uint8_t _pin : {_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
            gpio_set_level((gpio_num_t)_pin, REG1[l % 16]);   // we have 16 bits shifters and write the same value all over the matrix array

        if (l == PIXELS_PER_ROW - 11) {         // pull the latch 11 clocks before the end of matrix so that REG1 starts counting to save the value
            gpio_set_level((gpio_num_t)_cfg.gpio.lat, HIGH);
        }
        CLK_PULSE
    }

    // drop the latch and save data to the REG1 all over the DP3246 chips
    gpio_set_level((gpio_num_t)_cfg.gpio.lat, LOW);

    // Send Data to control register REG2
    for (int l = 0; l < PIXELS_PER_ROW; l++) {
        for (uint8_t _pin : {_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
            gpio_set_level((gpio_num_t)_pin, REG2[l % 16]);   // we have 16 bits shifters and we write the same value all over the matrix array

        if (l == PIXELS_PER_ROW - 12) {       // pull the latch 12 clocks before the end of matrix so that REG2 starts counting to save the value
            gpio_set_level((gpio_num_t)_cfg.gpio.lat, HIGH);
        }
        CLK_PULSE
    }

    // drop the latch and save data to the REG2 all over the DP3246 chips
    gpio_set_level((gpio_num_t)_cfg.gpio.lat, LOW);
    CLK_PULSE

    // blank data regs to keep matrix clear after manipulations
    for (uint8_t _pin : {_cfg.gpio.r1, _cfg.gpio.r2, _cfg.gpio.g1, _cfg.gpio.g2, _cfg.gpio.b1, _cfg.gpio.b2})
        gpio_set_level((gpio_num_t)_pin, LOW);

    for (int l = 0; l < PIXELS_PER_ROW; ++l) {
        if (l == PIXELS_PER_ROW - 3) {       // DP3246 wants the latch dropped for 3 clk cycles
            gpio_set_level((gpio_num_t)_cfg.gpio.lat, HIGH);
        }
        CLK_PULSE
    }

    gpio_set_level((gpio_num_t)_cfg.gpio.lat, LOW);
    gpio_set_level((gpio_num_t)_cfg.gpio.oe, LOW); // enable Display
    CLK_PULSE
}