В принципе можно и на другие МК 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, то все-таки делаю поправки, что так осуществлен протокол общения именно с 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); //читаем принятые данные
}
Код: Выделить всё
//********************************************************************************************
//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;
}
Код: Выделить всё
//********************************************************************************************
//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`ах тоже ничего



Вот тут мы видим, что 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;
}
}
Функция 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;
}

Ну, а чтобы эти команды давать, надо написать основную программу.
Код: Выделить всё
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);
}
}
}
}
