/* Copyright 2008, Marc Lindow.  This program is distributed under the terms of the Lesser GNU General Public Licence */

#include <stdio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/resource.h>

#define ALL_START 0xE8000000
#define GPIO1A_DAT 0x10
#define GPIO1A_DIR 0x20
#define B1 2
#define FAILED_TO_RESET 9999

// THESE WORK AT THE TS7800 REGISTER LEVEL ONLY, SEE THE FUNCTIONS FOR DS18S20 INTERFACES:
#define WR1(regs,s) regs[s.dati]|=s.datb
#define WR0(regs,s) regs[s.dati]&=~s.datb
#define READ(regs,s) regs[s.dati]&s.datb
#define SETI(regs,s) regs[s.diri]&=~s.dirb
#define SETO(regs,s) regs[s.diri]|=s.dirb

// HOLDS THE INFO FOR A SINGLE DS18S20 SENSOR:
typedef struct {
  char rom[64];
  int dati; // the index of the 32 bit int where the data resides
  int datb; // the bit within the 32 bits where the data resides
  int diri; // the index of the 32 bit int where the direction resides (1 is output, 0 is in)
  int dirb; // the bit within the 32 bits where the direction resides
} ds18s20;


// ds18s20 command sequences:
char skipRom[] = {0,0,1,1,0,0,1,1};
char readRom[] = {1,1,0,0,1,1,0,0};
char matchRom[] = {1,0,1,0,1,0,1,0};
char convertT[] = {0,0,1,0,0,0,1,0};
char readScratch[] = {0,1,1,1,1,1,0,1};



// MY OWN VERSION OF USLEEP (USLEEP SEEMS TO HAVE MIN SLEEP OF 10ms which is way too much)
void sleepu(int us) {
  int i;
  int limit = us*34;
  for (i = 0; i < limit; i++) { }
}


// RESET THE TEMP SENSOR
int ds18s20reset(char * regs, ds18s20 s) {
  SETO(regs,s);
  WR0(regs,s);
  sleepu(800);
  SETI(regs,s);
  sleepu(68);
  if ((regs[s.dati] & s.datb) > 0) {
    return 0; // didn't get the presence pulse ;^(
  }
  sleepu(500);
  return 1;
}


// SEND A 1 TO THE DS18S20
void send1(char * regs, ds18s20 s) {
  regs[s.dati] &= ~s.datb;
  sleepu(3);
  regs[s.dati] |= s.datb;
  sleepu(90);
}

// SEND A 0 TO THE DS18S20
void send0(char * regs, ds18s20 s) {
  regs[s.dati] &= ~s.datb;
  sleepu(90);
  regs[s.dati] |= s.datb;
  sleepu(10);
}

// SEND A READ REQUEST TO THE DS18S20
void sendR(char * regs, ds18s20 s) {
  sleepu(60);
  regs[s.diri] |= s.dirb;
  regs[s.dati] &= ~s.datb;
  sleepu(1);
  regs[s.diri] &= ~s.dirb;
  sleepu(10);
}



void sendCommand(char * regs, ds18s20 s, char * byte) {
  int i;
  SETO(regs,s);
  for (i = 0; i < 8; i++) {
    if (byte[i]) {
      send1(regs, s);
    }
    else {
      send0(regs, s);
    }
  }
}



ds18s20 readSingleRom(char * regs, ds18s20 s) {
  int i;
  if (!ds18s20reset(regs, s)) {
    printf("not good\n");
    return;
  }
  sendCommand(regs, s, readRom);
  for (i = 0; i < 64; i++) {
    sendR(regs, s);
    s.rom[i] = READ(regs,s);
  }
  return s;
}




