Go Down

Topic: attiny85 and TimerOne.h (Read 7045 times) previous topic - next topic

pyrforos

it works but the flickering stays... its like the update function is actually enough slow to mess up your eyes
 

Jack Christensen

Thinking some more on this.

From what you've said, I suspect you are telling the IDE (via Tools > Board) that you have an ATtiny85 @ 8MHz, but in fact it's running at 1MHz. Have you reset the fuses from their factory default values? Can you use AVRDUDE to read out the fuse bytes? What are their values? The factory defaults are Low Fuse=0x62, High Fuse=0xDF, Extended Fuse=0xFF. If you will change the Low Fuse to 0xE2, that will change the clock to 8MHz, which will work better for you I think.

Choosing different clock frequencies via Tools > Board in the IDE does not change the clock frequency in the hardware, it just tells the compiler what speed the clock is set to. So obviously the two need to match.

Then also be sure to uncomment these lines in the code, despite my previous post:

Code: [Select]
#if F_CPU == 1000000                //1MHz system clock
    TCCR1 |= _BV(CS12);             //set prescaler to divide by 8 (this starts the timer)
#elif F_CPU == 8000000              //8MHz system clock
    TCCR1 |= _BV(CS12) | _BV(CS11) | _BV(CS10);    //set prescaler to divide by 64 (this starts the timer)
#else
    Clock must be 1MHz or 8MHz!     //Error, only 1MHz or 8MHz clock (F_CPU) supported.
#endif
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

pyrforos

sorry for the delay...
No i did not touch any of the fuses... i don't know how to...
im on linux so i cant use avrdude, only arduino IDE, and the only option in boards is the name of the chip am using.

Jack Christensen

#18
Oct 21, 2011, 09:31 pm Last Edit: Oct 21, 2011, 09:48 pm by Jack Christensen Reason: 1
Not sure about Linux, but on Windows, avrdude is installed as part of the Arduino IDE, under hardware\tools\avr\bin.

But, lacking that, you could also change the clock prescaler on the fly! I should look at HLT again, maybe it assumes an 8MHz clock. Let's proceed on that assumption, and also factory fuse settings. Enter these as the first two lines in setup() to set the clock to 8MHz:

Code: [Select]
   CLKPR = 0x80;    //set the Clock Prescaler Change Enable bit
   CLKPR = 0x00;    //set the Clock Prescaler to divide by one
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Jack Christensen

