GCC Code Coverage Report


Directory: ./
File: util/MCP23x17Utils.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 92 112 82.1%
Functions: 12 13 92.3%
Branches: 49 112 43.8%

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 #include "fpp-pch.h"
14
15 #include <fcntl.h>
16
17 #include "MCP23x17Utils.h"
18 #include "SPIUtils.h"
19
20 // SPI Command codes
21 #define CMD_WRITE 0x40
22 #define CMD_READ 0x41
23
24 // MCP23x17 Registers
25 #define MCP23x17_IODIRA 0x00
26 #define MCP23x17_IPOLA 0x02
27 #define MCP23x17_GPINTENA 0x04
28 #define MCP23x17_DEFVALA 0x06
29 #define MCP23x17_INTCONA 0x08
30 #define MCP23x17_IOCON 0x0A
31 #define MCP23x17_GPPUA 0x0C
32 #define MCP23x17_INTFA 0x0E
33 #define MCP23x17_INTCAPA 0x10
34 #define MCP23x17_GPIOA 0x12
35 #define MCP23x17_OLATA 0x14
36
37 #define MCP23x17_IODIRB 0x01
38 #define MCP23x17_IPOLB 0x03
39 #define MCP23x17_GPINTENB 0x05
40 #define MCP23x17_DEFVALB 0x07
41 #define MCP23x17_INTCONB 0x09
42 #define MCP23x17_IOCONB 0x0B
43 #define MCP23x17_GPPUB 0x0D
44 #define MCP23x17_INTFB 0x0F
45 #define MCP23x17_INTCAPB 0x11
46 #define MCP23x17_GPIOB 0x13
47 #define MCP23x17_OLATB 0x15
48
49 // Bits in the IOCON register
50 #define IOCON_UNUSED 0x01
51 #define IOCON_INTPOL 0x02
52 #define IOCON_ODR 0x04
53 #define IOCON_HAEN 0x08
54 #define IOCON_DISSLW 0x10
55 #define IOCON_SEQOP 0x20
56 #define IOCON_MIRROR 0x40
57 #define IOCON_BANK_MODE 0x80
58
59 // Default initialisation mode
60 #define IOCON_INIT (IOCON_SEQOP)
61
62 static SPIUtils* MCP23x17_SPI = nullptr;
63 static uint8_t status_port_a;
64 static uint8_t status_port_b;
65
66 142 static int writeByte(uint8_t reg, uint8_t data, uint8_t devId = 0) {
67
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 if (MCP23x17_SPI) {
68 uint8_t spiData[4];
69
70 142 spiData[0] = CMD_WRITE | ((devId & 7) << 1);
71 142 spiData[1] = reg;
72 142 spiData[2] = data;
73
1/2
✓ Branch 0 taken 142 times.
✗ Branch 1 not taken.
142 return MCP23x17_SPI->xfer(spiData, spiData, 3);
74 }
75 ✗ return -1;
76 }
77 146 static int32_t readByte(uint8_t reg, uint8_t devId = 0) {
78
1/2
✓ Branch 0 taken 146 times.
✗ Branch 1 not taken.
146 if (MCP23x17_SPI) {
79 146 uint8_t spiData[4] = { 0, 0, 0, 0 };
80 146 spiData[0] = CMD_READ | ((devId & 7) << 1);
81 146 spiData[1] = reg;
82
1/2
✓ Branch 0 taken 146 times.
✗ Branch 1 not taken.
146 int i = MCP23x17_SPI->xfer(spiData, spiData, 3);
83
2/2
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 28 times.
146 if (i == -1) {
84 118 return -1;
85 }
86 28 return spiData[2];
87 }
88 ✗ return -1;
89 }
90
91 69 MCP23x17PinCapabilities::MCP23x17PinCapabilities(const std::string& n, uint32_t kg, int chip) :
92 69 PinCapabilitiesFluent(n, kg) {
93 69 gpioIdx = chip;
94 69 }
95
96 4 int MCP23x17PinCapabilities::configPin(const std::string& mode,
97 bool directionOut,
98 const std::string& desc) const {
99
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
4 if (mode == "pwm" || mode == "uart") {
100 ✗ return 0;
101 }
102 4 int pin = this->gpio;
103 4 int reg = (pin < 8) ? MCP23x17_IODIRA : MCP23x17_IODIRB;
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 int pureg = (pin < 8) ? MCP23x17_GPPUA : MCP23x17_GPPUB;
105
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (pin > 7) {
106 4 pin &= 0x07;
107 }
108 4 int mask = 1 << pin;
109 4 int old = readByte(reg);
110
111
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (directionOut) {
112 4 old &= (~mask);
113 } else {
114 ✗ old |= mask;
115 }
116 4 writeByte(reg, old);
117
118 4 old = readByte(pureg);
119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (mode == "gpio_pu") {
120 ✗ old |= mask;
121 } else {
122 4 old &= (~mask);
123 }
124 4 writeByte(pureg, old);
125 4 return 0;
126 }
127
128 12 bool MCP23x17PinCapabilities::getValue() const {
129 12 int pin = this->gpio;
130
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 int gpio = (pin < 8) ? MCP23x17_GPIOA : MCP23x17_GPIOB;
131
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (pin > 7) {
132 12 pin &= 0x07;
133 }
134 12 int mask = 1 << pin;
135 12 int value = readByte(gpio);
136 12 return ((value & mask) != 0);
137 }
138 8 void MCP23x17PinCapabilities::setValue(bool i) const {
139 8 int pin = this->gpio;
140 8 int bit = 1 << (pin & 7);
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 int old = (pin < 8) ? status_port_a : status_port_b;
142
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 if (i == 0) {
143 4 old &= (~bit);
144 } else {
145 4 old |= bit;
146 }
147
148
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (pin < 8) {
149 ✗ writeByte(MCP23x17_GPIOA, old);
150 ✗ status_port_a = old;
151 } else {
152 8 writeByte(MCP23x17_GPIOB, old);
153 8 status_port_b = old;
154 }
155 8 }
156
157 class NullMCP23x17GPIOPinCapabilities : public MCP23x17PinCapabilities {
158 public:
159 65 NullMCP23x17GPIOPinCapabilities() :
160
2/4
✓ Branch 0 taken 65 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 65 times.
✗ Branch 3 not taken.
195 MCP23x17PinCapabilities("-none-", 0, 0) {}
161 75 virtual const PinCapabilities* ptr() const override { return nullptr; }
162 };
163 static NullMCP23x17GPIOPinCapabilities NULL_WP_INSTANCE;
164
165 static std::vector<MCP23x17PinCapabilities> MCP23x17_PINS;
166
167 63 void MCP23x17PinCapabilities::Init(int chip) {
168
2/6
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 63 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
63 MCP23x17_SPI = new SPIUtils(0, 4000000);
169
1/2
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
63 if (MCP23x17_SPI->isOk()) {
170
1/2
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
63 writeByte(MCP23x17_IOCON, IOCON_INIT | IOCON_HAEN);
171
1/2
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
63 writeByte(MCP23x17_IOCONB, IOCON_INIT | IOCON_HAEN);
172
173
1/2
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
63 int iA = readByte(MCP23x17_OLATA);
174
1/2
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
63 int iB = readByte(MCP23x17_OLATB);
175
176
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
63 if (iA == -1 || iB == -1) {
177
1/2
✓ Branch 0 taken 59 times.
✗ Branch 1 not taken.
59 delete MCP23x17_SPI;
178 59 MCP23x17_SPI = nullptr;
179 63 return;
180 }
181 4 status_port_a = iA;
182 4 status_port_b = iB;
183
184
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
8 MCP23x17PinCapabilities testPin("MCP23x17-1", chip, 0);
185
3/6
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
12 testPin.configPin("gpio");
186
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 int a = testPin.getValue();
187
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 testPin.setValue(1);
188
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 int b = testPin.getValue();
189
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 testPin.setValue(0);
190
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 int c = testPin.getValue();
191
192
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 if (a == b && b == c) {
193 // could not change the value, assume no PiFace found
194
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 delete MCP23x17_SPI;
195 4 MCP23x17_SPI = nullptr;
196 4 return;
197 }
198
199 ✗ for (int x = 0; x < 16; x++) {
200 ✗ std::string name = "MCP23x17-" + std::to_string(x + 1);
201 ✗ MCP23x17_PINS.push_back(MCP23x17PinCapabilities(name, x, chip));
202 ✗ }
203
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 } else {
204 ✗ delete MCP23x17_SPI;
205 ✗ MCP23x17_SPI = nullptr;
206 }
207 }
208 56 void MCP23x17PinCapabilities::getPinNames(std::vector<std::string>& ret) {
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
56 for (auto& a : MCP23x17_PINS) {
210 ✗ ret.push_back(a.name);
211 }
212 56 }
213
214 12 const PinCapabilities& MCP23x17PinCapabilities::getPinByName(const std::string& name) {
215
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 for (auto& a : MCP23x17_PINS) {
216 ✗ if (a.name == name) {
217 ✗ return a;
218 }
219 }
220 12 return NULL_WP_INSTANCE;
221 }
222 63 const PinCapabilities& MCP23x17PinCapabilities::getPinByGPIO(int chip, int gpio) {
223
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 for (auto& a : MCP23x17_PINS) {
224 ✗ if (a.gpio == gpio && a.gpioIdx == chip) {
225 ✗ return a;
226 }
227 }
228 63 return NULL_WP_INSTANCE;
229 }
230
231 ✗ const PinCapabilities& MCP23x17PinCapabilities::getPinByUART(const std::string& n) {
232 ✗ return NULL_WP_INSTANCE;
233 }
234