initial commit
[usb-nixie.git] / nixie.c
1 /* Teensy Serial to OGI Lumen Nixie Driver board  rev 0.1
2  * 
3  * Based on (read as "a quick hack of"):
4  * Simple example for Teensy USB Development Board
5  * http://www.pjrc.com/teensy/
6  * Copyright (c) 2008 PJRC.COM, LLC
7  * 
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  * 
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  * 
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  */
26
27 /* 
28  * Nixie Driver Pin     Teensy Pin
29  * -------              ---------
30  * 2  GND               GND
31  * 5  Vcc               +5v
32  * 1  SER               D4
33  * 3  SCK               D5
34  * 4  RCK               D6
35  * 6  NC                -
36  *
37  * For 3d-ish effects:
38  * Nixie digit order, front to back:  3894057621
39  */
40
41 #include <avr/io.h>
42 #include <avr/pgmspace.h>
43 #include <stdint.h>
44 #include <util/delay.h>
45 #include "usb_serial.h"
46
47 #define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
48 #define NIXIE_SER       (1 << 4)
49 #define NIXIE_SCK       (1 << 5)
50 #define NIXIE_RCK       (1 << 6)
51
52 void send_str(const char *s);
53 uint8_t recv_str(char *buf, uint8_t size);
54 void parse_and_execute_command(const char *buf, uint8_t num);
55
56 void nixie_init(void)
57 {
58         PORTD &= ~(NIXIE_SER);
59         PORTD &= ~(NIXIE_SCK);
60         PORTD &= ~(NIXIE_RCK);
61         DDRD |= (NIXIE_SER | NIXIE_SCK | NIXIE_RCK);
62 }
63
64 void nixie_send_digit(uint8_t digit)
65 {
66         for (int i=0; i < 4; i++) {
67                 PORTD &= ~(NIXIE_SCK);  
68                 if ((digit & 0x8) != 0) {
69                         PORTD |= NIXIE_SER;
70                 } else {
71                         PORTD &= ~(NIXIE_SER);  
72                 }
73                 _delay_us(10);
74                 PORTD |= NIXIE_SCK;
75                 _delay_us(10);
76                 digit <<= 1;
77         }
78         PORTD &= ~(NIXIE_SCK);
79         PORTD |= NIXIE_SER;
80         _delay_us(10);
81 }
82
83 void nixie_show(void)
84 {
85         PORTD |= NIXIE_RCK;
86         _delay_us(10);
87         PORTD &= ~(NIXIE_RCK);
88         _delay_us(10);
89 }
90
91 int main(void)
92 {
93         char buf[32];
94         uint8_t n;
95
96         // set for 16 MHz clock, and turn on the LED
97         CPU_PRESCALE(0);
98
99         // initialize the USB, and then wait for the host
100         // to set configuration.  If the Teensy is powered
101         // without a PC connected to the USB port, this 
102         // will wait forever.
103         usb_init();
104         while (!usb_configured()) /* wait */ ;
105         _delay_ms(1000);
106         nixie_init();
107         _delay_ms(2);
108         nixie_send_digit(8);
109         nixie_send_digit(7);
110         nixie_send_digit(6);
111         nixie_send_digit(5);
112         nixie_send_digit(4);
113         nixie_send_digit(3);
114         nixie_send_digit(2);
115         nixie_send_digit(1);
116         nixie_show();
117
118         while (1) {
119                 // wait for the user to run their terminal emulator program
120                 // which sets DTR to indicate it is ready to receive.
121                 while (!(usb_serial_get_control() & USB_SERIAL_DTR)) /* wait */ ;
122
123                 // discard anything that was received prior.  Sometimes the
124                 // operating system or other software will send a modem
125                 // "AT command", which can still be buffered.
126                 usb_serial_flush_input();
127
128                 // print a nice welcome message
129                 send_str(PSTR("\r\nTeensy USB Serial Example, "
130                         "Simple Pin Control Shell\r\n\r\n"
131                         "Example Commands\r\n"
132                         "  B0?   Read Port B, pin 0\r\n"
133                         "  C2=0  Write Port C, pin 1 LOW\r\n"
134                         "  D6=1  Write Port D, pin 6 HIGH  (D6 is LED pin)\r\n\r\n"));
135
136                 // and then listen for commands and process them
137                 while (1) {
138                         send_str(PSTR("> "));
139                         n = recv_str(buf, sizeof(buf));
140                         if (n == 255) break;
141                         send_str(PSTR("\r\n"));
142                         // parse_and_execute_command(buf, n);
143                 }
144         }
145 }
146
147 // Send a string to the USB serial port.  The string must be in
148 // flash memory, using PSTR
149 //
150 void send_str(const char *s)
151 {
152         char c;
153         while (1) {
154                 c = pgm_read_byte(s++);
155                 if (!c) break;
156                 usb_serial_putchar(c);
157         }
158 }
159
160 // Receive a string from the USB serial port.  The string is stored
161 // in the buffer and this function will not exceed the buffer size.
162 // A carriage return or newline completes the string, and is not
163 // stored into the buffer.
164 // The return value is the number of characters received, or 255 if
165 // the virtual serial connection was closed while waiting.
166 //
167 uint8_t recv_str(char *buf, uint8_t size)
168 {
169         int16_t r;
170         uint8_t count=0;
171
172         while (count < size) {
173                 r = usb_serial_getchar();
174                 if (r != -1) {
175                         if (r == '\r' || r == '\n') {
176                                 nixie_show();   
177                                 return count;
178                         }
179                         if (r >= '0' && r <= '9') {
180                                 *buf++ = r;
181                                 usb_serial_putchar(r);
182                                 nixie_send_digit(r);
183                                 count++;
184                         } else if (r == ' ') {
185                                 usb_serial_putchar(r);
186                                 nixie_send_digit(10);
187                         }
188                 } else {
189                         if (!usb_configured() ||
190                           !(usb_serial_get_control() & USB_SERIAL_DTR)) {
191                                 // user no longer connected
192                                 return 255;
193                         }
194                         // just a normal timeout, keep waiting
195                 }
196         }
197         return count;
198 }
199
200 // parse a user command and execute it, or print an error message
201 //
202 void parse_and_execute_command(const char *buf, uint8_t num)
203 {
204         uint8_t port, pin, val;
205
206         if (num < 3) {
207                 send_str(PSTR("unrecognized format, 3 chars min req'd\r\n"));
208                 return;
209         }
210         // first character is the port letter
211         if (buf[0] >= 'A' && buf[0] <= 'F') {
212                 port = buf[0] - 'A';
213         } else if (buf[0] >= 'a' && buf[0] <= 'f') {
214                 port = buf[0] - 'a';
215         } else {
216                 send_str(PSTR("Unknown port \""));
217                 usb_serial_putchar(buf[0]);
218                 send_str(PSTR("\", must be A - F\r\n"));
219                 return;
220         }
221         // second character is the pin number
222         if (buf[1] >= '0' && buf[1] <= '7') {
223                 pin = buf[1] - '0';
224         } else {
225                 send_str(PSTR("Unknown pin \""));
226                 usb_serial_putchar(buf[0]);
227                 send_str(PSTR("\", must be 0 to 7\r\n"));
228                 return;
229         }
230         // if the third character is a question mark, read the pin
231         if (buf[2] == '?') {
232                 // make the pin an input
233                 *(uint8_t *)(0x21 + port * 3) &= ~(1 << pin);
234                 // read the pin
235                 val = *(uint8_t *)(0x20 + port * 3) & (1 << pin);
236                 usb_serial_putchar(val ? '1' : '0');
237                 send_str(PSTR("\r\n"));
238                 return;
239         }
240         // if the third character is an equals sign, write the pin
241         if (num >= 4 && buf[2] == '=') {
242                 if (buf[3] == '0') {
243                         // make the pin an output
244                         *(uint8_t *)(0x21 + port * 3) |= (1 << pin);
245                         // drive it low
246                         *(uint8_t *)(0x22 + port * 3) &= ~(1 << pin);
247                         return;
248                 } else if (buf[3] == '1') {
249                         // make the pin an output
250                         *(uint8_t *)(0x21 + port * 3) |= (1 << pin);
251                         // drive it high
252                         *(uint8_t *)(0x22 + port * 3) |= (1 << pin);
253                         return;
254                 } else {
255                         send_str(PSTR("Unknown value \""));
256                         usb_serial_putchar(buf[3]);
257                         send_str(PSTR("\", must be 0 or 1\r\n"));
258                         return;
259                 }
260         }
261         // otherwise, error message
262         send_str(PSTR("Unknown command \""));
263         usb_serial_putchar(buf[0]);
264         send_str(PSTR("\", must be ? or =\r\n"));
265 }
266
267