Commits

Trammell Hudson committed 36cf26b Merge

merged

Comments (0)

Files changed (3)

 
 
 # Target file name (without extension).
-TARGET = teletype
+TARGET ?= teletype
 
 
 # List C source files here. (C dependencies are automatically generated.)
 #   so your program will run at the correct speed.  You should also set this
 #   variable to same clock speed.  The _delay_ms() macro uses this, and many
 #   examples use this variable to calculate timings.  Do not add a "UL" here.
-F_CPU = 16000000
+F_CPU = 4000000
 
 
 # Output format. (can be srec, ihex, binary)
 
 # Define programs and commands.
 SHELL = sh
-AVR_PATH = /Applications/Arduino.app/Contents//Resources/Java/hardware/tools/avr
+#AVR_PATH = /Applications/Arduino.app/Contents//Resources/Java/hardware/tools/avr
+AVR_PATH = $(HOME)/arduino-1.0.5/hardware/tools/avr
 CC = $(AVR_PATH)/bin/avr-gcc
 OBJCOPY = $(AVR_PATH)/bin/avr-objcopy
 OBJDUMP = $(AVR_PATH)/bin/avr-objdump
+/**
+ * \file 110 current loop interface for an ASR33
+ *
+ * Can also turn off the teletype with a powerswitch tail on D0
+ * and a wakeup switch on D1.
+ *
+ * ASR33 Wiring:   Teensy Wiring
+ * TX+ == Green    Vcc
+ * TX- == Red      PD2 (RXD1), tie to ground via 330 Ohm
+ * RX+ == Brown    PD3 (TXD1)
+ * RX- == Yellow   330 Ohm resistor to ground
+ *
+ * Teensy clock 4 MHz
+ * UBRR1 = 2272
+ * UCSR1C = | (1 << UPM11) // even parity
+ *          | (0 << UPM10)
+ *          | (0 << UCSZ12) // 7 data bits
+ *          | (1 << UCSZ11)
+ *          | (0 << UCSZ10)
+ *          | (1 << USBS1) // 2 stop bits
+ */
+
+#include <avr/io.h>
+#include <avr/pgmspace.h>
+#include <avr/interrupt.h>
+#include <stdint.h>
+#include <string.h>
+#include <util/delay.h>
+#include "usb_serial.h"
+#include "bits.h"
+
+#define CONFIG_FOLD_CASE
+
+// Send a string to the USB serial port.  The string must be in
+// flash memory, using PSTR
+//
+void send_str(const char *s)
+{
+	char c;
+	while (1) {
+		c = pgm_read_byte(s++);
+		if (!c) break;
+		usb_serial_putchar(c);
+	}
+}
+
+
+static char
+hexdigit(
+	uint8_t x
+)
+{
+	x &= 0xF;
+	if (x < 0xA)
+		return x + '0';
+	else
+		return x + 'A' - 0xA;
+}
+
+//#define TTY_RX 0xD2
+#define TTY_RX 0xD2
+#define TTY_TX 0xD3
+#define TTY_DEBUG 0xD7
+#define BIT_CLOCK 9090
+
+
+static void
+tty_out(
+	uint8_t c
+)
+{
+#if 0
+	// start bit
+	out(TTY_TX, 1);
+
+	_delay_us(BIT_CLOCK);
+	uint8_t parity = 1;
+
+	for (int i = 0 ; i < 7 ; i++)
+	{
+		int x = c & 1;
+		c >>= 1;
+
+		if (x)
+		{
+			out(TTY_TX, 0);
+			parity++;
+		} else {
+			out(TTY_TX, 1);
+		}
+
+		_delay_us(BIT_CLOCK);
+	}
+
+	out(TTY_TX, parity);
+	_delay_us(BIT_CLOCK);
+
+	out(TTY_TX, 0);
+	_delay_us(BIT_CLOCK);
+	_delay_us(BIT_CLOCK);
+#else
+	while (bit_is_clear(UCSR1A, UDRE1))
+		;
+	UDR1 = c;
+#endif
+}
+
+
+/**
+ * Enable ADC and select input ADC7 / F7
+ * Select high-speed mode, left aligned
+ * Use clock divisor of 2
+ * System clock is 16 MHz on teensy, 8 MHz on tiny,
+ * conversions take 13 ticks, so divisor == 128 (1,1,1) should
+ * give 9.6 KHz of samples.
+ */
+static void
+adc_init(void)
+{
+	ADMUX = 0
+		| 7
+		| (0 << REFS1)
+		| (1 << REFS0)
+		;
+
+	ADCSRA = 0
+		| (1 << ADEN) // enable ADC
+		| (0 << ADSC) // don't start yet
+		| (0 << ADIE) // don't enable the interrupts
+		| (0 << ADPS2)
+		| (1 << ADPS1)
+		| (1 << ADPS0)
+		;
+
+	ADCSRB = 0
+		| (1 << ADHSM) // enable highspeed mode
+		;
+
+	DDRF = 0;
+
+	// Start the first conversion!
+	sbi(ADCSRA, ADSC);
+}
+
+
+static uint16_t
+adc_read_block(void)
+{
+	// Wait for the conversion to complete
+	while (bit_is_set(ADCSRA, ADSC))
+		continue;
+
+	const uint16_t value = ADC;
+
+	// Start the next conversion
+	sbi(ADCSRA, ADSC);
+
+	// Read the value
+	return value;
+}
+
+
+
+static uint16_t
+tty_in(void)
+{
+	uint16_t byte = 0;
+
+#undef CONFIG_WAVEFORM
+#ifndef CONFIG_WAVEFORM
+	// 1 start bit, 7 data, 1 parity, 2 stop bits
+#define NUM_BITS (1 + 7 + 1 + 2)
+
+	for (int bit = 0 ; bit < NUM_BITS ; bit++)
+	{
+		if (bit == 0)
+			_delay_us(BIT_CLOCK/4*2);
+		else
+			_delay_us(BIT_CLOCK);
+
+		out(TTY_DEBUG, bit & 1);
+		if (in(TTY_RX))
+			byte = (byte >> 1) | (((uint16_t) 1) << NUM_BITS);
+		else
+			byte >>= 1;
+	}
+
+	out(TTY_DEBUG, 1);
+#else
+#define TTY_BITS 64
+	char buf[TTY_BITS];
+	adc_read_block();
+
+	for (int bit = 0 ; bit < TTY_BITS ; bit++)
+	{
+		//int x = in(TTY_RX);
+		//buf[bit] = x ? '0' : '1';
+		int x = adc_read_block();
+		buf[bit] = hexdigit(x >> 6);
+		_delay_us(BIT_CLOCK/4);
+// 220000BB9045608B7000078B79AB9000099DA8BBADCC998A9A9AAAAABBCBBBBB
+// 22 0000 BB90 4560 8B70 0007 8B79 AB90 0009 9DA8 BBAD CC99 8A9A 9AAAAABBCBBBBB
+	}
+	usb_serial_write(buf, sizeof(buf));
+	usb_serial_putchar('\r');
+	usb_serial_putchar('\n');
+#endif
+
+	return byte;
+}
+
+
+int main(void)
+{
+	// set for 4 MHz clock
+#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
+	CPU_PRESCALE(2);
+
+	// Disable the ADC
+	ADMUX = 0;
+	adc_init();
+
+	ddr(TTY_TX, 1);
+	out(TTY_TX, 0); // pull down
+
+// About 1 minute = 4000000
+#define POWER_OFF_DELAY (1000000)
+#define POWER_RELAY 0xD0
+#define POWER_SWITCH 0xD1
+
+	ddr(POWER_SWITCH, 0); // input
+	out(POWER_SWITCH, 1); // pull up
+
+	// be sure TX has reached a steady state
+	_delay_ms(100);
+	ddr(POWER_RELAY, 1); // output
+	out(POWER_RELAY, 1); // high
+	uint8_t power = 1;
+
+
+	// D2 is used for input (since it is also a UART)
+	// it has an external pull up resistor to +5v
+	ddr(TTY_RX, 0);
+	out(TTY_RX, 0);
+
+	ddr(TTY_DEBUG, 1);
+	ddr(TTY_DEBUG, 1);
+
+#define CONFIG_ASR33_UART
+#ifdef CONFIG_ASR33_UART
+	// 110 E72 is too SLOW at 16 MHz.  Wow.
+	// UBRR is 12 bits, max 4096.  So we run at 4 MHz
+	// to keep UBRR0 in range.
+	UBRR1 = 2272;
+	UCSR1B = 0
+		| (1 << RXEN1)
+		| (1 << TXEN1)
+		;
+
+	UCSR1C = 0
+		| (1 << UPM11) // even parity
+		| (0 << UPM10)
+		| (0 << UCSZ12) // 7 data bits
+		| (1 << UCSZ11)
+		| (0 << UCSZ10)
+		| (1 << USBS1) // 2 stop bits
+		;
+#endif
+
+	// initialize the USB, and then wait for the host
+	// to set configuration.  If the Teensy is powered
+	// without a PC connected to the USB port, this 
+	// will wait forever.
+	usb_init();
+	while (!usb_configured())
+		continue;
+
+	_delay_ms(500);
+
+/*
+	// wait for the user to run their terminal emulator program
+	// which sets DTR to indicate it is ready to receive.
+	while (!(usb_serial_get_control() & USB_SERIAL_DTR))
+		continue;
+*/
+
+	// discard anything that was received prior.  Sometimes the
+	// operating system or other software will send a modem
+	// "AT command", which can still be buffered.
+	usb_serial_flush_input();
+
+	ddr(0xD6, 1); // LED
+
+	int status = 0;
+	uint32_t loop_count = 0;
+
+	while (1)
+	{
+		if (in(POWER_SWITCH) == 0)
+		{
+			loop_count = 0;
+			power = 1;
+			out(POWER_RELAY, 1);
+		}
+
+		if (loop_count == POWER_OFF_DELAY)
+		{
+			loop_count = 0;
+			out(POWER_RELAY, 0);
+			power = 0;
+		} else {
+			loop_count++;
+		}
+
+		int c = usb_serial_getchar();
+		if (c != -1)
+		{
+			loop_count = 0;
+			if (!power)
+			{
+				out(POWER_RELAY, 1);
+				power = 1;
+				_delay_ms(1000);
+			}
+
+			out(0xD6, 1);
+			tty_out(c);
+			out(0xD6, 0);
+		}
+
+#ifndef CONFIG_ASR33_UART
+		int x = in(TTY_RX);
+
+		// Wait for start bit
+		if (x)
+			continue;
+
+		uint16_t byte = tty_in();
+#ifndef CONFIG_WAVEFORM
+#define CONFIG_DEBUG_BYTES
+#ifdef CONFIG_DEBUG_BYTES
+		usb_serial_putchar(hexdigit(byte >> 12));
+		usb_serial_putchar(hexdigit(byte >> 8));
+		usb_serial_putchar(hexdigit(byte >> 4));
+		usb_serial_putchar(hexdigit(byte >> 0));
+		usb_serial_putchar(' ');
+#endif
+		usb_serial_putchar((byte >> 2) & 0x7F);
+#endif
+#else
+		if (!bit_is_set(UCSR1A, RXC1))
+			continue;
+
+		uint8_t ch = UDR1;
+#ifdef CONFIG_FOLD_CASE
+		if ('A' <= ch && ch <= 'Z')
+			ch += 'a' - 'A';
+#endif
+
+		loop_count = 0;
+		usb_serial_putchar(ch);
+#endif
+	}
+}
+
 /**
  * \file 45 baud sync teletype interface
  *
- * Uses a boost converter to make the 50-100V for driving the ciol.
+ * Uses a boost converter to make the 50-100V for driving the coil.
+ * Test string:
+ * now is the time for all good men to come to the aid of their country
+ * the quick brown fox jumps over the lazy dogs
+ * abcdefghijklmnopqrstuvwxyz
+ * ryryryryryryryryryryryryry
+ * y becomes q or x
+ * r becomes g (rarely)
+ * cvcvcvcvcvcvcvcvcvcvcvcvcvcv
+ *
+ * At 2 MHz the 45.45 baud (22ms) can be generated with the clock.
+ *
+ * The KSR models use the 5-bit UART as well, but require a current loop
+ * to generate the signal.  To wire this I used a 470 Ohm resistor:
+ *
+ *  TX+ --------------- PD0 with pullup (Vcc (+5V))
+ *  TX- -----+--------- PD2 (uart rx)
+ *           |
+ *           +-/\/\/\-- GND
+ *             470 Ohms
+ *
+ * Since there are two separate current loops, this makes the KSR
+ * capable of full duplex operation.
  */
 
