IRremote library on custom PCB with Atmega328 8 MHz

I’ve got custom PCB with Atmega328 without oscilator (fuses are set for internal 8 MHz). I’ve got code which works on Arduino, but it looks like i’ve got a problem with IR communication on my custom device. Is it a problem with program? My device doesnt respond to any codes from remote, everything else works fine.

My code:

#include <IRremote.h>
int input_pin = 2; 
IRrecv irrecv(input_pin); //IR pin
decode_results results;
unsigned long currentButtonCode;
unsigned long lastButtonCode;
unsigned long LastChrono;
boolean MotorON = false;
void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); 
  pinMode(6, OUTPUT); //PWM from motor
  pinMode(7, OUTPUT); //motor forward
  pinMode(8, OUTPUT); //motor backward
}
void GoUp() {
  LastChrono = millis();
  MotorON = true;
  analogWrite(6, 255); //pwm
  digitalWrite(7, LOW); 
  digitalWrite(8, HIGH);
}
void GoDown() {
  LastChrono = millis();
  MotorON = true;
  analogWrite(6, 255); 
  digitalWrite(7, HIGH); 
  digitalWrite(8, LOW);
}
void Stop() { 
  analogWrite(6, 0); 
  digitalWrite(7, LOW);
  digitalWrite(8, LOW);
  MotorON = false;
}
void loop() {
  if (irrecv.decode(&results)) {
    currentButtonCode = results.value; //ignoring reapeat 0xFFFFFF
    if (currentButtonCode == 0xFFFFFFFF) { //ignoring reapeat 0xFFFFFF
      currentButtonCode = lastButtonCode; //ignoring reapeat 0xFFFFFF
    } else {
      lastButtonCode = currentButtonCode; ///ignoring reapeat 0xFFFFFF
    }
    Serial.print(F("result = 0x")); //received code
    Serial.println(currentButtonCode, HEX); // conversion to HEX
 
    switch (currentButtonCode) {
      case 0xFF18E7: //GO UP
        GoUp();
        break;
      case 0xFF4AB5: //GO DOWN
        GoDown();
        break;
      case 0xFF38C7:
        Stop();
        break;
    }
       irrecv.resume();
  }
    if (MotorON == true  && millis() - LastChrono >= 100) {
    Stop();
  }
}

The obvious answer is, the library likely depends on a certain CPU clock frequency. So look into the library for clues about that, ideas about fixing it. There might be less obvious problems. Difficult to say without more investigation. Is millis() accurate?

I cant check millis on PCB, when IRremote doesnt work at all :slight_smile: It was working fine on Arduino. I am gonna check library files.

Why would different CPU frequency destroy my whole program? I understand that something may work different (slower, glitched etc), but in my case device doesnt react on IR codes at all.

Oh man @aarg, thats crazy, but my program works perfectly fine after changing 1 value in library files (from 16k to 8k of course). Thank you very much!

Good news.

hardtofindanynick:
Why would different CPU frequency destroy my whole program? I understand that something may work different (slower, glitched etc), but in my case device doesnt react on IR codes at all.

The IR pulses consist of specific intervals of a carrier frequency (typically around 38 kHz). When you are using the 8 MHz internal RC oscillator instead of an external 16 MHz crystal or resonator, your processor is running at half speed. That means that the timer used to generate the 38 kHz carrier will be generating a 19 kHz carrier. If you send a 19 kHz carrier, no normal IR receiver will be able to sense the signal. The timer will also be sampling the input every 100 microseconds instead of the usual 50 microseconds. All of the pulses will appear to be half the length of what is expected so input won't be recognized.
The IRremote library should have used the 'F_CPU' value set as part of the compiling process. That would adjust the timing for the target board.

johnwasser:
The IRremote library should have used the 'F_CPU' value set as part of the compiling process. That would adjust the timing for the target board.

It might also be that the OP didn't update F_CPU.

johnwasser:
The IR pulses consist of specific intervals of a carrier frequency (typically around 38 kHz). When you are using the 8 MHz internal RC oscillator instead of an external 16 MHz crystal or resonator, your processor is running at half speed. That means that the timer used to generate the 38 kHz carrier will be generating a 19 kHz carrier. If you send a 19 kHz carrier, no normal IR receiver will be able to sense the signal. The timer will also be sampling the input every 100 microseconds instead of the usual 50 microseconds. All of the pulses will appear to be half the length of what is expected so input won't be recognized.
The IRremote library should have used the 'F_CPU' value set as part of the compiling process. That would adjust the timing for the target board.

It makes sense, thanks for explaining.

It might also be that the OP didn't update F_CPU.

I added "F_CPU 8000000; on the top of my program, i hope its enough to make it work properly.

There are also other mechanisms you can use also.

This is what I tend to do when I have an arduino with a 16MHz crystal, but need to run it at 4 or 8 MHz for lower voltage operation, and that is is to (a) ensure that the desired effective clock frequency is selected by choosing an appropriate board in the IDE (or creating your own entry in boards.txt) and (b) using the prescaler to reduce the effective clock frequency down from 16MHz to either 8 or 4 MHz.
It has the advantage that you benefit from the accuracy of a crystal and can still use a bootloader compiled for 16MHz, but keep a low clock rate for battery operation. Timing functions (for correctly written applications) should still work.

This code then goes in setup():

 // prescaler from http://forum.arduino.cc/index.php?topic=271364.0
 // combined with "board" and selected clock frequency.
 // locate early in script

 // note: this block is to allow a 16MHz crystal to be used with different effective clock speeds.
 // If you are using another crystal value adapt or delete this block.
 if (F_CPU == 8000000L) clock_prescale_set(clock_div_2);
 else if (F_CPU == 4000000L) clock_prescale_set(clock_div_4);
 else if (F_CPU == 2000000L) clock_prescale_set(clock_div_8);
 else if (F_CPU == 1000000L) clock_prescale_set(clock_div_16);

hardtofindanynick:
It makes sense, thanks for explaining.
I added "F_CPU 8000000; on the top of my program, i hope its enough to make it work properly.

It will if it is defined with an #ifdef/#ifndef elsewhere