manchester decoding teensy 3.2 with uart

Status
Not open for further replies.
Quick update,got it working exactly as it should. Just changed frequency to 98khz and it lined up perfect. Still have to learn exactly what all the code is doing, but have already learned a lot from implementing this code. Thanks again.
 
I'm working thru the code in this thread to get an OPB9000 working myself.

Have you made any progress since this last post on reliably getting the information decoded and/or can share any additional code?

Thanks!
 
Hi,

Well this was long time ago, pulled up my old code from end of 2017 and had to look at it. I don't remember what exactly I did or what worked. I think I had it so I had to determine the frequency values and LD value (not sure what that is), but once I knew those I could calibrate the OPB9000 (but if I remember correctly, the calibration from factory was just as good, so I didn't do much more with it). I have two sensors, Index(Idx) and Degree(deg). I will post code below, maybe it has some helpful information for you. This code was specific to a custom PCB, so make sure you change your pins.
Code:
//Has to be on white for read command to work
//Low Signal is when sensor is on White when Push-Pull inverted
//Command 1 — send write command to set output type (OP and DS bits, table 1) and comparator reference level bits (table 2)
//Command 2 — send command to calibrate LED drive
//Command 3 — send write command to change comparator to new desired reference level
//Degree 30, calibration 25
//Index 2, calibration 24
//103E3 Unit 6 V4 test unit ldval=550 Index, degree ldval=650
//Benchtop test unit, index=103(ldval=550), Degree=98(ldval=650)
//Added ability to read 13ms calibration success command
//Can read output by changing read level to either high or low, seems to work well

//#define calPin 25 //all commands sent via the cal/stat pin
//#define outputPin 30
//Index
//#define calPin 24
//#define outputPin 2
byte calPin = 24 ;//25
byte outputPin = 2;//30

int led = 13;

//sensor setting arrays
//first one is benchtop test unit
uint16_t idxLD[] = {550, 550, 750, 750, 650, 750, 750};
uint16_t degLD[] = {650, 650, 750, 650, 650, 750, 750};
uint32_t idxFREQ[] = {103000, 103000, 101000, 102000, 102000, 98500, 99000};
uint32_t degFREQ[] = {98000, 103000, 98000, 102000, 100500, 98000, 97500};
uint16_t LDVAL = 550;
uint32_t freq = 103000;
int cycles;
uint32_t val = 0;
uint16_t CalTime;
uint16_t CalStart;
boolean CalStatus = false;
byte readLevel = HIGH;
byte readOffset = 0;

boolean READ_FLAG = false;
//bit reversal for 16 bit types
#define bitrev16(dat)   (((unsigned short) bitrev8(dat) << 8) | ((unsigned short) bitrev8(dat >> 8)))
#define testPin1 31

volatile uint8_t buf[37];
volatile unsigned bitCnt = 0;
String wData;
word writeArray[16] = {
  0b0101101010100000,
  0b0101101010010000,
  0b0101101001100000,
  0b0101101001010000,
  0b0101100110100000,
  0b0101100110010000,
  0b0101100101100000,
  0b0101100101010000,
  0b0101011010100000,
  0b0101011010010000,
  0b0101011001100000,
  0b0101011001010000,
  0b0101010110100000,
  0b0101010110010000,
  0b0101010101100000,
  0b0101010101010000
};

IntervalTimer pit;
KINETISK_PIT_CHANNEL_t* PIT_Channel;
bool stop = true;
//00 = 1010
//01 = 1001
//10 = 0110
//11 = 0101
word ccmd = 0b0101101001010000;//only 12 bit, but need to use 16 bit, add 0000 pad
word rcmd = 0b0101101010010000;//1100-01
word wcmd = 0b0101101001100000;

void setup() {
  Serial.begin(9600);
  pinMode(calPin, OUTPUT);
  pinMode(outputPin, INPUT);
  pinMode(testPin1, OUTPUT);
  pinMode(led, OUTPUT);
  pinMode(17, OUTPUT);//for v4 board to keep powered up
  digitalWrite(17, HIGH);

  digitalWriteFast(testPin1, LOW);
  digitalWrite(calPin, HIGH);

  // setup interval timer
  pit.priority(0);           // highest
  pit.begin(PIT_ISR, 1E6);   // let teensyduino grab a free timer and do all the initialization work.

  // Get hold of the register block for the used timer
  PIT_Channel = KINETISK_PIT_CHANNELS + ((IRQ_NUMBER_t)pit - IRQ_PIT_CH0);
  PIT_Channel->TCTRL = 0;   // stop the timer

  stop = true;

  ccmd = bitrev16(ccmd);//reverse since read LSB-MSB
  rcmd = bitrev16(rcmd);
  wcmd = bitrev16(wcmd);

  while (!Serial) {
    yield();
  }
  Serial.println();
  Serial.println("Set sensor # before reading/calibrating");
  Serial.println("Index Pin initialized");
  Serial.println(F("S# - Select Sensor eg:S1 = Sensor 1"));
  Serial.println(F("I - Initialize Index Sensor"));
  Serial.println(F("D - Initialize Degree Sensor"));
  Serial.println(F("r - read settings"));
  Serial.println(F("w# - Write Level eg:w0 = level 0"));
  Serial.println(F("c - Calibrate"));
  Serial.println(F("l - Switch Read Level High/Low"));
  Serial.println(F("p - Print Options"));
}

