81_83.pdf

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