summaryrefslogtreecommitdiff
path: root/boards/ek-lm4f232/qs-logger/flashstore.c
diff options
context:
space:
mode:
authorYuval Adam <yuv.adm@gmail.com>2014-06-29 12:34:32 +0300
committerYuval Adam <yuv.adm@gmail.com>2014-06-29 12:34:32 +0300
commitc3e4c9a25c2910d2d66d52215b3406b13d5b23d5 (patch)
treeadded370d1e356901f8579f076e3263fb0464db7 /boards/ek-lm4f232/qs-logger/flashstore.c
parent990090a4cc9070837d31e66b58d40f0c3d038741 (diff)
Add more board models
Diffstat (limited to 'boards/ek-lm4f232/qs-logger/flashstore.c')
-rw-r--r--boards/ek-lm4f232/qs-logger/flashstore.c625
1 files changed, 625 insertions, 0 deletions
diff --git a/boards/ek-lm4f232/qs-logger/flashstore.c b/boards/ek-lm4f232/qs-logger/flashstore.c
new file mode 100644
index 0000000..30cbcea
--- /dev/null
+++ b/boards/ek-lm4f232/qs-logger/flashstore.c
@@ -0,0 +1,625 @@
+//*****************************************************************************
+//
+// flashstore.c - Data logger module to handle storage in flash.
+//
+// Copyright (c) 2011-2014 Texas Instruments Incorporated. All rights reserved.
+// Software License Agreement
+//
+// Texas Instruments (TI) is supplying this software for use solely and
+// exclusively on TI's microcontroller products. The software is owned by
+// TI and/or its suppliers, and is protected under applicable copyright
+// laws. You may not combine this software with "viral" open-source
+// software in order to form a larger program.
+//
+// THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
+// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
+// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
+// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+// DAMAGES, FOR ANY REASON WHATSOEVER.
+//
+// This is part of revision 2.1.0.12573 of the EK-LM4F232 Firmware Package.
+//
+//*****************************************************************************
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include "inc/hw_types.h"
+#include "driverlib/debug.h"
+#include "driverlib/flash.h"
+#include "utils/ustdlib.h"
+#include "qs-logger.h"
+#include "usbstick.h"
+#include "flashstore.h"
+
+//*****************************************************************************
+//
+// This module manages the storage of data logger data into flash memory.
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Define the beginning and end of the flash storage area. You must make sure
+// that this area is well clear of any space occupied by the application
+// binary, and that this space is not used for any other purpose.
+// The start and end addresses must be 1K aligned. The end address is
+// exclusive - it is 1 value greater than the last valid location used for
+// storage.
+//
+//*****************************************************************************
+#define FLASH_STORE_START_ADDR 0x20000
+#define FLASH_STORE_END_ADDR 0x40000
+
+//*****************************************************************************
+//
+// The next address in flash, that will be used for storing a data record.
+//
+//*****************************************************************************
+static uint32_t g_ui32StoreAddr;
+
+//*****************************************************************************
+//
+// A buffer used to assemble a complete record of data prior to storing it
+// in the flash.
+//
+//*****************************************************************************
+static uint32_t g_pui32RecordBuf[32];
+
+//*****************************************************************************
+//
+// Initializes the flash storage. This is a stub because there is nothing
+// special to do.
+//
+//*****************************************************************************
+void
+FlashStoreInit(void)
+{
+}
+
+//*****************************************************************************
+//
+// Saves data records that are stored in the flash to an externally connected
+// USB memory storage device (USB stick).
+// The flash memory is scanned for the presence of store data records. When
+// records are found they are written in CSV format to the USB stick. This
+// function assumes a non-corrupted storage area, and that any records, once
+// found, are contiguous with all stored records. It will find the oldest
+// record and start with that when storing.
+//
+//*****************************************************************************
+int32_t
+FlashStoreSave(void)
+{
+ uint32_t ui32Addr, ui32OldestRecord, ui32OldestSeconds, ui32Count,
+ ui32PartialCount;
+ tLogRecord *psRecord;
+
+ //
+ // Initialize locals.
+ //
+ ui32OldestRecord = FLASH_STORE_START_ADDR;
+ ui32OldestSeconds = 0xFFFFFFFF;
+
+ //
+ // Show a message to the user.
+ //
+ SetStatusText("SAVE", "SCANNING", "FLASH", 0);
+
+ //
+ // Start at beginning of flash storage area
+ //
+ ui32Addr = FLASH_STORE_START_ADDR;
+
+ //
+ // Search all of flash area checking every stored record.
+ //
+ while(ui32Addr < FLASH_STORE_END_ADDR)
+ {
+ //
+ // If a record signature is found, check for oldest record, then
+ // increment to the next record
+ //
+ if((HWREG(ui32Addr) & 0xFFFFFF00) == 0x53554100)
+ {
+ //
+ // Get a pointer to the data record (account for flash header word)
+ //
+ psRecord = (tLogRecord *)(ui32Addr + 4);
+
+ //
+ // If the seconds in this record are older than any found so far
+ // then save the seconds value, and the address of this record
+ //
+ if(psRecord->ui32Seconds < ui32OldestSeconds)
+ {
+ ui32OldestSeconds = psRecord->ui32Seconds;
+ ui32OldestRecord = ui32Addr;
+ }
+
+ //
+ // Advance the address to the next record.
+ //
+ ui32Addr += HWREG(ui32Addr) & 0xFF;
+ }
+ else
+ {
+ //
+ // A record was not found so just advance to the next location in
+ // flash
+ //
+ ui32Addr += 4;
+ }
+ }
+
+ //
+ // If no "oldest" seconds was found, then there is no valid data stored
+ //
+ if(ui32OldestSeconds == 0xFFFFFFFF)
+ {
+ SetStatusText("SAVE", "NO RECORDS", "FOUND", "PRESS <");
+ return(1);
+ }
+
+ //
+ // Open the output file on the USB stick. It will return NULL if there
+ // was any problem.
+ //
+ if(!USBStickOpenLogFile(0))
+ {
+ SetStatusText("SAVE", 0, "USB ERROR", "PRESS <");
+ return(1);
+ }
+
+ //
+ // Notify user we are saving data to USB
+ //
+ SetStatusText("SAVE", "SAVING", "TO USB", 0);
+
+ //
+ // Start reading records from flash, start at the address of the oldest
+ // record, as found above. We scan through records, assuming the flash
+ // store is not corrupted. Continue scanning until a blank space is
+ // found which should indicate the end of recorded data, or until we
+ // have read all the records.
+ //
+ ui32Addr = ui32OldestRecord;
+ while(HWREG(ui32Addr) != 0xFFFFFFFF)
+ {
+ //
+ // If a record signature is found (which it should be), extract the
+ // record data and send it to USB stick.
+ //
+ if((HWREG(ui32Addr) & 0xFFFFFF00) == 0x53554100)
+ {
+ //
+ // Get byte count for this record
+ //
+ ui32Count = HWREG(ui32Addr) & 0xFF;
+
+ //
+ // Adjust the count and the address to remove the flash header
+ //
+ ui32Count -= 4;
+ ui32Addr += 4;
+
+ //
+ // Adjust for memory wrap
+ //
+ if(ui32Addr >= FLASH_STORE_END_ADDR)
+ {
+ ui32Addr = FLASH_STORE_START_ADDR;
+ }
+
+ //
+ // If the contents of this record go past the end of the memory
+ // storage area, then perform a partial copy first.
+ //
+ ui32PartialCount = 0;
+ if((ui32Addr + ui32Count) >= FLASH_STORE_END_ADDR)
+ {
+ //
+ // Find how many bytes are left on this page
+ //
+ ui32PartialCount = FLASH_STORE_END_ADDR - ui32Addr;
+
+ //
+ // Copy the portion until the end of memory store, adjust
+ // remaining count and address
+ //
+ memcpy(g_pui32RecordBuf, (void *)ui32Addr, ui32PartialCount);
+ ui32Count -= ui32PartialCount;
+ ui32Addr = FLASH_STORE_START_ADDR;
+ }
+
+ //
+ // Copy entire record (or remaining part of record if memory wrap)
+ // into record buffer
+ //
+ memcpy(&g_pui32RecordBuf[ui32PartialCount / 4], (void *)ui32Addr,
+ ui32Count);
+
+ //
+ // Update address pointer to next record
+ //
+ ui32Addr += ui32Count;
+
+ //
+ // Now we have an entire data logger record copied from flash
+ // storage into a local (contiguous) memory buffer. Pass it
+ // to the USB file writing function to write the record to the
+ // USB stick.
+ //
+ USBStickWriteRecord((tLogRecord *)g_pui32RecordBuf);
+ }
+ else
+ {
+ //
+ // This should not happen, but it means we ended up in a non-blank
+ // location that is not the start of a record. In this case just
+ // advance through memory until either a blank location or another
+ // record is found.
+ //
+ // Increment to next word in flash, adjust for memory wrap.
+ //
+ ui32Addr += 4;
+ if(ui32Addr >= FLASH_STORE_END_ADDR)
+ {
+ ui32Addr = FLASH_STORE_START_ADDR;
+ }
+ }
+ }
+
+ //
+ // Close the USB stick file so that any buffers will be flushed.
+ //
+ USBStickCloseFile();
+
+ //
+ // Inform user that save is complete.
+ //
+ SetStatusText("SAVE", "USB SAVE", "COMPLETE", "PRESS <");
+
+ //
+ // Return success
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+// This is called at the start of logging to prepare space in flash for
+// storage of logged data. It searches for the first blank area in the
+// flash storage to be used for storing records.
+//
+// If a starting address is specified then the search is skipped and it goes
+// directly to the new address. If the starting address is 0, then it performs
+// the search.
+//
+//*****************************************************************************
+int32_t
+FlashStoreOpenLogFile(uint32_t ui32StartAddr)
+{
+ uint32_t ui32Addr;
+
+ //
+ // If a valid starting address is specified, then just use that and skip
+ // the search below.
+ //
+ if((ui32StartAddr >= FLASH_STORE_START_ADDR) &&
+ (ui32StartAddr < FLASH_STORE_END_ADDR))
+ {
+ g_ui32StoreAddr = ui32StartAddr;
+ return(0);
+ }
+
+ //
+ // Start at beginning of flash storage area
+ //
+ ui32Addr = FLASH_STORE_START_ADDR;
+
+ //
+ // Search until a blank is found or the end of flash storage area
+ //
+ while((HWREG(ui32Addr) != 0xFFFFFFFF) && (ui32Addr < FLASH_STORE_END_ADDR))
+ {
+ //
+ // If a record signature is found, then increment to the next record
+ //
+ if((HWREG(ui32Addr) & 0xFFFFFF00) == 0x53554100)
+ {
+ ui32Addr += HWREG(ui32Addr) & 0xFF;
+ }
+ else
+ {
+ //
+ // Just advance to the next location in flash
+ //
+ ui32Addr += 4;
+ }
+ }
+
+ //
+ // If we are at the end of flash that means no blank area was found.
+ // So reset to the beginning and erase the first page.
+ //
+ if(ui32Addr >= FLASH_STORE_END_ADDR)
+ {
+ ui32Addr = FLASH_STORE_START_ADDR;
+ FlashErase(ui32Addr);
+ }
+
+ //
+ // When we reach here we either found a blank location, or made a new
+ // blank location by erasing the first page.
+ // To keep things simple we are making an assumption that the flash store
+ // is not corrupted and that the first blank location implies the start
+ // of a blank area suitable for storing data records.
+ //
+ g_ui32StoreAddr = ui32Addr;
+
+ //
+ // Return success indication to caller
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+// This is called each time there is a new data record to log to the flash
+// storage area. A simple algorithm is used which rotates programming
+// data log records through an area of flash. It is assumed that the current
+// page is blank. Records are stored on the current page until a page
+// boundary is crossed. If the page boundary is crossed and the new page
+// is not blank (testing only the first location), then the new page is
+// erased. Finally the entire record is programmed into flash and the
+// storage pointers are updated.
+//
+// While storing and when crossing to a new page, if the flash page is not
+// blank it is erased. So this algorithm overwrites old data.
+//
+// The data is stored in flash as a record, with a flash header prepended,
+// and with the record length padded to be a multiple of 4 bytes. The flash
+// header is a 3-byte magic number and one byte of record length.
+//
+//*****************************************************************************
+int32_t
+FlashStoreWriteRecord(tLogRecord *psRecord)
+{
+ uint32_t ui32Idx, ui32ItemCount, *pui32Record;
+
+ //
+ // Check the arguments
+ //
+ ASSERT(psRecord);
+ if(!psRecord)
+ {
+ return(1);
+ }
+
+ //
+ // Determine how many channels are to be logged
+ //
+ ui32Idx = psRecord->ui16ItemMask;
+ ui32ItemCount = 0;
+ while(ui32Idx)
+ {
+ if(ui32Idx & 1)
+ {
+ ui32ItemCount++;
+ }
+ ui32Idx >>= 1;
+ }
+
+ //
+ // Add 16-bit count equivalent of record header, time stamp, and
+ // selected items mask. This is the total number of 16 bit words
+ // of the record.
+ //
+ ui32ItemCount += 6;
+
+ //
+ // Convert the count to bytes, be sure to pad to 32-bit alignment.
+ //
+ ui32ItemCount = ((ui32ItemCount * 2) + 3) & ~3;
+
+ //
+ // Create the flash record header, which is a 3-byte signature and a
+ // one byte count of bytes in the record. Save it at the beginning
+ // of the write buffer.
+ //
+ ui32Idx = 0x53554100 | (ui32ItemCount & 0xFF);
+ g_pui32RecordBuf[0] = ui32Idx;
+
+ //
+ // Copy the rest of the record to the buffer, and get a pointer to
+ // the buffer.
+ //
+ memcpy(&g_pui32RecordBuf[1], psRecord, ui32ItemCount - 4);
+ pui32Record = g_pui32RecordBuf;
+
+ //
+ // Check to see if the record is going to cross a page boundary.
+ //
+ if(((g_ui32StoreAddr & 0x3FF) + ui32ItemCount) > 0x3FF)
+ {
+ //
+ // Find number of bytes remaining on this page
+ //
+ ui32Idx = 0x400 - (g_ui32StoreAddr & 0x3FF);
+
+ //
+ // Program part of the record on the space remaining on the current
+ // page
+ //
+ FlashProgram(pui32Record, g_ui32StoreAddr, ui32Idx);
+
+ //
+ // Increment the store address by the amount just written, which
+ // should make the new store address be at the beginning of the next
+ // flash page.
+ //
+ g_ui32StoreAddr += ui32Idx;
+
+ //
+ // Adjust the remaining bytes to program, and the pointer to the
+ // remainder of the record data.
+ //
+ ui32ItemCount -= ui32Idx;
+ pui32Record = &g_pui32RecordBuf[ui32Idx / 4];
+
+ //
+ // Check to see if the new page is past the end of store and adjust
+ //
+ if(g_ui32StoreAddr >= FLASH_STORE_END_ADDR)
+ {
+ g_ui32StoreAddr = FLASH_STORE_START_ADDR;
+ }
+
+ //
+ // If new page is not blank, then erase it
+ //
+ if(HWREG(g_ui32StoreAddr) != 0xFFFFFFFF)
+ {
+ FlashErase(g_ui32StoreAddr);
+ }
+ }
+
+ //
+ // Now program the remaining part of the record (if we crossed a page
+ // boundary above) or the full record to the current location in flash
+ //
+ FlashProgram(pui32Record, g_ui32StoreAddr, ui32ItemCount);
+
+ //
+ // Increment the storage address to the next location.
+ //
+ g_ui32StoreAddr += ui32ItemCount;
+
+ //
+ // Return success indication to caller.
+ //
+ return(0);
+}
+
+//*****************************************************************************
+//
+// Return the current address being used for storing records.
+//
+//*****************************************************************************
+uint32_t
+FlashStoreGetAddr(void)
+{
+ return(g_ui32StoreAddr);
+}
+
+//*****************************************************************************
+//
+// Erase the data storage area of flash.
+//
+//*****************************************************************************
+void
+FlashStoreErase(void)
+{
+ uint32_t ui32Addr;
+
+ //
+ // Inform user we are erasing
+ //
+ SetStatusText("ERASE", 0, "ERASING", 0);
+
+ //
+ // Loop through entire storage area and erase each page.
+ //
+ for(ui32Addr = FLASH_STORE_START_ADDR; ui32Addr < FLASH_STORE_END_ADDR;
+ ui32Addr += 0x400)
+ {
+ FlashErase(ui32Addr);
+ }
+
+ //
+ // Inform user the erase is done.
+ //
+ SetStatusText("SAVE", "ERASE", "COMPLETE", "PRESS <");
+}
+
+//*****************************************************************************
+//
+// Determine if the flash block that contains the address is blank.
+//
+//*****************************************************************************
+static int32_t
+IsBlockFree(uint32_t ui32BaseAddr)
+{
+ uint32_t ui32Addr;
+
+ //
+ // Make sure we start at the beginning of a 1K block
+ //
+ ui32BaseAddr &= ~0x3FF;
+
+ //
+ // Loop through every address in this block and test if it is blank.
+ //
+ for(ui32Addr = 0; ui32Addr < 0x400; ui32Addr += 4)
+ {
+ if(HWREG(ui32BaseAddr + ui32Addr) != 0xFFFFFFFF)
+ {
+ //
+ // Found a non-blank location, so return indication that block
+ // is not free.
+ //
+ return(0);
+ }
+ }
+
+ //
+ // If we made it to here then every location in this block is erased,
+ // so return indication that the block is free.
+ //
+ return(1);
+}
+
+//*****************************************************************************
+//
+// Report to the user the amount of free space and used space in the data
+// storage area.
+//
+//*****************************************************************************
+void
+FlashStoreReport(void)
+{
+ uint32_t ui32Addr, ui32FreeBlocks, ui32UsedBlocks = 0;
+ static char pcBufFree[16], pcBufUsed[16];
+
+ //
+ // Initialize locals.
+ //
+ ui32FreeBlocks = 0;
+ ui32UsedBlocks = 0;
+
+ //
+ // Loop through each block of the storage area and count how many blocks
+ // are free and non-free.
+ //
+ for(ui32Addr = FLASH_STORE_START_ADDR; ui32Addr < FLASH_STORE_END_ADDR;
+ ui32Addr += 0x400)
+ {
+ if(IsBlockFree(ui32Addr))
+ {
+ ui32FreeBlocks++;
+ }
+ else
+ {
+ ui32UsedBlocks++;
+ }
+ }
+
+ //
+ // Report the result to the user via a status display screen.
+ //
+ usnprintf(pcBufFree, sizeof(pcBufFree), "FREE: %3uK", ui32FreeBlocks);
+ usnprintf(pcBufUsed, sizeof(pcBufUsed), "USED: %3uK", ui32UsedBlocks);
+ SetStatusText("FREE FLASH", pcBufFree, pcBufUsed, "PRESS <");
+}