logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[Atmega32] [Bascom] sterowanie PWM

maximus22_kr 29 Gru 2010 08:23 5629 10
REKLAMA
  • #1 8927669
    maximus22_kr
    Poziom 18  
    Witam
    Projektuję sterowanie halogenami LED 12V, które oprócz ściemniania/rozjaśniania będzie szybko wyłączać/włączać - każde wyjście osobnym, ale tylko jednym przyciskiem.
    Założenie jest takie:
    dłuższe ( powyżej 1 sek ) przytrzymanie - rozjaśnianie ( na przemian )
    dłuższe ( powyżej 1 sek ) przytrzymanie - przyciemnianie ( na przemian )
    krótkie ( poniżej 1 sek ) przytrzymanie - włączenie ( na przemian )
    krótkie ( poniżej 1 sek ) przytrzymanie - wyłączenie ( na przemian )
    O ile przyciemnianie/rozjaśnianie na przemian działa ( wykorzystałem Timer1 do PWM ) oraz wyłączenie/włączenie - ale każda z tych funkcji osobno, to nie udaje mi się ich zmusić do współpracy razem.
    Na jednym z kursów znalazłem przykład mierzenia czasu przy użyciu Timer0, ale nie mam pomysłu jak to zastosować ( dopiero 3 tydzień z Bascomem ).
    Poniżej kod programu - dodałem podprogram mierzący czas.
    Czy przy założeniu, że obydwa przyciski nie będą razem naciskane, wyrobi się ten jeden timer ?
    Jeszcze pytanie techniczne - jako element wykonawczy najlepszy jest BUZ11, chciałbym jednak odseparować układ od zasilania 12V halogenów LED, co polecacie ?

    Kod programu:
    [color=blue]'********************* Konfiguracja
    $regfile = "m32def.dat"
    $crystal = 16000000
    
    Config Timer1 = Pwm , Prescale = 64 , Compare A Pwm = Clear Up , Compare B Pwm = Clear Up
    Config Timer0 = Timer , Prescale = 64
    
    Config Portc = &B11111100                                   'Pc.0, Pc1 jako wejścia
            Portc = &B11111111                                  'wszędzie stan spoczynkowy 1
    Config Portd = &B11111111                                   'wszystkie jako wyjścia
            Portd = &B11111111                                  'wygaszone
    
    Enable Interrupts
    Enable Timer1
    Enable Timer0
    
    '********************* Deklaracje podprogramów
    Declare Sub Up1
    Declare Sub Dn1
    Declare Sub Up2
    Declare Sub Dn2
    Declare Sub S1
    Declare Sub S2
    On Timer0 Przerwanie_co_1ms
    
    '********************* Deklaracje wejść i wyjść
    W1 Alias Pinc.0
    W2 Alias Pinc.1
    St1 Alias Portd.5
    St2 Alias Portd.4
    
    '********************* Deklaracje zmiennych
    Dim A As Byte
    Dim B As Byte
    Dim Licznik As Integer
    Licznik = 0
    
    
    A = 3
    B = 3
    
    
    '********************* Program główny
    Do
     If W1 = 0 Then                                             ' jesli przycisk W1 wciśnięty
     Gosub Przerwanie_co_1ms
     If A = 3 Then                                              ' jeśli zmienna A = 3
     Gosub Up1
       Waitms 3                                                 ' idź do podprogramu Up1
     If W1 = 1 Then                                             'jesli przycisk W1 rozłączony
     Decr A                                                     ' zmniejsz wartość zmiennej A o 1
    
    End If
    End If
    End If
    
    If W1 = 0 Then                                              ' jesli przycisk W1 wciśnięty
    If A = 2 Then                                               ' jeśli zmienna A = 2
    Gosub Dn1
       Waitms 3                                                 ' idź do podprogramu Dn1
    If W1 = 1 Then                                              'jesli przycisk W1 rozłączony
    Incr A                                                      ' zwiększ wartość zmiennej A o 1
    
    End If
    End If
    End If
    
    
     If W2 = 0 Then
     If B = 3 Then
    Gosub Up2
     If W2 = 1 Then
     Decr B
    
    End If
    End If
    End If
    
    If W2 = 0 Then
    If B = 2 Then
    Gosub Dn2
    If W2 = 1 Then
     Incr B
    
    End If
    End If
    End If
    
    Loop
    End
    
    '********************* Podprogramy
    
    Up1:
         If Pwm1a < 255 Then                                    ' rozjaśnianie LED na wyjściu 1 - Portd.5
        Incr Pwm1a
        End If
        Waitms 10
       Return
    
    Dn1:
    If Pwm1a > 0 Then                                           ' ściemianie LED na wyjściu 1 - Portd.5
        Decr Pwm1a
        End If
        Waitms 10
        Return
    
    Up2:
      If Pwm1b < 255 Then
        Incr Pwm1b
        End If
        Waitms 10
        Return
    
    Dn2:
    If Pwm1b > 0 Then
        Decr Pwm1b
        End If
        Waitms 10
        Return
    
    S1:
    Toggle St1                                                  ' Przełączanie wyjścia 1 - Portd.5
    Return
    
    S2:                                                         ' Przełączanie wyjścia 2 - Portd.4
    Toggle St2
    Return
    
    Przerwanie_co_1ms:                                          'podprogram przerwania od przepełnienia timer0
       Counter0 = Counter0 + 6                                 'ustawienie początkowej wartości timera0 poprzez
                                                                'dodanie do niego wartości 6 (odliczone zostanie
                                                                'dokładnie kolejne 250 impulsów)
    
      Incr Licznik                                              'zwiększenie licznika
      If Licznik = 1000 Then                                    'jeśli zmienna licznik równa się 500 (czyli
                                                                'odliczone zostało 1000*1ms=1sek), to
    
                                                                '
    Licznik = 0                                                 'wyzeruj zmienną licznik
      End If
    Return                                                      'powrót z przerwania[/color]


    Pozdrawiam
    Piotr
  • REKLAMA
  • #2 8928206
    piotrva
    VIP Zasłużony dla elektroda
    Ogólnie podam Ci algorytm, bo mam nadzieję, że ujęcie go w kod nie sprawi problemu.
    masz 2 zmienne typu integer np. T1, i T2 oraz stałą
    Const max_pulse = 1000
    i zmienne bitowe long1 i long2:
    ustawiasz przerwanie timera tak jak masz to zrobione, tyle, że robisz tam tak:
    
    if w1 = 0 then incr t1
    if w2 = 0 then incr t2
    If T1 = max_pulse then
    t1=0
    long1 = 1
    gosub dlugie_1
    endif
    If T2 = max_pulse then
    t2=0
    long2 = 1
    gosub dlugie_2
    endif
    

    Powoduje to że po odmierzeniu 1 sek. zostanie wywołana procedura długiego przyciśnięcia klawisza i będzie ona powtarzana co 1 sek.
    w pętli główej robisz tak:
    
    if w1 = 1 then
    if long1 = 0 then if t1 > 200 then gosub krotkie_1
    t1=0
    long1=0
    endif
    if w2 = 1 then
    if long2 = 0 then if t2 > 200 then gosub krotkie_2
    t2=0
    long2=0
    endif
    

    dzięki temu jeśli przycisk zostanie puszczony i nie był przytrzymywany (zmienne long1 i long2), a jednocześnie był trzymany przez 0,2s (eliminacja drgań styków) to wtedy wywoływana jest procedura krótkiego przyciśnięcia.
    Resztę już dasz radę dopisać sam
  • REKLAMA
  • #3 8932833
    maximus22_kr
    Poziom 18  
    Dziękuję za odpowiedź.
    Czy mógłbyś napisać to samo ale z komentarzem do każdej linii ?

    Nie wiem, czy dobrze rozumiem idee Timera.
    Enable Interrupts ---- uruchamia przerwania

    Enable Timer0 ----- uruchamia Timer0

    On Timer0 Przerwanie_co_1s ---- nazwa programu, który obsługuje przepełnienie timer0

    Dim T1 as Integer ---- zmienna typu Integer dla pierwszego wejścia
    Dim T2 as Integer ---- zmienna typu Integer dla drugiego wejścia

    Przerwanie_co_1s: ---- podprogram dla Timer0

    if w1 = 0 then incr t1 --- jeśli stan w1 jest 0 to zwiększaj T1
    if w2 = 0 then incr t2 --- jeśli stan w2 jest 0 to zwiększaj T2
    If T1 = max_pulse then
    t1=0
    long1 = 1
    gosub dlugie_1
    endif
    If T2 = max_pulse then
    t2=0
    long2 = 1
    gosub dlugie_2
    endif


    Jeśli chodzi o sam Timer0 - to według informacji z kursów jest 8-bitowy, czyli liczy do 256 impulsów. Czy mógłbyś mi wyjaśnić jak rozumieć stałą max_pulse o wartości 1000, czyli większej niż 256. Czy należy rozumieć to tak, że zliczenie 256 impulsów dla taktowania 16 MHz zajmie dla licznika Timer0 1 ms.

    Pozdrawiam i dziękuję za cierpliwość
    Piotr
  • REKLAMA
  • #4 8954756
    maximus22_kr
    Poziom 18  
    Witam ponownie
    Dziękuje za pomoc - kod się sprawdził.
    Działa, ale tylko w przypadku, gdy wyjściami nie są PWM ( ATmega 32 - PIN PD4 i PD.5 ). Ustawienie wyjść na porty PWM, powoduje albo brak reakcji, albo powolne stopniowe rozjaśnianie LED - zależy od drobnych zmian w kodzie.

    Pozdrawiam
    Piotr

    Dodano po 1 [godziny] 9 [minuty]:

    Jeszcze jeden problem.
    Jeśli zmieniam stan wyjścia po krótkim przytrzymaniu i robię to cały czas to jest OK, ale jak przytrzymam dłużej, to działa tylko drugie wyjście przypisane do długiego przytrzymania przycisku. Nie można zmienić stanu wyjścia przypisanego do krótkiego przytrzymania.

    Pozdrawiam
    Pitor
  • #5 8955269
    piotrva
    VIP Zasłużony dla elektroda
    1. kod działa na tej zasadzie:
    
    If T1 = max_pulse then
    'jeżeli trzymamy przycisk długo, tak że ilość przerwań timera zrówna się ze zdefiniowaną stałą to
    t1=0
    'resetujemy licznik przerwań
    long1 = 1
    'ustawiamy flagę długiego trzymania przycisku
    gosub dlugie_1
    'wykonujemy funkcję
    endif
    'to samo dla drugiego przycisku
    If T2 = max_pulse then
    t2=0
    long2 = 1
    gosub dlugie_2
    endif 
    

    a w pętli głównej:
    
    if w1 = 1 then
    'jeśli przycisk został puszczony
    if long1 = 0 then if t1 > 200 then gosub krotkie_1
    'jeśli nie był trzymany długo (flaga z przerwania) i jeśli nie był trzymany ekstremalnie krótko (czyli jeśli był trzymany dłużej niż 0,2s, ale krócej niż 1,0s), to wtedy wykonujemy funkcję krótkie
    t1=0
    'tak czy inaczej został puszczony, więc resetujemy licznik przerwań
    long1=0
    'i flagę długiego przytrzymania
    endif
    'to samo dla przycisku 2
    if w2 = 1 then
    if long2 = 0 then if t2 > 200 then gosub krotkie_2
    t2=0
    long2=0
    endif 
    

    co do nierozpoznawania szybkich kliknięć to spróbuj wrzucić tę część z pętli głównej do przerwania timera po tym pierwotnym kodzie.
    Poza tym wrzuć cały kod, to zobaczymy co w trawie piszczy (wrzucę to u siebie na uP i sprawdzę)
  • #6 8955291
    maximus22_kr
    Poziom 18  
    Witam
    Na razie zrobiłem to tak: ( wywaliłem obsługę rozjaśniania/ściemniania bo coś się timery "gryzły" - zamiast Twojej Long1 jest Wl1 ), na razie uprościłem kod do jednego wyjścia

    
    '********************* Konfiguracja
    $regfile = "m32def.dat"
    $crystal = 16000000
    
    
    Config Timer0 = Timer , Prescale = 64
    
    Config Portc = &B11111100                                   'Pc.0, Pc1 jako wejścia
            Portc = &B11111111                                  'wszędzie stan spoczynkowy 1
    Config Portd = &B11111111                                   'wszystkie jako wyjścia
            Portd = &B11111111
    '********************* Deklaracje podprogramów
    Declare Sub S1                                              ' deklaracja podprogramu S1
    Declare Sub S2                                              ' deklaracja podprogramu S2
    
    '********************* Deklaracje wejść i wyjść
    W1 Alias Pinc.0                                             ' przypisanie nazw do portów
    St1 Alias Portc.7                                           ' przypisanie nazw do portów
    St2 Alias Portc.6                                           ' przypisanie nazw do portów
    
    '********************* Deklaracje zmiennych
    Dim Wl1 As Bit                                              ' deklaracje zmiennej bitowej
    Dim T1 As Integer                                           ' deklaracja zmiennej licznikowej
    Const Max1 = 1000                                           ' deklaracja stałej Max1
    
    '********************* Program główny
    Do
     If W1 = 1 Then                                             ' jesli przycisk W1 wciśnięty
        If Wl1 = 0 Then                                         ' jeśli zmienna bitowa = 0
        If T1 > 200 Then                                        ' jeśli zmienna licznikowa T większa od 200
       T1 = 0                                                   ' wyzeruj zmienną licznikową T1
       Wl1 = 0                                                  ' zmień stan zmiennej bitowej Wl1 na 0
    Gosub S1                                                    ' idź do podprogramu S1
    
    
    
    
    End If
    End If
    End If
    
    
     Loop
    End
    
    '********************* Podprogramy
    Przerwanie_co_1s:                                           ' podprogram przerwania od przepełnienia timer0
    Counter0 = Counter0 + 6                                   ' dodanie do Timer0 wartości 6
                                                                ' licznik będzie liczył 250
    
       If W1 = 0 Then                                           ' jeśli przycisk W1 włączony
         Incr T1                                                ' zwiększaj wartość zmiennej licznikowej T1
         If T1 = Max1 Then                                      ' jeśli wartość zmiennej T1 osiągnie Max1
         T1 = 0                                                 ' wyzeruj zmienną licznikową T1
         Wl1 = 1                                                 ' zmień stan zmienne bitowej Wl1 na 1
    
    Gosub S2
    
    
    End If
    End If
    
    Return                                                      ' powrót z przerwania
    
    
    S1:
    Toggle St1                                                  ' Przełączanie wyjścia 1 - Portd.5
    Return
    
    S2:
    Toggle St2                                                  ' Przełączanie wyjścia 2 - Portd.4
    Return


    Kod, czy też pseudokod, proszę umieszczać w znacznikach [code]
    [zumek]
  • REKLAMA
  • #7 8955593
    piotrva
    VIP Zasłużony dla elektroda
    pozmieniałeś nieco moje fragmenty, i dlatego nie działa dobrze.
    oto gotowy kod według mojego pomysłu i w miarę prosto. Pętla główna pusta.
    Testowałem na atmega644p i działa jak należy. Przeanalizuj różnice między swoim a moim kodem i zobaczysz dlaczego nie działało
    
    $regfile = "m644pdef.dat"
    $crystal = 16000000
    '$baud = 9600
    
    Config Timer1 = Pwm , Pwm = 8 , Compare A Pwm = Clear Up , Compare B Pwm = Clear Up , Prescale = 1
    Config Pind.4 = Output
    Config Pind.5 = Output
    
    Pwm1a = 0
    Pwm1b = 0
    'konfiguracja PWM
    
    Config Timer0 = Timer , Prescale = 64
    Enable Interrupts
    Enable Timer0
    On Timer0 Prze_t0
    'konfiguracja Timera0, przerwania z częstotliwością ~977 Hz
    
    Config Pina.0 = Input
    Config Pina.1 = Input
    Set Porta.0
    Set Porta.1
    S1 Alias Pina.0
    S2 Alias Pina.1
    'konfiguracja przycisków
    
    Dim T1 As Integer , T2 As Integer , Long1 As Bit , Long2 As Bit
    Dim A As Byte
    
    Const Max_pulse = 300
    Const Min_pulse = 50
    'konfiguracja czasuw przytrzymania przycisków (odpowiednio ok. 300 ms, 50 ms)
    Const Val_push = 10
    'konfiguracja zmiany wartości PWM przy każdorazowym wywołaniu funkcji
    
    Do
    
    Loop
    
    Prze_t0:
    If S1 = 0 Then Incr T1
    If S2 = 0 Then Incr T2
    'jeśli przycisk jest wciśnięty to zwiększaj jego licznik
    If T1 = Max_pulse Then
    'jeżeli osiągniemy maksymalną wartość, to znaczy, że był długo trzymany
    T1 = 0
    Long1 = 1
    Gosub Dlugie_1
    'więc resetujemy zmienne i wykonujemy działanie
    End If
    'to samo dla drugiego przycisku
    If T2 = Max_pulse Then
    T2 = 0
    Long2 = 1
    Gosub Dlugie_2
    End If
    
    If S1 = 1 Then
    'jeżeli zwolniono przycisk
    If Long1 = 0 Then If T1 > Min_pulse Then Gosub Krotkie_1
    'i nie był wcześniej długo trzymany, a zarazem był trzymany przez minimum 50ms (określone w ustawieniach)
    'to wykonujemy działanie. Stała Min_pulse chroni przed rozpoznawaniem drgać styku jako kolejnych naciśnięć przycisku
    T1 = 0
    Long1 = 0
    'jeśli przycisk był zwolniony, to niezależnie od czasu trwania naciśnięcia resetujemy zmienne
    End If
    'to samo dla drugiego switcha
    If S2 = 1 Then
    If Long2 = 0 Then If T2 > Min_pulse Then Gosub Krotkie_2
    T2 = 0
    Long2 = 0
    End If
    
    Return
    
    Dlugie_1:
    'Print "Dlugie_1"
    For A = 0 To Val_push Step 1
    'zwiększamy wartość pwm o 10
    If Pwm1a < 255 Then Incr Pwm1a
    Next A
    Return
    
    Krotkie_1:
    'Print "Krotkie_1"
    For A = 0 To Val_push Step 1
    'zmienjszamy wartość pwm o 10
    If Pwm1a > 0 Then Decr Pwm1a
    Next A
    Return
    
    Dlugie_2:
    'Print "Dlugie_2"
    For A = 0 To Val_push Step 1
    If Pwm1b < 255 Then Incr Pwm1b
    Next A
    Return
    
    Krotkie_2:
    'Print "Krotkie_2"
    For A = 0 To Val_push Step 1
    If Pwm1b > 0 Then Decr Pwm1b
    Next A
    Return
    
    
    
  • #8 8956275
    maximus22_kr
    Poziom 18  
    Witam
    Dzięki za odzew. Sprawdzę kod w domu, jak wrócę.
    Tylko nie widzę samego przełączania - Toggle.

    Bo to miało być tak ( dla każdego przycisku osobno W1 - > S1, W2 -> S2 ):
    dłuższe przytrzymanie np. 1 sek - rozjaśnianie ( jeśli poprzednio było ściemniane )
    dłuższe przytrzymanie np. 1 sek - ściemnianie ( jeśli poprzednio było rozjaśniane )

    Szybkie włączenie/wyłączenie
    krótkie przyciśnięcie np. do 0,2 sek wyłączenie ( jeśli było włączone, nawet jeśli PWM nie było 100% )
    krótkie przyciśnięcie np. do 0,2 sek włączenie ( jeśli było wyłączone )

    o ile osobno obsługę PWM i osobno Toogle robię w miarę nieźle to połączenie tych dwóch funkcji jest dla ( na razie ) problematyczne.

    Pozdrawiam
    Piotr
  • #9 8958108
    piotrva
    VIP Zasłużony dla elektroda
    Ją nie czytałem i zrobiłem tylko jasno ciemno. Jak będę jutro przy sprzęcie to napiszę tak jak być powinno;-)
  • #10 8958335
    maximus22_kr
    Poziom 18  
    Witam

    Ok, nie ma problemu, może nie dokładnie opisałem na początku
    Obsługo PWM działa bez problemu w kodzie, który podałem w pierwszym poście - zmiana przy pomocy zmiennej A ( dla W1 ) i B ( dla W2 )

    dłuższe przytrzymanie np. 1 sek - rozjaśnianie ( jeśli poprzednio było ściemniane )
    dłuższe przytrzymanie np. 1 sek - ściemnianie ( jeśli poprzednio było rozjaśniane )

    To oczywiście polecenie Toggle
    Szybkie włączenie/wyłączenie
    krótkie przyciśnięcie np. do 0,2 sek wyłączenie ( jeśli było włączone, nawet jeśli PWM nie było 100% )
    krótkie przyciśnięcie np. do 0,2 sek włączenie ( jeśli było wyłączone )

    Timer0 chciałem wykorzystać do określenia, czy ma wyłączać/włączać ( np 100 - 500 ms ), czy ściemniać/rozjaśniać ( np. 1000 ms ) po określonym czasie - z racji, że rozjaśnianie/ściemnianie jest realizowane na dwóch podprogramach mam z tym ( jeszcze ) problem.

    Tzn. mam koncepcję, aby użyć jeszcze jednej zmiennej, która w zależności od ilości czasu, który upłynął, miała by różną wartość i następnie w zależności od jej stanu realizować dany podprogram,


    Pozdrawiam
    Piotr
  • #11 8960406
    piotrva
    VIP Zasłużony dla elektroda
    do wyjść typu pwm nie używaj toggle. po prostu aby włączyć przypisuj wartość pwm=255, a dla wyłączenia pwm=0.
    np. tak:
    
    If Pwm1a <> 0 Then Pwm1a = 0 Else Pwm1a = 255
    

    i to przy krótkim przyciśnięciu załatwi całą sprawę
    co do naprzemiennego pojaśniania i ściemniania dodaj mniej więcej coś takiego
    
    dim o1 as bit, o2 as bit
    

    i potem w przerwaniu:
    
    If S1 = 0 Then Incr T1
    If S2 = 0 Then Incr T2
    'jeśli przycisk jest wciśnięty to zwiększaj jego licznik
    If T1 = Max_pulse Then
    'jeżeli osiągniemy maksymalną wartość, to znaczy, że był długo trzymany
    T1 = 0
    Long1 = 1
    Gosub Dlugie_1
    'więc resetujemy zmienne i wykonujemy działanie
    End If
    'to samo dla drugiego przycisku
    If T2 = Max_pulse Then
    T2 = 0
    Long2 = 1
    Gosub Dlugie_2
    End If
    
    If S1 = 1 Then
    'jeżeli zwolniono przycisk
    If Long1 = 0 Then If T1 > Min_pulse Then Gosub Krotkie_1
    'i nie był wcześniej długo trzymany, a zarazem był trzymany przez minimum 50ms (określone w ustawieniach)
    'to wykonujemy działanie. Stała Min_pulse chroni przed rozpoznawaniem drgać styku jako kolejnych naciśnięć przycisku
    If Long1 = 1 Then If O1 = 1 Then O1=0 Else O1=1
    'jeśli przycisk był trzymany  długo i został puszczony, to wtedy zmieniamy wartość zmiennej bitowej
    T1 = 0
    Long1 = 0
    'jeśli przycisk był zwolniony, to niezależnie od czasu trwania naciśnięcia resetujemy zmienne
    End If
    'to samo dla drugiego switcha
    If S2 = 1 Then
    If Long2 = 0 Then If T2 > Min_pulse Then Gosub Krotkie_2
    If Long2 = 1 Then If O2 = 1 Then O2=0 Else O2=1
    T2 = 0
    Long2 = 0
    End If
    
    

    a w podprogramie do obsługi długiego przyciśnięcia:
    
    'Print "Dlugie_n"
    If O1 = 1 then
    For A = 0 To Val_push Step 1
    If Pwm1a < 255 Then Incr Pwm1a
    Next A
    else
    For A = 0 To Val_push Step 1
    If Pwm1a > 0 Then Decr Pwm1a
    Next A
    end if 
    

    Nie sprawdzałem na sprzęcie czy to działa, bo jeszcze do niego nie miałem dostępu dzisiaj ;-)
REKLAMA