Attiny 45 38khz flash

Maybe the "tone()" function will work, though I don't know its limitations...

Hi!

Thanks for all your suggestions, I will try this.

Happy new year!

Hi!

Thanks for the help!
I used the Timer that dc 42 suggested an it worked fine.
I added 1 ms delay in the loop function as below:

void loop() {
on ();
delay (1);
off ();
delay (1);
}

It worked like a charm as a virtual wall for my Roomba robot.

Thanks again!

Can anyone post a Arduino code ???
I need to light up a IR led at 38Khz

You can use the code I posted in reply #3. Call setFrequency(38000) to select 38kHz. After that, call on() to start a burst of IR, and off() to stop it.

Hi All ,

I am trying to port my daikin AC remote control application to Attiny85 , but currently stuck as I am not expert at all.
To switch on AC I need to send the following sequence

unsigned int power_ON[] ={ 3420 , 1660 , 460 , 1220 , 460 , 380 , 480 , 360 , 500 , 340 , 500 , 1200 , 460 , 380 , 480 , 360 , 460 , 380 , 480 , 360 , 460 , 1240 , 460 , 380 , 460 , 1220 , 460 , 1240 , 480 , 360 , 480 , 1200 , 460 , 1240 , 480 , 1200 , 460 , 1220 , 500 , 1200 , 480 , 360 , 460 , 380 , 500 , 1180 , 500 , 360 , 480 , 360 , 460 , 380 , 480 , 360 , 460 , 380 , 460 , 380 , 460 , 1240 , 460 , 1220 , 480 , 1200 , 500 , 1200 , 460 , 380 , 460 , 380 , 460 , 380 , 460 , 380 , 480 , 360 , 500 , 360 , 460 , 380 , 460 , 380 , 460 , 380 , 460 , 380 , 460 , 380 , 460 , .. , 460 , 1240 , 460 , 380 , 460 , 380 , 460 , 380 , 460 , 380 , 460 , 1220 , 460 , 1240 , 460 , 380 , 460 , 1220 , 460 , 400 , 440 , 400 , 460 , 380 , 460 , 380 , 460 , 380 , 460 , 380 , 460 , 380 , 460 , 380 , 460 , 380 , ..};

I am using the following code available 

// 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(long microsecs) {
  // we'll count down from the number of microseconds we are told to wait

  cli();  // this turns off any background interrupts

  while (microsecs > 0) {
    // 38 kHz is about 13 microseconds high and 13 microseconds low
   digitalWrite(IRledPin, HIGH);  // this takes about 3 microseconds to happen
   delayMicroseconds(10);         // hang out for 10 microseconds
   digitalWrite(IRledPin, LOW);   // this also takes about 3 microseconds
   delayMicroseconds(10);         // hang out for 10 microseconds

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

  sei();  // this turns them back on
}

void sendDaikinOn()
{

 int i1;
for (i1 = 0; i1 < size_off; i1++)
 {
        pulseIR(power_ON[i1]);
        i1++;
        delayMicroseconds(power_ON[i1]);
 }  
 
}

As per the advice from , dc42 using the following code ,

void setup()
{
  PORTB = 0;
  DDRB =  0b00000010;		// set PB1 (= OCR1A) to be an output
  setFrequency(38000) ;
}

// Set the frequency that we will get on pin OCR1A but don't turn it on
void setFrequency(uint16_t freq)
{
  uint32_t requiredDivisor = (F_CPU/2)/(uint32_t)freq;

  uint16_t prescalerVal = 1;
  uint8_t prescalerBits = 1;
  while ((requiredDivisor + prescalerVal/2)/prescalerVal > 256)
  {
    ++prescalerBits;
    prescalerVal <<= 1;
  }
  
  uint8_t top = ((requiredDivisor + (prescalerVal/2))/prescalerVal) - 1;
  TCCR1 = (1 << CTC1) | prescalerBits;
  GTCCR = 0;
  OCR1C = top;
}

// Turn the frequency on
void on()
{
  TCNT1 = 0;
  TCCR1 |= (1 << COM1A0);
}

