Raw IR Code Transmission - ATTiny13a Inaccurate Oscillator?

9.6 MHz / 8 MHz = 1.2. Seems suspiciously similar. However, if that were the problem, the code would have to be compiled to run at 9.6 MHz but be running at 8 MHz. Does not seem very likely.

Add this to the top of setup in the blink sketch…

void setup( void )
{

  while ( OSCCAL < 0x7F )
  {
    OSCCAL = OSCCAL + 1;
  }

}

Should speed up the processor to about 17 MHz. The “ten second” blink should take about seven seconds.

Just to confirm, this is the exact board entry you are using...

###############################################################
attiny13.name=ATtiny13 @ 9.6MHz (interne 9.6 MHz clock)
attiny13.upload.using=arduino:arduinoisp
attiny13.bootloader.low_fuses=0x7a
attiny13.bootloader.high_fuses=0xff
attiny13.upload.maximum_size=1024
attiny13.build.mcu=attiny13
attiny13.build.f_cpu=9600000
attiny13.build.core=core13
###############################################################

f_cpu, low_fuses, and high_fuses are especially important.

And well done on the video. That illustrates the problem very nicely.

Yes, it's the exact one I'm using. And you're welcome. It's very evident that speed is the issue, and without an oscilloscope, it's the best guess I've got. Not much of a guess anymore lol.

EDIT: About the code I added to the top of the setup: yes, it did shorten the blink to about 7.8 seconds. What does that mean, though? Can you use that to modify the clock speed?

tincanman8: EDIT: About the code I added to the top of the setup: yes, it did shorten the blink to about 7.8 seconds. What does that mean, though?

Well, it means several things...

• The processor appears to be functioning correctly.

• The initial speed is outside of the guaranteed ±10%. (Where did you buy the processor?)

• At this point, the only identifiable problem is the clock speed.

• You can very likely get your project working with some tuning.

Can you use that to modify the clock speed?

Absolutely. Adjusting OSCCAL is how the internal oscillator is tuned.

You can fiddle with OSCCAL until the timing is close. Increase the value to speed the processor. Decrease the value to slow the processor. When adjusting the value, use a while-loop like in Reply #20.

Or, you can use Poor Man's Tiny Tuner. Version 1 uses 'x' at 9600 baud for tuning. It gives good results but it was not meant to work with the t13 processor. You can find it with this... https://www.google.com/search?q=poor+man%27s+tiny+tuner

Version 2 uses a timing signal from the programmer (your 2560 board). It gives better results and was meant to work with the t13 processor. It looks like I did not make a separate thread for it... https://www.google.com/search?q=tiny+tuner+2+site:forum.arduino.cc

Erni graciously provides some documentation here... http://ernstc.dk/arduino/tinytuner.html

I'm off to bed...

Awesome. Thanks for all your help, and I will let you know if I get anything. ;)