+#undef F_CPU
+#define F_CPU  2000000UL
+#define CONFIG_UART
+
 #include <avr/io.h>
 #include <avr/pgmspace.h>
 #include <avr/interrupt.h>
 }
 
 
+#if 1
+#define BAUDOT(a,b,c,d,e,m) ( \
+	((!a) << 0) | \
+	((!b) << 1) | \
+	((!c) << 2) | \
+	((!d) << 3) | \
+	((!e) << 4) | \
+	(m << 5) | \
+	0 )
+#else
 #define BAUDOT(a,b,c,d,e,m) ( \
 	(a << 0) | \
 	(b << 1) | \
 	(e << 4) | \
 	(m << 5) | \
 	0 )
+#endif
 
 #define BAUDOT_LC(a,b,c,d,e) BAUDOT(a,b,c,d,e,0x1)
 #define BAUDOT_UC(a,b,c,d,e) BAUDOT(a,b,c,d,e,0x2)
 
 };
 
-static uint8_t current_mode;
+static uint8_t output_mode;
+static uint8_t input_mode = 1;
 
 static const uint8_t mode_select[] = {
 	[1] = BAUDOT_BOTH(0,0,0,0,0),
 	[2] = BAUDOT_BOTH(0,0,1,0,0),
 };
 
+//#define BIT_CLOCK 22
+#define BIT_CLOCK 22000
+#define one_advance 0
+#define TX_PIN 0xD3
+
 
 static void
 tty_out_raw(
 	uint8_t c
 )
 {
+#ifdef CONFIG_UART
+	// The UART has 5N1 configured; we add a half stop
+	sbi(UCSR1A, TXC1);
+	UDR1 = c;
+	while (bit_is_clear(UCSR1A, TXC1))
+		;
+	_delay_us(BIT_CLOCK/2);
+/*
+	_delay_us(BIT_CLOCK);
+	_delay_us(BIT_CLOCK);
+	_delay_us(BIT_CLOCK);
+	_delay_us(BIT_CLOCK);
+	_delay_us(BIT_CLOCK);
+*/
+#else
+	uint8_t ocr_one = OCR1C;
+	uint8_t ocr_zero = OCR1C;
+
+
 	// start bit
-	out(0xD7, 0);
+	out(TX_PIN, 0);
+	OCR1C = ocr_zero;
 
-#define BIT_CLOCK 22
-//#define BIT_CLOCK 16666
-
-	_delay_ms(BIT_CLOCK); // + BIT_CLOCK/2);
+	_delay_us(BIT_CLOCK - 1000);
 
 	for (int i = 0 ; i < 5 ; i++)
 	{
 		c >>= 1;
 
 		if (!x)
-			out(0xD7, 1);
-		else
-			out(0xD7, 0);
+		{
+			OCR1C = ocr_one;
+			out(TX_PIN, 1);
+			_delay_us(100);
+		} else {
+			OCR1C = ocr_zero;
+			out(TX_PIN, 0);
+		}
 
-		_delay_ms(BIT_CLOCK);
+		_delay_us(BIT_CLOCK);
 	}
 
-	out(0xD7, 1);
-	//_delay_ms(BIT_CLOCK);
-	//_delay_ms(BIT_CLOCK + BIT_CLOCK/2); // 1.42 stop bits
-	_delay_ms(6*BIT_CLOCK); // give it a few bits, slow but reliable
+	// And now send 1.5 stop bits
+	OCR1C = ocr_one;
+	out(TX_PIN, 1);
+
+	_delay_us(BIT_CLOCK);
+	_delay_us(BIT_CLOCK/2); // 1.42 stop bits
+
+	_delay_us(BIT_CLOCK); // give it a few bits, slow but reliable
+	_delay_us(BIT_CLOCK); // give it a few bits, slow but reliable
+	_delay_us(BIT_CLOCK); // give it a few bits, slow but reliable
+	_delay_us(BIT_CLOCK); // give it a few bits, slow but reliable
+#endif
 }
 
 
 	uint8_t ch
 )
 {
+	if ('A' <= ch && ch <= 'Z')
+		ch += ('a' - 'A');
+
 	uint8_t c = ch ? baudot[ch] : 0x00;
 	if (c == 0 && ch != 0)
 		return;
 	// If we are not in the current mode, send the special
 	// code to switch to the other typebar and record that
 	// we are in that mode.
-	if (current_mode != mode && mode != 0x3)
+	if (output_mode != mode && mode != 0x3)
 	{
 		tty_out_raw(mode_select[mode]);
-		_delay_ms(5*BIT_CLOCK); // give it a few bits
-		current_mode = mode;
+		_delay_us(BIT_CLOCK); // give it a few bits
+		_delay_us(BIT_CLOCK); // give it a few bits
+		_delay_us(BIT_CLOCK); // give it a few bits
+		_delay_us(BIT_CLOCK); // give it a few bits
+		output_mode = mode;
 	}
 
 	tty_out_raw(c);
+
+	// give the carriage return time to make it all the way
+	if (ch == '\n')
+		_delay_ms(200);
 }
 
 
         cbi(TCCR1B, CS11);
         sbi(TCCR1B, CS10);
 