// Turn the frequency off and turn off the IR LED.
// We let the counter continue running, we just turn off the OCR1A pin.
void off()
{
  TCCR1 &= ~(1 << COM1A0);
}

void loop() {
delay(10000);
sendDaikinOn();
delay(10000);

}


void sendDaikinOn()
{

 int i1;
for (i1 = 0; i1 < size_on; i1++)
 {
      on ();
       delayMicroseconds(power_ON[i1]);
    //    pulseIR(power_ON[i1]);
        i1++;
        off ();
        delayMicroseconds(power_ON[i1]);
 }  
}

But this is not working , can some one please help me on this.

Moderator edit:
</mark> <mark>[code]</mark> <mark>

</mark> <mark>[/code]</mark> <mark>
tags added.

What clock frequency are running the attiny45 at? Did you set F_CPU in the board definition entry to match that frequency? If you are using the internal 8MHz clock, did you clear the CLKDIV8 fuse in the attiny?

I am using external crysal 16mhz and in board definition i set the frquency as 1600000. I could see this clock vlaue when trying to compile the program. I am using arduini editor to compile and load the program.

So have you programmed the fuses in the attiny to use the external crystal and clear the CLKDIV8 fuse?

I normally use the 8MHz internal clock on the attiny. The frequency doesn't need to be precise for 38kHz IR.

Thank you very much for the reply , dc42. I have programmed the fuses in the attiny to use the external crystal and cleared the CLKDIV8 fuse. Somewhere I read that internal clock is not very precise so that's the reason I have a 16mHx external clock.

In my case I have to send the pulses for 3420 and sleep 1660 and again send pulses for 460 and sleep for 1220 micro seconds.

What is the logic I need to use to send this kind of data using IR transmitter with attiny85.

I used the code available in
Overview | IR Sensor | Adafruit Learning System for the arduino , but the same is working for attimy85.

In case it helps, here is code I am using to turn 3 devices on or off when a button is pressed. I build it in AVR Studio, so it doesn't use the Arduino libraries (although I borrowed some code from them), and I use the internal 8MHz oscillator.

/*
 * RemoteControl.cpp
 *
 * Created: 25/11/2012 12:41:35
 *  Author: David
 */ 


#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

#define F_CPU	(8000000UL)		// 8MHz

// Class to hold the data for the IR on/off signal for a particular device
class RemoteData
{
	uint16_t headerMarkLength;
	uint16_t headerNormalLength;
	uint16_t headerRepLength;
	uint16_t frequency;
	uint16_t bitLength;
	uint16_t bitPeriod;
	uint32_t burstPeriod;
	uint8_t numRepeats;
	uint8_t numCycles;
	const uint8_t *normalData;
	const uint8_t *repeatData;
	
	void generateBurst(bool isRepeat) const;
	
public:
	RemoteData(uint16_t hml, uint16_t hnl, uint16_t hrl, uint16_t f, uint16_t bl, uint16_t bp, uint32_t up,
					uint8_t nr, uint8_t nc, const uint8_t* nd, const uint8_t* rd)
		: headerMarkLength(hml), headerNormalLength(hnl), headerRepLength(hrl), frequency(f), bitLength(bl), bitPeriod(bp), burstPeriod(up),
			numRepeats(nr), numCycles(nc), normalData(nd), repeatData(rd)
	{ }
	
	void activate() const;
};

// Data for PVR on/off signal
const uint8_t pvrNormalData[] = {0b11111111, 0b11111011, 0b11111111, 0b11010101, 0b01010101, 0b01000000, 0};
const uint8_t pvrRepeatData[] = {0b10000000, 0};

const RemoteData pvr(9000, 13500, 11100, 38000, 555, 1110, 108000UL, 2, 1, pvrNormalData, pvrRepeatData);

// Data for TV on/off signal
const uint8_t tvNormalData[] = {0b10111111, 0b01011011, 0b11011000, 0};
const uint8_t tvRepeatData[] = {0b10111110, 0b01110110, 0b10101101, 0};

const RemoteData tv(0, 0, 0, 38000, 250, 1060, 66200UL, 1, 3, tvNormalData, tvRepeatData);

