src/hp34comm.cpp

Tue, 20 Sep 2016 23:50:45 +0200

author
David Douard <david.douard@logilab.fr>
date
Tue, 20 Sep 2016 23:50:45 +0200
changeset 5
f1c85c2500f2
child 8
55021f3f1929
permissions
-rw-r--r--

several improvements

- add (used parts of) unigraphics in repo
- extract hp communication protocol listener in a dedicated file

#include "hp34comm.h"
#include "mbed.h"
#include "CircularBuffer.h"

/***** HP 34970A communication class ***/

#define HP_SERIAL_TX PC_7 // serial6 RX
#define HP_SERIAL_RX PA_1 // serial4 RX

#ifdef DEBUG

DigitalOut inrx(PC_0);
DigitalOut intx(PC_1);
DigitalOut ack(PC_3);
DigitalOut staterx(PC_2);
DigitalOut statetx(PH_1);

#endif

  
HPSerial::HPSerial():
  ncmd(0),
  serial_tx(NC, HP_SERIAL_TX), serial_rx(NC, HP_SERIAL_RX) {
  
  //pc.printf("HPSerial init\n");

  for(uint8_t i=0; i<MAX_ERRS; i++)
    errs[i] = 0;
  reset();
  
  serial_tx.baud(187500);
  serial_tx.attach(this, &HPSerial::txIrq, Serial::RxIrq); //sic!
  
  serial_rx.baud(187500);
  serial_rx.attach(this, &HPSerial::rxIrq, Serial::RxIrq);
}

bool HPSerial::cmd_available(void) {
  return !cmdbuf.empty();
}

bool HPSerial::pop(CMD& cmd) {
  return cmdbuf.pop(cmd);
}

bool HPSerial::cmd_buf_full(void) {
  return cmdbuf.full();
}

unsigned int HPSerial::nerrors(uint8_t errorno) {
  return errs[errorno];
}
    
		     
  
void HPSerial::reset(uint8_t errorno) {
  timeouter.detach();
  head = 0;
  tx_state = Idle;
  setstatedbg();
  tx_cmd = 0xFF;
  tx_ack = false;
  tx_len = 0xFF;
  memset(buf, 0, BUF_SIZE);
  if (errorno != 0xFF){
    errs[errorno]++;
  }
  
}

void HPSerial::handleAck(uint8_t val) {
  if ((tx_state == Rx) & (tx_cmd == 0xFF) & (tx_ack == false) & (val == 0x66)) {
    // special case: keypad does not acknwledge and takes precedence
    // on the "bus"
    tx_state = Tx;
    setstatedbg();
  } else
    
    if (tx_ack == true)
      reset(0);
  
    else
      if (tx_cmd == 0xFF) // still at the beginning of a packet, expect 0x99 as ack
	if (val == 0x99)
	  {
	    tx_ack = true;
#ifdef DEBUG
	    ack = 1;
	    wait_us(2);
	    ack = 0;
#endif
	  }
	else 
	  reset(1);

      else // expect 0x00 as ack
	if (val == 0x00)
	  {
	    tx_ack = true;
#ifdef DEBUG
	    ack = 1;
	    wait_us(2);
	    ack = 0;
	    wait_us(2);
	    ack = 1;
	    wait_us(2);
	    ack = 0;
#endif
	  }
	else
	  reset(2);
}

void HPSerial::pushCmd(TrState direction, uint8_t cmd, uint8_t size, char *payload) {
  CMD val;
  uint8_t i;
  val.id = ncmd++;
  val.direction = direction;
  val.cmd = cmd;
  val.size = size;
  for(i=0; i<size; i++)
    val.value[i] = payload[i];
  val.value[i] = 0x00;
  cmdbuf.push(val);
}

void HPSerial::handleChar(uint8_t val) {
  if (tx_ack == false)
    reset(3);
  else // remaining of the state machine
    if (tx_cmd == 0xFF) {
      // begin of transmission, expect a cmd
      tx_cmd = val;
      tx_ack = false;
      if (tx_state == Rx)
	tx_len = 0xFF;
      else
	tx_len = 0x00; // no payload: tx_cmd is the key stroke
    }
    else if (tx_len == 0xFF) {
      // got a cmd, expect a payload size
      tx_len = val;
      tx_ack = false;
    }
    else if (tx_len > 0) {
      // a payload char
      buf[head++] = val;
      tx_len--;
      tx_ack = false;
    }
    else { // end of payload, manage sent content
      uint8_t cur_state = tx_state;
      pushCmd(tx_state, tx_cmd, head, buf); 
      
      switch(val) {
      case 0x66: // a new transmisson
	reset();
	tx_state = cur_state;
	setstatedbg();
	break;
      case 0x55:
	reset();
	break;
      default:
	reset(6);
	break;
      }
    }
}



void HPSerial::setstatedbg(void) {
  /*
    if (tx_state == Rx)
    staterx = 1;
    else
    staterx = 0;
    if (tx_state == Tx)
    statetx = 1;
    else
    statetx = 0;
  */
}

void HPSerial::rxIrq(void) {
  uint8_t val;
  if(serial_rx.readable()) { // no reason why we would end here without this condition, but hey
#ifdef DEBUG
    inrx=1;
#endif
    val = serial_rx.getc();
    
    timeouter.attach(this, &HPSerial::timeout, 0.001); // if nothing else happen in the next ms, reset
    
    if (tx_state == Idle)
      if (val == 0x66) {
	// no transmission in progress, expect a start of transmission
	tx_state = Rx;
	tx_ack = false;
	setstatedbg();
      }
      else
	reset(4);
    
    else if (tx_state == Tx)  // manage the acks
      handleAck(val);
    
    else 
      handleChar(val);
#ifdef DEBUG
    inrx=0;
#endif
  }
}

void HPSerial::txIrq(void) {
  uint8_t val;
  if(serial_tx.readable()) { // no reason why we would end here without this condition, but hey
#ifdef DEBUG
    intx=1;
#endif
    val = serial_tx.getc();
    
    timeouter.attach(this, &HPSerial::timeout, 0.001); // if nothing else happen in the next ms, reset
    
    if (tx_state == Idle)
      if (val == 0x66) {
	// no transmission in progress, expect a start of transmission
	tx_state = Tx;
	tx_ack = false;
	setstatedbg();
      }
      else
	reset(5);
    
    else if (tx_state == Rx)  // manage the acks
      handleAck(val);
    
    else 
      handleChar(val);
  }
#ifdef DEBUG
  intx=0;
#endif
}

void HPSerial::timeout(void) {
  if (tx_state != Idle) {
    reset(7);
  }
}

mercurial