Eli Hughes
/
COMPACT_FLASH_2
FAT_FS/diskio.c
- Committer:
- emh203
- Date:
- 2012-02-16
- Revision:
- 0:76427232f435
File content as of revision 0:76427232f435:
#include "mbed.h" #include "LPC17xx.h" #include "integer.h" #include "diskio.h" #include "stdarg.h" #include "USBHost.h" #include "Terminal.h" /*-------------------------------------------------------------------------- Module Private Functions ---------------------------------------------------------------------------*/ static volatile DSTATUS Stat[3] = {STA_NOINIT | STA_NODISK}; /* Disk status */ static volatile BYTE DiskProcTimer[3]={0}; /* 100Hz decrement timer */ BYTE RAM_DISK[512]; DWORD BlockSize; DWORD BlockCount; //#define _CF_DEBUG_MESSAGES //#define _CF_DEBUG_READ_ATA #ifdef _CF_DEBUG_MESSAGES #define CF_DEBUG(...) printf(__VA_ARGS__) #else #define CF_DEBUG do{}while(0); #endif void disk_timerproc (void); #define LED1_MASK (uint32_t )(1<<18) #define LED2_MASK (uint32_t )(1<<20) #define LED3_MASK (uint32_t )(1<<21) #define LED4_MASK (uint32_t )(1<<23) #define LED1_ON LPC_GPIO1->FIOSET=LED1_MASK #define LED1_OFF LPC_GPIO1->FIOCLR=LED1_MASK #define LED1_TOGGLE LPC_GPIO1->FIOPIN^=LED1_MASK #define LED2_ON LPC_GPIO1->FIOSET=LED2_MASK #define LED2_OFF LPC_GPIO1->FIOCLR=LED2_MASK #define LED2_TOGGLE LPC_GPIO1->FIOPIN^=LED2_MASK #define LED3_ON LPC_GPIO1->FIOSET=LED3_MASK #define LED3_OFF LPC_GPIO1->FIOCLR=LED3_MASK #define LED3_TOGGLE LPC_GPIO1->FIOPIN^=LED3_MASK #define LED4_ON LPC_GPIO1->FIOSET=LED4_MASK #define LED4_OFF LPC_GPIO1->FIOCLR=LED4_MASK #define LED4_TOGGLE LPC_GPIO1->FIOPIN^=LED4_MASK //8-bit Data Bus is on Pins P0.4 - P0.11 #define DATA_BUS_MASK (uint32_t )((0xFF)<<4) //3 bit address is on Port 2.0 - 2.2 #define ADDRESS_BUS_MASK (uint32_t )(0x7) //ChipSelects are on port 2 #define CS0_MASK (uint32_t )(1<<3) #define CS1_MASK (uint32_t )(1<<4) //IORD and IOWR are on port 0 #define IORD_MASK (uint32_t )(1<<24) #define IOWR_MASK (uint32_t )(1<<23) //Reset and power enable are on port 1 #define COMPACT_FLASH_RESET_MASK (uint32_t )(1<<30) #define COMPACT_FLASH_POWER_ENABLE_MASK (uint32_t )(0x80000000) //Card Detect is on Port 2 #define COMPACT_FLASH_CARD_DETECT_MASK (uint32_t )(1<<5) //Low Level Bus Operation Macros //Note: LPC 176x have dedicate set and clear registers #define SET_DATA_BUS_TO_INPUTS LPC_GPIO0->FIODIR &= ~(DATA_BUS_MASK); #define SET_DATA_BUS_TO_OUTPUT LPC_GPIO0->FIODIR |= (DATA_BUS_MASK); #define CS0_ACTIVE LPC_GPIO2->FIOCLR = CS0_MASK #define CS0_INACTIVE LPC_GPIO2->FIOSET = CS0_MASK #define CS1_ACTIVE LPC_GPIO2->FIOCLR = CS1_MASK #define CS1_INACTIVE LPC_GPIO2->FIOSET = CS1_MASK #define IORD_ACTIVE LPC_GPIO0->FIOCLR = IORD_MASK #define IORD_INACTIVE LPC_GPIO0->FIOSET = IORD_MASK #define IOWR_ACTIVE LPC_GPIO0->FIOCLR = IOWR_MASK #define IOWR_INACTIVE LPC_GPIO0->FIOSET = IOWR_MASK #define COMPACT_FLASH_RESET_ACTIVE LPC_GPIO1->FIOCLR = COMPACT_FLASH_RESET_MASK #define COMPACT_FLASH_RESET_INACTIVE LPC_GPIO1->FIOSET = COMPACT_FLASH_RESET_MASK #define COMPACT_FLASH_POWER_ENABLE LPC_GPIO1->FIOCLR = COMPACT_FLASH_POWER_ENABLE_MASK #define COMPACT_FLASH_POWER_DISABLE LPC_GPIO1->FIOSET = COMPACT_FLASH_POWER_ENABLE_MASK #define COMPACT_FLASH_CARD_DETECTED (!((LPC_GPIO2->FIOPIN)&COMPACT_FLASH_CARD_DETECT_MASK)) //To set the Address and Data Bus Lines we will use the convient Mask register in the Port I/O modules //The Hardware will mask out pins that are set to 1 in the MASK register #define SET_CF_ADDRESS(ADDRESS) LPC_GPIO2->FIOMASK=~(ADDRESS_BUS_MASK); \ LPC_GPIO2->FIOPIN=ADDRESS; \ LPC_GPIO2->FIOMASK=0 //Always remember to reset the mask for other operations to complete correctly #define SET_CF_DATA(DATA) LPC_GPIO0->FIOMASK=~(DATA_BUS_MASK); \ LPC_GPIO0->FIOPIN=(((uint32_t)DATA)<<4); \ LPC_GPIO0->FIOMASK=0 //Always remember to reset the mask for other operations to complete correctly #define GET_CF_DATA(DATA) LPC_GPIO0->FIOMASK=~(DATA_BUS_MASK); \ (DATA) = (LPC_GPIO0->FIOPIN)>>4; \ LPC_GPIO0->FIOMASK=0 //Always remember to reset the mask for other operations to complete correctly #define SET_DATA_BUS_AS_OUTPUTS LPC_GPIO0->FIODIR|=DATA_BUS_MASK #define SET_DATA_BUS_AS_INPUTS LPC_GPIO0->FIODIR&=~DATA_BUS_MASK /* ATA command */ #define CMD_RESET 0x08 /* DEVICE RESET */ #define CMD_READ 0x20 /* READ SECTOR(S) */ #define CMD_WRITE 0x30 /* WRITE SECTOR(S) */ #define CMD_IDENTIFY 0xEC /* DEVICE IDENTIFY */ #define CMD_SETFEATURES 0xEF /* SET FEATURES */ /* ATA register bit definitions */ #define LBA 0xE0 #define BUSY 0x80 #define DRDY 0x40 #define DF 0x20 #define DRQ 0x08 #define ERR 0x01 #define SRST 0x40 #define nIEN 0x20 /* Bit definitions for Control Port */ #define CTL_READ 0x20 #define CTL_WRITE 0x40 #define CTL_RESET 0x80 #define REG_DATA 0x0 #define REG_ERROR 0x1 #define REG_FEATURES 0x1 #define REG_COUNT 0x2 #define REG_SECTOR 0x3 #define REG_CYLL 0x4 #define REG_CYLH 0x5 #define REG_DEV 0x6 #define REG_COMMAND 0x7 #define REG_STATUS 0x7 #define REG_DEVCTRL 0xE #define REG_ALTSTAT 0xE void InitCompactFlashInterface() { SET_DATA_BUS_AS_INPUTS; LPC_GPIO2->FIODIR |= ADDRESS_BUS_MASK | CS0_MASK | CS1_MASK; LPC_GPIO2->FIODIR &=~(COMPACT_FLASH_CARD_DETECT_MASK); LPC_GPIO1->FIODIR |= COMPACT_FLASH_RESET_MASK | COMPACT_FLASH_POWER_ENABLE_MASK | LED1_MASK | LED2_MASK | LED3_MASK | LED4_MASK; LPC_GPIO0->FIODIR |= IORD_MASK | IOWR_MASK ; COMPACT_FLASH_RESET_ACTIVE; COMPACT_FLASH_POWER_DISABLE; CS0_INACTIVE; CS1_INACTIVE; SysTick_Config(SystemCoreClock/100); NVIC_SetVector(SysTick_IRQn, (uint32_t)(&disk_timerproc)); } /*-----------------------------------------------------------------------*/ /* Read an ATA register */ /*-----------------------------------------------------------------------*/ static BYTE read_ata ( BYTE reg /* Register to be read */ ) { BYTE rd; CS0_ACTIVE; SET_DATA_BUS_AS_INPUTS; SET_CF_ADDRESS(reg); IORD_ACTIVE; __nop(); __nop(); __nop(); __nop(); GET_CF_DATA(rd); __nop(); __nop(); __nop(); __nop(); IORD_INACTIVE; CS0_INACTIVE; #ifdef _CF_DEBUG_READ_ATA CF_DEBUG("rd 0x%2x\r\n",rd); #endif return rd; } /*-----------------------------------------------------------------------*/ /* Write a byte to an ATA register */ /*-----------------------------------------------------------------------*/ static void write_ata ( BYTE reg, /* Register to be written */ BYTE dat /* Data to be written */ ) { __nop(); CS0_ACTIVE; SET_DATA_BUS_AS_OUTPUTS; SET_CF_ADDRESS(reg); SET_CF_DATA(dat); IOWR_ACTIVE; __nop(); __nop(); __nop(); __nop(); IOWR_INACTIVE; __nop(); __nop(); __nop(); __nop(); CS0_INACTIVE; SET_DATA_BUS_AS_INPUTS; } /*-----------------------------------------------------------------------*/ /* Read a part of data block */ /*-----------------------------------------------------------------------*/ static void read_part ( BYTE *buff, /* Data buffer to store read data */ BYTE ofs, /* Offset of the part of data in unit of word */ BYTE count /* Number of word to pick up */ ) { BYTE c = 0, dl, dh; SET_CF_ADDRESS(REG_DATA); /* Select Data register */ __nop(); __nop(); SET_DATA_BUS_AS_INPUTS; CS0_ACTIVE; __nop(); __nop(); do { IORD_ACTIVE; /* IORD = L */ __nop(); __nop(); __nop(); __nop(); GET_CF_DATA(dl); /* Read Even data */ IORD_INACTIVE; /* IORD = H */ __nop(); __nop(); __nop(); __nop(); IORD_ACTIVE; /* IORD = L */ __nop(); __nop(); __nop(); __nop(); GET_CF_DATA(dh); /* Read Odd data */ IORD_INACTIVE; /* IORD = H */ __nop(); __nop(); __nop(); __nop(); if (count && (c >= ofs)) { /* Pick up a part of block */ *buff++ = dl; *buff++ = dh; count--; } } while (++c); CS0_INACTIVE; read_ata(REG_ALTSTAT); read_ata(REG_STATUS); } /*-----------------------------------------------------------------------*/ /* Wait for Data Ready */ /*-----------------------------------------------------------------------*/ static int wait_data (void) { BYTE s; DiskProcTimer[CF] = 100; /* Time out = 1 sec */ do { if (!DiskProcTimer[CF]) return 0; /* Abort when timeout occured */ s = read_ata(REG_STATUS); /* Get status */ } while ((s & (BUSY|DRQ)) != DRQ); /* Wait for BUSY goes low and DRQ goes high */ read_ata(REG_ALTSTAT); return 1; } /*-----------------------------------------------------------------------*/ /* Initialize Disk Drive */ /*-----------------------------------------------------------------------*/ DSTATUS disk_initialize ( BYTE drv /* Physical drive number (0) */ ) { DSTATUS RetVal; switch(drv) { case COMPACT_FLASH: Stat[CF] |= STA_NOINIT; for (DiskProcTimer[CF] = 10; DiskProcTimer[CF]; ); /* 100ms */ if (Stat[CF] & STA_NODISK) return Stat[CF]; /* Exit when socket is empty */ COMPACT_FLASH_POWER_ENABLE; /* Initialize CFC control port */ for (DiskProcTimer[CF] = 1;DiskProcTimer[CF]; ); /* 10ms */ SET_DATA_BUS_AS_INPUTS; for (DiskProcTimer[CF] = 5; DiskProcTimer[CF]; ); /* 50ms */ COMPACT_FLASH_RESET_INACTIVE; for (DiskProcTimer[CF] = 5; DiskProcTimer[CF]; ); /* 50ms */ write_ata(REG_DEV, LBA); /* Select Device 0 */ DiskProcTimer[CF] = 200; do { /* Wait for card goes ready */ if (!DiskProcTimer[CF]) { CF_DEBUG("Timeout waiting for card BUSY to go inactive\r\n"); return Stat[CF]; } } while (read_ata(REG_STATUS) & BUSY); write_ata(REG_DEVCTRL, SRST | nIEN); /* Software reset */ for (DiskProcTimer[CF] = 2; DiskProcTimer[CF]; ); /* 20ms */ write_ata(REG_DEVCTRL, nIEN); /* Release software reset */ for (DiskProcTimer[CF] = 2; DiskProcTimer[CF]; ); /* 20ms */ DiskProcTimer[CF] = 200; do { /* Wait for card goes ready */ if (!DiskProcTimer[CF]) { CF_DEBUG("Timeout waiting for card DRDY\r\n"); return Stat[CF]; } } while ((read_ata(REG_STATUS) & (DRDY|BUSY)) != DRDY); CF_DEBUG("Setting to 8-bit PIO MOD\r\n"); write_ata(REG_FEATURES, 0x01); /* Select 8-bit PIO transfer mode */ write_ata(REG_COMMAND, CMD_SETFEATURES); DiskProcTimer[CF] = 200; do { if (!DiskProcTimer[CF]) { CF_DEBUG("Timeout waiting after trying to call the SETFEATURES command\r\n"); return Stat[CF]; } } while (read_ata(REG_STATUS) & (BUSY | ERR)); Stat[CF] &= ~STA_NOINIT; /* When device goes ready, clear STA_NOINIT */ RetVal = Stat[CF]; break; case USB: if(USB_Disk_Detected) { TERMINAL_PRINTF("USB Disk Detected.\r\n"); Stat[USB] &= ~(STA_NOINIT | STA_NODISK); } else { TERMINAL_PRINTF("USB Disk not attached.\r\n"); Stat[USB] |= STA_NOINIT | STA_NODISK; } RetVal = Stat[USB]; break; case RAM: Stat[RAM] &= ~STA_NOINIT; RetVal = Stat[RAM]; break; default: RetVal = STA_NOINIT; break; } return RetVal; } /*-----------------------------------------------------------------------*/ /* Return Disk Status */ /*-----------------------------------------------------------------------*/ DSTATUS disk_status ( BYTE drv ) { DSTATUS RetVal; switch(drv) { case CF: RetVal = Stat[CF]; break; case USB: RetVal = Stat[USB]; break; case RAM: RetVal = Stat[RAM]; break; default: RetVal = STA_NOINIT; break; } return RetVal; } /*-----------------------------------------------------------------------*/ /* Read Sector(s) */ /*-----------------------------------------------------------------------*/ DRESULT disk_read ( BYTE drv, /* Physical drive nmuber (0) */ BYTE *buff, /* Data buffer to store read data */ DWORD sector, /* Sector number (LBA) */ BYTE count /* Sector count (1..255) */ ) { BYTE c; DWORD i; switch(drv) { case CF: if (Stat[CF] & STA_NOINIT) return RES_NOTRDY; /* Issue Read Setor(s) command */ write_ata(REG_COUNT, count); write_ata(REG_SECTOR, (BYTE)sector); write_ata(REG_CYLL, (BYTE)(sector >> 8)); write_ata(REG_CYLH, (BYTE)(sector >> 16)); write_ata(REG_DEV, ((BYTE)(sector >> 24) & 0x0F) | LBA); write_ata(REG_COMMAND, CMD_READ); do { if (!wait_data()) return RES_ERROR; /* Wait data ready */ SET_CF_ADDRESS(REG_DATA); __nop(); __nop(); __nop(); __nop(); CS0_ACTIVE; c = 0; __nop(); __nop(); __nop(); SET_DATA_BUS_AS_INPUTS; do { IORD_ACTIVE; /* IORD = L */ __nop(); __nop(); __nop(); GET_CF_DATA(*buff++); /* Get even data */ __nop(); __nop(); __nop(); __nop(); __nop(); __nop(); IORD_INACTIVE; /* IORD = H */ __nop(); __nop(); __nop(); IORD_ACTIVE; /* IORD = L */ __nop(); __nop(); __nop(); GET_CF_DATA(*buff++); /* Get Odd data */ __nop(); __nop(); __nop(); __nop(); IORD_INACTIVE; /* IORD = H */ __nop(); __nop(); __nop(); } while (--c); } while (--count); CS0_INACTIVE; read_ata(REG_ALTSTAT); read_ata(REG_STATUS); return RES_OK; break; case USB: if(MassStorage_Read(sector, 1,(u8 *) buff, 512) == 0) return RES_OK; else return RES_NOTRDY; break; case RAM: for(i=0;i<512;i++) { buff[i] = RAM_DISK[i]; } return RES_OK; break; default: return RES_PARERR; break; } } /*-----------------------------------------------------------------------*/ /* Write Sector(s) */ /*-----------------------------------------------------------------------*/ DRESULT disk_write ( BYTE drv, /* Physical drive number (0) */ const BYTE *buff, /* Data to be written */ DWORD sector, /* Sector number (LBA) */ BYTE count /* Sector count (1..255) */ ) { BYTE s, c; DWORD i; DRESULT RetVal; switch(drv) { case CF: /* Issue Write Setor(s) command */ write_ata(REG_COUNT, count); write_ata(REG_SECTOR, (BYTE)sector); write_ata(REG_CYLL, (BYTE)(sector >> 8)); write_ata(REG_CYLH, (BYTE)(sector >> 16)); write_ata(REG_DEV, ((BYTE)(sector >> 24) & 0x0F) | LBA); write_ata(REG_COMMAND, CMD_WRITE); do { if (!wait_data()) return RES_ERROR; SET_CF_ADDRESS(REG_DATA); __nop(); __nop(); __nop(); CS0_ACTIVE; __nop(); __nop(); __nop(); SET_DATA_BUS_AS_OUTPUTS; c = 0; do { SET_CF_DATA(*buff++); /* Set even data */ __nop(); __nop(); __nop(); IOWR_ACTIVE; /* IOWR = L */ __nop(); __nop(); __nop(); IOWR_INACTIVE; /* IOWR = H */ __nop(); __nop(); __nop(); SET_CF_DATA(*buff++); /* Set odd data */ __nop(); __nop(); __nop(); IOWR_ACTIVE; /* IOWR = L */ __nop(); __nop(); __nop(); IOWR_INACTIVE; /* IOWR = H */ __nop(); __nop(); __nop(); } while (--c); } while (--count); SET_DATA_BUS_AS_INPUTS; CS0_INACTIVE; DiskProcTimer[CF] = 100; do { if (!DiskProcTimer[CF]) return RES_ERROR; s = read_ata(REG_STATUS); } while (s & BUSY); if (s & ERR) return RES_ERROR; read_ata(REG_ALTSTAT); read_ata(REG_STATUS); RetVal = RES_OK; break; case USB: if( MassStorage_Write(sector, 1, (u8 *)buff, 512) == 0) RetVal = RES_OK; else RetVal = RES_NOTRDY; break; case RAM: for(i=0;i<512;i++) { RAM_DISK[i] = buff[i]; } RetVal = RES_OK; break; default: RetVal = RES_PARERR; break; } return RetVal; } /*-----------------------------------------------------------------------*/ /* Miscellaneous Functions */ /*-----------------------------------------------------------------------*/ DRESULT disk_ioctl ( BYTE drv, /* Physical drive nmuber (0) */ BYTE ctrl, /* Control code */ void *buff /* Buffer to send/receive data block */ ) { BYTE n, w, ofs, dl, dh, *ptr = (BYTE *)buff; switch(drv) { case CF: switch (ctrl) { case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ ofs = 60; w = 2; n = 0; break; case GET_BLOCK_SIZE : /* Get erase block size in sectors (DWORD) */ *(DWORD*)buff = 32; return RES_OK; break; case CTRL_SYNC : /* Nothing to do */ return RES_OK; break; case ATA_GET_REV : /* Get firmware revision (8 chars) */ ofs = 23; w = 4; n = 4; break; case ATA_GET_MODEL : /* Get model name (40 chars) */ ofs = 27; w = 20; n = 20; break; case ATA_GET_SN : /* Get serial number (20 chars) */ ofs = 10; w = 10; n = 10; break; default: return RES_PARERR; } write_ata(REG_COMMAND, CMD_IDENTIFY); if (!wait_data()) return RES_ERROR; read_part(ptr, ofs, w); while (n--) { dl = *ptr; dh = *(ptr+1); *ptr++ = dh; *ptr++ = dl; } return RES_OK; break; case USB: if (USB_Disk_Detected == 0) { // TERMINAL_PRINTF("USB Disk not attached\r\n"); return RES_ERROR; } else { Stat[USB] &= ~STA_NOINIT; switch (ctrl) { case GET_SECTOR_COUNT: /* if(MassStorage_ReadCapacity(USB_Disk_Device, &BlockCount, &BlockSize) == 0) { *(DWORD *)(buff) = BlockCount; } else { return RES_ERROR; }*/ return RES_OK; break; case GET_BLOCK_SIZE : /* if(MassStorage_ReadCapacity(USB_Disk_Device, &BlockCount, &BlockSize) == 0) { *(DWORD *)(buff) = BlockSize; return RES_OK; } else { return RES_ERROR; }*/ return RES_OK; break; case CTRL_SYNC : return RES_OK; break; case ATA_GET_REV : strcpy ((CHAR *)buff,"00000"); return RES_OK; break; case ATA_GET_MODEL : strcpy ((CHAR *)buff, "000000"); return RES_OK; break; case ATA_GET_SN : strcpy ((CHAR *)buff,"00000000"); return RES_OK; break; default: return RES_PARERR; } } break; case RAM: switch (ctrl) { case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */ *(DWORD *)(buff) = 1; break; case GET_BLOCK_SIZE : /* Get erase block size in sectors (DWORD) */ *(DWORD *)(buff) = 1; return RES_OK; break; case CTRL_SYNC : /* Nothing to do */ return RES_OK; break; case ATA_GET_REV : /* Get firmware revision (8 chars) */ strcpy ((CHAR *)buff,"Rev 0.01"); break; case ATA_GET_MODEL : /* Get model name (40 chars) */ strcpy ((CHAR *)buff,"Wavenumber RAM Drive"); break; case ATA_GET_SN : /* Get serial number (20 chars) */ strcpy ((CHAR *)buff,"12345"); break; default: return RES_PARERR; } return RES_OK; break; default: return RES_PARERR; break; } return RES_OK; } /*-----------------------------------------------------------------------*/ /* Device timer interrupt procedure */ /*-----------------------------------------------------------------------*/ /* This function must be called in period of 10ms */ void disk_timerproc (void) { static BYTE pv; BYTE n; n = DiskProcTimer[CF]; /* 100Hz decrement timer */ if (n) DiskProcTimer[CF] = --n; n = DiskProcTimer[USB]; /* 100Hz decrement timer */ if (n) DiskProcTimer[USB] = --n; n = DiskProcTimer[RAM]; /* 100Hz decrement timer */ if (n) DiskProcTimer[RAM] = --n; n = pv; pv = COMPACT_FLASH_CARD_DETECTED ; /* Sapmle socket switch */ //Check Compact Flash Card Detect if (n == pv) { /* Have contacts stabled? */ if (!COMPACT_FLASH_CARD_DETECTED ) { /* CD1 or CD2 is high (Socket empty) */ Stat[CF] |= (STA_NODISK | STA_NOINIT); SET_DATA_BUS_TO_INPUTS; /* Float D0-D7 */ COMPACT_FLASH_RESET_ACTIVE; /* Assert RESET# */ COMPACT_FLASH_POWER_DISABLE; /* Power OFF */ LED1_OFF; } else { /* CD1 and CD2 are low (Card inserted) */ Stat[CF] &= ~STA_NODISK; LED1_ON; } } //Check to see if a USB drive is connected if(USB_Disk_Detected>0) LED2_ON; else LED2_OFF; } DWORD get_fattime(void) { time_t CurrentTimeStamp; tm *CurrentLocalTime; DWORD FATFSTimeCode; CurrentTimeStamp = time(NULL); CurrentLocalTime = localtime(&CurrentTimeStamp); //Map the tm struct time into the FatFs time code FATFSTimeCode = ((CurrentLocalTime->tm_year-80)<<25) | ((CurrentLocalTime->tm_mon+1)<<21) | ((CurrentLocalTime->tm_mday)<<16) | ((CurrentLocalTime->tm_hour)<<11) | ((CurrentLocalTime->tm_min)<<5) | ((CurrentLocalTime->tm_sec)); return FATFSTimeCode; }