[Tinyos-contrib-commits] CVS: tinyos-1.x/contrib/handhelds/apps/FAT_EKG ClockSet.nc, NONE, 1.1 DataFileInit.c, NONE, 1.1 DataFileRead.c, NONE, 1.1 FAT_EKG.nc, NONE, 1.1 FAT_EKG_M.nc, NONE, 1.1 Makefile, NONE, 1.1 PowerMonitor.nc, NONE, 1.1 SHIMMER2_SD_cardImage.zip, NONE, 1.1 SerialLink.nc, NONE, 1.1 crc16.h, NONE, 1.1 dostime.c, NONE, 1.1 dostime.h, NONE, 1.1 fileCatalog.txt, NONE, 1.1 plotEKG.m, NONE, 1.1 plotEKGdata.plt, NONE, 1.1 readme.doc, NONE, 1.1 readme.txt, NONE, 1.1 shimmerSerial.c, NONE, 1.1 shimmerSerial.h, NONE, 1.1
steve ayer
ayer1 at users.sourceforge.net
Tue Aug 19 11:46:14 PDT 2008
Update of /cvsroot/tinyos/tinyos-1.x/contrib/handhelds/apps/FAT_EKG
In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv17032
Added Files:
ClockSet.nc DataFileInit.c DataFileRead.c FAT_EKG.nc
FAT_EKG_M.nc Makefile PowerMonitor.nc
SHIMMER2_SD_cardImage.zip SerialLink.nc crc16.h dostime.c
dostime.h fileCatalog.txt plotEKG.m plotEKGdata.plt readme.doc
readme.txt shimmerSerial.c shimmerSerial.h
Log Message:
stephen linder's app using his fat-oriented sd driver to record data
--- NEW FILE: ClockSet.nc ---
// function prototypes
void force8MHzMasterClockAnd4MHzSBMCK ();
void enable8MHzMasterClockAnd4MHzSMCK ();
void force4MHzMasterClockAnd1MHzSMCK ();
void enable4MHzMasterClockAnd1MHzSMCK ();
bool MCLK_8MHz_SBMCK_4MHz_flag = FALSE;
//////////////////////////////////////////////////////////////////////////
// Change the master clock to XT2, an externeral 8 MHz crystal on the SHIMMER
// As of 11/2007 the default clock rate for the SHIMMER is 4 MHZ.
//
// constants are defined in mspgcc\msp430\include\msp430\basic_clock.h
//
// this has the negative side effect of making the serial connection to a
// host not work.
//////////////////////////////////////////////////////////////////////////
void force8MHzMasterClockAnd4MHzSBMCK () {
MCLK_8MHz_SBMCK_4MHz_flag = FALSE;
enable8MHzMasterClockAnd4MHzSMCK ();
}
void enable8MHzMasterClockAnd4MHzSMCK () {
uint32_t i;
uint16_t flags;
return;
if (MCLK_8MHz_SBMCK_4MHz_flag == TRUE) {
return;
}
MCLK_8MHz_SBMCK_4MHz_flag = TRUE;
// BCSCTL1
// .XT2OFF = 0; enable the external oscillator for SCLK and MCLK
// .XTS = 0; set low frequency mode for LXFT1
// .DIVA = 0; set the divisor on ACLK to 1
// .RSEL, do not modify
// clearing BCSCTL1 does not work possibly because of the RSEL bits
// being cleared.
atomic CLR_FLAG(BCSCTL1, XT2OFF);
// wait for the oscillator fault flag to clear
do {
atomic CLR_FLAG(IFG1, OFIFG);
for(i = 0; i < 0xff; i++);
atomic flags = READ_FLAG (IFG1, OFIFG);
} while ( flags );
i = 5000UL;
TOSH_uwait (i);
// select XT2CLK to get the crystal oscillator
// DIVM divider for master clock
// SELS select the source of the sub master clock
// DIVS set the SMCLK divider: 3 = 8, 2 = 4, 1 = 2, 0 = 1
// DCOR resister set to external or cleared for internal
// atomic CLR_FLAG(BCSCTL2, DIVM_DIV8);
// atomic SET_FLAG(BCSCTL2, DIVM_DIV2);
atomic CLR_FLAG(BCSCTL2, SELM_3);
atomic SET_FLAG(BCSCTL2, SELM_XT2CLK);
atomic CLR_FLAG(BCSCTL2, DIVS_DIV8);
atomic SET_FLAG(BCSCTL2, DIVS_DIV1);
// if we speed up the SMCLK we need to divide the input into the
// timerA
atomic CLR_FLAG(TACTL, ID1 + ID0);
atomic SET_FLAG(TACTL, ID_2); // ID_1 = /2 ID_2 = /4
// used to debug clock signals
// TinyOS will put the MSP430 to sleep when the task queue is empty.
// Adding an infinite loop here allows the probing of the
// clock signals with an osciliscope.
// while (1);
}
///////////////////////////////////////////////////////
void force4MHzMasterClockAnd1MHzSMCK () {
MCLK_8MHz_SBMCK_4MHz_flag = TRUE;
enable4MHzMasterClockAnd1MHzSMCK ();
}
void enable4MHzMasterClockAnd1MHzSMCK () {
uint32_t i;
uint16_t flags;
// if you are having problems with the crystal uncomment
// the following code and use the scope to see if the
// clock signals are correct. The XT2 external oscillator is not
// normally used and it might not be sodered on correctly.
//
// test the clock signals on the port 5 I/O pins
// Pin Number Name Connected to
// 50 P5.6/ACLK NC_RADIO_VREG_EN
// 49 P5.5/SMCLK BT_RESET
// 48 P5.4/MCLK SPI1_CS_RADIO_N
// 47 P5.3/UCLK1 SPI1_SCLK
///////////////////////////////////////////////////////////
// P5SEL |= 0x70; // set to peripherial function
// P5DIR |= 0x70; // set to output
if (MCLK_8MHz_SBMCK_4MHz_flag == FALSE) {
return;
}
MCLK_8MHz_SBMCK_4MHz_flag = FALSE;
// BCSCTL1
// .XT2OFF = 1; disables the external oscillator for SCLK and MCLK
// .XTS = 0; set low frequency mode for LXFT1
// .DIVA = 0; set the divisor on ACLK to 1
// .RSEL, do not modify
// clearing BCSCTL1 does not work possibly because of the RSEL bits
// being cleared.
atomic SET_FLAG(BCSCTL1, XT2OFF);
// wait for the oscillator fault flag to clear
do {
atomic CLR_FLAG(IFG1, OFIFG);
for(i = 0; i < 0xff; i++);
atomic flags = READ_FLAG (IFG1, OFIFG);
} while ( flags );
i = 5000UL;
TOSH_uwait (i);
// select XT2CLK to get the crystal oscillator
// DIVM divider for master clock
// SELS select the source of the sub master clock
// DIVS set the SMCLK divider: 3 = 8, 2 = 4, 1 = 2, 0 = 1
// DCOR resister set to external or cleared for internal
atomic CLR_FLAG(BCSCTL2, SELM_3);
atomic SET_FLAG(BCSCTL2, SELM_DCOCLK);
atomic CLR_FLAG(BCSCTL2, DIVS_DIV8);
atomic SET_FLAG(BCSCTL2, DIVS_DIV4);
// if we speed up the SMCLK we need to divide the input into the
// timerA
atomic CLR_FLAG(TACTL, ID1 + ID0);
atomic SET_FLAG(TACTL, ID_0); // ID_0 = /1 ID_2 = /4
}
--- NEW FILE: DataFileInit.c ---
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <time.h>
#include <math.h>
#include "shimmerSerial.h"
///////////////////////////////////////////////////////////////////////////////
// WARNING
// only works under cygwin because of the use of win32 drive letters.
//
///////////////////////////////////////////////////////////////////////////////
// FUNCTION PROTOTYPES of external funtions. Stops gcc from issuing warnings
int atoi( const char *str );
void exit(int status );
char* getDataDiskBlock( FILE* filePointer, uint blockNumber, char buffer[]);
void addDataFile (int sizeKB);
//#define _DEBUG_SERIAL
//////////////////////////////////////////////////////////////////////////
int main( int argc, char ** argv ) {
int sizeKb;
if (argc == 2) {
sizeKb = atoi(argv[1]);
} else {
fprintf (stderr, "Data file initializer - creates data file of specified length\n");
fprintf (stderr, "Usage %s <size in kB>\n", argv[0] );
exit (0);
}
addDataFile (sizeKb);
exit(0);
}
// add a data file to the disk
////////////////////////////////////////////////////
void addDataFile (int sizeKb) {
char filePathName[128];
char blockData[BLOCK_LENGTH];
int numberOfBlocks = sizeKb *1024 /BLOCK_LENGTH;
int i;
strcpy(filePathName, DATA_FILE_NAME);
// w+ open for reading and writing. Deletes content and overwrites the file.
fprintf (stderr, "Opening %s \n", filePathName);
FILE* filePointer = fopen(filePathName, "w+b");
if (filePointer == NULL) {
perror ("Unable to open file");
exit(0);
}
fprintf (stderr, "Initializing file to size of %i kB\n", sizeKb);
memset(blockData, 'x', sizeof(blockData) );
for (i = 0; i < numberOfBlocks; i++) {
sprintf (blockData, "Block Number: %i", i);
int charCount = fwrite(blockData, 1, sizeof(blockData), filePointer);
if (charCount != sizeof(blockData)){
fprintf (stderr, "Unable to write block to file %s", filePathName);
}
}
fprintf (stderr, "Closing file");
int error = fclose (filePointer);
if (error) {
perror ("Unable to close file.");
}
}
--- NEW FILE: DataFileRead.c ---
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <time.h>
#include <math.h>
#include "shimmerSerial.h"
#include "crc16.h"
// FUNCTION PROTOTYPES of external funtions. Stops gcc from issuing warnings
int atoi( const char *str );
void exit(int status );
void getAndPrintAllDataBlocks (FILE* filePointer);
void printADCdata( FILE* fd,
char* buffer, char* nextBuffer,
uint32_t* LinuxStartTimeSec,
uint32_t firstBlockShimmerTime,
uint32_t* LinuxBlockTimeSec,
BOOL* blockOutOfSequenceFlag);
int getDataDiskBlock( FILE* filePointer, uint blockNumber, char buffer[]);
void getHeaderBlock (FILE* filePointer,
dataCollectionStatus_t* status,
uint32_t* blocksToRead,
uint32_t* LinuxStartTimeSec,
uint16_t* samplePeriod,
double* durationOfBlock,
uint8_t* numberOfSamplesPerRecord );
void readDataFile ();
///////////////////////////////////////////////////////////////////////////////
// WARNING
// only works under cygwin because of the use of win32 drive letters.
//
///////////////////////////////////////////////////////////////////////////////
uint16_t SamplePeriod;
double DurationOfBlockSec;
uint8_t NumberOfSamplesPerRecord;
BOOL START_TIME_AT_BEGINNING_ZERO = FALSE;
//#define _DEBUG_SERIAL
//////////////////////////////////////////////////////////////////////////
int main( int argc, char ** argv ) {
fprintf (stderr,
"Shimmer Data File Read Version %s \n\n", SHIMMER_VERSION);
///////////////////////////////////////////////////////////////////
// error checking
HEADER_BLOCK_STRUCTURE headerBlock;
if (sizeof(headerBlock) != BLOCK_LENGTH) {
fprintf (stderr,
"\nERROR: header block structure length: %i, is not equal to block length: %i\n\n",
sizeof(headerBlock), BLOCK_LENGTH);
exit (0);
}
DATA_BLOCK_STRUCTURE dataBlock;
if (sizeof(dataBlock) != BLOCK_LENGTH) {
fprintf (stderr,
"\nERROR: data block structure length: %i, is not equal to block length: %i\n\n",
sizeof(dataBlock), BLOCK_LENGTH);
exit (0);
}
if (argc != 1) {
fprintf (stderr, "Reads binary data file and generates space delimited ASCII data file\n");
fprintf (stderr, "Usage %s >data.txt", argv[0] );
exit (0);
}
readDataFile ();
exit(0);
}
// read binary data from data file
////////////////////////////////////////////////////
void readDataFile () {
char filePathName[128];
strcpy(filePathName, DATA_FILE_NAME);
FILE* filePointer = fopen(filePathName, "rb");
if (filePointer == NULL) {
perror ("Unable to open file containing SHIMMER data");
exit(0);
}
getAndPrintAllDataBlocks (filePointer);
fprintf (stderr, "Closing file \n");
int error = fclose (filePointer);
if (error) {
perror ("Unable to close file.");
}
}
// get all the data blocks from Shimmer
////////////////////////////////////////////////////
void getAndPrintAllDataBlocks (FILE* filePointer) {
DATA_BLOCK_STRUCTURE block;
DATA_BLOCK_STRUCTURE nextBlock;
uint32_t i;
dataCollectionStatus_t status;
uint32_t blocksToRead;
uint32_t LinuxStartTimeSec;
uint32_t firstBlockShimmerTime;
uint32_t LinuxBlockTimeSec;
uint32_t experimentID;
BOOL ignoreBlocksToReadFlag;
BOOL timeStampOutOfSequence = FALSE;
#define BLOCK_MESSAGE_FREQUENCY 50.0
memset(&block, 1, sizeof(block));
getHeaderBlock(filePointer, &status, &blocksToRead, &LinuxStartTimeSec,
&SamplePeriod, &DurationOfBlockSec,
&NumberOfSamplesPerRecord);
if (NumberOfSamplesPerRecord + SamplePeriod + LinuxStartTimeSec == 0) {
fprintf (stderr,
"\n\nERROR: Header block indicates that no data was collected . \n" );
exit(0);
}
if (NumberOfSamplesPerRecord != NUMBER_OF_SAMPLES_PER_DATA_RECORD) {
fprintf (stderr,
"\n\n ERROR: Number of samples per data block does not match. \n" );
fprintf (stderr,
"Possible mismatch between embedded code on SHIMMER and server side software. \n" );
exit(0);
}
// need to subtract the timeStamp from the first block
// from all subsequent blocks
getDataDiskBlock(filePointer, FIRST_DATA_BLOCK_OFFSET, (char*) &block);
firstBlockShimmerTime = block.ShimmerTime;
LinuxBlockTimeSec = LinuxStartTimeSec ;
experimentID = LinuxStartTimeSec & 0xFFFF;
if (START_TIME_AT_BEGINNING_ZERO) {
LinuxStartTimeSec = 0;
}
if ( (status == nullStatus) ||
(blocksToRead == 0) ) {
ignoreBlocksToReadFlag = TRUE;
fprintf (stderr,
"\n\nHeader block indicates that the data collection was interrupted. \n" );
fprintf (stderr,
" Retriving data blocks that have a experminent ID of %d . \n\n",
(unsigned int)experimentID );
} else {
ignoreBlocksToReadFlag = FALSE;
}
i = FIRST_DATA_BLOCK_OFFSET;
// fprintf (stderr, "\nRequesting block %i to %i from Shimmer .",
// i, i + (int)BLOCK_MESSAGE_FREQUENCY - 2);
// fprintf (stderr, "\nRequesting data collect at %s .",
// ctime(&LinuxStartTimeSec));
fprintf (stderr, "\nRequesting DATA collected at %s .",
ctime(&LinuxBlockTimeSec));
getDataDiskBlock(filePointer, i, (char*) &block);
while ( i < FIRST_DATA_BLOCK_OFFSET + blocksToRead ||
(ignoreBlocksToReadFlag && experimentID == block.experimentID ) ) {
if(remainder((double)(i), BLOCK_MESSAGE_FREQUENCY) == 0.0 ) {
fprintf (stderr, "\nRequesting DATA collected at %s .",
ctime(&LinuxBlockTimeSec));
} else {
fprintf (stderr, ".");
}
// get the next block. Used to figure out time step
getDataDiskBlock(filePointer, i+1, (char*) &nextBlock);
if (experimentID != nextBlock.experimentID) {
nextBlock.experimentID = 0;
}
if (experimentID == block.experimentID) {
// side effect is to change timeOffset when there
// is a shimmer clock overflow and
// to set timeStampOutOf Sequence flag
printADCdata(stdout, (char*) &block, (char*)&nextBlock,
&LinuxStartTimeSec, firstBlockShimmerTime,
&LinuxBlockTimeSec,
&timeStampOutOfSequence);
} else if (block.experimentID != 0) {
fprintf (stderr,
"\nBlock ID does not match, Stopping data dump. \n\n");
/* // debugging code to dump block
fprintf (stderr, " Dumping block received...\n");
int i;
for (i = 0; i < BLOCK_LENGTH; i++) {
fprintf (stderr, "%2x ", (unsigned char)(((char*)&block) [i]));
if(remainder((double)(i + 1), 16.0) == 0.0) {
fprintf (stderr, " \n");
}
}
fprintf (stderr, " \n");
*/
// return;
} else {
fprintf (stderr,
"\nBlock ID of block %i is zero. Continuing data dump. \n\n",
(unsigned int)i);
}
if(timeStampOutOfSequence) {
fprintf (stderr,
"\nTime stamp of block %i is out of sequence. Continuing data dump. \n\n",
(unsigned int)i);
}
block = nextBlock;
i++;
}
fprintf (stderr, "\n");
}
// get header block from Shimmer
////////////////////////////////////////////////////
void getHeaderBlock (FILE* filePointer,
dataCollectionStatus_t* status,
uint32_t* blocksToRead,
uint32_t* LinuxStartTimeSec,
uint16_t* samplePeriod,
double* durationOfBlock,
uint8_t* numberOfSamplesPerRecord ) {
HEADER_BLOCK_STRUCTURE header;
memset(&header, 1, sizeof(header));
long byteAddress = (HEADER_BLOCK_OFFSET) * BLOCK_LENGTH;
rewind(filePointer);
size_t len;
if(fseek(filePointer, byteAddress, SEEK_SET)) {
perror ("Unable to seek to data block");
exit(0);
} else {
len = fread((char*) &header, 1, BLOCK_LENGTH, filePointer);
}
// blocks written includes the header block
*status = header.dataCollectionStatus;
*blocksToRead = header.numberOfBlocksWritten - 1;
*LinuxStartTimeSec = header.LinuxStartTimeSec;
*samplePeriod = header.samplePeriod;
*numberOfSamplesPerRecord = header.numberOfSamplesPerDataRecord;
*durationOfBlock = ((double) (header.numberOfSamplesPerDataRecord *
header.samplePeriod) /
(double)TINYOS_TIMER_TICS_SEC);
if (LinuxStartTimeSec == 0) {
fprintf (stderr, "ERROR: bad start time. Try again.\n\n");
exit(0);
}
if(strcmp(header.ShimmerVer, SHIMMER_VERSION) ) {
fprintf (stderr, "\n WARNING SOFTWARE VERSIONS DO NOT MATCH.\n");
fprintf (stderr, "\nShimmer firmware: %s, PC software: %s \n",
header.ShimmerVer, SHIMMER_VERSION );
fprintf (stderr, " Data fields are possibly misaligned. Check if data looks reasonable.\n\n");
}
fprintf (stderr, "Shimmer Type: %s \n",
header.ShimmerType);
fprintf (stderr, "Release Date: %s \n",
header.ShimmerDate);
fprintf (stderr, "Shimmer ID: %s \n",
header.ShimmerID );
fprintf (stderr, "Shimmer Status: %s \n",
STATUS_MESSAGES[*status]);
fprintf (stderr, "Starting time of data collection: %s",
ctime(LinuxStartTimeSec));
fprintf (stderr, "Sampling period: %i, samples per record %i \n",
header.samplePeriod, header.numberOfSamplesPerDataRecord );
fprintf (stderr,
"SHIMMER collected %d minutes of data in %d blocks. \n",
(unsigned int)(*blocksToRead * DurationOfBlockSec / 60),
(unsigned int)*blocksToRead);
// fprintf (stderr, "\n");
}
// write the ADC data to the file descriptor
// Format of each line
// time x y z
// the separator between fields is given by SEPARATOR
//
// main task is keeping track of time.
// zero the shimmer time - the shimmer time of the first block is really zero
// overflows - shimmer time overflows.
////////////////////////////////////////////////////
void printADCdata( FILE* fd,
char* buffer, char* nextBuffer,
uint32_t* LinuxStartTimeSec,
uint32_t firstBlockShimmerTime,
uint32_t* LinuxBlockTimeSec,
BOOL* blockOutOfSequenceFlag) {
char SEPARATOR[] = " ";
int i;
SAMPLE_STRUCTURE* samples;
static double deltaTime;
static double clockInverted = 1.0 / ((double) TINYOS_CLOCK_TICS_SEC);
// number of seconds that can be counted before the TinyOS clock overflows
// 2^32 / 2^15 = 2^17 = 131072
static double shimmerClockOverflowPeriodSec = 131072;
double shimmerTimeSec;
static double oldShimmerTimeSec = 0;
DATA_BLOCK_STRUCTURE* block = (DATA_BLOCK_STRUCTURE*) buffer;
DATA_BLOCK_STRUCTURE* nextBlock = (DATA_BLOCK_STRUCTURE*) nextBuffer;
// must figure out the actual sampling interval.
// we will do this by using the time stamps from successive records and
// then dividing by NumberOfSamplesPerRecord
////////////////////////////////////////////////////////////////////////////
if ((*nextBlock).experimentID == 0) {
deltaTime = floor( (double)(SamplePeriod * TINYOS_CLOCK_TICS_SEC) / 1000.0);
deltaTime = deltaTime / (double)TINYOS_CLOCK_TICS_SEC;
} else {
deltaTime = (((*nextBlock).ShimmerTime - (*block).ShimmerTime) * clockInverted) /
NumberOfSamplesPerRecord;
}
#ifdef _DEBUG_SERIAL
FILE *fdi = fopen("foofoo.exe", "w");
if(fdi > 0) {
fwrite (buffer, 1, BLOCK_LENGTH, fdi);
}
fclose(fdi);
#endif
shimmerTimeSec = (double) ((*block).ShimmerTime - firstBlockShimmerTime) * clockInverted;
// check for overflow
// if overflow we need to add time to clock offset
// this is hard to test because it only happens after 36.4 hours
double shimmerTimeDifferenceBetweenBlocks = shimmerTimeSec - oldShimmerTimeSec;
if (shimmerTimeDifferenceBetweenBlocks < 0) {
*LinuxStartTimeSec += shimmerClockOverflowPeriodSec;
}
oldShimmerTimeSec = shimmerTimeSec;
double blockStartTime = (double)(*LinuxStartTimeSec) + shimmerTimeSec;
*LinuxBlockTimeSec = (uint32_t)blockStartTime;
// keep track of start times of blocks to insure that blocks are insequence
// initialze to time of first block
static double lastBlockStartTime = -1.0;
if (lastBlockStartTime == -1.0) {
lastBlockStartTime = blockStartTime;
}
double difference = (blockStartTime - lastBlockStartTime);
if ( difference > 2.0* DurationOfBlockSec ) {
*blockOutOfSequenceFlag = TRUE;
lastBlockStartTime = blockStartTime;
fprintf (stderr, "difference: %f block duration: %f\n",
difference, DurationOfBlockSec );
return;
} else {
lastBlockStartTime = blockStartTime;
*blockOutOfSequenceFlag = FALSE;
}
// fprintf (stderr, " shimmer time sec: %f \n", shimmerTimeSec);
// fprintf (stderr, " start time: %f \n\n", startTime);
samples = &((*block).data[0]);
for (i = 0; i < NumberOfSamplesPerRecord; i++) {
fprintf(fd, "%14.6f", blockStartTime + (double)i * deltaTime);
fprintf(fd, SEPARATOR);
fprintf(fd, "%5i", samples[i].EKG);
fprintf(fd, SEPARATOR);
fprintf(fd, "%5i", samples[i].x);
fprintf(fd, SEPARATOR);
fprintf(fd, "%5i", samples[i].y);
fprintf(fd, SEPARATOR);
fprintf(fd, "%5i", samples[i].z);
fprintf(fd, "\n");
}
}
// get block of data from Shimmer
////////////////////////////////////////////////////
int getDataDiskBlock( FILE* filePointer, uint blockNumber, char buffer[]) {
long byteAddress = blockNumber * BLOCK_LENGTH;
// to speed things up this should probably
// be only done for first block in sequence
rewind(filePointer);
size_t len;
if(fseek(filePointer, byteAddress, SEEK_SET)) {
perror ("Unable to seek to data block");
exit(0);
} else {
len = fread(buffer, 1, BLOCK_LENGTH, filePointer);
}
// verify that the block has not been corrupted.
// This is mostly a check of whether there has been a problem with the SD card.
// Some SD cards do seem to work well with SHIMMER. If the SHIMMER was unable
// to get a block from the SD card it will light the red LED and send a block of
// junk data
DATA_BLOCK_STRUCTURE* block = (DATA_BLOCK_STRUCTURE*)buffer;
uint16_t blockCRC = CRC16( buffer, BLOCK_LENGTH-2);
if ( (block -> CRC) != blockCRC ) {
fprintf (stderr, "ERROR: SD block has bad CRC. Block read failed. \n");
fprintf (stderr, "Possible bad SD card or we have read past end of data. \n");
exit(0);
}
return len;
}
--- NEW FILE: FAT_EKG.nc ---
// $Id: FAT_EKG.nc,v 1.1 2008/08/19 18:46:11 ayer1 Exp $
/*
* Copyright (c) 2007, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Stephen Paul Linder
* May 2008
*/
configuration FAT_EKG {
}
implementation {
components Main,
FAT_EKG_M,
PowerSupplyMonitorC,
TimerC,
TimerJiffyAsyncC,
HPLUARTC,
HPLUSART0M,
LedsC,
DMA_M,
MMA7260_AccelM,
DS2411LiteM, // module \tos\system\DS2411LiteM.nc
RovingNetworksC,
SD_DMA_C,
FAT_SD_C;
// if you want to have the init, start and stop functions of the StdControl
// interface to be called automatically you must wire them here.
// Because the SD card interface was originally not wired I developed all the
// code with out using this feature SPL 2-25-08
Main.StdControl -> FAT_EKG_M;
Main.StdControl -> TimerC;
Main.StdControl -> SD_DMA_C;
FAT_EKG_M.PSMStdControl -> PowerSupplyMonitorC;
FAT_EKG_M.PowerSupplyMonitor -> PowerSupplyMonitorC;
FAT_EKG_M.blinkRunAndPowerLEDTimer -> TimerC.Timer[unique("Timer")];
FAT_EKG_M.blinkGreenTimer -> TimerC.Timer[unique("Timer")];
FAT_EKG_M.retryDataCollectionTimer -> TimerC.Timer[unique("Timer")];
FAT_EKG_M.sampleADC_JiffyTimer -> TimerJiffyAsyncC;
FAT_EKG_M.sampleADC_JiffyTimerAsyncControl -> TimerJiffyAsyncC.StdControl;
FAT_EKG_M.IDChip -> DS2411LiteM.IDChip;
FAT_EKG_M.HPLUART -> HPLUARTC;
FAT_EKG_M.USARTControl -> HPLUSART0M;
FAT_EKG_M.Leds -> LedsC;
FAT_EKG_M.LocalTime -> TimerC;
FAT_EKG_M.SD -> SD_DMA_C;
FAT_EKG_M.FAT -> FAT_SD_C;
FAT_EKG_M.DMA0 -> DMA_M.DMA[0];
FAT_EKG_M.AccelStdControl -> MMA7260_AccelM;
FAT_EKG_M.Accel -> MMA7260_AccelM;
FAT_EKG_M.BTStdControl -> RovingNetworksC;
FAT_EKG_M.Bluetooth -> RovingNetworksC;
}
--- NEW FILE: FAT_EKG_M.nc ---
/*
* Copyright (c) 2007,2008, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Stephen Linder
* 2007-2008
*****************************************************************************
This code is based on FAT_DataLogger was never publically released.
Samples four channels of ADC data upto 1 KHz while archiving data on SD card.
This was achieved by
--- removing depencies on DMA channel for ADC conversion. It takes 12 msec
to write a block to the SD card using SD_DMA. Because the SD module uses
the DMA channel this starves the DMA used in transfering data from the ADC.
The older sd_m.nc module takes 60 msec.
The MSP430 only has 1 DMA channel. That channel has three sets of registers.
Only one channel at a time can run.
--- The timer used to schedule the sampling of the ADC was switched from
TimerC.Timer to TimerJiffyAsyncC. TimerC uses a task to update the
software timers. SInce only one task runs at a time, the SD card
task starved the software timers.
The jiffy timer does not use a task but it appears that only one can be instantiated.
The timer interrupt handler directly calls the sampleADC_JiffyTimer.fired()
interrupt handler.
--- Removed an atomic statement from SD_DMA_M.nc that starved the interrupts.
Features added are
--- Command line switch to shimmerSerial.c that lets you set the sampling period.
--- Modifided DataFileRead.c so time stamp for each sample is calculate by extrapolating
the time stamps between successive blocks. Before I was using the actual time in clock
ticks. There is enough of a latency in handling timer interrupts that a block of 63
samples took two milliseconds longer to collect than the clock rate would indicate.
This is because the Jiffy timer does not have a repeat mode. The next compare
value was be loaded into the timer register for every interrupt.
--- Collect data from EKG(ECG) card. Connect a Polar chest strap to the red and black
connectors. Data is collected from ADC 7
--- Toggle SER0_CTS during the sample interrupt so a user can confirm sampling timing.
--- Data can be streamed over Bluetooth by setting ENABLE_BLUETOOTH_DATA_TRANSMISSION.
Data format is set in sendDataSetViaBluetooth(), and is currently ASCII
Testing
-------
--- Test timing of sampling interrupt using a logic analyzer. When sampling at 1 k Hz
the jitter seems to be in the 10 usec range. No discernible gitter occurred because
of saving data to the SD card.
--- used a FLUKE EKG simulator to test SHIMMER with an EKG daughter card. Visual inspection looked
good for data collected at 1 Khz.
Known Issues
------------
--- There maybe a bug in the DataFileRead code that calculate the time stamps. I have only
seen it once.
*****************************************************************************
Revision History
*****************************************************************************
June 5, 2008
Change code around so data is logged when ever the SHIMMER2 is removed from the docking
station. If you would like to disable this behavior, comment out the call to
startRecordingDataTaskRequest() in the function StdControl.start()
Since the header is not sent from the host PC the data will not have some variables
initialized including the start time.
Instead of using a default of 0 for the start time and the record ID we now read the 16-bit
shimmer clock for the default time and record ID.
*/
includes msp430baudrates;
includes DMA;
includes PowerSupplyMonitor;
includes crc; // error checking CRC
includes SD_DMA;
includes FAT_SD;
module FAT_EKG_M {
provides {
interface StdControl;
}
uses {
interface PowerSupplyMonitor;
interface Leds;
interface Timer as blinkRunAndPowerLEDTimer;
interface Timer as blinkGreenTimer;
interface Timer as retryDataCollectionTimer;
interface TimerJiffyAsync as sampleADC_JiffyTimer;
interface StdControl as sampleADC_JiffyTimerAsyncControl;
interface HPLUART;
interface HPLUSARTControl as USARTControl;
interface LocalTime;
interface SD as SD;
interface FAT_SD as FAT;
interface StdControl as AccelStdControl;
interface StdControl as PSMStdControl;
interface MMA7260_Accel as Accel;
interface DMA as DMA0;
interface IDChip;
interface StdControl as BTStdControl;
interface Bluetooth;
}
}
implementation {
// if placed by the other includes an error is generated
// In file included from KinematicsM.nc:114:
// shimmerSerial.h:17: parse error before `int'
#include "shimmerSerial.h"
#include "math.h"
#include "SD_DMA.h" // won't compile without this. Not sure why.
// it is also include above!
#include "ClockSet.nc"
#include "PowerMonitor.nc"
// function prototypes
extern int sprintf(char *str, const char *format, ...) __attribute__ ((C));
void initSerialLinkToHost();
void writeHeaderBlock (dataCollectionStatus_t status);
void startRecordingDataTaskRequest();
task void startRecordingDataTask();
task void writeSDblock();
void blinkGreenLED ();
void initADC();
void startADCconversion ();
void sendDataSetViaBluetooth (uint16_t* pData );
// two buffers used to save data from ADC
DATA_BLOCK_STRUCTURE dataBuffer0, dataBuffer1;
DATA_BLOCK_STRUCTURE* currentDataBufferPtr;
uint32_t dataBufferTimeStamp; // time stamp for current data buffer
uint8_t dataBlockTransferCount; // how many blocks ADC channels transfered
uint16_t* dataTransferPtr;
uint16_t* ADCdataRegisterPtr;
uint16_t sampleIntervalJiffies;
HEADER_BLOCK_STRUCTURE headerBlock; // used to save parameters to SD card
uint32_t blocksWrittenToSD;
uint16_t experimentID;
uint8_t ShimmerID[6];
// FAT file system access to data file uses these
uint32_t DataFile_StartBlock;
uint32_t DataFile_LengthInBlocks;
FILE DataFile;
bool CollectingandRecordingDataFlag = FALSE;
bool RequestDataCollectionFlag = FALSE;
bool BluetoothReady;
char BluetoothMessageBuffer[60];
#include "SerialLink.nc"
///////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////
// standard interface
//////////////////////////////////////////////////////
command result_t StdControl.init() {
call Leds.init();
call AccelStdControl.init();
force4MHzMasterClockAnd1MHzSMCK ();
initSerialLinkToHost();
///////////////////////////////////////////////////////////////////
// SPL June 2, 2008
// WARNING When using the SHIMMER AnEx board with SHIMMER2 the
// power detection monitor is triggered. Have not explored why.
///////////////////////////////////////////////////////////////////
powerSupplyMonitorInit ();
call IDChip.read(ShimmerID);
SerialLinkInit ();
call retryDataCollectionTimer.stop();
if (ENABLE_BLUETOOTH_DATA_TRANSMISSION == TRUE) {
call BTStdControl.init ();
}
TOSH_MAKE_SER0_CTS_OUTPUT(); // debugging output on AnEx board
TOSH_SEL_SER0_CTS_IOFUNC();
return SUCCESS;
}
//////////////////////////////////////////////////////////////////////////
command result_t StdControl.start() {
powerSupplyMonitorStart ();
// putting this in command StdControl.init() does not work
call blinkRunAndPowerLEDTimer.start(TIMER_ONE_SHOT, 10);
if (ENABLE_BLUETOOTH_DATA_TRANSMISSION == TRUE) {
call BTStdControl.start ();
}
// data will be recorded once the SHIMMER2 is undocked.
startRecordingDataTaskRequest();
return SUCCESS;
}
//////////////////////////////////////////////////////////////////////////
// Shut down Shimmer in a low power mode so battery does not drain.
//
// On a Shimmer without a Bluetooth module or SD card the power drain
// reduces to 45 micro amps. On a Shimmer with Bluetooth(not running)
// and a 128 megabyte SD card the power drain reduces from 3.9 milliamps.
// 250 micro amps. Removing the SD card further reduces it to 162 micro amps
//////////////////////////////////////////////////////////////////////////
command result_t StdControl.stop() {
call HPLUART.stop();
call sampleADC_JiffyTimerAsyncControl.stop();
call AccelStdControl.stop();
call DMA0.ADCstopConversion();
call Leds.greenOff();
call Leds.yellowOff();
call Leds.redOff();
call BTStdControl.stop ();
powerSupplyMonitorStop ();
return SUCCESS;
}
//////////////////////////////////////////////////////////////////////
// start RECORDING data from ADC channels
// Initialize
// the two buffers that are used,
// the accelerometer
// the ADC channels,
// the timer that is used to initiate conversions
//
//////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
event result_t retryDataCollectionTimer.fired() {
call Leds.redOff();
call Leds.greenOff();
post startRecordingDataTask();
return SUCCESS;
}
void startRecordingDataTaskRequest() {
atomic RequestDataCollectionFlag = TRUE;
post startRecordingDataTask ();
}
task void startRecordingDataTask() {
bool tempFlag;
double tempDouble;
call blinkRunAndPowerLEDTimer.stop();
call Leds.greenOn();
call Leds.yellowOff();
// if already collecting data we should
// not start over again
atomic tempFlag = CollectingandRecordingDataFlag;
if (tempFlag == TRUE) {
return;
}
if(!TOSH_READ_DOCK_N_PIN()){
call retryDataCollectionTimer.start(TIMER_ONE_SHOT, 1000);
return; // unavailable SD card
} else {
// only start data collection if previously requested
atomic tempFlag = RequestDataCollectionFlag;
if (tempFlag == FALSE) {
return;
}
}
if (call FAT.init() != MMC_SUCCESS) {
// could not initialize
// SD card. Need to try again later.
call retryDataCollectionTimer.start(TIMER_ONE_SHOT, 1000);
call Leds.redOn();
return;
}
atomic {
CollectingandRecordingDataFlag = TRUE;
RequestDataCollectionFlag = FALSE;
}
force4MHzMasterClockAnd1MHzSMCK ();
blocksWrittenToSD = 0;
// data record blinks the green LED so we don't need a
// run LED blinking
call blinkRunAndPowerLEDTimer.stop();
if (call FAT.fopen(DATA_FILE_NAME, "w", &DataFile) != MMC_SUCCESS) {
// file could not be open.
// need to figure out an error code
// no point in retrying
call Leds.redOn();
return;
}
DataFile_StartBlock = DataFile.startBlock;
DataFile_LengthInBlocks = DataFile.lengthInBlocks;
call FAT.setDateTime(&DataFile,
headerBlock.DOS_date,
headerBlock.DOS_time);
// save lower 16-bits of the start time as an ID stamp for every
// data block except when start time is zero because time has not been set.
//------------------------------------------------------------------------
if ((headerBlock.LinuxStartTimeSec & 0xFFFF0000) == 0) {
experimentID = call LocalTime.read();
headerBlock.LinuxStartTimeSec = experimentID;
} else {
experimentID = (uint16_t) (headerBlock.LinuxStartTimeSec) & 0xFFFF;
}
strncpy(headerBlock.ShimmerID, "Data collection started without initialization", 46);
// timer period is sent over in the hearder block from the host.
// if not set to a positive number than use default
if ( headerBlock.samplePeriod <= 0) {
headerBlock.samplePeriod = ADC_TIMER_PERIOD_MSEC;
}
headerBlock.numberOfSamplesPerDataRecord = NUMBER_OF_SAMPLES_PER_DATA_RECORD;
headerBlock.unused[0] = 0;
// Periodically fills buffer with ADC data
tempDouble = ((double) headerBlock.samplePeriod *
(double)TINYOS_CLOCK_TICS_SEC) / 1000.0;
atomic sampleIntervalJiffies = (uint32_t) tempDouble;
writeHeaderBlock (nullStatus);
blocksWrittenToSD = FIRST_DATA_BLOCK_OFFSET;
// Set up the two record buffers.
// We alternate between the two. While one is being written too
// the other is being saved to the SD card (or where ever)
memset(&dataBuffer0, 1, sizeof(dataBuffer0) ); // uses byte transfers
memset(&dataBuffer1, 2, sizeof(dataBuffer1) );
// ENABLE DATA SOURCES
// Set up accelerometer
call AccelStdControl.start();
call Accel.setSensitivity(SHIMER_ACCELEROMETER_GAIN);
// set up pins to EKG
TOSH_MAKE_ADC_1_INPUT(); // ecg 1
TOSH_MAKE_ADC_2_INPUT(); // ecg 2
TOSH_SEL_ADC_1_MODFUNC(); // module function
TOSH_SEL_ADC_2_MODFUNC();
atomic {
currentDataBufferPtr = &dataBuffer0;
dataBlockTransferCount = 0;
dataTransferPtr = (uint16_t*) currentDataBufferPtr->data;
ADCdataRegisterPtr = (uint16_t*) &ADC12MEM0;
}
// Starts the ADC
initADC();
// Periodically starts ADC
call sampleADC_JiffyTimer.setOneShot(sampleIntervalJiffies);
}
//////////////////////////////////////////////////////////////////////////
// Stop recording data to the SD card and switch to monitoring the
// serial port to the host computer. The same MSP430 hardware is used for
// both serial transfers
//
// Last thing written to the card is a header block with the
// number of block written to the SD card. Look at shimmerSerial.h for the
// the complete structure.
//////////////////////////////////////////////////////////////////////////
task void stopRecordingData() {
bool tempFlag;
tempFlag = CollectingandRecordingDataFlag;
if ( tempFlag == TRUE) {
writeHeaderBlock(completedNormal);
atomic CollectingandRecordingDataFlag = FALSE;
}
call blinkGreenTimer.stop();
call sampleADC_JiffyTimerAsyncControl.stop();
// use this to enable faster download speed.
// This only works on SHIMMER1. SHIMMER2 blocks access to the SD card
// while SHIMMER2 is docked.
enable8MHzMasterClockAnd4MHzSMCK ();
initSerialLinkToHost();
call blinkRunAndPowerLEDTimer.start(TIMER_ONE_SHOT, 10);
}
void writeHeaderBlock (dataCollectionStatus_t status) {
uint8_t *ptr;
uint16_t crc_fsc, i;
// update file marker with blocks written
headerBlock.numberOfBlocksWritten = blocksWrittenToSD;
headerBlock.dataCollectionStatus = status;
strncpy(headerBlock.ShimmerVer, SHIMMER_VERSION, HEADER_VERSION_LENGTH);
strncpy(headerBlock.ShimmerType, SHIMMER_TYPE, HEADER_TYPE_LENGTH);
strncpy(headerBlock.ShimmerDate, SHIMMER_DATE, HEADER_DATE_LENGTH);
// calc CRC16 of data, excluding the last two bytes that hold
// the CRC
crc_fsc = 0;
for (ptr = (uint8_t *) &headerBlock, i = 0;
i < BLOCK_LENGTH - 2;
i++, ptr++) {
crc_fsc = crcByte(crc_fsc, *ptr);
}
headerBlock.CRC = crc_fsc;
call SD.writeBlock(DataFile_StartBlock + HEADER_BLOCK_OFFSET,
(uint8_t *)&headerBlock);
}
//////////////////////////////////////////////////////////////////////////
// Sets up the ADC to sample multiple channels
//
// Sets sample hold time
// reference voltage
// channels are mapped to registers(0, 1 2 ...)
//////////////////////////////////////////////////////////////////////////
void initADC() {
// multiple conversions without more triggers
// SHT0_0 sample and hold time 4 adc12clk cycles for first eight registers
atomic ADC12CTL0 = ADC12ON + SHT0_0 + MSC ;
// s&h from adc12sc bit;
// sample from sampling timer;
ADC12CTL1 = SHS_0 + SHP;
ADC12CTL1 |= ADC12SSEL_3; // clk from smclk
SET_FLAG(ADC12CTL1, ADC12DIV_7);
// conversion start address
SET_FLAG(ADC12CTL1, CSTARTADD_0); // really a zero, for clarity
// set up ADC channels for consecutive conversion
SET_FLAG(ADC12CTL1, CONSEQ_1);
// set reference to internal analog voltage AVcc
// this is different than using the external pin
CLR_FLAG(ADC12MCTL0, SREF_7);
CLR_FLAG(ADC12MCTL1, SREF_7);
CLR_FLAG(ADC12MCTL2, SREF_7);
CLR_FLAG(ADC12MCTL3, SREF_7);
SET_FLAG(ADC12MCTL0, INCH_2); // EKG
SET_FLAG(ADC12MCTL1, INCH_5); // accel x
SET_FLAG(ADC12MCTL2, INCH_4); // accel y
SET_FLAG(ADC12MCTL3, INCH_3); // accel z
SET_FLAG(ADC12MCTL3, EOS); // sez "this is the last reg"
// enable interrupts when last channel is converted
ADC12IE = 1 << (NUMBER_OF_ADC_CHANNELS - 1);
}
void startADCconversion () {
ADC12CTL0 |= ADC12SC + ENC;
}
//////////////////////////////////////////////////////////////////////////
// time to take another sample...
//////////////////////////////////////////////////////////////////////////
async event result_t sampleADC_JiffyTimer.fired() {
call sampleADC_JiffyTimer.setOneShot(sampleIntervalJiffies);
// toggle pin on AnEx board
TOSH_TOGGLE_SER0_CTS_PIN ();
startADCconversion ();
return SUCCESS;
}
//////////////////////////////////////////////////////////////////////////
// We should *not* see this, as the adc interrupts should go to the
// adc interrupt!
//////////////////////////////////////////////////////////////////////////
async event void DMA0.transferComplete() {
// call Leds.redOn();
}
//////////////////////////////////////////////////////////////////////////
// Called when the data from the ADC conversions have completed.
// May 30 2008 SPL
// modified from routine that handled the DMA interrupt from when the
// ADC conversion used DMA channel to transfer data from registers to
// memory buffer
//
// WARNING: Must read the data from the channel generating the interrupt
// or new interrupts are not generated. Seems to even block timer interrupts.
//////////////////////////////////////////////////////////////////////////
async event void DMA0.ADCInterrupt(uint8_t regnum) {
uint16_t localTransferCount;
int i;
atomic {
dataBlockTransferCount++;
localTransferCount = dataBlockTransferCount;
}
// check to see if there is enough room for another set
// of samples in the buffer
// if not swap active data buffers,
// reset destination pointer to start of new buffer
// reset count that keeps track of the number of buffers
// post task that writes buffer to SD card
//////////////////////////////////////////////////////////////////////
if ( (localTransferCount)
> NUMBER_OF_SAMPLES_PER_DATA_RECORD ) {
atomic {
if (currentDataBufferPtr == &dataBuffer0) {
currentDataBufferPtr = &dataBuffer1;
}
else {
currentDataBufferPtr = &dataBuffer0;
}
dataTransferPtr = (uint16_t*)currentDataBufferPtr->data;
dataBlockTransferCount = 1; // the first write will happen before exiting
dataBufferTimeStamp = call LocalTime.read();
} // end atomic
if (ENABLE_BLUETOOTH_DATA_TRANSMISSION != TRUE) {
post writeSDblock();
}
}
// copy data from ADC registers to current data buffer
// this assumes all data is consecutive
////////////////////////////////////////////////////
for (i = 0; i < NUMBER_OF_ADC_CHANNELS; i++) {
*dataTransferPtr++ = *(ADCdataRegisterPtr + i);
}
// send latest data over bluetooth to host
////////////////////////////////////////////////////
if (ENABLE_BLUETOOTH_DATA_TRANSMISSION == TRUE) {
// dataTransferPtr should be pointing to the start of the last
// block of ADC data
// sendDataSetViaBluetooth ( ADCdataRegisterPtr);
}
}
//////////////////////////////////////////////////////
// write a 512 byte block to the SD card
//////////////////////////////////////////////////////
task void writeSDblock() {
uint8_t *ptr, *timePtr; // allows for byte manipulation
DATA_BLOCK_STRUCTURE* currentSDWriteBuffer;
uint16_t i; // counter
uint16_t crc_fsc; // crc checksum
uint8_t writeStatus;
atomic {
if (currentDataBufferPtr == &dataBuffer0) {
currentSDWriteBuffer = &dataBuffer1;
}
else {
currentSDWriteBuffer = &dataBuffer0;
}
}
// Insert the time stamp we saved earlier, Must do byte swapping because
// straight assignment of a uint32_t to currentSDWriteBuffer[0]
// does not work. Did not look into the details. SPL
timePtr = (uint8_t *) &dataBufferTimeStamp;
ptr = (uint8_t *)currentSDWriteBuffer;
for (i = 0; i < 4; i++) {
ptr[i] = timePtr[i];
}
// Insert padding for block
currentSDWriteBuffer->experimentID = experimentID;
// Calc CRC16 of data
crc_fsc = 0;
for (i = 0; i < 510; i++) {
crc_fsc = crcByte(crc_fsc, *(ptr + i));
}
currentSDWriteBuffer->CRC = crc_fsc;
writeStatus = call SD.writeBlock(DataFile_StartBlock +
blocksWrittenToSD, ptr);
if(writeStatus == MMC_SUCCESS) {
blinkGreenLED (); // we are alive
blocksWrittenToSD++;
}
// check if there is room to write more data
if (blocksWrittenToSD > DataFile_LengthInBlocks) {
// stop recording and save status
writeHeaderBlock(dataFileFull);
call StdControl.stop();
}
}
///////////////////////////////////////////////////////////////////////////
// used to signal when the SD card is available
// check to see if data recording was requesting when SD card becomes
// accessible.
// Alway stop data recording when SD card becomes unaccessible.
///////////////////////////////////////////////////////////////////////////
// used to remove NESC compiler warning
task void startRecordDataSoon () {
call retryDataCollectionTimer.start(TIMER_ONE_SHOT, 1000);
}
// if SHIMMER is reset when out of the dock we don't necessarily want
// the SD card inialized immediately.
async event void SD.available() {
post startRecordDataSoon ();
}
async event void SD.unavailable() {
post stopRecordingData();
}
///////////////////////////////////////////////////////////////////////////
void blinkGreenLED () {
call Leds.greenOn();
call blinkGreenTimer.start(TIMER_ONE_SHOT, LED_BLINK_ON_MSEC);
}
event result_t blinkGreenTimer.fired() {
call Leds.greenOff();
return SUCCESS;
}
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
// BLUETOOTH INTERFACE
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
async event void Bluetooth.commandModeEnded () {
atomic BluetoothReady = TRUE;
}
async event void Bluetooth.dataAvailable (uint8_t data) {
if (data == 's') {
startRecordingDataTaskRequest();
}
}
async event void Bluetooth.connectionClosed (uint8_t reason) {
atomic BluetoothReady = FALSE;
call Leds.redOff ();
}
event void Bluetooth.writeDone (){
}
async event void Bluetooth.connectionMade (uint8_t status) {
}
task void sendData () {
call Bluetooth.write(BluetoothMessageBuffer,
strlen (BluetoothMessageBuffer) );
}
void sendDataSetViaBluetooth (uint16_t data[]) {
uint32_t time;
atomic if (!BluetoothReady) { return;}
call Leds.yellowToggle();
time = call LocalTime.read();
sprintf(BluetoothMessageBuffer, "%lu %d %d %d %d\n\r",
(unsigned long)time, data[0], data[1], data[2], data[3]);
post sendData ();
}
}
--- NEW FILE: Makefile ---
# $Id: Makefile,v 1.1 2008/08/19 18:46:11 ayer1 Exp $
#
# Copyright (c) 2007, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# Neither the name of the Intel Corporation nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: Stephen Paul Linder
# January 2008
COMPONENT=FAT_EKG
PFLAGS=-DDEFAULT_BAUDRATE=115200
# PFLAGS += -g
include ../Makerules
--- NEW FILE: PowerMonitor.nc ---
////////////////////////////////////////////////////////////
#define WARNING_VOLTAGE_THRESHOLD TWO_8V
#define WARNING_VOLTAGE_SAMPLE_PERIOD_MSEC (1000U)*60*2
#define STOP_VOLTAGE_THRESHOLD TWO_65V
#define STOP_VOLTAGE_SAMPLE_PERIOD_MSEC 1000*10
bool warningVoltageThresholdReached = FALSE;
#define LED_BLINK_ON_MSEC 20
////////////////////////////////////////////////////////////
void powerSupplyMonitorInit () {
///////////////////////////////////////////////////////////////////
// SPL June 2, 2008
// WARNING When using the SHIMMER AnEx board with SHIMMER2 the
// power detection monitor is triggered. Have not explored why.
///////////////////////////////////////////////////////////////////
call PSMStdControl.init();
call PowerSupplyMonitor.
setVoltageThreshold(WARNING_VOLTAGE_THRESHOLD);
call PowerSupplyMonitor.
setMonitorInterval(WARNING_VOLTAGE_SAMPLE_PERIOD_MSEC);
}
void powerSupplyMonitorStart () {
call PowerSupplyMonitor.
setVoltageThreshold(WARNING_VOLTAGE_THRESHOLD);
call PowerSupplyMonitor.
setMonitorInterval(WARNING_VOLTAGE_SAMPLE_PERIOD_MSEC);
call PSMStdControl.start();
// putting this in command StdControl.init() does not work
call blinkRunAndPowerLEDTimer.start(TIMER_ONE_SHOT, 10);
}
void powerSupplyMonitorStop () {
call PSMStdControl.stop();
call blinkRunAndPowerLEDTimer.stop();
}
//////////////////////////////////////////////////////////////////////////
event result_t PowerSupplyMonitor.voltageThresholdReached(uint8_t t) {
if(t == WARNING_VOLTAGE_THRESHOLD){
// change voltage threshold to the voltage at which we will shut
// down SHIMMER
call PowerSupplyMonitor.
setVoltageThreshold(STOP_VOLTAGE_THRESHOLD);
call PowerSupplyMonitor.clearLowVoltageCondition();
// now check at a faster rate
call PowerSupplyMonitor.
setMonitorInterval(STOP_VOLTAGE_SAMPLE_PERIOD_MSEC);
warningVoltageThresholdReached = TRUE;
call blinkRunAndPowerLEDTimer.start(TIMER_ONE_SHOT, 100);
// writeHeaderBlock(lowPowerWarning);
}
else {
// writeHeaderBlock (poweredDown);
call StdControl.stop();
}
return SUCCESS;
}
//////////////////////////////////////////////////////
// This is for the LED that flashes when the power is low
// and the standby blinking yellow LED
//////////////////////////////////////////////////////
event result_t blinkRunAndPowerLEDTimer.fired() {
static int LEDstate = 0;
LEDstate++;
if (warningVoltageThresholdReached) {
if((LEDstate & 3) == 1){
call blinkRunAndPowerLEDTimer.start(TIMER_ONE_SHOT, 20);
call Leds.redOn();
}
if((LEDstate & 3) == 2){
call blinkRunAndPowerLEDTimer.start(TIMER_ONE_SHOT, 200);
call Leds.redOff();
}
if((LEDstate & 3) == 3){
call blinkRunAndPowerLEDTimer.start(TIMER_ONE_SHOT, 20);
call Leds.redOn();
}
if((LEDstate & 3) == 0){
call blinkRunAndPowerLEDTimer.start(TIMER_ONE_SHOT, 1000);
call Leds.redOff();
}
} else {
if((LEDstate & 3) == 1){
call blinkRunAndPowerLEDTimer.start(TIMER_ONE_SHOT, LED_BLINK_ON_MSEC);
call Leds.yellowOn();
}
else{
call blinkRunAndPowerLEDTimer.start(TIMER_ONE_SHOT, 1000);
call Leds.yellowOff();
}
}
return SUCCESS;
}
--- NEW FILE: SHIMMER2_SD_cardImage.zip ---
PK
Â0ïû9y,ú
pausePK
©³²
*JCTdÿïÃ.Z¾×uý®ëï£`wæù>snÞçõ>çÌaÔ]ERIð I>ÿ¥Kÿýþïг´´%êã^>ÃÈ{~ÿܸÙsºoNþqógÍzÈwïÔ¸9Yq÷Ï˼mtÜMÚ¯}û¶6-,Ii0K¹Ó˲$Siü
t\èÕÎ6@:a¤Ã%i$K
$+ZÐ|úÆù6HüÿÅÑÏv§rÑw«vÊô«(Zza!f $mÆ¥á-´üë,
øßû×Ï>u~*µpvXAÿ&Áýù³õjøQ
L<ü.³pqPeý¦äÛóáóì·æ2©¡\:XöÃ`ÜõP;»~¯±JÁÿKïwïܹø¹úiøQßIºØ?'¦7Ó£º:ÃïOBùtLoÎÜ9á3Õ1Ôµ¿GF·æ Sg> Õ9Ô½¿ï¸Ú?)
>±i»/Hc:·æªû_7>S Ó5nl('¤°~÷kõ¦ûÄøÒ+ë7ùû¦OÍ-IQpäRébÿÀ]ôÄÿþËM*pÕÅ˶Àh_þ`õÅÙùòÄSÛá{:0û°údG£â°ÊEf8hólÆ*£
Ü)_Ýñ¢ErÕźö>²ÑFß,«Î(/´e»Í@üD¤ÞyK$'ÛTÃ×í¥ð¥¾ëx½zè`ëͳõCSI2¸¾¯GR"V9Ö¦~¼láò¥Ö8I¢±µô±pê
_`n¬jÃÍQRýücæYWÛã¦<2+ÿÁû'çÏùHÜ©ö©s¼ÖÔ¸åOË2eÎÔ¹sã¦=4'îê+ç^?=ÖSã®Ý;!îÊ,-©þ»Kj|}ÒÞ}ø]+QU{WäøÀ¡u×Z4~O£5ðmô5åL¯·JùjV$)F«Ì£¤7*éÆõe`=e°ÚMwõ®vEº~6ËC
ïrßyçIÞWSûqëM<¥d¡]°Qbä¬:f¡Þ²'Ãi$*FO}¤/
CuËÀ¤2Õ÷P¸à7µ[ª²êñú_/ª÷fXå,;ðV7^Êôõõr±Ö«dÕ5¤
vsUõ/Ð< -°Ê´Õá© Þ-Wiå;ÚU§8J(¦~Ýð-·ZóáÒözÞèf¯+d¯ö+ÍÞ¶Kö¡tUÖ)Êz%«^É=åÚ8áîå%OBnÐ °WS9LpµÈjø|iÜ( ÆØjËÖÎqáË×w÷AǵOäÑâL?wIøó{úü/øémñÜ¿+Ã1_ÿTÈ\Éê[Ä5?|éÜé°+áÔ÷DIáøy£ÁâY§ñ÷ôÛ1??fàc×Óoq£¡¶È²-|#\JK
zñú¿¼j¾?¿ùl£4ëÍmýI°öÏ[¸6VÔvòDQ(=Q¬/gëËÁºäãÍ"û¥ó¸eÌP÷&JF\û ëºfïâMPøíÿA³<¹íKÉf$'¨v[ÿýÍtÉW[UÖ/]Üov·\ÒÛrIßD[éjÛ´ÐeñëloÛ¿ÐÍÿÊDâ0«ÞR£ZZ^lïí)³x~Ôë»ä7¤[
ö8õ£ÿDIéw%ö±§'ô4Øï¿«Ñ³4%¤nDe¨=B¾>Ív+ÉÎ÷?àïóÙ¿ %kÒÁ´¼ö«ðü%p>©a{ÖÆ(Íêõ¿EXÉå>
þ´ûúäç ͱ¶Rº4VpÁ¼<\çÂ?R~°!JJ:X[DeõdOT!©¾gIVtQ?s#ÈE+½°*ZvDæ·¯A%6±-eÌâÞ%mp,d0G»0êD¸(vqü|;|¦
óîͪÇòß
µÍmï£l%-Ëù@7¨«^ÐE¡îêåìÀÀ{ÿ°«"}NnP8!×
~^&±Uei2~x¿Öù:`Åò1³MÜ ;»ã2èq%ý%îµêæêöpо¯©5L®ÚÓ0!%·N.Â4
*ðâÀ»#[{Pè=ÎW
ꦣRÁ w¢©4Y-¯g#æ&fXiÌ
©Å`):A®)Hµ*±ÝfÝ]ÅÚUf|%éÉ }FOÝ
úÀ'Zv9ÌG_ _!çÛüóqÿå¼ñ¿¿ô¿ûÛóW>Ê[õß{ëIßô_ÎþËùÌo ³{WYÜÄäÏÁyõ¢ç'ºã,«öº£,«Ê¢ª1zü£õE¦%-Ï¿|I¹>þñÒE(Zè±ÏæKûyü ç'úÇZâéÄÚCæÃk·czõØuõ
|¶./ÆÀy¡ß[þ¹êÌni¤[îÛ#}ôMïÜFõ÷
©#ÎpPÉ|Ü(g4jÀãFx@W
Ù¿ï¨àù«n
³NZþUæ®¶xÖáw(½Suw.:)9LËÊUu(y_KrÕ¥{3m×¹×cÝy2½2ãdR¬ââæÎfÕG3Ѿwµ±õ]
¾qPÌ}£wÃcïB)/4jÓ¯«n2ègFÁÏ~g
ü¦Ë»| À,;0ÞU,¢$êÃáÜó¾.kÉJÈH)åæ< ãI ÈÎmÚ!ñÌU«'n+¦qnTNî@`èS]±åt$Á Ñ¡!]µ^-ßÕfßþIÊ+W]¸ÎH¨Ìåú²[IâÆÈUòLÛxõx¶Òp¥3Ýã8ýá¬L_{}ÁÄòù.r=xÒHÍÁP3WE^yÁYIË÷]A3þ¾Ôö>ìMó¡^÷úr-%r(ëÁxøqZ
¸ü:N,b%-LeBcc7sÒy÷ZwX-ÑÚB¯ ÛÃfz§aFÊüyAkÞ!÷kU¶BIÈópþ»Új£*¶DrÆL[üQ8ÑðÛFÕ>þ=ÚµÄL>Iâ9z$ä§©ÌL
!ÉÊÓkpâÂ÷¿°ÊFèýöë§7à§ÚzItųKc$U#ÙµzkÌ=ªÜ¤_¡vëÛñé]òÁÏ.Íï´1õwÛKë\u﫤T3̮ؼUæ<ñz
UéVt7.z"ø>o7ùbø±xa.+v~KØÕþ[P=ëY¤´^áT
°Ç¯Å¼_`ðSl,[ólqJNncßè]-ï©÷¨ÑÅ$=Ь$^Ôßq³kE¤tÖ|Î(©ÂÑ}ÄÂjw$i¿¼f`\8É@+P=BýAéäa6è L0z8ê±®÷Â<ölÏù¿(&!,þÁÕ8yI;WÙ9ÃTm¼Oõ4B5ÂçXèbí©F¦à5Ê oz Bß´[SÃëÎ7¥<S¹fHßÑ8ÁÙHªåóheòõÕ¦
jÄO@í!¬W]*tÍ<¡rÐòà ÑÉwd
¾®{Ð1\Éh¹~Å ïÍ2YÜ'±·6îÆVüü§Æù÷Èx3ûR©·Núú=ÀUZ²r½9e
æzÎÆrÚSÏiÞÓBr°ÚwÁÕçhJÙ
!<ö!õ¹· Õ«wÆé|Ø
[...12944 lines suppressed...]
ëmýZ?¦ÅZËõF9Ç3Vi*F§KÅçåÍÎNôégùÔº
MMsਪÐxÛ³ã±y^Þ°ÝܶDBM¬ûáåâe*«Ã¾ºÜÞE¹wt"~Gûìt×quMÝ(ÿÝy'g«\Ó bXûÎõo<*"þIø>LE+ZZW4¨¿rÉ7!¢z½RcÏàù!p^533_ý¾9YÕXfE£ßÐö¤
}SáÉ
55
Ón»Ë®Ë¬x²=U$"¢À÷µ|>¥À×ôôôUÁJ#«¿¿8_¼=IDä/_Ø 03ÃÓnnqn?/ûm"÷Í-">}oÇç
3
qóêtûÕC[SðýÂî¾'Ò%4ý;E"¼+l
|ï½ôtã3¾ÆÆ"|?uÌA|÷FDD½ÿ-âÞù|¿ã?1¢c|ÌÕ¿1wÑ">¿FýÜoÜOóâÎS>_`fföìÙ3þ3vó*ÐåûI{¾ñÿRÜÿ±}ÇÇPK
:
°v
·8
--- NEW FILE: SerialLink.nc ---
task void sendSD_block();
result_t sendSerialBuffer (uint8_t* data, uint16_t length);
result_t sendNextSerialByte();
enum {
S_IDLE = 0, // Not processing any commands
S_CLOCK_OFFSET_RECV = 1, // Currently receiving time offset from host
S_HEADER_BLOCK_RECEIVE = 2, // Currently receiving file marker from host
S_BLOCK_NUMBER_RECEIVE = 3, // Currently receiving a block number from host
};
// Used by circular buffer that hold data that is written out the serial port
#define SERIAL_BUFFER_SIZE 600
#define NEXT_BUFFER_PTR(ent) (((ent) >= ((SERIAL_BUFFER_SIZE) - 1)) ? 0 : ((ent) + 1))
uint8_t state; // current command when in command state
uint16_t head; // Next entry in the buffer to fill
uint16_t tail; // Oldest entry in the buffer
uint8_t serialBuffer[SERIAL_BUFFER_SIZE]; // Circular buffer of bytes
bool bytePending; // TRUE if we have put without a putDone
uint8_t transmitBuffer[BLOCK_LENGTH]; // stores data read from the SD card
uint32_t transmitSD_blockNumber; // block to send to the host
//////////////////////////////////////////////////////
//// SERIAL PORT
//// parse commands from serial port and download
//// data from SD card
//////////////////////////////////////////////////////
/* Gets called when we have input from the host PC. To keep things
simple we're just allowing one byte commands to be received.
Also note that the the offset is stored at a double (8 bytes) and
on TinyOS a double is 4 bytes, so we keep it around as an array
of bytes. */
void SerialLinkInit () {
atomic {
head = 0;
tail = 0;
bytePending = FALSE;
state = S_IDLE;
blocksWrittenToSD = 0;
CollectingandRecordingDataFlag = FALSE;
}
}
///////////////////////////////////////////////////////
// setting BCSCTL2 to
// SELM_XT2CLK | DIVM_DIV1 | SELS | DIVS_DIV8
// breaks the serial port connection to the host PC even though
// SMCLK is still at 1 MHz
// There are ocassional high bits being set.
// use http://mspgcc.sourceforge.net/baudrate.html
// to calcuate the values you need for the baudrate
///////////////////////////////////////////////////////
void initSerialLinkToHost() {
uint16_t temp;
// disable the SD card chip select.
call SD.deSelect();
call HPLUART.init();
U0TCTL &= ~(SSEL_3); // clear the clock select bits
U0TCTL |= SSEL_SMCLK; // set the clock source to SMCLK
// must check to see what the SMCLK speed is set to.
// the assumption is that we only have two choices: 1 and 4 MHz
// Set the baud rate to 230400
// call USARTControl.setClockRate(UBR_SMCLK_115200, UMCTL_SMCLK_115200);
// UBR00=0x04; UBR10=0x00; UMCTL0=0x55; // uart0 1048576Hz 233016bps
// UBR00=0x24; UBR10=0x00; UMCTL0=0x29; // uart0 4194304Hz 115228bps
// UBR00=0x12; UBR10=0x00; UMCTL0=0x84; // uart0 4194304Hz 230456bps
// must check to see what the SMCLK speed is set to.
// the assumption is that we always use the internal oscillator set to 4 Mhz
// as the source and we just need to check the divisor.
// Set the baud rate to 230400
temp = BCSCTL2;
temp = temp & DIVS_DIV8;
if (temp == DIVS_DIV1) {
call USARTControl.setClockRate(0x12, 0x84);
} else if (temp == DIVS_DIV4) {
call USARTControl.setClockRate(0x04, 0x55);
}
}
event async result_t HPLUART.get(uint8_t data) {
static uint16_t Index = 0; // how many bytes of data received
#define CLOCK_OFFSET_LENGTH 4
static uint8_t clockOffset[CLOCK_OFFSET_LENGTH]; // constructed clockOffset
static uint8_t block[4]; // constructed block number
if (state == S_HEADER_BLOCK_RECEIVE) {
// receive block mode
((char*) &headerBlock)[Index] = data;
if (Index == BLOCK_LENGTH - 1) {
// got all our data
Index = 0;
state = S_IDLE;
}
else {
Index++;
}
}
// block indices are recieved as little endian
else if (state == S_BLOCK_NUMBER_RECEIVE) {
// receive block mode
block[Index] = data;
if (Index == 3) {
// got all our data
Index = 0;
state = S_IDLE;
atomic {
transmitSD_blockNumber = *((uint32_t *)block);
}
post sendSD_block();
}
else {
Index++;
}
}
else if (state == S_CLOCK_OFFSET_RECV) {
// receive offset mode
clockOffset[Index] = data;
if (Index == CLOCK_OFFSET_LENGTH - 1) {
// got all our data
Index = 0;
state = S_IDLE;
}
else {
Index++;
}
}
else {
// command mode
if (data == 'a') {
// start Collecting and Recoding Data
startRecordingDataTaskRequest();
}
else if (data == 'd') {
// toggle on the red led
call Leds.redToggle();
}
else if (data == 'e') {
// send current file marker
sendSerialBuffer((char *) &headerBlock, BLOCK_LENGTH);
}
else if (data == 'f') {
// Send a block (512 bytes) of data. The block to send is
// sent as next 4 bytes and is 1 based (i.e. 1 is the first
// block that was written to the SD card.
state = S_BLOCK_NUMBER_RECEIVE;
}
else if (data == 'g') {
// turns off the orange led
// call Leds.orangeOff();
}
else if (data == 'h') {
// turns on the green led
call Leds.greenOn();
}
else if (data == 'm') {
// receive header block
state = S_HEADER_BLOCK_RECEIVE;
}
else if (data == 'o') {
// send clock offset (used to determine offset was received correctly)
sendSerialBuffer(clockOffset, CLOCK_OFFSET_LENGTH);
}
else if (data == 'r') {
// receive time offset
state = S_CLOCK_OFFSET_RECV;
}
else if (data == 's') {
// send localtime (used for timing calculations)
dataBufferTimeStamp = call LocalTime.read();
sendSerialBuffer((uint8_t *)&dataBufferTimeStamp, 4);
}
}
return SUCCESS;
}
// Called when the UART is ready to send another byte
//////////////////////////////////////////////////////
event async result_t HPLUART.putDone() {
atomic {
bytePending = FALSE;
}
if (head == tail) {
return SUCCESS; // buffer is empty
}
tail = NEXT_BUFFER_PTR(tail);
sendNextSerialByte();
return SUCCESS;
}
// precede each block with 0xaaaaaaaa
// must now check if SD card is available 2/10/08 SPL
// SHIMMER2 can not access SD card while docked.
// WARNING -- THIS DOES NOT WORK WITH SHIMMER 2
// SHIMMER2 blocks access to the SD card while the
// SHIMMER2 is docked.
//////////////////////////////////////////////////////
task void sendSD_block() {
uint8_t err;
uint32_t blockNumber;
uint32_t marker = 0xaaaaaaaa;
// check and see if clock is speed up.
enable8MHzMasterClockAnd4MHzSMCK ();
atomic {
blockNumber = transmitSD_blockNumber;
}
// read the block from the SD card.
// if there is an error the SD module should have reinitialized card
// the SD so we can try one more time.
///////////////////////////////////////////////////////////////
err = call SD.init(); // will only init if not initialized already
if(!err) {
err = call SD.readBlock ((DataFile_StartBlock + blockNumber),
transmitBuffer);
if(err) {
err = call SD.readBlock ((DataFile_StartBlock + blockNumber),
transmitBuffer);
}
}
// this makes the CRC fail for the block. If readBlock fails what gets
// sent is the last good block which has a good CRC 11/15/07 SPL
if(err) {
transmitBuffer[0] = 0xFF;
}
initSerialLinkToHost();
atomic {
sendSerialBuffer ((uint8_t *)&marker, sizeof(marker));
sendSerialBuffer (transmitBuffer, BLOCK_LENGTH);
}
blinkGreenLED ();
}
// Send data across the serial line
//////////////////////////////////////////////////////
result_t sendSerialBuffer (uint8_t* data, uint16_t length) {
uint16_t i;
uint16_t size;
bool not_busy;
// see if there's enough room for this packet
size = (head < tail) ? head + SERIAL_BUFFER_SIZE - tail : head - tail;
if (size + length >= SERIAL_BUFFER_SIZE) {
return FAIL; // not enough room
}
// copy incoming bytes to the buffer
for (i = 0; i < length; i++) {
serialBuffer[head] = data[i];
head = NEXT_BUFFER_PTR(head);
}
atomic {
not_busy = (bytePending == FALSE);
}
if (not_busy) sendNextSerialByte();
return SUCCESS;
}
// Send one byte across the serial line
//////////////////////////////////////////////////////
result_t sendNextSerialByte() {
bool busy;
atomic {
busy = (bytePending == TRUE);
}
if (busy) {
return FAIL;
}
if (head == tail) {
return SUCCESS; // buffer is empty
}
atomic bytePending = TRUE;
call HPLUART.put(serialBuffer[tail]);
return SUCCESS;
}
--- NEW FILE: crc16.h ---
#ifndef CRC16_H_
#define CRC16_H_
// CCITT CRC16 lookup table
uint16_t CRC_table[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
};
uint16_t updcrc(uint16_t p_crc, char* cp, int size)
{
uint16_t crc = p_crc;
while (size-- > 0) {
uint16_t partOne = (crc << 8) & 0xFF00;
uint16_t aByte = *cp++ & 0xFF;
crc = partOne ^ CRC_table[(crc>>8) ^ aByte];
}
return (crc);
}
// Calculate CCITT CRC16.
uint16_t CRC16(char* buffer, int byteSize)
{
return updcrc(0x0000, buffer, byteSize);
}
#endif /*CRC16_H_*/
--- NEW FILE: dostime.c ---
/* dostime.c - convert dos time to/from time_t.
Copyright (C) 2002 Free Software Foundation
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <time.h>
#include "dostime.h"
/*
* The specification to which this was written. From Joe Buck.
* The DOS format appears to have only 2 second resolution. It is an
* unsigned long, and ORs together
*
* (year-1980)<<25
* month<<21 (month is tm_mon + 1, 1=Jan through 12=Dec)
* day<<16 (day is tm_mday, 1-31)
* hour<<11 (hour is tm_hour, 0-23)
* min<<5 (min is tm_min, 0-59)
* sec>>1 (sec is tm_sec, 0-59, that's right, we throw away the LSB)
*
* DOS uses local time, so the localtime() call is used to turn the time_t
* into a struct tm.
*/
time_t
dos2unixtime (unsigned long dostime)
{
struct tm ltime;
time_t now = time (NULL);
/* Call localtime to initialize timezone in TIME. */
ltime = *localtime (&now);
ltime.tm_year = (dostime >> 25) + 80;
ltime.tm_mon = ((dostime >> 21) & 0x0f) - 1;
ltime.tm_mday = (dostime >> 16) & 0x1f;
ltime.tm_hour = (dostime >> 11) & 0x0f;
ltime.tm_min = (dostime >> 5) & 0x3f;
ltime.tm_sec = (dostime & 0x1f) << 1;
ltime.tm_wday = -1;
ltime.tm_yday = -1;
ltime.tm_isdst = -1;
return mktime (<ime);
}
unsigned long
unix2dostime (time_t *time)
{
struct tm *ltime = localtime (time);
int year = ltime->tm_year - 80;
if (year < 0)
year = 0;
return (year << 25
| (ltime->tm_mon + 1) << 21
| ltime->tm_mday << 16
| ltime->tm_hour << 11
| ltime->tm_min << 5
| ltime->tm_sec >> 1);
}
--- NEW FILE: dostime.h ---
/*
dostime.h - function prototypes
Copyright (C) 1999 Bryan Burns
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
time_t dos2unixtime(unsigned long dostime);
unsigned long dostime(int, int, int, int, int, int);
unsigned long unix2dostime(time_t*);
--- NEW FILE: fileCatalog.txt ---
Catalog of all the files in the FAT_EKG project
fileCatalog.txt This file
shimmerSerial.h Constants for the TinyOS module for SHIMMER and for
PC-side interface programs
FAT_EKG.nc TinyOS wiring file
FAT_EKG_M.nc TinyOS module
PowerMonitor.nc Code used to monitor power on SHIMMER
SerialLink.nc Serial interface
ClockSet.nc Sets the MSP430 clock
Makefile Used by TinyOS to build module
crc16.h Used to calculate CRC of data blocks
dostime.h
dostime.c Translates between DOS and Unix time
shimmerSerial.c Serial interface to Shimmer. See readme.html
DataFileInit.c Creates an data file of specified length
DataFileRead.c Reads binary data file from SD card and generates a space delimited text file.
readme.doc MS Word readme file used to generate the following
readme.html HTML readme file
readme.txt Text readme file
plotEKG.m Matlab script that plots data after downloaded to t.txt
plotEKGdata.plt Gnuplot script for plotting data collected from SHIMMER
Dependencies
++++++++++++
This module requires updated (after August 08, 2008) versions of TinyOS library files:
tos/interface/FAT_SD.nc interface for FAT-16 access to SD card
tos/interface/FAT_SD.h constants used by FAT-16 module
tos/platform/shimmer2/FAT_SD_C.nc TinyOS wiring digram
tos/lib/FAT/FAT_SD_M.nc FAT-16 module
tos/lib/FAT/dsfs.c DOSFS Embedded FAT-Compatible Filesystem
tos/lib/FAT/dosfs.h by Lewin A.R.W. Edwards (sysadm at zws.com)
tos/lib/FAT/dosfs_protos.h
--- NEW FILE: plotEKG.m ---
%close all
TINYOS_CLOCK_TICS_SEC = hex2dec('8000');
clockInverted = 1.0 / ( TINYOS_CLOCK_TICS_SEC);
x = load('t.txt');
figure
time = x(:,1) - min(x(:,1));
%time = time * clockInverted;
plot(time, x(:,2), 'k')
hold on
plot(time, x(:,3), 'r')
plot(time, x(:,4), 'g')
plot(time, x(:,5), 'b')
xlabel('time(sec)')
ylabel ('raw data (12-bit)')
elapsedTimeSec = time(length(time));
elapsedTimeMin = elapsedTimeSec / 60
elapsedTimeHour = elapsedTimeMin / 60
zoom on
figure
plot(time, x(:,2), 'k')
xlabel('time(sec)')
ylabel ('raw data (12-bit)')
zoom on
return
figure
d = diff(time);
%x = floor((1:length(d)) / 84);
plot(d)
xlabel('sample count')
ylabel ('delta time between samples (sec)')
y = find (d > 0.023)
diff(y)
rangeOfTimeIntervalsBetweenSamples = max(d) - min(d)
blockTimes = time(1:64:length(time));
blockInterval = diff(blockTimes);
figure
plot(blockInterval)
--- NEW FILE: plotEKGdata.plt ---
# gnuplot script for plotting three axis accelerometer data collected from SHIMMER
#
set title "Acceleration Data"
set style line 1 lt 2 lw 3
set key box linestyle 1
set xlabel "Time (secs)
plot "t.txt" using 1:3 title "X-axis" with lines, \
"t.txt" using 1:4 title "Y-axis" with lines, \
"t.txt" using 1:5 title "Z-axis" with lines
# "t.txt" using 1:2 title "EKG" with lines, \
pause -1 "SHIMMER data plotted! Press key to continue..."
--- NEW FILE: readme.doc ---
ÐÏࡱá
$
$
$
t
l
t
l
t
l
t
l
t
l
t
l
t
l
t
l
t
t
t
t
t
t
t
t
l
--- NEW FILE: readme.txt ---
Data Logging to SD Card formatted with a FAT-16 File System
Stephen Paul Linder
August 2008
spl at alum.mit.edu
The FAT_EKG TinyOS program for SHIMMER2 logs data from an EKG chest-strap and the three axis accelerometer to the SD card. The SD card is formatted with the FAT-16 file system and contains a log file: data_out.bin. SHIMMER2 queries the SD card for the location and length of the file on the SD-card and uses this information to overlay the collected data on the file.
After SHIMMER2 is docked, the data can be accessed directly as a normal file. The program DataFileRead.c is provide to translate the binary data file to a space delimited text file.
Preparing the SD card
The SD- card must be formatted with the FAT-16 file system. If you are unsure what file system is being used on your SD-card you can reformat the SD-card using either Linux of Microsoft Windows. Place SHIMMER2 in a SHIMMER2 docking station, or remove the SD card and place it in a card reader. Under Windows you right click on the drive and select Format. Under File System select FAT (not FAT32) and then format.
After formatting the SD card you can either install the SD-card image that is provided in SHIMMER2_SD_cardImage.zip or you create your own image. The only required file on the SD-card is data_out.bin. This file should be the first file copied to the SD-card to insure that the file occupies consecutive blocks on the SD card. The file can be any size up to 1 gigabyte. The FAT_EKG application will stop logging data when the end of the file is reached.
SHIMMER2_SD_cardImage.zip contains the following files
autorun.inf
gives the name and icon of the drive, and adds commands to the drives context menu
cygwin1.dll
Library used by executables written under Cygwin: EKGread.exe.
dataout.bin
Data file used by FAT_EKG
EKGread.exe
Program compiled from DataFileRead.c that reads EKG data from dataout.bin
geatAndPlot.bat
Windows batch file that runs EKGread.exe, saves data to t.txt and then uses plotEKGdata.plt script to plot the collected data
plotEKGdata.plt
A gnuplot script that plots data from t.txt
SHIMMER/
autorun.ico
An icon used by the file explorer for the SD-card.
gnuplot/
Folder contain the open source plotting program used to plot the data acquired by FAT_EKG
If dataout.bin is smaller than you require, a new data file can be made using DataFileInit.c. The c-prgram creates a file of specified length that can be copied to the SD-card.
Start up
The default behavior of this application is to begin data logging when the SHIMMER2 is removed from the docking station. If this is not desirable than comment out the call to
startRecordingDataTaskRequest() in the function StdControl.start() in FAT_EKG_m.nc.
The SHIMMER2 data collection can then be initiated using the ss.exe program compiled from the shimmerSerial.c. It shares a shimmerSerial.h header file with the SHIMMER-side software. The shimmerSerial.c was written under Cygwin and should compile and run under Linux. Compile by using gcc
gcc shimmerSerial.c -o ss.exe
Place the SHIMMER in the dock station. Reset the SHIMMER and confirm that the green LED is on. Confirm that the serial link to the SHIMMER (thru the USB interface) is working by toggling the red LED on the SHIMMER
./ss.exe -d
The response to this command is
$ ./ss -d
Shimmer Serial version 2.10
Attempting port /dev/ttyS3
Port /dev/ttyS3 open to Shimmer.
Toggling red LED ...
Closing port
The SHIMMER docking station uses a FTDI chip to interface the SHIMMERs serial port to the USB port. FTDI provides a device driver that maps the USB port to a virtual serial port. ss.exe will search through the first ten serial ports to find the SHIMMER. Sometimes you may need to specify the serial port. You can do this using -p option.
./ss.exe -p /dev/ttyS3 -d
The -p option must be the first option. A list of all options can be obtained by running ss without any options.
Initiating Data Collection
Start data collection by placing the SHIMMER in the dock station. Reset the SHIMMER and confirm that the green LED is on.
Run ss.exe under Cygwin with the following option to commence data logging
./ss.exe -A "Shimmer ID String" // Linux
To run ss.exe on a Windows PC without Cygqin, have the cyqwin1.dll in the same subdirectory as ss.exe.
ss -A "Shimmer ID String"
Data collection begins when the SHIMMER2 is undocked. When data is being collected the green LED blink every time a data block is saved to the SD card. If the red LED blinks there has either been an error writing to the SD card or the battery is running low.
You can also specify the sample period for data collection using the -t option.
Terminating Data Collection
Data collection can be terminated normally by inserting the SHIMMER in the docking station and The green LEDs stops blinking and if the SD card contains the image provide in SHIMMER2_SD_cardImage.zip a MS-Windows based computer will auto detect the SD card and ask if you would like to plot the SHIMMER data.
This will run the EKGread.exe program on the SD card which translates the binary data in data_out.bin to a space delimited ASCII file: t.txt. The first column will be the time stamp of the data (using Unix time), and the subsequent columns the raw data from the 12-bit ADC on the MSP430.
Data logging Limitations
The application concurrently collects and save data. Through put of the system is limited by 12-msec it takes to save a 512-byte block to the SD card. Data logging has been tested at 1000 Hz with four channels of 12-bit data.
Bluetooth
Data can be streamed over Bluetooth by setting ENABLE_BLUETOOTH_ DATA_ TRANSMISSION. The data format is set in sendDataSetViaBluetooth(), and is currently space delimited ASCII text.
--- NEW FILE: shimmerSerial.c ---
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <time.h>
#include <math.h>
#include "crc16.h"
#include "shimmerSerial.h"
#include "dostime.c"
// FUNCTION PROTOTYPES of external funtions. Stops gcc from issuing warnings
int atoi( const char *str );
void exit(int status );
// FUNCTION PROTOTYPES
int openSerialPort (char defaultPort[]);
void setPortConfiguration (int fd);
[...1043 lines suppressed...]
}
#ifdef _DEBUG_SERIAL
// debugging code to dump block
fprintf (stderr, " \n Dumping block received...\n");
int i;
for (i = 0; i < length; i++) {
fprintf (stderr, "%2x ", (unsigned char)(buffer [i]));
if(remainder((double)(i + 1), 16.0) == 0.0) {
fprintf (stderr, " \n");
}
}
fprintf (stderr, " \n");
#endif
return charCount;
}
--- NEW FILE: shimmerSerial.h ---
#ifndef __shimmerSerial_h
#define __shimmerSerial_h
#define SHIMMER_VERSION "2.30"
#define SHIMMER_DATE "12-AUG-08"
////////////////////////////////////////////////////////////////////////////
// serial link
//
// B256000 does not seem to be supported. It set the baudrate to B115200
//#define BAUDRATE B115200
#define BAUDRATE B230400
#define START_RECORDING_CMD "a"
#define READ_BLOCK_CMD "f"
#define WRITE_HEADER_BLOCK_CMD "m"
#define TOGGLE_RED_LED_CMD "d"
#define GET_SHIMMER_TIME_CMD "s"
#define GET_SHIMMER_CLOCK_OFFSET_CMD "o"
#define SET_SHIMMER_CLOCK_OFFSET_CMD "r"
////////////////////////////////////////////////////////////////////////////
typedef enum {
Accel_Shimmer = 0,
EKG_Shimmer
} Shimmer_t;
char* SHIMMER_TYPE_STRINGS[] = {
"3-axis accel",
"EKG and 3-axis" };
#define SHIMMER_TYPE SHIMMER_TYPE_STRINGS[EKG_Shimmer]
#define ENABLE_BLUETOOTH_DATA_TRANSMISSION FALSE
char* SHIMMER_ACCEL_GAIN_STRINGS[] = {
"1.5g",
"2.0g",
"4.0g",
"6.0g" };
// not saved on SD card yet
#define SHIMER_ACCELEROMETER_GAIN RANGE_1_5G
// Shimmer/TinyOS has two clock/timers that are used.
// The time that is used to schedule the DC conversion
// call sampleTimer.start(TIMER_REPEAT, ADC_TIMER_PERIOD_MSEC);
// has one tic rate TINYOS_TIMER_TICS_SEC
//
// The time stamp that comes with each block is gotten by
// atomic dmaBufferTimeStamp = call LocalTime.read()
// uses a time base with another number of clock ticks per second
//
// if shimmer has a clock with 0x8000 (32k) ticks per second
// and time is saved in 32 bits then it will rollover every
// 36.4 hours
#define ADC_TIMER_PERIOD_MSEC 20
#define TINYOS_TIMER_TICS_SEC 0x0400
#define TINYOS_CLOCK_TICS_SEC 0x8000
#define BACKUP_HEADER_COUNT 40.0
#define LED_BLINK_ON_MSEC 20
typedef char BOOL;
enum {
FALSE = 0,
TRUE = 1 };
typedef enum {
nullStatus = 0, // starting status
completedNormal,
lowPowerWarning,
poweredDown,
dataFileFull,
errorWritingBlock // not used yet
} dataCollectionStatus_t;
char* STATUS_MESSAGES[] = {
"Data collection terminated without setting status.",
"Data collection completed normally",
"Low battery warning received.",
"Shimmer powered down because of low battery voltage",
"Data file full, stopped data collection",
"Error writing to SD card, possible end of card reached." };
#define UNUSED_BYTES_IN_HEADER 52
#define HEADER_ID_LENGTH 400
#define HEADER_VERSION_LENGTH 10
#define HEADER_TYPE_LENGTH 20
#define HEADER_DATE_LENGTH 10
typedef struct{
uint32_t LinuxStartTimeSec;
uint32_t numberOfBlocksWritten; // includes the first status block
char ShimmerVer [HEADER_VERSION_LENGTH];
char ShimmerType [HEADER_TYPE_LENGTH];
char ShimmerDate [HEADER_DATE_LENGTH];
uint8_t dataCollectionStatus;
uint8_t errorCount; // not used yet
uint16_t DOS_date;
uint16_t DOS_time;
uint16_t numberOfSamplesPerDataRecord;
uint16_t samplePeriod;
char ShimmerID [HEADER_ID_LENGTH];
uint8_t unused[UNUSED_BYTES_IN_HEADER]; // needed to make block 512 bytes long
uint16_t CRC;
} __attribute__((packed)) HEADER_BLOCK_STRUCTURE;
typedef struct {
uint16_t EKG;
uint16_t x;
uint16_t y;
uint16_t z;
} __attribute__ ((packed)) SAMPLE_STRUCTURE;
#define NUMBER_OF_ADC_CHANNELS 4 // Number of ADC channels used
#define NUMBER_OF_SAMPLES_PER_DATA_RECORD 63
#define UNUSED_BYTES_IN_DATA_BLOCK 0
typedef struct {
uint32_t ShimmerTime;
SAMPLE_STRUCTURE data[NUMBER_OF_SAMPLES_PER_DATA_RECORD];
// uint8_t unused[UNUSED_BYTES_IN_DATA_BLOCK]; // needed to make block 512 bytes long
uint16_t experimentID;
uint16_t CRC;
} __attribute__ ((packed)) DATA_BLOCK_STRUCTURE;
// used by the data conversion and DMA transfer to RAM
//************* needs to be cleaned up *******************
//****** can get out of synch DATA_BLOCK_STRUCTURE ************
//*****************************************************************
//**********************************************************************
// Used to access FAT file system
//**********************************************************************
#define DATA_FILE_NAME "data_out.bin"
#define BLOCK_LENGTH 512
#define HEADER_BLOCK_OFFSET 0
#define FIRST_DATA_BLOCK_OFFSET 1
/////////////////////////////////////////////////////////////
#endif
More information about the Tinyos-contrib-commits
mailing list