K U R S
Obsługa kart pamięci Flash
za pomocą mikrokontrolerów,
część 3 Po miesiÍcznej przerwie przedstawiamy kolejnÄ… czÍśĘ artykuÅ‚u,
w ktÛrym prezentujemy sposoby obsÅ‚ugi kart pamiÍciowych
Procedury zapisu i odczytu
Flash za pomocÄ… mikrokontrolerÛw AVR. Tym razem skupiamy
kart CF
W poprzednich odcinkach kur-
siÍ na omÛwieniu (i pokazaniu!) procedur zapisu i odczytu
su zebraliśmy całą teoretyczną
kart CF.
wiedzÍ wymaganÄ… do osiÄ…gniÍcia
sukcesu - czyli do skomunikowa-
nia siÍ z kartami CF podÅ‚Ä…czony- tora dla kart typu Compact Flash, skompilowania za pomocÄ… kompi-
mi do mikrokontrolera AVR. wykorzystujÄ…cego procedury latora AVR-GCC dla mikrokontro-
W tym miejscu dochodzimy do w jÍzyku C z ukierunkowaniem na lera typu ATmega161. Zastosowa-
konkretnego sposobu implementa- mikrokontrolery AVR. Przykładowe nie innego kompilatora lub inne-
cji komend zapisu i odczytu sek- procedury są przeznaczone do go typu mikrokontrolera mołe wy-
magaĘ dokonania niewielkich mo-
List. 1.
dyfikacji kodu ürÛdÅ‚owego.
#define NOEXTRAM // Wstawienie komentarza w tej linii
Stosując podłączenie według
// oznacza współpracę z układem według rys 5
//
rys. 4 (EP2/2004), naleły ułyĘ
// Deklaracje typów (skrótów)
mikrokontrolera posiadajÄ…cego od-
//
typedef unsigned char u08;
powiedniÄ… ilośĘ wewnÍtrznej pa-
typedef unsigned short u16;
typedef unsigned long u32; miÍci RAM - powyÅ‚ej 600 bajtÛw,
poniewaÅ‚ potrzebujemy 512 bajtÛw
//
// Bity rejestru statusu
na sam bufor sektora, no i oczy-
//
wiście dodatkowo co najmniej kil-
#define SR_BUSY 0x80
#define SR_DRDY 0x40
kadziesiÄ…t komÛrek na zmienne
#define SR_DF 0x20
i stos. W obu przypadkach mikro-
#define SR_DSC 0x10
#define SR_DRQ 0x08
kontroler musi posiadaĘ interfejs
#define SR_CORR 0x04
#define SR_IDX 0x02 dla zewnÍtrznej pamiÍci RAM - bo
#define SR_ERR 0x01
w ten sposÛb jest podÅ‚Ä…czona i ob-
//
sługiwana karta. ATmega161 spo-
// Adresy rejestrów ATA (Bloki Command i Control)
kojnie spełnia te załołenie - po-
//
siada 1 kB wewnÍtrznego RAM-u
#ifdef NOEXTRAM
i ma interfejs do zewnÍtrznej pa-
#define ATAPI_Data *((volatile u08*)0xF000) // Rejestr danych, ODCZYT/ZAPIS
miÍci. OczywiÅ›cie w przypadku
#define ATAPI_ErrorReg *((volatile u08*)0xF100) // Rejestr błędów, ODCZYT
#define ATAPI_Features *((volatile u08*)0xF100) // Rejestr Features, ZAPIS procesora nieposiadajÄ…cego interfej-
#define ATAPI_SectorCount *((volatile u08*)0xF200) // Liczba sektorów,
su RAM, do komunikacji z kartÄ…
// ODCZYT/ZAPIS
#define ATAPI_Sector *((volatile u08*)0xF300) // Numer Sektora, ODCZYT/ZAPIS
mołna ułyĘ standardowych linii
#define ATAPI_CylLo *((volatile u08*)0xF400) // Nr Cylindra [LSB],ODCZYT/ZAPIS
portÛw wejÅ›cia-wyjÅ›cia, a odpo-
#define ATAPI_CylHi *((volatile u08*)0xF500) // Nr Cylindra [MSB],ODCZYT/ZAPIS
#define ATAPI_DrvHead *((volatile u08*)0xF600) // Numer GÅ‚owicy, ODCZYT/ZAPIS
wiednie kombinacje sygnaÅ‚Ûw ste-
#define ATAPI_Status *((volatile u08*)0xF700) // Rejestr Statusu, ODCZYT
rujących generowaĘ programowo,
#define ATAPI_Cmd *((volatile u08*)0xF700) // Rejestr Komend, ZAPIS
#define ATAPI_AltStat *((volatile u08*)0xFE00) // Rejestr Alt. Stat, ODCZYT
lecz bÍdzie to wymagaÅ‚o napisania
#define ATAPI_DevCtrl *((volatile u08*)0xFE00) // Rejestr Kontrolny, ZAPIS
innych procedur zapisu i odczytu
#else
rejestrÛw karty.
#define ATAPI_Data *((volatile u08*)0x8000) // Rejestr danych, ODCZYT/ZAPIS
Na list. 1 umieszczono dekla-
#define ATAPI_ErrorReg *((volatile u08*)0x8001) // Rejestr błędów, ODCZYT
racje bitÛw rejestru statusu oraz
#define ATAPI_Features *((volatile u08*)0x8001) // Rejestr Features, ZAPIS
#define ATAPI_SectorCount *((volatile u08*)0x8002) // Liczba sektorów,
adresy poszczegÛlnych rejestrÛw
// ODCZYT/ZAPIS
#define ATAPI_Sector *((volatile u08*)0x8003) // Numer Sektora, ODCZYT/ZAPIS
#define ATAPI_CylLo *((volatile u08*)0x8004) // Nr Cylindra [LSB],ODCZYT/ZAPIS
#define ATAPI_CylHi *((volatile u08*)0x8005) // Nr Cylindra [MSB],ODCZYT/ZAPIS
W pierwszej czÍÅ›ci artykuÅ‚u
#define ATAPI_DrvHead *((volatile u08*)0x8006) // Numer GÅ‚owicy, ODCZYT/ZAPIS
#define ATAPI_Status *((volatile u08*)0x8007) // Rejestr Statusu, ODCZYT
w tab. 2 opisujÄ…cej funkcje
#define ATAPI_Cmd *((volatile u08*)0x8007) // Rejestr Komend, ZAPIS
sygnaÅ‚Ûw wystÍpujÄ…cych na
#define ATAPI_AltStat *((volatile u08*)0x800E) // Rejestr Alt. Stat, ODCZYT
#define ATAPI_DevCtrl *((volatile u08*)0x800E) // Rejestr Kontrolny, ZAPIS
złączu karty CF wystąpił błąd
w opisie sygnału -CSEL.
#endif
Podłączenie tej linii do masy
// Prototypy funkcji
u08 cf_read_data(u32 lba, u08 *buffer); konfiguruje kartÍ jako MASTER,
u08 cf_write_data(u32 lba, u08 *buffer);
a pozostawienie jej
u08 cf_identify(u08 *buffer);
u08 cf_reset(void);
niepodłączonej - jako SLAVE,
czyli jest dokładnie na
void cf_ata_command(u32 lba, u16 count, u08 cmd);
u08 cf_wait_drq(void);
Elektronika Praktyczna 5/2004
81
K U R S
karty CF w przestrzeni adresowej
List. 2.
mikrokontrolera. Dyrektywa kom- #include
#include cf.h
pilacji warunkowej wraz z dekla-
// *******************************************************************************
racją #define NOEXTRAM umołli- // Opóznienie okolo 5us (dla kwarcu 8MHz)
// *******************************************************************************
wia dostosowanie programu do
void delay5us(void)
podłączenia karty według schema-
{
tu z rys. 4 lub rys. 5 (EP2/2004).
u08 i = 8;
while(--i)
Deklaracje typÛw u08, u16 i u32
asm volatile( nop );
mają na celu ułatwienie pisania
}
programu, bo w koÒcu Å‚atwiej
// *******************************************************************************
i szybciej jest napisaĘ u16 nił // Zapis rejestrów karty CF i wysłanie żądanej komendy
// *******************************************************************************
unsigned short, a na dodatek ja-
void cf_ata_command(u32 lba, u16 count, u08 cmd)
sno informujÄ…, Å‚e dany typ jest {
u16 cyl, head, sector;
unsigned o dÅ‚ugoÅ›ci 16 bitÛw.
// przeliczenie adresu LBA na dane dla rejestrow cylindra, głowicy i sektora
Na list. 2 zawarto wszystkie
sector = (u16) ( lba & 0x000000ffL );
niezbÍdne procedury umoÅ‚liwiajÄ…ce
lba = lba >> 8;
cyl = (u16) ( lba & 0x0000ffffL );
komunikacjÍ z kartÄ… CF. NajwaÅ‚-
lba = lba >> 16;
niejsza z nich to void
head = (u16) ( lba & 0x0fL );
cf_ata_command(u32 lba, u16
ATAPI_DrvHead = 0xE0 | head; // rejestr głowicy, Tryb LBA / Drive0 / head
count, u08 cmd). Właśnie ona po-
while ((ATAPI_Status & (SR_BUSY|SR_DRDY))==SR_BUSY); // Czekaj na !BUSY i DRY
woduje zapisanie rejestrÛw karty
odpowiednimi danymi, a nastÍpnie // Ustawienie pozostaÅ‚ych rejestrów karty
ATAPI_CylHi = cyl>>8; // Cylinder MSB
wywołanie odpowiedniej komendy
ATAPI_CylLo = cyl; // Cylinder LSB
ATA. Na początku tej funkcji ma- ATAPI_SectorCount = (u08)count; // liczba sektorów
ATAPI_Sector = sector; // numer sektora
my przeliczenie adresu interesujÄ…ce-
// I na koniec wywołanie żądanej komendy
go nas sektora wyrałonego wartoś-
ATAPI_Cmd = cmd; // komenda ATA
ciÄ… LBA na dane wpisywane do
delay5us(); // odczekanie 5us
}
rejestrÛw numerÛw sektora, cylind-
ra - 2 bajty, oraz gÅ‚owicy. NastÍp- // *******************************************************************************
// Odczekanie na gotowość do transmisji danych
nie do rejestru numeru głowicy
// Zwraca kod błędu 0->Nastąpił Błąd, 1->OK
wpisujemy obliczoną wartośĘ na // *******************************************************************************
u08 cf_wait_drq(void)
pozycje HS0...HS3 (patrz opis re-
{
jestrÛw z poprzedniej czÍÅ›ci artyku- u08 stat;
Å‚u) oraz ustawiamy bity D5...D7, co
// odczytuj w pętli rejestr ALT STATUS dopóki BUSY = 0
oznacza, Å‚e uÅ‚ywaĘ bÍdziemy try- while ( (ATAPI_AltStat & SR_BUSY) == SR_BUSY );
bu LBA w odwołaniu do urządze-
// odczytaj rejestr STATUS i zresetuj żądanie przerwania
stat = ATAPI_Status;
nia Master.
Samo wpisanie danych do re-
if ( stat & SR_ERR ) // błąd jeśli w rejestrze statusu
return 0; // jest ustawiony bit błędu
jestru polega po prostu na przepi-
saniu wymaganej wartości pod za- if ( ! (stat & SR_DRQ) ) // błąd jeśli nie wystawiony sygnał DRQ
return 0;
deklarowany wcześniej adres rejes-
tru, ktÛry zachowuje siÍ w tym return 1; // wszystko OK
}
momencie jak komÛrka zewnÍtrznej
pamiÍci RAM podÅ‚Ä…czonej do mik-
rokontrolera. Odpowiednie kombina- by sektorÛw ma na celu umoÅ‚li- zostaÅ‚a prawidÅ‚owo wykonana.
cje na liniach adresowych, danych wienie Å‚Ä…dania 256 sektorÛw zarÛ- O przyczynie kÅ‚opotÛw moÅ‚emy siÍ
oraz sterujÄ…cych pamiÍciÄ… genero- wno za pomocÄ… wartoÅ›ci 0, jak dowiedzieĘ czytajÄ…c rejestr statusu
wane sÄ… automatycznie przez wbu- i 256. No i na sam koniec wpisu- oraz rejestr bÅ‚ÍdÛw.
dowany w mikrokontroler interfejs jemy interesujÄ…cÄ… nas komendÍ do PozostaÅ‚e funkcje nie interpre-
do zewnÍtrznej pamiÍci RAM. rejestru komend, po czym odcze- tujÄ… bÅ‚ÍdÛw, lecz przekazujÄ… in-
W tym momencie karta rozpoz- kujemy okoÅ‚o 5 µs, aby karta zdÄ…- formacje o jego wystÄ…pieniu. Jest
naje (po bicie D4), Å‚e wysyÅ‚ane Å‚yÅ‚a wystawiĘ flagÍ BUSY i rozpo- to swego rodzaju uproszczenie,
dane sÄ… przeznaczone dla niej. czÍÅ‚a interpretacjÍ komendy. Czas ale w wiÍkszoÅ›ci przypadkÛw wy-
NastÍpnie sprawdzamy w pÍtli stan ten nie jest krytyczny, lecz jeÅ›li starcza. Najprostszym wyjÅ›ciem
flagi BUSY i DRY z rejestru statu- bÍdzie zbyt krÛtki, to moÅ‚emy siÍ z takiej sytuacji jest programowe
su. Wyzerowanie BUSY i ustawie- spodziewaĘ kÅ‚opotÛw ze starszymi wyzerowanie karty i powtÛrzenie
nie DRY oznacza gotowośĘ na typami kart. łądanej komendy, bo jeśli błąd
przyjÍcie reszty danych. NastÍpnie Funkcja u08 cf_wait_drq(void) powstaÅ‚ z przyczyn zewnÍtrznych,
wpisujemy pozostaÅ‚Ä… czÍśĘ adresu ma za zadanie sprawdzenie goto- a nie z powodu bÅ‚Ídnych danych
LBA do rejestrÛw numeru cylind- woÅ›ci karty do transferu danych wejÅ›ciowych, to powinniÅ›my
ra i sektora oraz Å‚Ä…danÄ… liczbÍ po wykonaniu komendy wymagajÄ…- w ten sposÛb odzyskaĘ kontrolÍ
sektorÛw do rejestru iloÅ›ci sekto- cej takowego. JeÅ›li zwrÛci ona war- nad kartÄ….
rÛw. Rzutowanie 16-bitowej zmien- tośĘ rÛwnÄ… zero, oznacza to, Å‚e No i na koniec docieramy do
nej count na 8-bitowy rejestr licz- mamy problem, bo komenda nie właściwych funkcji odczytu, zapi-
Elektronika Praktyczna 5/2004
82
K U R S
su i identyfikacji karty. Wszystkie
List. 2. - cd.
trzy sÄ… do siebie podobne. Naj-
// *******************************************************************************
pierw wywołują cf_ata_command,
// Odczyt sektora o numerze w lba do bufora
// Zwraca kod błędu 0->Nastąpił Błąd, 1->OK
przekazujÄ…c do niej adres LBA, Å‚Ä…-
// *******************************************************************************
u08 cf_read_data(u32 lba, u08 *buffer) danie jednego sektora oraz odpo-
{
wiedniÄ… komendÍ (0x20, 0x30 lub
u08 r;
u16 i;
0xEC). W przypadku komendy
cf_ata_command(lba, 1, 0x20); // komenda odczytu sektora
identyfikacji, adres LBA jest usta-
r = cf_wait_drq(); // odczekanie na sygnał DRQ
wiany na zero. Kolejno nastÍpuje
for (i=0;i<512;i++) // transfer danych
odczekanie na gotowośĘ do trans-
buffer[i] = ATAPI_Data;
return r;
feru danych i na koniec 512 od-
}
czytÛw lub zapisÛw rejestru da-
// *******************************************************************************
nych karty CF. Przy odczycie da-
// Zapis bufora do sektora o numerze w lba
// Zwraca kod błędu 0->Nastąpił Błąd, 1->OK
ne sÄ… umieszczane w buforze *buf-
// *******************************************************************************
u08 cf_write_data(u32 lba, u08 *buffer)
fer, a przy zapisie do karty - ko-
{
u08 r; lejno z niego pobierane.
u16 i;
Ach, byłbym zapomniał... Oczy-
cf_ata_command(lba, 1, 0x30); // komenda zapisu sektora
wiÅ›cie przyda siÍ nam jeszcze fun-
r = cf_wait_drq(); // odczekanie na sygnał DRQ
kcja zerujÄ…ca kartÍ CF. DziaÅ‚a ona
for (i=0;i<512;i++) // transfer danych
według algorytmu opisanego w po-
ATAPI_Data = buffer[i];
return r;
przedniej czÍÅ›ci tego kursu, wiÍc
}
nie bÍdÍ siÍ na jej temat zbytnio
// *******************************************************************************
// Identyfikacja karty CF. Zwrócone dane znajdą się w buforze buffer rozpisywał. Dodam jedynie, łe
// Zwraca kod błędu 0->Nastąpił Błąd, 1->OK
zwraca ona status karty po zero-
// *******************************************************************************
u08 cf_identify(u08 *buffer)
waniu, i jeśli wynosi on 0, to kar-
{
u08 r;
ta jest gotowa do pracy, jeśli 1,
u16 i;
to powÛd bÅ‚Ídu znajduje siÍ w re-
cf_ata_command(0, 0, 0xEC); // komenda odczytu danych identyfikacyjnych
jestrze bÅ‚ÍdÛw, a jeÅ›li 2, to karta
r = cf_wait_drq(); // odczekanie na sygnał DRQ
wykonaÅ‚a komendÍ, ale zerowanie
for (i=0;i<512;i++) // transfer danych
buffer[i] = ATAPI_Data; nie przebiegło poprawnie i rejestr
return r;
numeru sektora nie przyjÄ…Å‚ wartoÅ›-
}
ci 1, tak jak powinien. Jeśli z ja-
// *******************************************************************************
// Wybranie karty CF jako Drive 0 oraz programowy reset karty CF
kichÅ› powodÛw procedura zerowa-
// Zwraca kod błędu 0->OK, 1->Błąd, 2->Błąd sygnatury resetu karty
nia karty nie powiodÅ‚a siÍ, moÅ‚e
// *******************************************************************************
u08 cf_reset(void)
to oznaczaĘ, łe np. przypadkowo
{
ATAPI_DevCtrl = 0x06; // Ustaw bity SW RST i -IEn
wprowadziliÅ›my kartÍ w tryb True
delay5us(); // odczekanie 5us
delay5us(); // odczekanie 5us IDE, podajÄ…c stan niski na styk
ATAPI_DevCtrl = 0x02; // Ustaw bit -IEn i wyzeruj SW RST
9 karty (sygnał -OE/-ATA_SEL)
delay5us(); // odczekanie 5us
delay5us(); // odczekanie 5us
podczas załączania zasilania karty.
// Czekaj na gotowość karty
while ( (ATAPI_Status & (SR_BUSY|SR_DRDY)) != SR_DRDY );
Jest na to tylko jeden sposÛb:
if (ATAPI_Sector != 1 ) // Sprawdz sygnaturę resetu wyłączyĘ i ponownie załączyĘ zasi-
return (2); // Wróć z błędem sygnatury resetu
lanie karty lub całego urządzenia.
return (ATAPI_Status & 1); // Zwróć status błędu
Wprawdzie według specyfikacji kar-
ta powinna na tym wejściu posia-
List. 3.
daĘ wewnÍtrzny rezystor podciÄ…ga-
#include
jący je do VCC, ale jeśli przez
#include cf.h
u08 buf[512]; // Bufor danych w pamięci RAM mikrokontrolera
przypadek wystąpią opisane wyłej
problemy, moÅ‚na sprÛbowaĘ im za-
void send_buf(void)
{
radziĘ poprzez dodanie zewnÍtrzne-
u16 i;
for(i=0; i<512; i++)
go rezystora o wartości około 10 k&!
{
pomiÍdzy styk 9 karty a VCC.
while( !(UCSR0A & (1<UDR0 = buf[i]; // Wyślij bajt z bufora
Na koniec, na list. 3 przedsta-
}
} wiam malutki przykładzik wyko-
rzystania tych procedur w postaci
int main(void)
{
krÛtkiego programu wysyÅ‚ajÄ…cego
UCSR0B = (1<UBRRH = 0; poprzez szeregowy interfejs RS232
UBRR0 = 25; // Ustawienie 19200 bodów przy kwarcu 8MHz
dane identyfikacyjne karty,
u08 i;
a nastÍpnie zawartośĘ pierwszych
cf_reset(); // Reset karty
cf_identify(buf); // Identyfikacja karty 10 sektorÛw karty CF.
send_buf(); // Wyślij dane z identyfikacji
W nastÍpnej czÍÅ›ci kursu przed-
for(i=0; i<10; i++)
{
stawiÍ drugi z omawianych typÛw
cf_read_data(i, buf); // Odczytaj sektor o adresie LBA w zmiennej i
kart pamiÍci, czyli karty Multime-
send_buf(); // Wyślij dane przez UART
}
dia Card (MMC).
while(1); // Koniec pracy
}
Romulad Biały
Elektronika Praktyczna 5/2004
83
Wyszukiwarka
Podobne podstrony:
Obsługa kart pamięci Flash, część 2
Obsługa kart pamięci Flash, część 7
Obsługa kart pamięci Flash, część 4
Obsługa kart pamięci Flash, część 6
Obsługa kart pamięci Flash, część 5
Obsługa kart pamięci Flash, część 1
Obsługa kart pamięciowych SD, cz 2
PAMIECI FLASH
Digital Image Recovery do odzyskiwania danych z kart pamięci fotograficznych
Odczytanie identyfikatorów VID i PID pendrive i pamięci flash
Odczytanie identyfikatorów VID i PID pendrive i pamięci flash
utk2 pamieci flash
Mikrokontrolery STM32 Obsługa kart SD i FatFs
Programowanie pamięci FLASH ROM Jak uruchomić programator FLASH
Instalacja Windows XP z USB, pendrive a lub karty pamięci flash
Programowanie pamięci Flash mikrokontrolerów STM32 – Flash Loader
więcej podobnych podstron