Skocz do zawartości
omnixcrs

Zakłócenia odczytu z DS18B20

Pomocna odpowiedź

Witam,
mam problem z poprawnym odczytem wartości z cyfrowych czujników temp. DALLAS DS18B20.

4 Czujniki mam podłączone do zasilania na płytce arduino 5V i GND, sygnały mam pod A1 i podciągnięte do 5V przez R=4k7. poniższym kodem odczytuje temp i wyświetlam na LCD.

Zwrócicie uwagę na filmik, niby temperatury są ok ale raz po raz na każdej z nich potrafi mignąć np. -121 stopni, czy ktoś ma pomysł ??

filmik :

#include <DallasTemperature.h>
#include <OneWire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

OneWire oneWire(A1); //Podłączenie do A1
DallasTemperature sensors(&oneWire); //Przekazania informacji do biblioteki
DeviceAddress tempKLARA = { 0x28, 0x9D, 0x5F, 0x59, 0x8, 0x0, 0x0, 0x71 };
DeviceAddress tempSYPIALNIA = { 0x28, 0x48, 0x7C, 0x59, 0x8, 0x0, 0x0, 0xE8 };
DeviceAddress tempKORYTARZ = { 0x28, 0xFF, 0x57, 0x50, 0x93, 0x15, 0x4, 0xEA };
DeviceAddress tempSALON = { 0x28, 0xA2, 0x1, 0x59, 0x8, 0x0, 0x0, 0x8F };

void setup()
{
lcd.begin(16, 2); 
sensors.begin();


void loop(){

sensors.requestTemperatures(); //Pobranie temperatury czujnika
lcd.setCursor(0, 0); //Ustawienie kursora
lcd.print("K:");
lcd.setCursor(3, 0);
lcd.println(sensors.getTempC(tempKLARA));
lcd.setCursor(8, 0);
lcd.print("S:");
lcd.setCursor(10, 0);
lcd.println(sensors.getTempC(tempSYPIALNIA));
lcd.setCursor(0, 1);
lcd.print("K:");
lcd.setCursor(3, 1);
lcd.println(sensors.getTempC(tempKORYTARZ));
lcd.setCursor(8, 1);
lcd.print("S:");
lcd.setCursor(10, 1);
lcd.println(sensors.getTempC(tempSALON));
}

Udostępnij ten post


Link to post
Share on other sites

Witam

Opis programu sugeruje, że jest to długa linia danych - różne pomieszczenia budynku. 4.7k jest OK dla nawet 10 czujników na płytce stykowej, dla większych długości kabli można próbować zmniejszyć ten rezystor do np. 2k. Z czego przewód też ma znaczenie. Jeśli jest to topologia gwiazdy to jeszcze gorzej, bo zwykle kabla jest więcej, pojemność większa, a trzeba go "napełniać i opróżniać" z prądu żeby były te 0 i 1. No i miga -127 nie -121, można też to sprawdzać w programie, jak jest jakaś bzdurka to zamiast niej wypisać np. "ERR". Jak zmniejszenie rezystora nie pomoże (to załatwia tylko tę część napełniania prądem) to trzeba zastosować inną komunikację lub przyjrzeć się kablom.

Udostępnij ten post


Link to post
Share on other sites

Tak jak mówisz, jest to topologia gwiazdy gdzie czujniki są oddalone od arduino o jakieś 10-15 m skrętką ftp. No ok więc spróbuje wstawic inne R moze wlasnie 2k i dam znać

Udostępnij ten post


Link to post
Share on other sites

Dzięki za podpowiedzi, dałem R 1K i odczyt jest poprawny. Niestety pojawił się inny problem, a mianowicie chyba działanie części programu odpowiadającej za odczyt z DS'ów i przesyłanie ich na LCD spowolniło działanie reszty programu w której mam zawarte załączanie świateł w domu, czyli: poprzednie (zanim dołożyłem do pętli część programu z DS'ami) jak naciskałem wyłącznik dzwonkowy na sekundę to normalnie zapalało się światło teraz muszę przytrzymać ten włącznik czasem nawet z 2s :/, macie pomysł czemu tak ?? poniżej podaje jak wygląda część kodu w pętli z DS i lcd

