Trouble using interrupts with timers together on Attiny85

I have created a small program which receives an IR command and sends back an IR command based on the status of two input pins. The program needed to be small enough to fit into a Attiny85 with room to spare. Hence the reason for using the code from the following sources;

The IR transmitter section is taken from David Johnson’s “IR Remote Wand v2” (see Technoblogy - IR Remote Wand.

And the receiver section was taken from his other project “IR Remote Control Detective v4” (see Technoblogy - IR Remote Control Detective [Updated]).

Both sections use Timer/Counter1 to execute their functions.

The problem that I am experiencing is once either function is activated (or triggered), the other function will not execute. The Attint85 basically halts and a reset is required.

The sequence of events is as follows;

  1. Wait for a specific IR code to be received.
  2. If Code is received, read the status of the two input pins and send back a corresponding IR code.
    Loop
    I have tried many things such as disabling interrupts right after the IR code is received, then re-enabling interrupts after sending the IR code, but that did not seem to work.
    I even went as far as using an I/O pin to externally trigger a reset condition, which works, but is not a fix I willing to live with.

So, I am hoping that someone can explain to me why these two functions do not want to play well together.

The settings are; Clock = 1MHz, BOD disabled.

My code is below.
Any help with this is greatly appreciated.

// IR transmitter **********************************************


const int LED = 1;           // IR LED output

int rx_flag = 0;
int addr = 0;
int cmnd = 0;


const int top = 23;                       // 1000000/26 = 38.5kHz
const int match = 18;                     // approx. 25% mark/space ratio

// Set up Timer/Counter1 to output PCM on OC1A (PB1)
void SetupPCM () {
  TCCR1 = 1<<PWM1A | 3<<COM1A0 | 1<<CS10; // Inverted PWM output on OC1A divide by 1
  OCR1C = top;                            // 38.5kHz
  OCR1A = top;                            // Keep output low  
}

// Generate count cycles of carrier followed by gap cycles of gap
void Pulse (int count, int gap) {
  OCR1A = match;                          // Generate pulses  
  for (int i=0; i<2; i++) {
    for (int c=0; c<count; c++) {
      while ((TIFR & 1<<TOV1) == 0);
      TIFR = 1<<TOV1;
    }
  count = gap;                            // Generate gap
  OCR1A = top;
  }
}

void Send (char IRtype, unsigned int Address, unsigned int Command) {
  
  // NEC or Samsung codes
  if ((IRtype == 'N') || (IRtype == 'M')) {
    unsigned long code = ((unsigned long) Command<<16 | Address);
    TCNT1 = 0;                            // Start counting from 0
    // Send Start pulse
    if (IRtype == 'N') Pulse(342, 171); else Pulse(190, 190);
    // Send 32 bits
    for (int Bit=0; Bit<32; Bit++)
      if (code & ((unsigned long) 1<<Bit)) Pulse(21, 64); else Pulse(21, 21);
    Pulse(21, 0);
  }
                     

}


// NEC IR Receiver **********************************************

#define IR_OUT          PB3               // IR receiver pin

// Globals
volatile unsigned long RecdData;
volatile int Bit, Edge;
volatile char IRtype;
  
// Protocol timings
const int Micros = 64;     // Number of microseconds per TCNT1 count

const int SonyIntro = 2400/Micros;
const int SonyMean = 900/Micros;

const int NECIntro = 9000/Micros;
const int NECGap = 4500/Micros;
const int NECPulse = 562/Micros;
const int NECMean = 1125/Micros;

const int SamsungIntro = 5000/Micros;
const int SamsungGap = 5000/Micros;

const int RC5Half = 889/Micros;    // 0.889ms
const int RC5Full = 1778/Micros;
const int RC5Mean = 1334/Micros;

// IR types
const uint8_t NECtype = 1;
const uint8_t SAMtype = 2;
const uint8_t RC5type = 3;
const uint8_t SONtype = 4;


void ReceivedCode(char IRtype, unsigned int Address, unsigned int Command) {

    addr = Address;
    cmnd = Command;
    rx_flag = 1;

}

// Calculate difference
uint16_t diff(uint16_t a, uint16_t b) {
  if (a > b) return (a - b);
  return (b - a);
}

// Interrupt service routine - called on Timer/Counter1 overflow
ISR(TIMER1_OVF_vect) {
  ReceivedCode(Bit, RecdData>>7, RecdData & 0x7F);
  TIMSK = TIMSK & ~(1<<TOIE1);                  // Disable overflow interrupt
  TCNT1 = 250;                 // clear counter
}

// Interrupt service routine - called on each edge of IR pin
ISR(PCINT0_vect) {
  static uint8_t Mid;                            // edge: 0 = falling, 1 = rising
  uint16_t Time = TCNT1;
  if(TIFR & 1<<TOV1) { IRtype = 0; Edge = 1; }  // overflow
  else if(Edge != (PINB>>PINB3 & 1));           // ignore if wrong edge
  else if(IRtype == 0) {
  
    // End of intro pulse
    if(diff(Time, RC5Half) < 5) {
      IRtype = RC5type; RecdData = 0x2000; Bit = 12; Edge = 0; Mid = 0;
    } else if(diff(Time, RC5Full) < 5) {
      IRtype = RC5type; RecdData = 0x2000; Bit = 11; Edge = 0; Mid = 1;
    } else if((diff(Time, SonyIntro) < 5) && (Edge == 1)) {
      IRtype = SONtype; RecdData = 0; Bit = 0;
      TIMSK = TIMSK | 1<<TOIE1;                            // enable overflow interrupt
    } else if(diff(Time, SamsungIntro) < 18) {
      IRtype = SAMtype; RecdData = 0; Bit = -1; Edge = 0;  // ignore first falling edge
    } else if(diff(Time, NECIntro) < 18) { 
      IRtype = NECtype; RecdData = 0; Bit = -1; Edge = 0;  // ignore first falling edge
    }
  
  // Data bit
  } else if(IRtype == RC5type) {
    Edge = !Edge;
    if((Time < RC5Mean) && Mid) {
      Mid = 0;
    } else {
      Mid = 1;
      RecdData = RecdData | ((uint32_t) Edge<<Bit);
      if(Bit == 0) ReceivedCode(RC5type, RecdData>>6 & 0x1F, (~(RecdData>>6) & 0x40) | (RecdData & 0x3F));
      Bit--;
    }
  
  
  } else if((IRtype == NECtype) || (IRtype == SAMtype)) {
    if(Time > NECMean && Bit >= 0) RecdData = RecdData | ((uint32_t) 1<<Bit);
    Bit++;
    if(Bit == 32) ReceivedCode(IRtype, RecdData & 0xFFFF, RecdData>>16);
  }
  
  TCNT1 = 0;                  // clear counter
  TIFR = TIFR | 1<<TOV1;      // clear overflow


}



// Setup demo **********************************************


void setup() {
  pinMode(PB4, OUTPUT);
  digitalWrite(PB4,LOW);
  pinMode(LED, OUTPUT);
  pinMode(PB5, INPUT_PULLUP);

  pinMode(PB2, INPUT_PULLUP); 
  pinMode(PB0, INPUT_PULLUP);
  
  
  
  TCCR1 = 0x07;     // 7<<CS10; // no compare matches ; /64
  PCMSK = 1<<IR_OUT;            // interrupt on IR pin (PB3)
  GIMSK = 1<<PCIE;              // enable pin change interrupts
  sei();                        // enable global interrupts

 
}

void loop() {


 if (rx_flag == 1){

        if ((addr == 0x0909) && (cmnd == 0x0D55)){    

            
           SetupPCM();
             
             delay(500);
                        
            if((digitalRead(PB0)==1) && (digitalRead(PB2)==1)){
              Send('M', 0x2525, 0x0B55);
             
            } 
            else if (digitalRead(PB0)==1){
              Send('M', 0x2525, 0x0C55);              
            }
            else if (digitalRead(PB2)==1){
              Send('M', 0x2525, 0x0C66);
                            
            }
            else
            {
              Send('M', 0x2525, 0x0B66);              
            }
            
            rx_flag = 0;
        }
 }

}

The referred links have described a project of building an IR-based Remote Controller using ATtiny85 and an ATtiny85 based Receiver that decodes the arrived IR command and shows it on OLED display.

What are you trying to build based on the codes of the referred links? Please, clarify in a simple way.

Thank you for reviewing my issue.
Well, my code executes fine on the first pass (after correctly receiving, decoding and retransmitting). The issue is that I can't get it to loop back to the power-on state.
I could re-write the entire code, but this will take some time and further testing. Since this code does work, I just need it to work repeatedly.
My external reset patch works but is not feasible as I need another I/O pin for other functions.
Is there a way to soft-reset the code ? perhaps an in-line assembly code that does a relative jump to a specific starting point?

Hi GolamMostafa, my project has two parts, this part receives an IR code from another unit (the other part of my project) and responds to this other unit by transmitting a response IR code.

Both units need to operate in a 'wait-and-respond' mode, so the IR receiver portion of the code needs to execute and not execute again until the response IR code is transmitted.

The only way I have been able to do this is by implementing an external reset using a I/O pin.

So, I have the same issue on the transmitter initiation the IR code. Once I figure this out on this code, I need to adjust the transmitter's code in a similar way.

Ok, so wouldn't adding the receiver setup code after the transmitter has completed it's task, be a possible solution?

 TCCR1 = 0x07;     // 7<<CS10; // no compare matches ; /64
  PCMSK = 1<<IR_OUT;            // interrupt on IR pin (PB3)
  GIMSK = 1<<PCIE;              // enable pin change interrupts
  sei();                        // enable global interrupts

I have tried this, but it did not work as I expected.
I admit that I have not studied how this particular timer/count works and I should probably take the time to understand it. The datasheet is not exactly easy to follow with respect to these timer functions.
But thank you for taking the time to help me. Perhaps I will try to re-write this code once I understand the timer function better.

I would suggest you develop your project using Arduino UNO as it provides debugging environemnet and then map the codes for ATtiny85. This is how I deveop ATtiny85 based systems.

Which one of the following protocols you are receiving and sending?

NEC and Samsung protocols
Sony SIRC protocol
RC-5 protocol

Yes, I think I now need to go that route in order to better understand these functions and then create my implementation.

Thank you GolamMostafa and Delta_G for your comments/suggestions.

1 Like

The protocols I am using are Samsung, NEC and RC-5.

Which protocol you are receiveing? Give the name of one protocol.

Which protocol you are transmitting? Give the name of one protocol.

Well, last time I checked, the IRremote code reference is too large to fit into a Attiny85.

:laughing: :laughing: ... Well, the 'Fun' is all in the challenge !
Also, I have other projects that I have built using these little guys and I just think they are quite nice to work with.
I used to use 12C509s before I moved to 85s.

You take an ordinary RC-5 TV Remote Controller. Equip the Arduino UNO with an IR Receiver and decode the incoming IR Commands of RC-5 and show on I2CLCD. Now, move the sketch and Hardware to ATtiny85. Rememer that the ATtiny 85 has only 8 kbytes flash and if is using Digispark Dev Board, then you have only about 6 kbytes of flash.

Similalrly, you can develop codes for IR Transmitter using Arduino UNO and then migrate those codes onto ATtiny85.

Only now, you can say that the codes would not be fitting in ATtiny85.

I have already built an IR decoder which I currently use to verify the function of this project. This project does not require a display, simply just needs to receive, decode and respond.

I appreciate all the comments and recommendations. I am going to go learn more about the Attiny's Timers and Interrupts and then write my own version of the code.

I was hoping to just use what some else has already created, however, sometimes it is less time consuming to just do it yourself.

Once I figure it out (and I will), I will post my solution in the event that someone else needs a tiny IR transmitter/receiver for their project.

Thanks again.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.