Skocz do zawartości

[C] Atmega328P [Timer0] Jak ustawić dokładną częstotliwość PWM na 36kHz


shaslyk135

Pomocna odpowiedź

Witam wszystkich

Uprzedzam, że nie mam dużego doświadczenia w programowaniu.

Atmega328P taktowana jest kwarcem 16MHz. Chcę wygenerować fast PWM (tryb 7) na timerze0 o częstotliwości 36kHz dla diody czujnika odbiciowego. Zastanawia mnie jednak czy to wogóle jest to możliwe na timerze 8 bitowym? Poniżej przedstawię jak ja to sobie wyobrażam.

Obliczenia:

16000 000Hz/36000Hz≈444.(4) - Timer ma pojemność 255 więc muszę podzielić preskalerem. Najmniejsza wartość to 8 więc:

444/8≈55

16000 000Hz/8/55≈36 363Hz Około 36kHz czyli się zgadza.

Według noty wartość maksymalna jest określana rejestrem OCRA (myślę, że działa to tak samo jak w timerze1). Wstawiam screeny z noty katalogowej.

TRYBY TIMERA0

TRYBY TIMERA1 (fragment)

Niestety po ustawieniu tego rejestru na wartość 55 Atmel Studio wyświetla błąd:

Error 1 'OCRA' undeclared (first use in this function)

Wrzucam kod

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>  

int x=1;

int main(void)
{

DDRB |= 0xFF;   //wszystkie porty jako wyjśćia
DDRC |= 0xFF;
DDRD |= 0xFF;

PORTB^= (1<<PB5); //dioda kontrolna

/*ustawienie trybu*/
TCCR0A |=  (1<<WGM02) | (1<<WGM01) | (1<<WGM00) ; //tryb 7 fastPWM TOP=OCRA 
TCCR0A |=  (1<<COM0A1); //Clear OC0A on Compare Match, set OC0A at BOTTOM, (non-inverting mode).
TCCR0A |=  (1<<COM0B1);  // Jak wyżej tylko OC0B

/*preskaler*/
TCCR0B |= (1<<CS01); //clkI/O/8 (From prescaler)

OCRA = 55; //wartosc maksymalna (tu wywala błąd)
OCR0A = 22; //wypelnienie poczatkowe




/* Początek nieskończonej pętli */
while(1)
{

	PORTB ^= (1<<PB5); //kontrolne miganie diody
	_delay_ms(20);

		/* fragment poniżej służył mi do testów czy PWM jest generowany wtedy nie wpisywałem nic do rejestru OCRA
			OCR0A+=x;  
			if(OCR0A==250) x*=-1;
			if(OCR0A==0)   x*=-1;
		*/
}
}

Domyślam się, że nie można zmienić wartości maksymalnej tak jak w timerze1, ale proszę o opinię kogoś kto lepiej się zna.

Link do komentarza
Share on other sites

W tabelce jest drobna nieścisłość: timer 0 nie ma rejestru nazywającego się OCRA i nic dziwnego, że kompilator tej nazwy nie zna. Chodzi tu o rejestr OCR0A i jeśli chcesz, by Twój PWM miał ustawianą zarówno częstotliwość jak i wypełnienie, to rzeczywiście musisz użyć trybu 7. Ponieważ wtedy okres całego Timera 0 programujesz za pomocą OCR0A, to do generacji PWM zostaje tylko kanał B z jego rejestrem OCR0B i wyjściem na pinie OC0B (PD5).

Oba kanały (a kanał A w szczególności) możesz wykorzystać jedynie w przypadku pracy w trybach gdzie TOP=0xFF, bo do tego nie trzeba marnować żadnego rejestru.

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

EDIT: Zapomniałem dodać, że oczekując podziału przez 55, do rejestru OCR1A musisz wpisać 54, bo licznik timera jest zerowany dopiero po komparacji z OCR1A. Łącznie ze stanem 0 daje to 55 stanów licznika.

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

Dziękuję za odpowiedź. Dziwne, że ten błąd nie został poprawiony przez producenta.

Muszę wygenerować dwa przebiegi równocześnie, więc chyba nie pozostaje mi nic innego jak wykorzystać timer1. Rozumiem, że błąd ten występuje też w tabelce dotyczącej timera2 ponieważ jest tam rejestr OCRA

Link do komentarza
Share on other sites

