wtorek, 4 kwietnia 2017

Pamięć EERAM 47L16 - zapis i odczyt danych o różnych typach, studium przypadku.

W artykule zmierzymy się z zagadnieniem zapisu i odczytu do zewnętrznej pamięci, danych o różnych typach. Temat jest ciekawy i wart przybliżenia a w praktyce  może się przydać.
W tym zagadnieniu pamięć EERAM z uwagi na brak stronicowania uwidacznia swoje zalety.


O ile zapis tablicy lub jednego bajtu do zewnętrznej pamięci nie stanowi problemu i takie przykłady najczęściej królują w necie np przy okazji zapisu do EEPROM o tyle brak jest przykładów jak poradzić sobie z zapisem w jednym rzucie danych o różnych typach np.uint8_, uint16_t czy char a w szczególności jak zapisać np całą strukturę do pamięci i potem to odczytać jeszcze.

W artykule tym przybliżymy zagadnienie zapisu struktury do zewnętrznej pamięci EERAM . Struktura będzie zawierała elementy o różnych typach.
Schemat połaczeń taki jak w artykule Pamięć EERAM 47L16 - rodzynek wśród pamięci
Najpierw zadeklarujmy co chcemy konkretnie zapisać w pamięci EERAM.
Przyjmijmy taką postać danych :

 uint8_t   dzien;      /*1xbajt*/
 uint8_t   miesiac;  /*1xbajt*/
 uint16_t rok;         /*2xbajt*/
 char      znak;       /*1xbajt*/

Jak widzimy mamy tutaj trzy rodzaje typów (uint8_t, uint16_t i char). Możemy to też określić inaczej jako zmienną o rozmiarze jednego bajtu (uint8_t i char) i zmienną o rozmiarze dwóch bajtów (uint16_t).
Ponieważ pamięć EERAM ma rozmiar komórki równy jednemu bajtowi dlatego zapis zmiennej nie ważne w tym momencie jakiego typu musimy sprowadzić do zapisu jedno bajtowego. Z uint8_t i char z definicji nie ma problemu bo są to zmienne jedno bajtowe. Problem jest natomiast ze zmienną uint16_t , musimy ją zdemontować na dwa kawałki jednobajtowe i w takich kawałkach zapisać do pamięci EERAM.

Teoretycznie można by było już przystąpić do wysyłania danych w postaci pojedyńczych zmiennych ale komfort w obsłudze takiego sposobu zapisu i odczytu jest co najmniej wątpliwy. Prawdziwy komfor zapewni nam dopiero opakowanie zmiennych w strukturę. Czyli zamiast kilku cukierków luźno rozsypanych dostajemy jeden mega cukierek w jednym papierku. Poręczniej jest operować jednym opakowaniem niż kilkoma luźnymi.
Ale my pójdziemy krok dalej i komfort jaki daje struktura w operowaniu zbiorem danych podniesiemy zawijając ją w unię :). Po cholerę jeszcze unia ??
Połączenie Unii i struktury daje nam nowe możliwości efektywniejszego operowania na danych i rozszerzeniu możliwości w dostępie do nich.
Beletrystykę można uprawiać na różne sposoby ale nic nie zastąpi przykładu praktycznego.

Przedstawmy zatem postać docelowego opakowanie dla naszych danych :

   
    /*deklaracja pustej Unii ,dostep do danych : DATA.dzien lub DATA.tabela[0]*/
    typedef union{
        uint8_t tabela[4];/*rozmiar taki ile mamy bajtow w strukturze ponizej*/
        struct {
            uint8_t dzien; /*1xbajt*/
            uint8_t miesiac;/*1xbajt*/
            uint16_t rok;/*2xbajt*/
            char znak;/*1xbajt*/
        };
    } DATA;
    
    DATA rocznica_out ; /*tworzymy zmienna o typie DATA*/
    /*definjujemy dane w strukturze, wszystko musi być sprowadzone do jednego bajta*/
    rocznica_out.tabela[0] = 6;
    rocznica_out.tabela[1] = 5;
    rocznica_out.tabela[2] = (1980 & 0x00FF); /*wyodrebnienie mlodszego bajtu*/
    rocznica_out.tabela[3] = ((1980 & 0xFF00) >> 8); /*wyodrebnienie starszego bajtu*/
    rocznica_out.tabela[4] = 'I'; 
   


Takie połączenie unii i struktury daje nam możliwość indeksowania danych za pomocą tablicy. Gdybyśmy posłużyli się samą strukturą bez unii , dostęp do danych byłby w postaci DATA.dzien, DATA.miesiac etc.
Unia rozszerza nam możliwość dostępu do danych o postać : DATA.tabela[0], DATA.tabela[1]. Tabelę możemy indeksować i w wygodny sposób np zerować dane. Ale najważniejszą rolą unii w naszym przypadku jest rozbicie każdego typu danych w strukturze na jedno bajtowe elementy tablicy.

Daje nam to możliwość wygodnego zapisu danych do struktury z rozbiciem tych elementów, które są większe niż jeden bajt z racji typu. Zapis danych z wykorzystaniem unii przedstawia dolna część powyższego fragmentu kodu.

Widzymy jak zmienna rok typu uint16_t jest ładnie rozbita na dwa kawałki jednobajtowe i zapisana w tablicy pod indeksem 2 i 3. Rok (1980) w postaci uint16_t w pierwszym kroku maskujemy starszy bajt i to zapisujemy do tabela[2], w drugim kroku maskujemy młodszy bajt i przesuwamy starszy bajt w miejsce młodszego, wynik działania zapisujemy w tabela[3].
I w ten o to fajny sposób mamy ładnie przygotowane dane w postaci paczki zawierającej jednobajtowe dane.

