mirror of
				https://github.com/marqs85/ossc
				synced 2025-10-30 23:46:02 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			508 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			508 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *  File: sd_io.c
 | |
|  *  Author: Nelson Lombardo
 | |
|  *  Year: 2015
 | |
|  *  e-mail: nelson.lombardo@gmail.com
 | |
|  *  License at the end of file.
 | |
|  */
 | |
| 
 | |
| #include "sd_io.h"
 | |
| 
 | |
| #ifdef _M_IX86  // For use over x86
 | |
| /*****************************************************************************/
 | |
| /* Private Methods Prototypes - Direct work with PC file                     */
 | |
| /*****************************************************************************/
 | |
| 
 | |
| /**
 | |
|  * \brief Get the total numbers of sectors in SD card.
 | |
|  * \param dev Device descriptor.
 | |
|  * \return Quantity of sectors. Zero if fail.
 | |
|  */
 | |
| DWORD __SD_Sectors (SD_DEV* dev);
 | |
| 
 | |
| /*****************************************************************************/
 | |
| /* Private Methods - Direct work with PC file                                */
 | |
| /*****************************************************************************/
 | |
| 
 | |
| DWORD __SD_Sectors (SD_DEV *dev)
 | |
| {
 | |
|     if (dev->fp == NULL) return(0); // Fail
 | |
|     else {
 | |
|         fseek(dev->fp, 0L, SEEK_END);
 | |
|         return (((DWORD)(ftell(dev->fp)))/((DWORD)512)-1);
 | |
|     }
 | |
| }
 | |
| #else   // For use with uControllers
 | |
| /******************************************************************************
 | |
|  Private Methods Prototypes - Direct work with SD card
 | |
| ******************************************************************************/
 | |
| 
 | |
| /**
 | |
|     \brief Simple function to calculate power of two.
 | |
|     \param e Exponent.
 | |
|     \return Math function result.
 | |
| */
 | |
| DWORD __SD_Power_Of_Two(BYTE e);
 | |
| 
 | |
| /**
 | |
|      \brief Assert the SD card (SPI CS low).
 | |
|  */
 | |
| inline void __SD_Assert (void);
 | |
| 
 | |
| /**
 | |
|     \brief Deassert the SD (SPI CS high).
 | |
|  */
 | |
| inline void __SD_Deassert (void);
 | |
| 
 | |
| /**
 | |
|     \brief Change to max the speed transfer.
 | |
|     \param throttle
 | |
|  */
 | |
| void __SD_Speed_Transfer (BYTE throttle);
 | |
| 
 | |
| /**
 | |
|     \brief Send SPI commands.
 | |
|     \param cmd Command to send.
 | |
|     \param arg Argument to send.
 | |
|     \return R1 response.
 | |
|  */
 | |
| BYTE __SD_Send_Cmd(BYTE cmd, DWORD arg);
 | |
| 
 | |
| /**
 | |
|     \brief Write a data block on SD card.
 | |
|     \param dat Storage the data to transfer.
 | |
|     \param token Inidicates the type of transfer (single or multiple).
 | |
|  */
 | |
| SDRESULTS __SD_Write_Block(SD_DEV *dev, void *dat, BYTE token);
 | |
| 
 | |
| /**
 | |
|     \brief Get the total numbers of sectors in SD card.
 | |
|     \param dev Device descriptor.
 | |
|     \return Quantity of sectors. Zero if fail.
 | |
|  */
 | |
| DWORD __SD_Sectors (SD_DEV *dev);
 | |
| 
 | |
| /******************************************************************************
 | |
|  Private Methods - Direct work with SD card
 | |
| ******************************************************************************/
 | |
| 
 | |
| DWORD __SD_Power_Of_Two(BYTE e)
 | |
| {
 | |
|     DWORD partial = 1;
 | |
|     BYTE idx;
 | |
|     for(idx=0; idx!=e; idx++) partial *= 2;
 | |
|     return(partial);
 | |
| }
 | |
| 
 | |
| inline void __SD_Assert(void){
 | |
|     SPI_CS_Low();
 | |
| }
 | |
