HW/DAS/USBscopes/usbtm/lib/usbscope.c

#include "usbscope.h"

#define SCOPE50_HWRAMSIZE 3072 /* 0xC00 */
#define SCOPE50_SWRAMSIZE 3000

#define SCOPE50_POINTER_READ    0
#define SCOPE50_POINTER_WRITE   1

#undef DEBUG

struct channel_info {
  unsigned char upper;      /* upper nibble of reg 0x0 */
  float raw_offset;
  float o2dac_scale;
  int trig_offset;
  /*int ris_min;
  int ris_max;*/
} cinfo[MAXFD];

unsigned pretrigger;
unsigned startwdp;
int use_ris;
float corr_gain = 1.06;     /* independent of channel */
int baseclk;                /* 10, 12(.5), 25, or 50 - in MHz */
unsigned char baseclk_ix;   /* 3, 2, 1, 0 acc. to baseclk */

unsigned static char clamp(int val) {
  if(val < 0) return 0;
  else if(val > 255) return 255;
  else return val;
}

/* Doplnit RIS */
int scope50_acquisition_start(int fd) {
  if(use_ris) {
    unsigned char seq[] = {
      0x20,  cinfo[fd].upper | 0xC,
      0x16,  0,
      0x17,  0,
      0x27,  0,
      0x28,  0,
      0x20,  cinfo[fd].upper | 0x0,
      0x20,  cinfo[fd].upper | 0x1,   /* Run*/
      0x20,  cinfo[fd].upper | 0x3};  /* Arm */
    return writeregmulti(fd, seq, countof(seq));
  } else {
    unsigned char seq[] = {
      0x20,  cinfo[fd].upper | 0x8,
      0x16,  (unsigned char)(pretrigger & 0xFF),
      0x17,  (unsigned char)(pretrigger >> 8),
      0x27,  (unsigned char)(startwdp & 0xFF),
      0x28,  (unsigned char)(startwdp >> 8),
      0x20,  cinfo[fd].upper | 0xC,
      0x20,  cinfo[fd].upper | 0xF,
      0x20,  (cinfo[fd].upper | 0xF) & 0xDF,
      0x20,  cinfo[fd].upper | 0xF};
    return writeregmulti(fd, seq, countof(seq));
  }
}

int scope50_set_ris_mode(int fd, int state) {
  use_ris = state;
  return changereg(fd, 0x10, ~0x06, state?0x02:0);
}

int scope50_set_trig_master(int fd, int state) {
  if(state) cinfo[fd].upper |= 0x40;
  else cinfo[fd].upper &= ~0x40;
  return writereg(fd, 0x0, cinfo[fd].upper);
}

int scope50_get_ris_data(int fd, unsigned char *ret) {
  return readreg(fd, 0x11, ret);
}

int scope50_set_pretrig_depth(int samples) {
  //if(pct >= 0) pretrigger = (unsigned)(pct * SCOPE50_SWRAMSIZE);
  if(samples >= 0 && samples < SCOPE50_HWRAMSIZE-10) pretrigger = samples;
  else return 0;
  startwdp = SCOPE50_HWRAMSIZE - 10 - pretrigger;
  return 1;
}

int scope50_set_trigger_delay(int fd, unsigned val) {
  return writeregdouble(fd, 0x12, val);
}

int scope50_set_cal_source(int fd, int state) {
  return changereg(fd, 0xA, (unsigned char)~0x80, state?0x80:0);
}

/* Base ADC clock = 10, 12.5, 25, or 50 MHz
 * 10 MHz (only!) can be divided by the decimation ratios:
 *   1, 2, 4, 10, 20, 40, ..., 400000
 * => min sample interval = 20 ns (50 MHz)
 *    max sample interval = 40 ms (10 MHz : 4E5)
 */
int scope50_set_base_adc_clk(int fd, int clk) {
  baseclk = clk;
  switch(clk) {
    case 10: baseclk_ix = 3; break;
    case 12: baseclk_ix = 2; break;
    case 25: baseclk_ix = 1; break;
    default: baseclk_ix = 0;
  }
  //printf("clk %i -> %i\n", clk, baseclk_ix);
  return changereg(fd, 0xC, ~0x3, baseclk_ix);
}

int scope50_set_decimation_ratio(int fd, int dr) {
  unsigned char code = 0;
  while(dr >= 10) {
    dr /= 10;
    code += 4;
  }
  if(dr == 2) code += 1;
  else if(dr != 1) code += 2;
  //printf("dec %i -> %i\n", dr, code);
  return changereg(fd, 0xC, 0x3, code << 2);
}

