Skocz do zawartości

Miernik napięcia z siedmiosegmentowym wyświetlaczem


wojtekq2

Pomocna odpowiedź

Witam,
Chciałbym się zwrócić do Was o pomoc w sprawdzeniu programu pod względem błędów.

Mianowicie chcę zrobić miernik napięcia (od 0 do 5V) na arduino. Do tej pory mam wykonane połączenie i program który wgrywam do Arduino, jednak coś jest nie tak. Jak chce wyświetlić samą liczbę to nie mam z tym problemu, natomiast jak wykonam pomiar, przeliczę go na Volty, prze konwertuje zmienną za pomocą funkcji dtostrf na tablice znaków i teraz:

jak wyświetlam na monitorze portu szeregowego który kol wiek znak tej tablicy wszystko ładnie pokazuje, a jak próbuje wyświetlić go na wyświetlaczu to brak reakcji (wyświetlacz niedziała).

Czy moglibyście pomóc znaleźć mi błąd, ewentualnie powiedzieć co robię nie tak.

Kod:

#include <sevenSegmentDisplay.h> //do pobrania z github

sevenSegmentDisplay name(COMMON_ANODE, 11,7,3,5,6,10,2,8);

int VCC=12; 
int VC1=9; 
int VC2=13;
int odczytanaWartosc = 0;                 //Odczytana wartość z ADC
float napiecie = 0;                       //Wartość przeliczona na napięcie w V

#define op 2
#define cz 2

char tablica1[4];

void setup() {
 Serial.begin(9600);
 pinMode(VCC, OUTPUT);
 pinMode(VC1, OUTPUT);
 pinMode(VC2, OUTPUT);
}

void loop() {
 odczytanaWartosc = analogRead(A5);                  //Odczytujemy wartość napięcia
 napiecie = odczytanaWartosc * (5.0/1023.0);         //Przeliczenie wartości na napięcie

   Serial.println(napiecie);                          //Wysyłamy zmierzone napięcie
   dtostrf(napiecie,1,2,tablica1);                   //konwersja float na tablice char
   Serial.println(tablica1[0]);                    //sprawdzenie na monitorze portu szeregowego

    int czas_oczekiwania = 100;
 while(czas_oczekiwania>=20){
 name.set(tablica1[0]);
 digitalWrite(VCC, HIGH);
 delay(cz);
 digitalWrite(VCC, LOW);
 delay (op);
 name.set(tablica1[2]);
 digitalWrite(VC1, HIGH);
 delay(cz);
 digitalWrite(VC1, LOW);
 delay(op);
 name.set(2);
 digitalWrite(VC2, HIGH);
 delay(cz);
 digitalWrite(VC2, LOW);
 delay(op);


    czas_oczekiwania--;
 }
       delay(500);

}
Link do komentarza
Share on other sites

Opisz dokładnie, ale własnymi słowami co chciałbyś, by robiło to wywołanie:

dtostrf(napiecie,1,2,tablica1);

Postaraj się napisać co znaczy każdy argument i dlaczego dostał taką a nie inną wartość.

A potem wytłumacz dlaczego na wyświetlaczu pokazujesz [0] i [2] pozycję tablicy oraz cyfrę 2.

Link do komentarza
Share on other sites

dtostrf(napiecie,1,2,tablica1);

Tu widzę, że wkradł mi się chochlik o którym już wiedziałem wcześniej, a nie jest powodem nie działania poprawnie programu.

Poprawnie napisana funkcja:

dtostrf(napiecie,4,2,tablica1);

dtostrf-funkcja zamieniająca float na tablice char,
napięcie - tutaj przekazuje właśnie zmienną która ma być przekonwertowana,
4 - nie jestem do końca pewien, ale ilość znaków które zawiera zmienna float,
2 - to jest ilość miejsc po przecinku do których zostanie obcięty float,
tablica1 - tablica ciągu znaków do której chcę aby została zapisana zmienna napięcie,

Na wyświetlaczu pokazuje:

[0] - pierwszą wartość w tablicy,
[2] - trzecią wartość w tablicy (pomijam separator liczby),
cyfrę 2 wyświetlam w celu sprawdzenia czy wyświetlacz działa (bo nie wyświetlał nic z tablicy);

Link do komentarza
Share on other sites

Proponuję rzecz najprostszą: uruchamiaj mniejsze kawałki kodu. Zrób pomiar i obliczenia wypisując cały wynik na porcie szeregowym. Potem zajmij się konwersją float->char. Mógłbym Ci tu opisywać jak działa funkcja dtostrf(), ale nic nie zastąpi tzw. pouczających doświadczeń 🙂 Na pewno czytałeś Mikołajka. Zarezerwuj sobie długi bufor, np. 20 znakowy, napisz dodatkową funkcję, która wypisuje go znak po znaku na port szeregowy, sprawdź czy działa wypełniając bufor jakimś znanym wzorcem a potem testuj dtostrf(). Wołaj ją dla różnych liczb, np. zawsze jakoś tak:

dtostrf(liczba, 10, 2, bufor);

lub tak jak założyłeś:

dtostrf(liczba, 4, 2, bufor);

i patrz co Ci oddaje dla kilku dobrze dobranych liczb udających wyniki pomiarów. Zdziwisz się a jednocześnie być może zrozumiesz dlaczego Twój kod nie działa jak sobie zaplanowałeś.

A jeśli konwersja będzie już w porządku, zajmij się wyświetlaniem.

Dlaczego samodzielnie multipleksujesz wyświetlacze? I to jeszcze z jakimiś dziwnymi opóźnieniami? Zapalasz cyfrę na chwilę a potem na tyle samo czasu robisz przerwę. Marnujesz 50% czasu i możliwej jasności LED.

EDIT: I zapomniałem: masz także problem z odróżnianiem znaków '2' o kodzie 0x32 (oddawanych np. przez funkcję konwersji) od liczby 2 która w pamięci wygląda jak 0x02.

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

Wypełniłem tablicę ciągiem znaków i wszystko się ładnie wyświetla na monitorze portu szeregowego :

char tablica1[20]={'1','2','3','4','5','6','7','8','9',',','0','0','1','2','3','4'};  // tablica uzupełniona 16 znakami

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

void loop() {

 for(int i=0; i<sizeof(tablica1); i++)
 {
   Serial.print(i);
   Serial.print("=");
   Serial.println(tablica1[i]);
   delay(500);
 }
   Serial.print("tablica=");
   Serial.println(tablica1);
}

2) nie czytałem Mikołajka i nie wiem o co chodzi 😉

I funkcja dtostrf

Zrobiłem tak jak pisałeś, przetestowałem tą funkcję, dla różnych wariantów i wnioski mam następujące:

-jeśli zmienna float jest za duża to zostaje obcięta jej część całkowita i zaokrąglona w górę albo w dół,
-dtostrf(liczba, 10, 2, bufor)- teraz liczba 10 to tak jakby "miejsca w tablicy na których jest wpisywana zmienna float i jeśli jest ona większa od tej zmiennej to w tablice są wpisywane puste pola i bufor tak będzie uzupełniany aby skończył się wpisywać na 10 miejscu w tablicy. Natomiast jeśli będzie na odwrót to ta sytuacja nie występuje, bufor się uzupełnia po kolei.

Tutaj niestety muszę się przyznać, że nie rozumie dlaczego wyświetlacz nie reaguje jak wywołuje znak z tablicy. Wkleje jeszcze raz kod. Na monitorze portu szeregowego wszystko się ładnie wypisuje a wyświetlacz pokazuje tylko dwójkę wpisaną "ręcznie".

#include <sevenSegmentDisplay.h> //do pobrania z github

sevenSegmentDisplay name(COMMON_ANODE, 11,7,3,5,6,10,2,8);

int VCC=12; 
int VC1=9; 
int VC2=13;
//int odczytanaWartosc = 0;                 //Odczytana wartość z ADC
float napiecie = 4.1234;                       //Wartość przeliczona na napięcie w V
#define op 2
#define cz 4

char tablica1[5];

void setup() {
 Serial.begin(9600);
 pinMode(VCC, OUTPUT);
 pinMode(VC1, OUTPUT);
 pinMode(VC2, OUTPUT);
}

void loop() {



 //odczytanaWartosc = analogRead(A5);                  //Odczytujemy wartość napięcia
 //napiecie = odczytanaWartosc * (5.0/1023.0);         //Przeliczenie wartości na napięcie
   //Serial.println(napiecie);                          //Wysyłamy zmierzone napięcie
   dtostrf(napiecie,5,3,tablica1);                    //konwersja float na tablice char
  for(int i=0; i<sizeof(tablica1); i++)
 {
   Serial.print(i);
   Serial.print("=");
   Serial.println(tablica1[i]);
   delay(100);
 }
   Serial.print("tablica=");
   Serial.println(tablica1);


  // Serial.println(tablica1[0]);                    //sprawdzenie na monitorze portu szeregowego

  int czas_oczekiwania = 100;
 while(czas_oczekiwania>=20){
 name.set(tablica1[0]);
 digitalWrite(VCC, HIGH);
 delay(cz);
 digitalWrite(VCC, LOW);
 delay (op);
 name.set(tablica1[2]);
 digitalWrite(VC1, HIGH);
 delay(cz);
 digitalWrite(VC1, LOW);
 delay(op);
 name.set(2);
 digitalWrite(VC2, HIGH);
 delay(cz);
 digitalWrite(VC2, LOW);
 delay(op);

    czas_oczekiwania--;
 }
       delay(500);

}

