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