Potrzebujesz dwóch przebiegów o tej samej częstotliwości i różnym wypełnieniu? To faktycznie tylko Timer 1 - ma tryb 14 w którym okres jest programowany rejestrem ICR, niepotrzebnym do PWM. Wtedy oba OCR1x zostają do regulacji wypełnienia.

  • 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

Jak napiszę kod to wstawię go tu w celu weryfikacji 🙂

[ Dodano: 05-02-2015, 15:46 ]

Przerobiłem kod timera0 na PWM silników robota i użyłem timera1 do generowania PWMa dla diod IR.

#define F_CPU 16000000UL 

/**************moje definicje*************/
#define SILNIK1_PWM OCR0A
#define SILNIK2_PWM OCR0B

#define IR1_PWM OCR1A
#define IR2_PWM OCR1B

/**************moje definicje*************/
#include <avr/io.h>
#include <util/delay.h>  

static inline void pwm_silniki_init (void)
{
/*ustawienie trybu*/
TCCR0A |=  (1<<WGM02) | (1<<WGM01) | (1<<WGM00) ; //tryb 7 fastPWM TOP=OCRA
TCCR0A |=  (1<<COM0A1); //Clear OC0A on Compare Match, set OC0A at BOTTOM, (non-inverting mode).
TCCR0A |=  (1<<COM0B1);  // Jak wyżej tylko OC0B

/*preskaler*/
TCCR0B |= (1<<CS01) | (1<<CS00); //clkI/O/64 (From prescaler)   ~976 Hz

SILNIK1_PWM = 0; //wypelnienie poczatkowe
SILNIK2_PWM = 0;
}


static inline void pwm_tsop_init (void)
{
//częstotliwość PWM-------> 16000 000/preskaler/max+1
//16000 000/1/443+1=36.036kHz

TCCR1A |= (1<<COM1A1) | (1<<COM1B1);  //Clear OC1A/OC1B on Compare Match, set OC1A/OC1B at BOTTOM (non-inverting mode)
TCCR1A |= (1<<WGM13) | (1<<WGM12) | (1<<WGM11); //tryb 14 fastPWM top=ICR1

TCCR1B |= (1<<CS10); //clk/1 (No prescaling)
ICR1 = 443; //wartość maksymalna
IR1_PWM = 0; //wypelnienie początkowe
IR2_PWM = 0;
}

uint8_t x=1;
uint8_t y=1;

/*funkcja główna*/
int main(void)
{

DDRB |= 0xFF;   //wszystkie porty jako wyjścia
DDRC |= 0xFF;
DDRD |= 0xFF;
PORTB^= (1<<PB5); //dioda kontrolna

pwm_silniki_init();
   pwm_tsop_init();

/* Początek nieskończonej pętli */
while(1)
{

	PORTB ^= (1<<PB5); //kontrolne miganie diody
	_delay_ms(20);
	SILNIK1_PWM+=x;  
	SILNIK2_PWM= ~SILNIK1_PWM; //stan przeciwny do powyższego
	if(SILNIK1_PWM==250) x*=-1;
	if(SILNIK1_PWM==0)   x*=-1;

    IR1_PWM +=y;
    IR2_PWM = IR1_PWM;
	if(IR1_PWM==443) y*=-1;
	if(IR1_PWM==0)   y*=-1;

}
}

Teraz mam wątpliwości jakie wypełnienie dać dla diod podczerwieni żeby były wykrywane przez czujnik TSOP.

Link do komentarza
Share on other sites

Nie wiem czy to coś zmieni ale zapomniałem wspomnieć, że chodzi mi jedynie o wykrywanie ścian w robocie. Atmega ma generować PWM dla diody IR podłączonej przez tranzystor, a czujnik TSOP (nie wiem czy TSOP4836 się nada) w razie wykrycia przeszkody generowałby odpowiedni sygnał. Docelowo mają być dwa czujniki na dwóch osobnych diodach i TSOPach. Chciałbym osiągnąć taki efekt jak na filmiku.

Myślałem, że jest to łatwe do zrealizowania, ponieważ sygnał na filmie był nadawany przez NE555.

Czy bardzo się mylę co do metody nadawania sygnału?

Link do komentarza
Share on other sites

Acha, czyli nie zadałeś sobie trudu żeby przeczytać i próbować zrozumieć wskazany tekst ani nie zajrzałeś do danych katalogowych swojego TSOPa.