Alright, after spending almost a day trying to figure out how to work TinyTuner2 with no luck (no XTAL2 pin on the ATTiny13a, TinyISP doesn't compile, etc.), I decided to change the hex value for OSCCAL manually and just measure each 10 second blink and stop at the one which was the closest. I got OSCCAL = 0x70 to be the optimum value (very close to 10 seconds; +-0.05 seconds). I tried uploading your code from reply #9 with the clock tuned and... IT WORKED ON THE ATTINY13A! Thank you so much for your help.

Now, how do I make this work for my original code? The one with all the values in an array? I just don't know why it worked with my Arduino Mega 2560 and not the ATTiny13a... I know I can just use Excel to write the codes, but what if one of my projects includes reading an IR signal from an external source and spitting it out again (without using the IR library)?

Sorry for extending the issue, but I would really appreciate it if I can get this second part figured out.

Thanks :D

tincanman8: TinyISP doesn't compile

For a Mega 2560 board, correct?

I tried uploading your code from reply #9 with the clock tuned and... IT WORKED ON THE ATTINY13A! Thank you so much for your help.

Excellent. You are welcome.

Now, how do I make this work for my original code?

Have you tried the original code? If delayMicroseconds works correctly on t13(a) processors your original version should work.

Yes, for the Arduino Mega 2560. It does compile before I add the code below to _TinyISP_BuildOptions.h.

//Set to 1 if using KnockBang and comment out #include <SoftwareSerial.h> in TinyISP
#define RELAY_KNOCK_BANG_ENABLED 1

//Set to 1 if using Serial Relay, and uncomment #include <SoftwareSerial.h> in TinyISP
//Transmit on Arduino is A0
#define RELAY_SERIAL_ENABLED  0

//Set to 1 if using TinyTuner2
//Tuning signal pin 3 on UNO, connect to xtal2 on target
#define TUNING_SIGNAL_ENABLED  1

This gives then gives me the following error:

Arduino: 1.0.6 (Windows NT (unknown)), Board: "Arduino Mega 2560 or Mega ADK"
TinyISP_Programmer.cpp.o: In function `reset_target':
C:\Users\Amar\AppData\Local\Temp\build3083592119038487509.tmp/TinyISP_Programmer.cpp:329: undefined reference to `stop_tuning_signal()'
TinyISP_Programmer.cpp.o: In function `release_target':
C:\Users\Amar\AppData\Local\Temp\build3083592119038487509.tmp/TinyISP_Programmer.cpp:353: undefined reference to `start_tuning_signal()'
TinyISP_Programmer.cpp.o: In function `programmer_reset_target()':
C:\Users\Amar\AppData\Local\Temp\build3083592119038487509.tmp/TinyISP_Programmer.cpp:883: undefined reference to `start_tuning_signal()'

About my original code, I have tried it. It doesn’t work. I’ve tried varying OSCCAL from 0x50 to 0x75 (in hexadecimal; I know :stuck_out_tongue: ), and nothing worked properly. I set the delay between calls to the function “SendTVOnCode_P” to 5 seconds, and I varied OSCCAL to get it as close to 5 seconds as possible. Even at the optimal value, my TV is still on. I don’t know why. Should I make another comparison video?

This gives then gives me the following error:

Thanks. When I have time I will try to fix that.

If the "new" code works and the original does not the only explanation is delayMicroseconds. It must not work correctly at 9.6 MHz.

I can think of a few choices that may help you...

• Use the "new" code. There are some things that can be done to make it more palatable.

• Get the core author to fix delayMicroseconds at 9.6 MHz.

• Tune the processor to run at 8 MHz and use delayMicroseconds from a core that supports 8 MHz.

• Develop your own delayMicroseconds.

• Swap your t13a processor for a t85 processor.

I have read that delayMicroseconds() doesn't work properly on ATTiny microcontrollers sometimes. I've been considering getting an ATTiny85 for a while now, but I was sure that it would eventually work on the ATTiny13a, and I don't want to just throw them all out lol. I'm a tiny bit stubborn, too.

If I want to accurately tune my processor (not just using what looks close), how can I get TinyTuner(2) to work? Like how do I even get anything to output to the Serial Monitor, even if nothing's connected to XTAL2 (non-existent on ATTiny13)? I looked around online, and the explanations aren't very thorough for anything other than an ATTiny84 or ATTiny85.

Once again, thank you so much for your help, and I will fiddle around with the clock speed and slow-mo video (no access to an oscilloscope until late next week) until I get something right. If I have any more related problems in the next few days, I will post them here. Otherwise, in a new thread.

Toodles!

I did a little experiment on an ATTiny13 running at 9.6 MHz.

Using the sketch below and a logic analyzer I could see pulses at 21 microseconds, which tells me that a digitalwrite takes aprox. 11 microseconds.

Another thing is that it is not consistent, because every 3. or 4. of the pulses are only 14 microseconds.
I don’t know why this is happening, but it is not good for your IR-code.

how can I get TinyTuner(2) to work?

You just connect pin 3 on arduino to pin PB4 on the t13 (physical pin 3)

#include <util/delay.h>
int IRledPin=3;

void setup() {
  OSCCAL = 0x65;
  pinMode(IRledPin, OUTPUT);
}

void loop() {
  digitalWrite(IRledPin, HIGH);  
   _delay_us(10);         
   digitalWrite(IRledPin, LOW); 
   _delay_us(9);         
}

My experience with ATTINY13a is:

1) The oscillator is very inaccurate: often up to +/- 35% (more often -35% than +35%) 2) It is possible to "tune" the oscillator by adjusting the OSCCAL but this must be done with every individual ATTINY13 chip.

