STM32F4Discovery + SPI2 + DMA + SDCard + FatFs

dtvims
Site Admin
Сообщения: 135
Зарегистрирован: Пн авг 02, 2010 2:43 pm

STM32F4Discovery + SPI2 + DMA + SDCard + FatFs

Сообщение dtvims » Пн июл 01, 2013 4:36 pm

STM32F4Discovery + SPI2 + DMA + SDCard + FatFs. Настройка и использование!

В принципе можно и на другие МК 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 - глюк стабильно повторялся один в один).

dtvims
Site Admin
Сообщения: 135
Зарегистрирован: Пн авг 02, 2010 2:43 pm

Re: STM32F4Discovery + SPI2 + DMA + SDCard + FatFs

Сообщение dtvims » Пт июл 05, 2013 1:19 pm

Маленькое дополнение ко всему вышесказанному. Чтобы не добавлять доп. инклуды во все файлы подряд, где они нужны, их необходимо помещать (раскомментировать) в файле "stm32f4xx_conf.h". Например, вот эти инклуды, что я воткнул в "diskio.c":

Код: Выделить всё

#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_spi.h"
#include "stm32f4xx_dma.h"

Они же были в "main.c". Их можно было только указать в "stm32f4xx_conf.h".

rfalex
Сообщения: 4
Зарегистрирован: Пт апр 04, 2014 6:10 pm

Re: STM32F4Discovery + SPI2 + DMA + SDCard + FatFs

Сообщение rfalex » Пт апр 04, 2014 6:18 pm

Добрый вечер. Хотел попробовать ваш пример. Но вот беда от куда у вас взялись вот эти переменные: CT_SD2
CT_BLOCK
CT_SD1
CT_MMC
Может Вы их забыли в дефайнах указать? Их даже не обзывается в коде Чена FatFS

dtvims
Site Admin
Сообщения: 135
Зарегистрирован: Пн авг 02, 2010 2:43 pm

Re: STM32F4Discovery + SPI2 + DMA + SDCard + FatFs

Сообщение dtvims » Вт апр 08, 2014 9:20 am

Они объявлены в diskio.h

Код: Выделить всё

/*-----------------------------------------------------------------------
/  Low level disk interface modlue include file   (C)ChaN, 2013
/-----------------------------------------------------------------------*/

#ifndef _DISKIO_DEFINED
#define _DISKIO_DEFINED

#ifdef __cplusplus
extern "C" {
#endif

#define _USE_WRITE   1   /* 1: Enable disk_write function */
#define _USE_IOCTL   1   /* 1: Enable disk_ioctl fucntion */

#include "integer.h"


/* Status of Disk Functions */
typedef BYTE   DSTATUS;

/* Results of Disk Functions */
typedef enum {
   RES_OK = 0,      /* 0: Successful */
   RES_ERROR,      /* 1: R/W Error */
   RES_WRPRT,      /* 2: Write Protected */
   RES_NOTRDY,      /* 3: Not Ready */
   RES_PARERR      /* 4: Invalid Parameter */
} DRESULT;


/*---------------------------------------*/
/* Prototypes for disk control functions */


DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE*buff, DWORD sector, BYTE count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, BYTE count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);


/* Disk Status Bits (DSTATUS) */
#define STA_NOINIT      0x01   /* Drive not initialized */
#define STA_NODISK      0x02   /* No medium in the drive */
#define STA_PROTECT      0x04   /* Write protected */


/* Command code for disk_ioctrl fucntion */

/* Generic command (used by FatFs) */
#define CTRL_SYNC         0   /* Flush disk cache (for write functions) */
#define GET_SECTOR_COUNT   1   /* Get media size (for only f_mkfs()) */
#define GET_SECTOR_SIZE      2   /* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */
#define GET_BLOCK_SIZE      3   /* Get erase block size (for only f_mkfs()) */
#define CTRL_ERASE_SECTOR   4   /* Force erased a block of sectors (for only _USE_ERASE) */

