SD odczyt

Jednym z najbardziej użytecznych urządzeń jest pamięć zewnętrzna. Na płytce ZL29ARM mamy gniazdo karty SD, do której mamy dostęp poprzez SPI. W książce Marka Galewskiego znajdziecie kompletny opis karty SD, począwszy od standardowego systemu plikowego FAT12, 16 i 32, a skończywszy na przykładach odczytu i zapisu na karę SD. Dlatego nie będę ich opisywał, jak ktoś się będzie tym interesował to kupi sobie książkę :). Biblioteki obsługujące system plikowy FAT można bezpłatnie pobrać ze strony elm-chan.org, albo ode mnie wraz z projektem.

Program otwiera plik z karty SD, który nazywa się plik.txt i wypisuje na ekran LCD dane zawarte w tym pliku. Miałem drobny problem z przerobieniem tego programu na płytkę ZL29ARM, ponieważ nie wiem gdzie na tej płytce podłączyli detektor wykrywający kartę. W nocie katalogowej płytki zamiast numeru portu podali dźwięczną nazwę CARD_DET co raczej jest mało użyteczne w programowaniu. Dlatego PA8 podciągnąłem pull-down'em (GPIO_Mode_IPD) i teraz program zawsze wykrywa kartę.

Aby wgrać program wystarczy podmienić pliki które umieściłem poniżej w katalogu SRC naszego projektu. Kliknij, aby pobrać program

#include <stm32f10x_conf.h>
#include "ff.h"
#include "diskio.h"
#include "lcd_hd44780_lib.h"


void RCC_Config(void);
void GPIO_Config(void);
void NVIC_Config(void);
void SPI_Config(void);
 unsigned long int SysTick_Config_Mod(unsigned long int SysTick_CLKSource, unsigned long int Ticks);


#define STAN_PROG_OCZEKIWANIE  0
#define STAN_PROG_JEST_KARTA   2
#define STAN_PROG_BRAK_KARTY   3
#define STAN_PROG_BRAK_PLIKU   4
#define STAN_PROG_ODCZYT_PLIKU 5
char stanProg=STAN_PROG_OCZEKIWANIE;

static FATFS g_sFatFs; //obiekt FATFs
int main(void)
{
 volatile unsigned long int i;
 char poprzedniStanGPIOA8=0;
 char obecnyStanGPIOA8=0;

 FRESULT fresult;
 FIL     plik;
 char    bufor[20]={0};
 UINT    odczytanychBajtow=0;
 UINT    bajtowDoOdczytu=0;

 //konfiguracja systemu
 RCC_Config();
 GPIO_Config();
 NVIC_Config();
 SPI_Config();

 /*Tu nalezy umiescic ewentualne dalsze funkcje konfigurujace system*/
 //GPIO_ResetBits(GPIOE,GPIO_Pin_14 | GPIO_Pin_15);


 LCD_Initialize();                           //inicjalizacja wysietlacza
 LCD_WriteCommand(HD44780_CLEAR);            //wyczysc wyswietlacz
 LCD_WriteText("SD - DEMO\0");

 if (SysTick_Config_Mod(SysTick_CLKSource_HCLK, 720000ul))    {
 while(1);        // W razie bledu petla nieskonczona
 }

 obecnyStanGPIOA8=(BitAction)(1-GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8));//zanegowanie rzeczywistego stanu GPIOA8 spowoduje, ze stan obecny i stan poprzedni beda rozne -> wymusi to testowanie obecnosci karty przy pierwszym wejsciu w STAN_PROG_OCZEKIWANIE

 while (1) {
 /*Tu nalezy umiescic glowny kod programu*/
 poprzedniStanGPIOA8=obecnyStanGPIOA8;
 obecnyStanGPIOA8=GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8);
 switch (stanProg){
 case STAN_PROG_OCZEKIWANIE : {
 if (obecnyStanGPIOA8==poprzedniStanGPIOA8){
 for (i=0;i<10000ul;i++);
 } else {
 if ((obecnyStanGPIOA8==SET)&&(poprzedniStanGPIOA8==RESET)){
 stanProg=STAN_PROG_BRAK_KARTY;
 //   stanProg=STAN_PROG_JEST_KARTA;
 } else {
 stanProg=STAN_PROG_JEST_KARTA;
 }
 }
 } break;
 case STAN_PROG_BRAK_KARTY : {
 LCD_WriteTextXY((char *)"Brak karty        \0",0,1);
 stanProg=STAN_PROG_OCZEKIWANIE;
 } break;
 case STAN_PROG_JEST_KARTA : {
 for (i=0;i<10000ul;i++);                     //odczkaj chwile az karta bedzie stabilnie umieszczona w gniezdzie
 fresult = f_mount(0, &g_sFatFs);
 if (fresult==0) {
 stanProg=STAN_PROG_ODCZYT_PLIKU;
 } else {
 stanProg=STAN_PROG_BRAK_KARTY;
 }
 } break;
 case STAN_PROG_BRAK_PLIKU : {
 LCD_WriteTextXY((char *)"Brak pliku        \0",0,1);
 stanProg=STAN_PROG_OCZEKIWANIE;
 } break;
 case STAN_PROG_ODCZYT_PLIKU : {
 fresult = f_open(&plik,"plik.txt", FA_READ);
 if (fresult==0){                           //jesli uda sie otworzyc plik

 bajtowDoOdczytu=f_size(&plik);
 if (bajtowDoOdczytu>16) {bajtowDoOdczytu=16;} //zabezpieczenie przed przepelnieniem bufora
 fresult = f_read(&plik, bufor, bajtowDoOdczytu, &odczytanychBajtow);
 bufor[bajtowDoOdczytu]=0;                //dodanie znaku \0 na koncu tekstu
 LCD_WriteTextXY((unsigned char *)bufor,0,1);
 fresult = f_close (&plik);
 stanProg=STAN_PROG_OCZEKIWANIE;
 } else {                                   //brak pliku
 stanProg=STAN_PROG_BRAK_PLIKU;
 }
 } break;

 default : stanProg=STAN_PROG_OCZEKIWANIE;
 }
 };
 return 0;
 }

