Added TODO.
[dmxpainter.git] / src / tlc.c
1 #include <avr/io.h>
2
3 #include "tlc.h"
4
5 #include "mcu.h"
6
7 #include "buf.h"
8
9
10 /*********************************************************************/
11 /* Declaration of private constants.                                 */
12
13 #define TLC_N_CHANNELS_PER_TLC 16
14 #define TLC_N_TLCS_PER_PAINTER  3
15
16 #define TLC_N_CHANNELS_PER_PAINTER (TLC_N_TLCS_PER_PAINTER * TLC_N_CHANNELS_PER_TLC)
17
18 #define TLC_N_CHANNELS (N_PAINTER * TLC_N_CHANNELS_PER_PAINTER)
19
20 #define TLC_DC_ONCE
21
22
23 /*********************************************************************/
24 /* Declaration of private global variables.                          */
25
26 /**
27  * Flag to indicate that data is currently shifted out.
28  */
29 static volatile uint8_t data_shifting_;
30
31
32 /*********************************************************************/
33 /* Declaration of private functions.                                 */
34
35 static void start_gscycle(void);
36
37 static void set_shifting_off(void);
38 static void clock_xlat(void);
39 static void clock_sclk(void);
40 static void set_blnk_on(void);
41 static void set_blnk_off(void);
42 static void set_vprg_gs_mode(void);
43 static void set_vprg_dc_mode(void);
44
45 static void send_dc_data(void);
46 static void send_gs_data(void);
47
48
49 /*********************************************************************/
50 /* Implementation of public interrupts.                              */
51
52 /**
53  * Handler for Output-Compare-Match interrupt on 16-bit timer:
54  * Syncs on GSCLK to start GS cycle.
55  */
56 void tlc_int_timer1_ocma(void)
57 {
58   // First, disable this interrupt.
59   mcu_int_timer1_ocma_disable();
60
61   // Leave BLNK mode (switch on LEDs and start GS cycle).
62   set_blnk_off();
63 }
64
65 /**
66  * Handler for Output-Compare-Match interrupt on 8-bit timer:
67  * Disables PWM when a full GSCK cycle is done.
68  */
69 void tlc_int_timer2_ocm(void)
70 {
71   // Go into BLNK mode (switch off LEDs and reset GSCLK counter)
72   set_blnk_on();
73
74   // Disable GS cycle timeout timer.
75   mcu_int_timer2_ocm_disable();
76
77   // Wait for next DMX packet.
78   set_shifting_off();
79 }
80
81
82 /*********************************************************************/
83 /* Implementation of public functions.                               */
84
85 void tlc_init(void)
86 {
87   // We have to use the 16-bit timer for the GSCLK and the 8-bit
88   // timer for the timeout even though it would be better the other
89   // way round:  We need the OC2 pin for SPI and thus can generate 
90   // a PWM on OC1A (A because it can use ICR1 for TOP, p85) only.
91   // Do this setup before enabling the output (p86).
92
93   // Timer 1 is for our GSCLK:  We refresh with a GS cycle of
94   // about 100 Hz (cf. Timer 2), ie. each 10 ms.  For each full 
95   // cycle we needed to clock the PWM 4096 times which would be 
96   // a count of about 39 times between the pules:
97   //   n = 16 MHz / (4096 * 100 Hz) = 39.0625
98   // But we have to consider the time it takes to shift out the data
99   // so we don't have 10 ms for 4096 pulses, so we've got to speed 
100   // up, ie. count less clocks.  The count is adapted on demand later
101   // on, we'll initialize it with a reasonable default.
102   mcu_set_timer1_ic(38);
103   // Duty cycle as short as possible (see COM1A below).
104   mcu_set_timer1_ocma(1);
105   // * CS1   =  001: No prescaler. (p100)
106   // * WGM1  = 1110: Fast PWM, TOP at ICR1 (p78, p89, p98)
107   // * COM1A =   10: Set OC1A at 0, clear at OCM1A (p97)
108   // * COM1B =   00: No output on OC1B (p97)
109   TCCR1B = bits_value_indexed(CS1, 0)
110          | bits_value_indexed(WGM1, 3)
111          | bits_value_indexed(WGM1, 2);
112   TCCR1A = bits_value_indexed(WGM1, 1)
113          | bits_value_indexed(COM1A, 1);
114
115   // Timer 2 is the refresh timer which determines the time one GS
116   // cycle is finished; triggers Output Compare Match.
117   // * AS2  =   0: Use IO-clock (16 MHz) for base frequency (p119)
118   // * CS2  = 111: Use a prescaler of 1024 (p119)
119   // * WGM2 =  00: Normal mode, no PWM, count upwards (p117)
120   // * COM2 =  00: Disable Output on OC2, needed for SPI (p117)
121   TCCR2 = bits_value_indexed(CS2, 2)
122         | bits_value_indexed(CS2, 1)
123         | bits_value_indexed(CS2, 0);
124   // With a prescaler of 1024 this timer counts at 15.625 kHz,
125   // to get a 100 Hz clock we need to count 157 times (~ 99.5 Hz)
126   // and refresh after that (that equals to 4 PWM pulses when
127   // ignoring the shifting time).
128   //   n = (16 MHz / 1024) / 100 Hz = 156.25
129   mcu_set_timer2_ocm(156);
130
131   // All these pins write to the painter.
132   pin_out(PIN_TLC_GSCK);
133   pin_out(PIN_TLC_VPRG);
134   pin_out(PIN_TLC_XLAT);
135   pin_out(PIN_TLC_SCLK);
136   pin_out(PIN_TLC_SIN);
137
138   // This one writes too, but has to be initialized blanked
139   // (ie. LEDs off).  The external pullup took care against
140   // flickering on boot.
141   pin_out(PIN_TLC_BLNK);
142   set_blnk_on();
143
144   // Here we could read the return from the painter.
145   pin_in( PIN_TLC_SRTN);
146 }
147
148 void tlc_exec(void)
149 {
150   // If enabled, shift out DC once.
151   #ifdef TLC_DC_ONCE
152     send_dc_data();
153     clock_xlat();
154   #endif
155 }
156
157 void tlc_update(void)
158 {
159   // TODO: Make this routine iterative.
160   // TODO: Is it possible to shift while GS cycle is active and 
161   //       XLAT when cycle is done?
162
163   // Don't send anything if PWM is still active.
164   if (data_shifting_) return;
165
166   // Restart and enable 100 Hz-timeout timer now so
167   // it includes the time we need to shift out data.
168   mcu_set_timer2_cnt(0);
169   mcu_int_timer2_ocm_enable();
170
171   // If not disabled, always shift out DC first.
172   #ifndef TLC_DC_ONCE
173     send_dc_data();
174     clock_xlat();
175   #endif
176
177   // No extra SCLK needed, just shift out all GS data.
178   send_gs_data();
179   clock_xlat();
180
181   // A final SCLK to notify 
182   clock_sclk();
183
184   // Start PWM and continue in background...
185   start_gscycle();
186 }
187
188
189 /*********************************************************************/
190 /* Implementation of private functions.                              */
191
192 static void set_shifting_off(void)
193 {
194   data_shifting_ = 0;
195 }
196
197 // XLAT pulse to apply data to internal register.
198 static void clock_xlat(void)
199 {
200   pin_on(PIN_TLC_XLAT);
201   pin_off(PIN_TLC_XLAT);
202 }
203
204 // SCLK pulse to clock in serial data from SIN.
205 static void clock_sclk(void)
206 {
207   pin_on(PIN_TLC_SCLK);
208   pin_off(PIN_TLC_SCLK);
209 }
210
211 static void set_blnk_on(void)
212 {
213   pin_on(PIN_TLC_BLNK);
214 }
215
216 static void set_blnk_off(void)
217 {
218   pin_off(PIN_TLC_BLNK);
219 }
220
221 static void set_vprg_gs_mode(void)
222 {
223   pin_off(PIN_TLC_VPRG);
224 }
225
226 static void set_vprg_dc_mode(void)
227 {
228   pin_on(PIN_TLC_VPRG);
229 }
230
231 /*********************************************************************/
232
233 static void start_gscycle(void)
234 {
235   data_shifting_ = 1;
236   // Start counter with next GS pulse.
237   mcu_int_timer1_ocma_enable();
238 }
239
240 /*********************************************************************/
241
242 static void shift8(uint8_t byte)
243 {
244   // Shift out all eight bits.
245   for (uint8_t bit = bits_uint8(1, 0, 0, 0, 0, 0, 0, 0); bit; bit >>= 1) {
246     if (byte & bit) {
247       pin_on(PIN_TLC_SIN);
248     } else {
249       pin_off(PIN_TLC_SIN);
250     }
251     clock_sclk();
252   }
253 }
254
255 static void shift12(uint8_t byte)
256 {
257   // The data in the upper 8 bits.
258   shift8(byte);
259
260   // Plus 4 zero bits (makes a shift by 4).
261   pin_off(PIN_TLC_SIN);
262   for (uint8_t bit = 4; bit; bit--) {
263     clock_sclk();
264   }
265 }
266
267 /*********************************************************************/
268
269 static void send_gs_data(void)
270 {
271   // Set VPRG to GS mode.
272   set_vprg_gs_mode();
273   // Because the TLCs are daisy-chained, we have to shift out the RGB data
274   // starting at the end.  Each painter has 3 TLCs (with 16 channels each), 
275   // for the colors red, green, blue.  So we've got to shift out the 16 blue
276   // channels of the last TLC first, then 16 green ones and finally 16 red 
277   // ones.  The last data we shift out is thus the first red of the first
278   // painter.
279   // This will always point to the start of the current painter data, 
280   // starting with the last one.
281   char * painter_gs = buf_gs__
282                     + TLC_N_CHANNELS
283                     - TLC_N_CHANNELS_PER_PAINTER;
284   // Find the current data byte to shift out, starting with the last one.
285   // Its signed so we can determine when we reached the end/start, eight
286   // bit are enough to index 48 channels per painter.
287 #if TLC_N_CHANNELS_PER_PAINTER != 48
288 #error What a weird painter...
289 #endif
290   while (1) {
291     int8_t index = TLC_N_CHANNELS_PER_PAINTER - 1;
292     while (1) {
293       // Shift out current channel.
294       shift12(painter_gs[index]);
295
296       // Skip two colors.
297       index -= 3;
298
299       // If we reached the start, we jump to the next color.
300       if (index < 0) {
301         // Did we just finish the last (ie. red) channel?
302         if (index == -3)
303           break;
304
305         // Jump to end again and skip to next color.
306         index += TLC_N_CHANNELS_PER_PAINTER - 1;
307       }
308     }
309
310     // Did we just finish the last (ie. first) painter?
311     if (painter_gs == buf_gs__)
312       break;
313
314     // Move to next painter.
315     painter_gs -= TLC_N_CHANNELS_PER_PAINTER;
316   }
317 }
318
319 static void send_dc_data(void)
320 {
321   // Set VPRG to DC mode. 
322   set_vprg_dc_mode();
323
324   // All TLCs on all the connected painters will get the same DC value.
325   // That makes it easy to generate the 6-Bit format we need:  We just
326   // create a constant buffer for the packed rgb values, containing four
327   // values for each color.
328   uint8_t dc_out[3][3];
329   for (int8_t rgb = 2; rgb >= 0; rgb--) {
330     uint8_t dc_data = buf_dc__[rgb] & bits_uint8(1, 1, 1, 1, 1, 1, 0, 0);
331     dc_out[rgb][2] = (dc_data << 0) | (dc_data >> 6);
332     dc_out[rgb][1] = (dc_data << 2) | (dc_data >> 4);
333     dc_out[rgb][0] = (dc_data << 4) | (dc_data >> 2);
334   }
335
336   // Now, shift out the dc-data like we do it with the gs-data:  First the
337   // last blue, then green and red of the last painter, until we reach the
338   // first red.
339   int8_t painter = N_PAINTER;
340   do {
341     int8_t rgb = 3 - 1;
342     do {
343       int8_t index = (TLC_N_CHANNELS_PER_TLC / 4) * 3 - 1;
344       do {
345         shift8(dc_out[rgb][index % 3]);
346         index--;
347       } while (index != -1);
348       rgb--;
349     } while (rgb != -1);
350     painter--;
351   } while (painter != 0);
352 }
353
354 /*********************************************************************/