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