test fork
Dependencies: SPI_TFTx2 SPI_TFTx2_ILI9341 TFT_fonts TOUCH_TFTx2 mbed
Fork of CANary_9341 by
Revision 129:8991d0de01ab, committed 2013-07-21
- Comitter:
- TickTock
- Date:
- Sun Jul 21 23:59:00 2013 +0000
- Parent:
- 64:a2b3c7201faa
- Parent:
- 128:25314f339565
- Child:
- 130:1a9d2a6d99ce
- Commit message:
- Merged into default (with errors)
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CHAN_FS/diskio.c Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,180 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* This is a stub disk I/O module that acts as front end of the existing */ +/* disk I/O modules and attach it to FatFs module with common interface. */ +/*-----------------------------------------------------------------------*/ + +#include "diskio.h" +#include "usbhost_inc.h" +#include "mbed.h" + +uint32_t _numBlks = 0; +uint32_t _blkSize = 512; + +int initialise_msc(); +void print_inquiry(USB_INT08U *inqReply); + + +int disk_sync() { return 0; } +int disk_sectors() { return _numBlks; } + +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; +} + +DSTATUS disk_status(BYTE Drive) +{ + return 0; +} + + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + + switch(ctrl) + { + case CTRL_SYNC: + res = RES_OK; + break; + + case GET_SECTOR_SIZE: + res = RES_OK; + *(WORD *)buff = 512; + break; + + case GET_SECTOR_COUNT: + res = RES_OK; + *(DWORD *)buff = (WORD)disk_sectors(); + break; + + case GET_BLOCK_SIZE: + res = RES_OK; + *(DWORD *)buff = 1; + break; + + default: + res = RES_OK; + break; + } + return res; +} + +DSTATUS disk_initialize(BYTE Drive) { + + if ( initialise_msc() != OK ) + return 1; + return 0; +} + +DRESULT disk_write(BYTE Drive,const BYTE * Buffer, DWORD SectorNumber, BYTE SectorCount) +{ + if ( OK == MS_BulkSend(SectorNumber, SectorCount, (USB_INT08U *)Buffer) ) + return RES_OK; + return RES_ERROR; +} + +DRESULT disk_read(BYTE Drive, BYTE * Buffer,DWORD SectorNumber, BYTE SectorCount) +{ + if ( OK == MS_BulkRecv(SectorNumber, SectorCount, (USB_INT08U *)Buffer) ) + return RES_OK; + return RES_ERROR; +} + + + + + +void print_inquiry(USB_INT08U *inqReply) +{ + // see USB Mass Storage Class � UFI Command Specification, + // 4.2 INQUIRY Command + printf("Inquiry reply:\n"); + uint8_t tmp = inqReply[0]&0x1F; + printf("Peripheral device type: %02Xh\n", tmp); + if ( tmp == 0 ) + printf("\t- Direct access (floppy)\n"); + else if ( tmp == 0x1F ) + printf("\t- none (no FDD connected)\n"); + else + printf("\t- unknown type\n"); + tmp = inqReply[1] >> 7; + printf("Removable Media Bit: %d\n", tmp); + tmp = inqReply[2] & 3; + printf("ANSI Version: %02Xh\n", tmp); + if ( tmp != 0 ) + printf("\t- warning! must be 0\n"); + tmp = (inqReply[2]>>3) & 3; + printf("ECMA Version: %02Xh\n", tmp); + if ( tmp != 0 ) + printf("\t- warning! should be 0\n"); + tmp = inqReply[2]>>6; + printf("ISO Version: %02Xh\n", tmp); + if ( tmp != 0 ) + printf("\t- warning! should be 0\n"); + tmp = inqReply[3] & 0xF; + printf("Response Data Format: %02Xh\n", tmp); + if ( tmp != 1 ) + printf("\t- warning! should be 1\n"); + tmp = inqReply[4]; + printf("Additional length: %02Xh\n", tmp); + if ( tmp != 0x1F ) + printf("\t- warning! should be 1Fh\n"); + printf("Vendor Information: '%.8s'\n", &inqReply[8]); + printf("Product Identification: '%.16s'\n", &inqReply[16]); + printf("Product Revision: '%.4s'\n", &inqReply[32]); +} + + + +int initialise_msc() +{ + USB_INT32S rc; + USB_INT08U inquiryResult[INQUIRY_LENGTH]; + + //print_clock(); + Host_Init(); /* Initialize the host controller */ + rc = Host_EnumDev(); /* Enumerate the device connected */ + if (rc != OK) + { + fprintf(stderr, "Could not enumerate device: %d\n", rc); + return rc; + } + + + /* Initialize the mass storage and scsi interfaces */ + rc = MS_Init( &_blkSize, &_numBlks, inquiryResult ); + if (rc != OK) + { + fprintf(stderr, "Could not initialize mass storage interface: %d\n", rc); + return rc; + } + printf("Successfully initialized mass storage interface; %d blocks of size %d\n", _numBlks, _blkSize); + print_inquiry(inquiryResult); + // FATFileSystem supports only 512-byte blocks + return _blkSize == 512 ? OK : 1; +} + + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CHAN_FS/diskio.h Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,68 @@ +/*----------------------------------------------------------------------- +/ Low level disk interface modlue include file R0.07 (C)ChaN, 2009 +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO + +#define _READONLY 0 /* 1: Read-only mode */ +#define _USE_IOCTL 1 + +#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 */ + +extern BOOL assign_drives (int argc, char *argv[]); +extern DSTATUS disk_initialize (BYTE); +extern DSTATUS disk_status (BYTE); +extern DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); +extern DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); +extern DRESULT disk_ioctl (BYTE, BYTE, void*); + +extern int _sd_sectors(); + + +/* 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() */ + +/* Generic command */ +#define CTRL_SYNC 0 /* Mandatory for write functions */ +#define GET_SECTOR_COUNT 1 /* Mandatory for only f_mkfs() */ +#define GET_SECTOR_SIZE 2 /* Mandatory for multiple sector size cfg */ +#define GET_BLOCK_SIZE 3 /* Mandatory for only f_mkfs() */ +#define CTRL_POWER 4 +#define CTRL_LOCK 5 +#define CTRL_EJECT 6 +/* MMC/SDC command */ +#define MMC_GET_TYPE 10 +#define MMC_GET_CSD 11 +#define MMC_GET_CID 12 +#define MMC_GET_OCR 13 +#define MMC_GET_SDSTAT 14 +/* ATA/CF command */ +#define ATA_GET_REV 20 +#define ATA_GET_MODEL 21 +#define ATA_GET_SN 22 + + +#define _DISKIO +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CHAN_FS/ff.c Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,3154 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.07e (C)ChaN, 2009 +/-----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following trems. +/ +/ Copyright (C) 2009, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Feb 26,'06 R0.00 Prototype. +/ +/ Apr 29,'06 R0.01 First stable version. +/ +/ Jun 01,'06 R0.02 Added FAT12 support. +/ Removed unbuffered mode. +/ Fixed a problem on small (<32M) patition. +/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM). +/ +/ Sep 22,'06 R0.03 Added f_rename(). +/ Changed option _FS_MINIMUM to _FS_MINIMIZE. +/ Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast. +/ Fixed f_mkdir() creates incorrect directory on FAT32. +/ +/ Feb 04,'07 R0.04 Supported multiple drive system. +/ Changed some interfaces for multiple drive system. +/ Changed f_mountdrv() to f_mount(). +/ Added f_mkfs(). +/ Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive. +/ Added a capability of extending file size to f_lseek(). +/ Added minimization level 3. +/ Fixed an endian sensitive code in f_mkfs(). +/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG. +/ Added FSInfo support. +/ Fixed DBCS name can result FR_INVALID_NAME. +/ Fixed short seek (<= csize) collapses the file object. +/ +/ Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs(). +/ Fixed f_mkfs() on FAT32 creates incorrect FSInfo. +/ Fixed f_mkdir() on FAT32 creates incorrect directory. +/ Feb 03,'08 R0.05a Added f_truncate() and f_utime(). +/ Fixed off by one error at FAT sub-type determination. +/ Fixed btr in f_read() can be mistruncated. +/ Fixed cached sector is not flushed when create and close +/ without write. +/ +/ Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets(). +/ Improved performance of f_lseek() on moving to the same +/ or following cluster. +/ +/ Apr 01,'09 R0.07 Merged Tiny-FatFs as a buffer configuration option. +/ Added long file name support. +/ Added multiple code page support. +/ Added re-entrancy for multitask operation. +/ Added auto cluster size selection to f_mkfs(). +/ Added rewind option to f_readdir(). +/ Changed result code of critical errors. +/ Renamed string functions to avoid name collision. +/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg. +/ Added multiple sector size support. +/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error. +/ Fixed wrong cache control in f_lseek(). +/ Added relative path feature. +/ Added f_chdir() and f_chdrive(). +/ Added proper case conversion to extended char. +/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h. +/ Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH. +/ Fixed name matching error on the 13 char boundary. +/ Added a configuration option, _LFN_UNICODE. +/ Changed f_readdir() to return the SFN with always upper +/ case on non-LFN cfg. +/---------------------------------------------------------------------------*/ + +#include "ff.h" /* FatFs configurations and declarations */ +#include "diskio.h" /* Declarations of low level disk I/O functions */ + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 0x007E +#error Wrong include file (ff.h). +#endif + +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area must not be used in re-entrant configuration. +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } + +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res + +#endif + +#define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); } + +#ifndef NULL +#define NULL 0 +#endif + +/* Name status flags */ +#define NS 11 /* Offset of name status byte */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ + + + + +/*-------------------------------------------------------------------------- + + Private Work Area + +---------------------------------------------------------------------------*/ + +#if _DRIVES < 1 || _DRIVES > 9 +#error Number of drives must be 1-9. +#endif +static +FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */ + +static +WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH +static +BYTE Drive; /* Current drive */ +#endif + + +#if _USE_LFN == 1 /* LFN with static LFN working buffer */ +static +WCHAR LfnBuf[_MAX_LFN + 1]; +#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR *lp = LfnBuf +#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp + +#elif _USE_LFN > 1 /* LFN with dynamic LFN working buffer */ +#define NAMEBUF(sp,lp) BYTE sp[12]; WCHAR lbuf[_MAX_LFN + 1], *lp = lbuf +#define INITBUF(dj,sp,lp) dj.fn = sp; dj.lfn = lp + +#else /* No LFN */ +#define NAMEBUF(sp,lp) BYTE sp[12] +#define INITBUF(dj,sp,lp) dj.fn = sp + +#endif + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, int cnt) { + char *d = (char*)dst; + const char *s = (const char *)src; + while (cnt--) *d++ = *s++; +} + +/* Fill memory */ +static +void mem_set (void* dst, int val, int cnt) { + char *d = (char*)dst; + while (cnt--) *d++ = (char)val; +} + +/* Compare memory to memory */ +static +int mem_cmp (const void* dst, const void* src, int cnt) { + const char *d = (const char *)dst, *s = (const char *)src; + int r = 0; + while (cnt-- && (r = *d++ - *s++) == 0) ; + return r; +} + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) { + while (*str && *str != chr) str++; + return *str; +} + + + +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +#if _FS_REENTRANT + +static +BOOL lock_fs ( + FATFS *fs /* File system object */ +) +{ + return ff_req_grant(fs->sobj); +} + + +static +void unlock_fs ( + FATFS *fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (res != FR_NOT_ENABLED && + res != FR_INVALID_DRIVE && + res != FR_INVALID_OBJECT && + res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT move_window ( + FATFS *fs, /* File system object */ + DWORD sector /* Sector number to make apperance in the fs->win[] */ +) /* Move to zero only writes back dirty window */ +{ + DWORD wsect; + + + wsect = fs->winsect; + if (wsect != sector) { /* Changed current window */ +#if !_FS_READONLY + if (fs->wflag) { /* Write back dirty window if needed */ + if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK) + return FR_DISK_ERR; + fs->wflag = 0; + if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */ + BYTE nf; + for (nf = fs->n_fats; nf > 1; nf--) { /* Refrect the change to all FAT copies */ + wsect += fs->sects_fat; + disk_write(fs->drive, fs->win, wsect, 1); + } + } + } +#endif + if (sector) { + if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK) + return FR_DISK_ERR; + fs->winsect = sector; + } + } + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Clean-up cached data */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */ + FATFS *fs /* File system object */ +) +{ + FRESULT res; + + + res = move_window(fs, 0); + if (res == FR_OK) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { + fs->winsect = 0; + mem_set(fs->win, 0, 512); + ST_WORD(fs->win+BS_55AA, 0xAA55); + ST_DWORD(fs->win+FSI_LeadSig, 0x41615252); + ST_DWORD(fs->win+FSI_StrucSig, 0x61417272); + ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust); + ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust); + disk_write(fs->drive, fs->win, fs->fsi_sector, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drive, CTRL_SYNC, (void*)NULL) != RES_OK) + res = FR_DISK_ERR; + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + + +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Interal error, Else:Cluster status */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to get the link information */ +) +{ + UINT wc, bc; + DWORD fsect; + + + if (clst < 2 || clst >= fs->max_clust) /* Range check */ + return 1; + + fsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = clst; bc += bc / 2; + if (move_window(fs, fsect + (bc / SS(fs)))) break; + wc = fs->win[bc & (SS(fs) - 1)]; bc++; + if (move_window(fs, fsect + (bc / SS(fs)))) break; + wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8; + return (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (move_window(fs, fsect + (clst / (SS(fs) / 2)))) break; + return LD_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)]); + + case FS_FAT32 : + if (move_window(fs, fsect + (clst / (SS(fs) / 4)))) break; + return LD_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF; + } + + return 0xFFFFFFFF; /* An error occured at the disk I/O layer */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY + +FRESULT put_fat ( + FATFS *fs, /* File system object */ + DWORD clst, /* Cluster# to be changed in range of 2 to fs->max_clust - 1 */ + DWORD val /* New value to mark the cluster */ +) +{ + UINT bc; + BYTE *p; + DWORD fsect; + FRESULT res; + + + if (clst < 2 || clst >= fs->max_clust) { /* Range check */ + res = FR_INT_ERR; + + } else { + fsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = clst; bc += bc / 2; + res = move_window(fs, fsect + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->wflag = 1; + res = move_window(fs, fsect + (bc / SS(fs))); + if (res != FR_OK) break; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + res = move_window(fs, fsect + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + ST_WORD(&fs->win[((WORD)clst * 2) & (SS(fs) - 1)], (WORD)val); + break; + + case FS_FAT32 : + res = move_window(fs, fsect + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + ST_DWORD(&fs->win[((WORD)clst * 4) & (SS(fs) - 1)], val); + break; + + default : + res = FR_INT_ERR; + } + fs->wflag = 1; + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT remove_chain ( + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to remove a chain from */ +) +{ + FRESULT res; + DWORD nxt; + + + if (clst < 2 || clst >= fs->max_clust) { /* Check the range of cluster# */ + res = FR_INT_ERR; + + } else { + res = FR_OK; + while (clst < fs->max_clust) { /* Not a last link? */ + nxt = get_fat(fs, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */ + if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */ + res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */ + if (res != FR_OK) break; + if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */ + fs->free_clust++; + fs->fsi_flag = 1; + } + clst = nxt; /* Next cluster */ + } + } + + return res; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch or Create a cluster chain */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to stretch. 0 means create a new chain. */ +) +{ + DWORD cs, ncl, scl, mcl; + + + mcl = fs->max_clust; + if (clst == 0) { /* Create new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (scl == 0 || scl >= mcl) scl = 1; + } + else { /* Stretch existing chain */ + cs = get_fat(fs, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* It is an invalid cluster */ + if (cs < mcl) return cs; /* It is already followed by next cluster */ + scl = clst; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= mcl) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free custer */ + } + cs = get_fat(fs, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 0xFFFFFFFF || cs == 1)/* An error occured */ + return cs; + if (ncl == scl) return 0; /* No free custer */ + } + + if (put_fat(fs, ncl, 0x0FFFFFFF)) /* Mark the new cluster "in use" */ + return 0xFFFFFFFF; + if (clst != 0) { /* Link it to the previous one if needed */ + if (put_fat(fs, clst, ncl)) + return 0xFFFFFFFF; + } + + fs->last_clust = ncl; /* Update FSINFO */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; + fs->fsi_flag = 1; + } + + return ncl; /* Return new cluster number */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + + +DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */ + FATFS *fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Seek directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_seek ( + eDIR *dj, /* Pointer to directory object */ + WORD idx /* Directory index number */ +) +{ + DWORD clst; + WORD ic; + + + dj->index = idx; + clst = dj->sclust; + if (clst == 1 || clst >= dj->fs->max_clust) /* Check start cluster range */ + return FR_INT_ERR; + if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */ + clst = dj->fs->dirbase; + + if (clst == 0) { /* Static table */ + dj->clust = clst; + if (idx >= dj->fs->n_rootdir) /* Index is out of range */ + return FR_INT_ERR; + dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32); /* Sector# */ + } + else { /* Dynamic table */ + ic = SS(dj->fs) / 32 * dj->fs->csize; /* Entries per cluster */ + while (idx >= ic) { /* Follow cluster chain */ + clst = get_fat(dj->fs, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= dj->fs->max_clust) /* Reached to end of table or int error */ + return FR_INT_ERR; + idx -= ic; + } + dj->clust = clst; + dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32); /* Sector# */ + } + + dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32; /* Ptr to the entry in the sector */ + + return FR_OK; /* Seek succeeded */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not streach */ + eDIR *dj, /* Pointer to directory object */ + BOOL streach /* FALSE: Do not streach table, TRUE: Streach table if needed */ +) +{ + DWORD clst; + WORD i; + + + i = dj->index + 1; + if (!i || !dj->sect) /* Report EOT when index has reached 65535 */ + return FR_NO_FILE; + + if (!(i % (SS(dj->fs) / 32))) { /* Sector changed? */ + dj->sect++; /* Next sector */ + + if (dj->clust == 0) { /* Static table */ + if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */ + return FR_NO_FILE; + } + else { /* Dynamic table */ + if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(dj->fs, dj->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + if (clst >= dj->fs->max_clust) { /* When it reached end of dynamic table */ +#if !_FS_READONLY + BYTE c; + if (!streach) return FR_NO_FILE; /* When do not streach, report EOT */ + clst = create_chain(dj->fs, dj->clust); /* Streach cluster chain */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; + /* Clean-up streached table */ + if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */ + mem_set(dj->fs->win, 0, SS(dj->fs)); /* Clear window buffer */ + dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */ + for (c = 0; c < dj->fs->csize; c++) { /* Fill the new cluster with 0 */ + dj->fs->wflag = 1; + if (move_window(dj->fs, 0)) return FR_DISK_ERR; + dj->fs->winsect++; + } + dj->fs->winsect -= c; /* Rewind window address */ +#else + return FR_NO_FILE; /* Report EOT */ +#endif + } + dj->clust = clst; /* Initialize data for new cluster */ + dj->sect = clust2sect(dj->fs, clst); + } + } + } + + dj->index = i; + dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN chars in the directory entry */ + + +static +BOOL cmp_lfn ( /* TRUE:Matched, FALSE:Not matched */ + WCHAR *lfnbuf, /* Pointer to the LFN to be compared */ + BYTE *dir /* Pointer to the directory entry containing a part of LFN */ +) +{ + int i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13; /* Get offset in the LFN buffer */ + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + wc = ff_wtoupper(uc); /* Convert it to upper case */ + if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */ + return FALSE; /* Not matched */ + } else { + if (uc != 0xFFFF) return FALSE; /* Check filler */ + } + } while (++s < 13); /* Repeat until all chars in the entry are checked */ + + if ((dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i]) /* Last segment matched but different length */ + return FALSE; + + return TRUE; /* The part of LFN matched */ +} + + + +static +BOOL pick_lfn ( /* TRUE:Succeeded, FALSE:Buffer overflow */ + WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */ + BYTE *dir /* Pointer to the directory entry */ +) +{ + int i, s; + WCHAR wc, uc; + + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + s = 0; wc = 1; + do { + uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */ + if (wc) { /* Last char has not been processed */ + if (i >= _MAX_LFN) return FALSE; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return FALSE; /* Check filler */ + } + } while (++s < 13); /* Read all character in the entry */ + + if (dir[LDIR_Ord] & 0x40) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return FALSE; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return TRUE; +} + + +#if !_FS_READONLY +static +void fit_lfn ( + const WCHAR *lfnbuf, /* Pointer to the LFN buffer */ + BYTE *dir, /* Pointer to the directory entry */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* SFN sum */ +) +{ + int i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set check sum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + ST_WORD(dir+LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective char */ + ST_WORD(dir+LfnOfs[s], wc); /* Put it */ + if (!wc) wc = 0xFFFF; /* Padding chars following last char */ + } while (++s < 13); + if (wc == 0xFFFF || !lfnbuf[i]) ord |= 0x40; /* Bottom LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Create numbered name */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +void gen_numname ( + BYTE *dst, /* Pointer to genartated SFN */ + const BYTE *src, /* Pointer to source SFN to be modified */ + const WCHAR *lfn, /* Pointer to LFN */ + WORD num /* Sequense number */ +) +{ + char ns[8]; + int i, j; + + + mem_cpy(dst, src, 11); + + if (num > 5) { /* On many collisions, generate a hash number instead of sequencial number */ + do num = (num >> 1) + (num << 15) + (WORD)*lfn++; while (*lfn); + } + + /* itoa */ + i = 7; + do { + ns[i--] = (num % 10) + '0'; + num /= 10; + } while (num); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Calculate sum of an SFN */ +/*-----------------------------------------------------------------------*/ +#if _USE_LFN +static +BYTE sum_sfn ( + const BYTE *dir /* Ptr to directory entry */ +) +{ + BYTE sum = 0; + int n = 11; + + do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n); + return sum; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( + eDIR *dj /* Pointer to the directory object linked to the file name */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord, sum; +#endif + + res = dir_seek(dj, 0); /* Rewind directory object */ + if (res != FR_OK) return res; + +#if _USE_LFN + ord = sum = 0xFF; +#endif + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == 0xE5 || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (dj->lfn) { + if (c & 0x40) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= 0xBF; ord = c; /* LFN start order */ + dj->lfn_idx = dj->index; + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */ + ord = 0xFF; dj->lfn_idx = 0xFFFF; /* Reset LFN sequence */ + if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break; /* SFN matched? */ + } + } +#else /* Non LFN configuration */ + if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +FRESULT dir_read ( + eDIR *dj /* Pointer to the directory object that pointing the entry to be read */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN + BYTE a, ord = 0xFF, sum = 0xFF; +#endif + + res = FR_NO_FILE; + while (dj->sect) { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + dir = dj->dir; /* Ptr to the directory entry of current index */ + c = dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN /* LFN configuration */ + a = dir[DIR_Attr] & AM_MASK; + if (c == 0xE5 || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & 0x40) { /* Is it start of LFN sequence? */ + sum = dir[LDIR_Chksum]; + c &= 0xBF; ord = c; + dj->lfn_idx = dj->index; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */ + dj->lfn_idx = 0xFFFF; /* It has no LFN. */ + break; + } + } +#else /* Non LFN configuration */ + if (c != 0xE5 && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */ + break; +#endif + res = dir_next(dj, FALSE); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dj->sect = 0; + + return res; +} +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */ + eDIR *dj /* Target directory with object name to be created */ +) +{ + FRESULT res; + BYTE c, *dir; +#if _USE_LFN /* LFN configuration */ + WORD n, ne, is; + BYTE sn[12], *fn, sum; + WCHAR *lfn; + + + fn = dj->fn; lfn = dj->lfn; + mem_cpy(sn, fn, 12); + + if (_FS_RPATH && (sn[NS] & NS_DOT)) return FR_INVALID_NAME; /* Cannot create dot entry */ + + if (sn[NS] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + fn[NS] = 0; dj->lfn = NULL; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(fn, sn, lfn, n); /* Generate a numbered name */ + res = dir_find(dj); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + fn[NS] = sn[NS]; dj->lfn = lfn; + } + + if (sn[NS] & NS_LFN) { /* When LFN is to be created, reserve reserve an SFN + LFN entries. */ + for (ne = 0; lfn[ne]; ne++) ; + ne = (ne + 25) / 13; + } else { /* Otherwise reserve only an SFN entry. */ + ne = 1; + } + + /* Reserve contiguous entries */ + res = dir_seek(dj, 0); + if (res != FR_OK) return res; + n = is = 0; + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; /* Check the entry status */ + if (c == 0xE5 || c == 0) { /* Is it a blank entry? */ + if (n == 0) is = dj->index; /* First index of the contigulus entry */ + if (++n == ne) break; /* A contiguous entry that requiered count is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dj, TRUE); /* Next entry with table streach */ + } while (res == FR_OK); + + if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */ + res = dir_seek(dj, is); + if (res == FR_OK) { + sum = sum_sfn(dj->fn); /* Sum of the SFN tied to the LFN */ + ne--; + do { /* Store LFN entries in bottom first */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum); + dj->fs->wflag = 1; + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK && --ne); + } + } + +#else /* Non LFN configuration */ + res = dir_seek(dj, 0); + if (res == FR_OK) { + do { /* Find a blank entry for the SFN */ + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + c = *dj->dir; + if (c == 0xE5 || c == 0) break; /* Is it a blank entry? */ + res = dir_next(dj, TRUE); /* Next entry with table streach */ + } while (res == FR_OK); + } +#endif + + if (res == FR_OK) { /* Initialize the SFN entry */ + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + dir = dj->dir; + mem_set(dir, 0, 32); /* Clean the entry */ + mem_cpy(dir, dj->fn, 11); /* Put SFN */ + dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */ + dj->fs->wflag = 1; + } + } + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY && !_FS_MINIMIZE +static +FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */ + eDIR *dj /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; +#if _USE_LFN /* LFN configuration */ + WORD i; + + i = dj->index; /* SFN index */ + res = dir_seek(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx)); /* Goto the SFN or top of the LFN entries */ + if (res == FR_OK) { + do { + res = move_window(dj->fs, dj->sect); + if (res != FR_OK) break; + *dj->dir = 0xE5; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + if (dj->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */ + res = dir_next(dj, FALSE); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } + +#else /* Non LFN configuration */ + res = dir_seek(dj, dj->index); + if (res == FR_OK) { + res = move_window(dj->fs, dj->sect); + if (res == FR_OK) { + *dj->dir = 0xE5; /* Mark the entry "deleted" */ + dj->fs->wflag = 1; + } + } +#endif + + return res; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( + eDIR *dj, /* Pointer to the directory object */ + const XCHAR **path /* Pointer to pointer to the segment in the path string */ +) +{ +#ifdef _EXCVT + static const BYTE cvt[] = _EXCVT; +#endif + +#if _USE_LFN /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + int i, ni, si, di; + const XCHAR *p; + + /* Create LFN in Unicode */ + si = di = 0; + p = *path; + lfn = dj->lfn; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */ + if (di >= _MAX_LFN) /* Reject too long name */ + return FR_INVALID_NAME; +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* If it is a DBC 1st byte */ + b = p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(b)) /* Reject invalid code for DBC */ + return FR_INVALID_NAME; + w = (w << 8) + b; + } + w = ff_convert(w, 1); /* Convert OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */ + return FR_INVALID_NAME; + lfn[di++] = w; /* Store the Unicode char */ + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ +#if _FS_RPATH + if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */ + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { + lfn[di] = 0; + for (i = 0; i < 11; i++) + dj->fn[i] = (i < di) ? '.' : ' '; + dj->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Strip trailing spaces and dots */ + w = lfn[di - 1]; + if (w != ' ' && w != '.') break; + di--; + } + if (!di) return FR_INVALID_NAME; /* Reject null string */ + + lfn[di] = 0; /* LFN is created */ + + /* Create SFN in directory form */ + mem_set(dj->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + b = i = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN char */ + if (!w) break; /* Break on enf of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII char */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = cvt[w - 0x80]; /* Convert extended char to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* Double byte char */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dj->fn[i++] = (BYTE)(w >> 8); + } else { /* Single byte char */ + if (!w || chk_chr("+,;[=]", w)) { /* Replace illegal chars for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN; /* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dj->fn[i++] = (BYTE)w; + } + + if (dj->fn[0] == 0xE5) dj->fn[0] = 0x05; /* If the first char collides with deleted mark, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */ + cf |= NS_LFN; + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended char, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dj->fn[NS] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* Non-LFN configuration */ + BYTE b, c, d, *sfn; + int ni, si, i; + const char *p; + + /* Create file name in directory form */ + sfn = dj->fn; + mem_set(sfn, ' ', 11); + si = i = b = 0; ni = 8; + p = *path; +#if _FS_RPATH + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = &p[si]; /* Rerurn pointer to the next segment */ + sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */ + return FR_OK; + } +#endif + for (;;) { + c = p[si++]; + if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */ + if (c == '.' || i >= ni) { + if (ni != 8 || c != '.') return FR_INVALID_NAME; + i = 8; ni = 11; + b <<= 2; continue; + } + if (c >= 0x80) { /* Extended char */ +#ifdef _EXCVT + c = cvt[c - 0x80]; /* Convert extend char (SBCS) */ +#else + b |= 3; /* Eliminate NT flag if ext char is exist */ +#if !_DF1S /* ASCII only cfg */ + return FR_INVALID_NAME; +#endif +#endif + } + if (IsDBCS1(c)) { /* DBC 1st byte? */ + d = p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */ + return FR_INVALID_NAME; + sfn[i++] = c; + sfn[i++] = d; + } else { /* Single byte code */ + if (chk_chr(" \"*+,[=]|\x7F", c)) /* Reject illegal chrs for SFN */ + return FR_INVALID_NAME; + if (IsUpper(c)) { /* ASCII large capital? */ + b |= 2; + } else { + if (IsLower(c)) { /* ASCII small capital? */ + b |= 1; c -= 0x20; + } + } + sfn[i++] = c; + } + } + *path = &p[si]; /* Rerurn pointer to the next segment */ + c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */ + + if (!i) return FR_INVALID_NAME; /* Reject null string */ + if (sfn[0] == 0xE5) sfn[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */ + + if (ni == 8) b <<= 2; + if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Filename has only small capital) */ + + sfn[NS] = c; /* Store NT flag, File name is created */ + + return FR_OK; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ +#if _FS_MINIMIZE <= 1 +static +void get_fileinfo ( /* No return code */ + eDIR *dj, /* Pointer to the directory object */ + FILINFO *fno /* Pointer to the file information to be filled */ +) +{ + int i; + BYTE c, nt, *dir; + char *p; + + + p = fno->fname; + if (dj->sect) { + dir = dj->dir; + nt = dir[DIR_NTres]; /* NT flag */ + for (i = 0; i < 8; i++) { /* Copy name body */ + c = dir[i]; + if (c == ' ') break; + if (c == 0x05) c = 0xE5; + if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20; + *p++ = c; + } + if (dir[8] != ' ') { /* Copy name extension */ + *p++ = '.'; + for (i = 8; i < 11; i++) { + c = dir[i]; + if (c == ' ') break; + if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20; + *p++ = c; + } + } + fno->fattrib = dir[DIR_Attr]; /* Attribute */ + fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */ + fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */ + fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */ + } + *p = 0; + +#if _USE_LFN + if (fno->lfname) { + XCHAR *tp = fno->lfname; + WCHAR w, *lfn; + + i = 0; + if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */ + lfn = dj->lfn; + while ((w = *lfn++) != 0) { /* Get an LFN char */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM conversion */ + if (!w) { i = 0; break; } /* Could not convert, no LFN */ + if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC */ + tp[i++] = (XCHAR)(w >> 8); +#endif + if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overrun, no LFN */ + tp[i++] = (XCHAR)w; + } + } + tp[i] = 0; /* Terminator */ + } +#endif +} +#endif /* _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + eDIR *dj, /* Directory object to return last directory and found object */ + const XCHAR *path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE *dir, last; + + + while (!_USE_LFN && *path == ' ') path++; /* Skip leading spaces */ +#if _FS_RPATH + if (*path == '/' || *path == '\\') { /* There is a heading separator */ + path++; dj->sclust = 0; /* Strip it and start from the root dir */ + } else { /* No heading saparator */ + dj->sclust = dj->fs->cdir; /* Start from the current dir */ + } +#else + if (*path == '/' || *path == '\\') /* Strip heading separator if exist */ + path++; + dj->sclust = 0; /* Start from the root dir */ +#endif + + if ((UINT)*path < ' ') { /* Null path means the start directory itself */ + res = dir_seek(dj, 0); + dj->dir = NULL; + + } else { /* Follow path */ + for (;;) { + res = create_name(dj, &path); /* Get a segment */ + if (res != FR_OK) break; + res = dir_find(dj); /* Find it */ + last = *(dj->fn+NS) & NS_LAST; + if (res != FR_OK) { /* Could not find the object */ + if (res == FR_NO_FILE && !last) + res = FR_NO_PATH; + break; + } + if (last) break; /* Last segment match. Function completed. */ + dir = dj->dir; /* There is next segment. Follow the sub directory */ + if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */ + res = FR_NO_PATH; break; + } + dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load boot record and check if it is an FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record, 3:Error */ + FATFS *fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK) /* Load boot record */ + return 3; + if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */ + return 2; + + if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */ + return 0; + if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146) + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Make sure that the file system is valid */ +/*-----------------------------------------------------------------------*/ + + +FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occured */ + const XCHAR **path, /* Pointer to pointer to the path name (drive number) */ + FATFS **rfs, /* Pointer to pointer to the found file system object */ + BYTE chk_wp /* !=0: Check media write protection for write access */ +) +{ + BYTE fmt, *tbl; + UINT vol; + DSTATUS stat; + DWORD bsect, fsize, tsect, mclst; + const XCHAR *p = *path; + FATFS *fs; + + /* Get logical drive number from the path name */ + vol = p[0] - '0'; /* Is there a drive number? */ + if (vol <= 9 && p[1] == ':') { /* Found a drive number, get and strip it */ + p += 2; *path = p; /* Return pointer to the path name */ + } else { /* No drive number is given */ +#if _FS_RPATH + vol = Drive; /* Use current drive */ +#else + vol = 0; /* Use drive 0 */ +#endif + } + + /* Check if the logical drive is valid or not */ + if (vol >= _DRIVES) /* Is the drive number valid? */ + return FR_INVALID_DRIVE; + *rfs = fs = FatFs[vol]; /* Returen pointer to the corresponding file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock file system */ + + if (fs->fs_type) { /* If the logical drive has been mounted */ + stat = disk_status(fs->drive); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized (has not been changed), */ +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + return FR_OK; /* The file system object is valid */ + } + } + + /* The logical drive must be mounted. Following code attempts to mount the volume */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drive = (BYTE)LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drive); /* Initialize low level disk I/O layer */ + if (stat & STA_NOINIT) /* Check if the drive is ready */ + return FR_NOT_READY; +#if _MAX_SS != 512 /* Get disk sector size if needed */ + if (disk_ioctl(fs->drive, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS) + return FR_NO_FILESYSTEM; +#endif +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + /* Search FAT partition on the drive */ + fmt = check_fs(fs, bsect = 0); /* Check sector 0 as an SFD format */ + if (fmt == 1) { /* Not an FAT boot record, it may be patitioned */ + /* Check a partition listed in top of the partition table */ + tbl = &fs->win[MBR_Table + LD2PT(vol) * 16]; /* Partition table */ + if (tbl[4]) { /* Is the partition existing? */ + bsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */ + fmt = check_fs(fs, bsect); /* Check the partition */ + } + } + if (fmt == 3) return FR_DISK_ERR; + if (fmt || LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* No valid FAT patition is found */ + return FR_NO_FILESYSTEM; + + /* Initialize the file system object */ + fsize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */ + if (!fsize) fsize = LD_DWORD(fs->win+BPB_FATSz32); + fs->sects_fat = fsize; + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + fsize *= fs->n_fats; /* (Number of sectors in FAT area) */ + fs->fatbase = bsect + LD_WORD(fs->win+BPB_RsvdSecCnt); /* FAT start sector (lba) */ + fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Nmuber of root directory entries */ + tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */ + if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32); + fs->max_clust = mclst = (tsect /* Last cluster# + 1 (Number of clusters + 2) */ + - LD_WORD(fs->win+BPB_RsvdSecCnt) - fsize - fs->n_rootdir / (SS(fs)/32) + ) / fs->csize + 2; + + fmt = FS_FAT12; /* Determine the FAT sub type */ + if (mclst >= 0xFF7) fmt = FS_FAT16; /* Number of clusters >= 0xFF5 */ + if (mclst >= 0xFFF7) fmt = FS_FAT32; /* Number of clusters >= 0xFFF5 */ + + if (fmt == FS_FAT32) + fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */ + else + fs->dirbase = fs->fatbase + fsize; /* Root directory start sector (lba) */ + fs->database = fs->fatbase + fsize + fs->n_rootdir / (SS(fs)/32); /* Data start sector (lba) */ + +#if !_FS_READONLY + /* Initialize allocation information */ + fs->free_clust = 0xFFFFFFFF; + fs->wflag = 0; + /* Get fsinfo if needed */ + if (fmt == FS_FAT32) { + fs->fsi_flag = 0; + fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo); + if (disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK && + LD_WORD(fs->win+BS_55AA) == 0xAA55 && + LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 && + LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) { + fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free); + fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count); + } + } +#endif + fs->fs_type = fmt; /* FAT sub-type */ + fs->winsect = 0; /* Invalidate sector cache */ +#if _FS_RPATH + fs->cdir = 0; /* Current directory (root dir) */ +#endif + fs->id = ++Fsid; /* File system mount ID */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/dir object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ + FATFS *fs, /* Pointer to the file system object */ + WORD id /* Member id of the target object to be checked */ +) +{ + if (!fs || !fs->fs_type || fs->id != id) + return FR_INVALID_OBJECT; + + ENTER_FF(fs); /* Lock file system */ + + if (disk_status(fs->drive) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Locical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + BYTE vol, /* Logical drive number to be mounted/unmounted */ + FATFS *fs /* Pointer to new file system object (NULL for unmount)*/ +) +{ + FATFS *rfs; + + + if (vol >= _DRIVES) /* Check if the drive number is valid */ + return FR_INVALID_DRIVE; + rfs = FatFs[vol]; /* Get current fs object */ + + if (rfs) { +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR; +#endif + rfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL *fp, /* Pointer to the blank file object */ + const XCHAR *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + eDIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + fp->fs = NULL; /* Clear file object */ +#if !_FS_READONLY + mode &= (FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW); + res = chk_mounted(&path, &dj.fs, (BYTE)(mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW))); +#else + mode &= FA_READ; + res = chk_mounted(&path, &dj.fs, 0); +#endif + if (res != FR_OK) LEAVE_FF(dj.fs, res); + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + +#if !_FS_READONLY + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + DWORD ps, cl; + + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) /* There is no file to open, create a new entry */ + res = dir_register(&dj); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + mode |= FA_CREATE_ALWAYS; + dir = dj.dir; /* Created entry (SFN entry) */ + } + else { /* Any object is already existing */ + if (mode & FA_CREATE_NEW) /* Cannot create new */ + LEAVE_FF(dj.fs, FR_EXIST); + dir = dj.dir; + if (!dir || (dir[DIR_Attr] & (AM_RDO | AM_DIR))) /* Cannot overwrite it (R/O or DIR) */ + LEAVE_FF(dj.fs, FR_DENIED); + if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero on over write mode */ + cl = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); /* Get start cluster */ + ST_WORD(dir+DIR_FstClusHI, 0); /* cluster = 0 */ + ST_WORD(dir+DIR_FstClusLO, 0); + ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */ + dj.fs->wflag = 1; + ps = dj.fs->winsect; /* Remove the cluster chain */ + if (cl) { + res = remove_chain(dj.fs, cl); + if (res) LEAVE_FF(dj.fs, res); + dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */ + } + res = move_window(dj.fs, ps); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + } + } + if (mode & FA_CREATE_ALWAYS) { + dir[DIR_Attr] = 0; /* Reset attribute */ + ps = get_fattime(); + ST_DWORD(dir+DIR_CrtTime, ps); /* Created time */ + dj.fs->wflag = 1; + mode |= FA__WRITTEN; /* Set file changed flag */ + } + } + /* Open an existing file */ + else { +#endif /* !_FS_READONLY */ + if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */ + dir = dj.dir; + if (!dir || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */ + LEAVE_FF(dj.fs, FR_NO_FILE); +#if !_FS_READONLY + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + LEAVE_FF(dj.fs, FR_DENIED); + } + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#endif + fp->flag = mode; /* File access mode */ + fp->org_clust = /* File start cluster */ + ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */ + fp->fptr = 0; fp->csect = 255; /* File pointer */ + fp->dsect = 0; + fp->fs = dj.fs; fp->id = dj.fs->id; /* Owner file system object of the file */ + + LEAVE_FF(dj.fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + void *buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT *br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + DWORD clst, sect, remain; + UINT rcnt, cc; + BYTE *rbuff = (BYTE *)buff; + + + *br = 0; /* Initialize bytes read */ + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data transferred */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_fat(fp->fs, fp->curr_clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector offset in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect; + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - fp->csect; + if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 +#if _FS_TINY + if (fp->fs->wflag && fp->fs->winsect - sect < cc) /* Replace one of the read sectors with cached data if it contains a dirty sector */ + mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs)); +#else + if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc) /* Replace one of the read sectors with cached data if it contains a dirty sector */ + mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs)); +#endif +#endif + fp->csect += (BYTE)cc; /* Next sector address in the cluster */ + rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write sector I/O buffer if needed */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if (disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + fp->csect++; /* Next sector address in the cluster */ + } + rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */ + if (rcnt > btr) rcnt = btr; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#else + mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */ +#endif + } + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL *fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT *bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + DWORD clst, sect; + UINT wcnt, cc; + const BYTE *wbuff = (const BYTE *)buff; + + + *bw = 0; /* Initialize bytes written */ + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + if (fp->fsize + btw < fp->fsize) btw = 0; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data transferred */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->org_clust; /* Follow from the origin */ + if (clst == 0) /* When there is no cluster chain, */ + fp->org_clust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of the file */ + clst = create_chain(fp->fs, fp->curr_clust); /* Follow or streach cluster chain */ + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector address in the cluster */ + } +#if _FS_TINY + if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write back data buffer prior to following direct transfer */ + ABORT(fp->fs, FR_DISK_ERR); +#else + if (fp->flag & FA__DIRTY) { /* Write back data buffer prior to following direct transfer */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect; + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - fp->csect; + if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#if _FS_TINY + if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */ + mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->fs->wflag = 0; + } +#else + if (fp->dsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs)); + fp->flag &= ~FA__DIRTY; + } +#endif + fp->csect += (BYTE)cc; /* Next sector address in the cluster */ + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->fsize) { /* Avoid silly buffer filling at growing edge */ + if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR); + fp->fs->winsect = sect; + } +#else + if (fp->dsect != sect) { /* Fill sector buffer with file data */ + if (fp->fptr < fp->fsize && + disk_read(fp->fs->drive, fp->buf, sect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + } +#endif + fp->dsect = sect; + fp->csect++; /* Next sector address in the cluster */ + } + wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Put partial sector into file I/O buffer */ + if (wcnt > btw) wcnt = btw; +#if _FS_TINY + if (move_window(fp->fs, fp->dsect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->fs->wflag = 1; +#else + mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */ + fp->flag |= FA__DIRTY; +#endif + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file changed flag */ + + LEAVE_FF(fp->fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD tim; + BYTE *dir; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ +#if !_FS_TINY /* Write-back dirty buffer */ + if (fp->flag & FA__DIRTY) { + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + LEAVE_FF(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + /* Update the directory entry */ + res = move_window(fp->fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + ST_DWORD(dir+DIR_FileSize, fp->fsize); /* Update file size */ + ST_WORD(dir+DIR_FstClusLO, fp->org_clust); /* Update start cluster */ + ST_WORD(dir+DIR_FstClusHI, fp->org_clust >> 16); + tim = get_fattime(); /* Updated time */ + ST_DWORD(dir+DIR_WrtTime, tim); + fp->flag &= ~FA__WRITTEN; + fp->fs->wflag = 1; + res = sync(fp->fs); + } + } + } + + LEAVE_FF(fp->fs, res); +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + + +#if _FS_READONLY + res = validate(fp->fs, fp->id); + if (res == FR_OK) fp->fs = NULL; + LEAVE_FF(fp->fs, res); +#else + res = f_sync(fp); + if (res == FR_OK) fp->fs = NULL; + return res; +#endif +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Current Drive/Directory */ +/*-----------------------------------------------------------------------*/ + +#if _FS_RPATH + +FRESULT f_chdrive ( + BYTE drv /* Drive number */ +) +{ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; + + Drive = drv; + + return FR_OK; +} + + + + +FRESULT f_chdir ( + const XCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + dir = dj.dir; /* Pointer to the entry */ + if (!dir) { + dj.fs->cdir = 0; /* No entry (root dir) */ + } else { + if (dir[DIR_Attr] & AM_DIR) /* Reached to the dir */ + dj.fs->cdir = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + else + res = FR_NO_PATH; /* Could not reach the dir (it is a file) */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj.fs, res); +} + +#endif + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL *fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + FRESULT res; + DWORD clst, bcs, nsect, ifptr; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); +// if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +//#if !_FS_READONLY /* LAJ: Always clip offset with file size */ +// && !(fp->flag & FA_WRITE) +//#endif + if (ofs > fp->fsize) + ofs = fp->fsize; /* LAJ: Always clip offset with file size */ + + ifptr = fp->fptr; + fp->fptr = nsect = 0; fp->csect = 255; + if (ofs > 0) { + bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->curr_clust; + } else { /* When seek to back cluster, */ + clst = fp->org_clust; /* start from the first cluster */ +#if !_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(fp->fs, 0); + if (clst == 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->org_clust = clst; + } +#endif + fp->curr_clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + clst = create_chain(fp->fs, clst); /* Force streached if in write mode */ + if (clst == 0) { /* When disk gets full, clip file size */ + ofs = bcs; break; + } + } else +#endif + clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */ + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fp->fs->max_clust) ABORT(fp->fs, FR_INT_ERR); + fp->curr_clust = clst; + fp->fptr += bcs; + ofs -= bcs; + } + fp->fptr += ofs; + fp->csect = (BYTE)(ofs / SS(fp->fs)); /* Sector offset in the cluster */ + if (ofs % SS(fp->fs)) { + nsect = clust2sect(fp->fs, clst); /* Current sector */ + if (!nsect) ABORT(fp->fs, FR_INT_ERR); + nsect += fp->csect; + fp->csect++; + } + } + } + if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) { +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty buffer if needed */ + if (disk_write(fp->fs->drive, fp->buf, fp->dsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); + fp->flag &= ~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drive, fp->buf, nsect, 1) != RES_OK) + ABORT(fp->fs, FR_DISK_ERR); +#endif + fp->dsect = nsect; + } +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set changed flag if the file size is extended */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + + LEAVE_FF(fp->fs, res); +} + + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directroy Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + eDIR *dj, /* Pointer to directory object to create */ + const XCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj->fs, 0); + if (res == FR_OK) { + INITBUF((*dj), sfn, lfn); + res = follow_path(dj, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + dir = dj->dir; + if (dir) { /* It is not the root dir */ + if (dir[DIR_Attr] & AM_DIR) { /* The object is a directory */ + dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + } else { /* The object is not a directory */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + dj->id = dj->fs->id; + res = dir_seek(dj, 0); /* Rewind dir */ + } + } + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(dj->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entry in Sequense */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + eDIR *dj, /* Pointer to the open directory object */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + NAMEBUF(sfn, lfn); + + + res = validate(dj->fs, dj->id); /* Check validity of the object */ + if (res == FR_OK) { + INITBUF((*dj), sfn, lfn); + if (!fno) { + res = dir_seek(dj, 0); + } else { + res = dir_read(dj); + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dj, fno); /* Get the object information */ + res = dir_next(dj, FALSE); /* Increment index for next */ + if (res == FR_NO_FILE) { + dj->sect = 0; + res = FR_OK; + } + } + } + } + + LEAVE_FF(dj->fs, res); +} + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const XCHAR *path, /* Pointer to the file path */ + FILINFO *fno /* Pointer to file information to return */ +) +{ + FRESULT res; + eDIR dj; + NAMEBUF(sfn, lfn); + + + res = chk_mounted(&path, &dj.fs, 0); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follwo completed */ + if (dj.dir) /* Found an object */ + get_fileinfo(&dj, fno); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + } + + LEAVE_FF(dj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const XCHAR *path, /* Pointer to the logical drive number (root dir) */ + DWORD *nclst, /* Pointer to the variable to return number of free clusters */ + FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */ +) +{ + FRESULT res; + DWORD n, clst, sect, stat; + UINT i; + BYTE fat, *p; + + + /* Get drive number */ + res = chk_mounted(&path, fatfs, 0); + if (res != FR_OK) LEAVE_FF(*fatfs, res); + + /* If number of free cluster is valid, return it without cluster scan. */ + if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) { + *nclst = (*fatfs)->free_clust; + LEAVE_FF(*fatfs, FR_OK); + } + + /* Get number of free clusters */ + fat = (*fatfs)->fs_type; + n = 0; + if (fat == FS_FAT12) { + clst = 2; + do { + stat = get_fat(*fatfs, clst); + if (stat == 0xFFFFFFFF) LEAVE_FF(*fatfs, FR_DISK_ERR); + if (stat == 1) LEAVE_FF(*fatfs, FR_INT_ERR); + if (stat == 0) n++; + } while (++clst < (*fatfs)->max_clust); + } else { + clst = (*fatfs)->max_clust; + sect = (*fatfs)->fatbase; + i = 0; p = 0; + do { + if (!i) { + res = move_window(*fatfs, sect++); + if (res != FR_OK) + LEAVE_FF(*fatfs, res); + p = (*fatfs)->win; + i = SS(*fatfs); + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; i -= 2; + } else { + if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++; + p += 4; i -= 4; + } + } while (--clst); + } + (*fatfs)->free_clust = n; + if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1; + *nclst = n; + + LEAVE_FF(*fatfs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD ncl; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check abort flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_WRITE)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + if (fp->fsize > fp->fptr) { + fp->fsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA__WRITTEN; + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(fp->fs, fp->org_clust); + fp->org_clust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(fp->fs, fp->curr_clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fp->fs->max_clust) { + res = put_fat(fp->fs, fp->curr_clust, 0x0FFFFFFF); + if (res == FR_OK) res = remove_chain(fp->fs, ncl); + } + } + } + if (res != FR_OK) fp->flag |= FA__ERROR; + + LEAVE_FF(fp->fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const XCHAR *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + eDIR dj, sdj; + NAMEBUF(sfn, lfn); + BYTE *dir; + DWORD dclst; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res != FR_OK) LEAVE_FF(dj.fs, res); /* Follow failed */ + + dir = dj.dir; + if (!dir) /* Is it the root directory? */ + LEAVE_FF(dj.fs, FR_INVALID_NAME); + if (dir[DIR_Attr] & AM_RDO) /* Is it a R/O object? */ + LEAVE_FF(dj.fs, FR_DENIED); + dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO); + + if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */ + if (dclst < 2) LEAVE_FF(dj.fs, FR_INT_ERR); + mem_cpy(&sdj, &dj, sizeof(eDIR)); /* Check if the sub-dir is empty or not */ + sdj.sclust = dclst; + res = dir_seek(&sdj, 2); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + res = dir_read(&sdj); + if (res == FR_OK) res = FR_DENIED; /* Not empty sub-dir */ + if (res != FR_NO_FILE) LEAVE_FF(dj.fs, res); + } + + res = dir_remove(&dj); /* Remove directory entry */ + if (res == FR_OK) { + if (dclst) + res = remove_chain(dj.fs, dclst); /* Remove the cluster chain */ + if (res == FR_OK) res = sync(dj.fs); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const XCHAR *path /* Pointer to the directory path */ +) +{ + FRESULT res; + eDIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir, n; + DWORD dsect, dclst, pclst, tim; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any file or directory is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res != FR_NO_FILE) /* Any error occured */ + LEAVE_FF(dj.fs, res); + + dclst = create_chain(dj.fs, 0); /* Allocate a new cluster for new directory table */ + res = FR_OK; + if (dclst == 0) res = FR_DENIED; + if (dclst == 1) res = FR_INT_ERR; + if (dclst == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) + res = move_window(dj.fs, 0); + if (res != FR_OK) LEAVE_FF(dj.fs, res); + dsect = clust2sect(dj.fs, dclst); + + dir = dj.fs->win; /* Initialize the new directory table */ + mem_set(dir, 0, SS(dj.fs)); + mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + tim = get_fattime(); + ST_DWORD(dir+DIR_WrtTime, tim); + ST_WORD(dir+DIR_FstClusLO, dclst); + ST_WORD(dir+DIR_FstClusHI, dclst >> 16); + mem_cpy(dir+32, dir, 32); /* Create ".." entry */ + dir[33] = '.'; + pclst = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pclst == dj.fs->dirbase) + pclst = 0; + ST_WORD(dir+32+DIR_FstClusLO, pclst); + ST_WORD(dir+32+DIR_FstClusHI, pclst >> 16); + for (n = 0; n < dj.fs->csize; n++) { /* Write dot entries and clear left sectors */ + dj.fs->winsect = dsect++; + dj.fs->wflag = 1; + res = move_window(dj.fs, 0); + if (res) LEAVE_FF(dj.fs, res); + mem_set(dir, 0, SS(dj.fs)); + } + + res = dir_register(&dj); + if (res != FR_OK) { + remove_chain(dj.fs, dclst); + } else { + dir = dj.dir; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(dir+DIR_WrtTime, tim); /* Crated time */ + ST_WORD(dir+DIR_FstClusLO, dclst); /* Table start cluster */ + ST_WORD(dir+DIR_FstClusHI, dclst >> 16); + dj.fs->wflag = 1; + res = sync(dj.fs); + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change File Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const XCHAR *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + eDIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Is it a root directory? */ + res = FR_INVALID_NAME; + } else { /* File or sub directory */ + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const XCHAR *path, /* Pointer to the file/directory name */ + const FILINFO *fno /* Pointer to the timestamp to be set */ +) +{ + FRESULT res; + eDIR dj; + NAMEBUF(sfn, lfn); + BYTE *dir; + + + res = chk_mounted(&path, &dj.fs, 1); + if (res == FR_OK) { + INITBUF(dj, sfn, lfn); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + if (res == FR_OK) { + dir = dj.dir; + if (!dir) { /* Root directory */ + res = FR_INVALID_NAME; + } else { /* File or sub-directory */ + ST_WORD(dir+DIR_WrtTime, fno->ftime); + ST_WORD(dir+DIR_WrtDate, fno->fdate); + dj.fs->wflag = 1; + res = sync(dj.fs); + } + } + } + + LEAVE_FF(dj.fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const XCHAR *path_old, /* Pointer to the old name */ + const XCHAR *path_new /* Pointer to the new name */ +) +{ + FRESULT res; + eDIR dj_old, dj_new; + NAMEBUF(sfn, lfn); + BYTE buf[21], *dir; + DWORD dw; + + + INITBUF(dj_old, sfn, lfn); + res = chk_mounted(&path_old, &dj_old.fs, 1); + if (res == FR_OK) { + dj_new.fs = dj_old.fs; + res = follow_path(&dj_old, path_old); /* Check old object */ + if (_FS_RPATH && res == FR_OK && (dj_old.fn[NS] & NS_DOT)) + res = FR_INVALID_NAME; + } + if (res != FR_OK) LEAVE_FF(dj_old.fs, res); /* The old object is not found */ + + if (!dj_old.dir) LEAVE_FF(dj_old.fs, FR_NO_FILE); /* Is root dir? */ + mem_cpy(buf, dj_old.dir+DIR_Attr, 21); /* Save the object information */ + + mem_cpy(&dj_new, &dj_old, sizeof(eDIR)); + res = follow_path(&dj_new, path_new); /* Check new object */ + if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */ + if (res == FR_NO_FILE) { /* Is it a valid path and no name collision? */ + res = dir_register(&dj_new); /* Register the new object */ + if (res == FR_OK) { + dir = dj_new.dir; /* Copy object information into new entry */ + mem_cpy(dir+13, buf+2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + dj_old.fs->wflag = 1; + if (dir[DIR_Attr] & AM_DIR) { /* Update .. entry in the directory if needed */ + dw = clust2sect(dj_new.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO)); + if (!dw) { + res = FR_INT_ERR; + } else { + res = move_window(dj_new.fs, dw); + dir = dj_new.fs->win+32; + if (res == FR_OK && dir[1] == '.') { + dw = (dj_new.fs->fs_type == FS_FAT32 && dj_new.sclust == dj_new.fs->dirbase) ? 0 : dj_new.sclust; + ST_WORD(dir+DIR_FstClusLO, dw); + ST_WORD(dir+DIR_FstClusHI, dw >> 16); + dj_new.fs->wflag = 1; + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj_old); /* Remove old entry */ + if (res == FR_OK) + res = sync(dj_old.fs); + } + } + } + + LEAVE_FF(dj_old.fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly (Available on only _FS_TINY cfg) */ +/*-----------------------------------------------------------------------*/ +#if _USE_FORWARD && _FS_TINY + +FRESULT f_forward ( + FIL *fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btr, /* Number of bytes to forward */ + UINT *bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + DWORD remain, clst, sect; + UINT rcnt; + + + *bf = 0; + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) LEAVE_FF(fp->fs, res); + if (fp->flag & FA__ERROR) /* Check error flag */ + LEAVE_FF(fp->fs, FR_INT_ERR); + if (!(fp->flag & FA_READ)) /* Check access mode */ + LEAVE_FF(fp->fs, FR_DENIED); + + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr && (*func)(NULL, 0); /* Repeat until all data transferred or stream becomes busy */ + fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_fat(fp->fs, fp->curr_clust); + if (clst <= 1) ABORT(fp->fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR); + fp->curr_clust = clst; /* Update current cluster */ + fp->csect = 0; /* Reset sector address in the cluster */ + } + fp->csect++; /* Next sector address in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust); /* Get current data sector */ + if (!sect) ABORT(fp->fs, FR_INT_ERR); + sect += fp->csect - 1; + if (move_window(fp->fs, sect)) /* Move sector window */ + ABORT(fp->fs, FR_DISK_ERR); + fp->dsect = sect; + rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */ + if (rcnt > btr) rcnt = btr; + rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt); + if (!rcnt) ABORT(fp->fs, FR_INT_ERR); + } + + LEAVE_FF(fp->fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create File System on the Drive */ +/*-----------------------------------------------------------------------*/ +#define N_ROOTDIR 512 /* Multiple of 32 and <= 2048 */ +#define N_FATS 1 /* 1 or 2 */ +#define MAX_SECTOR 131072000UL /* Maximum partition size */ +#define MIN_SECTOR 2000UL /* Minimum partition size */ + + +FRESULT f_mkfs ( + BYTE drv, /* Logical drive number */ + BYTE partition, /* Partitioning rule 0:FDISK, 1:SFD */ + WORD allocsize /* Allocation unit size [bytes] */ +) +{ + static const DWORD sstbl[] = { 2048000, 1024000, 512000, 256000, 128000, 64000, 32000, 16000, 8000, 4000, 0 }; + static const WORD cstbl[] = { 32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512 }; + BYTE fmt, m, *tbl; + DWORD b_part, b_fat, b_dir, b_data; /* Area offset (LBA) */ + DWORD n_part, n_rsv, n_fat, n_dir; /* Area size */ + DWORD n_clst, d, n; + WORD as; + FATFS *fs; + DSTATUS stat; + + + /* Check validity of the parameters */ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; + if (partition >= 2) return FR_MKFS_ABORTED; + + /* Check mounted drive and clear work area */ + fs = FatFs[drv]; + if (!fs) return FR_NOT_ENABLED; + fs->fs_type = 0; + drv = LD2PD(drv); + + /* Get disk statics */ + stat = disk_initialize(drv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; +#if _MAX_SS != 512 /* Get disk sector size */ + if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK + || SS(fs) > _MAX_SS) + return FR_MKFS_ABORTED; +#endif + if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR) + return FR_MKFS_ABORTED; + if (n_part > MAX_SECTOR) n_part = MAX_SECTOR; + b_part = (!partition) ? 63 : 0; /* Boot sector */ + n_part -= b_part; + for (d = 512; d <= 32768U && d != allocsize; d <<= 1) ; /* Check validity of the allocation unit size */ + if (d != allocsize) allocsize = 0; + if (!allocsize) { /* Auto selection of cluster size */ + d = n_part; + for (as = SS(fs); as > 512U; as >>= 1) d >>= 1; + for (n = 0; d < sstbl[n]; n++) ; + allocsize = cstbl[n]; + } + if (allocsize < SS(fs)) allocsize = SS(fs); + + allocsize /= SS(fs); /* Number of sectors per cluster */ + + /* Pre-compute number of clusters and FAT type */ + n_clst = n_part / allocsize; + fmt = FS_FAT12; + if (n_clst >= 0xFF5) fmt = FS_FAT16; + if (n_clst >= 0xFFF5) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + switch (fmt) { + case FS_FAT12: + n_fat = ((n_clst * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs); + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + case FS_FAT16: + n_fat = ((n_clst * 2) + 4 + SS(fs) - 1) / SS(fs); + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + default: + n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 33 - partition; + n_dir = 0; + } + b_fat = b_part + n_rsv; /* FATs start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */ + b_data = b_dir + n_dir; /* Data start sector */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED; + n = (b_data + n - 1) & ~(n - 1); + n_fat += (n - b_data) / N_FATS; + /* b_dir and b_data are no longer used below */ + + /* Determine number of cluster and final check of validity of the FAT type */ + n_clst = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize; + if ( (fmt == FS_FAT16 && n_clst < 0xFF5) + || (fmt == FS_FAT32 && n_clst < 0xFFF5)) + return FR_MKFS_ABORTED; + + /* Create partition table if needed */ + if (!partition) { + DWORD n_disk = b_part + n_part; + + mem_set(fs->win, 0, SS(fs)); + tbl = fs->win+MBR_Table; + ST_DWORD(tbl, 0x00010180); /* Partition start in CHS */ + if (n_disk < 63UL * 255 * 1024) { /* Partition end in CHS */ + n_disk = n_disk / 63 / 255; + tbl[7] = (BYTE)n_disk; + tbl[6] = (BYTE)((n_disk >> 2) | 63); + } else { + ST_WORD(&tbl[6], 0xFFFF); + } + tbl[5] = 254; + if (fmt != FS_FAT32) /* System ID */ + tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06; + else + tbl[4] = 0x0c; + ST_DWORD(tbl+8, 63); /* Partition start in LBA */ + ST_DWORD(tbl+12, n_part); /* Partition size in LBA */ + ST_WORD(tbl+64, 0xAA55); /* Signature */ + if (disk_write(drv, fs->win, 0, 1) != RES_OK) + return FR_DISK_ERR; + partition = 0xF8; + } else { + partition = 0xF0; + } + + /* Create boot record */ + tbl = fs->win; /* Clear buffer */ + mem_set(tbl, 0, SS(fs)); + ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB); /* Boot code (jmp $, nop) */ + ST_WORD(tbl+BPB_BytsPerSec, SS(fs)); /* Sector size */ + tbl[BPB_SecPerClus] = (BYTE)allocsize; /* Sectors per cluster */ + ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + ST_WORD(tbl+BPB_RootEntCnt, SS(fs) / 32 * n_dir); /* Number of rootdir entries */ + if (n_part < 0x10000) { /* Number of total sectors */ + ST_WORD(tbl+BPB_TotSec16, n_part); + } else { + ST_DWORD(tbl+BPB_TotSec32, n_part); + } + tbl[BPB_Media] = partition; /* Media descripter */ + ST_WORD(tbl+BPB_SecPerTrk, 63); /* Number of sectors per track */ + ST_WORD(tbl+BPB_NumHeads, 255); /* Number of heads */ + ST_DWORD(tbl+BPB_HiddSec, b_part); /* Hidden sectors */ + n = get_fattime(); /* Use current time as a VSN */ + if (fmt != FS_FAT32) { + ST_DWORD(tbl+BS_VolID, n); /* Volume serial number */ + ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of secters per FAT */ + tbl[BS_DrvNum] = 0x80; /* Drive number */ + tbl[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab, "NO NAME FAT ", 19); /* Volume lavel, FAT signature */ + } else { + ST_DWORD(tbl+BS_VolID32, n); /* Volume serial number */ + ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of secters per FAT */ + ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory cluster (2) */ + ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (bs+1) */ + ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (bs+6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(tbl+BS_VolLab32, "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */ + } + ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature */ + if (SS(fs) > 512U) { + ST_WORD(tbl+SS(fs)-2, 0xAA55); + } + if (disk_write(drv, tbl, b_part+0, 1) != RES_OK) + return FR_DISK_ERR; + if (fmt == FS_FAT32) + disk_write(drv, tbl, b_part+6, 1); + + /* Initialize FAT area */ + for (m = 0; m < N_FATS; m++) { + mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + if (fmt != FS_FAT32) { + n = (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00; + n |= partition; + ST_DWORD(tbl, n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + ST_DWORD(tbl+0, 0xFFFFFFF8); /* Reserve cluster #0-1 (FAT32) */ + ST_DWORD(tbl+4, 0xFFFFFFFF); + ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ + } + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + mem_set(tbl, 0, SS(fs)); /* Following FAT entries are filled by zero */ + for (n = 1; n < n_fat; n++) { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + } + } + + /* Initialize Root directory */ + m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir); + do { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_DISK_ERR; + } while (--m); + + /* Create FSInfo record if needed */ + if (fmt == FS_FAT32) { + ST_WORD(tbl+BS_55AA, 0xAA55); + ST_DWORD(tbl+FSI_LeadSig, 0x41615252); + ST_DWORD(tbl+FSI_StrucSig, 0x61417272); + ST_DWORD(tbl+FSI_Free_Count, n_clst - 1); + ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF); + disk_write(drv, tbl, b_part+1, 1); + disk_write(drv, tbl, b_part+7, 1); + } + + return (disk_ioctl(drv, CTRL_SYNC, (void*)NULL) == RES_OK) ? FR_OK : FR_DISK_ERR; +} + +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ +char* f_gets ( + char* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer */ + FIL* fil /* Pointer to the file object */ +) +{ + int i = 0; + char *p = buff; + UINT rc; + + + while (i < len - 1) { /* Read bytes until buffer gets filled */ + f_read(fil, p, 1, &rc); + if (rc != 1) break; /* Break when no data to read */ +#if _USE_STRFUNC >= 2 + if (*p == '\r') continue; /* Strip '\r' */ +#endif + i++; + if (*p++ == '\n') break; /* Break when reached end of line */ + } + *p = 0; + return i ? buff : NULL; /* When no data read (eof or error), return with error. */ +} + + + +#if !_FS_READONLY +#include <stdarg.h> +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ +int f_putc ( + int chr, /* A character to be output */ + FIL* fil /* Ponter to the file object */ +) +{ + UINT bw; + char c; + + +#if _USE_STRFUNC >= 2 + if (chr == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */ +#endif + if (!fil) { /* Special value may be used to switch the destination to any other device */ + /* put_console(chr); */ + return chr; + } + c = (char)chr; + f_write(fil, &c, 1, &bw); /* Write a byte to the file */ + return bw ? chr : EOF; /* Return the result */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ +int f_puts ( + const char* str, /* Pointer to the string to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + int n; + + + for (n = 0; *str; str++, n++) { + if (f_putc(*str, fil) == EOF) return EOF; + } + return n; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ +int f_printf ( + FIL* fil, /* Pointer to the file object */ + const char* str, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + UCHAR c, f, r; + ULONG val; + char s[16]; + int i, w, res, cc; + + + va_start(arp, str); + + for (cc = res = 0; cc != EOF; res += cc) { + c = *str++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape cahracter */ + cc = f_putc(c, fil); + if (cc != EOF) cc = 1; + continue; + } + w = f = 0; + c = *str++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *str++; + } + while (c >= '0' && c <= '9') { /* Precision */ + w = w * 10 + (c - '0'); + c = *str++; + } + if (c == 'l') { /* Prefix: Size is long int */ + f |= 2; c = *str++; + } + if (c == 's') { /* Type is string */ + cc = f_puts(va_arg(arp, char*), fil); + continue; + } + if (c == 'c') { /* Type is character */ + cc = f_putc(va_arg(arp, int), fil); + if (cc != EOF) cc = 1; + continue; + } + r = 0; + if (c == 'd') r = 10; /* Type is signed decimal */ + if (c == 'u') r = 10; /* Type is unsigned decimal */ + if (c == 'X') r = 16; /* Type is unsigned hexdecimal */ + if (r == 0) break; /* Unknown type */ + if (f & 2) { /* Get the value */ + val = (ULONG)va_arg(arp, long); + } else { + val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int); + } + /* Put numeral string */ + if (c == 'd') { + if (val & 0x80000000) { + val = 0 - val; + f |= 4; + } + } + i = sizeof(s) - 1; s[i] = 0; + do { + c = (UCHAR)(val % r + '0'); + if (c > '9') c += 7; + s[--i] = c; + val /= r; + } while (i && val); + if (i && (f & 4)) s[--i] = '-'; + w = sizeof(s) - 1 - w; + while (i && i > w) s[--i] = (f & 1) ? '0' : ' '; + cc = f_puts(&s[i], fil); + } + + va_end(arp); + return (cc == EOF) ? cc : res; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CHAN_FS/ff.h Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,596 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.07e (C)ChaN, 2009 +/----------------------------------------------------------------------------/ +/ FatFs module is a generic FAT file system module for small embedded systems. +/ This is a free software that opened for education, research and commercial +/ developments under license policy of following trems. +/ +/ Copyright (C) 2009, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is NO WARRANTY. +/ * No restriction on use. You can use, modify and redistribute it for +/ personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY. +/ * Redistributions of source code must retain the above copyright notice. +/----------------------------------------------------------------------------*/ + +#ifndef _FATFS +#define _FATFS 0x007E + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONFIG +#error Wrong configuration file (ffconf.h). +#endif + + +/* DBCS code ranges and SBCS extend char conversion table */ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 720 /* Arabic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian (OEM) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \ + 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF} + +#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1253 /* Greek (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \ + 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF} + +#elif _CODE_PAGE == 1254 /* Turkish (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F} + +#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1256 /* Arabic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 1257 /* Baltic (Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF} + +#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \ + 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#define _DF1S 0 + +#else +#error Unknown code page + +#endif + + + +/* Character code support macros */ + +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) + +#if _DF1S /* DBCS configuration */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* SBCS configuration */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + + +/* Definitions corresponds to multi partition */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ + +typedef struct _PARTITION { + BYTE pd; /* Physical drive# */ + BYTE pt; /* Partition # (0-3) */ +} PARTITION; + +extern +const PARTITION Drives[]; /* Logical drive# to physical location conversion table */ +#define LD2PD(drv) (Drives[drv].pd) /* Get physical drive# */ +#define LD2PT(drv) (Drives[drv].pt) /* Get partition# */ + +#else /* Single partition configuration */ + +#define LD2PD(drv) (drv) /* Physical drive# is equal to the logical drive# */ +#define LD2PT(drv) 0 /* Always mounts the 1st partition */ + +#endif + + + +/* Definitions corresponds to multiple sector size */ + +#if _MAX_SS == 512 /* Single sector size */ +#define SS(fs) 512U + +#elif _MAX_SS == 1024 || _MAX_SS == 2048 || _MAX_SS == 4096 /* Multiple sector size */ +#define SS(fs) ((fs)->s_size) + +#else +#error Sector size must be 512, 1024, 2048 or 4096. + +#endif + + + +/* Type of file name on FatFs API */ + +#if _LFN_UNICODE && _USE_LFN +typedef WCHAR XCHAR; /* Unicode */ +#else +typedef char XCHAR; /* SBCS, DBCS */ +#endif + + + +/* File system object structure */ + +typedef struct _FATFS_ { + BYTE fs_type; /* FAT sub type */ + BYTE drive; /* Physical drive number */ + BYTE csize; /* Number of sectors per cluster */ + BYTE n_fats; /* Number of FAT copies */ + BYTE wflag; /* win[] dirty flag (1:must be written back) */ + BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (0 on FAT32) */ +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if _MAX_SS != 512 + WORD s_size; /* Sector size */ +#endif +#if !_FS_READONLY + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ + DWORD fsi_sector; /* fsinfo sector */ +#endif +#if _FS_RPATH + DWORD cdir; /* Current directory (0:root)*/ +#endif + DWORD sects_fat; /* Sectors per fat */ + DWORD max_clust; /* Maximum cluster# + 1. Number of clusters is max_clust - 2 */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (Cluster# on FAT32) */ + DWORD database; /* Data start sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS];/* Disk access window for Directory/FAT */ +} FATFS; + + + +/* Directory object structure */ + +typedef struct _DIR_ { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + WORD index; /* Current read/write index number */ + DWORD sclust; /* Table start cluster (0:Static table) */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector */ + BYTE* dir; /* Pointer to the current SFN entry in the win[] */ + BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */ +#if _USE_LFN + WCHAR* lfn; /* Pointer to the LFN working buffer */ + WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */ +#endif +} eDIR; + + + +/* File object structure */ + +typedef struct _FIL_ { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE flag; /* File status flags */ + BYTE csect; /* Sector address in the cluster */ + DWORD fptr; /* File R/W pointer */ + DWORD fsize; /* File size */ + DWORD org_clust; /* File start cluster */ + DWORD curr_clust; /* Current cluster */ + DWORD dsect; /* Current data sector */ +#if !_FS_READONLY + DWORD dir_sect; /* Sector containing the directory entry */ + BYTE* dir_ptr; /* Ponter to the directory entry in the window */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS];/* File R/W buffer */ +#endif +} FIL; + + + +/* File status structure */ + +typedef struct _FILINFO_ { + DWORD fsize; /* File size */ + WORD fdate; /* Last modified date */ + WORD ftime; /* Last modified time */ + BYTE fattrib; /* Attribute */ + char fname[13]; /* Short file name (8.3 format) */ +#if _USE_LFN + XCHAR* lfname; /* Pointer to the LFN buffer */ + int lfsize; /* Size of LFN buffer [chrs] */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* 0 */ + FR_DISK_ERR, /* 1 */ + FR_INT_ERR, /* 2 */ + FR_NOT_READY, /* 3 */ + FR_NO_FILE, /* 4 */ + FR_NO_PATH, /* 5 */ + FR_INVALID_NAME, /* 6 */ + FR_DENIED, /* 7 */ + FR_EXIST, /* 8 */ + FR_INVALID_OBJECT, /* 9 */ + FR_WRITE_PROTECTED, /* 10 */ + FR_INVALID_DRIVE, /* 11 */ + FR_NOT_ENABLED, /* 12 */ + FR_NO_FILESYSTEM, /* 13 */ + FR_MKFS_ABORTED, /* 14 */ + FR_TIMEOUT /* 15 */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const XCHAR*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ +FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ +FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ +FRESULT f_close (FIL*); /* Close an open file object */ +FRESULT f_opendir (eDIR*, const XCHAR*); /* Open an existing directory */ +FRESULT f_readdir (eDIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const XCHAR*, FILINFO*); /* Get file status */ +FRESULT f_getfree (const XCHAR*, DWORD*, FATFS**); /* Get number of free clusters on the drive */ +FRESULT f_truncate (FIL*); /* Truncate file */ +FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ +FRESULT f_unlink (const XCHAR*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const XCHAR*); /* Create a new directory */ +FRESULT f_chmod (const XCHAR*, BYTE, BYTE); /* Change attriburte of the file/dir */ +FRESULT f_utime (const XCHAR*, const FILINFO*); /* Change timestamp of the file/dir */ +FRESULT f_rename (const XCHAR*, const XCHAR*); /* Rename/Move a file or directory */ +FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */ +FRESULT f_mkfs (BYTE, BYTE, WORD); /* Create a file system on the drive */ +FRESULT f_chdir (const XCHAR*); /* Change current directory */ +FRESULT f_chdrive (BYTE); /* Change current drive */ + +#if _USE_STRFUNC +int f_putc (int, FIL*); /* Put a character to the file */ +int f_puts (const char*, FIL*); /* Put a string to the file */ +int f_printf (FIL*, const char*, ...); /* Put a formatted string to the file */ +char* f_gets (char*, int, FIL*); /* Get a string from the file */ +#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0) +#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0) +#ifndef EOF +#define EOF -1 +#endif +#endif + + + +/*--------------------------------------------------------------*/ +/* User defined functions */ + +/* Real time clock */ +#if !_FS_READONLY +DWORD get_fattime (void); /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */ + /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */ +#endif + +/* Unicode - OEM code conversion */ +#if _USE_LFN +WCHAR ff_convert (WCHAR, UINT); +WCHAR ff_wtoupper (WCHAR); +#endif + +/* Sync functions */ +#if _FS_REENTRANT +BOOL ff_cre_syncobj(BYTE, _SYNC_t*); +BOOL ff_del_syncobj(_SYNC_t); +BOOL ff_req_grant(_SYNC_t); +void ff_rel_grant(_SYNC_t); +#endif + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#if _FS_READONLY == 0 +#define FA_WRITE 0x02 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#define FA__DIRTY 0x40 +#endif +#define FA__ERROR 0x80 + + +/* FAT sub type (FATFS.fs_type) */ + +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 + + +/* File attribute bits for directory entry */ + +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* FatFs refers the members in the FAT structures with byte offset instead +/ of structure member because there are incompatibility of the packing option +/ between various compilers. */ + +#define BS_jmpBoot 0 +#define BS_OEMName 3 +#define BPB_BytsPerSec 11 +#define BPB_SecPerClus 13 +#define BPB_RsvdSecCnt 14 +#define BPB_NumFATs 16 +#define BPB_RootEntCnt 17 +#define BPB_TotSec16 19 +#define BPB_Media 21 +#define BPB_FATSz16 22 +#define BPB_SecPerTrk 24 +#define BPB_NumHeads 26 +#define BPB_HiddSec 28 +#define BPB_TotSec32 32 +#define BS_55AA 510 + +#define BS_DrvNum 36 +#define BS_BootSig 38 +#define BS_VolID 39 +#define BS_VolLab 43 +#define BS_FilSysType 54 + +#define BPB_FATSz32 36 +#define BPB_ExtFlags 40 +#define BPB_FSVer 42 +#define BPB_RootClus 44 +#define BPB_FSInfo 48 +#define BPB_BkBootSec 50 +#define BS_DrvNum32 64 +#define BS_BootSig32 66 +#define BS_VolID32 67 +#define BS_VolLab32 71 +#define BS_FilSysType32 82 + +#define FSI_LeadSig 0 +#define FSI_StrucSig 484 +#define FSI_Free_Count 488 +#define FSI_Nxt_Free 492 + +#define MBR_Table 446 + +#define DIR_Name 0 +#define DIR_Attr 11 +#define DIR_NTres 12 +#define DIR_CrtTime 14 +#define DIR_CrtDate 16 +#define DIR_FstClusHI 20 +#define DIR_WrtTime 22 +#define DIR_WrtDate 24 +#define DIR_FstClusLO 26 +#define DIR_FileSize 28 +#define LDIR_Ord 0 +#define LDIR_Attr 11 +#define LDIR_Type 12 +#define LDIR_Chksum 13 +#define LDIR_FstClusLO 26 + + + +/*--------------------------------*/ +/* Multi-byte word access macros */ + +#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) +#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) +#else /* Use byte-by-byte access to the FAT structure */ +#define LD_WORD(ptr) (WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(((DWORD)*(BYTE*)((ptr)+3)<<24)|((DWORD)*(BYTE*)((ptr)+2)<<16)|((WORD)*(BYTE*)((ptr)+1)<<8)|*(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8) +#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24) +#endif + + +#endif /* _FATFS */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CHAN_FS/ffconf.h Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,166 @@ +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file R0.07e (C)ChaN, 2009 +/----------------------------------------------------------------------------/ +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/----------------------------------------------------------------------------*/ +#ifndef _FFCONFIG +#define _FFCONFIG 0x007E + + +/*---------------------------------------------------------------------------/ +/ Function and Buffer Configurations +/----------------------------------------------------------------------------*/ + +#define _FS_TINY 0 /* 0 or 1 */ +/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system +/ object instead of the sector buffer in the individual file object for file +/ data transfer. This reduces memory consumption 512 bytes each file object. */ + + +#define _FS_READONLY 0 /* 0 or 1 */ +/* Setting _FS_READONLY to 1 defines read only configuration. This removes +/ writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename, +/ f_truncate and useless f_getfree. */ + + +#define _FS_MINIMIZE 0 /* 0, 1, 2 or 3 */ +/* The _FS_MINIMIZE option defines minimization level to remove some functions. +/ +/ 0: Full function. +/ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename +/ are removed. +/ 2: f_opendir and f_readdir are removed in addition to level 1. +/ 3: f_lseek is removed in addition to level 2. */ + + +#define _USE_STRFUNC 1 /* 0, 1 or 2 */ +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + + +#define _USE_MKFS 0 /* 0 or 1 */ +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + + +#define _USE_FORWARD 0 /* 0 or 1 */ +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + + + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/----------------------------------------------------------------------------*/ + +#define _CODE_PAGE 437 +/* The _CODE_PAGE specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows) +/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows) +/ 949 - Korean (DBCS, OEM, Windows) +/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) +/ 1250 - Central Europe (Windows) +/ 1251 - Cyrillic (Windows) +/ 1252 - Latin 1 (Windows) +/ 1253 - Greek (Windows) +/ 1254 - Turkish (Windows) +/ 1255 - Hebrew (Windows) +/ 1256 - Arabic (Windows) +/ 1257 - Baltic (Windows) +/ 1258 - Vietnam (OEM, Windows) +/ 437 - U.S. (OEM) +/ 720 - Arabic (OEM) +/ 737 - Greek (OEM) +/ 775 - Baltic (OEM) +/ 850 - Multilingual Latin 1 (OEM) +/ 858 - Multilingual Latin 1 + Euro (OEM) +/ 852 - Latin 2 (OEM) +/ 855 - Cyrillic (OEM) +/ 866 - Russian (OEM) +/ 857 - Turkish (OEM) +/ 862 - Hebrew (OEM) +/ 874 - Thai (OEM, Windows) +/ 1 - ASCII only (Valid for non LFN cfg.) +*/ + + +#define _USE_LFN 1 /* 0, 1 or 2 */ +#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN. _MAX_LFN and _LFN_UNICODE have no effect. +/ 1: Enable LFN with static working buffer on the bss. NOT REENTRANT. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ +/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. When enable LFN, +/ two Unicode handling functions ff_convert() and ff_wtoupper() must be added +/ to the project. */ + + +#define _LFN_UNICODE 0 /* 0 or 1 */ +/* To switch the character code set on FatFs API to Unicode, +/ enable LFN feature and set _LFN_UNICODE to 1. +*/ + + +#define _FS_RPATH 0 /* 0 or 1 */ +/* When _FS_RPATH is set to 1, relative path feature is enabled and f_chdir, +/ f_chdrive function are available. +/ Note that output of the f_readdir fnction is affected by this option. */ + + + +/*---------------------------------------------------------------------------/ +/ Physical Drive Configurations +/----------------------------------------------------------------------------*/ + +#define _DRIVES 1 +/* Number of volumes (logical drives) to be used. */ + + +#define _MAX_SS 1024 /* 512, 1024, 2048 or 4096 */ +/* Maximum sector size to be handled. +/ Always set 512 for memory card and hard disk but a larger value may be +/ required for floppy disk (512/1024) and optical disk (512/2048). +/ When _MAX_SS is larger than 512, GET_SECTOR_SIZE command must be implememted +/ to the disk_ioctl function. */ + + +#define _MULTI_PARTITION 0 /* 0 or 1 */ +/* When _MULTI_PARTITION is set to 0, each volume is bound to the same physical +/ drive number and can mount only first primaly partition. When it is set to 1, +/ each volume is tied to the partitions listed in Drives[]. */ + + + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _WORD_ACCESS 0 /* 0 or 1 */ +/* The _WORD_ACCESS option defines which access method is used to the word +/ data on the FAT volume. +/ +/ 0: Byte-by-byte access. Always compatible with all platforms. +/ 1: Word access. Do not choose this unless following condition is met. +/ +/ When the byte order on the memory is big-endian or address miss-aligned +/ word access results incorrect behavior, the _WORD_ACCESS must be set to 0. +/ If it is not the case, the value can also be set to 1 to improve the +/ performance and code size. */ + + +#define _FS_REENTRANT 0 /* 0 or 1 */ +#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */ +#define _SYNC_t HANDLE /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */ +/* The _FS_REENTRANT option switches the reentrancy of the FatFs module. +/ +/ 0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect. +/ 1: Enable reentrancy. Also user provided synchronization handlers, +/ ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj +/ function must be added to the project. */ + + +#endif /* _FFCONFIG */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CHAN_FS/integer.h Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,39 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _INTEGER + +#if 0 +#include <windows.h> +#else + +/* These types must be 16-bit, 32-bit or larger integer */ +typedef int INT; +typedef unsigned int UINT; + +/* These types must be 8-bit integer */ + +#define CHAR char +//typedef signed char CHAR; +typedef unsigned char UCHAR; +typedef unsigned char BYTE; + +/* These types must be 16-bit integer */ +typedef short SHORT; +typedef unsigned short USHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types must be 32-bit integer */ +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long DWORD; + +/* Boolean type */ +typedef enum { FALSE = 0, TRUE } BOOL; + +#endif + +#define _INTEGER +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CHAN_FS/option/ccsbcs.c Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,538 @@ +/* (SBCS code pages) */ +/*------------------------------------------------------------------------*/ +/* 437 U.S. (OEM) +/ 720 Arabic (OEM) +/ 1256 Arabic (Windows) +/ 737 Greek (OEM) +/ 1253 Greek (Windows) +/ 1250 Central Europe (Windows) +/ 775 Baltic (OEM) +/ 1257 Baltic (Windows) +/ 850 Multilingual Latin 1 (OEM) +/ 852 Latin 2 (OEM) +/ 1252 Latin 1 (Windows) +/ 855 Cyrillic (OEM) +/ 1251 Cyrillic (Windows) +/ 866 Russian (OEM) +/ 857 Turkish (OEM) +/ 1254 Turkish (Windows) +/ 858 Multilingual Latin 1 + Euro (OEM) +/ 862 Hebrew (OEM) +/ 1255 Hebrew (Windows) +/ 874 Thai (OEM, Windows) +/ 1258 Vietnam (OEM, Windows) +*/ + +#include "../ff.h" + + +#if _CODE_PAGE == 437 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP437(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 720 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP720(0x80-0xFF) to Unicode conversion table */ + 0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9, + 0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642, + 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, + 0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0xO650, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 737 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP737(0x80-0xFF) to Unicode conversion table */ + 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, + 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, + 0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, + 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, + 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, + 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD, + 0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E, + 0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 775 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP775(0x80-0xFF) to Unicode conversion table */ + 0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107, + 0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A, + 0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4, + 0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6, + 0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118, + 0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D, + 0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B, + 0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144, + 0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019, + 0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E, + 0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 850 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP850(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, + 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 852 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP852(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7, + 0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106, + 0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A, + 0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E, + 0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A, + 0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE, + 0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, + 0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4, + 0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 855 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP855(0x80-0xFF) to Unicode conversion table */ + 0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404, + 0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408, + 0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C, + 0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A, + 0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414, + 0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438, + 0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E, + 0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580, + 0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443, + 0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116, + 0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D, + 0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 857 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP857(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000, + 0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 858 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP858(0x80-0xFF) to Unicode conversion table */ + 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, + 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5, + 0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, + 0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0, + 0x00A9, 0x2563, 0x2551, 0x2557, 0x2550, 0x00A2, 0x00A5, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4, + 0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE, + 0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00C6, 0x00CC, 0x2580, + 0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE, + 0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4, + 0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8, + 0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 862 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP862(0x80-0xFF) to Unicode conversion table */ + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192, + 0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, + 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, + 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, + 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 866 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP866(0x80-0xFF) to Unicode conversion table */ + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, + 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, + 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, + 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, + 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, + 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, + 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E, + 0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0 +}; + +#elif _CODE_PAGE == 874 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP874(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, + 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, + 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, + 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, + 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, + 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, + 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, + 0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F, + 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, + 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, + 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, + 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +#elif _CODE_PAGE == 1250 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1250(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A, + 0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B, + 0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C, + 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, + 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, + 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, + 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, + 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, + 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, + 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, + 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 +}; + +#elif _CODE_PAGE == 1251 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1251(0x80-0xFF) to Unicode conversion table */ + 0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021, + 0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F, + 0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2111, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F, + 0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7, + 0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407, + 0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7, + 0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042A, 0x042D, 0x042C, 0x042D, 0x042E, 0x042F, + 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, + 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, + 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, + 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F +}; + +#elif _CODE_PAGE == 1252 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1252(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x00DD, 0x00DE, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF +}; + +#elif _CODE_PAGE == 1253 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1253(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0000, 0x2039, 0x000C, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7, + 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, + 0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, + 0x03A8, 0x03A9, 0x03AA, 0x03AD, 0x03AC, 0x03AD, 0x03AE, 0x03AF, + 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, + 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000 +}; + +#elif _CODE_PAGE == 1254 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1254(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x210A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, + 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x0130, 0x015E, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, + 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF +}; + +#elif _CODE_PAGE == 1255 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1255(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7, + 0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF, + 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3, + 0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, + 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, + 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, + 0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000 +}; + +#elif _CODE_PAGE == 1256 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1256(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, + 0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA, + 0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F, + 0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, + 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, + 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7, + 0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0640, 0x0642, 0x0643, + 0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF, + 0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7, + 0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2 +} + +#elif _CODE_PAGE == 1257 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1257(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021, + 0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000, + 0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7, + 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, + 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, + 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, + 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, + 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, + 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, + 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, + 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, + 0x0173, 0x014E, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9 +}; + +#elif _CODE_PAGE == 1258 +#define _TBLDEF 1 +static +const WCHAR Tbl[] = { /* CP1258(0x80-0xFF) to Unicode conversion table */ + 0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, + 0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000, + 0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, + 0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178, + 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, + 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, + 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, + 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, + 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7, + 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF, + 0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7, + 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF, + 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7, + 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF, + 0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7, + 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF +}; + +#endif + + +#if !_TBLDEF || !_USE_LFN +#error This file is not needed in current configuration +#endif + + +WCHAR ff_convert ( /* Converted character, Returns zero on error */ + WCHAR src, /* Character code to be converted */ + UINT dir /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */ +) +{ + WCHAR c; + + + if (src < 0x80) { /* ASCII */ + c = src; + + } else { + if (dir) { /* OEMCP to Unicode */ + c = (src >= 0x100) ? 0 : Tbl[src - 0x80]; + + } else { /* Unicode to OEMCP */ + for (c = 0; c < 0x80; c++) { + if (src == Tbl[c]) break; + } + c = (c + 0x80) & 0xFF; + } + } + + return c; +} + + +WCHAR ff_wtoupper ( /* Upper converted character */ + WCHAR chr /* Input character */ +) +{ + static const WCHAR tbl_lower[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA1, 0x00A2, 0x00A3, 0x00A5, 0x00AC, 0x00AF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x0FF, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11B, 0x11D, 0x11F, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12B, 0x12D, 0x12F, 0x131, 0x133, 0x135, 0x137, 0x13A, 0x13C, 0x13E, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14B, 0x14D, 0x14F, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15B, 0x15D, 0x15F, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16B, 0x16D, 0x16F, 0x171, 0x173, 0x175, 0x177, 0x17A, 0x17C, 0x17E, 0x192, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x45E, 0x45F, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0 }; + static const WCHAR tbl_upper[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x21, 0xFFE0, 0xFFE1, 0xFFE5, 0xFFE2, 0xFFE3, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10A, 0x10C, 0x10E, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11A, 0x11C, 0x11E, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12A, 0x12C, 0x12E, 0x130, 0x132, 0x134, 0x136, 0x139, 0x13B, 0x13D, 0x13F, 0x141, 0x143, 0x145, 0x147, 0x14A, 0x14C, 0x14E, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15A, 0x15C, 0x15E, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16A, 0x16C, 0x16E, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17B, 0x17D, 0x191, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x40E, 0x40F, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, 0x216C, 0x216D, 0x216E, 0x216F, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0 }; + int i; + + + for (i = 0; tbl_lower[i] && chr != tbl_lower[i]; i++) ; + + return tbl_lower[i] ? tbl_upper[i] : chr; +}
--- a/FatFileSystem.lib Wed Apr 10 13:31:35 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/mbed_unsupported/code/FatFileSystem/#333d6e93e58f
--- a/MSCFileSystem.lib Wed Apr 10 13:31:35 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/chris/code/MSCFileSystem/#dcc326e4d358
--- a/SPI_TFTx2.lib Wed Apr 10 13:31:35 2013 +0000 +++ b/SPI_TFTx2.lib Sun Jul 21 23:59:00 2013 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/TickTock/code/SPI_TFTx2/#ff509eb02e37 +http://mbed.org/users/TickTock/code/SPI_TFTx2/#4ad7de7d2220
--- a/TFT_fonts.lib Wed Apr 10 13:31:35 2013 +0000 +++ b/TFT_fonts.lib Sun Jul 21 23:59:00 2013 +0000 @@ -1,1 +1,1 @@ -http://mbed.org/users/TickTock/code/TFT_fonts/#49b5ef080441 +http://mbed.org/users/TickTock/code/TFT_fonts/#af72b26394d8
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHostLite/usbhost_cpu.h Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,35 @@ +/* +************************************************************************************************************** +* NXP USB Host Stack +* +* (c) Copyright 2008, NXP SemiConductors +* (c) Copyright 2008, OnChip Technologies LLC +* All Rights Reserved +* +* www.nxp.com +* www.onchiptech.com +* +* File : usbhost_cpu.h +* Programmer(s) : Ravikanth.P +* Version : +* +************************************************************************************************************** +*/ + +#ifndef USBHOST_CPU_H +#define USBHOST_CPU_H + +/* +************************************************************************************************************** +* TYPE DEFINITIONS OF DATA TYPES +************************************************************************************************************** +*/ + +typedef unsigned int USB_INT32U; +typedef signed int USB_INT32S; +typedef unsigned short USB_INT16U; +typedef signed short USB_INT16S; +typedef unsigned char USB_INT08U; +typedef signed char USB_INT08S; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHostLite/usbhost_err.h Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,63 @@ +/* +************************************************************************************************************** +* NXP USB Host Stack +* +* (c) Copyright 2008, NXP SemiConductors +* (c) Copyright 2008, OnChip Technologies LLC +* All Rights Reserved +* +* www.nxp.com +* www.onchiptech.com +* +* File : usbhost_err.h +* Programmer(s) : Ravikanth.P +* Version : +* +************************************************************************************************************** +*/ + +#ifndef USBHOST_ERR_H +#define USBHOST_ERR_H + + +/* +************************************************************************************************************** +* GENERAL DEFINITIONS +************************************************************************************************************** +*/ + +#define OK 0 +#define MATCH_FOUND 0 + +/* +************************************************************************************************************** +* HOST CONTROLLER SPECIFIC ERROR CODES +************************************************************************************************************** +*/ + +#define ERR_TD_FAIL -1 + +/* +************************************************************************************************************** +* MASS STORAGE SPECIFIC ERROR CODES +************************************************************************************************************** +*/ + +#define ERR_MS_CMD_FAILED -10 +#define ERR_BAD_CONFIGURATION -11 +#define ERR_NO_MS_INTERFACE -12 + +/* +************************************************************************************************************** +* FAT SPECIFIC ERROR CODES +************************************************************************************************************** +*/ + +#define MATCH_NOT_FOUND -20 +#define ERR_FAT_NOT_SUPPORTED -21 +#define ERR_OPEN_LIMIT_REACHED -22 +#define ERR_INVALID_BOOT_SIG -23 +#define ERR_INVALID_BOOT_SEC -24 +#define ERR_ROOT_DIR_FULL -25 + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHostLite/usbhost_inc.h Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,39 @@ +/* +************************************************************************************************************** +* NXP USB Host Stack +* +* (c) Copyright 2008, NXP SemiConductors +* (c) Copyright 2008, OnChip Technologies LLC +* All Rights Reserved +* +* www.nxp.com +* www.onchiptech.com +* +* File : usbhost_inc.h +* Programmer(s) : Ravikanth.P +* Version : +* +************************************************************************************************************** +*/ + +#ifndef USBHOST_INC_H +#define USBHOST_INC_H + +/* +************************************************************************************************************** +* INCLUDE HEADER FILES +************************************************************************************************************** +*/ + +#include "usbhost_cpu.h" +#include "usbhost_err.h" +#include "usbhost_lpc17xx.h" +#include "usbhost_ms.h" +#include "mbed.h" + + +#ifdef TARGET_LPC2368 +#error "There is no USB host on the LPC2368!" +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHostLite/usbhost_lpc17xx.c Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,820 @@ +/* +************************************************************************************************************** +* NXP USB Host Stack +* +* (c) Copyright 2008, NXP SemiConductors +* (c) Copyright 2008, OnChip Technologies LLC +* All Rights Reserved +* +* www.nxp.com +* www.onchiptech.com +* +* File : usbhost_lpc17xx.c +* Programmer(s) : Ravikanth.P +* Version : +* +************************************************************************************************************** +*/ + +/* +************************************************************************************************************** +* INCLUDE HEADER FILES +************************************************************************************************************** +*/ + +#include "usbhost_lpc17xx.h" + +/* +************************************************************************************************************** +* GLOBAL VARIABLES +************************************************************************************************************** +*/ +int gUSBConnected; + +volatile USB_INT32U HOST_RhscIntr = 0; /* Root Hub Status Change interrupt */ +volatile USB_INT32U HOST_WdhIntr = 0; /* Semaphore to wait until the TD is submitted */ +volatile USB_INT08U HOST_TDControlStatus = 0; +volatile HCED *EDCtrl; /* Control endpoint descriptor structure */ +volatile HCED *EDBulkIn; /* BulkIn endpoint descriptor structure */ +volatile HCED *EDBulkOut; /* BulkOut endpoint descriptor structure */ +volatile HCTD *TDHead; /* Head transfer descriptor structure */ +volatile HCTD *TDTail; /* Tail transfer descriptor structure */ +volatile HCCA *Hcca; /* Host Controller Communications Area structure */ + USB_INT16U *TDBufNonVol; /* Identical to TDBuffer just to reduce compiler warnings */ +volatile USB_INT08U *TDBuffer; /* Current Buffer Pointer of transfer descriptor */ + +// USB host structures +// AHB SRAM block 1 +#define HOSTBASEADDR 0x2007C000 +// reserve memory for the linker +static USB_INT08U HostBuf[0x200] __attribute__((at(HOSTBASEADDR))); +/* +************************************************************************************************************** +* DELAY IN MILLI SECONDS +* +* Description: This function provides a delay in milli seconds +* +* Arguments : delay The delay required +* +* Returns : None +* +************************************************************************************************************** +*/ + +void Host_DelayMS (USB_INT32U delay) +{ + volatile USB_INT32U i; + + + for (i = 0; i < delay; i++) { + Host_DelayUS(1000); + } +} + +/* +************************************************************************************************************** +* DELAY IN MICRO SECONDS +* +* Description: This function provides a delay in micro seconds +* +* Arguments : delay The delay required +* +* Returns : None +* +************************************************************************************************************** +*/ + +void Host_DelayUS (USB_INT32U delay) +{ + volatile USB_INT32U i; + + + for (i = 0; i < (4 * delay); i++) { /* This logic was tested. It gives app. 1 micro sec delay */ + ; + } +} + +// bits of the USB/OTG clock control register +#define HOST_CLK_EN (1<<0) +#define DEV_CLK_EN (1<<1) +#define PORTSEL_CLK_EN (1<<3) +#define AHB_CLK_EN (1<<4) + +// bits of the USB/OTG clock status register +#define HOST_CLK_ON (1<<0) +#define DEV_CLK_ON (1<<1) +#define PORTSEL_CLK_ON (1<<3) +#define AHB_CLK_ON (1<<4) + +// we need host clock, OTG/portsel clock and AHB clock +#define CLOCK_MASK (HOST_CLK_EN | PORTSEL_CLK_EN | AHB_CLK_EN) + +/* +************************************************************************************************************** +* INITIALIZE THE HOST CONTROLLER +* +* Description: This function initializes lpc17xx host controller +* +* Arguments : None +* +* Returns : +* +************************************************************************************************************** +*/ +void Host_Init (void) +{ + PRINT_Log("In Host_Init\n"); + NVIC_DisableIRQ(USB_IRQn); /* Disable the USB interrupt source */ + + // turn on power for USB + LPC_SC->PCONP |= (1UL<<31); + // Enable USB host clock, port selection and AHB clock + LPC_USB->USBClkCtrl |= CLOCK_MASK; + // Wait for clocks to become available + while ((LPC_USB->USBClkSt & CLOCK_MASK) != CLOCK_MASK) + ; + + // it seems the bits[0:1] mean the following + // 0: U1=device, U2=host + // 1: U1=host, U2=host + // 2: reserved + // 3: U1=host, U2=device + // NB: this register is only available if OTG clock (aka "port select") is enabled!! + // since we don't care about port 2, set just bit 0 to 1 (U1=host) + LPC_USB->OTGStCtrl |= 1; + + // now that we've configured the ports, we can turn off the portsel clock + LPC_USB->USBClkCtrl &= ~PORTSEL_CLK_EN; + + // power pins are not connected on mbed, so we can skip them + /* P1[18] = USB_UP_LED, 01 */ + /* P1[19] = /USB_PPWR, 10 */ + /* P1[22] = USB_PWRD, 10 */ + /* P1[27] = /USB_OVRCR, 10 */ + /*LPC_PINCON->PINSEL3 &= ~((3<<4) | (3<<6) | (3<<12) | (3<<22)); + LPC_PINCON->PINSEL3 |= ((1<<4)|(2<<6) | (2<<12) | (2<<22)); // 0x00802080 + */ + + // configure USB D+/D- pins + /* P0[29] = USB_D+, 01 */ + /* P0[30] = USB_D-, 01 */ + LPC_PINCON->PINSEL1 &= ~((3<<26) | (3<<28)); + LPC_PINCON->PINSEL1 |= ((1<<26)|(1<<28)); // 0x14000000 + + PRINT_Log("Initializing Host Stack\n"); + + Hcca = (volatile HCCA *)(HostBuf+0x000); + TDHead = (volatile HCTD *)(HostBuf+0x100); + TDTail = (volatile HCTD *)(HostBuf+0x110); + EDCtrl = (volatile HCED *)(HostBuf+0x120); + EDBulkIn = (volatile HCED *)(HostBuf+0x130); + EDBulkOut = (volatile HCED *)(HostBuf+0x140); + TDBuffer = (volatile USB_INT08U *)(HostBuf+0x150); + + /* Initialize all the TDs, EDs and HCCA to 0 */ + Host_EDInit(EDCtrl); + Host_EDInit(EDBulkIn); + Host_EDInit(EDBulkOut); + Host_TDInit(TDHead); + Host_TDInit(TDTail); + Host_HCCAInit(Hcca); + + Host_DelayMS(50); /* Wait 50 ms before apply reset */ + LPC_USB->HcControl = 0; /* HARDWARE RESET */ + LPC_USB->HcControlHeadED = 0; /* Initialize Control list head to Zero */ + LPC_USB->HcBulkHeadED = 0; /* Initialize Bulk list head to Zero */ + + /* SOFTWARE RESET */ + LPC_USB->HcCommandStatus = OR_CMD_STATUS_HCR; + LPC_USB->HcFmInterval = DEFAULT_FMINTERVAL; /* Write Fm Interval and Largest Data Packet Counter */ + + /* Put HC in operational state */ + LPC_USB->HcControl = (LPC_USB->HcControl & (~OR_CONTROL_HCFS)) | OR_CONTROL_HC_OPER; + LPC_USB->HcRhStatus = OR_RH_STATUS_LPSC; /* Set Global Power */ + + LPC_USB->HcHCCA = (USB_INT32U)Hcca; + LPC_USB->HcInterruptStatus |= LPC_USB->HcInterruptStatus; /* Clear Interrrupt Status */ + + + LPC_USB->HcInterruptEnable = OR_INTR_ENABLE_MIE | + OR_INTR_ENABLE_WDH | + OR_INTR_ENABLE_RHSC; + + NVIC_SetPriority(USB_IRQn, 0); /* highest priority */ + /* Enable the USB Interrupt */ + NVIC_EnableIRQ(USB_IRQn); + PRINT_Log("Host Initialized\n"); +} + +/* +************************************************************************************************************** +* INTERRUPT SERVICE ROUTINE +* +* Description: This function services the interrupt caused by host controller +* +* Arguments : None +* +* Returns : None +* +************************************************************************************************************** +*/ + +void USB_IRQHandler (void) __irq +{ + USB_INT32U int_status; + USB_INT32U ie_status; + + int_status = LPC_USB->HcInterruptStatus; /* Read Interrupt Status */ + ie_status = LPC_USB->HcInterruptEnable; /* Read Interrupt enable status */ + + if (!(int_status & ie_status)) { + return; + } else { + + int_status = int_status & ie_status; + if (int_status & OR_INTR_STATUS_RHSC) { /* Root hub status change interrupt */ + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CSC) { + if (LPC_USB->HcRhStatus & OR_RH_STATUS_DRWE) { + /* + * When DRWE is on, Connect Status Change + * means a remote wakeup event. + */ + HOST_RhscIntr = 1;// JUST SOMETHING FOR A BREAKPOINT + } + else { + /* + * When DRWE is off, Connect Status Change + * is NOT a remote wakeup event + */ + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_CCS) { + if (!gUSBConnected) { + HOST_TDControlStatus = 0; + HOST_WdhIntr = 0; + HOST_RhscIntr = 1; + gUSBConnected = 1; + } + else + PRINT_Log("Spurious status change (connected)?\n"); + } else { + if (gUSBConnected) { + LPC_USB->HcInterruptEnable = 0; // why do we get multiple disc. rupts??? + HOST_RhscIntr = 0; + gUSBConnected = 0; + } + else + PRINT_Log("Spurious status change (disconnected)?\n"); + } + } + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_CSC; + } + if (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRSC) { + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; + } + } + if (int_status & OR_INTR_STATUS_WDH) { /* Writeback Done Head interrupt */ + HOST_WdhIntr = 1; + HOST_TDControlStatus = (TDHead->Control >> 28) & 0xf; + } + LPC_USB->HcInterruptStatus = int_status; /* Clear interrupt status register */ + } + return; +} + +/* +************************************************************************************************************** +* PROCESS TRANSFER DESCRIPTOR +* +* Description: This function processes the transfer descriptor +* +* Arguments : ed Endpoint descriptor that contains this transfer descriptor +* token SETUP, IN, OUT +* buffer Current Buffer Pointer of the transfer descriptor +* buffer_len Length of the buffer +* +* Returns : OK if TD submission is successful +* ERROR if TD submission fails +* +************************************************************************************************************** +*/ + +USB_INT32S Host_ProcessTD (volatile HCED *ed, + volatile USB_INT32U token, + volatile USB_INT08U *buffer, + USB_INT32U buffer_len) +{ + volatile USB_INT32U td_toggle; + + + if (ed == EDCtrl) { + if (token == TD_SETUP) { + td_toggle = TD_TOGGLE_0; + } else { + td_toggle = TD_TOGGLE_1; + } + } else { + td_toggle = 0; + } + TDHead->Control = (TD_ROUNDING | + token | + TD_DELAY_INT(0) | + td_toggle | + TD_CC); + TDTail->Control = 0; + TDHead->CurrBufPtr = (USB_INT32U) buffer; + TDTail->CurrBufPtr = 0; + TDHead->Next = (USB_INT32U) TDTail; + TDTail->Next = 0; + TDHead->BufEnd = (USB_INT32U)(buffer + (buffer_len - 1)); + TDTail->BufEnd = 0; + + ed->HeadTd = (USB_INT32U)TDHead | ((ed->HeadTd) & 0x00000002); + ed->TailTd = (USB_INT32U)TDTail; + ed->Next = 0; + + if (ed == EDCtrl) { + LPC_USB->HcControlHeadED = (USB_INT32U)ed; + LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | OR_CMD_STATUS_CLF; + LPC_USB->HcControl = LPC_USB->HcControl | OR_CONTROL_CLE; + } else { + LPC_USB->HcBulkHeadED = (USB_INT32U)ed; + LPC_USB->HcCommandStatus = LPC_USB->HcCommandStatus | OR_CMD_STATUS_BLF; + LPC_USB->HcControl = LPC_USB->HcControl | OR_CONTROL_BLE; + } + + Host_WDHWait(); + +// if (!(TDHead->Control & 0xF0000000)) { + if (!HOST_TDControlStatus) { + return (OK); + } else { + return (ERR_TD_FAIL); + } +} + +/* +************************************************************************************************************** +* ENUMERATE THE DEVICE +* +* Description: This function is used to enumerate the device connected +* +* Arguments : None +* +* Returns : None +* +************************************************************************************************************** +*/ + +USB_INT32S Host_EnumDev (void) +{ + USB_INT32S rc; + + PRINT_Log("Connect a Mass Storage device\n"); + while (!HOST_RhscIntr) + __WFI(); + Host_DelayMS(100); /* USB 2.0 spec says atleast 50ms delay beore port reset */ + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRS; // Initiate port reset + while (LPC_USB->HcRhPortStatus1 & OR_RH_PORT_PRS) + __WFI(); // Wait for port reset to complete... + LPC_USB->HcRhPortStatus1 = OR_RH_PORT_PRSC; // ...and clear port reset signal + Host_DelayMS(200); /* Wait for 100 MS after port reset */ + + EDCtrl->Control = 8 << 16; /* Put max pkt size = 8 */ + /* Read first 8 bytes of device desc */ + rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_DEVICE, 0, TDBuffer, 8); + if (rc != OK) { + PRINT_Err(rc); + return (rc); + } + EDCtrl->Control = TDBuffer[7] << 16; /* Get max pkt size of endpoint 0 */ + rc = HOST_SET_ADDRESS(1); /* Set the device address to 1 */ + if (rc != OK) { + PRINT_Err(rc); + return (rc); + } + Host_DelayMS(2); + EDCtrl->Control = (EDCtrl->Control) | 1; /* Modify control pipe with address 1 */ + /* Get the configuration descriptor */ + rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, TDBuffer, 9); + if (rc != OK) { + PRINT_Err(rc); + return (rc); + } + /* Get the first configuration data */ + rc = HOST_GET_DESCRIPTOR(USB_DESCRIPTOR_TYPE_CONFIGURATION, 0, TDBuffer, ReadLE16U(&TDBuffer[2])); + if (rc != OK) { + PRINT_Err(rc); + return (rc); + } + rc = MS_ParseConfiguration(); /* Parse the configuration */ + if (rc != OK) { + PRINT_Err(rc); + return (rc); + } + rc = USBH_SET_CONFIGURATION(1); /* Select device configuration 1 */ + if (rc != OK) { + PRINT_Err(rc); + } + Host_DelayMS(100); /* Some devices may require this delay */ + return (rc); +} + +/* +************************************************************************************************************** +* RECEIVE THE CONTROL INFORMATION +* +* Description: This function is used to receive the control information +* +* Arguments : bm_request_type +* b_request +* w_value +* w_index +* w_length +* buffer +* +* Returns : OK if Success +* ERROR if Failed +* +************************************************************************************************************** +*/ + +USB_INT32S Host_CtrlRecv ( USB_INT08U bm_request_type, + USB_INT08U b_request, + USB_INT16U w_value, + USB_INT16U w_index, + USB_INT16U w_length, + volatile USB_INT08U *buffer) +{ + USB_INT32S rc; + + + Host_FillSetup(bm_request_type, b_request, w_value, w_index, w_length); + rc = Host_ProcessTD(EDCtrl, TD_SETUP, TDBuffer, 8); + if (rc == OK) { + if (w_length) { + rc = Host_ProcessTD(EDCtrl, TD_IN, TDBuffer, w_length); + } + if (rc == OK) { + rc = Host_ProcessTD(EDCtrl, TD_OUT, NULL, 0); + } + } + return (rc); +} + +/* +************************************************************************************************************** +* SEND THE CONTROL INFORMATION +* +* Description: This function is used to send the control information +* +* Arguments : None +* +* Returns : OK if Success +* ERR_INVALID_BOOTSIG if Failed +* +************************************************************************************************************** +*/ + +USB_INT32S Host_CtrlSend ( USB_INT08U bm_request_type, + USB_INT08U b_request, + USB_INT16U w_value, + USB_INT16U w_index, + USB_INT16U w_length, + volatile USB_INT08U *buffer) +{ + USB_INT32S rc; + + + Host_FillSetup(bm_request_type, b_request, w_value, w_index, w_length); + + rc = Host_ProcessTD(EDCtrl, TD_SETUP, TDBuffer, 8); + if (rc == OK) { + if (w_length) { + rc = Host_ProcessTD(EDCtrl, TD_OUT, TDBuffer, w_length); + } + if (rc == OK) { + rc = Host_ProcessTD(EDCtrl, TD_IN, NULL, 0); + } + } + return (rc); +} + +/* +************************************************************************************************************** +* FILL SETUP PACKET +* +* Description: This function is used to fill the setup packet +* +* Arguments : None +* +* Returns : OK if Success +* ERR_INVALID_BOOTSIG if Failed +* +************************************************************************************************************** +*/ + +void Host_FillSetup (USB_INT08U bm_request_type, + USB_INT08U b_request, + USB_INT16U w_value, + USB_INT16U w_index, + USB_INT16U w_length) +{ + int i; + for (i=0;i<w_length;i++) + TDBuffer[i] = 0; + + TDBuffer[0] = bm_request_type; + TDBuffer[1] = b_request; + WriteLE16U(&TDBuffer[2], w_value); + WriteLE16U(&TDBuffer[4], w_index); + WriteLE16U(&TDBuffer[6], w_length); +} + + + +/* +************************************************************************************************************** +* INITIALIZE THE TRANSFER DESCRIPTOR +* +* Description: This function initializes transfer descriptor +* +* Arguments : Pointer to TD structure +* +* Returns : None +* +************************************************************************************************************** +*/ + +void Host_TDInit (volatile HCTD *td) +{ + + td->Control = 0; + td->CurrBufPtr = 0; + td->Next = 0; + td->BufEnd = 0; +} + +/* +************************************************************************************************************** +* INITIALIZE THE ENDPOINT DESCRIPTOR +* +* Description: This function initializes endpoint descriptor +* +* Arguments : Pointer to ED strcuture +* +* Returns : None +* +************************************************************************************************************** +*/ + +void Host_EDInit (volatile HCED *ed) +{ + + ed->Control = 0; + ed->TailTd = 0; + ed->HeadTd = 0; + ed->Next = 0; +} + +/* +************************************************************************************************************** +* INITIALIZE HOST CONTROLLER COMMUNICATIONS AREA +* +* Description: This function initializes host controller communications area +* +* Arguments : Pointer to HCCA +* +* Returns : +* +************************************************************************************************************** +*/ + +void Host_HCCAInit (volatile HCCA *hcca) +{ + USB_INT32U i; + + + for (i = 0; i < 32; i++) { + + hcca->IntTable[i] = 0; + hcca->FrameNumber = 0; + hcca->DoneHead = 0; + } + +} + +/* +************************************************************************************************************** +* WAIT FOR WDH INTERRUPT +* +* Description: This function is infinite loop which breaks when ever a WDH interrupt rises +* +* Arguments : None +* +* Returns : None +* +************************************************************************************************************** +*/ + +void Host_WDHWait (void) +{ + while (!HOST_WdhIntr) + __WFI(); + + HOST_WdhIntr = 0; +} + +/* +************************************************************************************************************** +* READ LE 32U +* +* Description: This function is used to read an unsigned integer from a character buffer in the platform +* containing little endian processor +* +* Arguments : pmem Pointer to the character buffer +* +* Returns : val Unsigned integer +* +************************************************************************************************************** +*/ + +USB_INT32U ReadLE32U (volatile USB_INT08U *pmem) +{ + USB_INT32U val = *(USB_INT32U*)pmem; +#ifdef __BIG_ENDIAN + return __REV(val); +#else + return val; +#endif +} + +/* +************************************************************************************************************** +* WRITE LE 32U +* +* Description: This function is used to write an unsigned integer into a charecter buffer in the platform +* containing little endian processor. +* +* Arguments : pmem Pointer to the charecter buffer +* val Integer value to be placed in the charecter buffer +* +* Returns : None +* +************************************************************************************************************** +*/ + +void WriteLE32U (volatile USB_INT08U *pmem, + USB_INT32U val) +{ +#ifdef __BIG_ENDIAN + *(USB_INT32U*)pmem = __REV(val); +#else + *(USB_INT32U*)pmem = val; +#endif +} + +/* +************************************************************************************************************** +* READ LE 16U +* +* Description: This function is used to read an unsigned short integer from a charecter buffer in the platform +* containing little endian processor +* +* Arguments : pmem Pointer to the charecter buffer +* +* Returns : val Unsigned short integer +* +************************************************************************************************************** +*/ + +USB_INT16U ReadLE16U (volatile USB_INT08U *pmem) +{ + USB_INT16U val = *(USB_INT16U*)pmem; +#ifdef __BIG_ENDIAN + return __REV16(val); +#else + return val; +#endif +} + +/* +************************************************************************************************************** +* WRITE LE 16U +* +* Description: This function is used to write an unsigned short integer into a charecter buffer in the +* platform containing little endian processor +* +* Arguments : pmem Pointer to the charecter buffer +* val Value to be placed in the charecter buffer +* +* Returns : None +* +************************************************************************************************************** +*/ + +void WriteLE16U (volatile USB_INT08U *pmem, + USB_INT16U val) +{ +#ifdef __BIG_ENDIAN + *(USB_INT16U*)pmem = (__REV16(val) & 0xFFFF); +#else + *(USB_INT16U*)pmem = val; +#endif +} + +/* +************************************************************************************************************** +* READ BE 32U +* +* Description: This function is used to read an unsigned integer from a charecter buffer in the platform +* containing big endian processor +* +* Arguments : pmem Pointer to the charecter buffer +* +* Returns : val Unsigned integer +* +************************************************************************************************************** +*/ + +USB_INT32U ReadBE32U (volatile USB_INT08U *pmem) +{ + USB_INT32U val = *(USB_INT32U*)pmem; +#ifdef __BIG_ENDIAN + return val; +#else + return __REV(val); +#endif +} + +/* +************************************************************************************************************** +* WRITE BE 32U +* +* Description: This function is used to write an unsigned integer into a charecter buffer in the platform +* containing big endian processor +* +* Arguments : pmem Pointer to the charecter buffer +* val Value to be placed in the charecter buffer +* +* Returns : None +* +************************************************************************************************************** +*/ + +void WriteBE32U (volatile USB_INT08U *pmem, + USB_INT32U val) +{ +#ifdef __BIG_ENDIAN + *(USB_INT32U*)pmem = val; +#else + *(USB_INT32U*)pmem = __REV(val); +#endif +} + +/* +************************************************************************************************************** +* READ BE 16U +* +* Description: This function is used to read an unsigned short integer from a charecter buffer in the platform +* containing big endian processor +* +* Arguments : pmem Pointer to the charecter buffer +* +* Returns : val Unsigned short integer +* +************************************************************************************************************** +*/ + +USB_INT16U ReadBE16U (volatile USB_INT08U *pmem) +{ + USB_INT16U val = *(USB_INT16U*)pmem; +#ifdef __BIG_ENDIAN + return val; +#else + return __REV16(val); +#endif +} + +/* +************************************************************************************************************** +* WRITE BE 16U +* +* Description: This function is used to write an unsigned short integer into the charecter buffer in the +* platform containing big endian processor +* +* Arguments : pmem Pointer to the charecter buffer +* val Value to be placed in the charecter buffer +* +* Returns : None +* +************************************************************************************************************** +*/ + +void WriteBE16U (volatile USB_INT08U *pmem, + USB_INT16U val) +{ +#ifdef __BIG_ENDIAN + *(USB_INT16U*)pmem = val; +#else + *(USB_INT16U*)pmem = (__REV16(val) & 0xFFFF); +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHostLite/usbhost_lpc17xx.h Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,254 @@ +/* +************************************************************************************************************** +* NXP USB Host Stack +* +* (c) Copyright 2008, NXP SemiConductors +* (c) Copyright 2008, OnChip Technologies LLC +* All Rights Reserved +* +* www.nxp.com +* www.onchiptech.com +* +* File : usbhost_lpc17xx.h +* Programmer(s) : Ravikanth.P +* Version : +* +************************************************************************************************************** +*/ + +#ifndef USBHOST_LPC17xx_H +#define USBHOST_LPC17xx_H + +/* +************************************************************************************************************** +* INCLUDE HEADER FILES +************************************************************************************************************** +*/ + +#include "usbhost_inc.h" + +/* +************************************************************************************************************** +* PRINT CONFIGURATION +************************************************************************************************************** +*/ + +#define PRINT_ENABLE 0 + +#if PRINT_ENABLE +#define PRINT_Log(...) printf(__VA_ARGS__) +#define PRINT_Err(rc) printf("ERROR: In %s at Line %u - rc = %d\n", __FUNCTION__, __LINE__, rc) + +#else +#define PRINT_Log(...) do {} while(0) +#define PRINT_Err(rc) do {} while(0) + +#endif + +/* +************************************************************************************************************** +* GENERAL DEFINITIONS +************************************************************************************************************** +*/ + +#define DESC_LENGTH(x) x[0] +#define DESC_TYPE(x) x[1] + + +#define HOST_GET_DESCRIPTOR(descType, descIndex, data, length) \ + Host_CtrlRecv(USB_DEVICE_TO_HOST | USB_RECIPIENT_DEVICE, GET_DESCRIPTOR, \ + (descType << 8)|(descIndex), 0, length, data) + +#define HOST_SET_ADDRESS(new_addr) \ + Host_CtrlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, SET_ADDRESS, \ + new_addr, 0, 0, NULL) + +#define USBH_SET_CONFIGURATION(configNum) \ + Host_CtrlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_DEVICE, SET_CONFIGURATION, \ + configNum, 0, 0, NULL) + +#define USBH_SET_INTERFACE(ifNum, altNum) \ + Host_CtrlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_INTERFACE, SET_INTERFACE, \ + altNum, ifNum, 0, NULL) + +/* +************************************************************************************************************** +* OHCI OPERATIONAL REGISTER FIELD DEFINITIONS +************************************************************************************************************** +*/ + + /* ------------------ HcControl Register --------------------- */ +#define OR_CONTROL_CLE 0x00000010 +#define OR_CONTROL_BLE 0x00000020 +#define OR_CONTROL_HCFS 0x000000C0 +#define OR_CONTROL_HC_OPER 0x00000080 + /* ----------------- HcCommandStatus Register ----------------- */ +#define OR_CMD_STATUS_HCR 0x00000001 +#define OR_CMD_STATUS_CLF 0x00000002 +#define OR_CMD_STATUS_BLF 0x00000004 + /* --------------- HcInterruptStatus Register ----------------- */ +#define OR_INTR_STATUS_WDH 0x00000002 +#define OR_INTR_STATUS_RHSC 0x00000040 + /* --------------- HcInterruptEnable Register ----------------- */ +#define OR_INTR_ENABLE_WDH 0x00000002 +#define OR_INTR_ENABLE_RHSC 0x00000040 +#define OR_INTR_ENABLE_MIE 0x80000000 + /* ---------------- HcRhDescriptorA Register ------------------ */ +#define OR_RH_STATUS_LPSC 0x00010000 +#define OR_RH_STATUS_DRWE 0x00008000 + /* -------------- HcRhPortStatus[1:NDP] Register -------------- */ +#define OR_RH_PORT_CCS 0x00000001 +#define OR_RH_PORT_PRS 0x00000010 +#define OR_RH_PORT_CSC 0x00010000 +#define OR_RH_PORT_PRSC 0x00100000 + + +/* +************************************************************************************************************** +* FRAME INTERVAL +************************************************************************************************************** +*/ + +#define FI 0x2EDF /* 12000 bits per frame (-1) */ +#define DEFAULT_FMINTERVAL ((((6 * (FI - 210)) / 7) << 16) | FI) + +/* +************************************************************************************************************** +* TRANSFER DESCRIPTOR CONTROL FIELDS +************************************************************************************************************** +*/ + +#define TD_ROUNDING (USB_INT32U) (0x00040000) /* Buffer Rounding */ +#define TD_SETUP (USB_INT32U)(0) /* Direction of Setup Packet */ +#define TD_IN (USB_INT32U)(0x00100000) /* Direction In */ +#define TD_OUT (USB_INT32U)(0x00080000) /* Direction Out */ +#define TD_DELAY_INT(x) (USB_INT32U)((x) << 21) /* Delay Interrupt */ +#define TD_TOGGLE_0 (USB_INT32U)(0x02000000) /* Toggle 0 */ +#define TD_TOGGLE_1 (USB_INT32U)(0x03000000) /* Toggle 1 */ +#define TD_CC (USB_INT32U)(0xF0000000) /* Completion Code */ + +/* +************************************************************************************************************** +* USB STANDARD REQUEST DEFINITIONS +************************************************************************************************************** +*/ + +#define USB_DESCRIPTOR_TYPE_DEVICE 1 +#define USB_DESCRIPTOR_TYPE_CONFIGURATION 2 +#define USB_DESCRIPTOR_TYPE_INTERFACE 4 +#define USB_DESCRIPTOR_TYPE_ENDPOINT 5 + /* ----------- Control RequestType Fields ----------- */ +#define USB_DEVICE_TO_HOST 0x80 +#define USB_HOST_TO_DEVICE 0x00 +#define USB_REQUEST_TYPE_CLASS 0x20 +#define USB_RECIPIENT_DEVICE 0x00 +#define USB_RECIPIENT_INTERFACE 0x01 + /* -------------- USB Standard Requests -------------- */ +#define SET_ADDRESS 5 +#define GET_DESCRIPTOR 6 +#define SET_CONFIGURATION 9 +#define SET_INTERFACE 11 + +/* +************************************************************************************************************** +* TYPE DEFINITIONS +************************************************************************************************************** +*/ + +typedef struct hcEd { /* ----------- HostController EndPoint Descriptor ------------- */ + volatile USB_INT32U Control; /* Endpoint descriptor control */ + volatile USB_INT32U TailTd; /* Physical address of tail in Transfer descriptor list */ + volatile USB_INT32U HeadTd; /* Physcial address of head in Transfer descriptor list */ + volatile USB_INT32U Next; /* Physical address of next Endpoint descriptor */ +} HCED; + +typedef struct hcTd { /* ------------ HostController Transfer Descriptor ------------ */ + volatile USB_INT32U Control; /* Transfer descriptor control */ + volatile USB_INT32U CurrBufPtr; /* Physical address of current buffer pointer */ + volatile USB_INT32U Next; /* Physical pointer to next Transfer Descriptor */ + volatile USB_INT32U BufEnd; /* Physical address of end of buffer */ +} HCTD; + +typedef struct hcca { /* ----------- Host Controller Communication Area ------------ */ + volatile USB_INT32U IntTable[32]; /* Interrupt Table */ + volatile USB_INT32U FrameNumber; /* Frame Number */ + volatile USB_INT32U DoneHead; /* Done Head */ + volatile USB_INT08U Reserved[116]; /* Reserved for future use */ + volatile USB_INT08U Unknown[4]; /* Unused */ +} HCCA; + +/* +************************************************************************************************************** +* EXTERN DECLARATIONS +************************************************************************************************************** +*/ + +extern volatile HCED *EDBulkIn; /* BulkIn endpoint descriptor structure */ +extern volatile HCED *EDBulkOut; /* BulkOut endpoint descriptor structure */ +extern volatile HCTD *TDHead; /* Head transfer descriptor structure */ +extern volatile HCTD *TDTail; /* Tail transfer descriptor structure */ +extern volatile USB_INT08U *TDBuffer; /* Current Buffer Pointer of transfer descriptor */ + +/* +************************************************************************************************************** +* FUNCTION PROTOTYPES +************************************************************************************************************** +*/ + +void Host_Init (void); + +extern "C" void USB_IRQHandler(void) __irq; + +USB_INT32S Host_EnumDev (void); + +USB_INT32S Host_ProcessTD(volatile HCED *ed, + volatile USB_INT32U token, + volatile USB_INT08U *buffer, + USB_INT32U buffer_len); + +void Host_DelayUS ( USB_INT32U delay); +void Host_DelayMS ( USB_INT32U delay); + + +void Host_TDInit (volatile HCTD *td); +void Host_EDInit (volatile HCED *ed); +void Host_HCCAInit (volatile HCCA *hcca); + +USB_INT32S Host_CtrlRecv ( USB_INT08U bm_request_type, + USB_INT08U b_request, + USB_INT16U w_value, + USB_INT16U w_index, + USB_INT16U w_length, + volatile USB_INT08U *buffer); + +USB_INT32S Host_CtrlSend ( USB_INT08U bm_request_type, + USB_INT08U b_request, + USB_INT16U w_value, + USB_INT16U w_index, + USB_INT16U w_length, + volatile USB_INT08U *buffer); + +void Host_FillSetup( USB_INT08U bm_request_type, + USB_INT08U b_request, + USB_INT16U w_value, + USB_INT16U w_index, + USB_INT16U w_length); + + +void Host_WDHWait (void); + + +USB_INT32U ReadLE32U (volatile USB_INT08U *pmem); +void WriteLE32U (volatile USB_INT08U *pmem, + USB_INT32U val); +USB_INT16U ReadLE16U (volatile USB_INT08U *pmem); +void WriteLE16U (volatile USB_INT08U *pmem, + USB_INT16U val); +USB_INT32U ReadBE32U (volatile USB_INT08U *pmem); +void WriteBE32U (volatile USB_INT08U *pmem, + USB_INT32U val); +USB_INT16U ReadBE16U (volatile USB_INT08U *pmem); +void WriteBE16U (volatile USB_INT08U *pmem, + USB_INT16U val); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHostLite/usbhost_ms.c Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,455 @@ +/* +************************************************************************************************************** +* NXP USB Host Stack +* +* (c) Copyright 2008, NXP SemiConductors +* (c) Copyright 2008, OnChip Technologies LLC +* All Rights Reserved +* +* www.nxp.com +* www.onchiptech.com +* +* File : usbhost_ms.c +* Programmer(s) : Ravikanth.P +* Version : +* +************************************************************************************************************** +*/ + +/* +************************************************************************************************************** +* INCLUDE HEADER FILES +************************************************************************************************************** +*/ + +#include "usbhost_ms.h" + +/* +************************************************************************************************************** +* GLOBAL VARIABLES +************************************************************************************************************** +*/ + +USB_INT32U MS_BlkSize; + +/* +************************************************************************************************************** +* INITIALIZE MASS STORAGE INTERFACE +* +* Description: This function initializes the mass storage interface +* +* Arguments : None +* +* Returns : OK if Success +* ERR_INVALID_BOOTSIG if Failed +* +************************************************************************************************************** +*/ + +USB_INT32S MS_Init (USB_INT32U *blkSize, USB_INT32U *numBlks, USB_INT08U *inquiryResult) +{ + USB_INT08U retry; + USB_INT32S rc; + + MS_GetMaxLUN(); /* Get maximum logical unit number */ + retry = 80; + while(retry) { + rc = MS_TestUnitReady(); /* Test whether the unit is ready */ + if (rc == OK) { + break; + } + MS_GetSenseInfo(); /* Get sense information */ + retry--; + } + if (rc != OK) { + PRINT_Err(rc); + return (rc); + } + rc = MS_ReadCapacity(numBlks, blkSize); /* Read capacity of the disk */ + MS_BlkSize = *blkSize; // Set global + rc = MS_Inquire (inquiryResult); + return (rc); +} +/* +************************************************************************************************************** +* PARSE THE CONFIGURATION +* +* Description: This function is used to parse the configuration +* +* Arguments : None +* +* Returns : OK if Success +* ERR_INVALID_BOOTSIG if Failed +* +************************************************************************************************************** +*/ + +USB_INT32S MS_ParseConfiguration (void) +{ + volatile USB_INT08U *desc_ptr; + USB_INT08U ms_int_found; + + + desc_ptr = TDBuffer; + ms_int_found = 0; + + if (desc_ptr[1] != USB_DESCRIPTOR_TYPE_CONFIGURATION) { + return (ERR_BAD_CONFIGURATION); + } + desc_ptr += desc_ptr[0]; + + while (desc_ptr != TDBuffer + ReadLE16U(&TDBuffer[2])) { + + switch (desc_ptr[1]) { + + case USB_DESCRIPTOR_TYPE_INTERFACE: /* If it is an interface descriptor */ + if (desc_ptr[5] == MASS_STORAGE_CLASS && /* check if the class is mass storage */ + desc_ptr[6] == MASS_STORAGE_SUBCLASS_SCSI && /* check if the subclass is SCSI */ + desc_ptr[7] == MASS_STORAGE_PROTOCOL_BO) { /* check if the protocol is Bulk only */ + ms_int_found = 1; + desc_ptr += desc_ptr[0]; /* Move to next descriptor start */ + } + break; + + case USB_DESCRIPTOR_TYPE_ENDPOINT: /* If it is an endpoint descriptor */ + if ((desc_ptr[3] & 0x03) == 0x02) { /* If it is Bulk endpoint */ + if (desc_ptr[2] & 0x80) { /* If it is In endpoint */ + EDBulkIn->Control = 1 | /* USB address */ + ((desc_ptr[2] & 0x7F) << 7) | /* Endpoint address */ + (2 << 11) | /* direction */ + (ReadLE16U(&desc_ptr[4]) << 16); /* MaxPkt Size */ + desc_ptr += desc_ptr[0]; /* Move to next descriptor start */ + } else { /* If it is Out endpoint */ + EDBulkOut->Control = 1 | /* USB address */ + ((desc_ptr[2] & 0x7F) << 7) | /* Endpoint address */ + (1 << 11) | /* direction */ + (ReadLE16U(&desc_ptr[4]) << 16); /* MaxPkt Size */ + desc_ptr += desc_ptr[0]; /* Move to next descriptor start */ + } + } else { /* If it is not bulk end point */ + desc_ptr += desc_ptr[0]; /* Move to next descriptor start */ + } + break; + + default: /* If the descriptor is neither interface nor endpoint */ + desc_ptr += desc_ptr[0]; /* Move to next descriptor start */ + break; + } + } + if (ms_int_found) { + PRINT_Log("Mass Storage device connected\n"); + return (OK); + } else { + PRINT_Log("Not a Mass Storage device\n"); + return (ERR_NO_MS_INTERFACE); + } +} + +/* +************************************************************************************************************** +* GET MAXIMUM LOGICAL UNIT +* +* Description: This function returns the maximum logical unit from the device +* +* Arguments : None +* +* Returns : OK if Success +* ERR_INVALID_BOOTSIG if Failed +* +************************************************************************************************************** +*/ + +USB_INT32S MS_GetMaxLUN (void) +{ + USB_INT32S rc; + + + rc = Host_CtrlRecv(USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_INTERFACE, + MS_GET_MAX_LUN_REQ, + 0, + 0, + 1, + TDBuffer); + return (rc); +} + +/* +************************************************************************************************************** +* GET SENSE INFORMATION +* +* Description: This function is used to get sense information from the device +* +* Arguments : None +* +* Returns : OK if Success +* ERROR if Failed +* +************************************************************************************************************** +*/ + +USB_INT32S MS_GetSenseInfo (void) +{ + USB_INT32S rc; + + + Fill_MSCommand(0, 0, 0, MS_DATA_DIR_IN, SCSI_CMD_REQUEST_SENSE, 6); + rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE); + if (rc == OK) { + rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, 18); + if (rc == OK) { + rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE); + if (rc == OK) { + if (TDBuffer[12] != 0) { + rc = ERR_MS_CMD_FAILED; + } + } + } + } + return (rc); +} + +/* +************************************************************************************************************** +* TEST UNIT READY +* +* Description: This function is used to test whether the unit is ready or not +* +* Arguments : None +* +* Returns : OK if Success +* ERROR if Failed +* +************************************************************************************************************** +*/ + +USB_INT32S MS_TestUnitReady (void) +{ + USB_INT32S rc; + + + Fill_MSCommand(0, 0, 0, MS_DATA_DIR_NONE, SCSI_CMD_TEST_UNIT_READY, 6); + rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE); + if (rc == OK) { + rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE); + if (rc == OK) { + if (TDBuffer[12] != 0) { + rc = ERR_MS_CMD_FAILED; + } + } + } + return (rc); +} + +/* +************************************************************************************************************** +* READ CAPACITY +* +* Description: This function is used to read the capacity of the mass storage device +* +* Arguments : None +* +* Returns : OK if Success +* ERROR if Failed +* +************************************************************************************************************** +*/ + +USB_INT32S MS_ReadCapacity (USB_INT32U *numBlks, USB_INT32U *blkSize) +{ + USB_INT32S rc; + + + Fill_MSCommand(0, 0, 0, MS_DATA_DIR_IN, SCSI_CMD_READ_CAPACITY, 10); + rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE); + if (rc == OK) { + rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, 8); + if (rc == OK) { + if (numBlks) + *numBlks = ReadBE32U(&TDBuffer[0]); + if (blkSize) + *blkSize = ReadBE32U(&TDBuffer[4]); + rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE); + if (rc == OK) { + if (TDBuffer[12] != 0) { + rc = ERR_MS_CMD_FAILED; + } + } + } + } + return (rc); +} + + + +USB_INT32S MS_Inquire (USB_INT08U *response) +{ + USB_INT32S rc; + USB_INT32U i; + + Fill_MSCommand(0, 0, 0, MS_DATA_DIR_IN, SCSI_CMD_INQUIRY, 6); + rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE); + if (rc == OK) { + rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, INQUIRY_LENGTH); + if (rc == OK) { + if (response) { + for ( i = 0; i < INQUIRY_LENGTH; i++ ) + *response++ = *TDBuffer++; +#if 0 + MemCpy (response, TDBuffer, INQUIRY_LENGTH); + StrNullTrailingSpace (response->vendorID, SCSI_INQUIRY_VENDORCHARS); + StrNullTrailingSpace (response->productID, SCSI_INQUIRY_PRODUCTCHARS); + StrNullTrailingSpace (response->productRev, SCSI_INQUIRY_REVCHARS); +#endif + } + rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE); + if (rc == OK) { + if (TDBuffer[12] != 0) { // bCSWStatus byte + rc = ERR_MS_CMD_FAILED; + } + } + } + } + return (rc); +} + +/* +************************************************************************************************************** +* RECEIVE THE BULK DATA +* +* Description: This function is used to receive the bulk data +* +* Arguments : None +* +* Returns : OK if Success +* ERR_INVALID_BOOTSIG if Failed +* +************************************************************************************************************** +*/ + +USB_INT32S MS_BulkRecv ( USB_INT32U block_number, + USB_INT16U num_blocks, + volatile USB_INT08U *user_buffer) +{ + USB_INT32S rc; + int i; + volatile USB_INT08U *c = user_buffer; + for (i=0;i<MS_BlkSize*num_blocks;i++) + *c++ = 0; + + + Fill_MSCommand(block_number, MS_BlkSize, num_blocks, MS_DATA_DIR_IN, SCSI_CMD_READ_10, 10); + + rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE); + if (rc == OK) { + rc = Host_ProcessTD(EDBulkIn, TD_IN, user_buffer, MS_BlkSize * num_blocks); + if (rc == OK) { + rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE); + if (rc == OK) { + if (TDBuffer[12] != 0) { + rc = ERR_MS_CMD_FAILED; + } + } + } + } + return (rc); +} + +/* +************************************************************************************************************** +* SEND BULK DATA +* +* Description: This function is used to send the bulk data +* +* Arguments : None +* +* Returns : OK if Success +* ERR_INVALID_BOOTSIG if Failed +* +************************************************************************************************************** +*/ + +USB_INT32S MS_BulkSend ( USB_INT32U block_number, + USB_INT16U num_blocks, + volatile USB_INT08U *user_buffer) +{ + USB_INT32S rc; + + + Fill_MSCommand(block_number, MS_BlkSize, num_blocks, MS_DATA_DIR_OUT, SCSI_CMD_WRITE_10, 10); + + rc = Host_ProcessTD(EDBulkOut, TD_OUT, TDBuffer, CBW_SIZE); + if (rc == OK) { + rc = Host_ProcessTD(EDBulkOut, TD_OUT, user_buffer, MS_BlkSize * num_blocks); + if (rc == OK) { + rc = Host_ProcessTD(EDBulkIn, TD_IN, TDBuffer, CSW_SIZE); + if (rc == OK) { + if (TDBuffer[12] != 0) { + rc = ERR_MS_CMD_FAILED; + } + } + } + } + return (rc); +} + +/* +************************************************************************************************************** +* FILL MASS STORAGE COMMAND +* +* Description: This function is used to fill the mass storage command +* +* Arguments : None +* +* Returns : OK if Success +* ERR_INVALID_BOOTSIG if Failed +* +************************************************************************************************************** +*/ + +void Fill_MSCommand (USB_INT32U block_number, + USB_INT32U block_size, + USB_INT16U num_blocks, + MS_DATA_DIR direction, + USB_INT08U scsi_cmd, + USB_INT08U scsi_cmd_len) +{ + USB_INT32U data_len; + static USB_INT32U tag_cnt = 0; + USB_INT32U cnt; + + + for (cnt = 0; cnt < CBW_SIZE; cnt++) { + TDBuffer[cnt] = 0; + } + switch(scsi_cmd) { + + case SCSI_CMD_TEST_UNIT_READY: + data_len = 0; + break; + case SCSI_CMD_READ_CAPACITY: + data_len = 8; + break; + case SCSI_CMD_REQUEST_SENSE: + data_len = 18; + break; + case SCSI_CMD_INQUIRY: + data_len = 36; + break; + default: + data_len = block_size * num_blocks; + break; + } + WriteLE32U(TDBuffer, CBW_SIGNATURE); + WriteLE32U(&TDBuffer[4], tag_cnt); + WriteLE32U(&TDBuffer[8], data_len); + TDBuffer[12] = (direction == MS_DATA_DIR_NONE) ? 0 : direction; + TDBuffer[14] = scsi_cmd_len; /* Length of the CBW */ + TDBuffer[15] = scsi_cmd; + if ((scsi_cmd == SCSI_CMD_REQUEST_SENSE) + || (scsi_cmd == SCSI_CMD_INQUIRY)) { + TDBuffer[19] = (USB_INT08U)data_len; + } else { + WriteBE32U(&TDBuffer[17], block_number); + } + WriteBE16U(&TDBuffer[22], num_blocks); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/USBHostLite/usbhost_ms.h Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,101 @@ +/* +************************************************************************************************************** +* NXP USB Host Stack +* +* (c) Copyright 2008, NXP SemiConductors +* (c) Copyright 2008, OnChip Technologies LLC +* All Rights Reserved +* +* www.nxp.com +* www.onchiptech.com +* +* File : usbhost_ms.h +* Programmer(s) : Ravikanth.P +* Version : +* +************************************************************************************************************** +*/ + +#ifndef USBHOST_MS_H +#define USBHOST_MS_H + +/* +************************************************************************************************************** +* INCLUDE HEADER FILES +************************************************************************************************************** +*/ + +#include "usbhost_inc.h" + +/* +************************************************************************************************************** +* MASS STORAGE SPECIFIC DEFINITIONS +************************************************************************************************************** +*/ + +#define MS_GET_MAX_LUN_REQ 0xFE +#define MASS_STORAGE_CLASS 0x08 +#define MASS_STORAGE_SUBCLASS_SCSI 0x06 +#define MASS_STORAGE_PROTOCOL_BO 0x50 + +#define INQUIRY_LENGTH 36 +/* +************************************************************************************************************** +* SCSI SPECIFIC DEFINITIONS +************************************************************************************************************** +*/ + +#define CBW_SIGNATURE 0x43425355 +#define CSW_SIGNATURE 0x53425355 +#define CBW_SIZE 31 +#define CSW_SIZE 13 +#define CSW_CMD_PASSED 0x00 +#define SCSI_CMD_REQUEST_SENSE 0x03 +#define SCSI_CMD_TEST_UNIT_READY 0x00 +#define SCSI_CMD_INQUIRY 0x12 +#define SCSI_CMD_READ_10 0x28 +#define SCSI_CMD_READ_CAPACITY 0x25 +#define SCSI_CMD_WRITE_10 0x2A + +/* +************************************************************************************************************** +* TYPE DEFINITIONS +************************************************************************************************************** +*/ + +typedef enum ms_data_dir { + + MS_DATA_DIR_IN = 0x80, + MS_DATA_DIR_OUT = 0x00, + MS_DATA_DIR_NONE = 0x01 + +} MS_DATA_DIR; + +/* +************************************************************************************************************** +* FUNCTION PROTOTYPES +************************************************************************************************************** +*/ + +USB_INT32S MS_BulkRecv ( USB_INT32U block_number, + USB_INT16U num_blocks, + volatile USB_INT08U *user_buffer); + +USB_INT32S MS_BulkSend ( USB_INT32U block_number, + USB_INT16U num_blocks, + volatile USB_INT08U *user_buffer); +USB_INT32S MS_ParseConfiguration(void); +USB_INT32S MS_TestUnitReady (void); +USB_INT32S MS_ReadCapacity (USB_INT32U *numBlks, USB_INT32U *blkSize); +USB_INT32S MS_GetMaxLUN (void); +USB_INT32S MS_GetSenseInfo (void); +USB_INT32S MS_Init (USB_INT32U *blkSize, USB_INT32U *numBlks, USB_INT08U *inquiryResult); +USB_INT32S MS_Inquire (USB_INT08U *response); + +void Fill_MSCommand ( USB_INT32U block_number, + USB_INT32U block_size, + USB_INT16U num_blocks, + MS_DATA_DIR direction, + USB_INT08U scsi_cmd, + USB_INT08U scsi_cmd_len); +#endif
--- a/common.h Wed Apr 10 13:31:35 2013 +0000 +++ b/common.h Sun Jul 21 23:59:00 2013 +0000 @@ -11,19 +11,28 @@ #define changedScreen 10 #define playbackScreen 11 #define dateScreen 12 -#define config1Screen 13 +#define configScreen 13 #define indexScreen 14 -#define maxScreens 14 +#define tripScreen 15 +#define healthScreen 16 +#define maxScreens 16 #define btnGap 10 #define ttSkin 0 #define ggSkin 1 +#define maxSkin 1 -#define maxBufLen 512 +#define maxBufLen 1024 #define canTimeout 5 #define userTimeout 15 -// gg - 4x4 touch -// #define tNavRow = 2 ; // for 3x3 -extern unsigned char tNavRow ; // gg - 4x4 +#define BatDataBaseG1 0x00 // 6 frames - SOH, SOC, Ah +#define BatDataBaseG2 0x06 // 29 frames - Cell Pair data +#define BatDataBaseG3 0x23 // 5 frames +#define BatDataBaseG4 0x28 // 3 frames - Temperature data +#define BatDataBaseG5 0x2B // 11 frames +#define BatDataBaseG6 0x36 // 4 frames +#define BatDataBaseG7 0x3A +#define BatDataBufMax 0x196 // 7 x 3A bytes +#define VP230Sleep 0 // Set to 0 if using VP231 (sleep disables RX) \ No newline at end of file
--- a/displayModes.cpp Wed Apr 10 13:31:35 2013 +0000 +++ b/displayModes.cpp Sun Jul 21 23:59:00 2013 +0000 @@ -4,6 +4,163 @@ char sTemp1[40]; char sTemp2[16]; +void mainDisplay (bool force, bool showButtons){ + unsigned short gids, SOC_x10, packV_x2, tireP; + static unsigned short lgids=0, lSOC=0, lSOH=0, lpackV_x2=0, ltireP=0, maxPS=0; + static float lmaxTemp=0; + static float lkW=0, laccV=0, lmpkWh=0; + static unsigned long lAh=0; + CANMessage msg; + + msg = lastMsg[indexLastMsg[0x5bc]]; //Get gids + gids = (msg.data[0]<<2)+(msg.data[1]>>6); + msg = lastMsg[indexLastMsg[0x55b]]; //Get SOC + SOC_x10 = (msg.data[0]<<2)+(msg.data[1]>>6); + msg = lastMsg[indexLastMsg[0x1db]]; //Get pack volts + packV_x2 = (msg.data[2]<<2)+(msg.data[3]>>6); + msg = lastMsg[indexLastMsg[0x385]]; //Get tire pressure + tireP = msg.data[2]+msg.data[3]+msg.data[4]+msg.data[5]; + + tt.background(Navy); + tt.foreground(Yellow); + tt.set_font((unsigned char*) Arial28x28); + if(force) tt.cls(); + if(skin==ttSkin){ + if(force||gids!=lgids||mpkWh[dtePeriod]!=lmpkWh){ + tt.locate(10,10); + printf("%4d gids \n",gids); + if(debugMode){ + if(pointerSep>maxPS){maxPS=pointerSep;} + tt.locate(10,70); + printf("%3d sep %3d max\n",pointerSep,maxPS); + } + tt.locate(10,40); + printf("%4.1f kWh \n",(float)(gids-5)*0.075); + tt.set_font((unsigned char*) SCProSB31x55); + tt.foreground(Green); + tt.locate(60,96); + printf("%4.1f %s \n",convertDistance(mpkWh[dtePeriod]*((float)(gids-5)*.075)),distanceUnit()); //LM - add metric conversion + lgids=gids; + lmpkWh=mpkWh[dtePeriod]; + tt.foreground(Yellow); + tt.set_font((unsigned char*) Arial28x28); + } + if(force||tireP!=ltireP){ + tt.foreground(LightGrey); + if(msg.data[6]&0x80){ + tt.locate(40,150); + printf("%3.1f\n",(float)msg.data[2]/4); + } + if(msg.data[6]&0x40){ + tt.locate(114,150); + printf("%3.1f\n",(float)msg.data[3]/4); + } + if(msg.data[6]&0x20){ + tt.locate(40,178); + printf("%3.1f\n",(float)msg.data[4]/4); + } + if(msg.data[6]&0x10){ + tt.locate(114,178); + printf("%3.1f\n",(float)msg.data[5]/4); + } + tt.foreground(Yellow); + ltireP=tireP; + } + if(force||SOC_x10!=lSOC){ + tt.locate(200,10); + printf("%4.1f%s\n",(float)SOC_x10/10,"% "); + lSOC=SOC_x10; + } + if(force||packV_x2!=lpackV_x2){ + tt.locate(200,206); + printf("%4.1fV \n",(float)packV_x2/2); + lpackV_x2=packV_x2; + } + if(force||maxTemp!=lmaxTemp){ + tt.locate(200,176); + printf("%4.1f%s\n",convertTemperature(maxTemp),temperatureUnit()); + lmaxTemp=maxTemp; + } + if(force||accV!=laccV){ + tt.locate(20,206); + printf("%3.1fV \n",accV); + laccV=accV; + } + if(force||kW[0]!=lkW){ + if(kW[0]<-10){ //Right justify + tt.locate(171,40); + printf("%4.2fkW\n",kW[0]); + } else if (kW[0]<0){ + tt.locate(171,40); + printf(" %4.2fkW\n",kW[0]); + } else if (kW[0]<10){ + tt.locate(165,40); + printf(" %4.2fkW\n",kW[0]); + } else { + tt.locate(165,40); + printf(" %4.2fkW\n",kW[0]); + } + lkW=kW[0]; + } + tt.foreground(LightGrey); + if(force||Ah_x10000!=lAh){ + tt.locate(10,70); + printf("% 4.2fAh \n",(float)Ah_x10000/10000); + lAh=Ah_x10000; + } + if(force||SOH_x100!=lSOH){ + tt.locate(206,70); + printf("%4.1f%s\n",(float)SOH_x100/100,"% "); + lSOH=SOH_x100; + } + }else {//if(skin==ggSkin){ + if(force||gids!=lgids){ + tt.locate(10,10); + printf("%4d GIDs \n",gids); + + tt.locate(40,40); // gg - add GIDs Percent of 281 + printf("%4.1f%s \n", (float)gids*0.355872, "% ") ; + tt.locate(20,70); + printf("%4.1f kwh \n",(float)gids*0.075); // gg - closer to usable + + tt.set_font((unsigned char*) SCProSB31x55); + tt.foreground(Green); + //tt.locate(60,96); + tt.locate(60,116); // gg - move down a little + printf("%4.1f %s \n",convertDistance((float)(gids-5)*0.31),distanceUnit()); // Approx for now - LM added metric + lgids=gids; + tt.foreground(Yellow); + tt.set_font((unsigned char*) Arial28x28); + } + + if(force||SOC_x10!=lSOC){ + tt.locate(200,10); + printf("%4.1f%s\n",(float)SOC_x10/10,"% "); + lSOC=SOC_x10; + } + if(force||packV_x2!=lpackV_x2){ + tt.locate(200,200); + printf("%4.1fV \n",(float)packV_x2/2); + lpackV_x2=packV_x2; + } + if(force||accV!=laccV){ + tt.locate(20,200); + printf("%3.1fV \n",accV); + laccV=accV; + } + if(force||kW[0]!=lkW){ + tt.locate(160,40); // gg - move left to keep from wrap + printf("%3.2fkw \n",kW[0]); // use small w to save space + lkW=kW[0]; + } + } + if(led4){ + tt.fillcircle(310,10,6,Red); + }else{ + tt.fillcircle(310,10,6,Navy); + } +} + void printLast (bool force, bool showButtons){ CANMessage msg; tt.locate(0,6); @@ -15,7 +172,7 @@ msg = lastMsg[i+indexOffset]; printf("%03x : %02x %02x %02x %02x %02x %02x %02x %02x \n",msg.id,msg.data[0],msg.data[1],msg.data[2],msg.data[3],msg.data[4],msg.data[5],msg.data[6],msg.data[7]); } - if((sMode==1)&&showButtons){ + if(showButtons){ tt.foreground(Yellow); tt.background(DarkCyan); tt.set_font((unsigned char*) Arial12x12); @@ -43,11 +200,10 @@ i++; }// if changed }while(i<19&&j<99); - if((sMode==1)&&showButtons){ + if(showButtons){ tt.foreground(Yellow); tt.background(DarkCyan); tt.set_font((unsigned char*) Arial12x12); - showButton(0,0," <up>","",4,4); showButton(2,0," <down>","",4,4); showButton(1,0," Reset","Baseline",4,4); @@ -55,121 +211,124 @@ } void printLog (bool force, bool showButtons){ - static unsigned char lastDisplayLoc = 0; - if(force||displayLoc!=lastDisplayLoc){ //only update if changed + static unsigned char lastldl = 0; + unsigned char ldl=displayLoc; + if(force||ldl!=lastldl){ //only update if changed tt.foreground(Amber); tt.background(Black); tt.cls(); tt.locate(0,6); tt.set_font((unsigned char*) Arial12x12); for(int i=0; i<19; i++){ - printf("%s",displayLog[displayLoc]); - displayLoc=displayLoc>17?0:displayLoc+1; + printf("%s",displayLog[ldl]); + ldl=ldl>17?0:ldl+1; } } - lastDisplayLoc=displayLoc; + lastldl=ldl; } -void mainDisplay (bool force, bool showButtons){ - unsigned short gids, SOC, packV; - static unsigned short lgids=0, lSOC=0, lpackV=0; - static float lkW=0, laccV=0, lmpkWh=0; +void tripDisplay (bool force, bool showButtons){ + static float lkWh=0; + tt.background(White); + if(force){ + tt.cls(); + } + if(force||(int)(lkWh*100)!=(int)(kWh_trip[0]*100)){ //only update if changed + tt.foreground(Navy); + tt.set_font((unsigned char*) Arial28x28); + tt.locate(6,210); + printf("kWh : %s : Eff\n",distanceUnit()); + for(int i=0; i<3; i++){ + tt.locate(6,20+i*60); + printf("%3.2f : %3.1f : %2.1f\n",kWh_trip[i],convertDistance(miles_trip[i]),convertDistance(miles_trip[i])/kWh_trip[i]); + } + } + if(showButtons){ + tt.set_font((unsigned char*) Arial12x12); + tt.background(DarkCyan); + tt.foreground(Yellow); + showButton(3,1," Reset"," A",4,4); + showButton(3,2," Reset"," B",4,4); + lkWh=kWh_trip[0]; + } +} + +void healthDisplay (bool force, bool showButtons){ + unsigned short gids, SOC_x10; + static unsigned short lgids=0, lSOC=0, lSOH=0; + static float lmaxTemp=0, lresr=0, lunlV=0; + static unsigned long lAh=0; CANMessage msg; msg = lastMsg[indexLastMsg[0x5bc]]; //Get gids gids = (msg.data[0]<<2)+(msg.data[1]>>6); msg = lastMsg[indexLastMsg[0x55b]]; //Get SOC - SOC = (msg.data[0]<<2)+(msg.data[1]>>6); - msg = lastMsg[indexLastMsg[0x1db]]; //Get pack volts - packV = (msg.data[2]<<2)+(msg.data[3]>>6); + SOC_x10 = (msg.data[0]<<2)+(msg.data[1]>>6); - tt.background(Navy); + tt.background(Blue); tt.foreground(Yellow); tt.set_font((unsigned char*) Arial28x28); if(force) tt.cls(); - if(skin==ttSkin){ - if(force||gids!=lgids||mpkWh[dtePeriod]!=lmpkWh){ - tt.locate(10,10); - printf("%4d gids \n",gids); - tt.locate(10,40); - printf("%4.1f kWh \n",(float)(gids-5)*0.075); - tt.set_font((unsigned char*) SCProSB31x55); - tt.foreground(Green); - tt.locate(60,96); - printf("%4.1f mi \n",mpkWh[dtePeriod]*((float)(gids-5)*.075)); - lgids=gids; - lmpkWh=mpkWh[dtePeriod]; - tt.foreground(Yellow); - tt.set_font((unsigned char*) Arial28x28); - } - if(force||SOC!=lSOC){ - tt.locate(200,10); - printf("%4.1f%s\n",(float)SOC/10,"% "); - lSOC=SOC; - } - if(force||packV!=lpackV){ - tt.locate(200,200); - printf("%4.1fV \n",(float)packV/2); - lpackV=packV; - } - if(force||accV!=laccV){ - tt.locate(20,200); - printf("%3.1fV \n",accV); - laccV=accV; - } - if(force||kW[0]!=lkW){ - tt.locate(180,40); - printf("%3.2fkW \n",kW[0]); - //printf("%3.1f mpkWh \n",mpkWh[0]); - lkW=kW[0]; - } - }else {//if(skin==ggSkin){ - if(force||gids!=lgids){ - tt.locate(10,10); - printf("%4d GIDs \n",gids); - - tt.locate(40,40); // gg - add GIDs Percent of 281 - printf("%4.1f%s \n", (float)gids*0.355872, "% ") ; - tt.locate(20,70); - //printf("%4.1f kWh \n",(float)gids*0.08); // is input, not usable - printf("%4.1f kwh \n",(float)gids*0.075); // gg - closer to usable - - tt.set_font((unsigned char*) SCProSB31x55); - tt.foreground(Green); - //tt.locate(60,96); - tt.locate(60,116); // gg - move down a little - printf("%4.1f mi \n",(float)(gids-5)*0.31); // Approx for now - lgids=gids; - tt.foreground(Yellow); - tt.set_font((unsigned char*) Arial28x28); - } - - if(force||SOC!=lSOC){ - tt.locate(200,10); - printf("%4.1f%s\n",(float)SOC/10,"% "); - lSOC=SOC; - } - if(force||packV!=lpackV){ - tt.locate(200,200); - printf("%4.1fV \n",(float)packV/2); - lpackV=packV; - } - if(force||accV!=laccV){ - tt.locate(20,200); - printf("%3.1fV \n",accV); - laccV=accV; - } - if(force||kW[0]!=lkW){ - tt.locate(160,40); // gg - move left to keep from wrap - printf("%3.2fkw \n",kW[0]); // use small w to save space - lkW=kW[0]; - } + + /*if(force||tock){ // for esr debug + tt.locate(10,10); + printf("%d %d amps\n",Imax,Imin); + tt.locate(10,40); + printf(" %4.1f %4.1f\n",incRmax/2,incRmin/2); + tt.locate(10,70); + printf(" %4.1f %4.1f\n",redRmax/2,redRmin/2); + tt.locate(10,100); + printf(" %4.1f %4.1f\n",curRmax/2,curRmin/2); + //tt.locate(10,130); + curRmin=1000; + curRmax=0; + incRmin=1000; + incRmax=0; + redRmin=1000; + redRmax=0; + Imax=-1000; + Imin=1000; + }*/ + if(force||gids!=lgids){ + tt.locate(10,10); + printf("%4d gids \n",gids); + lgids=gids; + } + if(force||SOC_x10!=lSOC){ + tt.locate(10,40); + printf(" %4.1f%s \n",(float)SOC_x10/10,"% SOC"); + lSOC=SOC_x10; + } + if(force||SOH_x100!=lSOH){ + tt.locate(10,70); + printf(" %4.1f%s \n",(float)SOH_x100/100,"% SOH"); + lSOH=SOH_x100; + } + if(force||Ah_x10000!=lAh){ + tt.locate(10,100); + printf(" %4.2f Ah cap \n",(float)Ah_x10000/10000); + lAh=Ah_x10000; + } + if(force||maxTemp!=lmaxTemp){ + tt.locate(10,130); + printf(" %4.1f %s (max) \n",convertTemperature(maxTemp),temperatureUnit()); + lmaxTemp=maxTemp; + } + if(force||unloadedV_x2!=lunlV){ + tt.locate(10,160); + printf(" %4.1f V \n",unloadedV_x2/2); + lunlV=unloadedV_x2; + } + if(force||Resr!=lresr){ + tt.locate(10,190); + printf(" %3.0f mOhms \n",Resr*1000); + lresr=Resr; } } void braking (bool force, bool showButtons, bool prdata=false){ unsigned long targetBraking, regenBraking; - static unsigned long maxTarget = 1000, maxRegen = 1000, tarDivReg = 1000; + static unsigned long maxTarget = 1000, maxRegen = 1000, tardivreg_x1000 = 1000; unsigned long temp; static unsigned char lastPressure[4] = {200,200,200,200}; unsigned char i,r,t; @@ -191,7 +350,7 @@ steering = (msg.data[1]<<8)+msg.data[0]; if(skin==ttSkin){ - s= (unsigned short) ((steering/10)+160)%310; // this modulo wraps display + s= (unsigned short) ((steering/10)+155)%310; // this modulo wraps display }else{// if(skin==ggSkin){ // do not go off screen left or right. gg - steering short ss = (short) ((steering/15)+160); // less gain 10 -> 15 @@ -201,18 +360,6 @@ } //-------------- - msg = lastMsg[indexLastMsg[0x1cb]]; //Get Target and Regen - regenBraking = (msg.data[0]<<3)+(msg.data[1]>>5); - targetBraking = (msg.data[2]<<3)+(msg.data[3]>>5); - if ((targetBraking>50)&&(regenBraking>50)){ - temp = targetBraking; - temp *= 1000; - temp /= regenBraking; - if (temp<tarDivReg) tarDivReg=temp; - } - if (targetBraking>maxTarget) maxTarget=targetBraking; - if (regenBraking>maxRegen) maxRegen=regenBraking; - msg = lastMsg[indexLastMsg[0x1ca]]; //Get brake pressure tt.background(Navy); if (force) { @@ -270,44 +417,63 @@ } } - temp = targetBraking; - temp *=200; - temp /= maxTarget; - t = (char) temp; - if (t>200) t=200; - temp = regenBraking; - temp *= tarDivReg; - temp /= maxTarget; - temp /= 5; - r = (char) temp; - if (r>200) r=200; - if(lr!=r&&prdata){ - tt.foreground(Yellow); - tt.set_font((unsigned char*) Arial28x28); - tt.locate(100,40); - printf("%d %d \n",regenBraking,maxRegen); - tt.locate(100,70); - printf("%3.1f (%3.1f%s) \n",(float)tarDivReg/10,(float)regenBraking*tarDivReg/targetBraking/10,"%"); - } - if(lt!=t&&prdata){ - tt.foreground(Yellow); - tt.set_font((unsigned char*) Arial28x28); - tt.locate(100,10); - printf("%d %d \n",targetBraking,maxTarget); + msg = lastMsg[indexLastMsg[0x1cb]]; //Get Target and Regen + regenBraking = (msg.data[0]<<3)+(msg.data[1]>>5); + targetBraking = (msg.data[2]<<3)+(msg.data[3]>>5); + msg = lastMsg[indexLastMsg[0x421]]; //Get Drive Mode + if (msg.data[0]==0x18) { // In Neutral + regenBraking = 0; // No regen when in Neutral } - if (r>t) t=r; //Should never happen - if((lr!=r||lt!=t)&&!prdata){ - tt.fillrect(190,10,260,239-t,Navy); - tt.fillrect(190,239-t,260,239-r,Red); - tt.fillrect(190,239-r,260,239,Green); + + if (targetBraking<2045){ + if ((targetBraking>50)&&(regenBraking>50)){ + temp = targetBraking; + temp *= 1000; + temp /= regenBraking; + if (temp<tardivreg_x1000) tardivreg_x1000=temp; + } + if (targetBraking>maxTarget) maxTarget=targetBraking; + if (regenBraking>maxRegen) maxRegen=regenBraking; + temp = targetBraking; + temp *=200; + temp /= maxTarget; + t = (char) temp; + if (t>200) t=200; + temp = regenBraking; + temp *= tardivreg_x1000; + temp /= maxTarget; + temp /= 5; // 1000/200=5 + r = (char) temp; + if (r>200) r=200; + if(lr!=r&&prdata){ + tt.foreground(Yellow); + tt.set_font((unsigned char*) Arial28x28); + tt.locate(100,40); + printf("%d %d \n",regenBraking,maxRegen); + tt.locate(100,70); + printf("%3.1f (%3.1f%s) \n",(float)tardivreg_x1000/10,(float)regenBraking*tardivreg_x1000/targetBraking/10,"%"); + } + if(lt!=t&&prdata){ + tt.foreground(Yellow); + tt.set_font((unsigned char*) Arial28x28); + tt.locate(100,10); + printf("%d %d \n",targetBraking,maxTarget); + } + if (r>t) t=r; //Should never happen + if((lr!=r||lt!=t)&&!prdata){ + tt.fillrect(190,10,260,239-t,Navy); + tt.fillrect(190,239-t,260,239-r,Red); + tt.fillrect(190,239-r,260,239,Green); + } + lt=t; + lr=r; } - lt=t; - lr=r; } void cpData(bool force, bool showButtons){ short unsigned max, min, jv, i, bd; unsigned avg; + static char step=0; // counter to allow incremental update if(force){ tt.foreground(White); tt.background(Navy); @@ -316,7 +482,7 @@ min=9999; avg=0; for(i=0; i<96; i++){ - bd=(battData[i*2+3]<<8)+battData[i*2+4]; + bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; avg+=bd; if(bd>max) max=bd; if(bd<min) min=bd; @@ -327,29 +493,48 @@ } else { // Only compute judgement value if min cellpair meets <= 3712mV requirement jv=0; } - tt.cls(); - tt.locate(0,6); - printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n",max,min,avg,jv,battData[224+5],battData[224+8],battData[224+11],battData[224+14]); - tt.locate(0,36); - for(i=0; i<16; i++){ - printf("%02d-%02d : %04d %04d %04d %04d %04d %04d\n",i*6+1,i*6+6,(battData[i*12+3]<<8)+battData[i*12+4],(battData[i*12+5]<<8)+battData[i*12+6],(battData[i*12+7]<<8)+battData[i*12+8],(battData[i*12+9]<<8)+battData[i*12+10],(battData[i*12+11]<<8)+battData[i*12+12],(battData[i*12+13]<<8)+battData[i*12+14]); + + char* sTemperatureUnit = temperatureUnit(); + switch(step){ + case 0: + tt.cls(); + showCP=true; + break; + case 1: + tt.locate(0,6); + printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %2.0f%s %2.0f%s %2.0f%s %2.0f%s\n\n", + max,min,avg,jv, convertTemperature(battData[BatDataBaseG4*7+5]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+8]),sTemperatureUnit, + convertTemperature(battData[BatDataBaseG4*7+11]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+14]),sTemperatureUnit); + tt.rect(8+0*41,16,40+0*41,28,Green); + tt.rect(8+1*41,16,40+1*41,28,Yellow); + //tt.rect(8+2*41,16,40+2*41,28,White); + tt.rect(8+3*41,16,40+3*41,28,Red); + break; + default: + tt.locate(0,36+(step-2)*48); + for(i=(step-2)*4; i<(step-1)*4; i++){ + printf("%02d-%02d : %04d %04d %04d %04d %04d %04d\n", + i*6+1,i*6+6, + (battData[BatDataBaseG2*7+i*12+3]<<8)+battData[BatDataBaseG2*7+i*12+4],(battData[BatDataBaseG2*7+i*12+5]<<8)+battData[BatDataBaseG2*7+i*12+6], + (battData[BatDataBaseG2*7+i*12+7]<<8)+battData[BatDataBaseG2*7+i*12+8],(battData[BatDataBaseG2*7+i*12+9]<<8)+battData[BatDataBaseG2*7+i*12+10], + (battData[BatDataBaseG2*7+i*12+11]<<8)+battData[BatDataBaseG2*7+i*12+12],(battData[BatDataBaseG2*7+i*12+13]<<8)+battData[BatDataBaseG2*7+i*12+14]); + } + for(i=(step-2)*24; i<(step-1)*24; i++){ + bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; + if(bd>0){ + if(bd==max) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Green); + //if(bd==avg) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,White); + if(bd==min) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Yellow); + if(bd<jv) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Red); + } + } } - tt.rect(8+0*41,16,40+0*41,28,Green); - tt.rect(8+1*41,16,40+1*41,28,Yellow); - //tt.rect(8+2*41,16,40+2*41,28,White); - tt.rect(8+3*41,16,40+3*41,28,Red); - for(i=0; i<96; i++){ - bd=(battData[i*2+3]<<8)+battData[i*2+4]; - if(bd>0){ - if(bd==max) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Green); - //if(bd==avg) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,White); - if(bd==min) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Yellow); - if(bd<jv) tt.rect(58+(i%6)*41,34+(int)(i/6)*12,90+(i%6)*41,46+(int)(i/6)*12,Red); - } + step=step<5?step+1:0; + if(step==0){ + showCP=false; } - showCP=false; } - if((sMode==1)&&showButtons){ + if(showButtons){ tt.foreground(Yellow); tt.background(DarkCyan); tt.set_font((unsigned char*) Arial12x12); @@ -359,6 +544,51 @@ } //---------------- +// gg - index +void showIndex(bool force, bool showButtons){ + + if(force){ + tt.foreground(White); + tt.background(Navy); + //tt.set_font((unsigned char*) Arial12x12_prop); // select the font + + tt.cls(); + + // add the buttons to GoTo to other screens + + tt.foreground(Yellow); + tt.background(DarkCyan); + tt.set_font((unsigned char*) Arial12x12); + + // top row + showButton(0,0," GoTo"," Main",4,4); + showButton(1,0," GoTo"," Brake",4,4); + showButton(2,0," GoTo"," EFF",4,4); + showButton(3,0," GoTo","Health",4,4); + // middle row + showButton(0,1," GoTo","CP Data",4,4); + showButton(1,1," GoTo","CP Hist",4,4); + showButton(2,1," GoTo","CP Bars",4,4); + showButton(3,1," GoTo"," Config",4,4); + // bottom (not Nav) row + showButton(0,2," GoTo","Playback",4,4); + //showButton(1,2," GoTo","Set Time",4,4); + showButton(2,2," GoTo"," Log",4,4); + showButton(3,2," GoTo"," Trip",4,4); + + showCP=false; + } + + if(sMode==1&&showButtons){ + tt.foreground(Yellow); + tt.background(DarkCyan); + tt.set_font((unsigned char*) Arial12x12); + + // do nothing here? + } +} + +//---------------- // gg - cpbars void cpBarPlot(bool force, bool showButtons){ short unsigned max, min, jv, i, bd; @@ -375,7 +605,7 @@ // calc each cell-pair voltage, find max and min for(i=0; i<96; i++){ - bd=(battData[i*2+3]<<8)+battData[i*2+4]; + bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; nBar[i] = bd; // init to bar height avg+=bd; if(bd>max) max=bd; @@ -429,11 +659,14 @@ } // values, for now + // BatDataBaseG4 * 7 = 280 tt.locate( 0, yWinMax+40 ); - printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n", - max,min,avg,jv, - battData[224+5],battData[224+8], - battData[224+11],battData[224+14]); + char* sTemperatureUnit = temperatureUnit(); + printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %2.0f%s %2.0f%s %2.0f%s %2.0f%s\n\n", + max,min,avg,jv, convertTemperature(battData[BatDataBaseG4*7+5]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+8]),sTemperatureUnit, + convertTemperature(battData[BatDataBaseG4*7+11]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+14]),sTemperatureUnit); + //printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n", + // max,min,avg,jv, battData[BatDataBaseG4*7+5],battData[BatDataBaseG4*7+8], battData[BatDataBaseG4*7+11],battData[BatDataBaseG4*7+14]); // label the X axis (approximate) tt.locate( 2, yWinMax+5); printf("%04d", min ); @@ -452,8 +685,11 @@ if( height > 100 ) height = 100 ; // clip tops // draw the bar, is always inside x-window - tt.fillrect( xPos,yWinMax-height, xPos+nBarWidth-1,yWinMax, Green); - + if (shunt[i]){ + tt.fillrect( xPos,yWinMax-height, xPos+nBarWidth-1,yWinMax, Red); + } else { + tt.fillrect( xPos,yWinMax-height, xPos+nBarWidth-1,yWinMax, Green); + } // tic mark the y axis each 5 if(i%5 == 4){ tt.line( xPos,yWinMax+2, xPos,yWinMax+5, White); // a white tick mark @@ -496,7 +732,7 @@ min=9999; avg=0; for(i=0; i<96; i++){ - bd=(battData[i*2+3]<<8)+battData[i*2+4]; + bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; avg+=bd; if(bd>max) max=bd; if(bd<min) min=bd; @@ -534,7 +770,7 @@ // do the bin counting for(int i=0; i<96; i++){ - bd=(battData[i*2+3]<<8)+battData[i*2+4] - min ; + bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4] - min ; if( bd > iBinValMax ) bd = iBinValMax ; nBin[bd] ++ ; } @@ -553,8 +789,14 @@ } // the values, for now + // BatDataBaseG4 * 7 = 280 tt.locate( 0, yWinMax+40 ); - printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n",max,min,avg,jv,battData[224+5],battData[224+8],battData[224+11],battData[224+14]); + char* sTemperatureUnit = temperatureUnit(); + printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %2.0f%s %2.0f%s %2.0f%s %2.0f%s\n\n", + max,min,avg,jv, convertTemperature(battData[BatDataBaseG4*7+5]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+8]),sTemperatureUnit, + convertTemperature(battData[BatDataBaseG4*7+11]),sTemperatureUnit,convertTemperature(battData[BatDataBaseG4*7+14]),sTemperatureUnit); + //printf(" MAX MIN AVG CVLI T1 T2 T3 T4\n %04d %04d %04d %04d %02dC %02dC %02dC %02dC\n\n", + // max,min,avg,jv, battData[BatDataBaseG2*7+BatDataBaseG4*7+5],battData[BatDataBaseG2*7+BatDataBaseG4*7+8], battData[BatDataBaseG2*7+BatDataBaseG4*7+11],battData[BatDataBaseG2*7+BatDataBaseG4*7+14]); //--------------- // show the bars @@ -590,7 +832,7 @@ } //--------------- -void config1(bool force, bool showButtons){ +void config(bool force, bool showButtons){ if (force) { tt.background(Black); tt.cls(); @@ -599,14 +841,23 @@ tt.background(DarkCyan); tt.set_font((unsigned char*) Arial12x12); + //-------- top row -------- showButton(0,0,"Calibrate"," Touch",4,4); // gg - 4x4 - showButton(1,0," Reset","",4,4); + showButton(2,0," Save"," Config",4,4); + + // a button to step to the next skin + unsigned int nextSkin = skin + 1 ; + if( nextSkin > maxSkin ) nextSkin = 0 ; - showButton(2,0," Save"," Config",4,4); + if( nextSkin == ttSkin ) sprintf(sTemp1,"Skin TT"); + else if( nextSkin == ggSkin ) sprintf(sTemp1,"Skin GG"); + else sprintf(sTemp1,"Skin %d",nextSkin); + + showButton(3,0," Use",sTemp1,4,4); //------- second row ----- - if (logEn) { + if (logEn&&usbEn) { sprintf(sTemp1,"Disable"); } else { sprintf(sTemp1,"Enable"); @@ -627,6 +878,29 @@ sprintf(sTemp1,"Enable"); } showButton(2,1,sTemp1,"Batt Log",4,4); + + // add Enable/Disable Debug - debugMode + if (debugMode) { + sprintf(sTemp1," Disable"); + } else { + sprintf(sTemp1," Enable"); + } + showButton(3,1,sTemp1," Debug",4,4); + + if(metric) + showButton(0,2,"Imperial","",4,4); + else + showButton(0,2,"Metric","",4,4); + + showButton(1,2," Set"," Time",4,4); + if (showHealth) { + sprintf(sTemp1," Hide"); + } else { + sprintf(sTemp1," Show"); + } + showButton(2,2,sTemp1," Health",4,4); + showButton(3,2,"Update","Firmware",4,4); + } void pbScreen(bool force, bool showButtons){ @@ -672,7 +946,7 @@ tt.set_font((unsigned char*) Arial12x12); strftime(sTemp1, 32, "%a %m/%d/%Y %X \n", &t); printf("%s",sTemp1); - if((sMode==1)&&showButtons){ + if(showButtons){ switch(dtMode){ case 0: sprintf(sTemp1,"Year"); @@ -692,15 +966,11 @@ case 5: sprintf(sTemp1,"Second"); break; - case 6: - sprintf(sTemp1,"Select"); - break; default: break; } tt.background(DarkCyan); - showButton(0,1,sTemp1,"",4,4); - + showButton(0,1,sTemp1,"",4,4); showButton(1,1," UP","",4,4); showButton(2,1," DOWN","",4,4); } @@ -708,35 +978,26 @@ } void dteDisplay(bool force, bool showButtons, bool showMiles){ - unsigned short i,x,y,lx,ly,gids,radius,color; + unsigned short i,x,y,lx,ly,gids,radius,color,r,t; + unsigned char toVal; static unsigned short lgids=0; static unsigned char leff[39]={0}; CANMessage msg; + unsigned long targetBraking, regenBraking, temp; + static unsigned long maxTarget = 1000, maxRegen = 1000, tardivreg_x1000 = 1000; + static unsigned char lr=0, lt=0; msg = lastMsg[indexLastMsg[0x5bc]]; //Get gids gids = (msg.data[0]<<2)+(msg.data[1]>>6); if(gids==0){ - gids=281; + gids=281; // Display new, fully charged capacity until real data obtained } tt.background(Navy); tt.foreground(Yellow); if(force){ + tt.set_font((unsigned char*) Arial12x12); tt.cls(); - } - if(force||lgids!=gids){ - tt.set_font((unsigned char*) Arial12x12); - for(i=0;i<10;i++){ - y=200-i*20; - tt.locate(10,y-8); - if (showMiles){ - printf("%3.0f\n",i*((float)(gids-5)*.075)); - }else{ - printf("%d.0\n",i); - } - tt.line(40,y,280,y,DarkGrey); - } - x=50+0*6; tt.locate(x-10,226); printf("sec\n"); @@ -748,7 +1009,7 @@ x=50+18*6; tt.locate(x-10,226); printf("hour\n"); - tt.line(x,10,x,220,DarkGrey); + tt.line(x,10,x,220,DarkGrey); x=50+25*6; tt.locate(x-10,226); printf("day\n"); @@ -758,19 +1019,35 @@ printf("mon\n"); tt.line(x,10,x,220,DarkGrey); x=50+38*6; - tt.locate(x-10,226); - printf("year\n"); - tt.line(x,10,x,220,DarkGrey); + //tt.locate(x-10,226); + //printf("year\n"); + //tt.line(x,10,x,220,DarkGrey); + toVal=33; + } else { + toVal=24;// no need to constantly update the long tc values + } + if(force||lgids!=gids){ // update Y axis when kWh changes + //tt.set_font((unsigned char*) Arial12x12); + tt.set_font((unsigned char*) Arial24x23); + //for(i=0;i<10;i++){ + //y=200-i*20; + for(i=2;i<7;i++){ + y=200-(i-2)*40; + tt.locate(0,y-8); + if (showMiles){ + printf("%3.0f\n",convertDistance(i*((float)(gids-5)*.075))); // LM - Added metric support + //printf("%2.0f \n",i*((float)(gids-5)*.075)); + }else{ + printf("%d.0\n",i); + } + tt.line(48,y,toVal*6+56,y,DarkGrey); + } lgids=gids; } - if(force||updateDTE){ - for(i=0;i<10;i++){ - y=200-i*20; - if (y>60){ - tt.line(40,y,280,y,DarkGrey); - } else { - tt.line(40,y,150,y,DarkGrey); - } + if(tock||force){ + for(i=2;i<7;i++){ + y=200-(i-2)*40; + tt.line(40,y,158,y,DarkGrey); } x=50+0*6; @@ -779,53 +1056,74 @@ tt.line(x,10,x,220,DarkGrey); x=50+18*6; tt.line(x,10,x,220,DarkGrey); - x=50+25*6; - tt.line(x,180,x,220,DarkGrey); - x=50+32*6; - tt.line(x,180,x,220,DarkGrey); - x=50+38*6; - tt.line(x,180,x,220,DarkGrey); + //x=50+25*6; + //tt.line(x,60,x,220,DarkGrey); + //x=50+32*6; + //tt.line(x,60,x,220,DarkGrey); + //x=50+38*6; + //tt.line(x,60,x,220,DarkGrey); tt.set_font((unsigned char*) SCProSB31x55); tt.foreground(Green); if (showMiles){ - tt.locate(160,10); - printf("%4.1f \n",mpkWh[dtePeriod]*((float)(gids-5)*.075)); + float miles = mpkWh[dtePeriod]*((float)(gids-5)*.075); + miles = convertDistance(miles); // LM - Metric support + // Right justify + if (miles>99.9){ //space=18; num=31; . = 23 + tt.locate(161,8); + printf("%4.1f\n",miles); + } else if (miles>9.9){ + tt.locate(156,8); + printf(" %3.1f\n",miles); + } else { + tt.locate(151,8); + printf(" %2.1f\n",miles); + } + tt.foreground(Cyan); + tt.set_font((unsigned char*) Arial24x23); + tt.locate(198,70); + printf("%3.1f \n",mpkWh[dtePeriod]); } else { - tt.locate(180,10); - printf("%3.1f\n",mpkWh[dtePeriod]); + tt.locate(200,10); + printf("%3.1f \n",mpkWh[dtePeriod]); } lx=50; - ly=mpkWh[0]*20; - if(ly<200) { - ly=200-ly; - }else{ - ly=0; - } + ly=mpkWh[0]*40; if(dtePeriod==0){ radius=6; - color=Magenta; + color=Yellow; }else{ radius=2; color=Green; } + if(ly<100){ + ly=220; + color=Red; + }else if(ly<320) { + ly=320-ly; + }else{ + ly=0; + } tt.fillcircle(lx,leff[0],radius,Navy); tt.fillcircle(lx,ly,radius,color); - for(i=1;i<39;i++){ + for(i=1;i<toVal;i++){ x=50+i*6; - y=mpkWh[i]*20; - if(y<200) { - y=200-y; - }else{ - y=0; - } + y=mpkWh[i]*40; if(i==dtePeriod){ - radius=4; - color=Magenta; + radius=6; + color=Yellow; }else{ radius=2; color=Green; } + if(y<60){ + y=220; + color=Red; + }else if(y<280) { + y=280-y; + }else{ + y=0; + } tt.fillcircle(x,leff[i],radius,Navy); tt.line(x-6,leff[i-1],x,leff[i],Navy); leff[i-1]=ly; @@ -837,53 +1135,101 @@ ly=y; } leff[i-1]=y; - updateDTE=false; + } + + msg = lastMsg[indexLastMsg[0x1cb]]; //Get Target and Regen + regenBraking = (msg.data[0]<<3)+(msg.data[1]>>5); + targetBraking = (msg.data[2]<<3)+(msg.data[3]>>5); + msg = lastMsg[indexLastMsg[0x421]]; //Get Drive Mode + if (msg.data[0]==0x18) { // In Neutral + regenBraking = 0; // No regen when in Neutral + } + + if (targetBraking<2045){ + if ((targetBraking>50)&&(regenBraking>50)){ + temp = targetBraking; + temp *= 1000; + temp /= regenBraking; + if (temp<tardivreg_x1000) tardivreg_x1000=temp; + } + if (targetBraking>maxTarget) maxTarget=targetBraking; + if (regenBraking>maxRegen) maxRegen=regenBraking; + + temp = targetBraking; + temp *=200; + temp /= maxTarget; + t = (char) temp; + if (t>175) t=175; + temp = regenBraking; + temp *= tardivreg_x1000; + temp /= maxTarget; + temp /= 5; // 1000/200=5 + r = (char) temp; + if (r>175) r=175; + if (r>t) t=r; //Should never happen + if(lr!=r||lt!=t){ + tt.fillrect(264,64,310,239-t,Navy); + tt.fillrect(264,239-t,310,239-r,Red); + tt.fillrect(264,239-r,310,239,Green); + } + lt=t; + lr=r; } } void updateDisplay(char display){ - bool changed; + bool changed,showButtons; changed = dMode[display]!=lastDMode[display]; + showButtons = (display==whichTouched)&&(sMode==1); tt.set_display(display); switch (dMode[display]) { case logScreen: - printLog(changed,(display==whichTouched)); + printLog(changed,showButtons); break; case mainScreen: - mainDisplay(changed,(display==whichTouched)); + mainDisplay(changed,showButtons); break; case brakeScreen: - braking(changed,(display==whichTouched)); + braking(changed,showButtons); break; case dteScreen: - dteDisplay(changed,(display==whichTouched),true); + dteDisplay(changed,showButtons,true); break; case effScreen: - dteDisplay(changed,(display==whichTouched),false); + dteDisplay(changed,showButtons,false); break; case monitorScreen: - printLast(changed,(display==whichTouched)); + printLast(changed,showButtons); break; case changedScreen: - printChanged(changed,(display==whichTouched)); + printChanged(changed,showButtons); break; case cpScreen: - cpData(changed||showCP,(display==whichTouched)); + cpData(changed||showCP,showButtons); break; - case config1Screen: - config1(changed,(display==whichTouched)); + case configScreen: + config(changed,showButtons); break; case playbackScreen: - pbScreen(changed,(display==whichTouched)); + pbScreen(changed,showButtons); break; case dateScreen: - showDateTime(changed,(display==whichTouched)); + showDateTime(changed,showButtons); break; case cpHistScreen: // gg - hist - cpHistogram(changed||showCP,(display==whichTouched)); + cpHistogram(changed||showCP,showButtons); break; case cpBarScreen: // gg - cpbars - cpBarPlot(changed||showCP,(display==whichTouched)); + cpBarPlot(changed||showCP,showButtons); + break; + case indexScreen: + showIndex(changed,showButtons); + break; + case tripScreen: + tripDisplay(changed,showButtons); + break; + case healthScreen: + healthDisplay(changed,showButtons); break; default: if (changed){ @@ -902,10 +1248,11 @@ tt.set_font((unsigned char*) Arial12x12); showButton(0,tNavRow," <-Prev","",4,4); // gg - 4x4 + // col 1 see below + showButton(2,tNavRow," Go To"," Index",4,4); // gg - index showButton(3,tNavRow," Next->","",4,4); // gg - move next - - showButton(2,tNavRow," Go To"," Main",4,4); // gg - index + // col 1 in Nav row switch (dMode[display]) { case offScreen: sprintf(sTemp2," Off"); @@ -932,11 +1279,11 @@ sprintf(sTemp2,"DeltaMon"); break; case cpScreen: - sprintf(sTemp2,"Cell Pair"); + sprintf(sTemp2,"CP Data"); break; - case config1Screen: + case configScreen: sprintf(sTemp2," Config"); - break; + break; case playbackScreen: sprintf(sTemp2,"Playback"); break; @@ -949,6 +1296,15 @@ case cpBarScreen: // gg - cpbars sprintf(sTemp2,"CP Bars"); break; + case tripScreen: + sprintf(sTemp2," Trip"); + break; + case healthScreen: + sprintf(sTemp2,"Health"); + break; + case indexScreen: // gg - index + sprintf(sTemp2," Index"); + break; } showButton(1,tNavRow," Select",sTemp2,4,4); @@ -981,7 +1337,8 @@ break; } } -} + tock=false; +} // updateDisplay //--------------------- // gg - highlight @@ -993,14 +1350,21 @@ x2=(column+1)*(320/columns)-btnGap/2; y1=row*(240/rows)+btnGap/2; y2=(row+1)*(240/rows)-btnGap/2; - /*if( tScn == 0 ) - // paint the whole button box - tt.fillrect(x1,y1,x2,y2,White); // DarkCyan); - else - tt.fillrect(x1,y1,x2,y2,Green); // DarkCyan);*/ + + tt.set_display(tScn); + + if( skin == ggSkin ){ + // paint the whole button box, for a better visual effect + // especially on a screen with a yellow background + if( tScn == 0 ) + tt.fillrect(x1,y1,x2,y2,White); // DarkCyan); + else + tt.fillrect(x1,y1,x2,y2,Green); // DarkCyan); + } else { + tt.fillrect(x1,y1,x2,y2,Green); // DarkCyan); + } // paint the outer pixel as a yellow frame - tt.set_display(tScn); tt.rect(x1,y1,x2,y2,Yellow) ; // DarkCyan); } @@ -1022,97 +1386,34 @@ printf("%s\n",text2); } -//------------- -// below is braking screen normalized to power rather than force -// changed to force since power had too large a dynamic range -/*void braking (bool force, bool showButtons, bool prdata=false){ - unsigned long targetBraking, regenBraking, speed; - static unsigned long maxTarget = 20000, maxRegen = 20000, tarDivReg = 1000; - short rpm; - unsigned long temp; - static unsigned char lastPressure[4] = {200,200,200,200}; - unsigned char i,r,t; - static unsigned char lr, lt; - CANMessage msg; - msg = lastMsg[indexLastMsg[0x1cb]]; //Get Target and Regen - regenBraking = (msg.data[0]<<3)+(msg.data[1]>>5); - targetBraking = (msg.data[2]<<3)+(msg.data[3]>>5); - msg = lastMsg[indexLastMsg[0x176]]; //Get rpms - not sure what this is but scales to mph with .0725 - rpm = ((short)msg.data[0]<<8)+msg.data[1]; - speed =rpm>0?rpm>>3:-rpm>>3; //Take absolute to get speed; div8 - if ((targetBraking>2039)||(speed>200)) { //Filter weird messages - targetBraking = 0; - regenBraking = 0; - } else { - if ((targetBraking>50)&&(regenBraking>50)){ - temp = targetBraking; - temp *= 1000; - temp /= regenBraking; - if (temp<tarDivReg) tarDivReg=temp; - } - targetBraking *= speed; - regenBraking *= speed; - if (targetBraking>maxTarget) maxTarget=targetBraking; - if (regenBraking>maxRegen) maxRegen=regenBraking; +//The temps are stored as metric, distances as imperial... I'm assuming the input based on that - LM +float convertTemperature(float input) +{ + if (!metric) { + //convert! + float output = input *1.8f; + output += 32.0f; + return output; } - - msg = lastMsg[indexLastMsg[0x1ca]]; //Get brake pressure - tt.background(Navy); - if (force) { - tt.cls(); - tt.rect(0,111,170,239,White); - tt.line(0,207,170,207,White); - tt.line(0,175,170,175,White); - tt.line(0,143,170,143,White); - lastPressure[0] = 200; - lastPressure[1] = 200; - lastPressure[2] = 200; - lastPressure[3] = 200; - } - // plot bar graph for each wheel pressure - for (i=0; i<4; i++){ - if (msg.data[i]<239) { - if (msg.data[i]>lastPressure[i]){ - tt.fillrect(10+40*i,239-msg.data[i],40+40*i,239,Red); - } else if (msg.data[i]<lastPressure[i]) { - tt.fillrect(10+40*i,238-lastPressure[i],40+40*i,238-msg.data[i],Navy); - } - lastPressure[i]=msg.data[i]; - } + return input; +} +float convertDistance(float input) +{ + if (metric) { + return input / 0.62137f; } - - temp = targetBraking; - temp *=200; - temp /= maxTarget; - t = (char) temp; - if (t>200) t=200; - temp = regenBraking; - temp *= tarDivReg; - temp /= maxTarget; - temp /= 5; - r = (char) temp; - if (r>200) r=200; - if(lr!=r&&prdata){ - tt.foreground(Yellow); - tt.set_font((unsigned char*) Arial28x28); - tt.locate(100,40); - printf("%d %d \n",regenBraking,maxRegen); - tt.locate(100,70); - printf("%3.1f (%3.1f%s) \n",(float)tarDivReg/10,(float)regenBraking*tarDivReg/targetBraking/10,"%"); - } - if(lt!=t&&prdata){ - tt.foreground(Yellow); - tt.set_font((unsigned char*) Arial28x28); - tt.locate(100,10); - printf("%d %d \n",targetBraking,maxTarget); - } - if (r>t) t=r; //Should never happen - if((lr!=r||lt!=t)&&!prdata){ - tt.fillrect(200,10,300,239-t,Navy); - tt.fillrect(200,239-t,300,239-r,Red); - tt.fillrect(200,239-r,300,239,Green); - } - lt=t; - lr=r; -}*/ \ No newline at end of file + return input; +} +char* distanceUnit() +{ + if(metric) + return "km"; + return "mi"; +} +char* temperatureUnit() +{ + if(metric) + return "C"; + return "F"; +}
--- a/displayModes.h Wed Apr 10 13:31:35 2013 +0000 +++ b/displayModes.h Sun Jul 21 23:59:00 2013 +0000 @@ -15,9 +15,13 @@ extern char displayLog[20][40]; extern unsigned char displayLoc; extern char indexLastMsg[0x800]; -extern unsigned char battData[256]; + +//extern unsigned char battData[256]; // +extern unsigned char battData[BatDataBufMax]; // BatDataBufMax + extern bool showCP; extern bool logEn; +extern bool usbEn; extern bool yesBattLog; // gg - Batt Log extern bool repeatPoll; extern unsigned char dMode[2]; @@ -29,12 +33,31 @@ extern bool playbackEn; extern bool playbackOpen; extern float playbackInt; +extern float miles_trip[3]; +extern float kWh_trip[3]; extern float kW[39]; extern float mpkWh[39]; extern unsigned char whichTouched; extern unsigned char skin; extern unsigned char dtePeriod; -extern bool updateDTE; +extern bool tock; +extern DigitalOut led4; +extern unsigned char tNavRow; // gg - 4x4 +extern unsigned short pointerSep; // log write buffer pointer separation +extern float maxTemp; +extern unsigned long Ah_x10000; +extern unsigned long SOC_x10000; +extern unsigned short SOH_x100; +extern bool metric; +extern unsigned char tNavRow ; // gg - 4x4 +extern char revStr[7]; // gg - version +extern bool debugMode; +extern float unloadedV_x2; +extern float Resr; +extern bool shunt[96]; +extern float unloadedV_x2,Resr,curRmax,curRmin,redRmax,redRmin,incRmax,incRmin; +extern signed short Imax, Imin; +extern bool showHealth; extern "C" { void printLast (bool force, bool showButtons); @@ -46,9 +69,13 @@ void cpData(bool force, bool showButtons); void cpHistogram(bool force, bool showButtons); // gg - hist void cpBarPlot(bool force, bool showButtons); // gg - cpbars + void showIndex(bool force, bool showButtons); // gg - index void showDateTime(bool force, bool showButtons); void updateDisplay(char display); void showButton(unsigned char column, unsigned char row, char * text1, char * text2, unsigned char columns, unsigned char rows); void highlightButton(unsigned char column, unsigned char row, unsigned char tScn, unsigned char columns, unsigned char rows); - + float convertDistance(float input); // LM - Metric + float convertTemperature(float input); // LM - Metric + char* distanceUnit(); // LM - Metric + char* temperatureUnit(); // LM - Metric } \ No newline at end of file
--- a/main.cpp Wed Apr 10 13:31:35 2013 +0000 +++ b/main.cpp Sun Jul 21 23:59:00 2013 +0000 @@ -1,33 +1,39 @@ // main.cpp - +// //To Do: -// * USB device detect -// * Ability to update binary from the thumb-drive (requires file timestamp) // * Audible friction brake feedback // * User-configurable watchpoint -// * Better graphical DTE display with historic efficiency information considered and displayed // * Add 50% charge option -// * Tire Pressure Sensor display +// * Add coasting regen to regen/braking display +// * Change semilog efficiency graph to linear with 10 minute values +// * Add additional 79b bank readouts +// * Add ability to transfer settings config file to/from USB +// * Subtract accessory power from efficiency history (add back in when displaying) #include "mbed.h" #include "CAN.h" #include "beep.h" -#include "MSCFileSystem.h" +#include "ff.h" #include "PowerControl.h" #include "EthernetPowerControl.h" #include "utility.h" #include "displayModes.h" #include "TOUCH_TFTx2.h" +char revStr[7] = "128b"; // gg - revision string, max 6 characters + +FATFS USBdrive; LocalFileSystem local("local"); - +bool waitasec = true; +unsigned char wait5secs = 5; // to write to USB Flash Drives, or equivalent (SD card in Reader/Writer) -MSCFileSystem fs("usb"); // to write to a USB Flash Drive - +// class10 SDcard in Reader/Writer recommended +FRESULT mfr = f_mount(0,&USBdrive); time_t seconds ; Ticker autoPoll; Ticker playback; +Ticker msgReq; Timer timer; DigitalOut led1(LED1); @@ -45,22 +51,30 @@ PwmOut dled(p23); Beep spkr(p21); -bool logEn = false, logOpen = false; -bool yesBattLog = false ; // gg - Batt Log -unsigned char tNavRow = 3 ; // gg - 4x4 touch +bool debugMode = false; +bool usbEn = false; +bool logEn = false; +bool logOpen = false; +bool yesBattLog = true; // gg - Batt Log +unsigned char tNavRow = 3; // gg - 4x4 touch -FILE *cfile; -FILE *file; -char fileName[35] = "" ; -char writeBuffer[maxBufLen][13]; // buffer for USB write +FILE *hfile; // history file +FIL efile; // external usb file +FRESULT efr; // external file access flags +unsigned int bytesRW; +char fileName[35] = ""; +char writeBuffer[maxBufLen][13] __attribute__ ((section("AHBSRAM1"))); // buffer for USB write char indexLastMsg[0x800]={0}; // index table for last message CANMessage lastMsg[100]; // table to store last message of eachtype -unsigned char battData[256]={0}; + +unsigned char battData[BatDataBufMax]={0}; // 7 * 0x3D = BatDataBufMax + unsigned char msgChanged[100]; // inidcates which bytes changed char c; volatile int writePointer = 0; -volatile int secsNoMsg = 0; -volatile int secsNoTouch = 0; +int readPointer=0; +volatile unsigned short secsNoMsg = 0; +volatile unsigned short secsNoTouch = 0; volatile bool canIdle; volatile bool userIdle; bool touched=false; //flag to read touchscreen @@ -69,16 +83,18 @@ unsigned char dMode[2] = {mainScreen,brakeScreen}; //display mode unsigned char sMode = 0; // setup mode unsigned char lastDMode[2] = {0,0}; //last screen mode -unsigned char dtMode = 6; +unsigned char dtMode = 0; char displayLog[20][40]; unsigned char displayLoc = 0; +unsigned int fwCount=1; unsigned char indexOffset = 1; bool showCP = false; -bool pollCP = false; bool logCP = false; //Turbo3 -bool repeatPoll = false; +bool logOnce = false; +bool repeatPoll = true; bool headlights = false; bool tick = false; +bool ZeroSecTick = false; float ledHi = 0.8; // Bright LED value (until config file read) float ledLo = 0.1; // Dim LED value (until config file read) unsigned short pollInt = 300; // polling interval=5 minutes (until config file read) @@ -92,41 +108,48 @@ bool playbackEn = false; bool playbackOpen = false; //float playbackInt = 0.05; //read messages every 50 ms -float playbackInt = 0.005; //read messages every 50 ms +float playbackInt = 0.005; //read messages every 5 ms bool step = false; char header[5]; char data[8]; signed long motorRPM; -unsigned char skin = 0; +unsigned char skin = ttSkin ; unsigned char dtePeriod = 14; //ten minute averaging interval +float kWh_trip[3]={0}; +float miles_trip[3]={0}; float mph[39]={0}; float kW[39]={0}; float mpkWh[39]={0}; +float unloadedV_x2,Resr,curRmax,curRmin,redRmax,redRmin,incRmax,incRmin; +signed short Imax, Imin; // Logarithmic division scale (roughly - snapped to common units of time) float timeConstant[39] = {1, 1.58, 2.51, 3.98, 6.31, 10, 15.8, 25.1, 39.8, 60, // 1 minute 60*1.58, 60*2.51, 60*3.98, 60*6.31, 60*10, 60*15.8, 60*25.1, 60*39.8, 60*60, // 1 hour 60*60*1.58, 60*60*2.51, 60*60*3.98, 60*60*6.31, 60*60*10, 60*60*15.8, 60*60*24, // 1 day 60*60*24*1.58, 60*60*24*2.51, 60*60*24*3.98, 60*60*24*6.31, 60*60*24*10, 60*60*24*15.8, 60*60*24*30, // 1 month 60*60*24*39.8, 60*60*24*63.1, 60*60*24*100, 60*60*24*158, 60*60*24*251, 60*60*24*365}; // 1 year -bool updateDTE = false; +bool tock = false; +unsigned short pointerSep; +unsigned char reqMsgCnt = 99; +unsigned long Ah_x10000 = 0; +unsigned long SOC_x10000 = 0; +unsigned short SOH_x100 = 0; +float maxTemp = 0; +bool metric = false; +bool shunt[96]={0}; +bool charging=false; +bool showHealth=false; +unsigned char saveDmode=99; +bool moving=false; int main() { - int readPointer=0; + //can1SleepMode.mode(OpenDrain); + //can2SleepMode.mode(OpenDrain); char sTemp[40]; unsigned long secs; unsigned char i,j,display=0,lwt=0; point lastTouch; float average; - - can1.monitor(true); // set to snoop mode - can2.monitor(true); // set to snoop mode - can1.frequency(500000); - can2.frequency(500000); - can1SleepMode = 1; // Turn on Monitor_only Mode - can2SleepMode = 1; // Turn on Monitor_only Mode - can1.attach(&recieve1); - can2.attach(&recieve2); - tt.set_orientation(1); tt.background(Black); tt.set_display(2); // select both displays @@ -135,12 +158,12 @@ touchpad.rise(&touch_ISR); tt.wfi(); // enable interrupt on touch dled = 0.8; // turn on display LED 80% - + Resr = 0.075; // initial guess of Resr timer.start() ; RTC_Init(); // start the RTC Interrupts that sync the timer struct tm t; // pointer to a static tm structure - NVIC_SetPriority(TIMER3_IRQn, 1); //set ticker priority - NVIC_SetPriority(CAN_IRQn, 2); //higher than can (so RTC sync works) + NVIC_SetPriority(CAN_IRQn, 2); //set can priority just below RTC + NVIC_SetPriority(TIMER3_IRQn, 3); //set ticker priority just below can seconds = time(NULL); t = *localtime(&seconds) ; @@ -154,64 +177,27 @@ } t = *localtime(&seconds) ; strftime(sTemp, 32, "%a %m/%d/%Y %X\n", &t); - logMsg(sTemp); + printMsg(sTemp); // record RTC // revision - sprintf(sTemp,"CANary firmware rev64\n"); - logMsg(sTemp); - - // Look for new binary on thumbdrive - // Can't make this work right now since USB doesn't attach the right timestamp (so new binary isn't loaded) - /*cfile = fopen("/usb/CANary.bin", "rb"); - lastDMode[whichTouched]=99;//force refresh - if (cfile!=NULL){ //found a new binary on the thumbdrive so copy it over - sprintf(sTemp,"New binary found.\n"); - logMsg(sTemp); - file = fopen("/local/CANary.bin", "wb"); - if (file==NULL){ //failed to open destination - sprintf(sTemp,"Unable to open destination file.\n"); - logMsg(sTemp); - } else { - tt.set_display(2); - tt.foreground(White); - tt.background(Black); - tt.cls(); - tt.locate(1,40); - printf("%s\n","Copying binary - Do no remove power."); - tt.locate(1,80); - printf("CANary will reset when complete.\n"); - wait(1); //Wait 1 sec for display DMA to finish before writing file - while ( int size = fread( writeBuffer, sizeof(char), maxBufLen*13, cfile )){ - fwrite( writeBuffer, sizeof(char), size, file ); - led4=led3; - led3=led2; - led2=led1; - led1=!led4; - } - fclose(cfile); - fclose(file); - remove("/usb/CANary.bin"); // delete original - mbed_reset(); //restart - } - }*/ + sprintf(sTemp,"CANary firmware rev%s\n", revStr); // gg - for Logging the revision + printMsg(sTemp); // revision secsNoMsg = 0; //read efficiency history data - cfile = fopen("/local/ehist.cny", "r"); - if (cfile!=NULL){ // found a efficiency history file + hfile = fopen("/local/ehist.cny", "r"); + if (hfile!=NULL){ // found a efficiency history file for(i=0;i<39;i++){ - if(!feof(cfile)){ - fscanf(cfile,"%f %f\r\n",&mph[i],&kW[i]); + if(!feof(hfile)){ + fscanf(hfile,"%f %f\r\n",&mph[i],&kW[i]); mpkWh[i]=mph[i]/kW[i]; } } - fclose(cfile); - sprintf(sTemp,"History Loaded.\n"); - logMsg(sTemp); + fclose(hfile); + printMsg("History Loaded.\n"); // History loaded } else { // create initial file - sprintf(sTemp,"History not found. Created.\n"); - logMsg(sTemp); + printMsg("History not found. Created.\n"); // history not found, created for(i=0;i<39;i++){ // Pre-load with 4 mpkWh @ 40 mph mph[i]=40*timeConstant[i]; @@ -222,71 +208,94 @@ // Read config file readConfig(); + if (repeatPoll) { // enable autopolling if enabled + autoPoll.attach(&autoPollISR,pollInt); + } + + // Start monitors + can1.monitor(true); // set to snoop mode + can2.monitor(true); // set to snoop mode + can1.frequency(500000); + can2.frequency(500000); + can1SleepMode = VP230Sleep; // Turn on Monitor_only Mode + can2SleepMode = VP230Sleep; // Turn on Monitor_only Mode + can1.attach(&recieve1); + can2.attach(&recieve2); touched=false; secsNoTouch=2; while (true) { if (!logOpen) { // Open new file if one is not already open - if(logEn){ //logging enable + if(logEn&&usbEn){ //logging enabled and USB device detected + strftime(fileName, 32, "%m%d%H%M.alc", &t); //mmddhhmm.alc + efr = f_open(&efile,fileName,FA_WRITE|FA_OPEN_ALWAYS); seconds = time(NULL); t = *localtime(&seconds) ; - strftime(fileName, 32, "/usb/%m%d%H%M.alc", &t); //mmddhhmm.alc - //sprintf(sTemp,"Using file %s\n",fileName); - //logMsg(sTemp); - file = fopen(fileName, "ab"); - lastDMode[whichTouched]=99;//force refresh - if(file==NULL){ - sprintf(sTemp,"\nUnable to open %s\n\n\n\n",fileName); - logMsg(sTemp); + lastDMode[0]=99;//force refresh + lastDMode[1]=99;//force refresh + if(efr != FR_OK){ + sprintf(sTemp,"\nERR:%d Unable to open %s\n\n\n\n",efr,fileName); + printMsg(sTemp); // cannot open alc file logEn=false; spkr.beep(1000,0.25); + wait_ms(500); + spkr.beep(1000,0.25); } else { logOpen = true; readPointer=writePointer; sprintf(sTemp,"Starting Can Log %s\n",fileName); - logMsg(sTemp); - logTS(); + printMsg(sTemp); // starting alc log file + + logTS(); // Date Time at start + logEvent("Starting"); // Log startup msg for testing + sprintf(sTemp,"Cr%s",revStr); + logEvent(sTemp); // gg - log firmware version spkr.beep(2000,0.25); } - }//logging enabled + }//logging enabled and USB detected } else { // if (logOpen) - if (((writePointer+maxBufLen-readPointer)%maxBufLen)>(maxBufLen/16)||canIdle||!logEn) { + pointerSep=(writePointer+maxBufLen-readPointer)%maxBufLen; + if (pointerSep>(maxBufLen/16)||canIdle||!logEn) { // Dump buffer if > 1/16 full or canbus has stopped - if (file == NULL) { + //if (&efile == NULL) { + if (efr != FR_OK) { logOpen = false; - sprintf(sTemp,"Failed to append log file.\n"); - logMsg(sTemp); - spkr.beep(1000,0.25); + printMsg("Failed to append log file.\n"); // failed to append + spkr.beep(3000,0.25); + spkr.beep(1500,0.25); + spkr.beep(750,0.25); + spkr.beep(375,0.25); logEn=false; } else { - if (((writePointer+maxBufLen-readPointer)%maxBufLen)>(maxBufLen*7/8)) { // Hi-water mark - sprintf(sTemp,"Write buffer overrun.\n"); - logMsg(sTemp); - spkr.beep(1000,0.25); + while (readPointer != writePointer) { + efr=f_write(&efile,&writeBuffer[readPointer][0],13,&bytesRW); + if(++readPointer >= maxBufLen){ + readPointer=0; + led4 = !led4; + } } - while (readPointer != writePointer) { - for (j = 0; j<13; j++){ - fprintf(file,"%c",writeBuffer[readPointer][j]); - } - if(++readPointer >= maxBufLen) - readPointer=0; - } - led4 = !led4; } } // if > 1/16 full, canbus has stopped, or logging stopped if (!logEn) { - fclose(file); + sprintf(sTemp,"Stopping Can Log %s\n",fileName); + printMsg(sTemp); // stopping alc log file + f_close(&efile); logOpen=false; + pointerSep=0; + led4=false; } } // if logOpen if (canIdle&&userIdle&&!playbackEn) { // canbus idle --> sleep to save power - if (logOpen){ - fclose(file); - } // if (logOpen)*/ + if (repeatPoll) { // stop autopolling if enabled + autoPoll.detach(); + } + if (logOpen){ //close file to dump buffer + f_close(&efile); + } // if (logOpen) seconds = time(NULL); t = *localtime(&seconds) ; strftime(sTemp, 40, "Sleeping: %a %m/%d/%Y %X\n", &t); - logMsg(sTemp); + printMsg(sTemp); // sleeping date time updateDisplay(0); //Added for turbo3 who has a display override and wants to see the sleep message before going to sleep updateDisplay(1); //LPC_RTC->CIIR=0x00; // block RTC interrupts @@ -302,20 +311,29 @@ //__wfi(); // freeze CPU and wait for interrupt (from canbus or touch) Sleep(); } + lastDMode[0]=99; + lastDMode[1]=99; secsNoTouch=2; canIdle=secsNoMsg>canTimeout; - //userIdle=!touched; dled=0.8; // turn on display LED seconds = time(NULL); t = *localtime(&seconds) ; strftime(sTemp, 40, "Waking: %a %m/%d/%Y %X\n", &t); - logMsg(sTemp); + printMsg(sTemp); // wakeup date time if (time(NULL)>(secs+1800)) { - logOpen = false; // Start new file if asleep for more than 30 minutes + if (logOpen){ + f_close(&efile); + logOpen = false; // Start new file if asleep for more than 30 minutes + } // if (logOpen) if (secsNoTouch>100) secsNoTouch = 100; // also mostly reset user Idle counter } else if (logOpen){ // insert timestamp on each wake if logging enabled (disabled for now) - file = fopen(fileName, "ab"); - logTS(); + efr = f_open(&efile,fileName,FA_WRITE|FA_OPEN_ALWAYS); + f_lseek(&efile,0xffffffff); // goto end of file (append existing) + logEvent("WakingUp"); // gg - use messeges + logTS(); // Date-Time at wakeup + } + if (repeatPoll) { // re-enable autopolling if enabled + autoPoll.attach(&autoPollISR,pollInt); } } // if idle @@ -341,7 +359,7 @@ sMode = 1; } //sprintf(sTemp,"%d,%d ",lastTouch.x,lastTouch.y); - //logMsg(sTemp); + //printMsg(sTemp); // touch x,y - for debug touched = false; // clear interrupt flag } //--------------- @@ -355,7 +373,7 @@ secsNoTouch +=2; // increment to prevent double touch sMode = 1; //sprintf(sTemp,"button %d %d,%d %d\n",i,buttonX(lastTouch.x,3),buttonY(lastTouch.y,3),lastTouch.x); - //logMsg(sTemp); + //printMsg(sTemp); // button parms - for debug switch (sMode) { case 0: // no select break; @@ -372,10 +390,13 @@ if( tRow == tNavRow ) tRow = 7 ; // gg switch ( (tCol*10) + tRow ) { //--------------------------------- - case 00: // 00 on screen 0 or 1 + case 00: // top row, left button on screen 0 or 1 if (dMode[whichTouched]==monitorScreen||dMode[whichTouched]==changedScreen) { - indexOffset=indexOffset>4?indexOffset-4:1; - } else if (dMode[whichTouched]==config1Screen) { + indexOffset=indexOffset>4?indexOffset-4:1; + } else if (dMode[whichTouched] == indexScreen) { // gg - index + dMode[whichTouched] = mainScreen ; // GoTo Main Screen + sMode=0; + } else if (dMode[whichTouched]==configScreen) { wait_ms(500); tt.background(Black); tt.calibrate(); @@ -390,17 +411,23 @@ } break; //----------------------------------------------- - case 10: // 1,0 (col,row) on screen 0 or 1 + case 10: // 1,0 (col left of center,top row) on screen 0 or 1 if (dMode[whichTouched]==changedScreen) { for(j=0;j<100;j++) msgChanged[j]=0; // clear changed data lastDMode[whichTouched]=99;//force refresh + } else if (dMode[whichTouched] == indexScreen) { // gg - index + sMode=0; + dMode[whichTouched] = brakeScreen ; // GoTo Brake Screen } else if (dMode[whichTouched]==cpScreen) { - pollCP=true; + reqMsgCnt=0; + msgReq.attach(&sendReq,0.015); } else if (dMode[whichTouched]==cpHistScreen) { // gg - hist - pollCP=true; + reqMsgCnt=0; + msgReq.attach(&sendReq,0.015); } else if (dMode[whichTouched]==cpBarScreen) { // gg - cpbars - pollCP=true; - } else if (dMode[whichTouched]==config1Screen) { + reqMsgCnt=0; + msgReq.attach(&sendReq,0.015); + } else if (dMode[whichTouched]==configScreen) { mbed_reset(); } else if (dMode[whichTouched]==playbackScreen) { // pause/unpause playbackEn=!playbackEn; @@ -418,9 +445,11 @@ case 20: // col 2 and row 0 on either screen 0 or 1 if (dMode[whichTouched]==monitorScreen||dMode[whichTouched]==changedScreen) { indexOffset=indexOffset<77?indexOffset+4:80; - } else if (dMode[whichTouched]==config1Screen) { - sprintf(sTemp,"Saving config file.\n"); - logMsg(sTemp); + } else if (dMode[whichTouched] == indexScreen) { // gg - index + dMode[whichTouched] = effScreen ; // GoTo EFF Screen + sMode=0; + } else if (dMode[whichTouched]==configScreen) { + printMsg("Saving config file.\n"); // saving config saveConfig(); spkr.beep(2000,0.25); } else if (dMode[whichTouched]==playbackScreen) { // faster @@ -431,19 +460,41 @@ playback.attach(&playbackISR,playbackInt); } } - }else{ + } else { + lastDMode[whichTouched]=99;//repaint to clear highlight + } + + break; + + case 30: // right-most on top row + + if (dMode[whichTouched]==configScreen) { + // step through skins + if( skin < maxSkin ) skin += 1 ; + else skin = 0 ; + + // repaint both screens, I think + lastDMode[whichTouched]=99;//repaint to clear highlight + // and re-paint the other screen too, to see new skin there + lastDMode[whichTouched ^ 1]=99; // repaint other screen (^ = XOR) + } else if (dMode[whichTouched] == indexScreen) { // gg - index + dMode[whichTouched] = healthScreen ; // Goto health screen + sMode=0; + } else { lastDMode[whichTouched]=99;//repaint to clear highlight } break; //---------------------------------- //---------------------------------- - case 01: // col 0 row 1 - if (dMode[whichTouched]==config1Screen) { + case 01: // left col middle row + if (dMode[whichTouched]==configScreen) { logEn = !logEn; - if (!logEn) repeatPoll=false; // disable auto polling, too + } else if (dMode[whichTouched] == indexScreen) { // gg - index + dMode[whichTouched] = cpScreen ; // GoTo CP Data Screen + sMode=0; } else if (dMode[whichTouched]==dateScreen){ - dtMode=(dtMode<6)?dtMode+1:0; + dtMode=(dtMode<5)?dtMode+1:0; lastDMode[whichTouched]=99; } else { lastDMode[whichTouched]=99;//repaint to clear highlight @@ -452,40 +503,42 @@ break; //------------------------------ case 11: - if (dMode[whichTouched]==config1Screen){ - repeatPoll = !repeatPoll&&logEn; + if (dMode[whichTouched]==configScreen){ + repeatPoll = !repeatPoll; if (repeatPoll) { autoPoll.attach(&autoPollISR,pollInt); } else { autoPoll.detach(); } + } else if (dMode[whichTouched] == indexScreen) { // gg - index + dMode[whichTouched] = cpHistScreen ; // GoTo CP Hist Screen + sMode=0; } else if (dMode[whichTouched]==playbackScreen) { // Start/stop playback if(!playbackOpen){ - if(!logOpen){ - file = fopen("/usb/playback.alc", "rb"); + if(!canIdle){ + printMsg("Cannot playback while connected to canbus\n"); + }else if(!logOpen){ + efr = f_open(&efile,"playback.alc",FA_READ|FA_OPEN_EXISTING); lastDMode[whichTouched]=99;//force refresh - if(file==NULL){ - sprintf(sTemp,"Unable to open /usb/playback.alc\n"); - logMsg(sTemp); + if(efr != FR_OK){ + printMsg("Unable to open /usb/playback.alc\n"); // no playback.alc spkr.beep(1000,0.25); } else { playbackOpen = true; playbackEn=true; playback.attach(&playbackISR,playbackInt); - sprintf(sTemp,"Starting playback\n"); - logMsg(sTemp); + printMsg("Starting playback\n"); // start playback spkr.beep(2000,0.25); can1.attach(&doNothing);// Stop recieving CAN data can2.attach(&doNothing); } } else { - sprintf(sTemp,"Must stop logging first\n"); - logMsg(sTemp); + printMsg("Must stop logging first\n"); } } else { playback.detach(); - fclose(file); + f_close(&efile); playbackOpen=false; playbackEn=false; can1.attach(&recieve1);// Restore CAN data recieve @@ -502,9 +555,11 @@ break; //--------------------------------- case 21: // col 2 row 1 - if (dMode[whichTouched]==config1Screen) { // gg - Batt Log Enable Button + if (dMode[whichTouched]==configScreen) { // gg - Batt Log Enable Button yesBattLog = !yesBattLog; - + } else if (dMode[whichTouched] == indexScreen) { // gg - index + dMode[whichTouched] = cpBarScreen ; // GoTo CP Bars Screen + sMode=0; } else if (dMode[whichTouched]==dateScreen){ upDate(dtMode,false); lastDMode[whichTouched]=99; @@ -514,6 +569,66 @@ break; + case 31: // col 3 row 1 + if (dMode[whichTouched]==configScreen) { // gg - Batt Log Enable Button + debugMode = !debugMode; + } else if (dMode[whichTouched] == indexScreen) { // gg - index + dMode[whichTouched] = configScreen ; // GoTo Config Screen + } else if (dMode[whichTouched] == tripScreen) { + miles_trip[1]=0; + kWh_trip[1]=0; + sMode=0; + lastDMode[whichTouched]=99;//repaint to clear highlight + } else { + lastDMode[whichTouched]=99;//repaint to clear highlight + } + break; + + //----------------------------------- + case 02: // left col, bottom row (not nav) + if (dMode[whichTouched] == indexScreen) { // gg - index + dMode[whichTouched] = playbackScreen ; // GoTo Playback Screen + } else if (dMode[whichTouched]==configScreen) { + metric = !metric; // toggle metric/imperial display + } else { + lastDMode[whichTouched]=99;//repaint to clear highlight + } + break; + + case 12: // left-middle col, bottom row (not nav) + if (dMode[whichTouched] == configScreen) { // gg - index + dMode[whichTouched] = dateScreen ; // GoTo Set Date/Time Screen + } else { + lastDMode[whichTouched]=99;//repaint to clear highlight + } + break; + + case 22: // right-middle col, bottom row (not nav) + if (dMode[whichTouched] == indexScreen) { // gg - index + dMode[whichTouched] = logScreen ; + } else if (dMode[whichTouched]==configScreen) { + showHealth = !showHealth; + } else { + lastDMode[whichTouched]=99;//repaint to clear highlight + } + break; + + case 32: // right col, bottom row (not nav) + if (dMode[whichTouched] == configScreen) { + logEn=false; + updateFirmware(); + } else if (dMode[whichTouched] == tripScreen) { + miles_trip[2]=0; + kWh_trip[2]=0; + sMode=0; + lastDMode[whichTouched]=99;//repaint to clear highlight + } else if (dMode[whichTouched] == indexScreen) { + dMode[whichTouched] = tripScreen ; + } else { + lastDMode[whichTouched]=99;//repaint to clear highlight + } + break; + //----------------------------------- //----------------------------------- // Prev Navigation @@ -530,7 +645,7 @@ //----------------------------------- // Index Navigation case 27: // col 2 row tNavRow - dMode[whichTouched]= mainScreen; // indexScreen ; // gg - index + dMode[whichTouched]= indexScreen ; // gg - index break; //------------------------------------ // Next Navigation @@ -560,63 +675,123 @@ } if(tick){ // Executes once a second + tick=false; + headlights = (lastMsg[indexLastMsg[0x358]].data[1]&0x80)?true:false; // headlight/turn signal indicator accV=floor(mon12V*scale12V*10+0.5)/10; //Round to nearest 10th accOn=(accV>5)?true:false; + moving=(mph[0]>0.1); + charging=!moving&&(kW[0]<-1); // not moving and generating energy so must be charging if(laccOn&&!accOn){ // Car turned off + if (repeatPoll) { // Log on shutdown if autopoll enabled + tripLog(); // Write trip log on powerdown + } //write efficiency history data - cfile = fopen("/local/ehist.cny", "w"); - if (cfile!=NULL){ // found a efficiency history file + hfile = fopen("/local/ehist.cny", "w"); + if (hfile!=NULL){ // found a efficiency history file for(i=0;i<39;i++){ - fprintf(cfile,"%f %f\r\n",mph[i],kW[i]); + fprintf(hfile,"%f %f\r\n",mph[i],kW[i]); } - fclose(cfile); + fclose(hfile); + } + } + if(!laccOn&&accOn){ // Car turned on + miles_trip[0]=0; + kWh_trip[0]=0; + wait5secs=5; + if(showHealth){ + saveDmode=dMode[0]; + dMode[0]=healthScreen; } } laccOn=accOn; - if(!accOn&&!logEn&&userIdle&&!playbackEn){ - //sprintf(sTemp,"Display Off %4.2f\n",accV); - //logMsg(sTemp); - dled = 0; // turn off display if car off and logging disabled and no user activity + if(!accOn&&!logEn&&userIdle&&!playbackEn){ // Car off and logging disabled and no user activity + dled = 0; }else if(!headlights){ dled = ledHi; - }else{ + } else { dled = ledLo; } - + if(wait5secs>0){ // Wait a few seconds after poweron to give BMS time to measure CP's + wait5secs-=1; + if (repeatPoll&&(wait5secs==0)) { // Poll on startup if autopoll enabled + logOnce=true; + reqMsgCnt=0; + msgReq.attach(&sendReq,0.015); + } + } + if(moving&&(saveDmode<99)&&(wait5secs==0)){ + dMode[0]=saveDmode; + saveDmode=99; + } + //compute historic efficiency if(numSsamples>0){ // Avoid div0 - mph[0]=((float) motorRPM)/numSsamples/215; // Empirically derived - may change car to car - }else{ + mph[0]=((float) motorRPM)/numSsamples/220; // Empirically derived with MXV4s - may change with different wheels&tires + } else { mph[0]=0; } if(mph[0]>99){ mph[0]=0; } - mpkWh[0]=mph[0]; + numSsamples=0; + if(numWsamples>0){ // Avoid div0 + mpkWh[0]=mph[0]; kW[0]=((float) mWs_x4)/numWsamples/4e3; mpkWh[0]/=kW[0]; if (mpkWh[0]<0) { mpkWh[0]=99;// negative means inf. } - }else{ + } else { kW[0]=0; mpkWh[0]=0; } - //mpkWh[0]=floor(mpkWh[0]*10+0.5)/10; // Round to nearest 10th + numWsamples=0; + + if (!charging){ + miles_trip[0]+=mph[0]/3600; + miles_trip[1]+=mph[0]/3600; + miles_trip[2]+=mph[0]/3600; + kWh_trip[0]+=kW[0]/3600; + kWh_trip[1]+=kW[0]/3600; + kWh_trip[2]+=kW[0]/3600; + } + motorRPM=0; - numSsamples=0; mWs_x4=0; - numWsamples=0; - if(accOn||playbackEn){ + + // Compute ESR + if((Imax-Imin)<40){ // do nothing - insufficient delta_I to measure + unloadedV_x2 = (curRmax+curRmin)/2; + }else if ((redRmax-redRmin)<(curRmax-curRmin)) { + Resr-=0.001; + unloadedV_x2 = (redRmax+redRmin)/2; + } else if ((incRmax-incRmin)<(curRmax-curRmin)) { + Resr+=0.001; + unloadedV_x2 = (incRmax+incRmin)/2; + } else { + unloadedV_x2 = (curRmax+curRmin)/2; + } + curRmin=1000; + curRmax=0; + incRmin=1000; + incRmax=0; + redRmin=1000; + redRmax=0; + Imax=-1000; + Imin=1000; + + if((accOn||playbackEn)&&!charging){ for(i=1;i<39;i++){ average=mph[i]/timeConstant[i]; mph[i]-=average; mph[i]+=mph[0]; mpkWh[i]=average; average=kW[i]/timeConstant[i]; - kW[i]-=average; - kW[i]+=kW[0]; + if(!charging){ //Not charging - so include in efficiency data + kW[i]-=average; + kW[i]+=kW[0]; + } mpkWh[i]/=average; if (mpkWh[i]<0) { mpkWh[i]=99;// negative means inf. @@ -624,31 +799,33 @@ //mpkWh[i]=floor(mpkWh[i]*10+0.5)/10; // Round to nearest 10th } } - updateDTE=true; - if(logCP) - logPackVoltages(); // Turbo3 - tick=false; - } + if(logCP&&usbEn){ + if(logOnce){ + tripLog(); + logOnce=false; + } + logPackVoltages(); // Turbo3, only call + } + if(!usbEn&&!waitasec){ + usbEn=detectUSB(); // Keep looking if none found + } + waitasec=false; // work around to avoid hang when USB tries to init immediately + tock=true; + } // tick display=display<1?display+1:0; // toggle display updateDisplay(display); - - if(pollCP){ // We do this inside main loop instead of ticker so CAN RX will not be blocked - sendCPreq(); // send cellpair data request. - wait_ms(16); - sendTreq(); //send temperature request - pollCP=false; - } if(step){ // playback if(playbackOpen&&playbackEn){ for(i=0;i<120;i++){ - if(!feof(file)){ - fscanf(file,"%5c%8c",&header,&data); - logCan(header[0],CANMessage(0x7ff&((header[4]<<8)+header[3]), data, 8)); - }else{ - fclose(file); // restart - file = fopen("/usb/playback.alc", "rb"); + if(!f_eof(&efile)){ + efr=f_read(&efile,&header,5,&bytesRW); + efr=f_read(&efile,&data,8,&bytesRW); + logCan(header[0],CANMessage(0x7ff&((header[4]<<8)+header[3]), data, 8)); // Playback + } else { + f_close(&efile); // restart + efr = f_open(&efile,"playback.alc",FA_READ|FA_OPEN_EXISTING); lastDMode[whichTouched]=99;//force refresh spkr.beep(2000,0.25); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Sun Jul 21 23:59:00 2013 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/e2ac27c8e93e \ No newline at end of file
--- a/mbed41.bld Wed Apr 10 13:31:35 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/10b9abbe79a6 \ No newline at end of file
--- a/utility.cpp Wed Apr 10 13:31:35 2013 +0000 +++ b/utility.cpp Sun Jul 21 23:59:00 2013 +0000 @@ -9,6 +9,8 @@ userIdle=(++secsNoTouch>userTimeout)?true:false; LPC_RTC->ILR |= (1<<0); // clear interrupt to prepare for next tick=true; + // will use this to generate a logTP() just before the next Message received. + if( (time(NULL) % 60) == 0) ZeroSecTick = true; // gg - at 0-second of each minute } void RTC_Init (void) { @@ -19,7 +21,7 @@ NVIC_EnableIRQ( RTC_IRQn ); } -void logMsg (char *msg) { +void printMsg (char *msg) { strcpy(displayLog[displayLoc],msg); displayLoc=displayLoc>17?0:displayLoc+1; } @@ -37,165 +39,414 @@ } void logCan (char mType, CANMessage canRXmsg) { - char sTemp[40]; - unsigned short ts = getTimeStamp(); - static unsigned char ii = 0, lasti = 0; // indexindex - unsigned char changed,i; + + // re-arranged to put static first + static unsigned char ii = 0; + static unsigned char lasti = 0; // indexindex static unsigned char bdi=0; - signed short packV; - signed short packA; static signed short imotorRPM = 0; + static unsigned short nLost = 0; // gg - overrun + + char sTemp[40]; + unsigned char changed; + unsigned short i,j,k; + signed short packV_x2; + signed short packA_x2; signed long imWs_x4; + unsigned short ts; + secsNoMsg=0; // reset deadman switch - if(logOpen){ - if(canRXmsg.id>0) { - writeBuffer[writePointer][0]=mType; - writeBuffer[writePointer][1]=(ts&0xff00)>>8; - writeBuffer[writePointer][2]=(ts&0x00ff); - writeBuffer[writePointer][3]=canRXmsg.id&0xff; - writeBuffer[writePointer][4]=(canRXmsg.id>>8)+(canRXmsg.len<<4); - for(i=5;i<13;i++){ // Is there a better way to do this? (writeBuffer[writePointer][i]=canRXmsg.data?) - writeBuffer[writePointer][i]=canRXmsg.data[i-5]; + if(debugMode||(skin==ggSkin)){ + // code to insert actual number of dropped frames for overrun debug - skiped in normal mode to keep logcan short + if(logOpen){ + // check to see if buffer is already full (read - write) = 1 + // actually the last buffer location cannot be used because then + // the buffer would look empty after writePointer++ + + //if (((writePointer+maxBufLen-readPointer)%maxBufLen)>(maxBufLen/16)) // modulo is slow? + + // pointers are 0 through maxBufLen-1 + if( (readPointer - writePointer) == 1 || (writePointer - readPointer) == (maxBufLen - 1)) { + // the buffer is "full", so Lose this message + + // point to the last-stored message + int tempWritePointer = writePointer - 1 ; + if( tempWritePointer < 0 ) tempWritePointer = maxBufLen - 1; + char strLost[9] ; + + if( nLost == 0 ) { + // this is the first message lost + // and we must overwrite the last message with an FFE comment message + // So, there will be two messages lost as the comment message is laid in. + nLost = 2; + sprintf(strLost,"%s","Lost0002"); // indicate two messages lost + + // overlay the last message with a "Lost0002" comment + writeBuffer[tempWritePointer][0]=0; + // leave the ts of the overlaid message + //writeBuffer[tempWritePointer][1]=(ts&0xff00)>>8; // Time Stamp (2 bytes_ + //writeBuffer[tempWritePointer][2]=(ts&0x00ff); + // force the MsgID to an Event Message + writeBuffer[tempWritePointer][3]=0xfe; // MsgID, low byte + writeBuffer[tempWritePointer][4]=0xff; // Len nibble, and MsgID high nibble + // lay in the "Lost0002" text + for(i=5;i<13;i++){ + writeBuffer[tempWritePointer][i]= strLost[i-5]; + } + } else { + // at least one message was previously lost + // increment the loat counter + nLost += 1; + + // lay the new count into the comment + sprintf(strLost,"%04d",nLost); + for(i=9;i<13;i++){ + writeBuffer[tempWritePointer][i]= strLost[i-9]; + } + } + } else { + // there is room to insert the message + // get it inserted quickly + ts=getTimeStamp(); + writeBuffer[writePointer][0]=mType; + writeBuffer[writePointer][1]=(ts&0xff00)>>8; // Time Stamp (2 bytes_ + writeBuffer[writePointer][2]=(ts&0x00ff); + writeBuffer[writePointer][3]=canRXmsg.id&0xff; // MsgID, low byte + char sLen = canRXmsg.len ; + writeBuffer[writePointer][4]=(canRXmsg.id>>8)+(sLen<<4); // Len nibble, and MsgID high nibble + for(i=0;i<8;i++){ // Is there a better way to do this? (writeBuffer[writePointer][i]=canRXmsg.data?) + if(i<sLen) + writeBuffer[writePointer][i+5]=canRXmsg.data[i]; + else // i>=sLen + // force unused data bytes to FF for CAN-Do compatibility + writeBuffer[writePointer][i+5]=0xFF; + } + + //-------------- + // Note, this is not protected from the interrupt. + // Due to the nLost code above, this no longer + // overflows to writePointer = readPointer + // which would make the buffer look empty + if (++writePointer >= maxBufLen) { + writePointer = 0; + led3 = !led3; + } + //-------------- + // log a local message if we had lost messages. gg - logcan + if( nLost > 0 ) { + // We previously lost messages that did not get into the buffer + sprintf(sTemp,"-- Lost %d Messages.\n", nLost); + printMsg(sTemp); // write buffer overrun + //spkr.beep(500,0.25); + + nLost = 0 ; + } + //-------------- } - if (++writePointer >= maxBufLen) { + } + }else{ // not debugMode - keep code short + if(logOpen){ + NVIC_DisableIRQ(CAN_IRQn); // Block interrupts until write pointer assigned + int localWritePointer = writePointer++; // create local copy to make logCan reentrant + // note that the static variables do not prevent safe reentry + // since they are only used for msgId<0x800 which will never interrupt + // another msgId<0x800 (both CANbusses are same priority) + if (writePointer >= maxBufLen) { writePointer = 0; led3 = !led3; } + NVIC_EnableIRQ(CAN_IRQn); // Unblock interrupts once local pointer set and global pointer incremented + ts=getTimeStamp(); + writeBuffer[localWritePointer][0]=mType; + writeBuffer[localWritePointer][1]=(ts&0xff00)>>8; + writeBuffer[localWritePointer][2]=(ts&0x00ff); + writeBuffer[localWritePointer][3]=canRXmsg.id&0xff; + writeBuffer[localWritePointer][4]=(canRXmsg.id>>8)+(canRXmsg.len<<4); + for(i=5;i<13;i++){ // Is there a better way to do this? (writeBuffer[localWritePointer][5]=canRXmsg.data?) + writeBuffer[localWritePointer][i]=canRXmsg.data[i-5]; + } + if (writePointer==readPointer) { + // Just caught up to read pointer + printMsg("Write buffer overrun.\n"); // write buffer overrun + spkr.beep(500,0.25); + } } } - if(indexLastMsg[canRXmsg.id]==0) { //Check if no entry - ii=ii<99?ii+1:0; - indexLastMsg[canRXmsg.id]=ii; //Create entry if first message - } - if(dMode[0]==changedScreen||dMode[1]==changedScreen){ - changed=msgChanged[indexLastMsg[canRXmsg.id]]; - for(i=0;i<8;i++){ - if(lastMsg[indexLastMsg[canRXmsg.id]].data[i]!=canRXmsg.data[i]){ - changed |= 1<<i; + if(canRXmsg.id<0x800){ // Block FFE and FFF messages + if(indexLastMsg[canRXmsg.id]==0) { //Check if no entry + //ii=ii<99?ii+1:0; // Should never wrap - less than 100 different messages ever used + if(ii<99) { + //indexLastMsg[canRXmsg.id]=ii; //Create entry if first message + indexLastMsg[canRXmsg.id]=++ii; //Create entry for first MsgID occurance + // ii max is 99 here + } else { + // the ii array is full, more than 100 MsgIDs found + if(ii==99) { + ii++; // step to 100 to log only one error + printMsg("MsgID buffer overrun.\n"); // write buffer overrun + } } } - msgChanged[indexLastMsg[canRXmsg.id]]=changed; - } - - lastMsg[indexLastMsg[canRXmsg.id]]=canRXmsg; //Store in table - - //Miscellaneous on-recieve operations below - if((mType==2)&&(canRXmsg.id==0x358)){ // headlight/turn signal indicator - headlights = (canRXmsg.data[1]&0x80)?true:false; - }else if((mType==1)&&(canRXmsg.id==0x7bb)){ // is battery data? Need to store all responses - if(canRXmsg.data[0]<0x20){ - if(canRXmsg.data[3]==2){//cellpair data - bdi=0; - sprintf(sTemp,"Getting cell pair data\n"); - logMsg(sTemp); - }else if(canRXmsg.data[3]==4){//temperature data - bdi=0x20; - sprintf(sTemp,"Getting temperature data\n"); - logMsg(sTemp); - }else bdi=0; - lasti=0; - } - i=canRXmsg.data[0]&0x0f; //lower nibble of D0 is index - if(lasti>i){ //detect rollover and offset index appropriately - bdi=0x10; + + //---------------- + if(dMode[0]==changedScreen||dMode[1]==changedScreen){// Skip if not using (for execution speed) + changed=msgChanged[indexLastMsg[canRXmsg.id]]; + // This is cleared in the main loop when reset button is touched + for(i=0;i<8;i++){ + if(lastMsg[indexLastMsg[canRXmsg.id]].data[i]!=canRXmsg.data[i]){ + changed |= 1<<i; + } + } + msgChanged[indexLastMsg[canRXmsg.id]]=changed; } - lasti=i; //remember the msb to detect rollover next time around - i+=bdi; - //if(i==22) logCP=true; //Turbo3 - //if( (i==22) && (yesBattLog) ) logCP=true; // only if enabled gg - Batt Log - if(i==22){ - logCP=yesBattLog; // Only log is logging enabled - showCP=true; // Always show - } - i*=7; - if(i<0xfa){ // Is there a better way to do this? - battData[i+0]=canRXmsg.data[1]; - battData[i+1]=canRXmsg.data[2]; - battData[i+2]=canRXmsg.data[3]; - battData[i+3]=canRXmsg.data[4]; - battData[i+4]=canRXmsg.data[5]; - battData[i+5]=canRXmsg.data[6]; - battData[i+6]=canRXmsg.data[7]; + + lastMsg[indexLastMsg[canRXmsg.id]]=canRXmsg; //Store in table + + //------------------- + //Miscellaneous on-recieve operations below + if((mType==1)&&(canRXmsg.id==0x7bb)){ // is battery data? Need to store all responses + if(canRXmsg.data[0]<0x20){ + if(canRXmsg.data[3]==1){//Group 1 data + bdi=BatDataBaseG1; // index offset for Group 1 data + if(debugMode){ + printMsg(" Getting Group 1 data\n"); + } + + }else if(canRXmsg.data[3]==2){//Group 2 = cellpair data + bdi=BatDataBaseG2; // index offset for CP data + if(debugMode){ + printMsg(" Getting cell pair data\n"); + } + + }else if(canRXmsg.data[3]==3){//Group 3 data + bdi=BatDataBaseG3; // index offset for Group 3 data + if(debugMode){ + printMsg(" Getting Group 3 data\n"); + } + + }else if(canRXmsg.data[3]==4){//Group 4 = temperature data + bdi=BatDataBaseG4; // index offset for Temperature data + if(debugMode){ + printMsg(" Getting temperature data\n"); + } + + }else if(canRXmsg.data[3]==5){//Group 5 data + bdi=BatDataBaseG5; // index offset for Group 5 data + if(debugMode){ + printMsg(" Getting Group 5 data\n"); + } + + }else if(canRXmsg.data[3]==6){//Group 6 data = shunt data + bdi=BatDataBaseG6; // index offset for Group 6 data + if(debugMode){ + printMsg(" Getting Group 6 data\n"); + } + + }else bdi=0xff; // ignore other messages (for now) + lasti=0; + } + + if(bdi<0xff){ + i=canRXmsg.data[0]&0x0f; //lower nibble of D0 is index + if(lasti>i){ //detect rollover and offset index appropriately + bdi += 0x10; // for CP data + } + lasti=i; //remember the msb to detect rollover next time around + i+=bdi; + //------- + //------- + i*=7; + if(i+6 < BatDataBufMax) { + battData[i+0]=canRXmsg.data[1]; + battData[i+1]=canRXmsg.data[2]; + battData[i+2]=canRXmsg.data[3]; + battData[i+3]=canRXmsg.data[4]; + battData[i+4]=canRXmsg.data[5]; + battData[i+5]=canRXmsg.data[6]; + battData[i+6]=canRXmsg.data[7]; + } + if(i==(BatDataBaseG6+3)*7){ // All data loaded + logCP=yesBattLog; // Only log if logging enabled + showCP=true; // Always show + + // Find hottest temperature by finding smallest ADC value + // 2013 models only have three sensors + k=battData[(BatDataBaseG4*7)+3]*0x100+battData[(BatDataBaseG4*7)+4]; + j=battData[(BatDataBaseG4*7)+6]*0x100+battData[(BatDataBaseG4*7)+7]; + if(j<k)k=j; + j=battData[(BatDataBaseG4*7)+9]*0x100+battData[(BatDataBaseG4*7)+10]; + if(j<k)k=j; + j=battData[(BatDataBaseG4*7)+12]*0x100+battData[(BatDataBaseG4*7)+13]; + if(j<k)k=j; + //interpolate from lookup table + unsigned short temp_adc[8] = {1000,589,487,401,365,340,309,000}; + float temp_C[8] = { -27, 13, 23, 32, 36, 39, 43, 76}; + char ii=0; + while(k<=temp_adc[++ii]) { } // Find section in table + maxTemp=(float)(k-temp_adc[ii]); + maxTemp/=(float)(temp_adc[ii-1]-temp_adc[ii]); + maxTemp*=(temp_C[ii-1]-temp_C[ii]); + maxTemp+=temp_C[ii]; + + // Get state of health + SOH_x100=battData[(BatDataBaseG1*7)+29]*0x100+battData[(BatDataBaseG1*7)+30]; + Ah_x10000=battData[(BatDataBaseG1*7)+36]*0x10000+battData[(BatDataBaseG1*7)+37]*0x100+battData[(BatDataBaseG1*7)+38]; + SOC_x10000=battData[(BatDataBaseG1*7)+32]*0x10000+battData[(BatDataBaseG1*7)+33]*0x100+battData[(BatDataBaseG1*7)+34]; + + // Save shunt data + for(j=0; j<24; j++){ + shunt[j*4+0]=battData[BatDataBaseG6*7+j+3]&0x08; + shunt[j*4+1]=battData[BatDataBaseG6*7+j+3]&0x04; + shunt[j*4+2]=battData[BatDataBaseG6*7+j+3]&0x02; + shunt[j*4+3]=battData[BatDataBaseG6*7+j+3]&0x01; + } + } + } + }else if((mType==1)&&(canRXmsg.id==0x1db)){ //Battery Volts and Amps + packV_x2=((canRXmsg.data[2]<<2)|(canRXmsg.data[3]>>6)); // 1 LSB = 0.5V + packA_x2=((canRXmsg.data[0]<<3)|(canRXmsg.data[1]>>5)); // 1 LSB = 0.5A + if(packA_x2>0x03ff){ + packA_x2|=0xf800;//extend sign; + } + packA_x2 -= 1; //Slight correction to value required (unique to my Leaf?) + if (-packA_x2<Imin){ + Imin=-packA_x2; + } else if (-packA_x2>Imax){ + Imax=-packA_x2; + } + imWs_x4 = packV_x2; // Volts*milliSeconds*2 + imWs_x4 *= -packA_x2; // milliWattseconds*4 + mWs_x4 += imWs_x4; // total mWs_x4 + float temp; + temp = Resr; + temp *= (float) -packA_x2; + temp += (float) packV_x2; + if(temp>curRmax){ + curRmax=temp; + } else if(temp<curRmin){ + curRmin=temp; + } + temp = Resr-0.001; + temp *= (float) -packA_x2; + temp += (float) packV_x2; + if(temp>redRmax){ + redRmax=temp; + } else if(temp<redRmin){ + redRmin=temp; + } + temp = Resr+0.001; + temp *= (float) -packA_x2; + temp += (float) packV_x2; + if(temp>incRmax){ + incRmax=temp; + } else if(temp<incRmin){ + incRmin=temp; + } + numWsamples++; + }else if((mType==1)&&(canRXmsg.id==0x1da)){ //Motor Speed + imotorRPM=((canRXmsg.data[4]<<8)|(canRXmsg.data[5])); + if(imotorRPM<0){ // take absolute value + imotorRPM=-imotorRPM; + } + motorRPM+=imotorRPM; + numSsamples++; } - }else if((mType==1)&&(canRXmsg.id==0x1db)){ //Battery Volts and Amps - packV=((canRXmsg.data[2]<<2)|(canRXmsg.data[3]>>6)); // 1 LSB = 0.5V - packA=((canRXmsg.data[0]<<3)|(canRXmsg.data[1]>>5)); // 1 LSB = 0.5A - if(packA>0x03ff){ - packA|=0xf800;//extend sign; - } - imWs_x4 = packV; // Volts*milliSeconds*2 - imWs_x4 *= -packA; // milliWattseconds*4 - if (!((imotorRPM<2)&&(imWs_x4<0))){ //Ignore if charging from wall - mWs_x4 += imWs_x4; // total mWs_x4 - numWsamples++; - } - }else if((mType==1)&&(canRXmsg.id==0x1da)){ //Motor Speed - imotorRPM=((canRXmsg.data[4]<<8)|(canRXmsg.data[5])); - if(imotorRPM<0){ // take absolute value - imotorRPM=-imotorRPM; - } - motorRPM+=imotorRPM; - numSsamples++; } } +//----------------------------- void logTS () { CANMessage tsMsg; unsigned long secs = time(NULL); // seconds past 12:00:00 AM 1 Jan 1900 + // NOTE: In Mbed, I believe that this is seconds past start of 1970, not 1900 + // but this is good, since seconds past 1970 is what CAN-Do expects. GG - Date Time tsMsg.id=0xfff; tsMsg.len=0xf; - tsMsg.data[0]=secs&0xff; + tsMsg.data[0]=secs&0xff; tsMsg.data[1]=(secs>>8)&0xff; tsMsg.data[2]=(secs>>16)&0xff; - tsMsg.data[3]=secs>>24; - tsMsg.data[4]=0xff; - tsMsg.data[5]=0xff; - tsMsg.data[6]=0xff; + tsMsg.data[3]=(secs>>24)&0xff; + tsMsg.data[4]=0; // 0xff; gg - Date Time + tsMsg.data[5]=0; // 0xff; for CAN-Do + tsMsg.data[6]=0; // 0xff; tsMsg.data[7]=0xff; - logCan(0,tsMsg); + logCan(0,tsMsg); // Date-Time +} + +void logEvent (char * errMsg) { + // log CAN-Do 8-character Pseudo Message + CANMessage tsMsg; + tsMsg.id=0xffe; // pseudo Message to CAN-Do log + tsMsg.len=0xf; + int iMsgLen = strlen(errMsg); + // 8 character message compatible with CAN-Do + for(int i=0; i<8; i++){ + tsMsg.data[i]=' '; + if( i < iMsgLen ) tsMsg.data[i]=errMsg[i]; + } + logCan(0,tsMsg); // FFE Comment Message } -void sendCPreq() { - char i; - char data[8] = {0x02, 0x21, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff}; - can1.monitor(false); // set to active mode - can1SleepMode = 0; // enable TX - can1.write(CANMessage(0x79b, data, 8)); - logCan(1,CANMessage(0x79b, data, 8)); - data[0]=0x30; //change to request next line message - data[1]=0x01; - data[2]=0x00; - for(i=0;i<29;i++){ - wait_ms(16); //wait 16ms +void sendReq() { + static char data[8] = {0x02, 0x21, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff}; + if(reqMsgCnt<99){ + switch (reqMsgCnt){ + case BatDataBaseG1: + can1.monitor(false); // set to active mode + can1SleepMode = 0; // enable TX + data[0]=0x02; //change to request group 1 + data[1]=0x21; + data[2]=0x01; + break; + case BatDataBaseG2: // group 1 has 6 frames + data[0]=0x02; //change to request group 2 (cp data) + data[1]=0x21; + data[2]=0x02; + break; + case BatDataBaseG3: // group 2 has 29 frames + data[0]=0x02; //change to request group 3 + data[1]=0x21; + data[2]=0x03; + break; + case BatDataBaseG4: // group 3 has 5 frames + data[0]=0x02; //change to request group 4 (temperature) + data[1]=0x21; + data[2]=0x04; + break; + case BatDataBaseG5: // group 4 has 3 frames + data[0]=0x02; //change to request group 5 + data[1]=0x21; + data[2]=0x05; + break; + case BatDataBaseG6: // group 4 has 3 frames + data[0]=0x02; //change to request group 5 + data[1]=0x21; + data[2]=0x06; + break; + case BatDataBaseG7: // group 5 has 11 frames + reqMsgCnt = 99; + can1SleepMode = VP230Sleep; // disable TX + can1.monitor(true); // set to snoop mode + msgReq.detach(); // stop ticker + default: + data[0]=0x30; //change to request next line message + data[1]=0x01; + data[2]=0x00; + } can1.write(CANMessage(0x79b, data, 8)); + reqMsgCnt++; } - can1SleepMode = 1; // disable TX - can1.monitor(true); // set to snoop mode } -void sendTreq() { - char i; - char data[8] = {0x02, 0x21, 0x04, 0xff, 0xff, 0xff, 0xff, 0xff}; - can1.monitor(false); // set to active mode - can1SleepMode = 0; // enable TX - can1.write(CANMessage(0x79b, data, 8)); - logCan(1,CANMessage(0x79b, data, 8)); - data[0]=0x30; //change to request next line message - data[1]=0x01; - data[2]=0x00; - for(i=0;i<3;i++){ - wait_ms(16); //wait 16ms - can1.write(CANMessage(0x79b, data, 8)); - } - can1SleepMode = 1; // disable TX - can1.monitor(true); // set to snoop mode +void autoPollISR(){ + //char sTemp[40]; // just for debug + //sprintf(sTemp,"Requesting cp data\n"); // just for debug + //printMsg(sTemp); // just for debug + reqMsgCnt = 0; //reset message counter + msgReq.attach(&sendReq,0.015); } -void autoPollISR() { //This is the ticker ISR for auto-polling - pollCP=true; //Set a flag to do in main loop instead of here -} //since ticker blocks other interrupts - void playbackISR() { //Used for autoplayback step=true; } @@ -206,15 +457,24 @@ void recieve1() { CANMessage msg1; can1.read(msg1); - logCan(1, msg1); //EVcan - led1 = !led1; + + if( ZeroSecTick ) { ZeroSecTick = false; logTS(); } // gg - 0-second EV bus + if(msg1.id>0) { + logCan(1, msg1); // EVcan Message Received + led1 = !led1; + } } void recieve2() { CANMessage msg2; can2.read(msg2); - logCan(2, msg2); //CARcan - led2 = !led2; + + if( ZeroSecTick ) { ZeroSecTick = false; logTS(); } // gg - 0-second EV bus + + if(msg2.id>0) { + logCan(2, msg2); // CARcan Message Received + led2 = !led2; + } } unsigned char buttonX(unsigned short X, unsigned char columns) { @@ -229,8 +489,8 @@ void saveConfig(){ FILE *cfile; - cfile = fopen("/local/config.txt", "w"); - fprintf(cfile,"format 3\r\n"); + cfile = fopen("/local/config.txt", "w"); + fprintf(cfile,"format 6\r\n"); fprintf(cfile,"x0_off %d\r\n",tt.x0_off); fprintf(cfile,"y0_off %d\r\n",tt.y0_off); fprintf(cfile,"x0_pp %d\r\n",tt.x0_pp); @@ -240,11 +500,11 @@ fprintf(cfile,"x1_pp %d\r\n",tt.x1_pp); fprintf(cfile,"y1_pp %d\r\n",tt.y1_pp); fprintf(cfile,"x_mid %d\r\n",tt.x_mid); - if (dMode[0]==config1Screen) + if (dMode[0]==configScreen) fprintf(cfile,"dMode0 %d\r\n",mainScreen); else fprintf(cfile,"dMode0 %d\r\n",dMode[0]); - if (dMode[1]==config1Screen) + if (dMode[1]==configScreen) fprintf(cfile,"dMode1 %d\r\n",mainScreen); else fprintf(cfile,"dMode1 %d\r\n",dMode[1]); @@ -254,59 +514,76 @@ fprintf(cfile,"scale12V %4.2f\r\n",scale12V); fprintf(cfile,"skin %d\r\n",skin); fprintf(cfile,"dtePeriod %d\r\n",dtePeriod); + fprintf(cfile,"DebugMode %d\r\n",(debugMode?1:0)); + fprintf(cfile,"metric %d\r\n",(metric?1:0)); + fprintf(cfile, "firmware %d\r\n", fwCount ); + fprintf(cfile,"showHealth %d\r\n",(showHealth?1:0)); fclose(cfile); } - void readConfig(){ +void readConfig(){ FILE *cfile; int ff; - char sTemp[40]; + cfile = fopen("/local/config.txt", "r"); if (cfile==NULL){ // if doesn't exist --> create - sprintf(sTemp,"No config file found.\n"); - logMsg(sTemp); - sprintf(sTemp,"Calibrating touch screen.\n"); - logMsg(sTemp); + printMsg("No config file found.\n"); // no config file + printMsg("Calibrating touch screen.\n"); // calibrating //tt.setcal(5570, 34030, 80, 108, 33700, 5780, 82, 108, 32500);// bypass calibration using my values tt.calibrate(); // run touchscreen calibration routine // NOTE: calibrates screen 1 first, then screen 0. saveConfig(); } else { - ledHi = 0.823; + ledHi = 0.8; ledLo = 0.1; - pollInt = 300; + pollInt = 60; scale12V = 16.2; skin = ttSkin; - fscanf(cfile, "format %d\r\n", &ff ) ; - fscanf(cfile, "x0_off %d\r\n", &tt.x0_off ) ; - fscanf(cfile, "y0_off %d\r\n", &tt.y0_off ) ; - fscanf(cfile, "x0_pp %d\r\n", &tt.x0_pp ) ; - fscanf(cfile, "y0_pp %d\r\n", &tt.y0_pp ) ; - fscanf(cfile, "x1_off %d\r\n", &tt.x1_off ) ; - fscanf(cfile, "y1_off %d\r\n", &tt.y1_off ) ; - fscanf(cfile, "x1_pp %d\r\n", &tt.x1_pp ) ; - fscanf(cfile, "y1_pp %d\r\n", &tt.y1_pp ) ; - fscanf(cfile, "x_mid %d\r\n", &tt.x_mid ) ; - fscanf(cfile, "dMode0 %d\r\n", &dMode[0] ) ; - fscanf(cfile, "dMode1 %d\r\n", &dMode[1] ) ; + fscanf(cfile, "format %d\r\n", &ff ); + fscanf(cfile, "x0_off %d\r\n", &tt.x0_off ); + fscanf(cfile, "y0_off %d\r\n", &tt.y0_off ); + fscanf(cfile, "x0_pp %d\r\n", &tt.x0_pp ); + fscanf(cfile, "y0_pp %d\r\n", &tt.y0_pp ); + fscanf(cfile, "x1_off %d\r\n", &tt.x1_off ); + fscanf(cfile, "y1_off %d\r\n", &tt.y1_off ); + fscanf(cfile, "x1_pp %d\r\n", &tt.x1_pp ); + fscanf(cfile, "y1_pp %d\r\n", &tt.y1_pp ); + fscanf(cfile, "x_mid %d\r\n", &tt.x_mid ); + fscanf(cfile, "dMode0 %d\r\n", &dMode[0] ); + fscanf(cfile, "dMode1 %d\r\n", &dMode[1] ); if(ff>1){ - fscanf(cfile, "ledHi %f\r\n", &ledHi ) ; - fscanf(cfile, "ledLo %f\r\n", &ledLo ) ; - fscanf(cfile, "pollInt %d\r\n", &pollInt ) ; - fscanf(cfile, "scale12V %f\r\n", &scale12V ) ; + fscanf(cfile, "ledHi %f\r\n", &ledHi ); + fscanf(cfile, "ledLo %f\r\n", &ledLo ); + fscanf(cfile, "pollInt %d\r\n", &pollInt ); + fscanf(cfile, "scale12V %f\r\n", &scale12V ); } if(ff>2){ - fscanf(cfile, "skin %d\r\n", &skin ) ; - fscanf(cfile, "dtePeriod %d\r\n", &dtePeriod ) ; + fscanf(cfile, "skin %d\r\n", &skin ); + fscanf(cfile, "dtePeriod %d\r\n", &dtePeriod ); + } + if(ff>3){ + int iDebug; + fscanf(cfile, "DebugMode %d\r\n", &iDebug ); + debugMode = (bool)iDebug; + } + if(ff>4) { + int iMetric; + fscanf(cfile, "metric %d\r\n", &iMetric ); + metric = (bool)iMetric; + fscanf(cfile, "firmware %d\r\n", &iMetric ); + fwCount = iMetric; + } + if(ff>5){ + int ishowHealth; + fscanf(cfile, "showHealth %d\r\n", &ishowHealth ); + showHealth = (bool)ishowHealth; } fclose(cfile); - if(ff<3){//If not latest format, save as latest format + if(ff<6){//If not latest format, save as latest format saveConfig(); - sprintf(sTemp,"Config file format updated.\n"); - logMsg(sTemp); + printMsg("Config file format updated.\n"); // config forat updates } - sprintf(sTemp,"Config file loaded.\n"); - logMsg(sTemp); + printMsg("Config file loaded.\n"); // config file loaded } } @@ -369,8 +646,69 @@ struct tm t; // pointer to a static tm structure short unsigned max, min, jv, i, bd; unsigned avg; - unsigned short gids, SOC, packV; - signed short packA; + unsigned short gids, SOC, packV_x2; + signed short packA_x2; + time_t seconds ; + + CANMessage msg; + + seconds = time(NULL); // Turbo3 + t = *localtime(&seconds) ; // Turbo3 + + msg = lastMsg[indexLastMsg[0x5bc]]; //Get gids + gids = (msg.data[0]<<2)+(msg.data[1]>>6); + msg = lastMsg[indexLastMsg[0x55b]]; //Get SOC + SOC = (msg.data[0]<<2)+(msg.data[1]>>6); + msg = lastMsg[indexLastMsg[0x1db]]; //Get pack volts + packV_x2 = (msg.data[2]<<2)+(msg.data[3]>>6); + packA_x2 = (msg.data[0]<<3)+(msg.data[1]>>5); + if (packA_x2 & 0x400) packA_x2 |= 0xf800; + + max=0; + min=9999; + avg=0; + for(i=0; i<96; i++) { + bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; + avg+=bd; + if(bd>max) max=bd; + if(bd<min) min=bd; + } + avg /= 96; + if(min<3713) { + jv=avg-(max-avg)*1.5; + } else { // Only compute judgement value if min cellpair meets <= 3712mV requirement + jv=0; + } + + FIL bfile; + FRESULT bfr; + bfr = f_open(&bfile,"batvolt.txt",FA_WRITE|FA_OPEN_ALWAYS); + if(bfr==FR_OK) { + f_lseek(&bfile,0xffffffff); // go to end of file to append + strftime(sTemp, 40, "%a %m/%d/%Y %X", &t); + f_printf(&bfile,"%s,",sTemp); + sprintf(sTemp,"%d,%5.1f%%,%5.1f,%5.1f,%d,%d,%d,%d,%d",gids,(float)SOC/10,(float)packV_x2/2,(float)packA_x2/2,max,min,avg,max-min,jv); + f_printf(&bfile,"%s,",sTemp); + f_printf(&bfile,"%d,%d,%d,%d,",(battData[(BatDataBaseG4*7)+ 3]<<8)+battData[(BatDataBaseG4*7)+ 4],battData[(BatDataBaseG4*7)+ 5],(battData[(BatDataBaseG4*7)+ 6]<<8)+battData[(BatDataBaseG4*7)+ 7],battData[(BatDataBaseG4*7)+ 8]); + f_printf(&bfile,"%d,%d,%d,%d", (battData[(BatDataBaseG4*7)+ 9]<<8)+battData[(BatDataBaseG4*7)+10],battData[(BatDataBaseG4*7)+11],(battData[(BatDataBaseG4*7)+12]<<8)+battData[(BatDataBaseG4*7)+13],battData[(BatDataBaseG4*7)+14]); + for(i=0; i<96; i++) { + bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; + f_printf(&bfile,",%d",bd); + } + f_printf(&bfile,"\r\n"); + f_close(&bfile); + } + logCP=false; + showCP=true; +} + +void tripLog() { // Daily log + char sTemp[40]; + struct tm t; // pointer to a static tm structure + short unsigned max, min, jv, i, bd; + unsigned avg; + unsigned short gids, SOC, packV_x2; + signed short packA_x2; time_t seconds ; CANMessage msg; @@ -383,15 +721,15 @@ msg = lastMsg[indexLastMsg[0x55b]]; //Get SOC SOC = (msg.data[0]<<2)+(msg.data[1]>>6); msg = lastMsg[indexLastMsg[0x1db]]; //Get pack volts - packV = (msg.data[2]<<2)+(msg.data[3]>>6); - packA = (msg.data[0]<<3)+(msg.data[1]>>5); - if (packA & 0x400) packA |= 0xf800; + packV_x2 = (msg.data[2]<<2)+(msg.data[3]>>6); + packA_x2 = (msg.data[0]<<3)+(msg.data[1]>>5); + if (packA_x2 & 0x400) packA_x2 |= 0xf800; max=0; min=9999; avg=0; for(i=0; i<96; i++) { - bd=(battData[i*2+3]<<8)+battData[i*2+4]; + bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; avg+=bd; if(bd>max) max=bd; if(bd<min) min=bd; @@ -403,22 +741,167 @@ jv=0; } - FILE *bfile; - //bfile = fopen("/local/batvolt.txt", "a"); - bfile = fopen("/usb/batvolt.txt", "a"); - if(bfile!=NULL) { + FIL bfile; + FRESULT bfr; + bfr = f_open(&bfile,"triplog.txt",FA_WRITE|FA_OPEN_ALWAYS); + if(bfr==FR_OK) { + f_lseek(&bfile,0xffffffff); // go to end of file to append strftime(sTemp, 40, "%a %m/%d/%Y %X", &t); - fprintf(bfile,"%s,%d,%5.1f%%,%5.1f,%5.1f,%d,%d,%d,%d,%d",sTemp,gids,(float)SOC/10,(float)packV/2,(float)packA/2,max,min,avg,max-min,jv); - fprintf(bfile,"%d,%d,%d,%d,",(battData[224+ 3]<<8)+battData[224+ 4],battData[224+ 5],(battData[224+ 6]<<8)+battData[224+ 7],battData[224+ 8]); - fprintf(bfile,"%d,%d,%d,%d", (battData[224+ 9]<<8)+battData[224+10],battData[224+11],(battData[224+12]<<8)+battData[224+13],battData[224+14]); - for(i=0; i<96; i++) { - bd=(battData[i*2+3]<<8)+battData[i*2+4]; - fprintf(bfile,",%d",bd); + f_printf(&bfile,"%s,",sTemp); + sprintf(sTemp,"%3.1f,%d,%5.1f%%,%5.1f%%, %4.2f, %5.1f,%4.1f,%d,%d,%d,%d,%d",accV,gids,(float)SOC/10, (float)SOH_x100/100,(float)Ah_x10000/10000,(float)packV_x2/2,(float)packA_x2/2,max,min,avg,max-min,jv); + f_printf(&bfile,"%s,",sTemp); + f_printf(&bfile,"%d,%d,%d,%d,",(battData[(BatDataBaseG4*7)+ 3]<<8)+battData[(BatDataBaseG4*7)+ 4],battData[(BatDataBaseG4*7)+ 5],(battData[(BatDataBaseG4*7)+ 6]<<8)+battData[(BatDataBaseG4*7)+ 7],battData[(BatDataBaseG4*7)+ 8]); + f_printf(&bfile,"%d,%d,%d,%d", (battData[(BatDataBaseG4*7)+ 9]<<8)+battData[(BatDataBaseG4*7)+10],battData[(BatDataBaseG4*7)+11],(battData[(BatDataBaseG4*7)+12]<<8)+battData[(BatDataBaseG4*7)+13],battData[(BatDataBaseG4*7)+14]); + /*for(i=0; i<96; i++) { + bd=(battData[BatDataBaseG2*7+i*2+3]<<8)+battData[BatDataBaseG2*7+i*2+4]; + f_printf(&bfile,",%d",bd); + }*/ + // temporariliy dump everything + for(i=0; i<BatDataBufMax; i++) { + sprintf(sTemp,",%02x",battData[i]); + f_printf(&bfile,"%s",sTemp); } - fprintf(bfile,"\r\n"); - fclose(bfile); + + f_printf(&bfile,"\r\n"); + f_close(&bfile); } - logCP=false; - showCP=true; } +//LM - updates firmware off a usb key, eliminating the need to plug +//the CANary into a computer for updates. +void updateFirmware() +{ + FIL sfile; // external usb file + FRESULT sfr; // external file access flags + unsigned int bytesRW; + char sTemp[40]; + const int bufSize = 2048; + char buffer[bufSize]; + FILE *destFile; + + // Check for config file on USB drive + sfr = f_open(&sfile,"CONFIG.TXT",FA_READ|FA_OPEN_EXISTING); + if(sfr == FR_OK) + { + printf("Copy config file from USB\n"); + destFile = fopen("/local/CONFIG.TXT", "w"); + while (!f_eof(&sfile)) + { + sfr=f_read(&sfile,&buffer,bufSize,&bytesRW); + fwrite(buffer, 1, bytesRW, destFile); + } + fflush(destFile); + fclose(destFile); + f_close(&sfile); + } + + sfr = f_open(&sfile,"firmware.bin",FA_READ|FA_OPEN_EXISTING); + if(sfr != FR_OK) + { + sprintf(sTemp,"Couldn't find firmware.bin\n"); + printf(sTemp); + wait(3); + lastDMode[whichTouched]=99;//force refresh + return; + } + fwCount ++; + saveConfig(); + tt.cls(); + printf("Saved Configuration\n"); + //delete all bin files in /local + DIR *dir; + struct dirent *ent; + printf("Starting update\n"); + printf("deleting old firmware files\n"); + if ((dir = opendir ("/local/")) != NULL) { + // print all the files and directories within directory + while ((ent = readdir (dir)) != NULL) { + //printf("FILE: %s\n",ent->d_name); + char dest[4] = ""; + strncat(dest, &ent->d_name[strlen(ent->d_name)-3],3); + dest[0] = tolower(dest[0]); + dest[1] = tolower(dest[1]); + dest[2] = tolower(dest[2]); + if(strcmp(dest,"bin")==0) + { + sprintf(sTemp,"/local/%s",ent->d_name); + int result = remove(sTemp); + printf("REMOVED: %s",ent->d_name); + } + } + closedir (dir); + } else { + //could not open directory + printf("Couldnt open folder"); + wait(3); + return; + } + printf("copying new firmware\n"); + tt.cls(); + //Copy the new firmware from usb->local + //The newest bin file is the one that is used by the mbed + sprintf(sTemp,"/local/fw%d.bin",fwCount); + printf("Writing %s\n",sTemp); + wait(2); + destFile = fopen(sTemp, "wb"); + if(destFile == NULL) + { + printf("Couldn't Open Destination\n"); + wait(3); + return; + } + + while (!f_eof(&sfile)) + { + sfr=f_read(&sfile,&buffer,bufSize,&bytesRW); + fwrite(buffer, 1, bytesRW, destFile); + } + + fflush(destFile); + fclose(destFile); + f_close(&sfile); + tt.cls(); + printf("Succesful\n\n"); + printf("Rebooting in 3 seconds\n"); + wait(3); + //Now run new firmware + mbed_reset(); +} + +bool detectUSB(void){ + /*FIL tfile; // external usb file + bool usbEn = (f_open(&tfile,"usb.det",FA_WRITE|FA_OPEN_ALWAYS)==FR_OK); + if(usbEn){ + f_close(&tfile); + f_unlink("usb.det"); + } + return(usbEn);*/ + return(true + ); +} + +//Sample CONFIG.TXT +/* +format 5 +x0_off 5732 +y0_off 34009 +x0_pp 77 +y0_pp 106 +x1_off 33955 +y1_off 6310 +x1_pp 80 +y1_pp 104 +x_mid 31986 +dMode0 4 +dMode1 2 +ledHi 0.800 +ledLo 0.100 +pollInt 300 +scale12V 16.20 +skin 0 +dtePeriod 14 +DebugMode 0 +metric 0 +firmware 11 +showHealth 1 +*/ \ No newline at end of file
--- a/utility.h Wed Apr 10 13:31:35 2013 +0000 +++ b/utility.h Sun Jul 21 23:59:00 2013 +0000 @@ -1,13 +1,16 @@ // utility.h -//#include "mbed.h" #include "CAN.h" #include "common.h" #include "TOUCH_TFTx2.h" +#include "beep.h" +#include <cctype> +#include "ff.h" extern Timer timer; -extern volatile int secsNoMsg; -extern volatile int secsNoTouch; +extern Ticker msgReq; +extern volatile unsigned short secsNoMsg; +extern volatile unsigned short secsNoTouch; extern volatile bool canIdle; extern volatile bool userIdle; extern char displayLog[20][40]; @@ -19,16 +22,18 @@ extern bool showCP; extern char writeBuffer[maxBufLen][13]; extern volatile int writePointer; +extern int readPointer; extern DigitalOut led1,led2,led3,led4; extern char indexLastMsg[0x800]; extern unsigned char dMode[2]; extern unsigned char msgChanged[100]; -extern unsigned char battData[256]; +extern unsigned char battData[BatDataBufMax]; // BatDataBufMax extern CANMessage lastMsg[100]; extern CAN can1,can2; +extern unsigned int fwCount; extern DigitalOut can1SleepMode,can2SleepMode; -extern bool pollCP; extern bool tick; +extern bool ZeroSecTick; extern bool headlights; extern TOUCH_TFTx2 tt; extern float ledHi; @@ -45,18 +50,37 @@ extern unsigned short numSsamples; extern unsigned char skin; extern unsigned char dtePeriod; +extern Beep spkr; +extern unsigned char reqMsgCnt; +extern float maxTemp; +extern unsigned long Ah_x10000; +extern unsigned long SOC_x10000; +extern unsigned short SOH_x100; +extern bool metric; +extern unsigned char lastDMode[2]; +extern unsigned char whichTouched; +extern float unloadedV_x2,Resr,curRmax,curRmin,redRmax,redRmin,incRmax,incRmin; +extern signed short Imax, Imin; +extern unsigned char tNavRow ; // gg - 4x4 +extern char revStr[7]; // gg - version +extern bool debugMode; +extern bool shunt[96]; +extern float accV; +extern bool showHealth; extern "C" { void mbed_reset(); void RTC_IRQHandler(); void RTC_Init (void); - void logMsg (char *msg); + void printMsg (char *msg); void touch_ISR(); unsigned short getTimeStamp(); void logCan (char mType, CANMessage canRXmsg); void logTS (); - void sendCPreq(); - void sendTreq(); + void logEvent (char * errMsg); // gg - messeges + //void sendCPreq(); + //void sendTreq(); + void sendReq(); void autoPollISR(); void playbackISR(); void recieve1(); @@ -68,6 +92,9 @@ void readConfig(); void upDate(unsigned char field, bool updownbar); void logPackVoltages(); // Turbo3 + void tripLog(); // Turbo3 + void updateFirmware(); // LM - Update firmware off USB + bool detectUSB(); } //LEAF OBD