/* Generic command (not used by FatFs) */
#define CTRL_POWER         5   /* Get/Set power status */
#define CTRL_LOCK         6   /* Lock/Unlock media removal */
#define CTRL_EJECT         7   /* Eject media */
#define CTRL_FORMAT         8   /* Create physical format on the media */

/* MMC/SDC specific ioctl command */
#define MMC_GET_TYPE      10   /* Get card type */
#define MMC_GET_CSD         11   /* Get CSD */
#define MMC_GET_CID         12   /* Get CID */
#define MMC_GET_OCR         13   /* Get OCR */
#define MMC_GET_SDSTAT      14   /* Get SD status */

/* ATA/CF specific ioctl command */
#define ATA_GET_REV         20   /* Get F/W revision */
#define ATA_GET_MODEL      21   /* Get model name */
#define ATA_GET_SN         22   /* Get serial number */


/* MMC card type flags (MMC_GET_TYPE) */
#define CT_MMC      0x01      /* MMC ver 3 */
#define CT_SD1      0x02      /* SD ver 1 */
#define CT_SD2      0x04      /* SD ver 2 */
#define CT_SDC      (CT_SD1|CT_SD2)   /* SD */
#define CT_BLOCK   0x08      /* Block addressing */


#ifdef __cplusplus
}
#endif

#endif


Поскольку файл настройки я модифицировал из готового примера, то не счел необходимостью его выкладывать.

rfalex
Сообщения: 4
Зарегистрирован: Пт апр 04, 2014 6:10 pm

Re: STM32F4Discovery + SPI2 + DMA + SDCard + FatFs

Сообщение rfalex » Вт апр 08, 2014 11:12 pm

А есть возможность выложить полный проект Ваш. А то так я не могу разобраться что от куда берется. Возможно у нас с Вами библиотеки FatFS разные.

dtvims
Site Admin
Сообщения: 135
Зарегистрирован: Пн авг 02, 2010 2:43 pm

Re: STM32F4Discovery + SPI2 + DMA + SDCard + FatFs

Сообщение dtvims » Ср апр 09, 2014 8:40 am

Весь проект выкладывать не буду, т.к. он уже сильно переродился. Собственно я полностью описал шаги, чтобы все заработало. Саму библиотеку брал последней версии на тот момент. Вот ее и приложу...
fat_fs.zip
Fat fs для stm32F4
(61.53 КБ) 3068 скачиваний

Дополнительно файл настроек, от которого тоже многое зависит:
system_stm32f4xx.zip
Основной конфиг stm32f4
(5.4 КБ) 2907 скачиваний

rfalex
Сообщения: 4
Зарегистрирован: Пт апр 04, 2014 6:10 pm

Re: STM32F4Discovery + SPI2 + DMA + SDCard + FatFs

Сообщение rfalex » Пт апр 18, 2014 7:40 pm

Добрый вечер. Вот с fat fs что вы выложили заработало у меня только инициализация карты

Код: Выделить всё

st=disk_initialize(0);
- возвращает 0, т.е. все успешно.
Вот строчки к примеру

Код: Выделить всё

st=f_mount(0, &fs);

или вот эта

Код: Выделить всё

st=f_open (0, "12.txt", FA_CREATE_NEW);

если я их прописываю (другие не пробовал пока) у меня перестаёт иницилизироваться дисплей (подключен по FSMC, контроллере ili9481), вот я думаю в этих функция используются переменные которые залазиют на память отведенную на FSMC

Код: Выделить всё

#define REG *((volatile u16 *)0X60000000) //Команды
#define RAM *((volatile u16 *)0X60020000) //Данные

Может что подскажите.

rfalex
Сообщения: 4
Зарегистрирован: Пт апр 04, 2014 6:10 pm

Re: STM32F4Discovery + SPI2 + DMA + SDCard + FatFs

Сообщение rfalex » Пн апр 21, 2014 6:47 pm

