Skocz do zawartości

ATtiny85 - generacja PWM z f > 20 KHz i krokiem 0,5 us


FlyingDutch

Pomocna odpowiedź

Cześć,

udało się może komuś wygenerować przebieg PWM z cżęstotliwością >= 20 KHz na Attiny85 i mozliwością regulacji wypełnienia z dokładnością >= 0,5 us?

Wiem, że post bez kodu jest nic nie wart, ale się śpieszyłem i nie zdążyłem skopiować (nie mam go teraz). Ogólnie program jest napisany na rejestrach ale w Arduino IDE (z użyciem ATtinyCore).

Udało mi się wygenerować przerwanie od timera co 50 us i to działa (f=20Khz), ale nie działa mi poprawnie instrukcja micros() odliczaa czasy 8 razy dłuższe niż powinna.

Problem polega na tym, że mam zegar 8 MHz (internal) i staram się włączyć programowo pętlę PLL (właśnie ze względu na jej obecność wybrałem Attiny85) aby taktowanie timera wynosiło 64 Mhz.

Ale to nie działa - włączenie PLL.

Gdy w procedurze obsługi przerwania od timera zmieniam tylko sten portu wyjściowego (PB0) za pomocą instrukcji:

PORTB |= (1<<0);
PORTB  ^= (1<<0);

zajmuje to 4 microsekundy (co jest czasem 8 razy za długim dla moich potrzeb).

Nie jestem zbyt biegły w programowaniu AVR. Sprawdziłem sporo przykładów, ale żaden z nich nie działał, tak aby spelnial podane założenia.

Może ktoś może pomóc? Jutro wkleję kod programu.

Pozdrawiam

Link do komentarza
Share on other sites

1 godzinę temu, FlyingDutch napisał:

i staram się włączyć programowo pętlę PLL

A mógłbyś opisać na czym te starania polegały? Bo wg mnie PLL odpalasz poprzez ustawienie (programatorem) fusebitów CKSEL na 0001. Wtedy dostajesz zegar timera 64MHz i zegar CPU 16MHz. Tego nie przeżyje typowy bootloader Arduino więc z definicji przy takich zabawach musisz używać programatora SPI/ISP, oczywiście może być "napędzany" z IDE Arduino.

EDIT: Czy próbujesz zrobić wyjście audio?

Edytowano przez marek1707
  • Lubię! 2
Link do komentarza
Share on other sites

Żeby sprawdzić ile to zajmuje czasu użyj oscyloskopu lub analizatora logicznego Salae, micros po prostu nie zmierzy krótszych czasów - jest to opisane w reference Arduino. PLL ustawisz też z Arduino na zegar 16MHz - ustawiasz co chcesz i wgrywasz bootloader, przecież nie zje ci flasha, ATtinyCore nie dogrywa bootloadera, tylko ustawia tą opcją fusy.

  • Lubię! 1
Link do komentarza
Share on other sites

Funkcja micros nie ma nic wspólnego z timerem - to programowa pętla opóźniejąca pracująca z szybkością CPU, gdzie kompilator generuje odpowiedniie opóźnienie przez wytracanie czasu procesora na wykonywnaiu pustej pętli.

Tryb pracy z PLL jest dość specyficzny, bo CPU ma wciąż tylko 16MHz i lepiej nie będzie, możesz je co najwyżej zwalniać podzielnikiem zegara. W takich warunkach, przy pomocy swojej programowej metody "ustaw pin -> wyzeruj pin" uzyskasz min czas 125ns (2 cykle), ale skalowanie tego (przez wstawianie NOP-ów między operacje I/O) będzie niewygodne. Natomiast timer jest wtedy taktowany z 64MHz i pracuje 4 razy szybciej niż CPU co także może przysporzyć kłopotów, więc uważaj. Nie możesz wtedy czytać ani pisać rejestru głównego podczas odliczania, bo wyniki mogą być od czapy - zmieniają się 4 razy poodczas jednego cyklu dostępu. Możesz za to ustawiać wypełnienie 🙂 bo rejestr okresu ma "statyczną" zawartość więc generacja szybkiego PWM zadziała. Robiłem tak przy przetwornicach DCDC i wciąż mając 8 bitów rozdzielczości działało na 62.5kHz.

  • Lubię! 1
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

8 godzin temu, kaczakat napisał:

Żeby sprawdzić ile to zajmuje czasu użyj oscyloskopu lub analizatora logicznego Salae, micros po prostu nie zmierzy krótszych czasów - jest to opisane w reference Arduino. PLL ustawisz też z Arduino na zegar 16MHz - ustawiasz co chcesz i wgrywasz bootloader, przecież nie zje ci flasha, ATtinyCore nie dogrywa bootloadera, tylko ustawia tą opcją fusy.

Cześć,

właśnie używałem oscyloskopu i zmiana stanu portu wyjściowego (bez żadnych operacji zajmuje 4 microsekundy. Wyizolowałem prosty kawałek kodu (Arduino IDE), aby zobrazować problem:

void setup() {
  // Enable 64 MHz PLL and use as source for Timer
  PLLCSR = 1<<PCKE | 1<<PLLE;     

  //pinMode(0, OUTPUT);            // Enable PWM output pin
  DDRB |= _BV(0);                  
   
  // Set up Timer/Counter0 for 20kHz interrupt 
  TCCR0A = 3<<WGM00;             // Fast PWM
  //TCCR0B = 1<<WGM02 | 2<<CS00;   // 1/8 prescale
  TCCR0B = 1<<WGM02 | 1<<CS00;   // no prescale
  TIMSK = 1<<OCIE0A;             // Enable compare match, disable overflow
  OCR0A = 50;                    // Divide by 50
  OCR0B = 50;
  
  //----------------------------------------------

}

void loop() { }

//wywolywane co 50 us
ISR(TIMER0_COMPA_vect) {
  PORTB |= (1<<0); //Ustawiamy PB0 na 1
  PORTB  ^= (1<<0); //zmiana stanu na przeciwny
}

Przerwanie wywoływane jest co prawidłowy czas 50 us (sprawdzałem oscyloskopem). Natomiast ustawienie stanu portu wyjściowego i jego zmiana zajmuje 4 us, co jest dużo za dużo. Nie udało mi się wygenerować przebiegu PWM w trybie fast PWM timera bo f_clk jest dzielona co najmniej przez 256 co jest dużo za dużo (wychodzi za mała częstotliwość PWM). Próba wgrania bootloadera z "Arduino IDE" kończy się błędem (chociaż upload programów przebiega bez błędów. Używam progarmatora firmy "Olimex" zgodnego z AVR-ISP-MKII.

Pozdrawiam

 

27 minut temu, marek1707 napisał:

Funkcja micros nie ma nic wspólnego z timerem - to programowa pętla opóźniejąca pracująca z szybkością CPU, gdzie kompilator generuje odpowiedniie opóźnienie przez wytracanie czasu procesora na wykonywnaiu pustej pętli.

Tryb pracy z PLL jest dość specyficzny, bo CPU ma wciąż tylko 16MHz i lepiej nie będzie, możesz je co najwyżej zwalniać podzielnikiem zegara. W takich warunkach, przy pomocy swojej programowej metody "ustaw pin -> wyzeruj pin" uzyskasz min czas 125ns (2 cykle), ale skalowanie tego (przez wstawianie NOP-ów między operacje I/O) będzie niewygodne. Natomiast timer jest wtedy taktowany z 64MHz i pracuje 4 razy szybciej niż CPU co także może przysporzyć kłopotów, więc uważaj. Nie możesz wtedy czytać ani pisać rejestru głównego podczas odliczania, bo wyniki mogą być od czapy - zmieniają się 4 razy poodczas jednego cyklu dostępu. Możesz za to ustawiać wypełnienie 🙂 bo rejestr okresu ma "statyczną" zawartość więc generacja szybkiego PWM zadziała. Robiłem tak przy przetwornicach DCDC i wciąż mając 8 bitów rozdzielczości działało na 62.5kHz.

Cześć,

spróbuję ustawić fuse-bit CKSEL na 0001 za pomocą zewnętrznego programu. A mógłbyś może zamieścić kod źródłowy tego rozwiązania z fpwm = 62.5 KHz?

Chodzi mi głównie o setup timera do jego generacji. Dziękuję za wyczerpującą odpowiedź.

Pozdrawiam

Link do komentarza
Share on other sites

PLL top układ quasi analogowy, nie możesz tak po prostu go włączyć i już. Sekwencja przełączania na taktowanie timera z PLL powinna wyglądać tak:

1. Ustawiasz PLLE (już masz go ustawionego gdy fusebity wskazują domyślnie na pracę z PLL).

2. Czekasz kilkadziesiąt us i dopiero wtedy zaczynasz sprawdzać stan PLOCK - czekasz na stan 1, to pokazuje "zaskok" pętli synchronizacji i stabilną pracę generatora VCO.

3. Dopiero wtedy ustawiasz PCKE (zostawiając wciąż ustawiony PLLE).

15 minut temu, FlyingDutch napisał:

Nie udało mi się wygenerować przebiegu PWM w trybie fast PWM timera bo f_clk jest dzielona co najmniej przez 256

Jest dzielona przez tyle ile ustawisz. W szczególnym przypadku możesz ustawić prescaler na 1 i mieć 8-bitowy licznik popędzany wprost z 64MHz co daje PWM właśnie pomnad 60kHz. Nie rozumiem tego co napisałeś.

Link do komentarza
Share on other sites

@FlyingDutch może zacznij od czegoś łatwiejszego. Na początek zostaw PLL w spokoju, nie używaj przerwań. Napisz program, który w pętli będzie machał testowym pinem najszybciej jak potrafi. Wtedy chociaż zobaczymy czy pomiary wychodzą zgodnie z oczekiwaniami.

Link do komentarza
Share on other sites

1 godzinę temu, marek1707 napisał:

PLL top układ quasi analogowy, nie możesz tak po prostu go włączyć i już. Sekwencja przełączania na taktowanie timera z PLL powinna wyglądać tak:

1. Ustawiasz PLLE (już masz go ustawionego gdy fusebity wskazują domyślnie na pracę z PLL).

2. Czekasz kilkadziesiąt us i dopiero wtedy zaczynasz sprawdzać stan PLOCK - czekasz na stan 1, to pokazuje "zaskok" pętli synchronizacji i stabilną pracę generatora VCO.

3. Dopiero wtedy ustawiasz PCKE (zostawiając wciąż ustawiony PLLE).

----------------------------------------------

Jest dzielona przez tyle ile ustawisz. W szczególnym przypadku możesz ustawić prescaler na 1 i mieć 8-bitowy licznik popędzany wprost z 64MHz co daje PWM właśnie pomnad 60kHz. Nie rozumiem tego co napisałeś.

Cześć Marek,

czyli wynika z tego, że mogę ustawić korzystanie z PLL całkowicie programowo (nie ustawiając fuse-bits)?

To chyba ja coś. źle zrozumiałem. W nocie katalogowej dla Attiny85 jest napisane (dla trybu fast PWM):

The PWM frequency for the output can be calculated by the following equation:
   Fo = Fclk/N*256
The N variable represents the prescale factor (1, 8, 64, 256, or 1024).

(Strona 74 noty katalogowej) i moje próby to potwierdzają. Chyba mówisz o trybie CTC timera (tego nie próbowałem).

Czy mógłbyś zamieścić kawałek kodu jak ustawić timer dla trybu CTC dla wysokiej częstotliwości PWM (dla Attiny85)?

Pozdrawiam

 

 

1 godzinę temu, Elvis napisał:

@FlyingDutch może zacznij od czegoś łatwiejszego. Na początek zostaw PLL w spokoju, nie używaj przerwań. Napisz program, który w pętli będzie machał testowym pinem najszybciej jak potrafi. Wtedy chociaż zobaczymy czy pomiary wychodzą zgodnie z oczekiwaniami.

Cześć Elvis,

wolałbym wygenerować PWM o częstotliwości około 20 KHz korzystając wyłącznie z konfiguracji timera (na razie jednak mi się to nie udało).

Pozdrawiam

Link do komentarza
Share on other sites

Ja to postaram się napisać jak najprościej. Jak miałem problemy tego typu, przepisywałem zwyczajnie fragment kodu w ASM z dokumentacji procesora do swojego programu, który uruchamiał i konfigurował mi dany moduł, i w 99,9% się sprawdzało. Zazwyczaj w dokumentacji są przykłady tego typu jak uruchomić PLL, watchdog itd.. Języki wysokiego poziomu, czasami tego nie ogarniają bo są obostrzenia czasowe co do wykonywania operacji, przynajmniej mi się zdarzyło i prościej wstawić kawałek kodu w ASM z noty katalogowej, niż kombinować czemu kawałek kodu w C który powinien działać, nie działa.

Edytowano przez BlackJack
  • Pomogłeś! 1
Link do komentarza
Share on other sites

Cześć,

ustawiłem fuse-bit CKSEL i pętla PLL zadziałała. Czyli mam zegar CPU = 16 MHz i taktowanie timera 64 MHz. Gdy zmieniłem sposób zmiany stanu portu wyj z:

  PORTB |= (1<<0); //Ustawiamy PB0 na 1
  PORTB  ^= (1<<0);

na

  PORTB |= (1<<0); //Ustawiamy PB0 na 1
  PORTB  &= ~(1<<0);

i mierzony czas zmiany zmiany stanu pinu wyjściowego zmalał do 1 us (co od biedy już ujdzie). Sprawdzę, czy jakbym zrobił to wstawką asemblerową (tu ukłon w kierunku @BlackJack) aby zmieniać stan pinu, czy da się zejść poniżej tej 1 us.

Dziękuję wszystkim za uwagi 🙂

Pozdrawiam

Hej,

nasuwa mi się w związku z tym tematem taka refleksja (może trochę off-topic). Wolę układy programowalne (CPLD/FPGA) bo tam generacja przebiegów z dużo większą dokładnością niż pojedyńcze mikrosekundy jest bajecznie prosta. Ale to taka subiektywna uwaga i oczywiście nie wszyscy muszą podzielać moje zdanie.

Pozdrawiam

Link do komentarza
Share on other sites

Cześć,

mam jeszcze jedno pytanie: macie może jakiegoś kandydata do odliczania czasu z rozdzielczością 1 us? Próbowałem pętli prostych operacji arytmetycznych dla integer (np. dodawania, czy mnożenia, ale słabo się skalują - to co wychodzi np. z 10 obiegów pętli nie jest równe jednostkowemu czasowi dla np. 100 przebiegów pętli. próbowałem też delayMicroseconds() cale też słabo się skaluje i ma za małą rozdzielczość.

Pozdrawiam

Link do komentarza
Share on other sites

Ależ błądzisz. Właśnie do takich zadań jak sub- i mikrosekundowe impulsy są w tych (i innych) mikrokontrolerach timery wraz z całą menażerią generatorów PWM czy rejestrów OCRxx i wyjść OCxx. Przecież możesz tam sprzętowo wygenerować impuls (lub ich ciąg) z dokładnością do jednego zegara timera. Dla zwykłej 16MHz ATmegi to już jest rozdzielczość 62.5ns.Może napisz po prostu (ale precyzyjnie) co chcesz zrobić, bo delay na odliczaniu cykli CPU to się w przedszkolu robi. Oczywiście zawsze znajdzie się aplikacja w której nawet fafnaście wybajerzonych timerów sporego STM32F3 czy F4 nie dadzą rady, ale Ty chyba czegoś skomplikowanego - przynajmniej tak to wygląda z powyższych prób - nie robisz.

3 godziny temu, FlyingDutch napisał:

Wolę układy programowalne (CPLD/FPGA) bo tam generacja przebiegów...

A ja wolę (off-topic) Forda Transita niż Dodge Chargera bo przewiezienie tym pierwszym tony ziemniaków to bajka... Co za dziecinada.

Link do komentarza
Share on other sites

Dnia 4.12.2018 o 21:53, marek1707 napisał:

Ależ błądzisz. Właśnie do takich zadań jak sub- i mikrosekundowe impulsy są w tych (i innych) mikrokontrolerach timery wraz z całą menażerią generatorów PWM czy rejestrów OCRxx i wyjść OCxx. Przecież możesz tam sprzętowo wygenerować impuls (lub ich ciąg) z dokładnością do jednego zegara timera. Dla zwykłej 16MHz ATmegi to już jest rozdzielczość 62.5ns.Może napisz po prostu (ale precyzyjnie) co chcesz zrobić, bo delay na odliczaniu cykli CPU to się w przedszkolu robi. Oczywiście zawsze znajdzie się aplikacja w której nawet fafnaście wybajerzonych timerów sporego STM32F3 czy F4 nie dadzą rady, ale Ty chyba czegoś skomplikowanego - przynajmniej tak to wygląda z powyższych prób - nie robisz.

A ja wolę (off-topic) Forda Transita niż Dodge Chargera bo przewiezienie tym pierwszym tony ziemniaków to bajka... Co za dziecinada.

Cześć Marek,

gdyby mi działała prawidłowo konfiguracja timera na ATtiny85 to nie robiłbym takich karkołomnych eksperymentów. Zrobiłem też próbę na Attiny84 (patrz ten artykuł):

https://andreasrohner.at/posts/Electronics/How-to-set-the-PWM-frequency-for-the-Attiny84/

i też nie chce mi działać (timer - prawidłowa generacja PWM).

Dochodzę do wniosku, że to ATinyCore:

https://github.com/SpenceKonde/ATTinyCore

nie działa prawidłowo z moim  " Arduino IDE" (wersja 1.6.12). Wszystkie próby robiłem z "Arduino IDE". Muszę zainstalować sobie "Atmel Studio" i sprawdzę to z tym kompilatorem.

Co do forda Transita versus Dodge Chargera to nie mam zdania bo nie posiadam samochodu. Jeśli natomiast chodzi o generację przebiegów o dużej rozdzielczości czasowej i dużych częstotliwościach to nadal uważam, że na układach programowalnych jest to dużo prostsze do wykonania niż dla dla mikro-kontrolerów 😉

Pozdrawiam

 

Link do komentarza
Share on other sites

Gość es2
	    //enable analog output
    PLLCSR = 0x07;    //fast mode
    TCCR1 = 0x01;    //timer1 on, no PWM on pin A, clock is PCK
    GTCCR = 0x60;    //pwm on pin B, pin notB disconnected
    DDRB |= 1 << 4;
    OCR1B = 0;
    
    //enable sample timer
    // we can either get it exacty right (32KHz) at cost of 2x as many interrupts since we
    // have no clk/2 prescaler which we'd need to count to 500, or we can get it almost right
    // and be 0.8% slower or faster. For now we choose the 0.8% slower version
    TCCR0A = 0x02;    //CTC mode
    TCCR0B = 0x02;    //clk/8 prescaler
    OCR0A = 63;
    TIMSK = 0x10;    //int on match with A
    
    sei();
	

obraz.thumb.png.1778d5c8615cbf0d9f1e8d70733491d6.png

Pamiętaj o odpowiednim ustawieniu fuses.

obraz.png

Link do komentarza
Share on other sites

Witam

Zrobiłem coś podobnego na attiny13, ale skok 1us, może przez zegar 9600000, na attiny45 po ustawieniu na 16MHz w Arduino (ja akurat mam core Attiny Microcontrollers) po prostu chodzi OK z krokiem 500n, więc pewnie rejestry są takie same i już nie sprawdzałem. Wypełnienie ustala się potencjometrem na PB2, wyjście PWM na PB1. PWM ma ~20kHz, pewnie zależy od egz.

#define LED PB1
void adc_setup (void)
{
    // Set the ADC input to PB2/ADC1
    ADMUX |= (1 << MUX0);
    ADMUX |= (1 << ADLAR);

    // Set the prescaler to clock/128 & enable ADC
    // At 9.6 MHz this is 75 kHz.
    // See ATtiny13 datasheet, Table 14.4.
    ADCSRA |= (1 << ADPS1) | (1 << ADPS0) | (1 << ADEN);
}

int adc_read (void)
{
    // Start the conversion
    ADCSRA |= (1 << ADSC);

    // Wait for it to finish
    while (ADCSRA & (1 << ADSC));

    return ADCH/2;
}

void pwm_setup (void)
{
    // Set Timer 0 prescaler to clock/8.
    // At 9.6 MHz this is 1.2 MHz.
    // See ATtiny13 datasheet, Table 11.9.
    TCCR0B |= (1 << CS01);

    // Set to 'Fast PWM' mode TOP=0xFF
   // TCCR0A |= (1 << WGM01) | (1 << WGM00);

    // Set to 'Fast PWM' mode TOP= OCR0A
    TCCR0A |= (1 << WGM01) | (1 << WGM00);
    // co ciekawe ten WGM02 jest w innym rejestrze
    TCCR0B|= (1 << WGM02);


    OCR0A = 99;

    // Clear OC0B output on compare match, upwards counting.
//    TCCR0A |= (1 << COM0B1);
    // Set OC0B output on compare match, down counting.
    TCCR0A |= (1 << COM0B1)|(1 << COM0B0);
}

void pwm_write (int val)
{
    OCR0B = val;
}

int main (void)
{


    // LED is an output.
    DDRB |= (1 << LED);

    adc_setup();
    pwm_setup();

    while (1) {
        // Get the ADC value
       uint8_t adc_in = adc_read();
        // Now write it to the PWM counter

        pwm_write(adc_in);
    }
}

 

 

  • Lubię! 1
Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.