cc59553e00c56cacfc78f64c1ff41478bad142a7
[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(UCSZ1) | bits_value(UCSZ0)
217         | bits_value(USBS);
218   // Enable USART RXD interrupt (and clear UCSZ2 and *XEN).
219   UCSRB = bits_value(RXCIE);
220
221   // Enable timer interrupt (p72).
222   bits_on(TIMSK, TOIE0);
223
224   // Trigger INT0 on any edge (ISC0 = 01, p67).
225   bits_on(MCUCR, ISC00);
226   // But explicitly disable it for now.
227   disable_trigger();
228 }
229
230 void dmx_exec(void)
231 {
232   // Just enable the trigger for the pin.
233   enable_trigger();
234 }
235
236
237 /*********************************************************************/
238 /* Implementation of private functions.                              */
239
240 static void enable_timer(int8_t us)
241 {
242   // Prepare timer counter.
243   timer_ = 0xFF - 2 * us;
244   reset_timer();
245
246   // Enable timer, use a frequency prescaler of 8 (p72).
247   bits_mask_on(TCCR0, (1 << CS01));
248 }
249
250 static void disable_timer(void)
251 {
252   // Disable timer (p72).
253   bits_mask_off(TCCR0, (1 << CS02) | (1 << CS01) | (1 << CS00));
254 }
255
256 static void reset_timer()
257 {
258   TCNT0 = timer_;
259 }
260
261
262 static void enable_trigger(void)
263 {
264   // Enable interrupt triggered by edge on pin.
265   bits_on(GICR, INT0);
266 }
267
268 static void disable_trigger(void)
269 {
270   // Disable interrupt triggered by edge on pin.
271   bits_off(GICR, INT0);
272 }
273
274 static void enable_usart(void)
275 {
276
277   // Enable RXD.
278   bits_on(UCSRB, RXEN);
279 }
280
281 static void disable_usart(void)
282 {
283   // Disable RXD.
284   bits_off(UCSRB, RXEN);
285 }