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