HomeMade

Pełna wersja: Fork-Heńka... czyli jak sobie poradzić z DDS za pomocą ARDUINO
Aktualnie przeglądasz uproszczoną wersję forum. Kliknij tutaj, by zobaczyć wersję z pełnym formatowaniem.
Stron: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
Doceniam doceniam :-)
Jeżeli chodzi o odpowiedź, to "wiem ale nie powiem" żeby nie psuć zabawy :-)
W celach edukacyjnych zwrócę jednak uwagę na pojęcia:
"skończy się",
"przekręci się"
oraz na to, co w rzeczywistości dzieje się z liczbami jak licznik "dochodzi do końca".
Można sobie nawet na kartce zasymulować licznik liczący np do pięciu i zastanowić się PO CO korzystamy z licznika i jakie dane z niego (po przetworzeniu) wykorzystujemy.

miłej zabawy
MAc
mrn
No masz rację Mac. prawidłowa nomenklatura to rzecz ważna...

No dobrze zanim pójdziemy dalej w naszych pracach nad Złomkiem, proponuję wykonać proste ćwiczenie.
Na początek napiszemy sobie kawałek kodu by sprawdzić co dzieje się gdy licznik się przepełnia. Zrobimy to na przykładzie zmiennej typu integer. Zmienna ta jak wiemy jest przechowywana w dwóch bajtach czyli maksymalna jej wartość to 32767 a wartość minimalna -32768, kod wygląda następująco:

Kod:
int licznik = 32750;

void setup(){
  Serial.begin(9600);
}

void loop(){
  Serial.println(licznik);
  licznik++;
  delay(1000);  
}

Tak przygotowany program, wgrywamy do arduino i w monitorze portu szeregowego obserwujemy co się dzieje.
Wartość "licznik" ustawiłem celowo na samym końcu zakresu tak by było widać co się stanie z jego wartością w momencie przepełnienia i by nie musieć czekać długo na efekt.

Następnie zrobimy to samo ćwiczenie ale dla zmiennej typu: unsigned int.
Kod wyglada bardzo podobnie:

Kod:
int unsigned licznik = 65525;

void setup(){
  Serial.begin(9600);  
}

void loop(){
  Serial.println(licznik);
  licznik++;
  delay(1000);  
}

Ciekaw jestem czy efekty są takie jak podejrzewaliscie/oczekiwaliście.
Ale fajna......odjazdowa zabawa. Programiści wiedzieli co się stanie, ale ja niestety....nie. Ale już wiem....pouczające doświadczenie...Tongue
Rysio!
Bardzo śliczny przykład Ryśka chytrze poprzekręcam (mając w tym paskudny cel)

Kod:
//Wyobraźmy sobie, że  "licznik" to "millis"
// specjalnie używamy "char" bo się szybko "przekręca" - liczy tylko do 255 :-)
//pamiętajmy, że używamy "unsigned char" - bo zwraca nam liczby od 0-255
//w odróżnieniu od "char", które zwróci nam znaki ascii

unsigned char licznik = 0;

void setup(){
  Serial.begin(9600);
delay(500);  
}

void loop(){
  Serial.println(licznik);
  licznik++;
  delay(50);  
}

Zamiana "int" na "unsigned char" ma swój ukryty cel - ale o tym w następnym odcinku

MAc
// *****************

No to następny kawałek :-)
Dla wyjaśnienia - "charami" posługujemy się wyłącznie dlatego, że są jednobajtowe, czyli dziesiętnie przyjmują wartość od 0 do 255, co pozwala nam kilkakrotnie "przewinąć licznik" w sensownym czasie.

Jeżeli dla eksperymentu do tego co mieliśmy wcześniej dołączymy wywołanie i funkcję smetra, to serialmonitor pokaże nam co się dzieje z działaniem funkcji po przewinięciu licznika.
Otóż licznik już po pierwszym przekroczeniu 255 zaczyna głupieć.

Tyle tylko, że w naszym przypadku nie ma to większego znaczenia - od czasu do czasy sprawdzi smeter trochę "gęściej" i tyle. Niewykluczone nawet, że tego nie zrobi, bo sama funkcja smetra zajmuje trochę czasu, co spowoduje "przeskoczenie" licznika millis().
Tego się nie da niestety zasymulować (chyba, że ktoś wymyśli jak) - prosty "delay()" nie pomaga, bo wstrzymuje też funkcję symulującą millis()
Podsumowując - wygląda na to, że trzeba zostawić złomka na półtora miesiąca i zobaczyć :-)


Kod:
//Wyobraźmy sobie, że  "counter" to "millis"
// specjalnie używamy "char" bo się szybko "przekręca"
unsigned char licznik = 0;
unsigned char s_metr_update_time = 0;
unsigned char currentlicznik = 0;
unsigned char s_metr_update_interval=50;

void setup() {
   Serial.begin(9600);
   delay(500);  
   Serial.println("Zaczynamy...");
}
void loop() {
   for (int x=0; x<1000; x++) { // parę razy przekręcić unsigned char od 0 do 255
// ******* to counter udaje millis()
       licznik++; // powiększa się()
       Serial.println(licznik);
// ******
    show_smetr();  
   }
Serial.println("koniec!");
while(1); // koniec wysylania na serialport
}

void show_smetr(){
  
        if(licznik >= s_metr_update_time){ // czy już wyświetlić?
         Serial.println("Update smeter"); //albo cokolwiek innego zaleznego od czasu
         s_metr_update_time = licznik + s_metr_update_interval;
      }
  
}


Idąc dalej.
Teraz nam to nie przeszkadza, ale może się zdarzyć, że (oczywiście edukacyjnie) będzie to miało znaczenie.
Co nam zatem zostaje?
Zastanowić się jak uniknąć nieuniknionego.
Oszukać przeznaczenie. (1, 2, 3)

