Able to initialise card, and create file using FatFs, but not write to the file

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Evening all,

 

Some of you may have seen some related threads of mine recently, such as https://www.avrfreaks.net/forum/failing-pfmount-using-petitfs-cannot-determine-why

 

For those that haven't, this is a custom board application, avr-gcc, ATmega1284P, using elm-chan's Fatfs. I've overcome the previous issues I was having, and am now able to successfully initialise my SD card, mount the card, and create a file on the card. So my hardware and low-level IO is good. Note that amongst desperation in the previous thread, I compiled an Arduino example which wrote data successfully to the card, so I'm entirely convinced the issue I'm having is software only.

 

I'm debugging over JTAG into the f_printf function, and the function returns -1 (not in enum), which is not one of the valid return codes. That said, single stepping through the subequent functions and lines line by line, f_printf returns FR_OK as expected, but still writes no data to the card?

 

void sd_init(void){

	fr = f_mount(&FatFs, "0:", 0);
	if (fr != FR_OK){
		if(fr == FR_DISK_ERR){
			PORTB &= ~(1 << LED_1);
		}
	}

	fr = f_open(&File,"0:DATA.DAT", FA_OPEN_APPEND);
	fr = f_printf(&File, "%d", 1234);
	f_close(&File);

}

When not single stepping, such that I see a -1 return, the source of the -1 return is, where EOF is defined as -1.

/* Flush remaining characters in the buffer */

static int putc_flush (putbuff* pb)
{
	UINT nw;

	if (   pb->idx >= 0	/* Flush buffered characters to the file */
		&& f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK
		&& (UINT)pb->idx == nw) return pb->nchr;
	return EOF;
}

Any ideas on what to look for next?

This topic has a solution.
Last Edited: Mon. Mar 30, 2020 - 05:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How about FA_WRITE instead of FA_OPEN_APPEND ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

How about FA_WRITE instead of FA_OPEN_APPEND ?

 

For a second... I thought this would be it! I've added FA_WRITE before FA_OPEN_APPEND. However, I still just get an empty file?

 

void sd_init(void){

	fr = f_mount(&FatFs, "0:", 0);
	if (fr != FR_OK){
		if(fr == FR_DISK_ERR){
			PORTB &= ~(1 << LED_1);
		}
	}

	fr = f_open(&File,"0:DATA.DAT", FA_WRITE | FA_OPEN_APPEND);
	fr = f_printf(&File, "%d", 1234);
	f_close(&File);

}

It would appear that sync_window() is returning FR_DISK_ERR for some reason...

static FRESULT move_window (	/* Returns FR_OK or FR_DISK_ERR */
	FATFS* fs,		/* Filesystem object */
	LBA_t sect		/* Sector LBA to make appearance in the fs->win[] */
)
{
	FRESULT res = FR_OK;


	if (sect != fs->winsect) {	/* Window offset changed? */
#if !FF_FS_READONLY
		res = sync_window(fs);		/* Flush the window */
#endif
		if (res == FR_OK) {			/* Fill sector window with new data */
			if (disk_read(fs->pdrv, fs->win, sect, 1) != RES_OK) {
				sect = (LBA_t)0 - 1;	/* Invalidate window if read data is not valid */
				res = FR_DISK_ERR;
			}
			fs->winsect = sect;
		}
	}
	return res;
}

 

Last Edited: Fri. Mar 27, 2020 - 06:32 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No I didn't say both I said FA_WRITE as an alternative. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

No I didn't say both I said FA_WRITE as an alternative. 

 

Indeed, that was my first attempt - which also resulted in a no-write unfortunately... The ORing of both options I tried secondly as per the example on chan's site.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

 

So to try and rule out a software & hardware compatibility issue (hardware previously excluded, see OP), I've dug out a V3 MMC card I had lying around, which also successfully initialises, mounts and creates a file, but also will not write to the card.

 

I've moved further along now; in the initialisation routine there is a local (to the function) variable 'ty' which holds the card type info; at the end of this function, this get's written into CardType which is a static (to the source file) variable. This statement was never being executed, despite not being skipped due to a condition etc.

 

The line below, 'CardType = ty;', my breakpoint does not stop on this line. The line immediately after, 'deselect(); is executed. However, I have since found CardType was defined, but not declared. Adding a declaration into the relevant header now allows CardType to be updated. As a result, many of the disk control functions which reference CardType are now behaving differently.

DSTATUS mmc_disk_initialize (void)
{
	**** preceeding code removed for post length*****

	CardType = ty;
	deselect();

	if (ty) {			/* Initialization succeded */
		Stat &= ~STA_NOINIT;		/* Clear STA_NOINIT */
		spi_init(SPI_8MHZ);
	} else {			/* Initialization failed */
		// power_off();
	}

	return Stat;
}

Now, the issue I'm experiencing has changed. In order, I can successfully (i.e. function returns FR_OK):

 

  1. initialise the card
  2. mount the card
  3. create a file
  4. open the file

 

but cannot

 

  1. write to the file (function returns FR_NO_FILE)
  2. close the file (function returns FR_DISK_ERR)

 

This is the wrapper function that includes all of these calls. I'm currently a little lost how the file that was created by my board (not created on a PC first) is then not found...

void sd_init(void){
	
	PORTB |= (1 << LED_1);
	
	fr = disk_initialize(0);
	fr = f_mount(&FatFs, "", 0);
	fr = f_open(&File,"DATA.DAT", FA_OPEN_APPEND);
	fr = f_open(&File,"DATA.DAT", FA_WRITE); // OPEN_APPEND
	fr = f_printf(&File, "%d", 1234); 
	fr = f_close(&File);
	
	if (fr != FR_OK){
		PORTB &= ~(1 << LED_1);
	}
}

Any ideas on how to proceed? I'm currently stepping through the writing function as we speak but otherwise lost!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Forgot to ask, what does your ffconf.h look like? Things like FF_FS_READONLY etc.

 

BTW what on earth is the purpose of fr in this code:

	fr = disk_initialize(0);
	fr = f_mount(&FatFs, "", 0);
	fr = f_open(&File,"DATA.DAT", FA_OPEN_APPEND);
	fr = f_open(&File,"DATA.DAT", FA_WRITE); // OPEN_APPEND
	fr = f_printf(&File, "%d", 1234); 
	fr = f_close(&File);
	
	if (fr != FR_OK){
		PORTB &= ~(1 << LED_1);
	}