Do szczęścia potrzebujemy jeszcze podretuszować funkcję do zapisu i odczytu naszej unio-struktury. Zatem w bibliotece i2c pojawiają się dwie dodatkowe funkcje :
/*zapis i odczyt struktur*/
void EERAM_write_structure(uint8_t adr_device, uint16_t subAddr,  uint16_t len, void *struktura);
 

void EERAM_sequential_read_structure(uint8_t adr_device, uint16_t subAddr, uint16_t len, void *struktura);
 
Na czerwono zaznaczyłem zmianę w przekazywanych zmiennych do funkcji w stosunku do przykładów zapisu i odczytu z poprzedniego artykułu o EERAM 47L16. Za pomocą void *struktura przekazać możemy dowolny typ danych.

Wewnątrz funkcji pojawia dodatkowy wskaźnik o typie uint8_t do którego przypisujemy przekazaną za pomocą void *struktura do funkcji unio-strukturę.
Zdefiniowany wskaźnik wewnątrz funkcji wskazuje nam na pierwszy element naszej unio-struktury. Wszystkie dane zatem będą zapisywane w pamięci w formacie uint8_t.
A jak to potem odebrać i poukładać w pustej strukturze a w szczególności jak odtworzyć typy jakie były pierwotnie w strukturze źródłowej ?? Bez obaw za pomocą kodu i przykładu zobaczymy , że jest to możliwe i działa doskonale.
W sumie sam byłem zaskoczony , jak to fajnie zadziałało :)

Po krótce co robimy w pliku main.c :
1 deklarujemy pustą unię DATA zawierającą strukturę z danymi i tablicę indeksującą naszą strukturę,
2 tworzymy obiekt rocznica_out o typie DATA,
3 wypełniamy obiekt rocznica_out danymi (dostęp do poszczególnych bajtów struktury za pomocą tablicy, indeksującej.) Tutaj rozbijamy typ uint16_t na dwa bajtowe kawałki,
4 ustawiamy rejestr pamięci 47L16  do trybu AUTO STORE,
5. zapisujemy wreszcie naszą strukturę opakowaną w unię czyli rocznica_out do pamięci 47L16, tutaj ważny jest sposób przekazania do funkcji zapisującej w postaci &rocznica_out, gdyby to była tablica użylibyśmy samej jej nazwy bez specyfikatora wyłuskania adresu &.
6.tworzymy pusty obiekt rocznica_in o typie DATA, do obiektu tego przepiszemy dane z pamięci EERAM i rozpakujemy dane do typów źródłowych w zasadzie dane same się usadowią w odpowiednich typach :)
7 wyświetlamy na LCD odczytane z pamięci EERAM dane i umieszczone w obiekcie rocznica_in
8 klaszczemy uszami z radości, że wszytko poszło gładko i ładnie zatrybiło.  :)

Myślę, że udowodniłem w miarę przystępny sposób, że zapis różnych typów danych opakowanych w strukturę (i unię) do zewnętrznej pamięci EERAM jest możliwe i działa. W przypadku zapisu do typowego EEPROM-u musimy wziąć poprawkę na stronicowanie pamięci czyli nie możemy rozkosznie przesłać np jednego bloku 2048 bajtowego (47L16) danych ale jesteśmy ograniczeni do bloków np 128 bajtów (Microchip) i 64 bajtów (ATMEL)

EERAM to naprawdę fajny wynalazek a struktury i unie to fajny pojemnik na dane do zapisu.

 1 /************************* 
 2    File:   ustaw_zegar.h
 3  *************************/
 4 
 5 #ifndef USTAW_ZEGAR_H
 6 #define USTAW_ZEGAR_H
 7 
 8 #define FCY 40000000 /* podajemy wartosc ustawionego zegara (40 MHz), wazne 
 9 aby przed includowaniem <libpic30.h>, potrzebne to jest do wyliczania delay-i*/
10 /*deklaracja funkcji*/
11 void ustaw_zegar(void) ;
12 
13 #endif  /* USTAW_ZEGAR_H */

 1 /************************* 
 2      File:   lcd.h         
 3  *************************/
 4 #ifndef LCD_H
 5 #define LCD_H
 6 
 7 /*_TRISB6 --> TRISBbits.TRISB6*/
 8 #define TRIS_RS_LCD   _TRISB11
 9 #define TRIS_EN_LCD   _TRISB10
10 #define TRIS_DB4_LCD  _TRISB9
11 #define TRIS_DB5_LCD  _TRISB8
12 #define TRIS_DB6_LCD  _TRISB7
13 #define TRIS_DB7_LCD  _TRISB6
14 /*_RB6 --> PORTBbits.RB6*/
15 #define RS_LCD   _RB11
16 #define EN_LCD   _RB10
17 #define DB4_LCD  _RB9
18 #define DB5_LCD  _RB8
19 #define DB6_LCD  _RB7
20 #define DB7_LCD  _RB6
21 
22 /*przyporzadkowanie adresow pamieci DD-RAM do pol wyswietlacza*/
23 /*Uwaga dla wyswietlacza 4x20 [0x00,0x40,0x14,0x54*/
24 #define LCD_Line1 0x00 /*adres 1 znaku 1 wiersza */
25 #define LCD_Line2 0x40 /*adres 1 znaku 2 wiersza */
26 #define LCD_Line3 0x10 /*adres 1 znaku 3 wiersza */
27 #define LCD_Line4 0x50 /*adres 1 znaku 4 wiersza */
28 
29  void Wyslij_do_LCD(unsigned char bajt);
30  void CzyscLCD();
31  void WlaczLCD();
32  void WyswietlLCD(char *napis);
33  void UstawKursorLCD(uint8_t y, uint8_t x);
34  void WpiszSwojeZnaki(void);
35  void lcd_int(int val);
36  void lcd_hex(int val);
37  void lcd_char(char c);
38 #endif  /* LCD_H */

 1 /************************* 
 2        File:  i2c.h         
 3  *************************/
 4 
 5 #ifndef I2C_H
 6 #define I2C_H
 7 extern uint8_t error_flag ;
 8 /*deklaracja funkcji*/
 9 void ustaw_I2C1(void);  /*Init the I2C module*/