// Data for hifi on/off signal
const uint8_t hifiNormalData[] = {0b11110111, 0b01111111, 0b01010110, 0b10110101, 0b01010101, 0b11011111, 0b10101101, 0b01010101, 0};
const uint8_t hifiRepeatData[] = {0b10000000, 0};

const RemoteData hifi(9000, 13500, 13500, 38000, 555, 1120, 108000UL, 2, 1, hifiNormalData, hifiRepeatData);

// Set the frequency that we will get on pin OCR1A but don't turn it on
void setFrequency(uint16_t freq)
{
  uint32_t requiredDivisor = (F_CPU/2)/(uint32_t)freq;

  uint16_t prescalerVal = 1;
  uint8_t prescalerBits = 1;
  while ((requiredDivisor + prescalerVal/2)/prescalerVal > 256)
  {
    ++prescalerBits;
    prescalerVal <<= 1;
  }
  
  uint8_t top = ((requiredDivisor + (prescalerVal/2))/prescalerVal) - 1;
  TCCR1 = (1 << CTC1) | prescalerBits;
  GTCCR = 0;
  OCR1C = top;
}

// Turn the frequency on
void on()
{
  TCNT1 = 0;
  TCCR1 |= (1 << COM1A0);
}

// Turn the frequency off and turn off the IR LED.
// We let the counter continue running, we just turn off the OCR1A pin.
void off()
{
  TCCR1 &= ~(1 << COM1A0);
}

volatile uint32_t timer0_overflow_count = 0;	// microseconds timer

// ISR for timer 0, counts microseconds
ISR(TIMER0_OVF_vect)
{
	timer0_overflow_count++;
	//TIFR |= _BV(TOV0);
}

// ISR for INT0, wakes up from sleep mode when button is pressed
ISR(INT0_vect)
{
	GIMSK = 0;	// disable INT0	
}

// Return the number of microseconds from start
uint32_t micros() 
{
	uint8_t oldSREG = SREG;	
	cli();
	uint32_t m = timer0_overflow_count;
	uint8_t t = TCNT0;
	
	if ((TIFR & (1 << TOV0)) && (t < 255))
	{
		m++;
	}
	SREG = oldSREG;
	
	return (m << 8) | t;
}

void RemoteData::generateBurst(bool repeat) const
{
  setFrequency(frequency);
  uint32_t start = micros();
  if (headerMarkLength != 0)
  {
    on();
    while (micros() - start < headerMarkLength) {}
    off();
    uint32_t headerLength = repeat ? headerRepLength : headerNormalLength;
    while (micros() - start < headerLength) {}
  }

  const uint8_t* p = (repeat ? repeatData : normalData);
  uint32_t bitStart = micros();
  for(;;)
  {
    uint8_t b = *p++;
    if (b == 0) break;
    for (uint8_t i = 0; i < 8; ++i)
    {
      if (b & 0x80)
      {
        on();
        while (micros() - bitStart < bitLength) {}
        off();
      }
      while (micros() - bitStart < bitPeriod) {}
      b <<= 1;
      bitStart += bitPeriod;
    }
  }
  while (micros() - start < burstPeriod) {};
}

void RemoteData::activate() const
{
  for (uint8_t c = 0; c < numCycles; ++c)
  {
    generateBurst(false);
    for (uint8_t i = 0; i < numRepeats; ++i)
    {
      generateBurst(true);
    }
  }
}

void setup()
{
	PORTB = 0b00011100;		// pullup resistor on PB2/3/4 enabled, outputs PB0 and PB1 low
	DDRB =  0b00000011;		// PB0 and PB1 are outputs, rest are inputs

	TCCR0A = 0;
	TCCR0B = _BV(CS01);		// prescaler = 8 so 1MHz count rate

	TIMSK = _BV(TOIE0);		// enable timer 0 overflow interrupt
	GIMSK = 0;
	PCMSK = 0;
}

