Go Down

Topic: millis() and the disabled timer (Read 2213 times) previous topic - next topic

joey2

I am editing the code below, it's for sniffing i2c bus on the fm-tuner. In this code i wanna use millis() for measuring time between block_2 and block_4, and also for other things. But code use interrupts and built-in timer switched to off. If I enable it all decoding become very unstable. So how can i use millis()? Stand alone oscillator or something can be changing in the code?

Code: [Select]
/*
 
  Arduino program for interfacing the Sony XDR-F1HD Receiver to RDS Spy.
  (Tested on both ATMEGA168 and ATMEGA328 based Arduinos.)
 
  (c) 2010-2011 by Ray H. Dees
 
  Version   Modified By     Date         Comments
  .10       R. Dees      12/23/2010      Initial Release.
  .11       R. Dees      01/04/2011      Modified to support RDS Spy (Version 0.94) "RESET" command.
  .12       R. Dees      04/10/2011      Modified to support Arduinos running at 16mHz on 3.3v.
 
 
  Inputs:
 
  Serial Data (SDA)  -  Arduino Digital Pin 2.
  Serial Clock (SCK) -  Arduino Digital Pin 3.
 
  Ouputs:
 
  RDS data formatted in RDS Spy protocol via the Arduino USB port.
 
  Setup:
 
  RDS Spy should be configured as follows:
 
  Select Configure,
  Select RDS Source,
  Select P75/P175 FM Analyzer,
  Set Connection Type RS232/USB,
  Select the RS232 COM Port that your Arduino appears as.
 
  You may use this program for personal purposes only.  The use of this program
  in a commercial product requires explicit written permission from the author.
  The author is not responsible or liable for damage or loss that may be caused
  by the use of this program.

*/
#include <WProgram.h>
//
//  Definitions
//
#define SDA              2
#define SCK              3
#define BUFFER_LENGTH   64
#define DELAY  asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop"); asm volatile("nop");
//
enum {IDLE, START, RCVG, STOP};
//
//  Global variables
//
byte* rxBuffer = 0;
volatile byte rxBufferIndex = 0;
volatile byte rxBits = 0;
volatile byte rxComplete = 0;
volatile byte rxCount = 0;
volatile byte rxState = 0;
//
word address = 0;
word data = 0;
byte block = 0;
word block_1;
word block_2;
word block_3;
word block_4;
byte group_complete = 0;
byte synchronized = 0;
//
//  Setup begins here.
//
void setup()
{
  delay(500);      //  A 500ms delay, change this to suit your taste!
  pinMode(SDA, INPUT);
  digitalWrite(SDA, HIGH);
  pinMode(SCK, INPUT);
  digitalWrite(SCK, HIGH);
  delay(100); 
  initialize();    // Call initialize to set everything up.
  delay(100);
  Serial.begin(19200);
}
//
//  Main Program Loop.
//
void loop()
{
   while(1)
    {
      if (rxComplete)
        {
          update_blocks();
          rxComplete = 0;
        } 
           
      if (group_complete)
        {
          send_data();
          group_complete = 0;
        }
    }     
}   
//
//  RDS Blocks get updated and sorted here.  First the register address, then the corresponding data.
//
void update_blocks()
{
  if (rxBuffer[0] != 0x38)  //  This version does not support HD, return.
    return;
 
  if (rxBuffer[4] == 0xC4)  //  Any write to address 0xC4 is a channel change.
    {
      clear();              //  Reset RDS Spy.
      return;
    }   
 
  if (rxBuffer[3] == 0x30)   
    {
      address = 0x0000 | rxBuffer[5] << 8 | rxBuffer[6];
      synchronized = 1;
     
      switch (address)
        {
          case 0x0080:
            block = 1;
            break;
         
          case 0x0084:
            block = 2;
            break;
         
          case 0x0088:
            block = 3;
            break;
           
          case 0x008C:
            block = 4;
            break;
           
          default:
            block = 0;
            synchronized = 0;
        }     
      return;
    }
 
  if (!synchronized)
    return;
   
  if (rxBuffer[3] == 0x31)
    {
      data = 0x0000 | rxBuffer[5] << 8 | rxBuffer[6];
     
      switch (block)
        {
          case 1:
            block_1 = data;
            break;
         
          case 2:
            block_2 = data;
            break;   
         
          case 3:
            block_3 = data;
            break;
         
          case 4:
            block_4 = data;
            group_complete = 1;
            break;
       }
    }   
}
//
//  Send the data to RDS Spy.
//
void send_data()
{
  Serial.println("G:");
  printHex(block_1);
  printHex(block_2);
  printHex(block_3);
  printHex(block_4);
  Serial.println();
  Serial.println();

//
//  Four digit hexadecimal print routine.
//
void printHex(word value)
{
  Serial.print(value >> 12 & 0x0F, HEX);
  Serial.print(value >>  8 & 0x0F, HEX);
  Serial.print(value >>  4 & 0x0F, HEX);
  Serial.print(value >>  0 & 0x0F, HEX);
}
//
//  This routine resets RDS Spy.
//
void clear()
{
  Serial.println("G:");
  Serial.println("RESET");
  Serial.println();
  rxState = IDLE;
  group_complete = 0;

//
//  Setup the microprocessor.
//
void initialize()
{
  cli();   //  Disable global interrupts while we make our changes.
     
  EICRA |= (1 << ISC11 | 1 << ISC10 | 1 << ISC00);  //  Setup Interrupt 0 & 1.
  EIMSK |= (1 << INT1 | 1 << INT0);  //  Enable Interrupt 0 & 1;
 
  ADCSRA &= ~(1 << ADEN);    //  Disable the analog comparator.
  TIMSK0 &= ~(1 << TOIE0);   //  Disable Timer 0.
  TIMSK1 &= ~(1 << TOIE1);   //  Disable Timer 1 - Turns Off PWM.
  TIMSK2 &= ~(1 << TOIE2);   //  Disable Timer 2.
 
  rxBuffer = (uint8_t*) calloc(BUFFER_LENGTH, sizeof(uint8_t));  //  Initialize the receive buffer.
 
  sei();    //  Enable global interrupts.
}
//
//  Interrupt 0 Service Routine - Triggered on the Rising and Falling edge of Serial Data.
//
ISR(INT0_vect, ISR_NOBLOCK)
{
  byte mask = (PIND & 0x0C);
 
  switch (rxState)
    {
      case IDLE:
        if (mask != 0x08)
          return;
        rxBufferIndex = 0;
        rxState = START;
        break;
               
      case START:
        rxBits = 0;
        rxCount = 0;
        rxState = RCVG;
        break;
       
      case RCVG:
        if (mask == 0x08)
          {
            rxState = START;
            return;
          } 
     
      case STOP:   
        if (mask != 0x0C)
          return;
        rxComplete = 1;
        rxState = IDLE;
        break;
       
      default:
        rxState = IDLE;
        break;   
    }
}
//
//  Interrupt 1 Service Routine - Triggered on the Rising edge of Serial Clock.
//
ISR(INT1_vect, ISR_NOBLOCK)
{
  DELAY;
  switch (rxCount)
    {
      case 0:
      case 1:
      case 2:
      case 3:
      case 4:
      case 5:
      case 6:
      case 7:
        if (PIND & _BV(2))
          bitSet(rxBits, (7 - rxCount));
        rxCount++;
        break;
      case 8:
        rxBuffer[rxBufferIndex] = rxBits;
        rxBufferIndex++;
        rxBits = 0;
        rxCount = 0;
        break;
      default:
        rxState = IDLE;
        break; 
    }
}
//
//  That's all.
//

AWOL

"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

joey2

No, millis() are stopped at 1623. Micros() varies in a very small interval ~1624000 - 1624999.

At startup runs initialize(), where built-in timer turned off.
Code: [Select]
void initialize()
{
  cli();   //  Disable global interrupts while we make our changes.
     
  EICRA |= (1 << ISC11 | 1 << ISC10 | 1 << ISC00);  //  Setup Interrupt 0 & 1.
  EIMSK |= (1 << INT1 | 1 << INT0);  //  Enable Interrupt 0 & 1;
 
  ADCSRA &= ~(1 << ADEN);    //  Disable the analog comparator.
  TIMSK0 &= ~(1 << TOIE0);   //  Disable Timer 0.
  TIMSK1 &= ~(1 << TOIE1);   //  Disable Timer 1 - Turns Off PWM.
  TIMSK2 &= ~(1 << TOIE2);   //  Disable Timer 2.
 
  rxBuffer = (uint8_t*) calloc(BUFFER_LENGTH, sizeof(uint8_t));  //  Initialize the receive buffer.
 
  sei();    //  Enable global interrupts.
}

Nick Gammon

You could turn on Timer 1, which is a 16-bit timer. If it doesn't generate interrupts I don't see why it should interfere with your code in any way. Choose a prescaler which gives the resolution you want. Then just query the current count in the hardware timer to find the elapsed time, when required. The larger the prescaler the longer the timer will go without wrapping but the lower the resolution of each "tick".

More about timers:

http://www.gammon.com.au/forum/?id=11504
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

joey2


You could turn on Timer 1, which is a 16-bit timer. If it doesn't generate interrupts I don't see why it should interfere with your code in any way. Choose a prescaler which gives the resolution you want. Then just query the current count in the hardware timer to find the elapsed time, when required. The larger the prescaler the longer the timer will go without wrapping but the lower the resolution of each "tick".

More about timers:

http://www.gammon.com.au/forum/?id=11504


Sorry, dont understand exactly, what i need to use instead millis(), TCNT1?

Nick Gammon

Exactly, micros() uses TCNT0, so if you were using timer 1 you would use TCNT1.

You would need to allow for overflows though. Probably the simplest thing is just to add 65536 if the time interval happens to be negative.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

joey2

I dont know why, but TCNT0, TCNT1 and TCNT2 varies only from 0 to 255.

Code: [Select]
unsigned long time;
void setup()
{
Serial.begin(19200);
}
void loop()
{
  time = TCNT1;
Serial.println();
Serial.print(time);
}

Nick Gammon

Because init sets them up to do that:

Code: [Select]

// put timer 1 in 8-bit phase correct pwm mode
sbi(TCCR1A, WGM10);


Being in 8-bit PWM mode it counts up to 255.
Please post technical questions on the forum, not by personal message. Thanks!

More info:
http://www.gammon.com.au/electronics

joey2

Nick Gammon, thanks for helping! Now i can use timer1 instead millis(). :)

Go Up