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

PWM programowy 8-bit + ADC 10-bit = problem z obróbką danych

kinderm 15 Mar 2007 17:05 4878 9
REKLAMA
  • #1 3681264
    kinderm
    Poziom 14  
    Posty: 167
    Pomógł: 2
    Ocena: 33
    Witam!

    Muszę napisać program do mikrokontrolera ATmega8. Ma on być kontrolerem PWM żarówki (12V/35W) do stolika nocnego.

    Mam taki oto problem: nie wiem jak przekształcić dane pobierane z ADC. Wychodzą w postaci 10-bit (0-1023), a PWM (200Hz) jest 8-bit (0-255). Sam coś próbowałem tworzyć, ale mi nie wychodzi. Regulacja ma być wykonywana potencjometrem w PWM 20%-100%. Problem jest prawdopodobnie w kilku ostatnich linijkach kodu (Bascom).

    Kod, na którym próbowałem coś zrobić:
    $regfile = "m8def.dat"
    $crystal = 8000000                                          'Procesor taktowany 8MHz
    
    Config Pinc.5 = Output
    Bulb Alias Portc.5
    
    Dim Cycle As Byte
    Dim Bright As Byte
    Dim Temp As Word
    Dim Max_bright As Eram Word
    Dim Min_bright As Eram Word
    
    Config Adc = Single , Prescaler = Auto , Reference = Avcc
    
    Config Timer0 = Timer , Prescale = 1
    On Timer0 T0_int
    Counter0 = 100
    Enable Timer0
    Enable Interrupts
    Start Timer0
    
    Do
    Loop
    
    T0_int:
    
    Counter0 = 100
    
    If Cycle < Bright Then
       Set Bulb
    Else
       Reset Bulb
    End If
    
    If Cycle < 253 Then
       Incr Cycle
    Else
       Cycle = 0
    End If
    
    Temp = Getadc(4)
    If Temp > Max_bright Then Max_bright = Temp       'coś w rodzaju kalibracji
    If Temp < Min_bright Then Min_bright = Temp
    Temp = Temp - 512
    Temp = Temp / 2
    If Temp < 50 Then Temp = 0                                  'regulacja pwm od 20%
    Bright = Temp
    
    Return


    Proszę o pomoc w rozwiązaniu problemu. Schemat połączenia jest w załączniku.

    Pozdrawiam.
    Załączniki:
    • PWM programowy 8-bit + ADC 10-bit = problem z obróbką danych schemat.PNG (11.89 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • REKLAMA
  • #2 3681557
    pubus
    Poziom 30  
    Posty: 1289
    Pomógł: 139
    Ocena: 32
    Najprościej to zrobić tak...
    pwm = 0.2*adc+50...
    pwm to wartość odpowiadająca za wypełnienie wstawiana do rejstru timera... np. OCR2...
    adc to wartość odczytana z rejestrów przetwornika bez przeliczania na napięcie... 0-1023...
  • REKLAMA
  • #3 3684893
    kinderm
    Poziom 14  
    Posty: 167
    Pomógł: 2
    Ocena: 33
    Zapomniałem dodać, że potencjometr do +5V jest 4,7kohm.

    Niestety Twój kod nie zadziała. Ten rezystor do masy równolegle z potencjometrem tworzy dzielnik napięcia (zależnie od ustawienia potencjometru daje 2,5V - 5V). Odczyty ADC, jakich się spodziewam, to około 512 przy minimalnym ustawieniu i ok. 1023 przy maksymalnym.

    Pozdrawiam.

    PS: Dzisaj męczyłem uC tym oto kodem:
    $regfile = "m8def.dat"
    $crystal = 8000000                                          'Procesor taktowany 8MHz
    
    Config Pinc.5 = Output
    Bulb Alias Portc.5
    
    Dim Cycle As Byte
    Dim Bright As Byte
    Dim Temp As Word
    
    Config Adc = Single , Prescaler = Auto , Reference = Avcc
    
    Config Timer0 = Timer , Prescale = 1
    On Timer0 T0_int
    Counter0 = 100
    Enable Timer0
    Enable Interrupts
    Start Timer0
    
    Do
    Loop
    
    T0_int:
    
    Counter0 = 100
    
    If Cycle < Bright Then
       Set Bulb
    Else
       Reset Bulb
    End If
    
    If Cycle < 253 Then
       Incr Cycle
    Else
       Cycle = 0
    End If
    
    Temp = Getadc(4)
    Temp = Temp - 512
    Temp = Temp / 2
    If Temp > 50 Then Bright = Temp
    
    Return
  • #4 3685114
    M. S.
    Poziom 34  
    Posty: 2107
    Pomógł: 259
    Ocena: 680
    Cytat:
    Note that ADC channels ADC4 and ADC5 are limited to 8-bit accuracy.

    Znaczysię kanał 4 i 5 mają rozdzielczość 8 bitową, więc potencjometr pomiędzy masę i Vcc, ślizgacz na ADC4 lub ADC5, napięcie odniesienia 5V, bezpośrednie podstawienie np. Pwm1a = Getad (4).
    Aaaaaa! Kolega realizuje PWM programowy?
    Nie lepiej sprzętowo!
    Poniżej program dla ATMEGA8 demonstrukący działanie i konfigurację timerów w trybie PWM.
    $regfile = "m8def.dat"
    $crystal = 8000000
    Config Lcd = 16 * 2
    Config Lcdpin = Pin , Db4 = Portd.6 , Db5 = Portd.5 , Db6 = Portb.7 , Db7 = Portb.6 , E = Portd.7 , Rs = Portb.0
    Config Pinb.1 = Output
    Config Pinb.2 = Output
    Config Pinb.3 = Output
    Cursor Off
    Config Timer1 = Pwm , Pwm = 8 , Compare A Pwm = Clear Up , Compare B Pwm = Clear Down , Prescale = 8
    Config Timer2 = Pwm , Async = Off , Prescale = 8 , Pwm = On , Compare Pwm = Clear Down
    Dim I As Byte
    Do
    For I = 0 To 255
    Pwm1a = I
    Pwm1b = I
    Ocr2 = I
    Cls
    Lcd "I= ";I
    Waitms 10
    Next I
    For I = 255 To 0 Step -1
    Pwm1a = I
    Pwm1b = I
    Ocr2 = I
    Cls
    Lcd "I= " ; I
    Waitms 10
    Next I
    Loop
    End
  • REKLAMA
  • #5 3685213
    crazy_phisic
    VIP Zasłużony dla elektroda
    Posty: 2244
    Pomógł: 278
    Ocena: 131
    Masz do zrobienia prostą operację, ponieważ i tak korzystasz tylko z 9 bitów więc od rezultatu pomiaru odejmujesz 512 (wspomnianą połowę zakresu), otrzymujesz zatem zakres: 512(0) - 1023(511) czyli 511 kroków z ADC, teraz musisz stworzyć takie przekształcenie które z 0 ADC wyprodukuje 0 PWM i z max ADC czyli 511 da 255 dla PWM. Jest to zwykłe równanie prostej przechodzącej przez (0.0) o wzorze y(PWM)=a*x(ADC), stąd współczynnik proporcjonalności wynosi a=y/x, a=255/511=0.499

    Procedura wygląda tak
    ADC=ADC-512
    PWM=0.499*ADC

    Ponieważ ograniczasz zakres PWM do 80% zatem również wypełnienie będzie zmieniać się w zakresie od 51 do 255 (20% do 100%)
    zatem we wzorze nie mamy 255 tylko 204 a zatem współczynnik wynosi
    a=204/511=0.399 a procedura:
    ADC=ADC-512
    PWM=0.399*ADC+51
  • #6 3687848
    kinderm
    Poziom 14  
    Posty: 167
    Pomógł: 2
    Ocena: 33
    Ale głupi błąd zrobiłem :/ . Aż mi wstyd :| . Zapomniałem dodać na początku komendy "Start Adc".

    Mam teraz taki program (zmieniłem na regulację 10% - 100%):
    $regfile = "m8def.dat"
    $crystal = 8000000                                          'Procesor taktowany 8MHz
    
    Config Pinc.5 = Output
    Bulb Alias Portc.5
    Config Pinc.4 = Input
    
    Dim Cycle As Byte
    Dim Bright As Byte
    Dim Adc_read As Word
    
    Config Adc = Single , Prescaler = Auto , Reference = Avcc
    Start Adc
    
    Config Timer0 = Timer , Prescale = 1
    On Timer0 T0_int
    Counter0 = 100
    Enable Timer0
    Enable Interrupts
    Start Timer0
    
    Do
    Adc_read = Getadc(4)
    If Adc_read > 511 Then
       Adc_read = Adc_read - 512
       Adc_read = Adc_read / 2
       If Adc_read > 25 Then
          Bright = Adc_read
       Else
          Bright = 25
       End If
    Else
       Bright = 25
    End If
    Loop
    
    T0_int:
    
    Counter0 = 100
    
    If Cycle < Bright Then
       Set Bulb
    Else
       Reset Bulb
    End If
    
    If Cycle < 253 Then
       Incr Cycle
    Else
       Cycle = 0
    End If
    
    Return


    Działa, ale ma jedną wadę: regulacja zaczyna się od około 1/8 obrotu potencjometru - wcześniej jest martwy punkt.

    :arrow: Crazy_phisic: Możesz mi wyjaśnić, jak mam zrobić mnożenie przez 0,399 w Bascom? Kompilator (a raczej symulator) marudzi. W końcu to liczba z przecinkiem.

    :arrow: M. S.: Nie lepiej sprzętowo, bo uczę się do trochę większego projektu, który wymaga więcej wyjść PWM, niż 2.

    Przy żarówce i układzie (albo transformatorze) słychać cichutkie brzęczenie. Wydaje mi się, że to coś ok. 200Hz. Nie da się tych odgłosów pracy wyeliminować?

    Pozdrawiam i dziękuję za pomoc.
  • REKLAMA
  • #7 3687880
    tzok
    VIP Zasłużony dla elektroda
    Posty: 38940
    Pomógł: 3179
    Ocena: 6568
    Nie 0,399 a 0.399 ;) i ew. jawnie rzutuj to na integer... ale tak na marginesie to... można to było zrobić na jednym NE555 :)
  • #8 3688190
    kinderm
    Poziom 14  
    Posty: 167
    Pomógł: 2
    Ocena: 33
    Ej no... podejrzewasz mnie o taką niewiedzę?

    Jak uruchamiam układ z kodem poniżej (skopiowałem treść głównego programu z pętli Do...Loop), to żarówka świeci jakby była ok. 80% PWM i zmiana położenia potencjometru nic nie zmienia.

    Adc_read = Getadc(4)
    Adc_read = Adc_read - 512
    Adc_read = Adc_read * 0.399
    Adc_read = Adc_read + 51
    Bright = Adc_read


    O co chodzi z tym rzutowaniem na integer?

    Pozdrawiam.
  • Pomocny post
    #9 3688604
    crazy_phisic
    VIP Zasłużony dla elektroda
    Posty: 2244
    Pomógł: 278
    Ocena: 131
    A przypadkiem zmienna Adc_read nie jest typu całkowitego (np Word)?? Jeżeli pomnożymy przez coś co nie jest całkowite, to domyślnie kompilator obetnie część "zaogonkową". Jednym słowem dla kompilatora to co wcześniej napisałeś wygląda tak

    Adc_read = Getadc(4)
    Adc_read = Adc_read - 512
    Adc_read = Adc_read * 0
    Adc_read = Adc_read + 51
    Bright = Adc_read

    jeśli zmienisz typ na zmiennoprzecinkowy wtedy operacja będzie działać poprawnie. A przed wpisaniem wyniku do zmiennej Bright obetniesz wynik do wartości całkowitej (zrzutujesz).
  • #10 3689533
    kinderm
    Poziom 14  
    Posty: 167
    Pomógł: 2
    Ocena: 33
    Działa! Dzięki wszystkim za pomoc, a szczególnie Crazy_phisic. Zrobiłem regulację PWM 10%-100% (20%-owy minimalny próg był ciut za wysoki). Kod poprawnie działający:
    
    $regfile = "m8def.dat"
    $crystal = 8000000                                          'Procesor taktowany 8MHz
    
    Config Pinc.5 = Output
    Bulb Alias Portc.5
    Config Pinc.4 = Input
    
    Dim Cycle As Byte
    Dim Bright As Byte
    Dim Adc_read As Single
    Dim Temp As Word
    Dim I As Byte
    
    Config Adc = Single , Prescaler = Auto , Reference = Avcc
    Start Adc
    
    Config Timer0 = Timer , Prescale = 1
    On Timer0 T0_int
    Counter0 = 100
    Enable Timer0
    Enable Interrupts
    Start Timer0
    
    Do
    Temp = Getadc(4)
    Adc_read = Temp
    Adc_read = Adc_read - 512
    Adc_read = Adc_read * 0.45
    Adc_read = Adc_read + 26
    Bright = Adc_read
    Loop
    
    T0_int:
    
    Counter0 = 100
    
    If Cycle < Bright Then
       Set Bulb
    Else
       Reset Bulb
    End If
    
    If Cycle < 253 Then
       Incr Cycle
    Else
       Cycle = 0
    End If
    
    Return


    Pozdrawiam i jeszcze raz dziękuję za pomoc.