/* Allowed values: 25, 50, 100, 250, ..., 1E7, 1.25E7, 2.5E7, 5E7 */
int scope50_set_sample_frequency(int fd, int freq, int *real) {
  int clk, dec, pow10;
  if(freq < 25) freq = 25;
  if(freq > 5E7) freq = 5E7;
  if(freq > 1E7) {
    dec = 1;
    freq /= 1E6;
    if(freq < 11) clk = 10;
    else if(freq < 20) clk = 12;
    else if(freq < 40) clk = 25;
    else clk = 50;
  } else {
    clk = 10;
    dec = 1E7 / freq;
  }
  for(pow10 = 1; dec >= 10; ) {
    pow10 *= 10;
    dec /= 10;
  }
  if(dec > 2) dec = 4;
  dec *= pow10;
  if(real) *real = clk * 1E6 / dec;
  if(!scope50_set_base_adc_clk(fd, clk)) return 0;
  if(!scope50_set_decimation_ratio(fd, dec)) return 0;
  return 1;
}

/* code parameter is optional */
int scope50_set_offset(int fd, float pct, unsigned char *code) {
  unsigned char tmp;
  tmp = clamp(cinfo[fd].raw_offset + 0x80*(pct/100)*cinfo[fd].o2dac_scale);
  if(code) *code = tmp;
  if(!writereg(fd, 0x3, 1)) return 0;
  if(!writereg(fd, 0x4, tmp)) return 0;
  return delaycycle(fd, 6);
}

int scope50_set_trig_thresh(int fd, float pct) {
  unsigned char tmp;
  tmp = clamp(cinfo[fd].trig_offset + 73*(pct/100));
  if(!writereg(fd, 0x3, 2)) return 0;
  return writereg(fd, 0x4, tmp);
}

int scope50_set_run(int fd, int state) {
  return changereg(fd, 0x0, ~0x1, state?0x1:0);
}

int scope50_set_arm(int fd, int state) {
  return changereg(fd, 0x0, ~0x2, state?0x2:0);
}

int scope50_set_adc_clk(int fd, int state) {
  return changereg(fd, 0x0, ~0x4, state?0x4:0);
}

int scope50_set_ctr_clk(int fd, int state) {
  return changereg(fd, 0x0, ~0x8, state?0x8:0);
}

int scope50_get_acquisition_state(int fd, int *state) {
  unsigned char res;
  if(!readreg(fd, 0x0, &res)) return 0;
  *state = res & 0x1;
  return 1;
}

int scope50_acquisition_end(int fd) {
  return writereg(fd, 0x0, cinfo[fd].upper & 0xF0);
}

int scope50_set_trig_type(int fd, int type) {
  return changereg(fd, 0x1, ~0x3, type);
}

int scope50_set_norm_trig(int fd, int state) {
  if(state) {
    if(!changereg(fd, 0x0, ~0x30, 0x30)) return 0;
    cinfo[fd].upper |= 0x30;
    scope50_set_pretrig_depth(-1); /* Use current value */
  } else {
    if(!changereg(fd, 0x0, ~0x30, 0x20)) return 0;
    cinfo[fd].upper &= ~0x30;
    cinfo[fd].upper |= 0x20;
    if(!scope50_set_trigger_delay(fd, 0)) return 0;
    scope50_set_pretrig_depth(0); /* No pre-trig */
  }
  return 1;
}

int scope50_setup_front_end(int fd, int range, int coupl, int ris) {
  unsigned char reg;
  if(!readreg(fd, 0x2, &reg)) return 0;
  if(ris) reg |= 0x20;
  else {
    reg &= 0x80;
    switch(coupl) {
      case SCOPE50_COUPLING_AC:
        reg |= 0x10;
        break;
      case SCOPE50_COUPLING_DC:
        reg |= 0x18;
        break;
      case SCOPE50_COUPLING_GND:
        reg &= 0xE0;
        range = SCOPE50_RANGE_300V;
    }
    reg |= 1 << range;
  }
  if(!writereg(fd, 0x2, reg & 0xF8)) return 0; /* Without amps */
  if(!delaycycle(fd, 5)) return 0;
  if(!writereg(fd, 0x2, reg)) return 0;
  if(!delaycycle(fd, 5)) return 0;
  return 1;
}

int scope50_set_ptr(int fd, int ptr, unsigned val) {
  return writeregdouble(fd, ptr?0x7:0x5, val);
}

int scope50_get_ptr(int fd, int ptr, unsigned *ret) {
  int i;
  unsigned char a, b, c;
  for(i = 0; i < 100; i++) {
    if(!readreg(fd, ptr?0x8:0x6, &a)) return 0;
    if(!readreg(fd, ptr?0x7:0x5, &b)) return 0;
    if(!readreg(fd, ptr?0x8:0x6, &c)) return 0;
    if(a == c) break;   /* The pointer is changing */
  }
  if(i == 100) *ret = 0;
  else *ret = ((((int)c)<<8)+b) & 0x7FFF;
  return 1;
}

