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