Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
Charlieplex.hxx
Go to the documentation of this file.
1
34#ifndef _UTILS_CHARLIEPLEXING_HXX_
35#define _UTILS_CHARLIEPLEXING_HXX_
36
37#include "os/Gpio.hxx"
38#include "utils/Fixed16.hxx"
40
41template <unsigned N> struct CharlieplexHelper;
42
44template <> struct CharlieplexHelper<3>
45{
47 static const uint8_t pinlist[];
49 static unsigned num_bits()
50 {
51 return 6;
52 }
53
56 static unsigned pin_high(unsigned bit)
57 {
58 return pinlist[bit << 1];
59 }
62 static unsigned pin_low(unsigned bit)
63 {
64 return pinlist[(bit << 1) | 1];
65 }
66};
67
69 0, 2, //
70 0, 1, //
71 1, 2, //
72 1, 0, //
73 2, 1, //
74 2, 0, //
75};
76
78template <> struct CharlieplexHelper<4>
79{
81 static const uint8_t pinlist[];
83 static constexpr unsigned num_bits()
84 {
85 return 12;
86 }
87
90 static unsigned pin_high(unsigned bit)
91 {
92 return pinlist[bit << 1];
93 }
96 static unsigned pin_low(unsigned bit)
97 {
98 return pinlist[(bit << 1) | 1];
99 }
100};
101
103 3, 0, //
104 2, 0, //
105 1, 0, //
106 3, 1, //
107 3, 2, //
108 2, 1, //
109 2, 3, //
110 1, 2, //
111 0, 2, //
112 0, 3, //
113 0, 1, //
114 1, 3, //
115};
116
135template <unsigned N, class helper = CharlieplexHelper<N>> class Charlieplex
136{
137public:
143 Charlieplex(const Gpio *const pins[N])
144 : pins_(pins)
145 , nextBit_(0)
146 , bits_(0)
147 {
148 for (unsigned i = 0; i < N; ++i)
149 {
150 pins_[i]->set_direction(Gpio::Direction::DINPUT);
151 }
152 }
153
157 void tick()
158 {
159 pins_[helper::pin_high(nextBit_)]->set_direction(
160 Gpio::Direction::DINPUT);
161 pins_[helper::pin_low(nextBit_)]->set_direction(Gpio::Direction::DINPUT);
162 nextBit_++;
163 if (nextBit_ >= helper::num_bits()) {
164 nextBit_ = 0;
165 }
166 if (bits_ & (1 << nextBit_))
167 {
168 pins_[helper::pin_high(nextBit_)]->set_direction(
169 Gpio::Direction::DOUTPUT);
170 pins_[helper::pin_high(nextBit_)]->set();
171 pins_[helper::pin_low(nextBit_)]->set_direction(
172 Gpio::Direction::DOUTPUT);
173 pins_[helper::pin_low(nextBit_)]->clr();
174 }
175 }
176
179 unsigned *payload()
180 {
181 return &bits_;
182 }
183
184private:
185 const Gpio *const *pins_;
186 unsigned nextBit_;
187 unsigned bits_;
188};
189
192template <unsigned N, class helper = CharlieplexHelper<N>> class WeightedCharlieplex
193{
194public:
200 WeightedCharlieplex(const Gpio *const pins[helper::num_bits()])
201 : pins_(pins)
202 , nextBit_(0)
203 , bits_(0)
204 {
205 for (unsigned i = 0; i < N; ++i)
206 {
207 pins_[i]->set_direction(Gpio::Direction::DINPUT);
208 }
209 for (unsigned i = 0; i < helper::num_bits(); ++i) {
211 desiredIntensity_[i] = 0;
212 }
213 }
214
218 void tick()
219 {
220 pins_[helper::pin_high(nextBit_)]->set_direction(
221 Gpio::Direction::DINPUT);
222 pins_[helper::pin_low(nextBit_)]->set_direction(Gpio::Direction::DINPUT);
223 nextBit_++;
224 if (nextBit_ >= helper::num_bits()) {
225 nextBit_ = 0;
226 }
227 bool lit = false;
228 if (bits_ & (1 << nextBit_))
229 {
230 lit = true;
231 }
232 else if (desiredIntensity_[nextBit_] > 0)
233 {
234 (*actualIntensity_[nextBit_]) *= EWMA_RATIO;
235 if (actualIntensity_[nextBit_]->round() <
237 {
238 lit = true;
239 (*actualIntensity_[nextBit_]) += EWMA_SUM;
240 }
241 }
242 if (lit)
243 {
244 pins_[helper::pin_high(nextBit_)]->set_direction(
245 Gpio::Direction::DOUTPUT);
246 pins_[helper::pin_high(nextBit_)]->set();
247 pins_[helper::pin_low(nextBit_)]->set_direction(
248 Gpio::Direction::DOUTPUT);
249 pins_[helper::pin_low(nextBit_)]->clr();
250 }
251 }
252
255 unsigned *payload()
256 {
257 return &bits_;
258 }
259
263 {
264 return desiredIntensity_;
265 }
266
267private:
268 static constexpr Fixed16 EWMA_RATIO{0, 0xF000};
269 static constexpr Fixed16 EWMA_SUM{16, 0};
270
271 const Gpio *const *pins_;
272 unsigned nextBit_;
273 unsigned bits_;
274 uint8_t desiredIntensity_[helper::num_bits()];
276};
277
278template<unsigned N, typename helper>
280
281#endif // _UTILS_CHARLIEPLEXING_HXX_
Class that implements a Charlieplexing LED driver, operating N choose 2 output LEDs from N GPIO pins.
unsigned * payload()
unsigned nextBit_
LED that comes next.
const Gpio *const * pins_
array of all GPIO pins to use
unsigned bits_
Desired output state of LEDs.
void tick()
Switches to the next output configuration of the charlieplexing pins.
Charlieplex(const Gpio *const pins[N])
Constructor.
OS-independent abstraction for GPIO.
Definition Gpio.hxx:43
virtual void clr() const =0
Clears the GPIO output pin to low.
virtual void set_direction(Direction dir) const =0
Sets the GPIO direction.
virtual void set() const =0
Sets the GPIO output pin to high.
Variant of the Charlieplex class that allows intensity to be set for each individual pin.
uint8_t desiredIntensity_[helper::num_bits()]
Partial lighting of each output.
const Gpio *const * pins_
array of all GPIO pins to use
unsigned nextBit_
LED that comes next.
void tick()
Switches to the next output configuration of the charlieplexing pins.
uninitialized< Fixed16 > actualIntensity_[helper::num_bits()]
How much we actually lit a given pin.
uint8_t * desired_intensity()
WeightedCharlieplex(const Gpio *const pins[helper::num_bits()])
Constructor.
unsigned bits_
Desired output state of LEDs.
Template class that allows allocating storage for an object but not calling its constructor.
T & emplace(Args &&... args)
Constructs the embedded object.
static unsigned num_bits()
static unsigned pin_low(unsigned bit)
Which pin to drive low for a given LED.
static unsigned pin_high(unsigned bit)
Which pin to drive high for a given LED.
static unsigned pin_low(unsigned bit)
Which pin to drive low for a given LED.
static constexpr unsigned num_bits()
static unsigned pin_high(unsigned bit)
Which pin to drive high for a given LED.