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*/
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';
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.
Pozdrawiam
picmajster.blog@gmail.com
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 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 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