int main(void)
{
	setup();
	sei();
	for (uint8_t i = 0; i < 6; ++i)
	{
		uint32_t start = micros();
		PORTB ^= 0b00000001;						// toggle visible LED
		while (micros() - start < 250000UL) {}		// wait 0.25 secs before toggling it again
	}

    for (;;)
    {
		// Wait for button to be pressed
		do 
		{
			set_sleep_mode(SLEEP_MODE_PWR_DOWN);
			cli();
			if (PINB & 0b00000100)
			{
				GIMSK = _BV(INT0);
				sleep_enable();
				sei();
				sleep_cpu();
				sleep_disable();
			}
			sei();
			uint32_t now = micros();
			while ((micros() - now) < 5000) {}				
		} while (PINB & 0b00000100);

		PORTB |= 0b00000001;	// turn on visible LED
		uint32_t start = micros();
		
		// The TV doesn't always respond to the PVR HDMI signal up unless we activate it a short while before the PVR. So start the TV first and PVR last.
		tv.activate();
		while (micros() - start < 250000UL) {}		// wait until 200ms after we started
		hifi.activate();
		while (micros() - start < 500000UL) {}		// wait until 500ms after we started
		pvr.activate();
		
		while (micros() - start < 1000000UL) {}		// keep LED on for at least a second

		PORTB &= 0b11111110;	// turn LED off

		// Wait for button to be released
		start = micros();
		for (;;)
		{
			uint32_t now = micros();
			if (!(PINB & 0b00000100))
			{
				start = now;
			}
			else if (now - start >= 5000)
			{
				break;
			}
		}
    }
}

Thank you dc42 , it is working fine now. The problem is with the big array size. I used PROGMEM to load this array to flash instead of RAM. After this it is working fine.

  const uint16_t power_ON[] PROGMEM ={ 3420 , 1660 , 460 , 1220 , 460 , 380 , 480 , 360 , 500 , 340 , 500 , 1200 , 460 , 380 , 480 , 360 , 460 , 380 , 480 , 360 , 460 , 1240 , 460 , 380 , 460 , 1220 , .... , very big array };

int size_on = sizeof(power_ON) / sizeof(uint16_t);

void sendDaikinOn()
{

const uint16_t * dataPointer;

  int i1;
  dataPointer = power_ON;
for (i1 = 0; i1 < size_on; i1++)
{
  
        on();
     
        _delay_us(pgm_read_word(dataPointer)-200);
        dataPointer++;
        i1++;
        off();
    
        _delay_us(pgm_read_word(dataPointer)-200);
         dataPointer++;
}  

}

dc42:
To generate 38KHz it's better to use one of the timer/counters. I use timer 1 so that timer 0 is still available for supporting the micros() function. This is the code I use:

void setup()

{
  PORTB = 0;
  DDRB =  0b00000010; // set PB1 (= OCR1A) to be an output
}

// Set the frequency that we will get on pin OCR1A but don't turn it on
void setFrequency(uint16_t freq)
{
 uint32_t requiredDivisor = (F_CPU/2)/(uint32_t)freq;

uint16_t prescalerVal = 1;
 uint8_t prescalerBits = 1;
 while ((requiredDivisor + prescalerVal/2)/prescalerVal > 256)
 {
   ++prescalerBits;
   prescalerVal <<= 1;
 }
 
 uint8_t top = ((requiredDivisor + (prescalerVal/2))/prescalerVal) - 1;
 TCCR1 = (1 << CTC1) | prescalerBits;
 GTCCR = 0;
 OCR1C = top;
}

// Turn the frequency on
void on()
{
 TCNT1 = 0;
 TCCR1 |= (1 << COM1A0);
}

// Turn the frequency off and turn off the IR LED.
// We let the counter continue running, we just turn off the OCR1A pin.
void off()
{
 TCCR1 &= ~(1 << COM1A0);
}

I'd like to try this code for the Arduino UNO using Analog Pin #4 (PC4). I think in the setup I need to change it to PORTC = 0;
DDRC = 0b00010000; However when I this into my Arduino IDE (1.0.5) it doesn't recognize "TCCR1", "CTC1" & "OCR1C" in the code. As you can tell here, I'm new here to these advance concepts. Thank you in advance.