sensors.requestTemperatures(); //Pobranie temperatury czujnika
lcd.setCursor(0, 0); //Ustawienie kursora
lcd.print("K:");
lcd.setCursor(3, 0);
lcd.println(sensors.getTempC(tempKLARA));
lcd.setCursor(8, 0);
lcd.print("S:");
lcd.setCursor(10, 0);
lcd.println(sensors.getTempC(tempSYPIALNIA));
lcd.setCursor(0, 1);
lcd.print("K:");
lcd.setCursor(3, 1);
lcd.println(sensors.getTempC(tempKORYTARZ));
lcd.setCursor(8, 1);
lcd.print("S:");
lcd.setCursor(10, 1);
lcd.println(sensors.getTempC(tempSALON));

Udostępnij ten post


Link to post
Share on other sites

Poszukaj info ile czasu zajmuje odczyt z Ds-a... nie rób odczytu w kazdej pętli tylko co jakiś czas, wtedy problem z włącznikiem zniknie no chyba że akurat trafisz z włączaniem na moment odczytu temperatury.

Udostępnij ten post


Link to post
Share on other sites

ok, opanowałem zrobiłem sobie IF'a w pętli z resztą i jest ok, a tak trochę z innej beczki: macie jakiś pomysł na to aby włączać i wyłączać LCD keypad shield np. przyciskiem ?? nie mogę tego zrobić poprzez po prostu zdjęcie zasilania bo kiedy odłączę np. 5V to lcd nadal świeci tylko słabiej na samym gnd

Udostępnij ten post


Link to post
Share on other sites

Z tym rezystorem to nie przeginaj. To pamaga w "napełnieniu" prądem i podbiciem napięcia, ale pogarsza odpływ prądu, bo w między czasie dużo go napływa, to też może fałszować wyniki odczytu, bo DS będzie się grzał, 4,7 jest optymalnie, a przy dużych kablach trzeba schodzić w dół małymi kroczkami.

Co do czasu to daję info, czas zapytania i odczytu jednego czujnika to 30ms, każdy kolejny to +25ms, no i czas oczekiwania na pomiar 750ms. Wysyłanie na LCD też trochę trwa. Teraz ten Twój program robi te rzeczy przez 99,9% czasu procesora i zauważenie Twojego klawisza ma w poważaniu.

Powinieneś włączyć odczyt asynchroniczny poleceniem sensors.setWaitForConversion(0), ale od tej pory sam dbasz o to by nie pytać o temperaturę wcześniej niż po 750ms (przy 12 bitach) od polecenia pomiaru. A w tym czasie procek jest gotowy do innych zajęć. Normalnie biblioteka go zatrzymuje na 750ms delayem. Zaglądając do przykładu blinkwithoutdelay wszystko staje się jasne:

#include <DallasTemperature.h> 
#include <OneWire.h> 
#include <LiquidCrystal.h> 
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 
uint32_t teraz,ostatnio;
uint8_t sekundy;
#define led LED_BUILTIN
OneWire oneWire(A1); //Podłączenie do A1 
DallasTemperature sensors(&oneWire); //Przekazania informacji do biblioteki 
DeviceAddress tempKLARA = { 0x28, 0x9D, 0x5F, 0x59, 0x8, 0x0, 0x0, 0x71 }; 
DeviceAddress tempSYPIALNIA = { 0x28, 0x48, 0x7C, 0x59, 0x8, 0x0, 0x0, 0xE8 }; 
DeviceAddress tempKORYTARZ = { 0x28, 0xFF, 0x57, 0x50, 0x93, 0x15, 0x4, 0xEA }; 
DeviceAddress tempSALON = { 0x28, 0xA2, 0x1, 0x59, 0x8, 0x0, 0x0, 0x8F }; 

