Compact Flash C Demo
Periodic Logging of Data to Compact Flash
This example program creates a filename based on the current time set in the real-time clock, then periodically opens the file, appends a line of data, and closes the file again. This program will only run on controllers that have a real-time clock.
// // Copyright 2012 Mosaic Industries, Inc. // Disclaimer: THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT ANY // WARRANTIES OR REPRESENTATIONS EXPRESS OR IMPLIED, INCLUDING, BUT NOT // LIMITED TO, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS // FOR A PARTICULAR PURPOSE. // // Each time this demo program is run, it creates a file name based on the // current time reported by the realtime clock. Once per second for ten // seconds it opens that file, appends to it a line of raw analog counts // from four of the built-in analog to digital converter channels, and // closes the file. Errors are thoroughly checked and reported to // demonstrate what might go wrong at each step. // // This include statement should appear at the top of each source code file. #include <mosaic\allqed.h> #ifdef __GNUC__ // For PDQ line platforms, the driver is enabled by simply including // the header file below. #include <wcfm.h> #else // For the QED/Q line platform, we include the kernel extension manager // generated library.c. We assume that it is present in this directory. #include "library.h" #include "library.c" // this is a kernel extension file; // assume it's in the source code directory; if not, edit the file specification #endif // __GNUC__ #ifdef _MOSAIC_LITE #error "The PDQ Board Lite does not support addressable Wildcards, so this demo is not applicable. Go to Project -> Project Compiler Options and select the kernel version without the 'L' suffix to compile for the full PDQ Board." #endif // NOTE: YOU MUST MAKE SURE THAT THIS CONSTANT CORRESPONDS TO YOUR MODULE SELECT JUMPERS! // For example, to access the Wildcard at address 4: // remove both module select jumper caps and mount the card on Wildcard Module Bus 1 #define CF_MODULE_NUM 1 // Modulenum 4 corresponds to a WildCard plugged into Module Port 1 with no jumpers set. // A WildCard plugged directly into a QCard is plugged into Module Port 0. // WildCards can be jumper-configured for module addresses 0 to 7; // see documentation for details // Note: if you are using a QScreen, QVGA or Handheld, accessing a wildcard at module address 0 // is not allowed; this module address is reserved for the GUI on the QScreen, QVGA and Handheld. // Which built-in analog input channels will be used for data acquisition. // For more information on the PDQ Board's built-in analog to digital conversion, see: // http://www.mosaic-industries.com/embedded-systems/sbc-single-board-computers/freescale-hcs12-9s12-c-language/instrument-control/analog-data-acquisition-10-bit-adc // For more information on the QScreen's built-in analot to digital converstion, see: // http://www.mosaic-industries.com/embedded-systems/gui-user-interface/68hc11-lcd-touch-panel/instrument-control/microcontroller-analog-data-acquisition-8-bit-16-bit-adc #define ANALOG_START_CHANNEL 0 #define ANALOG_CHANNEL_COUNT 4 #define MAXIMUM_LINE_LENGTH 80 #define LINE_COUNT 10 // The QED-Forth task pad is a buffer that can // be used temporarily by application code. #define FORTH_PAD ((char*)( (unsigned)UPAD )) char* get_filename( void ) { static char fn_buf[16]; unsigned long timestamp; ReadWatch(); // results are placed in watch_results structure // Upper 10 bits contain the number of months since beginning of 2000. timestamp = ( 12uL * WATCH_YEAR + WATCH_MONTH - 1 ) << 22; // Lower 22 bits contain number of seconds since beginning of current month. timestamp |= ( ( ( WATCH_DATE - 1 ) * 24uL + WATCH_HOUR ) * 60uL + WATCH_MINUTE ) * 60uL + WATCH_SECONDS; // Generated filename will be unique until 2085. #ifdef __GNUC__ snprintf( fn_buf, sizeof(fn_buf), "%.8lX.TXT", (unsigned long)timestamp ); #else sprintf( fn_buf, "%.8lX.TXT", (unsigned long)timestamp ); #endif // Contents of fn_buf are valid until this function is called again. return fn_buf; } int init_demo( void ) { int result; printf( "\nInitializing file system...\n" ); Set_CF_Module( CF_MODULE_NUM ); // Must be called before calling Init_File_System(). result = Init_File_System(); if( result == 0 ) { printf( "Initializing data acquisition...\n" ); #ifdef __GNUC__ ATDOn( ANALOG_START_CHANNEL ); #else AD8On(); #endif printf( "Initialization complete.\n" ); } else if( result == -1 ) { printf( "ERROR: Larger heap area needed.\n" ); } else { if( result & ERR_NOT_DOSFAT_DRIVE ) printf( "ERROR: CF card is not FAT formatted.\n" ); if( result & ERR_ATA_READ ) printf( "ERROR: ATA read failed.\n" ); if( result & ERR_FAIL_ID_DRIVE ) printf( "ERROR: ATA drive ID failed.\n" ); if( result & ~( ERR_NOT_DOSFAT_DRIVE | ERR_ATA_READ | ERR_FAIL_ID_DRIVE ) ) printf( "UNKNOWN error bits in initialization result: %#.4x\n", (unsigned)result ); } return result; } int main( void ) { int result, file_id; unsigned string_size, i; char* filename; unsigned char last_secs = '\xff'; #ifdef __GNUC__ unsigned* data_buf; // Disable libc output buffering, which causes unexpected behavior on embedded systems. // If I/O buffering would benefit your application, see the Queued Serial demo. setbuf( stdout, NULL ); #else unsigned data_buf[ANALOG_CHANNEL_COUNT]; unsigned data_i; #endif result = init_demo(); if( result == 0 ) { Dir(); filename = get_filename(); printf( "\nSampling data and logging to %s", filename ); for( i = 0; i < LINE_COUNT && result == 0; ++i ) { // Wait until the next second on the realtime clock. while( WATCH_SECONDS == last_secs ) { MicrosecDelay(-1); ReadWatch(); } last_secs = WATCH_SECONDS; Emit('.'); // Sample channels 0 - 3. #ifdef __GNUC__ data_buf = ATDSample( ANALOG_START_CHANNEL, ANALOG_CHANNEL_COUNT ); #else for( data_i = 0; data_i < ANALOG_CHANNEL_COUNT; ++data_i ) data_buf[data_i] = AD8Sample( data_i ) << 8; #endif // Open file. file_id = File_Open( filename, 0, -1, A_MODE ); result = File_Open_Error(); if( result != 0 || file_id < 0 ) { // Place readable error messages in Forth task pad and print them. // Must subtract 1 from buffer size passed to this function, // as it will write a null character AFTER it reaches that size. string_size = Report_File_Open_Errors( FORTH_PAD, 0, MAXIMUM_LINE_LENGTH - 1 ); printf( "ERROR %#.4x (file id %d): %.80s\n", result, file_id, FORTH_PAD ); } else { // Format the output line in the Forth task pad. // NOTE that \r\n rather than just \n is needed for // viewing the resulting text file on a Windows PC. string_size = #ifdef __GNUC__ snprintf( PAD, MAXIMUM_LINE_LENGTH, #else sprintf( FORTH_PAD, #endif "%3u %.2u:%.2u:%.2u %5u %5u %5u %5u\r\n", i, WATCH_HOUR, WATCH_MINUTE, WATCH_SECONDS, data_buf[0], data_buf[1], data_buf[2], data_buf[3] ); if( string_size >= MAXIMUM_LINE_LENGTH - 1 ) printf( "ERROR: line %u may be truncated.\n", i ); // Write the output line to the file. result = File_Puts( FORTH_PAD, 0, MAXIMUM_LINE_LENGTH, file_id ); if( result != string_size ) printf( "ERROR: Wrote %d chars of a %u char string.\n", result, string_size ); // Errors from writing to files are stored internally // and retrieved with this function. result = File_Error( file_id ); if( result != 0 ) { // Place readable error messages in Forth task pad and print them. // Must subtract 1 from buffer size passed to this function, // as it will write a null character AFTER it reaches that size. string_size = Report_File_Errors( file_id, FORTH_PAD, 0, MAXIMUM_LINE_LENGTH - 1 ); printf( "ERROR %#.4x: %.80s\n", result, FORTH_PAD ); } // Close the file. result = File_Close( file_id ); if( result & ERR_ATA_WRITE ) printf( "ERROR: ATA write failed on final flush on file close.\n" ); if( result & ERR_BAD_OR_UNOPEN_FILEID ) printf( "ERROR: Attempt to close bad or unopen file ID: %d\n", file_id ); if( result & ~( ERR_ATA_WRITE | ERR_BAD_OR_UNOPEN_FILEID ) ) printf( "UNKNOWN error bits in file close result: %#.4x", (unsigned)result ); } } } Emit('\n'); if( result == 0 ) Dir(); return result; }
Low-Level File I/O
This demonstration program is included in distributions of Mosaic IDE and Mosaic IDE Plus revisions lower than 1300, and demonstrates low-level file creation and access. For instructions on how to use the demo, see the comments at the end of this code listing. The distribution also contains a parallel file named cfmdemo.4TH for Forth programmers.
// wcfmdemo.c // See the "HOW TO RUN THE DEMO" section at the bottom of this file for instructions. // Be sure to define CF_MODULE_NUM so that it matches the hardware settings! // This file demonstrates how to use some of the common file manipulation functions // in the CF Card Software Package. // Both Forth (*.4th) and C (*.C) language versions of this wcfmdemo file exist. // The Demo function creates a file named "ALPHABET.TXT" and writes to it a // sequence of bytes. It uses WPLUS_MODE to open the file, which means that if // the file already exists it is truncated to zero size, and that the file is // readable and writeable. Using the pre-dimensioned File_Contents buffer, // we write increasing values from 0 to 255 in each of the first 256 bytes. // Then we store a message string in the file; it says: // " Alphabet in forward order: " // We then selectively read parts of the file using a buffer in // common RAM, printing the message followed by an upper case alphabetic listing. // Finally, we close the file. // This File I/O code can be used as an example logging data acquisition points gathered during // instrumentation. This code demonstrates the following: // How to open and close a file; // How to use File_Set_Pos and File_Tell_Pos to control random file access; // How to use the pre-dimensioned File_Contents buffer in heap as a scratchpad; // How to use another buffer (we call it show_buffer) as a scratchpad; // How to use File_Write, File_Puts and File_Put_CRLF to put content into a file; // How to use File_Gets to fetch contents out of a file. // For clarity, only simple error handling is performed in this code. // As an exercise for yourself, you can add more robust error handling. // In most cases, you can either report the returned error codes, // or simply check that the number of characters read or written // (as returned by the function) matches what you expect. // To report one overall code, you can logically OR the error codes of the // individual file operations. // Copyright 2009 Mosaic Industries, Inc. All Rights Reserved. // Disclaimer: THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT ANY // WARRANTIES OR REPRESENTATIONS EXPRESS OR IMPLIED, INCLUDING, BUT NOT // LIMITED TO, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS // FOR A PARTICULAR PURPOSE. // **** #include "mosaic/allqed.h" #ifdef __GNUC__ // For PDQ line platforms, the driver is enabled by simply including // the header file below. #include <wcfm.h> #else // For the QED/Q line platform, we include the kernel extension manager // generated library.c. We assume that it is present in this directory. #include "library.h" #include "library.c" // this is a kernel extension file; // assume it's in the source code directory; if not, edit the file specification #endif // __GNU__ // ****** Constants, Variables and Buffers ************ #define CF_MODULE_NUM 0 // MUST match hardware jumper settings! #define NUM_ASCENDING_BYTES 256 #define SHOW_BUF_SIZE 60 // bigger than we need #define LETTERS_PER_ALPHABET ('Z' + 2 - 'A') #define BYTES_PER_PAGE (1024 * 32) // 1 page= 32 Kb; see Make_Image_File static int demo_file_id; // used to save the file_id static long string1_start; // holds 32bit offset of the message string in file static long numbytes_accessed; // value returned by file_read or file_write; not checked static int f_error; // holds logical or of error codes; not checked static char show_buffer[SHOW_BUF_SIZE]; // allocate buffer for Show_File below // ********* Demo code showing how to use the file I/O functions *********** _Q void Init_Contents_Buffer( int file_id ) // writes an ascending pattern starting at zero into the File_Contents buffer // note that we can't embed the call to File_Contents in the parameter list of // StoreChar, as nested calls of _forth functions are not permitted. { int i; xaddr buf_xaddress; for( i=0; i < NUM_ASCENDING_BYTES; i++ ) { buf_xaddress = File_Contents( i, file_id); StoreChar( (char) i, buf_xaddress); } } _Q void Make_File( void ) // Opens a file named ALPHABET.TXT, transfers an ascending sequence of 256 // bytes to the file, followed by a message string. // Initializes the global variables demo_file_id and string1_start { xaddr source_xaddr; char* string_ptr = "Alphabet in forward order: "; int string_size = strlen(string_ptr); int demo_file_id = File_Open("ALPHABET.TXT",THIS_PAGE,-1,WPLUS_MODE); if(demo_file_id < 0) // negative file_id means file_open failed printf("\nFile open failed!\n"); else // if no error, continue... { Init_Contents_Buffer(demo_file_id); // put ascending byte pattern source_xaddr = File_Contents(0, demo_file_id); numbytes_accessed = // write buffer to file; we discard numbytes written File_Write( XADDR_TO_ADDR(source_xaddr),XADDR_TO_PAGE(source_xaddr), (long) NUM_ASCENDING_BYTES, demo_file_id); string1_start = File_Tell_Pos(demo_file_id); // save string offset numbytes_accessed = // write string -> file; ignore numchars written File_Puts( string_ptr, THIS_PAGE, string_size, demo_file_id); f_error |= File_Put_CRLF(demo_file_id); // mark line end; or error code } } _Q void Show_File( void ) // this function prints some of the contents of the file whose id // is in the demo_file_id variable. // First we print the message that starts at d.offset = string1_start, // and then we print the upper case alphabet which starts at // an offset equal to ASCII A (promoted to 32 bits). { int numchars_read; f_error |= File_Set_Pos( demo_file_id, string1_start); // get "ascending" label numchars_read = File_Gets(show_buffer,THIS_PAGE,SHOW_BUF_SIZE,demo_file_id); show_buffer[ numchars_read ] = 0; // put terminating null printf("\n%s",show_buffer); // type string1 (ends in CRLF) f_error |= File_Set_Pos(demo_file_id,(long) 'A'); numchars_read = // ends when 26 letters are read File_Gets(show_buffer,THIS_PAGE,LETTERS_PER_ALPHABET,demo_file_id); show_buffer[ numchars_read ] = 0; // put terminating null printf("%s\n",show_buffer); // type ascending alphabet, add newline // as an excercise: add a line showing the lower case alphabet! } _Q void Demo( void ) // this is the demonstration function // Initializes file system, Opens and initializes ALPHABET.TXT file, // reports selected contents, then closes the file. { f_error = 0; printf("\nInitializing File System..."); Set_CF_Module( CF_MODULE_NUM ); // must init before calling Init_File_System if(Init_File_System()) // if error... printf("\nCouldn't initialize file system!\n"); else // if no error... { printf("\nCreating and initializing the ALPHABET.TXT file...\n"); Make_File(); Show_File(); // print announcement and alphabet File_Close(demo_file_id); // ignore error flag } } _Q void dir( void ) // This function calles Dir() (with a capital D) which is provided by the driver // in wcfm.h. After running main, you can type // dir( ) // at the prompt and it will print a list files on the cf module. Look to // see that ALPHABET.TXT has been created. { Dir(); } int main( void ) // sets up automated file system init and calls the Demo function // this top-level function can be declared as a priority.autostart routine. { Set_CF_Module( CF_MODULE_NUM ); // must init before calling Init_File_System // Do_Autoexec(); // un-comment this if you want to use the autoexec.qed capability Demo(); return 0; } /* ************* HOW TO RUN THE DEMO USING C ******************** Make sure that your PDQ Board or is communicating with your PC, and that the CF Module is installed. Check that the module port and jumper settings match the CF_MODULE_NUM defined in this program. Plug a formatted CF Card into the socket on the CF Module. Follow these steps: 1. Compile this demo program by clicking Build->Build 2. Launch the Mosaic terminal by clicking Tools->Mosaic Terminal. Send the generated wcfmdemo.dlf file to the PDQ Board. 3. Type MAIN from your terminal to run the demo program from this file. You should see the following: Initializing File System... Creating and initializing the ALPHABET.TXT file... Alphabet in forward order: ABCDEFGHIJKLMNOPQRSTUVWXYZ 4. You can optionally type dir( ) to list any files on the cf card. 5. You can optionally type CFA.FOR MAIN PRIORITY.AUTOSTART from your terminal to automatically run the program after every startup. To remove the autostart, type NO.AUTOSTART from your terminal. */
For more examples, see one of the following → Compact Flash Glossary]
Compact Flash Forth Demo
Compact Flash Wildcard Users Guide
This page is about: Write File to Embedded Compact Flash C Example, Embedded Flash Memory – Follow this example program to read and write a file on a compact flash card attached to an embedded computer. Compact Flash, CF Card, software package, C language example