| 
 | |
| inline void __SD_Deassert(void){
 | |
|     SPI_CS_High();
 | |
| }
 | |
| 
 | |
| void __SD_Speed_Transfer(BYTE throttle) {
 | |
|     if(throttle == HIGH) SPI_Freq_High();
 | |
|     else SPI_Freq_Low();
 | |
| }
 | |
| 
 | |
| BYTE __SD_Send_Cmd(BYTE cmd, DWORD arg)
 | |
| {
 | |
|     BYTE wiredata[10];
 | |
|     BYTE crc, res;
 | |
|     int timer_set;
 | |
| 
 | |
|     //printf("Sending SD CMD 0x%x with arg 0x%x\n", cmd, arg);
 | |
| 
 | |
|     // ACMD«n» is the command sequense of CMD55-CMD«n»
 | |
|     if(cmd & 0x80) {
 | |
|         cmd &= 0x7F;
 | |
|         res = __SD_Send_Cmd(CMD55, 0);
 | |
|         if (res > 1) return (res);
 | |
|     }
 | |
| 
 | |
|     // Select the card
 | |
|     __SD_Deassert();
 | |
|     SPI_R(NULL, 4);
 | |
|     __SD_Assert();
 | |
| 
 | |
|     // Send complete command set
 | |
|     wiredata[0] = cmd;                  // Start and command index
 | |
|     wiredata[1] = (arg >> 24);          // Arg[31-24]
 | |
|     wiredata[2] = (arg >> 16);          // Arg[23-16]
 | |
|     wiredata[3] = (arg >> 8 );          // Arg[15-08]
 | |
|     wiredata[4] = (arg >> 0 );          // Arg[07-00]
 | |
| 
 | |
|     // CRC?
 | |
|     crc = 0x01;                         // Dummy CRC and stop
 | |
|     if(cmd == CMD0) crc = 0x95;         // Valid CRC for CMD0(0)
 | |
|     if(cmd == CMD8) crc = 0x87;         // Valid CRC for CMD8(0x1AA)
 | |
|     wiredata[5] = crc;
 | |
| 
 | |
|     SPI_W(wiredata, 6);
 | |
| 
 | |
|     // Receive command response
 | |
|     // Wait for a valid response in timeout of 5 milliseconds
 | |
|     timer_set = SPI_Timer_On(5);
 | |
|     do {
 | |
|         SPI_R(&res, 1);
 | |
|     } while((res & 0x80)&&(SPI_Timer_Status()==TRUE));
 | |
|     if (timer_set == 0)
 | |
|         SPI_Timer_Off();
 | |
|     // Return with the response value
 | |
|     //printf("CMD_res: %u\n", res);
 | |
|     return(res);
 | |
| }
 | |
| 
 | |
| SDRESULTS __SD_Write_Block(SD_DEV *dev, void *dat, BYTE token)
 | |
