Go Down

Topic: TIMSK1 equivilent on Due?? (Read 664 times) previous topic - next topic

duramaxhd

Hi everyone....I have a sketch that does exactly what I need it to on the Uno....but it wont compile properly for the Due.

"TIMSK1 not declared in this scope".

Ive done some searching and reading on this and understand that its because the timers/interrupts work a lot differently on the 8-bit AVR's vs the 32-bit ARM.

My question is...how would I/what would be the best way to "convert/translate" this sketch into something equivalent that will work on the Due??

The sketch is to read/interpret the J1850-VPW automotive databus protocol, and then print it to the serial terminal as decoded 8-byte hex messages from the J1850 bus.

===========================================================================

Quick background: the J1850 VPW bus uses a series of alternating high/low pulses on a single wire of either 64uS or 128uS to signify a "1" bit or "0" bit. A 200uS "high" pulse is first sent by the transmitting node denotes start of frame, to alert all nodes on the bus that a message is about to be sent out...

A "low" pulse of 128uS or a "high" pulse of 64uS signifies a "1" bit.
A "high" pulse of 128uS or a "low" pulse of 64uS signifies a "0" bit.

The entire message starts as a 200uS "high" pulse...then 3 bytes of header info, 8 bytes max data payload, a 1 byte CRC, and finally a 200uS "low" pulse to signify end of message transmission.

===========================================================================


Code: [Select]
// I/O and interrupts. Pin/Int
// UNO:   2/0, 3/1.

#define J1850_PIN  3
#define J1850_INT  1


// Timing for start of frame
#define SOF_TIME      200
#define SOF_DEV        18

// Timing for end of frame
#define EOF_TIME      200
#define EOF_DEV        18

// Timing for a long bit pulse
#define LONGBIT_TIME   128
#define LONGBIT_DEV     16

// Timing for short bit pulse
#define SHORTBIT_TIME   64
#define SHORTBIT_DEV    15

// timeout after 250 microsec
#define  TMR_PRELOAD (65536 - (EOF_TIME*16))

#define TMROVF_INT_OFF   TIMSK1 &= (~_BV(TOIE1))
#define TMROVF_INT_ON    TIMSK1 |= _BV(TOIE1)
#define TMROVF_INT_CLR   TIFR1 &= _BV(TOV1)


// forward decl
void j1850_interrupt(void);

volatile boolean idle = true;
// Storage, max 11 data bytes + CRC
#define BUFSIZE 13
volatile uint8_t msgbuf[BUFSIZE];
volatile uint8_t msgLen;


//
// Initialization
//
void setup(void)

  pinMode(J1850_PIN, INPUT);

  Serial.begin(115200);
  delay(1000);
  Serial.println(F("j1850decoder/v1.5"));

  TMROVF_INT_OFF;
  TCCR1A = 0;
  TCNT1 = TMR_PRELOAD;  // preload timer 65536-16MHz/256/2Hz
  TCCR1B = _BV(CS10);  // no prescaler, start timer

  idle = true;
  msgLen = 0;
  attachInterrupt(J1850_INT, j1850_interrupt, CHANGE);
  interrupts();
}


//
// Background loop - print message when available
//
void loop(void)
{
  if (msgLen > 0) {
    Serial.print(F("> "));
    for (int i = 0; i < msgLen; i++) {
          if ((i == 3) || (i == msgLen -1)){
        Serial.print("   ");
      }
      if (msgbuf[i] < 16) Serial.print("0");
      Serial.print(msgbuf[i], HEX);
    }
    Serial.println();
    msgLen = 0;
  }
}

//
// Interrupt routine for changes on j1850 data pin
//

volatile unsigned long lastInt = 0;
volatile uint8_t bitCnt;
volatile long delta;
volatile unsigned long tstamp;
volatile uint8_t aByte;
volatile uint8_t buf[BUFSIZE];
volatile uint8_t bufIdx;


