src/hp34comm.h~

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
permissions
-rw-r--r--

several improvements

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

#ifndef HP34COMM_H
#define HP34COMM_H

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

class HPSerial {

public:
  enum TrState {
        Idle = 0,
        Tx,
        Rx,
    };
  typedef struct _CMD
  {
    TrState direction;
    uint8_t cmd;
    uint8_t size;
    char value[MAX_BUFF+1];
    unsigned long id;
  } CMD;


  
  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 cmd_available(void) {
    return !cmdbuf.empty();
  }

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

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

  unsigned int nerrors(uint8_t errorno) {
    return errs[errorno];
  }
    
		     
  
private:
  void reset(uint8_t errorno=0xFF) {
    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]++;
      pulse(errorno+1);
    }
    
  }

  void 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 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 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 setstatedbg(void) {
    /*
    if (tx_state == Rx)
      staterx = 1;
    else
      staterx = 0;
    if (tx_state == Tx)
      statetx = 1;
    else
      statetx = 0;
    */
  }
    
  void 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 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 timeout(void) {
    if (tx_state != Idle) {
      reset(7);
    }
  }
private:
  RawSerial serial_tx;
  RawSerial serial_rx;
  uint8_t buf[BUF_SIZE];
  uint8_t head;
  uint8_t tx_state;
  uint8_t tx_cmd;
  uint8_t tx_len;
  bool tx_ack;
  CircularBuffer<CMD, 32> cmdbuf;
  unsigned long ncmd;
  unsigned int errs[MAX_ERRS];
  Ticker timeouter;
};

#endif

mercurial