Jak wygląda kod na Attiny 2313A , sprzętowy , PWM , zmiana polaryzacji LOW 64 sekund , HIGH 64 sekund , okres 128 sekund , na pinach PB3 i PB4 , z kwarcem zegarkowym 32768 kHz,Wedłu poniższych wskazówek : " Cały czas mowa o precyzyjnych częstotliwościach, współczynniku wypełnienia a uczepiłaś się najgorszego z możliwych rozwiązań mając sprzętowe sterowanie GPIO, gdzie trzeba liczyć każdy cykl maszynowy jak w latach 80.
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
DDRB |= _BV(PB4);
}
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."
Tak, da się to zrobić sprzętowo na Timer1, bez delay() i bez liczenia cykli w pętli.
Natomiast trzeba od razu skorygować dwie istotne kwestie:
Jeżeli więc masz zewnętrzny sygnał zegarowy 32768 Hz, to najlepsze rozwiązanie to Timer1 w trybie CTC z przełączaniem OC1A i OC1B.
Da to dokładnie:
Piny są poprawne:
W Twoim przypadku nie potrzebujesz „PWM” w sensie regulowanego wypełnienia, tylko przebiegu prostokątnego 50% o bardzo długim okresie.
Najlepszy tryb sprzętowy to:
W trybie nie-PWM bity COM1A1:0 i COM1B1:0 = 01 powodują automatyczne przełączanie stanu wyjścia przy porównaniu. To dokładnie to, czego potrzebujesz. (ww1.microchip.com)
Dla CTC obowiązuje zależność częstotliwości wyjściowej:
\[
f{OC1A} = \frac{f{clk_I/O}}{2 \cdot N \cdot (1 + OCR1A)}
\]
gdzie:
Chcesz:
\[
T = 128\ \text{s}
\]
czyli:
\[
f = \frac{1}{128}\ \text{Hz}
\]
Przy zegarze:
\[
f_{clk} = 32768\ \text{Hz}
\]
wygodnie dobrać preskaler:
\[
N = 64
\]
Wtedy:
\[
1 + OCR1A = \frac{32768}{2 \cdot 64 \cdot (1/128)} = 32768
\]
czyli:
\[
OCR1A = 32767
\]
To oznacza:
To jest dokładne, bez błędu kwantyzacji. (ww1.microchip.com)
Część przykładowych odpowiedzi jest technicznie nietrafiona w dwóch miejscach:
ATtiny2313A nie ma dedykowanego oscylatora „watch crystal” 32,768 kHz jako źródła system clock, a tabelka źródeł zegara przewiduje:
Zakres dla oscylatora kwarcowego zaczyna się od 0,4 MHz, więc 32,768 kHz jako sam kwarc jest poza zakresem pracy tego oscylatora. (ww1.microchip.com)
Dla Timer1 w Fast PWM ustawienie COM1x=01 nie działa symetrycznie dla obu kanałów. W szczególności dla trybów z WGM13=1:
Czyli jeśli chcesz sprzętowo sterować jednocześnie PB3 i PB4, to w tym zadaniu poprawny jest tryb nie-PWM/CTC, a nie Fast PWM.
Poniższy kod zakłada, że mikrokontroler jest taktowany z zewnętrznego sygnału 32768 Hz podanego na XTAL1
(nie z gołego rezonatora zegarkowego).
Wersja poniżej generuje przebiegi komplementarne:
#include <avr/io.h>
#define F_CPU 32768UL
static void timer1_init_64s_complementary(void)
{
// Zatrzymaj Timer1
TCCR1A = 0;
TCCR1B = 0;
TCCR1C = 0;
TCNT1 = 0;
// 64 s na półokres przy F_CPU=32768 Hz i preskalerze 64:
// f_timer = 32768 / 64 = 512 Hz
// 64 s * 512 = 32768 taktów
// OCR = 32768 - 1 = 32767
OCR1A = 32767;
OCR1B = 32767;
/*
* Inicjalizacja stanów początkowych sprzętowo:
* OC1A = 1
* OC1B = 0
*
* Robimy to w trybie non-PWM przez Force Output Compare.
*/
TCCR1A = (1 << COM1A1) | (1 << COM1A0) | // OC1A: SET on compare
(1 << COM1B1); // OC1B: CLEAR on compare
TCCR1C = (1 << FOC1A) | (1 << FOC1B); // wymuszenie porównania
/*
* Właściwa praca:
* CTC, oba kanały przełączane (toggle) przy compare match
*/
TCCR1A = (1 << COM1A0) | (1 << COM1B0);
// Najpierw ustaw OC1x, potem dopiero włącz wyjścia na pinach
DDRB |= (1 << PB3) | (1 << PB4);
// CTC (WGM12=1), preskaler = 64 (CS11=1, CS10=1)
TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
}
int main(void)
{
timer1_init_64s_complementary();
while (1)
{
// CPU może robić cokolwiek; przebieg jest generowany sprzętowo.
// Można tu też wejść w tryb IDLE, jeśli chcesz oszczędzać energię.
}
}
To rozwiązanie wykorzystuje:
PB3 = OC1APB4 = OC1BFOC1A/FOC1B do nadania stanów startowych,CTC + toggle do dalszej pracy całkowicie sprzętowej. (ww1.microchip.com)Jeżeli przez „LOW 64 s, HIGH 64 s na pinach PB3 i PB4” rozumiesz:
to nie trzeba ustawiać stanów przeciwnych na starcie. Wystarczy zainicjalizować oba tak samo, np. oba LOW:
#include <avr/io.h>
#define F_CPU 32768UL
static void timer1_init_64s_inphase(void)
{
TCCR1A = 0;
TCCR1B = 0;
TCCR1C = 0;
TCNT1 = 0;
OCR1A = 32767;
OCR1B = 32767;
// Start od LOW na obu kanałach
TCCR1A = (1 << COM1A1) | (1 << COM1B1); // CLEAR on compare
TCCR1C = (1 << FOC1A) | (1 << FOC1B);
// Potem oba toggle
TCCR1A = (1 << COM1A0) | (1 << COM1B0);
DDRB |= (1 << PB3) | (1 << PB4);
TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);
}
int main(void)
{
timer1_init_64s_inphase();
while (1) { }
}
delay()To jest dokładnie zgodne z cytowaną wskazówką: mając sprzętowe sterowanie wyjściem, nie ma sensu robić tego programowo.
Zalety:
W praktyce projektowej dla bardzo wolnych przebiegów prostokątnych na AVR stosuje się dziś najczęściej trzy podejścia:
Właśnie dlatego w tym konkretnym przypadku najważniejsza jest obserwacja, że ATtiny2313A nie ma właściwej infrastruktury do bezpośredniego „watch crystal” jako typowego źródła czasu RTC, więc jeśli koniecznie chcesz użyć samego rezonatora 32,768 kHz, to lepszy będzie inny mikrokontroler z timerem asynchronicznym/RTC. Dla samego zewnętrznego zegara 32768 Hz ATtiny2313A nadal nadaje się bardzo dobrze. (ww1.microchip.com)
W ATtiny2313A:
PB3 pełni funkcję OC1A,PB4 pełni funkcję OC1B. (ww1.microchip.com)TCCR1A — wybór zachowania wyjść OC1A/OC1B,TCCR1B — wybór trybu pracy i preskalera,TCCR1C — FOC1A/FOC1B, czyli wymuszenie zdarzenia compare w trybach non-PWM,OCR1A, OCR1B — progi porównania,DDRB — włączenie fizycznych driverów na PB3/PB4. (ww1.microchip.com)FOC1A/FOC1BTo jest elegancki sposób na ustawienie stanu początkowego kanałów sprzętowych bez ręcznego „mieszania” PORTB i bez ryzyka, że po przejęciu pinu przez blok OC stan będzie inny niż oczekiwany. Nota katalogowa wprost wskazuje, że tak można inicjalizować stan wyjść OC w trybach non-PWM. (ww1.microchip.com)
W tym zadaniu aspekt prawny jest marginalny, ale są istotne kwestie bezpieczeństwa technicznego:
Sam fakt, że sygnał jest generowany sprzętowo, nie zwalnia z analizy stanów przejściowych przy starcie i resecie.
Jeśli naprawdę masz tylko goły kwarc 32,768 kHz, to na ATtiny2313A nie jest to rozwiązanie zalecane/specyfikowane jako zegar systemowy.
Masz trzy sensowne drogi:
Dla wariantu z zewnętrznym sygnałem zegarowym na XTAL1:
CKDIV8 wyłączony, jeśli chcesz liczyć dokładnie z 32768 Hz na wejściu systemowym. (ww1.microchip.com)Jeśli zostawisz CKDIV8 włączony, system będzie chodził z:
\[
32768 / 8 = 4096\ \text{Hz}
\]
i wtedy trzeba zmienić preskaler/liczenie. Nadal da się to zrobić dokładnie, ale kod musi być odpowiednio przeliczony. (ww1.microchip.com)
Przy okresie 128 s zwykły szybki podgląd bywa mylący. Dobrze sprawdzić:
Jeżeli nic więcej nie robisz w main(), możesz rozważyć IDLE. Timer1 wtedy dalej pracuje, a pobór prądu maleje.
Największe zastrzeżenie dotyczy samego źródła zegara:
ATtiny2313A + goły kwarc zegarkowy 32,768 kHz na XTAL1/XTAL2 nie jest poprawnym rozwiązaniem katalogowym. (ww1.microchip.com)
Jeżeli zależy Ci na rozwiązaniu „książkowo RTC”, ten mikrokontroler nie jest tu idealnym wyborem.
Jeśli koniecznie chcesz zostać przy ATtiny2313A i przy częstotliwości 32768 Hz, to najczystsza praktycznie opcja to:
Warto dalej sprawdzić:
PB3=0, PB4=1,PB3=1, PB4=0,Jeśli chcesz, mogę przygotować od razu:
Najkrócej:
Jeżeli chcesz, w następnym kroku podam Ci jedną finalną wersję kodu „do wklejenia” dokładnie dla:
jaki najlepiej dać goły kwarc do XTAL1 I XTAK2, proszę kod z tym kwarcem
Jeżeli chodzi o ATtiny2313A i chcesz użyć „gołego” kwarcu na pinach XTAL1 i XTAL2, to najpraktyczniejszy wybór to:
To jest rozwiązanie najbezpieczniejsze, najpopularniejsze i wygodne programowo.
Najważniejsza korekta techniczna:
jeżeli wcześniej rozważałeś kwarc 32,768 kHz, to dla ATtiny2313A na XTAL1/XTAL2 nie jest to dobre rozwiązanie jako główny zegar systemowy. Ten mikrokontroler standardowo pracuje z kwarcami w znacznie wyższym zakresie częstotliwości. Dlatego do zwykłego podłączenia pod XTAL1/XTAL2 wybierz raczej 1 MHz, 4 MHz, 8 MHz albo 16 MHz, z czego 8 MHz jest najbardziej rozsądne.
Dla ATtiny2313A kwarc 8 MHz ma kilka istotnych zalet:
W praktyce:
Schemat połączenia dla „gołego” kwarcu:
Typowy układ:
22 pF 22 pF
XTAL1 ---+---||--- GND GND ---||---+--- XTAL2
| |
+-----------[ 8 MHz ]--------------+
Pojemność kondensatorów dobiera się według parametrów kwarcu i pojemności pasożytniczych PCB.
Dla prostych układów najczęściej stosuje się:
Jeżeli nie masz noty katalogowej kwarcu, zacznij od 22 pF.
Samo podłączenie kwarcu nie wystarczy. Trzeba jeszcze ustawić fuse bity tak, aby mikrokontroler korzystał z:
Dokładna wartość fuse zależy od:
Jeżeli chcesz, mogę w kolejnym kroku podać Ci dokładną komendę avrdude dla ATtiny2313A.
W praktyce projektowej dla starszych AVR, takich jak ATtiny2313A, nadal stosuje się trzy główne podejścia:
Dla długich czasów, np. dziesiątek sekund lub minut, współcześnie częściej:
To ważne, bo ATtiny2313A nie jest optymalny do pracy z zegarkowym 32,768 kHz jako głównym kwarcem systemowym.
Nie całkiem.
„Goły kwarc” zwykle oznacza:
Nie należy mylić tego z:
Czyli:
Poniżej podaję prosty kod dla ATtiny2313A, zakładając:
To rozwiązanie jest poprawne i praktyczne.
#include <avr/io.h>
#define F_CPU 1000000UL
static void clock_prescaler_init(void)
{
// Odblokowanie zmiany preskalera
CLKPR = (1 << CLKPCE);
// Podział 8 MHz przez 8 => 1 MHz
CLKPR = (1 << CLKPS1) | (1 << CLKPS0);
}
static void timer1_init_toggle_pb3_64s(void)
{
// PB3 = OC1A jako wyjście
DDRB |= (1 << PB3);
// Zatrzymaj i wyzeruj timer
TCCR1A = 0;
TCCR1B = 0;
TCCR1C = 0;
TCNT1 = 0;
// Tryb CTC, TOP = OCR1A
// F_timer = 1 MHz / 1024 = 976.5625 Hz
// 64 s * 976.5625 = 62500 impulsów
// OCR1A = 62499
OCR1A = 62499;
// Przełączaj stan OC1A przy compare match
TCCR1A = (1 << COM1A0);
// CTC + preskaler 1024
TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10);
}
int main(void)
{
clock_prescaler_init();
timer1_init_toggle_pb3_64s();
while (1)
{
// Nic nie robimy - przełączanie realizuje sprzętowo Timer1
}
}
Jeżeli chcesz:
to mogę od razu dopisać właściwą wersję.
W tym zagadnieniu aspekty etyczne są mało istotne, ale technicznie ważne są:
Dla AVR nie wolno ignorować zależności:
Jeżeli układ ma pracować niezawodnie przemysłowo, trzeba uwzględnić:
Sprawdź kolejno:
Tu trzeba jasno powiedzieć o jednej nieścisłości, która pojawiła się w przykładowych odpowiedziach:
Czyli poprawiona rekomendacja brzmi:
Jeżeli chcesz rozwinąć projekt, warto sprawdzić dalej:
Mogę też przygotować:
Najlepiej do XTAL1 i XTAL2 w ATtiny2313A dać:
To jest najbardziej praktyczne rozwiązanie dla „gołego kwarcu”.
Kod powyżej działa z takim kwarcem i przełącza PB3 sprzętowo z użyciem Timer1.
Jeżeli chcesz, w następnej wiadomości mogę podać Ci: