123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- #include <stdio.h>
- #include <string.h>
- #include <unistd.h>
- #include <stdint.h>
- #include <armbianio.h>
- #include "max7219.h"
- //
- // MAX7219 LED Matrix controller library
- // Copyright (c) 2018 BitBank Software, Inc.
- // Written by Larry Bank
- // bitbank@pobox.com
- // Project started 3/10/2018
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
- //
- // I purchased a 4-module unit of red 8x8 LED matrices. They're wired such that the leftmost module is the last
- // in the chain and the bits are arranged such that the MSB is on the left and row 0 is the top row.
- //
- // This library contains a basic set of functions to initialize the modules, set the intensity, draw text in 2 font
- // sizes and scroll a bitmap (which can contain text or any graphics you put there
- //
- // The modules are connected to the SPI clock and data lines as well as a digital output pin to control the latching
- // of the data. The SPI CS line is not sufficient to properly control the loading of the data. Each module expects
- // to receive 16-bits of data at a time with D15 sent first and D0 sent last. The data contains an instruction in
- // D11-D8 and data for that instruction in D7-D0. In my case of 4 modules cascaded together, a data stream of
- // 16 x 4 = 64 bits must be transmitted at a time to latch data to all modules simultaneously. The controllers
- // randomly power up in test mode (all LEDs on at their brightest). For this reason, the init function disables
- // test mode just in case.
- //
- // Each module shifts bits in and the next bit is shifted out. For example, in the case of my setup with 4 modules
- const uint8_t ucFont[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x81,0x95,0xb1,0xb1,0x95,0x81,0x7e,
- 0x7e,0xff,0xeb,0xcf,0xcf,0xeb,0xff,0x7e,0x0e,0x1f,0x3f,0x7e,0x3f,0x1f,0x0e,0x00,
- 0x08,0x1c,0x3e,0x7f,0x3e,0x1c,0x08,0x00,0x38,0x9a,0x9f,0xff,0x9f,0x9a,0x38,0x00,
- 0x10,0xb8,0xfc,0xfe,0xfc,0xb8,0x10,0x00,0x00,0x00,0x18,0x3c,0x3c,0x18,0x00,0x00,
- 0xff,0xff,0xe7,0xc3,0xc3,0xe7,0xff,0xff,0x00,0x3c,0x66,0x42,0x42,0x66,0x3c,0x00,
- 0xff,0xc3,0x99,0xbd,0xbd,0x99,0xc3,0xff,0x70,0xf8,0x88,0x88,0xfd,0x7f,0x07,0x0f,
- 0x00,0x4e,0x5f,0xf1,0xf1,0x5f,0x4e,0x00,0xc0,0xe0,0xff,0x7f,0x05,0x05,0x07,0x07,
- 0xc0,0xff,0x7f,0x05,0x05,0x65,0x7f,0x3f,0x99,0x5a,0x3c,0xe7,0xe7,0x3c,0x5a,0x99,
- 0x7f,0x3e,0x3e,0x1c,0x1c,0x08,0x08,0x00,0x08,0x08,0x1c,0x1c,0x3e,0x3e,0x7f,0x00,
- 0x00,0x24,0x66,0xff,0xff,0x66,0x24,0x00,0x00,0x5f,0x5f,0x00,0x00,0x5f,0x5f,0x00,
- 0x06,0x0f,0x09,0x7f,0x7f,0x01,0x7f,0x7f,0xc0,0x9a,0xbf,0xa5,0xbd,0xd9,0x43,0x02,
- 0x00,0x70,0x70,0x70,0x70,0x70,0x70,0x00,0x80,0x94,0xb6,0xff,0xff,0xb6,0x94,0x80,
- 0x00,0x04,0x06,0x7f,0x7f,0x06,0x04,0x00,0x00,0x10,0x30,0x7f,0x7f,0x30,0x10,0x00,
- 0x08,0x08,0x08,0x2a,0x3e,0x1c,0x08,0x00,0x08,0x1c,0x3e,0x2a,0x08,0x08,0x08,0x00,
- 0x3c,0x3c,0x20,0x20,0x20,0x20,0x20,0x00,0x08,0x1c,0x3e,0x08,0x08,0x3e,0x1c,0x08,
- 0x30,0x38,0x3c,0x3e,0x3e,0x3c,0x38,0x30,0x06,0x0e,0x1e,0x3e,0x3e,0x1e,0x0e,0x06,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x5f,0x06,0x00,0x00,
- 0x00,0x07,0x07,0x00,0x07,0x07,0x00,0x00,0x14,0x7f,0x7f,0x14,0x7f,0x7f,0x14,0x00,
- 0x24,0x2e,0x2a,0x6b,0x6b,0x3a,0x12,0x00,0x46,0x66,0x30,0x18,0x0c,0x66,0x62,0x00,
- 0x30,0x7a,0x4f,0x5d,0x37,0x7a,0x48,0x00,0x00,0x04,0x07,0x03,0x00,0x00,0x00,0x00,
- 0x00,0x1c,0x3e,0x63,0x41,0x00,0x00,0x00,0x00,0x41,0x63,0x3e,0x1c,0x00,0x00,0x00,
- 0x08,0x2a,0x3e,0x1c,0x1c,0x3e,0x2a,0x08,0x00,0x08,0x08,0x3e,0x3e,0x08,0x08,0x00,
- 0x00,0x00,0x80,0xe0,0x60,0x00,0x00,0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00,
- 0x00,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x60,0x30,0x18,0x0c,0x06,0x03,0x01,0x00,
- 0x3e,0x7f,0x59,0x4d,0x47,0x7f,0x3e,0x00,0x40,0x42,0x7f,0x7f,0x40,0x40,0x00,0x00,
- 0x62,0x73,0x59,0x49,0x6f,0x66,0x00,0x00,0x22,0x63,0x49,0x49,0x7f,0x36,0x00,0x00,
- 0x18,0x1c,0x16,0x53,0x7f,0x7f,0x50,0x00,0x27,0x67,0x45,0x45,0x7d,0x39,0x00,0x00,
- 0x3c,0x7e,0x4b,0x49,0x79,0x30,0x00,0x00,0x03,0x03,0x71,0x79,0x0f,0x07,0x00,0x00,
- 0x36,0x7f,0x49,0x49,0x7f,0x36,0x00,0x00,0x06,0x4f,0x49,0x69,0x3f,0x1e,0x00,0x00,
- 0x00,0x00,0x00,0x66,0x66,0x00,0x00,0x00,0x00,0x00,0x80,0xe6,0x66,0x00,0x00,0x00,
- 0x08,0x1c,0x36,0x63,0x41,0x00,0x00,0x00,0x00,0x14,0x14,0x14,0x14,0x14,0x14,0x00,
- 0x00,0x41,0x63,0x36,0x1c,0x08,0x00,0x00,0x00,0x02,0x03,0x59,0x5d,0x07,0x02,0x00,
- 0x3e,0x7f,0x41,0x5d,0x5d,0x5f,0x0e,0x00,0x7c,0x7e,0x13,0x13,0x7e,0x7c,0x00,0x00,
- 0x41,0x7f,0x7f,0x49,0x49,0x7f,0x36,0x00,0x1c,0x3e,0x63,0x41,0x41,0x63,0x22,0x00,
- 0x41,0x7f,0x7f,0x41,0x63,0x3e,0x1c,0x00,0x41,0x7f,0x7f,0x49,0x5d,0x41,0x63,0x00,
- 0x41,0x7f,0x7f,0x49,0x1d,0x01,0x03,0x00,0x1c,0x3e,0x63,0x41,0x51,0x33,0x72,0x00,
- 0x7f,0x7f,0x08,0x08,0x7f,0x7f,0x00,0x00,0x00,0x41,0x7f,0x7f,0x41,0x00,0x00,0x00,
- 0x30,0x70,0x40,0x41,0x7f,0x3f,0x01,0x00,0x41,0x7f,0x7f,0x08,0x1c,0x77,0x63,0x00,
- 0x41,0x7f,0x7f,0x41,0x40,0x60,0x70,0x00,0x7f,0x7f,0x0e,0x1c,0x0e,0x7f,0x7f,0x00,
- 0x7f,0x7f,0x06,0x0c,0x18,0x7f,0x7f,0x00,0x1c,0x3e,0x63,0x41,0x63,0x3e,0x1c,0x00,
- 0x41,0x7f,0x7f,0x49,0x09,0x0f,0x06,0x00,0x1e,0x3f,0x21,0x31,0x61,0x7f,0x5e,0x00,
- 0x41,0x7f,0x7f,0x09,0x19,0x7f,0x66,0x00,0x26,0x6f,0x4d,0x49,0x59,0x73,0x32,0x00,
- 0x03,0x41,0x7f,0x7f,0x41,0x03,0x00,0x00,0x7f,0x7f,0x40,0x40,0x7f,0x7f,0x00,0x00,
- 0x1f,0x3f,0x60,0x60,0x3f,0x1f,0x00,0x00,0x3f,0x7f,0x60,0x30,0x60,0x7f,0x3f,0x00,
- 0x63,0x77,0x1c,0x08,0x1c,0x77,0x63,0x00,0x07,0x4f,0x78,0x78,0x4f,0x07,0x00,0x00,
- 0x47,0x63,0x71,0x59,0x4d,0x67,0x73,0x00,0x00,0x7f,0x7f,0x41,0x41,0x00,0x00,0x00,
- 0x01,0x03,0x06,0x0c,0x18,0x30,0x60,0x00,0x00,0x41,0x41,0x7f,0x7f,0x00,0x00,0x00,
- 0x08,0x0c,0x06,0x03,0x06,0x0c,0x08,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
- 0x00,0x00,0x03,0x07,0x04,0x00,0x00,0x00,0x20,0x74,0x54,0x54,0x3c,0x78,0x40,0x00,
- 0x41,0x7f,0x3f,0x48,0x48,0x78,0x30,0x00,0x38,0x7c,0x44,0x44,0x6c,0x28,0x00,0x00,
- 0x30,0x78,0x48,0x49,0x3f,0x7f,0x40,0x00,0x38,0x7c,0x54,0x54,0x5c,0x18,0x00,0x00,
- 0x48,0x7e,0x7f,0x49,0x03,0x06,0x00,0x00,0x98,0xbc,0xa4,0xa4,0xf8,0x7c,0x04,0x00,
- 0x41,0x7f,0x7f,0x08,0x04,0x7c,0x78,0x00,0x00,0x44,0x7d,0x7d,0x40,0x00,0x00,0x00,
- 0x60,0xe0,0x80,0x84,0xfd,0x7d,0x00,0x00,0x41,0x7f,0x7f,0x10,0x38,0x6c,0x44,0x00,
- 0x00,0x41,0x7f,0x7f,0x40,0x00,0x00,0x00,0x7c,0x7c,0x18,0x78,0x1c,0x7c,0x78,0x00,
- 0x7c,0x78,0x04,0x04,0x7c,0x78,0x00,0x00,0x38,0x7c,0x44,0x44,0x7c,0x38,0x00,0x00,
- 0x84,0xfc,0xf8,0xa4,0x24,0x3c,0x18,0x00,0x18,0x3c,0x24,0xa4,0xf8,0xfc,0x84,0x00,
- 0x44,0x7c,0x78,0x4c,0x04,0x0c,0x18,0x00,0x48,0x5c,0x54,0x74,0x64,0x24,0x00,0x00,
- 0x04,0x04,0x3e,0x7f,0x44,0x24,0x00,0x00,0x3c,0x7c,0x40,0x40,0x3c,0x7c,0x40,0x00,
- 0x1c,0x3c,0x60,0x60,0x3c,0x1c,0x00,0x00,0x3c,0x7c,0x60,0x30,0x60,0x7c,0x3c,0x00,
- 0x44,0x6c,0x38,0x10,0x38,0x6c,0x44,0x00,0x9c,0xbc,0xa0,0xa0,0xfc,0x7c,0x00,0x00,
- 0x4c,0x64,0x74,0x5c,0x4c,0x64,0x00,0x00,0x08,0x08,0x3e,0x77,0x41,0x41,0x00,0x00,
- 0x00,0x00,0x00,0x77,0x77,0x00,0x00,0x00,0x41,0x41,0x77,0x3e,0x08,0x08,0x00,0x00,
- 0x02,0x03,0x01,0x03,0x02,0x03,0x01,0x00,0x70,0x78,0x4c,0x46,0x4c,0x78,0x70,0x00};
- // 5x7 font (in 6x8 cell)
- const uint8_t ucSmallFont[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x45,0x51,0x45,0x3e,0x00,0x3e,0x6b,0x6f,
- 0x6b,0x3e,0x00,0x1c,0x3e,0x7c,0x3e,0x1c,0x00,0x18,0x3c,0x7e,0x3c,0x18,0x00,0x30,
- 0x36,0x7f,0x36,0x30,0x00,0x18,0x5c,0x7e,0x5c,0x18,0x00,0x00,0x18,0x18,0x00,0x00,
- 0x00,0xff,0xe7,0xe7,0xff,0xff,0x00,0x3c,0x24,0x24,0x3c,0x00,0x00,0xc3,0xdb,0xdb,
- 0xc3,0xff,0x00,0x30,0x48,0x4a,0x36,0x0e,0x00,0x06,0x29,0x79,0x29,0x06,0x00,0x60,
- 0x70,0x3f,0x02,0x04,0x00,0x60,0x7e,0x0a,0x35,0x3f,0x00,0x2a,0x1c,0x36,0x1c,0x2a,
- 0x00,0x00,0x7f,0x3e,0x1c,0x08,0x00,0x08,0x1c,0x3e,0x7f,0x00,0x00,0x14,0x36,0x7f,
- 0x36,0x14,0x00,0x00,0x5f,0x00,0x5f,0x00,0x00,0x06,0x09,0x7f,0x01,0x7f,0x00,0x22,
- 0x4d,0x55,0x59,0x22,0x00,0x60,0x60,0x60,0x60,0x00,0x00,0x14,0xb6,0xff,0xb6,0x14,
- 0x00,0x04,0x06,0x7f,0x06,0x04,0x00,0x10,0x30,0x7f,0x30,0x10,0x00,0x08,0x08,0x3e,
- 0x1c,0x08,0x00,0x08,0x1c,0x3e,0x08,0x08,0x00,0x78,0x40,0x40,0x40,0x40,0x00,0x08,
- 0x3e,0x08,0x3e,0x08,0x00,0x30,0x3c,0x3f,0x3c,0x30,0x00,0x03,0x0f,0x3f,0x0f,0x03,
- 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x5f,0x06,0x00,0x00,0x07,0x03,0x00,
- 0x07,0x03,0x00,0x24,0x7e,0x24,0x7e,0x24,0x00,0x24,0x2b,0x6a,0x12,0x00,0x00,0x63,
- 0x13,0x08,0x64,0x63,0x00,0x36,0x49,0x56,0x20,0x50,0x00,0x00,0x07,0x03,0x00,0x00,
- 0x00,0x00,0x3e,0x41,0x00,0x00,0x00,0x00,0x41,0x3e,0x00,0x00,0x00,0x08,0x3e,0x1c,
- 0x3e,0x08,0x00,0x08,0x08,0x3e,0x08,0x08,0x00,0x00,0xe0,0x60,0x00,0x00,0x00,0x08,
- 0x08,0x08,0x08,0x08,0x00,0x00,0x60,0x60,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,
- 0x00,0x3e,0x51,0x49,0x45,0x3e,0x00,0x00,0x42,0x7f,0x40,0x00,0x00,0x62,0x51,0x49,
- 0x49,0x46,0x00,0x22,0x49,0x49,0x49,0x36,0x00,0x18,0x14,0x12,0x7f,0x10,0x00,0x2f,
- 0x49,0x49,0x49,0x31,0x00,0x3c,0x4a,0x49,0x49,0x30,0x00,0x01,0x71,0x09,0x05,0x03,
- 0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x06,0x49,0x49,0x29,0x1e,0x00,0x00,0x6c,0x6c,
- 0x00,0x00,0x00,0x00,0xec,0x6c,0x00,0x00,0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x24,
- 0x24,0x24,0x24,0x24,0x00,0x00,0x41,0x22,0x14,0x08,0x00,0x02,0x01,0x59,0x09,0x06,
- 0x00,0x3e,0x41,0x5d,0x55,0x1e,0x00,0x7e,0x11,0x11,0x11,0x7e,0x00,0x7f,0x49,0x49,
- 0x49,0x36,0x00,0x3e,0x41,0x41,0x41,0x22,0x00,0x7f,0x41,0x41,0x41,0x3e,0x00,0x7f,
- 0x49,0x49,0x49,0x41,0x00,0x7f,0x09,0x09,0x09,0x01,0x00,0x3e,0x41,0x49,0x49,0x7a,
- 0x00,0x7f,0x08,0x08,0x08,0x7f,0x00,0x00,0x41,0x7f,0x41,0x00,0x00,0x30,0x40,0x40,
- 0x40,0x3f,0x00,0x7f,0x08,0x14,0x22,0x41,0x00,0x7f,0x40,0x40,0x40,0x40,0x00,0x7f,
- 0x02,0x04,0x02,0x7f,0x00,0x7f,0x02,0x04,0x08,0x7f,0x00,0x3e,0x41,0x41,0x41,0x3e,
- 0x00,0x7f,0x09,0x09,0x09,0x06,0x00,0x3e,0x41,0x51,0x21,0x5e,0x00,0x7f,0x09,0x09,
- 0x19,0x66,0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x01,0x01,0x7f,0x01,0x01,0x00,0x3f,
- 0x40,0x40,0x40,0x3f,0x00,0x1f,0x20,0x40,0x20,0x1f,0x00,0x3f,0x40,0x3c,0x40,0x3f,
- 0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x07,0x08,0x70,0x08,0x07,0x00,0x71,0x49,0x45,
- 0x43,0x00,0x00,0x00,0x7f,0x41,0x41,0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00,
- 0x41,0x41,0x7f,0x00,0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x80,0x80,0x80,0x80,0x80,
- 0x00,0x00,0x03,0x07,0x00,0x00,0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x7f,0x44,0x44,
- 0x44,0x38,0x00,0x38,0x44,0x44,0x44,0x28,0x00,0x38,0x44,0x44,0x44,0x7f,0x00,0x38,
- 0x54,0x54,0x54,0x08,0x00,0x08,0x7e,0x09,0x09,0x00,0x00,0x18,0xa4,0xa4,0xa4,0x7c,
- 0x00,0x7f,0x04,0x04,0x78,0x00,0x00,0x00,0x00,0x7d,0x40,0x00,0x00,0x40,0x80,0x84,
- 0x7d,0x00,0x00,0x7f,0x10,0x28,0x44,0x00,0x00,0x00,0x00,0x7f,0x40,0x00,0x00,0x7c,
- 0x04,0x18,0x04,0x78,0x00,0x7c,0x04,0x04,0x78,0x00,0x00,0x38,0x44,0x44,0x44,0x38,
- 0x00,0xfc,0x44,0x44,0x44,0x38,0x00,0x38,0x44,0x44,0x44,0xfc,0x00,0x44,0x78,0x44,
- 0x04,0x08,0x00,0x08,0x54,0x54,0x54,0x20,0x00,0x04,0x3e,0x44,0x24,0x00,0x00,0x3c,
- 0x40,0x20,0x7c,0x00,0x00,0x1c,0x20,0x40,0x20,0x1c,0x00,0x3c,0x60,0x30,0x60,0x3c,
- 0x00,0x6c,0x10,0x10,0x6c,0x00,0x00,0x9c,0xa0,0x60,0x3c,0x00,0x00,0x64,0x54,0x54,
- 0x4c,0x00,0x00,0x08,0x3e,0x41,0x41,0x00,0x00,0x00,0x00,0x77,0x00,0x00,0x00,0x00,
- 0x41,0x41,0x3e,0x08,0x00,0x02,0x01,0x02,0x01,0x00,0x00,0x3c,0x26,0x23,0x26,0x3c};
- static uint8_t iNumControllers, iCSPin;
- static int file_spi = -1;
- //
- // Transmit a sequence of N x 16 bits to the cascaded controllers
- //
- // Send an atomic sequence of uint8_ts for loading all chained controllers (16 bits per controller)
- // Each controller acts like a 16-bit shift register and passes on the bits to the next controller. The
- // last 16-bits to sit in the controller will be latched when the CS line goes high.
- // uint8_t 0 -> D15-D8 (XXXX A3 A2 A1 A0), 4-bits unused and 4-bit register address
- // uint8_t 1 -> D7-D0 (8-bit data)
- //
- void maxSendSequence(uint8_t *pSequence, uint8_t len)
- {
- // The CS line stays low throughout a "transaction". Send all of the control uint8_ts for all of the chained
- // controllers in a single transaction. When the CS line rises, the data will be latched
- AIOWriteGPIO(iCSPin, 0);
- AIOWriteSPI(file_spi, pSequence, len);
- AIOWriteGPIO(iCSPin, 1);
-
- } /* maxSendSequence() */
- //
- // Power on or off the LED controllers
- //
- void maxPowerUp(uint8_t bPowerUp)
- {
- uint8_t i;
- uint8_t *d, bTemp[32]; // up to 16 controllers
- d = bTemp;
- for (i=0; i<iNumControllers; i++)
- {
- *d++ = 0x0C; // power up/down
- *d++ = bPowerUp;
- }
- maxSendSequence(bTemp, iNumControllers * 2); // send the power up/down instruction
- } /* maxPowerUp() */
- //
- // Set the intensity (duty cycle of PWM signal) for the LED segments
- // valid values are 0 (dimmest) to 15 (brightest)
- //
- void maxSetIntensity(uint8_t bIntensity)
- {
- uint8_t *d, bTemp[32];
- uint8_t i;
- d = bTemp;
- for (i=0; i<iNumControllers; i++)
- {
- *d++ = 0x0A; // set intensity
- *d++ = bIntensity;
- } // for i
- maxSendSequence(bTemp, iNumControllers * 2);
- } /* maxSetIntensity() */
- //
- // Set the segment decode mode (BCD or none)
- //
- void maxSetSegmentMode(uint8_t bMode)
- {
- uint8_t i, *d, bTemp[32];
- d = bTemp;
- for (i=0; i<iNumControllers; i++)
- {
- *d++ = 0x09; // decode mode
- *d++ = (bMode) ? 0xff : 0x00;
- } // for i
- maxSendSequence(bTemp, iNumControllers * 2); // send the scan limit instructions to all controllers
- } /* maxSetSegmentMode() */
- static unsigned char lookup[16] = {
- 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
- 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf, };
- uint8_t reverse(uint8_t n) {
- // Reverse the top and bottom nibble then swap them.
- return (lookup[n&0b1111] << 4) | lookup[n>>4];
- }
- //
- // Send image data to the array of controllers
- // The image data is transmitted as N by 8 lines tall (N is the number of MAX7219 controllers)
- // The pitch (uint8_ts per line) can be any value
- //
- void maxSendImage(uint8_t *pImage, int iPitch, uint8_t rotated)
- {
- uint8_t i, j;
- uint8_t *s, *d, bTemp[32];
- if(rotated) {
- for (j=0; j<8; j++) // 8 rows to transmit
- {
- s = &pImage[iPitch * (j+1) - 1];
- d = bTemp;
- for (i=0; i<iNumControllers; i++)
- {
- *d++ = (8-j); // row number is the "instruction"
- *d++ = reverse(*s--); // image data
- } // for each controller
- maxSendSequence(bTemp, iNumControllers * 2);
- } // for each row of image
- } else {
- for (j=0; j<8; j++) // 8 rows to transmit
- {
- s = &pImage[iPitch * j];
- d = bTemp;
- for (i=0; i<iNumControllers; i++)
- {
- *d++ = (j+1); // row number is the "instruction"
- *d++ = *s++; // image data
- } // for each controller
- maxSendSequence(bTemp, iNumControllers * 2);
- } // for each row of image
- }
- } /* maxSendImage() */
- //
- // Enable (1) or disable (0) test mode
- // This mode lights up every LED at max brightness
- // It can sometimes power up in test mode
- //
- void maxSetTestMode(uint8_t bOn)
- {
- uint8_t i, *d, bTemp[32];
- d = bTemp;
- for (i=0; i<iNumControllers; i++)
- {
- *d++ = 0x0F; // test mode
- *d++ = bOn;
- } // for i
- maxSendSequence(bTemp, iNumControllers * 2); // send the scan limit instructions to all controllers
- } /* maxSetTestMode() */
- //
- // Number of "digits/rows" to control
- // valid values are 1-8 active digits/rows
- //
- void maxSetLimit(uint8_t bLimit)
- {
- uint8_t i, *d, bTemp[32];
- d = bTemp;
- for (i=0; i<iNumControllers; i++)
- {
- *d++ = 0x0B; // set scan limit
- *d++ = (bLimit - 1);
- } // for i
- maxSendSequence(bTemp, iNumControllers * 2); // send the scan limit instructions to all controllers
- } /* maxSetLimit() */
- //
- // Send an ASCII string of numbers/spaces/decimal points
- // to a 7-segment display
- //
- void maxSegmentString(char *pString)
- {
- unsigned char ucTemp[4];
- int iDigit;
- memset(ucTemp, 0, sizeof(ucTemp));
- iDigit = 0;
- while (*pString && iDigit < 8)
- {
- ucTemp[0] = 8 - (iDigit & 7); // cmd byte to write
- if (pString[0] >= '0' && pString[0] <= '9')
- {
- ucTemp[1] = *pString++; // store digit
- if (pString[0] == '.')
- {
- ucTemp[1] |= 0x80; // turn on decimal point
- pString++;
- }
- }
- else
- {
- ucTemp[1] = 0xf; // space = all segments off
- pString++;
- }
- iDigit++;
- maxSendSequence(ucTemp, 2); // need to latch each byte pair
- }
- while (iDigit < 8) // blank out remaining digits
- {
- ucTemp[0] = 8 - (iDigit & 7);
- ucTemp[1] = 0xf; // all segments off
- iDigit++;
- maxSendSequence(ucTemp, 2);
- }
- } /* maxSegmentString() */
- //
- // Draw a string of characters into the image buffer
- // Normal characters are 8x8 and drawn on uint8_t boundaries
- // Small characters are 6x8 and drawn on bit boundaries
- //
- void maxDrawString(char *pString, uint8_t *pImage, uint8_t iPitch, uint8_t bSmall)
- {
- uint8_t b, bMask, i, j, *d, bCol;
- const uint8_t *pFont;
- const uint8_t *s;
- int iWidth;
- d = pImage;
- bCol = 0;
- if (bSmall)
- {
- pFont = ucSmallFont;
- iWidth = 6;
- }
- else
- {
- pFont = ucFont;
- iWidth = 8;
- }
- while (*pString)
- {
- b = *pString++;
- s = &pFont[(int)b * iWidth]; // 6 or 8 uint8_ts per character in ASCII order
- for (i=0; i<iWidth; i++) // column
- {
- bMask = (0x80 >> (bCol & 7));
- b = *s++; // current font uint8_t
- for (j=0; j<8; j++) // bit number of source becomes destination row
- {
- if (b & 1) // start from LSB
- d[j*iPitch] |= bMask;
- else
- d[j*iPitch] &= ~bMask;
- b >>= 1; // shift down font uint8_t
- } // for j
- bCol++;
- if ((bCol & 7) == 0) // next uint8_t
- d++;
- } // for i
- } // while string
- } /* maxDrawString() */
- //
- // Scroll a bitmap N bits left (positive) or right (negative)
- // Valid scroll values are +1 to +7 and -1 to -7
- // A bitmap is assumed to be iPitch uint8_ts wide by 8 rows tall
- // Bits which scroll off one end are added back to the other end
- //
- void maxScrollBitmap(uint8_t *pBitmap, int iPitch, int iScroll)
- {
- uint8_t b, bEdge, *s;
- int col, row;
- if (iScroll > 0) // scroll left
- {
- for (row=0; row<8; row++)
- {
- s = &pBitmap[row * iPitch];
- bEdge = s[0] >> (8-iScroll);
- for (col=0; col<iPitch; col++)
- {
- b = s[col] << iScroll;
- b |= (col == iPitch-1) ? bEdge : (s[col+1] >> (8-iScroll));
- s[col] = b;
- } // for col
- } // for row
- }
- else // scroll right
- {
- iScroll = 0 - iScroll; // make it a positive number
- for (row=0; row<8; row++)
- {
- s = &pBitmap[row * iPitch];
- bEdge = s[iPitch-1] << (8-iScroll);
- for (col=iPitch-1; col>=0; col--)
- {
- b = s[col] >> iScroll;
- b |= (col == 0) ? bEdge : (s[col-1] << (8-iScroll));
- s[col] = b;
- } // for col
- } // for row
- }
- } /* maxScrollBitmap() */
- //
- // Initialize the controllers
- //
- int maxInit(uint8_t iNum, uint8_t bDecodeMode, uint8_t iChannel, uint8_t iSelect)
- {
- if (!AIOInit())
- {
- fprintf(stderr, "Error initializing ArmbianIO library\n");
- return -1;
- }
- iNumControllers = iNum;
- iCSPin = iSelect; // header pin number used for select line
- file_spi = AIOOpenSPI(iChannel, 2000000); // 2Mhz is a reasonable speed
- if (file_spi == -1)
- {
- fprintf(stderr, "Failed to open the SPI bus\n");
- file_spi = -1;
- return -1;
- }
- AIOAddGPIO(iCSPin, GPIO_OUT);
- AIOWriteGPIO(iCSPin, 1);
- maxPowerUp(1); // turn on the LED controllers
- maxSetLimit(8); // tell it to display 8 rows of 8 pixels
- maxSetIntensity(0); // set the minimum intensity to start (1/32 duty cycle)
- maxSetSegmentMode(bDecodeMode); // sets BCD (7-segment), or none (pixels
- maxSetTestMode(0); // disable test mode (it can accidentally get set at power up)
- return 0;
- } /* maxInit() */
- void maxShutdown(void)
- {
- maxPowerUp(0); // turn off the LED controllers
- AIOCloseSPI(file_spi);
- file_spi = -1;
- AIOShutdown();
- } /* maxShutdown() */
|