logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

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. * This driver started as a port of Arduino's LedControl to QMK. The
  30. * original Arduino code can be found here:
  31. *
  32. * https://github.com/wayoda/LedControl
  33. *
  34. * Unlike LedControl we are using the native SPI support, you will need to
  35. * use the native SPI pins for your MCU. You can set the CS pin with
  36. * `#define MAX7219_LOAD <pin>`.
  37. *
  38. * This has only been tested on AVR, specifically a Teensy 2.0++.
  39. */
  40. #include "max7219.h"
  41. #include "spi_master.h"
  42. #include "debug.h"
  43. #include "gpio.h"
  44. #include "wait.h"
  45. #include "font.h"
  46. // Datastructures
  47. bool max7219_led_scrolling = true;
  48. uint16_t max7219_buffer_end = 0;
  49. uint8_t max7219_spidata[MAX_BYTES];
  50. uint8_t max7219_led_a[8][MAX7219_BUFFER_SIZE];
  51. /* Write max7219_spidata to all the max7219's
  52. */
  53. void max7219_write_all(void) {
  54. dprintf("max7219_write_all()\n");
  55. if (spi_start(MAX7219_LOAD, false, 0, 8)) {
  56. for(int i = MAX_BYTES; i>0; i--) {
  57. dprintf("spi_write(%d)\n", max7219_spidata[i-1]);
  58. spi_write(max7219_spidata[i-1]);
  59. }
  60. spi_stop();
  61. } else {
  62. xprintf("Could not spi_start!\n");
  63. }
  64. }
  65. /* Write the current frame in max7219_led_a to all the max7219's
  66. */
  67. void max7219_write_frame(void) {
  68. dprintf("max7219_write_frame()\n");
  69. // Set our opcode and data
  70. for (int col=0; col<8; col++) {
  71. for (int device_num=0; device_num<MAX7219_CONTROLLERS; device_num++) {
  72. int offset=device_num*2;
  73. max7219_spidata[offset] = max7219_led_a[col][device_num];
  74. max7219_spidata[offset+1] = col+1;
  75. }
  76. max7219_write_all();
  77. }
  78. }
  79. /* Stores a message in the sign buffer.
  80. *
  81. * message should be a 2d array with the outer array having a length of your
  82. * message and the inner array having a length of 6. Use the CHR_<letter>
  83. * macros from font.h to populate your array.
  84. *
  85. * Example:
  86. *
  87. * 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};
  88. * max7219_message(message, 10);
  89. */
  90. void max7219_message_sign(uint8_t message[][6], size_t message_len) {
  91. uint8_t letter_num = 0;
  92. uint8_t letter_col = 0;
  93. max7219_buffer_end = message_len * 6 + 32;
  94. for (int device_num=0; device_num<MAX7219_BUFFER_SIZE; device_num++) {
  95. for (int col=0; col<8; col++) {
  96. if (letter_num >= message_len) {
  97. max7219_led_a[col][device_num] = 0b00000000;
  98. } else {
  99. max7219_led_a[col][device_num] = message[letter_num][letter_col];
  100. if (letter_col == 5) {
  101. letter_num++;
  102. letter_col = 0;
  103. } else {
  104. letter_col++;
  105. }
  106. }
  107. }
  108. }
  109. max7219_write_frame();
  110. }
  111. /* Scroll the content on the sign left by 1 column.
  112. *
  113. * When loop_message is true columns that slide off the left will be added
  114. * to the right to be displayed again.
  115. */
  116. void max7219_message_sign_task(bool loop_message) {
  117. uint8_t left_col = 0b00000000;
  118. if (!max7219_led_scrolling) {
  119. return;
  120. }
  121. if (loop_message) {
  122. left_col = max7219_led_a[0][0];
  123. }
  124. int i=0;
  125. for (int device_num=0; device_num<MAX7219_BUFFER_SIZE; device_num++) {
  126. for (int col=0; col<8; col++) {
  127. i++;
  128. if (i == max7219_buffer_end) {
  129. max7219_led_a[col][device_num] = left_col;
  130. device_num=MAX7219_BUFFER_SIZE;
  131. break;
  132. } else if (col < 7) {
  133. max7219_led_a[col][device_num] = max7219_led_a[col+1][device_num];
  134. } else if (device_num == MAX7219_BUFFER_SIZE-1) {
  135. max7219_led_a[col][device_num] = left_col;
  136. } else {
  137. max7219_led_a[col][device_num] = max7219_led_a[0][device_num+1];
  138. }
  139. }
  140. }
  141. max7219_write_frame();
  142. }
  143. /* Write data to a single max7219
  144. */
  145. void max7219_write(int device_num, volatile uint8_t opcode, volatile uint8_t data) {
  146. dprintf("max7219_write(%d, %d, %d)\n", device_num, opcode, data);
  147. // Clear the data array
  148. for(int i = MAX_BYTES; i>0; i--) {
  149. max7219_spidata[i-1]=0;
  150. }
  151. // Set our opcode and data
  152. uint8_t offset = device_num*2;
  153. max7219_spidata[offset] = data;
  154. max7219_spidata[offset+1] = opcode;
  155. // Write the data
  156. max7219_write_all();
  157. }
  158. /* Turn off all the LEDs
  159. */
  160. void max7219_clear_display(void) {
  161. dprintf("max7219_clear_display();\n");
  162. for (int col=0; col<8; col++) {
  163. for (int device_num=0; device_num<MAX7219_BUFFER_SIZE; device_num++) {
  164. max7219_led_a[col][device_num] = 0b00000000;
  165. }
  166. }
  167. max7219_write_frame();
  168. }
  169. /* Enable the display test (IE turn on all 64 LEDs)
  170. */
  171. void max7219_display_test(int device_num, bool enabled) {
  172. dprintf("max7219_display_test(%d, %d);\n", device_num, enabled);
  173. if (device_num<0 || device_num >= MAX7219_CONTROLLERS) {
  174. return;
  175. }
  176. max7219_write(device_num, OP_DISPLAYTEST, enabled);
  177. }
  178. /* Initialize the max7219 system and set the controller(s) to a default state.
  179. */
  180. void max7219_init(void) {
  181. wait_ms(1500);
  182. dprintf("max7219_init()\n");
  183. gpio_set_pin_output(MAX7219_LOAD);
  184. gpio_write_pin_high(MAX7219_LOAD);
  185. spi_init();
  186. for (int i=0; i<MAX7219_CONTROLLERS; i++) {
  187. max7219_shutdown(i, true);
  188. }
  189. for (int i=0; i<MAX7219_CONTROLLERS; i++) {
  190. // Reset everything to defaults and enable the display
  191. max7219_display_test(i, false);
  192. max7219_set_scan_limit(i, 7);
  193. max7219_set_decode_mode(i, 0);
  194. max7219_set_intensity(i, MAX7219_LED_INTENSITY);
  195. }
  196. max7219_clear_display();
  197. #ifndef MAX7219_NO_STARTUP_TEST
  198. for (int i=0; i<MAX7219_CONTROLLERS; i++) {
  199. // Test this display
  200. max7219_display_test(i, true);
  201. wait_ms(75);
  202. max7219_display_test(i, false);
  203. }
  204. #endif
  205. for (int i=0; i<MAX7219_CONTROLLERS; i++) {
  206. max7219_shutdown(i, false);
  207. }
  208. }
  209. /* Set the decode mode of the controller. You probably don't want to change this.
  210. */
  211. void max7219_set_decode_mode(int device_num, int mode) {
  212. dprintf("max7219_set_decode_mode(%d, %d);\n", device_num, mode);
  213. if (device_num<0 || device_num >= MAX7219_CONTROLLERS) {
  214. return;
  215. }
  216. max7219_write(device_num, OP_DECODEMODE, mode);
  217. }
  218. /* Set the intensity (brightness) for the LEDs.
  219. */
  220. void max7219_set_intensity(int device_num, int intensity) {
  221. dprintf("max7219_set_intensity(%d, %d);\n", device_num, intensity);
  222. if (device_num<0 || device_num >= MAX7219_CONTROLLERS) {
  223. return;
  224. }
  225. if (intensity >= 0 && intensity<16) {
  226. max7219_write(device_num, OP_INTENSITY, intensity);
  227. }
  228. }
  229. /* Control a single LED.
  230. */
  231. void max7219_set_led(int row, int column, bool state) {
  232. dprintf("max7219_set_led(%d, %d, %d);\n", row, column, state);
  233. if (column<0 || column>8*MAX7219_CONTROLLERS) {
  234. xprintf("max7219_set_led: column (%d) out of bounds\n", column);
  235. return;
  236. }
  237. if (row<0 || row>7) {
  238. xprintf("max7219_set_led: row (%d) out of bounds\n", row);
  239. return;
  240. }
  241. /* At this point we reverse the sense of row and column to match the
  242. * physical layout of my LEDs.
  243. */
  244. uint8_t device_num = column / 8;
  245. uint8_t col = column % 8;
  246. uint8_t val = 0b10000000 >> row;
  247. if (state) {
  248. max7219_led_a[col][device_num] = max7219_led_a[col][device_num]|val;
  249. } else {
  250. val = ~val;
  251. max7219_led_a[col][device_num] = max7219_led_a[col][device_num]&val;
  252. }
  253. max7219_write(device_num, col+1, max7219_led_a[col][device_num]);
  254. }
  255. /* Set the number of digits (rows) to be scanned.
  256. */
  257. void max7219_set_scan_limit(int device_num, int limit) {
  258. dprintf("max7219_set_scan_limit(%d, %d);\n", device_num, limit);
  259. if (device_num<0 || device_num >= MAX7219_CONTROLLERS) {
  260. return;
  261. }
  262. if (limit >= 0 && limit < 8) {
  263. max7219_write(device_num, OP_SCANLIMIT, limit);
  264. }
  265. }
  266. /* Enable (true) or disable (false) the controller.
  267. */
  268. void max7219_shutdown(int device_num, bool shutdown) {
  269. dprintf("max7219_shutdown(%d, %d);\n", device_num, shutdown);
  270. if (device_num<0 || device_num >= MAX7219_CONTROLLERS) {
  271. return;
  272. }
  273. max7219_write(device_num, OP_SHUTDOWN, !shutdown);
  274. }