| {
 | |
|     WORD idx;
 | |
|     BYTE line;
 | |
|     // Send token (single or multiple)
 | |
|     SPI_WW(token);
 | |
|     // Single block write?
 | |
|     if(token != 0xFD)
 | |
|     {
 | |
|         // Send block data
 | |
|         for(idx=0; idx!=SD_BLK_SIZE; idx++) SPI_WW(*((BYTE*)dat + idx));
 | |
|         /* Dummy CRC */
 | |
|         SPI_WW(0xFF);
 | |
|         SPI_WW(0xFF);
 | |
|         // If not accepted, returns the reject error
 | |
|         if((SPI_RW(0xFF) & 0x1F) != 0x05) return(SD_REJECT);
 | |
|     }
 | |
| #ifdef SD_IO_WRITE_WAIT_BLOCKER
 | |
|     // Waits until finish of data programming (blocked)
 | |
|     while(SPI_RW(0xFF)==0);
 | |
|     return(SD_OK);
 | |
| #else
 | |
|     // Waits until finish of data programming with a timeout
 | |
|     SPI_Timer_On(SD_IO_WRITE_TIMEOUT_WAIT);
 | |
|     do {
 | |
|         line = SPI_RW(0xFF);
 | |
|     } while((line==0)&&(SPI_Timer_Status()==TRUE));
 | |
|     SPI_Timer_Off();
 | |
| #ifdef SD_IO_DBG_COUNT
 | |
|     dev->debug.write++;
 | |
| #endif
 | |
|     if(line==0) return(SD_BUSY);
 | |
|     else return(SD_OK);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| DWORD __SD_Sectors (SD_DEV *dev)
 | |
| {
 | |
|     BYTE csd[18];
 | |
|     BYTE tkn;
 | |
|     DWORD ss = 0;
 | |
|     WORD C_SIZE = 0;
 | |
|     BYTE C_SIZE_MULT = 0;
 | |
|     BYTE READ_BL_LEN = 0;
 | |
|     int timer_set;
 | |
| 
 | |
|     if(__SD_Send_Cmd(CMD9, 0)==0)
 | |
|     {
 | |
|         // Wait for response
 | |
|         timer_set = SPI_Timer_On(5);  // Wait for data packet (timeout of 5ms)
 | |
|         do {
 | |
|             SPI_R(&tkn, 1);
 | |
|         } while((tkn==0xFF)&&(SPI_Timer_Status()==TRUE));
 | |
|         if (timer_set == 0)
 | |
|             SPI_Timer_Off();
 | |
| 
 | |
|         if(tkn!=0xFE)
 | |
|             return 0;
 | |
| 
 | |
|         // TODO: CRC check
 | |
|         SPI_R(csd, 18);
 | |
| 
 | |
|         if(dev->cardtype & SDCT_SD1)
 | |
|         {
 | |
|             ss = csd[0];
 | |
|             // READ_BL_LEN[83:80]: max. read data block length
 | |
|             READ_BL_LEN = (csd[5] & 0x0F);
 | |
|             // C_SIZE [73:62]
 | |
|             C_SIZE = (csd[6] & 0x03);
 | |
|             C_SIZE <<= 8;
 | |
|             C_SIZE |= (csd[7]);
 | |
|             C_SIZE <<= 2;
 | |
|             C_SIZE |= ((csd[8] >> 6) & 0x03);
 | |
|             // C_SIZE_MULT [49:47]
 | |
|             C_SIZE_MULT = (csd[9] & 0x03);
 | |
|             C_SIZE_MULT <<= 1;
 | |
|             C_SIZE_MULT |= ((csd[10] >> 7) & 0x01);
 | |
|         }
 | |
|         else if(dev->cardtype & SDCT_SD2)
 | |
|         {
 | |
|             // C_SIZE [69:48]
 | |
|             C_SIZE = (csd[7] & 0x3F);
 | |
|             C_SIZE <<= 8;
 | |
|             C_SIZE |= (csd[8] & 0xFF);
 | |
|             C_SIZE <<= 8;
 | |
|             C_SIZE |= (csd[9] & 0xFF);
 | |
|             // C_SIZE_MULT [--]. don't exits
 | |
|             C_SIZE_MULT = 17;   //C_SIZE_MULT+2 = 19
 | |
|             printf("csize: %u\n", C_SIZE);
 | |
|         }
 | |
|         ss = (C_SIZE + 1);
 | |
|         // SD_BLK_SIZE = 2^9
 | |
|         //ss *= __SD_Power_Of_Two(C_SIZE_MULT + 2 + READ_BL_LEN - 9);
 | |
|         ss *= 1 << (C_SIZE_MULT + 2 + READ_BL_LEN - 9);
 | |
|         //ss /= SD_BLK_SIZE;
 | |
|         return (ss);
 | |
|     } else return (0); // Error
 | |
| }
 | |
| #endif // Private methods for uC
 | |
| 
 | |
| /******************************************************************************
 | |
|  Public Methods - Direct work with SD card
 | |
| ******************************************************************************/
 | |
| 
 | |
| SDRESULTS SD_Init(SD_DEV *dev)
 | |
| {
 | |
|     BYTE initdata[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
 | |
| #if defined(_M_IX86)    // x86
 | |
|     dev->fp = fopen(dev->fn, "r+");
 | |
|     if (dev->fp == NULL)
 | |
|         return (SD_ERROR);
 | |
|     else
 | |
|     {
 | |
|         dev->last_sector = __SD_Sectors(dev);
 | |
| #ifdef SD_IO_DBG_COUNT
 | |
|         dev->debug.read = 0;
 | |
|         dev->debug.write = 0;
 | |
| #endif
 | |
|         return (SD_OK);
 | |
|     }
 | |
| #else   // uControllers
 | |
|     BYTE n, cmd, ct, ocr[4];
 | |
|     BYTE idx;
 | |
|     BYTE init_trys;
 | |
|     ct = 0;
 | |
|     for(init_trys=0; ((init_trys!=SD_INIT_TRYS)&&(!ct)); init_trys++)
 | |
|     {
 | |
|         // Initialize SPI for use with the memory card
 | |
|         SPI_Init();
 | |
| 
 | |
|         __SD_Deassert();
 | |
|         SPI_Freq_Low();
 | |
| 
 | |
|         // 80 dummy clocks
 | |
|         //for(idx = 0; idx != 10; idx++) SPI_RW(0xFF);
 | |
|         SPI_W(initdata, sizeof(initdata));
 | |
| 
 | |
|         /*SPI_Timer_On(500);
 | |
|         while(SPI_Timer_Status()==TRUE);
 | |
|         SPI_Timer_Off();*/
 | |
| 
 | |
|         dev->mount = FALSE;
 | |
|         /*SPI_Timer_On(500);
 | |
|         while ((__SD_Send_Cmd(CMD0, 0) != 1)&&(SPI_Timer_Status()==TRUE));
 | |
|         SPI_Timer_Off();*/
 | |
|         if (__SD_Send_Cmd(CMD0, 0) != 1)
 | |
|             continue;
 | |
|         // Idle state
 | |
| 
 | |
|         // SD version 2?
 | |
|         if (__SD_Send_Cmd(CMD8, 0x1AA) == 1) {
 | |
|             // Get trailing return value of R7 resp
 | |
|             SPI_R(ocr, 4);
 | |
|             // VDD range of 2.7-3.6V is OK?
 | |
|             if ((ocr[2] == 0x01)&&(ocr[3] == 0xAA))
 | |
|             {
 | |
|                 // Wait for leaving idle state (ACMD41 with HCS bit)...
 | |
|                 SPI_Timer_On(1000);
 | |
|                 while ((SPI_Timer_Status()==TRUE)&&(__SD_Send_Cmd(ACMD41, 1UL << 30)));
 | |
|                 // CCS in the OCR?
 | |
|                 if ((SPI_Timer_Status()==TRUE)&&(__SD_Send_Cmd(CMD58, 0) == 0))
 | |
|                 {
 | |
|                     SPI_R(ocr, 4);
 | |
|                     // SD version 2?
 | |
|                     ct = (ocr[0] & 0x40) ? SDCT_SD2 | SDCT_BLOCK : SDCT_SD2;
 | |
|                 }
 | |
|                 SPI_Timer_Off();
 | |
|             }
 | |
|         } else {
 | |
|             // SD version 1 or MMC?
 | |
|             if (__SD_Send_Cmd(ACMD41, 0) <= 1)
 | |
|             {
 | |
|                 // SD version 1
 | |
|                 ct = SDCT_SD1;
 | |
|                 cmd = ACMD41;
 | |
|             } else {
 | |
|                 // MMC version 3
 | |
|                 ct = SDCT_MMC;
 | |
|                 cmd = CMD1;
 | |
|             }
 | |
|             // Wait for leaving idle state
 | |
|             SPI_Timer_On(250);
 | |
|             while((SPI_Timer_Status()==TRUE)&&(__SD_Send_Cmd(cmd, 0)));
 | |
| 
 | |
|             if(SPI_Timer_Status()==FALSE) ct = 0;
 | |
|             SPI_Timer_Off();
 | |
|             if(__SD_Send_Cmd(CMD59, 0))   ct = 0;   // Deactivate CRC check (default)
 | |
|             if(__SD_Send_Cmd(CMD16, 512)) ct = 0;   // Set R/W block length to 512 bytes
 | |
|         }
 | |
|     }
 | |
|     if(ct) {
 | |
|         dev->cardtype = ct;
 | |
|         dev->mount = TRUE;
 | |
|         dev->last_sector = __SD_Sectors(dev) - 1;
 | |
|         printf("lastsec %lu\n", dev->last_sector);
 | |
| #ifdef SD_IO_DBG_COUNT
 | |
|         dev->debug.read = 0;
 | |
|         dev->debug.write = 0;
 | |
| #endif
 | |
|         __SD_Speed_Transfer(HIGH); // High speed transfer
 | |
|     }
 | |
|     SPI_Release();
 | |
|     return (ct ? SD_OK : SD_NOINIT);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| SDRESULTS SD_Read(SD_DEV *dev, void *dat, DWORD sector, WORD ofs, WORD cnt)
 | |
| {
 | |
| #if defined(_M_IX86)    // x86
 | |
|     // Check the sector query
 | |
|     if((sector > dev->last_sector)||(cnt == 0)) return(SD_PARERR);
 | |
|     if(dev->fp!=NULL)
 | |
|     {
 | |
|         if (fseek(dev->fp, ((512 * sector) + ofs), SEEK_SET)!=0)
 | |
|             return(SD_ERROR);
 | |
|         else {
 | |
|             if(fread(dat, 1, (cnt - ofs),dev->fp)==(cnt - ofs))
 | |
|             {
 | |
| #ifdef SD_IO_DBG_COUNT
 | |
|                 dev->debug.read++;
 | |
| #endif
 | |
|                 return(SD_OK);
 | |
|             }
 | |
|             else return(SD_ERROR);
 | |
|         }
 | |
|     } else {
 | |
|         return(SD_ERROR);
 | |
|     }
 | |
| #else   // uControllers
 | |
|     SDRESULTS res;
 | |
|     BYTE tkn;
 | |
|     WORD remaining;
 | |
|     res = SD_ERROR;
 | |
|     if ((sector > dev->last_sector)||(cnt == 0)) return(SD_PARERR);
 | |
|     // Convert sector number to byte address (sector * SD_BLK_SIZE) for SDC1
 | |
|     if (!(dev->cardtype & SDCT_BLOCK))
 | |
|         sector *= SD_BLK_SIZE;
 | |
| 
 | |
|     if (__SD_Send_Cmd(CMD17, sector) == 0) {
 | |
|         SPI_Timer_On(100);  // Wait for data packet (timeout of 100ms)
 | |
|         do {
 | |
|             SPI_R(&tkn, 1);
 | |
|         } while((tkn==0xFF)&&(SPI_Timer_Status()==TRUE));
 | |
|         SPI_Timer_Off();
 | |
|         // Token of single block?
 | |
|         if(tkn==0xFE) {
 | |
|             // Size block (512 bytes) + CRC (2 bytes) - offset - bytes to count
 | |
|             remaining = SD_BLK_SIZE + 2 - ofs - cnt;
 | |
|             // Skip offset
 | |
|             if(ofs) {
 | |
|                 SPI_R(NULL, ofs);
 | |
|             }
 | |
|             // I receive the data and I write in user's buffer
 | |
|             SPI_R((BYTE*)dat, cnt);
 | |
|             // Skip remaining
 | |
|             // TODO: CRC
 | |
|             SPI_R(NULL, remaining);
 | |
|             res = SD_OK;
 | |
|         }
 | |
|     }
 | |
|     SPI_Release();
 | |
| #ifdef SD_IO_DBG_COUNT
 | |
|     dev->debug.read++;
 | |
| #endif
 | |
|     return(res);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| #ifdef SD_IO_WRITE
 | |
| SDRESULTS SD_Write(SD_DEV *dev, void *dat, DWORD sector)
 | |
| {
 | |
| #if defined(_M_IX86)    // x86
 | |
|     // Query ok?
 | |
|     if(sector > dev->last_sector) return(SD_PARERR);
 | |
|     if(dev->fp != NULL)
 | |
|     {
 | |
|         if(fseek(dev->fp, SD_BLK_SIZE * sector, SEEK_SET)!=0)
 | |
|             return(SD_ERROR);
 | |
|         else {
 | |
|             if(fwrite(dat, 1, SD_BLK_SIZE, dev->fp)==SD_BLK_SIZE)
 | |
|             {
 | |
| #ifdef SD_IO_DBG_COUNT
 | |
|                 dev->debug.write++;
 | |
| #endif
 | |
|                 return(SD_OK);
 | |
|             }
 | |
|             else return(SD_ERROR);
 | |
|         }
 | |
|     } else return(SD_ERROR);
 | |
| #else   // uControllers
 | |
|     // Query ok?
 | |
|     if(sector > dev->last_sector) return(SD_PARERR);
 | |
|     // Convert sector number to byte address (sector * SD_BLK_SIZE) for SDC1
 | |
|     if (!(dev->cardtype & SDCT_BLOCK))
 | |
|         sector *= SD_BLK_SIZE;
 | |
| 
 | |
|     // Single block write (token <- 0xFE)
 | |
|     // Convert sector number to bytes address (sector * SD_BLK_SIZE)
 | |
|     if(__SD_Send_Cmd(CMD24, sector)==0)
 | |
|         return(__SD_Write_Block(dev, dat, 0xFE));
 | |
|     else
 | |
|         return(SD_ERROR);
 | |
| #endif
 | |
| }
 | |
| #endif
 | |
| 
 | |
| SDRESULTS SD_Status(SD_DEV *dev)
 | |
| {
 | |
| #if defined(_M_IX86)
 | |
|     return((dev->fp == NULL) ? SD_OK : SD_NORESPONSE);
 | |
| #else
 | |
|     return(__SD_Send_Cmd(CMD0, 0) ? SD_OK : SD_NORESPONSE);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| // «sd_io.c» is part of:
 | |
| /*----------------------------------------------------------------------------/
 | |
| /  ulibSD - Library for SD cards semantics            (C)Nelson Lombardo, 2015
 | |
| /-----------------------------------------------------------------------------/
 | |
| / ulibSD library is a free software that opened under license policy of
 | |
| / following conditions.
 | |
| /
 | |
| / Copyright (C) 2015, ChaN, all right reserved.
 | |
| /
 | |
| / 1. Redistributions of source code must retain the above copyright notice,
 | |
| /    this condition and the following disclaimer.
 | |
| /
 | |
| / This software is provided by the copyright holder and contributors "AS IS"
 | |
| / and any warranties related to this software are DISCLAIMED.
 | |
| / The copyright owner or contributors be NOT LIABLE for any damages caused
 | |
| / by use of this software.
 | |
| /----------------------------------------------------------------------------*/
 | |
| 
 | |
| // Derived from Mister Chan works on FatFs code (http://elm-chan.org/fsw/ff/00index_e.html):
 | |
| /*----------------------------------------------------------------------------/
 | |
| /  FatFs - FAT file system module  R0.11                 (C)ChaN, 2015
 | |
| /-----------------------------------------------------------------------------/
 | |
| / FatFs module is a free software that opened under license policy of
 | |
| / following conditions.
 | |
| /
 | |
| / Copyright (C) 2015, ChaN, all right reserved.
 | |
| /
 | |
| / 1. Redistributions of source code must retain the above copyright notice,
 | |
| /    this condition and the following disclaimer.
 | |
| /
 | |
| / This software is provided by the copyright holder and contributors "AS IS"
 | |
| / and any warranties related to this software are DISCLAIMED.
 | |
| / The copyright owner or contributors be NOT LIABLE for any damages caused
 | |
| / by use of this software.
 | |
| /----------------------------------------------------------------------------*/
 | 
