/*****************************************************************************
 * File: k-2805.c
 * Author: Clark Mills (clark@kiwi.gen.nz)
 * Date: Sun Aug  7 09:56:59 NZST 2005
 * Description: Routines to allow playing with the Dick Smith Electronics
 *              Discovery series K-2805 Parallel Port Interface
 * License: GPL
 * Keywords: Linux 2805 PCB printed circuit board kitset
 *****************************************************************************/

#include <stdio.h>
#include <unistd.h>
#include <sys/io.h>

/* defs */
#define BASEPORT 0x378 /* lp1 */

/* prototypes */
void push_byte(int byte);
void dac1_load( int byte );
void dac1_load( int byte );
void bits_load( int byte );
int adc_eoc();
int adc_data();

/* globals */
int port[3];	// Mirror of port values

/* functions */
int main()
{
int data;
int j;

  port_init();			// Reset registers
  card_power_on();		// Power up the card

  // Output ~2.48v which is tied to adc input 0 for testing
  dac1_load( 127 );		// Half of supply voltage
  dac1_output_enable();		// DAC is always enabled

  adc_cs_enable();		// Chip is always selected
  adc_addr_low();		// Address is always 0000
  usleep( 1000 );
  while (1)
  {
    data = 0;			// Collect our byte here
    for (j=0; j<8; j++)
    {
      clock_bit_high();
      usleep( 1000 );
      data <<= 1;		// Shift our byte
      data |= adc_data();	// Get a bit
      usleep( 1000 );
      clock_bit_low();
      usleep( 1000 );
    }
    printf( "%3d\r", data );	// Display our byte
    fflush( stdout );
    while (!adc_eoc())		// Wait for end-of-conversion flag
      usleep( 1000 );
  }
  
  card_shutdown();		// We never get here
  exit(0);
}

/*****************************************************************************
 * Low level routines.                                                       *
 *****************************************************************************/
port_init()
{
  if (ioperm(BASEPORT, 3, 1)) { perror("ioperm"); exit(1); }

  // Reset port and variables to a sane known state
  port[0] = port[1] = port[2] = 0;
  outb(port[0], BASEPORT+0);
  outb(port[1], BASEPORT+1);
  outb(port[2], BASEPORT+2);
}

card_power_on()
{
  port[0] |= 0x80;
  outb(port[0], BASEPORT+0);
}

card_power_off()
{
  port[0] &= 0x7F;
  outb(port[0], BASEPORT+0);
}

card_shutdown()
{
  card_power_off();
  if (ioperm(BASEPORT, 3, 0)) { perror("ioperm"); exit(1); }
}

/* Data bit routines */
data_bit_high()
{
  port[0] |= 0x01;
  outb(port[0], BASEPORT+0);
}

data_bit_low()
{
  port[0] &= 0xFE;
  outb(port[0], BASEPORT+0);
}

/* Clock routines */
clock_bit_high()
{
  port[0] |= 0x02;
  outb(port[0], BASEPORT+0);
}

clock_bit_low()
{
  port[0] &= 0xFD;
  outb(port[0], BASEPORT+0);
}

/* Shift in 1 byte */
void push_byte(int byte)
{
int j;

  for (j=0; j<8; j++)
  {
    if (byte & 0x80)	// MSB first
      data_bit_high();
    else
      data_bit_low();

    clock_bit_high();
    clock_bit_low();

    byte <<= 1;
  }
}

/*****************************************************************************
 * Routines that handle the digital to analogue converter 1                  *
 * Output can be read at: SK1                                                *
 * Loads IC1, 74HC595                                                        *
 *****************************************************************************/
dac1_output_enable()	// Inverse of expected bit state
{
  port[2] |= 0x01;
  outb(port[2], BASEPORT+2);
}

dac1_output_disable()	// Inverse of expected bit state
{
  port[2] &= 0xFE;
  outb(port[2], BASEPORT+2);
}

dac1_store_high()
{
  port[0] |= 0x04;
  outb(port[0], BASEPORT+0);
}

dac1_store_low()
{
  port[0] &= 0xFB;
  outb(port[0], BASEPORT+0);
}

dac1_store()
{
  dac1_store_high();
  dac1_store_low();
}

void dac1_load( int byte )
{
  push_byte(byte);
  dac1_store();
}

/*****************************************************************************
 * Routines that handle the digital to analogue converter 2                  *
 * Output can be read at: SK2                                                *
 * Loads IC2, 74HC595                                                        *
 *****************************************************************************/
dac2_output_enable()	// Inverse of expected bit state
{
  port[2] |= 0x02;
  outb(port[2], BASEPORT+2);
}

dac2_output_disable()	// Inverse of expected bit state
{
  port[2] &= 0xFD;
  outb(port[2], BASEPORT+2);
}

dac2_store_high()
{
  port[0] |= 0x08;
  outb(port[0], BASEPORT+0);
}

dac2_store_low()
{
  port[0] &= 0xF7;
  outb(port[0], BASEPORT+0);
}

dac2_store()
{
  dac2_store_high();
  dac2_store_low();
}

void dac2_load( int byte )
{
  push_byte(byte);
  push_byte(0);
  dac2_store();
}

/*****************************************************************************
 * Routines that handle the open collector outputs                           *
 * Output can be read at: SK4                                                *
 * Loads IC3, 74HC595                                                        *
 *****************************************************************************/
bits_output_enable()	// Inverse of expected bit state
{
  port[2] |= 0x08;
  outb(port[2], BASEPORT+2);
}

bits_output_disable()	// Inverse of expected bit state
{
  port[2] &= 0xF7;
  outb(port[2], BASEPORT+2);
}

bits_store_high()
{
  port[0] |= 0x10;
  outb(port[0], BASEPORT+0);
}

bits_store_low()
{
  port[0] &= 0xEF;
  outb(port[0], BASEPORT+0);
}

bits_store()
{
  bits_store_high();
  bits_store_low();
}

void bits_load( int byte )
{
  push_byte(byte);
  push_byte(0);
  push_byte(0);
  bits_store();
}

/*****************************************************************************
 * Routines that handle the analogue to digital converter and mux            *
 * Physical input is read from: SK5                                          *
 * Loads IC4, TLC542                                                         *
 *****************************************************************************/
adc_cs_enable()
{
  port[0] &= 0xDF;
  outb(port[0], BASEPORT+0);
}

adc_cs_disable()
{
  port[0] |= 0x20;
  outb(port[0], BASEPORT+0);
}

adc_addr_high()
{
  port[0] |= 0x40;
  outb(port[0], BASEPORT+0);
}

adc_addr_low()
{
  port[0] &= 0xBF;
  outb(port[0], BASEPORT+0);
}

int adc_eoc()
{
int data;

  data = inb( BASEPORT+1 );
  data &= 0x40;
  data >>= 6;
  return ( data );
}

int adc_data()
{
int data;

  data = inb( BASEPORT+1 );
  data &= 0x10;
  data >>= 4;
  return ( data );
}

/* EOF*/

