libDaisy
Hardware Library for Daisy
Loading...
Searching...
No Matches
leddriver.h
Go to the documentation of this file.
1#pragma once
2#ifndef SA_LED_DRIVER_H
3#define SA_LED_DRIVER_H
5#ifdef __cplusplus
6
7#include <stdint.h>
8#include "per/i2c.h"
9#include "per/gpio.h"
10
11namespace daisy
12{
31template <int numDrivers, bool persistentBufferContents = true>
33{
34 public:
36 struct __attribute__((packed)) PCA9685TransmitBuffer
37 {
39 uint8_t registerAddr = PCA9685_LED0;
40 struct __attribute__((packed))
41 {
43 uint16_t on;
45 uint16_t off;
46 } leds[16];
48 static constexpr uint16_t size = 16 * 4 + 1;
49 };
51 using DmaBuffer = PCA9685TransmitBuffer[numDrivers];
52
65 void Init(I2CHandle i2c,
66 const uint8_t (&addresses)[numDrivers],
67 DmaBuffer dma_buffer_a,
68 DmaBuffer dma_buffer_b,
69 dsy_gpio_pin oe_pin = {DSY_GPIOX, 0})
70 {
71 i2c_ = i2c;
72 draw_buffer_ = dma_buffer_a;
73 transmit_buffer_ = dma_buffer_b;
74 oe_pin_ = oe_pin;
75 for(int d = 0; d < numDrivers; d++)
76 addresses_[d] = addresses[d];
77 current_driver_idx_ = -1;
78
79 InitializeBuffers();
80 InitializeDrivers();
81 }
82
84 constexpr int GetNumLeds() const { return numDrivers * 16; }
85
87 void SetAllTo(float brightness)
88 {
89 const uint8_t intBrightness
90 = (uint8_t)(clamp(brightness * 255.0f, 0.0f, 255.0f));
91 SetAllTo(intBrightness);
92 }
93
95 void SetAllTo(uint8_t brightness)
96 {
97 const uint16_t cycles = gamma_table_[brightness];
98 SetAllToRaw(cycles);
99 }
100
102 void SetAllToRaw(uint16_t rawBrightness)
103 {
104 for(int led = 0; led < GetNumLeds(); led++)
105 SetLedRaw(led, rawBrightness);
106 }
107
109 void SetLed(int ledIndex, float brightness)
110 {
111 const uint8_t intBrightness
112 = (uint8_t)(clamp(brightness * 255.0f, 0.0f, 255.0f));
113 SetLed(ledIndex, intBrightness);
114 }
115
117 void SetLed(int ledIndex, uint8_t brightness)
118 {
119 const uint16_t cycles = gamma_table_[brightness];
120 SetLedRaw(ledIndex, cycles);
121 }
122
124 void SetLedRaw(int ledIndex, uint16_t rawBrightness)
125 {
126 const auto d = GetDriverForLed(ledIndex);
127 const auto ch = GetDriverChannelForLed(ledIndex);
128 // mask away the "full on" bit
129 const auto on = draw_buffer_[d].leds[ch].on & (0x0FFF);
130 draw_buffer_[d].leds[ch].off = (on + rawBrightness) & (0x0FFF);
131 // full on condition
132 if(rawBrightness >= 0x0FFF)
133 draw_buffer_[d].leds[ch].on = 0x1000 | on; // set "full on" bit
134 else
135 draw_buffer_[d].leds[ch].on = on; // clear "full on" bit
136 }
137
142 {
143 // wait for current transmission to complete
144 while(current_driver_idx_ >= 0) {};
145
146 // swap buffers
147 auto tmp = transmit_buffer_;
148 transmit_buffer_ = draw_buffer_;
149 draw_buffer_ = tmp;
150
151 // copy current transmit buffer contents to the new draw buffer
152 // to keep the led settings (if required)
153 if(persistentBufferContents)
154 {
155 for(int d = 0; d < numDrivers; d++)
156 for(int ch = 0; ch < 16; ch++)
157 draw_buffer_[d].leds[ch].off
158 = transmit_buffer_[d].leds[ch].off;
159 }
160
161 // start transmission
162 current_driver_idx_ = -1;
163 ContinueTransmission();
164 }
165
166 private:
167 void ContinueTransmission()
168 {
169 current_driver_idx_ = current_driver_idx_ + 1;
170 if(current_driver_idx_ >= numDrivers)
171 {
172 current_driver_idx_ = -1;
173 return;
174 }
175
176 const auto d = current_driver_idx_;
177 const uint8_t address = PCA9685_I2C_BASE_ADDRESS | addresses_[d];
178 const auto status = i2c_.TransmitDma(address,
179 (uint8_t*)&transmit_buffer_[d],
180 PCA9685TransmitBuffer::size,
181 &TxCpltCallback,
182 this);
183 if(status != I2CHandle::Result::OK)
184 {
185 // TODO: fix this :-)
186 // Reinit I2C (probably a flag to kill, but hey this works fairly well for now.)
187 i2c_.Init(i2c_.GetConfig());
188 }
189 }
190 uint16_t GetStartCycleForLed(int ledIndex) const
191 {
192 return (ledIndex << 2) & 0x0FFF; // shift each led by 4 cycles
193 }
194
195 uint8_t GetDriverForLed(int ledIndex) const { return ledIndex >> 4; }
196
197 uint8_t GetDriverChannelForLed(int ledIndex) const
198 {
199 return ledIndex & 0x0F;
200 }
201
202 void InitializeBuffers()
203 {
204 for(int led = 0; led < GetNumLeds(); led++)
205 {
206 const auto d = GetDriverForLed(led);
207 const auto ch = GetDriverChannelForLed(led);
208 const auto startCycle = GetStartCycleForLed(led);
209 draw_buffer_[d].registerAddr = PCA9685_LED0;
210 draw_buffer_[d].leds[ch].on = startCycle;
211 draw_buffer_[d].leds[ch].off = startCycle;
212 transmit_buffer_[d].registerAddr = PCA9685_LED0;
213 transmit_buffer_[d].leds[ch].on = startCycle;
214 transmit_buffer_[d].leds[ch].off = startCycle;
215 }
216 }
217
218 void InitializeDrivers()
219 {
220 // init OE pin and pull low to enable outputs
221 if(oe_pin_.port != DSY_GPIOX)
222 {
223 oe_pin_gpio_.pin = oe_pin_;
224 oe_pin_gpio_.mode = DSY_GPIO_MODE_OUTPUT_PP;
225 oe_pin_gpio_.pull = DSY_GPIO_NOPULL;
226 dsy_gpio_init(&oe_pin_gpio_);
227 dsy_gpio_write(&oe_pin_gpio_, 0);
228 }
229
230 // init the individual drivers
231 for(int d = 0; d < numDrivers; d++)
232 {
233 const uint8_t address = PCA9685_I2C_BASE_ADDRESS | addresses_[d];
234 uint8_t buffer[2];
235 buffer[0] = PCA9685_MODE1;
236 buffer[1] = 0x00;
237 i2c_.TransmitBlocking(address, buffer, 2, 1);
238 System::Delay(20);
239 buffer[0] = PCA9685_MODE1;
240 buffer[1] = 0x00;
241 i2c_.TransmitBlocking(address, buffer, 2, 1);
242 System::Delay(20);
243 buffer[0] = PCA9685_MODE1;
244 // auto increment on
245 buffer[1] = 0b00100000;
246 i2c_.TransmitBlocking(address, buffer, 2, 1);
247 System::Delay(20);
248 buffer[0] = PCA9685_MODE2;
249 // OE-high = high Impedance
250 // Push-Pull outputs
251 // outputs change on STOP
252 // outputs inverted
253 buffer[1] = 0b000110110;
254 i2c_.TransmitBlocking(address, buffer, 2, 5);
255 }
256 }
257
258 // no std::clamp available in C++14.... remove this when C++17 is available
259 template <typename T>
260 T clamp(T in, T low, T high)
261 {
262 return (in < low) ? low : (high < in) ? high : in;
263 }
264
265 // an internal function to handle i2c callbacks
266 // called when an I2C transmission completes and the next driver must be updated
267 static void TxCpltCallback(void* context, I2CHandle::Result result)
268 {
269 auto drv_ptr = reinterpret_cast<
270 LedDriverPca9685<numDrivers, persistentBufferContents>*>(context);
271 drv_ptr->ContinueTransmission();
272 }
273
274 I2CHandle i2c_;
275 PCA9685TransmitBuffer* draw_buffer_;
276 PCA9685TransmitBuffer* transmit_buffer_;
277 uint8_t addresses_[numDrivers];
278 dsy_gpio_pin oe_pin_;
279 dsy_gpio oe_pin_gpio_;
280 // index of the dirver that is currently updated.
281 volatile int8_t current_driver_idx_;
282 const uint16_t gamma_table_[256] = {
283 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
284 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 5,
285 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 15, 16,
286 17, 18, 20, 21, 23, 25, 26, 28, 30, 32, 34, 36,
287 38, 40, 43, 45, 48, 50, 53, 56, 59, 62, 65, 68,
288 71, 75, 78, 82, 85, 89, 93, 97, 101, 105, 110, 114,
289 119, 123, 128, 133, 138, 143, 149, 154, 159, 165, 171, 177,
290 183, 189, 195, 202, 208, 215, 222, 229, 236, 243, 250, 258,
291 266, 273, 281, 290, 298, 306, 315, 324, 332, 341, 351, 360,
292 369, 379, 389, 399, 409, 419, 430, 440, 451, 462, 473, 485,
293 496, 508, 520, 532, 544, 556, 569, 582, 594, 608, 621, 634,
294 648, 662, 676, 690, 704, 719, 734, 749, 764, 779, 795, 811,
295 827, 843, 859, 876, 893, 910, 927, 944, 962, 980, 998, 1016,
296 1034, 1053, 1072, 1091, 1110, 1130, 1150, 1170, 1190, 1210, 1231, 1252,
297 1273, 1294, 1316, 1338, 1360, 1382, 1404, 1427, 1450, 1473, 1497, 1520,
298 1544, 1568, 1593, 1617, 1642, 1667, 1693, 1718, 1744, 1770, 1797, 1823,
299 1850, 1877, 1905, 1932, 1960, 1988, 2017, 2045, 2074, 2103, 2133, 2162,
300 2192, 2223, 2253, 2284, 2315, 2346, 2378, 2410, 2442, 2474, 2507, 2540,
301 2573, 2606, 2640, 2674, 2708, 2743, 2778, 2813, 2849, 2884, 2920, 2957,
302 2993, 3030, 3067, 3105, 3143, 3181, 3219, 3258, 3297, 3336, 3376, 3416,
303 3456, 3496, 3537, 3578, 3619, 3661, 3703, 3745, 3788, 3831, 3874, 3918,
304 3962, 4006, 4050, 4095};
305
306 static constexpr uint8_t PCA9685_I2C_BASE_ADDRESS = 0b01000000;
307 static constexpr uint8_t PCA9685_MODE1
308 = 0x00; // location for Mode1 register address
309 static constexpr uint8_t PCA9685_MODE2
310 = 0x01; // location for Mode2 reigster address
311 static constexpr uint8_t PCA9685_LED0
312 = 0x06; // location for start of LED0 registers
313 static constexpr uint8_t PRE_SCALE_MODE
314 = 0xFE; //location for setting prescale (clock speed)
315};
316
317} // namespace daisy
318
319#endif
320#endif
Definition i2c.h:25
const Config & GetConfig() const
Result TransmitDma(uint16_t address, uint8_t *data, uint16_t size, CallbackFunctionPtr callback, void *callback_context)
Result Init(const Config &config)
Result
Definition i2c.h:71
Result TransmitBlocking(uint16_t address, uint8_t *data, uint16_t size, uint32_t timeout)
Definition leddriver.h:33
void SetLedRaw(int ledIndex, uint16_t rawBrightness)
Definition leddriver.h:124
void SetLed(int ledIndex, uint8_t brightness)
Definition leddriver.h:117
constexpr int GetNumLeds() const
Definition leddriver.h:84
void SetAllToRaw(uint16_t rawBrightness)
Definition leddriver.h:102
void Init(I2CHandle i2c, const uint8_t(&addresses)[numDrivers], DmaBuffer dma_buffer_a, DmaBuffer dma_buffer_b, dsy_gpio_pin oe_pin={DSY_GPIOX, 0})
Definition leddriver.h:65
struct __attribute__((packed)) PCA9685TransmitBuffer
Definition leddriver.h:36
void SetAllTo(uint8_t brightness)
Definition leddriver.h:95
void SetLed(int ledIndex, float brightness)
Definition leddriver.h:109
void SwapBuffersAndTransmit()
Definition leddriver.h:141
PCA9685TransmitBuffer[numDrivers] DmaBuffer
Definition leddriver.h:51
void SetAllTo(float brightness)
Definition leddriver.h:87
static void Delay(uint32_t delay_ms)
void dsy_gpio_write(const dsy_gpio *p, uint8_t state)
void dsy_gpio_init(const dsy_gpio *p)
@ DSY_GPIO_MODE_OUTPUT_PP
Definition gpio.h:159
@ DSY_GPIO_NOPULL
Definition gpio.h:168
@ DSY_GPIOX
Definition daisy_core.h:193
Hardware defines and helpers for daisy field platform.
Definition index.h:2
Definition daisy_core.h:205
dsy_gpio_port port
Definition daisy_core.h:206
Definition gpio.h:175
dsy_gpio_mode mode
Definition gpio.h:177
dsy_gpio_pin pin
Definition gpio.h:176
dsy_gpio_pull pull
Definition gpio.h:178