d2c0421c7d6aa819e9931e0f17ed49703fcbe512
[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 inline 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   set_blnk_on();\r
84 \r
85   // Timer 1 is for our GSCLK:  We refresh with a GS cycle of\r
86   // about 100 Hz (cf. Timer 2), for each full cycle we need to\r
87   // clock the PWM 4096 times.\r
88   // Disable output for now.\r
89   mcu_pin_timer2_ocm_disable();\r
90   // Shortest duty cycle possible.\r
91   mcu_set_timer1_ocma(1);\r
92   // We need about 39 clocks to get 4096 cycles at 100 Hz.\r
93   mcu_set_timer1_ic(39);\r
94   // * CS1 = 0001:  No prescaler. (p100)\r
95   // * WGM1 = 1110: Fast PWM, TOP at ICR1\r
96   // * COM1A = 10: Set at 0, clear at Compare Match)\r
97   TCCR1B = _BV(CS10) | _BV(WGM13) | _BV(WGM12);\r
98   TCCR1A = _BV(WGM11) | _BV(COM1A1);\r
99 \r
100   /* Timer 2: Refresh-Timer */\r
101   // * AS2 = 0: Use IO-clock (16 MHz) for base frequency (p119)\r
102   // * Mode: Normal mode, no PWM, count upwards (WGM21:0 = 00) (p117)\r
103   // * Disable Output on OC2, needed for SPI (COM21:0 = 00) (p117)\r
104   // * Prescaler: 1024 (CS22:0 = 111) => 15625 Hz\r
105   TCCR2 = _BV(CS22) | _BV(CS21) | _BV(CS20);\r
106   // To get a 100 Hz clock we need to count 157 times (~ 99.5 Hz).\r
107   mcu_set_timer1_ic(156);\r
108 \r
109   // Wait for first DMX packet.\r
110   sched_put(&wait_for_data);\r
111 }\r
112 \r
113 void tlc_start(void)\r
114 {\r
115   g_data_done = 1;\r
116 }\r
117 \r
118 /////////////////////////////////////////\r
119 \r
120 void tlc_start_gscycle(void)\r
121 {\r
122   // Start counter with next GS pulse.\r
123   mcu_int_timer1_ocma_enable();\r
124 }\r
125 \r
126 void tlc_start_gscycle_timeout(void)\r
127 {\r
128   // First, disable this interrupt.\r
129   mcu_int_timer1_ocma_disable();\r
130 \r
131   // Enable PWM output.\r
132   mcu_pin_timer1_ocma_enable();\r
133 \r
134   // Restart and enable timeout timer.\r
135   mcu_set_timer2_cnt(0);\r
136   mcu_int_timer2_ocm_enable();\r
137 \r
138   // Switch off BLNK.\r
139   set_blnk_off();\r
140 }\r
141 \r
142 void tlc_stop_gscycle(void)\r
143 {\r
144   // Go into BLNK mode (switch off LEDs and reset GSCLK counter)\r
145   set_blnk_on();\r
146 \r
147   // Disable GS cycle timeout timer.\r
148   mcu_int_timer2_ocm_disable();\r
149 \r
150   // Wait for next DMX packet.\r
151   sched_put(&wait_for_data);\r
152   // TODO: next data\r
153   sched_put(&buffer_test_next);\r
154 }\r
155 \r
156 /////////////////////////////////////////\r
157 \r
158 void shift8(uint8_t byte)\r
159 {\r
160   for (uint8_t bit = _B(1, 0, 0, 0, 0, 0, 0, 0); bit; bit >>= 1) {\r
161         if (bit & byte) {\r
162       pin_on(PIN_TLC_SIN);\r
163     } else {\r
164       pin_off(PIN_TLC_SIN);\r
165     }\r
166     clock_sclk();\r
167   }\r
168 }\r
169 \r
170 void shift12(uint8_t byte)\r
171 {\r
172   // The data in the upper 8 bits.\r
173   shift8(byte);\r
174 \r
175   // Plus 4 zero bits (makes a shift by 4).\r
176   pin_off(PIN_TLC_SIN);\r
177   for (uint8_t bit = _B(0, 0, 0, 0, 1, 0, 0, 0); bit; bit >>= 1) {\r
178     pin_on(PIN_TLC_SCLK);\r
179     pin_off(PIN_TLC_SCLK);\r
180   }\r
181 }\r
182 \r
183 /////////////////////////////////////////\r
184 \r
185 void tlc_send_dc(void)\r
186 {\r
187   \r
188   for (int rgb = 2; rgb != -1; rgb--) {\r
189     uint8_t dc_data = dc_buffer[rgb] & _B(1, 1, 1, 1, 1, 1, 0, 0);\r
190     uint8_t dc_out[3] = {\r
191       (dc_data << 0) | (dc_data >> 6),\r
192       (dc_data << 2) | (dc_data >> 4),\r
193       (dc_data << 4) | (dc_data >> 2)\r
194     };\r
195 \r
196     for (int i = 0; i < N_TLC_CHANNELS; i++) {\r
197       shift8(dc_out[i % 3]);\r
198     }\r
199   }\r
200 }\r
201 \r
202 void tlc_send_gs(void)\r
203 {\r
204   int16_t offset = N_TLC_CHANNELS - 1;\r
205   while (1) {\r
206     shift12(gs_buffer[offset]);\r
207 \r
208     offset -= 3;\r
209     if (offset < 0) {\r
210       offset += N_TLC_CHANNELS - 1; // Jump to end again, next color implicit\r
211       if (offset != N_TLC_CHANNELS - 1 - 3)\r
212         break;\r
213     }\r
214   }\r
215 }\r
216 \r
217 void tlc_update(void)\r
218 {\r
219   // Always shift out DC first.\r
220   tlc_send_dc();\r
221   clock_xlat();\r
222 \r
223   // No extra SCLK needed, just shift out all GS data.\r
224   tlc_send_gs();\r
225   clock_xlat();\r
226 }\r
227 \r
228 /////////////////////////////////////////\r