I got good results by "tuning", so the oscillator frequency is within about 1 or 2%. But it depends on temperature and voltage (stable Vcc is important).

It is not a good idea to use digitalWrite, when time accuracy matters, because it has a lot of overhead. Use direct port manipulation instead (like PORTB = 0b01000; instead of digitalWrite(3, HIGH) )

I have the experience that the internal oscillator of ATTINY25/45/85 is a little bit "better", but if accuracy is a must, you have to go with an EXTERNAL quartz or resonator.

Alright, so I’ve been doing a little bit of fiddling around trying to make my original code work. I used a 38kHz IR transistor to read the pulses from my LED controlled by the ATTiny13a running my original code. I was getting just one pulse. What this indicated was that there WAS a 38kHz pulse going through (the IRpulse() function was working), but the off-time delays weren’t working (at all). The only difference in the delayMicroseconds() calls from the IRpulse() function and the SendTVOnCode_P() function was the argument. The latter had delays in the hundreds, thousands, and tens of thousands of microseconds, whereas IRpulse() only had a delay of 10 microseconds.

I changed the delayMicroseconds() call in SendTVONCode_P() to 100 calls of the same function with 1/100 of the argument each time. In addition, I used the IR transistor to tweak the delays by a constant. I also found a pattern in the pulse/delay combination and I removed half of the array. I also calibrated the oscillator. Instead of using digitalWrite(), I used direct port manipulation for the pulseIR() function. I changed the delayMicroseconds() call in the pulseIR() function from 10 to 12 due to faster execution. This worked for me, and my TV is finally responding to the signal from my ATTiny13a. WOOT :smiley: . I even tried different buttons (VOLUP, VOLDOWN, etc.) without adjusting the constant in the delay, and that worked as well.

The problem is that I’m using 1024 out of 1024 bytes available flash memory LOL. I definitely tried putting the 100 calls into a for-loop, which shaves off ~400 bytes, but it doesn’t work (the loops don’t run). I also tried a while-loop, same problem. Does anybody know why the for-loops aren’t executing? They are supposed to execute INSIDE the big for-loop, so is it nested for-loops that the ATTiny13a doesn’t like?

This is my code:

#include "avr/pgmspace.h"
#include <avr/delay.h>

int IRledPin = 4;    // LED connected to digital pin 4

int tvONsignal[] PROGMEM = {
// ON, OFF (in 10's of microseconds)
316, 272,
72, 120,
72, 120,
72, 120,
72, 120,
72, 120,
72, 120,
72, 220,
72, 120,
72, 120,
72, 120,
72, 120,
72, 120,
72, 220,
72, 120,
72, 220,
60, 232,
60, 380,
60, 2236,
};

int ONcodeLength = sizeof(tvONsignal)/(2*sizeof(int));

// The setup() method runs once, when the sketch starts
 
void setup()   {
  OSCCAL = 0x70;
  DDRB = 0b010000; // PB4 on ATTiny13a
}
 
void loop()                     
{
  SendTVONCode_P();
  delay(2*1000);  // wait two seconds (2 seconds * 1000 milliseconds)
}
 
// This procedure sends a 38KHz pulse to the IRledPin 
// for a certain # of microseconds. We'll use this whenever we need to send codes
void pulseIR(int microsecs) {
  // we'll count down from the number of microseconds we are told to wait
  
  while (microsecs > 0) {
    // 38 kHz is about 13 microseconds high and 13 microseconds low
   PORTB = 1 << PB4; //takes 1 microsecond (?)
   delayMicroseconds(12);         // hang out for 12 microseconds
   PORTB = 0 << PB4; //takes 1 microsecond (?)
   delayMicroseconds(12);         // hang out for 12 microseconds
 
   // so 26 microseconds altogether
   microsecs -= 26;
  }
}
 void SendTVONCode_P()
 {
     for (int i = 0; i < ONcodeLength; i++)
     {
      pulseIR(10*pgm_read_word(&tvONsignal[2*i])); // ON
      
      int quiet = pgm_read_word(&tvONsignal[2*i + 1])/10 + 10; // OFF
      delayMicroseconds(quiet);      
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
      delayMicroseconds(quiet);
     }
 }