3)Dlaczego samodzielnie multipleksuje wyświetlacze- ponieważ dorwałem taki wyświetlacz ze starego dekodera SATELITY i postanowiłem że zrobię miernik napięcia w ramach ćwiczeń.

Odnośnie czasów jakimi zapalam i gaszę poszczególne segmenty to znalazłem podobne w czyimś projekcie i tym się zasugerowałem. Jak powinny wyglądać one prawidłowo, np 2 milisekundy wyłączone i 4 zapalone?, niestety nie mam na tyle czasu aby wszystko wyczytać/ sprawdzić lub też znajdę takie a nie inne informacje i przyjmuje je za poprawne. Aczkolwiek wiem, że w Internetach jest dużo błędnych informacji.

4)Teraz podejrzewam, że najważniejsza kwestia, zmienna liczbowa przekonwertowana na typ char jest już zapisana w kodzie inaczej i program jej nie wyświetli ponieważ nie widzi jej poprawnie?

Co powinienem zrobić, przekonwertować typ char poszczególnych elementów tablicy na typ np. int?

Czy w ogóle mój tok myślenia jest błędny?

0x32 to 50

0x02 to 2

Link do komentarza
Share on other sites

Brawo, nie zmarnowałeś czasu. Jesteś już bardzo blisko rozwiązania. Wiesz już co dostajesz w buforze po konwersji więc wiesz jak wygląda znakowa postać twojego float'a. I teraz słowo klucz: znakowa. Funkcje operujące na znakach w najprostszej formie działają na 7-bitowych kodach ASCII (znajdź tabelę takich znaków, jest tego pełno). To historyczny już dzisiaj skrót oznaczający pewien standard kodowania znaków czy sposobu interpretacji kodów przez urządzenie pokazujące tekst człowiekowi. Jak szybko się zorientujesz jest tam kilka "stref" liczbowych:

0x00-0x1F: to kody tzw. sterujące urządzeniami. W tej grupie masz przejazdy kursora do początku linii, wysunięcie o 1 wiersz, nowa strona itd. Tak, ASCII powstawało w czasach dalekopisów i drukarek wierszowych 🙂

0x20-0x3F: to znaczki typu kropka, przecinek itd. W tej grupie masz cyfry (0x30-0x39).

0x40-0x5F: to są głównie duże litery plus kilka znaczków dodatkowych.

0x60-0x7F: tu są małe litery i kilka pozostałych znaczków.

Mam nadzieję, że już rozumiesz: cyfry po konwersji w buforze wyjściowym funkcji dtostrf() są znakami. Mają więc kody 0x30-0x39. Czegoś takiego spodziewa się urządzenie (konsola/terminal) podłączone przez port szeregowy i dlatego bufor wyświetla się poprawnie. Wysłanie przez Serial.print() bajtu o kodzie np. 0x43 powoduje wyświetlenie litery 'C' a 0x32 to cyfra '2'. Natomiast niestety funkcje biblioteki z której korzystasz oczekują prawdziwych wartości cyfr. Żeby pokazać znak '2' musisz im zapodać po prostu 2 czyli wartość 0x02. To głupie i niekonsekwentne, bo już żeby pokazać literę 'A" (na 7 segmentach da się pokazać niektóre litery) trzeba wysłać kod znaku 'A' czyli 0x41.

Na szczęście konwersja z cyfr kodowanych w ASCII na ich wartości jest prosta: jeżeli znak ma wartość w zakresie 0x30-0x39 to odejmujesz 0x30 i dostajesz wartość 0-9 🙂

A pytałem o samodzielne multipleksowanie dlatego, że np. biblioteka sevenseg (inna niż ta Twoja) umie robić dużo więcej. Tam po prostu zapodajesz jej na jakich pinach masz dołączony wyświetlacz LED (gdzie segmenty a gdzie anody/katody) a ona sama organizuje przełączanie cyfr i świecenie wszystkiego. To dużo wygodniejsze, spróbuj. Na pewno nie wolno tego robić tak jak zrobiłeś: w pętli z delay'ami. To prymitywne i zżera 100% czasu procesora.

http://playground.arduino.cc/Main/SevenSeg

Link do komentarza
Share on other sites

Miernik działa poprawnie 😉 uporałem się z nim.

Kod dla miernika:

#include <sevenSegmentDisplay.h> //do pobrania z github
sevenSegmentDisplay name(COMMON_ANODE, 11,7,3,5,6,10,2,8);

int a,b,c;
int VCC=9; 
int VC1=13; 
int VC2=12;
int odczytanaWartosc = 0;                 //Odczytana wartość z ADC
float napiecie = 0;                       //Wartość przeliczona na napięcie w V
#define op 2 //opóźnienie
#define cz 4 //czas palenia się segmentów
char tablica1[4];

