Database
Note: If you have downloaded version 4.0.9 or above of the SDFileSystem library module, this module is now redundant as the functionality has been added to the SD library.
This is a simple module to help with the building and use of database files on SD or MMC cards. It implements reading and writing records as structures. It utilises the SDFileSystem library module.
Note: As overwriting of existing files is required, the updated Version 2.0 (or above) of the library module must first be downloaded (latest version at http://www.sfcompiler.co.uk/wiki/pmwiki.php?n=SwordfishUser.SDFileSystemVersion).
The module contains five subroutines/functions:
- SetRecordLength - must be called before records are accessed, to declare the size of each record in the file. It should be equal in length to the size of the structure used to access each record. If bits or booleans are used, the number of these should be added and divided by 8, rounding up. 1 should be added for every byte, 2 for every word, 4 for every longword etc... If the structure contains another structure, then the size of this structures should be calculated first, rounding up to a whole number of bytes for each structue.
- GetRecord - gets a record from the file. The function fills a structure variable with the record. Note that the record number is zero-based. Note also that the address of the variable, and not the variable itself, must be passed as a parameter.
- SetRecord - sets a record in the file. The function writes the contents of a structure variable to the record. Note that the record number is zero-based. Note also that the address of the variable, and not the variable itself, must be passed as a parameter.
- NewRecord - creates a new record in the file. The function writes the contents of a structure variable to the record. Note that the address of the variable, and not the variable itself, must be passed as a parameter.
- NumberOfRecords - returns the number of records in the file.
The database file must first be opened for read/write, using the OpenFileRW command of the SDFileSystem library module. The file should also be closed at the end using the Close command. See the example code below for more details.
The example below also includes a routine for sorting the records in the database, in this case in ascending order of course name.
Example Code
{ ******************************************************************************** * Name : DatabaseExample.BAS * * Author : S Wright * * Notice : Copyright (c) 2007 S Wright * * : All Rights Reserved * * Date : 21/10/2007 * * Version : 1.0 * * Notes : * * : * ******************************************************************************** } Device = 18F452 Clock = 40 Config OSC = HSPLL Include "Convert.bas" Include "USART.bas" Include "SDFileSystem.bas" Include "Database.bas" Include "String.bas" Structure THole Yards As Word Par As Byte StrokeIndex As Byte End Structure Structure TCourse IsDeleted As Boolean Index As Byte Name As String(20) Hole(18) As THole End Structure Dim USARTResponse As String(2) { ******************************************************************************** * Name : InputCourse * ******************************************************************************** } Sub InputCourse() Dim Course As TCourse Dim HoleNumber As Byte Dim StringEntry As String(20) Course.IsDeleted = False Course.Index = DB.NumberOfRecords + 1 USART.Write("Enter Course Name:", 13, 10) USART.Read(StringEntry) Course.Name = StringEntry For HoleNumber = 0 To 17 USART.Write("Enter Yards for Hole ", DecToStr(HoleNumber + 1), ":", 13, 10) USART.Read(StringEntry) Course.Hole(HoleNumber).Yards = StrToDec(StringEntry) Next For HoleNumber = 0 To 17 USART.Write("Enter Par for Hole ", DecToStr(HoleNumber + 1), ":", 13, 10) USART.Read(StringEntry) Course.Hole(HoleNumber).Par = StrToDec(StringEntry) Next For HoleNumber = 0 To 17 USART.Write("Enter Stroke Index for Hole ", DecToStr(HoleNumber + 1), ":", 13, 10) USART.Read(StringEntry) Course.Hole(HoleNumber).StrokeIndex = StrToDec(StringEntry) Next DB.NewRecord(AddressOf(Course)) USART.Write(13, 10) End Sub { ******************************************************************************** * Name : OutputCourses * ******************************************************************************** } Sub OutputCourses() Dim Course As TCourse Dim CourseNumber As Byte Dim HoleNumber As Byte For CourseNumber = 0 To DB.NumberOfRecords() - 1 DB.GetRecord(CourseNumber, AddressOf(Course)) If Not Course.IsDeleted Then USART.Write("Course Index: ", DecToStr(Course.Index), 13, 10) USART.Write("Name: ", Course.Name, 13, 10) For HoleNumber = 0 To 17 USART.Write("Hole: ", DecToStr(HoleNumber + 1), ", Yards: ", DecToStr(Course.Hole(HoleNumber).Yards), ", Par: ", DecToStr(Course.Hole(HoleNumber).Par), ", StrokeIndex: ", DecToStr(Course.Hole(HoleNumber).StrokeIndex), 13, 10) Next USART.Write(13, 10) EndIf Next End Sub { ******************************************************************************** * Name : SortCourses * ******************************************************************************** } Sub SortCourses() Dim Course1, Course2 As TCourse Dim CourseNumber As Byte Dim SortOccurred As Boolean If DB.NumberOfRecords() > 1 Then Repeat SortOccurred = False For CourseNumber = 0 To DB.NumberOfRecords() - 2 DB.GetRecord(CourseNumber, AddressOf(Course1)) DB.GetRecord(CourseNumber + 1, AddressOf(Course2)) If Str.Compare(Course1.Name, Course2.Name) > 0 Then DB.SetRecord(CourseNumber, AddressOf(Course2)) DB.SetRecord(CourseNumber + 1, AddressOf(Course1)) SortOccurred = True EndIf Next Until Not SortOccurred EndIf usart.write("Courses Sorted:", 13, 10, 13, 10) End Sub // Program start... USART.SetBaudrate(br115200) USART.ReadTerminator = #13 DelayMS(100) USART.Write("Insert SD/MMC:", 13, 10) Repeat Until SD.Init(spiOscDiv4) If Not SD.FileExists("COURSES.SDB") Then USART.Write("New DB:", 13, 10) SD.NewFile("COURSES.SDB") SD.CloseFile EndIf SD.OpenFileRW("COURSES.SDB") DB.SetRecordLength(94) Repeat USART.Write("Enter Menu Option: 1 - Add course, 2 - Display all, 3 - Sort, 4 - Save file & exit", 13, 10, 13, 10) USART.Read(USARTResponse) Select USARTResponse Case "1" InputCourse() Case "2" OutputCourses() Case "3" SortCourses() Case "4" Break EndSelect Until False SD.CloseFile USART.Write("Completed:", 13, 10) DelayMS(1000) End
Module Code
{ ******************************************************************************** * Name : Database.BAS * * Author : S Wright * * Notice : Copyright (c) 2007 S Wright * * : All Rights Reserved * * Date : 21/10/2007 * * Version : 1.0 * * Notes : * * : * * Uses : FSR1 * * : * ******************************************************************************** } Module DB Include "SDFileSystem.bas" Dim RecordLength As Word { ******************************************************************************** * Name : SetRecordLength (PUBLIC) * * Purpose : Sets length of one record (in bytes) - must be called before * * : accessing any records * ******************************************************************************** } Public Sub SetRecordLength(pLength As Word) RecordLength = pLength End Sub { ******************************************************************************** * Name : GetRecord (PUBLIC) * * Purpose : Fills structure starting at pAddressOfStructure with record number * * : pRecordNumber (zero-based index). * * : Return: errOK = Success, errRWError = Read/Write error, * * : errFileNotOpen = File not open, errBeyondEOF = Trying to seek * * : position beyond end of file * ******************************************************************************** } Public Function GetRecord(pRecordNumber As Word, pAddressOfStructure As Word) As Byte Dim Index As Word Dim ByteRead As Byte Result = SD.FSeek(pRecordNumber * RecordLength) If Result = errOK Then Index = 0 Repeat ByteRead = SD.ReadByte FSR1 = pAddressOfStructure + Index POSTINC1 = ByteRead Inc(Index) Until Index = RecordLength EndIf End Function { ******************************************************************************** * Name : SetRecord (PUBLIC) * * Purpose : Fills record number pRecordNumber (zero-based index) with * * : structure starting at pAddressOfStructure. * * : Return: errOK = Success, errRWError = Read/Write error, * * : errFileNotOpen = File not open, errBeyondEOF = Trying to seek * * : position beyond end of file * ******************************************************************************** } Public Function SetRecord(pRecordNumber As Word, pAddressOfStructure As Word) As Byte Dim Index As Word Result = SD.FSeek(pRecordNumber * RecordLength) If Result = errOK Then Index = 0 Repeat FSR1 = pAddressOfStructure + Index SD.Write(POSTINC1) Inc(Index) Until Index = RecordLength EndIf End Function { ******************************************************************************** * Name : NewRecord (PUBLIC) * * Purpose : Creates new record and fills with pRecord. * * : Return: errOK = Success, errRWError = Read/Write error, * * : errFileNotOpen = File not open * ******************************************************************************** } Public Function NewRecord(pAddressOfStructure As Word) As Byte Dim Index As Word Result = SD.FSeek(SD.FileSize) If Result = errOK Then Index = 0 Repeat FSR1 = pAddressOfStructure + Index SD.Write(POSTINC1) Inc(Index) Until Index = RecordLength EndIf End Function { ******************************************************************************** * Name : NumberOfRecords (PUBLIC) * * Purpose : Returns the number of records in the database (ensure that * * : SerRecordLength has been called first) * ******************************************************************************** } Public Function NumberOfRecords() As Word Result = SD.FileSize / RecordLength End Function