Ta strona używa ciasteczek (plików cookies), dzięki którym może działać lepiej. Dowiedz się więcejRozumiem i akceptuję
Odbierz rabat na filament!

Miernik napięcia z siedmiosegmentowym wyświetlaczem

Autor Wiadomość
wojtekq2 



Posty: 4
Wysłany: 15-04-2017, 17:27   Miernik napięcia z siedmiosegmentowym wyświetlaczem

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:
Kod programu: Zaznacz cały

#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);
 
}
Polecany artykuł z FORBOT.PL:
Recenzja - Język C dla mikrokontrolerów AVR

Według autora po lekturze tej pozycji: poznamy budowę oraz podstawy programowania mikrokontrolerów, dowiemy si... Czytaj całość

Warto przeczytać » STM32. Aplikacje i ćwiczenia w języku C


Postaw piwo autorowi tego posta
 
 
marek1707 



Posty: 3991
Pomógł: 429 razy
Otrzymał 578 piw(a)
Skąd: WAW
Programuję w:
C, asm
Wysłany: 15-04-2017, 18:03   

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.

Postaw piwo autorowi tego posta
 
 
wojtekq2 



Posty: 4
Wysłany: 15-04-2017, 18:20   

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);

Postaw piwo autorowi tego posta
 
 
marek1707 



Posty: 3991
Pomógł: 429 razy
Otrzymał 578 piw(a)
Skąd: WAW
Programuję w:
C, asm
Wysłany: 16-04-2017, 00:08   

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.

Warto przeczytać » Klawiatura skrótów z Arduino, która ułatwia codzienną pracę


Ostatnio zmieniony przez marek1707 16-04-2017, 00:11, w całości zmieniany 1 raz  
Postaw piwo autorowi tego posta
 
 
wojtekq2 



Posty: 4
Wysłany: 18-04-2017, 11:41   

Wypełniłem tablicę ciągiem znaków i wszystko się ładnie wyświetla na monitorze portu szeregowego :
Kod programu: Zaznacz cały
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".
Kod programu: Zaznacz cały

#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

Ostatnio zmieniony przez wojtekq2 18-04-2017, 11:44, w całości zmieniany 1 raz  
Postaw piwo autorowi tego posta
 
 
marek1707 



Posty: 3991
Pomógł: 429 razy
Otrzymał 578 piw(a)
Skąd: WAW
Programuję w:
C, asm
Wysłany: 18-04-2017, 13:14   

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

Postaw piwo autorowi tego posta
 
 
wojtekq2 



Posty: 4
Wysłany: 20-04-2017, 08:10   

Miernik działa poprawnie ;) uporałem się z nim.
Kod dla miernika:
Kod programu: Zaznacz cały
#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:
Kod programu: Zaznacz cały

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.

Ostatnio zmieniony przez wojtekq2 20-04-2017, 08:19, w całości zmieniany 1 raz  
Postaw piwo autorowi tego posta
 
 
marek1707 



Posty: 3991
Pomógł: 429 razy
Otrzymał 578 piw(a)
Skąd: WAW
Programuję w:
C, asm
Wysłany: 20-04-2017, 11:20   

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ł.
Kod programu: Zaznacz cały
#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:
Kod programu: Zaznacz cały
tablica_liczb[i] = ascii2bin(tablica_znakow[i]);
definiując wcześniej funkcję:
Kod programu: Zaznacz cały
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 :)

Postaw piwo autorowi tego posta
 
 
Wyświetl posty z ostatnich:   
Odpowiedz do tematu
Nie możesz pisać nowych tematów
Nie możesz odpowiadać w tematach
Nie możesz zmieniać swoich postów
Nie możesz usuwać swoich postów
Nie możesz głosować w ankietach
Nie możesz załączać plików na tym forum
Możesz ściągać załączniki na tym forum
Wersja do druku

Skocz do:  

Nie rozwiązałeś swojego problemu? Zobacz podobne tematy: Przysyłanie/Odbieran... Poszukuję kogoś z A... Arduino, problem z o... Atmega - problem z b...
lub przeszukaj forum po wybranych tagach: miernik, napiecia, siedmiosegmentowym, wyswietlaczem


Powered by phpBB modified by Przemo © 2003 phpBB Group
Popularne kursy: Arduinopodstawy elektroniki