Krótkie kody. Może komuś akurat się przydadzą.
$regfile = "m32def.dat" $crystal = 16000000 $hwstack = 64 $swstack = 32 $framesize = 128 '*********************************************************** '* * '* PROSTY MIERNIK CZĘSTOTLIWOŚCI, WYPEŁNIENIA I CZASÓW * '* * '* BARTek niveasoft * '*********************************************************** Config Lcd = 20x4 Config Lcdpin = Pin , Rs = Portb.4 , E = Portb.3 , Db4 = Portb.2 , Db5 = Porta.0 , Db6 = Porta.1 , Db7 = Porta.2 Cursor Off , Noblink Cls Config Timer1 = Counter , Prescale = 64 , Capture_edge = Falling , Noise_cancel = 1 '262ms max Config Timer2 = Timer , Prescale = 1024 , Compare = Disconnect , Clear_timer = 1 Enable Compare2 : On Compare2 Przerwanie_timer2 Nosave Compare2 = 155 '10ms @16MHz/1024 Dim Miliseconds As Byte , 500ms As Byte , 1000ms As Byte Dim Mig As Byte , Helpb As Byte Dim New_tim As Word , Old_tim As Word , New_hi As Word , New_lo As Word , Period As Word Dim New_impuls As Byte , Countdown As Byte , Copy_hi As Word , Copy_lo As Word Dim Helpd As Dword , Freq As Dword , Time_hi As Dword , Time_lo As Dword , Duty As Dword Config Portd.6 = Input : Signal_in Alias Pind.6 On Capture1 Capture_isr Nosave Enable Capture1 Enable Interrupts Const Edge_fall = &B10000011 Const Edge_rise = &B11000011 Const Ticks = _xtal / 64 ' for 16MHz = 250 000 Countdown = 1 Do If New_impuls = 1 Then New_impuls = 0 If Countdown = 0 Then Cls Countdown = 10 End If If 500ms = 1 Then 500ms = 0 Locate 1 , 16 If Mig = 0 Then Mig = 1 : Lcd "*" Else Mig = 0 : Lcd " " End If If Countdown > 0 Then Decr Countdown If Countdown = 0 Then Locate 1 , 1 : Lcd "No signal..." ; Spc(4) Else Copy_hi = New_hi Copy_lo = New_lo Helpd = Copy_hi + Copy_lo 'for 1kHz = 250 'obliczanie wypelnienia Duty = Copy_hi : Duty = Duty * 100 Duty = Duty / Helpd Locate 2 , 8 : Lcd "Duty=" ; Duty ; "% " 'oblicznie freq Shift Helpd , Left , 6 'for 1kHz = 16000 Freq = _xtal / Helpd : Locate 2 , 1 : Lcd Freq ; "Hz " Shift Copy_hi , Left , 2 Shift Copy_lo , Left , 2 Locate 1 , 3 : Lcd Copy_hi ; "us/" ; Copy_lo ; "us" End If End If End If Loop End Capture_isr: $asm !PUSH R16 !PUSH R17 !PUSH R20 !PUSH R21 !PUSH R23 !PUSH R24 !PUSH R25 !PUSH R26 !PUSH R27 !in R24, sreg !PUSH R24 $end Asm If Signal_in = 0 Then New_hi = Capture1 - Old_tim Tccr1b = Edge_rise Else New_lo = Capture1 - Old_tim New_impuls = 1 Tccr1b = Edge_fall End If Old_tim = Capture1 ' Tuned with NoSave Tool 1.10 $asm !POP R24 !out sreg, r24 !POP R27 !POP R26 !POP R25 !POP R24 !POP R23 !POP R21 !POP R20 !POP R17 !POP R16 $end Asm Return Przerwanie_timer2: $asm !PUSH R16 !PUSH R24 !PUSH R26 !PUSH R27 !in R24, sreg !PUSH R24 $end Asm If Miliseconds < 49 Then Incr Miliseconds Else Miliseconds = 0 500ms = 1 End If ' Tuned with NoSave Tool 1.10 $asm !POP R24 !out sreg, r24 !POP R27 !POP R26 !POP R24 !POP R16 $end Asm Return
$regfile = "m328pdef.dat" $crystal = 8000000 $hwstack = 64 $swstack = 32 $framesize = 128 'ROBOT DYN 6-DIGIT LED DISPLAY ' 6x50Hz=300Hz refresh rate ~3ms period Rck_pin Alias Portd.0 : Config Rck_pin = Output Clk_pin Alias Portd.1 : Config Clk_pin = Output Dio_pin Alias Portd.2 : Config Dio_pin = Output Dim Arr(2) As Byte , Mux As Byte Dim Muxw As Word At Arr(1) Overlay ' 321 654 Arr(1) = &B01110111 Arr(2) = &B10111111 ' ' *GFEDCBA ' A ' _ ' F|_|B ' E|_|C ' D ' Do Incr Mux : If Mux > 6 Then Mux = 1 Arr(1) = Lookup(mux , Displays) Arr(2) = Lookup(mux , Digits) Shiftout Dio_pin , Clk_pin , Muxw , 0 Set Rck_pin : Reset Rck_pin Waitms 3 Loop End Displays: ' dummy 1 2 3 4 5 6 Data &B00000000 , &B00010000 , &B00100000 , &B01000000 , &B00000001 , &B00000010 , &B00000100 Digits: ' 0 1 2 3 4 5 Data &B11000000 , &B11111001 , &B10100100 , &B10110000 , &B10011001 , &B10010010 ' 6 7 8 9 10 blank Data &B10000010 , &B11111000 , &B10000000 , &B10010000 , &B11111111
Przy okazji pokazuje, że kiedy jest już w jakimś języku gotowy kod do obsługi danego mikroukładu to można go z powodzeniem dostosować do Bascom.
...i działa
wystarczy napisać Initialize(Frequency, NodeID,NetID),
a potem można po prostu używać Send(Dane)
'************************************************** '* GENERATOR PROGRAMOWANY Z KLAWIATURY 4x4 * '* * '* WPISUJESZ CZĘSTOTLIWOŚĆ I ZATWIERDZASZ * '* DŁUGIM WCIŚNIĘCIEM "A" * '* * '* ZA WYPEŁNIENIE PROCENTOWE ODPOWIEDZIALANA JEST * '* ZMIENNA "PROC" * '* KLIK W "B" ZWIĘKSZA WYPEŁNIENIE * '* KLIK W "C" ZMNIEJSZA WYPEŁNIENIE * '* * '* by BARTek w Niedzielę (błąd mógł się wkraść) * '* * '* ver 1.02 2017 * '************************************************** $regfile = "m328Pdef.dat" $crystal = 16000000 $hwstack = 80 $swstack = 80 $framesize = 80 Config Submode = New Config Kbd = Portd , Debounce = 50 , Delay = 50 'W1-PA0, K4-PA7 Config Portb.1 = Output : Gen Alias Portb.1 Config Portb.0 = Output : Neg_gen Alias Portb.2 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0 Config Lcd = 16x2 Cursor Off Cls Config Timer2 = Timer , Prescale = 1024 , Compare_a = Disconnect , Clear_timer = 1 Compare2a = 155 '10ms @16MHz Dim 10ms As Byte , Ticks As Byte Dim Keyread As Byte , Key As String * 1 , Lock As Byte Dim Got_key As Byte Dim Status As Byte , Value_str As String * 10 , Pos As Byte , Lenght As Byte Dim Value As Dword , Ustaw As Word , Prescaler As Word , Help_d As Dword Dim Started As Byte , Wypelnienie As Word , Presc As Byte , Proc As Word Const Ticks_max = 25 Const Freq = _xtal 'Config Portd.5 = Output Proc = 20 'taka początkowa wartośc, można wpisać 50 Sub Calc_duty() Help_d = Value * Proc Help_d = Help_d / 100 Wypelnienie = Help_d Ocr1a = Wypelnienie Call Show_duty() End Sub Sub Show_duty() Locate 2 , 1 : Lcd "D=" ; Proc ; "%" If Proc < 10 Then Lcd " " End Sub Enable Interrupts Dim Wartosc_dla_tccr1b As Byte '---------------------------------------------------------------- 'TCCR1A -> |COM1A1| COM1A0 COM1B1 COM1B0 FOC1A FOC1B WGM11 WGM10 FOR MEGA32 MEGA8 'TCCR1B -> | ICNC1| ICES1 -- WGM13 WGM12 CS12 CS11 CS10 '---------------------------------------------------------------- '---------------------------------------------------------------- 'TCCR1A -> |COM1A1 COM1A0 COM1B1 COM1B0 -- -- WGM11 WGM10 'TCCR1B -> | ICNC1 ICES1 -- WGM13 WGM12 CS12 CS11 CS10 FOR MEGA328P 'TCCR1C -> | FOC1A FOC1B -- -- -- -- -- -- '---------------------------------------------------------------- ' WGM13-| Const Pwm_presc1 = &B00010001 'for TCCR1B Const Pwm_presc8 = &B00010010 Const Pwm_presc64 = &B00010011 Const Pwm_presc256 = &B00010100 Const Pwm_presc1024 = &B00010101 Const Set_output_mode = &B10100000 'for TCCR1A Locate 2 , 13 : Lcd "0 Hz" Call Show_duty() Do 'procedura odczytu klawiatury 4x4 z detekcja długiego przycisnięcia ;) '---------------------------------------------------------------------- If Tifr2.ocf2a = 1 Then Tifr2.ocf2a = 1 'flagę kasuje się wpisując jedynkę Keyread = Getkbd() If Keyread <> 16 Then If Lock = 0 Then Key = Lookupstr(keyread , Dta) Select Case Keyread Case 0 To 11 Lock = 1 : Got_key = 1 Case 12 To 15 If Ticks < Ticks_max Then Incr Ticks If Ticks = Ticks_max Then If Key = "A" Then Key = "E" If Key = "B" Then Key = "F" If Key = "D" Then Key = "G" Lock = 1 : Got_key = 1 Ticks = 0 End If End If Case 14 To 15 Lock = 1 : Got_key = 1 End Select End If Else Lock = 0 If Ticks > 0 Then Ticks = 0 Got_key = 1 End If End If End If '----------------------------------------------------------------------- 'mamy coś z klawiatury... '----------------------------------------------------------------------- If Got_key = 1 Then Got_key = 0 Select Case Key Case "0" To "9" If Started <> 1 Then If Started = 2 Then Started = 0 Value_str = "" Locate 2 , 1 : Lcd Spc(12) ; "0" End If Lenght = Len(value_str) If Lenght < 7 Then Value_str = Value_str + Key Pos = 13 - Lenght Locate 2 , Pos : Lcd Value_str End If End If Case "E" 'E oznacza długie nacisniecie A Help_d = Val(value_str) If Help_d > 0 Then Value = Freq 'przepisz Freq procka do zmiennej Select Case Help_d Case 1 '256 Wartosc_dla_tccr1b = Pwm_presc256 Shift Value , Right , 8 Presc = 25 'nie miesci sie 256 Case 2 To 15 '64 Wartosc_dla_tccr1b = Pwm_presc64 Shift Value , Right , 6 Presc = 64 Case 16 To 122 '16000000Hz/131072 = 15Hz ' '8 Wartosc_dla_tccr1b = Pwm_presc8 Shift Value , Right , 3 Presc = 8 Case Else '16000000Hz/131072 = 122Hz '1 Wartosc_dla_tccr1b = Pwm_presc1 Presc = 1 End Select Value = Value \ Help_d 'rzeczywistą po preskalerze podziel przez żądaną Shift Value , Right , 1 'podziel na pół (skróć bo stan odwraca się raz na okres) Decr Value : Ustaw = Value 'odejmij jeden Call Calc_duty() Home : Lcd Spc(16) Home : Lcd "Pre=" ; Presc ; " Val=" ; Value 'info Icr1 = Ustaw Tccr1a = Set_output_mode 'set count up, reset count down Tccr1b = Wartosc_dla_tccr1b 'ustaw prescaler i WGM13 Started = 1 End If Case "B" If Proc < 99 Then Incr Proc Call Show_duty() Call Calc_duty() End If Case "C" If Proc > 1 Then Decr Proc Call Show_duty() Call Calc_duty() End If Case "D" 'krótkie nacisniecie D wyłacza sygnał Config Timer1 = Timer , Prescale = 1 , Compare_a = Disconnect , Compare_b = Disconnect , Clear_timer = 1 Stop Timer1 Reset Gen If Started = 1 Then Started = 2 Else Value_str = "" Locate 2 , 1 : Lcd Spc(12) ; "0" Call Show_duty() End If End Select End If Portb.0 = Not Portb.1 Loop End Dta: Data "1" , "4" , "7" , "*" , "2" , "5" , "8" , "0" , "3" , "6" , "9" , "#" , "A" , "B" , "C" , "D"
$regfile = "m32def.dat" $crystal = 16000000 $hwstack = 80 $swstack = 80 $framesize = 80 Config Submode = New Config Kbd = Porta , Debounce = 50 , Delay = 50 'W1-PA0, K4-PA7 Config Portd.5 = Output : Gen Alias Portd.5 Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.1 , Rs = Portc.0 Config Lcd = 16x2 Cursor Off Cls Config Timer2 = Timer , Prescale = 1024 , Compare = Disconnect , Clear_timer = 1 Compare2 = 155 '10ms @16MHz Dim 10ms As Byte , Ticks As Byte Dim Keyread As Byte , Key As String * 1 , Lock As Byte Dim Got_key As Byte Dim Status As Byte , Value_str As String * 10 , Pos As Byte , Lenght As Byte Dim Value As Dword , Ustaw As Word , Prescaler As Word , Help_d As Dword Dim Started As Byte Const Ticks_max = 25 Const Freq = _Xtal 'obliczenia będą automatyczne do zastosowanego rezonatora Config Portd.5 = Output Enable Interrupts Locate 2 , 13 : Lcd "0 Hz" Do 'procedura odczytu klawiatury 4x4 z detekcja długiego przycisnięcia ;) '---------------------------------------------------------------------- If Tifr.ocf2 = 1 Then Tifr.ocf2 = 1 'flagę kasuje się wpisując jedynkę Keyread = Getkbd() If Keyread <> 16 Then If Lock = 0 Then Key = Lookupstr(keyread , Dta) Select Case Keyread Case 0 To 11 Lock = 1 : Got_key = 1 Case 12 To 15 If Ticks < Ticks_max Then Incr Ticks If Ticks = Ticks_max Then If Key = "A" Then Key = "E" If Key = "B" Then Key = "F" If Key = "D" Then Key = "G" Lock = 1 : Got_key = 1 Ticks = 0 End If End If Case 14 To 15 Lock = 1 : Got_key = 1 End Select End If Else Lock = 0 If Ticks > 0 Then Ticks = 0 Got_key = 1 End If End If End If '----------------------------------------------------------------------- 'mamy coś z klawiatury... '----------------------------------------------------------------------- If Got_key = 1 Then Got_key = 0 Select Case Key Case "0" To "9" If Started <> 1 Then If Started = 2 Then Started = 0 Value_str = "" Locate 2 , 1 : Lcd Spc(12) ; "0" End If Lenght = Len(value_str) If Lenght < 7 Then Value_str = Value_str + Key Pos = 13 - Lenght Locate 2 , Pos : Lcd Value_str End If End If Case "E" 'E oznacza długie nacisniecie A Help_d = Val(value_str) If Help_d > 0 Then Select Case Help_d Case 1 To 3 '256 Config Timer1 = Timer , Prescale = 256 , Compare_a = Toggle , Compare_b = Disconnect , Clear_timer = 1 Value = Freq Shift Value , Right , 8 Case 4 To 29 '64 Config Timer1 = Timer , Prescale = 64 , Compare_a = Toggle , Compare_b = Disconnect , Clear_timer = 1 Value = Freq Shift Value , Right , 6 Case 30 To 243 '8 Config Timer1 = Timer , Prescale = 8 , Compare_a = Toggle , Compare_b = Disconnect , Clear_timer = 1 Value = Freq Shift Value , Right , 3 Case Else '1 Config Timer1 = Timer , Prescale = 1 , Compare_a = Toggle , Compare_b = Disconnect , Clear_timer = 1 Value = Freq End Select Value = Value \ Help_d Shift Value , Right , 1 Decr Value : Ustaw = Value Home : Lcd Value ; Spc(6) 'wyswietla obliczona wartość (dla porównania z kalkulatorami - Debug)' Compare1a = Ustaw Started = 1 End If Case "D" 'krótkie nacisniecie D wyłacza sygnał Config Timer1 = Timer , Prescale = 1 , Compare_a = Disconnect , Compare_b = Disconnect , Clear_timer = 1 Stop Timer1 Reset Gen If Started = 1 Then Started = 2 Else Value_str = "" Locate 2 , 1 : Lcd Spc(12) ; "0" End If End Select End If Loop End Dta: Data "1" , "4" , "7" , "*" , "2" , "5" , "8" , "0" , "3" , "6" , "9" , "#" , "A" , "B" , "C" , "D"
Diody mają nieliniową charakterystykę jasności świecenia w stosunku do prądu jaki przez nie przepuszczasz (tak w skrócie).
Kiedy PWM ma wartość 1, 2, 3, 4 czyli niską to przyrost jasności w tych pierwszych krokach jest duży. Póżniej jednak "dokładanie" już zmienia niewiele.
Taki prosty, liniowy PWM powodowałby że ściemnianie i rozświetlanie diody nie wyglądałoby tak jak teraz tylko szybko by się rozjaśniała a przy wyłączaniu długo długo nic by się nie działo po czym by zgasła..
Tak więc wpadłem na pomysł by wydłużać odstępy pomiędzy zmianami PWM kiedy jego wartość jest niska, a przyspieszać zmiany kiedy wartości są większe.
Teraz trochę prostej matematyki
Kiedy PWM ma wartość 1 to odejmuję 1 od 127 czyli mam 126
Żeby uzyskać efekt "do kwadratu" mnożę ten wynik razy 2
Zamiast mnożenia razy 2 użyłem szybszego Shift
Wartość 127 bierze się z tego że kiedy PWM ma wartość 0 to wynik (127) nawet pomnożony razy dwa (254) mieści się w bajcie.
Ta wartość jest wyliczana po to by odliczać czas pomiędzy zmianami wartości PWM. Kiedy PWM ma wartość 0 to odczekać należy 254 tick`i zegara. Kiedy ma wartość 99 to wystarczy już 56 ticków by wartość się zmieniła.
Przeliczeń dokonuję wtedy kiedy wartości się zmieniają. Czyli jeśli wartość PWM dojdzie do 100 lub do 0 to już wartość ta zgadza się z poprzednim odczytem (Old_val) i nie są dokonywane dalsze obliczenia.
Shift potrafi dzielić lub mnożyć przez potęgę liczby 2. Shift Left 1 oznacza "pomnóż przez dwa"
$regfile = "m8def.dat" $crystal = 8000000 $hwstack = 80 $swstack = 64 $framesize = 64 Ddrb = &B00011111 : Portb = &B11100000 Ddrc = &B00111100 : Portc = &B00000000 Ddrd = &B11111111 : Portd = &B00000000 Config Timer2 = Timer , Prescale = 8 , Compare = Disconnect , Clear Timer = 1 Enable Compare2 : On Compare2 Tim2_isr : Compare2 = 124 Enable Interrupts '****************** Zmienne ******************* '*********************************************** Dim Licznik As Byte Dim Led(3) As Byte 'pomocnicza Dim Z As Byte D1 Alias Portc.2 : D2 Alias Portc.3 : D3 Alias Portc.4 '*************** przyciski ***************** Sw1 Alias Pinb.5 Dim Leds_pwm As Byte , M As Byte , Speed As Byte , Old_val As Byte Do If Speed = 0 Then If Sw1 = 0 Then If Leds_pwm < 100 Then Incr Leds_pwm Else If Leds_pwm > 0 Then Decr Leds_pwm End If If Old_val <> Leds_pwm Then 'jesli wartość różni się od poprzedniej Old_val = Leds_pwm ' to zapamiętaj tę wartość Speed = 127 - Leds_pwm ' odejmij aktualną wartość PWM od 127 Shift Speed , Left , 1 ' Pomnóż razy dwa M = Memcopy(leds_pwm , Led(1) , 3 , 2) ' skopiuj obliczenia do trzech bajtów End If End If Loop '************ Przerwanie timer 0 *************** '*********************************************** Tim2_isr: Incr Licznik If Licznik = 100 Then Licznik = 0 If Licznik < Led(1) Then Set D1 Else Reset D1 If Licznik < Led(2) Then Set D2 Else Reset D2 If Licznik < Led(3) Then Set D3 Else Reset D3 If Speed > 0 Then Decr Speed Return
Memcopy to wykonywane maszynowo, bardzo szybkie kopiowanie pomiędzy tablicami (zbiór bajtów o tej samej nazwie tylko z indeksami) Można więc szybko skopiować jedną wartość do tych trzech Led(1), Led(2) i Led(3) Memcopy potrzebuje jednej jakiejś pomocniczej zmiennej która musisz postawić przed nią. Użyłem "M" a normalnie wykorzystuję jakiś podręczny pomocniczy Bajt H_byte. Memcopy ma kilka możliwych konfiguracji. Użyłem takiej która skopiuje 1 bajt do trzech kopiując jeden i ten sam wzorzec (czyli Leds_pwm do trzech bajtów)
$regfile = "m32adef.dat" $crystal = 8000000 $hwstack = 128 $swstack = 128 $framesize = 128 Config Submode = New Config Lcd = 16x2 Config Lcdpin = Pin , Db4 = Portd.4 , Db5 = Portd.1 , Db6 = Portd.7 , Db7 = Portd.0 , E = Portd.5 , Rs = Portd.6 Deflcdchar 1 , 32 , 32 , 32 , 32 , 32 , 4 , 4 , 21 Deflcdchar 2 , 16 , 16 , 16 , 16 , 16 , 20 , 20 , 21 Deflcdchar 3 , 8 , 8 , 8 , 8 , 8 , 12 , 12 , 29 Deflcdchar 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 21 Deflcdchar 5 , 2 , 2 , 2 , 2 , 2 , 6 , 6 , 23 Deflcdchar 6 , 1 , 1 , 1 , 1 , 1 , 5 , 5 , 21 Cursor Off Noblink Cls ' 16 kratek x 5 pozycji = 80 // Na tyle moze być podzielona skala ' 87,5MHz - 108MHz 1080-875=205 205/80=2,5625 <<<< 256(Shift 8) Dim Value As Word Dim Skala As String * 16 Skala = String(16 , 1) '---------[FOR ENCODER ONNLY]--------------------------------- Config Portd.2 = Input : Set Portd.2 : Encoder_a Alias Pind.2 Config Portb.1 = Input : Set Portb.1 : Encoder_b Alias Pinb.1 Config Portc.7 = Input : Set Portc.7 : Encoder_sw Alias Pinc.7 Config Porta.0 = Output : Relay Alias Porta.0 : Reset Relay Config Int0 = Falling : Enable Int0 : On Int0 Encoder_isr Nosave Dim Encoder_turn_left As Byte , Encoder_turn_right As Byte Enable Interrupts '------------------------------------------------------------- Sub Draw_scale(byval Value As Word) Local Help_w As Word Local Help_b As Byte Local X_pos As Byte Local Char As Byte Local Freq As String * 5 Freq = Str(value) Freq = Format(freq , "00.0") Locate 1 , 4 If Value < 1000 Then Lcd Chr(32) Lcd Freq ; "MHz" Lowerline : Lcd Skala Help_w = Value - 875 Help_w = Help_w * 100 Shift Help_w , Right , 8 Help_b = Help_w X_pos = Help_b / 5 X_pos = X_pos + 1 Char = Help_b Mod 5 Char = Char + 2 Locate 2 , 1 : Lcd Skala Locate 2 , X_pos : Lcd Chr(char) End Sub Value = 892 Call Draw_scale(value) Do If Encoder_turn_right > 0 Then Decr Encoder_turn_right If Value < 1079 Then '79 zero based = 80 Incr Value Call Draw_scale(value) End If End If If Encoder_turn_left > 0 Then Decr Encoder_turn_left If Value > 875 Then Decr Value Call Draw_scale(value) End If End If Loop End Encoder_isr: $asm PUSH R24 !in R24, sreg PUSH R24 $end Asm If Encoder_b = 1 Then Encoder_turn_right = 1 Else Encoder_turn_left = 1 End If ' Tuned with NoSave Tool $asm POP R24 !out sreg, r24 POP R24 $end Asm Return
Dobre baterie tanie nie są, a ładowanie akumulatorków kosztuje czas. Każdy pewnie się ucieszy jeśli jego urządzenie pracowałoby dłużej na bateriach. Postanowiłem pokazać krótki przykład i wyjaśnić o co chodzi.
Mikrokontroler można uśpić tak by brał mniej prądu, ale z niektórych stanów uśpienia mogą go wybudzić tylko przerwanie albo ...reset. Mikrokontroler ma też w sobie zaszyte narzędzie które resetuje go jeśli je włączymy a potem zostawimy same sobie. Taki odliczający sekundnik. Można to wykorzystać. Można go ustawić na maksymalną długość - w przypadku Attiny2313 na około 2 sekundy. Po dwóch sekundach układ się zresetuje.
Normalnie jednak wszystkie zmienne podczas Reset są zerowane. Można temu zapobiec wpisując dyrektywę $noramclear
Teraz wystarczy wziąć jedną zmienną i co dwie sekundy kiedy mikrokontroler się obudzi dodawać jeden. Kiedy nazbiera się odpowiadająca nam wartość dopiero wykonać interesujące nas czynności. Kiedy licznik nie ma jeszcze ustalonej wartości mikrokontroler budzi się, dodaje do licznika i znów zasypia. To go dużo nie kosztuje.
W przykładzie migam diodą co 10 sekund na czas 25ms. Resztę tego czasu mikrokontroler śpi.
Można to wykorzystać na przykład w zewnętrznych nadajnikach zbierających odczyty temperatury.
$regfile = "ATtiny2313.dat" $crystal = 8000000 $hwstack = 32 $swstack = 16 $framesize = 32 $noramclear 'nie czysc zmiennych w SRAM Config Watchdog = 2048 'okolo 2s Config Portd.6 = Output : Set Portd.6 : Led Alias Portd.6 Dim Wake_counter As Byte 'zmienna nie bedzie czyszczona przy RESET 'tutaj od nowa startuje program Incr Wake_counter If Wake_counter = 5 Then '5 x 2s = 10s Wake_counter = 0 'tutaj kod do wykonania co okreslony czas 'przykladowe migniecie dioda Reset Led Waitms 20 Set Led End If Start Watchdog Config Powermode = Powerdown End
Każdy kto bawił się tymi czujnikami temperatury nie raz pewnie widział jakieś dziwne chwilowe odczyty.
Pewnie obiło się też o uszy coś takiego jak to, że te czujniki nadają sumę kontrolną.
Jeśli nie chcesz już nigdy więcej oglądać krzaków na LCD to uzależnij przyjęcie danych jako poprawne od tego czy zgadza się suma kontrolna ;) To bardzo proste!
Wystarczy odczytać wszystkie 9 bajtów zamiast zwyczajowych dwóch i porównać ostatni 9 bajt w którym nadawana jest suma kontrolna z obliczeniami jakich dokona Bascom na tych pierwszych ośmiu bajtach. Muszą się zgadzać. Jeśli nie to znaczy że w transmisji nastąpił błąd.
$regfile = "m32def.dat" $crystal = 8000000 $hwstack = 64 $swstack = 32 $framesize = 32 Config Submode = New 'timer Config Timer1 = Timer , Prescale = 256 , Compare_a = Disconnect , Compare_b = Disconnect , Clear_timer = 1 Compare1a = 31249 '1s @8MHz/256 Dim 1s As Byte Dim Bytes(9) As Byte , Sum As Byte Dim T As Integer At Bytes(1) Overlay 'dwa pierwsze bajty tablicy tworzą tez Integer ;) Config 1wire = Portb.1 1wreset 1wwrite &HCC 1wwrite &H44 Do If Tifr.ocf1a = 1 Then 'jesli uplynela sekunda (te flage ustawia Timer) Tifr.ocf1a = 1 'skasuj flage by w petli dokonywalo sie to tylko raz na 1s 1wreset 1wwrite &HCC 1wwrite &HBE Bytes(1) = 1wread(9) 'odczytaj 9 bajtów do tablicy (8 bajtów i CRC obliczone przez czujnik) Sum = Crc8(bytes(1) , 8) 'sam oblicz CRC z ośmiu bajtów If Bytes(8) = &H10 Then 'sprawdzenie czy ósmy bajt zawiera &H10 If Sum = Bytes(9) Then 'jeśli nasze obliczenia zgadzaja się z tymi od czujnika... T = T * 10 T = T \ 16 Home : Lcd T 'należy sobie dorysować przecinek :D End If End If 1wreset 1wwrite &HCC 1wwrite &H44 'przygotuj nastepny odczyt temperatury End If Loop
Pilot przy przytrzymaniu przycisku nie nadaje stale tylko z przerwami ponawia komendę. Sterowany w ten sposób silnik by szarpał a dioda lub żarówka by migała. Poniższy kod utrzymuje pin portu w jednym stanie do momentu puszczenia przycisku pilota.
'Odbiornik RC5 'Wyjścia w stanie niskim tak długo jak trzyma się przycisk $regfile = "attiny13a.dat" '44% Flash $crystal = 9600000 $hwstack = 24 $swstack = 18 $framesize = 16 $lib "mcsbyte.lbx" 'wykorzystamy bibliotekę która ogranicza się do 'uzywania tylko bajtów celem zmniejszenia kodu Config Portb.3 = Output Config Portb.4 = Output 'tu diody ale mogą to być wyjścia Led_r Alias Portb.3 Led_l Alias Portb.4 Set Led_r 'żeby nie włączyć dwóch kierunków na raz Set Led_l Config Rc5 = Pinb.0 Dim Address As Byte 'typ urządzenia np.0-telewizor, 8-sat Dim Command As Byte 'np 17 ciszej,16 głośniej Dim Odczekaj As Byte Const Deelay = 20 Enable Interrupts ' zezwolenie na przerwania '*** START *** Do Getrc5(address , Command) 'sprawdź czy pilot nadaje If Address <> 255 Then 'jeśli Wpisać "If Address=0 Then" Command = Command And &B01111111 'to zawęzimy tylko do telewizorów Select Case Command Case 16 'przycisk pilota + Reset Led_l Odczekaj = Deelay Case 17 'przycisk pilota - Reset Led_r Odczekaj = Deelay End Select End If If Odczekaj <> 0 Then Decr Odczekaj If Odczekaj = 0 Then Set Led_l Set Led_r End If Loop End '*** KONIEC ***
Jeśli chcesz przełączać osiem pinów jednego portu przyciskami innego portu i mieć jeszcze możliwość różnej reakcji na długie i krótkie wciśnięcie to możesz zaadoptować na swoje potrzeby poniższy kod.
W momencie przyciśnięcia przycisku stan portu przycisków jest zapamiętywany w pomocniczej zmiennej Help_byte, ponieważ po puszczeniu przycisku to ona będzie pamiętać który przycisk był wciśnięty i to posłuży do przełączenia żądanego pinu.
Sprawa nie jest skomplikowana i zamyka osiem do szesnastu spraw w jednej procedurze :D
Wyłuskujemy wciśnięty przycisk, ale w całym bajcie ma on wtedy zapis stanu niskiego 0. Do odwrócenia portu poprzez XOR potrzebujemy 1 więc odwracamy stan całego bajtu poprzez Bajt = NOT Bajt i tym sposobem możemy już odwrócić stan wybranego pinu. Długie i krótkie przyciśniecie rozwiązywane jest prosto - jeśli przed puszczeniem przycisku zmienna _counter osiągnie zadaną wartość znaczy, że to długie wciśnięcie.
Dim Przyciski_port As Byte , Help_byte As Byte , _counter As Byte , Wyjscia_port As Byte '*** obsluga przyciskow wołana z petli głównej na przykład co 5ms *** Przyciski: Przyciski_port = Pina If Przyciski_port <> &B11111111 Then Help_byte = Przyciski_port If _counter < 255 Then Incr _counter If _counter = 100 Then Select Case Przyciski_port 'osiem różnych, zależnych od przycisku, kodów do wykonania po długim naciśnieciu End Select 'cos wykonywane zawsze niezależnie który przycisk, na przykład BEEP End If Else If _counter > 1 Then If _counter < 100 Then Help_byte = Not Help_byte Wyjscia_port = Pinb Wyjscia_port = Wyjscia_port Xor Help_byte Portb = Wyjscia_port 'odwraca stan pinu innego portu odpowiadajacy wcisnietemu przyciskowi End If _counter = 0 End If End If Return
Jeśli używasz urządzenia na bateriach to możesz chcieć je wyłączyć, ale na przykład za pomocą jednego przycisku, a nie mechanicznego wyłącznika. Możesz uśpić procesor poleceniami konfigurującymi oszczędzanie energii. Jest ich więcej i nie wszystkie wyłączają Timery, więc wariacji może być więcej, ale tu pokażę te z którego wybudzić procesor może tylko przerwanie zewnętrzne albo Watchdog. Żeby włączanie i wyłączanie działało na tym samym przycisku rozwiązane być musiało tak, że tryb Powerdown i zezwolenie na przerwanie włączane jest dopiero po puszczeniu przycisku.
Dim Lock As Word 'ilosc cykli do ponownego wcisniecia Dim Turn_off As Bit 'flaga ustawiana by zadziałało po puszczeniu Config Portc.5 = Output : Set Portc.5 'dowolny port na przykład podświetlanie LCD Led Alias Portc.5 '+ z procesora, dioda do masy Config Portd.2 = Input : Set Portd.2 'przycisk na wejściu INTx Switch Alias Pind.2 Config Int0 = Low Level 'ważne "LOW LEVEL" On Int0 Int0_isr Enable Interrupts '*** START *** Do If Switch = 0 Then 'jesli wcisnieto przycisk If Lock = 0 Then 'a zmienna już wyzerowana Lock = 10000 'to zablokuj poprzez wpis do zmiennej Turn_off = 1 'ustaw flage by po puszczeniu przycisku zadziałało End If Else If Lock > 0 Then Decr Lock 'jesli blokada to zdejmuj po jednej If Lock = 0 Then 'jesli blokada równa zero If Turn_off = 1 Then 'a byl wcisniety przycisk Turn_off = 0 'to skasuj flage Reset Led 'zgas diode Enable Int0 'dopiero zezwalaj na przerwanie(inaczej ten kod by sie nie wykonal) 'Display Off 'ewentualne wyłaczenie/czyszczenie LCD Config Powermode = Powerdown 'idź spac End If End If End If Loop End '*** KONIEC *** Int0_isr: Disable Int0 'wylacz to przerwanie Set Led 'wlacz diode 'Display On 'wlacz LCD Lock = 10000 'zablokuj automatyczne przejscie znow Return
Jeśli potrzebujesz w swoim projekcie zegar i Twój projekt może pracować na wewnętrznym kwarcu, to możesz zamiast rezonatora podłączyć taki malutki zegarkowy i mieć zegar oraz sygnał upłynięcia sekundy pisząc cztery linijki kodu.
Wpisując "Config Clock = Soft" kompilator użyje Timera2 oraz stworzy zmienne _sec, _min, _hour, _day, _month, i _year do których oczywiście masz dostęp. To naprawdę proste i łatwe do wykorzystania. Twój projekt może pracować normalnie z prędkością wewnętrznego oscylatora. Nie trzeba oczywiście wyświetlać wszystkich danych. Można jedynie korzystać z danych. Instrukcje Date$ i Time$ formatują tylko, po prostu dane tak, że niepotrzebne jest wstawianie zer jeśli dane mniejsze od dziesięciu i układaja je w całość za nas.
$regfile = "m328pdef.dat" $crystal = 8000000 'procesor działa na WEWNĘTRZNYM oscylatorze $hwstack = 64 $swstack = 64 $framesize = 64 Config Clock = Soft 'zamiast "kwarca" podłacz kwarc zegarkowy 32kHz Config Date = Dmy , Separator = / Date$ = "20/11/14" Time$ = "12:00:50" Dim Old_sec As Byte Enable Interrupts '*** START *** Do 'miejsce na kod programu' 'co sekunde lub jak bedziesz chciał sprawdzic czas' If Old_sec <> _sec Then 'jesli zapamietana sekunda rózni sie od aktualnej Old_sec = _sec 'zapamietaj aktualna Locate 1 , 2 : Lcd Date$ ; Spc(2) ; Time$ End If Loop End '*** KONIEC ***
'### TERMOMETR 4xDS18B20 ### $regfile = "m328pdef.dat" $crystal = 8000000 $hwstack = 128 $swstack = 64 $framesize = 64 Config Lcdpin = Pin , Db4 = Portb.2 , Db5 = Portb.3 , Db6 = Portb.4 , Db7 = Portb.5 , E = Portb.1 , Rs = Portc.0 Config Lcd = 20x4 Cursor Off Cls Dim N As Byte Dim T As Integer Dim S As String * 5 Dim Linia As Byte , Pozycja As Byte Do 'jesli piny sa obok siebie to po kolei For N = 4 To 7 'od numeru pinu w porcie do numeru pinu w porcie 1wreset Pind , N 'dzialaj dla aktualnie ustawionego pinu 1wwrite &HCC , 1 , Pind , N 1wwrite &HBE , 1 , Pind , N T = 1wread(2 , Pind , N) T = T * 10 T = T / 16 S = Str(t) S = Format(s , "+0.0") Select Case N 'w zaleznosci od tego ktory to pin Case 4 'ustaw kursor na wlasciwej pozycji Linia = 1 Pozycja = 1 Case 5 Linia = 1 Pozycja = 9 Case 6 Linia = 2 Pozycja = 1 Case 7 Linia = 2 Pozycja = 9 End Select Locate Linia , Pozycja 'rysuj na wybranym miejscu Lcd S ; Chr(223) ; "C " 1wreset Pind , N 'kaz przygotowac kolejny odczyt 1wwrite &HCC , 1 , Pind , N 1wwrite &H44 , 1 , Pind , N Next Wait 1 Loop End
Tak to wygląda. Zdjęcie wykonałem jeszcze na płytce testowej. Napisany z myślą o LCD16x2. Dla temperatur ujemnych pokazuje minus.
'### PODWÓJNY TERMOMETR ### Config Lcdpin = Pin , Db4 = Portd.1 , Db5 = Portd.2 , Db6 = Portd.3 , Db7 = Portd.4 , E = Portd.0 , Rs = Portd.5 Config Lcd = 16 * 2 Cursor Off Cls Config 1wire = Portd.6 Dim Dsid1(8) As Byte , Dsid2(8) As Byte Dim N As Byte Dim T As Integer Dim S As String * 5 Const Blad = " ERROR " 'dla powtarzajacych sie tekstow warto wpisac je tylko raz Dsid1(1) = 1wsearchfirst() Dsid2(1) = 1wsearchnext() Lcd " IN OUT" '*** START *** Do 1wreset 1wwrite &HCC 1wwrite &H44 Waitms 500 Locate 2 , 1 '1wreset ' 1wwrite &H55 1wverify Dsid1(1) '1wreset i 1wwrite &H55 niepotrzebne bo 1wverify samo to przeprowadza If Err = 0 Then 1wwrite &HBE T = 1wread(2) T = T * 10 T = T / 16 S = Str(t) S = Format(s , "+0.0") Lcd S ; Chr(223) ; "C " Else Lcd Blad End If 1wreset 1wwrite &HCC 1wwrite &H44 Waitms 500 Locate 2 , 10 '1wreset ' 1wwrite &H55 1wverify Dsid2(1) '1wreset i 1wwrite &H55 niepotrzebne bo 1wverify samo to przeprowadza If Err = 0 Then 1wwrite &HBE T = 1wread(2) T = T * 10 T = T / 16 S = Str(t) S = Format(s , "+0.0") Lcd S ; Chr(223) ; "C " Else Lcd Blad End If Loop End
Tak się pobawiłem żeby sprawdzić. Można każdą diodę ustawić na oddzielną wartość PWM. Cały PORTD wolny wiec można tym sterować w dowolny sposób. Samo Programowe PWM prawie nic nie waży. Generowanie różnych sekwencji dopiero.
Podoba mi się muzyka więc i Wam pokażę.
$regfile = "attiny2313.dat" $crystal = 8000000 '*** Timer0 Config for 8kHz Interrupt CTC Mode *** Config Timer0 = Timer , Prescale = 8 , Compare A = Disconnect , Compare B = Disconnect , Clear Timer = 1 Enable Compare0a : On Compare0a Int0_isr : Compare0a = 124 Config PORTB = Output '****************** Zmienne ******************* '*********************************************** Dim Licznik As Byte 'liczy od 1 do 100 w przerwaniu Dim Pwm_led(8) As Byte 'osiem bajtów, dla każdego kanału wartosc PWM 0%-100% Dim N As Byte Enable Interrupts Do 'w pętli wpisuj żądane wartości PWM w bajtach 'w miedzyczasie program moze robić coś innego Pwm_led(1) = 20 Pwm_led(2) = 20 Pwm_led(3) = 20 Pwm_led(4) = 20 Pwm_led(5) = 20 Pwm_led(6) = 20 Pwm_led(7) = 20 Pwm_led(8) = 20 Loop End '************ Przerwanie timer 0 *************** '*********************************************** Int0_isr: Incr Licznik 'zwieksz Licznik o jeden If Licznik = 100 Then Licznik = 0 'jesli Licznik = 100 to wyzeruj 'pętla dla wartości 0 do 7 czyli tak jak numery bitów w porcie For N = 0 To 7 'jesli Licznik ma mniej niż PWM dla danego pinu to ustaw 0 na pin , w przeciwnym wypadku ustaw 1 ' (do N trzeba dodac 1 bo tablice zaczynaja sie od 1, nie ma wartosci 0) If Licznik < Pwm_led(n + 1) Then Reset Portb.n Else Set Portb.n Next Return
Zastosowałem tam też takie rozwiązanie które wymaga wyjaśnienia. Diody led się zapalają juz przy niskiej wartości PWM wiec wymyśliłem sobie że im niższa wartość PWM tym dłużej wartość tego PWM ma sie nie zmieniać. Czyli jeśli PWM ma wartosc 1 to odejmuję 1 od 127 i mam 126. 126 mnoże razy dwa i zawsze mieszczę sie w bajcie. 252 cykle muszą upłynąć zanim znów zmienię wartość PWM. Za to potem wszystko przyspiesza proporcjonalnie ;)
$regfile = "attiny2313.dat" $crystal = 8000000 $hwstack = 40 $swstack = 16 $framesize = 24 Config Portb = &B11111111 : Portb = &B11111111 'PORTB cały wyjscia, PORTB cały w stan wysoki Config Portd = &B00000000 : Portd = &B11111111 'nieużywany PORTD cały wejście właczone podciąganie '*** Timer0 Config for 8kHz Interrupt CTC Mode *** Config Timer0 = Timer , Prescale = 8 , Compare A = Disconnect , Compare B = Disconnect , Clear Timer = 1 Enable Compare0a : On Compare0a Int0_isr : Compare0a = 124 Enable Interrupts '****************** Zmienne ******************* '*********************************************** Dim Licznik As Byte 'liczy od 1 do 100 w przerwaniu Dim Pwm_led(8) As Byte 'osiem bajtów, dla każdego kanału wartosc PWM 1%-100% Dim N As Byte 'pomocnicza Dim Speed As Byte 'spowolnienie akcji Dim Scenario As Byte 'wybor dwoch trybów Dim Led_nr As Byte 'numer bajtu w tablicy PWM'ów Led_nr = 1 'zmienna ma 0 wiec nadaje jej wartosc 1 '************** Petla glowna ****************** '*********************************************** Do If Speed = 0 Then 'jesli nadszedl czas na zmiane PWM Select Case Scenario 'sprawdz co robimy Case 0 'Scenariusz rozpalamy If Pwm_led(led_nr) < 100 Then 'jesli wartosc pwm AKTUALNIE OBRABIANEJ DIODY... 'zmienna led_nr pamieta ktora diode akurat rozjasniamy lub sciemniamy Incr Pwm_led(led_nr) Speed = 127 - Pwm_led(led_nr) Speed = Speed * 2 Else 'jesli doszlismy do 100 to bierzemy kolejna diode If Led_nr < 8 Then Incr Led_nr Else 'jesli doszlismy do konca to zabieram sie za sciemnianie Scenario = 1 Led_nr = 1 'zaczynamy sciemniac od poczatku End If End If Case 1 If Pwm_led(led_nr) > 0 Then Decr Pwm_led(led_nr) Speed = 127 - Pwm_led(led_nr) Speed = Speed * 2 Else If Led_nr < 8 Then Incr Led_nr Else Scenario = 0 Led_nr = 1 End If End If End Select End If Loop End '************ Przerwanie timer 0 *************** '*********************************************** Int0_isr: Incr Licznik 'zwieksz Licznik o jeden If Licznik = 100 Then Licznik = 0 'jesli Licznik = 100 to wyzeruj 'pętla dla wartości 0 do 7 czyli tak jak numery bitów w porcie For N = 0 To 7 'jesli Licznik ma mniej niż PWM dla danego pinu to ustaw 0 na pin , w przeciwnym wypadku ustaw 1 ' (do N trzeba dodac 1 bo tablice zaczynaja sie od 1, nie ma wartosci 0) If Licznik < Pwm_led(n + 1) Then Reset Portb.n Else Set Portb.n Next 'odliczanie do zmiann wartosci If Speed > 0 Then Decr Speed 'jesli Speed wieksza od zera to odejmuj jeden Return
Taka oto skala która przewija się na wyświetlaczu. Skala ma 10 możliwych stanów (offset). Co ciekawe napisana jest jako Sub więc można go załączyć do każdego projektu ponieważ w większości operuje na swoich zmiennych lokalnych. Mogę dopisać automatyczne dopasowanie się do żądanej długości i tym podobne , ale żeby pokazać zasadę działania załączam najprostszą wersję ;)
Żeby ją użyć wystarczy w programie wywołać ją Call Draw_skale(kierunek,offset,pozycja)
W swoim programie musimy tylko zadeklarować dwie zmienne trzymające pozycję i offset dla każdej ze skal jeśli jest ich więcej.
Sub Draw_scale(byval Kierunek As Byte , Byval Offset As Byte , Byval Pozycja As Byte) 'mozliwe kierunki Lewo = 1, Prawo = 2, Tylko_rysuj = 0 'mozliwe offsety 0 - 9 'Pozycja to pozycja linii Local Help_x As Byte Local Help_y As Byte , Help_y2 As Byte , Help_y3 As Byte Local N As Byte , X_offset As Byte , Del_byte As Byte , Help_byte As Byte Help_y = Pozycja Line(0 , Help_y) -(239 , Help_y) , 255 'rysowanie dwoch linii skali Incr Help_y Line(0 , Help_y) -(239 , Help_y) , 255 Help_y = Pozycja + 2 : Help_y2 = Pozycja + 10 : Help_y3 = Pozycja + 5 'wysokosci kreseczek X_offset = Offset 'na ktorej pozycji pierwsza duza kreska For N = 0 To 239 If N = X_offset Then X_offset = X_offset + 10 Line(n , Help_y) -(n , Help_y2) , 255 'rysowanie dluzszych If N < 235 Then Help_x = N + 5 Del_byte = N + 4 Else Help_byte = N + 5 Help_x = Help_byte - 240 End If Select Case Kierunek Case Prawo Del_byte = Help_x - 1 If Del_byte > 239 Then Del_byte = 239 Case Lewo Del_byte = Help_x + 1 If Del_byte > 239 Then Del_byte = 0 End Select Line(help_x , Help_y) -(help_x , Help_y3) , 255 'rysowanie krotszych Line(del_byte , Help_y) -(del_byte , Help_y3) , 0 'kasowanie krotszych Select Case Kierunek Case Prawo If N = 0 Then Help_x = 239 Else Help_x = N - 1 End If Case Lewo If N = 239 Then Help_x = 0 Else Help_x = N + 1 End If End Select If Kierunek <> Tylko_rysuj Then Line(help_x , Help_y) -(help_x , Help_y2) , 0 'kasowanie dluzszych End If Next End Sub
Sub Draw_scale2(byval Kierunek As Byte , Byval Offset As Byte , Byval Pozycja As Byte) Local Help_y As Byte Local Help_x As Byte , Help_x2 As Byte , Help_x3 As Byte Local N As Byte , Y_offset As Byte , Del_byte As Byte , Help_byte As Byte Help_x = Pozycja Line(help_x , 0) -(help_x , 127) , 255 'rysowanie dwoch linii skali Incr Help_x Line(help_x , 0) -(help_x , 127) , 255 Help_x = Pozycja + 2 : Help_x2 = Pozycja + 10 : Help_x3 = Pozycja + 5 'wysokosci kreseczek Y_offset = Offset 'na ktorej pozycji pierwsza duza kreska For N = 0 To 127 If N = Y_offset Then Y_offset = Y_offset + 10 Line(help_x , N) -(help_x2 , N) , 255 'rysowanie dluzszych If N < 123 Then Help_y = N + 5 Del_byte = N + 4 Else Help_byte = N + 5 Help_y = Help_byte - 128 End If Select Case Kierunek Case Prawo Del_byte = Help_y - 1 If Del_byte > 127 Then Del_byte = 127 Case Lewo Del_byte = Help_y + 1 If Del_byte > 127 Then Del_byte = 0 End Select Line(help_x , Help_y) -(help_x3 , Help_y) , 255 'rysowanie krotszych Line(help_x , Del_byte) -(help_x3 , Del_byte) , 0 'kasowanie krotszych Select Case Kierunek Case Prawo If N = 0 Then Help_y = 127 Else Help_y = N - 1 End If Case Lewo If N = 127 Then Help_y = 0 Else Help_y = N + 1 End If End Select If Kierunek <> Tylko_rysuj Then Line(help_x , Help_y) -(help_x2 , Help_y) , 0 'kasowanie dluzszych End If Next End Sub
Może się tak zdarzyć że mamy sygnał z potencjometru w jakimś "okrojonym" zakresie
Na przykład 0,5V do 4,5V a chcemy sterować współczynnikiem wypełnienia impulsu PWM
Ten kod uczy się skrajnych wartości i potem przelicza je na pełny zakres dla PWM.
$regfile = "attiny13.dat" $crystal = 9600000 $hwstack = 12 $swstack 6 $framesize = 16 ' Full Range PWM from ADC by BARTek ' https://bart-projects.pl/ ' The device is able to calculate the full scale PWM from ADC incomplete scope ' For example, the values range from 23 to 1010 will be scaled to the value 0 - 255 PWM ' To calibrate, press button (short pin to ground) and enter the potentiometer lowest and highest value. ' After releasing the button, the value will be calculated and memorized ' Fuses for ATtiny13 LOW 7A HIGH FF '$sim Config Timer0 = Pwm , Compare A Pwm = Clear Down , Prescale = 1 Config Adc = Single , Prescaler = Auto , Reference = Avcc Start Adc Config Portb = &B00001 : Portb = &B10110 V_out Alias Portb.0 Potenc Alias Pinb.3 Switch Alias Pinb.4 Dim Minimum As Word , Maximum As Word Dim Pot As Word , Help As Word , Suma As Word , Wynik As Word Dim Lock As Byte , Przelicznik As Dword , Wart As Byte , Mem As Word Readeeprom Mem , 2 Readeeprom Minimum , 4 Do Pot = Getadc(3) Select Case Lock Case 0 If Switch = 0 Then Waitms 50 If Switch = 0 Then Lock = 1 Minimum = 512 Maximum = 512 End If Else 'digital filter for ADC values Help = Suma Shift Help , Right , 3 Suma = Suma - Help Suma = Suma + Pot Help = Suma Shift Help , Right , 3 If Help < Minimum Then Help = Minimum If Help <> Wynik Then Wynik = Help Help = Wynik - Minimum Przelicznik = Help * Mem Przelicznik = Przelicznik \ 4000 Wart = Przelicznik Pwm0a = Wart End If End If Case 1 If Switch = 1 Then Lock = 0 Help = Maximum - Minimum Przelicznik = 1024000 \ Help Mem = Przelicznik Writeeeprom Mem , 2 Writeeeprom Minimum , 4 Else If Pot > Maximum Then Maximum = Pot If Pot < Minimum Then Minimum = Pot End If End Select Loop End