Explain what the global variables do.
[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          int16_t index_;
28
29
30 /*********************************************************************/
31 /* Declaration of private functions.                                 */
32
33 static void enable_timer(int8_t us);
34 static void disable_timer(void);
35 static void enable_trigger(void);
36 static void disable_trigger(void);
37 static void enable_usart(void);
38 static void disable_usart(void);
39
40
41 /*********************************************************************/
42 /* Implementation of public interrupts.                              */
43
44 void dmx_int_timer0_ovf(void)
45 {
46   // Disable this interrupt.
47   disable_timer();
48
49   switch (state_) {
50     case STATE_SYNC: {
51       // Line was low for 88 us, all is fine.
52       state_ = STATE_WAIT;
53       break;
54     }
55     case STATE_RECV:
56     case STATE_STOR: {
57     // We got a timeout, back to Idle.
58       disable_usart();
59       enable_trigger();
60       state_ = STATE_IDLE;
61       break;
62     }
63     default: {
64       break;
65     }
66   }
67 }
68
69 void dmx_int_ext(void)
70 {
71   switch (state_) {
72     case STATE_IDLE: {
73       // Only trigger on fallen edge.
74       if (!pin_is_set(PIN_DMX_RXD)) {
75         // Wait for 88 us.
76         enable_timer(88);;
77         state_ = STATE_SYNC;
78       }
79       break;
80     }
81     case STATE_SYNC: {
82       // Got a stray edge while Reset, back to Idle.
83       disable_timer();
84       state_ = STATE_IDLE;
85       break;
86     }
87     case STATE_WAIT: {
88       // This edge is the Mark, start the USART and disable
89       // this interrupt.
90       disable_trigger();
91       enable_usart();
92       state_ = STATE_RECV;
93       break;
94     }
95     default: {
96       break;
97     }
98   }
99 }
100
101 void dmx_int_usart_rxc(void)
102 {
103   // Read USART data (p146).
104   uint8_t rxd;
105   uint8_t err;
106   // Read Frame Error flag.
107   err = UCSRA & bits_value(FE);
108   // Read data byte (and clear RXC flag).
109   rxd = UDR;
110   if (err) {
111     goto last;
112   }
113
114   switch (state_) {
115     case STATE_RECV: {
116       // TODO: Check for valid start byte.
117       /*if (rxd != 0x00) {
118       goto last;
119     }*/
120
121       // Switch to data storage.
122       index_ = 0;
123       state_ = STATE_STOR;
124       break;
125     }
126     case STATE_STOR: {
127       // Write byte to buffer.
128       buf_gs__[index_] = rxd;
129       // Next index.
130       index_++;
131       // TODO: Don't store more data than necessary.
132       if (index_ == 512) {
133         goto last;
134       }
135       break;
136     }
137     default: {
138       break;
139     }
140   }
141
142   return;
143 last:
144   // Either an invalid or the last frame appeared, stop DMX.
145     disable_usart();
146   enable_trigger();
147   state_ = STATE_IDLE;
148   return;
149 }
150
151
152 /*********************************************************************/
153 /* Implementation of public functions.                               */
154
155 void dmx_init(void)
156 {
157   // Configure both pins as input.
158   pin_in(PIN_DMX_INT);
159   pin_in(PIN_DMX_RXD);
160
161   // Initialize USART (p156) with
162   // 250kbaud    (UBRR = 3),
163   // 8 data bits (UCSZ = 011),
164   // 2 stop bits (USBS = 1),
165   // no parity   (UPM  = 00).
166   UBRRL = F_CPU / (16 * 250e3) - 1;
167   UBRRH = (0 << URSEL) | 0;
168   UCSRC = (1 << URSEL)
169         | bits_value(UCSZ1) | bits_value(UCSZ0)
170         | bits_value(USBS);
171   // Enable USART RXD interrupt (and clear UCSZ2 and *XEN).
172   UCSRB = bits_value(RXCIE);
173
174   // Enable timer interrupt (p72).
175   bits_on(TIMSK, TOIE0);
176
177   // Trigger INT0 on any edge (ISC0 = 01, p67).
178   bits_on(MCUCR, ISC00);
179   // But explicitly disable it for now.
180   disable_trigger();
181 }
182
183 void dmx_exec(void)
184 {
185   // Just enable the trigger for the pin.
186   enable_trigger();
187 }
188
189
190 /*********************************************************************/
191 /* Implementation of private functions.                              */
192
193 static void enable_timer(int8_t us)
194 {
195   // Prepare timer counter.
196   TCNT0 = 0xFF - 2 * us;
197
198   // Enable timer, use a frequency prescaler of 8 (p72).
199   bits_mask_on(TCCR0, (1 << CS01));
200 }
201
202 static void disable_timer(void)
203 {
204   // Disable timer (p72).
205   bits_mask_off(TCCR0, (1 << CS02) | (1 << CS01) | (1 << CS00));
206 }
207
208
209 static void enable_trigger(void)
210 {
211   // Enable interrupt triggered by edge on pin.
212   bits_on(GICR, INT0);
213 }
214
215 static void disable_trigger(void)
216 {
217   // Disable interrupt triggered by edge on pin.
218   bits_off(GICR, INT0);
219 }
220
221 static void enable_usart(void)
222 {
223   // Enable RXD.
224   bits_on(UCSRB, RXEN);
225 }
226
227 static void disable_usart(void)
228 {
229   // Disable RXD.
230   bits_off(UCSRB, RXEN);
231 }