void setup() 
{ 
lcd.begin(16, 2); 
sensors.begin(); 
sensors.requestTemperatures(); //Rozkaz wykonania pomiaru
sensors.setWaitForConversion(0);
pinMode(led,OUTPUT);
}

void loop(){ 
teraz=millis();

if(teraz-ostatnio>=1000) //to sie wykona co sekunde, przy pomiarach w pokoju mozna co 10s
{
ostatnio=teraz;
sekundy++;
lcd.setCursor(0, 0); //Ustawienie kursora 
lcd.print("K:"); 
lcd.setCursor(3, 0); 
lcd.println(sensors.getTempC(tempKLARA)); 
lcd.setCursor(8, 0); 
lcd.print("S:"); 
lcd.setCursor(10, 0); 
lcd.println(sensors.getTempC(tempSYPIALNIA)); 
lcd.setCursor(0, 1); 
lcd.print("K:"); 
lcd.setCursor(3, 1); 
lcd.println(sensors.getTempC(tempKORYTARZ)); 
lcd.setCursor(8, 1); 
lcd.print("S:"); 
lcd.setCursor(10, 1); 
lcd.println(sensors.getTempC(tempSALON)); 
sensors.requestTemperatures(); //Rozkaz wykonania pomiaru
}
if(sekundy%5==0) digitalWrite(led, !(digitalRead(led))); // a analogicznie w czwartej sekundzie (%4==0) zapytac o temperature a w piatej temperature odczytac, jak sekundy przeleca zakres zmiennej to zaczna liczyc od zera

} 

Można tez kazać im w pierwszej sekundzie dokonać pomiaru, a w kolejnych sekundach po kolei robić update na LCD każdego z osobna. Wtedy procek jest ślepy tylko przez kilkadziesiąt ms w każdej sekundzie. Można też tak rozdzielić odczyty i wyświetlenia. Robiąc pomiary co 10s procek głownie czeka w gotowości do twoich usług zliczając tylko czy już ma coś robić.

Udostępnij ten post


Link to post
Share on other sites

ok dzięki za wskazówki, a co do LCD o którym pisałem wyżej macie jakieś pomysły ??

Udostępnij ten post


Link to post
Share on other sites

No z GND to nie świeci, zapewne ciągnie z linii danych. Natomiast jest pin podświetlenia, zwykle 15, który przez rezystor jest włączony do VCC. Jego się wpina pod pin procesora przez rezystor 300-700 (trzeba sobie wyliczyć z prawa Ohma, zależy jak jasno ma świecić, jakie jest VCC) i w programie piszesz, że jak naciśnięty jakiś klawisz to robi coś tam coś tam, a przy okazji odpala jakiś programowy licznik, by ten pin powodował że LCD świeci tak długo jak chcesz. Można też zrobić funkcję/procedurę/polecenie, że jak wciśniesz jakiś przycisk to pin dla LED LCD zmienia stan na przeciwny. Odłączać całkowicie LCD od VCC też zresztą nie można, bo po tym trzeba by robić inicjalizację LCD, trzeba po resecie ustawić jak się z nim komunikujesz.

Udostępnij ten post


Link to post
Share on other sites
Gość es2
Z tym rezystorem to nie przeginaj. To pamaga w "napełnieniu" prądem i podbiciem napięcia, ale pogarsza odpływ prądu, bo w między czasie dużo go napływa, to też może fałszować wyniki odczytu, bo DS będzie się grzał, 4,7 jest optymalnie, a przy dużych kablach trzeba schodzić w dół małymi kroczkami.

Zanim napisze sie taką głupotę, proponuje przeczytać zalecenia producenta.

[ Dodano: 06-04-2018, 21:58 ]

a co do LCD o którym pisałem wyżej macie jakieś pomysły ??

Obsługuj LCD w przerwaniach. W RAM uC zrób kopie ekranu a w przerwaniu np co 1ms wysyłaj jeden znak. Z ten sposób, dla programu głównego, obsługa LCD bedzie zajmować kilkaset us co 1ms czyli praktycznie 0% czasu procesora.

