| CODE |
LOWER = IDEALTIME - 50uS LOWER = 6103 - 50 LOWER = 6053 = $17:A5 |
| CODE |
UPPER = IDEAL + 50uS UPPER = 6103 + 50 UPPER = 6153 = $18:09 |
| CODE |
LOWER = IDEALTIME - 40uS LOWER = 6103 - 40 LOWER = 6043 = $17:AF |
| CODE |
UPPER = IDEAL + 40uS UPPER = 6103 + 40 UPPER = 6143 = $17:FF |
| CODE |
TSTOSC: DEC TMP ;SET-UP TIMERS STS OSCCAL,TMP OUT TIFR1,FF OUT TIFR2,FF W6103: SBIS TIFR2,TOV2;WAIT RJMP W6103 LDS XL,TCNT1L ;READ TIMER LDS XH,TCNT1H STS TCNT1H,ZERO STS TCNT1L,ZERO CPI XH,23 ;<===== CHECK IF HIGH BYTE IS $17 BRNE TSTOSC (INCOMPLETE AS YET) RET |
| CODE |
UPPER = IDEAL + 40uS UPPER = 6103 + 40 UPPER = 6143 = $17:FF |
| CODE |
TSTOSC: DEC TMP ;SET-UP TIMERS STS OSCCAL,TMP OUT TIFR1,FF OUT TIFR2,FF W6103: SBIS TIFR2,TOV2;WAIT RJMP W6103 LDS XL,TCNT1L ;READ TIMER LDS XH,TCNT1H STS TCNT1H,ZERO STS TCNT1L,ZERO CPI XH,23 ;<===== CHECK IF HIGH BYTE IS $17 BRNE TSTOSC CPI XL,175;<===== CHECK IF UNDER $AF BRLO TSTOSC;<=== ALSO SNEAKY TEST IF OVER $FF RET |
| CODE |
LDS TMP,OSCCAL TSTOSC: DEC TMP ;SET-UP TIMERS STS OSCCAL,TMP OUT TIFR1,FF OUT TIFR2,FF STS TCNT1H,ZERO STS TCNT1L ZERO STS TCCR1B,ONE STS TCNT2,ZERO W6103: SBIS TIFR2,1 ;WAIT RJMP W6103 LDS XL,TCNT1L;READ TIMER LDS XH,TCNT1H CPI XH,23;<=== HIGH BYTE = $17? BRNE TSTOSC CPI XL,175;<=== LOW BETWEEN $AF-$FF? BRLO TSTOSC RET |
| QUOTE ("koshchi") |
| The probability of this is, in fact, 0 (assuming that the crystal attached to the TOSC lines is really 32.768kHz and the cpu clock prescaler is set to 8 ). Consider this. 6103 comes from this: N = (F_CPU / 32.768kHz) * 200 Where the nominal cpu frequency is 1mHz, and 200 is the number of ticks of the 32.768kHz crystal that we wait for in the sample. To find the actual cpu frequency we turn it around: F_CPU = N * 32.768kHz / 200 If timer 1 overflowed then the count value would be at least 65536. Putting that into the equation we find that the actual F_CPU would have to be almost 11mHz. Since the OSCAL at maximum can achieve at most twice the nominal frequency, this will be impossible. And if you forgot to set the clock prescaler to 8 it may have gotten this high with the OSCAL at it's highest. But even with the lowest OSCAL, it would still result in a count of at least 32768, so it would not be possible to adjust it to within range. So if timer1 overflows, this is necessarily an error condition. Both your routine and Atmel's original code are not fault tolerant. If any condition occurs that doesn't allow the count to get into range, it will loop forever (which in real time is a significant amount ). You've got to be careful about the acceptable range you use. If you get too small, you might not be able to get within that range. Calculating it I find that an adjustment of 1 to OSCAL results in a change of about 18 in the count. So your range of 80 should work without a problem, but making it any narrower or lowering the number of ticks per sample could be dangerous. |
| QUOTE ("koshchi") |
| If the OSCAL setting is within range on the first pass, then the wait time is virtually zero. But if the setting is just out of range on the low side, the delay will be 1.5 secs. Whether or not this is acceptable would depend on the circumstances. If the calibration is being done upon waking from sleep in order to service a signal on the UART, this could be entirely unacceptable. Your suggestion of adjusting according to the size of the error is a good one for this type of thing. You could also use successive approximation. |
| QUOTE ("koshchi") |
| This is not a good thing since if the OSCAL happens to be set so that it is within range in the first couple of passes, then the oscillator will drift out of acceptable range after the routine is run. You must let the oscillator settle before you run the calibration routine. |
| QUOTE ("Koshchi") |
| Since this particular routine is done shortly after power-up (or reset) the longer it takes to adjust the OSCCAL register, the better, because it gives the oscillator more time to "settle." This is not a good thing since if the OSCAL happens to be set so that it is within range in the first couple of passes, then the oscillator will drift out of acceptable range after the routine is run. You must let the oscillator settle before you run the calibration routine. |
| QUOTE ("koshchi") |
| I got to thinking about successive approximation. Here's the algorythm: Code: Mask = 0b10000000; // start with the highest bit Value = Mask; for(8 times) // for 8 bits of accuracy { OSCAL = Value; Wait for timeout; if (CountIsTooLow) { //OSCCAL needs to be lower, so remove the bit Value = Value - Mask; } Mask >>= 1; //shift the mask one bit Value = Value | Mask; //add the bit into the value } This is pretty simple. You only need to compare the actual count with the desired count. No need to test against a range since we are guaranteed to be accurate to the nearest value. This gets the count to within around 20, which is even better than Dan's routine. It is also much quicker. The average time for Dan's is around 0.75 sec. For this it is about 0.05 sec. Furthermore, the time is constant (within microseconds). It also has the advantage that it is more fault tolerant. The only way it could get stuck is if there is no crystal at all on the TOSC pins. Using Dan's routine as a base here is my solution (CAVEAT: I have not had the time to check it yet, but I believe it is correct): Code: CLR TMP LDI MASK, 0X40 TSTOSC: OR TMP, MASK STS OSCCAL, TMP OUT TIFR1, FF OUT TIFR2, FF STS TCNT1H, ZERO STS TCNT1L, ZERO STS TCCR1B, ONE STS TCNT2, ZERO W6103: SBIS TIFR2,1 ;WAIT RJMP W6103 LDS XL, TCNT1L LDS XH, TCNT1H CPI XH, 26 BRLT NEXT SUB TMP, MASK NEXT: LSR MASK BRCC TSTOSC RET You'll notice that I changed the check of the high byte of TCNT1 to 26. What I did was increase the ticks per sample to 218 instead of 200. This makes the target count 6652, which is just 4 short of 0x1a00 (6656), which is close enough as to not matter. This eliminates the need for checking the low byte. Also I am ending the loop by seeing if the bit has been shifted into the carry, eliminating the need for a counter. This routine is only one line longer than Dan's in the loop. Edit: Corrected code Changed BRGE NEXT to BRLT NEXT. Changed LDI MASK, 0x80 to LDI MASK, 0x40 |
| CODE |
;------------------------------------; ; RETRO DAN's 15 LINE OSCCAL ROUTINE; ;------------------------------------; TSTOSC: DEC TMP ;ADJUST OSCAL STS OSCCAL,TMP OUT TIFR1,FF ;RESET OUT TIFR2,FF ;OSC COUNTER STS TCNT1H,ZERO;START IT STS TCNT1L,ZERO;FROM ZERO STS TCCR1B,ONE ;READY...GO! STS TCNT2,ZERO;CLEAR TIMER W6103: SBIS TIFR2,1 ;CHECK TIMER RJMP W6103 ;6103uS PASSED? LDS XL,TCNT1L ;READ COUNTER LDS XH,TCNT1H ;CHECK ACCURACY CPI XH,23 ;CHECK HIGH BYTE BRNE TSTOSC ;OUT-OF-RANGE RET |
| QUOTE ("koshchi") |
And throw your accuracy out the window. With successive approximation, you are guaranteed to get better accuracy with every bit, which is why I always do all 8 iterations and don't try to bail early. My routine will always result in a count within about 20 of 6656, making the accuracy better than 0.3%. Your routine needs a range. With your routine only checking the high byte, your range is now 5888 to 6143. At 5888 you are now 215 off of the target count of 6103. That make the clock 3.5 % high. |
| CODE |
;------------------------------------------; ; CALIBRATE INTERNAL RC OSCILLATOR: OSCCAL; ;------------------------------------------; RETRO_CAL: STS CLKPR,V128 ;DROP CLOCK TO 1Mhz STS CLKPR,THREE; STS TIMSK1,ZERO;SETUP TIMER1 STS TCCR1B,ONE ;TC1 = 1MHz = 1uS STS TIMSK2,ZERO;SETUP TIMER2 STS ASSR,EIGHT ;CLOCK FROM 32KHz OSCILLATOR LDI TMP1,200 ;OCR2A = 200 STS OCR2A,TMP1 ;200 / 32768Hz ~= 6103uS STS TCCR2A,ONE ;TC2 = 32768Hz ~= 30.5176uS LDS TMP,OSCCAL ;READ OSCCAL TSTOSC: STS OSCCAL,TMP ;WRITE OSCCAL DEC TMP ;ADJUST OSCCAL OUT TIFR2,FF ;RESET FLAG STS TCNT1H,ZERO;STARTEM @ZERO STS TCNT1L,ZERO; STS TCNT2,ZERO ; STS TCCR1B,ONE ;READY...GO! W6103: SBIS TIFR2,1 ;CHECK TIMER RJMP W6103 ;6103uS PASSED? LDS XL,TCNT1L ;READ COUNTER LDS XH,TCNT1H ; CPI XH,23 ;CHECK HIGH BYTE BRNE TSTOSC ;WAY OUT-OF-RANGE RET ;ITS MILLER TIME! |
| CODE |
CPI XL,175 ;CHECK LOW BYTE BRLO TSTOSC ;OUT-OF-RANGE |
| QUOTE ("koshchi") |
| I have corrected an error in my successive approximation code above. The line: BRGE NEXT has been changed to: BRLT NEXT Also, in looking at the datasheet, I noticed that OSCCAL is only 7 bits, not 8, so I could change my initial mask value to 0x40 instead of 0x80. A couple things I noticed in testing the code. First, it is possible that the second to last approximation could actually be closer to the target value than the last approximation, so the value could be one off. However the maximum error will still be about 1.1% Second, the count value between two successive OSCCAL values is much greater than I had expected. For my routine (with 218 ticks) is about 80. For 200 ticks it's about 75. So Dan, narrowing your range to 80 is getting pretty dangerous. Both of these things affect my accuracy. It is about four times what I had calculated. I'm still thinking about how to get my extra bit back. If I can I'll get half my accuracy back. One more thing, and this applies to both our routines. The line: STS TCCR1B,ONE to enable timer1 doesn't need to be there in the loop since we are never disabling it. This line can be moved to before the loop. |
| QUOTE ("C_oflynn") |
| Hey, Wow - that's an in depth tutorial! You weren't kidding. . . Again thanks for your tutorial, it is a great asset!. . . Warm Regards, -Colin O'Flynn |
| QUOTE ("koshchi") |
| I was looking at the C code and noticed a couple of things. After setting the registers for timer2, the code waits for the update of the registers to be complete (with timer2 in asynchronous mode the update is not immediate). However when timer2 is cleared within the loop, this wait is not done. I wondered if this would make a difference in the count, so I tested it. I am getting values that are consistently lower by about 32 when I wait for the update busy flag to clear than when I don't. (By the way, I moved the clearing of timer2 to just above the clearing of timer1 so that timer1 is not busy counting while I'm waiting for timer2.) I also noticed that the C code is centering on a count value of 6185 instead of 6103. This may have been done to compensate for the low readings that I observed. They may have even used an oscilloscope to calibrate the calibration routine. I don't have a scope. Maybe someone with a scope can check and see what actual frequency we are getting with our routines. |