Skocz do zawartości

[atmega88] [Timer0] PWM serwo na 0C0A


mati 6

Pomocna odpowiedź

Witam.

Długi czas szukałem w internecie gotowych rozwiązań na sterowanie serwem za pomocą PD6 ( OC0A) na atmega88, jednak nie znalazłem nic pomocnego.

Postanowiłem przełamać się i samodzielnie zanurzyć w głębiny not katalogowych.

Po dłuuugim czasie udało mi się rozgryźć jak ustawić PWM ( odpowiednie bity TCCR0A i TCCR0B), jednak gdy doszło do przekształcenia samego PWM na sygnał dla serwomechanizmu okazało się, że ludzie zazwyczaj korzystają z Timera1, który ma tryb ( 14) który jako wartość TOP przyjmuje ICR1.

Timer0 niestety takiej opcji nie ma.

Rozwiązniem mogloby być ustawienie trybu, w którym OCRA stanowi TOP, jednakże wówczas nóżka 0C0A ( tj. PD6) nie może służyć jako wyjście sygnału PWM.

Płytka jest już wytrawiona i polutowana, dlatego szukam rozwiązania nie ingerującego w hardware.

Jeśli dobrze rozumiem, to ustalenie ICR1 służy precyzyjnemu wyliczeniu częstotliwości sygnału ( tj. 50 Hz dla serwa).

Mając częstotliwość Uc 1MHz, wykorzystując prescaler 64 podstawiając do wzoru:

1MHz/(64*256) otrzymuję 61Hz, co nie jest aż tak oddalonym wynikiem, jednak wciąż nie mogę wysterować w ten sposób serwem podając różne wartości na 0CR0A.

TCCR0A = ( 1 << COM0A0 ) | (1<

TCCR0B = (1<

Jak wyżej, ustawiłem tryb fast pwm, top= 0xFF;

Prescaler=64

Oraz " Clear OC0A on compare match, set OC0A at Bottom ( non-inverting mode)"

i tak samo OC0B

Link do komentarza
Share on other sites

Moim zdaniem 61Hz powinno łyknąć każde serwo, to nie apteka.

Dlaczego piszesz, że nie możesz w tym trybie sterować położeniem serwa poprzez zmianę zawartości OCR0A i OCR0B?

Przy zegarze 1MHz i preskalerze 1/64 masz krok timera = 64us. Do uzyskania impulsu wyjściowego neutralnego (1.5ms) musisz wpisać do OCR0x 1500/64=23. Analogicznie jedno skrajne położenie serwa (1ms) to będzie 1000/64=16 a drugie (2ms) wymaga wpisania 2000/64=31. OK, w całym zakresie normalnej pracy serwa masz więc tylko 16 różnych położeń - nie jest to jakaś kosmiczna precyzja, ale czasem wystarcza nawet mniejsza.

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

Dzięki wielkie za odpowiedź 🙂

Wielka ulga, że to co napisałem miało choć połowicznie sens 🙂

EDIT: działa

#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>                



int main(void)
{

TCCR0A = ( 1 << COM0A1 ) | (1<<COM0B1) | (1<<WGM01) | (1<<WGM00);
TCCR0B = (1<<CS00) | (1<<CS01);



DDRD=0xFF;
PORTD=0xFF;
OCR0A=23;

while(1)

  {
  int i;
  for (i=16; i<32;i++)
	{
	OCR0A=i;
	_delay_ms(1000);
	}
 }

 }

Serwo podłączone do PD6 ( OC0A) ani drgnie przy takim programie ( prócz momentu, w którym podłączam zasilanie ). Sprawdziłem miernikiem, na zasilaniu dostaje piękne 5V, a ścieżka z sygnałem od PD6 z atmegi88 do goldpina dla serwa pięknie przewodzi.

Poniżej umieszczam również kroki, jakie wykonywałem podczas dłubania w nocie katalogowej:

===========================================================

Bazowałem na artykule: [Dla początkujących] PWM w praktyce - język Użytkownika Grabki

https://www.forbot.pl/forum/topics20/dla-poczatkujacych-pwm-w-praktyce-jezyk-c-vt7455.htm

Odnalazłem te tabele ( w punkcie dot. timera0 )

Wybrałem MODE 3, czyli muszę ustawić

WGM02 0

WGM01 1

WGM00 1

Bazując na 2 powyższych tabelach doszedłem do wniosku, że muszę zająć się jeszcze :

COM0A1, COM0A0, COM0B1, COMOB2, CS02, CS01, CS00, F0C0A, FOC0B

Tutaj wybrałem opcję 3 ( nie wiem czy dobrze)

COM0A1 = 1 COM0A0 = 0

Tutaj również opcja 3 ( nie wiem czy dobrze )

COM0B1 =1

COM0B0 =0

“The FOC0A bit is only active when the WGM bits specify a non-PWM mode.

However, for ensuring compatibility with future devices, this bit must be set to zero when

TCCR0B is written when operating in PWM mode.”

Zatem FOC0A = 0 ( tak jest ustawiony domyślnie )

Sprawa FOC0B wygląda identycznie jak wyżej, więc FOC0B=0 ( domyślnie )

tutaj prescaler 64, więc

CS02 = 0

CS01=1

CS00=1

Składając to do kupy wyszło mi , że w TCCR0A:

COM0A1 = 1 COM0A0 = 0 COM0B1 =1 COM0B0 =0 WGM01=1 WGM00=1

W TCCR0B:

FOC0A = 0 FOC0B=0 WGM02=0 CS02 = 0 CS01=1 CS00=1

Przy czym wszystkie bity są domyślnie ustawione na '0', więc zająłem się tylko jedynkami

TCCR0A = ( 1 << COM0A1 ) | (1<<COM0B1) | (1<<WGM01) | (1<<WGM00);
TCCR0B = (1<<CS00) | (1<<CS01);
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.