977422d10e41845e964808eddd38f9fef39556cd
[dmxpainter.git] / src / dmx.c
1 #include "dmx.h"
2
3 #include "mcu.h"
4
5 #include "buf.h"
6
7 /*********************************************************************/
8 /* Declaration of private global variables.                          */
9
10 /**
11  * The DMX parser is driven by a state machine using these states.
12  */
13 enum state {
14   STATE_IDLE,
15   STATE_SYNC,
16   STATE_WAIT,
17   STATE_RECV,
18   STATE_STOR
19 };
20 /**
21  * The current state of the DMX state machine.
22  */
23 #ifndef REG_DMX_STATE
24   static volatile enum state state_;
25 #else
26   #define state_ REG_DMX_STATE
27 #endif
28
29 /**
30  * Index of current DMX frame (between 0 and 512).
31  */
32 static volatile int16_t index_;
33
34 /**
35  * Current timer start value in case we want to reset.
36  */
37 static volatile uint8_t timer_;
38
39 /**
40  * Error flag, set on error, cleared after next start byte.
41  */
42 static volatile uint8_t error_;
43
44
45 /*********************************************************************/
46 /* Declaration of private functions.                                 */
47
48 static void enable_timer(int8_t us);
49 static void disable_timer(void);
50 static void reset_timer(void);
51
52 static void enable_trigger(void);
53 static void disable_trigger(void);
54
55 static void enable_usart(void);
56 static void disable_usart(void);
57
58
59 /*********************************************************************/
60 /* Declaration of private constants.                                 */
61
62 #define DMX_RESET_TIME   88
63 #define DMX_BIT_TIME     4
64 #define DMX_CHAR_TIME    (DMX_BIT_TIME * (8 + 3))
65 #define DMX_CHAR_TIMEOUT (DMX_CHAR_TIME * 2)
66
67
68 /*********************************************************************/
69 /* Implementation of public interrupts.                              */
70
71 void dmx_int_ext_edge(void)
72 {
73   switch (state_) {
74     // The line might be pulled low in Idle.
75     case STATE_IDLE: {
76       // Only trigger on fallen edge.
77       if (!pin_is_set(PIN_DMX_RXD)) {
78         // Wait for a Reset of 88 us (or more).
79         enable_timer(DMX_RESET_TIME);
80         state_ = STATE_SYNC;
81       }
82     } break;
83
84     // Got a stray edge while Reset, back to Idle.
85     case STATE_SYNC: goto err;
86
87     // The next rising edge is the Mark.
88     case STATE_WAIT: {
89       // Disable this interrupt...
90       disable_trigger();
91       // ... and start the USART and the timeout.
92       enable_usart();
93       enable_timer(DMX_CHAR_TIMEOUT);
94       state_ = STATE_RECV;
95     } break;
96
97     // Ignore edge in all other states.
98     default: break;
99   }
100   return;
101
102 err: {
103     error_ = 1;
104     disable_timer();
105     state_ = STATE_IDLE;
106     return;
107   }
108 }
109
110 void dmx_int_timer_ovf(void)
111 {
112   // Disable this interrupt.
113   disable_timer();
114
115   switch (state_) {
116     // Line was low long enough for a Reset.
117     case STATE_SYNC: {
118       // All is fine.
119       state_ = STATE_WAIT;
120     } break;
121
122     // We got a timeout, back to Idle.
123     case STATE_RECV: goto err;
124     case STATE_STOR: goto err;
125
126     // Ignore timer in all other states.
127     default: break;
128   }
129   return;
130
131 err: {
132     error_ = 1;
133     disable_usart();
134     enable_trigger();
135     state_ = STATE_IDLE;
136     return;
137   }
138 }
139
140 void dmx_int_usart_rxc(void)
141 {
142   // Read USART data (p146).
143   uint8_t rxd;
144   uint8_t err;
145   // Read Frame Error flag.
146   err = UCSRA & bits_value(FE);
147   // Read data byte (and clear RXC flag).
148   rxd = UDR;
149   // Bail out if start or stop bit was wrong.
150   if (err) {
151     goto err;
152   }
153
154   // Reset timeout.
155   reset_timer();
156
157   switch (state_) {
158     // The first byte is the start byte.
159     case STATE_RECV: {
160       // Bail out if the start byte is not all low.
161       if (rxd != 0x00) {
162         goto err;
163       }
164
165       // Clear error flag.
166       error_ = 0; 
167
168       // Switch to data storage.
169       index_ = 0;
170       state_ = STATE_STOR;
171     } break;
172
173     // All following bytes are stored as channel data.
174     case STATE_STOR: {
175       // Write byte to buffer.
176       buf_gs__[index_] = rxd;
177       // Jump out once we received all 512 channels.
178       if (index_ == 511) {
179         goto end;
180       }
181       // Next index.
182       index_++;
183     } break;
184
185     // In all other states we shouldn't trigger anyway.
186     default: break;
187   }
188   return;
189
190 err:
191   error_ = 1;
192   // Resume with default end procedure.
193 end: {
194     // Either an invalid or the last frame appeared, stop DMX.
195     disable_timer();
196     disable_usart();
197     enable_trigger();
198     state_ = STATE_IDLE;
199     return;
200   }
201 }
202
203
204 /*********************************************************************/
205 /* Implementation of public functions.                               */
206
207 void dmx_init(void)
208 {
209   // Initialize state (register), might be needed (p20) and 
210   // doesn't hurt.
211   state_ = STATE_IDLE;
212
213   // Configure both pins as input.
214   pin_in(PIN_DMX_INT);
215   pin_in(PIN_DMX_RXD);
216
217   // Initialize USART (p156) with
218   // 250kbaud    (UBRR = 3),
219   // 8 data bits (UCSZ = 011),
220   // 2 stop bits (USBS = 1),
221   // no parity   (UPM  = 00).
222   UBRRL = F_CPU / (16 * 250e3) - 1;
223   UBRRH = (0 << URSEL) | 0;
224   UCSRC = (1 << URSEL)
225         | bits_value_indexed(UCSZ, 1)
226         | bits_value_indexed(UCSZ, 0)
227         | bits_value(USBS);
228   // Enable USART RXD interrupt (and clear UCSZ2 and *XEN).
229   UCSRB = bits_value(RXCIE);
230
231   // Enable timer interrupt (p72).
232   bits_on(TIMSK, TOIE0);
233
234   // Trigger INT0 on any edge (ISC0 = 01, p67).
235   bits_on(MCUCR, ISC00);
236   // But explicitly disable it for now.
237   disable_trigger();
238 }
239
240 void dmx_exec(void)
241 {
242   // Just enable the trigger for the pin.
243   enable_trigger();
244 }
245
246
247 /*********************************************************************/
248 /* Implementation of private functions.                              */
249
250 static void enable_timer(int8_t us)
251 {
252   // Prepare timer counter.
253   timer_ = 0xFF - 2 * us;
254   reset_timer();
255
256   // Enable timer, use a frequency prescaler of 8 (p72).
257   bits_mask_on(TCCR0, (1 << CS01));
258 }
259
260 static void disable_timer(void)
261 {
262   // Disable timer (p72).
263   bits_mask_off(TCCR0, (1 << CS02) | (1 << CS01) | (1 << CS00));
264 }
265
266 static void reset_timer()
267 {
268   TCNT0 = timer_;
269 }
270
271
272 static void enable_trigger(void)
273 {
274   // Enable interrupt triggered by edge on pin.
275   bits_on(GICR, INT0);
276 }
277
278 static void disable_trigger(void)
279 {
280   // Disable interrupt triggered by edge on pin.
281   bits_off(GICR, INT0);
282 }
283
284 static void enable_usart(void)
285 {
286
287   // Enable RXD.
288   bits_on(UCSRB, RXEN);
289 }
290
291 static void disable_usart(void)
292 {
293   // Disable RXD.
294   bits_off(UCSRB, RXEN);
295 }