Are you using the same variable (i) for the second loop? If so use another (j).

I loaded your sketch up to a UNO and it does generate a signal. However, the carrier is more than 40kHz.

Try the following as it gets you 38.7kHz (measured on an oscilloscope)

void pulseIR(int microsecs) {
  // we'll count down from the number of microseconds we are told to wait
  
  while (microsecs > 0) {
    // 38 kHz is about 13 microseconds high and 13 microseconds low
   PORTB |= 0x10; //
   delayMicroseconds(12);         // hang out for 12 microseconds
   PORTB &= 0xEF; //
   delayMicroseconds(14);         // hang out for 14 microseconds

   // so 26 microseconds altogether
   microsecs -= 26;
  }
}

...at least with the 16MHz crystal on the UNO.

The PORTB instructions will only take one clock cycle (or at worst a few)...nowhere near 1uS unless you are running at 1MHz

The 2236 at the end of your signal can be replaced with any lower value, which would allow you to arrange the multiply/div factors you use more efficiently. It should be possible to use bytes instead of INTs. You have INTs in the array & delayMicroseconds can be as large as a little over 16000 uSecs.

Your logic for timing of spaces seems a bit off...for an entry of 120 (1200uSecs) you are getting 2200...because based on your code 120/10+10 = 22 22*100 = 2200 (not 1200) .....(maybe this is your real issue!!!)

If you take my suggestion above about the last space you can just issue one delayMicroseconds for each mark or space...no need to worry about for loops etc.

Other than the above it works OK ...amazingly.

I also tried a for loop with any issues on the UNO.

AnalysIR, thanks for your reply. The for-loop variable was j, not i. I made sure the issue wasn't as trivial as this :D .

I know I could change the 2236, but I tried to keep the pulse/delay numbers as close as possible to the original numbers I got from my IR transistor. This is so that I can use the IR blaster "dynamically", meaning I could read a code from a remote and spit it out right then without human analysis, all in one sketch.

I wasn't sure what the overhead was for port manipulation instructions, so I guessed 1 microsecond. Thanks for the info.

You have INTs in the array & delayMicroseconds can be as large as a little over 16000 uSecs.

I don't understand what you mean. Is it that delayMicroseconds() takes arguments up to a little more than 16000 microseconds, and no more?

As for my timing logic, I had to tweak the delays in order to make them work with what was being put out from the IR LED before. It ended up working with all of the numbers in my array before, so I used it :D

I will try to make the changes you mentioned. Once again, this code is very sketchy/ghetto, and I would be doing all this technical analysis with a scope myself, but I won't have access to an oscilloscope until next week. Then I could take an in-depth look at exactly what's going on. Thanks for doing it for me :D . Until then, I just have to work with what I have.

I don't understand what you mean. Is it that delayMicroseconds() takes arguments up to a little more than 16000 microseconds, and no more?

Correct. actually 16383uSecs is the max & 0 is not valid.

I know I could change the 2236, but I tried to keep the pulse/delay numbers as close as possible to the original numbers I got from my IR transistor. This is so that I can use the IR blaster "dynamically", meaning I could read a code from a remote and spit it out right then without human analysis, all in one sketch.

Yes, but because its the last space it doesn't matter what it is.(but should not be 0)

If you need more flash or SRAM, you can save some by : - Define your array as bytes instead of INTs (the only one that requires INT is the last 2236. - if you use INTs you can just use one delayMicroseconds directly instead of loops or *10. The 22360 value for the last space wont be a problem with delayMicrosecond, it will be just inaccurate which in this case is not an issue. - if you are doing it all dynamically then you wont be storing the signals in PROGMEM! - You will also notice that in each signal there are only a fixed set of durations, which provides scope for further refinement. Have a look at the internals of the IRremote library to see how a 'binary/HEX' value is converted into a signal.

this code is very sketchy/ghetto

You just won a prize for understatement :) ...but still will work fine once the minor issues are sorted.