ATtiny2313: Timer1 CTC, PB4 OC1B sprzętowy przebieg 128s, kwarc 1MHz lub 32,768kHz, kod
Pytanie
Jak wygląda kod na Attiny 2313 według tych wskazówek na pinie PB4 i PB5 , o okresie 128 sekund ,64 sekund stan niski , i 64 sekund stan wysoki , jaki kwarc dać najlepiej.
Wskazówka :
W Generatorze DDS z EP 6/2018 jest sprzętowe sterowanie wyjściem:
void Timer1_Start(uint8_t dzielnik)
{
// OCR1B=dzielnik;
OCR1B=dzielnik;
//Output compare toggles OC1A pin
// TCCR1A |= 0x40; // COM1A = 1
TCCR1A |= 0x10; // COM1B = 1
//start timer without prescaler
TCCR1B = 0b00001001; // WGM 12=1, CS=1
DDRD |= _BV(PD4);
}
Uzyskasz DOKŁADNIE 50% bez liczenia cykli. Nie muszą być włączone przerwania ale mogą. Program główny może robić co chce a nie wpłynie to na generowany przebieg bez względu na to, czy przerwania będą włączone czy nie. Max częstotliwość to Fclk/2, uzyskaj taką przez delay. Taki program jest krótszy niż z delay. Jeśli częstotliwość jest zbyt duża należy timery połączyć w kaskadę.
Można także użyć timera w trybie PWM. Używając OCRxA można ustawić próg zliczania a co za tym idzie, precyzyjnie regulować częstotliwość.
Głupotą jest, mając możliwości sprzętowe, rozwiązywać problem w sposób programowy.
Odpowiedź Sztucznej Inteligencji
Bezpośrednia odpowiedź na pytanie
- Najprościej i „sprzętowo” uzyskasz 128 s (64 s niski + 64 s wysoki) wykorzystując Timer1 w trybie CTC i sprzętowe przełączanie wyjścia (toggle) na pinie OC1B.
- Na ATtiny2313 pin PB4 = OC1B (sprzętowe wyjście timera). Pin PB5 nie jest wyjściem OC1x, więc identyczny sygnał na PB5 uzyskasz wygodnie przez jednorazowe przerwanie co 64 s (zerowe obciążenie CPU).
- Jaki kwarc? Dwie praktyczne opcje:
- 1,000 MHz (albo wewnętrzny 8 MHz z CKDIV8 → 1 MHz): daje idealnie całkowitą wartość OCR1A = 62499 (preskaler 1024), więc 64,000 s bez błędu zaokrągleń.
- 32,768 kHz (zegarkowy): najlepszy dla stabilności długoterminowej; z preskalerem 1024 wystarczy OCR1A = 2047.
Szczegółowa analiza problemu
-
Cel: T = 128 s, wypełnienie 50% → półokres = 64 s.
-
Tryb: CTC (WGM12=1) z „toggle on compare match” (COM1Bx0=1) daje idealne 50% bez liczenia cykli i bez jitteru na kanale sprzętowym.
-
Wzór: t_half = (OCR1A + 1) · preskaler / Fclk.
-
Warianty:
- Fclk = 1 MHz, preskaler = 1024:
- (OCR1A + 1) = 64 · 1 000 000 / 1024 = 62500 → OCR1A = 62499 (dokładnie 64,000 s).
- Ustaw OCR1B = OCR1A, aby sprzętowo przełączać PB4 (OC1B) w tym samym momencie „szczytu”.
- PB5: jedyne przerwanie COMPA co 64 s do „zlustrowania” stanu (praktycznie bezkosztowe).
- Fclk = 32,768 kHz, preskaler = 1024:
- Ftimer = 32 Hz; 64 s · 32 Hz = 2048 → OCR1A = 2047.
- Identycznie: COM1B0=1 dla PB4, a PB5 przełączany w ISR od COMPA co 64 s.
-
Mapowanie pinów (istotne): w ATtiny2313 sprzętowe wyjścia Timer1 to OC1A oraz OC1B. OC1B jest na PB4. PB5 nie jest linią OC1x, więc nie da się go przełączać w 100% sprzętowo Timerem1 bez ISR albo logiki zewnętrznej. Jeśli możesz zmienić wymagania pinów, najczyściej jest użyć PB3 (OC1A) + PB4 (OC1B) i wtedy oba kanały będą w pełni sprzętowe.
Aktualne informacje i trendy
- Do odmierzania długich odcinków czasu w prostych AVR nadal często wybiera się:
- niską Fclk (np. 32,768 kHz) dla stabilności temperaturowej,
- albo „całkowite” rozwiązanie przy 1 MHz z preskalerem = 1024 dla prostych, dokładnych nastaw bez błędu dzielenia.
- Dla najwyższej precyzji czasowej w aplikacjach długookresowych coraz częściej stosuje się zewnętrzne RTC (np. TCXO/DS3231) jako wzorzec czasu.
Wspierające wyjaśnienia i detale
- Dlaczego OCR1B = OCR1A? W trybie CTC „TOP” to OCR1A. Gdy TCNT1 == OCR1A, następuje Compare Match A oraz reset licznika. Jeśli jednocześnie OCR1B ma taką samą wartość, uzyskasz Compare Match B w tym samym takcie i sprzętowy „toggle” na OC1B (PB4) dokładnie co 64 s.
- Dlaczego PB5 w ISR? Bo nie ma tam sprzętowego powiązania z Timer1. ISR wywoływane raz na 64 s ma znikome obciążenie i nie wprowadza mierzalnych zakłóceń.
Aspekty etyczne i prawne
- Brak szczególnych implikacji. Upewnij się, że konfiguracja fuse nie zablokuje możliwości programowania układu (ostrożnie z RSTDISBL).
Praktyczne wskazówki
- Jeśli nie potrzebujesz dokładności „zegarkowej”, użyj wewnętrznego 8 MHz z CKDIV8 (1 MHz) – prostsze uruchomienie, zero elementów zewnętrznych.
- Jeśli ważna jest stabilność i niewielki dryft, użyj kwarcu 32,768 kHz i ustaw odpowiednie fuse dla oscylatora niskoczęstotliwościowego.
- Dodaj kondensatory przy kwarcu wg noty katalogowej.
- Test: podepnij LED + 1 kΩ do PB4/PB5 i obserwuj zmianę co ~64 s.
Ewentualne zastrzeżenia lub uwagi dodatkowe
- Jeśli w kodzie głównym wyłączysz globalne przerwania na dłużej niż kilkadziesiąt ms akurat w chwili Compare Match, zbocze na PB5 może się opóźnić – PB4 (sprzętowe) pozostanie idealne.
- Pełne sprzętowe wyjście na dwóch pinach bez ISR wymagałoby użycia dwóch linii OC (np. PB3+PB4) lub dodatkowej logiki zewnętrznej.
Sugestie dalszych badań
- Zastosowanie zewnętrznego RTC jako wzorca i synchronizacja timera.
- Analiza błędu temperaturowego dla wewnętrznego RC vs kwarc 32,768 kHz.
- Minimalizacja poboru prądu (tryby uśpienia z aktywnym Timer1).
Kod – wariant 1 (Fclk = 1 MHz, preskaler 1024; PB4 sprzętowo, PB5 w ISR)
#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
static inline void timer1_init_64s(void)
{
// PB4 (OC1B) i PB5 jako wyjścia
DDRB |= _BV(PB4) | _BV(PB5);
// Timer1: CTC, TOP=OCR1A, preskaler=1024
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
// 64 s = (OCR1A+1)*1024 / 1e6 => OCR1A = 62499
OCR1A = 62499;
OCR1B = 62499; // toggle OC1B dokładnie przy TOP
// Toggle na OC1B (PB4) przy Compare Match B
TCCR1A = (1<<COM1B0); // COM1B1=0, COM1B0=1 (toggle)
// CTC + preskaler 1024
TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10);
// Jedno przerwanie co 64 s – do lustrzanego przełączania PB5
TIMSK |= (1<<OCIE1A);
sei();
}
ISR(TIMER1_COMPA_vect)
{
// Lustrzane przełączenie PB5 co 64 s
PORTB ^= _BV(PB5);
}
int main(void)
{
timer1_init_64s();
for (;;)
{
// pętla główna może robić cokolwiek; PB4 generuje się sprzętowo
}
}
Kod – wariant 2 (Fclk = 32,768 kHz, preskaler 1024; PB4 sprzętowo, PB5 w ISR)
#define F_CPU 32768UL
#include <avr/io.h>
#include <avr/interrupt.h>
static inline void timer1_init_64s_32k(void)
{
DDRB |= _BV(PB4) | _BV(PB5);
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
// Ftimer = 32768/1024 = 32 Hz; 64 s * 32 = 2048 => OCR1A = 2047
OCR1A = 2047;
OCR1B = 2047;
TCCR1A = (1<<COM1B0); // toggle OC1B
TCCR1B = (1<<WGM12) | (1<<CS12) | (1<<CS10); // CTC, /1024
TIMSK |= (1<<OCIE1A); // jednorazowe ISR co 64 s dla PB5
sei();
}
ISR(TIMER1_COMPA_vect)
{
PORTB ^= _BV(PB5);
}
int main(void)
{
timer1_init_64s_32k();
for(;;) { }
}
Uwaga do fuse:
- Dla 1 MHz z wewnętrznego RC: włącz CKDIV8 lub ustaw preskaler systemowy tak, by Fclk=1 MHz.
- Dla 32,768 kHz: ustaw tryb oscylatora niskoczęstotliwościowego zgodnie z dokumentacją ATtiny2313 (czas startu i CKSEL).
Krótkie podsumowanie
- PB4 (OC1B) można przełączać w 100% sprzętowo co 64 s wykorzystując Timer1 w trybie CTC i „toggle on compare”.
- PB5 nie jest wyjściem OC1x w ATtiny2313 – najpraktyczniej zlustrować stan PB4 w ISR od COMPA wywoływanym co 64 s (brak wpływu na dokładność PB4, minimalne obciążenie).
- Kwarc: 1 MHz (idealne „całkowite” wyliczenie) lub 32,768 kHz (najlepsza długoterminowa stabilność). Jeśli możesz zmienić piny, użyj PB3 (OC1A) + PB4 (OC1B), wtedy oba kanały będą w 100% sprzętowe bez ISR.
Zastrzeżenie: Odpowiedzi udzielone przez sztuczną inteligencję (model językowy) mogą być niedokładne i wprowadzające w błąd. Elektroda nie ponosi odpowiedzialności za dokładność, rzetelność ani kompletność prezentowanych informacji. Wszystkie odpowiedzi powinny być zweryfikowane przez użytkownika.
Zadaj dodatkowe pytanie Sztucznej Inteligencji