A może to wcale nie licznik nam się przewija?
Może sami coś przewijamy korzystając z licznika?

Miłej zabawy

MAc
mrn
Abstrahująć od liczników mam coś z innej beczki.
Zmieniłem obsługę enkodera.
Teraz ciężko jest przyłapać enkoder na przekłamaniach od drgań, no i przy szybkim kręceniu nie gubi impulsów. Kondensatory na wejściach obsługujących enkoder trzeba usunąć. Zmodyfikowałem wersję 1.0.11, ponieważ w wersji 1.0.13 RIT mi się sam włączał i różne takie...
Mała demonstracja działania enkodera:





Dodatkowa biblioteka Timer1:
[attachment=9170]

Zmieniony kod:
[attachment=9171]
Mac w sumie chodziło Ci o to że jeśli funkcja s-metr wykona się przykładowo na 50msec przed przepełnieniem licznika to kolejny czas jej uruchomienia wypadnie za 100msec czyli poza zakresem licznika. I pytanie jest takie czy stanie się cokolwiek złego.

Witku gratuluję zmian, wprowadziłeś alternatywną obsługę enkodera, muszę to w wolnej chwili przeanalizować.

A przy okazji czy ktoś stwierdził problemy z ostatnią wersją softu, którą sygnalizuje Witek?
Ja nie mam,enkoder juz drugi model,poprzednio blokowalem kondensatorami teraz nie i dzala.Tylko ja testuje go co prawda czesto ale "na sucho",bez podlaczonego trxa.Wgrywam rozne wsady kombinuje ale takich przeklaman nie stwierdzilem
Trochę zagmatwałem :-)
1. Na początku uznałem, że problem z przepełnieniem faktycznie jest poważny i znalazłem na niego lekarstwo.

2. Następnie postanowiłem edukacyjnie pokazać na czym ten problem polega.

3. Jak już napisałem kawałek kodu demonstracyjnego (celowo z "błędem) i zobaczyłem wyświetlane wartości, to wyszło mi, że w NASZYM KONKRETNYM PRZYPADKU fakt przepełniania się licznika millis() najprawdopodobniej nie ma żadnego praktycznego znaczenia.
Smeter będzie działał i PRAWDOPODOBNIE (ponieważ millis działa na przerwaniach) czas potrzebny na obsługę wyświetlania smetra spowoduje, że tych kilka "dzikich" pomiarów występujących po przepełnieniu licznika millis() - (a w demo po przekroczeniu 255) zniweluje się błyskawicznie.

4. muszę jeszcze sprawdzić, ale wygląda na to, że tych "nieoczekiwanych" pomiarów będzie w realnym działaniu kilkadziesiąt - dopóki się liczniki nie wyrównają.

5. Zadanie domowe Ryśka jest cały czas obowiązujące.

Generalnie chodzi o to, że my sami powodujemy przepełnienie naszej zmiennej:
"s_metr_update_time (unsigned long)"
za pomocą linijki:
s_metr_update_time = millis() + s_metr_update_interval;

w której do "kończącego się" licznika millis() (też unsigned long) dodajemy wartość "s_metr_update_interval"
Czyli - jak millis() będzie na 99 przed końcem a my dodamy 100, to millis() się jeszcze nie przepełni ale nasza wartość " s_metr_update_time" się przepełni (przekroczy zakres przewidziany dla zmiennej typu unsigned long") i zacznie przyjmować kolejne wartości od zera.

Podsumowując - żeby się woda nie przelała nie możemy dodawać nic do millis().
.....
ale możemy odejmować ;-)

MAc
mrn

Ale jazda
Ja mam "własną" metodę na czytanie enkodera, robię to w przerwaniu co około 7kHz (tak akurat się dzieli 16MHz/256/8), tam sprawdzam tylko podstawowe wejścia/wyjścia - przyciski, enkoder, ptt, coś tam jeszcze, bo musi być to szybkie.
Natomiast wszystkie potencjalnie czas-żerne rzeczy robię w pętli w "main" czyli przede wszystkim obsługa LCD.

Wszystkie wejścia są podciągnięte, i mają 10nF kondensatory.
Robiłem test, i po przekręceniu enkoderem kilkanaście razy szybko w lewo a potem dokładnie tyle samo w prawo, licznik enkodera pokazywał zero, i tak powinno być.
Kod:
//rozdzielczość x4, czyli na ząbek 4 "kroki", co bywa kłopotliwe, bo zadaniem ząbka jest "stabilizacja" enkodera w konkretnej pozycji. W innym miejscu to dziele /4
A = get_input(ENC_A);
B = get_input(ENC_B);
if (A!=pA){
    if (A==B)     enc_ticks++;
             else             
              enc_ticks--;            
    pA = A;
    }
  if (B!=pB) {
        if (A==B)  enc_ticks--;
            else
             enc_ticks++;            
    pB = B;    
}

Ale jak popatrzyłem na kod z biblioteki arduino:
Kod:
uint8_t s = state & 3;            
        if (get_input(ENC_A)) s |= 4;
        if (get_input(ENC_B)) s |= 8;
        
        switch (s) {
            case 0: case 5: case 10: case 15:
                break;
            case 1: case 7: case 8: case 14:
                enc_ticks--; break;
            case 2: case 4: case 11: case 13:
                enc_ticks++; break;
            case 3: case 12:
                enc_ticks -= 2; break;
            default:
                enc_ticks += 2; break;
        }
        state = (s >> 2);
}

To może ta metoda j.w. jest ciut lepsza. Musiałbym sprawdzić co szybciej się wykonuje.

Pozdrawiam
Nie bawilem sie arduino, ale "klasycznie" switch jest szybszy niz if.

pz
Stron: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
Przekierowanie