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