// learning timer1 on atmega 328 // pins for timers //const byte poc2B = 3; // pd3 // reserved Tim2 const byte pcin1 = 5; // pd5 Input Count (for wgm 14,15) const byte picp1 = 8; // pb0 Input Capt const byte poc1A = 9; // pb1 const byte poc1B = 10; // pb2 //const byte poc2A = 11; // pb3 // reserved Tim2 // pins for debug const byte povf = 12; //pb4 const byte dovfS = B00010000; // to set/reset pin fast const byte pcap = 13; //pb5 const byte dcapS = B00100000; // counts for ints, just used to see if called byte volatile cntO; // overflow byte volatile cntA; // byte volatile cntB; // byte volatile cntC; // capture // Timer properties to change byte tim1cs = 3; // c clock select to TCCRB byte tim1cmoA = 1; // d compare Match Output A 0..3 to TCCRA byte tim1cmoB = 1; // e compare Match Output B 0..3 to TCCRA byte tim1ocie = 32 + 5; // f interrupt enable to TIMSK1 byte tim1wgm = 0; // w to TCCRA and TCCRB byte tim1ica = 2; // m ICNC1 and ICES1 0..3 to TCCRB const uint16_t tim1presc[6] = {0, 1, 8, 64, 256, 1024}; bool volatile autoOn = false; // y to switch uint16_t volatile ovcnt; bool volatile ferdisch = false; // measure complete // ringbuffer for captured values typedef union { uint32_t za32; uint16_t za16[2]; } zahl_t; const byte ripuM = 16; zahl_t ripu[ripuM]; byte volatile ripuP = 0; // pointer, also used to stop recording when >=ripuM float uspt = 0.06255; // usec per tick depending on quartz. must start with 0.0 use u to set inp * 0.0001 // handle numeric inputs uint16_t inp; bool inpAkt; unsigned long tickMs; // Memory saving helpers void prnt(PGM_P p) { // flash to serial until \0 while (1) { char c = pgm_read_byte(p++); if (c == 0) break; Serial.write(c); } Serial.write(' '); } void msgF(const __FlashStringHelper *ifsh, int16_t n) { PGM_P p = reinterpret_cast(ifsh); prnt(p); Serial.println(n); } void timer1Init() { TIMSK1 = 0; // no timer ints during init TCNT1 = 0; ovcnt = 0; // 7 6 5 4 3 2 1 0 // COMA1 COMA0 COMB1 COMB0 – – WGM11 WGM10 TCCR1A // cmoA cmoB 0 0 w w TCCR1A = (tim1cmoA << 6) | (tim1cmoB << 4) | (tim1wgm & 3); // ICN1 ICES1 – WGM13 WGM12 CS22 CS21 CS20 TCCR1B // i i 0 w w c c c TCCR1B = (tim1ica << 6) | tim1cs | ((tim1wgm << 1) & 0x18); // OCIE1B OCIE1A TOIE1 TIMSK1 // 0 0 0 0 0 o o o TIMSK1 = tim1ocie; // Timer interrupts //msgF(F("init tim1ocie"), tim1ocie); } ISR(TIMER1_OVF_vect) { PORTB |= dovfS; //debug cntO++; ovcnt++; PORTB &= !dovfS; //debug } ISR(TIMER1_COMPA_vect) { cntA++; } ISR(TIMER1_COMPB_vect) { cntB++; } ISR(TIMER1_CAPT_vect) { if (ripuP >= ripuM) return; PORTB |= dcapS; //debug ripu[ripuP].za16[0] = ICR1; //if ( (TIFR1 & 1 << TOV1)) { //TODO if pending timer verflow // ovcnt++; //} ripu[ripuP].za16[1] = ovcnt; ripuP++; if (ripuP >= ripuM) { if (autoOn) { // no more capture ints ferdisch = true; return; } ripuP = 0; } cntC++; PORTB &= !dcapS; //debug } float us2cs() { // returns duration of one tick depending on clock source if (tim1cs < 6) { return uspt * tim1presc[tim1cs]; } return 0.0; } void showCnts() { char str[80]; sprintf(str, "O:%4u A:%4u B:%4u C:%4u ", cntO, cntA, cntB, cntC); Serial.println(str); } void showRegs() { char str[80]; sprintf(str, "OCR1A:%8u OCR1B:%8u TCRA:%4u TCRB: %4u TIMSK1: %4u", OCR1A, OCR1B, TCCR1A, TCCR1B, TIMSK1); Serial.println(str); // sprintf(str, "OCR1AL:%4u OCR1AH:%4u OCR1BL:%4u OCR1BH:%4u TCNT1L: %4u TCNT1H: %4u", OCR1AL, OCR1AH, OCR1BL, OCR1BH, TCNT1L, TCNT1H); // Serial.println(str); msgF(F("_a OCR1A"), OCR1A); msgF(F("_b OCR1B"), OCR1B); msgF(F("_c cs "), tim1cs); msgF(F("_d cmoA "), tim1cmoA); msgF(F("_e cmoB "), tim1cmoB); msgF(F("_f ocie "), tim1ocie); msgF(F("_m ica "), tim1ica); msgF(F("_w wgm "), tim1wgm); } void explainRegs() { char str[80]; byte t, wgm, comA, comB, cs, ica; t = TCCR1A; wgm = t & 3; comA = t >> 6; comB = (t >> 4) & 3; t = TCCR1B; ica = t >> 6; cs = t & 7; t = t >> 1; wgm |= t & 12; sprintf(str, "wgm %3u, cs %3u, comA %2u, comB %2u, ica %2u", wgm, cs, comA, comB, ica); Serial.println(str); } void showRipu() { char str[120]; long delt, avg; long dif; float zwi = us2cs(); float dauer; // average is avg = ripu[ripuM - 1].za32 - ripu[2].za32; avg = avg / (ripuM - 3); sprintf(str, " P %2u auto %1u avg %10ld uspt ", ripuP, autoOn, avg); Serial.print(str); Serial.print(uspt, 6); //sprintf doesn't like floats Serial.print(" clo "); Serial.print(zwi, 6); dauer = zwi * avg; dauer = 1000000.0 / dauer; // freq Serial.print(" "); Serial.println(dauer, 3); Serial.println(F(" # Hi Lo long wert abw dur (us) freq")); for (byte i = 0; i < ripuM; i++ ) { if (i == 0) { delt = 0; dif = 0; } else { delt = ripu[i].za32 - ripu[i - 1].za32; dif = delt - avg; } // sprintf(str, "%2u %4u %6u %10lu %10ld %+7ld ", i, ripu[i].za16[1], ripu[i].za16[0], ripu[i].za32, delt, dif); if (i == 0) { Serial.println(str); } else { Serial.print(str); dauer = zwi * delt; Serial.print(dauer, 3); dauer = 1000000.0 / dauer; // freq Serial.print(" "); Serial.println(dauer, 3); } } } void help () { Serial.println (F("_a set OCR1A to change freq")); Serial.println (F("_b set OCR1B (= '0') && (tmp <= '9')) { if (inpAkt) { inp = inp * 10 + (tmp - '0'); } else { inpAkt = true; inp = tmp - '0'; } // Serial.print("\b\b\b\b"); // Serial.print(inp); return true; } //digit inpAkt = false; return false; } void doCmd(char x) { uint16_t tmpu; Serial.print(char(x)); if (doNum(x)) { return; } Serial.println(); switch (x) { case 13: //vt100Clrscr(); Serial.print("\x1B[H"); Serial.print("\x1B[J"); break; case ' ': showCnts(); break; case 'a': OCR1A = inp; tmpu = OCR1A; Serial.println(tmpu); msgF(F(":OCR1A"), OCR1A); break; case 'b': OCR1B = inp; msgF(F(":OCR1B"), OCR1B); break; case 'c': tim1cs = inp & 7; timer1Init(); msgF(F(":tim1cs"), tim1cs); break; case 'd': tim1cmoA = inp & 3; timer1Init(); msgF(F(":tim1cmoA"), tim1cmoA); break; case 'e': tim1cmoB = inp & 3; timer1Init(); msgF(F(":tim1cmoB"), tim1cmoB); break; case 'f': tim1ocie = inp ; timer1Init(); msgF(F(":tim1ocie"), tim1ocie); break; case 'h': help(); break; case 'i': timer1Init(); explainRegs(); msgF(F("TCCR1B"), TCCR1B); msgF(F("TIMSK1"), TIMSK1); break; case 'j': explainRegs(); showRegs(); break; case 'm': tim1ica = inp & 3; timer1Init(); msgF(F(":tim1ica"), tim1ica); break; case 'o': TCCR1C = 128 + 64; msgF(F("FOC"), TCCR1C); break; case 'p': pinMode(poc1A, OUTPUT); pinMode(poc1B, OUTPUT); msgF(F("Pinmodes"), 0); break; case 'r': digitalWrite(poc1A, LOW); digitalWrite(poc1B, LOW); msgF(F("Set to "), 0); break; case 's': digitalWrite(poc1A, HIGH); digitalWrite(poc1B, HIGH); msgF(F("Set to"), 1); break; case 't': // tickMs = inp; msgF(F("tickMs"), inp); break; case 'u': // msgF(F(" to uspt"), inp); uspt = 0.00001 * inp; Serial.println(uspt, 5); break; case 'v': break; case 'w': tim1wgm = inp & 15; timer1Init(); msgF(F("wgm"), tim1wgm); break; case 'x': //start in auto ovcnt = 0; // to get convenient values ripuP = 0; break; case 'y': autoOn = !autoOn; msgF(F("Auto"), autoOn); break; case 'z': showRipu(); break; default: Serial.print('?'); Serial.println(int(x)); } // case } void setup() { const char info[] = "timer1 " __DATE__ " " __TIME__; Serial.begin(38400); Serial.println(info); pinMode(picp1, INPUT_PULLUP); pinMode(pcin1, INPUT_PULLUP); pinMode(poc1A, OUTPUT); pinMode(poc1B, OUTPUT); pinMode(povf, OUTPUT); pinMode(pcap, OUTPUT); OCR1A = 100; OCR1B = 60; timer1Init(); tickMs = 1000; } void loop() { if (Serial.available() > 0) { doCmd( Serial.read()); } if (ferdisch) { ferdisch = false; showRipu(); } }