Спасибо, не стал разбираться с SPI и попробовал SDIO все заработало. У меня в вашем проекте что то SPI неправильно работает, я и на другой пробовал переключать, результата ноль, хотя тач скрин запустил через SPI безпроблем

dtvims
Site Admin
Сообщения: 135
Зарегистрирован: Пн авг 02, 2010 2:43 pm

Re: STM32F4Discovery + SPI2 + DMA + SDCard + FatFs

Сообщение dtvims » Ср апр 23, 2014 5:42 pm

Код под ф4дисковери и может меняться инициализация для других чипов. Также я мог двигать пины на альтернативные (уже не помню), также надо обратить внимание на cs, который определяет активность нужного устройства, нужно активировать в ручную.
Собственно у самого отвалилась инициализация дисплея, правда на софтварном spi, но он мне и не особо нужен был, потому забил пока...

dtvims
Site Admin
Сообщения: 135
Зарегистрирован: Пн авг 02, 2010 2:43 pm

STM32F103 + SPI1 + монитор 5110

Сообщение dtvims » Чт апр 24, 2014 3:58 pm

Вот реабилитировал работу Мониторчика 5110, но только с другим контроллером, все с тем же STM32.
Сразу попал на ряд трудностей:
0. Надо качать свежую библиотеку cmsys. От туда нужно только core_cm3.h, core_cmFunc.h, core_cmInstr.h, т.к. Coocox по умолчанию тянет старую версию, которая не компилируется с новым gcc.
1. При программировании/отладке через отладчик (SWD) на Discovery, отлаживаемый контроллер должен иметь свое отдельное питание.
2. Моник подрубается к пину именно MOSI контроллера.
3. Порты JTAG по умолчанию задействованы под JTAG и их нельзя использовать, пока не отключишь онного.
4. Необходимо внимательно проверять под какой монитор пример или готовая библиотека.
5. Моник отказался работать на максимальной скорости SPI.
6. Реализовал свои функции отображения текста (шрифты есть в сети) и наткнулся на ряд особенностей работы дисплея 5110. Передача каждого байта должна заканчиваться сменой значения пина D/C (если верить datasheet), но на аппаратном SPI - это проблематично, поэтому, чтобы последняя команда применилась, приходится выполнять дополнительное пустое действие с небольшой задержкой (см. комментарии в коде). Просто переключить D/C не помогает. Если подобного не делать, то не будут корректно выставляться координаты вывода или не будет отрисовываться последний байт изображения. Если дисплей используется для отображения только текста, то после каждого вывода символа доп. задержка НЕобязательна, т.к. последний байт является разделительным межсимвольным столбцом и всегда на одних и тех же местах (шрифт фиксированного размера 6х8), а пустой столбец успешно отрисуется после вывода, или следующего символа, или после смены координат позиции.

Собственно готовый пример main.cpp:

Код: Выделить всё


#include "stm32f10x.h"
#include "font.h"
#include <stdlib.h>

#define SCK_Pin  GPIO_Pin_5
#define SCK_Pin_Port GPIOA

#define MOSI_Pin GPIO_Pin_7
#define MOSI_Pin_Port GPIOA

#define DC_Pin  GPIO_Pin_7
#define DC_Pin_Port GPIOB

#define RST_Pin GPIO_Pin_5
#define RST_Pin_Port GPIOB

#define SS_Pin  GPIO_Pin_6
#define SS_Pin_Port GPIOB

#define LCDHeight 48
#define LCDWidth 84
#define BuffSize (LCDWidth*LCDHeight/8)

#define swap(a, b) { int16_t t = a; a = b; b = t; }

volatile __IO uint32_t TimingDelay;
volatile __IO uint32_t UserTimingDelay=0;

void Delay(__IO uint32_t nTime)
{
  TimingDelay = nTime;

  while(TimingDelay != 0);
}

