Im trying to implement the FatFs module for a project for several days now.
My low-level I/O configurations are correctly implemented as i can see the signals using an oscilloscope. But i cant seem to get a proper write working on the SD-Card. All i get is an empty file on my SD-Card which gets created correctly. Also a read of the files works just fine. As i was debugging using MPLAB X i found that the f_write function never leaves a for loop.
Here is my main.c code:
/*
Main application
*/
FATFS FatFs;
FIL fil1, fil2;
void main(void)
{
SYSTEM_Init();
BYTE buffer[4];
UINT bw, br;
FRESULT fr;
if(f_mount(&FatFs, "", 1) == FR_OK)
{
//open source file
fr = f_open(&fil1, "READ.TXT", FA_READ);
if(fr) return;
//create destination file
fr = f_open(&fil2, "WRITE.TXT", FA_WRITE | FA_CREATE_ALWAYS);
if(fr) return;
//copy 1 to 2
for(;;){
fr = f_read(&fil1, buffer, sizeof buffer, &br);
if(fr || br == 0) break;
fr = f_write(&fil2, buffer, br, &bw);
if(fr || bw < br) break;
}
// const char *writedata = &write;
// //f_printf(&fil, "%d", 1234);
// f_write(&fil, writedata, strlen(writedata), &bw);
f_close(&fil1);
f_close(&fil2);
f_mount(NULL, "", 0);
}
return;
}
Here is my diskio.c code:
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */
/*-----------------------------------------------------------------------*/
/* 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. */
/*-----------------------------------------------------------------------*/
#ifdef __XC8
#include <p18f46j50.h>
#endif
#ifndef __XC8
#include <p18cxxx.h>
#endif
#include "diskio.h" /* FatFs lower layer API */
#include "sdspi.h"
#include "sdctrl.h"
/* Definitions of physical drive number for each drive */
#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
#define _XTAL_FREQ 8000000
/*--------------------------------------------------------------------------
Module Private Functions
---------------------------------------------------------------------------*/
/* Definitions for SDC command */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND (MMC) */
#define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
#define CMD8 (8) /* SEND_IF_COND */
#define CMD9 (9) /* SEND_CSD */
#define CMD10 (10) /* SEND_CID */
#define CMD12 (12) /* STOP_TRANSMISSION */
#define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
#define CMD16 (16) /* SET_BLOCKLEN */
#define CMD17 (17) /* READ_SINGLE_BLOCK */
#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */
#define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24) /* WRITE_BLOCK */
#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
#define CMD32 (32) /* ERASE_ER_BLK_START */
#define CMD33 (33) /* ERASE_ER_BLK_END */
#define CMD38 (38) /* ERASE */
#define CMD55 (55) /* APP_CMD */
#define CMD58 (58) /* READ_OCR */
/* MMC card type flags (MMC_GET_TYPE) */
#define CT_MMC 0x01 /* MMC ver 3 */
#define CT_SD1 0x02 /* SD ver 1 */
#define CT_SD2 0x04 /* SD ver 2 */
#define CT_SDC (CT_SD1|CT_SD2) /* SD */
#define CT_BLOCK 0x08 /* Block addressing */
static
DSTATUS Stat = STA_NOINIT; /* Disk status */
static
BYTE CardType; /* Card type flags */
/*-----------------------------------------------------------------------*/
/* Wait for card ready */
/*-----------------------------------------------------------------------*/
static
BYTE wait_ready (void) /* 1:Ready, 0:Timeout */
{
UINT tmr;
for (tmr = 5000; tmr; tmr--) { /* Wait for ready in timeout of 500ms */
if (sdspi_rxByte() == 0xFF) break;
__delay_us(100);
}
return tmr ? 1 : 0;
}
/*-----------------------------------------------------------------------*/
/* Deselect the card and release SPI bus */
/*-----------------------------------------------------------------------*/
static
void deselect (void)
{
sd_deselect(); /* Set CS# high */
sdspi_rxByte(); /* Dummy clock (force DO hi-z for multiple slave SPI) */
}
/*-----------------------------------------------------------------------*/
/* Select the card and wait for ready */
/*-----------------------------------------------------------------------*/
static
BYTE select (void) /* 1:Successful, 0:Timeout */
{
sd_select(); /* Set CS# low */
sdspi_rxByte(); /* Dummy clock (force DO enabled) */
if (wait_ready()) return 1; /* Wait for card ready */
deselect();
return 0; /* Timeout */
}
/*-----------------------------------------------------------------------*/
/* Receive a data packet from MMC */
/*-----------------------------------------------------------------------*/
static
BYTE rcvr_datablock (
BYTE *buff, /* Data buffer to store received data */
UINT btr /* Byte count (must be multiple of 4) */
)
{
BYTE token;
UINT tmr;
for (tmr = 2000; tmr; tmr--) { /* Wait for data packet in timeout of 200ms */
token = sdspi_rxByte();
if (token != 0xFF) break;
// __delay_us(100);
}
if (token != 0xFE) return 0; /* If not valid data token, retutn with error */
do
*buff++ = sdspi_rxByte(); /* Receive the data block into buffer */
while (--btr);
sdspi_rxByte(); /* Discard CRC */
sdspi_rxByte();
return 1; /* Return with success */
}
/*-----------------------------------------------------------------------*/
/* Send a data packet to MMC */
/*-----------------------------------------------------------------------*/
#if _USE_WRITE
static
BYTE xmit_datablock (
const BYTE *buff, /* 512 byte data block to be transmitted */
BYTE token /* Data/Stop token */
)
{
BYTE resp;
WORD i;
if (!wait_ready()) return 0;
sdspi_txByte(token); /* Xmit data token */
if (token != 0xFD) { /* Is data token */
i = 512;
do
{
sdspi_txByte(*buff++); /* Xmit the data block to the MMC */
__delay_us(100);
}while (--i);
sdspi_rxByte(); /* CRC (Dummy) */
sdspi_rxByte();
resp = sdspi_rxByte(); /* Reveive data response */
if ((resp & 0x1F) != 0x05) /* If not accepted, return with error */
return 0;
}
return 1;
}
#endif
/*-----------------------------------------------------------------------*/
/* Send a command packet to MMC */
/*-----------------------------------------------------------------------*/
/* NOTE: XC8 compiler is unable to allow recursion,
/ so the send_cmd function had to be divided */
#ifdef __XC8
static
BYTE __send_cmd ( /* Returns R1 resp (bit7==1:Send failed) */
BYTE cmd, /* Command index */
DWORD arg /* Argument */
)
{
BYTE n, res;
/* Select the card and wait for ready except to stop multiple block read */
if (cmd != CMD12) {
sd_deselect();
if (!select()) return 0xFF;
// sd_select();
// if (wait_ready() != 0xFF) return 0xFF;
}
/* Send command packet */
sdspi_txByte(0x40 | cmd); /* Start + Command index */
sdspi_txByte((BYTE)(arg >> 24)); /* Argument[31..24] */
sdspi_txByte((BYTE)(arg >> 16)); /* Argument[23..16] */
sdspi_txByte((BYTE)(arg >> 8)); /* Argument[15..8] */
sdspi_txByte((BYTE)arg); /* Argument[7..0] */
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) + Stop */
if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) Stop */
sdspi_txByte(n);
/* Receive command response */
if (cmd == CMD12) sdspi_rxByte(); /* Skip a stuff byte when stop reading */
n = 10; /* Wait for a valid response in timeout of 10 attempts */
do
res = sdspi_rxByte();
while ((res & 0x80) && --n);
return res; /* Return with the response value */
}
#endif
static
BYTE send_cmd ( /* Returns R1 resp (bit7==1:Send failed) */
BYTE cmd, /* Command index */
DWORD arg /* Argument */
)
{
#ifndef __XC8
BYTE n;
#endif
BYTE res;
if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
cmd &= 0x7F;
#ifdef __XC8
res = __send_cmd(CMD55, 0);
#else
res = send_cmd(CMD55, 0);
#endif
if (res > 1) return res;
}
#ifdef __XC8
return __send_cmd(cmd, arg); /* Return with the response value */
#else
/* Select the card and wait for ready except to stop multiple block read */
if (cmd != CMD12) {
sd_deselect();
if (!select()) return 0xFF;
}
/* Send command packet */
sdspi_txByte(0x40 | cmd); /* Start + Command index */
sdspi_txByte((BYTE)(arg >> 24)); /* Argument[31..24] */
sdspi_txByte((BYTE)(arg >> 16)); /* Argument[23..16] */
sdspi_txByte((BYTE)(arg >> 8)); /* Argument[15..8] */
sdspi_txByte((BYTE)arg); /* Argument[7..0] */
n = 0x01; /* Dummy CRC + Stop */
if (cmd == CMD0) n = 0x95; /* Valid CRC for CMD0(0) + Stop */
if (cmd == CMD8) n = 0x87; /* Valid CRC for CMD8(0x1AA) Stop */
sdspi_txByte(n);
/* Receive command response */
if (cmd == CMD12) sdspi_rxByte(); /* Skip a stuff byte when stop reading */
n = 10; /* Wait for a valid response in timeout of 10 attempts */
do
res = sdspi_rxByte();
while ((res & 0x80) && --n);
return res; /* Return with the response value */
#endif
}
/*--------------------------------------------------------------------------
Public Functions
---------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber (0) */
)
{
BYTE n, cmd, ty, ocr[4];
UINT tmr;
if (pdrv) return STA_NOINIT; /* Supports only single drive */
if (Stat & STA_NODISK) return Stat; /* No card in the socket */
sdspi_enable(); /* Enable the SPI port */
sdspi_setSlowMode(); /* Setup for slow mode */
for (n = 10; n; n--) sdspi_rxByte(); /* 80 dummy clocks */
ty = 0;
if (send_cmd(CMD0, 0) == 1) { /* Enter Idle state */
if (send_cmd(CMD8, 0x1AA) == 1) { /* SDv2? */
for (n = 0; n < 4; n++) ocr[n] = sdspi_rxByte(); /* Get trailing return value of R7 resp */
if (ocr[2] == 0x01 && ocr[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */
for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state (ACMD41 with HCS bit) */
if (send_cmd(ACMD41, 1UL << 30) == 0) break;
__delay_ms(1);
}
if (tmr && send_cmd(CMD58, 0) == 0) { /* Check CCS bit in the OCR */
for (n = 0; n < 4; n++) ocr[n] = sdspi_rxByte();
ty = (ocr[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */
}
}
} else { /* SDv1 or MMCv3 */
if (send_cmd(ACMD41, 0) <= 1) {
ty = CT_SD1; cmd = ACMD41; /* SDv1 */
} else {
ty = CT_MMC; cmd = CMD1; /* MMCv3 */
}
for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state */
if (send_cmd(cmd, 0) == 0) break;
__delay_ms(1);
}
if (!tmr || send_cmd(CMD16, 512) != 0) /* Set R/W block length to 512 */
ty = 0;
}
}
CardType = ty;
deselect();
if (ty) { /* Initialization succeded */
Stat &= ~STA_NOINIT; /* Clear STA_NOINIT */
sdspi_setFastMode();
}
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Get Disk Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber (0) */
)
{
if (pdrv) return STA_NOINIT; /* Supports only single drive */
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
UINT count /* Sector count (1..128) */
)
{
BYTE cmd;
if (pdrv || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */
cmd = count > 1 ? CMD18 : CMD17; /* READ_MULTIPLE_BLOCK : READ_SINGLE_BLOCK */
if (send_cmd(cmd, sector) == 0) {
do {
if (!rcvr_datablock(buff, 512)) break;
buff += 512;
} while (--count);
if (cmd == CMD18) send_cmd(CMD12, 0); /* STOP_TRANSMISSION */
}
deselect();
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
#if _USE_WRITE
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber (0) */
const BYTE *buff, /* Pointer to the data to be written */
DWORD sector, /* Start sector number (LBA) */
UINT count /* Sector count (1..128) */
)
{
if (pdrv || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if (Stat & STA_PROTECT) return RES_WRPRT;
if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert to byte address if needed */
if (count == 1) { /* Single block write */
if ((send_cmd(CMD24, sector) == 0) /* WRITE_BLOCK */
&& xmit_datablock(buff, 0xFE))
count = 0;
}
else { /* Multiple block write */
if (CardType & CT_SDC) send_cmd(ACMD23, count);
if (send_cmd(CMD25, sector) == 0) { /* WRITE_MULTIPLE_BLOCK */
do {
if (!xmit_datablock(buff, 0xFC)) break;
buff += 512;
} while (--count);
if (!xmit_datablock(0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
deselect();
return count ? RES_ERROR : RES_OK;
}
#endif
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
//#if _USE_IOCTL
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
BYTE n, csd[16], *ptr = buff;
DWORD csize;
if (pdrv) return RES_PARERR;
res = RES_ERROR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
switch (cmd) {
case CTRL_SYNC : /* Make sure that no pending write process. Do not remove this or written sector might not left updated. */
if (select()) res = RES_OK;
break;
case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) {
if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
csize = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1;
*(DWORD*)buff = csize << 10;
} else { /* SDC ver 1.XX or MMC*/
n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
csize = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
*(DWORD*)buff = csize << (n - 9);
}
res = RES_OK;
}
break;
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
if (CardType & CT_SD2) { /* SDv2? */
if (send_cmd(ACMD13, 0) == 0) { /* Read SD status */
sdspi_rxByte();
if (rcvr_datablock(csd, 16)) { /* Read partial block */
for (n = 64 - 16; n; n--) sdspi_rxByte(); /* Purge trailing data */
*(DWORD*)buff = 16UL << (csd[10] >> 4);
res = RES_OK;
}
}
} else { /* SDv1 or MMCv3 */
if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) { /* Read CSD */
if (CardType & CT_SD1) { /* SDv1 */
*(DWORD*)buff = (((csd[10] & 63) << 1) + ((WORD)(csd[11] & 128) >> 7) + 1) << ((csd[13] >> 6) - 1);
} else { /* MMCv3 */
*(DWORD*)buff = ((WORD)((csd[10] & 124) >> 2) + 1) * (((csd[11] & 3) << 3) + ((csd[11] & 224) >> 5) + 1);
}
res = RES_OK;
}
}
break;
/* Following commands are never used by FatFs module */
case MMC_GET_TYPE : /* Get card type flags (1 byte) */
*ptr = CardType;
res = RES_OK;
break;
case MMC_GET_CSD : /* Receive CSD as a data block (16 bytes) */
if (send_cmd(CMD9, 0) == 0 /* READ_CSD */
&& rcvr_datablock(ptr, 16))
res = RES_OK;
break;
case MMC_GET_CID : /* Receive CID as a data block (16 bytes) */
if (send_cmd(CMD10, 0) == 0 /* READ_CID */
&& rcvr_datablock(ptr, 16))
res = RES_OK;
break;
case MMC_GET_OCR : /* Receive OCR as an R3 resp (4 bytes) */
if (send_cmd(CMD58, 0) == 0) { /* READ_OCR */
for (n = 4; n; n--) *ptr++ = sdspi_rxByte();
res = RES_OK;
}
break;
case MMC_GET_SDSTAT : /* Receive SD statsu as a data block (64 bytes) */
if (send_cmd(ACMD13, 0) == 0) { /* SD_STATUS */
sdspi_rxByte();
if (rcvr_datablock(ptr, 64))
res = RES_OK;
}
break;
default:
res = RES_PARERR;
}
deselect();
return res;
}
//#endif
Here is the f_write() function from the ff.c
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;
FATFS *fs;
DWORD clst, sect;
UINT wcnt, cc, csect;
const BYTE *wbuff = (const BYTE*)buff;
*bw = 0; /* Clear write byte counter */
res = validate(&fp->obj, &fs); /* Check validity of the file object */
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */
if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
/* Check fptr wrap-around (file size cannot reach 4GiB on FATxx) */
if ((!_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);
}
// HERE DOES THE LOOP HAPPEN
for ( ; btw; /* Repeat until all data written */
wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) {
if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */
csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */
if (csect == 0) { /* On the cluster boundary? */
if (fp->fptr == 0) { /* On the top of the file? */
clst = fp->obj.sclust; /* Follow from the origin */
if (clst == 0) { /* If no cluster is allocated, */
clst = create_chain(&fp->obj, 0); /* create a new cluster chain */
}
} else { /* On the middle or end of the file */
#if _USE_FASTSEEK
if (fp->cltbl) {
clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */
} else
#endif
{
clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */
}
}
if (clst == 0) break; /* Could not allocate a new cluster (disk full) */
if (clst == 1) ABORT(fs, FR_INT_ERR);
if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
fp->clust = clst; /* Update current cluster */
if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */
}
#if _FS_TINY
if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */
#else
if (fp->flag & FA_DIRTY) { /* Write-back sector cache */
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
fp->flag &= (BYTE)~FA_DIRTY;
}
#endif
sect = clust2sect(fs, fp->clust); /* Get current sector */
if (!sect) ABORT(fs, FR_INT_ERR);
sect += csect;
cc = btw / SS(fs); /* When remaining bytes >= sector size, */
if (cc) { /* Write maximum contiguous sectors directly */
if (csect + cc > fs->csize) { /* Clip at cluster boundary */
cc = fs->csize - csect;
}
if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
#if _FS_MINIMIZE <= 2
#if _FS_TINY
if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs));
fs->wflag = 0;
}
#else
if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));
fp->flag &= (BYTE)~FA_DIRTY;
}
#endif
#endif
wcnt = SS(fs) * cc; /* Number of bytes transferred */
continue;
}
#if _FS_TINY
if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */
if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);
fs->winsect = sect;
}
#else
if (fp->sect != sect && /* Fill sector cache with file data */
fp->fptr < fp->obj.objsize &&
disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) {
ABORT(fs, FR_DISK_ERR);
}
#endif
fp->sect = sect;
}
wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */
if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */
#if _FS_TINY
if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */
mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */
fs->wflag = 1;
#else
mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */
fp->flag |= FA_DIRTY;
#endif
}
fp->flag |= FA_MODIFIED; /* Set file change flag */
LEAVE_FF(fs, FR_OK);
}
Believe it or not, I had the same exact problem and messed with this for hours... It has something to do with how the XC8 compiler is interpreting the for loop iterator expressions. It looks like there's a hard limit on the number of expressions it will execute. It would be nice if the compiler at least threw a warning... to fix it, add
fp->fptr += wcnt;
fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize;
*bw += wcnt;
btw -= wcnt;
to the bottom of the for loop, and change the for loop to
for ( ; btw; wbuff += wcnt){//, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) {
(edit) This issue semms to be corrected in FF13 (/edit)
Same problem here. FF12c, XC8 compiler (v1.43) really seems to have a problem with the reinitialisation arguments. If you change the order and move the conditional operator ? : term to the end it works.
// wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt)
wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize)
The previous solution shown obove does not work for me if the first write access is for a complete sector. Because of the continue instruction the program does not execute the added code at the bottom of the loop!
-vloki-
Related
I am trying to transmit through CAN using dsPIC33EV256GM106 and MCC to a raspberry Pi.
I am new to CAN Bus and in c. I have configured CAN Bus and DMA via MCC, and I have called the functions in main.c. but nothing arrives in the Raspberry Pi. Here is my main.c, can.c and some pictures of MCC. If anyone has any ideas, how I can change the code to make the CAN bus work, I would appreciate any help.CAN Bus configuration
DMA
System_Module
#include "mcc_generated_files/system.h"
#include "mcc_generated_files/can_types.h"
#include <stdio.h>
/*
Main application
*/
uCAN_MSG msg;
int main(void)
{
// initialize the device
SYSTEM_Initialize();
CAN1_TransmitEnable();
CAN1_ReceiveEnable();
CAN1_OperationModeSet(CAN_CONFIGURATION_MODE);
msg.frame.id = 0x123;
msg.frame.idType = CAN_FRAME_STD;
msg.frame.msgtype = CAN_MSG_DATA;
msg.frame.dlc = 0x08;
msg.frame.data0 = 0x01;
msg.frame.data1 = 0x02;
msg.frame.data2 = 0x03;
msg.frame.data3 = 0x04;
msg.frame.data4 = 0x05;
msg.frame.data5 = 0x06;
msg.frame.data6 = 0x07;
msg.frame.data7 = 0x08;
while (1)
{
CAN1_Transmit(CAN_PRIORITY_HIGH, &msg);
}
return 1;
}
The pictures show the configuration of DMA and timer in MCC.
enter image description here
enter image description here
#include "can1.h"
#include "dma.h"
#define CAN1_TX_DMA_CHANNEL DMA_CHANNEL_0
#define CAN1_RX_DMA_CHANNEL DMA_CHANNEL_2
/* Valid options are 4, 6, 8, 12, 16, 24, or 32. */
#define CAN1_MESSAGE_BUFFERS 32
#define CAN1_FIFO_STARTING_BUFFER 0x1
#define CAN1_TX_BUFFER_COUNT 1
#define CAN1_RX_BUFFER_MSG_DATA_SIZE 8U // CAN RX Buffer Message object data field size
/* Private type definitions */
/******************************************************************************/
typedef struct __attribute__((packed))
{
unsigned priority :2;
unsigned remote_transmit_enable :1;
unsigned send_request :1;
unsigned error :1;
unsigned lost_arbitration :1;
unsigned message_aborted :1;
unsigned transmit_enabled :1;
} CAN1_TX_CONTROLS;
/**
Section: Private Variable Definitions
*/
/* This alignment is required because of the DMA's peripheral indirect
* addressing mode. */
static unsigned int can1msgBuf [CAN1_MESSAGE_BUFFERS][8] __attribute__((aligned(32 * 8 * 2)));
static void CAN1_DMACopy(uint8_t buffer_number, CAN_MSG_OBJ *message);
static void CAN1_MessageToBuffer(uint16_t* buffer, CAN_MSG_OBJ *message);
// CAN1 Default Interrupt Handler
static void (*CAN1_BusErrorHandler)(void) = NULL;
static void (*CAN1_TxErrorPassiveHandler)(void) = NULL;
static void (*CAN1_RxErrorPassiveHandler)(void) = NULL;
static void (*CAN1_BusWakeUpActivityInterruptHandler)(void) = NULL;
static void (*CAN1_RxBufferInterruptHandler)(void) = NULL;
static void (*CAN1_RxBufferOverFlowInterruptHandler)(void) = NULL;
/**
#Summary
Read the message object from Receive buffer and update to the user message object
pointer.
#Description
This routine read the message object from Receive buffer and update to the user
message object pointer.
#Preconditions
CAN1_Initialize function should be called before calling this function.
#Param
bufferNumber - A buffer number is in the Receive buffer where the message would
be stored.
message - pointer to the CAN1 Receive message object.
#Returns
None
#Example
None
*/
static void CAN1_DMACopy(uint8_t buffer_number, CAN_MSG_OBJ *message)
{
uint16_t ide=0;
uint16_t rtr=0;
uint32_t id=0;
/* read word 0 to see the message type */
ide=can1msgBuf[buffer_number][0] & 0x0001U;
/* check to see what type of message it is */
/* message is standard identifier */
if(ide==0U)
{
message->msgId =(can1msgBuf[buffer_number][0] & 0x1FFCU) >> 2U;
message->field.idType = CAN_FRAME_STD;
rtr=can1msgBuf[buffer_number][0] & 0x0002U;
}
/* message is extended identifier */
else
{
id=can1msgBuf[buffer_number][0] & 0x1FFCU;
message->msgId = id << 16U;
message->msgId += ( ((uint32_t) can1msgBuf[buffer_number][1] & (uint32_t)0x0FFF) << 6U );
message->msgId += ( ((uint32_t) can1msgBuf[buffer_number][2] & (uint32_t)0xFC00U) >> 10U );
message->field.idType = CAN_FRAME_EXT;
rtr=can1msgBuf[buffer_number][2] & 0x0200;
}
/* check to see what type of message it is */
/* RTR message */
if(rtr != 0U)
{
/* to be defined ?*/
message->field.frameType = CAN_FRAME_RTR;
}
/* normal message */
else
{
message->field.frameType = CAN_FRAME_DATA;
message->data[0] =(uint8_t) can1msgBuf[buffer_number][3];
message->data[1] =(uint8_t) ((can1msgBuf[buffer_number][3] & 0xFF00U) >> 8U);
message->data[2] =(uint8_t) can1msgBuf[buffer_number][4];
message->data[3] =(uint8_t) ((can1msgBuf[buffer_number][4] & 0xFF00U) >> 8U);
message->data[4] =(uint8_t) can1msgBuf[buffer_number][5];
message->data[5] =(uint8_t) ((can1msgBuf[buffer_number][5] & 0xFF00U) >> 8U);
message->data[6] =(uint8_t) can1msgBuf[buffer_number][6];
message->data[7] =(uint8_t) ((can1msgBuf[buffer_number][6] & 0xFF00U) >> 8U);
message->field.dlc =(uint8_t) (can1msgBuf[buffer_number][2] & 0x000FU);
}
}
/**
#Summary
Read the message object from user input and update to the CAN1 TX buffer.
#Description
This routine Read the message object from user input and update to the CAN1
TX buffer.
#Preconditions
CAN1_Initialize function should be called before calling this function.
#Param
buffer - pointer to the CAN1 Message object.
message - pointer to the CAN1 transmit message object.
#Returns
None
#Example
None
*/
static void CAN1_MessageToBuffer(uint16_t* buffer, CAN_MSG_OBJ* message)
{
if(message->field.idType == CAN_FRAME_STD)
{
buffer[0]= (message->msgId & 0x000007FF) << 2;
buffer[1]= 0;
buffer[2]= message->field.dlc & 0x0F;
}
else
{
buffer[0]= ( ( (uint16_t)(message->msgId >> 16 ) & 0x1FFC ) ) | 0x3;
buffer[1]= (uint16_t)(message->msgId >> 6) & 0x0FFF;
buffer[2]= (message->field.dlc & 0x0F) + ( (uint16_t)(message->msgId << 10) & 0xFC00);
}
if(message->data != NULL)
{
buffer[3]= ((message->data[1])<<8) + message->data[0];
buffer[4]= ((message->data[3])<<8) + message->data[2];
buffer[5]= ((message->data[5])<<8) + message->data[4];
buffer[6]= ((message->data[7])<<8) + message->data[6];
}
}
/**
Section: CAN1 APIs
*/
void CAN1_Initialize(void)
{
// Disable interrupts before the Initialization
IEC2bits.C1IE = 0;
C1INTE = 0;
// set the CAN1_initialize module to the options selected in the User Interface
/* put the module in configuration mode */
C1CTRL1bits.REQOP = CAN_CONFIGURATION_MODE;
while(C1CTRL1bits.OPMODE != CAN_CONFIGURATION_MODE);
/* Set up the baud rate*/
C1CFG1 = 0x03; //BRP TQ = (2 x 4)/FCAN; SJW 1 x TQ;
C1CFG2 = 0x43BE; //WAKFIL enabled; SEG2PHTS Freely programmable; SEG2PH 4 x TQ; SEG1PH 8 x TQ; PRSEG 7 x TQ; SAM Once at the sample point;
C1FCTRL = 0xC001; //FSA Transmit/Receive Buffer TRB1; DMABS 32;
C1FEN1 = 0x01; //FLTEN8 disabled; FLTEN7 disabled; FLTEN9 disabled; FLTEN0 enabled; FLTEN2 disabled; FLTEN10 disabled; FLTEN1 disabled; FLTEN11 disabled; FLTEN4 disabled; FLTEN3 disabled; FLTEN6 disabled; FLTEN5 disabled; FLTEN12 disabled; FLTEN13 disabled; FLTEN14 disabled; FLTEN15 disabled;
C1CTRL1 = 0x00; //CANCKS FOSC/2; CSIDL disabled; ABAT disabled; REQOP Sets Normal Operation Mode; WIN Uses buffer window; CANCAP disabled;
/* Filter configuration */
/* enable window to access the filter configuration registers */
/* use filter window*/
C1CTRL1bits.WIN=1;
/* select acceptance masks for filters */
C1FMSKSEL1bits.F0MSK = 0x0; //Select Mask 0 for Filter 0
/* Configure the masks */
C1RXM0SIDbits.SID = 0x7ff;
C1RXM1SIDbits.SID = 0x0;
C1RXM2SIDbits.SID = 0x0;
C1RXM0SIDbits.EID = 0x0;
C1RXM1SIDbits.EID = 0x0;
C1RXM2SIDbits.EID = 0x0;
C1RXM0EID = 0x00;
C1RXM1EID = 0x00;
C1RXM2EID = 0x00;
C1RXM0SIDbits.MIDE = 0x0;
C1RXM1SIDbits.MIDE = 0x0;
C1RXM2SIDbits.MIDE = 0x0;
/* Configure the filters */
C1RXF0SIDbits.SID = 0x123;
C1RXF0SIDbits.EID = 0x0;
C1RXF0EID = 0x00;
C1RXF0SIDbits.EXIDE = 0x0;
/* FIFO Mode */
C1BUFPNT1bits.F0BP = 0xf; //Filter 0 uses FIFO
/* clear window bit to access CAN1 control registers */
C1CTRL1bits.WIN=0;
/*configure CAN1 Transmit/Receive buffer settings*/
C1TR01CONbits.TXEN0 = 0x1; // Buffer 0 is a Transmit Buffer
C1TR01CONbits.TXEN1 = 0x0; // Buffer 1 is a Receive Buffer
C1TR23CONbits.TXEN2 = 0x0; // Buffer 2 is a Receive Buffer
C1TR23CONbits.TXEN3 = 0x0; // Buffer 3 is a Receive Buffer
C1TR45CONbits.TXEN4 = 0x0; // Buffer 4 is a Receive Buffer
C1TR45CONbits.TXEN5 = 0x0; // Buffer 5 is a Receive Buffer
C1TR67CONbits.TXEN6 = 0x0; // Buffer 6 is a Receive Buffer
C1TR67CONbits.TXEN7 = 0x0; // Buffer 7 is a Receive Buffer
C1TR01CONbits.TX0PRI = 0x0; // Message Buffer 0 Priority Level
C1TR01CONbits.TX1PRI = 0x0; // Message Buffer 1 Priority Level
C1TR23CONbits.TX2PRI = 0x0; // Message Buffer 2 Priority Level
C1TR23CONbits.TX3PRI = 0x0; // Message Buffer 3 Priority Level
C1TR45CONbits.TX4PRI = 0x0; // Message Buffer 4 Priority Level
C1TR45CONbits.TX5PRI = 0x0; // Message Buffer 5 Priority Level
C1TR67CONbits.TX6PRI = 0x0; // Message Buffer 6 Priority Level
C1TR67CONbits.TX7PRI = 0x0; // Message Buffer 7 Priority Level
/* clear the buffer and overflow flags */
C1RXFUL1 = 0x0000;
C1RXFUL2 = 0x0000;
C1RXOVF1 = 0x0000;
C1RXOVF2 = 0x0000;
/* configure the device to interrupt on the receive buffer full flag */
/* clear the buffer full flags */
C1INTFbits.RBIF = 0;
/* put the module in normal mode */
C1CTRL1bits.REQOP = CAN_NORMAL_OPERATION_MODE;
while(C1CTRL1bits.OPMODE != CAN_NORMAL_OPERATION_MODE);
/* Initialize Interrupt Handler*/
CAN1_SetBusErrorHandler(&CAN1_DefaultBusErrorHandler);
CAN1_SetTxErrorPassiveHandler(&CAN1_DefaultTxErrorPassiveHandler);
CAN1_SetRxErrorPassiveHandler(&CAN1_DefaultRxErrorPassiveHandler);
CAN1_SetBusWakeUpActivityInterruptHandler(&CAN1_DefaultBusWakeUpActivityHandler);
CAN1_SetRxBufferInterruptHandler(&CAN1_DefaultReceiveBufferHandler);
CAN1_SetRxBufferOverFlowInterruptHandler(&CAN1_DefaultRxBufferOverFlowHandler);
/* Enable CAN1 Interrupt */
IEC2bits.C1IE = 1;
/* Enable Receive interrupt */
C1INTEbits.RBIE = 1;
/* Enable Error interrupt*/
C1INTEbits.ERRIE = 1;
/* Enable Receive buffer Overflow interrupt */
C1INTEbits.RBOVIE = 1;
}
void CAN1_TransmitEnable()
{
/* setup channel 0 for peripheral indirect addressing mode
normal operation, word operation and select as Tx to peripheral */
/* DMA_PeripheralIrqNumberSet and DMA_TransferCountSet would be done in the
DMA */
/* setup the address of the peripheral CAN1 (C1TXD) */
DMA_PeripheralAddressSet(CAN1_TX_DMA_CHANNEL, (uint16_t) &C1TXD);
/* DPSRAM start address offset value */
DMA_StartAddressASet(CAN1_TX_DMA_CHANNEL, (uint16_t) (&can1msgBuf));
/* enable the channel */
DMA_ChannelEnable(CAN1_TX_DMA_CHANNEL);
}
void CAN1_ReceiveEnable()
{
/* setup DMA channel for peripheral indirect addressing mode
normal operation, word operation and select as Rx to peripheral */
/* setup the address of the peripheral CAN1 (C1RXD) */
/* DMA_TransferCountSet and DMA_PeripheralIrqNumberSet would be set in
the DMA_Initialize function */
DMA_PeripheralAddressSet(CAN1_RX_DMA_CHANNEL, (uint16_t) &C1RXD);
/* DPSRAM start address offset value */
DMA_StartAddressASet(CAN1_RX_DMA_CHANNEL, (uint16_t) (&can1msgBuf) );
/* enable the channel */
DMA_ChannelEnable(CAN1_RX_DMA_CHANNEL);
}
CAN_OP_MODE_STATUS CAN1_OperationModeSet(const CAN_OP_MODES requestMode)
{
CAN_OP_MODE_STATUS status = CAN_OP_MODE_REQUEST_SUCCESS;
if((CAN_CONFIGURATION_MODE == CAN1_OperationModeGet()) || (requestMode == CAN_DISABLE_MODE)
|| (requestMode == CAN_CONFIGURATION_MODE))
{
C1CTRL1bits.REQOP = requestMode;
while(C1CTRL1bits.OPMODE != requestMode);
}
else
{
status = CAN_OP_MODE_REQUEST_FAIL;
}
return status;
}
CAN_OP_MODES CAN1_OperationModeGet(void)
{
return C1CTRL1bits.OPMODE;
}
CAN_TX_MSG_REQUEST_STATUS CAN1_Transmit(CAN_TX_PRIOIRTY priority, CAN_MSG_OBJ *sendCanMsg)
{
CAN_TX_MSG_REQUEST_STATUS txMsgStatus = CAN_TX_MSG_REQUEST_SUCCESS;
CAN1_TX_CONTROLS * pTxControls = (CAN1_TX_CONTROLS*)&C1TR01CON;
uint_fast8_t i;
bool messageSent = false;
// CAN 2.0 mode DLC supports upto 8 byte
if(sendCanMsg->field.dlc > CAN_DLC_8)
{
txMsgStatus |= CAN_TX_MSG_REQUEST_DLC_ERROR;
}
if(CAN1_TX_BUFFER_COUNT > 0)
{
for(i=0; i<CAN1_TX_BUFFER_COUNT; i++)
{
if(pTxControls->transmit_enabled == 1)
{
if (pTxControls->send_request == 0)
{
CAN1_MessageToBuffer(&can1msgBuf[i][0], sendCanMsg);
pTxControls->priority = priority;
/* set the message for transmission */
pTxControls->send_request = 1;
messageSent = true;
break;
}
}
pTxControls++;
}
}
if(messageSent == false)
{
txMsgStatus |= CAN_TX_MSG_REQUEST_BUFFER_FULL;
}
return txMsgStatus;
}
bool CAN1_Receive(CAN_MSG_OBJ *recCanMsg)
{
uint_fast8_t currentBuffer;
uint_fast8_t shiftAmount;
bool messageReceived = false;
uint16_t receptionFlags;
if(C1INTFbits.RBOVIF == 1)
{
C1INTFbits.RBOVIF = 0;
/* Receive buffer overflow occured, call the notification function */
if(CAN1_RxBufferOverFlowInterruptHandler)
{
CAN1_RxBufferOverFlowInterruptHandler();
}
return messageReceived;
}
if(recCanMsg->data == NULL)
{
return messageReceived;
}
currentBuffer = C1FIFObits.FNRB;
if( currentBuffer < 16)
{
receptionFlags = C1RXFUL1;
shiftAmount = currentBuffer;
}
else
{
receptionFlags = C1RXFUL2;
shiftAmount = currentBuffer - 16;
}
if (((receptionFlags >> shiftAmount ) & 0x1) == 0x1)
{
CAN1_DMACopy(currentBuffer, recCanMsg);
if( currentBuffer < 16)
{
C1RXFUL1 &= ~(1 << shiftAmount);
}
else
{
C1RXFUL2 &= ~(1 << shiftAmount);
}
messageReceived = true;
}
return (messageReceived);
}
bool CAN1_IsBusOff()
{
return C1INTFbits.TXBO;
}
bool CAN1_IsRXErrorPassive()
{
return (C1INTFbits.RXBP);
}
bool CAN1_IsRxErrorWarning(void)
{
return (C1INTFbits.RXWAR);
}
bool CAN1_IsRxErrorActive(void)
{
bool errorState = false;
if((0 < C1ECbits.RERRCNT) && (C1ECbits.RERRCNT < 128))
{
errorState = true;
}
return errorState;
}
bool CAN1_IsTXErrorPassive()
{
return (C1INTFbits.TXBP);
}
bool CAN1_IsTxErrorWarning(void)
{
return (C1INTFbits.TXWAR);
}
bool CAN1_IsTxErrorActive(void)
{
bool errorState = false;
if((0 < C1ECbits.TERRCNT) && (C1ECbits.TERRCNT < 128))
{
errorState = true;
}
return errorState;
}
uint8_t CAN1_ReceivedMessageCountGet()
{
uint_fast8_t messageCount;
uint_fast8_t currentBuffer;
uint16_t receptionFlags;
messageCount = 0;
#if (CAN1_FIFO_STARTING_BUFFER<16)
/* Check any message in buffer 0 to buffer 15*/
receptionFlags = C1RXFUL1;
if (receptionFlags != 0)
{
/* check whether a message is received */
for (currentBuffer=0 ; currentBuffer < 16; currentBuffer++)
{
if (((receptionFlags >> currentBuffer ) & 0x1) == 0x1)
{
messageCount++;
}
}
}
#endif
/* Check any message in buffer 16 to buffer 32*/
receptionFlags = C1RXFUL2;
if (receptionFlags != 0)
{
/* check whether a message is received */
for (currentBuffer=0 ; currentBuffer < 16; currentBuffer++)
{
if (((receptionFlags >> currentBuffer ) & 0x1) == 0x1)
{
messageCount++;
}
}
}
return (messageCount);
}
void CAN1_Sleep(void)
{
C1INTFbits.WAKIF = 0;
C1INTEbits.WAKIE = 1;
/* put the module in disable mode */
C1CTRL1bits.REQOP = CAN_DISABLE_MODE;
while(C1CTRL1bits.OPMODE != CAN_DISABLE_MODE);
//Wake up from sleep should set the CAN1 module straight into Normal mode
}
void __attribute__((weak)) CAN1_DefaultBusErrorHandler(void)
{
CAN1_CallbackBusOff();
}
void CAN1_SetBusErrorHandler(void *handler)
{
CAN1_BusErrorHandler = handler;
}
void __attribute__((weak)) CAN1_DefaultTxErrorPassiveHandler(void)
{
CAN1_CallbackTxErrorPassive();
}
void CAN1_SetTxErrorPassiveHandler(void *handler)
{
CAN1_TxErrorPassiveHandler = handler;
}
void __attribute__((weak)) CAN1_DefaultRxErrorPassiveHandler(void)
{
CAN1_CallbackRxErrorPassive();
}
void CAN1_SetRxErrorPassiveHandler(void *handler)
{
CAN1_RxErrorPassiveHandler = handler;
}
void __attribute__((weak)) CAN1_DefaultBusWakeUpActivityHandler(void)
{
}
void CAN1_SetBusWakeUpActivityInterruptHandler(void *handler)
{
CAN1_BusWakeUpActivityInterruptHandler = handler;
}
void __attribute__((weak)) CAN1_DefaultReceiveBufferHandler(void)
{
CAN1_CallbackMessageReceived();
}
void CAN1_SetRxBufferInterruptHandler(void *handler)
{
CAN1_RxBufferInterruptHandler = handler;
}
void __attribute__((weak)) CAN1_DefaultRxBufferOverFlowHandler(void)
{
CAN1_CallbackRxBufferOverflow();
}
void CAN1_SetRxBufferOverFlowInterruptHandler(void *handler)
{
CAN1_RxBufferOverFlowInterruptHandler = handler;
}
void __attribute__((__interrupt__, no_auto_psv)) _C1Interrupt(void)
{
if (C1INTFbits.ERRIF)
{
if (C1INTFbits.TXBO == 1)
{
if(CAN1_BusErrorHandler)
{
CAN1_BusErrorHandler();
}
}
if (C1INTFbits.TXBP == 1)
{
if(CAN1_TxErrorPassiveHandler)
{
CAN1_TxErrorPassiveHandler();
}
}
if (C1INTFbits.RXBP == 1)
{
if(CAN1_RxErrorPassiveHandler)
{
CAN1_RxErrorPassiveHandler();
}
}
/* Call error notification function */
C1INTFbits.ERRIF = 0;
}
if(C1INTFbits.RBIF)
{
if(CAN1_RxBufferInterruptHandler)
{
CAN1_RxBufferInterruptHandler();
}
C1INTFbits.RBIF = 0;
}
if(C1INTFbits.WAKIF)
{
if(CAN1_BusWakeUpActivityInterruptHandler)
{
CAN1_BusWakeUpActivityInterruptHandler();
}
C1INTFbits.WAKIF = 0;
}
IFS2bits.C1IF = 0;
}
/*******************************************************************************
!!! Deprecated Definitions and APIs !!!
!!! These functions will not be supported in future releases !!!
*******************************************************************************/
/******************************************************************************
*
* Function: CAN1_transmit
* Description: Transmits the message from user buffer to CAN1 buffer
* as per the buffer number allocated.
* Allocation of the buffer number is done by user
*
* Arguments: priority : priority of the message to be transmitted
* sendCanMsg: pointer to the message object
*
* Return Value: true - Transmit successful
* false - Transmit failure
******************************************************************************/
bool CAN1_transmit(CAN_TX_PRIOIRTY priority, uCAN_MSG *sendCanMsg)
{
uint8_t msgObjData[8] = {0};
CAN_MSG_OBJ txCanMsg;
txCanMsg.data = msgObjData;
txCanMsg.msgId = sendCanMsg->frame.id;
txCanMsg.field.idType = sendCanMsg->frame.idType;
txCanMsg.field.frameType = sendCanMsg->frame.msgtype;
txCanMsg.field.dlc = sendCanMsg->frame.dlc;
txCanMsg.data[0] = sendCanMsg->frame.data0;
txCanMsg.data[1] = sendCanMsg->frame.data1;
txCanMsg.data[2] = sendCanMsg->frame.data2;
txCanMsg.data[3] = sendCanMsg->frame.data3;
txCanMsg.data[4] = sendCanMsg->frame.data4;
txCanMsg.data[5] = sendCanMsg->frame.data5;
txCanMsg.data[6] = sendCanMsg->frame.data6;
txCanMsg.data[7] = sendCanMsg->frame.data7;
return (CAN1_Transmit(priority, &txCanMsg));
}
/******************************************************************************
*
* Function: CAN1_receive
* Description: Receives the message from CAN1 buffer to user buffer
*
* Arguments: recCanMsg: pointer to the message object
*
* Return Value: true - Receive successful
* false - Receive failure
******************************************************************************/
bool CAN1_receive(uCAN_MSG *recCanMsg)
{
bool messageReceived = false;
uint8_t msgObjData[8] = {0};
CAN_MSG_OBJ rxCanMsg;
rxCanMsg.data = msgObjData;
if(true == CAN1_Receive(&rxCanMsg))
{
recCanMsg->frame.id = rxCanMsg.msgId;
recCanMsg->frame.idType = rxCanMsg.field.idType;
if(rxCanMsg.field.frameType == CAN_FRAME_RTR)
{
recCanMsg->frame.msgtype = CAN_MSG_RTR;
}
else
{
recCanMsg->frame.msgtype = CAN_MSG_DATA;
}
recCanMsg->frame.data0 = rxCanMsg.data[0];
recCanMsg->frame.data1 = rxCanMsg.data[1];
recCanMsg->frame.data2 = rxCanMsg.data[2];
recCanMsg->frame.data3 = rxCanMsg.data[3];
recCanMsg->frame.data4 = rxCanMsg.data[4];
recCanMsg->frame.data5 = rxCanMsg.data[5];
recCanMsg->frame.data6 = rxCanMsg.data[6];
recCanMsg->frame.data7 = rxCanMsg.data[7];
recCanMsg->frame.dlc = rxCanMsg.field.dlc;
messageReceived = true;
}
return (messageReceived);
}
/******************************************************************************
*
* Function: CAN1_isBusOff
* Description: Checks whether the transmitter in Bus off state
*
* Return Value: true - Transmitter in Bus Off state
* false - Transmitter not in Bus Off state
******************************************************************************/
bool CAN1_isBusOff()
{
return C1INTFbits.TXBO;
}
/******************************************************************************
*
* Function: CAN1_isRXErrorPassive
* Description: Checks whether the receive in error passive state
*
* Return Value: true - Receiver in Error Passive state
* false - Receiver not in Error Passive state
******************************************************************************/
bool CAN1_isRXErrorPassive()
{
return C1INTFbits.RXBP;
}
/******************************************************************************
*
* Function: CAN1_isTXErrorPassive
* Description: Checks whether the transmitter in error passive state
*
* Return Value: true - Transmitter in Error Passive state
* false - Transmitter not in Error Passive state
******************************************************************************/
bool CAN1_isTXErrorPassive()
{
return (C1INTFbits.TXBP);
}
/******************************************************************************
*
* Function: CAN1_messagesInBuffer
* Description: returns the number of messages that are received
*
* Return Value: Number of message received
******************************************************************************/
uint8_t CAN1_messagesInBuffer()
{
uint_fast8_t messageCount;
uint_fast8_t currentBuffer;
uint16_t receptionFlags;
messageCount = 0;
#if (CAN1_FIFO_STARTING_BUFFER<16)
/* Check any message in buffer 0 to buffer 15*/
receptionFlags = C1RXFUL1;
if (receptionFlags != 0)
{
/* check whether a message is received */
for (currentBuffer=0 ; currentBuffer < 16; currentBuffer++)
{
if (((receptionFlags >> currentBuffer ) & 0x1) == 0x1)
{
messageCount++;
}
}
}
#endif
/* Check any message in buffer 16 to buffer 32*/
receptionFlags = C1RXFUL2;
if (receptionFlags != 0)
{
/* check whether a message is received */
for (currentBuffer=0 ; currentBuffer < 16; currentBuffer++)
{
if (((receptionFlags >> currentBuffer ) & 0x1) == 0x1)
{
messageCount++;
}
}
}
return (messageCount);
}
/******************************************************************************
*
* Function: CAN1_sleep
* Description: Puts CAN1 module in disable mode.
*
******************************************************************************/
void CAN1_sleep(void)
{
C1INTFbits.WAKIF = 0;
C1INTEbits.WAKIE = 1;
/* put the module in disable mode */
C1CTRL1bits.REQOP = CAN_DISABLE_MODE;
while(C1CTRL1bits.OPMODE != CAN_DISABLE_MODE);
//Wake up from sleep should set the CAN1 module straight into Normal mode
}
/* Null weak implementations of callback functions. */
void __attribute__((weak)) CAN1_CallbackBusOff(void)
{
}
void __attribute__((weak)) CAN1_CallbackTxErrorPassive(void)
{
}
void __attribute__((weak)) CAN1_CallbackRxErrorPassive(void)
{
}
void __attribute__((weak)) CAN1_CallbackMessageReceived(void)
{
}
void __attribute__((weak)) CAN1_CallbackRxBufferOverflow()
{
}
/**
End of File
*/
I am using STM32F4-development board with a STM32F407 chip. To communicate with the SD card I use SPI1, and are using the FatFs library created by Chan.
So the gist of the problem is that I have successfully managed to create a file on the SD card, I am able to read from it. But when I try to write to the file it either corrupts the file or prints garbage data like this "{46040EDD-C". If I look at the memmory I can see the stuff I wrote, but somehow it is being written to the wrong memmory address.
Another problem that I have is that the first time I create a file and try to write to it with f_write I get the reply FR_DISK_ERR. The error comes form trying to create a cluster chain, when I step trough the code it works fine. So It might be some delay missing. The next time I run the programm it works, and f_write returns FR_OK.
I am not sure if these problems are related or not. I have been trying to get this to work for about two weeks now and would love any help I can get. Thank you.
The code:
main.c
int main(void)
{
int i;
//SD CARD INIT
//Fatfs object
FATFS FatFs;
//File object
FIL fil;
UINT fa;
FRESULT res_mount, res_open, res_seek, res_write;
delay(10ms);
res_mount = f_mount(&FatFs, "", 1);
if (res_mount == FR_OK) {
GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
delay(10ms);
res_open = f_open(&fil, "test.txt", FA_OPEN_ALWAYS | FA_READ | FA_WRITE);
if (res_open == FR_OK) {
GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
delay(10ms);
res_seek = f_lseek(&fil, f_size(&fil));
if(res_seek == FR_OK)
{
delay(10ms);
GPIO_ToggleBits(GPIOD, GPIO_Pin_14);
res_write = f_write(&fil, "Alpha Beta\n", 11, &fa);
if (fa > 0 && res_write == FR_OK) {
GPIO_ToggleBits(GPIOD, GPIO_Pin_15);
f_sync(&fil);
}
}
f_close(&fil);
}
f_mount(0, "", 1);
}
while(1);
//SD CARD INIT END
}
Chans diskio.c file that I have edited.
#include "diskio.h" /* FatFs lower layer API */
/* Definitions of physical drive number for each drive */
#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
static volatile DSTATUS Stat = STA_NOINIT; /* Disk status */
static BYTE CardType; /* Card type flags (b0:MMC, b1:SDv1, b2:SDv2, b3:Block addressing) */
/*-----------------------------------------------------------------------*/
/* Get Drive Status */
/*-----------------------------------------------------------------------*/
DSTATUS disk_status (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
if(pdrv)
return STA_NOINIT; // Supports only drive 0
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
/*-----------------------------------------------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* Physical drive nmuber to identify the drive */
)
{
if (Stat & STA_NODISK)
return Stat; /* No card in the socket? */
//SLOW
uint8_t ty = 0;
ty = SD_CARD_InitialiseCard();
CardType = ty;
if(ty)
Stat &= ~STA_NOINIT;
return Stat;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Start sector in LBA */
UINT count /* Number of sectors to read */
)
{
//DRESULT res;
//int result;
if(pdrv || !count)
return RES_PARERR;
if (Stat & STA_NOINIT)
return RES_NOTRDY;
if(!(CardType & SDCARD_BLOCK))
sector *= 512;
if(count == 1)
{
if((SD_CARD_Cmd(READ_SINGLE_BLOCK, sector) == 0x00) && SD_CARD_Read(buff, 512))
count = 0;
}
else
{
if(SD_CARD_Cmd(READ_MULTIPLE_BLOCKS, sector) == 0)
{
do
{
if(!SD_CARD_Read(buff, 512))
break;
buff += 512;
}
while(--count);
SD_CARD_Cmd(STOP_TRANSMISSION, 0);
}
}
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
/*-----------------------------------------------------------------------*/
DRESULT disk_write (
BYTE pdrv, /* Physical drive nmuber to identify the drive */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Start sector in LBA */
UINT count /* Number of sectors to write */
)
{
//DRESULT res;
//int result;
if (pdrv || !count)
return RES_PARERR;
if (Stat & STA_NOINIT)
return RES_NOTRDY;
if (Stat & STA_PROTECT)
return RES_WRPRT;
if(!(CardType & SDCARD_BLOCK))
sector *= 512;
if(count == 1)
{
if((SD_CARD_Cmd(WRITE_SINGLE_BLOCK, sector) == 0x00) && SD_CARD_Write(buff, 0xFE))
count = 0;
}
else
{
if (CardType & SDCARD_SDC)
SD_CARD_Cmd(SET_WR_BLOCK_ERASE_COUNT, count);
if(SD_CARD_Cmd(WRITE_MULTIPLE_BLOCKS, sector) == 0)
{
do
{
if(!SD_CARD_Write(buff, 0xFC))
break;
buff += 512;
}
while(--count);
if (!SD_CARD_Write(0, 0xFD)) /* STOP_TRAN token */
count = 1;
}
}
return count ? RES_ERROR : RES_OK;
}
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
/*-----------------------------------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* Physical drive nmuber (0..) */
BYTE cmd, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
//DRESULT res;
//int result;
//NOT NEEDED AT THE MOMENT
return RES_PARERR;
}
DWORD get_fattime (void)
{
/* Pack date and time into a DWORD variable */
return ((DWORD)(2017 - 1980) << 25)
| ((DWORD)1 << 21)
| ((DWORD)1 << 16)
| ((DWORD)0 << 11)
| ((DWORD)0 << 5)
| ((DWORD)0 >> 1);
}
SD_CARD.c:
#include "SD_CARD.h"
/*
SPI1(GPIOA): - Type: - SD CARD:
Pin4 - CS - Pin2
Pin5 - SCLK - Pin5
Pin6 - MISO - Pin7
Pin7 - MOSI - Pin3
*/
uint8_t SD_CARD_InitialiseCard()
{
INT i = 0;
SPI1ENABLE();
ChipSelect(SPI1, HIGH);
for(i = 0;i < 16;i++)
SPI_Send(SPI1, 0xFF);
for(i = 0;i < 0xFFFF;i++);
while(SD_CARD_Cmd(GO_IDLE_STATE, 0) != R1_IDLE_STATE); //CMD0
uint32_t r = SD_CARD_Cmd(SEND_IF_COND, 0x1AA); //CMD8
if(r == 0x1AA)
return SD_CARD_InitialiseCardV2();
else if(r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND))
return SD_CARD_InitialiseCardV1();
else
return SDCARD_FAIL;
}
uint8_t SD_CARD_WriteRead(INT arg)
{
SPI_Send(SPI1, arg);
uint8_t test = SPI_Read(SPI1);
return test;
}
uint8_t SD_CARD_InitialiseCardV1()
{
uint8_t cmd;
INT i = 0;
if(SD_CARD_Cmd(SD_SEND_OP_COND, 0x40040000) <= 1) //ACMD41 - set to 3V, use 0x40200000 for 3V3
cmd = SD_SEND_OP_COND;
else
cmd = SEND_OP_COND;
for(i = 0; i < SD_COMMAND_TIMEOUT;i++)
{
if(SD_CARD_Cmd(cmd, 0) == 0) //CMD1 or ACMD41
{
// Set block length to 512 (CMD16)
if(SD_CARD_Cmd(SET_BLOCKLEN, 512) != 0) //CMD16
return SDCARD_FAIL;
//Init: SEDCARD_V1
if(cmd == SD_SEND_OP_COND)
return SDCARD_V1;
else
return SDCARD_MMCV3;
}
}
//Timeout waiting for v1.x card
return SDCARD_FAIL;
}
uint8_t SD_CARD_InitialiseCardV2()
{
INT i = 0;
INT j = 0;
for(i = 0;i < SD_COMMAND_TIMEOUT;i++)
{
for(j = 0;j < 0xFF;j++);
if(SD_CARD_Cmd(SD_SEND_OP_COND, 0x40040000) == 0) //ACMD41 - set to 3V, use 0x40200000 for 3V3
{
uint32_t ocr = SD_CARD_Cmd(READ_OCR, 0); //CMD58
return (ocr & 0x40000000) ? SDCARD_V2 | SDCARD_BLOCK : SDCARD_V2;
}
}
//Timed out waiting for v2.x card
return SDCARD_FAIL;
}
uint32_t SD_CARD_Cmd(INT cmd, INT arg)
{
struct command_fields com;
com.start_bit = 0;
com.transmitter_bit = 1;
com.index = cmd;
com.argument = arg;
if(cmd == GO_IDLE_STATE)
com.crc = 0x4A;
else if(cmd == SEND_IF_COND)
com.crc = 0x43;
else
com.crc = 0x7F;
com.end_bit = 1;
if(cmd == SD_STATUS | cmd == SET_WR_BLOCK_ERASE_COUNT | cmd == SD_SEND_OP_COND) //ACMDx
SD_CARD_Cmd(APP_CMD, 0); //CMD55
SD_CARD_WriteCom(&com);
if(cmd == SEND_IF_COND)
return SD_CARD_RecieveR7();
else if(cmd == READ_OCR)
return SD_CARD_RecieveR3();
else
return SD_CARD_RecieveR1();
}
void SD_CARD_WriteCom(struct command_fields *com)
{
ChipSelect(SPI1, LOW);
SPI_Send(SPI1, 0xFF);
SPI_Send(SPI1, (0xFF & ((com->start_bit << 7) | (com->transmitter_bit << 6) | com->index)));
SPI_Send(SPI1, (0xFF & (com->argument >> 24)));
SPI_Send(SPI1, (0xFF & (com->argument >> 16)));
SPI_Send(SPI1, (0xFF & (com->argument >> 8)));
SPI_Send(SPI1, (0xFF & com->argument));
SPI_Send(SPI1, (0xFF & ((com->crc << 1) | com->end_bit)));
}
INT SD_CARD_Write(const BYTE *buffer, BYTE token)
{
INT i = 0;
ChipSelect(SPI1, LOW);
while(SD_CARD_WriteRead(0xFF) != 0xFF);
// indicate start of block
SPI_Send(SPI1, token);
if(token != 0xFD)
{
// write the data
for(i = 0;i < 512;i++)
{
SPI_Send(SPI1, *buffer);
buffer++;
}
// write the checksum
SPI_Send(SPI1, 0xFF);
SPI_Send(SPI1, 0xFF);
// check the repsonse token
if(((SD_CARD_WriteRead(0xFF)) & 0x1F) != 0x05)
{
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return SUCCESS;
}
}
// wait for write to finish
while(SD_CARD_WriteRead(0xFF) != 0xFF);
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return ERROR;
}
INT SD_CARD_Read(BYTE *buffer, INT length)
{
INT i = 0;
ChipSelect(SPI1, LOW);
for(i = 0; i < SD_COMMAND_TIMEOUT;i++)
{
// read until start byte (0xFF)
if(SD_CARD_WriteRead(0xFF) == 0xFE)
{
// read data
for(i = 0;i < length;i++)
buffer[i] = SD_CARD_WriteRead(0xFF);
SPI_Send(SPI1, 0xFF); // checksum
SPI_Send(SPI1, 0xFF);
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return SUCCESS;
}
}
return ERROR;
}
uint8_t SD_CARD_RecieveR1()
{
INT i;
uint8_t response = 0xFF;
for(i = 0;i < SD_COMMAND_TIMEOUT;i++)
{
response = SD_CARD_WriteRead(0xFF);
if((response == 0x00) || (response == 0x01))
{
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return response;
}
}
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return 0xFF;
}
uint32_t SD_CARD_RecieveR7()
{
INT i = 0, j = 0;
for(i = 0;i < (SD_COMMAND_TIMEOUT * 1000);i++)
{
uint8_t response[5];
response[0] = SD_CARD_WriteRead(0xFF);
if(!(response[0] & 0x80))
{
for(j = 1;j < 5;j++)
{
response[j] = SD_CARD_WriteRead(0xFF);
}
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return ((response[1] << 24) | (response[2] << 16) | (response[3] << 8) | response[4]);
}
}
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return 0xFFFFFFFF; // timeout
}
uint32_t SD_CARD_RecieveR3()
{
uint32_t ocr = 0;
INT response;
for(int i=0; i < SD_COMMAND_TIMEOUT; i++)
{
response = SD_CARD_WriteRead(0xFF);
if(!(response & 0x80))
{
ocr = SD_CARD_WriteRead(0xFF) << 24;
ocr |= SD_CARD_WriteRead(0xFF) << 16;
ocr |= SD_CARD_WriteRead(0xFF) << 8;
ocr |= SD_CARD_WriteRead(0xFF);
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return ocr;
}
}
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return 0xFFFFFFFF; // timeout
}
INT SD_CARD_InitialiseDisk()
{
if(SD_CARD_InitialiseCard() == SDCARD_FAIL)
return SDCARD_FAIL;
SPI_SetSpeed(SPI1, SPI_SPEED_1300KHz);
return SUCCESS;
}
SPI.c:
#include "SPI.h"
void SPI1ENABLE()
{
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
static uint8_t SPI1_ENABLED = 0;
if(SPI1_ENABLED)
return;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);
GPIO_InitStruct.GPIO_Pin = SPI1_SCLK | SPI1_MISO | SPI1_MOSI;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = SPI1_CS;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOA, &GPIO_InitStruct);
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &SPI_InitStruct);
SPI_Cmd(SPI1, ENABLE);
SPI1_ENABLED = 1;
}
uint8_t SPI_Read(SPI_TypeDef* SPIx)
{
while(SPI1->SR & SPI_I2S_FLAG_BSY); // wait until SPI is not busy anymore
while(!(SPI1->SR & SPI_I2S_FLAG_RXNE)); // wait until receive complete
return SPI_ReceiveData(SPIx);
}
void SPI_Send(SPI_TypeDef* SPIx, unsigned char Data)
{
while(SPI1->SR & SPI_I2S_FLAG_BSY); // wait until SPI is not busy anymore
while(!(SPI1->SR & SPI_I2S_FLAG_TXE)); // wait until transmit complete
SPI_SendData(SPIx, Data);
while(!(SPI1->SR & SPI_I2S_FLAG_RXNE)); // wait until receive complete
}
So I found out my problem. As stupid as I am, in my write function I forgot to return success when sending STOP_TRANs token. Here is how the write function looks like after editing.
INT SD_CARD_Write(const BYTE *buffer, BYTE token)
{
INT i = 0;
ChipSelect(SPI1, LOW);
while(SD_CARD_WriteRead(0xFF) != 0xFF);
// indicate start of block
SPI_Send(SPI1, token);
if(token != 0xFD)
{
// write the data
for(i = 0;i < 512;i++)
{
SPI_Send(SPI1, *buffer);
buffer++;
}
// write the checksum
SPI_Send(SPI1, 0xFF);
SPI_Send(SPI1, 0xFF);
// check the repsonse token
uint8_t resp = 0x00;
do
{
resp = SD_CARD_WriteRead(0xFF);
}
while(resp == 0x00);
if((resp & 0x1F) != 0x05)
{
// wait for write to finish
while(SD_CARD_WriteRead(0xFF) != 0xFF);
SPI_Send(SPI1, 0xFF);
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
return SUCCESS;
}
}
// wait for write to finish
while(SD_CARD_WriteRead(0xFF) != 0xFF);
SPI_Send(SPI1, 0xFF);
ChipSelect(SPI1, HIGH);
SPI_Send(SPI1, 0xFF);
if(token == 0xFD)
return SUCCESS;
return ERROR;
}
I am trying to create a file with FatFs on USB flash, but my f_open call trying to read boot sector for first time file system mount hangs on this function.
DRESULT disk_read (
BYTE drv, /* Physical drive number (0) */
BYTE *buff, /* Pointer to the data buffer to store read data */
DWORD sector, /* Start sector number (LBA) */
BYTE count /* Sector count (1..255) */
)
{
BYTE status = USBH_MSC_OK;
if (drv || !count) return RES_PARERR;
if (Stat & STA_NOINIT) return RES_NOTRDY;
if(HCD_IsDeviceConnected(&USB_OTG_Core))
{
do
{
status = USBH_MSC_Read10(&USB_OTG_Core, buff,sector,512 * count);
USBH_MSC_HandleBOTXfer(&USB_OTG_Core ,&USB_Host);
if(!HCD_IsDeviceConnected(&USB_OTG_Core))
{
return RES_ERROR;
}
}
while(status == USBH_MSC_BUSY ); // Loop which create hanging state
}
if(status == USBH_MSC_OK)
return RES_OK;
return RES_ERROR;
}
The main problem is the loop which creates hanging state
while(status == USBH_MSC_BUSY );
So I do not know what to do to avoid this. Using debugger I discover that state is caused by parameter CmdStateMachine of structure USBH_MSC_BOTXferParam, type USBH_BOTXfer_TypeDef is equal CMD_UNINITIALIZED_STATE which actually cause miss up of switch statement of USBH_MSC_Read10 function.
/**
* #brief USBH_MSC_Read10
* Issue the read command to the device. Once the response received,
* it updates the status to upper layer
* #param dataBuffer : DataBuffer will contain the data to be read
* #param address : Address from which the data will be read
* #param nbOfbytes : NbOfbytes to be read
* #retval Status
*/
uint8_t USBH_MSC_Read10(USB_OTG_CORE_HANDLE *pdev,
uint8_t *dataBuffer,
uint32_t address,
uint32_t nbOfbytes)
{
uint8_t index;
static USBH_MSC_Status_TypeDef status = USBH_MSC_BUSY;
uint16_t nbOfPages;
status = USBH_MSC_BUSY;
if(HCD_IsDeviceConnected(pdev))
{
switch(USBH_MSC_BOTXferParam.CmdStateMachine)
{
case CMD_SEND_STATE:
/*Prepare the CBW and relevant field*/
USBH_MSC_CBWData.field.CBWTransferLength = nbOfbytes;
USBH_MSC_CBWData.field.CBWFlags = USB_EP_DIR_IN;
USBH_MSC_CBWData.field.CBWLength = CBW_LENGTH;
USBH_MSC_BOTXferParam.pRxTxBuff = dataBuffer;
for(index = CBW_CB_LENGTH; index != 0; index--)
{
USBH_MSC_CBWData.field.CBWCB[index] = 0x00;
}
USBH_MSC_CBWData.field.CBWCB[0] = OPCODE_READ10;
/*logical block address*/
USBH_MSC_CBWData.field.CBWCB[2] = (((uint8_t*)&address)[3]);
USBH_MSC_CBWData.field.CBWCB[3] = (((uint8_t*)&address)[2]);
USBH_MSC_CBWData.field.CBWCB[4] = (((uint8_t*)&address)[1]);
USBH_MSC_CBWData.field.CBWCB[5] = (((uint8_t*)&address)[0]);
/*USBH_MSC_PAGE_LENGTH = 512*/
nbOfPages = nbOfbytes/ USBH_MSC_PAGE_LENGTH;
/*Tranfer length */
USBH_MSC_CBWData.field.CBWCB[7] = (((uint8_t *)&nbOfPages)[1]) ;
USBH_MSC_CBWData.field.CBWCB[8] = (((uint8_t *)&nbOfPages)[0]) ;
USBH_MSC_BOTXferParam.BOTState = USBH_MSC_SEND_CBW;
/* Start the transfer, then let the state machine
manage the other transactions */
USBH_MSC_BOTXferParam.MSCState = USBH_MSC_BOT_USB_TRANSFERS;
USBH_MSC_BOTXferParam.BOTXferStatus = USBH_MSC_BUSY;
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_WAIT_STATUS;
status = USBH_MSC_BUSY;
break;
case CMD_WAIT_STATUS:
if((USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_OK) && \
(HCD_IsDeviceConnected(pdev)))
{
/* Commands successfully sent and Response Received */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
status = USBH_MSC_OK;
}
else if (( USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_FAIL ) && \
(HCD_IsDeviceConnected(pdev)))
{
/* Failure Mode */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
}
else if ( USBH_MSC_BOTXferParam.BOTXferStatus == USBH_MSC_PHASE_ERROR )
{
/* Failure Mode */
USBH_MSC_BOTXferParam.CmdStateMachine = CMD_SEND_STATE;
status = USBH_MSC_PHASE_ERROR;
}
else
{
/* Wait for the Commands to get Completed */
/* NO Change in state Machine */
}
break;
default:
break;
}
}
return status;
}
Here is USBH_BOTXfer_TypeDef type declaration;
typedef struct _BOTXfer
{
uint8_t MSCState;
uint8_t MSCStateBkp;
uint8_t MSCStateCurrent;
uint8_t CmdStateMachine;
uint8_t BOTState;
uint8_t BOTStateBkp;
uint8_t* pRxTxBuff;
uint16_t DataLength;
uint8_t BOTXferErrorCount;
uint8_t BOTXferStatus;
} USBH_BOTXfer_TypeDef;
During the debug I discover that all fields of it is 0x00.
Here are my FatFs calls
int main(void)
{
FATFS Fat;
FIL file;
FRESULT fr;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
/* Enable SWO output */
DBGMCU->CR = 0x00000020;
GPIOD->MODER=0x55000000;
GPIOD->OTYPER = 0x00000000;
GPIOD->OSPEEDR = 0x00000001;
while(1)
{
if (!USB_MSC_IsInitialized())
{
USB_MSC_Initialize();
}
if (USB_MSC_IsConnected())
{
GPIOD->ODR = (1 << 15);
disk_initialize(0);
fr = f_mount(0, &Fat);
if(fr == FR_OK)
{
fr = f_open(&file,"0:DP_lab8.pdf",(FA_CREATE_ALWAYS | FA_WRITE));
if (fr == FR_OK)
{
f_close(&file);
}
f_mount(0, NULL);
}
}
else
{
GPIOD->ODR = (1 << 14);
}
USB_MSC_Main();
}
}
USB_MSC_IsConnected function is:
int USB_MSC_IsConnected(void)
{
if (g_USB_MSC_HostStatus == USB_DEV_NOT_SUPPORTED)
{
USB_MSC_Uninitialize();
}
return !(g_USB_MSC_HostStatus == USB_DEV_DETACHED ||
g_USB_MSC_HostStatus == USB_HOST_NO_INIT ||
g_USB_MSC_HostStatus == USB_DEV_NOT_SUPPORTED);
}
And device states are:
typedef enum
{
USB_HOST_NO_INIT = 0, /* USB interface not initialized */
USB_DEV_DETACHED, /* no device connected */
USB_SPEED_ERROR, /* unsupported USB speed */
USB_DEV_NOT_SUPPORTED, /* unsupported device */
USB_DEV_WRITE_PROTECT, /* device is write protected */
USB_OVER_CURRENT, /* overcurrent detected */
USB_DEV_CONNECTED /* device connected and ready */
} USB_HostStatus;
The value of g_USB_MSC_HostStatus is received by standard USB HOST user callbacks.
I think it is a bug in ST host library. I've hunted it down, as my usb host was unable to pass enumeration stage. After a fix the stack is Ok.
There is union _USB_Setup in usbh_def.h file in "STM32Cube/Repository/STM32Cube_FW_F7_V1.13.0/Middlewares/ST/STM32_USB_Host_Library/Core/Inc" (any chip, not only F7, any version, not only V1.13.0). It has uint16_t bmRequestType and uint16_t bRequest. These two fileds must be uint8_t as of USB specs. Fixing this issue made usb host go as needed. Enumeration stage passes ok, and all other stages as well.
I'm developing code for the NXP LPC1788 microcontroller and lately I've been trying to improve the way that the USB works. My current issue with the USB is that it's configured in slave mode and, due to the high volume of messages that have to be sent and received in normal operation, the CPU spends most of its time handling USB which creates a bottleneck.
I've been trying to resolve this issue by switching from slave-mode configuration to DMA-mode. I've been using example projects to help and I think most of the code I need is in place.
USB initialisation works fine, as before. The problem is that as soon as I try to send a USB message to an endpoint that I configure in DMA-mode (by enabling the appropriate bit in the EpDMAEn register), I get a USB System Error Interrupt for that endpoint. The only information I can get about this is that:
If a system error (AHB bus error) occurs when transferring the data or when fetching or
updating the DD the corresponding bit is set in this register. SysErrIntSt is a read-only
register.
At this point in the program, I haven't touched the UDCA or setup any DMA descriptors or anything like that past initialisation. This is an error that occurs as soon as the first message is received on the USB bus before I need to do any of that, I believe.
I'm using endpoints 2 IN and OUT, which are double-buffered bulk endpoints with a maximum packet size of 64 bytes.
I've confirmed that the USB works fine if I don't use the DMA.
I've confirmed that the USB works fine if I go through the process of initialising the DMA engine but configure the endpoint in slave-mode instead of DMA-mode.
I've confirmed that the USB Mass Storage example under Example Projects -> NXP -> LP17xx -> 177x_8x CMSIS works fine if I use its default configuration:
...
#define USB_DMA 1
#define USB_DMA_EP 0x00000000
...
but also breaks in the same way if I change this to:
...
#define USB_DMA 1
#define USB_DMA_EP 0x00000030 /* Endpoint 2 IN and OUT */
...
At the beginning of the USB hardware source file, I put the following:
#ifdef USB_DMA
// Stores information received using DMA on OUT endpoints.
//uint8_t dataBufferOUT[DD_BUFFER_SIZE*MAX_PACKET_SIZE];
uint8_t *dataBufferOUT = (uint8_t*)DMA_BUF_ADR;
// Stores information transferred using DMA on IN endpoints.
//uint8_t dataBufferIN[DD_BUFFER_SIZE*MAX_PACKET_SIZE];
uint8_t *dataBufferIN = (uint8_t*)(DMA_BUF_ADR+DD_BUFFER_SIZE*
USB_MAX_PACKET_SIZE);
// Current dataBufferOUT index;
uint16_t dataOUT;
// Current dataBufferIN index.
uint16_t dataIN;
#if defined (__CC_ARM)
#pragma arm section zidata = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#pragma arm section zidata
#elif defined ( __ICCARM__ )
#pragma location = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
#pragma location = "USB_RAM"
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
#pragma location = "USB_RAM"
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#else
uint32_t UDCA[USB_EP_NUM]__attribute__((section ("USB_RAM"))); /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]__attribute__((section ("USB_RAM"))); /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]__attribute__((section ("USB_RAM"))); /* Iso DMA Descriptor Memory */
#endif /*__GNUC__*/
uint32_t udca[USB_EP_NUM]; /* UDCA saved values */
uint32_t DDMemMap[2]; /* DMA Descriptor Memory Usage */
#endif
I initialise the USB peripheral with this code:
void USBInit()
{
// Configure USB pins.
PINSEL_ConfigPin(0, 29, 1); // USB_D+1
PINSEL_ConfigPin(0, 30, 1); // USB_D-1
PINSEL_ConfigPin(1, 18, 1); // USB_UP_LED1
PINSEL_ConfigPin(2, 9, 1); // USB_CONNECT1
PINSEL_ConfigPin(1, 30, 2); // USB_VBUS
// Turn on power and clock
CLKPWR_ConfigPPWR(CLKPWR_PCONP_PCUSB, ENABLE);
//PINSEL_SetPinMode(1, 30, PINSEL_BASICMODE_PLAINOUT);
// Set DEV_CLK_EN and AHB_CLK_EN.
LPC_USB->USBClkCtrl |= 0x12;
// Wait until change is reflected in clock status register.
while((LPC_USB->USBClkSt & 0x12) != 0x12);
// Enable NVIC USB interrupts.
NVIC_EnableIRQ(USB_IRQn);
// Reset the USB.
USBReset();
// Set device address to 0x0 and enable device & connection.
USBSetAddress(0);
// TEMP.
sendMessageFlag = 0;
#ifdef USB_DMA
dataIN = 0;
dataOUT = 0;
#endif
}
My USB reset code:
void USBReset()
{
LPC_USB->EpInd = 0;
LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
LPC_USB->EpInd = 1;
LPC_USB->MaxPSize = USB_MAX_PACKET_SIZE;
while ((LPC_USB->DevIntSt & EP_RLZED_INT) == 0);
LPC_USB->EpIntClr = 0xFFFFFFFF;
#ifdef USB_DMA
LPC_USB->EpIntEn = 0xFFFFFFFF ^ USB_DMA_EP;
#else
LPC_USB->EpIntEn = 0xFFFFFFFF;
#endif
LPC_USB->DevIntClr = 0xFFFFFFFF;
LPC_USB->DevIntEn = DEV_STAT_INT | EP_SLOW_INT /*| EP_FAST_INT*/ ;
#ifdef USB_DMA
uint32_t n;
LPC_USB->UDCAH = USB_RAM_ADR;
LPC_USB->DMARClr = 0xFFFFFFFF;
LPC_USB->EpDMADis = 0xFFFFFFFF;
LPC_USB->EpDMAEn = USB_DMA_EP;
LPC_USB->EoTIntClr = 0xFFFFFFFF;
LPC_USB->NDDRIntClr = 0xFFFFFFFF;
LPC_USB->SysErrIntClr = 0xFFFFFFFF;
LPC_USB->DMAIntEn = 0x00000007;
DDMemMap[0] = 0x00000000;
DDMemMap[1] = 0x00000000;
for (n = 0; n < USB_EP_NUM; n++) {
udca[n] = 0;
UDCA[n] = 0;
}
#endif
}
When ready, this is used to run the USB:
void USBRun()
{
USBSetConnection(TRUE);
}
Finally, my USB interrupt routine:
void USB_IRQHandler(void)
{
OS_EnterInterrupt();
uint32_t data, val, pIndex, lIndex, currEpisr;
uint32_t interruptData = LPC_USB->DevIntSt;
#ifdef USB_DMA
uint32_t dmaInterruptData = LPC_USB->DMAIntSt;
#endif
//printf("InterruptData: 0x%x\n", interruptData);
if (interruptData & ERR_INT)
{
writeSIECommand(CMD_RD_ERR_STAT);
data = readSIECommandData(DAT_RD_ERR_STAT);
// printf("Error data: 0x%x\n", data);
//getchar();
}
// Handle device status interrupt (reset, connection change, suspend/resume).
if(interruptData & DEV_STAT_INT)
{
LPC_USB->DevIntClr = DEV_STAT_INT;
writeSIECommand(CMD_GET_DEV_STAT);
data = readSIECommandData(DAT_GET_DEV_STAT);
//printf("Data: 0x%x\n", data);
// Device reset.
if(data & DEV_RST)
{
USBReset();
USBResetCore();
//printf("USB Reset\n");
}
// Connection change.
if(data & DEV_CON_CH)
{
//printf("Connection change\n");
/* Pass */
}
// Suspend/resume.
if(data & DEV_SUS_CH)
{
if(data & DEV_SUS)
{
//printf("USB Suspend\n");
USBSuspend();
}
else
{
//printf("USB Resume\n");
USBResume();
}
}
OS_LeaveInterrupt();
return;
}
// Handle endpoint interrupt.
if(interruptData & EP_SLOW_INT)
{
//printf("Endpoint slow\n");
data = LPC_USB->EpIntSt;
//printf("EP interrupt: 0x%x\n", data);
currEpisr = 0;
for(pIndex=0; pIndex < USB_EP_NUM; pIndex++)
{
lIndex = pIndex >> 1;
if(data == currEpisr) break;
if(data & (1 << pIndex))
{
currEpisr |= (1 << pIndex);
LPC_USB->EpIntClr = 1 << pIndex;
while((LPC_USB->DevIntSt & CDFULL_INT) == 0);
val = LPC_USB->CmdData;
// OUT endpoint.
if((pIndex & 1) == 0)
{
// Control OUT endpoint.
if(pIndex == 0)
{
// Setup Packet.
if(val & EP_SEL_STP)
{
if(USB_P_EP[0])
{
USB_P_EP[0](USB_EVT_SETUP);
continue;
}
}
}
if(USB_P_EP[lIndex])
{
USB_P_EP[lIndex](USB_EVT_OUT);
}
}
// IN endpoint.
else
{
if(USB_P_EP[lIndex])
{
if(lIndex > 0) clearSendMessageFlag(lIndex);
USB_P_EP[lIndex](USB_EVT_IN);
}
}
}
}
LPC_USB->DevIntClr = EP_SLOW_INT;
}
#ifdef USB_DMA
if (dmaInterruptData & 0x00000001) { /* End of Transfer Interrupt */
data = LPC_USB->EoTIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_EOT);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_EOT);
}
}
}
}
LPC_USB->EoTIntClr = data;
}
if (dmaInterruptData & 0x00000002) { /* New DD Request Interrupt */
data = LPC_USB->NDDRIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_NDR);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_NDR);
}
}
}
}
LPC_USB->NDDRIntClr = data;
}
if (dmaInterruptData & 0x00000004) { /* System Error Interrupt */
data = LPC_USB->SysErrIntSt;
for (pIndex = 2; pIndex < USB_EP_NUM; pIndex++) { /* Check All Endpoints */
if (data & (1 << pIndex)) {
lIndex = pIndex >> 1;
if ((pIndex & 1) == 0) { /* OUT Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_OUT_DMA_ERR);
}
} else { /* IN Endpoint */
if (USB_P_EP[lIndex]) {
USB_P_EP[lIndex](USB_EVT_IN_DMA_ERR);
}
}
}
}
LPC_USB->SysErrIntClr = data;
}
#endif /* USB_DMA */
OS_LeaveInterrupt();
}
What I'm ideally looking for is a solution if you can spot an error in any of my code or a working example program that can be run on an LPC1788 that demonstrates USB message transmission and reception using the DMA engine.
I'd also appreciate any information on what can cause an AHB bus error.
EDIT
In response to Turbo J's answer below:
Check the address of UDCA. The required alignment is very strict, 256 byte IIRC, so the address must end with 0x00 as LDB. GCC requires support for the USB_RAM section in the linker script.
In my USB hardware header file, I have:
/* USB RAM Definitions */
#define USB_RAM_ADR LPC_PERI_RAM_BASE /* USB RAM Start Address */
#define USB_RAM_SZ 0x00004000 /* USB RAM Size (4kB) */
LPC_PERI_RAM_BASE has the value 0x20000000UL.
In my source file, I have:
#if defined (__CC_ARM)
#pragma arm section zidata = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#pragma arm section zidata
#elif defined ( __ICCARM__ )
#pragma location = "USB_RAM"
uint32_t UDCA[USB_EP_NUM]; /* UDCA in USB RAM */
#pragma location = "USB_RAM"
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]; /* Non-Iso DMA Descriptor Memory */
#pragma location = "USB_RAM"
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]; /* Iso DMA Descriptor Memory */
#else
uint32_t UDCA[USB_EP_NUM]__attribute__((section ("USB_RAM"))); /* UDCA in USB RAM */
uint32_t DD_NISO_Mem[4*DD_NISO_CNT]__attribute__((section ("USB_RAM"))); /* Non-Iso DMA Descriptor Memory */
uint32_t DD_ISO_Mem [5*DD_ISO_CNT]__attribute__((section ("USB_RAM"))); /* Iso DMA Descriptor Memory */
#endif /*__GNUC__*/
uint32_t udca[USB_EP_NUM]; /* UDCA saved values */
uint32_t DDMemMap[2]; /* DMA Descriptor Memory Usage */
#endif
Where USB_EP_NUM is 32.
Therefore, UDCA should be a 128-byte array that begins at the start of the RAM memory block, I believe.
Check the address of UDCA. The required alignment is very strict, 256 byte IIRC, so the address must end with 0x00 as LDB. GCC requires support for the USB_RAM section in the linker script.
I have been playing around with ATtiny's and arduino for a while but am a complete newbee in 'real' AVR programming.
I am building a laser gun and target for my son and want to have the target play a random silly animal sound whenever it is hit.
The other functions of the target is maintained by Attiny85s and i therefore stumbled upon Elm-Chans code for interfacing a microSD card with Attiny85 and play .wav files by PWM.
However the code is written so that it plays files on the SD card root folder (or other dir) in order and skips to next in response to a pushbutton on one of the pins.
What i want it to do is to pick and play a random file from the root when button is pushed (i.e. target is hit).
It should play the sound only once and preferably interrupt an already playing file and play another if the target is hit again before the sound has ended.
I have been trying really hard to figure out what to change in the code but come to realize that i need to start from the beginning with learning AVR programming from scratch...however that will take some time and before i get there i humbly hope that any of you could grant me a quick fix and tell me what to do, so i can finish the toy.
Kind regards
Mads
/*----------------------------------------------------------------------------/
/ 8-pin SD audio player R0.05d (C)ChaN, 2011 /
/-----------------------------------------------------------------------------/
/ This project, program codes and circuit diagrams, is opened under license
/ policy of following trems.
/
/ Copyright (C) 2010, ChaN, all right reserved.
/
/ * This project 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 use UNDER YOUR RESPONSIBILITY.
/ * Redistributions of source code must retain the above copyright notice.
/
/----------------------------------------------------------------------------*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <string.h>
#include "pff.h"
#include "xitoa.h"
#ifndef MODE
#error Wrong make file.
#endif
#if MODE == 0 /* Single output */
FUSES = {0xE1, 0xDD, 0xFF}; /* Fuse bytes for mono: Low, High and Extended */
#else /* Dual output */
FUSES = {0xE1, 0x7D, 0xFF}; /* Fuse bytes for stereo and mono-HR: Low, High and Extended (*HVS mode only*) */
#endif
/* This is the fuse settings of this project. The fuse data will be included
in the output hex file with program code. However some old flash programmers
cannot load the fuse bits from hex file. If it is the case, remove this line
and use these values to program the fuse bits. */
#define FCC(c1,c2,c3,c4) (((DWORD)c4<<24)+((DWORD)c3<<16)+((WORD)c2<<8)+(BYTE)c1) /* FourCC */
void delay_us (WORD); /* Defined in asmfunc.S */
/*---------------------------------------------------------*/
/* Work Area */
/*---------------------------------------------------------*/
volatile BYTE FifoRi, FifoWi, FifoCt; /* FIFO controls */
BYTE Buff[256]; /* Wave output FIFO */
FATFS Fs; /* File system object */
DIR Dir; /* Directory object */
FILINFO Fno; /* File information */
WORD rb; /* Return value. Put this here to avoid avr-gcc's bug */
/*---------------------------------------------------------*/
static
DWORD load_header (void) /* 0:Invalid format, 1:I/O error, >=1024:Number of samples */
{
DWORD sz, f;
BYTE b, al = 0;
if (pf_read(Buff, 12, &rb)) return 1; /* Load file header (12 bytes) */
if (rb != 12 || LD_DWORD(Buff+8) != FCC('W','A','V','E')) return 0;
for (;;) {
wdt_reset();
pf_read(Buff, 8, &rb); /* Get Chunk ID and size */
if (rb != 8) return 0;
sz = LD_DWORD(&Buff[4]); /* Chunk size */
switch (LD_DWORD(&Buff[0])) { /* Switch by chunk ID */
case FCC('f','m','t',' ') : /* 'fmt ' chunk */
if (sz & 1) sz++; /* Align chunk size */
if (sz > 100 || sz < 16) return 0; /* Check chunk size */
pf_read(Buff, sz, &rb); /* Get content */
if (rb != sz) return 0;
if (Buff[0] != 1) return 0; /* Check coding type (LPCM) */
b = Buff[2];
if (b != 1 && b != 2) return 0; /* Check channels (1/2) */
GPIOR0 = al = b; /* Save channel flag */
b = Buff[14];
if (b != 8 && b != 16) return 0; /* Check resolution (8/16 bit) */
GPIOR0 |= b; /* Save resolution flag */
if (b & 16) al <<= 1;
f = LD_DWORD(&Buff[4]); /* Check sampling freqency (8k-48k) */
if (f < 8000 || f > 48000) return 4;
OCR0A = (BYTE)(F_CPU / 8 / f) - 1; /* Set sampling interval */
break;
case FCC('d','a','t','a') : /* 'data' chunk */
if (!al) return 0; /* Check if format is valid */
if (sz < 1024 || (sz & (al - 1))) return 0; /* Check size */
if (Fs.fptr & (al - 1)) return 0; /* Check word alignment */
return sz; /* Start to play */
case FCC('D','I','S','P') : /* 'DISP' chunk */
case FCC('L','I','S','T') : /* 'LIST' chunk */
case FCC('f','a','c','t') : /* 'fact' chunk */
if (sz & 1) sz++; /* Align chunk size */
pf_lseek(Fs.fptr + sz); /* Skip this chunk */
break;
default : /* Unknown chunk */
return 0;
}
}
return 0;
}
static
void ramp (
int dir /* 0:Ramp-down, 1:Ramp-up */
)
{
#if MODE != 3
BYTE v, d, n;
if (dir) {
v = 0; d = 1;
} else {
v = 128; d = (BYTE)-1;
}
n = 128;
do {
v += d;
OCR1A = v; OCR1B = v;
delay_us(100);
} while (--n);
#else
dir = dir ? 128 : 0;
OCR1A = (BYTE)dir; OCR1B = (BYTE)dir;
#endif
}
static
FRESULT play (
const char *dir, /* Directory */
const char *fn /* File */
)
{
DWORD sz;
FRESULT res;
BYTE sw;
WORD btr;
wdt_reset();
xsprintf((char*)Buff, PSTR("%s/%s"), dir, fn);
res = pf_open((char*)Buff); /* Open sound file */
if (res == FR_OK) {
sz = load_header(); /* Check file format and ready to play */
if (sz < 1024) return 255; /* Cannot play this file */
FifoCt = 0; FifoRi = 0; FifoWi = 0; /* Reset audio FIFO */
if (!TCCR1) { /* Enable audio out if not enabled */
PLLCSR = 0b00000110; /* Select PLL clock for TC1.ck */
GTCCR = 0b01100000; /* Enable OC1B as PWM */
TCCR1 = MODE ? 0b01100001 : 0b00000001; /* Start TC1 and enable OC1A as PWM if needed */
TCCR0A = 0b00000010; /* Statr TC0 as interval timer at 2MHz */
TCCR0B = 0b00000010;
TIMSK = _BV(OCIE0A);
ramp(1);
}
pf_read(0, 512 - (Fs.fptr % 512), &rb); /* Snip sector unaligned part */
sz -= rb;
sw = 1; /* Button status flag */
do { /* Data transfer loop */
wdt_reset();
btr = (sz > 1024) ? 1024 : (WORD)sz;/* A chunk of audio data */
res = pf_read(0, btr, &rb); /* Forward the data into audio FIFO */
if (rb != 1024) break; /* Break on error or end of data */
sz -= rb; /* Decrease data counter */
sw <<= 1; /* Break on button down */
} while ((PINB & 1) || ++sw != 1);
}
while (FifoCt) ; /* Wait for audio FIFO empty */
OCR1A = 128; OCR1B = 128; /* Return output to center level */
return res;
}
static
void delay500 (void)
{
wdt_reset();
TCCR0B = 0; TCCR0A = 0; /* Stop TC0 */
if (TCCR1) { /* Stop TC1 if enabled */
ramp(0);
TCCR1 = 0; GTCCR = 0;
}
WDTCR = _BV(WDE) | _BV(WDIE) | 0b101; /* Set WDT to interrupt mode in timeout of 0.5s */
set_sleep_mode(SLEEP_MODE_PWR_DOWN); /* Enter power down mode */
sleep_mode();
wdt_reset();
WDTCR = _BV(WDE) | 0b110; /* Set WDT to reset mode in timeout of 1s */
}
EMPTY_INTERRUPT(WDT_vect);
/*-----------------------------------------------------------------------*/
/* Main */
int main (void)
{
FRESULT res;
char *dir;
BYTE org_osc = OSCCAL;
MCUSR = 0;
WDTCR = _BV(WDE) | 0b110; /* Enable WDT reset in timeout of 1s */
PORTB = 0b101001; /* Initialize port: - - H L H L L P */
DDRB = 0b111110;
sei();
for (;;) {
if (pf_mount(&Fs) == FR_OK) { /* Initialize FS */
wdt_reset();
Buff[0] = 0;
if (!pf_open("osccal")) pf_read(Buff, 1, &rb); /* Adjust frequency */
OSCCAL = org_osc + Buff[0];
res = pf_opendir(&Dir, dir = "wav"); /* Open sound file directory */
if (res == FR_NO_PATH)
res = pf_opendir(&Dir, dir = ""); /* Open root directory */
while (res == FR_OK) { /* Repeat in the dir */
res = pf_readdir(&Dir, 0); /* Rewind dir */
while (res == FR_OK) { /* Play all wav files in the dir */
wdt_reset();
res = pf_readdir(&Dir, &Fno); /* Get a dir entry */
if (res || !Fno.fname[0]) break; /* Break on error or end of dir */
if (!(Fno.fattrib & (AM_DIR|AM_HID)) && strstr(Fno.fname, ".WAV"))
res = play(dir, Fno.fname); /* Play file */
}
}
}
delay500(); /* Delay 500ms in low power sleep mode */
}
}
I reckon your case requires more C-coding knowledge than AVR-hacking one.
Here:
if (!(Fno.fattrib & (AM_DIR|AM_HID)) && strstr(Fno.fname, ".WAV"))
res = play(dir, Fno.fname); /* Play file */
Every time matching file is found, play function is called. What you want instead is to add this file descriptor to some form of container (e.g. list) of available files. After all files are listed, you may want to wait for external input (hit by laser), and then play the file and move current file index to the next.
To enable pausing and resetting playback, I recommend using external interrupt for handling "on hit" events.
PS Building toys for your kid using AVR? +1 from me!