I installed HLT, and all of the entries in the boards.txt file have 1MHz clocks. I added one with 8MHz as below (also modified the name on the existing one to be clear it's 1MHz).

Code: [Select]
# modified the next line only
attiny85usbtinyisp.name=ATtiny85 @ 1MHz (w/ USB Tiny ISP)
attiny85usbtinyisp.upload.using=arduino:usbtinyisp
attiny85usbtinyisp.upload.maximum_size=8192
attiny85usbtinyisp.build.mcu=attiny85
attiny85usbtinyisp.build.f_cpu=1000000L
attiny85usbtinyisp.build.core=attiny45_85

# new entry
attiny85usbtinyisp8.name=ATtiny85 @ 8 MHz (w/ USB Tiny ISP)
attiny85usbtinyisp8.upload.using=arduino:usbtinyisp
attiny85usbtinyisp8.upload.maximum_size=8192
attiny85usbtinyisp8.build.mcu=attiny85
attiny85usbtinyisp8.build.f_cpu=8000000L
attiny85usbtinyisp8.build.core=attiny45_85


HOWEVER, something is funny with my demo code. It works when the 8MHz board is selected via Tools > Boards, but when using the 1MHz entry, the LED only blinks every eight seconds. Haven't figured out why yet, but I think you observed the same thing earlier. The same code works as expected at 1MHz with WinAVR or Arduino Tiny. Anyhoo, add an 8MHz entry to boards.txt, and try the code below, the LED should blink every second.
Code: [Select]
//Example for Arduino forum.
//Use ATtiny85 Timer/Counter1 to call a function every 10ms.
//In setup(), call setupTimer1() and pass it the name of your function which will
//run as part of the interrupt service routine (ISR) every 10ms. This function must take no
//arguments, return nothing, and should run as quickly as possible. The delay() and millis()
//functions will not work while the ISR is running and so should be avoided.
//Works with 1MHz or 8MHz system clock (others may be possible, but I got lazy).
//
//In this example, the myISR() function simply turns an LED on for a second, then off for a second.
//Connect the LED to DIP pin 3 (PB4).
//
//Jack Christensen 18Oct2011
//
//Scotchware License: If we meet some day, and you think this is worth it, you can buy me a scotch.

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

//function prototypes
void setupTimer1(void (*isrPointer)(void));
void setup(void);
void loop(void);
void myISR(void);

#define LED PORTB4                  //connect LED to this pin

#ifndef ARDUINO                     //main() function not required in Arduino environment
int main(void) {
   setup();
   for(;;) {loop();}
   return 0;
}
#endif

void setup(void) {
#if F_CPU == 8000000                //8MHz system clock
   CLKPR = 0x80;                   //set the Clock Prescaler Change Enable bit
   CLKPR = 0x00;                   //set the Clock Prescaler to divide by one (8Mhz assuming factory default fuses)
#endif
   DDRB |= _BV(LED);               //set the LED pin as an output
   setupTimer1(myISR);             //set timer1 up to call myISR every 10ms
}

void loop(void) {
}

void myISR() {                      //this function will be called every 10ms
   static uint8_t myCounter;
   
   if (++myCounter == 100) {       //has a second elapsed?
       myCounter = 0;
       PINB |= _BV(LED);           //toggle the LED
   }
}

//Copy code from here down, AND uncomment the following line and move it near the top of your sketch BEFORE the setup() function.
//void setupTimer1(void (*isrPointer)(void));        //function prototype for setupTimer1()
void (*userISR)(void);              //pointer to the user's ISR

void setupTimer1(void (*isrPointer)(void)) {
   userISR = isrPointer;           //save the pointer to the user's ISR
   //set timer1 up to generate an interrupt every millisecond
   TCCR1 |= _BV(CTC1);             //clear timer1 when it matches the value in OCR1C
   TIMSK |= _BV(OCIE1A);           //enable interrupt when OCR1A matches the timer value
   sei();                          //enable global interrupts
   OCR1A = 124;                    //set the match value for interrupt
   OCR1C = 124;                    //and the same match value to clear the timer
#if F_CPU == 1000000                //1MHz system clock
   TCCR1 |= _BV(CS12);             //set prescaler to divide by 8 (this starts the timer)
#elif F_CPU == 8000000              //8MHz system clock
   TCCR1 |= _BV(CS12) | _BV(CS11) | _BV(CS10);    //set prescaler to divide by 64 (this starts the timer)
#else
#error Only 1MHz or 8MHz clock (F_CPU) supported!
#endif
}

ISR(TIMER1_COMPA_vect) {            //handles the Timer1 Compare Match A interrupt
   static uint8_t interruptCount;
   
   if (++interruptCount == 10) {   //have there been 10 interrupts? (i.e. 10ms)
       interruptCount = 0;         //yes, reset the counter
       userISR();                  //and call the user's ISR
   }
}
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Jack Christensen

#20
Oct 22, 2011, 12:03 am Last Edit: Oct 22, 2011, 12:06 am by Jack Christensen Reason: 1
I figured it out. The HLT core uses Timer/Counter1 for PWM, so there were some conflicting values set in TCCR1. Changed the code below to ruthlessly set the registers as it needs them. So, if you want to use PWM, it may not work, and will certainly not work the same. If you don't need it, then this approach could be OK.

The code below should blink the LED once per second when used with HLT either with a clock frequency of 1MHz or 8MHz. It assumes the ATtiny85 has default factory fuse settings, and if compiled for 8MHz, it adjusts the clock prescaler register accordingly. For your application, I'd definitely run at 8MHz, because it sounds like you're doing some sort of multiplexing and the speed is important.

Code: [Select]
//Example for Arduino forum.
//Use ATtiny85 Timer/Counter1 to call a function every 10ms.
//In setup(), call setupTimer1() and pass it the name of your function which will
//run as part of the interrupt service routine (ISR) every 10ms. This function must take no
//arguments, return nothing, and should run as quickly as possible. The delay() and millis()
//functions will not work while the ISR is running and so should be avoided.
//Works with 1MHz or 8MHz system clock (others may be possible, but I got lazy).
//If used with the HTL core (http://hlt.media.mit.edu/?p=1229), PWM may not work,
//as HLT uses Timer/Counter1 for PWM.
//
//In this example, the myISR() function simply turns an LED on for a second, then off for a second.
//Connect the LED to DIP pin 3 (PB4).
//
//Jack Christensen 18Oct2011
//
//Scotchware License: If we meet some day, and you think this is worth it, you can buy me a scotch.

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

//function prototypes
void setupTimer1(void (*isrPointer)(void));
void setup(void);
void loop(void);
void myISR(void);

#define LED PORTB4                  //connect LED to this pin

#ifndef ARDUINO                     //main() function not required in Arduino environment
int main(void) {
   setup();
   for(;;) {loop();}
   return 0;
}
#endif

void setup(void) {
#if F_CPU == 8000000                //8MHz system clock
   CLKPR = 0x80;                   //set the Clock Prescaler Change Enable bit
   CLKPR = 0x00;                   //set the Clock Prescaler to divide by one (8Mhz assuming factory default fuses)
#endif
   DDRB |= _BV(LED);               //set the LED pin as an output
   setupTimer1(myISR);             //set timer1 up to call myISR every 10ms
}

void loop(void) {
}

void myISR() {                      //this function will be called every 10ms
   static uint8_t myCounter;
   
   if (++myCounter == 100) {       //has a second elapsed?
       myCounter = 0;
       PINB |= _BV(LED);           //toggle the LED
   }
}

//Copy code from here down, AND uncomment the following line and move it near the top of your sketch BEFORE the setup() function.
//void setupTimer1(void (*isrPointer)(void));        //function prototype for setupTimer1()
void (*userISR)(void);              //pointer to the user's ISR

void setupTimer1(void (*isrPointer)(void)) {
   userISR = isrPointer;           //save the pointer to the user's ISR
   //set timer1 up to generate an interrupt every millisecond
   TCCR1 = _BV(CTC1);              //clear timer1 when it matches the value in OCR1C
   TIMSK |= _BV(OCIE1A);           //enable interrupt when OCR1A matches the timer value
   sei();                          //enable global interrupts
   OCR1A = 124;                    //set the match value for interrupt
   OCR1C = 124;                    //and the same match value to clear the timer
#if F_CPU == 1000000                //1MHz system clock
   TCCR1 |= _BV(CS12);             //set prescaler to divide by 8 (this starts the timer)
#elif F_CPU == 8000000              //8MHz system clock
   TCCR1 |= _BV(CS12) | _BV(CS11) | _BV(CS10);    //set prescaler to divide by 64 (this starts the timer)
#else
#error Only 1MHz or 8MHz clock (F_CPU) supported!
#endif
}

ISR(TIMER1_COMPA_vect) {            //handles the Timer1 Compare Match A interrupt
   static uint8_t interruptCount;
   
   if (++interruptCount == 10) {   //have there been 10 interrupts? (i.e. 10ms)
       interruptCount = 0;         //yes, reset the counter
       userISR();                  //and call the user's ISR
   }
}
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

pyrforos

hi again..
wow! you did some  work! I apologise again for the delay  but i've been craaazy running...
so i tried your code with the attiny85 and i get this error on compile
Code: [Select]
In file included from /usr/lib/gcc/avr/4.5.3/../../../avr/include/util/delay.h:44:0,
                 from /usr/lib/gcc/avr/4.5.3/../../../avr/include/avr/delay.h:37,
                 from /home/pyrforos/Documents/electronic projects/hardware/attiny45_85/cores/attiny45_85/wiring_private.h:32,
                 from /home/pyrforos/Documents/electronic projects/hardware/attiny45_85/cores/attiny45_85/WInterrupts.c:36:
/usr/lib/gcc/avr/4.5.3/../../../avr/include/math.h:426:15: error: expected identifier or '(' before 'double'
/usr/lib/gcc/avr/4.5.3/../../../avr/include/math.h:426:15: error: expected ')' before '>=' token

Jack Christensen

#22
Nov 05, 2011, 03:13 pm Last Edit: Nov 05, 2011, 03:16 pm by Jack Christensen Reason: 1

hi again..
wow! you did some  work! I apologise again for the delay  but i've been craaazy running...
so i tried your code with the attiny85 and i get this error on compile


Not sure what's going on there. Did you try to run the code standalone, exactly as I posted? Do that first, before trying to integrate it into your project, just to be sure it works as expected.

I'm confused by those error messages, as my example code uses neither double nor math.h. I copied my latest code from the post above, and it compiles fine either with HLT or with Arduino Tiny. Your compile errors seem to be in math.h. Have you made changes in that area? If not, I might consider a reinstall, as it may have been changed inadvertently.
MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Chagrin

Sorry to pull this old post out from the dead but I have a few questions as to the operation of Jack's code. It's been extremely helpful to me and I've been spending quite a bit of time poking and prodding at it as I try to implement a software PWM for the ATTiny.

My questions are specific to this part of the code:

Code: [Select]

void setupTimer1(void (*isrPointer)(void)) {
    userISR = isrPointer;           //save the pointer to the user's ISR
    //set timer1 up to generate an interrupt every millisecond
    TCCR1 |= _BV(CTC1);             //clear timer1 when it matches the value in OCR1C
    TIMSK |= _BV(OCIE1A);           //enable interrupt when OCR1A matches the timer value
    sei();                          //enable global interrupts
    OCR1A = 124;                    //set the match value for interrupt
    OCR1C = 124;                    //and the same match value to clear the timer

    TCCR1 |= _BV(CS12) | _BV(CS11) | _BV(CS10);    //set prescaler to divide by 64 (this starts the timer)
}

ISR(TIMER1_COMPA_vect) {            //handles the Timer1 Compare Match A interrupt
    static uint8_t interruptCount;

    if (++interruptCount == 10) {   //have there been 10 interrupts? (i.e. 10ms)
        interruptCount = 0;         //yes, reset the counter
        userISR();                  //and call the user's ISR
    }
}



If I understand this correctly, the prescalar TCCR1, as set here, waits for 64 clock ticks and then increments the Timer/Counter by 1, and then when the Timer/Counter reaches 124 (the value of OCR1A) it triggers the interrupt. Then per your ISR you wait for 10 interrupts before you trigger the user's ISR function.

Apparently there's a bit of a balance here with these three different tunable values and that brings me to two questions. Would it be accurate to suggest that OCR1A and OCR1C be divided by 10 and then eliminate the interruptCount block could be eliminated? Also, is it most efficient to set the highest, appropriate prescalar and then adjust OCR1A/OCR1C values afterwards or does the balance between those values not yield any benefit?

Go Up