The LED changing stuff is ONLY reporting what the result of the f_close(0 was, the result of all previous operations is lost. Surely you want something like:

	fr = disk_initialize(0);
	if (fr != FR_OK) {
	    printf("could not init");
	    abort();
	}
	fr = f_mount(&FatFs, "", 0);
	if (fr != FR_OK) {
	    printf("could not mount");
	    abort();
	}
	fr = f_open(&File,"DATA.DAT", FA_OPEN_APPEND);
	if (fr != FR_OK) {
	    printf("could not open");
	    abort();
	}
	fr = f_open(&File,"DATA.DAT", FA_WRITE); // OPEN_APPEND
	if (fr != FR_OK) {
	    printf("Why on earth did I htink openning the same file twice would work???");
	    abort();
	}
	fr = f_printf(&File, "%d", 1234); 
	if (fr != FR_OK) {
	    printf("printing to file did not work");
	    abort();
	}
	fr = f_close(&File);
	
	if (fr != FR_OK){
		PORTB &= ~(1 << LED_1);
	}

 

Last Edited: Mon. Mar 30, 2020 - 01:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My ffconf.h file is setup as per below, effectively read/write, no real time clock, string functions enabled, make file system disabled etc

 

/*---------------------------------------------------------------------------/
/  FatFs Functional Configurations
/---------------------------------------------------------------------------*/

#define FFCONF_DEF	86606	/* Revision ID */

/*---------------------------------------------------------------------------/
/ Function Configurations
/---------------------------------------------------------------------------*/

#define FF_FS_READONLY	0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/  Read-only configuration removes writing API functions, f_write(), f_sync(),
/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/  and optional writing functions as well. */

#define FF_FS_MINIMIZE	0
/* This option defines minimization level to remove some basic API functions.
/
/   0: Basic functions are fully enabled.
/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
/      are removed.
/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/   3: f_lseek() function is removed in addition to 2. */

#define FF_USE_STRFUNC	1
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
/
/  0: Disable string functions.
/  1: Enable without LF-CRLF conversion.
/  2: Enable with LF-CRLF conversion. */

#define FF_USE_FIND		0
/* This option switches filtered directory read functions, f_findfirst() and
/  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */

#define FF_USE_MKFS		0
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */

#define FF_USE_FASTSEEK	0
/* This option switches fast seek function. (0:Disable or 1:Enable) */

#define FF_USE_EXPAND	0
/* This option switches f_expand function. (0:Disable or 1:Enable) */

#define FF_USE_CHMOD	0
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
/  (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */

#define FF_USE_LABEL	0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/  (0:Disable or 1:Enable) */

#define FF_USE_FORWARD	0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */

/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/

#define FF_CODE_PAGE	932
/* This option specifies the OEM code page to be used on the target system.
/  Incorrect code page setting can cause a file open failure.
/
/   437 - U.S.
/   720 - Arabic
/   737 - Greek
/   771 - KBL
/   775 - Baltic
/   850 - Latin 1
/   852 - Latin 2
/   855 - Cyrillic
/   857 - Turkish
/   860 - Portuguese
/   861 - Icelandic
/   862 - Hebrew
/   863 - Canadian French
/   864 - Arabic
/   865 - Nordic
/   866 - Russian
/   869 - Greek 2
/   932 - Japanese (DBCS)
/   936 - Simplified Chinese (DBCS)
/   949 - Korean (DBCS)
/   950 - Traditional Chinese (DBCS)
/     0 - Include all code pages above and configured by f_setcp()
*/

#define FF_USE_LFN		0
#define FF_MAX_LFN		255
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/   0: Disable LFN. FF_MAX_LFN has no effect.
/   1: Enable LFN with static  working buffer on the BSS. Always NOT thread-safe.
/   2: Enable LFN with dynamic working buffer on the STACK.
/   3: Enable LFN with dynamic working buffer on the HEAP.
/
/  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
/  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/  be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
/  specification.
/  When use stack for the working buffer, take care on stack overflow. When use heap
/  memory for the working buffer, memory management functions, ff_memalloc() and
/  ff_memfree() exemplified in ffsystem.c, need to be added to the project. */

#define FF_LFN_UNICODE	0
/* This option switches the character encoding on the API when LFN is enabled.
/
/   0: ANSI/OEM in current CP (TCHAR = char)
/   1: Unicode in UTF-16 (TCHAR = WCHAR)
/   2: Unicode in UTF-8 (TCHAR = char)
/   3: Unicode in UTF-32 (TCHAR = DWORD)
/
/  Also behavior of string I/O functions will be affected by this option.
/  When LFN is not enabled, this option has no effect. */

#define FF_LFN_BUF		255
#define FF_SFN_BUF		12
/* This set of options defines size of file name members in the FILINFO structure
/  which is used to read out directory items. These values should be suffcient for
/  the file names to read. The maximum possible length of the read file name depends
/  on character encoding. When LFN is not enabled, these options have no effect. */

#define FF_STRF_ENCODE	3
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
/  f_putc(), f_puts and f_printf() convert the character encoding in it.
/  This option selects assumption of character encoding ON THE FILE to be
/  read/written via those functions.
/
/   0: ANSI/OEM in current CP
/   1: Unicode in UTF-16LE
/   2: Unicode in UTF-16BE
/   3: Unicode in UTF-8
*/

#define FF_FS_RPATH		0
/* This option configures support for relative path.
/
/   0: Disable relative path and remove related functions.
/   1: Enable relative path. f_chdir() and f_chdrive() are available.
/   2: f_getcwd() function is available in addition to 1.
*/

/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/

#define FF_VOLUMES		1
/* Number of volumes (logical drives) to be used. (1-10) */

#define FF_STR_VOLUME_ID	0
#define FF_VOLUME_STRS		"RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
/  When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
/  number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
/  logical drives. Number of items must not be less than FF_VOLUMES. Valid
/  characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/  compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/  not defined, a user defined volume string table needs to be defined as:
/
/  const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/

#define FF_MULTI_PARTITION	0
/* This option switches support for multiple volumes on the physical drive.
/  By default (0), each logical drive number is bound to the same physical drive
/  number and only an FAT volume found on the physical drive will be mounted.
/  When this function is enabled (1), each logical drive number can be bound to
/  arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/  funciton will be available. */

#define FF_MIN_SS		512
#define FF_MAX_SS		512
/* This set of options configures the range of sector size to be supported. (512,
/  1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/  harddisk. But a larger value may be required for on-board flash memory and some
/  type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/  for variable sector size mode and disk_ioctl() function needs to implement
/  GET_SECTOR_SIZE command. */

#define FF_LBA64		0
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
/  To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */

#define FF_MIN_GPT		0x100000000
/* Minimum number of sectors to switch GPT format to create partition in f_mkfs and
/  f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */

#define FF_USE_TRIM		0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/  To enable Trim function, also CTRL_TRIM command should be implemented to the
/  disk_ioctl() function. */

/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/

#define FF_FS_TINY		0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/  At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
/  Instead of private sector buffer eliminated from the file object, common sector
/  buffer in the filesystem object (FATFS) is used for the file data transfer. */

#define FF_FS_EXFAT		0
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/  To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/  Note that enabling exFAT discards ANSI C (C89) compatibility. */

#define FF_FS_NORTC		1
#define FF_NORTC_MON	1
#define FF_NORTC_MDAY	1
#define FF_NORTC_YEAR	2020
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
/  any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
/  the timestamp function. Every object modified by FatFs will have a fixed timestamp
/  defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/  To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/  added to the project to read current time form real-time clock. FF_NORTC_MON,
/  FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/  These options have no effect in read-only configuration (FF_FS_READONLY = 1). */

#define FF_FS_NOFSINFO	0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/  option, and f_getfree() function at first time after volume mount will force
/  a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/  bit0=0: Use free cluster count in the FSINFO if available.
/  bit0=1: Do not trust free cluster count in the FSINFO.
/  bit1=0: Use last allocated cluster number in the FSINFO if available.
/  bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/

#define FF_FS_LOCK		0
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
/  and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
/  is 1.
/
/  0:  Disable file lock function. To avoid volume corruption, application program
/      should avoid illegal open, remove and rename to the open objects.
/  >0: Enable file lock function. The value defines how many files/sub-directories
/      can be opened simultaneously under file lock control. Note that the file
/      lock control is independent of re-entrancy. */

/* #include <somertos.h>	// O/S definitions */
#define FF_FS_REENTRANT	0
#define FF_FS_TIMEOUT	1000
#define FF_SYNC_t		HANDLE
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/  module itself. Note that regardless of this option, file access to different
/  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/  and f_fdisk() function, are always not re-entrant. Only file/directory access
/  to the same volume is under control of this function.
/
/   0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
/   1: Enable re-entrancy. 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. Samples are available in
/      option/syscall.c.
/
/  The FF_FS_TIMEOUT defines timeout period in unit of time tick.
/  The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/  SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/  included somewhere in the scope of ff.h. */

/*--- End of configuration options ---*/

The purpose of fr is there only to allow me to read the return value over JTAG as I step over each function, once I've got the basic functionality working, I will be doing exactly as you suggest. The term 'fr' is just carried over from chan's examples. So just to be clear, the functions that I've said are returning FR_OK, are indeed doing so (confirmed over JTAG), I'm not relying on the LED to tell me that.

 

As for why (right now) am I trying to open the file twice, I'm simply trying FA_WRITE by itself, FA_OPEN_APPEND by itself, the two OR'd as per Chan's example, two calls etcs.

 

Furthur, I've been looking into the contents of the FILE struct after the call to each of these functions, 16384 IS the physical sector number that contains the card root directory (confirmed by Winhex).

 

-		File	{FIL{data}@0x086c}	FIL{data}@0x086c
+		obj	{FFOBJID{data}@0x086c}	FFOBJID{data}@0x086c
		flag	2	                BYTE{data}@0x087a
		err	0	                BYTE{data}@0x087b
		fptr	0	                FSIZE_t{data}@0x087c
		clust	0	                DWORD{data}@0x0880
		sect	0	                LBA_t{data}@0x0884
		dir_sect	16384	        LBA_t{data}@0x0888
-		dir_ptr	0x06d6	                BYTE*{data}@0x088c
		*((File).dir_ptr)	68	BYTE{data}@0x06d6
+		buf	0x088e	                BYTE[512]{data}@0x088e

I can also see my newly created file IS there (and simply by looking in the card in Windows file explorer). Question in my mind, is what is dir_ptr meant to be looking at, if that's meant to be the byte offset in sector 16384 for the file, that's absolutely not where my file is.

Offset       0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

000400000   44 41 54 41 43 41 52 44  20 20 20 08 00 00 00 00   DATACARD
000400010   00 00 00 00 00 00 40 92  73 50 00 00 00 00 00 00         @’sP
000400020   42 20 00 49 00 6E 00 66  00 6F 00 0F 00 72 72 00   B  I n f o   rr
000400030   6D 00 61 00 74 00 69 00  6F 00 00 00 6E 00 00 00   m a t i o   n
000400040   01 53 00 79 00 73 00 74  00 65 00 0F 00 72 6D 00    S y s t e   rm
000400050   20 00 56 00 6F 00 6C 00  75 00 00 00 6D 00 65 00     V o l u   m e
000400060   53 59 53 54 45 4D 7E 31  20 20 20 16 00 A3 40 92   SYSTEM~1     £@’
000400070   73 50 73 50 00 00 41 92  73 50 03 00 00 00 00 00   sPsP  A’sP
000400080   44 41 54 41 20 20 20 20  44 41 54 20 00 00 00 00   DATA    DAT
000400090   21 50 00 00 00 00 00 00  00 00 00 00 00 00 00 00   !P              

 

Last Edited: Mon. Mar 30, 2020 - 02:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, next silly question - what are you doing about MMC_WP ? Does the socket even have a WP switch? If it doesn't what are you doing in disk_timerproc about "faking" the WP signal?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Socket does not support WP or CD (unfortunately) as the socket is integrated into an off the shelf display daughter board.

 

These are configured in mmc_avr_spi.c as below. 

/* Peripheral controls (Platform dependent) */
#define CS_LOW()		(!(PORTC & (1 << SD_CS)))	/* Set MMC_CS = low */
#define	CS_HIGH()		(PORTC & (1 << SD_CS))	/* Set MMC_CS = high */
#define MMC_CD			1 /* Test if card detected.   yes:true, no:false, default:true */
#define MMC_WP			0 /* Test if write protected. yes:true, no:false, default:false */

and disk_timerproc is as per below, so both WP and CD should not be setting and faults (nor than I seen them returned anywhere).

 

void mmc_disk_timerproc (void)
{
	BYTE b;
	UINT n;

	b = Timer1;				/* 100Hz decrement timer */
	if (b) Timer1 = --b;
	n = Timer2;
	if (n) Timer2 = --n;

	b = Stat;
	if (MMC_WP) {				/* Write protected */
		b |= STA_PROTECT;
	} else {					/* Write enabled */
		b &= ~STA_PROTECT;
	}
	if (MMC_CD) {				/* Card inserted */
		b &= ~STA_NODISK;
	} else {					/* Socket empty */
		b |= (STA_NODISK | STA_NOINIT);
	}
	Stat = b;				/* Update MMC status */
}

 

I now also realise that mmc_disk_timerproc says it should be called every 10ms. I've added it to my scheduler to run every 10ms, but no change (which I wasn't expecting it to, given timerproc is clearing the NODISK, NOINIT and PROTECT flags.

 

EDIT - Confirmation that WP or CD are not the issue, the start of the write function at IO layer returns a RES_NOTRDY or RES_WRPRT error if either or those are being flagged...

 

DRESULT mmc_disk_write (
	const BYTE *buff,	/* Pointer to the data to be written */
	LBA_t sector,		/* Start sector number (LBA) */
	UINT count			/* Sector count (1..128) */
)
{
	DWORD sect = (DWORD)sector;


	if (!count) return RES_PARERR;
	if (Stat & STA_NOINIT) return RES_NOTRDY;
	if (Stat & STA_PROTECT) return RES_WRPRT;

 

Last Edited: Mon. Mar 30, 2020 - 03:43 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also to add... looking into the file struct more

 

-		dir_ptr	0x06d6	             BYTE*{data}@0x088c
		*((File).dir_ptr)	68	     BYTE{data}@0x06d6

I'm not sure what '68' is saying there, but it's not the byte offset in that sector, if I look at address 0x06d6 in RAM, I see the below. So the open function has read the file correctly for sure... My file is called DATA.DAT

 

EDIT - Just realised, 68 is just decimal of 0x44 located at 0x06D6 in RAM...

data 0x06D6  44 41 54 41 20 20 20 20 44 41 54 20 00 00 00 00 21 50 00 00 00 00 00 00 00 00 00  DATA    DAT ....!P.........

After the attempted write, the file struct changes to below

 

-		File	{FIL{data}@0x086c}	FIL{data}@0x086c
-		obj	{FFOBJID{data}@0x086c}	FFOBJID{data}@0x086c
+		fs	0x0627	                FATFS*{data}@0x086c
		id	1	                    WORD{data}@0x086e
		attr	0               	BYTE{data}@0x0870
		stat	0               	BYTE{data}@0x0871
		sclust	7               	DWORD{data}@0x0872
		objsize	4               	FSIZE_t{data}@0x0876
		flag	194             	BYTE{data}@0x087a
		err	0                      	BYTE{data}@0x087b
		fptr	4                 	FSIZE_t{data}@0x087c
		clust	7               	DWORD{data}@0x0880
		sect	16704            	LBA_t{data}@0x0884
		dir_sect	16384         	LBA_t{data}@0x0888
-		dir_ptr	0x06d6           	BYTE*{data}@0x088c
		*((File).dir_ptr)   	0	BYTE{data}@0x06d6
+		buf	0x088e              	BYTE[512]{data}@0x088e

 

Last Edited: Mon. Mar 30, 2020 - 02:55 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As further information, I have just confirmed I can read from the card...

 

I've just added 6 characters into the file in Windows using notepad, arbitrary numbers and read these back from the card...

 

void sd_init(void){
	
	uint8_t buffer[10] = {0};
	uint16_t bytes_read = 0;
	
	PORTB |= (1 << LED_1);
	
	fr = disk_initialize(0);
	fr = f_mount(&FatFs, "", 0);
	fr = f_open(&File,"DATA.DAT", FA_READ);
	fr = f_read(&File, &buffer, 6, &bytes_read);
	fr = f_close(&File);
	
	if (fr != FR_OK){
		PORTB &= ~(1 << LED_1);
	}
}

I was expecting 6 bytes to be read, and for the read function to return FR_OK. All good, 6 bytes (the correct 6 bytes) read back, and function returns FR_OK and says it read 6 bytes. So my IO layer code is reading just fine..

-		buffer	0x40ec	uint8_t[10]{data}@0x40ec ([R28]+1)
		[0]	      53	uint8_t{data}@0x40ec
		[1]     	52	uint8_t{data}@0x40ed
		[2]     	51	uint8_t{data}@0x40ee
		[3]	      56	uint8_t{data}@0x40ef
		[4]	      56	uint8_t{data}@0x40f0
		[5]	      52	uint8_t{data}@0x40f1
		[6]	       0	uint8_t{data}@0x40f2
		[7]	       0	uint8_t{data}@0x40f3
		[8]	       0	uint8_t{data}@0x40f4
		[9]	       0	uint8_t{data}@0x40f5
		bytes_read	6	uint16_t{data}@0x40f6 ([R28]+11)

But still no write... I'll never look at an SD card the same after this.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow... solved.

 

As it transpires, it was a very simple problem stopping me writing. I've spent rather a long time reading the SD card specification (I'd heard before it was supplied only to SD association members, under fee - but have since found it's just a free download off their site).

 

Whilst I was testing for the busy flag at the start of the xmit_datablock function, I was not doing this at the end also. Immediately after testing for the response byte back from the card and initiating the write process (the dummy byte sent at deselect), I was then closing the file. Closing the file inclueds an f_sync, and thus sends a command to the card.

 

Reading the spec, any command received whilst writing to the card cancels the entire write operation. The card holds DO low until the card is finished, thus the addition of an extra wait_ready testing for an 0xFF response from the card detects the card is busy still, and holds there (for a max of 500ms, but as short as this process takes). At which point, I'm able to write to the card!

 

See 'wait_ready(500); // NEW LINE ADDED HERE' below

 

#ifdef	_USE_WRITE // HACK, this just said #if before, surely it should be #ifdef
static
int xmit_datablock (
	const BYTE *buff,	/* 512 byte data block to be transmitted */
	BYTE token			/* Data/Stop token */
)
{
	BYTE resp;


	if (!wait_ready(500)) return 0;		/* Leading busy check: Wait for card ready to accept data block */

	spi_transfer(token);					/* Xmit data token */
	if (token == 0xFD) return 1;		/* Do not send data if token is StopTran */

	xmit_spi_multi(buff, 512);			/* Data */
	spi_transfer(DUMMY_HIGH_BYTE);		// two CRC bytes
	spi_transfer(DUMMY_HIGH_BYTE);		/* Dummy CRC */

	resp = spi_transfer(DUMMY_HIGH_BYTE);				/* Receive data resp */
	wait_ready(500); // NEW LINE ADDED HERE

	return (resp & 0x1F) == 0x05 ? 1 : 0;	/* Data was accepted or not */

	/* Busy check is done at next transmission */
}
#endif

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jtw_11 wrote:
it's just a free download off their site).

Really?

 

The free download used to be just a "simplified" version (covered only SPI; not the "full" interface) - has that now changed ?

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The documents are all titled 'Simplified Specification', but still describe both the SPI and full interface. Without reading the entire document it doesn't say what is 'simplified' about it, but the part 1 document alone is 262 pages so doesn't appear to be all that simplified...

 

https://www.sdcard.org/downloads/pls/index.html

 

"The following conditions apply to the release of any and all parts of the simplified specifications ("Simplified Specifications") by the SD Card Association and the SD Group. The Simplified Specifications are a subset of the complete SD Specifications which are owned by the SD Card Association and the SD Group. These Simplified Specifications are provided on a non-confidential basis subject to the disclaimers below. Any implementation of the Simplified Specifications or any portions there of may require a license from the SD Card Association, SD Group, SD-3C, LLC or other third parties."

Last Edited: Mon. Mar 30, 2020 - 05:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jtw_11 wrote:
The Simplified Specifications are a subset of the complete SD Specifications 

There you go - not the complete specifications!

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes I saw that, it just appears from a glance that even the SD spec is included; out of interest, do you know what sort of information is missing?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

'fraid not - I've only ever had the "simplified" versions.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...