Witam. Już od pewnego czasu męczę się z czujnikiem temperatury przez I2C.Program składa się z 3 głównych rzeczy:
obsługa wyswietlacza LCD, obsługa I2C i funkcja wyświetlająca "na zrozumiały tekst" temperaturę na wyświetlaczu. czy jest tutaj ktoś kto mógłby "żucić" na to okiem i fachowo ocenić? Może nawet dałby radę pomóc i doprowadzić program do końcowej realizacji. Jeżeli tak, to wtedy chętnie zapodam kod źródłowy. Zaznaczam, że cały czas się uczę C i mam z tym jeszcze sporo problemów wciąż.Pozdrawiam.
To gitara :) Siedzę nad tym już sporo i nic...poprostu nie mam głowy do C, a chciałbym...Dzięki el_bart za odpowiedź, kiedyś już raz mnie pomogłeś, może teraz też dasz radę :) Teraz szczegóły...wyświetlacz LCD 2x16, czujnik temperatury mcp9801/9801, szyna I2C. Program można podzielić na 3 etapy: obsługa wyświetlacza LCD(sprawdzone oddzielnie, działa),obsługa szyny I2C (pisałem na podstawie not aplikacyjnych, nie mam 100% pewności czy jest dobrze) i funkcja przekonwertująca na "zrozumiały język" z I2C na wyświetlacz LCD (tego nie pisałem osobiście i nie rozumiem tego w 100%). Problem jet w tym, że nie działa, cały czas wyświetla się 0Stopni (na LCD). I teraz nie wiem...czy sknociłem coś w I2C czy też znajomy pomieszał coś w tej funkcji wyświetlajacej temperaturę. Moje umiejętności z C nie potrafią tego stwierdzić...mam nadzieję, że program jest w miarę czytelny. Będę bardzo wdzięczny jeżeli ktoś spróbuje rozwikłać ten problem. Co więcej, może macie jakieś pomysły odnośnie dobrej literatury do tego?
Pozdrawiam.
#include <8051.h> // kwarc 6 MHz srodowisko M-IDE programator ISPPROG
#include <stdio.h>
// definiowanie portow wyswietlacza LCD
#define PORT P1 //deklaracja portu
#define RS P1_2 //deklaracja bitow sterujacych
#define E P1_3
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//LCD
void Delay2(unsigned int k)
{
unsigned int i,j;
for (j=0;j<k;j++)
for (i=0; i<=296;i++);
}
void WriteToLcd(char X) //zapis bajtu do LCD
{ //schemat kasiazkowy
E = 1;
PORT /= 0xF0;
PORT &= (X / 0x0F);
E = 0;
E = 1;
X <<= 4;
PORT /= 0xF0;
PORT &= (X / 0x0F);
E = 0;
Delay2(1);
}
void LcdInit(void) //inicjalizacja w trybie 4 bity
{ //schemat ksiazkowy
char i;
Delay2(15);
PORT = 0x0F;
for (i = 0; i<3; i++)
{
E = 1;
PORT &= 0x3F;
E = 0;
Delay2(5);
}
E = 1;
PORT &= 0x2F;
E = 0;
Delay2(1);
WriteCommand(0x28);
WriteCommand(0x08);
WriteCommand(0x01);
WriteCommand(0x06);
WriteCommand(0x0C);
}
// funkcja do odczytu temeperatury - chyba lepiej bo wtedy latwiej w mainie to umiesci
unsigned int i2c_tempodczyt(void)
{
unsigned char lsb, msb;
unsigned int i;
Start();
I2C_Send(0x92);
I2C_Send(0x00);
Start();
I2C_Send(0x93);
lsb = I2C_Read(0);
msb= I2C_Read(1);
Stop();
i = (unsigned int)(msb);
i *= (unsigned int)(256);
i += (unsigned int)(lsb);
return i;
void main(void) //program glowny
{
unsigned long l;
unsigned int i;
bit znak;
//LCD
LcdInit();
while (1)
{
// glowna petla programu
i = i2c_tempodczyt();
// sprawdzanie znaku odczytanej wartosci ( najstarszy bit = 1 to wartosc
//ujemna
if (i & 0x8000)
{
// jest minus
znak = 1; // ustawia znak ze jest minus
// obliczenie moduly z wartosci ujemnej w u2 ( negacja i zwiekszenie o
//jeden )
i ^= 0x7fff;
i += 1;
} else znak = 0;
l = (unsigned long)(i);
// - to gdy dokladnosc do 0,1 stopnia tylko wtedy trzeba bu dodac do
//wyswietlania zeby wstawial kropke przed ostatnia liczba
/*
l = (unsigned long)(i) * 10UL;
*/
l /= (unsigned long)(256);
if (l > 9999ul ) l = 9999ul ;
//else i = (unsigned int)(l);
// wyswietla znak
if (znak) lcd_wysw(1,'-');
else lcd_wysw(1,' ');
// wyswietla liczbe
lcd_wyswint(2,(unsigned int)(l));
WriteText("Stopni");
Delay2(50);
no coz - kompilatora w glowie nie mam a kod zawiera sporo szczegolow sprzetowych (a schematu tez nie posaidam ;)).
na pierwsze spojrzenie nie podobaja mi sie f-cje odpowiadajace za oczekiwanie (btw: jaka jest roznica miedzy Delay() a Delay2() ?). zauwaz, ze 8051 to maszyna 8-bitowa! jezeli wiec pracujesz na wartosciach>8-bitow (int) to oznacza, ze kompilator musi to przetlumaczyc na jakas wewnetrzna reprentacje i kazde dodwanie, odejmowanie, etc zajmuje n-cyki zegarowych. tutaj (przy wyliczaniu opoznien) wazne sa 2 rzeczy:
1) jakim kwarcem taktujesz uklad?
2) czy '51 ktorej uzywasz potrzebuje 12 cykli zegara na 1 cykl maszynowy, czy mniej?
dlatego wlasnie pisalem, ze prawdopodobnie masz problem z odliczaniem czasu. sprobuj zrobic eksperyment: uzywajac f-cji Delay() ustaw opoznienie na np: 2-3 sekundy i niech uklad mruga jakas lampka co ten ustalony czas i ze stoperem sprawdz, czy rzeczywiscie trwa to tyle ile powinno - jezeli gdzies sie walnales to wyjdzie tutaj (np: dostaniesz mruganie co 4,8,10 sekund...).
jeszcze 4 :) uwagi do czasu:
1) jak potrzebujesz miec pewnosc, ze kawalek kodu wykona sie w okreslonym czasie to praktycznie wylacznie asembler - C sie tu nie sprawdza...
2) sprawdz czy przypadkiem srodowisko, w ktorym piszesz nie udostepnia f-cji do opoznien (nie wiem czy sie to nazywalo przypadkiem delay()...) - to jest typowy problem przy pisaniu kodu wiec jest malo prawdopodobne, zeby nie bylo tego w zadnej bibliotece.
3) kiedys robilem sterownik na uC do serw - po 3 dniach debugowania i experymentowania okazalo sie, ze kwarc jest uszkodzony i daje jakies z dupy wziete taktowanie - sprawdz i to, bo glupie bledy sa najtrudniejsze do zauwazenia!
4) moze warto zastanowic sie nad wykorzystaniem timer'ow?
kolejna sprawa: uzywasz czesto dzielenia calkowito liczbowego na zmiennych bajtowych, dzielac je przez dosc duze wartosci (np: 0xF0) - efektem tego moze byc w zasadzie tylko 0 albo 1. nie wiem czy zawsze o to Ci chodzilo.
w kodzie pojawia sie tez masa pohardcodowanych wartosci. poniewaz to jest uzadzenie wbudowane, nie ma co szalec z podprocedurami, ale zwykle makro:
#define ADDRESS2BLABLA(add) ((add)/0x0F) // konewrtuje podany adres do postaci blabla
...
adresik=ADDRESS2BLABLA(adresik);
bedzie o stokroc czytelniejsze niz:
add/=0x0F;
z jakas magiczna wartoscia w srodq, zas efektywnsc identyczna.
uwazaj tez z konstrukcjami postaci:
(unsigned char)(i / 1000) / 0x30
z kompilatorami roznie bywa - moze sie zdazyc, ze jeden najpierw podzieli a potem calosc przekonwertuje na uchar'a a drugi moze zrobic odwrotnie. jak cos jest nie pewne, lub malo czytelne zawsze lepiej dodac nawias:
(unsigned char)( (i / 1000) / 0x30 )
i sprawa jest jasna a kompilator ma jednoznacznie powiedziane co ma z tym zrobic.
czytelnosc jest naprawde wazna. porownaj:
unsigned int i2c_tempodczyt(void)
{
unsigned char lsb, msb;
unsigned int i;
Start();
I2C_Send(0x92);
I2C_Send(0x00);
Start();
I2C_Send(0x93);
lsb = I2C_Read(0);
msb= I2C_Read(1);
Stop();
i = (unsigned int)(msb);
i *= (unsigned int)(256);
i += (unsigned int)(lsb);
return i;
// switch to initial state (czy tez co innego to robi w przypadku tego czujnika)
i2c_begin();
i2c_send(I2C_CMD_COSTAM1);
i2c_send(I2C_CMD_COSTAM2);
// send temp. read request (j/w)
i2c_begin();
i2c_send(I2C_CMD_COSTAM3);
// read temperature (2 bytes)
lsb = i2c_read(I2C_NO_ACK);
msb = i2c_read(I2C_SEND_ACK);
// signal end of transmition
i2c_end();
// return result:
return msb*256+lsb;
}
komentarze i stale to nie ozdobniki - to sie poprostu lepiej czyta i widac od razu co to robi - nie trzeba sie zastanawiac nad szczegolami: liczy sie koncept.
no - to chyba bedzie tyle. ;) daj znac, jak Ci poszly experymenty z czasem i postaraj sie ten kod doprowadzic do pozadq - bedzie Ci wtedy latwiej bledy znajdowac. :)
_______________________________
[baszerr.org]
No witam. Właśnie wszystko odczytałem. Teraz będę analizować i zobaczymy co z tego będzie. Napewno dam znać, będę eksperymentować. Dziękuję. Pozdrawiam.
_______________________________
Cristof
Więc wziąłem się do poprawek i dalszej kombinacji. Według Twoich sugestii zacząłem od funkcji Delay.
I tak.
Funkcji Delay() w programie, którego kod źródłowy powyżej przedstawiłem, jest wykorzystywana do wyświetlacza LCD. Nie wiem tylko, nigdzie nie mogę doczytać, jak długie ma być to opóźnienie.
Jednakże napotkałem pewien problem przy wyliczeniach.Chcę to zrobić przy użyciu Timera. Ale po kolei...
W tym projekcie używam kwarcu 6Mhz. W innym używałem 11.0592 MHz i nie miałem żadnych problemów z wyliczeniami.
Dla kwarcu 11.0592 MHz (timer 16 bitowy jest):
Najdłuższy odmierzany czas to : (12*65536)/11.0592 co daje 0.071s.
częstotliwość wynikająca z wewnętrznego podziału i będąca podstawą do wyznaczenia cyklu maszynowego to : 11.0592Mhz/12 co daje 921600.
Jezeli 921600 podziele przez np. 46080 to uzyskam 20Hz co daje 0,05 sekundy, co bardzo się przydaje przy do późniejszego odmierzania czasu.
następnie wartość 46080 zamieniam na szesnastkową co daje B400 i od FFFF odejmuje B400, uzyskując przy tym 4BFF. wartość 0x4B później używam przy Timerze a wartość 0,05s służy jako podstawa do odpowiedniego dobierania czasu, jakiego potrzeba (opóxnienia czyli Delay).
Jednakże w żadne sposób nie mogę, używając tego samego toku myślenia, postąpić w przypadku kwarcu 6Mhz, gdyż odmierzany czas to (12*65536)/6Mhz co daje 0.131 sekundy iw żadne sposób nie mogę dobrać tak czestotliwości abym mógl ją przemnażac, gdyż zawsze wychdzoi poza TIMER , po zamianie na szesnastkowy kod zawsze jest przepełenienie.
Np.
6Mhz/12= 500000
I teraz trzeba dobrać taką częstotliwość, żeby móc ją póxniej przemnażać.
Wynik z dzielenia zaminić na szesnastkowy i odjąć od FFFF.
Mam nadzieję, że udało mnie się wyjaśnić w czym tkwi problem. I nie wiem kurcze jak sobie z tym poradzić.
P.S.
Dla kwarcu 11.0592 didoa świeci tak jak powinna. Pozdrawiam.
Namieszałem...sprostowanie.
Da się :) tylko trzeba z rana trzeźwioej myśleć :)
Więc tak:
kwarc 6Mhz, najdłuższy odliczany czas to 12*65536/6Mhz to 0,131 s
Częstotliwość wynikająca z wewnętrznego podziału to : 6Mhz/12 = 500000
Jeżel ipodziele to przez 50000 ( amoge, gdyz pojemność timera przekracza tę wartość) to wtedy uzyskam 10Hz, czyli 0,1 sekundy. Zamieniam liczbę 50000 na szesnastkową i otrzymuję C350. Od wartoości FFFF odejmuję tę wartość i otrzymuję wtedy wartość 3CAF, co wpisuję do rejestra Timerów.
Czyli wszystko dobrze i wszystko się zgadza...a nie działa prawidłwoo, walnięty kwarc?
A oto testowy kawałek programu:
#include <8051.h>
#include <stdio.h>
#define PortLED P2_5
void Delay (unsigned int time)
{
unsigned char i;
for (i=0; i<10; i++) //10 oznacza 1 sekunde, 10*0.1
{
TH1 = 0x3CAF;
TL0 = 0;
TR1 = 1;
while (!TF1);
TF1 = 0;
}
TR1 = 0;
}
void Delay2 (unsigned int tim)
{
unsigned char j;
for (j=0; j<50; j++) //50 oznacza 5 sekund, 50*0.01
Edit:
Działa, ale gdy wstawie do rejstru wartość C350 zamiast 3CAF. Jak można to wytłumaczyć?
No ale grunt, że działa. teraz pytanie tylko, jak duże powinno być takie opóźnienie w programie ? Nie wiem tego i nigdize nie potrafię tego doczytać. Zmieniony przez - cristof_w w dniu 2007-05-29 08:32:43
Zmieniony przez - cristof_w w dniu 2007-05-29 08:38:04
_______________________________
Cristof
a czy wpisywanie nie powinno wygladac tak:
TH1 = 0x3C;
TL1 = 0xAF;
?
ogolnie Twoj tok rozumowania jest poprawny - pewnie masz jakis glupi blad. sprawdz dokladnie, jaki tryb pracy ustawiles timerowi (moze 13-bitowy?), etc...
btw: w obu f-cjach Delay masz ten sam kod powielony - jak trafia Ci sie koniecznosc "copy&paste" to prawie na pewno trzeba nie co przeorganizowac kod.
co do samego I2C, to niestety nie pomoge Ci - nie mam w tej chwili czasu przedzierac sie przez specyfikacje (choc jest to dobrze opisane w paru ksiazkach) a nie znam jej na pamieci... ;)
_______________________________
[baszerr.org]
Więc tak. Timer jest 16-bitowy na pewno, świadczy o tym
TMOD=0x10;
Natomiast chyba jest błąd tutaj:
TL0=0; powinno chyba być raczej TL1=0;
Chwilowo tego nie sprawdzę, mały problem sprzętowy, mój własnej roboty układ dydaktyczny odmówił współpracy, szukam przyczyny, pewnie się skubany wkurzył, że tak go ostatnio wykorzystuje dużo :)))
I jeszcze jedno.zakładając, że dojdę do ładu z tymi opóźnienieami, to jakie musi być to opóxnienie, wykorzystywnae tutaj np. przy LCD? Kilka milisekund?
Pozdrawiam.
Więc tak. Timer jest 16-bitowy na pewno, świadczy o tym
TMOD=0x10;
Natomiast chyba jest błąd tutaj:
TL0=0; powinno chyba być raczej TL1=0;
Chwilowo tego nie sprawdzę, mały problem sprzętowy, mój własnej roboty układ dydaktyczny odmówił współpracy, szukam przyczyny, pewnie się skubany wkurzył, że tak go ostatnio wykorzystuje dużo :)))
I jeszcze jedno.zakładając, że dojdę do ładu z tymi opóźnienieami, to jakie musi być to opóxnienie, wykorzystywnae tutaj np. przy LCD? Kilka milisekund?
Pozdrawiam.