int scope50_get_halt_ptr(int fd, unsigned *ret) {
  return readregdouble(fd, 0x14, ret);
}

int scope50_get_trig_ptr(int fd, unsigned *ret) {
  int r = readregdouble(fd, 0x18, ret);
  *ret &= 0x7FFF;
  return r;
}

int scope50_get_triggered_status(int fd, int *ret) {
  unsigned char res;
  if(!readreg(fd, 0x9, &res)) return 0;
  *ret = (res & 2) ? 1 : 0;
  return 1;
}

int scope50_get_samples_since_trigger(int fd, unsigned *ret) {
  unsigned a, b;
  int res;
  *ret = 0;
  if(!scope50_get_triggered_status(fd, &res)) return 0;
  if(res) return 1;
  if(!scope50_get_ptr(fd, SCOPE50_POINTER_WRITE, &a)) return 0;
  if(!scope50_get_trig_ptr(fd, &b)) return 0;
  if(a < startwdp && a > 10) return 1;
  *ret = (int)a - b;
  if(*ret < 0) *ret += SCOPE50_HWRAMSIZE;
  if(*ret) (*ret)--;
  return 1;
}

int scope50_get_buffer_blocks(int fd, float *data, int nblocks) {
  unsigned wdp;
  int i;
  unsigned char fld[512];
  if(!writereg(fd, 0xF, 0)) return 0;   /* Read 512 B */
  if(!scope50_get_halt_ptr(fd, &wdp)) return 0;
  if(!scope50_set_ptr(fd, SCOPE50_POINTER_READ, (wdp+1)%SCOPE50_HWRAMSIZE)) return 0;
  while(nblocks--) {
    if(!readregmulti(fd, 0xB, 512, fld)) return 0;
    for(i = 0; i < 512; i++) {
      *data = ((int)fld[i] - 0x80) * corr_gain;
      data++;
    }
  }
  return 1;
}

int scope50_get_buffer_blocks_raw(int fd, unsigned char *data, int nblocks) {
  unsigned wdp;
  int i;
  if(!writereg(fd, 0xF, 0)) return 0;   /* Read 512 B */
  if(!scope50_get_halt_ptr(fd, &wdp)) return 0;
  if(!scope50_set_ptr(fd, SCOPE50_POINTER_READ, (wdp+1)%SCOPE50_HWRAMSIZE)) return 0;
  for(i = 0; i < nblocks; i++)
    if(!readregmulti(fd, 0xB, 512, data + i*512)) return 0;
  return 1;
}

/* buffer blocks multi, incr, RIS */

int scope50_calibrate_offset(int fd) {
  unsigned char o1, o2;
  float a1, a2;
  unsigned char buf[100];
  int i;
  unsigned char seq[] = {
    0x20,  cinfo[fd].upper | 0x8,
    0x2F,  50,
    0x25,  0,
    0x26,  0,
    0x27,  0,
    0x28,  0,
    0x20,  cinfo[fd].upper | 0xC,
    0x20,  cinfo[fd].upper | 0xD,
    0x20,  cinfo[fd].upper | 0xD,
    0x20,  cinfo[fd].upper | 0x0};
  if(!scope50_setup_front_end(fd, 0, SCOPE50_COUPLING_GND, 0)) return 0;
  if(!scope50_set_norm_trig(fd, 0)) return 0;
  if(!scope50_set_base_adc_clk(fd, 50)) return 0;
  cinfo[fd].raw_offset = 0x80;  /* Assumption */
  cinfo[fd].o2dac_scale = 0.5;

  if(!scope50_set_offset(fd, 50, &o1)) return 0;
  if(!delaycycle(fd, 50)) return 0;
  if(!writeregmulti(fd, seq, countof(seq))) return 0;
  if(!readregmulti(fd, 0xB, 100, buf)) return 0;
  for(i = 0, a1 = 0; i < 100; i++)
    a1 += buf[i] - 0x80;
  a1 /= 100.0;

#ifdef DEBUG
  printf("o1 = %i\na1 = %f\n", o1, a1);
#endif

  if(!scope50_set_offset(fd, -50, &o2)) return 0;
  if(!delaycycle(fd, 50)) return 0;
  if(!writeregmulti(fd, seq, countof(seq))) return 0;
  if(!readregmulti(fd, 0xB, 100, buf)) return 0;
  for(i = 0, a2 = 0; i < 100; i++)
    a2 += buf[i] - 0x80;
  a2 /= 100.0;

#ifdef DEBUG
  printf("o2 = %i\na2 = %f\n", o2, a2);
#endif

  /*Original algorithm:
  cinfo[fd].o2dac_scale = (o1-o2) / (a1-a2);
  cinfo[fd].raw_offset = o2 - a2*cinfo[fd].o2dac_scale;
  cinfo[fd].o2dac_scale /= 1.09;*/
  /* New algorithm: */
  cinfo[fd].o2dac_scale = (o1-o2)/(a1-a2);
  cinfo[fd].raw_offset = (int)(1.1/2.-(o1*a2-o2*a1)/(a1-a2));

#ifdef DEBUG
  printf("%f\n%f\n", cinfo[fd].o2dac_scale, cinfo[fd].raw_offset);
#endif

  return 1;
}

