diff --git a/mendel.c b/mendel.c index 6a9d6d4..db28362 100644 --- a/mendel.c +++ b/mendel.c @@ -287,19 +287,12 @@ int main (void) #ifdef SD if (( ! gcode_active || gcode_active & GCODE_SOURCE_SD) && gcode_sources & GCODE_SOURCE_SD) { - c = sd_read_char(); - if (c == 0) { + if (sd_read_gcode_line()) { serial_writestr_P(PSTR("\nSD file done.\n")); gcode_sources &= ! GCODE_SOURCE_SD; // There is no pf_close(), subsequent reads will stick at EOF // and return zeros. } - else { - gcode_active = GCODE_SOURCE_SD; - line_done = gcode_parse_char(c); - if (line_done) - gcode_active = 0; - } } #endif diff --git a/pff.c b/pff.c index b8a8c1e..714bf27 100644 --- a/pff.c +++ b/pff.c @@ -951,6 +951,58 @@ FRESULT pf_read ( return FR_OK; } + + + +FRESULT pf_parse_line ( + uint8_t (*parser)(uint8_t) /* Pointer to the parser function, which + should return 1 on EOL, else zero. */ +) +{ + BYTE one_byte_buffer; + DRESULT dr; + CLUST clst; + DWORD sect; + UINT rcnt; + BYTE cs; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; /* Check file system */ + if (!(fs->flag & FA_OPENED)) /* Check if opened */ + return FR_NOT_OPENED; + + while (fs->fptr < fs->fsize) { /* Repeat until EOF. */ + if ((fs->fptr % 512) == 0) { /* On the sector boundary? */ + cs = (BYTE)(fs->fptr / 512 & (fs->csize - 1)); /* Sector offset in the cluster */ + if (!cs) { /* On the cluster boundary? */ + if (fs->fptr == 0) /* On the top of the file? */ + clst = fs->org_clust; + else + clst = get_fat(fs->curr_clust); + if (clst <= 1) ABORT(FR_DISK_ERR); + fs->curr_clust = clst; /* Update current cluster */ + } + sect = clust2sect(fs->curr_clust); /* Get current sector */ + if (!sect) ABORT(FR_DISK_ERR); + fs->dsect = sect + cs; + } + rcnt = 1; + // TODO: this is very inefficient, it reads a full sector for every + // single byte. Instead the parser function should be passed to + // disk_readp(), so it can read and parse the remaining sector + // in search for an EOL and pass back the number of bytes read. + // It should also pass back wether the line was completed or not. + // If yes, we're done; if no, we call again with the next sector. + dr = disk_readp(&one_byte_buffer, fs->dsect, (UINT)fs->fptr % 512, 1); + if (dr) ABORT(FR_DISK_ERR); + fs->fptr += rcnt; /* Update pointers and counters */ + if (parser(one_byte_buffer)) + return FR_OK; + } + + return FR_END_OF_FILE; +} #endif diff --git a/pff.h b/pff.h index c37a9b8..168d3e5 100644 --- a/pff.h +++ b/pff.h @@ -93,7 +93,8 @@ typedef enum { FR_NO_FILE, /* 3 */ FR_NOT_OPENED, /* 4 */ FR_NOT_ENABLED, /* 5 */ - FR_NO_FILESYSTEM /* 6 */ + FR_NO_FILESYSTEM, /* 6 */ + FR_END_OF_FILE, /* 7, used only by pf_read_gcode_line(). */ } FRESULT; @@ -105,6 +106,7 @@ FRESULT pf_mount (FATFS* fs); /* Mount a logical d void pf_unmount (FATFS* fs); /* Unmount a logical drive */ FRESULT pf_open (const char* path); /* Open a file */ FRESULT pf_read (void* buff, UINT btr, UINT* br); /* Read data from the open file */ +FRESULT pf_parse_line (uint8_t (*parser)(uint8_t)); /* Read and parse a line of data from the open file. */ FRESULT pf_write (const void* buff, UINT btw, UINT* bw); /* Write data to the open file */ FRESULT pf_lseek (DWORD ofs); /* Move file pointer of the open file */ FRESULT pf_opendir (DIR* dj, const char* path); /* Open a directory */ diff --git a/sd.c b/sd.c index 697b88b..d71e7f5 100644 --- a/sd.c +++ b/sd.c @@ -9,16 +9,12 @@ #include "delay.h" #include "serial.h" #include "sersendf.h" - -#define SD_BUFFER_SIZE 16 +#include "gcode_parse.h" static FATFS sdfile; static FRESULT result; -static uint8_t sd_buffer[SD_BUFFER_SIZE]; -static uint8_t sd_buffer_ptr = SD_BUFFER_SIZE; - /** Initialize SPI for SD card reading. */ void sd_init(void) { @@ -88,38 +84,35 @@ void sd_open(const char* filename) { } } -/** Read a character from a file. +/** Read a line of G-code from a file. - \return The character read, or zero if there is no such character (e.g. EOF). + \param A pointer to the parser function. This function should accept an + uint8_t with the character to parse and return an uint8_t wether + end of line (EOL) was reached. - In principle it'd be possible to read the file character by character. - However, Before too long this will cause the printer to read G-code from this file - until done or until stopped by G-code coming in over the serial line. + \return Wether end of line (EOF) was reached or an error happened. + + Juggling with a buffer smaller than 512 bytes means that the underlying + SD card handling code reads a full sector (512 bytes) in each operation, + but throws away everything not fitting into this buffer. Next read operation + reads the very same sector, but keeps a different part. That's ineffective. + + Much better is to parse straight as it comes from the card. This way there + is no need for a buffer at all. Sectors are still read multiple times, but + at least one line is read in one chunk (unless it crosses a sector boundary). */ -uint8_t sd_read_char(void) { - UINT read; - uint8_t this_char; +uint8_t sd_read_gcode_line(void) { - if (sd_buffer_ptr == SD_BUFFER_SIZE) { - result = pf_read(sd_buffer, SD_BUFFER_SIZE, &read); - if (result != FR_OK) { - sersendf_P(PSTR("E: failed to read from file. (%su)"), result); - return 0; - } - if (read < SD_BUFFER_SIZE) { - sd_buffer[read] = 0; // A zero marks EOF. - } - - sd_buffer_ptr = 0; + result = pf_parse_line(&gcode_parse_char); + if (result == FR_END_OF_FILE) { + return 1; + } + else if (result != FR_OK) { + sersendf_P(PSTR("E: failed to parse from file. (%su)"), result); + return 1; } - this_char = sd_buffer[sd_buffer_ptr]; - if (this_char == 0) - sd_buffer_ptr = SD_BUFFER_SIZE; // Start over, perhaps with next file. - else - sd_buffer_ptr++; - - return this_char; + return 0; } #endif /* SD */ diff --git a/sd.h b/sd.h index b7e58a4..fe4f048 100644 --- a/sd.h +++ b/sd.h @@ -25,7 +25,7 @@ void sd_list(const char* path); void sd_open(const char* filename); -uint8_t sd_read_char(void); +uint8_t sd_read_gcode_line(void); #endif /* SD_CARD_SELECT_PIN */