[ Dodano: 06-04-2018, 22:01 ]

Tak jak mówisz, jest to topologia gwiazdy gdzie czujniki są oddalone od arduino o jakieś 10-15 m

Problem może rozwiązać HUB 1-Wire.

Udostępnij ten post


Link to post
Share on other sites
Z ten sposób, dla programu głównego, obsługa LCD bedzie zajmować kilkaset us co 1ms czyli praktycznie 0% czasu procesora.

????? Albo któraś z wartości czasów jest nie tak, albo to nie będzie 'praktycznie 0% czasu procesora'.

Udostępnij ten post


Link to post
Share on other sites
Gość es2
Z ten sposób, dla programu głównego, obsługa LCD bedzie zajmować kilkaset us co 1ms czyli praktycznie 0% czasu procesora.

????? Albo któraś z wartości czasów jest nie tak, albo to nie będzie 'praktycznie 0% czasu procesora'.

Ile trwa wejście w przerwanie, pobranie danej z ram i wysłanie do LCD i wyjście z przerwania?

Jak mały LCD to można wysyłać co 10ms lub rzadziej.

Udostępnij ten post


Link to post
Share on other sites

No tak, ale chodzi mi o to, że kilkaset us z 1 ms to nie jest 'praktycznie 0% czasu procesora'. Jeśli przerwanie jest co 1ms i trwa ono powiedzmy 600us (czas całkowity), to przez 60% czasu procesor obsługuje przerwania. Gdzie tu 0%?

Udostępnij ten post


Link to post
Share on other sites
Gość es2
Jeśli przerwanie jest co 1ms i trwa ono powiedzmy 600us

600us wysłanie jednego znaku do LCD? "Gratuluję" pisania tak wydajnych kodów. Jak kolega pisze takie programy, to i ARM 168MHz jest za słaby do migania diodą.

W praktyce, zawsze używa się jakiegoś przerwania systemowego od timera 1 lub 10ms. Dodanie kilkunastu rozkazów obsługi LCD ile może zabrać czasu uC? Może kolega pokaże tan kod obsługi LCD trwający 600us, jestem naprawdę ciekaw "sztuki" pisania programów. Kto wie, może się czegoś nauczę.

[ Dodano: 07-04-2018, 10:22 ]

Poświęciłem się i odnalazłem kod obsługi LCD na przerwaniu

#define EXT_MEM_CELL(X) *((volatile unsigned char*)X)
lcdData[32];


SIGNAL(TIMER0_COMP_vect)
{
static x=0;


EXT_MEM_CELL( (word)0x8001 ) = lcdData[x];
x++;
if(x==16)
	{
	EXT_MEM_CELL( (word)0x8001 ) = 0x80+0x40;	// Kusror na 2 wiersz
	x=0;
	}
else if(x>=32)
	{
	EXT_MEM_CELL( (word)0x8001 ) = 0x80;	// Kusror na 1 wiersz
	x=0;
	}
}

W ASM wygląda tak:

SIGNAL(TIMER0_COMP_vect)
{
 b6:	1f 92       	push	r1
 b8:	0f 92       	push	r0
 ba:	0f b6       	in	r0, 0x3f	; 63
 bc:	0f 92       	push	r0
 be:	0b b6       	in	r0, 0x3b	; 59
 c0:	0f 92       	push	r0
 c2:	11 24       	eor	r1, r1
 c4:	2f 93       	push	r18
 c6:	8f 93       	push	r24
 c8:	9f 93       	push	r25
 ca:	ef 93       	push	r30
 cc:	ff 93       	push	r31
static x=0;


EXT_MEM_CELL( (word)0x8001 ) = lcdData[x];
 ce:	80 91 00 01 	lds	r24, 0x0100					// 2 cykle
 d2:	90 91 01 01 	lds	r25, 0x0101					// 2
 d6:	fc 01       	movw	r30, r24					// 1
 d8:	ee 0f       	add	r30, r30					// 1
 da:	ff 1f       	adc	r31, r31					// 1
 dc:	ee 5f       	subi	r30, 0xFE	; 254				// 1
 de:	fe 4f       	sbci	r31, 0xFE	; 254				// 1
 e0:	20 81       	ld	r18, Z						// 1
 e2:	20 93 01 80 	sts	0x8001, r18					// 2
x++;
 e6:	01 96       	adiw	r24, 0x01	; 1				// 1
 e8:	90 93 01 01 	sts	0x0101, r25					// 2
 ec:	80 93 00 01 	sts	0x0100, r24					// 2
if(x==16)
 f0:	80 31       	cpi	r24, 0x10	; 16				// 1
 f2:	91 05       	cpc	r25, r1						// 1
 f4:	11 f4       	brne	.+4      	; 0xfa <__vector_15+0x44>	// 2 bo prawda (1 gdy fałsz)
	{
	EXT_MEM_CELL( (word)0x8001 ) = 0x80+0x40;	// Kusror na 2 wiersz
 f6:	80 ec       	ldi	r24, 0xC0	; 192
 f8:	03 c0       	rjmp	.+6      	; 0x100 <__vector_15+0x4a>
	x=0;
	}
else if(x>=32)
 fa:	80 97       	sbiw	r24, 0x20	; 32
 fc:	3c f0       	brlt	.+14     	; 0x10c <__vector_15+0x56>
	{
	EXT_MEM_CELL( (word)0x8001 ) = 0x80;	// Kusror na 1 wiersz
 fe:	80 e8       	ldi	r24, 0x80	; 128
100:	80 93 01 80 	sts	0x8001, r24
	x=0;
104:	10 92 01 01 	sts	0x0101, r1
108:	10 92 00 01 	sts	0x0100, r1
	}
}
10c:	ff 91       	pop	r31
10e:	ef 91       	pop	r30
110:	9f 91       	pop	r25
112:	8f 91       	pop	r24
114:	2f 91       	pop	r18
116:	0f 90       	pop	r0
118:	0b be       	out	0x3b, r0	; 59
11a:	0f 90       	pop	r0
11c:	0f be       	out	0x3f, r0	; 63
11e:	0f 90       	pop	r0
120:	1f 90       	pop	r1
122:	18 95       	reti

Zasadniczy kod wykonuje się w 21 cykli co daje 1000000/16e6*21 około 1,3us. Przyjąłem zegar tylko 16MHz a np Mega328 z ArduinoUNO może pracować z zegarem 20MHz. Jeśli przyjąć, że w przerwaniu wykonywana jest tylko obsługa LCD to należy dodać wejście i wyjście z przerwania po ok 20 cykli czyli ok 3,8us.

Nawet jak obsługa LCD to machanie pinami GPIO to max jaki widzę to 10us. 1,us w stosunku do 1ms to 1,3*100/1000=0,13% Machając GPIO niech będzie 1%. Czy kolega zmieni pracę bo będzie zarabiał 0,13% więcej?

Dla małych wyświetlaczy można wysyłać dane co 10ms. Odświeżanie wyświetlacza 16x2 będzie trwało 160ms. Wtedy zajętość czasu procesora to 0,013% max 0,1%!

Dla dużych LCD, jak 40x2 trzeba wysyłać co 2ms, odświeżanie takiego LCD to 160ms. LCD 40x4 co 1ms to odświerzanie 160ms. LCD są wolne i wystarczy odświeżanie 3..5 razy na sekundę.

Rozwiązania takiego używałem na żółwiach takich jak Z-80, 8051 przy przerwaniu 5ms. Tam obciążenie było większe (kilka %). O MC68000 i C166 nie będę pisał, bo te należałoby porównywać z ARM a ograniczeniem jest szybkość komunikacji z LCD..

Proszę się teraz pochwalić swoim kodem zajmującym 600us bo to tak na oko to 100razy dłużej niż trzeba prze niezbyt wydajnym kodzie.

Udostępnij ten post


Link to post
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!

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