int scope50_calibrate_trig(int fd) {
  unsigned char code, res;
  if(!scope50_set_offset(fd, 0, NULL)) return 0;
  if(!scope50_setup_front_end(fd, 0, SCOPE50_COUPLING_GND, 0)) return 0;
  cinfo[fd].trig_offset = 0x80;
  for(code = 100; code <= 156; code++) {
    if(!writereg(fd, 0x3, 2)) return 0;
    if(!writereg(fd, 0x4, code)) return 0;
    if(!delaycycle(fd, 1)) return 0;
    if(!readreg(fd, 0x9, &res)) return 0;
    if(!(res & 0x4)) {
      cinfo[fd].trig_offset = code;
      break;
    }
  }
  return 1;
}

/*int scope50_calibrate_ris(int fd) {
  int data[SCOPE50_HWRAMSIZE];
  int min, max;
  int i;
  unsigned char res;
  unsigned char seq[] = {
    0x20,  cinfo[fd].upper | 0xC,
    0x27,  0,
    0x28,  0,
    0x20,  cinfo[fd].upper | 0x0,
    0x20,  cinfo[fd].upper | 0x3,
    0x30,  3,
    0x80};
  if(!scope50_set_ris_mode(fd, 1)) return 0;
  if(!scope50_set_trig_master(fd, 1)) return 0;
  for(i = 0; i < 100; i++) {
    if(!writeregmulti(fd, seq, countof(seq))) return 0;
    if(!readreg(fd, 0x11, &res)) return 0;
    data[i] = (int)res - 0x80;
  }
  max = -128;
  min = 127;
  for(i = 0; i < 100; i++) {
    if(data[i] < 127 && data[i] > max) max = data[i];
    if(data[i] > -127 && data[i] < min) min = data[i];
  }
  cinfo[fd].ris_min = 0.75*min;
  cinfo[fd].ris_max = 0.75*max;
  if(!writereg(fd, 0x10, 0)) return 0;
  if(!scope50_set_trig_master(fd, 0)) return 0;
  return 1;
}*/

int scope50_init_scope(int fd, int master, float *raw, float *scale, int *trig) {
  cinfo[fd].upper = master?0x80:0;
  if(!writereg(fd, 0x0, cinfo[fd].upper)) return 0;
  if(!scope50_set_base_adc_clk(fd, 50)) return 0;
  if(!scope50_set_decimation_ratio(fd, 1)) return 0;
  /*cinfo[fd].ris_max = 127;
  cinfo[fd].ris_min = -128;
  if(!scope50_calibrate_ris(fd)) return 0;*/
  if(raw == NULL || scale == NULL || trig == NULL ||
      (*raw == 0 && *scale == 0 && *trig == 0)) {
    if(!scope50_calibrate_offset(fd)) return 0;
    if(!scope50_calibrate_trig(fd)) return 0;
  } else {
    cinfo[fd].raw_offset = *raw;
    cinfo[fd].o2dac_scale = *scale;
    cinfo[fd].trig_offset = *trig;
  }
  if(raw != NULL || scale != NULL || trig != NULL) {
    *raw = cinfo[fd].raw_offset;
    *scale = cinfo[fd].o2dac_scale;
    *trig = cinfo[fd].trig_offset;
  }
  if(!writereg(fd, 0x1, 0x20)) return 0;
  if(!writereg(fd, 0x1, 0x24)) return 0;
  if(!writereg(fd, 0x1, 0x04)) return 0;
  if(!writereg(fd, 0x1, 0x24)) return 0;
  if(!scope50_set_norm_trig(fd, 0)) return 0;
  if(!scope50_set_trig_type(fd, SCOPE50_TRIG_TYPE_EDGE_UP)) return 0;
  if(!usbtm_set_led_mode(fd, USBTM_LED_MODE_NORMAL)) return 0;
  if(!scope50_setup_front_end(fd, SCOPE50_RANGE_3V, SCOPE50_COUPLING_DC, 0)) return 0;
  if(!scope50_set_offset(fd, 0, NULL)) return 0;
  if(!scope50_set_trig_thresh(fd, 0)) return 0;
  use_ris = 0;
  return 1;
}