Lumitronix_Iflex_Pro_Workshop
Library to interact with the iFlexPro
NeoAvrMethod.h
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2 LumitronixIFlex library helper functions for Atmel AVR.
3 
4 Written by Michael C. Miller.
5 Some work taken from the Adafruit LumitronixIFlex library.
6 
7 I invest time and resources providing this open source code,
8 please support me by dontating (see https://github.com/Makuna)
9 
10 -------------------------------------------------------------------------
11 This file is part of the LUMITRONIX_iFlex_Workshop library.
12 
13 LumitronixIFlexBus is free software: you can redistribute it and/or modify
14 it under the terms of the GNU Lesser General Public License as
15 published by the Free Software Foundation, either version 3 of
16 the License, or (at your option) any later version.
17 
18 LumitronixIFlexBus is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU Lesser General Public License for more details.
22 
23 You should have received a copy of the GNU Lesser General Public
24 License along with LumitronixIFlex. If not, see
25 <http://www.gnu.org/licenses/>.
26 -------------------------------------------------------------------------*/
27 
28 #pragma once
29 
30 #if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
31 
32 extern "C"
33 {
34  void send_data_8mhz_800_PortD(uint8_t* data, size_t sizeData, uint8_t pinMask);
35  void send_data_8mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask);
36  void send_data_8mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
37  void send_data_12mhz_800_PortD(uint8_t* data, size_t sizeData, uint8_t pinMask);
38  void send_data_12mhz_800_PortB(uint8_t* data, size_t sizeData, uint8_t pinMask);
39  void send_data_12mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
40  void send_data_16mhz_800(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
41  void send_data_16mhz_400(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
42  void send_data_16mhz_600(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask);
43  void send_data_32mhz(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask, const uint8_t cycleTiming);
44 }
45 
46 class NeoAvrSpeed800KbpsBase
47 {
48 public:
49  static void send_data(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
50  {
51 #if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
52 #ifdef PORTD // PORTD isn't present on ATtiny85, etc.
53  if (port == &PORTD)
54  send_data_8mhz_800_PortD(data, sizeData, pinMask);
55  else if (port == &PORTB)
56 #endif // PORTD
57  send_data_8mhz_800_PortB(data, sizeData, pinMask);
58 
59 #elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
60 #ifdef PORTD // PORTD
61  if (port == &PORTD)
62  send_data_12mhz_800_PortD(data, sizeData, pinMask);
63  else if (port == &PORTB)
64 #endif // PORTD
65  send_data_12mhz_800_PortB(data, sizeData, pinMask);
66 
67 #elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000UL) // 16Mhz CPU
68  send_data_16mhz_800(data, sizeData, port, pinMask);
69 #elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU
70  send_data_32mhz(data, sizeData, port, pinMask, 3);
71 #else
72 #error "CPU SPEED NOT SUPPORTED"
73 #endif
74  }
75 
76 };
77 
78 class NeoAvrSpeed600KbpsBase
79 {
80 public:
81  static void send_data(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
82  {
83 #if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
84 #ifdef PORTD // PORTD isn't present on ATtiny85, etc.
85  if (port == &PORTD)
86  send_data_8mhz_800_PortD(data, sizeData, pinMask);
87  else if (port == &PORTB)
88 #endif // PORTD
89  send_data_8mhz_800_PortB(data, sizeData, pinMask);
90 
91 #elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
92 #ifdef PORTD // PORTD
93  if (port == &PORTD)
94  send_data_12mhz_800_PortD(data, sizeData, pinMask);
95  else if (port == &PORTB)
96 #endif // PORTD
97  send_data_12mhz_800_PortB(data, sizeData, pinMask);
98 
99 #elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000UL) // 16Mhz CPU
100  send_data_16mhz_600(data, sizeData, port, pinMask);
101 #elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU
102  send_data_32mhz(data, sizeData, port, pinMask, 3);
103 #else
104 #error "CPU SPEED NOT SUPPORTED"
105 #endif
106  }
107 
108 };
109 
110 class NeoAvrSpeedWs2812x : public NeoAvrSpeed800KbpsBase
111 {
112 public:
113  static const uint32_t ResetTimeUs = 300;
114 };
115 
116 class NeoAvrSpeedSk6812 : public NeoAvrSpeed800KbpsBase
117 {
118 public:
119  static const uint32_t ResetTimeUs = 80;
120 };
121 
122 class NeoAvrSpeedApa106 : public NeoAvrSpeed600KbpsBase
123 {
124 public:
125  static const uint32_t ResetTimeUs = 100;
126 };
127 
128 class NeoAvrSpeed600KbpsIps : public NeoAvrSpeed600KbpsBase
129 {
130 public:
131  static const uint32_t ResetTimeUs = 300;
132  static const uint16_t InterpixelTimeUs = 8; // 12.4, with loop overhead of about 5us for loop
133 };
134 
135 class NeoAvrSpeedTm1814 : public NeoAvrSpeed800KbpsBase
136 {
137 public:
138  static const uint32_t ResetTimeUs = 200;
139 };
140 
141 class NeoAvrSpeedTm1829 : public NeoAvrSpeed800KbpsBase
142 {
143 public:
144  static const uint32_t ResetTimeUs = 200;
145 };
146 
147 class NeoAvrSpeed800Kbps: public NeoAvrSpeed800KbpsBase
148 {
149 public:
150  static const uint32_t ResetTimeUs = 50;
151 };
152 
153 class NeoAvrSpeed400Kbps
154 {
155 public:
156  static void send_data(uint8_t* data, size_t sizeData, volatile uint8_t* port, uint8_t pinMask)
157  {
158 #if (F_CPU >= 7400000UL) && (F_CPU <= 9500000UL) // 8Mhz CPU
159  send_data_8mhz_400(data, sizeData, port, pinMask);
160 
161 #elif (F_CPU >= 11100000UL) && (F_CPU <= 14300000UL) // 12Mhz CPU
162  send_data_12mhz_400(data, sizeData, port, pinMask);
163 
164 #elif (F_CPU >= 15400000UL) && (F_CPU <= 19000000UL) // 16Mhz CPU
165  send_data_16mhz_400(data, sizeData, port, pinMask);
166 #elif (F_CPU >= 31000000UL) && (F_CPU <= 35000000UL) // 32Mhz CPU
167  send_data_32mhz(data, sizeData, port, pinMask, 7);
168 #else
169 #error "CPU SPEED NOT SUPPORTED"
170 #endif
171  }
172  static const uint32_t ResetTimeUs = 50;
173 };
174 
175 template<typename T_SPEED> class NeoAvrMethodBase
176 {
177 public:
178  typedef NeoNoSettings SettingsObject;
179 
180  NeoAvrMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
181  _sizeData(pixelCount * elementSize + settingsSize),
182  _pin(pin),
183  _port(NULL),
184  _pinMask(0)
185  {
186  pinMode(pin, OUTPUT);
187 
188  _data = static_cast<uint8_t*>(malloc(_sizeData));
189  // data cleared later in Begin()
190 
191  _port = portOutputRegister(digitalPinToPort(pin));
192  _pinMask = digitalPinToBitMask(pin);
193  }
194 
195  ~NeoAvrMethodBase()
196  {
197  pinMode(_pin, INPUT);
198 
199  free(_data);
200  }
201 
202  bool IsReadyToUpdate() const
203  {
204  uint32_t delta = micros() - _endTime;
205 
206  return (delta >= T_SPEED::ResetTimeUs);
207  }
208 
209  void Initialize()
210  {
211  digitalWrite(_pin, LOW);
212 
213  _endTime = micros();
214  }
215 
216  void Update(bool)
217  {
218  // Data latch = 50+ microsecond pause in the output stream. Rather than
219  // put a delay at the end of the function, the ending time is noted and
220  // the function will simply hold off (if needed) on issuing the
221  // subsequent round of data until the latch time has elapsed. This
222  // allows the mainline code to start generating the next frame of data
223  // rather than stalling for the latch.
224  while (!IsReadyToUpdate())
225  {
226 #if !defined(ARDUINO_TEEONARDU_LEO) && !defined(ARDUINO_TEEONARDU_FLORA)
227  yield(); // allows for system yield if needed
228 #endif
229  }
230 
231  noInterrupts(); // Need 100% focus on instruction timing
232 
233  T_SPEED::send_data(_data, _sizeData, _port, _pinMask);
234 
235  interrupts();
236 
237  // save EOD time for latch on next call
238  _endTime = micros();
239  }
240 
241  bool AlwaysUpdate()
242  {
243  // this method requires update to be called only if changes to buffer
244  return false;
245  }
246 
247  uint8_t* getData() const
248  {
249  return _data;
250  };
251 
252  size_t getDataSize() const
253  {
254  return _sizeData;
255  };
256 
257  void applySettings([[maybe_unused]] const SettingsObject& settings)
258  {
259  }
260 
261 protected:
262  const size_t _sizeData; // size of _data below
263  const uint8_t _pin; // output pin number
264 
265  uint32_t _endTime; // Latch timing reference
266  uint8_t* _data; // Holds data stream which include LED color values and other settings as needed
267 
268  volatile uint8_t* _port; // Output PORT register
269  uint8_t _pinMask; // Output PORT bitmask
270 };
271 
272 template<typename T_SPEED> class NeoAvrIpsMethodBase : public NeoAvrMethodBase<T_SPEED>
273 {
274 public:
275  NeoAvrIpsMethodBase(uint8_t pin, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
276  NeoAvrMethodBase<T_SPEED>(pin, pixelCount, elementSize, settingsSize),
277  _elementSize(elementSize)
278  {
279  }
280 
281  ~NeoAvrIpsMethodBase()
282  {
283  }
284 
285  void Update(bool)
286  {
287  // Data latch = 50+ microsecond pause in the output stream. Rather than
288  // put a delay at the end of the function, the ending time is noted and
289  // the function will simply hold off (if needed) on issuing the
290  // subsequent round of data until the latch time has elapsed. This
291  // allows the mainline code to start generating the next frame of data
292  // rather than stalling for the latch.
293  while (!NeoAvrMethodBase<T_SPEED>::IsReadyToUpdate())
294  {
295 #if !defined(ARDUINO_TEEONARDU_LEO) && !defined(ARDUINO_TEEONARDU_FLORA)
296  yield(); // allows for system yield if needed
297 #endif
298  }
299 
300  noInterrupts(); // Need 100% focus on instruction timing
301 
302  uint8_t* dataPixel = NeoAvrMethodBase<T_SPEED>::_data;
303  const uint8_t* dataEnd = dataPixel + NeoAvrMethodBase<T_SPEED>::_sizeData;
304 
305  while (dataPixel < dataEnd)
306  {
307  T_SPEED::send_data(dataPixel,
308  _elementSize,
309  NeoAvrMethodBase<T_SPEED>::_port,
310  NeoAvrMethodBase<T_SPEED>::_pinMask);
311  dataPixel += _elementSize;
312  delayMicroseconds(T_SPEED::InterpixelTimeUs);
313  }
314 
315  interrupts();
316 
317  // save EOD time for latch on next call
318  NeoAvrMethodBase<T_SPEED>::_endTime = micros();
319  }
320 
321 private:
322  const size_t _elementSize; // size of a single pixel
323 };
324 
325 typedef NeoAvrMethodBase<NeoAvrSpeedWs2812x> NeoAvrWs2812xMethod;
326 typedef NeoAvrMethodBase<NeoAvrSpeedSk6812> NeoAvrSk6812Method;
327 typedef NeoAvrMethodBase<NeoAvrSpeedApa106> NeoAvrApa106Method;
328 typedef NeoAvrIpsMethodBase<NeoAvrSpeed600KbpsIps> NeoAvr600KbpsIpsMethod;
329 
330 typedef NeoAvrMethodBase<NeoAvrSpeedTm1814> NeoAvrTm1814InvertedMethod;
331 typedef NeoAvrMethodBase<NeoAvrSpeedTm1829> NeoAvrTm1829InvertedMethod;
332 typedef NeoAvrMethodBase<NeoAvrSpeed800Kbps> NeoAvr800KbpsMethod;
333 typedef NeoAvrMethodBase<NeoAvrSpeed400Kbps> NeoAvr400KbpsMethod;
334 typedef NeoAvrTm1814InvertedMethod NeoAvrTm1914InvertedMethod;
335 
336 // AVR doesn't have alternatives yet, so there is just the default
337 typedef NeoAvrWs2812xMethod NeoWs2813Method;
338 typedef NeoAvrWs2812xMethod NeoWs2812xMethod;
339 typedef NeoAvr800KbpsMethod NeoWs2812Method;
340 typedef NeoAvrWs2812xMethod NeoWs2811Method;
341 typedef NeoAvrWs2812xMethod NeoWs2816Method;
342 typedef NeoAvrSk6812Method NeoSk6812Method;
343 typedef NeoAvrSk6812Method NeoLc8812Method;
344 typedef NeoAvrApa106Method NeoApa106Method;
345 typedef NeoAvrWs2812xMethod Neo800KbpsMethod;
346 typedef NeoAvr400KbpsMethod Neo400KbpsMethod;
347 
348 // there is no non-invert methods for avr, but the norm for TM1814 is inverted, so
349 typedef NeoAvrTm1814InvertedMethod NeoTm1814InvertedMethod;
350 typedef NeoAvrTm1914InvertedMethod NeoTm1914InvertedMethod;
351 typedef NeoAvrTm1829InvertedMethod NeoTm1829InvertedMethod;
352 #endif
353 
Definition: NeoSettings.h:29