Analog pin 4 does not support PWM, so you can't use the hardware timer/counters to generate a signal on that pin directly.

Hi everyone,

I'd like to share my sketch for flashing a led in raw mode with attiny 85.
First of all, I'm a newbie and I don't pretend this to be elegant or efficient, but it works and I realized by myself! Everybody here knows what I mean: I'm proud of it!

I got IRTinyTX.h library from here http://tetalab.org/blog/librairie-ir-nec-pour-attiny85 .
This library has to be modified adding the part related to sendRaw function in IRTinyTX.h and IRTinyTX.cpp copying from IRremote.h and IRremote.cpp. From original library I also removed RX files (IRTinyRX.h IRTinyRXint.h and relevant cpp files) that are not used and therefore create some errors while compiling.
Basically your IRTinyTX.h should be like this

#ifndef IRTinyTX_h
#define IRTinyTX_h

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#define IRLED PB1 // (Used as OC0B) - pin 6 on ATtiny85

// Ensures that the compiler knows it's running at 16MHz, if it's not defined in the makefile or not using the Arduino IDE
//#ifndef F_CPU
#define F_CPU 16000000UL  // 16 MHz
//#define F_CPU 8000000UL  // 8 MHz


//#endif

// pulse parameters in usec - I tweaked the values for my setup
#define PANASONIC_HDR_MARK 3502
#define PANASONIC_HDR_SPACE 1750
#define PANASONIC_BIT_MARK 502-50
#define PANASONIC_ONE_SPACE 1244
#define PANASONIC_ZERO_SPACE 400+100

#define JVC_HDR_MARK 8000
#define JVC_HDR_SPACE 4000
#define JVC_BIT_MARK 600
#define JVC_ONE_SPACE 1600
#define JVC_ZERO_SPACE 550

#define PANASONIC_BITS 48
#define TOPBIT 0x80000000

#define NEC_HDR_MARK	9000
#define NEC_HDR_SPACE	4500
#define NEC_BIT_MARK	560
#define NEC_ONE_SPACE	1600
#define NEC_ZERO_SPACE	560
#define NEC_RPT_SPACE	2250



class IRsend
{
public:
  IRsend() {}
  void sendJVC(uint32_t data, uint8_t nbits, uint8_t repeat); // *Note instead of sending the REPEAT constant if you want the JVC repeat signal sent, send the original code value and change the repeat argument from 0 to 1. JVC protocol repeats by skipping the header NOT by sending a separate code value like NEC does.
  void sendNEC(unsigned long data, int nbits);
  void enableIROut(uint8_t khz);
  void sendRaw(unsigned int buf[], int len, int hz);
private:
  void mark(int16_t usec);
  void space(int16_t usec);
};

#define USECPERTICK 50  // microseconds per clock interrupt tick
#define RAWBUF 100 // Length of raw duration buffer

// Marks tend to be 100us too long, and spaces 100us too short
// when received due to sensor lag.
#define MARK_EXCESS 100
#endif

and this should be your IRTinyTX.cpp

#include "IRTinyTX.h"


void IRsend::sendNEC(unsigned long data, int nbits)
{
  enableIROut(38);
  mark(NEC_HDR_MARK);
  space(NEC_HDR_SPACE);
  for (int i = 0; i < nbits; i++) {
    if (data & TOPBIT) {
      mark(NEC_BIT_MARK);
      space(NEC_ONE_SPACE);
    } 
    else {
      mark(NEC_BIT_MARK);
      space(NEC_ZERO_SPACE);
    }
    data <<= 1;
  }
  mark(NEC_BIT_MARK);
  space(0);
}




void IRsend::sendJVC(uint32_t data, uint8_t nbits, uint8_t repeat)
{
    //enableIROut(38);
    data = data << (32 - nbits);
    if (!repeat){
        mark(JVC_HDR_MARK);
        space(JVC_HDR_SPACE); 
    }
    for (int i = 0; i < nbits; i++) {
        if (data & TOPBIT) {
            mark(JVC_BIT_MARK);
            space(JVC_ONE_SPACE); 
        } 
        else {
            mark(JVC_BIT_MARK);
            space(JVC_ZERO_SPACE); 
        }
        data <<= 1;
    }
    mark(JVC_BIT_MARK);
    space(0); //Turn IR LED off    
}