unsigned char bitrev8(unsigned char bits) {
  unsigned char tmp = 0;
  //bit reversing, msb to lsb
  tmp |= (bits & 0x80) ? 0x01 : 0;
  tmp |= (bits & 0x40) ? 0x02 : 0;
  tmp |= (bits & 0x20) ? 0x04 : 0;
  tmp |= (bits & 0x10) ? 0x08 : 0;
  tmp |= (bits & 0x08) ? 0x10 : 0;
  tmp |= (bits & 0x04) ? 0x20 : 0;
  tmp |= (bits & 0x02) ? 0x40 : 0;
  tmp |= (bits & 0x01) ? 0x80 : 0;
  return tmp;
}


void loop() {

  if ( Serial.available()) {
    char ch = Serial.read();
    switch (ch) {
      case 'S': {//Set value of sensor to read/calibrate
          while (Serial.available() == 0); {
            val = Serial.parseInt();
            Serial.print("Sensor set to ");
            Serial.println(val);
          }
          break;
        }
      case 'p': { //print options again
          Serial.println();
          Serial.println(F("S# - Select Sensor eg:S1 = Sensor 1"));
          Serial.println(F("I - Initialize Index Sensor"));
          Serial.println(F("D - Initialize Degree Sensor"));
          Serial.println(F("r - read settings"));
          Serial.println(F("w# - Write Level eg:w0 = level 0"));
          Serial.println(F("c - Calibrate"));
          Serial.println(F("L - Switch Read Level High/Low"));
          Serial.println(F("p - Print Options"));
          break;
        }

      case'c': {
          //how to read pin and signal via LED, so we know we are on white
          //use while we wait for serial input
//          while (!Serial.available()) {
//            digitalWriteFast(led, digitalReadFast(outputPin));
//            if (Serial.available()) {
//              break;
//            }
//          }
          pinMode(calPin, OUTPUT);
          for (byte i = 0; i < 12; i++) {
            if (bitRead(ccmd, i) == 0) {
              digitalWriteFast(calPin, 0);
            } else {
              digitalWriteFast(calPin, 1);
            }
            delayMicroseconds(10);
          }
          digitalWriteFast(calPin, 0);
          Serial.println("Calibration Sent");
          pinMode(calPin, INPUT);//after calibrate it responds, so need to be input

          delay(2);
          digitalWriteFast(testPin1, HIGH);
          attachInterrupt(calPin, calStatus, CHANGE);
          delay(100);
          Serial.println(CalTime);
          if (CalTime > 10) Serial.println("Calibration Successful");
          detachInterrupt(calPin);
          break;
        }
              case 'L': {//change LDVAL value
          while (Serial.available() == 0); {
            LDVAL = Serial.parseInt();
            Serial.print("LDVAL set to ");
            Serial.println(LDVAL);
          }
          break;
        }
                      case 'F': {//change Freq value
          while (Serial.available() == 0); {
            freq = Serial.parseInt();
            Serial.print("freq set to ");
            Serial.println(freq);
          }
          break;
        }
                              case 'f': {
            Serial.print("freq: ");
            Serial.println(freq);
            Serial.print("LDVAL: ");
            Serial.println(LDVAL);
          break;
        }
      case 'I': {
          calPin = 24 ;
          outputPin = 2;
          LDVAL = idxLD[val];
          freq = idxFREQ[val];
          Serial.println("Index Sensor Initilized");
          Serial.print("LDVAL set to ");
          Serial.println(LDVAL);
          Serial.print("Read frequency set to ");
          Serial.println(freq);
          break;
        }
      case 'D': {
          calPin = 25 ;
          outputPin = 30;
          LDVAL = degLD[val];
          freq = degFREQ[val];
          Serial.println("Degree Sensor Initilized");
          Serial.print("LDVAL set to ");
          Serial.println(LDVAL);
          Serial.print("Read frequency set to ");
          Serial.println(freq);
          break;
        }
      case 'r': {
          pinMode(calPin, OUTPUT);
          attachInterrupt(outputPin, PIN_ISR, FALLING);//to read back response
          retrigger();
          for (byte i = 0; i < 12; i++) {
            if (bitRead(rcmd, i) == 0) {
              digitalWriteFast(calPin, 0);
            } else {
              digitalWriteFast(calPin, 1);
            }
            delayMicroseconds(10);//try different values to trigger response
          }
          pinMode(calPin, INPUT);
          delay(50);//delay to allow time to read response
          if (bitCnt == sizeof(buf)) //fill array at each tooth
          {
            detachInterrupt(outputPin);
            for (byte i = 0; i < sizeof(buf); i++) {
              Serial.print(buf[i]);
            }
            Serial.println();

            byte refValue = 0;
            for (int i = 3, j = 27 + readOffset; i > -1; i--) {
              refValue += buf[j--] << i;//since MSB when read back
            }
            Serial.print("Reference level: ");
            Serial.println(refValue);

            byte ledCount = 0;
            for (int i = 9, j = 23 + readOffset; i > -1; i--) {
              ledCount += buf[j--] << i;
            }
            Serial.print("LED Count: ");
            Serial.println(ledCount);

            byte AGC = 0;
            for (int i = 1, j = 13 + readOffset; i > -1; i--) {
              AGC += buf[j--] << i;
            }
            Serial.print("Gain Level: ");
            Serial.println(AGC);

            byte reservedCount = 0;//should always be 0
            for (int i = 6, j = 36 + readOffset; i > -1; i--) {
              reservedCount += buf[j--] << i;
            }
            Serial.print("Reserved Bits: ");
            Serial.println(reservedCount);

            byte bank3 = 0;//9 bits at beginning, always the same
            for (int i = 8, j = 10 + readOffset; i > -1; i--) {
              bank3 += buf[j--] << i;
            }
            Serial.print("Bank3 Bits: ");
            Serial.println(bank3);

            if (buf[28 + readOffset] == 0) {
              Serial.println("Open Drain");
            }
            else {
              Serial.println("Push-Pull");
            }

            if (buf[29 + readOffset] == 0) {
              Serial.println("Buffered");
            }
            else {
              Serial.println("Inverted");
            }

            if (buf[11 + readOffset] == 0) {
              Serial.println("Calibration Failed");
            }
            else {
              Serial.println("Calibration Successful");
            }
            if (buf[1 + readOffset] == 0) {
              Serial.println("No Errors");
            }
            else {
              Serial.println("Error");
            }
            //use below to make sure read values are correct since bank3 and last bank are always the same
            if (reservedCount == 0 && bank3 == 168) { //have to dbl check bank3 value
              Serial.println("No Read Errors");
            }
            else {
              Serial.println("Read Errors, check LDVAL or read Hz settings");
            }
          }
          break;
        }

      case 'w': {//Ref Level 0 0000 = 10101010

          while (Serial.available() == 0); {
            val = Serial.parseInt();
            Serial.print("Writing Reference Level ");
            Serial.println(val);
          }
          writeCmd(writeArray[val]);
          break;
        }

      case 'l': { //switch readlevel
          readLevel = !readLevel;
          readOffset = !readOffset;
          Serial.println(readLevel);
          Serial.println(readOffset);
        }
    }
  }
}

