środa, 29 marca 2017

Pamięć EERAM 47L16 - rodzynek wśród pamięci.

Dotarła wreszcie do mnie pamięć EERAM firmy Microchip 47L16. Bierzemy ją zatem na warsztat.


Pamięć EERAM to taki rodzynek wśród pamięci. Jest to pamięć na pokładzie której znajduje się pamięć SRAM i EEPROM. Zapis podczas normalnej pracy odbywa się do pamięci SRAM z prędkością max 1 MHz po I2C. Mamy tutaj nieograniczoną ilość cykli zapisu i odczytu.
Czyli tak jakbyśmy mieli do dyspozycji dodatkową pamięć RAM. W momencie kiedy nastąpi zanik zasilania lub jego chwilowy spadek, cała zawartość pamięci SRAM przepisywana jest automatycznie do pamięci nieulotnej EEPROM. Źródłem zasilania staje się wówczas zewnętrzny kondensator podłączony do wyprowadzenia Vcap układuPo powrocie zasilania lub jego stabilizacji następuje automatyczne odtworzenie danych z pamięci EEPROM do pamięci SRAM.
Przyczym przywrócenie zawartości SRAM może być również wyzwolone programowo w dowolnym momencie.

Podstawową zaletą pamięci EERAM w stosunku do typowego EEPROM jest brak oczekiwania za zapis i nieograniczony cykl zapisu podczas normalnej pracy (zapis do SRAM). W trybie nazwijmy to awaryjnym czyli podczas zaniku zasilania mamy do dyspozycji 1 mln cykli zapisu do EEPROM. Warto jeszcze odnotować, że rozmowa z pamięcią EERAM odbywa się z szybkością do 1 MHz, podczas  gdy typowy EEPROM powyżej 400 kHz dostanie arytmi serca.
Zaletą jest również brak podziału na strony co daje nam możliwość zapisu w jednym ciągu 2048 bajtów danych (dla 47L16).
Za wadę uważam maksymalną dostępną pojemność na poziomie 16 kb co daje nam w realu 2048 bajtów.

Pamięć 47L16 zasilana jest w zakresie 2.7-3.6V . Jeśli używamy mikrokontrolera 5V, to musimy zaopatrzyć się w model 47C16.

Typowy schemat podłączenia pamięci 47L16 do mikrokontrolera wygląda jak na poniższym rysunku :


Widzimy, że oprócz SDA i SCL mamy linię HS. A cóż to takiego ? Sprawa jest prosta jak świński ogon HS to skrót od Hardware Store i oznacza możliwość sprzętowego wymuszania przepisania zawartości SRAM do EEPROM. W naszych testach pominiemy ten aspekt i linię HS zakneblujemy masą.
Oprócz sprzętowego wymuszania trybu Store to EEPROM mamy możliwość uzyskania tego samego efektu za pomocą programowej zmiany przy wykorzystaniu COMMAND Register. W sumie pamięć 47L16 ma dwa narzędziowe rejestry : STATUS i COMMAND , więcej informacji znajdziemy na ich temat w linku pod artykułem. W naszych testach potrzebować będziemy tylko zmiany bitu ASE (Auto Store Enable) w rejestrze STATUS. Jak sama nazwa bitu wskazuje , włączamy tutaj tryb automatycznego przepisania zawartości SRAM do EEPROM. Będzie to zatem taka mini-inicjalizacja pamięci 47L16.

Poniżej prezentuję bazowy schemat podłączenia na którym będziemy testować naszą pamięć :


Rezystory na liniach SDA i SCL dajemy nisko na 2.2k, aby możliwa była komunikację I2C z prędkością 1MHz. Standardowo popędzimy to na 100kHz. Mikrokontroler PIC24HJ128GP502 pędzimy wewnętrznym zegarem 40 MHz (fajnie ,że mamy tyle).
Zasilanie z moduliku brrr chińskiego o którym wspominałem w jednym z poprzednich artykułów.

Biblioteka i2c zawiera funkcje specyficzne dla obsługi EERAM. W funkcjach zapisu znika opóźnienie wymagane przy zapisie do zwykłego EEPROM-u. Jedynym opóźnieniem jest zwłoka 1 ms przy zapisie do rejestru konfiguracyjnego.

Przy pierwszym wgraniu wsadu w pliku main.c Sekwencja 1 i Sekwencja 2 niech będą aktywne, w ten sposób zapiszemy dane z bufora nadawczego i odczytamy je z pamięci umieszczając w buforze odbiorczym.
Zawartość bufora odbiorczego zostanie wyświetlona na wyświetlaczu LCD.
Tak sprawdzimy poprawność działania zapisu i odczytu w EERAM.

W drugim kroku sprawdzimy czy po odłączeniu zasilania od pamięci EERAM dane będą dostępne po przywróceniu zasilania. Odłączamy zatem kabel z +3.3V zasilający pamięć (podczas braku zasilania dane z SRAM przepisywane są do EEPROM, zasilanie dla tego procesu daje C6 - 10uF) i ponownie podpinamy. Dezaktywujemy w pliku main.c Sekwencję 1 (zapis), zostawiamy aktywny tylko odczyt czyli Sekwencję 2.Wgrywamy ponownie wsad do mikrokontrolera i odczytujemy zawartość pamięci SRAM od adresu 0.
Jeśli na wyświetlaczu zobaczymy treść z bufora nadawczego to znaczy, że operacja się udała i pacjent przeżył :). Warto jeszcze nadmienić, że proces przepisania z pamięci EEPROM do SRAM trwa ok 5ms i tyle trzeba dać czasu od podaniu zasilania do wywołania funkcji odczytu.

