Introducing TLC_DC_ONCE.
[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 send_data(void);
36 static void start_gscycle(void);
37
38 static void set_shifting_off(void);
39 static void clock_xlat(void);
40 static void clock_sclk(void);
41 static void set_blnk_on(void);
42 static void set_blnk_off(void);
43 static void set_vprg_gs_mode(void);
44 static void set_vprg_dc_mode(void);
45
46 static void send_data(void);
47
48 static void send_dc_data(void);
49 static void send_gs_data(void);
50
51
52 /*********************************************************************/
53 /* Implementation of public interrupts.                              */
54
55 void tlc_int_timer1_ocma(void)
56 {
57   // First, disable this interrupt.
58   mcu_int_timer1_ocma_disable();
59
60   // Restart and enable timeout timer.
61   mcu_set_timer2_cnt(0);
62   mcu_int_timer2_ocm_enable();
63
64   // Switch off BLNK.
65   set_blnk_off();
66   // Hack: Switch on GSCLK
67   pin_out(PIN_TLC_GSCK);
68 }
69
70 void tlc_int_timer2_ocm(void)
71 {
72   // Hack: Switch off GSCLK
73   pin_in(PIN_TLC_GSCK);
74   // Go into BLNK mode (switch off LEDs and reset GSCLK counter)
75   set_blnk_on();
76
77   // Disable GS cycle timeout timer.
78   mcu_int_timer2_ocm_disable();
79
80   // Wait for next DMX packet.
81   set_shifting_off();
82 }
83
84
85 /*********************************************************************/
86 /* Implementation of public functions.                               */
87
88 void tlc_init(void)
89 {
90   // Timer 1 is for our GSCLK:  We refresh with a GS cycle of
91   // about 100 Hz (cf. Timer 2), for each full cycle we need to
92   // clock the PWM 4096 times.
93   // We need about 38 clocks to get 4096 cycles at 100 Hz.
94   mcu_set_timer1_ic(38);
95   // 50% duty cycle.
96   mcu_set_timer1_ocma(1);
97   // * CS1 = 0001:  No prescaler. (p100)
98   // * WGM1 = 1110: Fast PWM, TOP at ICR1
99   // * COM1A = 10: Set at 0, clear at Output Compare Match)
100   TCCR1B = bits_value(CS10) | bits_value(WGM13) | bits_value(WGM12);
101   TCCR1A = bits_value(WGM11) | bits_value(COM1A1);
102
103   /* Timer 2: Refresh-Timer */
104   // * AS2 = 0: Use IO-clock (16 MHz) for base frequency (p119)
105   // * Mode: Normal mode, no PWM, count upwards (WGM21:0 = 00) (p117)
106   // * Disable Output on OC2, needed for SPI (COM21:0 = 00) (p117)
107   // * Prescaler: 1024 (CS22:0 = 111) => 15625 Hz
108   TCCR2 = bits_value(CS22) | bits_value(CS21) | bits_value(CS20);
109   // To get a 100 Hz clock we need to count 157 times (~ 99.5 Hz).
110   mcu_set_timer2_ocm(156);
111
112   // All these pins write to the painter.
113   pin_out(PIN_TLC_GSCK);
114   pin_out(PIN_TLC_VPRG);
115   pin_out(PIN_TLC_XLAT);
116   pin_out(PIN_TLC_SCLK);
117   pin_out(PIN_TLC_SIN);
118
119   // This one writes too, but has to be initialized blanked
120   // (ie. LEDs off).  The external pullup took care against
121   // flickering on boot.
122   pin_out(PIN_TLC_BLNK);
123   set_blnk_on();
124
125   // Here we could read the return from the painter.
126   pin_in( PIN_TLC_SRTN);
127 }
128
129 void tlc_exec(void)
130 {
131   // If enabled, shift out DC once.
132   #ifdef TLC_DC_ONCE
133     send_dc_data();
134     clock_xlat();
135   #endif
136 }
137
138 void tlc_update(void)
139 {
140   // Don't send anything if PWM is still active.
141   if (data_shifting_) return;
142
143   // If not disabled, always shift out DC first.
144   #ifndef TLC_DC_ONCE
145     send_dc_data();
146     clock_xlat();
147   #endif
148
149   // No extra SCLK needed, just shift out all GS data.
150   send_gs_data();
151   clock_xlat();
152
153   // A final SCLK to notify 
154   clock_sclk();
155
156   // Start PWM and continue in background...
157   start_gscycle();
158 }
159
160
161 /*********************************************************************/
162 /* Implementation of private functions.                              */
163
164 static void set_shifting_off(void)
165 {
166   data_shifting_ = 0;
167 }
168
169 // XLAT pulse to apply data to internal register.
170 static void clock_xlat(void)
171 {
172   pin_on(PIN_TLC_XLAT);
173   pin_off(PIN_TLC_XLAT);
174 }
175
176 // SCLK pulse to clock in serial data from SIN.
177 static void clock_sclk(void)
178 {
179   pin_on(PIN_TLC_SCLK);
180   pin_off(PIN_TLC_SCLK);
181 }
182
183 static void set_blnk_on(void)
184 {
185   pin_on(PIN_TLC_BLNK);
186 }
187
188 static void set_blnk_off(void)
189 {
190   pin_off(PIN_TLC_BLNK);
191 }
192
193 static void set_vprg_gs_mode(void)
194 {
195   pin_off(PIN_TLC_VPRG);
196 }
197
198 static void set_vprg_dc_mode(void)
199 {
200   pin_on(PIN_TLC_VPRG);
201 }
202
203 /*********************************************************************/
204
205 static void start_gscycle(void)
206 {
207   data_shifting_ = 1;
208   // Start counter with next GS pulse.
209   mcu_int_timer1_ocma_enable();
210 }
211
212 /*********************************************************************/
213
214 static void shift8(uint8_t byte)
215 {
216   // Shift out all eight bits.
217   for (uint8_t bit = bits_uint8(1, 0, 0, 0, 0, 0, 0, 0); bit; bit >>= 1) {
218     if (byte & bit) {
219       pin_on(PIN_TLC_SIN);
220     } else {
221       pin_off(PIN_TLC_SIN);
222     }
223     clock_sclk();
224   }
225 }
226
227 static void shift12(uint8_t byte)
228 {
229   // The data in the upper 8 bits.
230   shift8(byte);
231
232   // Plus 4 zero bits (makes a shift by 4).
233   pin_off(PIN_TLC_SIN);
234   for (uint8_t bit = 4; bit; bit--) {
235     clock_sclk();
236   }
237 }
238
239 /*********************************************************************/
240
241 static void send_gs_data(void)
242 {
243   // Set VPRG to GS mode.
244   set_vprg_gs_mode();
245   // Because the TLCs are daisy-chained, we have to shift out the RGB data
246   // starting at the end.  Each painter has 3 TLCs (with 16 channels each), 
247   // for the colors red, green, blue.  So we've got to shift out the 16 blue
248   // channels of the last TLC first, then 16 green ones and finally 16 red 
249   // ones.  The last data we shift out is thus the first red of the first
250   // painter.
251   // This will always point to the start of the current painter data, 
252   // starting with the last one.
253   char * painter_gs = buf_gs__
254                     + TLC_N_CHANNELS
255                     - TLC_N_CHANNELS_PER_PAINTER;
256   // Find the current data byte to shift out, starting with the last one.
257   // Its signed so we can determine when we reached the end/start, eight
258   // bit are enough to index 48 channels per painter.
259 #if TLC_N_CHANNELS_PER_PAINTER != 48
260 #error What a weird painter...
261 #endif
262   while (1) {
263     int8_t index = TLC_N_CHANNELS_PER_PAINTER - 1;
264     while (1) {
265       // Shift out current channel.
266       shift12(painter_gs[index]);
267
268       // Skip two colors.
269       index -= 3;
270
271       // If we reached the start, we jump to the next color.
272       if (index < 0) {
273         // Did we just finish the last (ie. red) channel?
274         if (index == -3)
275           break;
276
277         // Jump to end again and skip to next color.
278         index += TLC_N_CHANNELS_PER_PAINTER - 1;
279       }
280     }
281
282     // Did we just finish the last (ie. first) painter?
283     if (painter_gs == buf_gs__)
284       break;
285
286     // Move to next painter.
287     painter_gs -= TLC_N_CHANNELS_PER_PAINTER;
288   }
289 }
290
291 static void send_dc_data(void)
292 {
293   // Set VPRG to DC mode. 
294   set_vprg_dc_mode();
295
296   // All TLCs on all the connected painters will get the same DC value.
297   // That makes it easy to generate the 6-Bit format we need:  We just
298   // create a constant buffer for the packed rgb values, containing four
299   // values for each color.
300   uint8_t dc_out[3][3];
301   for (int8_t rgb = 2; rgb >= 0; rgb--) {
302     uint8_t dc_data = buf_dc__[rgb] & bits_uint8(1, 1, 1, 1, 1, 1, 0, 0);
303     dc_out[rgb][2] = (dc_data << 0) | (dc_data >> 6);
304     dc_out[rgb][1] = (dc_data << 2) | (dc_data >> 4);
305     dc_out[rgb][0] = (dc_data << 4) | (dc_data >> 2);
306   }
307
308   // Now, shift out the dc-data like we do it with the gs-data:  First the
309   // last blue, then green and red of the last painter, until we reach the
310   // first red.
311   int8_t painter = N_PAINTER;
312   do {
313     int8_t rgb = 3 - 1;
314     do {
315       int8_t index = (TLC_N_CHANNELS_PER_TLC / 4) * 3 - 1;
316       do {
317         shift8(dc_out[rgb][index % 3]);
318         index--;
319       } while (index != -1);
320       rgb--;
321     } while (rgb != -1);
322     painter--;
323   } while (painter != 0);
324 }
325
326 static void send_data(void)
327 {
328
329 }
330
331 /*********************************************************************/