teletype / teletype.c

/**
 * \file 45 baud sync teletype interface
 *
 * Uses a boost converter to make the 50-100V for driving the ciol.
 * 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
 *
 * At 2 MHz the 45.45 baud (22ms) can be generated with the clock.
 */

#undef F_CPU
#define F_CPU  2000000UL

#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"


// 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 BAUDOT(a,b,c,d,e,m) ( \
	(a << 0) | \
	(b << 1) | \
	(c << 2) | \
	(d << 3) | \
	(e << 4) | \
	(m << 5) | \
	0 )

#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)
#define BAUDOT_BOTH(a,b,c,d,e) BAUDOT(a,b,c,d,e,0x3)

static uint8_t baudot[] = 
{
	[' '] = BAUDOT_BOTH(1,1,0,1,1),
	['\n'] = BAUDOT_BOTH(1,1,1,0,1),
	['\r'] = BAUDOT_BOTH(1,0,1,1,1),

	['a'] = BAUDOT_LC(0,0,1,1,1),
	['b'] = BAUDOT_LC(0,1,1,0,0),
	['c'] = BAUDOT_LC(1,0,0,0,1),
	['d'] = BAUDOT_LC(0,1,1,0,1),
	['e'] = BAUDOT_LC(0,1,1,1,1),
	['f'] = BAUDOT_LC(0,1,0,0,1),
	['g'] = BAUDOT_LC(1,0,1,0,0),
	['h'] = BAUDOT_LC(1,1,0,1,0),
	['i'] = BAUDOT_LC(1,0,0,1,1),
	['j'] = BAUDOT_LC(0,0,1,0,1),
	['k'] = BAUDOT_LC(0,0,0,0,1),
	['l'] = BAUDOT_LC(1,0,1,1,0),
	['m'] = BAUDOT_LC(1,1,0,0,0),
	['n'] = BAUDOT_LC(1,1,0,0,1),
	['o'] = BAUDOT_LC(1,1,1,0,0),
	['p'] = BAUDOT_LC(1,0,0,1,0),
	['q'] = BAUDOT_LC(0,0,0,1,0),
	['r'] = BAUDOT_LC(1,0,1,0,1),
	['s'] = BAUDOT_LC(0,1,0,1,1),
	['t'] = BAUDOT_LC(1,1,1,1,0),
	['u'] = BAUDOT_LC(0,0,0,1,1),
	['v'] = BAUDOT_LC(1,0,0,0,0),
	['w'] = BAUDOT_LC(0,0,1,1,0),
	['x'] = BAUDOT_LC(0,1,0,0,0),
	['y'] = BAUDOT_LC(0,1,0,1,0),
	['z'] = BAUDOT_LC(0,1,1,1,0),

	['0'] = BAUDOT_UC(1,0,0,1,0),
	['1'] = BAUDOT_UC(0,0,0,1,0),
	['2'] = BAUDOT_UC(0,0,1,1,0),
	['3'] = BAUDOT_UC(0,1,1,1,1),
	['4'] = BAUDOT_UC(1,0,1,0,1),
	['5'] = BAUDOT_UC(1,1,1,1,0),
	['6'] = BAUDOT_UC(0,1,0,1,0),
	['7'] = BAUDOT_UC(0,0,0,1,1),
	['8'] = BAUDOT_UC(1,0,0,1,1),
	['9'] = BAUDOT_UC(1,1,1,0,0),
	['-'] = BAUDOT_UC(0,0,1,1,1),
	['?'] = BAUDOT_UC(0,1,1,0,0),
	[':'] = BAUDOT_UC(1,0,0,0,1),
	['$'] = BAUDOT_UC(0,1,1,0,1),
	['!'] = BAUDOT_UC(0,1,0,0,1),
	['&'] = BAUDOT_UC(1,0,1,0,0),
	['#'] = BAUDOT_UC(1,1,0,1,0),
	['\''] = BAUDOT_UC(0,0,1,0,1),
	['('] = BAUDOT_UC(0,0,0,0,1),
	[')'] = BAUDOT_UC(1,0,1,1,0),
	['.'] = BAUDOT_UC(1,1,0,0,0),
	[','] = BAUDOT_UC(1,1,0,0,1),
	['\a'] = BAUDOT_UC(0,1,0,1,1),
	[';'] = BAUDOT_UC(1,0,0,0,0),
	['/'] = BAUDOT_UC(0,1,0,0,0),
	['"'] = BAUDOT_UC(0,1,1,1,0),

};

static uint8_t current_mode;

static const uint8_t mode_select[] = {
	[1] = BAUDOT_BOTH(0,0,0,0,0),
	[2] = BAUDOT_BOTH(0,0,1,0,0),
};


static void
tty_out_raw(
	uint8_t c
)
{
	uint8_t ocr_one = OCR1C;
	uint8_t ocr_zero = OCR1C;

//#define BIT_CLOCK 22
#define BIT_CLOCK 22000
#define one_advance 0


	// start bit
	out(0xD7, 0);
	OCR1C = ocr_zero;

	_delay_us(BIT_CLOCK - 1000);

	for (int i = 0 ; i < 5 ; i++)
	{
		int x = c & 1;
		c >>= 1;

		if (!x)
		{
			OCR1C = ocr_one;
			out(0xD7, 1);
			_delay_us(100);
		} else {
			OCR1C = ocr_zero;
			out(0xD7, 0);
		}

		_delay_us(BIT_CLOCK);
	}

	// And now send 1.5 stop bits
	OCR1C = ocr_one;
	out(0xD7, 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
}


static void
tty_out(
	uint8_t ch
)
{
	if ('A' <= ch && ch <= 'Z')
		ch += ('a' - 'A');

	uint8_t c = ch ? baudot[ch] : 0x00;
	if (c == 0 && ch != 0)
		return;

	const uint8_t mode = (c >> 5) & 0x3;

	// 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)
	{
		tty_out_raw(mode_select[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
		current_mode = mode;
	}

	tty_out_raw(c);
}


static void
boost_setup(void)
{

        // Configure OC1x in fast-PWM mode, 8-bit
        sbi(TCCR1B, WGM12);
        cbi(TCCR1A, WGM11);
        sbi(TCCR1A, WGM10);

        // OC1C is used to generate the boost converter pump
        // Configure output mode to clear on match, set at top
        sbi(TCCR1A, COM1C1);
        cbi(TCCR1A, COM1C0);

        // Configure clock 1 at clk/1
        cbi(TCCR1B, CS12);
        cbi(TCCR1B, CS11);
        sbi(TCCR1B, CS10);

	// 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
boost_adjust(
	int delta
)
{
	OCR1C += delta;

	char buf[] = {
		hexdigit(OCR1C >> 12),
		hexdigit(OCR1C >>  8),
		hexdigit(OCR1C >>  4),
		hexdigit(OCR1C >>  0),
		'\r',
		'\n'
	};

	usb_serial_write(buf, sizeof(buf));
}

// ckckckckckckckckckckckckckckckck
// ryryryryryryryryryryryryryryryry




int main(void)
{
	// set for 2 MHz clock so that we can do 45 baud
#define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
	CPU_PRESCALE(3);

	// Disable the ADC
	ADMUX = 0;

	ddr(0xD7, 1);
	out(0xD7, 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

	// 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;

	while (1)
	{
		int c = usb_serial_getchar();
		if (c == -1)
			continue;

		if (c == '{')
		{
			boost_adjust(10);
			continue;
		} else
		if (c == '}')
		{
			boost_adjust(-10);
			continue;
		}

		out(0xD6, 1);
		tty_out(c);
		//tty_out(0x00);
		out(0xD6, 0);
	}
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.