Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #45 on: January 30, 2013, 09:23:49 pm » |
== not =. But no, not at all. You don't have to use interrupts. http://www.gammon.com.au/forum/?id=11504I just hooked this up and tested it: const byte OUTPUT_PIN = 3; // Timer 2 "B" output: OC2B (chip pin 5)
const byte n = 31; // tick every 32 cycles
void setup() { pinMode (OUTPUT_PIN, OUTPUT);
ASSR = _BV (AS2); // asynchronous input from chip pins 9 and 10 (XTAL1 and XTAL2) TCCR2A = _BV (WGM20) | _BV (WGM21) | _BV (COM2B1); // fast PWM, clear OC2A on compare TCCR2B = _BV (WGM22) | _BV (CS20); // fast PWM, no prescaler OCR2A = n; OCR2B = ((n + 1) / 2) - 1; // 50% duty cycle } // end of setup
void loop() { }
No interrupts, no digitalWrite. I set the chip to run off the internal 8 MHz oscillator and shoved a 32.768 KHz crystal between pins 9 and 10. And it works! 1.024 KHz output, as expected: 1 / (32 * (1 / 32768)) = 1024
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 35
|
 |
« Reply #46 on: January 30, 2013, 09:32:18 pm » |
Neat! Works for me too. But this is only for crystal testing, right? The long-term goal, after all, is to tick a counter for the clock.
|
|
|
|
|
Logged
|
|
|
|
|
Grand Blanc, MI, USA
Offline
Faraday Member
Karma: 43
Posts: 2502
"We're a proud service of the Lost Electricity Reclamation Agency"
|
 |
« Reply #47 on: January 30, 2013, 10:12:29 pm » |
Using the code below, I'm getting right about 20ppm error with an inexpensive crystal from Tayda just stuck in the breadboard. Pics of the setup and my homebrew frequency counter here. //configure timer 2 for external 32768Hz crystal. //toggles OC2A at f/2 = 16384Hz.
void setup(void) { pinMode(11, OUTPUT); //OC2A/MOSI TCCR2B = 0; //stop the timer TCCR2A = 0; TIMSK2 = 0; //no interrupts TIFR2 = 0; ASSR = _BV(AS2); //Timer/Counter2 clocked from external crystal TCCR2A = _BV(COM2A0) | _BV(WGM21); //Toggle OC2A on compare match, CTC mode TCCR2B = _BV(CS20); //no prescaling OCR2A = 0; //top count, matches on every cycle, so toggles OC2A at f/2 TCNT2 = 0; //zero the counter while (ASSR & (_BV(TCN2UB) | _BV(TCR2AUB) | _BV(TCR2BUB))); //wait for the registers to be updated }
void loop(void) { }
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 35
|
 |
« Reply #48 on: January 30, 2013, 10:18:51 pm » |
Hoo boy! Quite the setup you've got there.
Well, curiosity kills the chip. I have discovered that bending the pins three times is one time too many, and now I have a perfectly working µC with no XTAL/TOSC pins. Maybe I'll see if I can come up with some non-time-critical project sometime. I'll try out your code when the new ones arrive!
|
|
|
|
|
Logged
|
|
|
|
|
Grand Blanc, MI, USA
Offline
Faraday Member
Karma: 43
Posts: 2502
"We're a proud service of the Lost Electricity Reclamation Agency"
|
 |
« Reply #49 on: January 30, 2013, 10:20:50 pm » |
Well, curiosity kills the chip. I have discovered that bending the pins three times is one time too many, and now I have a perfectly working µC with no XTAL/TOSC pins. Maybe I'll see if I can come up with some non-time-critical project sometime. I'll try out your code when the new ones arrive!
We mourn the passing of your AVR 
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #50 on: January 30, 2013, 11:29:34 pm » |
Neat! Works for me too. But this is only for crystal testing, right? The long-term goal, after all, is to tick a counter for the clock.
How often? Every second?
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #51 on: January 30, 2013, 11:39:32 pm » |
If you change the prescaler then it toggles every 500 mS giving a frequency of 1 Hz. const byte OUTPUT_PIN = 3; // Timer 2 "B" output: OC2B
const byte n = 31;
void setup() { pinMode (OUTPUT_PIN, OUTPUT);
ASSR = _BV (AS2); TCCR2A = _BV (WGM20) | _BV (WGM21) | _BV (COM2B1); // fast PWM, clear OC2A on compare TCCR2B = _BV (WGM22) | _BV (CS20) | _BV (CS21) | _BV (CS22); // fast PWM, prescaler of 1024 OCR2A = n; OCR2B = ((n + 1) / 2) - 1; // 50% duty cycle } // end of setup
void loop() { }
Maths: 1 / (32 * (1 / 32768)) / 1024 = 1
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #52 on: January 30, 2013, 11:54:05 pm » |
Using the code below, I'm getting right about 20ppm error ...
I'm not sure if I have the maths right here. I measured (with the prescaler of 1) a frequency of 1023.943 Hz when I was expecting 1024 Hz. Now I make that as 55.66 ppm error. Someone want to check that? (1024 - 1023.943)/1024 * 1e6 = 55.664062500016
|
|
|
|
|
Logged
|
|
|
|
|
Grand Blanc, MI, USA
Offline
Faraday Member
Karma: 43
Posts: 2502
"We're a proud service of the Lost Electricity Reclamation Agency"
|
 |
« Reply #53 on: January 30, 2013, 11:56:24 pm » |
Using the code below, I'm getting right about 20ppm error ...
I'm not sure if I have the maths right here. I measured (with the prescaler of 1) a frequency of 1023.943 Hz when I was expecting 1024 Hz. Now I make that as 55.66 ppm error. Someone want to check that? (1024 - 1023.943)/1024 * 1e6 = 55.664062500016
Yep, that's how I'd do it.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 35
|
 |
« Reply #54 on: January 31, 2013, 08:34:51 am » |
If you change the prescaler then it toggles every 500 mS giving a frequency of 1 Hz.
Right, but it's still toggling an output pin, not clicking something, right? I could set up some code to monitor that pin and second++ every tick or two ticks or whatever, but why would I do that when I have the ISR? Besides, eventually I want to sleep the chip and have the ISR wake it every second (or five seconds). Not sure if that's going to happen, but it's the goal.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Edison Member
Karma: 114
Posts: 2205
|
 |
« Reply #55 on: January 31, 2013, 09:09:44 am » |
why would I do that when I have the ISR? Besides, eventually I want to sleep the chip and have the ISR wake it every second (or five seconds). Not sure if that's going to happen, but it's the goal. Advance a counter / time struct in the isr. To wake it up, you need to have an isr on the LF oscillator.
|
|
|
|
|
Logged
|
|
|
|
|
Grand Blanc, MI, USA
Offline
Faraday Member
Karma: 43
Posts: 2502
"We're a proud service of the Lost Electricity Reclamation Agency"
|
 |
« Reply #56 on: January 31, 2013, 09:18:48 am » |
If you change the prescaler then it toggles every 500 mS giving a frequency of 1 Hz.
Right, but it's still toggling an output pin, not clicking something, right? I could set up some code to monitor that pin and second++ every tick or two ticks or whatever, but why would I do that when I have the ISR? Besides, eventually I want to sleep the chip and have the ISR wake it every second (or five seconds). Not sure if that's going to happen, but it's the goal. Completely possible. Power Save mode sleeps most of the µC but keeps Timer2 running, so it can generate an interrupt that wakes the system. Code from the link I posted earlier below. I must have measured the current, but I don't remember. Datasheet says Power Save mode should only require around a microamp or two (with brownout detector disabled). Timer setup: TIMSK2 = 0; //stop timer2 interrupts while we set up ASSR = _BV(AS2); //Timer/Counter2 clocked from external crystal TCCR2A = 0; //override arduino settings, ensure WGM mode 0 (normal mode) TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20); //prescaler clk/1024 -- TCNT2 will overflow once every 8 seconds TCNT2 = 0; //start the timer at zero while (ASSR & (_BV(TCN2UB) | _BV(TCR2AUB) | _BV(TCR2BUB))) {} //wait for the registers to be updated TIFR2 = _BV(OCF2B) | _BV(OCF2A) | _BV(TOV2); //clear the interrupt flags TIMSK2 = _BV(TOIE2); //enable interrupt on overflow
The ISR does the time keeping, it is the RTC: ISR(TIMER2_OVF_vect) { if ((tm.Second += 8) > 59) { tm.Second = tm.Second % 60; if (++tm.Minute > 59) { tm.Minute = 0; if(++tm.Hour > 23) { tm.Hour = 0; if(++tm.Day > (((tm.Month == 2) && isLeap(tm.Year)) ? monthDays[tm.Month] + 1 : monthDays[tm.Month]) ) { tm.Day = 1; if(++tm.Month > 12) { tm.Month = 1; ++tm.Year; } } } } } } Sleep function: void goToSleep() { byte adcsra, mcucr1, mcucr2;
//Cannot re-enter sleep mode within one TOSC cycle. This provides the needed delay. OCR2A = 0; //write to OCR2A, we're not using it, but no matter while (ASSR & _BV(OCR2AUB)) {} //wait for OCR2A to be updated
sleep_enable(); set_sleep_mode(SLEEP_MODE_PWR_SAVE); adcsra = ADCSRA; //save the ADC Control and Status Register A ADCSRA = 0; //disable ADC ATOMIC_BLOCK(ATOMIC_FORCEON) { //ATOMIC_FORCEON ensures interrupts are enabled so we can wake up again mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector mcucr2 = mcucr1 & ~_BV(BODSE); MCUCR = mcucr1; //timed sequence MCUCR = mcucr2; //BODS stays active for 3 cycles, sleep instruction must be executed while it's active } sleep_cpu(); //go to sleep //wake up here sleep_disable(); ADCSRA = adcsra; //restore ADCSRA }
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #57 on: January 31, 2013, 03:12:31 pm » |
Right, but it's still toggling an output pin, not clicking something, right?
What do you mean by "clicking something"? If the clicking thing is on the output pin, it will click, right? However I agree, if you want it to sleep, an ISR would be the way to go. However it doesn't have to do anything. Your main loop could look like: void loop () { go_to_sleep (); click_clock_hands (); }
Given that the timer wakes you up, all the ISR has to do is exist to achieve that.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 35
|
 |
« Reply #58 on: January 31, 2013, 03:47:26 pm » |
What do you mean by "clicking something"? If the clicking thing is on the output pin, it will click, right?
Sorry, I'm not being clear. I'm working with a tweaked version of Daniel Andrade's binary clock ( direct link to code), so the "click" I was talking about was setting the seconds (minutes, half-seconds, eight-seconds, whatever) counter ahead by one. Not a physical click like the second hand on a watch. Basically, the code keeps track of hours and minutes (and seconds, though it doesn't display them). Then it strips HH:MM into H, H, M, M and figures out which LEDs to turn on based on that. So the chip needs to be awake to set/check the seconds and change the LEDs, and also if the hour/minute-set buttons are pushed (which I'll use an external interrupt for). void loop () { go_to_sleep (); click_clock_hands (); }
If I'm reading this right, that makes the chip 1) go to sleep until the interrupt fires, 2) tick the time, and 3) repeat, yes?
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13896
Lua rocks!
|
 |
« Reply #59 on: January 31, 2013, 04:20:07 pm » |
Basically, the code keeps track of hours and minutes (and seconds, though it doesn't display them). Then it strips HH:MM into H, H, M, M and figures out which LEDs to turn on based on that. So the chip needs to be awake to set/check the seconds and change the LEDs, and also if the hour/minute-set buttons are pushed (which I'll use an external interrupt for). That's all achievable, although if you have glowing LEDs there isn't that much point going to sleep to save power. If I'm reading this right, that makes the chip 1) go to sleep until the interrupt fires, 2) tick the time, and 3) repeat, yes? That's right.
|
|
|
|
|
Logged
|
|
|
|
|
|