Created
February 5, 2020 00:13
-
-
Save nerdralph/a0d29e88d0c1664779d024c91ded373f to your computer and use it in GitHub Desktop.
MicroCore Oscillator Calibration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
ATtiny13 internal oscillator tuner | |
By Ralph Doncaster (2019) | |
Tweaked and tuned by MCUdude | |
------------------------------------------------------------------------------ | |
[ See diagram: https://github.com/MCUdude/MicroCore#minimal-setup ] | |
Tunes the internal oscillator using a software serial implementation | |
and store the calibrated value to EEPROM address 0. | |
Start off by opening the serial monitor and select the correct baud rate. | |
Make sure you're not sending any line ending characters (CR, LF). | |
Repedeatly press 'x' [send] to tune the internal oscillator. After a few | |
messages you'll eventually see readable text in the serial monitor, and a | |
new, stable OSCCAL value. Continue doing this until you'll get a message | |
saying that the value have been stored to EEPROM address 0. After this the | |
program will halt. | |
RECOMMENDED SETTINGS FOR THIS SKETCH | |
------------------------------------------------------------------------------ | |
Tools > Board : ATtiny13 | |
Tools > BOD : [Use any BOD level you like] | |
Tools > Clock : 9.6 MHz or 4.8 MHz depending on what osc. to tune | |
Tools > Timing : Micros disabled | |
SERIAL REMINDER | |
------------------------------------------------------------------------------ | |
The baud rate is IGNORED on the ATtiny13 due to using a simplified serial. | |
The actual Baud Rate used is dependant on the processor speed. | |
Note that you can specify a custom baud rate if the following ones does | |
not fit your application. | |
THESE CLOCKS USES 115200 BAUD: THIS CLOCK USES 57600 BAUD: | |
(External) 20 MHz (Internal) 4.8 MHz | |
(External) 16 MHz | |
(External) 12 MHz | |
(External) 8 MHz | |
(Internal) 9.6 MHz | |
THESE CLOCKS USES 19200 BAUD: THIS CLOCK USES 9600 BAUD: | |
(Internal) 1.2 MHz (Internal) 600 KHz | |
(External) 1 MHz | |
If you get garbage output: | |
1. Check baud rate as above | |
2. Check if you have anything else connected to TX/RX like an LED | |
3. You haven't sent enough 'x' characters yet | |
*/ | |
#include <EEPROM.h> | |
const int8_t delta_val = 5; | |
const uint8_t uart_rx_pin = 1; | |
// Converts 4-bit nibble to ascii hex | |
uint8_t nibbletohex(uint8_t value) | |
{ | |
value &= 0x0F; | |
if (value > 9) | |
value += 'A' - ':'; | |
return value + '0'; | |
} | |
// Function to run when Rx interrupt occurs | |
void rxInterrupt() | |
{ | |
// Start timer when pin transitions low | |
if ((PINB & _BV(uart_rx_pin)) == 0) | |
TCCR0B = _BV(CS00); | |
else | |
{ | |
uint8_t current = TCNT0; | |
// End of interval, reset counter | |
TCCR0B = 0; | |
TCNT0 = 0; | |
// The 'x' character begins with 3 zeros + start bit = 4 | |
uint8_t expected = (uint8_t)(4 * PUBIT_CYCLES() + 0.5); | |
int8_t delta = expected - current; | |
if (delta > delta_val) OSCCAL++; | |
if (delta < -delta_val) OSCCAL--; | |
asm("lpm"); // 3 cycle delay | |
// Print calculation | |
Serial.print(F("Delta: 0x")); | |
Serial.write(nibbletohex(delta >> 4)); | |
Serial.write(nibbletohex(delta)); | |
Serial.print(F(" New cal: 0x")); | |
Serial.write(nibbletohex(OSCCAL >> 4)); | |
Serial.write(nibbletohex(OSCCAL)); | |
Serial.write('\n'); | |
// Store new OSCCAL to EEPROM when stable | |
static uint8_t cal; | |
static uint8_t cal_counter; | |
if(cal != OSCCAL) | |
cal = OSCCAL; | |
else if(++cal_counter >= 5) | |
{ | |
EEPROM.write(0, OSCCAL); | |
Serial.print(F("New OSCCAL stored to EEPROM addr. 0\n")); | |
while(true); | |
} | |
} | |
// Clear interrupt flag in case another triggered | |
GIFR = _BV(INTF0); | |
} | |
void setup() | |
{ | |
// Note that any baud rate specified is ignored on the ATtiny13. See header above. | |
Serial.begin(); | |
// Prepare for sleep mode | |
MCUCR = _BV(SE); | |
// Enable Rx pin pullup | |
digitalWrite(uart_rx_pin,HIGH); | |
// Setup RX interrupt | |
attachInterrupt(0, rxInterrupt, CHANGE); | |
delay(1000); | |
// Print default message | |
Serial.write('x'); | |
Serial.write('\n'); | |
wait_x: | |
// Wait for tuning character to ensure not reading noise | |
// before entering tuning mode | |
uint8_t counter = 0; | |
while (PINB & _BV(uart_rx_pin)); | |
do | |
{ | |
counter++; | |
} while ((PINB & _BV(uart_rx_pin)) == 0); | |
// Low period should be 4 bit-times, margin is half that | |
uint8_t margin = (uint8_t)PUBIT_CYCLES()/8; | |
if (counter - (uint8_t)PUBIT_CYCLES() > margin) | |
goto wait_x; | |
else | |
{ | |
if ((uint8_t)PUBIT_CYCLES() - counter > margin) | |
goto wait_x; | |
} | |
delay(1); // Skip remaining bits in frame | |
// Reset counter for first interrupt | |
TCCR0B = 0; | |
TCNT0 = 0; | |
} | |
void loop() | |
{ | |
// Nothing here really... | |
asm("sleep"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment