Source

teletype / asr33.c

Full commit
/**
 * \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
	}
}