Reasumując pamięć 47L16, jest bardzo ciekawym wynalazkiem, który eliminuje wszystkie wady pamięci EEPROM w tym m.in stronicowanie, zwłoka przy zapisie danych . Mamy możliwość zrzucania danych w nieograniczonej ilości zapisów z prędkością do 1 MHz. Pamięć jest bardzo wygodna w użyciu.
Z pamięcią nie było żadnego problemu przy uruchamianiu, wszystko zadziałało od pierwszego kopa.


 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 #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        
142          while (len--) {
143         /*inkrementujemy tylko tablice z danymi,adres jest inkrementowany automatycznie przez EERAM*/
144             i2c_write(*buf++); 
145             }
146         i2c_stop();
147      }
148     /*zapis danych do pamieci EERAM - tryb pojedynczego bajtu*/
149     void EERAM_write_byte(uint8_t adr_device, uint16_t subAddr,  unsigned char i2c_data) {
150         i2c_start();
151         i2c_write(adr_device); /*wyslanie bajtu adresu z bitem R/W ustawionym na zero*/
152         i2c_write((subAddr & 0xFF00) >> 8);  /*wyslanie starszego bajtu adresu pamieci*/
153         i2c_write(subAddr & 0x00FF);             /*wyslanie mlodszego bajtu adresu pamieci*/
154         i2c_write(i2c_data);
155         i2c_stop();
156     }
157     /*odczyt sekwencyjny danych z pamieci EERAM.*/
158     void EERAM_sequential_read_buf(uint8_t adr_device, uint16_t subAddr, uint16_t len, char *buf) {
159         i2c_start();
160         i2c_write(adr_device); /*wyslanie adresu urzadzenia z bitem R/W w stanie niskim*/
161         i2c_write((subAddr & 0xFF00) >> 8) ; /*wyslanie starszego bajtu adresu pamieci*/
162         i2c_write(subAddr & 0x00FF)  ;       /*wyslanie mlodszego bajtu adresu pamieci*/
163         i2c_start();
164         i2c_write(adr_device + 1);/*zapisuje adres urzadzenia z bitem R/W ustawionym na 1 czyli o 1 zwiekszamy adres urzadzenia*/
165         i2c_ack();
166         while(len--)
167         {
168         *buf++ = i2c_read();
169         i2c_ack();
170         }
171         i2c_nack();
172         i2c_stop();
173     }
174     /*odczyt jednego bajtu z pamieci EERAM.*/
175     unsigned char EERAM_read_byte(uint8_t adr_device, uint16_t subAddr) {
176         unsigned char i2c_data;
177         i2c_start();
178         i2c_write(adr_device); /*wyslanie adresu urzadzenia z bitem R/W w stanie niskim*/
179         i2c_write((subAddr & 0xFF00) >> 8) ; /*wyslanie starszego bajtu adresu pamieci*/
180         i2c_write(subAddr & 0x00FF)  ;       /*wyslanie mlodszego bajtu adresu pamieci*/
181         i2c_start();
182         i2c_write(adr_device + 1);/*zapisuje adres urzadzenia z bitem R/W ustawionym na 1 czyli o 1 zwiekszamy adres urzadzenia*/
183         i2c_ack();
184         i2c_data = i2c_read();
185         i2c_nack();
186         i2c_stop();
187         return(i2c_data);
188     }

 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     char bufor_nadawczy[]="Test EERAM";
24     char bufor_odbiorczy[sizeof(bufor_nadawczy)];
25     ustaw_zegar();/*odpalamy zegar na ok 40MHz*/
26     ustaw_I2C1(); /*odpalamy I2C1*/
27     /* ustawiamy wszystkie piny analogowe (oznacznone ANx) jako cyfrowe
28        do zmiany mamy piny AN0-AN5 i AN9-AN12 co daje hex na 16 bitach = 0x1E3F*/
29     AD1PCFGL = 0x1E3F ;
30     TRISAbits.TRISA1 = 0 ; /*RA1 jako wyjscie, tu mamy podpieta LED*/
31    
32     /*ustaw Status Rejestr w 47L16 aby uaktywnic tryb Auto Store*/
33     EERAM_write_STATUS_REGISTER(AdressEERAM_CONTROL_REGISTER, AdressEERAM_STATUS_REGISTER,  AUTO_STORE_ON);
34     /*Sekwencja nr 1 - zapis w trybie sekwencyjnym - czyli ciag bajtow max 2048 dla 47L16*/
35     EERAM_write_buf(AdressEERAM, 0,  sizeof(bufor_nadawczy), bufor_nadawczy);
36     /*Sekwencja nr 2 - odczyt sekwencyjny po wylaczeniu zasilania i ponownym uruchomieniu*/
37     EERAM_sequential_read_buf(AdressEERAM,0,sizeof(bufor_nadawczy),bufor_odbiorczy);
38             
39     /*Disable I2C1 module*/
40     I2C1CONbits.I2CEN = 0;
41     
42     WlaczLCD();
43     UstawKursorLCD(1,1);
44     WyswietlLCD(bufor_odbiorczy);
45     /*napis testowy aby sprawdzic poprawnosc dzialania LCD*/
46     UstawKursorLCD(2,1);
47     WyswietlLCD("test");
48     while(1)
49       {
50         /*Glówna Petla Programu*/
51       }
52 }
Linki :
47L16 datasheet

Brak komentarzy:

Prześlij komentarz