-	OCR1C = 0xDA; // very little off time makes the mosfet unhappy
+	// for the big shiny coil
+	//OCR1C = 0xF0; // very little off time makes the mosfet unhappy
+
+	// for the small black coil
+	OCR1C = 0xD0;
+
 	ddr(0xB7, 1);
 	out(0xB7, 1);
 }
 
 
 static void
+ubrr_adjust(
+	int delta
+)
+{
+	UBRR1 += delta;
+
+	char buf[] = {
+		hexdigit(UBRR1 >> 12),
+		hexdigit(UBRR1 >>  8),
+		hexdigit(UBRR1 >>  4),
+		hexdigit(UBRR1 >>  0),
+		'\r',
+		'\n'
+	};
+
+	usb_serial_write(buf, sizeof(buf));
+}
+
+// ckckckckckckckckckckckckckckckck
+static void
 boost_adjust(
 	int delta
 )
 	usb_serial_write(buf, sizeof(buf));
 }
 
+// ckckckckckckckckckckckckckckckck
+// ryryryryryryryryryryryryryryryry
+
 
 
 
 int main(void)
 {
-	// set for 16 MHz clock
+	// set for 2 MHz clock so that we can do 45 baud
 #define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
-	CPU_PRESCALE(0);
+	CPU_PRESCALE(3);
 
 	// Disable the ADC
 	ADMUX = 0;
 
-	ddr(0xD7, 1);
-	out(0xD7, 1);
+	ddr(TX_PIN, 1);
+	out(TX_PIN, 1);
 
+#define CONFIG_BOOST
+#ifdef CONFIG_BOOST
 	boost_setup();
+#else
+	// do no enable boost; allow a separate current loop disable
+	// the to diode drive coil.  Pull the MOSFET high to turn it off
+	ddr(0xB7, 1);
+	out(0xB7, 1);
+#endif
+
+#ifdef CONFIG_UART
+	// drive D0 high to source current for RX current loop
+	ddr(0xD0, 1);
+	out(0xD0, 1);
+
+	// UBRR is 12 bits, max 4096.  So we run at 2 MHz
+	// to keep UBRR0 in range.
+	UBRR1 = 2749; // = 45.45 / (16 * 2 MHz) - 1
+	UCSR1B = 0
+		| (1 << RXEN1)
+		| (1 << TXEN1)
+		;
+
+	UCSR1C = 0
+		| (0 << UPM11) // no parity
+		| (0 << UPM10)
+		| (0 << UCSZ12) // 5 data bits
+		| (0 << UCSZ11)
+		| (0 << UCSZ10)
+		| (0 << USBS1) // 1 stop bit; we will make a separate half stop
+		//| (1 << USBS1) // 2 stop bits
+		;
+#endif
 
 	// initialize the USB, and then wait for the host
 	// to set configuration.  If the Teensy is powered
 
 	while (1)
 	{
+		if (bit_is_set(UCSR1A, RXC1))
+		{
+			uint8_t ch = UDR1;
+			uint8_t c;
+
+			// check for letters (mode 1) or figures (mode 2)
+			if (ch == (mode_select[1] & 0x1F))
+			{
+				input_mode = 1;
+				continue;
+			} else
+			if (ch == (mode_select[2] & 0x1F))
+			{
+				input_mode = 2;
+				continue;
+			}
+
+			for (c = 0 ; c < 128 ; c++)
+			{
+				const uint8_t b = baudot[c];
+				if ((b & 0x1F) != ch)
+					continue;
+
+				// Are we in the correct mode to match this?
+				if (((b >> 5) & input_mode) == 0)
+					continue;
+
+				// Yes!  We match.
+				break;
+			}
+
+			if (c != 128)
+			{
+				usb_serial_write(&c, 1);
+				continue;
+			}
+
+			// unknown; dump it
+			char buf[] = {
+				bit_is_set(ch, 0) ? '0' : '1',
+				bit_is_set(ch, 1) ? '0' : '1',
+				bit_is_set(ch, 3) ? '0' : '1',
+				bit_is_set(ch, 2) ? '0' : '1',
+				bit_is_set(ch, 4) ? '0' : '1',
+				' ',
+				hexdigit(input_mode),
+				'\r',
+				'\n',
+			};
+			usb_serial_write(buf, sizeof(buf));
+		}
+
 		int c = usb_serial_getchar();
 		if (c == -1)
 			continue;
 
-		if (c == '+')
+		if (c == '{')
 		{
 			boost_adjust(10);
 			continue;
 		} else
-		if (c == '-')
+		if (c == '}')
 		{
 			boost_adjust(-10);
 			continue;
+		} else
+		if (c == '[')
+		{
+			ubrr_adjust(-10);
+			continue;
+		} else
+		if (c == ']')
+		{
+			ubrr_adjust(+10);
+			continue;
 		}
 
 		out(0xD6, 1);