Podsumowanie tematu

LABEL_AI_GENERATED
Dyskusja dotyczy problemu przetwarzania danych z 10-bitowego przetwornika ADC mikrokontrolera ATmega8 na 8-bitowy sygnał PWM o częstotliwości 200 Hz, służący do regulacji jasności żarówki 12V/35W za pomocą potencjometru. Użytkownik miał trudności z konwersją wartości ADC (0-1023) na zakres PWM (0-255) w języku Bascom. Wskazano, że należy przesunąć zakres ADC, odejmując połowę wartości (512), a następnie przeskalować wynik do zakresu PWM, uwzględniając minimalny próg jasności (np. 10-20%). Podkreślono konieczność użycia zmiennych typu zmiennoprzecinkowego (Single) do poprawnego mnożenia przez współczynnik skalujący (np. 0,399 lub 0,45) oraz rzutowania wyniku na typ całkowity przed przypisaniem do zmiennej sterującej PWM. Zaproponowano także alternatywę w postaci sprzętowego PWM z wykorzystaniem timerów mikrokontrolera. Ostatecznie przedstawiono działający kod Bascom, który realizuje regulację PWM w zakresie 10%-100% jasności, poprawnie odczytując i przetwarzając sygnał z potencjometru podłączonego do kanału ADC4.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA