Skocz do zawartości

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


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?

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ć..

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ę.

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.

  • 1 miesiąc później...

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ć...

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...