void writeCmd(word wData) {
  //Write Bank 2 Bits 1100-10-bbbbbb
  //010110100110
  pinMode(calPin, OUTPUT);
  wData = bitrev16(wData);
  //Send write command first
  for (byte i = 0; i < 12; i++) {
    if (bitRead(wcmd, i) == 0) {
      digitalWriteFast(calPin, 0);
    } else {
      digitalWriteFast(calPin, 1);
    }
    delayMicroseconds(10);
  }
  //Writting Output type and comparator reference levels
  for (byte i = 0; i < 12; i++) {
    if (bitRead(wData, i) == 0) {
      digitalWriteFast(calPin, 0);
    } else {
      digitalWriteFast(calPin, 1);
    }
    delayMicroseconds(10);
  }
  Serial.println("Write Done");
}

void PIT_ISR() {
  if (bitCnt < sizeof(buf))
  {
    digitalWriteFast(testPin1, HIGH);
    buf[bitCnt] = digitalReadFast(outputPin);
    bitCnt++;
    digitalWriteFast(testPin1, LOW);               // debugging
  }
  else // stop timer
  {
    PIT_Channel->TCTRL = 0; //stop PIT
  }
}

// pin interrupt
void PIN_ISR()
{
  if (stop) return;   // just a quick hack to avoid looking up the correct register settings :-)
  stop = true;

  // To align the timer with the clock we need to manually adjust the first timer period.
  PIT_Channel->TCTRL = 0;     // to immediately change LDVAL the timer need to be stopped first. Otherwise LDVAL gets buffered and will be loaded at the next zero transition
  PIT_Channel->LDVAL = LDVAL;   // adjust, so that the first PIT_ISR() is called at the correct time.
  PIT_Channel->TCTRL = 3;     // start the PID

  // Set the timer reload values for the follwing periods according to the frequency of the Manchester signal
  //OPB9000 sends read at 100 kb/s ± 5 kb/s.
  //think only way is to either know data we will see and compare, or use scope to dial in each encoder
  //use scope to measure Hz of output, then enter that value in as freq
  //103 Unit 6 V4 test unit
  //Benchtop test unit, index=103(ldval=550), Degree=98(ldval=650)
  //constexpr float freq = 98E3;
  //constexpr unsigned cycles = F_BUS / freq;
  cycles = F_BUS / freq;
  PIT_Channel->LDVAL = cycles; // This value will be buffered and is loaded after at the first timer zero transition

  //The PIT will miss the first clock cycle (can not choose the first LDVAL small enough, maybe a T3.6 would be quick enough)
  //To avoid handling a 7bit byte we simply call the first PIT_ISR manually (after a short delay)
  volatile int dummy; for (int i = 0; i < 5; i++) {
    dummy *= dummy;
  }
  PIT_ISR();
}

