From 3e083ee4c14079b1830316d13734ff9d4f5aa09f Mon Sep 17 00:00:00 2001 From: Nathan Seidle Date: Mon, 28 Dec 2009 16:48:52 -0700 Subject: [PATCH] v1.1 - Fixes over-run resets and adds NewLog and SeqLog functions. Added lots-o-text.txt for testing. --- Code/Makefile | 19 +- Code/byteordering.h | 4 + Code/fat.c | 3 + Code/main.c | 652 ++++++++++++++++++++++++++-------------- Code/sd-reader_config.h | 1 + Code/sd_raw.c | 2 + Code/uart.c | 1 + OpenLog-Dimensional.pdf | Bin 0 -> 12446 bytes 8 files changed, 444 insertions(+), 238 deletions(-) create mode 100644 OpenLog-Dimensional.pdf diff --git a/Code/Makefile b/Code/Makefile index 77a0ee3..0884c99 100644 --- a/Code/Makefile +++ b/Code/Makefile @@ -104,6 +104,10 @@ ASRC = # Optimization level, can be [0, 1, 2, 3, s]. # 0 = turn off optimization. s = optimize for size. # (Note: 3 is not always the best optimization level. See avr-libc FAQ.) +#OPT = 0 //EEPROM functions fail at this level +#OPT = 1 //EEPROM functions fail at this level +#OPT = 2 //Ctrl+z doesn't work +#OPT = 3 //Ctrl+z doesn't work - this causes the asm("nop"); to fail OPT = s @@ -272,14 +276,19 @@ LDFLAGS += $(PRINTF_LIB) $(SCANF_LIB) $(MATH_LIB) #---------------- Programming Options (avrdude serial bootloader) ---------------- #"C:\arduino\hardware\tools\avr\bin\avrdude" -PCOM3 -c stk500v1 -patmega168 -b19200 -Uflash:w:Simon-PTH-v1.hex -V -F -C"C:\arduino\hardware\tools\avr\etc\avrdude.conf" -SERIAL_AVRDUDE = D:\arduino\hardware\tools\avr\bin\avrdude +#SERIAL_AVRDUDE = D:\arduino\hardware\tools\avr\bin\avrdude #SERIAL_AVRDUDE = avrdude -SERIAL_AVRDUDE_CONFIG = D:\arduino\hardware\tools\avr\etc\avrdude.conf -SERIAL_AVRDUDE_PORT = COM1 -SERIAL_AVRDUDE_SPEED = 115200 +#SERIAL_AVRDUDE_CONFIG = D:\arduino\hardware\tools\avr\etc\avrdude.conf +#SERIAL_AVRDUDE_PORT = COM5 +#SERIAL_AVRDUDE_SPEED = 57600 + +SERIAL_AVRDUDE = C:\arduino\hardware\tools\avr\bin\avrdude +SERIAL_AVRDUDE_CONFIG = C:\arduino\hardware\tools\avr\etc\avrdude.conf +SERIAL_AVRDUDE_PORT = COM3 +SERIAL_AVRDUDE_SPEED = 57600 SERIAL_AVRDUDE_PROGRAMMER = stk500v1 -SERIAL_AVRDUDE_FLAGS = -p $(MCU) -P $(SERIAL_AVRDUDE_PORT) -c $(SERIAL_AVRDUDE_PROGRAMMER) -b $(SERIAL_AVRDUDE_SPEED) +SERIAL_AVRDUDE_FLAGS = -p $(MCU) -P $(SERIAL_AVRDUDE_PORT) -c $(SERIAL_AVRDUDE_PROGRAMMER) -b $(SERIAL_AVRDUDE_SPEED) -V SERIAL_AVRDUDE_FLAGS += -C$(SERIAL_AVRDUDE_CONFIG) SERIAL_AVRDUDE_FLAGS += $(AVRDUDE_NO_VERIFY) SERIAL_AVRDUDE_FLAGS += $(AVRDUDE_VERBOSE) diff --git a/Code/byteordering.h b/Code/byteordering.h index 5face79..e138f39 100644 --- a/Code/byteordering.h +++ b/Code/byteordering.h @@ -13,6 +13,9 @@ #include +#define LITTLE_ENDIAN 1 +#define DOXYGEN 0 + #ifdef __cplusplus extern "C" { @@ -53,6 +56,7 @@ extern "C" * \returns The given 32-bit integer converted to little-endian byte order. */ + #if DOXYGEN || LITTLE_ENDIAN || __AVR__ #define HTOL16(val) (val) #define HTOL32(val) (val) diff --git a/Code/fat.c b/Code/fat.c index 1a6c5cf..80e9aab 100644 --- a/Code/fat.c +++ b/Code/fat.c @@ -16,6 +16,9 @@ #include +#define DOXYGEN 0 + + #if USE_DYNAMIC_MEMORY #include #endif diff --git a/Code/main.c b/Code/main.c index 059dbb5..157bd9b 100644 --- a/Code/main.c +++ b/Code/main.c @@ -70,12 +70,30 @@ TX-O pin will not be greater than 3.3V. This may cause problems with some systems - for example if your attached microcontroller requires 4V minimum for serial communication (this is rare). + v1.1 + Adding better defines for EEPROM settings + Adding new log and sequential log functions. + + Code is acting very weird with what looks to be stack crashes. I can get around this by turning the optimizer off ('0'). + Found an error : EEPROM functions fail when optimizer is set to '0' or '1'. + sd-reader_config.h contains flag for USE_DYNAMIC_MEMORY + + Looks like tweaking the optimization higher causes the asm("nop"); to fail inside the append_file routine. Changing this to + delay_us(1); works. + + I have a sneaking suspicion that I was having buffer overrun problems when defining the input_buffer at 1024 bytes. The + ATmega328 should have enough RAM (2K) but weird reset errors were occuring. With the buffer at 512bytes, append_file runs + just fine and is able to log at 115200 at a constant data rate. + + Added file called 'lots-o-text.txt' to version control. This text contains easy to scan text to be used for full data + rate checking and testing. */ #include +#include #include -#include #include + #include "fat.h" #include "fat_config.h" #include "partition.h" @@ -285,12 +303,22 @@ static uint8_t print_disk_info(const struct fat_fs_struct* fs); void ioinit(void); void print_menu(void); void init_media(void); -void config_menu(void); +void baud_menu(void); +void system_menu(void); uint8_t read_buffer(char* buffer, uint8_t buffer_length); +uint8_t append_file(char* file_name); +void newlog(void); +void seqlog(void); +void command_shell(void); + +void blink_error(uint8_t ERROR_TYPE); void EEPROM_write(uint16_t uiAddress, unsigned char ucData); unsigned char EEPROM_read(uint16_t uiAddress); +void delay_us(uint16_t x); +void delay_ms(uint16_t x); + #define sbi(port, port_pin) ((port) |= (uint8_t)(1 << port_pin)) #define cbi(port, port_pin) ((port) &= (uint8_t)~(1 << port_pin)) @@ -302,21 +330,28 @@ unsigned char EEPROM_read(uint16_t uiAddress); #define STAT2 5 #define STAT2_PORT PORTB -void blink_error(uint8_t ERROR_TYPE); - #define ERROR_SD_INIT 3 #define ERROR_NEW_BAUD 5 -void delay_us(uint16_t x); -void delay_ms(uint16_t x); +#define LOCATION_BAUD_SETTING 0x01 +#define LOCATION_SYSTEM_SETTING 0x02 +#define LOCATION_FILE_NUMBER 0x03 +#define BAUD_2400 0 +#define BAUD_9600 1 +#define BAUD_57600 2 +#define BAUD_115200 3 + +#define MODE_NEWLOG 0 +#define MODE_SEQLOG 1 +#define MODE_COMMAND 2 //Global variables struct fat_fs_struct* fs; struct partition_struct* partition; struct fat_dir_struct* dd; -#define BUFF_LEN 1024 +#define BUFF_LEN 512 char input_buffer[BUFF_LEN]; uint16_t read_spot, checked_spot; @@ -327,7 +362,7 @@ ISR(USART_RX_vect) { input_buffer[read_spot] = UDR0; read_spot++; - PORTD ^= (1< 5) + { + system_mode = MODE_NEWLOG; //By default, unit will turn on and go to new file logging + EEPROM_write(LOCATION_SYSTEM_SETTING, MODE_NEWLOG); + } + + //If we are in new log mode, find a new file name to write to + if(system_mode == MODE_NEWLOG) + newlog(); + + //If we are in sequential log mode, determine if seqlog.txt has been created or not, and then open it for logging + if(system_mode == MODE_SEQLOG) + seqlog(); + + //Once either one of these modes exits, go to normal command mode, which is called by returning to main() + command_shell(); + + return 0; +} + +void ioinit(void) +{ + //Init Timer0 for delay_us + //TCCR0B = (1< 5) + { + uart_speed = BAUD_9600; //Reset UART to 9600 if there is no speed stored + EEPROM_write(LOCATION_BAUD_SETTING, BAUD_9600); + } + + //Setup uart + uart_init(uart_speed); +#if DEBUG + uart_puts_p(PSTR("UART Init\n")); +#else + uart_puts_p(PSTR("1")); +#endif + + //Setup SPI, init SD card, etc + init_media(); + uart_puts_p(PSTR("2")); +} + +//Log to the same file every time the system boots, sequentially +//Checks to see if the file SEQLOG.txt is available +//If not, create it +//If yes, append to it +//Return 0 on error +//Return anything else on sucess +void seqlog(void) +{ + char seq_file_name[13]; + sprintf(seq_file_name, "SEQLOG.txt"); + + struct fat_file_struct* fd = open_file_in_dir(fs, dd, seq_file_name); + if(!fd) + { + uart_puts_p(PSTR("Creating SEQLOG\n")); + + struct fat_dir_entry_struct file_entry; + if(!fat_create_file(dd, seq_file_name, &file_entry)) + { + uart_puts_p(PSTR("Error creating SEQLOG\n")); + return; + } + } + + fat_close_file(fd); //Close the file so we can re-open it in append_file + + append_file(seq_file_name); +} + +//Log to a new file everytime the system boots +//Checks the spots in EEPROM for the next available LOG# file name +//Updates EEPROM and then appends to the new log file. +//Currently limited to 255 files but this should not always be the case. +void newlog(void) +{ + uint8_t new_file_number; + new_file_number = EEPROM_read(LOCATION_FILE_NUMBER); + + if(new_file_number == 255) + { + new_file_number = 0; //By default, unit will start at file number zero + EEPROM_write(LOCATION_FILE_NUMBER, new_file_number); + } + + char new_file_name[13]; + sprintf(new_file_name, "LOG%03d.txt", new_file_number); + + struct fat_dir_entry_struct file_entry; + while(!fat_create_file(dd, new_file_name, &file_entry)) + { + //Increment the file number because this file name is already taken + new_file_number++; + + sprintf(new_file_name, "LOG%03d.txt", new_file_number); + } + + //Record new_file number to EEPROM + EEPROM_write(LOCATION_FILE_NUMBER, new_file_number++); //Add one the new_file_number for the next power-up + +#if DEBUG + uart_puts_p(PSTR("\nCreated new file: ")); + uart_puts(new_file_name); + uart_puts_p(PSTR("\n")); +#endif - /* provide a simple shell */ + //Begin writing to file + append_file(new_file_name); +} + +void command_shell(void) +{ + //provide a simple shell char buffer[24]; while(1) { - /* print prompt */ + //print prompt uart_putc('>'); - //uart_putc(' '); - /* read command */ + //read command char* command = buffer; if(read_line(command, sizeof(buffer)) < 1) continue; - /* execute command */ + //execute command if(strcmp_P(command, PSTR("init")) == 0) { uart_puts_p(PSTR("Closing down file system\n")); @@ -376,10 +569,15 @@ int main(void) //Print available commands print_menu(); } + else if(strcmp_P(command, PSTR("baud")) == 0) + { + //Go into baud select menu + baud_menu(); + } else if(strcmp_P(command, PSTR("set")) == 0) { - //Go into configure mode - config_menu(); + //Go into system setting menu + system_menu(); } else if(strncmp_P(command, PSTR("cd "), 3) == 0) { @@ -550,139 +748,16 @@ int main(void) fat_close_file(fd); } + else if(strncmp_P(command, PSTR("append "), 7) == 0) { - //File the end of a current file and begins writing to it + //Find the end of a current file and begins writing to it //Ends only when the user inputs Ctrl+z (ASCII 26) command += 7; if(command[0] == '\0') continue; - - char* offset_value = command; - while(*offset_value != ' ' && *offset_value != '\0') - ++offset_value; - - /* search file in current directory and open it */ - struct fat_file_struct* fd = open_file_in_dir(fs, dd, command); - if(!fd) - { - uart_puts_p(PSTR("error opening ")); - uart_puts(command); - uart_putc('\n'); - continue; - } - -#if DEBUG - uart_puts_p(PSTR("File open\n")); -#endif - int32_t offset = 0; - //Seeks the end of the file : offset = EOF location - if(!fat_seek_file(fd, &offset, FAT_SEEK_END)) - { - uart_puts_p(PSTR("error seeking on ")); - uart_puts(command); - uart_putc('\n'); - - fat_close_file(fd); - continue; - } - -#if DEBUG - uart_puts_p(PSTR("Recording\n")); -#endif - /* give a different prompt */ - uart_putc('<'); - //uart_putc(' '); - - sbi(STAT1_PORT, STAT1); //Turn on indicator LED - - read_spot = 0; - checked_spot = 0; - - //Clear circular buffer - for(uint16_t i = 0 ; i < BUFF_LEN ; i++) - input_buffer[i] = 0; - //Start UART buffered interrupts - UCSR0B |= (1< 512 - { - //Record second half the buffer - if(fat_write_file(fd, (uint8_t*) input_buffer + (BUFF_LEN/2), (checked_spot - (BUFF_LEN/2)) ) != (checked_spot - (BUFF_LEN/2)) ) - { - uart_puts_p(PSTR("error writing to file\n")); - break; - } - } - - fat_close_file(fd); - - if(!sd_raw_sync()) - uart_puts_p(PSTR("error syncing disk\n")); - - cbi(STAT1_PORT, STAT1); //Turn off indicator LED - + append_file(command); //Uses circular buffer to capture full stream of text and append to file } else if(strncmp_P(command, PSTR("md "), 3) == 0) { @@ -713,90 +788,141 @@ int main(void) uart_putc('\n'); } } - + //Do we ever get this far? - uart_puts_p(PSTR("Exiting: closing down\n")); - - /* close file system */ - fat_close(fs); - - /* close partition */ - partition_close(partition); - - return 0; } -void ioinit(void) + +//Appends a stream of serial data to a given file +//We use the RX interrupt and a circular buffer of 1024 bytes so that we can capture a full stream of +//data even at 115200bps +//Does not exit until Ctrl+z (ASCII 26) is received +//Returns 0 on error +//Returns 1 on success +uint8_t append_file(char* file_name) { - /* we will just use ordinary idle mode */ - set_sleep_mode(SLEEP_MODE_IDLE); - //Init Timer0 for delay_us - //TCCR0B = (1< 5) + //Upon receiving the escape character, we may still have stuff left in the buffer + //Record the last of the buffer to memory + if(checked_spot == 0 || checked_spot == (BUFF_LEN/2)) { - uart_speed = 1; //Reset UART to 9600 if there is no speed stored - EEPROM_write(0x01, 1); + //Do nothing, we already recorded the buffers right before catching the escape character + } + else if(checked_spot < (BUFF_LEN/2)) + { + //Record first half the buffer + if(fat_write_file(fd, (uint8_t*) input_buffer, checked_spot) != checked_spot) + uart_puts_p(PSTR("error writing to file\n")); + } + else //checked_spot > (BUFF_LEN/2) + { + //Record second half the buffer + if(fat_write_file(fd, (uint8_t*) input_buffer + (BUFF_LEN/2), (checked_spot - (BUFF_LEN/2)) ) != (checked_spot - (BUFF_LEN/2)) ) + uart_puts_p(PSTR("error writing to file\n")); } - //Setup uart - uart_init(uart_speed); + fat_close_file(fd); + + cbi(STAT1_PORT, STAT1); //Turn off indicator LED + #if DEBUG - uart_puts_p(PSTR("UART Init\n")); + uart_puts_p(PSTR("Done!\n")); #endif - uart_puts_p(PSTR("1")); + uart_puts_p(PSTR("~")); //Indicate a successful record - //Setup SPI, init SD card, etc - init_media(); - uart_puts_p(PSTR("2")); + return(1); //Success! } + //Inits the SD interface, opens file system, opens root dir, and checks card info if wanted void init_media(void) { @@ -890,6 +1016,7 @@ uint8_t read_line(char* buffer, uint8_t buffer_length) while(read_length < buffer_length - 1) { PORTD ^= (1<> 8; //Set up address and data registers + EEARL = uiAddress; //Set up address and data registers + EEDR = ucData; + EECR |= (1<> 8; //Set up address and data registers + EEARL = uiAddress; //Set up address and data registers + EECR |= (1<\t\t: Creates \n")); uart_puts_p(PSTR("append \t\t: Appends text to end of . The text is read from the UART in a stream and is not echoed. Finish by sending Ctrl+z (ASCII 26)\n")); uart_puts_p(PSTR("write \t: Writes text to , starting from . The text is read from the UART, line by line. Finish with an empty line\n")); @@ -1092,21 +1240,24 @@ void print_menu(void) uart_puts_p(PSTR("init\t\t\t: Reinitializes and reopens the memory card\n")); uart_puts_p(PSTR("sync\t\t\t: Ensures all buffered data is written to the card\n")); - uart_puts_p(PSTR("set\t\t\t: Menu to configure baud rate\n")); + uart_puts_p(PSTR("\nMenus:\n")); + uart_puts_p(PSTR("set\t\t\t: Menu to configure system boot mode\n")); + uart_puts_p(PSTR("baud\t\t\t: Menu to configure baud rate\n")); } -void config_menu(void) +//Configure what baud rate to communicate at +void baud_menu(void) { - char buffer[24]; + char buffer[5]; while(1) { - uart_puts_p(PSTR("\nOpenLog Configuration:\n")); + uart_puts_p(PSTR("\nBaud Configuration:\n")); - uart_puts_p(PSTR("0) Set Baud rate 2400\n")); - uart_puts_p(PSTR("1) Set Baud rate 9600\n")); - uart_puts_p(PSTR("2) Set Baud rate 57600\n")); - uart_puts_p(PSTR("3) Set Baud rate 115200\n")); + uart_puts_p(PSTR("1) Set Baud rate 2400\n")); + uart_puts_p(PSTR("2) Set Baud rate 9600\n")); + uart_puts_p(PSTR("3) Set Baud rate 57600\n")); + uart_puts_p(PSTR("4) Set Baud rate 115200\n")); //print prompt uart_putc('>'); @@ -1118,63 +1269,98 @@ void config_menu(void) continue; //execute command - if(strcmp_P(command, PSTR("0")) == 0) + if(strcmp_P(command, PSTR("1")) == 0) { uart_puts_p(PSTR("\nGoing to 2400bps...\n")); //Set baud rate to 2400 - EEPROM_write(0x01, 0); + EEPROM_write(LOCATION_BAUD_SETTING, BAUD_2400); blink_error(ERROR_NEW_BAUD); return; } - if(strcmp_P(command, PSTR("1")) == 0) + if(strcmp_P(command, PSTR("2")) == 0) { uart_puts_p(PSTR("\nGoing to 9600bps...\n")); //Set baud rate to 9600 - EEPROM_write(0x01, 1); + EEPROM_write(LOCATION_BAUD_SETTING, BAUD_9600); blink_error(ERROR_NEW_BAUD); return; } - if(strcmp_P(command, PSTR("2")) == 0) + if(strcmp_P(command, PSTR("3")) == 0) { uart_puts_p(PSTR("\nGoing to 57600bps...\n")); //Set baud rate to 57600 - EEPROM_write(0x01, 2); + EEPROM_write(LOCATION_BAUD_SETTING, BAUD_57600); blink_error(ERROR_NEW_BAUD); return; } - if(strcmp_P(command, PSTR("3")) == 0) + if(strcmp_P(command, PSTR("4")) == 0) { uart_puts_p(PSTR("\nGoing to 115200bps...\n")); //Set baud rate to 115200 - EEPROM_write(0x01, 3); + EEPROM_write(LOCATION_BAUD_SETTING, BAUD_115200); blink_error(ERROR_NEW_BAUD); return; } } } -//Basic EEPROM functions to read/write to the internal EEPROM -void EEPROM_write(uint16_t uiAddress, unsigned char ucData) +//Change how OpenLog works +//1) Turn on unit, unit will create new file, and just start logging +//2) Turn on, append to known file, and just start logging +//3) Turn on, sit at command prompt +void system_menu(void) { - while(EECR & (1<> 8; //Set up address and data registers - EEARL = uiAddress; //Set up address and data registers - EEDR = ucData; - EECR |= (1<> 8; //Set up address and data registers - EEARL = uiAddress; //Set up address and data registers - EECR |= (1<'); + + //read command + char* command = buffer; + + if(read_line(command, sizeof(buffer)) < 1) + continue; + + //execute command + if(strcmp_P(command, PSTR("1")) == 0) + { + uart_puts_p(PSTR("New file logging\n")); + EEPROM_write(LOCATION_SYSTEM_SETTING, MODE_NEWLOG); + return; + } + if(strcmp_P(command, PSTR("2")) == 0) + { + uart_puts_p(PSTR("Append file logging\n")); + EEPROM_write(LOCATION_SYSTEM_SETTING, MODE_SEQLOG); + return; + } + if(strcmp_P(command, PSTR("3")) == 0) + { + uart_puts_p(PSTR("Command prompt\n")); + EEPROM_write(LOCATION_SYSTEM_SETTING, MODE_COMMAND); + return; + } + if(strcmp_P(command, PSTR("4")) == 0) + { + uart_puts_p(PSTR("New file number reset to zero\n")); + EEPROM_write(LOCATION_FILE_NUMBER, 0); + return; + } + } } #if FAT_DATETIME_SUPPORT diff --git a/Code/sd-reader_config.h b/Code/sd-reader_config.h index 55e08ac..6d98594 100644 --- a/Code/sd-reader_config.h +++ b/Code/sd-reader_config.h @@ -39,6 +39,7 @@ extern "C" * like file and directory handles, set to 0 to use pre-allocated * fixed-size handle arrays. */ +//Default = 0 #define USE_DYNAMIC_MEMORY 0 //#define USE_DYNAMIC_MEMORY 1 diff --git a/Code/sd_raw.c b/Code/sd_raw.c index c51b127..309e20f 100644 --- a/Code/sd_raw.c +++ b/Code/sd_raw.c @@ -12,6 +12,8 @@ #include #include "sd_raw.h" +#define DOXYGEN 0 + /** * \addtogroup sd_raw MMC/SD/SDHC card raw access * diff --git a/Code/uart.c b/Code/uart.c index 5cd4ac3..d64ef3a 100644 --- a/Code/uart.c +++ b/Code/uart.c @@ -97,6 +97,7 @@ void uart_init(uint8_t uart_speed) //UCSRB = (1 << RXEN) | (1 << TXEN) | (1 << RXCIE); UCSRB = (1 << RXEN) | (1 << TXEN); #endif + } void uart_putc(uint8_t c) diff --git a/OpenLog-Dimensional.pdf b/OpenLog-Dimensional.pdf new file mode 100644 index 0000000000000000000000000000000000000000..00bf611fbc6431af1246dfcaf1c343bc31b21b89 GIT binary patch literal 12446 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 literal 0 HcmV?d00001