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