qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

max7219.c (9629B)


      1 /*
      2  * Copyright (c) 2021 Zach White <skullydazed@gmail.com>
      3  * Copyright (c) 2007 Eberhard Fahle
      4  *
      5  *    max7219.c - A library for controling Leds with a MAX7219/MAX7221
      6  *
      7  *    Permission is hereby granted, free of charge, to any person
      8  *    obtaining a copy of this software and associated documentation
      9  *    files (the "Software"), to deal in the Software without
     10  *    restriction, including without limitation the rights to use,
     11  *    copy, modify, merge, publish, distribute, sublicense, and/or sell
     12  *    copies of the Software, and to permit persons to whom the
     13  *    Software is furnished to do so, subject to the following
     14  *    conditions:
     15  *
     16  *    This permission notice shall be included in all copies or
     17  *    substantial portions of the Software.
     18  *
     19  *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     20  *    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     21  *    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     22  *    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     23  *    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     24  *    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     25  *    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     26  *    OTHER DEALINGS IN THE SOFTWARE.
     27  */
     28 
     29 /*
     30  * This driver started as a port of Arduino's LedControl to QMK. The
     31  * original Arduino code can be found here:
     32  *
     33  * https://github.com/wayoda/LedControl
     34  *
     35  * Unlike LedControl we are using the native SPI support, you will need to
     36  * use the native SPI pins for your MCU. You can set the CS pin with
     37  * `#define MAX7219_LOAD <pin>`.
     38  *
     39  * This has only been tested on AVR, specifically a Teensy 2.0++.
     40  */
     41 
     42 #include "max7219.h"
     43 #include "spi_master.h"
     44 #include "debug.h"
     45 #include "gpio.h"
     46 #include "wait.h"
     47 #include "font.h"
     48 
     49 // Datastructures
     50 bool max7219_led_scrolling = true;
     51 uint16_t max7219_buffer_end = 0;
     52 uint8_t max7219_spidata[MAX_BYTES];
     53 uint8_t max7219_led_a[8][MAX7219_BUFFER_SIZE];
     54 
     55 /* Write max7219_spidata to all the max7219's
     56  */
     57 void max7219_write_all(void) {
     58     dprintf("max7219_write_all()\n");
     59     if (spi_start(MAX7219_LOAD, false, 0, 8)) {
     60         for(int i = MAX_BYTES; i>0; i--) {
     61             dprintf("spi_write(%d)\n", max7219_spidata[i-1]);
     62             spi_write(max7219_spidata[i-1]);
     63         }
     64         spi_stop();
     65     } else {
     66         xprintf("Could not spi_start!\n");
     67     }
     68 }
     69 
     70 /* Write the current frame in max7219_led_a to all the max7219's
     71  */
     72 void max7219_write_frame(void) {
     73     dprintf("max7219_write_frame()\n");
     74 
     75     // Set our opcode and data
     76     for (int col=0; col<8; col++) {
     77         for (int device_num=0; device_num<MAX7219_CONTROLLERS; device_num++) {
     78             int offset=device_num*2;
     79             max7219_spidata[offset] = max7219_led_a[col][device_num];
     80             max7219_spidata[offset+1] = col+1;
     81         }
     82         max7219_write_all();
     83     }
     84 }
     85 
     86 /* Stores a message in the sign buffer.
     87  *
     88  * message should be a 2d array with the outer array having a length of your
     89  * message and the inner array having a length of 6. Use the CHR_<letter>
     90  * macros from font.h to populate your array.
     91  *
     92  * Example:
     93  *
     94  *      uint8_t message[10][6] = {CHR_INTERROBANG, CHR_C, CHR_l, CHR_u, CHR_e, CHR_b, CHR_o, CHR_a, CHR_r, CHR_d};
     95  *      max7219_message(message, 10);
     96  */
     97 void max7219_message_sign(uint8_t message[][6], size_t message_len) {
     98     uint8_t letter_num = 0;
     99     uint8_t letter_col = 0;
    100     max7219_buffer_end = message_len * 6 + 32;
    101 
    102     for (int device_num=0; device_num<MAX7219_BUFFER_SIZE; device_num++) {
    103         for (int col=0; col<8; col++) {
    104             if (letter_num >= message_len) {
    105                 max7219_led_a[col][device_num] = 0b00000000;
    106             } else {
    107                 max7219_led_a[col][device_num] = message[letter_num][letter_col];
    108                 if (letter_col == 5) {
    109                     letter_num++;
    110                     letter_col = 0;
    111                 } else {
    112                     letter_col++;
    113                 }
    114             }
    115         }
    116     }
    117 
    118     max7219_write_frame();
    119 }
    120 
    121 /* Scroll the content on the sign left by 1 column.
    122  *
    123  * When loop_message is true columns that slide off the left will be added
    124  * to the right to be displayed again.
    125  */
    126 void max7219_message_sign_task(bool loop_message) {
    127     uint8_t left_col = 0b00000000;
    128 
    129     if (!max7219_led_scrolling) {
    130         return;
    131     }
    132 
    133     if (loop_message) {
    134         left_col = max7219_led_a[0][0];
    135     }
    136 
    137     int i=0;
    138 
    139     for (int device_num=0; device_num<MAX7219_BUFFER_SIZE; device_num++) {
    140         for (int col=0; col<8; col++) {
    141             i++;
    142 
    143             if (i == max7219_buffer_end) {
    144                 max7219_led_a[col][device_num] = left_col;
    145                 device_num=MAX7219_BUFFER_SIZE;
    146                 break;
    147             } else if (col < 7) {
    148                 max7219_led_a[col][device_num] = max7219_led_a[col+1][device_num];
    149             } else if (device_num == MAX7219_BUFFER_SIZE-1) {
    150                 max7219_led_a[col][device_num] = left_col;
    151             } else {
    152                 max7219_led_a[col][device_num] = max7219_led_a[0][device_num+1];
    153             }
    154         }
    155     }
    156     max7219_write_frame();
    157 }
    158 
    159 /* Write data to a single max7219
    160  */
    161 void max7219_write(int device_num, volatile uint8_t opcode, volatile uint8_t data) {
    162     dprintf("max7219_write(%d, %d, %d)\n", device_num, opcode, data);
    163 
    164     // Clear the data array
    165     for(int i = MAX_BYTES; i>0; i--) {
    166         max7219_spidata[i-1]=0;
    167     }
    168 
    169     // Set our opcode and data
    170     uint8_t offset = device_num*2;
    171     max7219_spidata[offset] = data;
    172     max7219_spidata[offset+1] = opcode;
    173 
    174     // Write the data
    175     max7219_write_all();
    176 }
    177 
    178 /* Turn off all the LEDs
    179  */
    180 void max7219_clear_display(void) {
    181     dprintf("max7219_clear_display();\n");
    182 
    183     for (int col=0; col<8; col++) {
    184         for (int device_num=0; device_num<MAX7219_BUFFER_SIZE; device_num++) {
    185             max7219_led_a[col][device_num] = 0b00000000;
    186         }
    187     }
    188     max7219_write_frame();
    189 }
    190 
    191 /* Enable the display test (IE turn on all 64 LEDs)
    192  */
    193 void max7219_display_test(int device_num, bool enabled) {
    194     dprintf("max7219_display_test(%d, %d);\n", device_num, enabled);
    195 
    196     if (device_num<0 || device_num >= MAX7219_CONTROLLERS) {
    197         return;
    198     }
    199 
    200     max7219_write(device_num, OP_DISPLAYTEST, enabled);
    201 }
    202 
    203 /* Initialize the max7219 system and set the controller(s) to a default state.
    204  */
    205 void max7219_init(void) {
    206     wait_ms(1500);
    207     dprintf("max7219_init()\n");
    208 
    209     gpio_set_pin_output(MAX7219_LOAD);
    210     gpio_write_pin_high(MAX7219_LOAD);
    211     spi_init();
    212 
    213     for (int i=0; i<MAX7219_CONTROLLERS; i++) {
    214         max7219_shutdown(i, true);
    215     }
    216 
    217     for (int i=0; i<MAX7219_CONTROLLERS; i++) {
    218         // Reset everything to defaults and enable the display
    219         max7219_display_test(i, false);
    220         max7219_set_scan_limit(i, 7);
    221         max7219_set_decode_mode(i, 0);
    222         max7219_set_intensity(i, MAX7219_LED_INTENSITY);
    223     }
    224 
    225     max7219_clear_display();
    226 
    227 #ifndef MAX7219_NO_STARTUP_TEST
    228     for (int i=0; i<MAX7219_CONTROLLERS; i++) {
    229         // Test this display
    230         max7219_display_test(i, true);
    231         wait_ms(75);
    232         max7219_display_test(i, false);
    233     }
    234 #endif
    235 
    236     for (int i=0; i<MAX7219_CONTROLLERS; i++) {
    237         max7219_shutdown(i, false);
    238     }
    239 }
    240 
    241 /* Set the decode mode of the controller. You probably don't want to change this.
    242  */
    243 void max7219_set_decode_mode(int device_num, int mode) {
    244     dprintf("max7219_set_decode_mode(%d, %d);\n", device_num, mode);
    245 
    246     if (device_num<0 || device_num >= MAX7219_CONTROLLERS) {
    247         return;
    248     }
    249 
    250     max7219_write(device_num, OP_DECODEMODE, mode);
    251 }
    252 
    253 /* Set the intensity (brightness) for the LEDs.
    254  */
    255 void max7219_set_intensity(int device_num, int intensity) {
    256     dprintf("max7219_set_intensity(%d, %d);\n", device_num, intensity);
    257 
    258     if (device_num<0 || device_num >= MAX7219_CONTROLLERS) {
    259         return;
    260     }
    261 
    262     if (intensity >= 0 && intensity<16) {
    263             max7219_write(device_num, OP_INTENSITY, intensity);
    264     }
    265 }
    266 
    267 /* Control a single LED.
    268  */
    269 void max7219_set_led(int row, int column, bool state) {
    270     dprintf("max7219_set_led(%d, %d, %d);\n", row, column, state);
    271 
    272     if (column<0 || column>8*MAX7219_CONTROLLERS) {
    273         xprintf("max7219_set_led: column (%d) out of bounds\n", column);
    274         return;
    275     }
    276 
    277     if (row<0 || row>7) {
    278         xprintf("max7219_set_led: row (%d) out of bounds\n", row);
    279         return;
    280     }
    281 
    282     /* At this point we reverse the sense of row and column to match the
    283      * physical layout of my LEDs.
    284      */
    285     uint8_t device_num = column / 8;
    286     uint8_t col = column % 8;
    287     uint8_t val = 0b10000000 >> row;
    288 
    289     if (state) {
    290         max7219_led_a[col][device_num] = max7219_led_a[col][device_num]|val;
    291     } else {
    292         val = ~val;
    293         max7219_led_a[col][device_num] = max7219_led_a[col][device_num]&val;
    294     }
    295     max7219_write(device_num, col+1, max7219_led_a[col][device_num]);
    296 }
    297 
    298 /* Set the number of digits (rows) to be scanned.
    299  */
    300 void max7219_set_scan_limit(int device_num, int limit) {
    301     dprintf("max7219_set_scan_limit(%d, %d);\n", device_num, limit);
    302 
    303     if (device_num<0 || device_num >= MAX7219_CONTROLLERS) {
    304         return;
    305     }
    306 
    307     if (limit >= 0 && limit < 8) {
    308         max7219_write(device_num, OP_SCANLIMIT, limit);
    309     }
    310 }
    311 
    312 /* Enable (true) or disable (false) the controller.
    313  */
    314 void max7219_shutdown(int device_num, bool shutdown) {
    315     dprintf("max7219_shutdown(%d, %d);\n", device_num, shutdown);
    316 
    317     if (device_num<0 || device_num >= MAX7219_CONTROLLERS) {
    318         return;
    319     }
    320 
    321     max7219_write(device_num, OP_SHUTDOWN, !shutdown);
    322 }