pcus.ru
2015-05-19 10:57

Прикручиваем к STM32 SD-карту по SPI

Зачем нужно подключать карту к микроконтроллеру? Очень просто, например для записи каких либо данных с датчиков и чтобы потом было легко просмотреть данные на компьютере, эдакий самописец. Но для этого надо не только физически подключить карту но и научиться читать и писать на неё. Ну и для полного оргазма подцепить туда FAT что бы гулять так с музыкой.

Вот распиновка SD-карты
Прикручиваем к STM32 SD-карту по SPI

Мы будем собачить к SPI так как SDIO у моей STM32VLdiscovery просто нет.

И так начнем. Нам надо подключить всего 4 проводка на порты, питание и землю.
Подключаем крест на крест MOSI и MISO входы. Остальные соответственно.
Как показано на картинке. Я подцеплял на SPI1. Кстати, если хотите на другой то ищите в даташите какие ноги выделены под тот SPI. Там прям так и указано моси, мисо, разберетесь.
Прикручиваем к STM32 SD-карту по SPI

И так, если всё подключено нам нужен код.

Первое
Пропишем макросы

#define GO_IDLE_STATE 0 //Программная перезагрузка 
#define SEND_IF_COND 8 //Для SDC V2 - проверка диапазона напряжений 
#define READ_SINGLE_BLOCK 17 //Чтение указанного блока данных 
#define WRITE_SINGLE_BLOCK 24 //Запись указанного блока данных
#define SD_SEND_OP_COND 41 //Начало процесса инициализации 
#define APP_CMD 55 //Главная команда из ACMD  команд
#define READ_OCR 58 //Чтение регистра OCR


Второе
Напишем функции инициализации, чтения и записи.
 
//глобальная переменная для определения типа карты 
uint8_t  SDHC;            
//макроопределения для управления выводом SS
#define CS_ENABLE         GPIOA->BSRR = GPIO_BSRR_BR4;         
#define CS_DISABLE    	  GPIOA->BSRR = GPIO_BSRR_BS4;  
//********************************************************************************************
//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 коррекцию адреса блока выполнять не нужно(постраничная адресация)	
 
  CS_ENABLE;
 
  //передать код команды и ее аргумент
  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();
 
  CS_DISABLE; 
 
  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 ;
 
  spi_init();                            //инициализировать модуль SPI                        
  for(i=0;i0x20)  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 1;                     
 }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;  
  CS_ENABLE;
  //ожидание  маркера данных
  while(spi_read() != 0xfe)                
  if(i++ > 0xfffe) {CS_DISABLE; return 1;}       
 
  //чтение 512 байт	выбранного сектора
  for(i=0; i 0xfffe){CS_DISABLE; return 1;}
  CS_DISABLE;
 
  return 0;
}
 
//*********************************************************************************************
//function  инициализация  SPI1                                                              //
//argument  none                                                                             //
//return    none                                                                             //
//*********************************************************************************************
void spi_init(void)
{
  RCC->APB2ENR |=  RCC_APB2ENR_AFIOEN;//включить тактирование альтернативных функций
  RCC->APB2ENR |=  RCC_APB2ENR_IOPAEN;//включить тактирование порта А
 
  //вывод управления SS: выход двухтактный, общего назначения,50MHz
  GPIOA->CRL   |=  GPIO_CRL_MODE4;    //
  GPIOA->CRL   &= ~GPIO_CRL_CNF4;     //
  GPIOA->BSRR   =  GPIO_BSRR_BS4;     //
 
  //вывод SCK: выход двухтактный, альтернативная функция, 50MHz
  GPIOA->CRL   |=  GPIO_CRL_MODE5;    //
  GPIOA->CRL   &= ~GPIO_CRL_CNF5;     //
  GPIOA->CRL   |=  GPIO_CRL_CNF5_1;   //
 
  //вывод MISO: вход цифровой с подтягивающим резистором, подтяжка к плюсу
  GPIOA->CRL   &= ~GPIO_CRL_MODE6;    //
  GPIOA->CRL   &= ~GPIO_CRL_CNF6;     //
  GPIOA->CRL   |=  GPIO_CRL_CNF6_1;   //
  GPIOA->BSRR   =  GPIO_BSRR_BS6;     //
 
  //вывод MOSI: выход двухтактный, альтернативная функция, 50MHz
  GPIOA->CRL   |=  GPIO_CRL_MODE7;    //
  GPIOA->CRL   &= ~GPIO_CRL_CNF7;     //
  GPIOA->CRL   |=  GPIO_CRL_CNF7_1;   //
 
  //настроить модуль SPI
  RCC->APB2ENR |= RCC_APB2ENR_SPI1EN; //подать тактирование
  SPI1->CR2     = 0x0000;             //
  SPI1->CR1     = SPI_CR1_MSTR;       //контроллер должен быть мастером,конечно    
  SPI1->CR1    |= SPI_CR1_BR;         //для начала зададим маленькую скорость
  SPI1->CR1    |= SPI_CR1_SSI;
  SPI1->CR1    |= SPI_CR1_SSM;
  SPI1->CR1    |= SPI_CR1_SPE;        //разрешить работу модуля SPI
}
 
//*********************************************************************************************
//function  обмен данными по SPI1                                                            //
//argument  передаваемый байт                                                                //
//return    принятый байт                                                                    //
//*********************************************************************************************
uint8_t spi_send (uint8_t data)
{ 
  while (!(SPI1->SR & SPI_SR_TXE));      //убедиться, что предыдущая передача завершена
  SPI1->DR = data;                       //загружаем данные для передачи
  while (!(SPI1->SR & SPI_SR_RXNE));     //ждем окончания обмена
  return (SPI1->DR);		         //читаем принятые данные
}
 
//*********************************************************************************************
//function  прием байт по SPI1                                                               //
//argument  none                                                                             //
//return    принятый байт                                                                    //
//*********************************************************************************************
uint8_t spi_read (void)
{ 
  return spi_send(0xff);		  //читаем принятые данные
}



И последнее

Пробуем прочитать и записать
 
uint8_t Buff[512];                  //буфер для чтения и записи сектора карты                  //
 
SD_init();                          //выполнить инициализацию
SD_ReadSector(2, &Buff);            //копировать сектор №2 в буфер
SD_WriteSector(8, &Buff);           //записать данные из буфера в сектор №8


Удачного программинга) Надеюсь найду время описать FAT