В принципе можно и на другие МК STM32, легко многое портировать, но осторожно
Связь с внешнем миром будем держать по USART, см. http://rukodelie-ds.ru/forum/viewtopic.php?f=13&t=638
Итак, есть у меня простой переходничок для SD-карты памяти, для подключения к МК по протоколу SPI. На данном переходнике разведены не все выводы, поэтому интерфейс SDIO использовать не получиться, вернее получится только допайкой новых выводов, что делать неохото (лучше заказать в китае новый).
У меня две основные задачи:
1. Разобраться с работой SPI в совокупности с SD.
2. Наладить работу с файловой системой FatFs, в том числе ипользуя DMA.
И как и ранее буду использовать штатные библиотеки.
1-е, с чем мы сталкиваемся - это проблема с использованными ножками МК, поэтому сперва долго и упорно выбираем какие ноги будем для какого интерфейса будем использовать. Выбор большой и я остановился на SPI2 с альтернативными ножками.
Код: Выделить всё
void SPI_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Peripheral Clock Enable -------------------------------------------------*/
/* Enable the SPI clock */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
/* Enable GPIO clocks */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_12); // CS - выбор устройства осуществлять будем ручками
// если 1, то выклечено, а если 0, то передаем данные
// Подключаем пины порта B к пинам как подписано на переходнике (сдесь активируем AF):
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2); // SCK
GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2); // MISO
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2); // MOSI
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // Все пины должны быть строго как AF
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOB, &GPIO_InitStructure);
SPI_I2S_DeInit(SPI2);
// Включаем SPI в нужный режим
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
/* Initializes the SPI communication */
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_Init(SPI2, &SPI_InitStructure);
SPI_Cmd(SPI2, ENABLE); // SPI - включен!
}
Теперь учимся общаться с SD. Вот тут я до конца не уверен почему именно так. В описании такого подключения обычно пишут, что в STM32 в SPI данные уходят через сдвиговый регистр, поэтому при приеме данных его нужно сперва очистить, отправив на него 0xFF, а только затем получать данные. Если отправляем какие либо данные, то они обязательно вернуться обратно, т.е. их надо прочитать после отправки, чтобы освободить регистр SPI.
Поскольку я подобное видел только в примерах работы с SD, то все-таки делаю поправки, что так осуществлен протокол общения именно с SD. Но на всякий случай запомним этот подход.
Код: Выделить всё
uint8_t spi_send (uint8_t data){
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI2, data);
while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
return SPI_I2S_ReceiveData(SPI2);
}
uint8_t spi_read (void)
{
return spi_send(0xff); //читаем принятые данные
}
Теперь организуем отправку команд к SD
Код: Выделить всё
//********************************************************************************************
//function посылка команды в SD //
//Arguments команда и ее аргумент //
//return 0xff - нет ответа //
//********************************************************************************************
uint8_t SD_sendCommand(uint8_t cmd, uint32_t arg)
{
uint8_t response, wait=0, tmp;
//для карт памяти SD выполнить корекцию адреса, т.к. для них адресация побайтная
if(SDHC == 0)
if(cmd == READ_SINGLE_BLOCK || cmd == WRITE_SINGLE_BLOCK ) {arg = arg << 9;}
//для SDHC корекцию адреса блока выполнять не нужно(постраничная адресация)
GPIO_ResetBits(GPIOB, GPIO_Pin_12); // CS_ENABLE GPIO_SetBits
//передать код команды и ее аргумент
spi_send(cmd | 0x40);
spi_send(arg>>24);
spi_send(arg>>16);
spi_send(arg>>8);
spi_send(arg);
//передать CRC (учитываем только для двух команд)
if(cmd == SEND_IF_COND) spi_send(0x87);
else spi_send(0x95);
//ожидаем ответ
while((response = spi_read()) == 0xff)
if(wait++ > 0xfe) break; //таймаут, не получили ответ на команду
//проверка ответа если посылалась команда READ_OCR
if(response == 0x00 && cmd == 58)
{
tmp = spi_read(); //прочитат один байт регистра OCR
if(tmp & 0x40) SDHC = 1; //обнаружена карта SDHC
else SDHC = 0; //обнаружена карта SD
//прочитать три оставшихся байта регистра OCR
spi_read();
spi_read();
spi_read();
}
spi_read();
GPIO_SetBits(GPIOB, GPIO_Pin_12); // CS_DISABLE GPIO_ResetBits
return response;
}
После того как у нас появилась функция отправки команд, надо обзавестись функцией инициализации
Код: Выделить всё
//********************************************************************************************
//function инициализация карты памяти //
//return 0 - карта инициализирована //
//********************************************************************************************
uint8_t SD_init(void)
{
uint8_t i;
uint8_t response;
uint8_t SD_version = 2; //по умолчанию версия SD = 2
uint16_t retry = 0 ;
for(i=0;i<10;i++) spi_send(0xff); //послать свыше 74 единиц
//выполним программный сброс карты
// CS_ENABLE;
while(SD_sendCommand(GO_IDLE_STATE, 0)!=0x01)
if(retry++>0x20) return 1;
// CS_DISABLE;
spi_send (0xff);
spi_send (0xff);
retry = 0;
while(SD_sendCommand(SEND_IF_COND,0x000001AA)!=0x01)
{
if(retry++>0xfe)
{
SD_version = 1;
break;
}
}
retry = 0;
do
{
response = SD_sendCommand(APP_CMD,0);
response = SD_sendCommand(SD_SEND_OP_COND,0x40000000);
retry++;
if(retry>0xffe) return 2;
}while(response != 0x00);
//читаем регистр OCR, чтобы определить тип карты
retry = 0;
SDHC = 0;
if (SD_version == 2)
{
while(SD_sendCommand(READ_OCR,0)!=0x00)
if(retry++>0xfe) break;
}
return 0;
}
Когда карточка SD проинициализирована, мы возможно захотим прочитать или записать сектор. Карта SD читается и записывается строго только секторами по 512 байт. Может меняться от типа карты, но по 512 байт работает на всех. Т.е. если мы хотим поменять всего несколько байт на карте, то нам их сперва надо прочитать целиком весь сектор 512 байт, найти там нужные для замены, исправить, и затем все 512 байт записать обратно на SD.
Код: Выделить всё
//********************************************************************************************
//function чтение выбранного сектора SD //
//аrguments номер сектора,указатель на буфер размером 512 байт //
//return 0 - сектор прочитан успешно //
//********************************************************************************************
uint8_t SD_ReadSector(uint32_t BlockNumb,uint8_t *buff)
{
uint16_t i=0;
//послать команду "чтение одного блока" с указанием его номера
if(SD_sendCommand(READ_SINGLE_BLOCK, BlockNumb)) return 1;
GPIO_ResetBits(GPIOB, GPIO_Pin_12); // CS_ENABLE;
//ожидание маркера данных
while(spi_read() != 0xfe)
if(i++ > 0xfffe) {GPIO_SetBits(GPIOB, GPIO_Pin_12); /*CS_DISABLE;*/ return 2;}
//чтение 512 байт выбранного сектора
for(i=0; i<512; i++) *buff++ = spi_read();
spi_read();
spi_read();
spi_read();
GPIO_SetBits(GPIOB, GPIO_Pin_12); // CS_DISABLE;
return 0;
}
//********************************************************************************************
//function запись выбранного сектора SD //
//аrguments номер сектора, указатель на данные для записи //
//return 0 - сектор записан успешно //
//********************************************************************************************
uint8_t SD_WriteSector(uint32_t BlockNumb,uint8_t *buff)
{
uint8_t response;
uint16_t i,wait=0;
//послать команду "запись одного блока" с указанием его номера
if( SD_sendCommand(WRITE_SINGLE_BLOCK, BlockNumb)) return 1;
GPIO_ResetBits(GPIOB, GPIO_Pin_12); // CS_ENABLE;
spi_send(0xfe);
//записать буфер сектора в карту
for(i=0; i<512; i++) spi_send(*buff++);
spi_send(0xff); //читаем 2 байта CRC без его проверки
spi_send(0xff);
response = spi_read();
if( (response & 0x1f) != 0x05) //если ошибка при приеме данных картой
{ GPIO_SetBits(GPIOB, GPIO_Pin_12); /*CS_DISABLE;*/ return 1; }
//ожидаем окончание записи блока данных
while(!spi_read()) //пока карта занята,она выдает ноль
if(wait++ > 0xfffe){GPIO_SetBits(GPIOB, GPIO_Pin_12); /*CS_DISABLE;*/ return 1;}
GPIO_SetBits(GPIOB, GPIO_Pin_12); //CS_DISABLE;
spi_send(0xff);
GPIO_ResetBits(GPIOB, GPIO_Pin_12); // CS_ENABLE;
while(!spi_read()) //пока карта занята,она выдает ноль
if(wait++ > 0xfffe){GPIO_SetBits(GPIOB, GPIO_Pin_12); /*CS_DISABLE;*/ return 1;}
GPIO_SetBits(GPIOB, GPIO_Pin_12); // CS_DISABLE;
return 0;
}
Вот теперь можно читать карточку
Код: Выделить всё
uint8_t Buff[512];
int main(void)
{
SystemInit();
initAll();
SPI_Config();
printf("Started USART!\n");
uint8_t sd_st=SD_init();
if(sd_st){
printf("Err SD Init! code:%d\n",sd_st);
}else{
printf("SD inited! SDHC:%d\n",SDHC); // тут выводим тип карты: простая или HC
}
sd_st=SD_ReadSector(2, (uint8_t *)&Buff); // читаем 2-й сектор
if(sd_st){
printf("Err reead SD! code:%d\n",sd_st);
}else{
uint8_t i;
for(i=0;i<100;i++){ // выводим первую сотню байт, где увидем, что у нас FAT32 на флешке
UsartPutData(Buff[i]); //см. мой вариант реализации общения по USART
}
}
UsartSend();
while(1){}
}
Вот проверили, что все работает и наверно разобрались как это работает. Теперь забудем про это, делаем все по другому.
Мы хотим использовать файловую систему, а не просто гонять туда сюда байты. Тем более, что, например, у меня не так много флешек, чтобы ими разбрасываться, сегодня флешка работает как реаниматор, завтра она в фотике, сегодня вот я над ней экспериментирую через STM32 (данных при экспериментах не потерял!).
Идем http://elm-chan.org/fsw/ff/00index_e.html и качаем последнюю версию библиотеки FatFs. надо подрубить все файлики в проект, но сразу оно не заработает. Сперва необходимо научить ее общаться с картой. Данная библиотека работает с файловой системой, а какому интерфейсу будет подключен диск - это вопрос, который нам и надо сперва решить.
Собственно, в файле diskio.c находится интерфейсная часть, кототрую и надо заполнить или написать свою. Практически для каждого МК можно найти уже готовый вариант этого файла, все остальное аппаратно независимо.
Я нашел diskio.c для STM32F1xx, что не очень оказалось совместимо с STM32F4xx. Опыт выше по настройке SPI помог поправить конфигурацию для SPI. И в принципе все заработало. А при включении DMA ничего никак не получалось
Что же с DMA. В целом, если внимательно посмотреть исходники, то от использования DMA мы ничего не выигрываем, но хотелось бы все-таки разобраться как оно работает.
В STM32F1xx настраиваются независимые каналы вроде DMA_Channel1, каждый из который связан с определенной периферией. А в STM32F4xx настраиваются потоки DMA1_Stream3, которых аж по 8 штук на 2-х DMA, а у каждого потока по 8 каналов, каждый из которых привязан к определенной периферии.
В обоих случаях ключевым является "привязан к определенной периферии". Я перекопал кучу примеров и НИГДЕ не было сказано почему выбран тот или иной поток и канал, у тем более чем они отличаются. В datasheet`ах тоже ничего . Но форуме от ST нашел совет смотреть в Reference manual, а именно для STM32F4 в RM0090, который моментально был найден. Там нашлась и схема работы и таблицы, что к чему привязано.
Вот тут мы видим, что SPI2 подключен к DMA1 на Stream3 (RX) и Stream4 (TX) по Channel0. Сразу после выставления этих параметров все полетело. Самое интересное, что я ведь нашел примеры FatFs для STM32F4, но там для DMA была ерунда какая-то, как будто человек попробовал, не получилось и забил, но исходники выложил.
Собственно вот полный РАБОЧИЙ код этого файла:
Код: Выделить всё
#include "stm32f4xx.h"
#include "diskio.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_spi.h"
#include "stm32f4xx_dma.h"
// demo uses a command line option for this (see Makefile):
#define STM32_USE_DMA
#ifdef STM32_USE_DMA
#warning Information only - using DMA
#endif
#define GPIO_CS GPIOB
#define RCC_AHB1Periph_GPIO_CS RCC_AHB1Periph_GPIOB
#define GPIO_Pin_CS GPIO_Pin_12
#define GPIO_PWR GPIOD
#define RCC_AHB1Periph_GPIO_PWR RCC_AHB1Periph_GPIOD
#define GPIO_Pin_PWR GPIO_Pin_10
#define GPIO_Mode_PWR GPIO_Mode_OUT /* pull-up resistor at power FET */
/* Definitions for MMC/SDC command */
#define CMD0 (0x40+0) /* GO_IDLE_STATE */
#define CMD1 (0x40+1) /* SEND_OP_COND (MMC) */
#define ACMD41 (0xC0+41) /* SEND_OP_COND (SDC) */
#define CMD8 (0x40+8) /* SEND_IF_COND */
#define CMD9 (0x40+9) /* SEND_CSD */
#define CMD10 (0x40+10) /* SEND_CID */
#define CMD12 (0x40+12) /* STOP_TRANSMISSION */
#define ACMD13 (0xC0+13) /* SD_STATUS (SDC) */
#define CMD16 (0x40+16) /* SET_BLOCKLEN */
#define CMD17 (0x40+17) /* READ_SINGLE_BLOCK */
#define CMD18 (0x40+18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (0x40+23) /* SET_BLOCK_COUNT (MMC) */
#define ACMD23 (0xC0+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (0x40+24) /* WRITE_BLOCK */
#define CMD25 (0x40+25) /* WRITE_MULTIPLE_BLOCK */
#define CMD55 (0x40+55) /* APP_CMD */
#define CMD58 (0x40+58) /* READ_OCR */
/* Port Controls (Platform dependent) */
#define SELECT() GPIO_ResetBits(GPIO_CS, GPIO_Pin_CS) /* MMC CS = L */
#define DESELECT() GPIO_SetBits(GPIO_CS, GPIO_Pin_CS) /* MMC CS = H */
#define PWR_ON() GPIO_ResetBits(GPIO_PWR, GPIO_Pin_PWR)
#define PWR_OFF() GPIO_SetBits(GPIO_PWR, GPIO_Pin_PWR)
#define PWR_ISON() ( ( GPIO_ReadOutputDataBit(GPIO_PWR, GPIO_Pin_PWR) == Bit_SET ) ? 0 : 1 )
/* Manley EK-STM32F board does not offer socket contacts -> dummy values: */
#define SOCKPORT 1 /* Socket contact port */
#define SOCKWP 0 /* Write protect switch (PB5) */
#define SOCKINS 0 /* Card detect switch (PB4) */
static void FCLK_SLOW(void) /* Set slow clock (100k-400k) */
{
DWORD tmp;
tmp = SPI2->CR1;
tmp = ( tmp | SPI_BaudRatePrescaler_256 );
SPI2->CR1 = tmp;
}
static void FCLK_FAST(void) /* Set fast clock (depends on the CSD) */
{
DWORD tmp;
tmp = SPI2->CR1;
tmp = ( tmp & ~SPI_BaudRatePrescaler_256 ) | SPI_BaudRatePrescaler_4; // 72MHz/4 here
SPI2->CR1 = tmp;
}
/*--------------------------------------------------------------------------
Module Private Functions
---------------------------------------------------------------------------*/
static volatile
DSTATUS Stat = STA_NOINIT; /* Disk status */
static volatile
DWORD Timer1, Timer2; /* 100Hz decrement timers */
static
BYTE CardType; /* Card type flags */
/*-----------------------------------------------------------------------*/
/* Transmit/Receive a byte to MMC via SPI (Platform dependent) */
/*-----------------------------------------------------------------------*/
static BYTE stm32_spi_rw( BYTE out )
{
/* Loop while DR register in not empty */
/// while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) { ; }
/* Send byte through the SPI1 peripheral */
SPI_I2S_SendData(SPI2, out);
/* Wait to receive a byte */
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) { ; }
/* Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(SPI2);
}
/*-----------------------------------------------------------------------*/
/* Transmit a byte to MMC via SPI (Platform dependent) */
/*-----------------------------------------------------------------------*/
#define xmit_spi(dat) stm32_spi_rw(dat)
/*-----------------------------------------------------------------------*/
/* Receive a byte from MMC via SPI (Platform dependent) */
/*-----------------------------------------------------------------------*/
static
BYTE rcvr_spi (void)
{
return stm32_spi_rw(0xff);
}
/* Alternative macro to receive data fast */
#define rcvr_spi_m(dst) *(dst)=stm32_spi_rw(0xff)
/*-----------------------------------------------------------------------*/
/* Wait for card ready */
/*-----------------------------------------------------------------------*/
static
BYTE wait_ready (void)
{
BYTE res;
Timer2 = 50; /* Wait for ready in timeout of 500ms */
rcvr_spi();
do
res = rcvr_spi();
while ((res != 0xFF) && Timer2);
return res;
}
/*-----------------------------------------------------------------------*/
/* Deselect the card and release SPI bus */
/*-----------------------------------------------------------------------*/
static
void release_spi (void)
{
DESELECT();
rcvr_spi();
}
#ifdef STM32_USE_DMA
/*-----------------------------------------------------------------------*/
/* Transmit/Receive Block using DMA (Platform dependent. STM32 here) */
/*-----------------------------------------------------------------------*/
static
void stm32_dma_transfer(
BOOL receive, /* FALSE for buff->SPI, TRUE for SPI->buff */
const BYTE *buff, /* receive TRUE : 512 byte data block to be transmitted
receive FALSE : Data buffer to store received data */
UINT btr /* receive TRUE : Byte count (must be multiple of 2)
receive FALSE : Byte count (must be 512) */
)
{
DMA_InitTypeDef DMA_InitStructure;
uint32_t rw_workbyte[] = { 0xffff };
/* shared DMA configuration values */
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(SPI2->DR));
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_BufferSize = btr;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
//DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_DeInit(DMA1_Stream3);
DMA_DeInit(DMA1_Stream4);
if ( receive ) {
/* DMA1 channel4 configuration SPI2 RX ---------------------------------------------*/
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buff;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_Init(DMA1_Stream3, &DMA_InitStructure);
/* DMA1 channel5 configuration SPI2 TX ---------------------------------------------*/
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)rw_workbyte;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_Init(DMA1_Stream4, &DMA_InitStructure);
} else {
/* DMA1 channel2 configuration SPI2 RX ---------------------------------------------*/
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)rw_workbyte;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_Init(DMA1_Stream3, &DMA_InitStructure);
/* DMA1 channel3 configuration SPI2 TX ---------------------------------------------*/
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)buff;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_Init(DMA1_Stream4, &DMA_InitStructure);
}
/* Enable DMA1 Channel4 */
DMA_Cmd(DMA1_Stream3, ENABLE);
/* Enable DMA1 Channel5 */
DMA_Cmd(DMA1_Stream4, ENABLE);
/* Enable SPI2 TX/RX request */
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
/* Wait until DMA1_Channel 5 Transfer Complete */
// not needed: while (DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET) { ; }
/* Wait until DMA1_Channel 4 Receive Complete */
while (DMA_GetFlagStatus(DMA1_Stream3,DMA_FLAG_TCIF3) == RESET) { ; }
/* Disable DMA1 Channel4 */
DMA_Cmd(DMA1_Stream2, DISABLE);
/* Disable DMA1 Channel5 */
DMA_Cmd(DMA1_Stream3, DISABLE);
/* Disable SPI1 RX/TX request */
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, DISABLE);
DMA_ClearFlag(DMA1_Stream3,DMA_FLAG_TCIF2);
DMA_ClearFlag(DMA1_Stream4,DMA_FLAG_TCIF3);
}
#endif /* STM32_USE_DMA */
/*-----------------------------------------------------------------------*/
/* Power Control (Platform dependent) */
/*-----------------------------------------------------------------------*/
/* When the target system does not support socket power control, there */
/* is nothing to do in these functions and chk_power always returns 1. */
static
void power_on (void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
volatile BYTE dummyread;
/* Enable SPI2 and GPIO clocks */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIO_CS | RCC_AHB1Periph_GPIO_PWR, ENABLE);
/* Configure I/O for Power FET */
// GPIO_InitStructure.GPIO_Pin = GPIO_Pin_PWR;
// GPIO_InitStructure.GPIO_Mode = GPIO_Mode_PWR;
// GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// GPIO_Init(GPIO_PWR, &GPIO_InitStructure);
// PWR_ON();
// for (Timer1 = 25; Timer1; ); /* Wait for 250ms */
/* Configure I/O for Flash Chip select */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_CS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_Init(GPIO_CS, &GPIO_InitStructure);
/* Deselect the Card: Chip Select high */
DESELECT();
/* Configure SPI2 pins: SCK and MOSI push-pull */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Configure MISO as Input with pull-up */
/* GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);*/
/* SPI2 configuration */
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128; // 24000kHz/128=187kHz < 400Hz
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI2, &SPI_InitStructure);
SPI_CalculateCRC(SPI2, DISABLE);
/* Enable SPIx */
SPI_Cmd(SPI2, ENABLE);
/* drain SPI */
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) { ; }
dummyread = SPI_I2S_ReceiveData(SPI2);
#ifdef STM32_USE_DMA
/* enable DMA clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);
#endif
}
static
void power_off (void)
{
GPIO_InitTypeDef GPIO_InitStructure;
if (!(Stat & STA_NOINIT)) {
SELECT();
wait_ready();
release_spi();
}
SPI_Cmd(SPI2, DISABLE);
SPI_I2S_DeInit(SPI2);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, DISABLE);
/* All SPI-Pins to input with weak internal pull-downs */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Chip select internal pull-down too */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_CS;
GPIO_Init(GPIO_CS, &GPIO_InitStructure);
// PWR_OFF();
Stat |= STA_NOINIT; /* Set STA_NOINIT */
}
static
int chk_power(void) /* Socket power state: 0=off, 1=on */
{
// return PWR_ISON() ? 1 : 0;
return 1;
}
/*-----------------------------------------------------------------------*/
/* Receive a data packet from MMC */
/*-----------------------------------------------------------------------*/
static
BOOL rcvr_datablock (
BYTE *buff, /* Data buffer to store received data */
UINT btr /* Byte count (must be multiple of 4) */
)
{
BYTE token;
Timer1 = 10;
do { /* Wait for data packet in timeout of 100ms */
token = rcvr_spi();
} while ((token == 0xFF) && Timer1);
if(token != 0xFE) return FALSE; /* If not valid data token, return with error */
#ifdef STM32_USE_DMA
stm32_dma_transfer( TRUE, buff, btr );
#else
do { /* Receive the data block into buffer */
rcvr_spi_m(buff++);
rcvr_spi_m(buff++);
rcvr_spi_m(buff++);
rcvr_spi_m(buff++);
} while (btr -= 4);
#endif /* STM32_USE_DMA */
rcvr_spi(); /* Discard CRC */
rcvr_spi();
return TRUE; /* Return with success */
}
/*-----------------------------------------------------------------------*/
/* Send a data packet to MMC */
/*-----------------------------------------------------------------------*/
#if _READONLY == 0
static
BOOL xmit_datablock (
const BYTE *buff, /* 512 byte data block to be transmitted */
BYTE token /* Data/Stop token */
)
{
BYTE resp;
#ifndef STM32_USE_DMA
BYTE wc;
#endif
if (wait_ready() != 0xFF) return FALSE;
xmit_spi(token); /* Xmit data token */
if (token != 0xFD) { /* Is data token */
#ifdef STM32_USE_DMA
stm32_dma_transfer( FALSE, buff, 512 );
#else
wc = 0;
do { /* Xmit the 512 byte data block to MMC */
xmit_spi(*buff++);
xmit_spi(*buff++);
} while (--wc);
#endif /* STM32_USE_DMA */
xmit_spi(0xFF); /* CRC (Dummy) */
xmit_spi(0xFF);
resp = rcvr_spi(); /* Receive data response */
if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */
return FALSE;
}
return TRUE;
}
#endif /* _READONLY */
/*-----------------------------------------------------------------------*/
/* Send a command packet to MMC */
/*-----------------------------------------------------------------------*/
static
BYTE send_cmd (
BYTE cmd, /* Command byte */
DWORD arg /* Argument */
)
{
BYTE n, res;
if (cmd & 0x80) { /* ACMD<n> is the command sequence of CMD55-CMD<n> */
cmd &= 0x7F;
res = send_cmd(CMD55, 0);
if (res > 1) return res;
}
/* Select the card and wait for ready */
DESELECT();
SELECT();
if (wait_ready() != 0xFF) return 0xFF;
/* Send command packet */
xmit_spi(cmd); /* Start + Command index */
xmit_spi((BYTE)(arg >> 24)); /* Argument[31..24] */
xmit_spi((BYTE)(arg >> 16)); /* Argument[23..16] */
xmit_spi((BYTE)(arg >> 8)); /* Argument[15..8] */
xmit_spi((BYTE)arg); /* Argument[7..0] */
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) */
if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) */
xmit_spi(n);
/* Receive command response */
if (cmd == CMD12) rcvr_spi(); /* Skip a stuff byte when stop reading */
n = 10; /* Wait for a valid response in timeout of 10 attempts */
do
res = rcvr_spi();
while ((res & 0x80) && --n);
return res; /* Return with the response value */
}
/*--------------------------------------------------------------------------
Public Functions
---------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE drv /* Physical drive number (0) */
)
{
BYTE n, cmd, ty, ocr[4];
if (drv) return STA_NOINIT; /* Supports only single drive */
if (Stat & STA_NODISK) return Stat; /* No card in the socket */
power_on(); /* Force socket power on */
FCLK_SLOW();
for (n = 10; n; n--) rcvr_spi(); /* 80 dummy clocks */
ty = 0;
if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */
Timer1 = 100; /* Initialization timeout of 1000 msec */
if (send_cmd(CMD8, 0x1AA) == 1) { /* SDHC */
for (n = 0; n < 4; n++) ocr[n] = rcvr_spi(); /* Get trailing return value of R7 resp */
if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */
while (Timer1 && send_cmd(ACMD41, 1UL << 30)); /* Wait for leaving idle state (ACMD41 with HCS bit) */
if (Timer1 && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
for (n = 0; n < 4; n++) ocr[n] = rcvr_spi();
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2;
}
}
} else { /* SDSC or MMC */
if (send_cmd(ACMD41, 0) <= 1) {
ty = CT_SD1; cmd = ACMD41; /* SDSC */
} else {
ty = CT_MMC; cmd = CMD1; /* MMC */
}
while (Timer1 && send_cmd(cmd, 0)); /* Wait for leaving idle state */
if (!Timer1 || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */
ty = 0;
}
}
CardType = ty;
release_spi();
if (ty) { /* Initialization succeeded */
Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */
FCLK_FAST();
} else { /* Initialization failed */
power_off();
}
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Get Disk Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE drv /* Physical drive number (0) */
)
{
if (drv) return STA_NOINIT; /* Supports only single drive */
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE drv, /* Physical drive number (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..255) */
)
{
if (drv || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */
if (count == 1) { /* Single block read */
if ((send_cmd(CMD17, sector) == 0) /* READ_SINGLE_BLOCK */
&& rcvr_datablock(buff, 512))
count = 0;
}
else { /* Multiple block read */
if (send_cmd(CMD18, sector) == 0) { /* READ_MULTIPLE_BLOCK */
do {
if (!rcvr_datablock(buff, 512)) break;
buff += 512;
} while (--count);
send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
}
}
release_spi();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if _READONLY == 0
DRESULT disk_write (
BYTE drv, /* Physical drive number (0) */
const BYTE *buff, /* Pointer to the data to be written */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..255) */
)
{
if (drv || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if (Stat & STA_PROTECT) return RES_WRPRT;
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */
if (count == 1) { /* Single block write */
if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */
&& xmit_datablock(buff, 0xFE))
count = 0;
}
else { /* Multiple block write */
if (CardType & CT_SDC) send_cmd(ACMD23, count);
if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
do {
if (!xmit_datablock(buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
release_spi();
return count ? RES_ERROR : RES_OK;
}
#endif /* _READONLY == 0 */
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
#if _USE_IOCTL != 0
DRESULT disk_ioctl (
BYTE drv, /* Physical drive number (0) */
BYTE ctrl, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
BYTE n, csd[16], *ptr = buff;
WORD csize;
if (drv) return RES_PARERR;
res = RES_ERROR;
if (ctrl == CTRL_POWER) {
switch (*ptr) {
case 0: /* Sub control code == 0 (POWER_OFF) */
if (chk_power())
power_off(); /* Power off */
res = RES_OK;
break;
case 1: /* Sub control code == 1 (POWER_ON) */
power_on(); /* Power on */
res = RES_OK;
break;
case 2: /* Sub control code == 2 (POWER_GET) */
*(ptr+1) = (BYTE)chk_power();
res = RES_OK;
break;
default :
res = RES_PARERR;
}
}
else {
if (Stat & STA_NOINIT) return RES_NOTRDY;
switch (ctrl) {
case CTRL_SYNC : /* Make sure that no pending write process */
SELECT();
if (wait_ready() == 0xFF)
res = RES_OK;
break;
case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
csize = csd[9] + ((WORD)csd[8] << 8) + 1;
*(DWORD*)buff = (DWORD)csize << 10;
} else { /* SDC ver 1.XX or MMC*/
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
*(DWORD*)buff = (DWORD)csize << (n - 9);
}
res = RES_OK;
}
break;
case GET_SECTOR_SIZE : /* Get R/W sector size (WORD) */
*(WORD*)buff = 512;
res = RES_OK;
break;
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
if (CardType & CT_SD2) { /* SDC ver 2.00 */
if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */
rcvr_spi();
if (rcvr_datablock(csd, 16)) { /* Read partial block */
for (n = 64 - 16; n; n--) rcvr_spi(); /* Purge trailing data */
*(DWORD*)buff = 16UL << (csd[10] >> 4);
res = RES_OK;
}
}
} else { /* SDC ver 1.XX or MMC */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */
if (CardType & CT_SD1) { /* SDC ver 1.XX */
*(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
} else { /* MMC */
*(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
}
res = RES_OK;
}
}
break;
case MMC_GET_TYPE : /* Get card type flags (1 byte) */
*ptr = CardType;
res = RES_OK;
break;
case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */
if (send_cmd(CMD9, 0) == 0 /* READ_CSD */
&& rcvr_datablock(ptr, 16))
res = RES_OK;
break;
case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */
if (send_cmd(CMD10, 0) == 0 /* READ_CID */
&& rcvr_datablock(ptr, 16))
res = RES_OK;
break;
case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */
if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */
for (n = 4; n; n--) *ptr++ = rcvr_spi();
res = RES_OK;
}
break;
case MMC_GET_SDSTAT : /* Receive SD status as a data block (64 bytes) */
if (send_cmd(ACMD13, 0) == 0) { /* SD_STATUS */
rcvr_spi();
if (rcvr_datablock(ptr, 64))
res = RES_OK;
}
break;
default:
res = RES_PARERR;
}
release_spi();
}
return res;
}
#endif /* _USE_IOCTL != 0 */
/*-----------------------------------------------------------------------*/
/* Device Timer Interrupt Procedure (Platform dependent) */
/*-----------------------------------------------------------------------*/
/* This function must be called in period of 10ms */
void disk_timerproc (void)
{
volatile static BYTE pv;
BYTE n, s;
n = Timer1; /* 100Hz decrement timer */
if (n) Timer1 = --n;
n = Timer2;
if (n) Timer2 = --n;
n = pv;
pv = SOCKPORT & (SOCKWP | SOCKINS); /* Sample socket switch */
if (n == pv) { /* Have contacts stabled? */
s = Stat;
if (pv & SOCKWP) /* WP is H (write protected) */
s |= STA_PROTECT;
else /* WP is L (write enabled) */
s &= ~STA_PROTECT;
if (pv & SOCKINS) /* INS = H (Socket empty) */
s |= (STA_NODISK | STA_NOINIT);
else /* INS = L (Card inserted) */
s &= ~STA_NODISK;
Stat = s;
}
}
Если не хотите использовать DMA, надо просто закомментировать строку в начале "#define STM32_USE_DMA".
Функция power_on() отвечает за инициализацию всей перефирии.
Функция stm32_dma_transfer() отвечает отправку пакетов 512 байт через DMA.
Ну вот интерфейсной частью разобрались. Есть еще ffconf.h - очень важный файл! Тут находится основная конфигурация библиотеки, что включить или выключить ради экономии размера кода или повышения надежности. Я тут выключил поддержку длинных имен, думал у меня из-за этого идут глюки, а они оказались в чем-то еще
И наконец, чтобы все совсем заработало: библиотека требует настройки таймера, чтобы делать четкие таймауты. В функции "main(void)", надо дать команду "SysTick_Config(SystemCoreClock / 1000);", чтобы раз в милисекунду сработало прерывание, в котором необходимо раз в 10 милисекунд вызвать функцию "disk_timerproc();" и если еще что хочется, то тоже можно
Код: Выделить всё
void SysTick_Handler(void)
{
volatile static uint8_t cntdiskio=0;
cntdiskio++;
if ( cntdiskio >= 10 ) {
cntdiskio = 0;
disk_timerproc(); /* to be called every 10ms */
}
//ff_test_term_timerproc(); /* to be called every ms */
}
Теперь пора применить!
В одном из примеров работы с библиотекой нашел вот такую функцию и чуток доработал ради поиска непонятной ошибки, но эта доработка стала фичей.
Код: Выделить всё
FRESULT scan_files (
char* path /* Начальная точка сканирования
(используется также как рабочая область) */)
{
FRESULT res;
FILINFO fno;
DIR dir;
int i;
char *fn; // Подразумевается, что конфигурация без Unicode.
#if _USE_LFN
static char lfn[_MAX_LFN + 1];
fno.lfname = lfn;
fno.lfsize = sizeof(lfn);
#endif
res = f_opendir(&dir, path); // Открытие директории
if (res == FR_OK)
{
i = strlen(path);
for (;;)
{
res = f_readdir(&dir, &fno); // Чтение объекта директории
if (res != FR_OK || fno.fname[0] == 0){
break; // Останов цикла при ошибке или при достижении
//конца списка директрории
}
if (fno.fname[0] == '.')
continue; // Игнорирование элемента 'точка'
#if _USE_LFN
fn = *fno.lfname ? fno.lfname : fno.fname;
#else
fn = fno.fname;
#endif
if ((fno.fattrib & AM_DIR) && i>1) // Если запрошена корневая директория, то в поддиректории не ходить!
{ // Это директория
sprintf(&path[i], "/%s", fn);
res = scan_files(path);
if (res != FR_OK) break;
}
else
{ // Это файл.
printf("%s/%s - %d \n\r", path, fn, fno.fattrib & AM_DIR);
}
}
}
return res;
}
Данная функция печатает список ВСЕХ файлов на флешке. Если в параметры передана строка "/", то это корень текушего диска и выведется просто список файлов, где через "-" будет: "0" - файл, а "16" директория. Если передать параметром "/dir", то будет выведен в список всех файлов данной директорий со всеми поддиректориями. В FatFs диски считаются по принципу Windows/DOS только вместо буковок цыферки . "0:" - Выведет ВСЕ файлы диска (обычно на SD карточке только один раздел, нулевой).
Ну, а чтобы эти команды давать, надо написать основную программу.
Код: Выделить всё
char path[256];
int main(void)
{
SystemInit();
initAll();
SysTick_Config(SystemCoreClock / 1000);
printf("Started USART!\n\r");
FRESULT st;
FATFS fs;
st=disk_initialize ( 0 ); // Обязательная инициализация Карты SD
if ( st == 0 ){
printf("SD inited!\n\r");
}else{
printf("Err SD Init! err=%d\n\r",st);
}
st=f_mount(0, &fs); // Реально ничего не делается, кроме присвоения адреса структуры.
if(st==FR_OK){ // Использоваться будет при первом обращении к карте
printf("Mount disk!\n\r");
}else{
printf("err Mount disk! err=%d\n\r",st);
}
// На самом деле ошибок монтирования быть не может
uint i=0;
while(1){
if(UsartTestData()){ // Если есть принятые данные
uint8_t tmp=UsartGetData(); // Если мы получаем комманду - надо читать
printf("%c",tmp); // создадим эхо на терминале
if (tmp==13){ // Команда пришла целиком - выполняем
st=scan_files((char *)&path);
if(st==FR_OK){
printf("Readed disk!\n\r");
}else{
printf("err Read disk! err=%d\n\r",st);
printf("lastPath=%s\n\r",path);
}
i=0;
}else if (tmp!=10){ // Перевод строки тоже может быть, но он нам не нужен
sprintf(&path[i++], "%c", tmp);
}
}
}
}
Ранее я упоминал про странный глюк, с которым боролся. Побороть его очевидным способом так и не удалось, видать проблема с оптимизатором gcc, поскольку глюк менялся от выбора оптимизатора, отключить который уже не получалось после подключения stdio - ошибка линковки. А именно глюк заключался в том, что у меня на флешке присутствуют куча фоток и дистрибутив WindowsXP (типа много много файликов), а при печати ВСЕХ файлов диска на каком-то моменте вывода файлов винды возникал сбой. Сбой был в том, что после открытия поддиректории вдруг менялось содержимое переменной path на что-то невразумительное, и разумеется возникала ошибка разной сложности, в плоть до зависания МК. Сбой менялся от выбора оптимизатора и размера переменной "path". При этом если выводить все директории на экран по очереди, ошибка не возникала в принципе, только на корне. Вот такая плавающая ошибка. Ошибка исчезла только после переноса определения переменной "path" из функции main() наружу, т.е. сделав ее глобальной. Почему ошибка появлялась только при выводе списка файлов из главной директории и только при инициализации переменной "path" внутри функции "main()" для меня так и осталось загадкой. С учетом виденных мной похожих описаний глюков именно gcc, списываю все на него (менял разные версии FatFs - глюк стабильно повторялся один в один).