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