void IRsend::sendRaw(unsigned int buf[], int len, int hz)
{
    enableIROut(hz);
    for (int i = 0; i < len; i++) {
        if (i & 1) {
            space(buf[i]);
        }
        else {
            mark(buf[i]);
        }
    }
    space(0); // Just to be sure
}

void IRsend::mark(int16_t time) {
    // Sends an IR mark for the specified number of microseconds.
    // The mark output is modulated at the PWM frequency.
    
    // Clear OC0A/OC0B on Compare Match when up-counting.
    // Set OC0A/OC0B on Compare Match when down-counting.    
    TCCR0A |= _BV(COM0B1); // Enable pin 6 (PB1) PWM output        
    delayMicroseconds(time);    
}

/* Leave pin off for time (given in microseconds) */
void IRsend::space(int16_t time) {
    // Sends an IR space for the specified number of microseconds.
    // A space is no output, so the PWM output is disabled.
    
    // Normal port operation, OC0A/OC0B disconnected.
    TCCR0A &= ~(_BV(COM0B1)); // Disable pin 6 (PB1) PWM output
    delayMicroseconds(time);    
}

void IRsend::enableIROut(uint8_t khz) {
  // Enables IR output.  The khz value controls the modulation frequency in kilohertz.
  // The IR output will be on pin 6 (OC0B).
  // This routine is designed for 36-40KHz; if you use it for other values, it's up to you
  // to make sure it gives reasonable results.  (Watch out for overflow / underflow / rounding.)
  // TIMER0 is used in phase-correct PWM mode, with OCR0A controlling the frequency and OCR0B
  // controlling the duty cycle.
  // There is no prescaling, so the output frequency is 16MHz / (2 * OCR0A)
  // To turn the output on and off, we leave the PWM running, but connect and disconnect the output pin.
  // A few hours staring at the ATmega documentation and this will all make sense.
  // See my Secrets of Arduino PWM at http://arcfn.com/2009/07/secrets-of-arduino-pwm.html for details.
  
  DDRB |= _BV(IRLED); // Set as output

  PORTB &= ~(_BV(IRLED)); // When not sending PWM, we want it low

  // Normal port operation, OC0A/OC0B disconnected  
  // COM0A = 00: disconnect OC0A
  // COM0B = 00: disconnect OC0B; to send signal set to 10: OC0B non-inverted    
  // WGM0 = 101: phase-correct PWM with OCR0A as top
  // CS0 = 000: no prescaling
  TCCR0A = _BV(WGM00);
  TCCR0B = _BV(WGM02) | _BV(CS00);

  // The top value for the timer.  The modulation frequency will be SYSCLOCK / 2 / OCR0A.
  OCR0A = F_CPU / 2 / khz / 1000;
  OCR0B = OCR0A / 3; // 33% duty cycle
}

Notice that F_CPU is set to 16 Mhz. In fact, I got it worked flashing the bootloader Attiny 85 @ 16 Mhz internal PPL.

I have to power on an AC when the temperature increases above 26 °C and power off when decreases below 24°C.

Here is the sketch

#include <IRTinyTX.h>

float temp;
int tempPin = 3;
int on_off = 0;
unsigned int rawCodes[] = {8850,  4500, 600,  1650, 600,  1650, 600,  550, 600,  600, 550,  550, 600,  550, 600,  550, 600,  1700, 550,  550, 600,  1650, 600,  1650, 650,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  1650, 600,  550, 650,  500, 600,  550, 600,  550, 600,  600, 600,  500, 650,  1650, 600,  500, 650,  550, 600,  550, 600,  550, 600,  1650, 600,  550, 600,  550, 600,  600, 550,  550, 600,  600, 600,  550, 600, 500, 600,  550, 600,  600, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600};             

IRsend irsend;

void setup()
{
   
}