void ResetOn() {
    GPIO_SetBits(RST_Pin_Port, RST_Pin);
}

void ResetOff() {
    GPIO_ResetBits(RST_Pin_Port, RST_Pin);
}

void DCOn() {
    GPIO_SetBits(DC_Pin_Port, DC_Pin);
}

void DCOff() {
    GPIO_ResetBits(DC_Pin_Port, DC_Pin);
}

void SSOff() {
    GPIO_ResetBits(SS_Pin_Port, SS_Pin);
}

void SSOn() {
    GPIO_SetBits(SS_Pin_Port, SS_Pin);
}

void SPISend(uint8_t data) {
    SPI_I2S_SendData(SPI1, data);  // отправили данные
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); // ждём, пока данные не отправятся
}


uint8_t picture[BuffSize] = // изначально дисплей 102х65, а у меня 84х48, лишнее комментиуем
{
      //0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0xc0, 0xf0, 0xfc,
      0xfa, 0xf6, 0xe6, 0xee, 0xce, 0xde, 0xfe, 0xbf,
      0xbf, 0xff, 0x7f, 0x7f, 0xff, 0xfe, 0xfe, 0xfe,
      0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe,
      0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf8, 0xf8,
      0xf8, 0xf8, 0xf8, 0xf0, 0xf0, 0xf0, 0xf0, 0xe0,
      0xe0, 0xe0, 0xe0, 0xc0, 0x40, 0x40, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, //0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x00, //0x30,
      0x78, 0xfc, 0xfc, 0xfe, 0xfe, 0xfe, 0xfe, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xe0, 0xc0, 0x8e, 0xbf, 0xbf, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe,
      0xff, 0xff, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
      0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
      0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc,
      0x3c, 0x1e, 0xe, 0x2, 0x0, 0x0, 0x80, 0x80,
      0x80, 0x80, 0x0, //0x0, //0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0,
      0x1, 0x1, 0x3, 0x3, 0x7, 0x7, 0xf, 0xf,
      0xf, 0x1f, 0x1f, 0x1f, 0x1f, 0x3f, 0x3f, 0x3f,
      0x3f, 0x7f, 0x7f, 0x7f, 0x7f, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xee, 0xe4,
      0xf0, 0xf0, 0xf0, 0xf8, 0xff, 0xff, 0xff, 0xff,
      0xff, //0xff, 0xff, 0xff, //0xff, 0xfe, 0xfe, 0xfe,
      //0xfc, 0xfc, 0xfc, 0xf8, 0xf8, 0xf8, 0xf0, 0xf0,
      //0xe0, 0xe0, 0xc0,
      0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0, 0x0, 0x2, 0x19, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf,
      0xf, 0xf, 0x1f, 0x1f, 0xdf, 0xff, 0xff, 0xff,
      0xff, 0x7f, 0x7f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f,
      0x3f, 0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0xcf, 0x1f,
      0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, //0x1f,
      //0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0xf,
      //0xf, 0xf, 0xf, 0xf, 0xf, 0xf, 0x7, 0x7, 0x3,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x70,
      0xf8, 0xfc, 0xfc, 0xfc, 0x3e, 0xe, 0x6, 0x2,
      0x87, 0xff, 0xdf, 0x3f, 0x7f, 0x7f, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
      0xff, 0xff, 0xff, 0x7, 0x7, 0xf, 0xf, 0xc,
      0xf8, 0xf8, 0x10, 0x0, 0x0, 0x0, 0x0, 0x0,
      0xe0, 0xf0, 0x30, 0x20, 0x20, 0x80, 0xc0, 0xc0,
      0xc0, 0xc0, 0xc4, 0xcc, 0xcc, 0xcc, 0xd8, 0xf8,
      0xf8, 0xf8, 0x38, 0x8, 0xd, 0xf, 0x0, 0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, //0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      0x0,
      0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x7,
      0xf, 0x3f, 0x7f, 0xfc, 0xf8, 0xf0, 0xe0, 0xc7,
      0x8f, 0x8f, 0x1e, 0x1c, 0x38, 0x30, 0x31, 0x21,
      0x63, 0x63, 0x47, 0x47, 0x4f, 0x4f, 0x9f, 0x9f,
      0x9f, 0xbf, 0x3f, 0x3f, 0x7f, 0x7f, 0x7f, 0xff,
      0xff, 0xfc, 0xfc, 0xf8, 0xf8, 0xf0, 0xff, 0xff,
      0xf0, 0xe0, 0xc0, 0xc0, 0xc0, 0xc0, 0x83, 0x81,
      0x81, 0x80, 0x80, 0x80, 0x0, 0x2, 0x86, 0x86,
      0xc2, 0xc0, 0xc0, 0xfc, 0xfc, 0xfc, 0x74, 0x77,
      0x33, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80,
      0xc0, 0x0, 0x0, //0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
      //0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x3, 0x7, 0x7, 0x7, 0xf, 0xe, 0xe, 0xe, 0xe, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x3c, 0x38, 0x38, 0x38, 0x38, 0x38, 0x39, 0xf9, 0xf9, 0x78, 0x38, 0x10, 0x10, 0x13, 0x31, 0x31, 0x31, 0x31, 0x31, 0x33, 0x33, 0xbf, 0xfd, 0x69, 0x1, 0x43, 0x43, 0x43, 0xe3, 0xf7, 0x9f, 0x83, 0x1, 0x9, 0x19, 0x10, 0x30, 0x60, 0xe0, 0xc0, 0x4, 0x4, 0x4, 0x4, 0x6, 0x6, 0x6, 0x7, 0x3, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1f, 0xff, 0x60, 0x30, 0x30, 0x30, 0x18, 0x18, 0x30, 0x18, 0x1c, 0xc, 0xe, 0x7, 0x3, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1, 0x3, 0x2, 0x6, 0x6, 0x4, 0xc, 0xf, 0xf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};

void LCD_clear() {
   uint16_t i;
   DCOn();
   for(i = 0; i < BuffSize; i++) SPISend(0);
   Delay(1);
   DCOff();
   SPISend(0x00);
}

void LCDInit(void) {

   ResetOff(); //Set LCD reset = 0;
   DCOn(); //Mode = command;
   SSOn(); //Unselect chip;

   //Keep reset pin low for 10 ms
   Delay(10);
   //Release Reset Pin
   ResetOn(); //LCD_RST = 1;
   SSOff();
   DCOff();
   //Configure LCD module
   SPISend(0x21);  //Extended instruction set selected Режим настроек
   SPISend(0xC0);  //Set LCD voltage (defined by experimentation...) Контраст!
   SPISend(0x13);  //Set Bias for 1/48
   //SPISend(0x06);  //Set temperature control (TC2) Необязательно
   SPISend(0x20);  //Revert to standard instruction set
   SPISend(0x0c);  //Set display on in "normal" mode (not inversed) Режим отображения

}

void LCD_set_XY(unsigned char X, unsigned char Y) {
   unsigned char x;
   x = 6 * X;

   DCOff();
   SPISend(0x80 | x);
   SPISend(0x40 | Y);
   Delay(1); // Задержка, чтобы успела примениться последняя команда
   DCOn(); // Принуждает выполнить последнюю команду
}


void LCD_write_char(unsigned char c) {
   unsigned char line;
   unsigned char ch = 0;

   c = c - 32;
   DCOn();

   for (line = 0; line < 6; line++) {
      ch = font6_8[c][line];
      SPISend(ch);

   }
   Delay(1); // Чтобы применился последний байт необходима задержка и пустая команда
   DCOff();  // При использования HW SPI данные действия обязательны, т.к. последний бит должен быть передан при переключении D/C
   SPISend(0x00); // Пустая команда
   Delay(1); // Задержка, чтобы команда была отработана
}

void LCD_write_string(char *s) {
   unsigned char ch;
   while (*s != '\0') {
      ch = *s;
      LCD_write_char(ch);
      s++;
   }
}

void LCD_Write_Dec(unsigned int b) {

   unsigned char datas[3];

   datas[0] = b / 1000;
   b = b - datas[0] * 1000;
   datas[1] = b / 100;
   b = b - datas[1] * 100;
   datas[2] = b / 10;
   b = b - datas[2] * 10;
   datas[3] = b;

   datas[0] += 48;
   datas[1] += 48;
   datas[2] += 48;
   datas[3] += 48;

   LCD_write_char(datas[0]);
   LCD_write_char(datas[1]);
   LCD_write_char(datas[2]);
   LCD_write_char(datas[3]);
}

void LCDisplay(){
    LCD_set_XY(0,0);

//Включаем режим данных и заливаем катинку
    DCOn();
    uint16_t i;
    for(i = 0; i < BuffSize; i++) SPISend(picture[i]);

   Delay(1); // Задержка и пустая команда, чтобы применился последний байт данных
   DCOff();
   SPISend(0x00);
}

void DispClear(){
    uint16_t i;
    for(i = 0; i < BuffSize; i++) picture[i]=0;
}

void DispSetPixel(int16_t x, int16_t y, uint16_t color){
   if(x<0)x=0;
   if(y<0)y=0;
   if(x>=LCDWidth) x = LCDWidth-1;
   if(y>=LCDHeight) y = LCDHeight-1;

   if(color){
     picture[(y/8)*LCDWidth+x] |= 1<<(y%8);
   }else{
     picture[(y/8)*LCDWidth+x] &= ~1<<(y%8);
   }
}

void drawLine(int16_t x0, int16_t y0,
             int16_t x1, int16_t y1,
             uint16_t color) {
  int16_t steep = abs(y1 - y0) > abs(x1 - x0);
  if (steep) {
    swap(x0, y0);
    swap(x1, y1);
  }

  if (x0 > x1) {
    swap(x0, x1);
    swap(y0, y1);
  }

  int16_t dx, dy;
  dx = x1 - x0;
  dy = abs(y1 - y0);

  int16_t err = dx / 2;
  int16_t ystep;

  if (y0 < y1) {
    ystep = 1;
  } else {
    ystep = -1;
  }

  for (; x0<=x1; x0++) {
    if (steep) {
       DispSetPixel(y0, x0, color);
    } else {
       DispSetPixel(x0, y0, color);
    }
    err -= dy;
    if (err < 0) {
      y0 += ystep;
      err += dx;
    }
  }
}

void interfaceINIT(void){
   // Включаем RCC_APB2Periph_AFIO модуль альтернативной функции, чтобы отключить JTAG и освободить PB4
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_SPI1, ENABLE);
      GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); // Отключаем JTAG

      GPIO_InitTypeDef PORT;
          // выбрали ноги для настройки
      PORT.GPIO_Pin   = SCK_Pin | MOSI_Pin;
          // установили наименьшую скорость (максимальная скорость контроллера 4 Мбита в секунду)
      PORT.GPIO_Speed = GPIO_Speed_50MHz;
          // (важно!) определяем предназначение ног. здесь - выбор "альтернативной функции" ног
      PORT.GPIO_Mode  = GPIO_Mode_AF_PP;
          // настроили ноги в порту А
      GPIO_Init(GPIOA, &PORT);

          // выбрали ноги для настройки
      PORT.GPIO_Pin   = DC_Pin | RST_Pin | SS_Pin | GPIO_Pin_4;
          // установили скорость (тут - без разницы)
      PORT.GPIO_Speed = GPIO_Speed_50MHz;
          // предназначение - общее, выход
      PORT.GPIO_Mode  = GPIO_Mode_Out_PP;
          // настроили ноги в порту B
      GPIO_Init(GPIOB, &PORT);

      SPI_InitTypeDef SPIConf;
          // указываем, что используем мы только передачу данных
      SPIConf.SPI_Direction = SPI_Direction_1Line_Tx;
          // указываем, что наше устройство - Master
      SPIConf.SPI_Mode = SPI_Mode_Master;
          // передавать будем по 8 бит (=1 байт)
      SPIConf.SPI_DataSize = SPI_DataSize_8b;
          // режим 00
      SPIConf.SPI_CPOL = SPI_CPOL_Low;
      SPIConf.SPI_CPHA = SPI_CPHA_1Edge;
      SPIConf.SPI_NSS = SPI_NSS_Soft;
          // установим скорость передачи. Быстрее чем при делителе на 8 не заводится
      SPIConf.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
          // передаём данные старшим битом вперёд (т.е. слева направо)
      SPIConf.SPI_FirstBit = SPI_FirstBit_MSB;
          // внесём настройки в SPI
      SPI_Init(SPI1, &SPIConf);
          // включим  SPI1
      SPI_Cmd(SPI1, ENABLE);
          // SS = 1
      SPI_NSSInternalSoftwareConfig(SPI1, SPI_NSSInternalSoft_Set);

      ADC_InitTypeDef ADC_InitStructure;
      RCC_ADCCLKConfig(RCC_PCLK2_Div2);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
      ADC_DeInit(ADC1);

      ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;
      ADC_InitStructure.ADC_ScanConvMode=DISABLE;
      ADC_InitStructure.ADC_ScanConvMode=DISABLE;
      ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
      ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
      ADC_InitStructure.ADC_NbrOfChannel=1;
      ADC_Init(ADC1, &ADC_InitStructure);

      ADC_Cmd(ADC1, ENABLE);
      ADC_ResetCalibration(ADC1);
      while(ADC_GetResetCalibrationStatus(ADC1));
      ADC_StartCalibration(ADC1);
      while(ADC_GetCalibrationStatus(ADC1));
}

