Skocz do zawartości

[C] Licznik modulo i wyświetlanie na LCD. Intel 8051.


Yggas

Pomocna odpowiedź

no dobra po kolei:

Napięcie nie będzie równe 123 bo:

licznik=PTAC;

napiecie= licznik/51;

Licznik dostaje max liczbe 255, a podzielone przez 51 nie da liczby większej niż 5.

Pod drugie, wszystko mi działa jak należy, jest skalowanie i jest zakres od 0.00V do 5.00V (bez żadnych wyskalowań ponad 5).

Po trzecie zapisałem wszystko po kolei i napisałem dobrze bo:

setki to cyfra X*10^2

dziesiątki to cyfra X*10^1

jednosci to cyfra X*10^0

Czyli zrobione w formie 0-100 tylko wstawiona jest w tablicy kropka pomiędzy setki i dziesiątki.

Po czwarte twój program u mnie nie działa bo zmienia tylko miejsce zaznaczone X: 0.0XV i to zmienia w zakresie 0-5.

A pomnożyłem licznik razy 2 gdyż wtedy otrzymuję większą skalę przez co licznik modulo prz wartości minimalnej i maksymalnej nie zostawia mi reszty czyli przedział się domyka idealnie. 255*2 to 510 i przy dzieleniu przez 10 skrajnych wartości mamy reszty 0.

Przepraszam ale nie widzę tu już niczego złego i chyba chcesz mnie wprowadzić w błąd, pytanie tylko czemu?

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

No dobrze, to może zacznijmy od początku, ale po mojemu.

Chcesz wypisywać napięcie z jakiegoś przetwornika 8-bitowego, którego pełna skala to 5V. I na dodatek masz to zrobić docelowo w asemblerze. Chcą to zrobić szybko i prosto nie powinieneś zatem używać zmiennych przecinków i podobnych ekstrawagancji. Z resztą nie jest to potrzebne. Zakładamy więc, że pracujemy tylko na liczbach całkowitych i z góry ograniczamy ich zakres do 16-bitów. To pomaga.

Masz więc 8-bitową liczbę bez znaku z zakresu 0-255 i chcesz z robić z niej liczbę 0-500 pokazywaną na wyświetlaczu w formie x.xxV. Potrzebny jest zatem współczynnik 500/255 przez który pomnożysz. Normalnie jest to 1.96, ale przecież nie masz arytmetyki ułamkowej. W takich sytuacjach używasz formatu ze stałym przecinkiem, ale przesuniętym.

Przypomnij sobie, co się dzieje gdy mnożysz na papierze dwie liczby, np takie: 1.23x2.5. Mnożenie wygląda jak 123*25 tylko w wyniku przesuwasz przecinek o 3 miejsca w lewo i dostajesz 3.075. Tutaj jest tak samo, ale ponieważ w systemie dwójkowym przesuwanie przecinka to nie mnożenie przez 10 tylko przez 2, to nasz współczynnik skalujemy np. przez 128 (7 bitów):

1.96078 x 128 = 250.98

ponieważ mamy tylko liczby całkowite, zaokrąglamy to z bardzo małym błędem do 251.

Co nam to daje? Ano popatrz: przykładowy odczyt z przetwornika równy 1/4 zakresu zakresu to 64, czyli ok. 1.25V na wejściu.

Robimy trochę magii na ulubionych przez małe procesory liczbach całkowitych:

64 * 251 = 16064

Tak, to dziwny wynik (16-bitowy), ale pamiętamy, że współczynnik był przecież przeskalowany o 128. Teraz robimy to samo z wynikiem, tylko w drugą stronę:

16064 / 128 = 125 // dzielenie jest oczywiście całkowite

Znajoma liczba? Właśnie dostałeś swoje napięcie w woltach 🙂 Ponieważ skala przetwornika jest 5.00[V] to i wynik jest tu 1.25[V]. Wystarczy odpowiednio pokazać przecinek.

Tak więc trzeba teraz przepisać algorytm na C i masz gotowy kod:

#define RANGE 500
#define SCALE 128
#define ADCMAX 255
#define COEFF ((RANGE*SCALE)/ADCMAX)
uint16_t 
  licznik,
  napiecie;

licznik=PTAC;
napiecie = (COEFF*licznik) / SCALE;

jednostki = napiecie%10;
napiecie /= 10;
dziesiatki=napiecie%10;
napiecie /= 10;
setki = napiecie;

Tym razem do konwersji wchodzi 3-cyfrowa liczba, która jest poprawnym napięciem. Reszta to już oklepany banał. Pracując na liczbach całkowitych musisz myśleć trochę inaczej. Po pewnej praktyce można się do tego przyzwyczaić 🙂

Co więcej, przepisanie takiego kodu na asembler jest proste - i zaraz to zrobimy. Weź to wreszcie odpal, bo zrobiła się z tego niezła dyskusja, chyba niewarta tematu. Okazało się, że nie tylko nie kumasz konwersji, ale i zwykłego przeliczania na int-ach. Mam nadzieję, że teraz sprawa wygląda jaśniej.

EDIT: Pisałem na szybko z głowy i musiałem potem poprawiać..

Link do komentarza
Share on other sites

Proste programy są często bardzo ciekawe, pewnie dlatego są w nieskończoność wałkowane na studiach. Skoro doszliśmy do wyjaśniania - każdy po swojemy, więc ja też dorzucę parę słów na koniec dyskusji.

Program który powstał był niezwykle ciekawy, ponieważ już prawie działał - chociaż chyba mocno przypadkiem. Jak Marek słusznie zauważył, do przeskalowania zakresu 0-255 na 0-500 powinniśmy użyć współczynnika 500/255, czyli ~1.96.

Możemy nieco uprościć sobie zadanie, przyjmując że błąd 2% nam nie przeszkadza - i zamiast współczynnika 1.96, użyć 2. Wtedy zakres będzie 0-510 (czyli 0-5.1V).

W programie ten współczynnik pojawił się już w nieco magiczny sposób:

jednostki=(licznik*2)%10; 
       licznik/=10; 
     dziesiatki=(licznik*2)%10; 

Autor tłumaczył jak do tego doszedł, ale ja nic nie zrozumiałem:

A pomnożyłem licznik razy 2 gdyż wtedy otrzymuję większą skalę przez co licznik modulo prz wartości minimalnej i maksymalnej nie zostawia mi reszty czyli przedział się domyka idealnie. 255*2 to 510 i przy dzieleniu przez 10 skrajnych wartości mamy reszty 0.

W każdym razie ten program był już prawie dobry - brakowało tylko poprawienia współczynnika przy najbardziej znaczącej cyfrze.

Kod dla setek wygląda tak:

napiecie= licznik/51;         
       setki = napiecie; 

Natomiast należałoby go zamienić na:

setki = licznik / 50;

i byłoby dużo lepiej.

Skąd taki kod? Idąc "krok, po kroku":

jednostki dostajemy od razu:

jednostki=(licznik*2)%10;

żeby dostać się do dziesiątek, musimy podzielić licznik przez 10, czyli do wygląda następująco:

dziesiatki=(licznik*2/10)%10;

teraz łatwo zgadnąć jak policzyć steki:

setki=(licznik*2/100)%10;

Ponieważ mnożenie przez 2, a następnie dzielenie niewiele daje, można od razu dzielić przez 5 i 50 - i to właśnie oryginalny program robił.

Błąd polegał nie na metodzie, ale użyciu innego współczynnika dla setek (/51) oraz pozostałych cyfr.

Jeszcze jeden błąd wynikał ze złej kolejności mnożenia i dzielenia - najpierw należy mnożyć przez 2, a później dzielić przez 10. Niestety to nie to samo.

Natomiast trzeba przyznać, że ta metoda miała jedną dość dużą zaletę - nie wymagała operacji na liczbach 16-bitowych, do obliczeń wystarczy 8-bitów. Ponieważ kod ma być napisany w asemblerze, taka zmiana może znacznie ułatwić napisanie programu. Więc jeśli błąd 2% nie przeszkadza, może warto ułatwić sobie życie i wykorzystać uproszczoną wersję.

Link do komentarza
Share on other sites

To teraz na mnie kolej z tłumaczeniem się z 16 bitów 🙂

Ten program (ten konkretnie) potrzebuje typu uint16_t tylko w jednym momencie: jako wyniku mnożenia 8-bitowego współczynnika i 8-bitowej zmiennej napiecie. Normalnie w C, aby "wyjść" z 16-bitami trzeba do arytmetyki "wejść" z takimi samymi argumentami i dlatego musiałem zrobić całe mnożenie na zmiennych 2-bajtowych. Jednak w asemblerze jest inaczej: mnożymy 8x8 bitów i dostajemy bitów 16. W 8051 instrukcja MUL AB mnoży AxB a jej wynik zamazuje oba argumenty, zostawiając w akumulatorze mniej a w rejestrze B bardziej znaczący bajt. Teraz wystarczy tylko przesunąć tę parę o 7 bitów w prawo - to nawet w '51 jest proste i mamy w A napięcie z ADC w woltach x100.

Dalsza konwersja tak uzyskanej liczby 8-bitowej sprowadza się do odpowiedniego użycia kilku instrukcji DIV AB (tak, tak, jest taka w 8-bitowym procesorze sprzed 30? lat) oddających zarówno iloraz jak i resztę.

Mając to wszystko w tyle głowy zaproponowałem taki a nie inny sposób liczenia. Moim zdaniem bardziej czytelny i łatwiej (w zasadzie 1:1) przekładalny na asembler '51.

Link do komentarza
Share on other sites

Trochę mnie nie było by to zadanie rozwiązać ale miałem trochę pilniejszych rzeczy 😋

Czyli podsumowanie:


unsigned char xdata tblcw[]="Napiecie: 0.0 V";
int setki; dziesiatki; jednostki;

   setki = PTAC/50; 

	dziesiatki=(PTAC*2/10)%10;

   jednostki=(PTAC*2)%10; 

   tblcw[10]=setki +'0'; 
   tblcw[12]=dziesiatki + '0'; 
   tblcw[13]=jednostki + '0'; 

   disptext(tblcw); 

to jest już gotowy program w który jedyny minus to dokładność? Czyli ten błąd pomiaru liczący 10%?

marek1707, twój przykład jeszcze sobie przeanalizuję na spokojnie 😋

Dodatkowe pytanie to wplątanie w to asemblera:

kod asemblerowy umieszczamy w kodzie z C? Czy trzeba jakiś inny plik tworzyć i się do niego odwołać? Składnię zadania za chwilę postaram się opracować tylko nie wiem jak to połączyć...

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.