/*
Description: Driving a MAX186 chip from the printer port from Linux
Date: Thu Jul 17 19:33:46 NZST 2008
Author: Clark Mills
Chip description: Maxim MAX186 12 bit Analogue to Digital Converter
*/

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

#define BASEPORT 0x378 /* lp1 */

// Writes to the "data" port are through this global byte to not clobber bits
int data;

void resetPort();
int r_DOUT( void );
int r_SSTRB( void );
void w_DIN_hi( void );
void w_DIN_lo( void );
void w_SCLK_hi( void );
void w_SCLK_lo( void );
void writeCtlPort( int data );
unsigned int readValue();

main()
{
  // Check that we have permissions to access the port...
  if (ioperm(BASEPORT, 3, 1))
  {
    // Nope, die.
    perror("ioperm");
    exit(1);
  }

  // Set ports to known state
  resetPort();

  // ADC conversion: Start, CH0, Unipolar, Single ended, Internal clock
  writeCtlPort( 0x8E );

  // Wait for conversion
  while (r_SSTRB() == 0)
    ;

  // Print the result
  printf( "Result=%u\n", readValue() );
}


// Read the resultant 12 bit value
unsigned int readValue()
{
  unsigned int result = 0;
  int j;

  // Clock out 16 bits
  for (j=0; j<16; j++)
  {
    w_SCLK_hi();
    w_SCLK_lo();
    result = result << 1;
    result |= r_DOUT();
  }

  // Discard 4 bits as only 12 bits valid
  result = result >> 4;

  return result;
}


// Write a byte to the control port
void writeCtlPort( int data )
{
  int j;

  // For 8 bits...
  for (j=0; j<8; j++)
  {
    // Set data line...
    if (data & 128)
      w_DIN_hi();
    else
      w_DIN_lo();

    // Clock the data out
    w_SCLK_hi();
    w_SCLK_lo();

    // Shift up next bit
    data = data << 1;
  }
  // Clear the data bit (for neatness)
  w_DIN_lo();
}


// Read DOUT line
int r_DOUT( void )
{
  return ((inb(BASEPORT+1) >> 4) & 1);
}


// Set the DIN bit high (write through "data" byte to preserve other bits)
void w_DIN_hi( void )
{
  data |= 0x01;
  outb(data, BASEPORT+0);
}


// Set the DIN bit low (write through "data" byte to preserve other bits)
void w_DIN_lo( void )
{
  data &= 0xFE;
  outb(data, BASEPORT+0);
}


// Set SCLK high (write through "data" byte to preserve other bits)
void w_SCLK_hi( void )
{
  data |= 0x02;
  outb(data, BASEPORT+0);
}


// Set SCLK low (write through "data" byte to preserve other bits)
void w_SCLK_lo( void )
{
  data &= 0xFD;
  outb(data, BASEPORT+0);
}


// Read SSTRB line
int r_SSTRB( void )
{
  return (~(inb(BASEPORT+1) >> 7) & 1);
}


// Set ports and data port mirror byte to known state
void resetPort()
{
  data = 0; // (write through "data" byte to preserve other bits)
  outb(data, BASEPORT+0);

  outb(0, BASEPORT+1);
  outb(0, BASEPORT+2);
}


/* EOF*/