void loop() 
{
  
  temp = analogRead(tempPin);
  temp = temp * 0.48828125;
   
  if (temp > 26.00 && on_off == 0) {
      irsend.sendRaw(rawCodes, 99, 38); 
       on_off = 1;
      }
  else if (temp < 24.00 && on_off == 1) {
        irsend.sendRaw(rawCodes, 99, 38); 
        on_off = 0;
      }
  delay(5000);
}

I'm eager to read your thoughts ! :smiley:

Thankx dc42! You won't believe me how much time I was spending to get this Attiny85 to send IR pulses... but then... I saw your code :wink:

Thanks DC42 for such great code.

I have a question: if I want to use PB0 port instead of PB1 port, what do I need to change in your code?

UN_Owen:
Hi all

I am new to Arduino and the forum.
I am trying to make a virtual wall for my Roomba robot.
It requires a 38kHz pulse with an IR led.
I managed this with my Arduino uno but I am having problems with my Attiny 45.
I have sucessfully downloaded the LED blink sketch to the Attiny 45 and it is blinking at 1Hz.
When I use the delayMicroseconds function it doesn't flash faster than approximately 100 ms.

void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delayMicroseconds(13) // wait for 13 ms
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delayMicroseconds(13) // wait for 13 ms
}

Could the problem be that I haven't been able to set the clock frequency to 8 MHz?
Thanks

The digitalWrite() functions have WAY too much overhead to be able to time 38 khz. You need a timer and ISR:

volatile uint8_t  state; // toggle LED at carrier rate
volatile uint8_t  onoff; // IR pulses on or off
volatile uint16_t count; // led pulse counter

// test for count == 0 using atomic read
void busy_wait (void)
{
	uint16_t tmp;
	do { // run at least once
		cli(); // interrupts off
		tmp = count; // atomic read of var
		sei(); // interrupts back on
	} while (tmp);
}

// turn IR led on or off
void led_ctrl (uint8_t on)
{
	on ? IO_PORT |= IR_BIT : IO_PORT &= ~IR_BIT;
}

ISR (TIMER0_COMPA_vect) // IR carrier generator
{
	if (count) { // if a count was pushed in...
		state ? led_ctrl (0) : led_ctrl (onoff); // pulse IR LED on or off at carrier rate
		count -= state; // decrement count (LED on times only)
		state ? state = 0 : state = 1; // if led was on set it off, else set it on
	}
}

void setup (void)
{
#define KHZ 38
	// set timer 0 (8 bit) to CTC mode, carrier freq. this times the IR pulses.
	TCCR0A = (0x00 | _BV(WGM01)); // mode 2 (CTC) OCR0A = top
	TCCR0B = (0x00 | _BV(CS00)); // F_CPU/1
	OCR0A = ((F_CPU / 2000.0 / KHZ) - 1); // divider for IR carrier freq
	TIMSK0 = (0x00 | _BV(OCIE0A)); // enable interrupt on OCR0A match
}

To generate an IR pulse, you write a value to "count". At 38 khz, each IR pulse cycle is 26.32 microeconds (you get an even 25 by using 40 khz BTW). Then, the number you write into "count" is your desired pulse duration / 26.32 (or /25).

For example, to send a 10 millisecond (10000 microsecond) IR burst, just write 400 decimal into "count", then call "busy_wait()" to know when it's done.

As is probably obvious, this code is normally used to send IR remote control codes using data obtained from Pronto format files.

Hope this helps.

dc42, are you familiar with the Digispark boards? I'm trying to get your timer code running on 1 but I get nothing out of the pin. I have tried specifying the digispark board as 16.5mhz, 16mhz, and 8mhz, I get the same thing every time, just a board that does nothing. I know the board works because I can put other code on it. I'm assuming maybe something in the bootloader is conflicting with the setFrequency code. Any thoughts?
Here is the code I'm trying to get running. Not my code, a bunch of other people are uising so I know the code is good, just cant get it to work on the digispark.