10 void i2c_start(void);   /*generates an I2C Start condition*/
11 void i2c_restart(void); /*generates an I2C Restart condition (for reads)*/
12 void i2c_stop(void);    /*generates an I2C Stop condition*/
13 void i2c_write(unsigned char i2c_data); /*writes a byte to the I2C bus*/
14 unsigned char i2c_read(void);    /*reads a byte from the I2C bus*/
15 void i2c_ack(void);     /*generates a Master Acknowledge*/
16 void i2c_nack(void);    /*generates a Master No Acknowledge*/
17 void obsluga_error(void);
18 void i2c_write_buf( uint8_t adr_device, uint8_t adr, uint8_t len, uint8_t *buf);
19 void i2c_read_buf(uint8_t adr_device, uint8_t adr, uint8_t len, uint8_t *buf);
20 /*dedykowana obsluga EERAM*/
21 void EERAM_write_STATUS_REGISTER(uint8_t adr_device, uint16_t subAddr,  unsigned char i2c_data);
22 void EERAM_write_buf(uint8_t adr_device, uint16_t subAddr,  uint16_t len, char *buf);
23 void EERAM_write_byte(uint8_t adr_device, uint16_t subAddr,  unsigned char i2c_data);
24 void EERAM_sequential_read_buf(uint8_t adr_device, uint16_t subAddr, uint16_t len, char *buf);
25 unsigned char EERAM_read_byte(uint8_t adr_device, uint16_t subAddr);
26 /*zapis i odczyt struktur*/
27 void EERAM_write_structure(uint8_t adr_device, uint16_t subAddr,  uint16_t len, void *struktura);
28 void EERAM_sequential_read_structure(uint8_t adr_device, uint16_t subAddr, uint16_t len, void *struktura);
29 #endif  /* I2C_H */

 1 /************************* 
 2    File:   ustaw_zegar.c 
 3  *************************/
 4 /*Ustawiamy zegar wewnetrzny na ok 40 MHz (dokladnie na 39.61375 MHz*/
 5 #include "xc.h" /* wykrywa rodzaj procka i includuje odpowiedni plik 
 6 naglówkowy "p24HJ128GP502.h"*/
 7 #include <stdio.h>
 8 #include <stdlib.h>
 9 #include <stdint.h> /*dyrektywy do uint8_t itp*/
10 #include "ustaw_zegar.h" /*z uwagi na FCY musi byc przed #include <libpic30.h>*/
11 
12 /*definicja funkcji*/
13 void ustaw_zegar(void) {
14  /*
15   * to co mozemy ustawic za pomoca '#pragma' jest dostepne w pliku 
16   * xc16/docs/config_index.html
17   */
18  #pragma config JTAGEN = OFF
19  // Watchdog timer enable/disable by user software
20  #pragma config FWDTEN = OFF 
21   
22  //********************Start Ustawien Zegara************************
23  /* 
24   * Fcy - zegar instrukcji , Fosc - zegar rdzenia (jest zawsze dwa razy wiekszy 
25   * od zegara instrukcji)) Ustawiamy zegar instrukcji na 40 Mhz z wewnetrznego 
26   * oscylatora Fin=7.37 MHz w/g wzoru Fcy=Fosc/2 gdzie Fosc=Fin x (M/(N1+N2))
27   * gdzie M=43, N2=2, N1=2 ustawiane w rejestrach PLLFBD/PLLPOST/PLLPRE
28   */
29  //Select Internal FRC (Fast RC Oscillator)
30  #pragma config FNOSC = FRC // FOSCSEL-->FNOSC=0b000 (Fast RC Oscillator (FRC))
31  //Enable Clock Switching and Configure
32  #pragma config FCKSM = CSECMD //FOSC-->FCKSM=0b01 - wlacz zegar
33  #pragma config OSCIOFNC = OFF //FOSC-->OSCIOFNC=1 - Fcy b?dzie na pinie OSCO
34  
35      /*Config PLL prescaler, PLL postscaler, PLL divisor*/
36       PLLFBD = 41 ; //M=43 (0 bit to 2 st?d 41 = 43 patrz w rejestrze), tutaj 3.685 x 43 = 158.455MHz
37       CLKDIVbits.PLLPRE=0 ;  //N1=2, tutaj 7.37 MHz / 2 = 3.685 MHz
38       CLKDIVbits.PLLPOST=0 ; //N2=2, tutaj 158.455 MHz / 2 = 79.2275 MHz (Fosc)
39    /*   
40    * UWAGA przerwania musza byc wylaczone podczas wywolywania ponizszych 
41    * dwóch funkcji __builtin_write_...brak definicji w pliku naglówkowym
42    * to wewnetrzne funkcje kompilatora patrz help M-LAB IDE
43    * i datasheet str 140(11.6.3.1 Control Register Lock)
44    */
45   /*Initiate Clock Switch to Internal FRC with PLL (OSCCON-->NOSC = 0b001)*/
46       __builtin_write_OSCCONH(0x01); //tutaj argumentem jest wartosc z NOSC
47   /*Start clock switching*/
48       __builtin_write_OSCCONL(0x01);
49       
50   /*Wait for Clock switch to occur*/
51       while(OSCCONbits.COSC !=0b001);
52       
53       /*Wait for PLL to lock*/
54       while(OSCCONbits.LOCK !=1) {};
55      
56 }

  1     /************************* 
  2           File:   lcd.c         
  3      *************************/
  4     #include "xc.h" /* wykrywa rodzaj procka i includuje odpowiedni plik*/
  5     #include <stdio.h>
  6     #include <stdlib.h>
  7     #include <string.h>
  8     #include <stdint.h> /*dyrektywy uint8_t itp*/
  9     #define FCY 40000000UL /* podajemy wartosc ustawionego zegara (40 MHz), wazne 
 10     aby przed includowaniem <libpic30.h>, potrzebne to jest to wyliczania delay-i*/
 11     #include <libpic30.h> /*biblioteka dajaca dostepp do delay-i.*/
 12     #include "lcd.h"
 13 
 14     /*definicje funkcji*/
 15     void Wyslij_do_LCD(unsigned char bajt)
 16     {
 17     /*ustaw linie EN, przed wysylkadanych*/
 18 
 19     EN_LCD = 1;
 20     /*wyslanie 4 najstarszych bitow danych*/
 21     if(bajt & 0x80)     DB7_LCD = 1; else DB7_LCD = 0;
 22     if(bajt & 0x40)     DB6_LCD = 1; else DB6_LCD = 0;
 23     if(bajt & 0x20)     DB5_LCD = 1; else DB5_LCD = 0;
 24     if(bajt & 0x10)     DB4_LCD = 1; else DB4_LCD = 0;
 25     __delay_us(1);
 26     /*potwierdzenie wyslania danych (opadajacym zboczem EN)*/
 27     EN_LCD = 0;
 28 
 29     /*ustawienie EN*/
 30     __delay_us(1);
 31     EN_LCD = 1;
 32     /*wyslanie 4 najmlodszych bitow danych*/    
 33     if(bajt & 0x08)     DB7_LCD = 1; else DB7_LCD = 0;
 34     if(bajt & 0x04)     DB6_LCD = 1; else DB6_LCD = 0;
 35     if(bajt & 0x02)     DB5_LCD = 1; else DB5_LCD = 0;
 36     if(bajt & 0x01)     DB4_LCD = 1; else DB4_LCD = 0;
 37     __delay_us(1);
 38     /*potwierdz wysylke danych opadajacym zboczem EN*/
 39     EN_LCD = 0;
 40 
 41     __delay_us(37);
 42 
 43     }   
 44 
 45     void CzyscLCD()
 46     {
 47     RS_LCD = 0;
 48     Wyslij_do_LCD(1);
 49     RS_LCD = 1;
 50     /*czekaj minimum 1.64 ms*/
 51     __delay_ms(2);
 52     } 
 53 
 54     void WlaczLCD()
 55     {
 56     /*ustawienie kierunku wyjsciowego linii podlaczonych do LCD*/
 57     TRIS_RS_LCD = 0;
 58     TRIS_EN_LCD = 0;
 59     TRIS_DB7_LCD = 0;
 60     TRIS_DB6_LCD = 0;
 61     TRIS_DB5_LCD = 0;
 62     TRIS_DB4_LCD = 0;
 63 
 64     /*zerowanie linii*/
 65     RS_LCD = 0; /*wskazuje na rejestr rozkazow*/
 66     EN_LCD = 0;
 67     DB7_LCD = 0;
 68     DB6_LCD = 0;
 69     DB5_LCD = 0;
 70     DB4_LCD = 0;
 71 
 72     /*Start Inicjalizacji HD44780*/
 73     /*zaczekaj co najmniej 45 ms na ustabilizowanie sie napiecia*/
 74     __delay_ms(45);
 75 
 76     /*powtarzaj 3 x sekwencje startowa… 0011 (hex 0x30 i wpisz ja do rejestru rozkazow */
 77     /*(RS=0 ustawione przy zerowaniu linii)*/
 78 
 79     /*ustaw linie EN, przed wysylka danych*/
 80     EN_LCD = 1;
 81     /*zaladuj sekwencje startowa 0011*/
 82     DB7_LCD = 0;
 83     DB6_LCD = 0;
 84     DB5_LCD = 1;
 85     DB4_LCD = 1;
 86 
 87     __delay_us(1);
 88     /*potwierdz wysylke danych opadajacym zboczem EN*/
 89     EN_LCD = 0;
 90     /*zaczekaj co najmniej minimum 4.1 ms*/
 91     __delay_ms(4.1);
 92 
 93      /*ustaw linie EN, przed wysylka danych*/
 94     EN_LCD = 1;
 95     /*zaladuj sekwencje startowa 0011*/
 96     __delay_us(1);
 97     /*potwierdz wysylke danych opadajacym zboczem EN*/
 98     EN_LCD = 0;
 99     /*zaczekaj 100 us*/
100     __delay_us(100);
101 
102      /*ustaw linie EN, przed wysylka danych*/
103     EN_LCD = 1;
104     /*zaladuj sekwencje startowa 0011*/
105     __delay_us(1);
106     /*potwierdz wysylke danych opadajacym zboczem EN*/
107     EN_LCD = 0;
108     /*zaczekaj 100 us*/
109     __delay_us(100);
110 
111     /* *********************Koniec Inicjalizacji HD44780***************** */
112 
113     /* ***********************Start ustawien HD44780 ******************** */   
114     /*ustaw parametry wyswietlacza, wyslanie slowa operacyjnego do rejestru rozkazow, RS na 0
115      bit 7 = 0 (musi byc 0)
116      bit 6 = 0 (musi byc 0)
117      bit 5 = 1 (musi byc 1)
118      bit 4 = 0 (slowo danych i interfejs ma 4 bity)
119      bit 3 = 1 (2 wiersze znakow)
120      bit 2 = 0 (matryca 5x8 pikseli)
121      bit 1 = 0 (bez znaczenia)
122      bit 0 = 0 (bez znaczenia) */
123     /*Uwaga po kazdym uzyciu RS = 0 (wybierz rejestr rozkazow), ustawiamy RS = 1*/
124     /*(rejestr danych)*/
125 
126     RS_LCD = 0; /*stan niski na lini RS, wybieramy rejestr instrukcji*/
127     Wyslij_do_LCD(0b00101000);//wysylamy instrukcj? do rejestru rozkazow
128     RS_LCD = 1; /*przelacz na rejestr danych */    
129 
130     /*wlacz wyswietlacz, wyslanie slowa operacyjnego, RS na 0
131      bit 7 = 0 (musi byc 0)
132      bit 6 = 0 (musi byc 0)
133      bit 5 = 0 (musi byc 0)
134      bit 4 = 0 (musi byc 0)
135      bit 3 = 1 (musi byc 1)
136      bit 2 = 1 (wyswietlacz wlaczony)
137      bit 1 = 0 (kursor wylaczony)
138      bit 0 = 0 (migotanie kursora wylaczone) */
139 
140     RS_LCD = 0; /*stan niski na lini RS, wybieramy rejestr instrukcji*/
141     Wyslij_do_LCD(0b00001100);//wysylamy instrukcje do rejestru rozkazow
142     RS_LCD = 1;  /*przelacz na rejestr danych */ 
143 
144     CzyscLCD();
145 
146     /*ustaw tryb pracy wyswietlacza
147     bit 7 = 0 (musi byc 0)
148     bit 6 = 0 (musi byc 0)
149     bit 5 = 0 (musi byc 0)
150     bit 4 = 0 (musi byc 0)
151     bit 3 = 0 (musi byc 0)
152     bit 2 = 1 (musi byc 1)
153     bit 1 = 1 (inkremetacja)
154     bit 0 = 0 (wpis znaku od lewej) */
155 
156     RS_LCD = 0; /*stan niski na lini RS, wybieramy rejestr instrukcji*/
157     Wyslij_do_LCD(0b00000110);/*wysylamy instrukcje do rejestru rozkazow*/
158     RS_LCD = 1;  /*przelacz na rejestr danych */    
159 
160     /*Koniec inicjalizacji i ustawien wyswietlacza HD44780*/      
161     }
162     /*wyslij jeden znak do LCD*/
163     void lcd_char(char c){
164     Wyslij_do_LCD(c);
165     }
166     /*wyslij stringa do LCD*/
167     void WyswietlLCD(char *napis)
168     {
169     while(*napis){
170     Wyslij_do_LCD(*napis++);
171     }
172 
173     }
174 
175     void UstawKursorLCD(uint8_t y, uint8_t x)
176     {
177     uint8_t n ;
178     /*y (wiersze) = 1 do 4*/
179     /*x (kolumna) = 1 do 16*/
180     /*ustal adres poczatku znaku w wierszu*/
181     switch(y)
182     {
183     case 1: y = LCD_Line1 ;break;
184     case 2: y = LCD_Line2 ;break;
185     case 3: y = LCD_Line3 ;break;
186     case 4: y = LCD_Line4 ;break;
187 
188     }
189     /*ustal nowy adres pamieci DD RAM*/
190     /*ustaw bajt do Set DDRAM adres*/
191     /* x odejmujemy jeden aby przekonwertowac z 0-15 na 1-16 */
192     n = 0b10000000 + y + (x-1) ;
193 
194     /*wyslij rozkaz ustawienia nowego adresu DD RAM*/
195     RS_LCD = 0; /*stan niski na liniÄ™ RS, wybieramy rejestr instrukcji*/
196     Wyslij_do_LCD(n);
197     RS_LCD = 1;  /*przelacz na rejestr danych */ 
198     }
199     // wyslanie liczby dziesietnej
200     void lcd_int(int val)
201     {
202     char bufor[17];
203     sprintf(bufor,"%i",val);
204     WyswietlLCD(bufor);
205     }
206 
207     // wyslanie liczby HEX
208     void lcd_hex(int val) {
209     char bufor[17];
210     sprintf(bufor,"%#06x",val); /*rozwninie do 6 znakow 0xffff*/
211     WyswietlLCD(bufor);
212     }
213     void WpiszSwojeZnaki(void) {
214     /*definicja wlasnych znakow maks 8 szt*/
215     char znak1[]= {0,0,14,17,31,16,14,2}; /* definicja literki e z ogonkiem */
216     int i; 
217     /* adresy poczatku definicji znaku to wielokrotnosc osmiu DEC(0,8,16,24,32,40,48,56)
218      * ale uwaga wlasne ! adresy kodowane sana 6 mlodszych bitach dwa najstarsze bity
219      * to zawsze  01 (01AAAAAA-gdzie A adres).Uwzgledniajac wartosc calego bajtu
220      * adresy poczatku bajtu wygladaja tak HEX(0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78)
221      * Aby wpisac do pamieci wyswietlacza zdefiniowany znak nalezy najpierw wyslac 
222      * do rejestru rozkazow (RS na 0) adres poczatku definicji znaku 
223      * a w drugim kroku przesylamy dane (RS=1) 8 x bajt (tablica) definjujace obraz znaku*/
224 
225     RS_LCD = 0 ;/*stan niski na linich RS, wybieramy rejestr instrukcji*/
226      /*wysylamy instrukcje do rejestru rozkazow (ustaw adres poczatkowy w CGRAM 
227       na nasz znak w tym przypadku znak na pozycji drugiej) */
228     Wyslij_do_LCD(0x48);/*wysylamy instrukcje do rejestru rozkazow 
229      (ustaw adres poczatkowy w CGRAM na nasz znak w tym przypadku znak na pozycji drugiej) */
230 
231     RS_LCD = 1 ;/*stan wysoki na lini RS, wybieramy rejestr danych*/
232     /*wysylamy 8 x bajt zdefiniowanego w tablicy znak1[] znaku*/
233     for(i=0;i<=7;i++)
234     {
235        Wyslij_do_LCD(znak1[i]);
236     }
237 
238     RS_LCD = 0 ;/*stan niski na lini RS, wybieramy rejestr instrukcji*/
239     /*ustawiamy adres DDRAM na pierwszy znak w pierwszej linii, nie zapomnijmy
240      o tym poniewaz inaczej zostaniemy w pamieci CGRAM*/
241     Wyslij_do_LCD(0x80);
242     }

  1     /************************* 
  2        File: i2c.c           
  3     *************************/
  4     #include "xc.h"
  5     #include <stdio.h>
  6     #include <stdlib.h>
  7     #include <stdint.h> /*dyrektywy uint8_t itp*/
  8     #include <string.h>
  9     #include "ustaw_zegar.h" /*tutaj m.in ustawione FCY*/
 10     #include <libpic30.h> /*biblioteka dajaca dostep do delay-i,musi byc po zaincludowaniu ustaw_zegar.h*/
 11     #include "i2c.h"
 12 
 13     uint8_t error_flag ;
 14     /*definicje funkcji*/
 15     void ustaw_I2C1(void){
 16     /*pin 17(SCL) i 18(SDA) sa cyfrowe na starcie, nie trzeba tutaj wylaczac wejsc analogowych*/
 17     I2C1BRG = 391 ; /*przy FCY ok 40 MHz dla bitrate 100 kHz I2C1BRG = 391 dla 400 kHz I2C1BRG=91*/
 18     I2C1STAT = 0x0000; /*Status Register - pasuje wszystko na zero */
 19     I2C1CON = 0x1200; /*Control Register - Relase SCL clock / Slew rate control disabled / 7-bit slave address*/ 
 20     /*zerujemy rejestr nadawczy i odbiorczy*/
 21     I2C1RCV = 0;
 22     I2C1TRN = 0;
 23     /*Enable I2C1 module*/
 24     I2C1CONbits.I2CEN = 1;
 25 
 26     }
 27 
 28     void obsluga_error(void){
 29     /*Timer1 zlicza przez ok 13 ms przy FCY 40MHz*/
 30     TMR1 =0; /*clear Timer1*/
 31     IFS0bits.T1IF=0 ; /*zeruj flage od przepelnienia Timer1*/
 32     T1CONbits.TCKPS = 0b01 ; /*Prescaler 1:8 , daje nam 5 MHz*/
 33     T1CONbits.TON=1;    /*start Timer1*/
 34     /*czekaj na flage MI2C1IF po zakonczeniu poprawnie dowolnej operacji przez Mastera na I2c
 35       lub na przepelnienie Timer1 czyli ok 13ms*/ 
 36     while(!(IFS1bits.MI2C1IF | IFS0bits.T1IF ));  
 37     if(IFS0bits.T1IF){ /*jesli Timer1 przepelniony*/
 38         IFS0bits.T1IF=0 ; /*zeruj flage od przepelnienia Timer1*/
 39         IFS1bits.MI2C1IF=0; /*clear the I2C general flag*/
 40         error_flag = 1; /*set the error flag*/
 41         PORTAbits.RA1 = 1 ; /*LED ON*/
 42         /*tutaj kod uzytkownika do obslugi bledu, np zapalenie diody LED etc*/
 43     }
 44     else {error_flag = 0; /*clear the error flag*/
 45         IFS1bits.MI2C1IF=0; /*clear the I2C general flag*/
 46         PORTAbits.RA1 = 0; /*LED OFF*/
 47     }
 48     T1CONbits.TON=0; /*stop Timer1*/
 49     TMR1 =0; /*clear Timer1*/
 50     }
 51 
 52 
 53     void i2c_start(void){
 54     while(I2C1STATbits.TRSTAT); /*czekaj az linia bedzie wolna */
 55     I2C1CONbits.SEN = 1;        /*generuj sygnal start*/
 56     obsluga_error();            
 57     }
 58 
 59     void i2c_restart(void){
 60     while(I2C1STATbits.TRSTAT); /*czekaj az linia bedzie wolna */
 61     I2C1CONbits.RSEN=1; /*generuj restart*/
 62     obsluga_error();  
 63     }
 64 
 65     void i2c_stop(void){
 66     while(I2C1STATbits.TRSTAT); /*czekaj az linia bedzie wolna */
 67     I2C1CONbits.PEN=1; /*generuj stop*/
 68     obsluga_error();  
 69     }
 70 
 71     void i2c_write(unsigned char i2c_data){
 72     while(I2C1STATbits.TRSTAT); /*czekaj az linia bedzie wolna */
 73     I2C1TRN=i2c_data; /*load byte in the transmiter buffer*/
 74     obsluga_error();
 75     }
 76 
 77     unsigned char i2c_read(void){
 78     while(I2C1STATbits.TRSTAT); /*czekaj az linia bedzie wolna */
 79     I2C1CONbits.RCEN=1; /*enable Master receive*/
 80     obsluga_error();
 81     return(I2C1RCV); /*return data in buffer*/
 82     }
 83 
 84     void i2c_ack(void){
 85     I2C1CONbits.ACKDT=0; /*clear the related flag for ACK*/
 86     I2C1CONbits.ACKEN=1; /*start ACK sequence*/
 87     obsluga_error();
 88     }
 89 
 90     void i2c_nack(void){
 91     I2C1CONbits.ACKDT=1; /*set the related flag for NotACK*/
 92     I2C1CONbits.ACKEN=1; /*start ACK sequence*/
 93     obsluga_error();
 94     I2C1CONbits.ACKDT=0; /*clear the related flag for ACK*/
 95     }
 96 
 97     void i2c_write_buf( uint8_t adr_device, uint8_t adr, uint8_t len, uint8_t *buf ) {
 98     i2c_start();
 99     i2c_write(adr_device); /*wyslanie adresu urzadzenia z bitem R/W w stanie niskim*/
100     i2c_write(adr);
101     while (len--) i2c_write(*buf++);
102     i2c_stop();
103     }
104 
105     void i2c_read_buf(uint8_t adr_device, uint8_t adr, uint8_t len, uint8_t *buf) {
106     i2c_start();
107     i2c_write(adr_device);/*wys?anie adresu urzadzenia z bitem R/W w stanie niskim*/
108     i2c_write(adr);
109     i2c_start();
110     i2c_write(adr_device + 1);/*zapisuje adres urzadzenia z bitem R/W ustawionym na 1 czyli o 1 zwiekszamy adres urzadzenia*/
111     while (len--){
112      if(len) {
113      *buf++ = i2c_read();
114      i2c_ack();
115         }
116      else {
117      *buf++ = i2c_read();
118      i2c_nack();
119          }
120      }
121     i2c_stop();
122     }
123     /*Funkcje dedykowane do obslugi pamieci EERAM PIC 47L04 lub 47L16*/
124     /*zapis danych do rejestru pamieci EERAM*/
125     void EERAM_write_STATUS_REGISTER(uint8_t adr_device, uint16_t subAddr,  unsigned char i2c_data) {
126         i2c_start();
127         i2c_write(adr_device); /*wyslanie bajtu adresu z bitem R/W ustawionym na zero*/
128         i2c_write((subAddr & 0xFF00) >> 8);  /*wyslanie starszego bajtu adresu pamieci*/
129         i2c_write(subAddr & 0x00FF);             /*wyslanie mlodszego bajtu adresu pamieci*/
130         i2c_write(i2c_data);
131         i2c_stop();
132     __delay_ms(1); /*oczekiwanie na zapis do STATUS REGISTER*/
133     }
134 
135     /*zapis danych do pamieci EERAM - tryb sekwencyjny czyli ciag bajtow*/
136     void EERAM_write_buf(uint8_t adr_device, uint16_t subAddr,  uint16_t len, char *buf) {
137         i2c_start();
138         i2c_write(adr_device); /*wyslanie bajtu adresu z bitem R/W ustawionym na zero*/
139         i2c_write((subAddr & 0xFF00) >> 8);  /*wyslanie starszego bajtu adresu pamieci*/
140         i2c_write(subAddr & 0x00FF);             /*wyslanie mlodszego bajtu adresu pamieci*/
141         while (len--) {
142         /*inkrementujemy tylko dane,adres jest inkrementowany automatycznie przez EERAM*/
143             i2c_write(*buf++); 
144             }
145         i2c_stop();
146      }
147     /*zapis danych do pamieci EERAM - tryb pojedynczego bajtu*/
148     void EERAM_write_byte(uint8_t adr_device, uint16_t subAddr,  unsigned char i2c_data) {
149         i2c_start();
150         i2c_write(adr_device); /*wyslanie bajtu adresu z bitem R/W ustawionym na zero*/
151         i2c_write((subAddr & 0xFF00) >> 8);  /*wyslanie starszego bajtu adresu pamieci*/
152         i2c_write(subAddr & 0x00FF);             /*wyslanie mlodszego bajtu adresu pamieci*/
153         i2c_write(i2c_data);
154         i2c_stop();
155     }
156     /*odczyt sekwencyjny danych z pamieci EERAM.*/
157     void EERAM_sequential_read_buf(uint8_t adr_device, uint16_t subAddr, uint16_t len, char *buf) {
158         i2c_start();
159         i2c_write(adr_device); /*wyslanie adresu urzadzenia z bitem R/W w stanie niskim*/
160         i2c_write((subAddr & 0xFF00) >> 8) ; /*wyslanie starszego bajtu adresu pamieci*/
161         i2c_write(subAddr & 0x00FF)  ;       /*wyslanie mlodszego bajtu adresu pamieci*/
162         i2c_start();
163         i2c_write(adr_device + 1);/*zapisuje adres urzadzenia z bitem R/W ustawionym na 1 czyli o 1 zwiekszamy adres urzadzenia*/
164         i2c_ack();
165         while(len--) {
166         /*inkrementujemy tylko dane,adres jest inkrementowany automatycznie przez EERAM*/
167         *buf++ = i2c_read();
168         i2c_ack();
169         }
170         i2c_nack();
171         i2c_stop();
172     }
173     /*odczyt jednego bajtu z pamieci EERAM.*/
174     unsigned char EERAM_read_byte(uint8_t adr_device, uint16_t subAddr) {
175         unsigned char i2c_data;
176         i2c_start();
177         i2c_write(adr_device); /*wyslanie adresu urzadzenia z bitem R/W w stanie niskim*/
178         i2c_write((subAddr & 0xFF00) >> 8) ; /*wyslanie starszego bajtu adresu pamieci*/
179         i2c_write(subAddr & 0x00FF)  ;       /*wyslanie mlodszego bajtu adresu pamieci*/
180         i2c_start();
181         i2c_write(adr_device + 1);/*zapisuje adres urzadzenia z bitem R/W ustawionym na 1 czyli o 1 zwiekszamy adres urzadzenia*/
182         i2c_ack();
183         i2c_data = i2c_read();
184         i2c_nack();
185         i2c_stop();
186         return(i2c_data);
187     }
188     
189     /*funkcje do zapisu i odczytu struktur*/
190     /*zapis danych do pamieci EERAM - tryb sekwencyjny czyli ciag bajtow*/
191     void EERAM_write_structure(uint8_t adr_device, uint16_t subAddr,  uint16_t len, void *struktura) {
192         /* tworzymy wskaznik ktory wskazuje na pierwszy element struktury zrzutowanej do typu uint8_t*/
193         uint8_t *wsk = (uint8_t*)struktura;
194         i2c_start();
195         i2c_write(adr_device); /*wyslanie bajtu adresu z bitem R/W ustawionym na zero*/
196         i2c_write((subAddr & 0xFF00) >> 8);  /*wyslanie starszego bajtu adresu pamieci*/
197         i2c_write(subAddr & 0x00FF);             /*wyslanie mlodszego bajtu adresu pamieci*/
198         while (len--) {
199         /*inkrementujemy tylko dane,adres jest inkrementowany automatycznie przez EERAM*/
200             i2c_write(*wsk++); 
201             }
202         i2c_stop();
203      }
204     
205     /*odczyt sekwencyjny danych z pamieci EERAM.*/
206     void EERAM_sequential_read_structure(uint8_t adr_device, uint16_t subAddr, uint16_t len, void *struktura) {
207         /* tworzymy wskaznik ktory wskazuje na pierwszy element struktury zrzutowanej do typu uint8_t*/
208         uint8_t *wsk = (uint8_t*)struktura;
209         i2c_start();
210         i2c_write(adr_device); /*wyslanie adresu urzadzenia z bitem R/W w stanie niskim*/
211         i2c_write((subAddr & 0xFF00) >> 8) ; /*wyslanie starszego bajtu adresu pamieci*/
212         i2c_write(subAddr & 0x00FF)  ;       /*wyslanie mlodszego bajtu adresu pamieci*/
213         i2c_start();
214         i2c_write(adr_device + 1);/*zapisuje adres urzadzenia z bitem R/W ustawionym na 1 czyli o 1 zwiekszamy adres urzadzenia*/
215         i2c_ack();
216         while(len--) {
217         /*inkrementujemy tylko dane,adres jest inkrementowany automatycznie przez EERAM*/
218         *wsk++ = i2c_read();
219         i2c_ack();
220         }
221         i2c_nack();
222         i2c_stop();
223     }

 1 /************************ 
 2       File: main.c          
 3  ************************/
 4 
 5 #include "xc.h"
 6 #include <stdio.h>
 7 #include <stdlib.h>
 8 #include <stdint.h> /*dyrektywy uint8_t itp*/
 9 #include <string.h>