Dlaczego ma mi się chcieć odpowiadać na Twoje pytania? Wierz mi, że zadałbyś inne gdybyś przeczytał.

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

Zobaczyłem że to dłuższy temat i chciałbym się zapoznać z całością, więc zostawiłem lekturę na później. Przepraszam za brak tej informacji. Dzisiaj w nocy zapoznam się z tym tekstem i mam nadzieję, że rozjaśni mi tą kwestię.

Link do komentarza
Share on other sites

Dobra przeczytałem fragment z linku, ale zrozumiałem tylko część. Zacząłem eksperymentować. Poniżej są moje spostrzeżenia.

Dla testu podłączyłem wyjście TSOPa do buzzera przez BC557. Na osobnej płytce przez tranzystor wysyłałem czysty sygnał 36kHz (kod z wcześniejszego posta) z różnymi wypełnieniami. Nadajnik i odbiornik były na przeciwko siebie, ale tylko na początku było słychać "piknięcie" oznaczające stan niski. Również po zasłonięciu i ponownym odsłonięciu nadajnika występował ten sam efekt. Dla porównania wziąłem pilota TV i naciskałem klawisz. W tym przypadku dźwięk buzzera powtarzał się co bardzo krótki okres.

Jeśli dobrze rozumiem to muszę zasymulować transmisję danych, teraz tylko nie wiem jak to zrobić. Nigdy wcześniej nie nadawałem ani nie dobierałem danych przez podczerwień. Z podlinkowanego tematu wynika, że do przebiegu 36kHz muszę dodać modulację.

Teraz zasadnicze pytanie - czy da się to zrobić w łatwy i szybki sposób. Jeśli nie to dam sobie póki co z tym spokój i lepiej zapoznam się z tematem.

Link do komentarza
Share on other sites

Musisz włączać i wyłączać modulację co jakiś czas, np 15 paczek. Najprościej możesz to zrobić włączając i wyłączając timer w pętli głównej z delayem, ale to mało eleganckie rozwiązanie.

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

Ostatnio (po wielu nieudanych próbach i długich przerwach) popełniłem czujnik optyczny na ATtiny13 i odbiorniku podczerwieni TSOP4836. Jak chcesz mogę pokazać kod (jest napisany w C).

Jak chodzi o paczkowanie, to na trzeciej stronie DS'a masz pokazane na wykresach jak to ma wyglądać.

DS --> http://www.farnell.com/datasheets/30500.pdf

Ja w pętli głównej zrobiłem tak:

- Włączam diodę IR

- Czekam 600us

- Sprawdzam stan na TSOP'ie

- Wyłączam diodę IR

- Czekam 600us

Mam nadzieję, że pomogło.

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

Proszę bardzo 🙂


// Podstawowe biblioteki
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <util/delay.h>


// Główna funkcja programu
int main(void) {

DDRB |= (1<<PB0);			// OC0A

DDRB |= (1<<PB1);			// OC0B


// Jeżeli czujnik wykrył przeszkodę - wieci dioda LED //
DDRB |= (1<<PB2);			// PB2 jako wyjcie - dioda LED - blue


// Wyjcie czujnika //
DDRB |= (1<<PB3);  			// PB3 jako wyjcie - dioda LED - red
PORTB |= (1<<PB3); 			// Wewnętrzyny rezystor podciągający pull-up


DDRB &= ~(1<<PB4);			// TSOP4836 - PB4 jako wejcie
PORTB |= (1<<PB4);			// Wewnętrzyny rezystor podciągający pull-up


OCR0B = 16;
OCR0A = 16;

TCCR0A |= (1<<WGM01) | (1<<COM0A0) |(1<<COM0B0);		// COM0B0:Toggle OC0B on Compare Match
TCCR0B |= (1<<CS00) | (1<<WGM02);		// CS00:clkI/O/(No prescaling)



//sei;
while(1) {

       PORTB |= (1<<PB3);			// Włączam diodę IR

       _delay_us(600);				// Czekam 600us


	if(!(PINB & (1<<PB4))) {	
		PORTB |= (1<<PB2);		// Wyjście = 1
	} else {
		PORTB &= ~(1<<PB2);		// Wyjście = 0
	}


       PORTB &= ~(1<<PB3);			// Wyłączam diodę IR

       _delay_us(600);				// Czekam 600us

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

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • 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.