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ładu. Po 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.
Pozdrawiam
picmajster.blog@gmail.com
Linki :
47L16 datasheet
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 wysylka danych*/ 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 sa na 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 }
Brak komentarzy:
Prześlij komentarz