GCC Code Coverage Report


Directory: ./
File: channeloutput/MAX7219Matrix.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 82 88 93.2%
Functions: 11 11 100.0%
Branches: 43 86 50.0%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the GPL v2 as described in the
10 * included LICENSE.GPL file.
11 */
12
13 /*
14 * This Channel Output is for SPI-attached MAX7219 chips connected to
15 * 8x8 LED panels. The chips/panels may be daisy-chained to provide
16 * wider displays.
17 *
18 * Sample channeloutputs.json config
19 *
20 * {
21 * "channelOutputs": [
22 * {
23 * "type": "MAX7219Matrix",
24 * "enabled": 1,
25 * "startChannel": 1,
26 * "channelCount": 128,
27 * "panels": 2,
28 * "channelsPerPixel": 1
29 * }
30 * ]
31 * }
32 *
33 */
34 #include "fpp-pch.h"
35
36 #include "fpp-json.h"
37
38 #include <unistd.h>
39
40 #include "../log.h"
41
42 #include "MAX7219Matrix.h"
43
44 #define MAX7219_DECODE_MODE 0x09
45 #define MAX7219_SHUTDOWN 0x0C
46 #define MAX7219_BRIGHTNESS 0x0A
47 #define MAX7219_SCAN_LIMIT 0x0B
48 #define MAX7219_TEST 0x0F
49
50 #include "Plugin.h"
51 class MAX7219MatrixPlugin : public FPPPlugins::Plugin, public FPPPlugins::ChannelOutputPlugin {
52 public:
53 3 MAX7219MatrixPlugin() :
54
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
9 FPPPlugins::Plugin("MAX7219Matrix") {
55 3 }
56 3 virtual ChannelOutput* createChannelOutput(unsigned int startChannel, unsigned int channelCount) override {
57
2/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
6 return new MAX7219MatrixOutput(startChannel, channelCount);
58 }
59 };
60
61 extern "C" {
62 3 FPPPlugins::Plugin* createPlugin() {
63
2/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
6 return new MAX7219MatrixPlugin();
64 }
65 }
66
67 /*
68 *
69 */
70 3 MAX7219MatrixOutput::MAX7219MatrixOutput(unsigned int startChannel, unsigned int channelCount) :
71 ChannelOutput(startChannel, channelCount),
72 3 m_panels(1),
73 3 m_channelsPerPixel(1),
74 3 m_pinCS(8),
75 3 m_csPin(nullptr),
76 3 m_spi(nullptr) {
77
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 LogDebug(VB_CHANNELOUT, "MAX7219MatrixOutput::MAX7219MatrixOutput(%u, %u)\n",
78 startChannel, channelCount);
79
80
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 m_csPin = PinCapabilities::getPinByGPIO(0, 8).ptr();
81 3 }
82
83 /*
84 *
85 */
86 12 MAX7219MatrixOutput::~MAX7219MatrixOutput() {
87 6 LogDebug(VB_CHANNELOUT, "MAX7219MatrixOutput::~MAX7219MatrixOutput()\n");
88
89 6 Close();
90
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 if (m_spi) {
91
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
6 delete m_spi;
92 }
93 12 }
94
95 /*
96 *
97 */
98 3 int MAX7219MatrixOutput::Init(Json::Value config) {
99 3 LogDebug(VB_CHANNELOUT, "MAX7219MatrixOutput::Init(JSON)\n");
100
101
2/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
3 m_spi = new SPIUtils(0, 1000000);
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!m_spi->isOk()) {
103 ✗ LogErr(VB_CHANNELOUT, "SPIUtils setup failed\n");
104 ✗ return 0;
105 }
106
107 3 m_panels = config["panels"].asInt();
108 3 m_channelsPerPixel = config["channelsPerPixel"].asInt();
109 // SendData() builds each line in a 256 byte stack buffer, two bytes a
110 // panel, and reads 64 pixels a panel from the channel data: more than
111 // 128 panels overflowed the stack, and a short channel count over-read.
112
4/8
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
3 if (m_panels < 1 || m_panels > 128 || (m_channelsPerPixel != 1 && m_channelsPerPixel != 3)) {
113 1 LogErr(VB_CHANNELOUT, "MAX7219: invalid config, %d panels (1-128) with %d channels per pixel (1 or 3)\n",
114 m_panels, m_channelsPerPixel);
115 1 return 0;
116 }
117
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (m_channelCount < m_panels * 64 * m_channelsPerPixel) {
118 1 LogErr(VB_CHANNELOUT, "MAX7219: %d panels need %d channels, the output has %d\n",
119 m_panels, m_panels * 64 * m_channelsPerPixel, m_channelCount);
120 1 return 0;
121 }
122
123
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
4 m_csPin->configPin("gpio", true);
124 1 m_usedGPIOS.push_back(m_csPin->name);
125
126 1 usleep(50000);
127
128 1 WriteCommand(MAX7219_DECODE_MODE, 0x00); // Use LED Matrix, not BCD Mode
129 1 WriteCommand(MAX7219_BRIGHTNESS, 0x00); // Lowest brightness
130 1 WriteCommand(MAX7219_SCAN_LIMIT, 0x07); // Use all 8 columns
131 1 WriteCommand(MAX7219_SHUTDOWN, 0x01); // (1 == On, 0 == Off)
132 1 WriteCommand(MAX7219_TEST, 0x00); // Turn Off Test mode
133
134
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 return ChannelOutput::Init(config);
135 }
136
137 2 void MAX7219MatrixOutput::GetRequiredChannelRanges(const std::function<void(int, int)>& addRange) {
138 2 addRange(m_startChannel, m_startChannel + m_channelCount - 1);
139 2 }
140
141 /*
142 *
143 */
144 4 int MAX7219MatrixOutput::Close(void) {
145 4 LogDebug(VB_CHANNELOUT, "MAX7219MatrixOutput::Close()\n");
146
147 4 return ChannelOutput::Close();
148 }
149
150 /*
151 *
152 */
153 5 void MAX7219MatrixOutput::WriteCommand(uint8_t cmd, uint8_t value) {
154 uint8_t c;
155 uint8_t v;
156 uint8_t data[256];
157 5 int bytes = 0;
158
159
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
10 for (int p = 0; p < m_panels; p++) {
160 5 data[bytes++] = cmd;
161 5 data[bytes++] = value;
162 }
163
164
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 m_csPin->setValue(0);
165
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 m_spi->xfer(data, nullptr, bytes);
166
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 m_csPin->setValue(1);
167 5 }
168
169 /*
170 *
171 */
172 4 int MAX7219MatrixOutput::SendData(unsigned char* channelData) {
173
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 LogExcess(VB_CHANNELOUT, "MAX7219MatrixOutput::SendData(%p)\n",
174 channelData);
175
176 uint8_t data[256];
177
178 // Line by line, send data starting with the last panel first and
179 // left most pixel on a panel is MSB
180 4 int c = 0;
181
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
36 for (int i = 1; i < 9; i++) {
182 32 int bytes = 0;
183 32 int offset = m_panels * 8 * m_channelsPerPixel;
184
185
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 32 times.
64 for (int p = 0; p < m_panels; p++) {
186 32 data[bytes++] = i;
187
188 32 uint8_t ch = 0;
189
2/2
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 32 times.
288 for (int j = 0; j < 8; j++) {
190
1/2
✓ Branch 0 taken 256 times.
✗ Branch 1 not taken.
256 if (m_channelsPerPixel == 1) {
191
2/2
✓ Branch 0 taken 128 times.
✓ Branch 1 taken 128 times.
256 if (channelData[c + offset - 1])
192 128 ch |= 0x01 << j;
193 } else { // 3 channels per pixel
194 ✗ if ((channelData[c + offset - 1]) ||
195 ✗ (channelData[c + offset - 2]) ||
196 ✗ (channelData[c + offset - 3]))
197 ✗ ch |= 0x01 << j;
198 }
199 256 offset -= m_channelsPerPixel;
200 }
201 32 data[bytes++] = ch;
202 }
203
204
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 m_csPin->setValue(0);
205
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 m_spi->xfer(data, nullptr, bytes);
206
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 m_csPin->setValue(1);
207
208 32 c += m_panels * 8 * m_channelsPerPixel;
209 }
210
211 4 return m_channelCount;
212 }
213
214 /*
215 *
216 */
217 1 void MAX7219MatrixOutput::DumpConfig(void) {
218 1 LogDebug(VB_CHANNELOUT, "MAX7219MatrixOutput::DumpConfig()\n");
219
220 1 ChannelOutput::DumpConfig();
221 1 }
222