void setup() {
 Serial.begin(9600);
 pinMode(VCC, OUTPUT);
 pinMode(VC1, OUTPUT);
 pinMode(VC2, OUTPUT);
}

void loop() {
 odczytanaWartosc = analogRead(A5);                  //Odczytujemy wartość napięcia
 napiecie = odczytanaWartosc * (5.0/1023.0);         //Przeliczenie wartości na napięcie
   Serial.println(napiecie);                          //Wysyłamy zmierzone napięcie
   dtostrf(napiecie,4,2,tablica1);                    //konwersja float na tablice char
  for(int i=0; i<sizeof(tablica1); i++)
 {
   Serial.print(i);
   Serial.print("=");
   Serial.println(tablica1[i]);
 }
 a=tablica1[0];
 b=tablica1[2];
 c=tablica1[3];
 a=a-48;
 b=b-48;
 c=c-48;

 int czas_oczekiwania = 40;
 while(czas_oczekiwania>=0){
 name.set(a);
 digitalWrite(VCC, HIGH);
 delay(cz);
 digitalWrite(VCC, LOW);
 delay (op);
 name.set(b);
 digitalWrite(VC1, HIGH);
 delay(cz);
 digitalWrite(VC1, LOW);
 delay(op);
 name.set(c);
 digitalWrite(VC2, HIGH);
 delay(cz);
 digitalWrite(VC2, LOW);
 delay(op);             
    czas_oczekiwania--;
 }
}

Próbowałem zrobić aby zamiana wartości z kodu Ascii wykonywała się w pętli for i wpisywała do tablicy sama, jednak coś nie gra. Moje leonardo się wieszało albo nie było żadnej reakcji.

Kod obliczeń i wpisywania do tablicy int:

char tablica1[4];
int tablicaliczb[4];

dtostrf(napiecie,4,2,tablica1);    
  for(int i=0; i<sizeof(tablica1); i++)
 {
   for(int j = 0; j<sizeof(tablicaliczb); j++)
   {
   tablicaliczb[j]=tablica1[i]-48;
  }
 } 

Czemu tak się dzieje? Na monitorze portu szeregowego dla tablica1 dostawałem różne dziwne znaki. Jak powinienem poprawnie to zrobić?

Dzięki Marek za pomoc, w sumie przy tym projekcie dużo się nauczyłem i nie uporał bym się z nim prawdopodobnie bez Twojej pomocy 😉

Pozdrawiam.

Link do komentarza
Share on other sites

Niepotrzebnie zagnieździłeś pętle. Przecież wystarczy raz przejechać przez wszystkie cyfry. I zamiast 16-bitowych int'ów używaj krótszego typu byte, w mikrokontrolerach każdy kawałek pamięci się liczy. No i staraj się unikać magicznych liczb rozrzuconych po kodzie, ale znaczących to samo. Gdybyś Ty za pół roku (albo ktoś inny używający jutroTwojego kodu) chciał zmienić długość/format wypisywanej liczby np. z obecnego 4/2 na 5/2, to które czwórki w kodzie ma zmienić, w ilu miejscach i czy wszystkie czwórki jakie znajdzie odpowiadają za to samo? Musiałby zająć się analizą działania całego programu. To zły pomysł.

#define CYFRY 4
char tablica_znakow[CYFRY];
byte tablica_liczb[CYFRY];

dtostrf(napiecie, CYFRY, 2, tablica_znakow);
for(int i=0; i<CYFRY; i++)
  tablica_liczb[i] = tablica_znakow[i] - '0';

Odejmowanie dziwnej stałej 48 nic nikomu nie powie. Warto pisać tak, by kod cokolwiek wyjaśniał. Może nawet tak:

tablica_liczb[i] = ascii2bin(tablica_znakow[i]);

definiując wcześniej funkcję:

byte ascii2bin(char ch)
{
  return (byte)(ch - '0');
}

Wtedy każdy czytający Twój kod (przecież nie piszesz go dla kompilatora, jemu jest wszystko jedno jak to wygląda) od razu zrozumie co się dzieje i co chciałeś zrobić.

Poza tym powyższe kody nie sprawdzają poprawności znaków. Gdyby w łańcuchu znaków zdarzyła się kropka, konwersja ascii2bin() odejmująca od wszystkiego kod 0x30 zrobi z niej kaszanę. Jeśli miałbyś napisać (bo dlaczego nie?) uniwersalną funkcję wypisującą skonwertowany w dowolnym formacie float na wyświetlacz 7-seg, to musiałbyś jeszcze analizować pozycję kropki i ew. spacji. Przymierz się do tego - tak dla sportu 🙂

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.