void NVIC_Config(void)
{
//Konfigurowanie kontrolera przerwan NVIC
#ifdef  VECT_TAB_RAM
 // Jezeli tablica wektorow w RAM, to ustaw jej adres na 0x20000000
 NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else  // VECT_TAB_FLASH
 // W przeciwnym wypadku ustaw na 0x08000000
 NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
}

void RCC_Config(void)
{
 ErrorStatus HSEStartUpStatus;

 // Reset ustawien RCC
 RCC_DeInit();
 // Wlacz HSE
 RCC_HSEConfig(RCC_HSE_ON);
 // Czekaj za HSE bedzie gotowy
 HSEStartUpStatus = RCC_WaitForHSEStartUp();
 if(HSEStartUpStatus == SUCCESS)
 {
 FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
 // zwloka dla pamieci Flash
 FLASH_SetLatency(FLASH_Latency_2);
 // HCLK = SYSCLK
 RCC_HCLKConfig(RCC_SYSCLK_Div1);
 // PCLK2 = HCLK
 RCC_PCLK2Config(RCC_HCLK_Div1);
 // PCLK1 = HCLK/2
 RCC_PCLK1Config(RCC_HCLK_Div2);
 // PLLCLK = 8MHz * 9 = 72 MHz
 RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_9);
 //ustaw PLLCLK = HSE*7 czyli 8MHz * 7 = 56 MHz - konieczne dla ADC
 //RCC_PLLConfig(RCC_PLLSource_PREDIV1, RCC_PLLMul_7);
 // Wlacz PLL
 RCC_PLLCmd(ENABLE);
 // Czekaj az PLL poprawnie sie uruchomi
 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
 // PLL bedzie zrodlem sygnalu zegarowego
 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
 // Czekaj az PLL bedzie sygnalem zegarowym systemu
 while(RCC_GetSYSCLKSource() != 0x08);
 }
}


void GPIO_Config(void)
{
 //konfigurowanie portow GPIO
 GPIO_InitTypeDef  GPIO_InitStructure;

 /*Tu nalezy umiescic kod zwiazany z konfiguracja sygnalow zegarowych potrzebnych w programie peryferiow*/
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE | RCC_APB2Periph_GPIOC, ENABLE);//wlacz taktowanie portu GPIO B, C (LED, LCD)
 //GPIO A i SPI inicjalizowane w sd_stm32.c
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 , ENABLE);

 /*Tu nalezy umiescic kod zwiazny z konfiguracja poszczegolnych portow GPIO potrzebnych w programie*/

 //port C - Joystick
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_12|GPIO_Pin_13;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
 GPIO_Init(GPIOC, &GPIO_InitStructure);

 // PA4 - nSS/CS SPI
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
 //SPI - SCK, MISO, MOSI
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);
 //linia DETECT zlacza SD
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);

}

void SPI_Config(void)
{
 //konfigurowanie interfejsu SPI
 SPI_InitTypeDef   SPI_InitStructure;

 SPI_InitStructure.SPI_Direction =  SPI_Direction_2Lines_FullDuplex;//transmisja z wykorzystaniem jednej linii, transmisja jednokierunkowa
 SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                     //tryb pracy SPI
 SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b ;                //8-bit ramka danych
 SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;                        //stan sygnalu taktujacego przy braku transmisji - wysoki
 SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;                      //aktywne zbocze sygnalu taktujacego - 2-gie zbocze
 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                         //programowa obsluga linii NSS (CS)
 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;//prescaler szybkosci tansmisji  72MHz/4=18MHz
 SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                //pierwszy bit w danych najbardziej znaczacy
 SPI_InitStructure.SPI_CRCPolynomial = 7;                          //stopien wielomianu do obliczania sumy CRC
 SPI_Init(SPI1, &SPI_InitStructure);                               //inicjalizacja SPI

 SPI_Cmd(SPI1, ENABLE);      // Wlacz SPI1
}


unsigned long int SysTick_Config_Mod(unsigned long int SysTick_CLKSource, unsigned long int Ticks)
{
 //inicjalizacja licznika SysTick
 //zastepuje funkcje z bibliotek STM w zwiazku z bledem w funcji SysTick_Config
 unsigned long int Settings;

 assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));

 if (Ticks > SysTick_LOAD_RELOAD_Msk)  return (1);             //Kontrola, czy wartosc poczatkowa nie przekracza max

 SysTick->LOAD = (Ticks & SysTick_LOAD_RELOAD_Msk) - 1;        //Ustaw wartosc poczatkowa licznika
 NVIC_SetPriority (SysTick_IRQn, 0);                           //Ustaw priorytet przerwania
 SysTick->VAL  = 0;                                            //Ustaw wartosc aktualna licznika
 Settings=SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;  //Ustaw flagi wlaczaenia SysTick IRQ  i samego licznika
 if (SysTick_CLKSource == SysTick_CLKSource_HCLK){             //Wybierz flage ustawien zrodla sygnalu zegarowego
 Settings |= SysTick_CLKSource_HCLK;
 } else {
 Settings &= SysTick_CLKSource_HCLK_Div8;
 }
 SysTick->CTRL = Settings;                                     //Zapisz ustawienia do rejestru sterujacego SysTick (i wlacz licznik)
 return (0);
}