void j1850_interrupt(void)
{
  tstamp = micros();

  uint8_t pin = digitalRead(J1850_PIN);

  // reload the overflow timer with EOF timeout
  TCNT1 = TMR_PRELOAD;           

  delta = tstamp - lastInt;
  long longbit, shortbit;

  if (idle)
  {
    if (pin == 0)
    {
      longbit = delta - SOF_TIME;
      if (abs(longbit) < SOF_DEV)
      {
        // found SOF, start header/data sampling
        idle = false;
        bitCnt = 0;
        bufIdx = 0;
        aByte = 0;
      }
    }
  }
  else
  {
    shortbit = delta - SHORTBIT_TIME;
    longbit = delta - LONGBIT_TIME;

    if (abs(shortbit) < SHORTBIT_DEV) {
      // short pulse
      if (pin == 0)
        // short pulse & pulse was high => active "1"
        aByte = (aByte << 1) | 0x01;
      else
        // short pulse & pulse was low => passive "0"
        aByte = (aByte << 1) & 0xFE;
      bitCnt++;

    }
    else if (abs(longbit) < LONGBIT_DEV) {
      // long pulse
      if (pin == 0)
        // long pulse & pulse was high => active "0"
        aByte = (aByte << 1) & 0xFE;
      else
        // long pulse & pulse was low => passive "1"
        aByte = (aByte << 1) | 0x01;
      bitCnt++;

    }
    else {
      // unknown bit, reset
      TMROVF_INT_OFF;
      idle = true;
      lastInt = tstamp;
      return;
    }

    if (bitCnt >= 8) {

      buf[bufIdx++] = aByte;
      bitCnt = 0;
      if (bufIdx >= sizeof(buf)) {
        // too many data bytes, error
        TMROVF_INT_OFF;
        idle = true;
      }
      else {
        // if all is ok, start the EOF timeout
        TMROVF_INT_CLR;
        TMROVF_INT_ON;
      }
    }
  }
  lastInt = tstamp;
}

// Timer overlflow interrupt
// Occurs when the EOF pulse times out the timer
//
ISR(TIMER1_OVF_vect) 
{
  TCNT1 = TMR_PRELOAD; 
  TMROVF_INT_OFF;

  // copy the data so that we can start to fill the buffer again
  // but only if the buffer has been consumed in the background
  if (bufIdx > 0 && msgLen == 0)
  {
    memcpy((void*)msgbuf, (const void*)buf, bufIdx);
    msgLen = bufIdx;
  }
  idle = true;
}



Ideally it would be nice to make this code "non-blocking" on the Due as well...Im sure its something that could be handled as a background task??

Thanks for any help/advice!

AdderD

I wouldn't even use a timer. On the Due you can interrupt on any digital pin changing state. So, set a pin change interrupt on the pin in question. Now you're half way there. The other part of the equation is how to detect how long the pulse was. Well, micros() gives you the number of microseconds elapsed since system start up. So you capture the current micros() value each time the pin state changes. You compare the new state to the old state and compare the current value with the old one and you end up with a determination of what happened. The pin change interrupt is just that so you don't need to spend a single moment polling for anything - it truly would be a background task.

So, look at the reference for attachInterrupt: https://cdn.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

bobcousins

TIMSK1 is the Timer 1 interrupt mask register. In order to replicate the timer function, I would use https://github.com/ivanseidel/DueTimer. Basically you need use a timer interrupt, and start, stop and set period functions.

TMROVF_INT_OFF   => stop the timer
TMROVF_INT_ON    => start the timer
TCNT1 = TMR_PRELOAD   =>    set the timer period
Please ask questions in the forum so everyone can benefit. PM me for paid work.

duramaxhd

I wouldn't even use a timer. On the Due you can interrupt on any digital pin changing state. So, set a pin change interrupt on the pin in question. Now you're half way there. The other part of the equation is how to detect how long the pulse was. Well, micros() gives you the number of microseconds elapsed since system start up. So you capture the current micros() value each time the pin state changes. You compare the new state to the old state and compare the current value with the old one and you end up with a determination of what happened. The pin change interrupt is just that so you don't need to spend a single moment polling for anything - it truly would be a background task.

So, look at the reference for attachInterrupt: https://cdn.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

I initially tried using attachInterrupt.....but it seemed to confuse the Due and it would act erratically.

Due to the short pulses (64uS) maybe?

I would have it jump to a function, and first line in the interrupt function would be detatchInterrupt (to stop it from restarting the interrupt function after that initial state change in the input wire)....but sometimes it would still do a couple loops of the interrupt function? It was almost like it wasnt detatching the interrupt quickly enough....? Is that even possible?

ard_newbie


MarkT

I wouldn't call detachInterrupt in an ISR, or at all come to that.  Basically call attachInterrupt once
only in setup for a pin, and then leave it like that.

Make the ISR manage its own state with volatile variables, much more likely to work.  You don't want
code to monkey with interrupt control registers in the middle of handling an ISR, thats full of
pitfalls for the unwary.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up