/**
 * EasyRaceLapTimer - Copyright 2015-2016 by airbirds.de, a project of polyvision UG (haftungsbeschränkt)
 *
 * Author: Alexander B. Bierbrauer
 *
 * This file is part of EasyRaceLapTimer.
 * 
 * Vresion: 0.2
 *
 * EasyRaceLapTimer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * EasyRaceLapTimer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License along with Foobar. If not, see http://www.gnu.org/licenses/.
 **/
// use 8MHZ internal clock of the Attiny
// Timer code from http://forum.arduino.cc/index.php?topic=139729.0


#define BIT_SET(a,b) ((a) |= (1<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1<<(b)))
#define BIT_CHECK(a,b) ((a) & (1<<(b)))

// CHANGE HERE THE ID OF TRANSPONDER 
// possible values are 1 to 63 
#define TRANSPONDER_ID 42




#define NUM_BITS  9
#define ZERO 250
#define ONE  650

unsigned int buffer[NUM_BITS];
unsigned int num_one_pulses = 0;

unsigned int get_pulse_width_for_buffer(int bit){
  if(BIT_CHECK(TRANSPONDER_ID,bit)){
    num_one_pulses += 1;
    return ONE;
  }

  return ZERO;
}

unsigned int control_bit(){
  if(num_one_pulses % 2 >= 1){
    return ONE;  
  }else{
    return ZERO;
  }
}

void setup()
{
  PORTB = 0;
  DDRB =  0b00000010;    // set PB1 (= OCR1A) to be an output

  setFrequency(38000); // 38 kHz

  buffer[0] = ZERO;    
  buffer[1] = ZERO;    
  buffer[2] = get_pulse_width_for_buffer(5);
  buffer[3] = get_pulse_width_for_buffer(4);
  buffer[4] = get_pulse_width_for_buffer(3);
  buffer[5] = get_pulse_width_for_buffer(2);
  buffer[6] = get_pulse_width_for_buffer(1);
  buffer[7] = get_pulse_width_for_buffer(0);
  buffer[8] = control_bit(); 
}

// Set the frequency that we will get on pin OCR1A but don't turn it on
void setFrequency(uint16_t freq)
{
 uint32_t requiredDivisor = (F_CPU/2)/(uint32_t)freq;

 uint16_t prescalerVal = 1;
 uint8_t prescalerBits = 1;
 while ((requiredDivisor + prescalerVal/2)/prescalerVal > 256)
 {
   ++prescalerBits;
   prescalerVal <<= 1;
 }
 
 uint8_t top = ((requiredDivisor + (prescalerVal/2))/prescalerVal) - 1;
 TCCR1 = (1 << CTC1) | prescalerBits;
 GTCCR = 0;
 OCR1C = top;
}

// Turn the frequency on
void ir_pulse_on()
{
 TCNT1 = 0;
 TCCR1 |= (1 << COM1A0);
}

// Turn the frequency off and turn off the IR LED.
// We let the counter continue running, we just turn off the OCR1A pin.
void ir_pulse_off()
{
 TCCR1 &= ~(1 << COM1A0);
}

void loop(){
  for(int i = 0; i < 3; i++){
    for(int b = 0; b < NUM_BITS; b++){
      switch(b){
        case 0:
          ir_pulse_on();
          delayMicroseconds(buffer[b]);
          break;
        case 1:
          ir_pulse_off();
          delayMicroseconds(buffer[b]);
          break;
        case 2:
          ir_pulse_on();
          delayMicroseconds(buffer[b]);
          break;
        case 3:
          ir_pulse_off();
          delayMicroseconds(buffer[b]);
          break;
        case 4:
          ir_pulse_on();
          delayMicroseconds(buffer[b]);
          break;
        case 5:
          ir_pulse_off();
          delayMicroseconds(buffer[b]);
          break;
        case 6:
          ir_pulse_on();
          delayMicroseconds(buffer[b]);
          break;
        case 7:
          ir_pulse_off();
          delayMicroseconds(buffer[b]);
          break;
        case 8:
          ir_pulse_on();
          delayMicroseconds(buffer[b]);
          break;
      }
      ir_pulse_off();
    } // going through the buffer
    
    delay(20 + random(0, 5));
  } // 3 times
} // end of main loop