// this simply waits until it detects 15µs bus inactivity.
void sync()
{
  int i = 0;
  while (i < 15)
  {
    //maybe read pin, soon as it goes low, start, first value always 0
    i = (digitalReadFast(outputPin) == readLevel) ? 0 : (i + 1); //if low =0, if high increment
    delayMicroseconds(1);
  }
}

void retrigger()
{
  sync();
  stop = false;
  bitCnt = 0;
}

void calStatus() { //time any Calibration signal time
  CalTime = millis() - CalStart;
  CalStart = millis();
}


// else if (c == 'b') {//Ref Level 1
//    writeCmd(0b0101101010010000);// 0001 = 10101001
//  }
//  else if (c == 'd') {//Ref Level //2 = 0010 = 10100110
//    writeCmd(0b0101101001100000);
//  }
//  else if (c == 'e') {//Ref Level //3 = 0011 = 10100101
//    writeCmd(0b0101101001010000);
//  }
//  else if (c == 'f') {//Ref Level  //4 = 0100 = 10011010
//    writeCmd(0b0101100110100000);
//  }
//  else if (c == 'g') {//Ref Level //5 = 0101 = 10011001
//    writeCmd(0b0101100110010000);
//  }
//  else if (c == 'h') {//Ref Level //6 = 0110 = 10010110
//    writeCmd(0b0101100101100000);
//  }
//  else if (c == 'i') {//Ref Level //7 = 0111 = 10010101
//    writeCmd(0b0101100101010000);
//  }
//  else if (c == 'j') {//Ref Level //8 = 1000 = 01101010
//    writeCmd(0b0101011010100000);
//  }
//  else if (c == 'k') {//Ref Level //9 = 1001 = 01101001
//    writeCmd(0b0101011010010000);
//  }
//  else if (c == 'l') {//Ref Level //10 = 1010 = 01100110
//    writeCmd(0b0101011001100000);
//  }
//  //00 = 1010
//  //01 = 1001
//  //10 = 0110
//  //11 = 0101
//  else if (c == 'm') {//Ref Level //11 = 1011 =01100101
//    writeCmd(0b0101011001010000);
//  }
//  else if (c == 'n') {//Ref Level //12 = 1100 = 01011010
//    writeCmd(0b0101010110100000);
//  }
//  else if (c == 'o') {//Ref Level //13 = 1101 = 01011001
//    writeCmd(0b0101010110010000);
//  }
//  else if (c == 'p') {//Ref Level //14 = 1110 = 01010110
//    writeCmd(0b0101010101100000);
//  }
//  else if (c == 'q') {//Ref Level //15 = 1111 = 01010101
//    writeCmd(0b0101010101010000);
//  }
 
Fantastic work. Thank you for sharing!!

The OPB9000 is a very cool sensor but they made the datasheet and interface as cryptic as possible.

Your code pretty much works out of the box (with pin modifications)
 
Status
Not open for further replies.
Back
Top