80bf4040f5c5e6f39cad9f3c92b4b9e7bf37f532
[dmxpainter.git] / src / tlc.c
1 #include <avr/io.h>\r
2 \r
3 #include "tlc.h"\r
4 \r
5 #include "config.h"\r
6 \r
7 #include "mcu.h"\r
8 #include "pins.h"\r
9 \r
10 #include "sched.h"\r
11 #include "buffer.h"\r
12 \r
13 /////////////////////////////////////////\r
14 \r
15 #define CHANNELS_PER_TLC 16\r
16 #define BITS_PER_CHANNEL 12\r
17 #define TLCS_PER_PAINTER  3\r
18 \r
19 #define BYTES_PER_CHANNEL ((CHANNELS_PER_TLC * BITS_PER_CHANNEL) / 8)\r
20 \r
21 /////////////////////////////////////////\r
22 \r
23 // XLAT pulse to apply data to internal register.\r
24 inline void clock_xlat(void)\r
25 {\r
26   pin_on(PIN_TLC_XLAT);\r
27   pin_off(PIN_TLC_XLAT);\r
28 }\r
29 \r
30 // SCLK pulse to clock in serial data from SIN.\r
31 inline void clock_sclk(void)\r
32 {\r
33   pin_on(PIN_TLC_SCLK);\r
34   pin_off(PIN_TLC_SCLK);\r
35 }\r
36 \r
37 void set_blnk_on(void)\r
38 {\r
39   pin_on(PIN_TLC_BLNK);\r
40 }\r
41 \r
42 inline void set_blnk_off(void)\r
43 {\r
44   pin_off(PIN_TLC_BLNK);\r
45 }\r
46 \r
47 inline void set_vprg_gs_mode(void)\r
48 {\r
49   pin_off(PIN_TLC_VPRG);\r
50 }\r
51 \r
52 inline void set_vprg_dc_mode(void)\r
53 {\r
54   pin_on(PIN_TLC_VPRG);\r
55 }\r
56 \r
57 /////////////////////////////////////////\r
58 \r
59 void tlc_update(void);\r
60 \r
61 uint8_t g_data_done;\r
62 sched_res_t wait_for_data(void)\r
63 {\r
64   if (!g_data_done) return SCHED_RE;\r
65   tlc_update();\r
66   tlc_start_gscycle();\r
67   return SCHED_OK;\r
68 }\r
69 \r
70 /////////////////////////////////////////\r
71 \r
72 void tlc_init(void)\r
73 {\r
74   // All these pins write to the painter.\r
75   pin_out_off(PIN_TLC_GSCK);\r
76   pin_out_off(PIN_TLC_BLNK);\r
77   pin_out_off(PIN_TLC_VPRG);\r
78   pin_out_off(PIN_TLC_XLAT);\r
79   pin_out_off(PIN_TLC_SCLK);\r
80   pin_out_off(PIN_TLC_SIN);\r
81 \r
82   // Initialize blanked (ie. LEDs off).\r
83   pin_on(PIN_TLC_BLNK);//set_blnk_on();\r
84   while (1) {}\r
85 \r
86   // Timer 1 is for our GSCLK:  We refresh with a GS cycle of\r
87   // about 100 Hz (cf. Timer 2), for each full cycle we need to\r
88   // clock the PWM 4096 times.\r
89   // Disable output for now.\r
90   mcu_pin_timer2_ocm_disable();\r
91   // Shortest duty cycle possible.\r
92   mcu_set_timer1_ocma(1);\r
93   // We need about 39 clocks to get 4096 cycles at 100 Hz.\r
94   mcu_set_timer1_ic(39);\r
95   // * CS1 = 0001:  No prescaler. (p100)\r
96   // * WGM1 = 1110: Fast PWM, TOP at ICR1\r
97   // * COM1A = 10: Set at 0, clear at Compare Match)\r
98   TCCR1B = _BV(CS10) | _BV(WGM13) | _BV(WGM12);\r
99   TCCR1A = _BV(WGM11) | _BV(COM1A1);\r
100 \r
101   /* Timer 2: Refresh-Timer */\r
102   // * AS2 = 0: Use IO-clock (16 MHz) for base frequency (p119)\r
103   // * Mode: Normal mode, no PWM, count upwards (WGM21:0 = 00) (p117)\r
104   // * Disable Output on OC2, needed for SPI (COM21:0 = 00) (p117)\r
105   // * Prescaler: 1024 (CS22:0 = 111) => 15625 Hz\r
106   TCCR2 = _BV(CS22) | _BV(CS21) | _BV(CS20);\r
107   // To get a 100 Hz clock we need to count 157 times (~ 99.5 Hz).\r
108   mcu_set_timer1_ic(156);\r
109 \r
110   // Wait for first DMX packet.\r
111   sched_put(&wait_for_data);\r
112 }\r
113 \r
114 void tlc_start(void)\r
115 {\r
116   g_data_done = 1;\r
117 }\r
118 \r
119 /////////////////////////////////////////\r
120 \r
121 void tlc_start_gscycle(void)\r
122 {\r
123   // Start counter with next GS pulse.\r
124   mcu_int_timer1_ocma_enable();\r
125 }\r
126 \r
127 void tlc_start_gscycle_timeout(void)\r
128 {\r
129   // First, disable this interrupt.\r
130   mcu_int_timer1_ocma_disable();\r
131 \r
132   // Enable PWM output.\r
133   mcu_pin_timer1_ocma_enable();\r
134 \r
135   // Restart and enable timeout timer.\r
136   mcu_set_timer2_cnt(0);\r
137   mcu_int_timer2_ocm_enable();\r
138 \r
139   // Switch off BLNK.\r
140   set_blnk_off();\r
141 }\r
142 \r
143 void tlc_stop_gscycle(void)\r
144 {\r
145   // Go into BLNK mode (switch off LEDs and reset GSCLK counter)\r
146   set_blnk_on();\r
147 \r
148   // Disable GS cycle timeout timer.\r
149   mcu_int_timer2_ocm_disable();\r
150 \r
151   // Wait for next DMX packet.\r
152   sched_put(&wait_for_data);\r
153   // TODO: next data\r
154   sched_put(&buffer_test_next);\r
155 }\r
156 \r
157 /////////////////////////////////////////\r
158 \r
159 void shift8(uint8_t byte)\r
160 {\r
161   for (uint8_t bit = _B(1, 0, 0, 0, 0, 0, 0, 0); bit; bit >>= 1) {\r
162         if (bit & byte) {\r
163       pin_on(PIN_TLC_SIN);\r
164     } else {\r
165       pin_off(PIN_TLC_SIN);\r
166     }\r
167     clock_sclk();\r
168   }\r
169 }\r
170 \r
171 void shift12(uint8_t byte)\r
172 {\r
173   // The data in the upper 8 bits.\r
174   shift8(byte);\r
175 \r
176   // Plus 4 zero bits (makes a shift by 4).\r
177   pin_off(PIN_TLC_SIN);\r
178   for (uint8_t bit = _B(0, 0, 0, 0, 1, 0, 0, 0); bit; bit >>= 1) {\r
179     pin_on(PIN_TLC_SCLK);\r
180     pin_off(PIN_TLC_SCLK);\r
181   }\r
182 }\r
183 \r
184 /////////////////////////////////////////\r
185 \r
186 void tlc_send_dc(void)\r
187 {\r
188   \r
189   for (int rgb = 2; rgb != -1; rgb--) {\r
190     uint8_t dc_data = dc_buffer[rgb] & _B(1, 1, 1, 1, 1, 1, 0, 0);\r
191     uint8_t dc_out[3] = {\r
192       (dc_data << 0) | (dc_data >> 6),\r
193       (dc_data << 2) | (dc_data >> 4),\r
194       (dc_data << 4) | (dc_data >> 2)\r
195     };\r
196 \r
197     for (int i = 0; i < N_TLC_CHANNELS; i++) {\r
198       shift8(dc_out[i % 3]);\r
199     }\r
200   }\r
201 }\r
202 \r
203 void tlc_send_gs(void)\r
204 {\r
205   int16_t offset = N_TLC_CHANNELS - 1;\r
206   while (1) {\r
207     shift12(gs_buffer[offset]);\r
208 \r
209     offset -= 3;\r
210     if (offset < 0) {\r
211       offset += N_TLC_CHANNELS - 1; // Jump to end again, next color implicit\r
212       if (offset != N_TLC_CHANNELS - 1 - 3)\r
213         break;\r
214     }\r
215   }\r
216 }\r
217 \r
218 void tlc_update(void)\r
219 {\r
220   // Always shift out DC first.\r
221   tlc_send_dc();\r
222   clock_xlat();\r
223 \r
224   // No extra SCLK needed, just shift out all GS data.\r
225   tlc_send_gs();\r
226   clock_xlat();\r
227 }\r
228 \r
229 /////////////////////////////////////////\r