max7219.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <unistd.h>
  4. #include <stdint.h>
  5. #include <armbianio.h>
  6. #include "max7219.h"
  7. //
  8. // MAX7219 LED Matrix controller library
  9. // Copyright (c) 2018 BitBank Software, Inc.
  10. // Written by Larry Bank
  11. // bitbank@pobox.com
  12. // Project started 3/10/2018
  13. //
  14. // This program is free software: you can redistribute it and/or modify
  15. // it under the terms of the GNU General Public License as published by
  16. // the Free Software Foundation, either version 3 of the License, or
  17. // (at your option) any later version.
  18. //
  19. // This program is distributed in the hope that it will be useful,
  20. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. // GNU General Public License for more details.
  23. //
  24. // You should have received a copy of the GNU General Public License
  25. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  26. //
  27. // I purchased a 4-module unit of red 8x8 LED matrices. They're wired such that the leftmost module is the last
  28. // in the chain and the bits are arranged such that the MSB is on the left and row 0 is the top row.
  29. //
  30. // This library contains a basic set of functions to initialize the modules, set the intensity, draw text in 2 font
  31. // sizes and scroll a bitmap (which can contain text or any graphics you put there
  32. //
  33. // The modules are connected to the SPI clock and data lines as well as a digital output pin to control the latching
  34. // of the data. The SPI CS line is not sufficient to properly control the loading of the data. Each module expects
  35. // to receive 16-bits of data at a time with D15 sent first and D0 sent last. The data contains an instruction in
  36. // D11-D8 and data for that instruction in D7-D0. In my case of 4 modules cascaded together, a data stream of
  37. // 16 x 4 = 64 bits must be transmitted at a time to latch data to all modules simultaneously. The controllers
  38. // randomly power up in test mode (all LEDs on at their brightest). For this reason, the init function disables
  39. // test mode just in case.
  40. //
  41. // Each module shifts bits in and the next bit is shifted out. For example, in the case of my setup with 4 modules
  42. const uint8_t ucFont[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x81,0x95,0xb1,0xb1,0x95,0x81,0x7e,
  43. 0x7e,0xff,0xeb,0xcf,0xcf,0xeb,0xff,0x7e,0x0e,0x1f,0x3f,0x7e,0x3f,0x1f,0x0e,0x00,
  44. 0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x08,0x00,0x38,0x9a,0x9f,0xff,0x9f,0x9a,0x38,0x00,
  45. 0x10,0xb8,0xfc,0xfe,0xfc,0xb8,0x10,0x00,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,
  46. 0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,
  47. 0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0x70,0xf8,0x88,0x88,0xfd,0x7f,0x07,0x0f,
  48. 0x00,0x4e,0x5f,0xf1,0xf1,0x5f,0x4e,0x00,0xc0,0xe0,0xff,0x7f,0x05,0x05,0x07,0x07,
  49. 0xc0,0xff,0x7f,0x05,0x05,0x65,0x7f,0x3f,0x99,0x5a,0x3c,0xe7,0xe7,0x3c,0x5a,0x99,
  50. 0x7f,0x3e,0x3e,0x1c,0x1c,0x08,0x08,0x00,0x08,0x08,0x1c,0x1c,0x3e,0x3e,0x7f,0x00,
  51. 0x00,0x24,0x66,0xff,0xff,0x66,0x24,0x00,0x00,0x5f,0x5f,0x00,0x00,0x5f,0x5f,0x00,
  52. 0x06,0x0f,0x09,0x7f,0x7f,0x01,0x7f,0x7f,0xc0,0x9a,0xbf,0xa5,0xbd,0xd9,0x43,0x02,
  53. 0x00,0x70,0x70,0x70,0x70,0x70,0x70,0x00,0x80,0x94,0xb6,0xff,0xff,0xb6,0x94,0x80,
  54. 0x00,0x04,0x06,0x7f,0x7f,0x06,0x04,0x00,0x00,0x10,0x30,0x7f,0x7f,0x30,0x10,0x00,
  55. 0x08,0x08,0x08,0x2a,0x3e,0x1c,0x08,0x00,0x08,0x1c,0x3e,0x2a,0x08,0x08,0x08,0x00,
  56. 0x3c,0x3c,0x20,0x20,0x20,0x20,0x20,0x00,0x08,0x1c,0x3e,0x08,0x08,0x3e,0x1c,0x08,
  57. 0x30,0x38,0x3c,0x3e,0x3e,0x3c,0x38,0x30,0x06,0x0e,0x1e,0x3e,0x3e,0x1e,0x0e,0x06,
  58. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x5f,0x06,0x00,0x00,
  59. 0x00,0x07,0x07,0x00,0x07,0x07,0x00,0x00,0x14,0x7f,0x7f,0x14,0x7f,0x7f,0x14,0x00,
  60. 0x24,0x2e,0x2a,0x6b,0x6b,0x3a,0x12,0x00,0x46,0x66,0x30,0x18,0x0c,0x66,0x62,0x00,
  61. 0x30,0x7a,0x4f,0x5d,0x37,0x7a,0x48,0x00,0x00,0x04,0x07,0x03,0x00,0x00,0x00,0x00,
  62. 0x00,0x1c,0x3e,0x63,0x41,0x00,0x00,0x00,0x00,0x41,0x63,0x3e,0x1c,0x00,0x00,0x00,
  63. 0x08,0x2a,0x3e,0x1c,0x1c,0x3e,0x2a,0x08,0x00,0x08,0x08,0x3e,0x3e,0x08,0x08,0x00,
  64. 0x00,0x00,0x80,0xe0,0x60,0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00,
  65. 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00,
  66. 0x3e,0x7f,0x59,0x4d,0x47,0x7f,0x3e,0x00,0x40,0x42,0x7f,0x7f,0x40,0x40,0x00,0x00,
  67. 0x62,0x73,0x59,0x49,0x6f,0x66,0x00,0x00,0x22,0x63,0x49,0x49,0x7f,0x36,0x00,0x00,
  68. 0x18,0x1c,0x16,0x53,0x7f,0x7f,0x50,0x00,0x27,0x67,0x45,0x45,0x7d,0x39,0x00,0x00,
  69. 0x3c,0x7e,0x4b,0x49,0x79,0x30,0x00,0x00,0x03,0x03,0x71,0x79,0x0f,0x07,0x00,0x00,
  70. 0x36,0x7f,0x49,0x49,0x7f,0x36,0x00,0x00,0x06,0x4f,0x49,0x69,0x3f,0x1e,0x00,0x00,
  71. 0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x80,0xe6,0x66,0x00,0x00,0x00,
  72. 0x08,0x1c,0x36,0x63,0x41,0x00,0x00,0x00,0x00,0x14,0x14,0x14,0x14,0x14,0x14,0x00,
  73. 0x00,0x41,0x63,0x36,0x1c,0x08,0x00,0x00,0x00,0x02,0x03,0x59,0x5d,0x07,0x02,0x00,
  74. 0x3e,0x7f,0x41,0x5d,0x5d,0x5f,0x0e,0x00,0x7c,0x7e,0x13,0x13,0x7e,0x7c,0x00,0x00,
  75. 0x41,0x7f,0x7f,0x49,0x49,0x7f,0x36,0x00,0x1c,0x3e,0x63,0x41,0x41,0x63,0x22,0x00,
  76. 0x41,0x7f,0x7f,0x41,0x63,0x3e,0x1c,0x00,0x41,0x7f,0x7f,0x49,0x5d,0x41,0x63,0x00,
  77. 0x41,0x7f,0x7f,0x49,0x1d,0x01,0x03,0x00,0x1c,0x3e,0x63,0x41,0x51,0x33,0x72,0x00,
  78. 0x7f,0x7f,0x08,0x08,0x7f,0x7f,0x00,0x00,0x00,0x41,0x7f,0x7f,0x41,0x00,0x00,0x00,
  79. 0x30,0x70,0x40,0x41,0x7f,0x3f,0x01,0x00,0x41,0x7f,0x7f,0x08,0x1c,0x77,0x63,0x00,
  80. 0x41,0x7f,0x7f,0x41,0x40,0x60,0x70,0x00,0x7f,0x7f,0x0e,0x1c,0x0e,0x7f,0x7f,0x00,
  81. 0x7f,0x7f,0x06,0x0c,0x18,0x7f,0x7f,0x00,0x1c,0x3e,0x63,0x41,0x63,0x3e,0x1c,0x00,
  82. 0x41,0x7f,0x7f,0x49,0x09,0x0f,0x06,0x00,0x1e,0x3f,0x21,0x31,0x61,0x7f,0x5e,0x00,
  83. 0x41,0x7f,0x7f,0x09,0x19,0x7f,0x66,0x00,0x26,0x6f,0x4d,0x49,0x59,0x73,0x32,0x00,
  84. 0x03,0x41,0x7f,0x7f,0x41,0x03,0x00,0x00,0x7f,0x7f,0x40,0x40,0x7f,0x7f,0x00,0x00,
  85. 0x1f,0x3f,0x60,0x60,0x3f,0x1f,0x00,0x00,0x3f,0x7f,0x60,0x30,0x60,0x7f,0x3f,0x00,
  86. 0x63,0x77,0x1c,0x08,0x1c,0x77,0x63,0x00,0x07,0x4f,0x78,0x78,0x4f,0x07,0x00,0x00,
  87. 0x47,0x63,0x71,0x59,0x4d,0x67,0x73,0x00,0x00,0x7f,0x7f,0x41,0x41,0x00,0x00,0x00,
  88. 0x01,0x03,0x06,0x0c,0x18,0x30,0x60,0x00,0x00,0x41,0x41,0x7f,0x7f,0x00,0x00,0x00,
  89. 0x08,0x0c,0x06,0x03,0x06,0x0c,0x08,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
  90. 0x00,0x00,0x03,0x07,0x04,0x00,0x00,0x00,0x20,0x74,0x54,0x54,0x3c,0x78,0x40,0x00,
  91. 0x41,0x7f,0x3f,0x48,0x48,0x78,0x30,0x00,0x38,0x7c,0x44,0x44,0x6c,0x28,0x00,0x00,
  92. 0x30,0x78,0x48,0x49,0x3f,0x7f,0x40,0x00,0x38,0x7c,0x54,0x54,0x5c,0x18,0x00,0x00,
  93. 0x48,0x7e,0x7f,0x49,0x03,0x06,0x00,0x00,0x98,0xbc,0xa4,0xa4,0xf8,0x7c,0x04,0x00,
  94. 0x41,0x7f,0x7f,0x08,0x04,0x7c,0x78,0x00,0x00,0x44,0x7d,0x7d,0x40,0x00,0x00,0x00,
  95. 0x60,0xe0,0x80,0x84,0xfd,0x7d,0x00,0x00,0x41,0x7f,0x7f,0x10,0x38,0x6c,0x44,0x00,
  96. 0x00,0x41,0x7f,0x7f,0x40,0x00,0x00,0x00,0x7c,0x7c,0x18,0x78,0x1c,0x7c,0x78,0x00,
  97. 0x7c,0x78,0x04,0x04,0x7c,0x78,0x00,0x00,0x38,0x7c,0x44,0x44,0x7c,0x38,0x00,0x00,
  98. 0x84,0xfc,0xf8,0xa4,0x24,0x3c,0x18,0x00,0x18,0x3c,0x24,0xa4,0xf8,0xfc,0x84,0x00,
  99. 0x44,0x7c,0x78,0x4c,0x04,0x0c,0x18,0x00,0x48,0x5c,0x54,0x74,0x64,0x24,0x00,0x00,
  100. 0x04,0x04,0x3e,0x7f,0x44,0x24,0x00,0x00,0x3c,0x7c,0x40,0x40,0x3c,0x7c,0x40,0x00,
  101. 0x1c,0x3c,0x60,0x60,0x3c,0x1c,0x00,0x00,0x3c,0x7c,0x60,0x30,0x60,0x7c,0x3c,0x00,
  102. 0x44,0x6c,0x38,0x10,0x38,0x6c,0x44,0x00,0x9c,0xbc,0xa0,0xa0,0xfc,0x7c,0x00,0x00,
  103. 0x4c,0x64,0x74,0x5c,0x4c,0x64,0x00,0x00,0x08,0x08,0x3e,0x77,0x41,0x41,0x00,0x00,
  104. 0x00,0x00,0x00,0x77,0x77,0x00,0x00,0x00,0x41,0x41,0x77,0x3e,0x08,0x08,0x00,0x00,
  105. 0x02,0x03,0x01,0x03,0x02,0x03,0x01,0x00,0x70,0x78,0x4c,0x46,0x4c,0x78,0x70,0x00};
  106. // 5x7 font (in 6x8 cell)
  107. const uint8_t ucSmallFont[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x45,0x51,0x45,0x3e,0x00,0x3e,0x6b,0x6f,
  108. 0x6b,0x3e,0x00,0x1c,0x3e,0x7c,0x3e,0x1c,0x00,0x18,0x3c,0x7e,0x3c,0x18,0x00,0x30,
  109. 0x36,0x7f,0x36,0x30,0x00,0x18,0x5c,0x7e,0x5c,0x18,0x00,0x00,0x18,0x18,0x00,0x00,
  110. 0x00,0xff,0xe7,0xe7,0xff,0xff,0x00,0x3c,0x24,0x24,0x3c,0x00,0x00,0xc3,0xdb,0xdb,
  111. 0xc3,0xff,0x00,0x30,0x48,0x4a,0x36,0x0e,0x00,0x06,0x29,0x79,0x29,0x06,0x00,0x60,
  112. 0x70,0x3f,0x02,0x04,0x00,0x60,0x7e,0x0a,0x35,0x3f,0x00,0x2a,0x1c,0x36,0x1c,0x2a,
  113. 0x00,0x00,0x7f,0x3e,0x1c,0x08,0x00,0x08,0x1c,0x3e,0x7f,0x00,0x00,0x14,0x36,0x7f,
  114. 0x36,0x14,0x00,0x00,0x5f,0x00,0x5f,0x00,0x00,0x06,0x09,0x7f,0x01,0x7f,0x00,0x22,
  115. 0x4d,0x55,0x59,0x22,0x00,0x60,0x60,0x60,0x60,0x00,0x00,0x14,0xb6,0xff,0xb6,0x14,
  116. 0x00,0x04,0x06,0x7f,0x06,0x04,0x00,0x10,0x30,0x7f,0x30,0x10,0x00,0x08,0x08,0x3e,
  117. 0x1c,0x08,0x00,0x08,0x1c,0x3e,0x08,0x08,0x00,0x78,0x40,0x40,0x40,0x40,0x00,0x08,
  118. 0x3e,0x08,0x3e,0x08,0x00,0x30,0x3c,0x3f,0x3c,0x30,0x00,0x03,0x0f,0x3f,0x0f,0x03,
  119. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x06,0x00,0x00,0x07,0x03,0x00,
  120. 0x07,0x03,0x00,0x24,0x7e,0x24,0x7e,0x24,0x00,0x24,0x2b,0x6a,0x12,0x00,0x00,0x63,
  121. 0x13,0x08,0x64,0x63,0x00,0x36,0x49,0x56,0x20,0x50,0x00,0x00,0x07,0x03,0x00,0x00,
  122. 0x00,0x00,0x3e,0x41,0x00,0x00,0x00,0x00,0x41,0x3e,0x00,0x00,0x00,0x08,0x3e,0x1c,
  123. 0x3e,0x08,0x00,0x08,0x08,0x3e,0x08,0x08,0x00,0x00,0xe0,0x60,0x00,0x00,0x00,0x08,
  124. 0x08,0x08,0x08,0x08,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,
  125. 0x00,0x3e,0x51,0x49,0x45,0x3e,0x00,0x00,0x42,0x7f,0x40,0x00,0x00,0x62,0x51,0x49,
  126. 0x49,0x46,0x00,0x22,0x49,0x49,0x49,0x36,0x00,0x18,0x14,0x12,0x7f,0x10,0x00,0x2f,
  127. 0x49,0x49,0x49,0x31,0x00,0x3c,0x4a,0x49,0x49,0x30,0x00,0x01,0x71,0x09,0x05,0x03,
  128. 0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x06,0x49,0x49,0x29,0x1e,0x00,0x00,0x6c,0x6c,
  129. 0x00,0x00,0x00,0x00,0xec,0x6c,0x00,0x00,0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x24,
  130. 0x24,0x24,0x24,0x24,0x00,0x00,0x41,0x22,0x14,0x08,0x00,0x02,0x01,0x59,0x09,0x06,
  131. 0x00,0x3e,0x41,0x5d,0x55,0x1e,0x00,0x7e,0x11,0x11,0x11,0x7e,0x00,0x7f,0x49,0x49,
  132. 0x49,0x36,0x00,0x3e,0x41,0x41,0x41,0x22,0x00,0x7f,0x41,0x41,0x41,0x3e,0x00,0x7f,
  133. 0x49,0x49,0x49,0x41,0x00,0x7f,0x09,0x09,0x09,0x01,0x00,0x3e,0x41,0x49,0x49,0x7a,
  134. 0x00,0x7f,0x08,0x08,0x08,0x7f,0x00,0x00,0x41,0x7f,0x41,0x00,0x00,0x30,0x40,0x40,
  135. 0x40,0x3f,0x00,0x7f,0x08,0x14,0x22,0x41,0x00,0x7f,0x40,0x40,0x40,0x40,0x00,0x7f,
  136. 0x02,0x04,0x02,0x7f,0x00,0x7f,0x02,0x04,0x08,0x7f,0x00,0x3e,0x41,0x41,0x41,0x3e,
  137. 0x00,0x7f,0x09,0x09,0x09,0x06,0x00,0x3e,0x41,0x51,0x21,0x5e,0x00,0x7f,0x09,0x09,
  138. 0x19,0x66,0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x01,0x01,0x7f,0x01,0x01,0x00,0x3f,
  139. 0x40,0x40,0x40,0x3f,0x00,0x1f,0x20,0x40,0x20,0x1f,0x00,0x3f,0x40,0x3c,0x40,0x3f,
  140. 0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x07,0x08,0x70,0x08,0x07,0x00,0x71,0x49,0x45,
  141. 0x43,0x00,0x00,0x00,0x7f,0x41,0x41,0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00,
  142. 0x41,0x41,0x7f,0x00,0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x80,0x80,0x80,0x80,0x80,
  143. 0x00,0x00,0x03,0x07,0x00,0x00,0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x7f,0x44,0x44,
  144. 0x44,0x38,0x00,0x38,0x44,0x44,0x44,0x28,0x00,0x38,0x44,0x44,0x44,0x7f,0x00,0x38,
  145. 0x54,0x54,0x54,0x08,0x00,0x08,0x7e,0x09,0x09,0x00,0x00,0x18,0xa4,0xa4,0xa4,0x7c,
  146. 0x00,0x7f,0x04,0x04,0x78,0x00,0x00,0x00,0x00,0x7d,0x40,0x00,0x00,0x40,0x80,0x84,
  147. 0x7d,0x00,0x00,0x7f,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x7f,0x40,0x00,0x00,0x7c,
  148. 0x04,0x18,0x04,0x78,0x00,0x7c,0x04,0x04,0x78,0x00,0x00,0x38,0x44,0x44,0x44,0x38,
  149. 0x00,0xfc,0x44,0x44,0x44,0x38,0x00,0x38,0x44,0x44,0x44,0xfc,0x00,0x44,0x78,0x44,
  150. 0x04,0x08,0x00,0x08,0x54,0x54,0x54,0x20,0x00,0x04,0x3e,0x44,0x24,0x00,0x00,0x3c,
  151. 0x40,0x20,0x7c,0x00,0x00,0x1c,0x20,0x40,0x20,0x1c,0x00,0x3c,0x60,0x30,0x60,0x3c,
  152. 0x00,0x6c,0x10,0x10,0x6c,0x00,0x00,0x9c,0xa0,0x60,0x3c,0x00,0x00,0x64,0x54,0x54,
  153. 0x4c,0x00,0x00,0x08,0x3e,0x41,0x41,0x00,0x00,0x00,0x00,0x77,0x00,0x00,0x00,0x00,
  154. 0x41,0x41,0x3e,0x08,0x00,0x02,0x01,0x02,0x01,0x00,0x00,0x3c,0x26,0x23,0x26,0x3c};
  155. static uint8_t iNumControllers, iCSPin;
  156. static int file_spi = -1;
  157. //
  158. // Transmit a sequence of N x 16 bits to the cascaded controllers
  159. //
  160. // Send an atomic sequence of uint8_ts for loading all chained controllers (16 bits per controller)
  161. // Each controller acts like a 16-bit shift register and passes on the bits to the next controller. The
  162. // last 16-bits to sit in the controller will be latched when the CS line goes high.
  163. // uint8_t 0 -> D15-D8 (XXXX A3 A2 A1 A0), 4-bits unused and 4-bit register address
  164. // uint8_t 1 -> D7-D0 (8-bit data)
  165. //
  166. void maxSendSequence(uint8_t *pSequence, uint8_t len)
  167. {
  168. // The CS line stays low throughout a "transaction". Send all of the control uint8_ts for all of the chained
  169. // controllers in a single transaction. When the CS line rises, the data will be latched
  170. AIOWriteGPIO(iCSPin, 0);
  171. AIOWriteSPI(file_spi, pSequence, len);
  172. AIOWriteGPIO(iCSPin, 1);
  173. } /* maxSendSequence() */
  174. //
  175. // Power on or off the LED controllers
  176. //
  177. void maxPowerUp(uint8_t bPowerUp)
  178. {
  179. uint8_t i;
  180. uint8_t *d, bTemp[32]; // up to 16 controllers
  181. d = bTemp;
  182. for (i=0; i<iNumControllers; i++)
  183. {
  184. *d++ = 0x0C; // power up/down
  185. *d++ = bPowerUp;
  186. }
  187. maxSendSequence(bTemp, iNumControllers * 2); // send the power up/down instruction
  188. } /* maxPowerUp() */
  189. //
  190. // Set the intensity (duty cycle of PWM signal) for the LED segments
  191. // valid values are 0 (dimmest) to 15 (brightest)
  192. //
  193. void maxSetIntensity(uint8_t bIntensity)
  194. {
  195. uint8_t *d, bTemp[32];
  196. uint8_t i;
  197. d = bTemp;
  198. for (i=0; i<iNumControllers; i++)
  199. {
  200. *d++ = 0x0A; // set intensity
  201. *d++ = bIntensity;
  202. } // for i
  203. maxSendSequence(bTemp, iNumControllers * 2);
  204. } /* maxSetIntensity() */
  205. //
  206. // Set the segment decode mode (BCD or none)
  207. //
  208. void maxSetSegmentMode(uint8_t bMode)
  209. {
  210. uint8_t i, *d, bTemp[32];
  211. d = bTemp;
  212. for (i=0; i<iNumControllers; i++)
  213. {
  214. *d++ = 0x09; // decode mode
  215. *d++ = (bMode) ? 0xff : 0x00;
  216. } // for i
  217. maxSendSequence(bTemp, iNumControllers * 2); // send the scan limit instructions to all controllers
  218. } /* maxSetSegmentMode() */
  219. static unsigned char lookup[16] = {
  220. 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
  221. 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf, };
  222. uint8_t reverse(uint8_t n) {
  223. // Reverse the top and bottom nibble then swap them.
  224. return (lookup[n&0b1111] << 4) | lookup[n>>4];
  225. }
  226. //
  227. // Send image data to the array of controllers
  228. // The image data is transmitted as N by 8 lines tall (N is the number of MAX7219 controllers)
  229. // The pitch (uint8_ts per line) can be any value
  230. //
  231. void maxSendImage(uint8_t *pImage, int iPitch, uint8_t rotated)
  232. {
  233. uint8_t i, j;
  234. uint8_t *s, *d, bTemp[32];
  235. if(rotated) {
  236. for (j=0; j<8; j++) // 8 rows to transmit
  237. {
  238. s = &pImage[iPitch * (j+1) - 1];
  239. d = bTemp;
  240. for (i=0; i<iNumControllers; i++)
  241. {
  242. *d++ = (8-j); // row number is the "instruction"
  243. *d++ = reverse(*s--); // image data
  244. } // for each controller
  245. maxSendSequence(bTemp, iNumControllers * 2);
  246. } // for each row of image
  247. } else {
  248. for (j=0; j<8; j++) // 8 rows to transmit
  249. {
  250. s = &pImage[iPitch * j];
  251. d = bTemp;
  252. for (i=0; i<iNumControllers; i++)
  253. {
  254. *d++ = (j+1); // row number is the "instruction"
  255. *d++ = *s++; // image data
  256. } // for each controller
  257. maxSendSequence(bTemp, iNumControllers * 2);
  258. } // for each row of image
  259. }
  260. } /* maxSendImage() */
  261. //
  262. // Enable (1) or disable (0) test mode
  263. // This mode lights up every LED at max brightness
  264. // It can sometimes power up in test mode
  265. //
  266. void maxSetTestMode(uint8_t bOn)
  267. {
  268. uint8_t i, *d, bTemp[32];
  269. d = bTemp;
  270. for (i=0; i<iNumControllers; i++)
  271. {
  272. *d++ = 0x0F; // test mode
  273. *d++ = bOn;
  274. } // for i
  275. maxSendSequence(bTemp, iNumControllers * 2); // send the scan limit instructions to all controllers
  276. } /* maxSetTestMode() */
  277. //
  278. // Number of "digits/rows" to control
  279. // valid values are 1-8 active digits/rows
  280. //
  281. void maxSetLimit(uint8_t bLimit)
  282. {
  283. uint8_t i, *d, bTemp[32];
  284. d = bTemp;
  285. for (i=0; i<iNumControllers; i++)
  286. {
  287. *d++ = 0x0B; // set scan limit
  288. *d++ = (bLimit - 1);
  289. } // for i
  290. maxSendSequence(bTemp, iNumControllers * 2); // send the scan limit instructions to all controllers
  291. } /* maxSetLimit() */
  292. //
  293. // Send an ASCII string of numbers/spaces/decimal points
  294. // to a 7-segment display
  295. //
  296. void maxSegmentString(char *pString)
  297. {
  298. unsigned char ucTemp[4];
  299. int iDigit;
  300. memset(ucTemp, 0, sizeof(ucTemp));
  301. iDigit = 0;
  302. while (*pString && iDigit < 8)
  303. {
  304. ucTemp[0] = 8 - (iDigit & 7); // cmd byte to write
  305. if (pString[0] >= '0' && pString[0] <= '9')
  306. {
  307. ucTemp[1] = *pString++; // store digit
  308. if (pString[0] == '.')
  309. {
  310. ucTemp[1] |= 0x80; // turn on decimal point
  311. pString++;
  312. }
  313. }
  314. else
  315. {
  316. ucTemp[1] = 0xf; // space = all segments off
  317. pString++;
  318. }
  319. iDigit++;
  320. maxSendSequence(ucTemp, 2); // need to latch each byte pair
  321. }
  322. while (iDigit < 8) // blank out remaining digits
  323. {
  324. ucTemp[0] = 8 - (iDigit & 7);
  325. ucTemp[1] = 0xf; // all segments off
  326. iDigit++;
  327. maxSendSequence(ucTemp, 2);
  328. }
  329. } /* maxSegmentString() */
  330. //
  331. // Draw a string of characters into the image buffer
  332. // Normal characters are 8x8 and drawn on uint8_t boundaries
  333. // Small characters are 6x8 and drawn on bit boundaries
  334. //
  335. void maxDrawString(char *pString, uint8_t *pImage, uint8_t iPitch, uint8_t bSmall)
  336. {
  337. uint8_t b, bMask, i, j, *d, bCol;
  338. const uint8_t *pFont;
  339. const uint8_t *s;
  340. int iWidth;
  341. d = pImage;
  342. bCol = 0;
  343. if (bSmall)
  344. {
  345. pFont = ucSmallFont;
  346. iWidth = 6;
  347. }
  348. else
  349. {
  350. pFont = ucFont;
  351. iWidth = 8;
  352. }
  353. while (*pString)
  354. {
  355. b = *pString++;
  356. s = &pFont[(int)b * iWidth]; // 6 or 8 uint8_ts per character in ASCII order
  357. for (i=0; i<iWidth; i++) // column
  358. {
  359. bMask = (0x80 >> (bCol & 7));
  360. b = *s++; // current font uint8_t
  361. for (j=0; j<8; j++) // bit number of source becomes destination row
  362. {
  363. if (b & 1) // start from LSB
  364. d[j*iPitch] |= bMask;
  365. else
  366. d[j*iPitch] &= ~bMask;
  367. b >>= 1; // shift down font uint8_t
  368. } // for j
  369. bCol++;
  370. if ((bCol & 7) == 0) // next uint8_t
  371. d++;
  372. } // for i
  373. } // while string
  374. } /* maxDrawString() */
  375. //
  376. // Scroll a bitmap N bits left (positive) or right (negative)
  377. // Valid scroll values are +1 to +7 and -1 to -7
  378. // A bitmap is assumed to be iPitch uint8_ts wide by 8 rows tall
  379. // Bits which scroll off one end are added back to the other end
  380. //
  381. void maxScrollBitmap(uint8_t *pBitmap, int iPitch, int iScroll)
  382. {
  383. uint8_t b, bEdge, *s;
  384. int col, row;
  385. if (iScroll > 0) // scroll left
  386. {
  387. for (row=0; row<8; row++)
  388. {
  389. s = &pBitmap[row * iPitch];
  390. bEdge = s[0] >> (8-iScroll);
  391. for (col=0; col<iPitch; col++)
  392. {
  393. b = s[col] << iScroll;
  394. b |= (col == iPitch-1) ? bEdge : (s[col+1] >> (8-iScroll));
  395. s[col] = b;
  396. } // for col
  397. } // for row
  398. }
  399. else // scroll right
  400. {
  401. iScroll = 0 - iScroll; // make it a positive number
  402. for (row=0; row<8; row++)
  403. {
  404. s = &pBitmap[row * iPitch];
  405. bEdge = s[iPitch-1] << (8-iScroll);
  406. for (col=iPitch-1; col>=0; col--)
  407. {
  408. b = s[col] >> iScroll;
  409. b |= (col == 0) ? bEdge : (s[col-1] << (8-iScroll));
  410. s[col] = b;
  411. } // for col
  412. } // for row
  413. }
  414. } /* maxScrollBitmap() */
  415. //
  416. // Initialize the controllers
  417. //
  418. int maxInit(uint8_t iNum, uint8_t bDecodeMode, uint8_t iChannel, uint8_t iSelect)
  419. {
  420. if (!AIOInit())
  421. {
  422. fprintf(stderr, "Error initializing ArmbianIO library\n");
  423. return -1;
  424. }
  425. iNumControllers = iNum;
  426. iCSPin = iSelect; // header pin number used for select line
  427. file_spi = AIOOpenSPI(iChannel, 2000000); // 2Mhz is a reasonable speed
  428. if (file_spi == -1)
  429. {
  430. fprintf(stderr, "Failed to open the SPI bus\n");
  431. file_spi = -1;
  432. return -1;
  433. }
  434. AIOAddGPIO(iCSPin, GPIO_OUT);
  435. AIOWriteGPIO(iCSPin, 1);
  436. maxPowerUp(1); // turn on the LED controllers
  437. maxSetLimit(8); // tell it to display 8 rows of 8 pixels
  438. maxSetIntensity(0); // set the minimum intensity to start (1/32 duty cycle)
  439. maxSetSegmentMode(bDecodeMode); // sets BCD (7-segment), or none (pixels
  440. maxSetTestMode(0); // disable test mode (it can accidentally get set at power up)
  441. return 0;
  442. } /* maxInit() */
  443. void maxShutdown(void)
  444. {
  445. maxPowerUp(0); // turn off the LED controllers
  446. AIOCloseSPI(file_spi);
  447. file_spi = -1;
  448. AIOShutdown();
  449. } /* maxShutdown() */