Add sd card support + savegames

This commit is contained in:
Vincent Mistler
2023-02-13 17:38:14 +01:00
parent 447ba93878
commit c8ee224092
42 changed files with 28650 additions and 4 deletions
+105
View File
@@ -0,0 +1,105 @@
/* f_util.c
Copyright 2021 Carl John Kugler III
Licensed under the Apache License, Version 2.0 (the License); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
*/
#include "ff.h"
const char *FRESULT_str(FRESULT i) {
switch (i) {
case FR_OK:
return "Succeeded";
case FR_DISK_ERR:
return "A hard error occurred in the low level disk I/O layer";
case FR_INT_ERR:
return "Assertion failed";
case FR_NOT_READY:
return "The physical drive cannot work";
case FR_NO_FILE:
return "Could not find the file";
case FR_NO_PATH:
return "Could not find the path";
case FR_INVALID_NAME:
return "The path name format is invalid";
case FR_DENIED:
return "Access denied due to prohibited access or directory full";
case FR_EXIST:
return "Access denied due to prohibited access (exists)";
case FR_INVALID_OBJECT:
return "The file/directory object is invalid";
case FR_WRITE_PROTECTED:
return "The physical drive is write protected";
case FR_INVALID_DRIVE:
return "The logical drive number is invalid";
case FR_NOT_ENABLED:
return "The volume has no work area (mount)";
case FR_NO_FILESYSTEM:
return "There is no valid FAT volume";
case FR_MKFS_ABORTED:
return "The f_mkfs() aborted due to any problem";
case FR_TIMEOUT:
return "Could not get a grant to access the volume within defined "
"period";
case FR_LOCKED:
return "The operation is rejected according to the file sharing "
"policy";
case FR_NOT_ENOUGH_CORE:
return "LFN working buffer could not be allocated";
case FR_TOO_MANY_OPEN_FILES:
return "Number of open files > FF_FS_LOCK";
case FR_INVALID_PARAMETER:
return "Given parameter is invalid";
default:
return "Unknown";
}
}
FRESULT delete_node (
TCHAR* path, /* Path name buffer with the sub-directory to delete */
UINT sz_buff, /* Size of path name buffer (items) */
FILINFO* fno /* Name read buffer */
)
{
UINT i, j;
FRESULT fr;
DIR dir;
fr = f_opendir(&dir, path); /* Open the sub-directory to make it empty */
if (fr != FR_OK) return fr;
for (i = 0; path[i]; i++) ; /* Get current path length */
path[i++] = '/';
for (;;) {
fr = f_readdir(&dir, fno); /* Get a directory item */
if (fr != FR_OK || !fno->fname[0]) break; /* End of directory? */
j = 0;
do { /* Make a path name */
if (i + j >= sz_buff) { /* Buffer over flow? */
fr = 100; break; /* Fails with 100 when buffer overflow */
}
path[i + j] = fno->fname[j];
} while (fno->fname[j++]);
if (fno->fattrib & AM_DIR) { /* Item is a sub-directory */
fr = delete_node(path, sz_buff, fno);
} else { /* Item is a file */
fr = f_unlink(path);
}
if (fr != FR_OK) break;
}
path[--i] = 0; /* Restore the path name */
f_closedir(&dir);
if (fr == FR_OK) fr = f_unlink(path); /* Delete the empty sub-directory */
return fr;
}
+463
View File
@@ -0,0 +1,463 @@
/* ff_stdio.c
Copyright 2021 Carl John Kugler III
Licensed under the Apache License, Version 2.0 (the License); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
*/
// For compatibility with FreeRTOS+FAT API
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "my_debug.h"
//
#include "f_util.h"
#include "ff_stdio.h"
#define TRACE_PRINTF(fmt, args...) {}
//#define TRACE_PRINTF printf
static BYTE posix2mode(const char *pcMode) {
if (0 == strcmp("r", pcMode)) return FA_READ;
if (0 == strcmp("r+", pcMode)) return FA_READ | FA_WRITE;
if (0 == strcmp("w", pcMode)) return FA_CREATE_ALWAYS | FA_WRITE;
if (0 == strcmp("w+", pcMode)) return FA_CREATE_ALWAYS | FA_WRITE | FA_READ;
if (0 == strcmp("a", pcMode)) return FA_OPEN_APPEND | FA_WRITE;
if (0 == strcmp("a+", pcMode)) return FA_OPEN_APPEND | FA_WRITE | FA_READ;
if (0 == strcmp("wx", pcMode)) return FA_CREATE_NEW | FA_WRITE;
if (0 == strcmp("w+x", pcMode)) return FA_CREATE_NEW | FA_WRITE | FA_READ;
return 0;
}
int fresult2errno(FRESULT fr) {
switch (fr) {
case FR_OK:
return 0;
case FR_DISK_ERR:
return EIO;
case FR_INT_ERR:
return EIO;
case FR_NOT_READY:
return EIO;
case FR_NO_FILE:
return ENOENT;
case FR_NO_PATH:
return ENOENT;
case FR_INVALID_NAME:
return ENAMETOOLONG;
case FR_DENIED:
return EACCES;
case FR_EXIST:
return EEXIST;
case FR_INVALID_OBJECT:
return EIO;
case FR_WRITE_PROTECTED:
return EACCES;
case FR_INVALID_DRIVE:
return ENOENT;
case FR_NOT_ENABLED:
return ENOENT;
case FR_NO_FILESYSTEM:
return ENOENT;
case FR_MKFS_ABORTED:
return EIO;
case FR_TIMEOUT:
return EIO;
case FR_LOCKED:
return EACCES;
case FR_NOT_ENOUGH_CORE:
return ENOMEM;
case FR_TOO_MANY_OPEN_FILES:
return ENFILE;
case FR_INVALID_PARAMETER:
return ENOSYS;
default:
return -1;
}
}
FF_FILE *ff_fopen(const char *pcFile, const char *pcMode) {
TRACE_PRINTF("%s\n", __func__);
// FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode);
// /* Open or create a file */ FRESULT f_open (
// FIL* fp, /* [OUT] Pointer to the file object structure */
// const TCHAR* path, /* [IN] File name */
// BYTE mode /* [IN] Mode flags */
//);
FIL *fp = malloc(sizeof(FIL));
if (!fp) {
errno = ENOMEM;
return NULL;
}
FRESULT fr = f_open(fp, pcFile, posix2mode(pcMode));
errno = fresult2errno(fr);
if (FR_OK != fr) {
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
free(fp);
fp = 0;
}
return fp;
}
int ff_fclose(FF_FILE *pxStream) {
TRACE_PRINTF("%s\n", __func__);
// FRESULT f_close (
// FIL* fp /* [IN] Pointer to the file object */
//);
FRESULT fr = f_close(pxStream);
if (FR_OK != fr)
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
free(pxStream);
if (FR_OK == fr)
return 0;
else
return -1;
}
// Populates an ff_stat_struct with information about a file.
int ff_stat(const char *pcFileName, FF_Stat_t *pxStatBuffer) {
TRACE_PRINTF("%s\n", __func__);
// FRESULT f_stat (
// const TCHAR* path, /* [IN] Object name */
// FILINFO* fno /* [OUT] FILINFO structure */
//);
myASSERT(pxStatBuffer);
FILINFO filinfo;
FRESULT fr = f_stat(pcFileName, &filinfo);
pxStatBuffer->st_size = filinfo.fsize;
if (FR_OK != fr)
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
if (FR_OK == fr)
return 0;
else
return -1;
}
size_t ff_fwrite(const void *pvBuffer, size_t xSize, size_t xItems,
FF_FILE *pxStream) {
TRACE_PRINTF("%s\n", __func__);
// FRESULT f_write (
// FIL* fp, /* [IN] Pointer to the file object structure */
// const void* buff, /* [IN] Pointer to the data to be written */
// UINT btw, /* [IN] Number of bytes to write */
// UINT* bw /* [OUT] Pointer to the variable to return number of
// bytes written */
//);
UINT bw = 0;
FRESULT fr = f_write(pxStream, pvBuffer, xSize * xItems, &bw);
if (FR_OK != fr)
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
return bw / xSize;
}
size_t ff_fread(void *pvBuffer, size_t xSize, size_t xItems,
FF_FILE *pxStream) {
TRACE_PRINTF("%s\n", __func__);
// FRESULT f_read (
// FIL* fp, /* [IN] File object */
// void* buff, /* [OUT] Buffer to store read data */
// UINT btr, /* [IN] Number of bytes to read */
// UINT* br /* [OUT] Number of bytes read */
//);
UINT br = 0;
FRESULT fr = f_read(pxStream, pvBuffer, xSize * xItems, &br);
if (FR_OK != fr)
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
return br / xSize;
}
int ff_chdir(const char *pcDirectoryName) {
TRACE_PRINTF("%s\n", __func__);
// FRESULT f_chdir (
// const TCHAR* path /* [IN] Path name */
//);
FRESULT fr = f_chdir(pcDirectoryName);
if (FR_OK != fr)
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
if (FR_OK == fr)
return 0;
else
return -1;
}
char *ff_getcwd(char *pcBuffer, size_t xBufferLength) {
TRACE_PRINTF("%s\n", __func__);
// FRESULT f_getcwd (
// TCHAR* buff, /* [OUT] Buffer to return path name */
// UINT len /* [IN] The length of the buffer */
//);
char buf[xBufferLength];
FRESULT fr = f_getcwd(buf, xBufferLength);
if (FR_OK != fr)
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
// If the current working directory name was successfully written to
// pcBuffer then pcBuffer is returned. Otherwise NULL is returned.
if (FR_OK == fr) {
if ('/' != buf[0]) {
// Strip off drive prefix:
char *p = strchr(buf, ':');
if (p)
++p;
else
p = buf;
strncpy(pcBuffer, p, xBufferLength);
}
return pcBuffer;
} else {
return NULL;
}
}
int ff_mkdir(const char *pcDirectoryName) {
TRACE_PRINTF("%s(pxStream=%s)\n", __func__, pcDirectoryName);
FRESULT fr = f_mkdir(pcDirectoryName);
if (FR_OK != fr && FR_EXIST != fr)
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
if (FR_OK == fr || FR_EXIST == fr)
return 0;
else
return -1;
}
int ff_fputc(int iChar, FF_FILE *pxStream) {
// TRACE_PRINTF("%s(iChar=%c,pxStream=%p)\n", __func__, iChar, pxStream);
// FRESULT f_write (
// FIL* fp, /* [IN] Pointer to the file object structure */
// const void* buff, /* [IN] Pointer to the data to be written */
// UINT btw, /* [IN] Number of bytes to write */
// UINT* bw /* [OUT] Pointer to the variable to return number of
// bytes written */
//);
UINT bw = 0;
uint8_t buff[1];
buff[0] = iChar;
FRESULT fr = f_write(pxStream, buff, 1, &bw);
if (FR_OK != fr)
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
// On success the byte written to the file is returned. If any other value
// is returned then the byte was not written to the file and the task's
// errno will be set to indicate the reason.
if (1 == bw)
return iChar;
else {
return -1;
}
}
int ff_fgetc(FF_FILE *pxStream) {
// TRACE_PRINTF("%s(pxStream=%p)\n", __func__, pxStream);
// FRESULT f_read (
// FIL* fp, /* [IN] File object */
// void* buff, /* [OUT] Buffer to store read data */
// UINT btr, /* [IN] Number of bytes to read */
// UINT* br /* [OUT] Number of bytes read */
//);
uint8_t buff[1] = {0};
UINT br;
FRESULT fr = f_read(pxStream, buff, 1, &br);
if (FR_OK != fr)
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
// On success the byte read from the file system is returned. If a byte
// could not be read from the file because the read position is already at
// the end of the file then FF_EOF is returned.
if (1 == br)
return buff[0];
else
return FF_EOF;
}
int ff_rmdir(const char *pcDirectory) {
TRACE_PRINTF("%s\n", __func__);
// FRESULT f_unlink (
// const TCHAR* path /* [IN] Object name */
//);
FRESULT fr = f_unlink(pcDirectory);
// If the directory was removed successfully then zero is returned. If the
// directory could not be removed then -1 is returned and the task's errno
// is set to indicate the reason.
errno = fresult2errno(fr);
if (FR_OK == fr)
return 0;
else
return -1;
}
int ff_remove(const char *pcPath) {
TRACE_PRINTF("%s\n", __func__);
FRESULT fr = f_unlink(pcPath);
errno = fresult2errno(fr);
if (FR_OK == fr)
return 0;
else
return -1;
}
long ff_ftell(FF_FILE *pxStream) {
TRACE_PRINTF("%s\n", __func__);
// FSIZE_t f_tell (
// FIL* fp /* [IN] File object */
//);
FSIZE_t pos = f_tell(pxStream);
myASSERT(pos < LONG_MAX);
return pos;
}
int ff_fseek(FF_FILE *pxStream, int iOffset, int iWhence) {
TRACE_PRINTF("%s\n", __func__);
FRESULT fr = -1;
switch (iWhence) {
case FF_SEEK_CUR: // The current file position.
if ((int)f_tell(pxStream) + iOffset < 0) return -1;
fr = f_lseek(pxStream, f_tell(pxStream) + iOffset);
break;
case FF_SEEK_END: // The end of the file.
if ((int)f_size(pxStream) + iOffset < 0) return -1;
fr = f_lseek(pxStream, f_size(pxStream) + iOffset);
break;
case FF_SEEK_SET: // The beginning of the file.
if (iOffset < 0) return -1;
fr = f_lseek(pxStream, iOffset);
break;
default:
myASSERT(!"Bad iWhence");
}
errno = fresult2errno(fr);
if (FR_OK == fr)
return 0;
else
return -1;
}
int ff_findfirst(const char *pcDirectory, FF_FindData_t *pxFindData) {
TRACE_PRINTF("%s(%s)\n", __func__, pcDirectory);
// FRESULT f_findfirst (
// DIR* dp, /* [OUT] Poninter to the directory object */
// FILINFO* fno, /* [OUT] Pointer to the file information structure
// */ const TCHAR* path, /* [IN] Pointer to the directory name to be
// opened */ const TCHAR* pattern /* [IN] Pointer to the matching pattern
// string */
//);
char buf1[ffconfigMAX_FILENAME] = {0};
if (pcDirectory[0]) {
FRESULT fr = f_getcwd(buf1, sizeof buf1);
errno = fresult2errno(fr);
if (FR_OK != fr) return -1;
fr = f_chdir(pcDirectory);
errno = fresult2errno(fr);
if (FR_OK != fr) return -1;
}
char buf2[ffconfigMAX_FILENAME] = {0};
FRESULT fr = f_getcwd(buf2, sizeof buf2);
TRACE_PRINTF("%s: f_findfirst(path=%s)\n", __func__, buf2);
fr = f_findfirst(&pxFindData->dir, &pxFindData->fileinfo, buf2, "*");
errno = fresult2errno(fr);
pxFindData->pcFileName = pxFindData->fileinfo.fname;
pxFindData->ulFileSize = pxFindData->fileinfo.fsize;
TRACE_PRINTF("%s: fname=%s\n", __func__, pxFindData->fileinfo.fname);
if (pcDirectory[0]) {
FRESULT fr2 = f_chdir(buf1);
errno = fresult2errno(fr2);
if (FR_OK != fr2) return -1;
}
if (FR_OK == fr)
return 0;
else
return -1;
}
int ff_findnext(FF_FindData_t *pxFindData) {
TRACE_PRINTF("%s\n", __func__);
// FRESULT f_findnext (
// DIR* dp, /* [IN] Poninter to the directory object */
// FILINFO* fno /* [OUT] Pointer to the file information structure
// */
//);
FRESULT fr = f_findnext(&pxFindData->dir, &pxFindData->fileinfo);
errno = fresult2errno(fr);
pxFindData->pcFileName = pxFindData->fileinfo.fname;
pxFindData->ulFileSize = pxFindData->fileinfo.fsize;
TRACE_PRINTF("%s: fname=%s\n", __func__, pxFindData->fileinfo.fname);
if (FR_OK == fr && pxFindData->fileinfo.fname[0]) {
return 0;
} else {
return -1;
}
}
FF_FILE *ff_truncate(const char *pcFileName, long lTruncateSize) {
TRACE_PRINTF("%s\n", __func__);
FIL *fp = malloc(sizeof(FIL));
if (!fp) {
errno = ENOMEM;
return NULL;
}
FRESULT fr = f_open(fp, pcFileName, FA_OPEN_APPEND | FA_WRITE);
if (FR_OK != fr)
printf("%s: f_open error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
if (FR_OK != fr) return NULL;
while (f_tell(fp) < (FSIZE_t)lTruncateSize) {
UINT bw = 0;
char c = 0;
fr = f_write(fp, &c, 1, &bw);
if (FR_OK != fr)
TRACE_PRINTF("%s error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
errno = fresult2errno(fr);
if (1 != bw) return NULL;
}
fr = f_lseek(fp, lTruncateSize);
errno = fresult2errno(fr);
if (FR_OK != fr)
printf("%s: f_lseek error: %s (%d)\n", __func__, FRESULT_str(fr), fr);
if (FR_OK != fr) return NULL;
fr = f_truncate(fp);
if (FR_OK != fr)
printf("%s: f_truncate error: %s (%d)\n", __func__, FRESULT_str(fr),
fr);
errno = fresult2errno(fr);
if (FR_OK == fr)
return fp;
else
return NULL;
}
int ff_seteof(FF_FILE *pxStream) {
TRACE_PRINTF("%s\n", __func__);
FRESULT fr = f_truncate(pxStream);
errno = fresult2errno(fr);
if (FR_OK == fr)
return 0;
else
return FF_EOF;
}
int ff_rename(const char *pcOldName, const char *pcNewName,
int bDeleteIfExists) {
TRACE_PRINTF("%s\n", __func__);
// FRESULT f_rename (
// const TCHAR* old_name, /* [IN] Old object name */
// const TCHAR* new_name /* [IN] New object name */
//);
// Any object with this path name except old_name must not be exist, or the
// function fails with FR_EXIST.
if (bDeleteIfExists) f_unlink(pcNewName);
FRESULT fr = f_rename(pcOldName, pcNewName);
errno = fresult2errno(fr);
if (FR_OK == fr)
return 0;
else
return -1;
}
char *ff_fgets(char *pcBuffer, size_t xCount, FF_FILE *pxStream) {
TRACE_PRINTF("%s\n", __func__);
TCHAR *p = f_gets(pcBuffer, xCount, pxStream);
// On success a pointer to pcBuffer is returned. If there is a read error
// then NULL is returned and the task's errno is set to indicate the reason.
if (p == pcBuffer)
return pcBuffer;
else {
errno = EIO;
return NULL;
}
}
+163
View File
@@ -0,0 +1,163 @@
/* glue.c
Copyright 2021 Carl John Kugler III
Licensed under the Apache License, Version 2.0 (the License); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
*/
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be */
/* attached to the FatFs via a glue function rather than modifying it. */
/* This is an example of glue functions to attach various exsisting */
/* storage control modules to the FatFs module with a defined API. */
/*-----------------------------------------------------------------------*/
#include <stdio.h>
//
#include "ff.h" /* Obtains integer types */
//
#include "diskio.h" /* Declarations of disk functions */
//
#include "hw_config.h"
#include "my_debug.h"
#include "sd_card.h"
#define TRACE_PRINTF(fmt, args...)
//#define TRACE_PRINTF printf // task_printf
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status(BYTE pdrv /* Physical drive nmuber to identify the drive */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *p_sd = sd_get_by_num(pdrv);
if (!p_sd) return RES_PARERR;
sd_card_detect(p_sd); // Fast: just a GPIO read
return p_sd->m_Status; // See http://elm-chan.org/fsw/ff/doc/dstat.html
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize(
BYTE pdrv /* Physical drive nmuber to identify the drive */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *p_sd = sd_get_by_num(pdrv);
if (!p_sd) return RES_PARERR;
return sd_init(p_sd); // See http://elm-chan.org/fsw/ff/doc/dstat.html
}
static int sdrc2dresult(int sd_rc) {
switch (sd_rc) {
case SD_BLOCK_DEVICE_ERROR_NONE:
return RES_OK;
case SD_BLOCK_DEVICE_ERROR_UNUSABLE:
case SD_BLOCK_DEVICE_ERROR_NO_RESPONSE:
case SD_BLOCK_DEVICE_ERROR_NO_INIT:
case SD_BLOCK_DEVICE_ERROR_NO_DEVICE:
return RES_NOTRDY;
case SD_BLOCK_DEVICE_ERROR_PARAMETER:
case SD_BLOCK_DEVICE_ERROR_UNSUPPORTED:
return RES_PARERR;
case SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED:
return RES_WRPRT;
case SD_BLOCK_DEVICE_ERROR_CRC:
case SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK:
case SD_BLOCK_DEVICE_ERROR_ERASE:
case SD_BLOCK_DEVICE_ERROR_WRITE:
default:
return RES_ERROR;
}
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read(BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *p_sd = sd_get_by_num(pdrv);
if (!p_sd) return RES_PARERR;
int rc = sd_read_blocks(p_sd, buff, sector, count);
return sdrc2dresult(rc);
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT disk_write(BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
LBA_t sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *p_sd = sd_get_by_num(pdrv);
if (!p_sd) return RES_PARERR;
int rc = sd_write_blocks(p_sd, buff, sector, count);
return sdrc2dresult(rc);
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
) {
TRACE_PRINTF(">>> %s\n", __FUNCTION__);
sd_card_t *p_sd = sd_get_by_num(pdrv);
if (!p_sd) return RES_PARERR;
switch (cmd) {
case GET_SECTOR_COUNT: { // Retrieves number of available sectors, the
// largest allowable LBA + 1, on the drive
// into the LBA_t variable pointed by buff.
// This command is used by f_mkfs and f_fdisk
// function to determine the size of
// volume/partition to be created. It is
// required when FF_USE_MKFS == 1.
static LBA_t n;
n = sd_sectors(p_sd);
*(LBA_t *)buff = n;
if (!n) return RES_ERROR;
return RES_OK;
}
case GET_BLOCK_SIZE: { // Retrieves erase block size of the flash
// memory media in unit of sector into the DWORD
// variable pointed by buff. The allowable value
// is 1 to 32768 in power of 2. Return 1 if the
// erase block size is unknown or non flash
// memory media. This command is used by only
// f_mkfs function and it attempts to align data
// area on the erase block boundary. It is
// required when FF_USE_MKFS == 1.
static DWORD bs = 1;
*(DWORD *)buff = bs;
return RES_OK;
}
case CTRL_SYNC:
return RES_OK;
default:
return RES_PARERR;
}
}
+39
View File
@@ -0,0 +1,39 @@
/* my_debug.c
Copyright 2021 Carl John Kugler III
Licensed under the Apache License, Version 2.0 (the License); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
*/
#include <stdio.h>
#include <stdarg.h>
#include "my_debug.h"
void my_printf(const char *pcFormat, ...) {
char pcBuffer[256] = {0};
va_list xArgs;
va_start(xArgs, pcFormat);
vsnprintf(pcBuffer, sizeof(pcBuffer), pcFormat, xArgs);
va_end(xArgs);
printf("%s", pcBuffer);
fflush(stdout);
}
void my_assert_func(const char *file, int line, const char *func,
const char *pred) {
printf("assertion \"%s\" failed: file \"%s\", line %d, function: %s\n",
pred, file, line, func);
fflush(stdout);
__asm volatile("cpsid i" : : : "memory"); /* Disable global interrupts. */
while (1) {
__asm("bkpt #0");
}; // Stop in GUI as if at a breakpoint (if debugging, otherwise loop
// forever)
}
+116
View File
@@ -0,0 +1,116 @@
/* rtc.c
Copyright 2021 Carl John Kugler III
Licensed under the Apache License, Version 2.0 (the License); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed
under the License is distributed on an AS IS BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
*/
#include <stdio.h>
#include <time.h>
//
#include "hardware/rtc.h"
#include "pico/stdio.h"
#include "pico/stdlib.h"
#include "pico/util/datetime.h"
//
#include "ff.h"
#include "util.h" // calculate_checksum
//
#include "rtc.h"
static time_t epochtime;
// Make an attempt to save a recent time stamp across reset:
typedef struct rtc_save {
uint32_t signature;
datetime_t datetime;
uint32_t checksum; // last, not included in checksum
} rtc_save_t;
static rtc_save_t rtc_save __attribute__((section(".uninitialized_data")));
static void update_epochtime() {
bool rc = rtc_get_datetime(&rtc_save.datetime);
if (rc) {
rtc_save.signature = 0xBABEBABE;
struct tm timeinfo = {
.tm_sec = rtc_save.datetime
.sec, /* Seconds. [0-60] (1 leap second) */
.tm_min = rtc_save.datetime.min, /* Minutes. [0-59] */
.tm_hour = rtc_save.datetime.hour, /* Hours. [0-23] */
.tm_mday = rtc_save.datetime.day, /* Day. [1-31] */
.tm_mon = rtc_save.datetime.month - 1, /* Month. [0-11] */
.tm_year = rtc_save.datetime.year - 1900, /* Year - 1900. */
.tm_wday = 0, /* Day of week. [0-6] */
.tm_yday = 0, /* Days in year.[0-365] */
.tm_isdst = -1 /* DST. [-1/0/1]*/
};
rtc_save.checksum = calculate_checksum((uint32_t *)&rtc_save,
offsetof(rtc_save_t, checksum));
epochtime = mktime(&timeinfo);
rtc_save.datetime.dotw = timeinfo.tm_wday;
// configASSERT(-1 != epochtime);
}
}
time_t time(time_t *pxTime) {
update_epochtime();
if (pxTime) {
*pxTime = epochtime;
}
return epochtime;
}
void time_init() {
rtc_init();
datetime_t t = {0, 0, 0, 0, 0, 0, 0};
rtc_get_datetime(&t);
if (!t.year && rtc_save.datetime.year) {
uint32_t xor_checksum = calculate_checksum(
(uint32_t *)&rtc_save, offsetof(rtc_save_t, checksum));
if (rtc_save.signature == 0xBABEBABE &&
rtc_save.checksum == xor_checksum) {
// Set rtc
rtc_set_datetime(&rtc_save.datetime);
}
}
}
// Called by FatFs:
DWORD get_fattime(void) {
datetime_t t = {0, 0, 0, 0, 0, 0, 0};
bool rc = rtc_get_datetime(&t);
if (!rc) return 0;
DWORD fattime = 0;
// bit31:25
// Year origin from the 1980 (0..127, e.g. 37 for 2017)
uint8_t yr = t.year - 1980;
fattime |= (0b01111111 & yr) << 25;
// bit24:21
// Month (1..12)
uint8_t mo = t.month;
fattime |= (0b00001111 & mo) << 21;
// bit20:16
// Day of the month (1..31)
uint8_t da = t.day;
fattime |= (0b00011111 & da) << 16;
// bit15:11
// Hour (0..23)
uint8_t hr = t.hour;
fattime |= (0b00011111 & hr) << 11;
// bit10:5
// Minute (0..59)
uint8_t mi = t.min;
fattime |= (0b00111111 & mi) << 5;
// bit4:0
// Second / 2 (0..29, e.g. 25 for 50)
uint8_t sd = t.sec / 2;
fattime |= (0b00011111 & sd);
return fattime;
}