10 #include "ustaw_zegar.h" /*tutaj m.in ustawione FCY*/
11 #include <libpic30.h> /*dostep do delay-i,musi byc po zaincludowaniu ustaw_zegar.h*/
12 #include "i2c.h"
13 #include "lcd.h"
14 
15 /*A1,A2 - do masy*/
16 #define AdressEERAM 0xA0 
17 #define AdressEERAM_STATUS_REGISTER 0x00
18 #define AdressEERAM_CONTROL_REGISTER 0x30 
19 #define AUTO_STORE_ON 0x02
20 
21 
22 int main(void) {
23     ustaw_zegar();/*odpalamy zegar na ok 40MHz*/
24     ustaw_I2C1(); /*odpalamy I2C1*/
25     /* ustawiamy wszystkie piny analogowe (oznacznone ANx) jako cyfrowe
26        do zmiany mamy piny AN0-AN5 i AN9-AN12 co daje hex na 16 bitach = 0x1E3F*/
27     AD1PCFGL = 0x1E3F ;
28     TRISAbits.TRISA1 = 0 ; /*RA1 jako wyjscie, tu mamy podpieta LED*/
29    
30     /*deklaracja pustej Unii ,dostep do danych : DATA.dzien lub DATA.tabela[0]*/
31     typedef union{
32         uint8_t tabela[4];/*rozmiar taki ile mamy bajtow w strukturze ponizej*/
33         struct {
34             uint8_t dzien; /*1xbajt*/
35             uint8_t miesiac;/*1xbajt*/
36             uint16_t rok;/*2xbajt*/
37             char znak;/*1xbajt*/
38         };
39     } DATA;
40     
41     DATA rocznica_out ; /*tworzymy zmienna o typie DATA*/
42     /*definjujemy dane w strukturze, wszystko musi by? sprowadzone do jednego bajta*/
43     rocznica_out.tabela[0] = 6;
44     rocznica_out.tabela[1] = 5;
45     rocznica_out.tabela[2] = (1980 & 0x00FF); /*wyodrebnienie mlodszego bajtu*/
46     rocznica_out.tabela[3] = ((1980 & 0xFF00) >> 8); /*wyodrebnienie starszego bajtu*/
47     rocznica_out.tabela[4] = 'A';
48    
49     /*ustaw Status Rejestr w 47L16 aby uaktywnic tryb Auto Store*/
50     EERAM_write_STATUS_REGISTER(AdressEERAM_CONTROL_REGISTER, AdressEERAM_STATUS_REGISTER,  AUTO_STORE_ON);
51     /*zapis struktury w trybie sekwencyjnym - czyli ciag bajtow max 2048 dla 47L16*/
52     EERAM_write_structure(AdressEERAM, 0,  sizeof(rocznica_out),&rocznica_out);
53     DATA rocznica_in; /*pusta zmienna o typie DATA do odbioru danych*/
54     /*odczyt sekwencyjny struktury i poukladanie danych w nowej strukturze rocznica_in*/
55     EERAM_sequential_read_structure(AdressEERAM,0,sizeof(rocznica_in),&rocznica_in);
56            
57     /*Disable I2C1 module*/
58     I2C1CONbits.I2CEN = 0;
59     
60     WlaczLCD();
61     UstawKursorLCD(1,1);
62     lcd_int(rocznica_in.dzien);
63     WyswietlLCD(" ");
64     lcd_int(rocznica_in.miesiac);
65     WyswietlLCD(" ");
66     lcd_int(rocznica_in.rok);
67     WyswietlLCD(" ");
68     lcd_char(rocznica_in.znak);
69     /*napis testowy aby sprawdzic poprawnosc dzialania LCD*/
70     UstawKursorLCD(2,1);
71     WyswietlLCD("test");
72     while(1)
73       {
74         /*Glówna Petla Programu*/
75       }
76 }

Brak komentarzy:

Prześlij komentarz