// THIS READS THE TEMP WITHOUT SENDING THE UNIQUE ROM CODE - ONLY WORKS WITH 1 SENSOR ON THE BUS
int readTempNoRom(char * regs, ds18s20 s) {
  int i;
  int valarr[9];
  int val = 0;
  int looplimit = 50;
  int mult = 0;
  float conv;

  if (!ds18s20reset(regs, s)) return FAILED_TO_RESET;
  sendCommand(regs, s, skipRom);
  sendCommand(regs, s, convertT);

  for (i = 0; i < looplimit; i++) { // WAIT UNTIL THE SENSOR SENDS A 1, WHICH MEANS THE TEMP IS READY
    sendR(regs, s);
    if (READ(regs,s)) break;
    sleepu(100000);
  }

  if (!ds18s20reset(regs, s)) return FAILED_TO_RESET;
  sendCommand(regs, s, skipRom);
  sendCommand(regs, s, readScratch);

  for (i = 0; i < 9; i++) {
    sendR(regs, s);
    valarr[i] = READ(regs,s)?1:0;
  }

  val = 0;
  mult = 1;
  for (i = 0; i < 8; i++) {
    val += (mult * valarr[i]);
    mult = mult * 2;
  }
  if (valarr[8]) {
    val = -val;
  }


  return (int)(32.5 + ((val * 9) / 10)); // 10 because the temp is doubled, 32.5 to get a round with (int)
}




// THIS READS THE TEMP USING THE UNIQUE ROM CODE - WORKS WITH MULITPLE SENSORS ON THE BUS
int readTempRom(char * regs, ds18s20 s) {
  int i;
  int valarr[9];
  int val = 0;
  int looplimit = 50;
  int mult = 0;
  float conv;

  if (!ds18s20reset(regs, s)) return FAILED_TO_RESET;
  sendCommand(regs, s, matchRom);
  for (i = 0; i < 64; i++) {  // send the rom
    if (s.rom[i]) send1(regs, s);
    else send0(regs, s);
  }
  sendCommand(regs, s, convertT);


  for (i = 0; i < looplimit; i++) { // WAIT UNTIL THE SENSOR SENDS A 1, WHICH MEANS THE TEMP IS READY
    sendR(regs, s);
    if (READ(regs,s)) break;
    sleepu(100000);
  }

  if (!ds18s20reset(regs, s)) return FAILED_TO_RESET;

  sendCommand(regs, s, matchRom);

  // send the rom
  for (i = 0; i < 64; i++) {
    if (s.rom[i]) send1(regs, s);
    else send0(regs, s);
  }

  sendCommand(regs, s, readScratch);

  for (i = 0; i < 9; i++) {
    sendR(regs, s);
    valarr[i] = READ(regs,s)?1:0;
  }

  val = 0;
  mult = 1;
  for (i = 0; i < 8; i++) {
    val += (mult * valarr[i]);
    mult = mult * 2;
  }
  if (valarr[8]) {
    val = -val;
  }

  return (int)(32.5 + ((val * 9) / 10)); // 10 because the temp is doubled, 32.5 to get a round with (int)
}






main() {
  int fd = open("/dev/mem", O_RDWR|O_SYNC);
  char *regs;
  int i;
  int var;
  int val;
  ds18s20 s;

  setpriority(PRIO_PROCESS, 0, -19);  // WE NEED TO MAKE SURE THE KERNEL DOES NOT SWAP PROCESSES, WHICH WOULD RUIN OUR TIMINGS - WE MAY NEED TO REVISIT THIS IF THE PROGRAM EXCEEDS THE TIMESLICE
  regs = (char *)mmap((caddr_t)0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, ALL_START);

  s.dati = GPIO1A_DAT;
  s.datb = B1;
  s.diri = GPIO1A_DIR;
  s.dirb = B1;

  s = readSingleRom(regs, s);

  for (i = 0; i < 64; i++) {
    printf("%d", s.rom[i]?1:0);
  }
  printf("\n\n");


  //s.rom[0] = 1;  // uncomment this line as a test - it should prevent readTempRom from working



  for (i = 0; i < 5; i++) {
    val = readTempRom(regs, s);
    if (val == FAILED_TO_RESET) {
      printf("AINT WORKING TODAY\n");
    }
    else {
      printf("%2d %d\n", i, val);
    }
  }
  

  
}





