Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[Atmega128][C] jak ustawic PWM

03 Lut 2011 23:28 4473 11
  • Poziom 9  
    Jak sterować PWM na wewnętrznym timerze? konkretnie potrzebuje 3 rozne,
    umie trochę programować w c, ale nie mogę zrozumieć jak to ma działać, ustawienie preskalera i tych innych "zmiennych" - nie wiem jak je nazwać, nie programowałem wcześniej uC, przeczytałem ten temat z kilku książek "Mikrokontroleey AVR w praktyce" Doliński, i podobnych o programowaniu AVR w C.
    Link link do strony z której próbowałem się coś dowiedzieć

    na początek pytanie jaki tryb PWM wybrać? fast pwm, correct phase pwm?

    pwm potrzebuje do kontrolowania 3 silników takiej jakby drukarce, to moj projekt na zaliczenie, sama mechanika i elektronika nie jest najważniesza bo to zaliczenie z języka Ada w którym jest zrobione GUI ale jednak chciałbym, żeby to działało

    zdjecie części tego co jest zrobione

    jeżeli znalazłby się ktoś kto miałby zbyt dużo czasu i mi wytłumaczył jak działa ten sprzętowy pwm byłbym wdzięczny, nie miałem wcześniej do czynienia z elektroniką zbyt dużo i wiem że to za dużo jak na start, ale już za późno,

    jeszcze zdjęcie podstawi pod atmege którą do tego zrobiłem 12

    wstawiłem zdjęcia, ponieważ sam nie lubię pomagac jak ktoś nie wnosi żadnego wkładu, tutaj wkład i chęci są, wiedzy brakuje
    jak wszystko mi się uda wykonać ze zrozumieniem to napiszę po kolei jak tym pwm sterować krotki kurs - sam znalazłem kilka po angielsku, ale nie dałem im rady
    prawdopodobnie będe miał jeszcze problemy z USART
    ale to jak narazie zrozumiełem i wytestowałem z pomocąLink

    pozdrawiam wszystkich
  • Poziom 9  
    według tabeli ze specyfikacji screen tabeli żeby ustawić tryb fast pwm trzeba do rejestru TCCR1A

    Code:
    //jezeli są standardowo ustawione na 0
    
    TCCR1A |= ((1 << COM1A1) | (1 << COM1B1)| (1 << COM1C1)| (1 << WGM11)| (1 << WGM10));
    //czy mozna to zapisac
    TCCR1A = 0xAB; 

    TCCR1B |=((1 << WGM13)| (1 << WGM12)| (1 << COM1B1)| (1 << CS12)| (1 << CS10));
    // cs ustawione na preskaler 1024
    //tutaj chyba nie można przypisać hex'a bo jeden z bitow jest tylko read


    czy to ustawienie da mi fast pwm

    i tutaj ustawienie OCR1x da mi rozdzielczość
    a wypełnienie TCNT1H i TCNT1L ?


    edit: układ na L298 zrobiłem tylko zasilacz mam z PC i boje się narazie tego podpinać razem do testowania żeby atmegi nie spalić

    edit2: jak np ustawić zeby mieć wypełnienie 75% przy max czestotliwości (przy preskalerze 1024 i F_CPU 16kk daje 15 625Hz)
  • Poziom 26  
    zapisy w stylu TCCR1A = 0xAB są mało czytelne i moim zdaniem nie powinno się ich stosować. Lepsza forma jest z ((1<<XX)|(1<<YY)|....). Sam popatrz na swoim przykładzie żeby rozszyfrować zapisy 0xAB, jak się ma to do rejestru w uC, trzeba włączyć kalkulator, sprawdzić jakie bity są ustawione, dalej włączyć datasheet i sprawdzać co one oznaczają. A teraz już widzę wszystko co i jak i łatwiej mi jest Tobie pomóc.

    AFAIK jeżeli nie ma wyrazie napisane że nie należy wpisywać/zmieniać wartości bitów, możesz wpisywać sobie co tam chcesz, a i tak się nie zmieni.

    Jeżeli chodzi o timery 16-bitowe, to rozdzielczość PWM'a to 16-bitów :) Czyli wartości od 0 - 65535. Wypełnienie wpisujemy do rejestru OCR1x.

    Do rzeczy:
    Więc OCR1x podłączasz do EnA, a In1 i In2 do portów GPIO. Jak masz 2 wejścia In1 i In2 w takim samym stanie, czyli wysokim lub niskim, to silnik stoi.
    Więc jak chcesz zakręcić silnikiem to ustawiasz In1 i In2 odpowiednio dla pożądanego kierunku na 10 lub 01 i włączasz PWM z zadanym wypełnieniem. W sumie PWM może chodzić cały czas i tylko będziesz zmieniał stany GPIO lub wartość wypełnienia. Ot cała tajemnica...
  • Poziom 9  
    Code:

    #include <avr/io.h>
    #define F_CPU 16000000L
    #include <util/delay.h>
    #include <float.h>
    #include <inttypes.h>
    #include <math.h>
    #include <stdio.h>

    #define stop1 PORTA|=(0<<PA3); PORTA|=(0<<PA4)
    #define lewo1 PORTA|=(0<<PA3); PORTA|=(1<<PA4)
    #define prawo1 PORTA|=(0<<PA4); PORTA|=(1<<PA3)

    #define stop2 PORTA|=(0<<PA5); PORTA|=(0<<PA6)
    #define lewo2 PORTA|=(0<<PA5); PORTA|=(1<<PA6)
    #define prawo2 PORTA|=(0<<PA6); PORTA|=(1<<PA5)

    #define stop3 PORTC|=(0<<PC4); PORTA|=(0<<PC5)
    #define lewo3 PORTC|=(0<<PC4); PORTA|=(1<<PC5)
    #define prawo3 PORTC|=(0<<PC5); PORTA|=(1<<PC4)

    #define stop4 PORTC|=(0<<PC2); PORTA|=(0<<PC3)
    #define lewo4 PORTC|=(0<<PC2); PORTA|=(1<<PC3)
    #define prawo4 PORTC|=(0<<PC3); PORTA|=(1<<PC2)

    volatile int jasnosc = 1;

    void PWMinit()
    {
       TCCR1A |= ((1 << COM1A1) | (1 << COM1B1)| (1 << COM1C1)| (1 << WGM11));//  | (1 << WGM10));
       TCCR1B |= ((1 << WGM12) | (1 << COM1B1) | (1 << CS11));  // timer 1 ustawienie fast pwm(mode 7), prescaler 8

       TCCR3A |= ((1 << COM3A1) | (1 << COM3B1)| (1 << COM3C1)| (1 << WGM31)  | (1 << WGM30));
       TCCR3B |= ((1 << WGM32) | (1 << COM3B1) | (1 << CS31));  // timer 3 ustawienie fast pwm(mode 7), prescaler 8

       /*
       CS 12 11 10  prescaler  fPWM(10bit) [Hz]
          0  0  0   1          7800
          0  1  0   8          1950
          0  1  1   64         240
          1  0  0   256        60
          1  0  1   1024       15
       */

       DDRA |= ((1<<PA6)|(1<<PA5)|(1<<PA4)|(1<<PA3));  // piny wyjsciowe dla sygnału lew/prawo/stop dla  silnikow 1 i 2
       DDRC |= ((1<<PC5)|(1<<PC4)|(1<<PC3)|(1<<PC2));  // piny wyjsciowe dla sygnału lew/prawo/stop dla  silnikow 3 i 4

       DDRB |= ((1<<PB5)|(1<<PB6)); // ustawienie portow wyjsciowych dla sygnalu pwm silnikow 1 i 2 (pb5 - oc1a , pb6 oc1b)
       DDRE |= ((1<<PE3)|(1<<PE4)); // ustawienie portow wyjsciowych dla sygnalu pwm silnikow 3 i 4 (pe3 - oc3a , pb4 oc3b)
    }




    void SetPWMOutput(uint16_t pwm1, uint16_t pwm2, uint16_t pwm3, uint16_t pwm4)
    {
       OCR1A=pwm1;
       OCR1B=pwm2;
       OCR3A=pwm3;
       OCR3B=pwm4;
    }




    int main(void)
    {
       PWMinit();
       

       while(1)
       {
          for(jasnosc = 1; jasnosc <1024; jasnosc++)
          {
          _delay_ms(50);
          OCR1A = jasnosc;
          }
          for(jasnosc = 1022; jasnosc >0; jasnosc--)
          {
          _delay_ms(50);
          OCR1A = jasnosc;
          }


       }

    }


    jako test chciałem rozjaśnić diodę, ale lekko świeci i nie zmienia się nic,
    Proszę o sprawdzenie kodu

    Mam jeszcze jedno pytanie, co należy dołączyć, żeby obsługiwało USART,
    mianowicie ponizszy kod wyrzuca mi że rejestry do ktorych chcę wpisywać nie istnieją
    Code:

    #define UART_BAUD_RATE 19200
    #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)


    void USARTInit()
    {

       //Set Baud rate

        UBRRH = (uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)>>8);
        UBRRL = (uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);


       UCSRC=(1<<URSEL)|(3<<UCSZ0); //8bit ramka


       UCSRB=(1<<RXEN)|(1<<TXEN);


    }

    char USARTReadChar()
    {

       while(!(UCSRA & (1<<RXC)));
       return UDR;
    }
  • Poziom 27  
    Używasz ATMEGA128 , a ona ma na swoim pokładzie 2 UARTY.
    Więc wszystkie rejestry od UARTÓW są z końcówkami 0 lub 1.

    Przykładowo
    UBRR0H a dla drugiego UBRR1H
    UBRR0L a dla drugiego UBRR1L

    dalej analogicznie.

    Jak używasz tylko jeden UART to dodaj tę cyferkę 0 na końcu.
  • Poziom 9  
    OK, dzięki , nie wiem jakim cudem tego nie zobaczyłem w specyfikacji
    a ten kod mógłby mi ktoś sprawdzić, wydaje mi się, że z tym rozjaśnianiem nie mogłem nic zepsuć.

    Code:

    #define UART_BAUD_RATE 19200
    #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)


    void USARTInit()
    {

       //Set Baud rate

        UBRR0H = (uint8_t)(UART_BAUD_CALC(UART_BAUD_RATE,F_OSC)>>8);
        UBRR0L = (uint8_t)UART_BAUD_CALC(UART_BAUD_RATE,F_OSC);


       UCSR0C=(3<<UCSZ0); //8bit ramka


       UCSR0B=(1<<RXEN)|(1<<TXEN);  // enable transmitter and receiver


    }

    void uart_putc(uint8_t data)
    {
       // Oczekiwanie na zakończenie nadawania
       while (!(UCSR0A & (1 << UDRE0)));
     
       // Wysłanie danych
       UDR0 = data;
    }
     
    // funkcja wysyłająca ciąg znaków
    void uart_puts(const char *s )
    {
       while (*s)
          uart_putc(*s++);
    }

    uint8_t uart_ischar()
    {
       // Czy w buforze są dane?
       return (UCSR0A & (1 << RXC0));
    }
     
    uint8_t uart_getc()
    {
       // Czy w buforze są dane?
       while(!uart_ischar());
     
       // Dane z bufora
       return UDR0;
    }

    void uart_gets(char* dane)
    {
       uint8_t i = 0;
       while (dane[i++]=uart_getc());
    }
  • Poziom 26  
    Z tego co pamiętam, rejestry OCR1x uaktualnia się w przerwaniu overflow do timera.
  • Poziom 9  
    czy jak to mam zrobić:
    dodaje <avr/interrupt.h>

    sei() // to uaktywnia przerwania?

    i dalej nie działa, trzeba to jakoś podpiąć przerwanie?
  • Poziom 26  
    Ustawiasz bit TOIE1 w rejestrze TIMSK, żeby włączyć przerwania przepełnienia, deklarujesz funkcję obsługi przerwania w sposób:
    Code:

    volatile uint16_t pwm; //zmienna globalna do wartości pwm
    SIGNAL(TIMER2_OVF_vect)  //funkcja wykona się po przepełnieniu timera 1.
    {
      OCR1A = pwm;
    }


    W swoim programie wpisujesz do zmiennej globalnej pwm wartość wypełnienia. oczywiście sei(); przed pętlą nieskończoną musi być.

    Dodano po 4 [minuty]:

    tak szczerze to już nie pamiętam czy to trzeba robić tak robić (w przerwaniu), bo zmieniłem architekturę... może ktoś napisze coś więcej....
  • Poziom 27  
    To mi nie pasuje:

    #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)*16l)-1)

    powinno być:

    #define UART_BAUD_CALC(UART_BAUD_RATE,F_OSC) ((F_OSC)/((UART_BAUD_RATE)/16)-1)

    poza tym jak wpisujesz do rejestru UCSR0C to musisz wpisywać także URSEL.
  • Poziom 9  
    PWM uruchomiłem, był tryb zgodności z 103 nie wyłączony,
    do uart założyć nowy temat?

    edit:
    silniki się fajnie kręcą, trochę ta konstrukcja nie jest dopracowana, ale jak na projekt z Ady wystarczy :) oddaje w poniedziałek, wiec wtedy zaczne ten tut PWM tłumaczyć, dogadałem się z gościem z jednej strony ze mogę tu wstawić jego tut w mojej polskiej wersji