uint16_t readADC1(uint8_t channel){
   ADC_RegularChannelConfig(ADC1,channel, 1, ADC_SampleTime_1Cycles5);
   ADC_SoftwareStartConvCmd(ADC1, ENABLE);
   while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)==RESET);
   return ADC_GetConversionValue(ADC1);
}

int main(void)
{
   RCC_ClocksTypeDef RCC_Clocks;
   SystemInit();
   RCC_GetClocksFreq(&RCC_Clocks);
   SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000); //Таймер для функции Delay, срабатывает раз Милисекунду.

   interfaceINIT();

   LCDInit(); // Инициализация дисплея
   LCDisplay(); // Вывод картинки

    Delay(5000);
    DispClear(); // Очищаем буфер
    drawLine(0,0,83,47,1); // Рисуем линию в буфер (по другому на данный дисплей никак)
    LCDisplay(); // Отрисовка изображения из буфера
    Delay(1000);
    drawLine(0,47,83,0,1); // Рисем еще линию в буфер
    LCDisplay();
    Delay(5000);

    LCD_clear(); // Очистка дисплея
    LCD_set_XY(0,0); // Установка координат для вывода текста
    LCD_write_string(" Core STM32F1 ");
    LCD_write_string("  by DTViMS   ");
    LCD_write_string("Nokia 5110 LCD");
    LCD_write_string("ADC:          ");
    LCD_write_string("Откомпилирован");
    LCD_write_string("  Coocox IDE  ");

    while(1)
    {
       LCD_set_XY(5,3); // Выставляем координаты для текста
       LCD_Write_Dec(readADC1(ADC_Channel_0)); // Вывод значений АЦП
    }
}

void SysTick_Handler(void) // Счетчики миллисекунд
{
   if (TimingDelay != 0x00)
      {
          TimingDelay--;
      }
   if (UserTimingDelay != 0x00)
      {
         UserTimingDelay--;
      }

}


Вернуться в «Микроконтроллеры и автоматизация»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 2 гостя