/FollowMe/trunk/FollowMe.pnproj |
---|
0,0 → 1,0 |
<Project name="FollowMe"><Folder name="Sources"><File path="ubx.c"></File><File path="analog.c"></File><File path="button.c"></File><File path="crc16.c"></File><File path="fat16.c"></File><File path="led.c"></File><File path="main.c"></File><File path="menu.c"></File><File path="printf_P.c"></File><File path="sdc.c"></File><File path="ssc.c"></File><File path="timer0.c"></File><File path="uart0.c"></File><File path="uart1.c"></File><File path="gps.c"></File><File path="logging.c"></File><File path="settings.c"></File><File path="gpx.c"></File><File path="kml.c"></File></Folder><Folder name="Header"><File path="ubx.h"></File><File path="analog.h"></File><File path="button.h"></File><File path="crc16.h"></File><File path="fat16.h"></File><File path="led.h"></File><File path="main.h"></File><File path="menu.h"></File><File path="printf_P.h"></File><File path="sdc.h"></File><File path="ssc.h"></File><File path="timer0.h"></File><File path="uart0.h"></File><File path="uart1.h"></File><File path="gps.h"></File><File path="settings.h"></File><File path="gpx.h"></File><File path="gpx_header.h"></File><File path="kml.h"></File><File path="kml_header.h"></File><File path="logging.h"></File></Folder></Project> |
/FollowMe/trunk/FollowMe.pnps |
---|
0,0 → 1,0 |
<pd><ViewState><e p="FollowMe" x="true"></e><e p="FollowMe\Header" x="true"></e><e p="FollowMe\Sources" x="true"></e></ViewState></pd> |
/FollowMe/trunk/FollowMe.ppg |
---|
0,0 → 1,0 |
<Workspace name="New Project Group"><Project path="d:\hobby\bl-UFO\Mikrocopter\01_Flight-Ctrl\my_Flight-Ctrl_V0_72o\flight.pnproj"></Project><Project path="FollowMe.pnproj"></Project></Workspace> |
/FollowMe/trunk/analog.c |
---|
0,0 → 1,109 |
#include <stdlib.h> |
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include "analog.h" |
volatile uint16_t Adc0, Adc1, Adc2, Adc3, Adc4, Adc5, Adc6, Adc7; |
volatile uint8_t ADReady = 1; |
/*****************************************************/ |
/* Initialize Analog Digital Converter */ |
/*****************************************************/ |
void ADC_Init(void) |
{ |
uint8_t sreg = SREG; |
// disable all interrupts before reconfiguration |
cli(); |
//ADC0 ... ADC7 is connected to PortA pin 0 ... 7 |
DDRA = 0x00; |
PORTA = 0x00; |
// Digital Input Disable Register 0 |
// Disable digital input buffer for analog adc_channel pins |
DIDR0 = 0xFF; |
// external reference AREF, adjust data to the right |
ADMUX &= ~((1 << REFS1)|(1 << REFS0)|(1 << ADLAR)); |
// set muxer to ADC adc_channel 0 (0 to 7 is a valid choice) |
ADMUX = (ADMUX & 0xE0) | 0x00; |
//Set ADC Control and Status Register A |
//Auto Trigger Enable, Prescaler Select Bits to Division Factor 128, i.e. ADC clock = SYSCKL/128 = 156.25 kHz |
ADCSRA = (0<<ADEN)|(0<<ADSC)|(0<<ADATE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(0<<ADIE); |
//Set ADC Control and Status Register B |
//Trigger Source to Free Running Mode |
ADCSRB &= ~((1 << ADTS2)|(1 << ADTS1)|(1 << ADTS0)); |
// Start AD conversion |
ADC_Enable(); |
// restore global interrupt flags |
SREG = sreg; |
} |
/*****************************************************/ |
/* Interrupt Service Routine for ADC */ |
/*****************************************************/ |
// runs at 312.5 kHz or 3.2 µs |
// if after (60.8µs) all 19 states are processed the interrupt is disabled |
// and the update of further ads is stopped |
#define ADC0 0 |
#define ADC1 1 |
#define ADC2 2 |
#define ADC3 3 |
#define ADC4 4 |
#define ADC5 5 |
#define ADC6 6 |
#define ADC7 7 |
ISR(ADC_vect) |
{ |
static uint8_t ad_channel = ADC0, state = 0; |
// state machine |
switch(state++) |
{ |
case 0: |
Adc0 = ADC; |
ad_channel = ADC1; |
break; |
case 1: |
Adc1 = ADC; |
ad_channel = ADC2; |
break; |
case 2: |
Adc2 = ADC; |
ad_channel = ADC3; |
break; |
case 3: |
Adc3 = ADC; |
ad_channel = ADC4; |
break; |
case 4: |
Adc4 = ADC; |
ad_channel = ADC5; |
break; |
case 5: |
Adc5 = ADC; |
ad_channel = ADC6; |
break; |
case 6: |
Adc6 = ADC; |
ad_channel = ADC7; |
break; |
case 7: |
Adc7 = ADC; |
ad_channel = ADC0; |
state = 0; |
ADReady = 1; |
break; |
default: |
ad_channel = ADC0; |
state = 0; |
ADReady = 1; |
break; |
} |
// set adc muxer to next ad_channel |
ADMUX = (ADMUX & 0xE0) | ad_channel; |
// after full cycle stop further interrupts |
if(state != 0) ADC_Enable(); |
} |
/FollowMe/trunk/analog.h |
---|
0,0 → 1,20 |
#ifndef _ANALOG_H |
#define _ANALOG_H |
#include <inttypes.h> |
extern volatile uint16_t Adc0, Adc1, Adc2, Adc3, Adc4, Adc5, Adc6, Adc7; |
extern volatile uint8_t ADReady; |
void ADC_Init(void); |
// clear ADC enable & ADC Start Conversion & ADC Interrupt Enable bit |
#define ADC_Disable() (ADCSRA &= ~((1<<ADEN)|(1<<ADSC)|(1<<ADIE))) |
// set ADC enable & ADC Start Conversion & ADC Interrupt Enable bit |
#define ADC_Enable() (ADCSRA |= (1<<ADEN)|(1<<ADSC)|(1<<ADIE)) |
#endif //_ANALOG_H |
/FollowMe/trunk/button.c |
---|
0,0 → 1,50 |
#include "timer0.h" |
#include "button.h" |
#ifdef USE_FOLLOWME |
#define BUTTON !(PINC & (1<<PINC6)) |
#endif |
#ifdef USE_SDLOGGER |
#define BUTTON !(PINC & (1<<PINC3)) |
#endif |
#define CNT_KEY 10 // at least 3 |
#define KEY_DELAY_MS 50 |
uint16_t ButtonTimer = 0; |
void Button_Init(void) |
{ |
// set port pin as input pullup |
#ifdef USE_FOLLOWME |
PORTC |= (1 << PORTC6); |
DDRC &= ~(1 << DDC6); |
#endif |
#ifdef USE_SDLOGGER |
PORTC |= (1 << PORTC3); |
DDRC &= ~(1 << DDC3); |
#endif |
ButtonTimer = SetDelay(KEY_DELAY_MS); |
} |
uint8_t GetButton(void) |
{ |
static uint8_t button = 0; |
uint8_t ret = 0; |
if(CheckDelay(ButtonTimer)) |
{ |
if(BUTTON) |
{ |
if(button++ == 0 || button == CNT_KEY) ret = 1; |
if(button == CNT_KEY) button = CNT_KEY - CNT_KEY / 3; |
} |
else button = 0; |
ButtonTimer = SetDelay(KEY_DELAY_MS); |
} |
return(ret); |
} |
/FollowMe/trunk/button.h |
---|
0,0 → 1,11 |
#ifndef _BUTTON_H |
#define _BUTTON_H |
#include <avr/io.h> |
#include <inttypes.h> |
extern void Button_Init(void); |
extern uint8_t GetButton(void); |
#endif //_BUTTON_H |
/FollowMe/trunk/crc16.c |
---|
0,0 → 1,48 |
#include "crc16.h" |
#include <avr/pgmspace.h> |
const uint16_t crc16tab[256] PROGMEM = |
{ |
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 CRC16(const uint8_t *pBuffer, uint32_t len) |
{ |
uint32_t counter; |
uint16_t crc = 0; |
for( counter = 0; counter < len; counter++) |
crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *pBuffer++)&0x00FF]; |
return crc; |
} |
/FollowMe/trunk/crc16.h |
---|
0,0 → 1,8 |
#ifndef _CRC16_H |
#define _CRC16_H |
#include <inttypes.h> |
extern uint16_t CRC16(const uint8_t * pBuffer, uint32_t len); |
#endif // _CRC16_H |
/FollowMe/trunk/fat16.c |
---|
0,0 → 1,1741 |
#include <string.h> |
#include "printf_P.h" |
#include "timer0.h" |
#include "fat16.h" |
#include "sdc.h" |
#include "uart1.h" |
/* |
FAT16 Drive Layout: |
Description Offset |
Volume Boot Sector Start of Partition |
Fat Tables Start + # of Reserved Sectors |
Root Directory Entry Start + # of Reserved + (# of Sectors Per FAT * 2) |
Data Area (Starts with Cluster #2) Start + # of Reserved + (# of Sectors Per FAT * 2) + ((Maximum Root Directory Entries * 32) / Bytes per Sector) |
*/ |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of an partition entry |
________________________________________________________________________________________________________________________________________ |
Partition Entry is 16 bytes long |
*/ |
typedef struct |
{ |
uint8_t PartitionState; // Current State of Partition (00h=Inactive, 80h=Active) |
uint8_t BeginningHead; // Beginning of Partition - Head |
uint16_t BeginningCylSec; // Beginning of Partition - Cylinder/Sector (See Below) |
uint8_t Type; // Type of Partition (See List Below) |
uint8_t EndHead; // End of Partition - Head |
uint16_t EndCylSec; // End of Partition - Cylinder/Sector |
uint32_t NoSectorsBeforePartition; // Number of Sectors between the MBR and the First Sector in the Partition |
uint32_t NoSectorsPartition ; // Number of Sectors in the Partition |
} __attribute__((packed)) PartitionEntry_t; |
/* |
Coding of Cylinder/Sector words |
Cylinder is 10 bits: [7:0] at [15:8] and [9:8] at [7:6] |
Sector is 5 bits: [5:0] at [5:0] |
*/ |
// Partition Types: |
#define PART_TYPE_UNKNOWN 0x00 |
#define PART_TYPE_FAT12 0x01 |
#define PART_TYPE_XENIX 0x02 |
#define PART_TYPE_FAT16_ST_32_MB 0x04 |
#define PART_TYPE_EXTDOS 0x05 |
#define PART_TYPE_FAT16_LT_32_MB 0x06 |
#define PART_TYPE_NTFS 0x07 |
#define PART_TYPE_FAT32 0x0B |
#define PART_TYPE_FAT32LBA 0x0C |
#define PART_TYPE_FAT16LBA 0x0E |
#define PART_TYPE_EXTDOSLBA 0x0F |
#define PART_TYPE_EISA 0x12 |
#define PART_TYPE_ONTRACK 0x33 |
#define PART_TYPE_NOVELL 0x40 |
#define PART_TYPE_DYNAMIC 0x42 |
#define PART_TYPE_PCIX 0x4B |
#define PART_TYPE_LINUX_SWAP 0x82 |
#define PART_TYPE_LINUX_NATIVE 0x83 |
#define PART_TYPE_LINUX_LVM 0x8E |
#define PART_TYPE_PHOENIXSAVE 0xA0 |
#define PART_TYPE_FREEBSD 0xA5 |
#define PART_TYPE_OPENBSD 0xA6 |
#define PART_TYPE_NETNBSD 0xA9 |
#define PART_TYPE_CPM 0xDB |
#define PART_TYPE_DBFS 0xE0 |
#define PART_TYPE_BBT 0xFF |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of the MasterBootRecord |
________________________________________________________________________________________________________________________________________ |
Master Boot Record is 512 bytes long |
The Master Boot Record is the same for pretty much all Operating Systems. |
It is located on the first Sector of the Hard Drive, at Cylinder 0, Head 0, Sector 1 |
*/ |
typedef struct |
{ |
uint8_t ExecutableCode[446]; // 446 bytes for machine start code |
PartitionEntry_t PartitionEntry1; // 16 bytes for partition entry 1 |
PartitionEntry_t PartitionEntry2; // 16 bytes for partition entry 2 |
PartitionEntry_t PartitionEntry3; // 16 bytes for partition entry 3 |
PartitionEntry_t PartitionEntry4; // 16 bytes for partition entry 4 |
uint16_t ExecutableMarker; // BIOS-Signature (0x55 0xAA) |
} __attribute__((packed)) MBR_Entry_t; |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of the VolumeBootRecord |
________________________________________________________________________________________________________________________________________ |
The Volume Boot Record is 512 bytes long |
This information is located in the first sector of every partition. |
*/ |
typedef struct |
{ |
uint8_t JumpCode[3]; // Jump Code + NOP |
int8_t OEMName[8]; // OEM Name |
uint16_t BytesPerSector; // Bytes Per Sector |
uint8_t SectorsPerCluster; // Sectors Per Cluster |
uint16_t ReservedSectors; // Reserved Sectors |
uint8_t NoFATCopies; // Number of Copies of FAT |
uint16_t MaxRootEntries; // Maximum Root Directory Entries |
uint16_t NoSectorsInPartSml32MB; // Number of Sectors in Partition Smaller than 32 MB |
uint8_t MediaDescriptor; // Media Descriptor (0xF8 for Hard Disks) |
uint16_t SectorsPerFAT; // Sectors Per FAT |
uint16_t SectorsPerTrack; // Sectors Per Track |
uint16_t NoHeads; // Number of Heads |
uint32_t NoHiddenSectors; // Number of Hidden Sectors in Partition |
uint32_t NoSectors; // Number of Sectors in Partition |
uint16_t DriveNo; // Logical Drive Number of Partition |
uint8_t ExtendedSig; // Extended Signature (0x29) |
uint32_t SerialNo; // Serial Number of the Partition |
int8_t VolumeName[11]; // Volume Name of the Partititon |
int8_t FATName[8]; // FAT Name (FAT16) |
uint8_t ExecutableCode[446]; // 446 bytes for machine start code |
uint16_t ExecutableMarker; // Executable Marker (0x55 0xAA) |
} __attribute__((packed)) VBR_Entry_t; |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of an directory entry |
________________________________________________________________________________________________________________________________________ |
Directory entry is 32 bytes. |
*/ |
typedef struct |
{ |
int8_t Name[8]; // 8 bytes name, padded with spaces. |
uint8_t Extension[3]; // 3 bytes extension, padded with spaces. |
uint8_t Attribute; // attribute of the directory entry (unused,archive,read-only,system,directory,volume) |
uint8_t Reserved[10]; // reserved bytes within the directory entry. |
uint32_t DateTime; // date and time of last write access to the file or directory. |
uint16_t StartCluster; // first cluster of the file or directory. |
uint32_t Size; // size of the file or directory in bytes. |
} __attribute__((packed)) DirEntry_t; |
#define SLOT_EMPTY 0x00 // slot has never been used |
#define SLOT_E5 0x05 // the real value is 0xe5 |
#define SLOT_DELETED 0xE5 // file in this slot deleted |
#define ATTR_NONE 0x00 // normal file |
#define ATTR_READONLY 0x01 // file is readonly |
#define ATTR_HIDDEN 0x02 // file is hidden |
#define ATTR_SYSTEM 0x04 // file is a system file |
#define ATTR_VOLUMELABEL 0x08 // entry is a volume label |
#define ATTR_LONG_FILENAME 0x0F // this is a long filename entry |
#define ATTR_SUBDIRECTORY 0x10 // entry is a directory name |
#define ATTR_ARCHIVE 0x20 // file is new or modified |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of an entry within the fileallocationtable. |
________________________________________________________________________________________________________________________________________ |
*/ |
typedef struct |
{ |
uint16_t NextCluster; // the next cluster of the file. |
} __attribute__((packed)) Fat16Entry_t; |
// secial fat entries |
#define FAT16_CLUSTER_FREE 0x0000 |
#define FAT16_CLUSTER_RESERVED 0x0001 |
#define FAT16_CLUSTER_USED_MIN 0x0002 |
#define FAT16_CLUSTER_USED_MAX 0xFFEF |
#define FAT16_CLUSTER_ROOTDIR_MIN 0xFFF0 |
#define FAT16_CLUSTER_ROOTDIR_MAX 0xFFF6 |
#define FAT16_CLUSTER_BAD 0xFFF7 |
#define FAT16_CLUSTER_LAST_MIN 0xFFF8 |
#define FAT16_CLUSTER_LAST_MAX 0xFFFF |
/*****************************************************************************************************************************************/ |
/* */ |
/* Global variables needed for read- or write-acces to the FAT16- filesystem. */ |
/* */ |
/*****************************************************************************************************************************************/ |
#define MBR_SECTOR 0x00 // the masterboot record is located in sector 0. |
#define DIRENTRY_SIZE 32 //bytes |
#define DIRENTRIES_PER_SECTOR BYTES_PER_SECTOR/DIRENTRY_SIZE |
#define FAT16_BYTES 2 |
#define FAT16_ENTRIES_PER_SECTOR BYTES_PER_SECTOR/FAT16_BYTES |
#define FSTATE_UNUSED 0 |
#define FSTATE_USED 1 |
typedef struct |
{ |
uint8_t IsValid; // 0 means invalid, else valid |
uint8_t SectorsPerCluster; // how many sectors does a cluster contain? |
uint8_t FatCopies; // Numbers of copies of the FAT |
uint16_t MaxRootEntries; // Possible number of entries in the root directory. |
uint16_t SectorsPerFat; // how many sectors does a fat16 contain? |
uint32_t FirstFatSector; // sector of the start of the fat |
uint32_t FirstRootDirSector; // sector of the rootdirectory |
uint32_t FirstDataSector; // sector of the first cluster containing data (cluster2). |
uint32_t LastDataSector; // the last data sector of the partition |
} Partition_t; |
Partition_t Partition; // Structure holds partition information |
File_t FilePointer[FILE_MAX_OPEN]; // Allocate Memmoryspace for each filepointer used. |
/****************************************************************************************************************************************/ |
/* Function: FileDateTime(DateTime_t *); */ |
/* */ |
/* Description: This function calculates the DOS date time from a pointer to a time structure. */ |
/* */ |
/* Returnvalue: Returns the DOS date time. */ |
/****************************************************************************************************************************************/ |
uint32_t FileDateTime(DateTime_t * pTimeStruct) |
{ |
uint32_t datetime = 0; |
if((pTimeStruct == 0) || !(pTimeStruct->Valid)) return datetime; |
datetime |= (0x0000007FL & (uint32_t)(pTimeStruct->Year - 1980))<<25; // set year |
datetime |= (0x0000000FL & (uint32_t)(pTimeStruct->Month))<<21; // set month |
datetime |= (0x0000001FL & (uint32_t)(pTimeStruct->Day))<<16; |
datetime |= (0x0000001FL & (uint32_t)(pTimeStruct->Hour))<<11; |
datetime |= (0x0000003FL & (uint32_t)(pTimeStruct->Min))<<5; |
datetime |= (0x0000001FL & (uint32_t)(pTimeStruct->Sec/2)); |
return datetime; |
} |
/****************************************************************************************************************************************/ |
/* Function: LockFilePointer(); */ |
/* */ |
/* Description: This function trys to lock a free file pointer. */ |
/* */ |
/* Returnvalue: Returns the Filepointer on success or 0. */ |
/****************************************************************************************************************************************/ |
File_t * LockFilePointer(void) |
{ |
uint8_t i; |
File_t * File = 0; |
for(i = 0; i < FILE_MAX_OPEN; i++) |
{ |
if(FilePointer[i].State == FSTATE_UNUSED) // found an unused one |
{ |
File = &FilePointer[i]; // set pointer to that entry |
FilePointer[i].State = FSTATE_USED; // mark it as used |
break; |
} |
} |
return(File); |
} |
/****************************************************************************************************************************************/ |
/* Function: UnlockFilePointer(file_t *); */ |
/* */ |
/* Description: This function trys to unlock a file pointer. */ |
/* */ |
/* Returnvalue: Returns 1 if file pointer was freed else 0. */ |
/****************************************************************************************************************************************/ |
uint8_t UnlockFilePointer(File_t * file) |
{ |
uint8_t cnt; |
if(file == NULL) return(0); |
for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++) |
{ |
if(&FilePointer[cnt] == file) // filepointer to be freed found? |
{ |
file->State = FSTATE_UNUSED; |
file->FirstSectorOfFirstCluster = 0; // Sectorpointer to the first sector of the first datacluster of the file. |
file->FirstSectorOfCurrCluster = 0; |
file->SectorOfCurrCluster = 0; // Pointer to the cluster which is edited at the moment. |
file->SectorOfCurrCluster = 0; // The sector which is edited at the moment (cluster_pointer + sector_index). |
file->ByteOfCurrSector = 0; // The bytelocation within the current sector (cluster_pointer + sector_index + byte_index). |
file->Mode = 0; // mode of fileoperation (read,write) |
file->Size = 0; // the size of the opend file in bytes. |
file->Position = 0; // pointer to a character within the file 0 < fileposition < filesize |
file->SectorInCache = 0; // the last sector read, wich is still in the sectorbuffer. |
file->DirectorySector = 0; // the sectorposition where the directoryentry has been made. |
file->DirectoryIndex = 0; // the index to the directoryentry within the specified sector. |
file->Attribute = 0; // the attribute of the file opened. |
file = NULL; |
return(1); |
} |
} |
return(0); |
} |
/****************************************************************************************************************************************/ |
/* Function: SeperateDirName(int8_t*, int8_t*); */ |
/* */ |
/* Description: This function seperates the first dirname from filepath and brings them */ |
/* into the needed format ('test.txt' -> 'TEST TXT') */ |
/* The subpath is the pointer to the remaining substring if the filepath */ |
/* */ |
/* Returnvalue: Return NULL on error or pointer to subpath */ |
/****************************************************************************************************************************************/ |
int8_t* SeperateDirName(const int8_t *filepath, int8_t *dirname) |
{ |
int8_t* subpath = NULL; |
uint8_t readpointer = 0; |
uint8_t writepointer = 0; |
// search subpath from beginning of filepath |
subpath = NULL; |
readpointer = 0; |
if(filepath[0] == '/') readpointer = 1; // ignore first '/' |
while(subpath == NULL) // search the filepath until a subpath was found. |
{ |
if(((filepath[readpointer] == 0) || (filepath[readpointer] == '/'))) // if '/' found or end of filepath reached |
{ |
subpath = (int8_t*)&filepath[readpointer]; // store the position of the first "/" found after the beginning of the filenpath |
} |
readpointer++; |
} |
// clear dirname with spaces |
dirname[11] = 0; // terminate dirname |
for(writepointer = 0; writepointer < 11; writepointer++) dirname[writepointer] = ' '; |
writepointer = 0; |
// start seperating the dirname from the filepath. |
readpointer = 0; |
if(filepath[0] == '/') readpointer = 1; // ignore first '/' |
while( &filepath[readpointer] < subpath) |
{ |
if(writepointer >= 11) return(NULL); // dirname to long |
if(filepath[readpointer] == '.') // seperating dirname and extension. |
{ |
if(writepointer <= 8) |
{ |
readpointer++; // next character in filename |
writepointer = 8; // jump to start of extension |
} |
else return(NULL); // dirbasename to long |
} |
else |
{ |
if((0x60 < filepath[readpointer]) && (filepath[readpointer] < 0x7B)) |
{ |
dirname[writepointer] = (filepath[readpointer] - 0x20); // all characters must be upper case. |
} |
else |
{ |
dirname[writepointer] = filepath[readpointer]; |
} |
readpointer++; |
writepointer++; |
} |
} |
return(subpath); |
} |
/**************************************************************************************************************************************+*/ |
/* Function: Fat16ClusterToSector( uint16_t cluster); */ |
/* */ |
/* Description: This function converts a cluster number given by the fat to the corresponding */ |
/* sector that points to the start of the data area that is represented by the cluster number. */ |
/* */ |
/* Returnvalue: The sector number with the data area of the given cluster */ |
/****************************************************************************************************************************************/ |
uint32_t Fat16ClusterToSector(uint16_t cluster) |
{ |
if(!Partition.IsValid) return 0; |
if (cluster < 2) cluster = 2; // the 0. and 1. cluster in the fat are used for the media descriptor |
return ( (cluster - 2) * Partition.SectorsPerCluster) + Partition.FirstDataSector; // the first data sector is represented by the 2nd cluster |
} |
/****************************************************************************************************************************************/ |
/* Function: SectorToFat16Cluster( uint32_t sector); */ |
/* */ |
/* Description: This function converts a given sector number given to the corresponding */ |
/* cluster number in the fat that represents this data area. */ |
/* */ |
/* Returnvalue: The cluster number representing the data area of the sector. */ |
/****************************************************************************************************************************************/ |
uint16_t SectorToFat16Cluster(uint32_t sector) |
{ |
if(!Partition.IsValid) return 0; |
return ((uint16_t)((sector - Partition.FirstDataSector) / Partition.SectorsPerCluster) + 2); |
} |
/****************************************************************************************************************************************/ |
/* Function: Fat16_Deinit(void); */ |
/* */ |
/* Description: This function uninitializes the fat 16 api */ |
/* */ |
/* Returnvalue: The function returns "0" on success */ |
/****************************************************************************************************************************************/ |
uint8_t Fat16_Deinit(void) |
{ |
int16_t returnvalue = 0; |
uint8_t cnt; |
// declare the filepointers as unused. |
for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++) |
{ |
if(FilePointer[cnt].State == FSTATE_USED) |
{ |
returnvalue += fclose_(&FilePointer[cnt]); // try to close open file pointers |
} |
} |
SDC_Deinit(); // uninitialize interface to sd-card |
Partition.IsValid = 0; // mark data in partition structure as invalid |
return(returnvalue); |
} |
/****************************************************************************************************************************************/ |
/* Function: Fat16_Init(void); */ |
/* */ |
/* Description: This function reads the Masterbootrecord and finds the position of the Volumebootrecord, the FAT and the Rootdir */ |
/* and stores the information in global variables. */ |
/* */ |
/* Returnvalue: The function returns "0" if the filesystem is initialized. */ |
/****************************************************************************************************************************************/ |
uint8_t Fat16_Init(void) |
{ |
uint8_t cnt = 0; |
uint32_t partitionfirstsector; |
VBR_Entry_t *VBR; |
MBR_Entry_t *MBR; |
File_t *file; |
uint8_t result = 0; |
printf("\r\n FAT16 init..."); |
Partition.IsValid = 0; |
// declare the filepointers as unused. |
for(cnt = 0; cnt < FILE_MAX_OPEN; cnt++) |
{ |
FilePointer[cnt].State = FSTATE_UNUSED; |
} |
// set current file pinter to first position in list |
file = &FilePointer[0]; |
// try to initialise the sd-card. |
if(SD_SUCCESS != SDC_Init()) |
{ |
printf("SD-Card could not be initialized."); |
result = 1; |
goto end; |
} |
// SD-Card is initialized successfully |
if(SD_SUCCESS != SDC_GetSector((uint32_t)MBR_SECTOR,file->Cache)) // Read the MasterBootRecord |
{ |
printf("Error reading the MBR."); |
result = 2; |
goto end; |
} |
MBR = (MBR_Entry_t *)file->Cache; // Enter the MBR using the structure MBR_Entry_t. |
if((MBR->PartitionEntry1.Type == PART_TYPE_FAT16_ST_32_MB) || |
(MBR->PartitionEntry1.Type == PART_TYPE_FAT16_LT_32_MB) || |
(MBR->PartitionEntry1.Type == PART_TYPE_FAT16LBA)) |
{ |
// get sector offset 1st partition |
partitionfirstsector = MBR->PartitionEntry1.NoSectorsBeforePartition; |
// Start of Partition is the Volume Boot Sector |
if(SD_SUCCESS != SDC_GetSector(partitionfirstsector,file->Cache)) // Read the volume boot record |
{ |
printf("Error reading the VBR."); |
result = 3; |
goto end; |
} |
} |
else // maybe the medium has no partition assuming sector 0 is the vbr |
{ |
partitionfirstsector = 0; |
} |
VBR = (VBR_Entry_t *) file->Cache; // Enter the VBR using the structure VBR_Entry_t. |
if(VBR->BytesPerSector != BYTES_PER_SECTOR) |
{ |
printf("VBR: Sector size not supported."); |
result = 4; |
goto end; |
} |
Partition.SectorsPerCluster = VBR->SectorsPerCluster; // Number of sectors per cluster. Depends on the memorysize of the sd-card. |
Partition.FatCopies = VBR->NoFATCopies; // Number of fatcopies. |
Partition.MaxRootEntries = VBR->MaxRootEntries; // How many Entries are possible in the rootdirectory (FAT16 allows max. 512 entries). |
Partition.SectorsPerFat = VBR->SectorsPerFAT; // The number of sectors per FAT. |
/* Calculate the sectorpositon of the FAT, the Rootdirectory and the first Datacluster. */ |
// Calculate the position of the FileAllocationTable: |
// Start + # of Reserved Sectors |
Partition.FirstFatSector = (uint32_t)(partitionfirstsector + (uint32_t)(VBR->ReservedSectors)); |
// Calculate the position of the Rootdirectory: |
// Start + # of Reserved Sectors + (# of Sectors Per FAT * # of FAT Copies) |
Partition.FirstRootDirSector = Partition.FirstFatSector + (uint32_t)((uint32_t)Partition.SectorsPerFat*(uint32_t)Partition.FatCopies); |
// Calculate the position of the first datacluster: |
// Start + # of Reserved + (# of Sectors Per FAT * # of FAT Copies) + ((Maximum Root Directory Entries * 32) / Bytes per Sector) |
Partition.FirstDataSector = Partition.FirstRootDirSector + (uint32_t)(Partition.MaxRootEntries>>4); // assuming 512 Byte Per Sector |
// Calculate the last data sector |
if(VBR->NoSectors == 0) |
{ |
printf("VBR: Bad number of sectors."); |
result = 5; |
goto end; |
} |
Partition.LastDataSector = Partition.FirstDataSector + VBR->NoSectors - 1; |
// check for FAT16 in VBR of first partition |
if(!((VBR->FATName[0]=='F') && (VBR->FATName[1]=='A') && (VBR->FATName[2]=='T') && (VBR->FATName[3]=='1')&&(VBR->FATName[4]=='6'))) |
{ |
printf("VBR: Partition ist not FAT16 type."); |
result = 6; |
goto end; |
} |
Partition.IsValid = 1; // mark data in partition structure as valid |
result = 0; |
end: |
if(result != 0) Fat16_Deinit(); |
else printf(" ...ok"); |
return(result); |
} |
/****************************************************************************************************************************************/ |
/* Function: Fat16_IsValid(void); */ |
/* */ |
/* Description: This function return the Fat 15 filesystem state */ |
/* */ |
/* Returnvalue: The function returns "1" on success */ |
/****************************************************************************************************************************************/ |
uint8_t Fat16_IsValid(void) |
{ |
return(Partition.IsValid); |
} |
/****************************************************************************************************************************************/ |
/* Function: ClearCurrCluster(File_t*); */ |
/* */ |
/* Description: This function fills the current cluster with 0. */ |
/* */ |
/* Returnvalue: The function returns 1 on success else 0. */ |
/****************************************************************************************************************************************/ |
uint8_t ClearCurrCluster(File_t * file) |
{ |
uint8_t retvalue = 1; |
uint32_t i; |
if((!Partition.IsValid) || (file == NULL)) return(0); |
for(i = 0; i < BYTES_PER_SECTOR; i++) file->Cache[i] = 0; // clear file cache |
for(i = 0; i < Partition.SectorsPerCluster; i++) |
{ |
file->SectorInCache = file->FirstSectorOfCurrCluster + i; |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) |
{ |
Fat16_Deinit(); |
retvalue = 0; |
} |
} |
return(retvalue); |
} |
/*****************************************************************************************************************************************/ |
/* Function: GetNextCluster(File_t* ); */ |
/* */ |
/* Description: This function finds the next datacluster of the file specified with File *File. */ |
/* */ |
/* Returnvalue: The function returns the next cluster or 0 if the last cluster has already reached. */ |
/*****************************************************************************************************************************************/ |
uint16_t GetNextCluster(File_t * file) |
{ |
uint16_t cluster = 0; |
uint32_t fat_byte_offset, sector, byte; |
Fat16Entry_t * fat; |
if((!Partition.IsValid) || (file == NULL)) return(cluster); |
// if sector is within the data area |
if((Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)&& (file->FirstSectorOfCurrCluster <= Partition.LastDataSector)) |
{ |
// determine current file cluster |
cluster = SectorToFat16Cluster(file->FirstSectorOfCurrCluster); |
// calculate byte offset in the fat for corresponding entry |
fat_byte_offset = ((uint32_t)cluster)<<1; // two FAT bytes (16 bits) for every cluster |
// calculate the sector that contains the current cluster within the fat |
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR); |
// calculate byte offset of the current cluster within that fat sector |
byte = fat_byte_offset % BYTES_PER_SECTOR; |
// read this sector to the file cache |
if(file->SectorInCache != sector) |
{ |
file->SectorInCache = sector; // update sector stored in buffer |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector from sd-card |
{ |
Fat16_Deinit(); |
return (cluster); |
} |
} |
// read the next cluster from cache |
fat = (Fat16Entry_t *)(&(file->Cache[byte])); |
cluster = fat->NextCluster; |
// if last cluster fat entry |
if(FAT16_CLUSTER_LAST_MIN <= cluster) |
{ |
cluster = 0; |
} |
else |
{ |
file->FirstSectorOfCurrCluster = Fat16ClusterToSector(cluster); |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
} |
} |
return(cluster); |
} |
/****************************************************************************************************************************************/ |
/* Function: FindNextFreeCluster(File_t *); */ |
/* */ |
/* Description: This function looks in the fat to find the next free cluster */ |
/* */ |
/* Returnvalue: The function returns the cluster number of the next free cluster found within the fat. */ |
/****************************************************************************************************************************************/ |
uint16_t FindNextFreeCluster(File_t *file) |
{ |
uint32_t fat_sector; // current sector within the fat relative to the first sector of the fat. |
uint32_t curr_sector; // current sector |
uint16_t fat_entry; // index to an fatentry within the actual sector (256 fatentries are possible within one sector). |
uint16_t free_cluster = 0; // next free cluster number. |
Fat16Entry_t * fat; |
if((!Partition.IsValid) || (file == NULL)) return(0); |
// start searching for an empty cluster at the beginning of the fat. |
fat_sector = 0; |
do |
{ |
curr_sector = Partition.FirstFatSector + fat_sector; // calculate sector to read |
file->SectorInCache = curr_sector; // upate the sector number of file cache. |
if( SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector of fat from sd-card. |
{ |
Fat16_Deinit(); |
return(free_cluster); |
} |
fat = (Fat16Entry_t *)file->Cache; // set fat pointer to file cache |
for(fat_entry = 0; fat_entry < FAT16_ENTRIES_PER_SECTOR; fat_entry++) // look for an free cluster at all entries in this sector of the fat. |
{ |
if(fat[fat_entry].NextCluster == FAT16_CLUSTER_FREE) // empty cluster found!! |
{ |
fat[fat_entry].NextCluster = FAT16_CLUSTER_LAST_MAX; // mark this fat-entry as used |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // and save the sector at the sd-card. |
{ |
Fat16_Deinit(); |
return(free_cluster); |
} |
free_cluster = (uint16_t)(fat_sector * FAT16_ENTRIES_PER_SECTOR + (uint32_t)fat_entry); |
fat_entry = FAT16_ENTRIES_PER_SECTOR; // terminate the search for a free cluster in this sector. |
} |
} |
fat_sector++; // continue the search in next fat sector |
// repeat until the end of the fat is reached and no free cluster has been found so far |
}while((fat_sector < Partition.SectorsPerFat) && (!free_cluster)); |
return(free_cluster); |
} |
/****************************************************************************************************************************************************/ |
/* Function: int16_t fseek_(File_t *, int32_t *, uint8_t) */ |
/* */ |
/* Description: This function sets the pointer of the stream relative to the position */ |
/* specified by origin (SEEK_SET, SEEK_CUR, SEEK_END) */ |
/* Returnvalue: Is 1 if seek was successful */ |
/****************************************************************************************************************************************************/ |
int16_t fseek_(File_t * const file, int32_t offset, int16_t origin) |
{ |
int32_t fposition = 0; |
int16_t retvalue = 1; |
if((!Partition.IsValid) || (file == NULL)) return(0); |
switch(origin) |
{ |
case SEEK_SET: // Fileposition relative to the beginning of the file. |
fposition = 0; |
break; |
case SEEK_END: // Fileposition relative to the end of the file. |
fposition = (int32_t)file->Size; |
break; |
case SEEK_CUR: // Fileposition relative to the current position of the file. |
default: |
fposition = file->Position; |
break; |
} |
fposition += offset; |
if((fposition >= 0) && (fposition <= (int32_t)file->Size)) // is the pointer still within the file? |
{ |
// reset file position to start of the file |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
file->Position = 0; |
while(file->Position < fposition) // repeat until the current position is less than target |
{ |
file->Position++; // increment file position |
file->ByteOfCurrSector++; // next byte in current sector |
if(file->ByteOfCurrSector >= BYTES_PER_SECTOR) |
{ |
file->ByteOfCurrSector = 0; // reading at the beginning of new sector. |
file->SectorOfCurrCluster++; // continue reading in next sector |
if(file->SectorOfCurrCluster >= Partition.SectorsPerCluster) // if end of cluster is reached, the next datacluster has to be searched in the FAT. |
{ |
if(GetNextCluster(file)) // Sets the clusterpointer of the file to the next datacluster. |
{ |
file->SectorOfCurrCluster = 0; |
} |
else // the last cluster was allready reached |
{ |
file->SectorOfCurrCluster--; // jump back to the ast sector in the last cluster |
file->ByteOfCurrSector = BYTES_PER_SECTOR; // set ByteOfCurrSector one byte over sector end |
} |
} |
} |
} |
} |
if(file->Position == fposition) retvalue = 0; |
return(retvalue); |
} |
/****************************************************************************************************************************************/ |
/* Function: uint16_t DeleteClusterChain(File *file); */ |
/* */ |
/* Description: This function trances along a cluster chain in the fat and frees all clusters visited. */ |
/* */ |
/****************************************************************************************************************************************/ |
uint8_t DeleteClusterChain(uint16_t StartCluster) |
{ |
uint16_t cluster; |
uint32_t fat_byte_offset, sector, byte; |
Fat16Entry_t * fat; |
uint8_t buffer[BYTES_PER_SECTOR]; |
uint32_t sector_in_buffer = 0; |
uint8_t repeat = 0; |
if(!Partition.IsValid) return 0; |
cluster = StartCluster; // init chain trace |
// calculate byte offset in the fat for corresponding entry |
fat_byte_offset = ((uint32_t)cluster)<<1; // two FAT bytes (16 bits) for every cluster |
// calculate the sector that contains the current cluster within the fat |
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR); |
// calculate byte offset of the current cluster within that fat sector |
byte = fat_byte_offset % BYTES_PER_SECTOR; |
do |
{ |
if(sector != sector_in_buffer) |
{ |
// read this sector to buffer |
sector_in_buffer = sector; |
if(SD_SUCCESS != SDC_GetSector(sector_in_buffer, buffer)) return 0; // read sector from sd-card |
} |
// read the next cluster from cache |
fat = (Fat16Entry_t *)(&(buffer[byte])); |
cluster = fat->NextCluster; |
if((FAT16_CLUSTER_USED_MIN <= cluster) && (cluster <= FAT16_CLUSTER_USED_MAX) ) repeat = 1; |
else repeat = 0; |
fat->NextCluster = FAT16_CLUSTER_FREE; // mark current cluster as free |
// calculate byte offset in the fat for corresponding entry |
fat_byte_offset = ((uint32_t)cluster)<<1; // two FAT bytes (16 bits) for every cluster |
// calculate the sector that contains the current cluster within the fat |
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR); |
// calculate byte offset of the current cluster within that fat sector |
byte = fat_byte_offset % BYTES_PER_SECTOR; |
// if new sector is not the sector in buffer or the last cluster in the chain was traced |
if((sector != sector_in_buffer) || !repeat) |
{ // write sector in buffer |
if(SD_SUCCESS != SDC_PutSector(sector_in_buffer,buffer)) return 0; |
} |
} |
while(repeat); |
return 1; |
} |
/****************************************************************************************************************************************/ |
/* Function: uint16_t AppendCluster(File *file); */ |
/* */ |
/* Description: This function looks in the fat to find the next free cluster and appends it to the file. */ |
/* */ |
/* Returnvalue: The function returns the appened cluster number or 0 of no cluster was appended. */ |
/****************************************************************************************************************************************/ |
uint16_t AppendCluster(File_t *file) |
{ |
uint16_t last_cluster, new_cluster = 0; |
uint32_t fat_byte_offset, sector, byte; |
Fat16Entry_t * fat; |
if((!Partition.IsValid) || (file == NULL)) return(new_cluster); |
new_cluster = FindNextFreeCluster(file); // the next free cluster found on the disk. |
if(new_cluster) |
{ // A free cluster was found and can be added to the end of the file. |
fseek_(file, 0, SEEK_END); // jump to the end of the file |
last_cluster = SectorToFat16Cluster(file->FirstSectorOfCurrCluster); // determine current file cluster |
fat_byte_offset = ((uint32_t)last_cluster)<<1; |
sector = Partition.FirstFatSector + ( fat_byte_offset / BYTES_PER_SECTOR); |
byte = fat_byte_offset % BYTES_PER_SECTOR; |
if(file->SectorInCache != sector) |
{ |
file->SectorInCache = sector; // update sector stored in buffer |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read sector from sd-card |
{ |
Fat16_Deinit(); |
return(0); |
} |
} |
fat = (Fat16Entry_t *)(&(file->Cache[byte])); |
fat->NextCluster = new_cluster; // append the free cluster to the end of the file in the FAT. |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // save the modified sector to the FAT. |
{ |
Fat16_Deinit(); |
return(0); |
} |
file->FirstSectorOfCurrCluster = Fat16ClusterToSector(new_cluster); |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
} |
return(new_cluster); |
} |
/****************************************************************************************************************************************************/ |
/* Function: DirectoryEntryExist(int8_t *, uint8_t, uint8_t, File_t *) */ |
/* */ |
/* Description: This function searches all possible dir entries until the file or directory is found or the end of the directory is reached */ |
/* */ |
/* Returnvalue: This function returns 1 if the directory entry specified was found. */ |
/****************************************************************************************************************************************************/ |
uint8_t DirectoryEntryExist(int8_t *dirname, uint8_t attribfilter, uint8_t attribmask, File_t *file) |
{ |
uint32_t dir_sector, max_dir_sector, curr_sector; |
uint16_t dir_entry = 0; |
uint16_t end_of_directory_not_reached = 0; |
uint8_t i = 0; |
uint8_t direntry_exist = 0; |
DirEntry_t * dir; |
// if incomming pointers are useless return immediatly |
if((!Partition.IsValid) || (file == NULL) || (dirname == NULL)) return(direntry_exist); |
// dir entries can be searched only in filesclusters that have |
// a corresponding dir entry with adir-flag set in its attribute |
// or direct within the root directory area |
file->FirstSectorOfFirstCluster = 0; |
// no current directory exist therefore assume searching in the root |
if(file->DirectorySector == 0) |
{ |
max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR; |
file->FirstSectorOfFirstCluster = Partition.FirstRootDirSector; |
} |
// within the root directory area we can read sectors sequentially until the end of this area |
else if((Partition.FirstRootDirSector <= file->DirectorySector) && (file->DirectorySector < Partition.FirstDataSector)) |
{ |
max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR; |
} |
// within the data clusters we can read sectors sequentially only within the cluster |
else if((Partition.FirstDataSector <= file->DirectorySector) && (file->DirectorySector <= Partition.LastDataSector)) |
{ |
max_dir_sector = Partition.SectorsPerCluster; // limit max secters before next cluster |
} |
else return (direntry_exist); // bad sector range for directory sector of the file |
// if search area is not defined yet |
if(file->FirstSectorOfFirstCluster == 0) |
{ |
// check if the directory entry of current file is existent and has the dir-flag set |
file->SectorInCache = file->DirectorySector; // update the sector number of file cache. |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
{ |
Fat16_Deinit(); |
return(direntry_exist); |
} |
dir = (DirEntry_t *)file->Cache; // set pointer to directory |
switch((uint8_t)dir[file->DirectoryIndex].Name[0]) // check if current directory exist |
{ |
case SLOT_EMPTY: |
case SLOT_DELETED: |
// the directrory pointer of this file points to a deleted or not existen directory |
// therefore no file or subdirectory can be created |
return (direntry_exist); |
break; |
default: // and is a real directory |
if((dir[file->DirectoryIndex].Attribute & ATTR_SUBDIRECTORY) != ATTR_SUBDIRECTORY) |
{ // current file is not a directory therefore no file or subdirectory can be created here |
return (direntry_exist); |
} |
break; |
} |
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(dir[file->DirectoryIndex].StartCluster); |
} |
// update current file data area position to start of first cluster |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
do // loop over all data clusters of the current directory entry |
{ |
dir_sector = 0; // reset sector counter within a new cluster |
do // loop over all sectors of a cluster or all sectors of the root directory |
{ |
curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number |
file->SectorInCache = curr_sector; // upate the sector number of file cache. |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read the sector |
{ |
Fat16_Deinit(); |
return(direntry_exist); |
} |
dir = (DirEntry_t *)file->Cache; // set pointer to directory |
// search all directory entries within that sector |
for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++) |
{ // check for existing dir entry |
switch((uint8_t)dir[dir_entry].Name[0]) |
{ |
case SLOT_EMPTY: |
case SLOT_DELETED: |
// ignore empty or deleted dir entries |
break; |
default: |
// if existing check attributes before names are compared will safe performance |
if ((dir[dir_entry].Attribute & attribmask) != attribfilter) break; // attribute must match |
// then compare the name to the giveb dirname (first 11 characters include 8 chars of basename and 3 chars extension.) |
i = 0; |
while((i < 11) && (dir[dir_entry].Name[i] == dirname[i])) i++; |
if (i < 10) break; // names does not match |
// if dirname and attribute have matched |
file->Attribute = dir[dir_entry].Attribute; // store attribute of found dir entry |
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(dir[dir_entry].StartCluster); // set sector of first data cluster |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
file->DirectorySector = curr_sector; // current sector |
file->DirectoryIndex = dir_entry; // current direntry in current sector |
file->Size = dir[dir_entry].Size; |
direntry_exist = 1; // mark as found |
dir_entry = DIRENTRIES_PER_SECTOR; // stop for-loop |
} // end of first byte of name check |
} |
dir_sector++; // search next sector |
// stop if we reached the end of the cluster or the end of the root dir |
}while((dir_sector < max_dir_sector) && (!direntry_exist)); |
// if we are seaching in the data area and the file not found in this cluster so take next cluster. |
if(!direntry_exist && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)) |
{ |
end_of_directory_not_reached = GetNextCluster(file); // updates File->FirstSectorOfCurrCluster |
} |
}while((end_of_directory_not_reached) && (!direntry_exist)); // repeat until a next cluster exist an no |
return(direntry_exist); |
} |
/****************************************************************************************************************************************/ |
/* Function: CreateDirectoryEntry(int8_t *, uint16_t, File_t *) */ |
/* */ |
/* Description: This function looks for the next free position in the directory and creates an entry. */ |
/* The type of an directory entry is specified by the file attribute. */ |
/* */ |
/* Returnvalue: Return 0 on error */ |
/****************************************************************************************************************************************/ |
uint8_t CreateDirectoryEntry(int8_t *dirname, uint8_t attrib, File_t *file) |
{ |
uint32_t dir_sector, max_dir_sector, curr_sector; |
uint16_t dir_entry = 0; |
uint16_t subdircluster, dircluster = 0; |
uint16_t end_of_directory_not_reached = 0; |
uint8_t i = 0; |
uint8_t retvalue = 0; |
DirEntry_t* dir; |
if((!Partition.IsValid) || (file == NULL) || (dirname == NULL)) return (retvalue); |
// It is not checked here that the dir entry that should be created is already existent! |
// Dir entries can be created only in file-clusters that have |
// the dir-flag set in its attribute or within the root directory |
file->FirstSectorOfFirstCluster = 0; |
// no current directory exist therefore assume creating in the root |
if(file->DirectorySector == 0) |
{ |
max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR; |
dircluster = 0; |
file->FirstSectorOfFirstCluster = Partition.FirstRootDirSector; |
} |
// within the root directory area we can read sectors sequentially until the end of this area |
else if((Partition.FirstRootDirSector <= file->DirectorySector) && (file->DirectorySector < Partition.FirstDataSector)) |
{ |
max_dir_sector = (Partition.MaxRootEntries * DIRENTRY_SIZE)/BYTES_PER_SECTOR; |
} |
// within the data clusters we can read sectors sequentially only within the cluster |
else if((Partition.FirstDataSector <= file->DirectorySector) && (file->DirectorySector <= Partition.LastDataSector)) |
{ |
max_dir_sector = Partition.SectorsPerCluster; |
} |
else return (retvalue); // bad sector range for directory sector of the file |
// if search area is not defined yet |
if(file->FirstSectorOfFirstCluster == 0) |
{ |
// check if the directory entry of current file is existent and has the dir-flag set |
file->SectorInCache = file->DirectorySector; // update the sector number of file cache. |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
{ |
Fat16_Deinit(); |
return(retvalue); |
} |
dir = (DirEntry_t *)file->Cache; // set pointer to directory |
switch((uint8_t)dir[file->DirectoryIndex].Name[0]) // check if current directory exist |
{ |
case SLOT_EMPTY: |
case SLOT_DELETED: |
return (retvalue); |
break; |
default: // and is a real directory |
if((dir[file->DirectoryIndex].Attribute & ATTR_SUBDIRECTORY) != ATTR_SUBDIRECTORY) |
{ // current file is not a directory therefore no file or subdirectory can be created here |
return (retvalue); |
} |
break; |
} |
dircluster = dir[file->DirectoryIndex].StartCluster; |
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(dircluster); |
} |
subdircluster = FindNextFreeCluster(file); // get the next free cluster on the disk and mark it as used. |
if(subdircluster) |
{ |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
file->SectorOfCurrCluster = 0; |
do // loop over all clusters of current directory |
{ |
dir_sector = 0; // reset sector counter within a new cluster |
do // loop over all sectors of a cluster or all sectors of the root directory |
{ |
curr_sector = file->FirstSectorOfCurrCluster + dir_sector; // calculate sector number |
file->SectorInCache = curr_sector; // upate the sector number of file cache. |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
{ |
Fat16_Deinit(); |
return(retvalue); |
} |
dir = (DirEntry_t *)file->Cache; // set pointer to directory |
// search all directory entries of a sector |
for(dir_entry = 0; dir_entry < DIRENTRIES_PER_SECTOR; dir_entry++) |
{ // check if current direntry is available |
if(((uint8_t)dir[dir_entry].Name[0] == SLOT_EMPTY) || ((uint8_t)dir[dir_entry].Name[0] == SLOT_DELETED)) |
{ // a free direntry was found |
for(i = 0; i < 11; i++) dir[dir_entry].Name[i] = dirname[i]; // Set dir name |
dir[dir_entry].Attribute = attrib; // Set the attribute of the new directoryentry. |
dir[dir_entry].StartCluster = subdircluster; // copy the location of the first datacluster to the directoryentry. |
dir[dir_entry].DateTime = FileDateTime(&SystemTime); // set date/time |
dir[dir_entry].Size = 0; // the new createted file has no content yet. |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // write back to card |
{ |
Fat16_Deinit(); |
return(retvalue); |
} |
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(subdircluster); // Calculate absolute sectorposition of first datacluster. |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; // Start reading the file with the first sector of the first datacluster. |
file->SectorOfCurrCluster = 0; // reset sector of cureen cluster |
file->ByteOfCurrSector = 0; // reset the byte location within the current sector |
file->Attribute = attrib; // set file attribute to dir attribute |
file->Size = 0; // new file has no size |
file->DirectorySector = curr_sector; |
file->DirectoryIndex = dir_entry; |
if((attrib & ATTR_SUBDIRECTORY) == ATTR_SUBDIRECTORY) // if a new directory was created then initilize the data area |
{ |
ClearCurrCluster(file); // fill cluster with zeros |
file->SectorInCache = file->FirstSectorOfFirstCluster; |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache))// read in the sector. |
{ |
Fat16_Deinit(); |
return(retvalue); |
} |
dir = (DirEntry_t *)file->Cache; |
// create direntry "." to current dir |
dir[0].Name[0] = 0x2E; |
for(i = 1; i < 11; i++) dir[0].Name[i] = ' '; |
dir[0].Attribute = ATTR_SUBDIRECTORY; |
dir[0].StartCluster = subdircluster; |
dir[0].DateTime = 0; |
dir[0].Size = 0; |
// create direntry ".." to the upper dir |
dir[1].Name[0] = 0x2E; |
dir[1].Name[1] = 0x2E; |
for(i = 2; i < 11; i++) dir[1].Name[i] = ' '; |
dir[1].Attribute = ATTR_SUBDIRECTORY; |
dir[1].StartCluster = dircluster; |
dir[1].DateTime = 0; |
dir[1].Size = 0; |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))// read in the sector. |
{ |
Fat16_Deinit(); |
return(retvalue); |
} |
} |
retvalue = 1; |
dir_entry = DIRENTRIES_PER_SECTOR; // stop for-loop |
} |
} |
dir_sector++; // search next sector |
// stop if we reached the end of the cluster or the end of the root dir |
}while((dir_sector < max_dir_sector) && (!retvalue)); |
// if we are seaching in the data area and the file not found in this cluster so take next cluster. |
if(!retvalue && ( Partition.FirstDataSector <= file->FirstSectorOfCurrCluster)) |
{ |
end_of_directory_not_reached = GetNextCluster(file); // updates File->FirstSectorOfCurrCluster |
} |
}while((end_of_directory_not_reached) && (!retvalue)); |
// Perhaps we are at the end of the last cluster of a directory file an have no free direntry found. |
// Then we would need to add a cluster to that file and create the new direntry there. |
// This code is not implemented yet, because its occurs only if more that 32*32=1024 direntries are |
// within a subdirectory of root. |
} |
return(retvalue); // return 1 if file has been created otherwise return 0. |
} |
/********************************************************************************************************************************************/ |
/* Function: FileExist(const int8_t* filename, uint8_t attribfilter, uint8_t attribmask, File_t *file); */ |
/* */ |
/* Description: This function looks for the specified file including its subdirectories beginning */ |
/* in the rootdirectory of the drive. If the file is found the Filepointer properties are */ |
/* updated. */ |
/* */ |
/* Returnvalue: 1 if file is found else 0. */ |
/********************************************************************************************************************************************/ |
uint8_t FileExist(const int8_t* filename, const uint8_t attribfilter, const uint8_t attribmask, File_t *file) |
{ |
int8_t* path = 0; |
int8_t* subpath = 0; |
uint8_t af, am, file_exist = 0; |
int8_t dirname[12]; // 8+3 + temination character |
// if incomming pointers are useless return immediatly |
if ((filename == NULL) || (file == NULL) || (!Partition.IsValid)) return 0; |
// trace along the filepath |
path = (int8_t*)filename; // start a the beginning of the filename string |
file->DirectorySector = 0; // start at RootDirectory with file search |
file->DirectoryIndex = 0; |
// as long as the file was not found and the remaining path is not empty |
while((*path != 0) && !file_exist) |
{ // separate dirname and subpath from filepath string |
subpath = SeperateDirName(path, dirname); |
if(subpath != NULL) |
{ |
if(*subpath == 0) |
{ // empty subpath indicates last element of dir chain |
af = attribfilter; |
am = attribmask; |
} |
else // it must be a subdirectory and no volume label |
{ |
af = ATTR_SUBDIRECTORY; |
am = ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL; |
} |
if(!DirectoryEntryExist(dirname, af, am, file)) |
{ |
return (file_exist); // subdirectory does not exist |
} |
else |
{ |
if (*subpath == 0) |
{ |
file_exist = 1; // last element of path chain was found with the given attribute filter |
} |
} |
} |
else // error seperating the subpath |
{ |
return file_exist; // bad subdir format |
} |
path = subpath; |
subpath = 0; |
} |
return (file_exist); |
} |
/********************************************************************************************************************************************/ |
/* Function: FileCreate(const s8* filename, u8 attrib, File_t *file); */ |
/* */ |
/* Description: This function looks for the specified file including its subdirectories beginning */ |
/* in the rootdirectory of the partition. If the file is found the Filepointer properties are */ |
/* updated. If file or its subdirectories are not found they will be created */ |
/* */ |
/* Returnvalue: 1 if file was created else 0. */ |
/********************************************************************************************************************************************/ |
uint8_t FileCreate(const int8_t* filename, const uint8_t attrib, File_t *file) |
{ |
int8_t *path = 0; |
int8_t *subpath = 0; |
uint8_t af, am, file_created = 0; |
int8_t dirname[12]; |
// if incomming pointers are useless return immediatly |
if ((filename == NULL) || (file == NULL) || (!Partition.IsValid)) return 0; |
// trace along the filepath |
path = (int8_t*)filename; // start a the beginning of the filename string |
file->DirectorySector = 0; // start at RootDirectory with file search |
file->DirectoryIndex = 0; |
// as long as the file was not created and the remaining file path is not empty |
while((*path != 0) && !file_created) |
{ // separate dirname and subpath from filepath string |
subpath = SeperateDirName(path, dirname); |
if(subpath != NULL) |
{ |
if(*subpath == 0) |
{ // empty subpath indicates last element of dir chain |
af = ATTR_NONE; |
am = ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL; // any file that is no subdir or volume label |
} |
else // it must be a subdirectory and no volume label |
{ |
af = ATTR_SUBDIRECTORY; |
am = ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL; |
} |
if(!DirectoryEntryExist(dirname, af, am, file)) // if subdir or file is not existent |
{ // try to create subdir or file |
if(*subpath == 0) af = attrib; // if last element in dir chain take the given attribute |
if(!CreateDirectoryEntry(dirname, af, file)) |
{ // could not be created |
return(file_created); |
} |
else if (*subpath == 0) file_created = 1; // last element of path chain was created |
} |
} |
else // error seperating the subpath |
{ |
return file_created; // bad subdir format |
} |
path = subpath; |
subpath = 0; |
} |
return (file_created); |
} |
/********************************************************************************************************************************************/ |
/* Function: File_t * fopen_(int8_t* filename, int8_t mode); */ |
/* */ |
/* Description: This function looks for the specified file in the rootdirectory of the drive. If the file is found the number of the */ |
/* corrosponding filepointer is returned. Only modes 'r' (reading) and 'a' append are implemented yet. */ |
/* */ |
/* Returnvalue: The filepointer to the file or 0 if faild. */ |
/********************************************************************************************************************************************/ |
File_t * fopen_(int8_t * const filename, const int8_t mode) |
{ |
File_t *file = 0; |
if((!Partition.IsValid) || (filename == 0)) return(file); |
// Look for an unused filepointer in the file pointer list? |
file = LockFilePointer(); |
// if no unused file pointer was found return 0 |
if(file == NULL) return(file); |
// now we have found a free filepointer and claimed it |
// so let initiate its property values |
file->FirstSectorOfFirstCluster = 0; // Sectorpointer to the first sector of the first datacluster of the file. |
file->FirstSectorOfCurrCluster = 0; // Pointer to the cluster which is edited at the moment. |
file->SectorOfCurrCluster = 0; // The sector which is edited at the moment (cluster_pointer + sector_index). |
file->ByteOfCurrSector = 0; // The bytelocation within the current sector (cluster_pointer + sector_index + byte_index). |
file->Mode = mode; // mode of fileoperation (read,write) |
file->Size = 0; // the size of the opened file in bytes. |
file->Position = 0; // pointer to a byte within the file 0 < fileposition < filesize |
file->SectorInCache = 0; // the last sector read, wich is still in the sectorbuffer. |
file->DirectorySector = 0; // the sectorposition where the directoryentry has been made. |
file->DirectoryIndex = 0; // the index to the directoryentry within the specified sector. |
file->Attribute = 0; // the attribute of the file opened. |
// check if a real file (no directory) to the given filename exist |
if(FileExist(filename, ATTR_NONE, ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL, file)) |
{ // file exist |
switch(mode) // check mode |
{ |
case 'a': // if mode is: append to file |
if((file->Attribute & ATTR_READONLY) == ATTR_READONLY) |
{ // file is marked as readonly --> do not open this file |
fclose_(file); |
file = NULL; |
} |
else |
{ // file is not marked as read only --> goto end of file |
fseek_(file, 0, SEEK_END); // point to the end of the file |
} |
break; |
case 'w': // if mode is: write to file |
if((file->Attribute & ATTR_READONLY) == ATTR_READONLY) |
{ // file is marked as readonly --> do not open this file |
fclose_(file); |
file = NULL; |
} |
else |
{ // file is not marked as read only --> goto start of file |
// free all clusters of that file |
DeleteClusterChain(SectorToFat16Cluster(file->FirstSectorOfFirstCluster)); |
// mar an empy cluster as the last one and store the corresponding sector |
file->FirstSectorOfFirstCluster = Fat16ClusterToSector(FindNextFreeCluster(file)); |
file->FirstSectorOfCurrCluster = file->FirstSectorOfFirstCluster; |
file->SectorOfCurrCluster = 0; |
file->ByteOfCurrSector = 0; |
file->Size = 0; |
file->Position = 0; |
fseek_(file, 0, SEEK_SET); |
} |
break; |
case 'r': // if mode is: read from file |
// goto end of file |
fseek_(file, 0, SEEK_SET); |
break; |
default: // other modes are not supported |
fclose_(file); |
file = NULL; |
break; |
} |
return(file); |
} |
else // file does not exist |
{ |
switch(mode) // check mode |
{ |
case 'a': |
case 'w': // if mode is write or append |
// try to create the file |
if(!FileCreate(filename, ATTR_ARCHIVE, file)) |
{ // if it could not be created |
fclose_(file); |
file = NULL; |
} |
break; |
case 'r': // else opened for 'r' |
default: // of unsupported mode |
fclose_(file); |
file = NULL; |
break; |
} |
return(file); |
} |
// we should never come to this point |
fclose_(file); |
file = NULL; |
return(file); |
} |
/****************************************************************************************************************************************************/ |
/* Function: fflush_(File *); */ |
/* */ |
/* Description: This function writes the data already in the buffer but not yet written to the file. */ |
/* */ |
/* Returnvalue: 0 on success EOF on error */ |
/****************************************************************************************************************************************************/ |
int16_t fflush_(File_t * const file) |
{ |
DirEntry_t *dir; |
if((file == NULL) || (!Partition.IsValid)) return (EOF); |
switch(file->Mode) |
{ |
case 'a': |
case 'w': |
if(file->ByteOfCurrSector > 0) // has data been added to the file? |
{ |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache))// save the data still in the buffer |
{ |
Fat16_Deinit(); |
return(EOF); |
} |
} |
file->SectorInCache = file->DirectorySector; |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) // read the directory entry for this file. |
{ |
Fat16_Deinit(); |
return(EOF); |
} |
dir = (DirEntry_t *)file->Cache; |
dir[file->DirectoryIndex].Size = file->Size; // update file size |
dir[file->DirectoryIndex].DateTime = FileDateTime(&SystemTime); // update date time |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) // write back to sd-card |
{ |
Fat16_Deinit(); |
return(EOF); |
} |
break; |
case 'r': |
default: |
return(EOF); |
break; |
} |
return(0); |
} |
/****************************************************************************************************************************************/ |
/* Function: fclose_(File *file); */ |
/* */ |
/* Description: This function closes the open file by writing the remaining data */ |
/* from the buffer to the device and entering the filesize in the directory entry. */ |
/* */ |
/* Returnvalue: 0 on success EOF on error */ |
/****************************************************************************************************************************************/ |
int16_t fclose_(File_t *file) |
{ |
int16_t returnvalue = EOF; |
if(file == NULL) return(returnvalue); |
returnvalue = fflush_(file); |
UnlockFilePointer(file); |
return(returnvalue); |
} |
/********************************************************************************************************************************************/ |
/* Function: fgetc_(File *file); */ |
/* */ |
/* Description: This function reads and returns one character from the specified file. Is the end of the actual sector reached the */ |
/* next sector of the cluster is read. If the last sector of the cluster read the next cluster will be searched in FAT. */ |
/* */ |
/* Returnvalue: The function returns the character read from the specified memorylocation as u8 casted to s16 or EOF. */ |
/********************************************************************************************************************************************/ |
int16_t fgetc_(File_t * const file) |
{ |
int16_t c = EOF; |
uint32_t curr_sector; |
if( (!Partition.IsValid) || (file == NULL)) return(c); |
// if the end of the file is not reached, get the next character. |
if((0 < file->Size) && ((file->Position+1) < file->Size) ) |
{ |
curr_sector = file->FirstSectorOfCurrCluster; // calculate the sector of the next character to be read. |
curr_sector += file->SectorOfCurrCluster; |
if(file->SectorInCache != curr_sector) |
{ |
file->SectorInCache = curr_sector; |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache,file->Cache)) |
{ |
Fat16_Deinit(); |
return(c); |
} |
} |
c = (int16_t) file->Cache[file->ByteOfCurrSector]; |
file->Position++; // increment file position |
file->ByteOfCurrSector++; // goto next byte in sector |
if(file->ByteOfCurrSector >= BYTES_PER_SECTOR) // if end of sector |
{ |
file->ByteOfCurrSector = 0; // reset byte location |
file->SectorOfCurrCluster++; // next sector |
if(file->SectorOfCurrCluster >= Partition.SectorsPerCluster) // if end of cluster is reached, the next datacluster has to be searched in the FAT. |
{ |
if(GetNextCluster(file)) // Sets the clusterpointer of the file to the next datacluster. |
{ |
file->SectorOfCurrCluster = 0; // start reading new cluster at first sector of the cluster. |
} |
else // the last cluster was allready reached |
{ |
file->SectorOfCurrCluster--; // jump back to the last sector in the last cluster |
file->ByteOfCurrSector = BYTES_PER_SECTOR; // set ByteOfCurrSector one byte over sector end |
} |
} |
} |
} |
return(c); |
} |
/********************************************************************************************************************************************/ |
/* Function: fputc_( const s8 c, File *file); */ |
/* */ |
/* Description: This function writes a byte to the specified file and takes care of writing the necessary FAT- Entries. */ |
/* next sector of the cluster is read. If the last sector of the cluster read the next cluster will be searched in FAT. */ |
/* */ |
/* Returnvalue: The function returns the character written to the stream or EOF on error. */ |
/********************************************************************************************************************************************/ |
int16_t fputc_(const int8_t c, File_t * const file) |
{ |
uint32_t curr_sector = 0; |
if((!Partition.IsValid) || (file == NULL)) return(EOF); |
// If file position equals to file size, then the end of file has reached. |
// In this chase it has to be checked that the ByteOfCurrSector is BYTES_PER_SECTOR |
// and a new cluster should be appended. |
if((file->Position >= file->Size) && (file->ByteOfCurrSector >= BYTES_PER_SECTOR)) |
{ |
if(!AppendCluster(file)) return(EOF); |
} |
curr_sector = file->FirstSectorOfCurrCluster; |
curr_sector += file->SectorOfCurrCluster; |
if(file->SectorInCache != curr_sector) |
{ |
file->SectorInCache = curr_sector; |
if(SD_SUCCESS != SDC_GetSector(file->SectorInCache, file->Cache)) |
{ |
Fat16_Deinit(); |
return(EOF); |
} |
} |
file->Cache[file->ByteOfCurrSector] = (uint8_t)c; // write databyte into the buffer. The byte will be written to the device at once |
if(file->Size == file->Position) file->Size++; // a character has been written to the file so the size is incremented only when the character has been added at the end of the file. |
file->Position++; // the actual positon within the file. |
file->ByteOfCurrSector++; // goto next byte in sector |
if(file->ByteOfCurrSector >= BYTES_PER_SECTOR) // if the end of this sector is reached yet |
{ // save the sector to the sd-card |
if(SD_SUCCESS != SDC_PutSector(file->SectorInCache, file->Cache)) |
{ |
Fat16_Deinit(); |
return(EOF); |
} |
file->ByteOfCurrSector = 0; // reset byte location |
file->SectorOfCurrCluster++; // next sector |
if(file->SectorOfCurrCluster >= Partition.SectorsPerCluster)// if end of cluster is reached, the next datacluster has to be searched in the FAT. |
{ |
if(!GetNextCluster(file)) // Sets the clusterpointer of the file to the next datacluster. |
{ // if current cluster was the last cluster of the file |
if(!AppendCluster(file)) // append a new and free cluster at the end of the file. |
{ |
file->SectorOfCurrCluster--; // jump back to last sector of last cluster |
file->ByteOfCurrSector = BYTES_PER_SECTOR; // set byte location to 1 byte over sector len |
return(EOF); |
} |
} |
else // next cluster |
{ |
file->SectorOfCurrCluster = 0; // start reading new cluster at first sector of the cluster. |
} |
} |
} |
return(0); |
} |
/****************************************************************************************************************************************/ |
/* Function: fread_(void *buffer, uint32_t size, uint32_t count, File *File); */ |
/* */ |
/* Description: This function reads count objects of the specified size */ |
/* from the actual position of the file to the specified buffer. */ |
/* */ |
/* Returnvalue: The function returns the number of objects (not bytes) read from the file. */ |
/****************************************************************************************************************************************/ |
uint32_t fread_(void * const buffer, uint32_t size, uint32_t count, File_t * const file) |
{ |
uint32_t object_cnt = 0; // count the number of objects read from the file. |
uint32_t object_size = 0; // count the number of bytes read from the actual object. |
uint8_t *pbuff = 0; // a pointer to the actual bufferposition. |
uint8_t success = 1; // no error occured during read operation to the file. |
int16_t c; |
if((!Partition.IsValid) || (file == NULL) || (buffer == NULL)) return(0); |
pbuff = (uint8_t *) buffer; // cast the void pointer to an u8 * |
while((object_cnt < count) && success) |
{ |
object_size = size; |
while((size > 0) && success) |
{ |
c = fgetc_(file); |
if(c != EOF) |
{ |
*pbuff = (uint8_t)c; // read a byte from the buffer to the opened file. |
pbuff++; |
size--; |
} |
else // error or end of file reached |
{ |
success = 0; |
} |
} |
if(success) object_cnt++; |
} |
return(object_cnt); // return the number of objects succesfully read from the file |
} |
/****************************************************************************************************************************************/ |
/* Function: fwrite_(void *buffer, uint32_t size, uint32_t count, File *file); */ |
/* */ |
/* Description: This function writes count objects of the specified size */ |
/* from the buffer pointer to the actual position in the file. */ |
/* */ |
/* Returnvalue: The function returns the number of objects (not bytes) read from the file. */ |
/****************************************************************************************************************************************/ |
uint32_t fwrite_(void * const buffer, uint32_t size, uint32_t count, File_t * const file) |
{ |
uint32_t object_cnt = 0; // count the number of objects written to the file. |
uint32_t object_size = 0; // count the number of bytes written from the actual object. |
uint8_t *pbuff = 0; // a pointer to the actual bufferposition. |
uint8_t success = 1; // no error occured during write operation to the file. |
int16_t c; |
if((!Partition.IsValid) || (file == NULL) || (buffer == NULL)) return(0); |
pbuff = (uint8_t *) buffer; // cast the void pointer to an u8 * |
while((object_cnt < count) && success) |
{ |
object_size = size; |
while((size > 0) && success) |
{ |
c = fputc_(*pbuff, file); // write a byte from the buffer to the opened file. |
if(c != EOF) |
{ |
pbuff++; |
size--; |
} |
else |
{ |
success = 0; |
} |
} |
if(success) object_cnt++; |
} |
return(object_cnt); // return the number of objects succesfully written to the file |
} |
/****************************************************************************************************************************************/ |
/* Function: fputs_(const int8_t *string, File_t *File); */ |
/* */ |
/* Description: This function writes a string to the specified file. */ |
/* */ |
/* Returnvalue: The function returns a no negative value or EOF on error. */ |
/****************************************************************************************************************************************/ |
int16_t fputs_(int8_t * const string, File_t * const file) |
{ |
uint8_t i=0; |
int16_t c = 0; |
if((!Partition.IsValid) || (file == NULL) || (string == NULL)) return(0); |
while((string[i] != 0)&& (c != EOF)) |
{ |
c = fputc_(string[i], file); |
i++; |
} |
return(c); |
} |
/****************************************************************************************************************************************/ |
/* Function: fgets_(int8 *, int16_t , File_t *); */ |
/* */ |
/* Description: This function reads a string from the file to the specifies string. */ |
/* */ |
/* Returnvalue: A pointer to the string read from the file or 0 on error. */ |
/****************************************************************************************************************************************/ |
int8_t * fgets_(int8_t * const string, int16_t length, File_t * const file) |
{ |
int8_t *pbuff; |
int16_t c = 0, bytecount; |
if((!Partition.IsValid) || (file == NULL) || (string == NULL) || (length > 1)) return (0); |
pbuff = string; |
bytecount = length; |
while(bytecount > 1) // read the count-1 characters from the file to the string. |
{ |
c = fgetc_(file); // read a character from the opened file. |
switch(c) |
{ |
case 0x0A: |
*pbuff = 0; // set string terminator |
return(string); // stop loop |
break; |
case EOF: |
*pbuff = 0; // set string terminator |
return(0); |
default: |
*pbuff++ = (int8_t)c; // copy byte to string |
bytecount--; |
break; |
} |
} |
*pbuff = 0; |
return(string); |
} |
/****************************************************************************************************************************************/ |
/* Function: fexist_(const int8_t*); */ |
/* */ |
/* Description: This function checks if a file already exist. */ |
/* */ |
/* Returnvalue: 1 if the file exist else 0. */ |
/****************************************************************************************************************************************/ |
uint8_t fexist_(int8_t* const filename) |
{ |
uint8_t exist = 0; |
File_t *file = 0; |
file = LockFilePointer(); |
exist = FileExist(filename, ATTR_NONE, ATTR_SUBDIRECTORY|ATTR_VOLUMELABEL, file); |
UnlockFilePointer(file); |
return(exist); |
} |
/****************************************************************************************************************************************/ |
/* Function: feof_(File_t *File); */ |
/* */ |
/* Description: This function checks wether the end of the file has been reached. */ |
/* */ |
/* Returnvalue: 0 if the end of the file was not reached otherwise 1. */ |
/****************************************************************************************************************************************/ |
uint8_t feof_(File_t *file) |
{ |
if(((file->Position)+1) < (file->Size)) |
{ |
return(0); |
} |
else |
{ |
return(1); |
} |
} |
/FollowMe/trunk/fat16.h |
---|
0,0 → 1,70 |
#ifndef _FAT16_H |
#define _FAT16_H |
//________________________________________________________________________________________________________________________________________ |
// |
// Definitions |
// |
//________________________________________________________________________________________________________________________________________ |
//#define __USE_TIME_DATE_ATTRIBUTE |
#define FILE_MAX_OPEN 3 // The number of files that can accessed simultaneously. |
#define SEEK_SET 0 |
#define SEEK_CUR 1 |
#define SEEK_END 2 |
#define EOF (-1) |
#define BYTES_PER_SECTOR 512 |
/* |
________________________________________________________________________________________________________________________________________ |
Structure of a filepointer |
________________________________________________________________________________________________________________________________________ |
*/ |
typedef struct |
{ |
uint32_t FirstSectorOfFirstCluster; // First sector of the first cluster of the file. |
uint32_t FirstSectorOfCurrCluster; // First sector of the cluster which is edited at the moment. |
uint8_t SectorOfCurrCluster; // The sector within the current cluster. |
uint16_t ByteOfCurrSector; // The byte location within the current sector. |
uint8_t Mode; // Mode of fileoperation (read,write) |
uint32_t Size; // The size of the opend file in bytes. |
uint32_t Position; // Pointer to a character within the file 0 < fileposition < filesize |
uint32_t DirectorySector; // the sectorposition where the directoryentry has been made. |
uint16_t DirectoryIndex; // The index to the directoryentry within the specified sector. |
uint8_t Attribute; // The attribute of the file opened. |
uint8_t Cache[BYTES_PER_SECTOR]; // Cache for read and write operation from or to the sd-card. |
uint32_t SectorInCache; // The last sector read, which is still in the sector cache. |
uint8_t State; // State of the filepointer (used/unused/...) |
} File_t; |
//________________________________________________________________________________________________________________________________________ |
// |
// API to the FAT16 filesystem |
// |
//________________________________________________________________________________________________________________________________________ |
extern uint8_t Fat16_Init(void); |
extern uint8_t Fat16_Deinit(void); |
extern uint8_t Fat16_IsValid(void); |
extern File_t * fopen_(int8_t * const filename, const int8_t mode); |
extern int16_t fclose_(File_t *file); |
extern uint8_t fexist_(int8_t * const filename); |
extern int16_t fflush_(File_t * const file); |
extern int16_t fseek_(File_t * const file, int32_t offset, int16_t origin); |
extern int16_t fgetc_(File_t * const file); |
extern int16_t fputc_(const int8_t c, File_t * const file); |
extern uint32_t fread_(void * const buffer, uint32_t size, uint32_t count, File_t * const file); |
extern uint32_t fwrite_(void *buffer, uint32_t size, uint32_t count, File_t *file); |
extern int16_t fputs_(int8_t * const string, File_t * const file); |
extern int8_t * fgets_(int8_t * const string, const int16_t length, File_t * const file); |
extern uint8_t feof_(File_t * const file); |
#endif //_FAT16_H |
/FollowMe/trunk/gps.c |
---|
0,0 → 1,98 |
#include "gps.h" |
#include "uart0.h" |
#include "main.h" |
#include "timer0.h" |
#define GPS_TIMEOUT 1000 // of ne new gps data arrivw within that time an error ist set |
#define GPS_MINSATS 4 |
//------------------------------------------------------------ |
// copy GPS position from source position to target position |
uint8_t GPS_CopyPosition(GPS_Pos_t * pGPSPosSrc, GPS_Pos_t* pGPSPosTgt) |
{ |
uint8_t retval = 0; |
if((pGPSPosSrc == 0) || (pGPSPosTgt == 0)) return(retval); // bad pointer |
// copy only valid positions |
if(pGPSPosSrc->Status != INVALID) |
{ |
// if the source GPS position is not invalid |
pGPSPosTgt->Longitude = pGPSPosSrc->Longitude; |
pGPSPosTgt->Latitude = pGPSPosSrc->Latitude; |
pGPSPosTgt->Altitude = pGPSPosSrc->Altitude; |
pGPSPosTgt->Status = NEWDATA; // mark data in target position as new |
retval = 1; |
} |
return(retval); |
} |
//------------------------------------------------------------ |
// clear position data |
uint8_t GPS_ClearPosition(GPS_Pos_t * pGPSPos) |
{ |
uint8_t retval = 0; |
if(pGPSPos == 0) return(retval); // bad pointer |
else |
{ |
pGPSPos->Longitude = 0; |
pGPSPos->Latitude = 0; |
pGPSPos->Altitude = 0; |
pGPSPos->Status = INVALID; |
retval = 1; |
} |
return (retval); |
} |
//------------------------------------------------------------ |
// check for new GPS data |
void GPS_Update(void) |
{ |
static uint16_t GPS_Timeout = 0; |
static uint16_t beep_rythm = 0; |
switch(GPSData.Status) |
{ |
case INVALID: |
Error |= ERROR_GPS_RX_TIMEOUT; |
GPS_ClearPosition(&(FollowMe.Position)); // clear followme position |
break; |
case PROCESSED: |
// wait for timeout |
if(CheckDelay(GPS_Timeout)) |
{ |
GPSData.Status = INVALID; |
} |
break; |
case NEWDATA: |
GPS_Timeout = SetDelay(GPS_TIMEOUT); // reset gps timeout |
Error &= ~ERROR_GPS_RX_TIMEOUT; // clear possible error |
beep_rythm++; |
// update data in the follow me message |
if((GPSData.SatFix & SATFIX_3D) && (GPSData.NumOfSats >= GPS_MINSATS)) |
{ |
GPS_CopyPosition(&(GPSData.Position),&(FollowMe.Position)); |
} |
else |
{ |
GPS_ClearPosition(&(FollowMe.Position)); // clear followme position |
} |
// NC like sound on bad gps signals |
if(SysState == STATE_SEND_FOLLOWME) |
{ |
if(!(GPSData.Flags & FLAG_GPSFIXOK) && !(beep_rythm % 5)) BeepTime = 100; |
else if ((GPSData.NumOfSats < GPS_MINSATS) && !(beep_rythm % 5)) BeepTime = 10; |
} |
GPSData.Status = PROCESSED; // set to processed unlocks the buffer for new data |
break; |
default: |
GPSData.Status = INVALID; |
break; |
} |
} |
/FollowMe/trunk/gps.h |
---|
0,0 → 1,7 |
#ifndef _GPS_H |
#define _GPS_H |
#include "ubx.h" |
extern void GPS_Update(void); |
#endif //_GPS_H |
/FollowMe/trunk/gpx.c |
---|
0,0 → 1,380 |
/*#######################################################################################*/ |
/* !!! THIS IS NOT FREE SOFTWARE !!! */ |
/*#######################################################################################*/ |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Copyright (c) 2008 Ingo Busker, Holger Buss |
// + Nur für den privaten Gebrauch |
// + FOR NON COMMERCIAL USE ONLY |
// + www.MikroKopter.com |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Es gilt für das gesamte Projekt (Hardware, Software, Binärfiles, Sourcecode und Dokumentation), |
// + dass eine Nutzung (auch auszugsweise) nur für den privaten (nicht-kommerziellen) Gebrauch zulässig ist. |
// + Sollten direkte oder indirekte kommerzielle Absichten verfolgt werden, ist mit uns (info@mikrokopter.de) Kontakt |
// + bzgl. der Nutzungsbedingungen aufzunehmen. |
// + Eine kommerzielle Nutzung ist z.B.Verkauf von MikroKoptern, Bestückung und Verkauf von Platinen oder Bausätzen, |
// + Verkauf von Luftbildaufnahmen, usw. |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Werden Teile des Quellcodes (mit oder ohne Modifikation) weiterverwendet oder veröffentlicht, |
// + unterliegen sie auch diesen Nutzungsbedingungen und diese Nutzungsbedingungen incl. Copyright müssen dann beiliegen |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Sollte die Software (auch auszugesweise) oder sonstige Informationen des MikroKopter-Projekts |
// + auf anderen Webseiten oder sonstigen Medien veröffentlicht werden, muss unsere Webseite "http://www.mikrokopter.de" |
// + eindeutig als Ursprung verlinkt werden |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Keine Gewähr auf Fehlerfreiheit, Vollständigkeit oder Funktion |
// + Benutzung auf eigene Gefahr |
// + Wir übernehmen keinerlei Haftung für direkte oder indirekte Personen- oder Sachschäden |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Die PORTIERUNG der Software (oder Teile davon) auf andere Systeme (ausser der Hardware von www.mikrokopter.de) ist nur |
// + mit unserer Zustimmung zulässig |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Die Funktion printf_P() unterliegt ihrer eigenen Lizenz und ist hiervon nicht betroffen |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Redistributions of source code (with or without modifications) must retain the above copyright notice, |
// + this list of conditions and the following disclaimer. |
// + * Neither the name of the copyright holders nor the names of contributors may be used to endorse or promote products derived |
// + from this software without specific prior written permission. |
// + * The use of this project (hardware, software, binary files, sources and documentation) is only permitted |
// + for non-commercial use (directly or indirectly) |
// + Commercial use (for excample: selling of MikroKopters, selling of PCBs, assembly, ...) is only permitted |
// + with our written permission |
// + * If sources or documentations are redistributet on other webpages, out webpage (http://www.MikroKopter.de) must be |
// + clearly linked as origin |
// + * PORTING this software (or part of it) to systems (other than hardware from www.mikrokopter.de) is NOT allowed |
// |
// + 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. |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
#include <stdlib.h> |
#include <stdio.h> |
#include <string.h> |
#include "gpx.h" |
#include "gpx_header.h" |
#include "timer0.h" |
//#include "spi_slave.h" |
#include "main.h" |
#include "uart1.h" |
#include "ubx.h" |
//________________________________________________________________________________________________________________________________________ |
// Function: GPX_DocumentInit(GPX_Document_t *) |
// |
// Description: This function initializes the gpx-document for further use. |
// |
// |
// Returnvalue: '1' if document was initialized |
//________________________________________________________________________________________________________________________________________ |
uint8_t GPX_DocumentInit(GPX_Document_t *doc) |
{ |
if(doc->state != GPX_DOC_CLOSED) GPX_DocumentClose(doc); // close file if it was opened before |
doc->state = GPX_DOC_CLOSED; // init state of the gpx-document |
doc->file = NULL; |
return(1); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: GPX_Document_Open(s8 *name, GPX_Document_t *doc); |
// |
// Description: This function opens a new gpx-document with the specified name and creates the document header within the file. |
// |
// |
// Returnvalue: '1' if the gpx-file could be created. |
//________________________________________________________________________________________________________________________________________ |
uint8_t GPX_DocumentOpen(int8_t *name, GPX_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
if(doc == NULL) return(0); |
GPX_DocumentInit(doc); // intialize the document with resetvalues |
doc->file = fopen_(name,'a'); // open a new file with the specified filename on the memorycard. |
if(doc->file != NULL) // could the file be opened? |
{ |
retvalue = 1; // the document could be created on the drive. |
doc->state = GPX_DOC_OPENED; // change document state to opened. At next a placemark has to be opened. |
fwrite_((void*)GPX_DOCUMENT_HEADER, sizeof(GPX_DOCUMENT_HEADER)-1,1,doc->file);// write the gpx-header to the document. |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: DocumentClose(GPX_Document_t *doc); |
// |
// Description: This function closes the document specified by doc. |
// |
// |
// Returnvalue: '1' if the gpx-file could be closed. |
//________________________________________________________________________________________________________________________________________ |
uint8_t GPX_DocumentClose(GPX_Document_t *doc) |
{ |
uint8_t retvalue = 1; |
if(doc == NULL) return(0); |
while(doc->state != GPX_DOC_CLOSED) // close linestring, placemark and document before closing the file on the memorycard |
{ |
switch(doc->state) |
{ |
case GPX_DOC_TRACKSEGMENT_OPENED: |
GPX_TrackSegmentEnd(doc); // write terminating tag to end tracksegment. |
break; |
case GPX_DOC_TRACK_OPENED: // write terminating tag to close track. |
GPX_TrackEnd(doc); |
break; |
case GPX_DOC_OPENED: // close the file on the memorycard |
if(doc->file != NULL) |
{ |
fwrite_((void*)GPX_DOCUMENT_FOOTER, sizeof(GPX_DOCUMENT_FOOTER)-1,1,doc->file); // write the gpx-footer to the document. |
fclose_(doc->file); |
retvalue = 1; |
} |
doc->state = GPX_DOC_CLOSED; |
break; |
default: |
doc->state = GPX_DOC_CLOSED; |
break; |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 GPX_TrackBegin(GPX_Document_t); |
// |
// Description: This function adds a track to the document. |
// |
// |
// Returnvalue: '1' if the track could be opened |
//________________________________________________________________________________________________________________________________________ |
uint8_t GPX_TrackBegin(GPX_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
if(doc->state == GPX_DOC_OPENED) |
{ |
if(doc->file != NULL) |
{ |
doc->state = GPX_DOC_TRACK_OPENED; |
retvalue = 1; |
fwrite_((void*)GPX_TRACK_HEADER, sizeof(GPX_TRACK_HEADER)-1,1,doc->file); |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 GPX_TrackEnd(KML_Document_t *doc) |
// |
// Description: This function ends the track opened before. |
// |
// |
// Returnvalue: 1' if the track could be closed |
//________________________________________________________________________________________________________________________________________ |
uint8_t GPX_TrackEnd(GPX_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
if(doc->state == GPX_DOC_TRACK_OPENED) |
{ |
if(doc->file != NULL) |
{ |
doc->state = GPX_DOC_OPENED; |
fwrite_((void*)GPX_TRACK_FOOTER, sizeof(GPX_TRACK_FOOTER)-1,1,doc->file); |
retvalue = 1; |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 GPX_TrackSegmentBegin(GPX_Document_t *doc); |
// |
// Description: This function starts a track segment. |
// |
// |
// Returnvalue: '1' if the track segement could be started |
//________________________________________________________________________________________________________________________________________ |
uint8_t GPX_TrackSegmentBegin(GPX_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
if(doc->state == GPX_DOC_TRACK_OPENED) |
{ |
if(doc->file != NULL) |
{ |
doc->state = GPX_DOC_TRACKSEGMENT_OPENED; |
fwrite_((void*)GPX_TRACKSEGMENT_HEADER, sizeof(GPX_TRACKSEGMENT_HEADER)-1,1,doc->file); |
retvalue = 1; |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 GPX_TrackSegmentEnd(GPX_Document_t *doc); |
// |
// Description: This function ends the tracksegment opened before. |
// |
// |
// Returnvalue: '1' if the track segment could be terminated |
//________________________________________________________________________________________________________________________________________ |
uint8_t GPX_TrackSegmentEnd(GPX_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
if(doc->state == GPX_DOC_TRACKSEGMENT_OPENED) |
{ |
if(doc->file != NULL) |
{ |
doc->state = GPX_DOC_TRACK_OPENED; |
fwrite_((void*)GPX_TRACKSEGMENT_FOOTER, sizeof(GPX_TRACKSEGMENT_FOOTER)-1,1,doc->file); |
retvalue = 1; |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 GPX_TrackSegementAddPoint(GPS_Pos_t * pGPS_Position ,GPX_Document_t *doc); |
// |
// Description: This function adds a pointof a track segement to the specified document. |
// |
// |
// Returnvalue: '1' if a point was added |
//________________________________________________________________________________________________________________________________________ |
uint8_t GPX_TrackSegementAddPoint(GPX_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
int8_t string[100]; |
if(doc == NULL) return(0); |
if(GPSData.Position.Status != INVALID) |
{ |
if(doc->state == GPX_DOC_TRACKSEGMENT_OPENED) |
{ |
if(doc->file != NULL) |
{ |
int16_t i16_1, i16_2, i16_3; |
uint8_t u8_1, u8_2; |
// write <trkpt> tag |
if(GPSData.Position.Latitude < 0) u8_1 = '-'; |
else u8_1 = '+'; |
i16_1 = abs((int16_t)(GPSData.Position.Latitude/10000000L)); |
i16_2 = abs((int16_t)((GPSData.Position.Latitude%10000000L)/10000L)); |
i16_3 = abs((int16_t)(((GPSData.Position.Latitude%10000000L)%10000L)/10L)); |
sprintf(string, "<trkpt lat=\"%c%d.%.3d%.3d\" ",u8_1, i16_1, i16_2, i16_3); |
fputs_(string, doc->file); |
if(GPSData.Position.Longitude < 0) u8_1 = '-'; |
else u8_1 = '+'; |
i16_1 = abs((int16_t)(GPSData.Position.Longitude/10000000L)); |
i16_2 = abs((int16_t)((GPSData.Position.Longitude%10000000L)/10000L)); |
i16_3 = abs((int16_t)(((GPSData.Position.Longitude%10000000L)%10000L)/10L)); |
sprintf(string, "<lon=\"%c%d.%.3d%.3d\" ",u8_1, i16_1, i16_2, i16_3); |
fputs_(string, doc->file); |
// write <time> tag only at a resolution of one second |
sprintf(string, "<time>%04d-%02d-%02dT%02d:%02d:%02dZ</time>\r\n",SystemTime.Year, SystemTime.Month, SystemTime.Day, SystemTime.Hour, SystemTime.Min, SystemTime.Sec); |
fputs_(string, doc->file); |
// write <sat> tag |
sprintf(string, "<sat>%d</sat>\r\n", GPSData.NumOfSats); |
fputs_(string, doc->file); |
// todo: add <extensions> tag with additional data to be logged |
sprintf(string, "<extensions>\r\n"); |
fputs_(string, doc->file); |
// Course in deg |
i16_1 = (int16_t)(GPSData.Heading/100000L); |
sprintf(string, "<Course>%03d</Course>\r\n", i16_1); |
fputs_(string, doc->file); |
// Ground Speed in cm/s |
sprintf(string, "<GroundSpeed>%d</GroundSpeed>\r\n", (uint16_t)GPSData.Speed_Ground); |
fputs_(string, doc->file); |
// Ubat |
u8_1 = UBat / 10; |
u8_2 = UBat % 10; |
sprintf(string, "<Voltage>%d.%01d</Voltage>\r\n", u8_1, u8_2); |
fputs_(string, doc->file); |
// eof extensions |
sprintf(string, "</extensions>\r\n"); |
fputs_(string, doc->file); |
sprintf(string, "</trkpt>\r\n"); |
fputs_(string, doc->file); |
retvalue = 1; |
} |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 KML_LoggGPSCoordinates(gps_data_t *, KML_Document_t *) |
// |
// Description: This function opens and adds gpscoordinates to an GPX-Document. The document will be opened, if not already done |
// |
// |
// Returnvalue: '1' if an gps coordinate was logged |
//________________________________________________________________________________________________________________________________________ |
uint8_t GPX_LoggGPSCoordinates(GPX_Document_t *doc) |
{ |
uint8_t retval = 0; |
while(doc->state != GPX_DOC_TRACKSEGMENT_OPENED) // automatic create document with default filename on the card. |
{ |
switch(doc->state) |
{ |
case GPX_DOC_CLOSED: // document hasn't been opened yet therefore it will be initialized automatically |
retval = GPX_DocumentOpen("default.gpx",doc); // open the gpx-document with a standardname. |
break; |
case GPX_DOC_OPENED: // if a document has been opened before but no track exists: |
retval = GPX_TrackBegin(doc); |
break; |
case GPX_DOC_TRACK_OPENED: // add tracksegement to the track |
retval = GPX_TrackSegmentBegin(doc); |
break; |
default: |
retval = 0; |
break; |
} |
if(retval != 1) return(retval); // stop on error |
} |
if(doc->state == GPX_DOC_TRACKSEGMENT_OPENED) // if the document was opened add coordinates to the document. |
{ |
retval = GPX_TrackSegementAddPoint(doc); // add a track segment point |
} |
return(retval); |
} |
/FollowMe/trunk/gpx.h |
---|
0,0 → 1,36 |
#ifndef _GPX_H |
#define _GPX_H |
#include "fat16.h" |
#include "gps.h" |
// possible state of a gpx-document |
typedef enum |
{ |
GPX_DOC_CLOSED, |
GPX_DOC_OPENED, |
GPX_DOC_TRACK_OPENED, |
GPX_DOC_TRACKSEGMENT_OPENED, |
GPX_DOC_END |
}GPX_DocState_t; |
// structure of an gpx-document |
typedef struct gpx_doc |
{ |
GPX_DocState_t state; // state of the gpx-document |
File_t *file; // filepointer to the file where the data should be saved. |
} GPX_Document_t; |
uint8_t GPX_LoggGPSCoordinates(GPX_Document_t *); // intializes the gpx-document with standard filename and adds points to the file |
uint8_t GPX_DocumentInit(GPX_Document_t *); // Init the new gpx-document |
uint8_t GPX_DocumentOpen(int8_t *, GPX_Document_t *); // opens a new gpx-document. a new file is created on the sd-memorycard |
uint8_t GPX_DocumentClose(GPX_Document_t *doc); // closes the specified document saving remaining data to the file. |
uint8_t GPX_TrackBegin(GPX_Document_t *doc); // opens a new track within the open gpx-document |
uint8_t GPX_TrackEnd(GPX_Document_t *doc); // ends the actual track |
uint8_t GPX_TrackSegmentBegin(GPX_Document_t *doc); // begins a new tracksegment within the actual track |
uint8_t GPX_TrackSegmentEnd(GPX_Document_t *doc); // ends the actual track segment within the actual track |
uint8_t GPX_TrackSegmentAddPoint(GPX_Document_t *); // adds a point to the tracksegment |
#endif //_GPX_H |
/FollowMe/trunk/gpx_header.h |
---|
0,0 → 1,72 |
//________________________________________________________________________________________________________________________________________ |
// |
// Definition of gpx-header and footer elements for documents |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t GPX_DOCUMENT_HEADER[] = |
{ |
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\r\n" |
"<gpx creator=\"NC\" version=\"1.0\" >\r\n" |
"\r\n" |
"<metadata>\r\n" |
"<link href=\"http://www.mikrokopter.de\">\r\n" |
"<text>MikroKopter</text>\r\n" |
"</link>\r\n" |
"</metadata>\r\n" |
"\r\n" |
}; |
//________________________________________________________________________________________________________________________________________ |
// |
// footer of an gpx-file. |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t GPX_DOCUMENT_FOOTER[] = |
{ |
"</gpx>\r\n" |
}; |
//________________________________________________________________________________________________________________________________________ |
// |
// Header of a track |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t GPX_TRACK_HEADER[] = |
{ |
"<trk>\r\n" |
"<name>Flight</name>\r\n" |
}; |
//________________________________________________________________________________________________________________________________________ |
// |
// Footer of a track |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t GPX_TRACK_FOOTER[] = |
{ |
"</trk>\r\n" |
}; |
//________________________________________________________________________________________________________________________________________ |
// |
// Header of a track segment |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t GPX_TRACKSEGMENT_HEADER[] = |
{ |
"<trkseg>\r\n" |
}; |
//________________________________________________________________________________________________________________________________________ |
// |
// Footer of a track segment |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t GPX_TRACKSEGMENT_FOOTER[] = |
{ |
"</trkseg>\r\n" |
}; |
/FollowMe/trunk/kml.c |
---|
0,0 → 1,380 |
/*#######################################################################################*/ |
/* !!! THIS IS NOT FREE SOFTWARE !!! */ |
/*#######################################################################################*/ |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Copyright (c) 2008 Ingo Busker, Holger Buss |
// + Nur für den privaten Gebrauch |
// + FOR NON COMMERCIAL USE ONLY |
// + www.MikroKopter.com |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Es gilt für das gesamte Projekt (Hardware, Software, Binärfiles, Sourcecode und Dokumentation), |
// + dass eine Nutzung (auch auszugsweise) nur für den privaten (nicht-kommerziellen) Gebrauch zulässig ist. |
// + Sollten direkte oder indirekte kommerzielle Absichten verfolgt werden, ist mit uns (info@mikrokopter.de) Kontakt |
// + bzgl. der Nutzungsbedingungen aufzunehmen. |
// + Eine kommerzielle Nutzung ist z.B.Verkauf von MikroKoptern, Bestückung und Verkauf von Platinen oder Bausätzen, |
// + Verkauf von Luftbildaufnahmen, usw. |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Werden Teile des Quellcodes (mit oder ohne Modifikation) weiterverwendet oder veröffentlicht, |
// + unterliegen sie auch diesen Nutzungsbedingungen und diese Nutzungsbedingungen incl. Copyright müssen dann beiliegen |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Sollte die Software (auch auszugesweise) oder sonstige Informationen des MikroKopter-Projekts |
// + auf anderen Webseiten oder sonstigen Medien veröffentlicht werden, muss unsere Webseite "http://www.mikrokopter.de" |
// + eindeutig als Ursprung verlinkt werden |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Keine Gewähr auf Fehlerfreiheit, Vollständigkeit oder Funktion |
// + Benutzung auf eigene Gefahr |
// + Wir übernehmen keinerlei Haftung für direkte oder indirekte Personen- oder Sachschäden |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Die PORTIERUNG der Software (oder Teile davon) auf andere Systeme (ausser der Hardware von www.mikrokopter.de) ist nur |
// + mit unserer Zustimmung zulässig |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Die Funktion printf_P() unterliegt ihrer eigenen Lizenz und ist hiervon nicht betroffen |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Redistributions of source code (with or without modifications) must retain the above copyright notice, |
// + this list of conditions and the following disclaimer. |
// + * Neither the name of the copyright holders nor the names of contributors may be used to endorse or promote products derived |
// + from this software without specific prior written permission. |
// + * The use of this project (hardware, software, binary files, sources and documentation) is only permitted |
// + for non-commercial use (directly or indirectly) |
// + Commercial use (for excample: selling of MikroKopters, selling of PCBs, assembly, ...) is only permitted |
// + with our written permission |
// + * If sources or documentations are redistributet on other webpages, out webpage (http://www.MikroKopter.de) must be |
// + clearly linked as origin |
// + * PORTING this software (or part of it) to systems (other than hardware from www.mikrokopter.de) is NOT allowed |
// |
// + 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. |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
#include <stdlib.h> |
#include <stdio.h> |
#include "kml.h" |
#include "kml_header.h" |
//________________________________________________________________________________________________________________________________________ |
// Module name: kml.c |
// Compiler used: avr-gcc 3.4.5 |
// Last Modifikation: 22.03.2009 |
// Version: 1.03 |
// Authors: Stephan Busker, Gregor Stobrawa |
// Description: Source files to write gps-coordinates to a file in the kml (keyhole markup language) fileformat |
// Copyright (C) 2007 Stephan Busker |
//........................................................................................................................................ |
// Functions: extern u8 KML_LoggGPSCoordinates(struct str_gps_nav_data , KML_Document_t *); // intializes the kml-document with standard filename and adds points to the file |
// extern u8 KML_DocumentInit(KML_Document_t *doc) // initializes the kml-document to resetvalues. |
// extern u8 KML_DocumentOpen(s8 *, KML_Document_t *); // opens a new kml document. A filename can be specified. |
// extern u8 KML_DocumentClose(KML_Document_t *doc); // closes an open document |
// extern u8 KML_PlaceMarkOpen(KML_Document_t *); // opens a new placemark within the specified document |
// extern u8 KML_PlaceMarkClose(KML_Document_t *); // Closes the placemark |
// extern u8 KML_LineStringBegin(KML_Document_t *); // begins a new line within the actual placemark |
// extern u8 KML_LineStringEnd(KML_Document_t *doc); // ends the actual linestring |
// extern u8 KML_LineStringAddPoint(struct str_gps_nav_data, KML_Document_t *); // adds a new point (gps-coordinates) to the actual linestring |
//........................................................................................................................................ |
// ext. functions: |
// |
//........................................................................................................................................ |
// |
// URL: www.Mikro-Control.de |
// mailto: stephan.busker@mikro-control.de |
//________________________________________________________________________________________________________________________________________ |
//________________________________________________________________________________________________________________________________________ |
// Function: KML_DocumentInit(KML_Document_t *) |
// |
// Description: This function initializes the kml-document for further use. |
// |
// |
// Returnvalue: '1' if document was initialized |
//________________________________________________________________________________________________________________________________________ |
uint8_t KML_DocumentInit(KML_Document_t *doc) |
{ |
doc->state = KML_DOC_CLOSED; // state of the kml-document |
doc->file = NULL; |
return(1); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: KML_Document_Open(void); |
// |
// Description: This function opens a new KML- document with the specified name and creates the document header within the file. |
// |
// |
// Returnvalue: '1' if the KML- file could be created. |
//________________________________________________________________________________________________________________________________________ |
uint8_t KML_DocumentOpen(int8_t *name, KML_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
if(doc == NULL) return(0); |
KML_DocumentInit(doc); // intialize the document with resetvalues |
doc->file = fopen_(name,'a'); // open a new file with the specified filename on the memorycard. |
if(doc->file != NULL) // could the file be opened? |
{ |
retvalue = 1; // the document could be created on the drive. |
doc->state = KML_DOC_OPENED; // change document state to opened. At next a placemark has to be opened. |
fwrite_((void*)KML_DOCUMENT_HEADER, sizeof(KML_DOCUMENT_HEADER)-1,1,doc->file);// write the KML-header to the document. |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: DocumentClose(KML_Document_t *doc); |
// |
// Description: This function closes the document specified by doc. |
// |
// |
// Returnvalue: '1' if the KML- file could be closed. |
//________________________________________________________________________________________________________________________________________ |
uint8_t KML_DocumentClose(KML_Document_t *doc) |
{ |
uint8_t retvalue = 1; |
if(doc == NULL) return(0); |
while(doc->state != KML_DOC_CLOSED) // close linestring, placemark and document before closing the file on the memorycard |
{ |
switch(doc->state) |
{ |
case KML_DOC_LINESTRING_OPENED: |
KML_LineStringEnd(doc); // write terminating tag to end linestring. |
break; |
case KML_DOC_PLACEMARK_OPENED: // write terminating tag to close placemark. |
KML_PlaceMarkClose(doc); |
break; |
case KML_DOC_OPENED: // close the file on the memorycard |
if(doc->file != NULL) |
{ |
fwrite_((void*)KML_DOCUMENT_FOOTER, sizeof(KML_DOCUMENT_FOOTER)-1,1,doc->file); // write the KML- footer to the document. |
fclose_(doc->file); |
retvalue = 1; |
} |
doc->state = KML_DOC_CLOSED; |
break; |
default: |
doc->state = KML_DOC_CLOSED; |
break; |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 KML_PlaceMarkOpen(KML_Document_t *doc); |
// |
// Description: This function adds a placemark to the document. |
// |
// |
// Returnvalue: '1' if the PlaceMark could be opened |
//________________________________________________________________________________________________________________________________________ |
uint8_t KML_PlaceMarkOpen(KML_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
if(doc->state == KML_DOC_OPENED) |
{ |
if(doc->file != NULL) |
{ |
doc->state = KML_DOC_PLACEMARK_OPENED; |
retvalue = 1; |
fwrite_((void*)KML_PLACEMARK_HEADER, sizeof(KML_PLACEMARK_HEADER)-1,1,doc->file); |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 PlaceMarkClose(KML_PlaceMark_t *place, File *file); |
// |
// Description: This function ends the placemark opened before. |
// |
// |
// Returnvalue: 1' if the PlaceMark could be closed |
//________________________________________________________________________________________________________________________________________ |
uint8_t KML_PlaceMarkClose(KML_Document_t *doc) |
{ |
uint8_t retvalue = 0; // close the Placemark-tag of the corosponding document. |
if(doc->state == KML_DOC_PLACEMARK_OPENED) |
{ |
if(doc->file != NULL) |
{ |
doc->state = KML_DOC_OPENED; |
fwrite_((void*)KML_PLACEMARK_FOOTER, sizeof(KML_PLACEMARK_FOOTER)-1,1,doc->file); |
retvalue = 1; |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 LineStringBegin(KML_Document_t *doc); |
// |
// Description: This function ends the placemark opened before. |
// |
// |
// Returnvalue: '1' if the LineString could be started |
//________________________________________________________________________________________________________________________________________ |
uint8_t KML_LineStringBegin(KML_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
if(doc->state == KML_DOC_PLACEMARK_OPENED) |
{ |
if(doc->file != NULL) |
{ |
doc->state = KML_DOC_LINESTRING_OPENED; |
fwrite_((void*)KML_LINESTRING_HEADER, sizeof(KML_LINESTRING_HEADER)-1,1,doc->file); |
retvalue = 1; |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 KML_LineStringEnd(KML_Document_t *doc) |
// |
// Description: This function ends the placemark opened before. |
// |
// |
// Returnvalue: '1' if the LineString could be terminated |
//________________________________________________________________________________________________________________________________________ |
uint8_t KML_LineStringEnd(KML_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
if(doc->state == KML_DOC_LINESTRING_OPENED) |
{ |
if(doc->file != NULL) |
{ |
doc->state = KML_DOC_PLACEMARK_OPENED; |
fwrite_((void*)KML_LINESTRING_FOOTER, sizeof(KML_LINESTRING_FOOTER)-1,1,doc->file); |
retvalue = 1; |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 LineStringAddPoint(gps_data_t, KML_Document_t *doc) |
// |
// Description: This function adds a point to the specified document. |
// |
// |
// Returnvalue: '1' if a ppoint was added could be started |
//________________________________________________________________________________________________________________________________________ |
uint8_t KML_LineStringAddPoint(KML_Document_t *doc) |
{ |
uint8_t retvalue = 0; |
int8_t string[50]; |
// int32_t rel_altitude = 0; |
if(doc == NULL) return(0); |
if(GPSData.Position.Status != INVALID) |
{ |
if(doc->state == KML_DOC_LINESTRING_OPENED) |
{ |
if(doc->file != NULL) |
{ |
int16_t i1, i2, i3; |
uint8_t sign; |
if(GPSData.Position.Longitude < 0) sign = '-'; |
else sign = '+'; |
i1 = abs((int16_t)(GPSData.Position.Longitude/10000000L)); |
i2 = abs((int16_t)((GPSData.Position.Longitude%10000000L)/10000L)); |
i3 = abs((int16_t)(((GPSData.Position.Longitude%10000000L)%10000L)/10L)); |
sprintf(string,"\r\n%c%d.%.3d%.3d,",sign, i1, i2, i3); |
fputs_(string, doc->file); |
if(GPSData.Position.Latitude < 0) sign = '-'; |
else sign = '+'; |
i1 = abs((int16_t)(GPSData.Position.Latitude/10000000L)); |
i2 = abs((int16_t)((GPSData.Position.Latitude%10000000L)/10000L)); |
i3 = abs((int16_t)(((GPSData.Position.Latitude%10000000L)%10000L)/10L)); |
sprintf(string,"%c%d.%.3d%.3d,",sign, i1, i2, i3); |
fputs_(string, doc->file); |
sign = '+'; |
sprintf(string,"%c%d.%.3d",sign, 0, 0); |
fputs_(string, doc->file); |
retvalue = 1; |
} |
} |
} |
return(retvalue); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: u8 KML_LoggGPSCoordinates(gps_data_t *, KML_Document_t *) |
// |
// Description: This function opens adds gpscoordinates to an KML-Document. The document will be opened, if not already done |
// |
// |
// Returnvalue: '1' if an gps coordinate was logged |
//________________________________________________________________________________________________________________________________________ |
uint8_t KML_LoggGPSCoordinates(KML_Document_t *doc) |
{ |
uint8_t retval = 0; |
while(doc->state != KML_DOC_LINESTRING_OPENED) // automatic create document with default filename on the card. |
{ |
switch(doc->state) |
{ |
case KML_DOC_CLOSED: // document hasn't been opened yet therefore it will be initialized automatically |
retval = KML_DocumentOpen("default.kml",doc); // open the kml-document with a standardname. |
break; |
case KML_DOC_OPENED: // if a document has been opened before but no placemark exists: |
retval = KML_PlaceMarkOpen(doc); |
break; |
case KML_DOC_PLACEMARK_OPENED: // add linestring to the placemark |
retval = KML_LineStringBegin(doc); |
break; |
default: |
retval = 0; |
break; |
} |
if(retval != 1) return(retval); // stop on error |
} |
if(doc->state == KML_DOC_LINESTRING_OPENED) // if the document was opened add coordinates to the document. |
{ |
retval = KML_LineStringAddPoint(doc); |
} |
return(retval); |
} |
/FollowMe/trunk/kml.h |
---|
0,0 → 1,37 |
#ifndef _KML_H |
#define _KML_H |
#include "fat16.h" |
#include "gps.h" |
// possible state of an kml-document |
typedef enum |
{ |
KML_DOC_CLOSED, |
KML_DOC_OPENED, |
KML_DOC_PLACEMARK_OPENED, |
KML_DOC_LINESTRING_OPENED, |
KML_DOC_END |
}KML_DocState_t; |
// structure of an kml-document |
typedef struct kml_doc |
{ |
KML_DocState_t state; // state of the kml-document |
File_t *file; // filepointer to the file where the data should be saved. |
} KML_Document_t; |
uint8_t KML_LoggGPSCoordinates(KML_Document_t *); // intializes the kml-document with standard filename and adds points to the file |
uint8_t KML_DocumentInit(KML_Document_t *); // Init the new kml-document |
uint8_t KML_DocumentOpen(int8_t *, KML_Document_t *); // opens a new kml-document. a new file is created on the sd-memorycard |
uint8_t KML_DocumentClose(KML_Document_t *doc); // closes the specified document saving remaining data to the file. |
uint8_t KML_PlaceMarkOpen(KML_Document_t *doc); // opens a new placemark within the open kml-document |
uint8_t KML_PlaceMarkClose(KML_Document_t *doc); // closes the actual placemark |
uint8_t KML_LineStringBegin(KML_Document_t *doc); // begins a new linestring within the actual placemark |
uint8_t KML_LineStringEnd(KML_Document_t *doc); // close the actual linestring within the actual placemark |
uint8_t KML_LineStringAddPoint(KML_Document_t *); // adds a point from the gps (longitude, altitude, height) to the linestring |
#endif //_KML_H |
/FollowMe/trunk/kml_header.h |
---|
0,0 → 1,78 |
//________________________________________________________________________________________________________________________________________ |
// |
// Definition of KML header and footer elements for documents, placemarks and linestrings |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t KML_DOCUMENT_HEADER[] = |
{ |
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" |
"<kml xmlns=\"http://earth.google.com/kml/2.2\">\r\n" |
"<Document>\r\n" |
"<name>Mikrokopter GPS logging</name>\r\n" |
"\r\n" |
"<Style id=\"MK_gps-style\">\r\n" |
"<LineStyle>\r\n" |
"<color>ff0000ff</color>\r\n" |
"<width>2</width>\r\n" |
"</LineStyle>\r\n" |
"</Style>\r\n" |
}; |
//________________________________________________________________________________________________________________________________________ |
// |
// footer of an KML- file. |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t KML_DOCUMENT_FOOTER[] = |
{ |
"</Document>\r\n" |
"</kml>\r\n" |
}; |
//________________________________________________________________________________________________________________________________________ |
// |
// Header of an placemark |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t KML_PLACEMARK_HEADER[] = |
{ |
"<Placemark>\r\n" |
"<name>Flight</name>\r\n" |
"<styleUrl>#MK_gps-style</styleUrl>\r\n" |
}; |
//________________________________________________________________________________________________________________________________________ |
// |
// Footer of an placemark |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t KML_PLACEMARK_FOOTER[] = |
{ |
"</Placemark>\r\n" |
}; |
//________________________________________________________________________________________________________________________________________ |
// |
// Header of an linestring |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t KML_LINESTRING_HEADER[] = |
{ |
"<LineString>\r\n" |
"<tessellate>1</tessellate>\r\n" |
"<altitudeMode>relativeToGround</altitudeMode>\r\n" // also possible "absolute" |
"<coordinates>\r\n" |
}; |
//________________________________________________________________________________________________________________________________________ |
// |
// Footer of an linestring |
// |
//________________________________________________________________________________________________________________________________________ |
const int8_t KML_LINESTRING_FOOTER[] = |
{ |
"\r\n</coordinates>" |
"\r\n</LineString>\r\n" |
}; |
/FollowMe/trunk/led.c |
---|
0,0 → 1,23 |
#include <inttypes.h> |
#include "led.h" |
// initializes the LED control outputs |
void LED_Init(void) |
{ |
#ifdef USE_SDLOGGER |
// set PB0 as output (control of red LED) |
DDRB |= (1<<DDB0); |
LEDRED_OFF; |
#endif |
#ifdef USE_FOLLOWME |
// set PB0 as output (control of green LED) |
DDRB |= (1<<DDB0); |
LEDGRN_OFF; |
// set PB1 as output (control of red LED) |
DDRB |= (1<<DDB1); |
LEDRED_OFF; |
#endif |
} |
/FollowMe/trunk/led.h |
---|
0,0 → 1,29 |
#ifndef _LED_H |
#define _LED_H |
#include <avr/io.h> |
#ifdef USE_SDLOGGER |
#define LEDRED_OFF PORTB |= (1<<PORTB0) |
#define LEDRED_ON PORTB &= ~(1<<PORTB0) |
#define LEDRED_TOGGLE PORTB ^= (1<<PORTB0) |
#define LEDGRN_OFF |
#define LEDGRN_ON |
#define LEDGRN_TOGGLE |
#endif |
#ifdef USE_FOLLOWME |
#define LEDGRN_OFF PORTB |= (1<<PORTB1) |
#define LEDGRN_ON PORTB &= ~(1<<PORTB1) |
#define LEDGRN_TOGGLE PORTB ^= (1<<PORTB1) |
#define LEDRED_OFF PORTB |= (1<<PORTB0) |
#define LEDRED_ON PORTB &= ~(1<<PORTB0) |
#define LEDRED_TOGGLE PORTB ^= (1<<PORTB0) |
#endif |
void LED_Init(void); |
#endif //_LED_H |
/FollowMe/trunk/logging.c |
---|
0,0 → 1,436 |
/*#######################################################################################*/ |
/* !!! THIS IS NOT FREE SOFTWARE !!! */ |
/*#######################################################################################*/ |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Copyright (c) 2008 Ingo Busker, Holger Buss |
// + Nur für den privaten Gebrauch |
// + FOR NON COMMERCIAL USE ONLY |
// + www.MikroKopter.com |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Es gilt für das gesamte Projekt (Hardware, Software, Binärfiles, Sourcecode und Dokumentation), |
// + dass eine Nutzung (auch auszugsweise) nur für den privaten (nicht-kommerziellen) Gebrauch zulässig ist. |
// + Sollten direkte oder indirekte kommerzielle Absichten verfolgt werden, ist mit uns (info@mikrokopter.de) Kontakt |
// + bzgl. der Nutzungsbedingungen aufzunehmen. |
// + Eine kommerzielle Nutzung ist z.B.Verkauf von MikroKoptern, Bestückung und Verkauf von Platinen oder Bausätzen, |
// + Verkauf von Luftbildaufnahmen, usw. |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Werden Teile des Quellcodes (mit oder ohne Modifikation) weiterverwendet oder veröffentlicht, |
// + unterliegen sie auch diesen Nutzungsbedingungen und diese Nutzungsbedingungen incl. Copyright müssen dann beiliegen |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Sollte die Software (auch auszugesweise) oder sonstige Informationen des MikroKopter-Projekts |
// + auf anderen Webseiten oder sonstigen Medien veröffentlicht werden, muss unsere Webseite "http://www.mikrokopter.de" |
// + eindeutig als Ursprung verlinkt werden |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Keine Gewähr auf Fehlerfreiheit, Vollständigkeit oder Funktion |
// + Benutzung auf eigene Gefahr |
// + Wir übernehmen keinerlei Haftung für direkte oder indirekte Personen- oder Sachschäden |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Die PORTIERUNG der Software (oder Teile davon) auf andere Systeme (ausser der Hardware von www.mikrokopter.de) ist nur |
// + mit unserer Zustimmung zulässig |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Die Funktion printf_P() unterliegt ihrer eigenen Lizenz und ist hiervon nicht betroffen |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Redistributions of source code (with or without modifications) must retain the above copyright notice, |
// + this list of conditions and the following disclaimer. |
// + * Neither the name of the copyright holders nor the names of contributors may be used to endorse or promote products derived |
// + from this software without specific prior written permission. |
// + * The use of this project (hardware, software, binary files, sources and documentation) is only permitted |
// + for non-commercial use (directly or indirectly) |
// + Commercial use (for excample: selling of MikroKopters, selling of PCBs, assembly, ...) is only permitted |
// + with our written permission |
// + * If sources or documentations are redistributet on other webpages, out webpage (http://www.MikroKopter.de) must be |
// + clearly linked as origin |
// + * PORTING this software (or part of it) to systems (other than hardware from www.mikrokopter.de) is NOT allowed |
// |
// + 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. |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
#include <stdio.h> |
#include "main.h" |
#include "timer0.h" |
#include "uart1.h" |
#include "kml.h" |
//#include "gpx.h" |
#include "ssc.h" |
#include "settings.h" |
#include "printf_P.h" |
#define LOG_FLUSH_INTERVAL 20000 // 20s |
typedef enum |
{ |
LOGFILE_IDLE, |
LOGFILE_START, |
LOGFILE_CLOSED, |
LOGFILE_OPENED, |
LOGFILE_ERROR |
} logfilestate_t; |
// logger handler prototypes |
logfilestate_t Logging_KML(uint16_t LogDelay); |
//logfilestate_t Logging_GPX(uint16_t LogDelay); |
typedef struct |
{ |
uint16_t KML_Interval; // the kml-log interval (0 = off) |
uint16_t GPX_Interval; // the gpx-log interval (0 = off) |
} LogCfg_t; |
LogCfg_t LogCfg = {500 , 1000}; |
//---------------------------------------------------------------------------------------------------- |
int8_t* GenerateKMLLogFileName(void) |
{ |
static uint16_t filenum = 0; // file name counter |
static int8_t filename[35]; |
static DateTime_t LastTime = {0,0,0,0,0,0,0,0}; |
if(SystemTime.Valid) |
{ |
// if the day has been changed |
if((LastTime.Year != SystemTime.Year) || (LastTime.Month != SystemTime.Month) || (LastTime.Day != SystemTime.Day)) |
{ |
LastTime.Year = SystemTime.Year; |
LastTime.Month = SystemTime.Month; |
LastTime.Day = SystemTime.Day; |
LastTime.Valid = 1; |
filenum = 0; // reset file counter |
} |
sprintf(filename, "LOG/%04i%02i%02i/KML/GPS%05i.KML", SystemTime.Year, SystemTime.Month, SystemTime.Day, filenum); |
filenum++; |
return filename; |
} |
else return NULL; |
} |
//---------------------------------------------------------------------------------------------------- |
/*int8_t* GenerateGPXLogFileName(void) |
{ |
static uint16_t filenum = 0; // file name counter |
static int8_t filename[35]; |
static DateTime_t LastTime = {0,0,0,0,0,0,0,0}; |
if(SystemTime.Valid) |
{ |
// if the day has been changed |
if((LastTime.Year != SystemTime.Year) || (LastTime.Month != SystemTime.Month) || (LastTime.Day != SystemTime.Day)) |
{ |
LastTime.Year = SystemTime.Year; |
LastTime.Month = SystemTime.Month; |
LastTime.Day = SystemTime.Day; |
LastTime.Valid = 1; |
filenum = 0; // reset file counter |
} |
sprintf(filename, "LOG/%04i%02i%02i/GPX/GPS%05i.GPX", SystemTime.Year, SystemTime.Month, SystemTime.Day, filenum); |
filenum++; |
return filename; |
} |
else return NULL; |
}*/ |
//---------------------------------------------------------------------------------------------------- |
// logs the current gps position to a kml file |
logfilestate_t Logging_KML(uint16_t LogDelay) |
{ |
static logfilestate_t logfilestate = LOGFILE_IDLE; // the current logfilestate |
static int8_t* logfilename = NULL; // the pointer to the logfilename |
static uint16_t logtimer = 0, flushtimer = 0; // the log update timer |
static KML_Document_t logfile; // the logfilehandle |
// initialize if LogDelay os zero |
if(!LogDelay) |
{ |
switch(logfilestate) |
{ |
case LOGFILE_OPENED: |
KML_DocumentClose(&logfile); // try to close it |
break; |
default: |
break; |
} |
logfilestate = LOGFILE_IDLE; |
logfilename = NULL; |
KML_DocumentInit(&logfile); |
logtimer = SetDelay(0); // set logtimer to now |
return logfilestate; |
} |
// no init |
if(CheckDelay(logtimer)) |
{ |
logtimer = SetDelay(LogDelay); // standard interval |
if(SysState == STATE_SEND_FOLLOWME) |
{ |
switch(logfilestate) |
{ |
case LOGFILE_IDLE: |
case LOGFILE_CLOSED: |
if((GPSData.Status != INVALID) && (GPSData.Flags & FLAG_GPSFIXOK) && (GPSData.SatFix == SATFIX_3D)) |
{ |
logfilestate = LOGFILE_START; |
} |
break; |
case LOGFILE_START: |
// find unused logfile name |
do |
{ // try to generate a new logfile name |
logfilename = GenerateKMLLogFileName(); |
}while((logfilename != NULL) && fexist_(logfilename)); |
// if logfilename exist |
if(logfilename != NULL) |
{ |
// try to create the log file |
if(KML_DocumentOpen(logfilename, &logfile)) |
{ |
flushtimer = SetDelay(LOG_FLUSH_INTERVAL); |
logfilestate = LOGFILE_OPENED; // goto next step |
printf("\r\nOpening kml-file: %s\r\n",logfilename); |
} |
else // could not be openend |
{ |
logfilestate = LOGFILE_ERROR; |
printf("\r\nError opening kml-file: %s\r\n", logfilename); |
logtimer = SetDelay(10); // try again in open logfile in 10 mili sec |
} |
} |
else |
{ |
logfilestate = LOGFILE_ERROR; |
printf("\r\nError getting free kml-file name\r\n"); |
} |
// else retry in next loop |
break; |
case LOGFILE_OPENED: |
// append new gps log data |
if((GPSData.Status != INVALID) && (GPSData.Flags & FLAG_GPSFIXOK) && (GPSData.SatFix == SATFIX_3D)) |
{ |
if(!KML_LoggGPSCoordinates(&logfile)) |
{ // error logging data |
printf("\r\nError logging to kml-file\r\n"); |
KML_DocumentClose(&logfile); |
logfilestate = LOGFILE_ERROR; |
} |
else // sucessfully logged |
{ |
if(CheckDelay(flushtimer)) |
{ |
flushtimer = SetDelay(LOG_FLUSH_INTERVAL); |
fflush_(logfile.file); |
} |
} |
} |
break; |
case LOGFILE_ERROR: |
break; |
default: |
logfilestate = LOGFILE_IDLE; |
break; |
} |
} // EOF follow me on |
else // follow me off |
{ // close log file if opened |
if(logfilestate == LOGFILE_OPENED) |
{ |
if(KML_DocumentClose(&logfile)) |
{ |
printf("\r\nClosing kml-file\r\n"); |
logfilestate = LOGFILE_CLOSED; |
} |
else // could not be closed |
{ |
printf("\r\nError closing kml-file\r\n"); |
logfilestate = LOGFILE_ERROR; |
} |
} |
} //EOF follow me off |
} // EOF Check LogTimer |
return logfilestate; |
} |
//---------------------------------------------------------------------------------------------------- |
/* |
// logs gps and state info to a gpx file |
logfilestate_t Logging_GPX(uint16_t LogDelay) |
{ |
static logfilestate_t logfilestate = LOGFILE_IDLE; // the current logfilestate |
static int8_t* logfilename = NULL; // the pointer to the logfilename |
static uint16_t logtimer = 0, flushtimer = 0; // the log update timer |
static GPX_Document_t logfile; // the logfilehandle |
// initialize if LogDelay os zero |
if(!LogDelay) |
{ |
switch(logfilestate) |
{ |
case LOGFILE_OPENED: |
GPX_DocumentClose(&logfile); // try to close it |
break; |
default: |
break; |
} |
logfilestate = LOGFILE_IDLE; |
logfilename = NULL; |
GPX_DocumentInit(&logfile); |
logtimer = SetDelay(0); // set logtimer to now |
return logfilestate; |
} |
// no init |
if(CheckDelay(logtimer)) |
{ |
logtimer = SetDelay(LogDelay); // standard interval |
if(SysState == STATE_SEND_FOLLOWME) |
{ |
switch(logfilestate) |
{ |
case LOGFILE_IDLE: |
case LOGFILE_CLOSED: |
//if((GPSData.Status != INVALID) && (GPSData.Flags & FLAG_GPSFIXOK) && (GPSData.SatFix == SATFIX_3D) ) |
{ |
logfilestate = LOGFILE_START; |
} |
break; |
case LOGFILE_START: |
// find unused logfile name |
do |
{ // try to generate a new logfile name |
logfilename = GenerateGPXLogFileName(); |
}while((logfilename != NULL) && fexist_(logfilename)); |
// if logfilename exist |
if(logfilename != NULL) |
{ |
// try to create the log file |
if(GPX_DocumentOpen(logfilename, &logfile)) |
{ |
flushtimer = SetDelay(LOG_FLUSH_INTERVAL); |
logfilestate = LOGFILE_OPENED; // goto next step |
printf("\r\nOpening gpx-file: %s\r\n", logfilename); |
} |
else // could not be openend |
{ |
logfilestate = LOGFILE_ERROR; |
printf("\r\nError opening gpx-file: %s\r\n", logfilename); |
logtimer = SetDelay(10); // try again in open logfile in 10 mili sec |
} |
} |
else |
{ |
logfilestate = LOGFILE_ERROR; |
printf("\r\nError getting free gpx-file name\r\n"); |
} |
// else retry in next loop |
break; |
case LOGFILE_OPENED: |
// append new gps log data |
if((GPSData.Status != INVALID) && (GPSData.Flags & FLAG_GPSFIXOK) && (GPSData.SatFix == SATFIX_3D)) |
{ |
if(!GPX_LoggGPSCoordinates(&logfile)) |
{ // error logging data |
printf("\r\nError logging to gpx-file\r\n"); |
GPX_DocumentClose(&logfile); |
logfilestate = LOGFILE_ERROR; |
} |
else // successful log |
{ |
if(CheckDelay(flushtimer)) |
{ |
flushtimer = SetDelay(LOG_FLUSH_INTERVAL); |
fflush_(logfile.file); |
} |
} |
} |
break; |
case LOGFILE_ERROR: |
break; |
default: |
logfilestate = LOGFILE_IDLE; |
break; |
} |
} // EOF follow me on |
else // follow me off |
{ // close log file if opened |
if(logfilestate == LOGFILE_OPENED) |
{ |
if(GPX_DocumentClose(&logfile)) |
{ |
printf("\r\nClosing gpx-file\r\n"); |
logfilestate = LOGFILE_CLOSED; |
} |
else // could not be closed |
{ |
printf("\r\nError closing gpx-file\r\n"); |
logfilestate = LOGFILE_ERROR; |
} |
} |
} //EOF follow me off |
} // EOF Check LogTimer |
return logfilestate; |
} |
*/ |
//---------------------------------------------------------------------------------------------------- |
// initialize logging |
void Logging_Init(void) |
{ |
LogCfg.KML_Interval = 500; //default |
Settings_GetParamValue(PID_KML_LOGGING, (uint16_t*)&LogCfg.KML_Interval); // overwrite by settings value |
Logging_KML(0); // initialize |
//LogCfg.GPX_Interval = 1000; //default |
//Settings_GetParamValue(PID_GPX_LOGGING, (uint16_t*)&LogCfg.GPX_Interval); // overwrite by settings value |
//Logging_GPX(0); // initialize |
} |
//---------------------------------------------------------------------------------------------------- |
// gobal logging handler |
void Logging_Update(void) |
{ |
static uint16_t logtimer = 0; |
static logfilestate_t logstate = LOGFILE_IDLE; |
if(SD_SWITCH) // a card is in slot |
{ |
if(CheckDelay(logtimer)) |
{ |
logtimer = SetDelay(10); // faster makes no sense |
// call the logger handlers if no error has occured |
if(logstate != LOGFILE_ERROR) logstate = Logging_KML(LogCfg.KML_Interval); |
//if(logstate != LOGFILE_ERROR) logstate = Logging_GPX(LogCfg.GPX_Interval); |
// a logging error has occured |
if(logstate == LOGFILE_ERROR) |
{ |
if(Fat16_IsValid()) // wait for reinizialization of fat16 from outside |
{ |
Logging_Init(); // initialize the logs |
logstate = LOGFILE_IDLE; |
logtimer = SetDelay(10); // try next log in 10 mili sec |
} |
else |
{ // retry in 5 seconds |
logtimer = SetDelay(5000); // try again in 5 sec |
} |
} //EOF logfile error |
} // EOF CheckDelay |
}// EOF Card in Slot |
} |
/FollowMe/trunk/logging.h |
---|
0,0 → 1,7 |
#ifndef _LOGGING_H |
#define _LOGGING_H |
void Logging_Init(void); |
void Logging_Update(void); // logs the current gps position to a kml file |
#endif //_LOGGING_H |
/FollowMe/trunk/main.c |
---|
0,0 → 1,227 |
#include <avr/boot.h> |
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include "main.h" |
#include "timer0.h" |
#include "uart0.h" |
#include "uart1.h" |
#include "fat16.h" |
#include "led.h" |
#include "menu.h" |
#include "printf_P.h" |
#include "analog.h" |
#include "gps.h" |
#include "button.h" |
#include "logging.h" |
#include "settings.h" |
#define FOLLOWME_INTERVAL 1000 // 1 second update |
#define CELLUNDERVOLTAGE 32 // lowest allowed voltage/cell; 32 = 3.2V |
#ifdef USE_FOLLOWME |
int16_t UBat = 120; |
int16_t Zellenzahl = 0; |
int16_t PowerOn = 0; |
int16_t i = 0; |
int16_t delay = 0; |
#endif |
uint16_t Error = 0; |
SysState_t SysState = STATE_UNDEFINED; |
int main (void) |
{ |
static uint16_t FollowMe_Timer = 0; |
// disable interrupts global |
cli(); |
// disable watchdog |
MCUSR &=~(1<<WDRF); |
WDTCSR |= (1<<WDCE)|(1<<WDE); |
WDTCSR = 0; |
// initalize modules |
LED_Init(); |
LEDRED_ON; |
TIMER0_Init(); |
USART0_Init(); |
UBX_Init(); |
USART1_Init(); |
ADC_Init(); |
Button_Init(); |
// enable interrupts global |
sei(); |
LEDRED_OFF; |
LEDGRN_ON; |
// try to initialize the FAT 16 filesystem on the SD-Card |
Fat16_Init(); |
// initialize the settings |
Settings_Init(); |
// initialize logging (needs settings) |
Logging_Init(); |
#ifdef USE_SDLOGGER |
printf("\r\n\r\nHW: SD-Logger"); |
#endif |
#ifdef USE_FOLLOWME |
printf("\r\n\r\nHW: Follow-Me"); |
#endif |
printf("\r\nFollow Me\n\rSoftware:V%d.%d%c ",VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH + 'a'); |
printf("\r\n------------------------------"); |
printf("\r\n"); |
//BeepTime = 2000; |
LCD_Clear(); |
FollowMe_Timer = SetDelay(FOLLOWME_INTERVAL); |
while (1) |
{ |
// get gps data to update the follow me position |
GPS_Update(); |
// update logging |
Logging_Update(); |
// check for button action and change state resectively |
if(GetButton()) |
{ |
BeepTime = 200; |
switch(SysState) |
{ |
case STATE_IDLE: |
if(!Error) SysState = STATE_SEND_FOLLOWME; // activate followme only of no error has occured |
break; |
case STATE_SEND_FOLLOWME: |
SysState = STATE_IDLE; |
break; |
default: |
SysState = STATE_IDLE; |
break; |
} |
} |
// state machine |
switch(SysState) |
{ |
case STATE_SEND_FOLLOWME: |
if(CheckDelay(FollowMe_Timer)) // time for next message? |
{ |
if(FollowMe.Position.Status == NEWDATA) // if new |
{ // update remaining data |
FollowMe_Timer = SetDelay(FOLLOWME_INTERVAL); // reset timer |
FollowMe.Heading = -1; // invalid heading |
FollowMe.ToleranceRadius = 1; // 1 meter |
FollowMe.HoldTime = 60; // go home after 60s without any update |
FollowMe.Event_Flag = 0; // no event |
FollowMe.reserve[0] = 0; // reserve |
FollowMe.reserve[1] = 0; // reserve |
FollowMe.reserve[2] = 0; // reserve |
FollowMe.reserve[3] = 0; // reserve |
Request_SendFollowMe = 1; // triggers serial tranmission |
} |
else // now new position avalable (maybe bad gps signal condition) |
{ |
FollowMe_Timer = SetDelay(FOLLOWME_INTERVAL/4); // reset timer on higer frequency |
} |
LEDGRN_TOGGLE; // indication of active follow me |
} |
break; |
case STATE_IDLE: |
// do nothing |
LEDGRN_ON; |
break; |
default: |
// triger to idle state |
SysState = STATE_IDLE; |
break; |
} |
// restart ADConversion if ready |
if(ADReady) |
{ |
DebugOut.Analog[0] = Adc0; |
DebugOut.Analog[1] = Adc1; |
DebugOut.Analog[2] = Adc2; |
DebugOut.Analog[3] = Adc3; |
DebugOut.Analog[4] = Adc4; |
DebugOut.Analog[5] = Adc5; |
DebugOut.Analog[6] = Adc6; |
DebugOut.Analog[7] = Adc7; |
#ifdef USE_FOLLOWME |
// AVcc = 5V --> 5V = 1024 counts |
// the voltage at the voltage divider reference point is 0.8V less that the UBat |
// because of the silicon diode inbetween. |
// voltage divider R2=10K, R3=3K9 |
// UAdc4 = R3/(R3+R2)*UBat= 3.9/(3.9+10)*UBat = UBat/3.564 |
UBat = (3 * UBat + (64 * Adc4) / 368) / 4; |
DebugOut.Analog[8] = UBat; |
// check for zellenzahl |
if(PowerOn < 100) |
{ |
if(UBat<=84) Zellenzahl = 2; |
else Zellenzahl = 3; |
PowerOn++; |
} |
DebugOut.Analog[16] = Zellenzahl; |
DebugOut.Analog[17] = PowerOn; |
//show recognised Zellenzahl to user |
if(i < Zellenzahl && PowerOn >= 100 && BeepTime == 0 && delay > 1000) |
{ |
BeepTime = 100; |
i++; |
delay = 0; |
} |
if(delay < 1500) delay++; |
// monitor battery undervoltage [...||(UBat<74) as temporary workaround to protect 2s lipo packs] |
if(((UBat < Zellenzahl * CELLUNDERVOLTAGE)||(UBat < 74)) && (PowerOn >= 100)) |
{ // sound for low battery |
BeepModulation = 0x0300; |
if(!BeepTime) |
{ |
//BeepTime = 6000; // 0.6 seconds |
} |
Error |= ERROR_LOW_BAT; |
} |
else |
{ |
Error &= ~ERROR_LOW_BAT; |
} |
#endif |
ADReady = 0; |
ADC_Enable(); // restart ad conversion sequence |
} |
// serial communication |
USART0_ProcessRxData(); |
USART0_TransmitTxData(); |
// indicate error, blinking code tbd. |
if(Error) LEDRED_ON; |
else LEDRED_OFF; |
} |
return (1); |
} |
/FollowMe/trunk/main.h |
---|
0,0 → 1,29 |
#ifndef _MAIN_H |
#define _MAIN_H |
#include <avr/io.h> |
#define SYSCLK F_CPU |
typedef enum |
{ |
STATE_UNDEFINED, |
STATE_IDLE, |
STATE_SEND_FOLLOWME |
} SysState_t; |
#define ERROR_GPS_RX_TIMEOUT 0x0001 |
#define ERROR_LOW_BAT 0x0002 |
extern uint16_t Error; |
extern int16_t UBat; |
extern SysState_t SysState; |
#endif //_MAIN_H |
/FollowMe/trunk/makefile |
---|
0,0 → 1,429 |
#-------------------------------------------------------------------- |
# MCU name |
MCU = atmega644p |
F_CPU = 20000000 |
#------------------------------------------------------------------- |
VERSION_MAJOR = 0 |
VERSION_MINOR = 1 |
VERSION_PATCH = 2 |
VERSION_SERIAL_MAJOR = 10 # Serial Protocol Major Version |
VERSION_SERIAL_MINOR = 0 # Serial Protocol Minor Version |
#------------------------------------------------------------------- |
#OPTIONS |
# Use one of the motor setups |
BOARD = FOLLOWME |
#BOARD = SDLOGGER |
#------------------------------------------------------------------- |
# get SVN revision |
REV := $(shell sh -c "cat .svn/entries | sed -n '4p'") |
ifeq ($(MCU), atmega644p) |
FUSE_SETTINGS = -u -U lfuse:w:0xff:m -U hfuse:w:0xdf:m |
HEX_NAME = MEGA644p_$(BOARD) |
endif |
ifeq ($(F_CPU), 20000000) |
QUARZ = 20MHZ |
endif |
# Output format. (can be srec, ihex, binary) |
FORMAT = ihex |
# Target file name (without extension). |
ifeq ($(VERSION_PATCH), 0) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)a_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 1) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)b_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 2) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)c_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 3) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)d_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 4) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)e_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 5) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)f_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 6) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)g_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 7) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)h_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 8) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)i_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 9) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)j_SVN$(REV) |
endif |
ifeq ($(VERSION_PATCH), 10) |
TARGET = FollowMe_$(HEX_NAME)_V$(VERSION_MAJOR)_$(VERSION_MINOR)k_SVN$(REV) |
endif |
# Optimization level, can be [0, 1, 2, 3, s]. 0 turns off optimization. |
# (Note: 3 is not always the best optimization level. See avr-libc FAQ.) |
OPT = 2 |
########################################################################################################## |
# List C source files here. (C dependencies are automatically generated.) |
SRC = main.c uart0.c uart1.c printf_P.c timer0.c menu.c led.c ubx.c analog.c button.c crc16.c ssc.c sdc.c fat16.c gps.c settings.c logging.c kml.c |
########################################################################################################## |
# List Assembler source files here. |
# Make them always end in a capital .S. Files ending in a lowercase .s |
# will not be considered source files but generated files (assembler |
# output from the compiler), and will be deleted upon "make clean"! |
# Even though the DOS/Win* filesystem matches both .s and .S the same, |
# it will preserve the spelling of the filenames, and gcc itself does |
# care about how the name is spelled on its command-line. |
ASRC = |
# List any extra directories to look for include files here. |
# Each directory must be seperated by a space. |
EXTRAINCDIRS = |
# Optional compiler flags. |
# -g: generate debugging information (for GDB, or for COFF conversion) |
# -O*: optimization level |
# -f...: tuning, see gcc manual and avr-libc documentation |
# -Wall...: warning level |
# -Wa,...: tell GCC to pass this to the assembler. |
# -ahlms: create assembler listing |
CFLAGS = -O$(OPT) \ |
-funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums \ |
-Wall -Wstrict-prototypes \ |
-Wa,-adhlns=$(<:.c=.lst) \ |
$(patsubst %,-I%,$(EXTRAINCDIRS)) |
# Set a "language standard" compiler flag. |
# Unremark just one line below to set the language standard to use. |
# gnu99 = C99 + GNU extensions. See GCC manual for more information. |
#CFLAGS += -std=c89 |
#CFLAGS += -std=gnu89 |
#CFLAGS += -std=c99 |
CFLAGS += -std=gnu99 |
CFLAGS += -DF_CPU=$(F_CPU) -DVERSION_MAJOR=$(VERSION_MAJOR) -DVERSION_MINOR=$(VERSION_MINOR) -DVERSION_PATCH=$(VERSION_PATCH) -DVERSION_SERIAL_MAJOR=$(VERSION_SERIAL_MAJOR) -DVERSION_SERIAL_MINOR=$(VERSION_SERIAL_MINOR) |
ifeq ($(BOARD), FOLLOWME) |
CFLAGS += -DUSE_FOLLOWME |
endif |
ifeq ($(BOARD), SDLOGGER) |
CFLAGS += -DUSE_SDLOGGER |
endif |
# Optional assembler flags. |
# -Wa,...: tell GCC to pass this to the assembler. |
# -ahlms: create listing |
# -gstabs: have the assembler create line number information; note that |
# for use in COFF files, additional information about filenames |
# and function names needs to be present in the assembler source |
# files -- see avr-libc docs [FIXME: not yet described there] |
ASFLAGS = -Wa,-adhlns=$(<:.S=.lst),-gstabs |
# Optional linker flags. |
# -Wl,...: tell GCC to pass this to linker. |
# -Map: create map file |
# --cref: add cross reference to map file |
LDFLAGS = -Wl,-Map=$(TARGET).map,--cref |
# Additional libraries |
# Minimalistic printf version |
#LDFLAGS += -Wl,-u,vfprintf -lprintf_min |
# Floating point printf version (requires -lm below) |
#LDFLAGS += -Wl,-u,vfprintf -lprintf_flt |
# -lm = math library |
LDFLAGS += -lm |
##LDFLAGS += -T./linkerfile/avr5.x |
# Programming support using avrdude. Settings and variables. |
# Programming hardware: alf avr910 avrisp bascom bsd |
# dt006 pavr picoweb pony-stk200 sp12 stk200 stk500 |
# |
# Type: avrdude -c ? |
# to get a full listing. |
# |
#AVRDUDE_PROGRAMMER = dt006 |
#AVRDUDE_PROGRAMMER = stk200 |
#AVRDUDE_PROGRAMMER = ponyser |
AVRDUDE_PROGRAMMER = avrispv2 |
#falls Ponyser ausgewählt wird, muss sich unsere avrdude-Configdatei im Bin-Verzeichnis des Compilers befinden |
#AVRDUDE_PORT = com1 # programmer connected to serial device |
#AVRDUDE_PORT = lpt1 # programmer connected to parallel port |
AVRDUDE_PORT = usb # programmer connected to USB |
#AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex |
AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex $(FUSE_SETTINGS) |
#AVRDUDE_WRITE_EEPROM = -U eeprom:w:$(TARGET).eep |
#avrdude -c avrispv2 -P usb -p m32 -U flash:w:blink.hex |
AVRDUDE_FLAGS = -p $(MCU) -P $(AVRDUDE_PORT) -c $(AVRDUDE_PROGRAMMER) |
# Uncomment the following if you want avrdude's erase cycle counter. |
# Note that this counter needs to be initialized first using -Yn, |
# see avrdude manual. |
#AVRDUDE_ERASE += -y |
# Uncomment the following if you do /not/ wish a verification to be |
# performed after programming the device. |
AVRDUDE_FLAGS += -V |
# Increase verbosity level. Please use this when submitting bug |
# reports about avrdude. See <http://savannah.nongnu.org/projects/avrdude> |
# to submit bug reports. |
#AVRDUDE_FLAGS += -v -v |
# --------------------------------------------------------------------------- |
# Define directories, if needed. |
DIRAVR = c:/winavr |
DIRAVRBIN = $(DIRAVR)/bin |
DIRAVRUTILS = $(DIRAVR)/utils/bin |
DIRINC = . |
DIRLIB = $(DIRAVR)/avr/lib |
# Define programs and commands. |
SHELL = sh |
CC = avr-gcc |
OBJCOPY = avr-objcopy |
OBJDUMP = avr-objdump |
SIZE = avr-size |
# Programming support using avrdude. |
AVRDUDE = avrdude |
REMOVE = rm -f |
COPY = cp |
HEXSIZE = $(SIZE) --target=$(FORMAT) $(TARGET).hex |
ELFSIZE = $(SIZE) -A $(TARGET).elf |
# Define Messages |
# English |
MSG_ERRORS_NONE = Errors: none |
MSG_BEGIN = -------- begin -------- |
MSG_END = -------- end -------- |
MSG_SIZE_BEFORE = Size before: |
MSG_SIZE_AFTER = Size after: |
MSG_COFF = Converting to AVR COFF: |
MSG_EXTENDED_COFF = Converting to AVR Extended COFF: |
MSG_FLASH = Creating load file for Flash: |
MSG_EEPROM = Creating load file for EEPROM: |
MSG_EXTENDED_LISTING = Creating Extended Listing: |
MSG_SYMBOL_TABLE = Creating Symbol Table: |
MSG_LINKING = Linking: |
MSG_COMPILING = Compiling: |
MSG_ASSEMBLING = Assembling: |
MSG_CLEANING = Cleaning project: |
# Define all object files. |
OBJ = $(SRC:.c=.o) $(ASRC:.S=.o) |
# Define all listing files. |
LST = $(ASRC:.S=.lst) $(SRC:.c=.lst) |
# Combine all necessary flags and optional flags. |
# Add target processor to flags. |
#ALL_CFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -I. $(CFLAGS) |
ALL_CFLAGS = -mmcu=$(MCU) -I. $(CFLAGS) |
ALL_ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp $(ASFLAGS) |
# Default target. |
all: begin gccversion sizebefore $(TARGET).elf $(TARGET).hex $(TARGET).eep \ |
$(TARGET).lss $(TARGET).sym sizeafter finished end |
# Eye candy. |
# AVR Studio 3.x does not check make's exit code but relies on |
# the following magic strings to be generated by the compile job. |
begin: |
@echo |
@echo $(MSG_BEGIN) |
finished: |
@echo $(MSG_ERRORS_NONE) |
end: |
@echo $(MSG_END) |
@echo |
# Display size of file. |
sizebefore: |
@if [ -f $(TARGET).elf ]; then echo; echo $(MSG_SIZE_BEFORE); $(ELFSIZE); echo; fi |
sizeafter: |
@if [ -f $(TARGET).elf ]; then echo; echo $(MSG_SIZE_AFTER); $(ELFSIZE); echo; fi |
# Display compiler version information. |
gccversion : |
@$(CC) --version |
# Convert ELF to COFF for use in debugging / simulating in |
# AVR Studio or VMLAB. |
COFFCONVERT=$(OBJCOPY) --debugging \ |
--change-section-address .data-0x800000 \ |
--change-section-address .bss-0x800000 \ |
--change-section-address .noinit-0x800000 \ |
--change-section-address .eeprom-0x810000 |
coff: $(TARGET).elf |
@echo |
@echo $(MSG_COFF) $(TARGET).cof |
$(COFFCONVERT) -O coff-avr $< $(TARGET).cof |
extcoff: $(TARGET).elf |
@echo |
@echo $(MSG_EXTENDED_COFF) $(TARGET).cof |
$(COFFCONVERT) -O coff-ext-avr $< $(TARGET).cof |
# Program the device. |
program: $(TARGET).hex $(TARGET).eep |
$(AVRDUDE) $(AVRDUDE_FLAGS) $(AVRDUDE_WRITE_FLASH) $(AVRDUDE_WRITE_EEPROM) |
# Create final output files (.hex, .eep) from ELF output file. |
%.hex: %.elf |
@echo |
@echo $(MSG_FLASH) $@ |
$(OBJCOPY) -O $(FORMAT) -R .eeprom $< $@ |
%.eep: %.elf |
@echo |
@echo $(MSG_EEPROM) $@ |
-$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \ |
--change-section-lma .eeprom=0 -O $(FORMAT) $< $@ |
# Create extended listing file from ELF output file. |
%.lss: %.elf |
@echo |
@echo $(MSG_EXTENDED_LISTING) $@ |
$(OBJDUMP) -h -S $< > $@ |
# Create a symbol table from ELF output file. |
%.sym: %.elf |
@echo |
@echo $(MSG_SYMBOL_TABLE) $@ |
avr-nm -n $< > $@ |
# Link: create ELF output file from object files. |
.SECONDARY : $(TARGET).elf |
.PRECIOUS : $(OBJ) |
%.elf: $(OBJ) |
@echo |
@echo $(MSG_LINKING) $@ |
$(CC) $(ALL_CFLAGS) $(OBJ) --output $@ $(LDFLAGS) |
# Compile: create object files from C source files. |
%.o : %.c |
@echo |
@echo $(MSG_COMPILING) $< |
$(CC) -c $(ALL_CFLAGS) $< -o $@ |
# Compile: create assembler files from C source files. |
%.s : %.c |
$(CC) -S $(ALL_CFLAGS) $< -o $@ |
# Assemble: create object files from assembler source files. |
%.o : %.S |
@echo |
@echo $(MSG_ASSEMBLING) $< |
$(CC) -c $(ALL_ASFLAGS) $< -o $@ |
# Target: clean project. |
clean: begin clean_list finished end |
clean_list : |
@echo |
@echo $(MSG_CLEANING) |
# $(REMOVE) $(TARGET).hex |
$(REMOVE) $(TARGET).eep |
$(REMOVE) $(TARGET).obj |
$(REMOVE) $(TARGET).cof |
$(REMOVE) $(TARGET).elf |
$(REMOVE) $(TARGET).map |
$(REMOVE) $(TARGET).obj |
$(REMOVE) $(TARGET).a90 |
$(REMOVE) $(TARGET).sym |
$(REMOVE) $(TARGET).lnk |
$(REMOVE) $(TARGET).lss |
$(REMOVE) $(OBJ) |
$(REMOVE) $(LST) |
$(REMOVE) $(SRC:.c=.s) |
$(REMOVE) $(SRC:.c=.d) |
# Automatically generate C source code dependencies. |
# (Code originally taken from the GNU make user manual and modified |
# (See README.txt Credits).) |
# |
# Note that this will work with sh (bash) and sed that is shipped with WinAVR |
# (see the SHELL variable defined above). |
# This may not work with other shells or other seds. |
# |
%.d: %.c |
set -e; $(CC) -MM $(ALL_CFLAGS) $< \ |
| sed 's,\(.*\)\.o[ :]*,\1.o \1.d : ,g' > $@; \ |
[ -s $@ ] || rm -f $@ |
# Remove the '-' if you want to see the dependency files generated. |
-include $(SRC:.c=.d) |
# Listing of phony targets. |
.PHONY : all begin finish end sizebefore sizeafter gccversion coff extcoff \ |
clean clean_list program |
/FollowMe/trunk/menu.c |
---|
0,0 → 1,184 |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Copyright (c) 04.2007 Holger Buss |
// + only for non-profit use |
// + www.MikroKopter.com |
// + see the File "License.txt" for further Informations |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
#include <stdlib.h> |
#include <inttypes.h> |
#include "uart0.h" |
#include "printf_P.h" |
#include "ubx.h" |
#include "timer0.h" |
uint8_t MaxMenuItem = 3; |
uint8_t MenuItem = 0; |
uint8_t RemoteKeys = 0; |
#define KEY1 0x01 |
#define KEY2 0x02 |
#define KEY3 0x04 |
#define KEY4 0x08 |
#define KEY5 0x10 |
#define DISPLAYBUFFSIZE 80 |
int8_t DisplayBuff[DISPLAYBUFFSIZE] = "Hello World"; |
uint8_t DispPtr = 0; |
/************************************/ |
/* Clear LCD Buffer */ |
/************************************/ |
void LCD_Clear(void) |
{ |
uint8_t i; |
for( i = 0; i < DISPLAYBUFFSIZE; i++) DisplayBuff[i] = ' '; |
} |
/************************************/ |
/* Update Menu on LCD */ |
/************************************/ |
// Display with 20 characters in 4 lines |
void LCD_PrintMenu(void) |
{ |
int16_t i1,i2,i3; |
uint8_t sign; |
if(RemoteKeys & KEY1) |
{ |
if(MenuItem) MenuItem--; |
else MenuItem = MaxMenuItem; |
} |
if(RemoteKeys & KEY2) |
{ |
if(MenuItem == MaxMenuItem) MenuItem = 0; |
else MenuItem++; |
} |
if((RemoteKeys & KEY1) && (RemoteKeys & KEY2)) MenuItem = 0; |
LCD_Clear(); |
if(MenuItem > MaxMenuItem) MenuItem = MaxMenuItem; |
// print menu item number in the upper right corner |
if(MenuItem < 10) |
{ |
LCD_printfxy(17,0,"[%i]",MenuItem); |
} |
else |
{ |
LCD_printfxy(16,0,"[%i]",MenuItem); |
} |
switch(MenuItem) |
{ |
case 0:// Version Info Menu Item |
LCD_printfxy(0,0,"+ Follow Me +"); |
#ifdef USE_SDLOGGER |
LCD_printfxy(0,1,"HW: SD-Logger"); |
#endif |
#ifdef USE_FOLLOWME |
LCD_printfxy(0,1,"HW: Follow-Me"); |
#endif |
LCD_printfxy(0,2,"SW: %d.%d%c", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH+'a'); |
LCD_printfxy(0,3," "); |
break; |
case 1: |
if (GPSData.Status == INVALID) |
{ |
LCD_printfxy(0,0,"No GPS data"); |
LCD_printfxy(0,1,"Lon: "); |
LCD_printfxy(0,2,"Lat: "); |
LCD_printfxy(0,3,"Alt: "); |
} |
else // newdata or processed |
{ |
switch (GPSData.SatFix) |
{ |
case SATFIX_NONE: |
LCD_printfxy(0,0,"Sats:%02d Fix:None", GPSData.NumOfSats); |
break; |
case SATFIX_2D: |
LCD_printfxy(0,0,"Sats:%02d Fix:2D ", GPSData.NumOfSats); |
break; |
case SATFIX_3D: |
LCD_printfxy(0,0,"Sats:%02d Fix:3D ", GPSData.NumOfSats); |
break; |
default: |
LCD_printfxy(0,0,"Sats:%02d Fix:?? ", GPSData.NumOfSats); |
break; |
} |
if(GPSData.Position.Longitude < 0) sign = '-'; |
else sign = '+'; |
i1 = abs((int16_t)(GPSData.Position.Longitude/10000000L)); |
i2 = abs((int16_t)((GPSData.Position.Longitude%10000000L)/10000L)); |
i3 = abs((int16_t)(((GPSData.Position.Longitude%10000000L)%10000L)/10L)); |
LCD_printfxy(0,1,"Lon: %c%d.%.3d%.3d deg",sign, i1, i2, i3); |
if(GPSData.Position.Latitude < 0) sign = '-'; |
else sign = '+'; |
i1 = abs((int16_t)(GPSData.Position.Latitude/10000000L)); |
i2 = abs((int16_t)((GPSData.Position.Latitude%10000000L)/10000L)); |
i3 = abs((int16_t)(((GPSData.Position.Latitude%10000000L)%10000L)/10L)); |
LCD_printfxy(0,2,"Lat: %c%d.%.3d%.3d deg",sign, i1, i2, i3); |
if(GPSData.Position.Altitude < 0) sign = '-'; |
else sign = '+'; |
i1 = abs((int16_t)(GPSData.Position.Altitude/1000L)); |
i2 = abs((int16_t)(GPSData.Position.Altitude%1000L)); |
LCD_printfxy(0,3,"Alt: %c%04d.%.03d m",sign, i1, i2); |
} |
break; |
case 2: |
if (GPSData.Status == INVALID) |
{ |
LCD_printfxy(0,0,"No GPS data"); |
LCD_printfxy(0,1,"Speed N: "); |
LCD_printfxy(0,2,"Speed E: "); |
LCD_printfxy(0,3,"Speed T: "); |
} |
else // newdata or processed |
{ |
switch (GPSData.SatFix) |
{ |
case SATFIX_NONE: |
LCD_printfxy(0,0,"Sats:%02d Fix:None", GPSData.NumOfSats); |
break; |
case SATFIX_2D: |
LCD_printfxy(0,0,"Sats:%02d Fix:2D ", GPSData.NumOfSats); |
break; |
case SATFIX_3D: |
LCD_printfxy(0,0,"Sats:%02d Fix:3D ", GPSData.NumOfSats); |
break; |
default: |
LCD_printfxy(0,0,"Sats:%02d Fix:?? ", GPSData.NumOfSats); |
break; |
} |
LCD_printfxy(0,1,"Speed N: %+4d cm/s",(int16_t)GPSData.Speed_North); |
LCD_printfxy(0,2,"Speed E: %+4d cm/s",(int16_t)GPSData.Speed_East); |
LCD_printfxy(0,3,"Speed T: %+4d cm/s",(int16_t)GPSData.Speed_Top); |
} |
break; |
case 3: |
LCD_printfxy(0,0,"GPS UTC Time"); |
if (!SystemTime.Valid) |
{ |
LCD_printfxy(0,1," "); |
LCD_printfxy(0,2," No time data! "); |
LCD_printfxy(0,3," "); |
} |
else // newdata or processed |
{ |
LCD_printfxy(0,1," "); |
LCD_printfxy(0,2,"Date: %02i/%02i/%04i",SystemTime.Month, SystemTime.Day, SystemTime.Year); |
LCD_printfxy(0,3,"Time: %02i:%02i:%02i.%03i", SystemTime.Hour, SystemTime.Min, SystemTime.Sec, SystemTime.mSec); |
} |
break; |
default: MaxMenuItem = MenuItem - 1; |
MenuItem = 0; |
break; |
} |
RemoteKeys = 0; |
} |
/FollowMe/trunk/menu.h |
---|
0,0 → 1,17 |
#ifndef _MENU_H |
#define _MENU_H |
#include <inttypes.h> |
#define DISPLAYBUFFSIZE 80 |
extern void LCD_PrintMenu(void); |
extern void LCD_Clear(void); |
extern int8_t DisplayBuff[DISPLAYBUFFSIZE]; |
extern uint8_t DispPtr; |
extern uint8_t MenuItem; |
extern uint8_t MaxMenuItem; |
extern uint8_t RemoteKeys; |
#endif //_MENU_H |
/FollowMe/trunk/old_macros.h |
---|
0,0 → 1,47 |
/* |
For backwards compatibility only. |
Ingo Busker ingo@mikrocontroller.com |
*/ |
#ifndef cbi |
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) |
#endif |
#ifndef sbi |
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) |
#endif |
#ifndef inb |
#define inb(sfr) _SFR_BYTE(sfr) |
#endif |
#ifndef outb |
#define outb(sfr, val) (_SFR_BYTE(sfr) = (val)) |
#endif |
#ifndef inw |
#define inw(sfr) _SFR_WORD(sfr) |
#endif |
#ifndef outw |
#define outw(sfr, val) (_SFR_WORD(sfr) = (val)) |
#endif |
#ifndef outp |
#define outp(val, sfr) outb(sfr, val) |
#endif |
#ifndef inp |
#define inp(sfr) inb(sfr) |
#endif |
#ifndef BV |
#define BV(bit) _BV(bit) |
#endif |
#ifndef PRG_RDB |
#define PRG_RDB pgm_read_byte |
#endif |
/FollowMe/trunk/printf_P.c |
---|
0,0 → 1,483 |
// Die Funktion printf_P() unterliegt ihrer eigenen Lizenz und ist nicht von der Lizenz für den MikroKopter-Teil unterstellt |
/* |
Copyright (C) 1993 Free Software Foundation |
This file is part of the GNU IO Library. This library 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, or (at your option) |
any later version. |
This library 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 library; see the file COPYING. If not, write to the Free |
Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
As a special exception, if you link this library with files |
compiled with a GNU compiler to produce an executable, this does not cause |
the resulting executable to be covered by the GNU General Public License. |
This exception does not however invalidate any other reasons why |
the executable file might be covered by the GNU General Public License. */ |
/* |
* Copyright (c) 1990 Regents of the University of California. |
* All rights reserved. |
* |
* Redistribution and use in source and binary forms, with or without |
* modification, are permitted provided that the following conditions |
* are met: |
* 1. Redistributions of source code must retain the above copyright |
* notice, this list of conditions and the following disclaimer. |
* 2. 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. |
* 3. [rescinded 22 July 1999] |
* 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. |
*/ |
/****************************************************************************** |
This file is a patched version of printf called _printf_P |
It is made to work with avr-gcc for Atmel AVR MCUs. |
There are some differences from standard printf: |
1. There is no floating point support (with fp the code is about 8K!) |
2. Return type is void |
3. Format string must be in program memory (by using macro printf this is |
done automaticaly) |
4. %n is not implemented (just remove the comment around it if you need it) |
5. If LIGHTPRINTF is defined, the code is about 550 bytes smaller and the |
folowing specifiers are disabled : |
space # * . - + p s o O |
6. A function void uart_sendchar(char c) is used for output. The UART must |
be initialized before using printf. |
Alexander Popov |
sasho@vip.orbitel.bg |
******************************************************************************/ |
/* |
* Actual printf innards. |
* |
* This code is large and complicated... |
*/ |
#include <string.h> |
#ifdef __STDC__ |
#include <stdarg.h> |
#else |
#include <varargs.h> |
#endif |
#include "old_macros.h" |
#include "printf_P.h" |
#include "menu.h" |
#include "uart0.h" |
//#define LIGHTPRINTF |
char PrintZiel; |
char Putchar(char zeichen) |
{ |
if(PrintZiel == OUT_LCD) { DisplayBuff[DispPtr++] = zeichen; return(1);} |
else return(uart_putchar(zeichen)); |
} |
void PRINT(const char * ptr, unsigned int len) |
{ |
for(;len;len--) Putchar(*ptr++); |
} |
void PRINTP(const char * ptr, unsigned int len) |
{ |
for(;len;len--) Putchar(pgm_read_byte(ptr++)); |
} |
void PAD_SP(signed char howmany) |
{ |
for(;howmany>0;howmany--) Putchar(' '); |
} |
void PAD_0(signed char howmany) |
{ |
for(;howmany>0;howmany--) Putchar('0'); |
} |
#define BUF 40 |
/* |
* Macros for converting digits to letters and vice versa |
*/ |
#define to_digit(c) ((c) - '0') |
#define is_digit(c) ((c)<='9' && (c)>='0') |
#define to_char(n) ((n) + '0') |
/* |
* Flags used during conversion. |
*/ |
#define LONGINT 0x01 /* long integer */ |
#define LONGDBL 0x02 /* long double; unimplemented */ |
#define SHORTINT 0x04 /* short integer */ |
#define ALT 0x08 /* alternate form */ |
#define LADJUST 0x10 /* left adjustment */ |
#define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ |
#define HEXPREFIX 0x40 /* add 0x or 0X prefix */ |
void _printf_P (char ziel,char const *fmt0, ...) /* Works with string from FLASH */ |
{ |
va_list ap; |
register const char *fmt; /* format string */ |
register char ch; /* character from fmt */ |
register int n; /* handy integer (short term usage) */ |
register char *cp; /* handy char pointer (short term usage) */ |
const char *fmark; /* for remembering a place in fmt */ |
register unsigned char flags; /* flags as above */ |
signed char width; /* width from format (%8d), or 0 */ |
signed char prec; /* precision from format (%.3d), or -1 */ |
char sign; /* sign prefix (' ', '+', '-', or \0) */ |
unsigned long _ulong=0; /* integer arguments %[diouxX] */ |
#define OCT 8 |
#define DEC 10 |
#define HEX 16 |
unsigned char base; /* base for [diouxX] conversion */ |
signed char dprec; /* a copy of prec if [diouxX], 0 otherwise */ |
signed char dpad; /* extra 0 padding needed for integers */ |
signed char fieldsz; /* field size expanded by sign, dpad etc */ |
/* The initialization of 'size' is to suppress a warning that |
'size' might be used unitialized. It seems gcc can't |
quite grok this spaghetti code ... */ |
signed char size = 0; /* size of converted field or string */ |
char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ |
char ox[2]; /* space for 0x hex-prefix */ |
PrintZiel = ziel; // bestimmt, LCD oder UART |
va_start(ap, fmt0); |
fmt = fmt0; |
/* |
* Scan the format for conversions (`%' character). |
*/ |
for (;;) { |
for (fmark = fmt; (ch = pgm_read_byte(fmt)) != '\0' && ch != '%'; fmt++) |
/* void */; |
if ((n = fmt - fmark) != 0) { |
PRINTP(fmark, n); |
} |
if (ch == '\0') |
goto done; |
fmt++; /* skip over '%' */ |
flags = 0; |
dprec = 0; |
width = 0; |
prec = -1; |
sign = '\0'; |
rflag: ch = PRG_RDB(fmt++); |
reswitch: |
#ifdef LIGHTPRINTF |
if (ch=='o' || ch=='u' || (ch|0x20)=='x') { |
#else |
if (ch=='u' || (ch|0x20)=='x') { |
#endif |
if (flags&LONGINT) { |
_ulong=va_arg(ap, unsigned long); |
} else { |
register unsigned int _d; |
_d=va_arg(ap, unsigned int); |
_ulong = flags&SHORTINT ? (unsigned long)(unsigned short)_d : (unsigned long)_d; |
} |
} |
#ifndef LIGHTPRINTF |
if(ch==' ') { |
/* |
* ``If the space and + flags both appear, the space |
* flag will be ignored.'' |
* -- ANSI X3J11 |
*/ |
if (!sign) |
sign = ' '; |
goto rflag; |
} else if (ch=='#') { |
flags |= ALT; |
goto rflag; |
} else if (ch=='*'||ch=='-') { |
if (ch=='*') { |
/* |
* ``A negative field width argument is taken as a |
* - flag followed by a positive field width.'' |
* -- ANSI X3J11 |
* They don't exclude field widths read from args. |
*/ |
if ((width = va_arg(ap, int)) >= 0) |
goto rflag; |
width = -width; |
} |
flags |= LADJUST; |
flags &= ~ZEROPAD; /* '-' disables '0' */ |
goto rflag; |
} else if (ch=='+') { |
sign = '+'; |
goto rflag; |
} else if (ch=='.') { |
if ((ch = PRG_RDB(fmt++)) == '*') { |
n = va_arg(ap, int); |
prec = n < 0 ? -1 : n; |
goto rflag; |
} |
n = 0; |
while (is_digit(ch)) { |
n = n*10 + to_digit(ch); |
ch = PRG_RDB(fmt++); |
} |
prec = n < 0 ? -1 : n; |
goto reswitch; |
} else |
#endif /* LIGHTPRINTF */ |
if (ch=='0') { |
/* |
* ``Note that 0 is taken as a flag, not as the |
* beginning of a field width.'' |
* -- ANSI X3J11 |
*/ |
if (!(flags & LADJUST)) |
flags |= ZEROPAD; /* '-' disables '0' */ |
goto rflag; |
} else if (ch>='1' && ch<='9') { |
n = 0; |
do { |
n = 10 * n + to_digit(ch); |
ch = PRG_RDB(fmt++); |
} while (is_digit(ch)); |
width = n; |
goto reswitch; |
} else if (ch=='h') { |
flags |= SHORTINT; |
goto rflag; |
} else if (ch=='l') { |
flags |= LONGINT; |
goto rflag; |
} else if (ch=='c') { |
*(cp = buf) = va_arg(ap, int); |
size = 1; |
sign = '\0'; |
} else if (ch=='D'||ch=='d'||ch=='i') { |
if(ch=='D') |
flags |= LONGINT; |
if (flags&LONGINT) { |
_ulong=va_arg(ap, long); |
} else { |
register int _d; |
_d=va_arg(ap, int); |
_ulong = flags&SHORTINT ? (long)(short)_d : (long)_d; |
} |
if ((long)_ulong < 0) { |
_ulong = -_ulong; |
sign = '-'; |
} |
base = DEC; |
goto number; |
} else |
/* |
if (ch=='n') { |
if (flags & LONGINT) |
*va_arg(ap, long *) = ret; |
else if (flags & SHORTINT) |
*va_arg(ap, short *) = ret; |
else |
*va_arg(ap, int *) = ret; |
continue; // no output |
} else |
*/ |
#ifndef LIGHTPRINTF |
if (ch=='O'||ch=='o') { |
if (ch=='O') |
flags |= LONGINT; |
base = OCT; |
goto nosign; |
} else if (ch=='p') { |
/* |
* ``The argument shall be a pointer to void. The |
* value of the pointer is converted to a sequence |
* of printable characters, in an implementation- |
* defined manner.'' |
* -- ANSI X3J11 |
*/ |
/* NOSTRICT */ |
_ulong = (unsigned int)va_arg(ap, void *); |
base = HEX; |
flags |= HEXPREFIX; |
ch = 'x'; |
goto nosign; |
} else if (ch=='s') { // print a string from RAM |
if ((cp = va_arg(ap, char *)) == NULL) { |
cp=buf; |
cp[0] = '('; |
cp[1] = 'n'; |
cp[2] = 'u'; |
cp[4] = cp[3] = 'l'; |
cp[5] = ')'; |
cp[6] = '\0'; |
} |
if (prec >= 0) { |
/* |
* can't use strlen; can only look for the |
* NUL in the first `prec' characters, and |
* strlen() will go further. |
*/ |
char *p = (char*)memchr(cp, 0, prec); |
if (p != NULL) { |
size = p - cp; |
if (size > prec) |
size = prec; |
} else |
size = prec; |
} else |
size = strlen(cp); |
sign = '\0'; |
} else |
#endif /* LIGHTPRINTF */ |
if(ch=='U'||ch=='u') { |
if (ch=='U') |
flags |= LONGINT; |
base = DEC; |
goto nosign; |
} else if (ch=='X'||ch=='x') { |
base = HEX; |
/* leading 0x/X only if non-zero */ |
if (flags & ALT && _ulong != 0) |
flags |= HEXPREFIX; |
/* unsigned conversions */ |
nosign: sign = '\0'; |
/* |
* ``... diouXx conversions ... if a precision is |
* specified, the 0 flag will be ignored.'' |
* -- ANSI X3J11 |
*/ |
number: if ((dprec = prec) >= 0) |
flags &= ~ZEROPAD; |
/* |
* ``The result of converting a zero value with an |
* explicit precision of zero is no characters.'' |
* -- ANSI X3J11 |
*/ |
cp = buf + BUF; |
if (_ulong != 0 || prec != 0) { |
register unsigned char _d,notlastdigit; |
do { |
notlastdigit=(_ulong>=base); |
_d = _ulong % base; |
if (_d<10) { |
_d+='0'; |
} else { |
_d+='a'-10; |
if (ch=='X') _d&=~0x20; |
} |
*--cp=_d; |
_ulong /= base; |
} while (notlastdigit); |
#ifndef LIGHTPRINTF |
// handle octal leading 0 |
if (base==OCT && flags & ALT && *cp != '0') |
*--cp = '0'; |
#endif |
} |
size = buf + BUF - cp; |
} else { //default |
/* "%?" prints ?, unless ? is NUL */ |
if (ch == '\0') |
goto done; |
/* pretend it was %c with argument ch */ |
cp = buf; |
*cp = ch; |
size = 1; |
sign = '\0'; |
} |
/* |
* All reasonable formats wind up here. At this point, |
* `cp' points to a string which (if not flags&LADJUST) |
* should be padded out to `width' places. If |
* flags&ZEROPAD, it should first be prefixed by any |
* sign or other prefix; otherwise, it should be blank |
* padded before the prefix is emitted. After any |
* left-hand padding and prefixing, emit zeroes |
* required by a decimal [diouxX] precision, then print |
* the string proper, then emit zeroes required by any |
* leftover floating precision; finally, if LADJUST, |
* pad with blanks. |
*/ |
/* |
* compute actual size, so we know how much to pad. |
*/ |
fieldsz = size; |
dpad = dprec - size; |
if (dpad < 0) |
dpad = 0; |
if (sign) |
fieldsz++; |
else if (flags & HEXPREFIX) |
fieldsz += 2; |
fieldsz += dpad; |
/* right-adjusting blank padding */ |
if ((flags & (LADJUST|ZEROPAD)) == 0) |
PAD_SP(width - fieldsz); |
/* prefix */ |
if (sign) { |
PRINT(&sign, 1); |
} else if (flags & HEXPREFIX) { |
ox[0] = '0'; |
ox[1] = ch; |
PRINT(ox, 2); |
} |
/* right-adjusting zero padding */ |
if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) |
PAD_0(width - fieldsz); |
/* leading zeroes from decimal precision */ |
PAD_0(dpad); |
/* the string or number proper */ |
PRINT(cp, size); |
/* left-adjusting padding (always blank) */ |
if (flags & LADJUST) |
PAD_SP(width - fieldsz); |
} |
done: |
va_end(ap); |
} |
/FollowMe/trunk/printf_P.h |
---|
0,0 → 1,19 |
#ifndef _PRINTF_P_H_ |
#define _PRINTF_P_H_ |
#include <avr/pgmspace.h> |
#define OUT_V24 0 |
#define OUT_LCD 1 |
void _printf_P (char, char const *fmt0, ...); |
extern char PrintZiel; |
#define printf_P(format, args...) _printf_P(OUT_V24,format , ## args) |
#define printf(format, args...) _printf_P(OUT_V24,PSTR(format) , ## args) |
#define LCD_printfxy(x,y,format, args...) { DispPtr = y * 20 + x; _printf_P(OUT_LCD,PSTR(format) , ## args);} |
#define LCD_printf(format, args...) { _printf_P(OUT_LCD,PSTR(format) , ## args);} |
#endif |
/FollowMe/trunk/sdc.c |
---|
0,0 → 1,688 |
#include <avr/io.h> |
#include <util/delay.h> |
#include <string.h> |
#include "sdc.h" |
#include "ssc.h" |
#include "timer0.h" |
#include "printf_P.h" |
#include "crc16.h" |
//#define _SD_DEBUG |
#define CMD_GO_IDLE_STATE 0x00 /* CMD00: response R1 */ |
#define CMD_SEND_OP_COND 0x01 /* CMD01: response R1 */ |
#define CMD_SEND_IF_COND 0x08 /* CMD08: response R7 */ |
#define CMD_SEND_CSD 0x09 /* CMD09: response R1 */ |
#define CMD_SEND_CID 0x0A /* CMD10: response R1 */ |
#define CMD_SEND_STATUS 0x0D /* CMD13: response R2 */ |
#define CMD_SET_BLOCKLEN 0x10 /* CMD16: arg0[31:0]: block length, response R1*/ |
#define CMD_READ_SINGLE_BLOCK 0x11 /* CMD17: arg0[31:0]: data address, response R1 */ |
#define CMD_WRITE_SINGLE_BLOCK 0x18 /* CMD24: arg0[31:0]: data address, response R1 */ |
#define CMD_APP_CMD 0x37 /* CMD55: response R1 */ |
#define CMD_READ_OCR 0x3A /* CMD58: response R3 */ |
#define CMD_CRC_ON_OFF 0x3B /* CMD59: arg0[31:1]: stuff bits, arg0[0:0]: crc option, response R1 */ |
#define ACMD_SEND_OP_COND 0x29 /* ACMD41: arg0[31]: stuff bits, arg0[30]: HCS, arg0[29:0] stuff bits*, response R1 */ |
#define R1_NO_ERR 0x00 |
#define R1_IDLE_STATE 0x01 |
#define R1_ERASE_RESET 0x02 |
#define R1_ILLEGAL_CMD 0x04 |
#define R1_COM_CRC_ERR 0x08 |
#define R1_ERASE_SEQUENCE_ERR 0x10 |
#define R1_ADDRESS_ERR 0x20 |
#define R1_PARAMETER_ERR 0x40 |
#define R1_BAD_RESPONSE 0x80 |
#define R2_NO_ERR 0x00 |
#define R2_CARD_LOCKED 0x01 |
#define R2_ERASE_WRITE_PROT_ERR 0x02 |
#define R2_UNKOWN_ERR 0x04 |
#define R2_CARD_CTRL_ERR 0x08 |
#define R2_CARD_ECC_ERR 0x10 |
#define R2_WRITE_PROT_ERR 0x20 |
#define R2_ERASE_PARAM_ERR 0x40 |
#define R2_OUT_OF_RANGE_ERR 0x80 |
#define DATA_START_TOKEN 0xFE |
#define DATA_RESPONSE_MASK 0x1F |
#define DATA_RESPONSE_OK 0x05 |
#define DATA_RESPONSE_CRC_ERR 0x0B |
#define DATA_RESPONSE_WRITE_ERR 0x1D |
typedef enum |
{ |
VER_UNKNOWN, |
VER_1X, |
VER_20 |
} SDVersion_t; |
typedef struct |
{ |
uint8_t Valid; |
SDVersion_t Version; // HW-Version |
uint32_t Capacity; // Memory capacity in bytes |
uint8_t CID[16]; // CID register |
uint8_t CSD[16]; // CSD register |
} __attribute__((packed)) SDCardInfo_t; |
volatile SDCardInfo_t SDCardInfo; |
//________________________________________________________________________________________________________________________________________ |
// Function: CRC7(uint8_t* cmd, uint32_t len); |
// |
// Description: This function calculated the CRC7 checksum used in the last byte of a spi command frame. |
// |
// |
// Returnvalue: the function returns the crc7 including bit 0 set to 1 |
//________________________________________________________________________________________________________________________________________ |
uint8_t CRC7(uint8_t *cmd, uint32_t len) |
{ |
uint8_t i, a; |
uint8_t crc, Data; |
crc = 0; // init CRC buffer |
for (a = 0; a < len ;a++) // for every byte in the msg |
{ |
Data = cmd[a]; |
for (i=0;i<8;i++) // for every bit in the byte |
{ |
crc <<= 1; // shift crc |
if ((Data & 0x80)^(crc & 0x80)) crc ^=0x09; //xor |
Data <<= 1; // shift data for next bit |
} |
} |
crc = (crc<<1)|1; // set terminating bit to 1 |
return(crc); |
} |
uint8_t SDC_WaitForBusy(uint16_t timeout) |
{ |
uint8_t rsp = 0; |
uint16_t timestamp = 0; |
SSC_Enable(); // enable chipselect. |
timestamp = SetDelay(timeout); |
do |
{ |
rsp = SSC_GetChar(); |
if(CheckDelay(timestamp)) break; |
}while(rsp != 0xFF); // wait while card is busy (data out low) |
return(rsp); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_SendCMDR1(uint8_t CmdNo, uint32_t arg); |
// |
// Description: This function send a command frame to the SD-Card in spi-mode. |
// |
// |
// Returnvalue: The function returns the first response byte like for R1 commands |
//________________________________________________________________________________________________________________________________________ |
uint8_t SDC_SendCMDR1(uint8_t CmdNo, uint32_t arg) |
{ |
uint8_t r1; |
uint16_t timeout = 0; |
uint16_t a; |
uint8_t cmd[6]; |
cmd[0] = 0x40|CmdNo; // set command index |
cmd[1] = (arg & 0xFF000000)>>24; |
cmd[2] = (arg & 0x00FF0000)>>16; |
cmd[3] = (arg & 0x0000FF00)>>8; |
cmd[4] = (arg & 0x000000FF); |
cmd[5] = CRC7(cmd, 5); // update checksum |
#ifdef _SD_DEBUG |
printf("\r\nCmd=%02X, arg=%04X%04X", CmdNo, (uint16_t)(arg>>16), (uint16_t)(0xFFFF & arg)); |
#endif |
SSC_Disable(); // disable chipselect. |
SSC_PutChar(0xFF); // dummy to sync |
SSC_Enable(); // enable chipselect. |
SDC_WaitForBusy(500); // wait 500ms until card is busy |
for (a = 0;a < 6; a++) // send the command sequence to the sdcard (6 bytes) |
{ |
SSC_PutChar(cmd[a]); |
_delay_loop_2(10); |
} |
// get response byte |
do |
{ |
r1 = SSC_GetChar(); // get byte from sd-card |
if (timeout++ > 500) break; |
}while(r1 == 0xFF); // wait for the response byte from sd-card. |
#ifdef _SD_DEBUG |
printf("-->R1=%02X", r1); |
#endif |
return(r1); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_SendACMDR1(uint8_t CmdNo, uint32_t arg); |
// |
// Description: This function send a application command frame to the SD-Card in spi-mode. |
// |
// |
// Returnvalue: The function returns the first response byte like for R1 commands |
//________________________________________________________________________________________________________________________________________ |
uint8_t SDC_SendACMDR1(uint8_t CmdNo, uint32_t arg) |
{ |
uint8_t r1 = 0xFF; |
r1 = SDC_SendCMDR1(CMD_APP_CMD, 0UL); |
if(r1 & R1_BAD_RESPONSE) return(r1); |
r1 = SDC_SendCMDR1(CmdNo, arg); |
return(r1); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_GetData(uint8_t * cmd ,u8 *Buffer, u32 len); |
// |
// Description: This function sneds cmd an reads a datablock of len from the sd-card |
// |
// |
// Returnvalue: SD_Result_t |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_GetData(uint8_t CmdNo, uint32_t addr, uint8_t *Buffer, uint32_t len) |
{ |
uint8_t rsp; |
uint16_t a, crc16; |
SD_Result_t result = SD_ERROR_UNKNOWN; |
// send the command |
rsp = SDC_SendCMDR1(CmdNo, addr); |
if (rsp != R1_NO_ERR) |
{ |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
do |
{ |
rsp = SSC_GetChar(); |
if((rsp & 0xF0) == 0x00) // data error token |
{ |
result = SD_ERROR_READ_DATA; |
goto end; |
} |
}while(rsp != DATA_START_TOKEN); |
// data start token received |
for (a = 0; a < len; a++) // read the block from the SSC |
{ |
Buffer[a] = SSC_GetChar(); |
} |
// Read two bytes CRC16-Data checksum |
crc16 = SSC_GetChar(); // highbyte first |
crc16 = (crc16<<8)|SSC_GetChar(); // lowbyte last |
/* if(crc16 != CRC16(Buffer, len)) result = SD_ERROR_CRC_DATA; |
else */result = SD_SUCCESS; |
end: |
if(result != SD_SUCCESS) |
{ |
printf("Error %02X reading data from sd card (R1=%02X).\r\n", result, rsp); |
} |
return(result); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_PrintCID(u8 * pCID); |
// |
// Description: This function prints the CIS register in a human readable format. |
// |
// |
// Returnvalue: the function returns nothing |
//________________________________________________________________________________________________________________________________________ |
void SDC_PrintCID(uint8_t * pCID) |
{ |
uint8_t pn[6]; |
uint16_t temp1, temp2; |
printf("\r\n Manufacturer ID: %i\r\n", pCID[0]); |
memcpy(pn, &pCID[1], 2); |
pn[2] = '\0'; // terminate string |
printf(" Application ID: %s\r\n",pn); |
memcpy(pn, &pCID[3], 5); |
pn[5] = '\0'; // terminate string |
printf(" Product Name: %s\r\n",pn); |
printf(" Product Rev.: %i.%i\r\n",pCID[8]>>4, pCID[8]&0xF); |
printf(" Serial No.: "); |
for(temp1 = 0; temp1<4; temp1++) |
{ |
printf("%02X", pCID[9+temp1]); |
} |
printf("\r\n"); |
temp1 = pCID[14] & 0x0F; // month |
temp2 = ((pCID[14]>>4)|(pCID[13]<<4)) + 2000; // year |
printf(" Manufac. Date: %i/%i\r\n\r\n",temp1, temp2); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_GetCID(uint8_t * pCID); |
// |
// Description: This function reads the CIS register form the sd card in spi mode. |
// |
// |
// Returnvalue: the function returns error state |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_GetCID(uint8_t * pCID) |
{ |
return SDC_GetData(CMD_SEND_CID, 0UL, pCID, 16); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_GetCSD(uint8_t * pCSD); |
// |
// Description: This function reads the CSD register form the sd card in spi mode. |
// |
// |
// Returnvalue: the function returns error state |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_GetCSD(uint8_t * pCSD) |
{ |
return SDC_GetData(CMD_SEND_CSD, 0UL, pCSD, 16); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_Init(void); |
// |
// Description: This function initialises the SDCard to spi-mode. |
// |
// |
// Returnvalue: the function returns 0 if the initialisation was successfull otherwise the function returns an errorcode. |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_Init(void) |
{ |
uint16_t timeout = 0; |
uint8_t rsp[6]; // SD-SPI response buffer |
SD_Result_t result = SD_ERROR_UNKNOWN; |
if(SD_SWITCH) // init only if the SD-Switch is indicating a card in the slot |
{ |
printf("\r\n SSC init..."); |
SSC_Init(); |
printf("ok"); |
//_delay_loop_2(1050); |
printf("\r\n SDC init..."); |
SDCardInfo.Valid = 0; |
/* The host shall supply power to the card so that the voltage is reached to Vdd_min within 250ms and |
start to supply at least 74 SD clocks to the SD card with keeping cmd line to high. In case of SPI |
mode, CS shall be held to high during 74 clock cycles. */ |
SSC_Disable(); // set SD_CS high |
for (timeout = 0; timeout < 15; timeout++) // 15*8 = 120 cycles |
{ |
SSC_PutChar(0xFF); |
} |
// switch to idle state |
#ifdef _SD_DEBUG |
printf("\r\nGoing idle state.."); |
#endif |
timeout = 0; |
do |
{ |
rsp[0] = SDC_SendCMDR1(CMD_GO_IDLE_STATE, 0UL); |
if (timeout++ > 500) |
{ |
printf("reset timeout"); |
result = SD_ERROR_RESET; |
goto end; |
} |
}while(rsp[0] != R1_IDLE_STATE); |
// enable crc feature |
/* if(SDC_SendCMDR1(CMD_CRC_ON_OFF, 1UL) != R1_IDLE_STATE) |
{ |
printf("Bad cmd59 R1=%02X.", rsp[0]); |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
}*/ |
// check for card hw version |
// 2.7-3.6V Range = 0x01, check pattern 0xAA |
rsp[0] = SDC_SendCMDR1(CMD_SEND_IF_COND, 0x000001AA); |
// answer to cmd58 is an R7 response (R1+ 4Byte IFCond) |
if(rsp[0] & R1_BAD_RESPONSE) |
{ |
printf("Bad cmd8 R1=%02X.", rsp[0]); |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
if(rsp[0] & R1_ILLEGAL_CMD) |
{ |
//Ver1.X SD Memory Card or not a SD Memory Card |
SDCardInfo.Version = VER_1X; |
} |
else |
{ |
// Ver2.00 or later SD Memory Card |
// reading the remaining bytes of the R7 response |
SDCardInfo.Version = VER_20; |
for(timeout = 1; timeout < 5; timeout++) |
{ |
rsp[timeout] = SSC_GetChar(); |
} |
//check pattern |
if(rsp[4]!= 0xAA) |
{ |
printf("Bad cmd8 R7 check pattern.\r\n"); |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
if ( (rsp[3] & 0x0F)!= 0x01 ) // voltage range is not 2.7-3.6V |
{ |
printf("Card is incompatible to 3.3V.\r\n"); |
result = SD_ERROR_BAD_VOLTAGE_RANGE; |
goto end; |
} |
} |
rsp[0] = SDC_SendCMDR1(CMD_READ_OCR, 0UL); |
// answer to cmd58 is an R3 response (R1 + 4Byte OCR) |
if(rsp[0] & R1_BAD_RESPONSE) |
{ |
printf("Bad cmd58 R1 %02x.", rsp[0]); |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
if(rsp[0] & R1_ILLEGAL_CMD) |
{ |
printf("Not an SD-CARD."); |
result = SD_ERROR_NO_SDCARD; |
goto end; |
} |
// read 4 bytes of OCR register |
for(timeout = 1; timeout < 5; timeout++) |
{ |
rsp[timeout] = SSC_GetChar(); |
} |
// FollowMe & SD-Logger uses 3.3 V, therefore check for bit 20 & 21 |
if((rsp[2] & 0x30) != 0x30) |
{ |
// supply voltage is not supported by sd-card |
printf("Card is incompatible to 3.3V."); |
result = SD_ERROR_BAD_VOLTAGE_RANGE; |
goto end; |
} |
// Initialize the sd-card sending continously ACMD_SEND_OP_COND (only supported by SD cards) |
timeout = SetDelay(2000); // set timeout to 2000 ms (large cards tend to longer) |
do |
{ |
rsp[0] = SDC_SendACMDR1(ACMD_SEND_OP_COND, 0UL); |
if(rsp[0] & R1_BAD_RESPONSE) |
{ |
printf("Bad Acmd41 R1=%02X.", rsp[0]); |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
if(CheckDelay(timeout)) |
{ |
printf("Init timeout."); |
result = SD_ERROR_INITIALIZE; |
goto end; |
} |
} while(rsp[0] & R1_IDLE_STATE); // loop until idle state |
if(rsp[0] != R1_NO_ERR) |
{ |
printf("Init error."); |
result = SD_ERROR_INITIALIZE; |
goto end; |
} |
/* set block size to 512 bytes */ |
if(SDC_SendCMDR1(CMD_SET_BLOCKLEN, 512UL) != R1_NO_ERR) |
{ |
printf("Error setting block length to 512."); |
result = SD_ERROR_SET_BLOCKLEN; |
goto end; |
} |
//SSC_Disable(); // set SD_CS high |
// here is the right place to inrease the SPI baud rate to maximum |
//SSC_Enable(); // set SD_CS high |
// read CID register |
result = SDC_GetCID((uint8_t *)&SDCardInfo.CID); |
if(result != SD_SUCCESS) |
{ |
printf("Error reading CID.\r\n"); |
goto end; |
} |
// read CSD register |
result = SDC_GetCSD((uint8_t *)&SDCardInfo.CSD); |
if(result != SD_SUCCESS) |
{ |
printf("Error reading CSD."); |
goto end; |
} |
printf("ok\r\n"); |
uint8_t c_size_mult, read_bl_len; |
uint32_t c_size; |
switch(SDCardInfo.CSD[0]>>6) // check CSD Version |
{ |
case 0x00: // if CSD is V1.0 structure (2GB limit) |
/* |
memory capacity = BLOCKNR * BLOCK_LEN |
BLOCKNR = (C_SIZE+1) * MULT |
MULT = 2^(C_SIZE_MULT+2) |
BLOCK_LEN = 2^READ_BL_LEN |
C_SIZE is 12 bits [73:62] in CSD register |
C_SIZE_MULT is 3 bits [49:47] in CSD register |
READ_BL_LEN is 4 bits [83:80] in CSD register |
*/ |
read_bl_len = (SDCardInfo.CSD[5] & 0x0F); //CSD[05] -> [87:80] |
c_size = ((uint32_t)(SDCardInfo.CSD[6] & 0x03))<<10; //CSD[06] -> [79:72] |
c_size |= ((uint32_t)SDCardInfo.CSD[7])<<2; //CSD[07] -> [71:64] |
c_size |= (uint32_t)(SDCardInfo.CSD[8]>>6); //CSD[08] -> [63:56] |
c_size_mult = (SDCardInfo.CSD[9] & 0x03)<<1; //CSD[09] -> [55:48] |
c_size_mult |=(SDCardInfo.CSD[10] & 0x80)>>7; //CSD[10] -> [47:40] |
SDCardInfo.Capacity = (uint32_t)(c_size+1)*(1L<<(c_size_mult+2))*(1L<<read_bl_len); |
break; |
case 0x01: // if CSD is V2.0 structure (HC SD-Card > 2GB) |
/* |
memory capacity = (C_SIZE+1) * 512K byte |
C_SIZE is 22 bits [69:48] in CSR register |
*/ |
c_size = ((uint32_t)(SDCardInfo.CSD[7] & 0x3F))<<16; //CSD[07] -> [71:64] |
c_size |= ((uint32_t)SDCardInfo.CSD[8])<<8; //CSD[08] -> [63:56] |
c_size |= (uint32_t)SDCardInfo.CSD[9]; //CSD[09] -> [55:48]; |
SDCardInfo.Capacity = (c_size + 1)* 512L * 1024L; |
break; |
default: //unknown CSD Version |
SDCardInfo.Capacity = 0; |
break; |
} |
switch(SDCardInfo.Version) |
{ |
case VER_1X: |
printf("\r\n SD-CARD V1.x"); |
break; |
case VER_20: |
printf("\r\n SD-CARD V2.0 or later"); |
default: |
break; |
} |
uint16_t mb_size = (uint16_t)(SDCardInfo.Capacity/(1024L*1024L)); |
printf("\r\n Capacity = %i MB", mb_size); |
SDC_PrintCID((uint8_t *)&SDCardInfo.CID); |
SDCardInfo.Valid = 1; |
// jump point for error condition before |
end: |
SSC_Disable(); |
} |
else |
{ |
SSC_Deinit(); |
SDCardInfo.Valid = 0; |
result = SD_ERROR_NOCARD; |
printf("No Card in Slot."); |
} |
return(result); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_Deinit(void); |
// |
// Description: This function deinitialises the SDCard interface. |
// |
// |
// Returnvalue: the function returns 0 if the initialisation was successfull otherwise the function returns an errorcode. |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_Deinit(void) |
{ |
printf("\r\n SDC deinit..."); |
SSC_Deinit(); |
SDCardInfo.Valid = 0; |
SDCardInfo.Capacity = 0; |
SDCardInfo.Version = VER_UNKNOWN; |
printf("ok"); |
return(SD_SUCCESS); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_PutSector(uint32_t addr, const uint8_t *Buffer) |
// |
// Description: This function writes one sector of data to the SSC |
// |
// |
// Returnvalue: SD_Result_t |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_PutSector(uint32_t addr, const uint8_t *Buffer) |
{ |
uint8_t rsp; |
uint16_t a, crc16; |
uint16_t timeout = 0; |
SD_Result_t result = SD_ERROR_UNKNOWN; |
addr = addr << 9; // convert sectoradress to byteadress |
rsp = SDC_SendCMDR1(CMD_WRITE_SINGLE_BLOCK, addr); |
if (rsp != R1_NO_ERR) |
{ |
result = SD_ERROR_BAD_RESPONSE; |
goto end; |
} |
for (a=0;a<20;a++) // at least one byte |
{ |
SSC_GetChar(); |
} |
crc16 = CRC16(Buffer, 512); // calc checksum for data block |
SSC_PutChar(DATA_START_TOKEN); // send data start of header to the SSC |
for (a=0;a<512;a++) // transmit one sector (normaly 512bytes) of data to the sdcard. |
{ |
SSC_PutChar(Buffer[a]); |
} |
// write two bytes of crc16 to the sdcard |
SSC_PutChar((uint8_t)(crc16>>8)); // write high byte first |
SSC_PutChar((uint8_t)(0x00FF&crc16)); // lowbyte last |
do // wait for data response token |
{ |
rsp = SSC_GetChar(); |
if(timeout++ > 500) |
{ |
result = SD_ERROR_TIMEOUT; |
goto end; |
} |
}while((rsp & 0x11) != 0x01 ); |
// analyse data response token |
switch(rsp & DATA_RESPONSE_MASK) |
{ |
case DATA_RESPONSE_OK: |
result = SD_SUCCESS; |
break; |
case DATA_RESPONSE_CRC_ERR: |
result = SD_ERROR_CRC_DATA; |
goto end; |
break; |
case DATA_RESPONSE_WRITE_ERR: |
result = SD_ERROR_WRITE_DATA; |
goto end; |
break; |
default: |
result = SD_ERROR_UNKNOWN; |
goto end; |
break; |
} |
// wait 2 seconds until the sdcard is busy. |
rsp = SDC_WaitForBusy(2000); |
if(rsp != 0xFF) |
{ |
result = SD_ERROR_TIMEOUT; |
goto end; |
} |
// check card status |
rsp = SDC_SendCMDR1(CMD_SEND_STATUS, 0); |
// first byte of R2 response is like R1 response |
if(rsp != R1_NO_ERR) |
{ |
result = SD_ERROR_BAD_RESPONSE; |
SSC_GetChar(); // read out 2nd byte |
goto end; |
} |
// 2nd byte of r2 response |
rsp = SSC_GetChar(); |
if(rsp != R2_NO_ERR) |
{ |
result = SD_ERROR_WRITE_DATA; |
SSC_GetChar(); |
goto end; |
} |
end: |
if(result != SD_SUCCESS) |
{ |
printf("Error %02X writing data to sd card (R=%02X).\r\n", result, rsp); |
} |
return(result); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SDC_GetSector(uint32_t addr,uint8_t *Buffer); |
// |
// Description: This function reads one sector of data from the SSC |
// |
// |
// Returnvalue: SD_Result_t |
//________________________________________________________________________________________________________________________________________ |
SD_Result_t SDC_GetSector(uint32_t addr,uint8_t *Buffer) |
{ |
addr = addr << 9; // convert sectoradress to byteadress |
return SDC_GetData(CMD_READ_SINGLE_BLOCK, addr, Buffer, 512); |
} |
/FollowMe/trunk/sdc.h |
---|
0,0 → 1,30 |
#ifndef _SDC_H_ |
#define _SDC_H_ |
#include <inttypes.h> |
typedef enum |
{ |
SD_SUCCESS = 0, |
SD_ERROR_NOCARD, |
SD_ERROR_RESET, |
SD_ERROR_INITIALIZE, |
SD_ERROR_BAD_RESPONSE, |
SD_ERROR_BAD_VOLTAGE_RANGE, |
SD_ERROR_NO_SDCARD, |
SD_ERROR_TIMEOUT, |
SD_ERROR_CRC_DATA, |
SD_ERROR_WRITE_DATA, |
SD_ERROR_READ_DATA, |
SD_ERROR_SET_BLOCKLEN, |
SD_ERROR_UNKNOWN |
} SD_Result_t; |
extern SD_Result_t SDC_Init(void); |
extern SD_Result_t SDC_GetSector (uint32_t Addr, uint8_t *pBuffer); |
extern SD_Result_t SDC_PutSector (uint32_t Addr, const uint8_t *pBuffer); |
extern SD_Result_t SDC_Deinit(void); |
#endif |
/FollowMe/trunk/settings.c |
---|
0,0 → 1,225 |
/*#######################################################################################*/ |
/* !!! THIS IS NOT FREE SOFTWARE !!! */ |
/*#######################################################################################*/ |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Copyright (c) 2008 Ingo Busker, Holger Buss |
// + Nur für den privaten Gebrauch |
// + FOR NON COMMERCIAL USE ONLY |
// + www.MikroKopter.com |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Es gilt für das gesamte Projekt (Hardware, Software, Binärfiles, Sourcecode und Dokumentation), |
// + dass eine Nutzung (auch auszugsweise) nur für den privaten (nicht-kommerziellen) Gebrauch zulässig ist. |
// + Sollten direkte oder indirekte kommerzielle Absichten verfolgt werden, ist mit uns (info@mikrokopter.de) Kontakt |
// + bzgl. der Nutzungsbedingungen aufzunehmen. |
// + Eine kommerzielle Nutzung ist z.B.Verkauf von MikroKoptern, Bestückung und Verkauf von Platinen oder Bausätzen, |
// + Verkauf von Luftbildaufnahmen, usw. |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Werden Teile des Quellcodes (mit oder ohne Modifikation) weiterverwendet oder veröffentlicht, |
// + unterliegen sie auch diesen Nutzungsbedingungen und diese Nutzungsbedingungen incl. Copyright müssen dann beiliegen |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Sollte die Software (auch auszugesweise) oder sonstige Informationen des MikroKopter-Projekts |
// + auf anderen Webseiten oder sonstigen Medien veröffentlicht werden, muss unsere Webseite "http://www.mikrokopter.de" |
// + eindeutig als Ursprung verlinkt werden |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Keine Gewähr auf Fehlerfreiheit, Vollständigkeit oder Funktion |
// + Benutzung auf eigene Gefahr |
// + Wir übernehmen keinerlei Haftung für direkte oder indirekte Personen- oder Sachschäden |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Die PORTIERUNG der Software (oder Teile davon) auf andere Systeme (ausser der Hardware von www.mikrokopter.de) ist nur |
// + mit unserer Zustimmung zulässig |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Die Funktion printf_P() unterliegt ihrer eigenen Lizenz und ist hiervon nicht betroffen |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Redistributions of source code (with or without modifications) must retain the above copyright notice, |
// + this list of conditions and the following disclaimer. |
// + * Neither the name of the copyright holders nor the names of contributors may be used to endorse or promote products derived |
// + from this software without specific prior written permission. |
// + * The use of this project (hardware, software, binary files, sources and documentation) is only permitted |
// + for non-commercial use (directly or indirectly) |
// + Commercial use (for excample: selling of MikroKopters, selling of PCBs, assembly, ...) is only permitted |
// + with our written permission |
// + * If sources or documentations are redistributet on other webpages, out webpage (http://www.MikroKopter.de) must be |
// + clearly linked as origin |
// + * PORTING this software (or part of it) to systems (other than hardware from www.mikrokopter.de) is NOT allowed |
// |
// + 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. |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include "printf_P.h" |
#include "fat16.h" |
#include "settings.h" |
#include "uart0.h" |
typedef struct |
{ |
ParamId_t ParamId; |
int8_t Name[17]; // 0 terminator is the last byte |
uint8_t Group; // 0 = reserved 1,2,3,4 |
uint16_t Value; |
uint16_t Default; |
uint16_t Min; |
uint16_t Max; |
} __attribute__((packed)) Parameter_t; |
Parameter_t CFG_Parameter[] = |
{ |
//{PID , "1234567890123456" , Group, Value, Default, Min, Max }, |
{PID_KML_LOGGING , "KMLLogging " , 1, 500, 500, 0, 60000}, // the log interval for KML logging, 0 = off |
{PID_GPX_LOGGING , "GPXLogging " , 1, 1000, 1000, 0, 60000} // the log interval for GPX logging, 0 = off |
}; |
//---------------------------------------------------------------------------------- |
// initialize all parameters by its default value |
void Settings_SetDefaultValues(void) |
{ |
uint8_t i; |
// run thrue all parameters and set value to default |
for (i = 0; i < sizeof(CFG_Parameter) / sizeof(Parameter_t); i++) |
{ |
CFG_Parameter[i].Value = CFG_Parameter[i].Default; |
} |
} |
//---------------------------------------------------------------------------------- |
// set parameter from string based name and value |
uint8_t Settings_SetParameterFromString(int8_t *name, int8_t *value) |
{ |
uint8_t i, retval = 0; |
int8_t string[] = " \0"; // null terminated string of 16 characters |
if((name == NULL) || (value == NULL)) return(retval); |
i = strlen(name); // lenght of the parameter name |
if (i > 16) i = 16; // cut off at 16 |
memcpy(string, name, i); // copy name to local buffer |
// search name in parameter list |
for (i = 0; i < sizeof(CFG_Parameter) / sizeof(Parameter_t); i++) |
{ |
if(strncmp(string, CFG_Parameter[i].Name, 16) == 0) // names are matching |
{ |
CFG_Parameter[i].Value = (uint16_t)atoi(value); // convert string to number and set value |
// out of range ? |
if((CFG_Parameter[i].Value < CFG_Parameter[i].Min) || (CFG_Parameter[i].Value > CFG_Parameter[i].Max)) |
{ // print a warning |
printf("\r\n%s <-- %d is out of range [%d...%d]", string, CFG_Parameter[i].Value, CFG_Parameter[i].Min, CFG_Parameter[i].Max); |
CFG_Parameter[i].Value = CFG_Parameter[i].Default; // fallback to default |
} |
retval = 1; // value in range |
break; // end loop |
} |
} |
if(!retval) |
{ |
printf("\r\n%s <-- unknown parameter\r\n", string); |
} |
return(retval); |
} |
//---------------------------------------------------------------------------------- |
// read settings from file on sd-card |
void Settings_Init(void) |
{ |
#define LINE_MAX 32 |
File_t *fp; |
int8_t settingsline[LINE_MAX]; |
char *name, *value; |
uint8_t i; |
char *tmp; |
printf("\r\n Settings init..."); |
Settings_SetDefaultValues(); |
if(Fat16_IsValid()) |
{ // check if settings file is existing |
if(fexist_("settings.ini")) |
{ |
fp = fopen_("settings.ini", 'r'); // try to open the file |
if (fp == NULL) // could not open the file |
{ |
printf("ERROR: Opening settings file!"); |
return; |
} |
// read all lines from file |
while(fgets_(settingsline, LINE_MAX, fp) != 0) |
{ |
if ( // ignorelines starting with \r,\n,' ',';','#' |
(settingsline[0] != '\n') && |
(settingsline[0] != '\r') && |
(settingsline[0] != ' ' ) && |
(settingsline[0] != ';' ) && |
(settingsline[0] != '#' ) |
) |
{ |
name = strtok_r(settingsline, "=", & tmp); // get name |
value = strtok_r(NULL, "=", & tmp); // get value |
if ((name != NULL) && (value != NULL)) |
{ |
Settings_SetParameterFromString(name, value); |
} |
} |
} |
fclose_(fp); |
printf("ok"); |
return; |
} |
else // settings file does not exist |
{ // create default settings file |
fp = fopen_("settings.ini", 'w'); // try to open the file |
if(fp == NULL) |
{ |
printf("ERROR: Creating default settings file!"); |
return; |
} |
// run thrue all parameters and set value to default |
for (i = 0; i < sizeof(CFG_Parameter) / sizeof(Parameter_t); i++) |
{ |
sprintf(settingsline, "%s = %d\r\n", CFG_Parameter[i].Name, CFG_Parameter[i].Default); |
fputs_(settingsline, fp); // write to file |
} |
fputs_("\r\n", fp); // newline at the end of file |
fclose_(fp); |
printf("Default settings file created!"); |
return; |
} |
} |
else // no acces to fat 16 filesystem |
{ |
printf("Using default values!"); |
return; |
} |
} |
//---------------------------------------------------------------------------------- |
// read value of a paramter identified by its id |
// returns 1 on success and 0 on error |
uint8_t Settings_GetParamValue(ParamId_t Pid, uint16_t * pValue) |
{ |
uint8_t i, retval = 0; |
for (i = 0; i < sizeof(CFG_Parameter) / sizeof(Parameter_t); i++) |
{ |
if(CFG_Parameter[i].ParamId == Pid) |
{ |
*pValue = CFG_Parameter[i].Value; |
retval = 1; |
break; |
} |
} |
return retval; |
} |
/FollowMe/trunk/settings.h |
---|
0,0 → 1,16 |
#ifndef _SETTINGS_H |
#define _SETTINGS_H |
typedef enum |
{ |
PID_KML_LOGGING, |
PID_GPX_LOGGING |
} ParamId_t; |
void Settings_Init(void); |
void Settings_SetDefaultValues(void); |
uint8_t Settings_GetParamValue(ParamId_t Pid, uint16_t* pValue); |
#endif // _SETTINGS_H |
/FollowMe/trunk/ssc.c |
---|
0,0 → 1,216 |
#include <avr/io.h> |
#include "ssc.h" |
//-------------------------------------- Hardware specific definitions -------------------------------------- |
#define PORTR_SPI PINB |
#define PORTW_SPI PORTB //Port to which the sd-card is connected (SPI Port) |
#define PORT_MISO PORTB6 //Port Pin that is connected to the DO of the MMC/SD-card |
#define PORT_MOSI PORTB5 //Port Pin that is connected to DI of the MMC/SD-card |
#define PORT_SCK PORTB7 //Port Pin that is connected the CLK of the MMC/SD-card |
#define PORT_SS PORTB4 //Slave Select is not used in SPI Master Mode, but must be defined |
#define PORT_CS PORTB4 //Port Pin that is connected to /CS of the MMC/SD-Karte |
#ifdef USE_SDLOGGER |
#define __SD_INTERFACE_INVERTED // the interface between the controller and the SD-card uses an inverting leveltranslator (transistorinverter) |
#endif // and therefore the signals to or from the memorycard have to be inverted. |
#ifdef USE_FOLLOWME // uses resitors, therefore its not inverted |
//#define __SD_INTERFACE_INVERTED // the interface between the controller and the MMC/SD-card uses an inverting leveltranslator (transistorinverter) |
#endif |
#define DDR_SPI DDRB |
#define DD_MISO DDB6 //Port Pin that is connected to the DO of the MMC/SD-card |
#define DD_MOSI DDB5 //Port Pin that is connected to DI of the MMC/SD-card |
#define DD_SCK DDB7 //Port Pin that is connected the CLK of the MMC/SD-card |
#define DD_SS DDB4 //Slave Select is not used in SPI Master Mode, but must be defined |
#define DD_CS DDB4 //Port Pin that is connected to /CS of the MMC/SD-Karte |
// for compatibility reasons gcc3.x <-> gcc4.x |
#ifndef SPCR |
#define SPCR SPCR0 |
#endif |
#ifndef SPIE |
#define SPIE SPIE0 |
#endif |
#ifndef SPE |
#define SPE SPE0 |
#endif |
#ifndef DORD |
#define DORD DORD0 |
#endif |
#ifndef MSTR |
#define MSTR MSTR0 |
#endif |
#ifndef CPOL |
#define CPOL CPOL0 |
#endif |
#ifndef CPHA |
#define CPHA CPHA0 |
#endif |
#ifndef SPR1 |
#define SPR1 SPR01 |
#endif |
#ifndef SPR0 |
#define SPR0 SPR00 |
#endif |
#ifndef SPDR |
#define SPDR SPDR0 |
#endif |
#ifndef SPSR |
#define SPSR SPSR0 |
#endif |
#ifndef SPIF |
#define SPIF SPIF0 |
#endif |
#ifndef WCOL |
#define WCOL WCOL0 |
#endif |
#ifndef SPI2X |
#define SPI2X SPI2X0 |
#endif |
//________________________________________________________________________________________________________________________________________ |
// Function: SSC_Init(void); |
// |
// Description: This function initialises the synchronus serial channel to the sdcard. |
// |
// |
// Returnvalue: none |
//________________________________________________________________________________________________________________________________________ |
void SSC_Init(void) |
{ |
// Set MOSI,SCK and CS as output |
DDR_SPI |= (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_CS); |
// set MISO as input |
DDR_SPI &= ~(1<<DD_MISO); |
SSC_Disable(); |
// 20MHz / 32 = 625 kHz |
#ifdef __SD_INTERFACE_INVERTED |
SPCR = (1<<SPE)|(1<<MSTR)|(0<<DORD)|(1<<CPOL)|(0<<CPHA)|(1<<SPR1)|(0<<SPR0); // Enable SSC in mastermode, inverted clockpolarity (idle high) |
#else |
SPCR = (1<<SPE)|(1<<MSTR)|(0<<DORD)|(0<<CPOL)|(0<<CPHA)|(1<<SPR1)|(0<<SPR0); // Enable SSC in mastermode, noninverted clockpolarity (idle low) |
#endif |
SPSR |= (1<<SPI2X); |
// set port pin as input pullup for SD-Card switch |
#ifdef USE_FOLLOWME |
PORTB |= (1 << PORTB2); |
DDRB &= ~(1 << DDB2); |
#endif |
#ifdef USE_SDLOGGER |
PORTB |= (1 << PORTB3); |
DDRB &= ~(1 << DDB3); |
#endif |
} |
void SSC_Deinit(void) |
{ |
SSC_Disable(); |
SPCR = 0; |
SPSR = 0; |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SSC_GetChar(void); |
// |
// Description: This function reads one byte from the SSC |
// |
// |
// Returnvalue: the byte received. |
//________________________________________________________________________________________________________________________________________ |
uint8_t SSC_GetChar (void) |
{ |
uint8_t Byte = 0; |
#ifdef __SD_INTERFACE_INVERTED |
SPDR = 0x00; // send dummy byte to initiate the reading |
#else |
SPDR = 0xFF; // send dummy byte to initiate the reading |
#endif |
while(!(SPSR & (1<<SPIF))) |
{ |
// wait until the data has been read. |
} |
Byte = SPDR; |
#ifdef __SD_INTERFACE_INVERTED |
Byte = ~Byte; |
#endif |
return(Byte); |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SSC_PutChar(u8 Byte); |
// |
// Description: This function writes one byte to the SSC |
// |
// |
// Returnvalue: none |
//________________________________________________________________________________________________________________________________________ |
void SSC_PutChar (uint8_t Byte) |
{ |
#ifdef __SD_INTERFACE_INVERTED |
SPDR = ~Byte; // send one byte of data to the SSC |
#else |
SPDR = Byte; // send one byte of data to the SSC |
#endif |
while(!(SPSR & (1<<SPIF))) |
{ |
// wait until the data has been sent. |
} |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SSC_Disable(void); |
// |
// Description: This function enables chipselect of the sdcard (active low) |
// |
// |
// Returnvalue: none |
//________________________________________________________________________________________________________________________________________ |
void SSC_Disable(void) |
{ |
#ifdef __SD_INTERFACE_INVERTED |
PORTW_SPI &= ~(1<<PORT_CS); // disable chipselect of the sdcard (active low). |
#else |
PORTW_SPI |= (1<<PORT_CS); // disable chipselect of the sdcard (active low). |
#endif |
} |
//________________________________________________________________________________________________________________________________________ |
// Function: SSC_Enable(void); |
// |
// Description: This function disables chipselect of the sdcard (active low) |
// |
// |
// Returnvalue: none |
//________________________________________________________________________________________________________________________________________ |
void SSC_Enable(void) |
{ |
#ifdef __SD_INTERFACE_INVERTED |
PORTW_SPI |= (1<<PORT_CS); // enable chipselect of the sdcard (active low). |
#else |
PORTW_SPI &= ~(1<<PORT_CS); // enable chipselect of the sdcard (active low). |
#endif |
} |
/FollowMe/trunk/ssc.h |
---|
0,0 → 1,20 |
#ifndef __SSC_H |
#define __SSC_H |
#include <inttypes.h> |
#ifdef USE_FOLLOWME |
#define SD_SWITCH !(PINB & (1<<PINB2)) |
#endif |
#ifdef USE_SDLOGGER |
#define SD_SWITCH !(PINB & (1<<PINB3)) |
#endif |
extern void SSC_Init(void); |
extern uint8_t SSC_GetChar(void); |
extern void SSC_PutChar(uint8_t); |
extern void SSC_Enable(void); |
extern void SSC_Disable(void); |
extern void SSC_Deinit(void); |
#endif //__SSC_H |
/FollowMe/trunk/timer0.c |
---|
0,0 → 1,138 |
#include <inttypes.h> |
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include "timer0.h" |
volatile uint16_t CountMilliseconds = 0; |
DateTime_t SystemTime; |
volatile uint16_t BeepTime = 0; |
volatile uint16_t BeepModulation = 0xFFFF; |
/*****************************************************/ |
/* Initialize Timer 0 */ |
/*****************************************************/ |
// timer 0 is used for the PWM generation to control the offset voltage at the air pressure sensor |
// Its overflow interrupt routine is used to generate the beep signal and the flight control motor update rate |
void TIMER0_Init(void) |
{ |
uint8_t sreg = SREG; |
// disable all interrupts before reconfiguration |
cli(); |
// configure speaker port as output |
#ifdef USE_FOLLOWME |
// Speaker at PC7 |
DDRC |= (1<<DDC7); |
PORTC &= ~(1<<PORTC7); |
#endif |
// Timer/Counter 0 Control Register A |
// Waveform Generation None (Bits WGM02 = 0, WGM01 = 0, WGM00 = 0) |
TCCR0A &= ~((1<<COM0A0)|(1<<COM0B0)|(1<<COM0A1)|(1<<COM0B1)|(1<<WGM01)|(1<<WGM00)); |
// Timer/Counter 0 Control Register B |
// set clock devider for timer 0 to SYSKLOCK/8 = 20MHz / 8 = 2.5MHz |
// i.e. the timer increments from 0x00 to 0xFF with an update rate of 2.5 MHz |
// hence the timer overflow interrupt frequency is 2.5 MHz / 256 = 9.765 kHz |
// divider 8 (Bits CS02 = 0, CS01 = 1, CS00 = 0) |
TCCR0B &= ~((1<<FOC0A)|(1<<FOC0B)|(1<<WGM02)); |
TCCR0B = (TCCR0B & 0xF8)|(0<<CS02)|(1<<CS01)|(0<<CS00); |
// init Timer/Counter 0 Register |
TCNT0 = 0; |
// Timer/Counter 0 Interrupt Mask Register |
// enable timer overflow interrupt only |
TIMSK0 &= ~((1<<OCIE0B)|(1<<OCIE0A)); |
TIMSK0 |= (1<<TOIE0); |
SystemTime.Year = 0; |
SystemTime.Month = 0; |
SystemTime.Day = 0; |
SystemTime.Hour = 0; |
SystemTime.Min = 0; |
SystemTime.Sec = 0; |
SystemTime.mSec = 0; |
SystemTime.Valid = 0; |
CountMilliseconds = 0; |
SREG = sreg; |
} |
/*****************************************************/ |
/* Interrupt Routine of Timer 0 */ |
/*****************************************************/ |
ISR(TIMER0_OVF_vect) // 9.765 kHz |
{ |
static uint8_t cnt = 0; |
#ifdef USE_FOLLOWME |
uint8_t Beeper_On = 0; |
#endif |
if(!cnt--) // every 10th run (9.765kHz/10 = 976Hz) |
{ |
cnt = 9; |
CountMilliseconds++; // increment millisecond counter |
} |
// beeper on if duration is not over |
if(BeepTime) |
{ |
BeepTime--; // decrement BeepTime |
if(BeepTime & BeepModulation) Beeper_On = 1; |
else Beeper_On = 0; |
} |
else // beeper off if duration is over |
{ |
Beeper_On = 0; |
BeepModulation = 0xFFFF; |
} |
#ifdef USE_FOLLOWME |
// if beeper is on |
if(Beeper_On) |
{ |
// set speaker port to high |
PORTC |= (1<<PORTC7); // Speaker at PC7 |
} |
else // beeper is off |
{ |
// set speaker port to low |
PORTC &= ~(1<<PORTC7);// Speaker at PC7 |
} |
#endif |
} |
// ----------------------------------------------------------------------- |
uint16_t SetDelay (uint16_t t) |
{ |
return(CountMilliseconds + t - 1); |
} |
// ----------------------------------------------------------------------- |
int8_t CheckDelay(uint16_t t) |
{ |
return(((t - CountMilliseconds) & 0x8000) >> 8); // check sign bit |
} |
// ----------------------------------------------------------------------- |
void Delay_ms(uint16_t w) |
{ |
unsigned int t_stop; |
t_stop = SetDelay(w); |
while (!CheckDelay(t_stop)); |
} |
/FollowMe/trunk/timer0.h |
---|
0,0 → 1,30 |
#ifndef _TIMER0_H |
#define _TIMER0_H |
#include <inttypes.h> |
typedef struct{ |
uint16_t Year; |
uint8_t Month; |
uint8_t Day; |
uint8_t Hour; |
uint8_t Min; |
uint8_t Sec; |
uint16_t mSec; |
uint8_t Valid; |
} DateTime_t; |
extern DateTime_t SystemTime; |
extern volatile uint16_t CountMilliseconds; |
extern volatile uint16_t BeepTime; |
extern volatile uint16_t BeepModulation; |
extern void TIMER0_Init(void); |
extern void Delay_ms(uint16_t w); |
extern void Delay_ms_Mess(uint16_t w); |
extern uint16_t SetDelay (uint16_t t); |
extern int8_t CheckDelay (uint16_t t); |
#endif //_TIMER0_H |
/FollowMe/trunk/uart0.c |
---|
0,0 → 1,524 |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
// + Copyright (c) 04.2007 Holger Buss |
// + only for non-profit use |
// + www.MikroKopter.com |
// + see the File "License.txt" for further Informations |
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include <avr/wdt.h> |
#include <stdarg.h> |
#include <string.h> |
#include "main.h" |
#include "menu.h" |
#include "timer0.h" |
#include "uart0.h" |
#include "ubx.h" |
#include "led.h" |
#define FC_ADDRESS 1 |
#define NC_ADDRESS 2 |
#define MK3MAG_ADDRESS 3 |
#define FM_ADDRESS 10 // FOLLOW ME |
#define FALSE 0 |
#define TRUE 1 |
uint8_t text[100]; // globally used text buffer |
//int8_t test __attribute__ ((section (".noinit"))); |
uint8_t Request_VerInfo = FALSE; |
uint8_t Request_Display = FALSE; |
uint8_t Request_Display1 = FALSE; |
uint8_t Request_ExternalControl = FALSE; |
uint8_t Request_DebugData = FALSE; |
uint8_t Request_DebugLabel = 255; |
uint8_t Request_SendFollowMe = FALSE; |
uint8_t DisplayLine = 0; |
volatile uint8_t txd_buffer[TXD_BUFFER_LEN]; |
volatile uint8_t rxd_buffer_locked = FALSE; |
volatile uint8_t rxd_buffer[RXD_BUFFER_LEN]; |
volatile uint8_t txd_complete = TRUE; |
volatile uint8_t ReceivedBytes = 0; |
volatile uint8_t *pRxData = 0; |
volatile uint8_t RxDataLen = 0; |
uint8_t PcAccess = 100; |
ExternControl_t ExternControl; |
DebugOut_t DebugOut; |
UART_VersionInfo_t UART_VersionInfo; |
uint16_t DebugData_Timer; |
uint16_t DebugData_Interval = 500; // in 1ms |
Waypoint_t FollowMe; |
const uint8_t ANALOG_LABEL[32][16] = |
{ |
//1234567890123456 |
"Analog_Ch0 ", //0 |
"Analog_Ch1 ", |
"Analog_Ch2 ", |
"Analog_Ch3 ", |
"Analog_Ch4 ", |
"Analog_Ch5 ", //5 |
"Analog_Ch6 ", |
"Analog_Ch7 ", |
"UBat ", |
"Speed_North ", |
"Speed_East ", //10 |
"Speed_Top ", |
"NumOfSats ", |
"Pos.Longitude ", |
"Pos.Latitude ", |
"Pos.Altitude ", //15 |
"Zellenzahl ", |
"PowerOn ", |
"Debug18 ", |
"Debug19 ", |
"Debug20 ", //20 |
"Debug21 ", |
"Debug22 ", |
"Debug23 ", |
"Debug24 ", |
"Debug25 ", //25 |
"Debug26 ", |
"Debug27 ", |
"Debug28 ", |
"Debug29 ", |
"Debug30 ", //30 |
"Debug31 " |
}; |
/****************************************************************/ |
/* Initialization of the USART0 */ |
/****************************************************************/ |
void USART0_Init (void) |
{ |
uint8_t sreg = SREG; |
uint16_t ubrr = (uint16_t) ((uint32_t) SYSCLK/(8 * USART0_BAUD) - 1); |
// disable all interrupts before configuration |
cli(); |
// disable RX-Interrupt |
UCSR0B &= ~(1 << RXCIE0); |
// disable TX-Interrupt |
UCSR0B &= ~(1 << TXCIE0); |
// set direction of RXD0 and TXD0 pins |
// set RXD0 (PD0) as an input pin |
PORTD |= (1 << PORTD0); |
DDRD &= ~(1 << DDD0); |
// set TXD0 (PD1) as an output pin |
PORTD |= (1 << PORTD1); |
DDRD |= (1 << DDD1); |
// USART0 Baud Rate Register |
// set clock divider |
UBRR0H = (uint8_t)(ubrr >> 8); |
UBRR0L = (uint8_t)ubrr; |
// USART0 Control and Status Register A, B, C |
// enable double speed operation in |
UCSR0A |= (1 << U2X0); |
// enable receiver and transmitter in |
UCSR0B = (1 << TXEN0) | (1 << RXEN0); |
// set asynchronous mode |
UCSR0C &= ~(1 << UMSEL01); |
UCSR0C &= ~(1 << UMSEL00); |
// no parity |
UCSR0C &= ~(1 << UPM01); |
UCSR0C &= ~(1 << UPM00); |
// 1 stop bit |
UCSR0C &= ~(1 << USBS0); |
// 8-bit |
UCSR0B &= ~(1 << UCSZ02); |
UCSR0C |= (1 << UCSZ01); |
UCSR0C |= (1 << UCSZ00); |
// flush receive buffer |
while ( UCSR0A & (1<<RXC0) ) UDR0; |
// enable interrupts at the end |
// enable RX-Interrupt |
UCSR0B |= (1 << RXCIE0); |
// enable TX-Interrupt |
UCSR0B |= (1 << TXCIE0); |
// initialize the debug timer |
DebugData_Timer = SetDelay(DebugData_Interval); |
// unlock rxd_buffer |
rxd_buffer_locked = FALSE; |
pRxData = 0; |
RxDataLen = 0; |
// no bytes to send |
txd_complete = TRUE; |
UART_VersionInfo.SWMajor = VERSION_MAJOR; |
UART_VersionInfo.SWMinor = VERSION_MINOR; |
UART_VersionInfo.SWPatch = VERSION_PATCH; |
UART_VersionInfo.ProtoMajor = VERSION_SERIAL_MAJOR; |
UART_VersionInfo.ProtoMinor = VERSION_SERIAL_MINOR; |
// restore global interrupt flags |
SREG = sreg; |
} |
/****************************************************************/ |
/* USART0 transmitter ISR */ |
/****************************************************************/ |
ISR(USART0_TX_vect) |
{ |
static uint16_t ptr_txd_buffer = 0; |
uint8_t tmp_tx; |
if(!txd_complete) // transmission not completed |
{ |
ptr_txd_buffer++; // die [0] wurde schon gesendet |
tmp_tx = txd_buffer[ptr_txd_buffer]; |
// if terminating character or end of txd buffer was reached |
if((tmp_tx == '\r') || (ptr_txd_buffer == TXD_BUFFER_LEN)) |
{ |
ptr_txd_buffer = 0; // reset txd pointer |
txd_complete = 1; // stop transmission |
} |
UDR0 = tmp_tx; // send current byte will trigger this ISR again |
} |
// transmission completed |
else ptr_txd_buffer = 0; |
} |
/****************************************************************/ |
/* USART0 receiver ISR */ |
/****************************************************************/ |
ISR(USART0_RX_vect) |
{ |
static uint16_t crc; |
static uint8_t ptr_rxd_buffer = 0; |
uint8_t crc1, crc2; |
uint8_t c; |
c = UDR0; // catch the received byte |
if(rxd_buffer_locked) return; // if rxd buffer is locked immediately return |
// the rxd buffer is unlocked |
if((ptr_rxd_buffer == 0) && (c == '#')) // if rxd buffer is empty and syncronisation character is received |
{ |
rxd_buffer[ptr_rxd_buffer++] = c; // copy 1st byte to buffer |
crc = c; // init crc |
} |
#if 0 |
else if (ptr_rxd_buffer == 1) // handle address |
{ |
rxd_buffer[ptr_rxd_buffer++] = c; // copy byte to rxd buffer |
crc += c; // update crc |
} |
#endif |
else if (ptr_rxd_buffer < RXD_BUFFER_LEN) // collect incomming bytes |
{ |
if(c != '\r') // no termination character |
{ |
rxd_buffer[ptr_rxd_buffer++] = c; // copy byte to rxd buffer |
crc += c; // update crc |
} |
else // termination character was received |
{ |
// the last 2 bytes are no subject for checksum calculation |
// they are the checksum itself |
crc -= rxd_buffer[ptr_rxd_buffer-2]; |
crc -= rxd_buffer[ptr_rxd_buffer-1]; |
// calculate checksum from transmitted data |
crc %= 4096; |
crc1 = '=' + crc / 64; |
crc2 = '=' + crc % 64; |
// compare checksum to transmitted checksum bytes |
if((crc1 == rxd_buffer[ptr_rxd_buffer-2]) && (crc2 == rxd_buffer[ptr_rxd_buffer-1])) |
{ // checksum valid |
rxd_buffer[ptr_rxd_buffer] = '\r'; // set termination character |
ReceivedBytes = ptr_rxd_buffer + 1;// store number of received bytes |
rxd_buffer_locked = TRUE; // lock the rxd buffer |
// if 2nd byte is an 'R' enable watchdog that will result in an reset |
if(rxd_buffer[2] == 'R') {wdt_enable(WDTO_250MS);} // Reset-Commando |
} |
else |
{ // checksum invalid |
rxd_buffer_locked = FALSE; // unlock rxd buffer |
} |
ptr_rxd_buffer = 0; // reset rxd buffer pointer |
} |
} |
else // rxd buffer overrun |
{ |
ptr_rxd_buffer = 0; // reset rxd buffer |
rxd_buffer_locked = FALSE; // unlock rxd buffer |
} |
} |
// -------------------------------------------------------------------------- |
void AddCRC(uint16_t datalen) |
{ |
uint16_t tmpCRC = 0, i; |
for(i = 0; i < datalen; i++) |
{ |
tmpCRC += txd_buffer[i]; |
} |
tmpCRC %= 4096; |
txd_buffer[i++] = '=' + tmpCRC / 64; |
txd_buffer[i++] = '=' + tmpCRC % 64; |
txd_buffer[i++] = '\r'; |
txd_complete = FALSE; |
UDR0 = txd_buffer[0]; // initiates the transmittion (continued in the TXD ISR) |
} |
// -------------------------------------------------------------------------- |
void SendOutData(uint8_t cmd, uint8_t addr, uint8_t numofbuffers, ...) // uint8_t *pdata, uint8_t len, ... |
{ |
va_list ap; |
uint16_t pt = 0; |
uint8_t a,b,c; |
uint8_t ptr = 0; |
uint8_t *pdata = 0; |
int len = 0; |
txd_buffer[pt++] = '#'; // Start character |
txd_buffer[pt++] = 'a' + addr; // Address (a=0; b=1,...) |
txd_buffer[pt++] = cmd; // Command |
va_start(ap, numofbuffers); |
if(numofbuffers) |
{ |
pdata = va_arg(ap, uint8_t*); |
len = va_arg(ap, int); |
ptr = 0; |
numofbuffers--; |
} |
while(len) |
{ |
if(len) |
{ |
a = pdata[ptr++]; |
len--; |
if((!len) && numofbuffers) |
{ |
pdata = va_arg(ap, uint8_t*); |
len = va_arg(ap, int); |
ptr = 0; |
numofbuffers--; |
} |
} |
else a = 0; |
if(len) |
{ |
b = pdata[ptr++]; |
len--; |
if((!len) && numofbuffers) |
{ |
pdata = va_arg(ap, uint8_t*); |
len = va_arg(ap, int); |
ptr = 0; |
numofbuffers--; |
} |
} |
else b = 0; |
if(len) |
{ |
c = pdata[ptr++]; |
len--; |
if((!len) && numofbuffers) |
{ |
pdata = va_arg(ap, uint8_t*); |
len = va_arg(ap, int); |
ptr = 0; |
numofbuffers--; |
} |
} |
else c = 0; |
txd_buffer[pt++] = '=' + (a >> 2); |
txd_buffer[pt++] = '=' + (((a & 0x03) << 4) | ((b & 0xf0) >> 4)); |
txd_buffer[pt++] = '=' + (((b & 0x0f) << 2) | ((c & 0xc0) >> 6)); |
txd_buffer[pt++] = '=' + ( c & 0x3f); |
} |
va_end(ap); |
AddCRC(pt); // add checksum after data block and initates the transmission |
} |
// -------------------------------------------------------------------------- |
void Decode64(void) |
{ |
uint8_t a,b,c,d; |
uint8_t x,y,z; |
uint8_t ptrIn = 3; |
uint8_t ptrOut = 3; |
uint8_t len = ReceivedBytes - 6; |
while(len) |
{ |
a = rxd_buffer[ptrIn++] - '='; |
b = rxd_buffer[ptrIn++] - '='; |
c = rxd_buffer[ptrIn++] - '='; |
d = rxd_buffer[ptrIn++] - '='; |
//if(ptrIn > ReceivedBytes - 3) break; |
x = (a << 2) | (b >> 4); |
y = ((b & 0x0f) << 4) | (c >> 2); |
z = ((c & 0x03) << 6) | d; |
if(len--) rxd_buffer[ptrOut++] = x; else break; |
if(len--) rxd_buffer[ptrOut++] = y; else break; |
if(len--) rxd_buffer[ptrOut++] = z; else break; |
} |
pRxData = &rxd_buffer[3]; |
RxDataLen = ptrOut - 3; |
} |
// -------------------------------------------------------------------------- |
void USART0_ProcessRxData(void) |
{ |
// if data in the rxd buffer are not locked immediately return |
if(!rxd_buffer_locked) return; |
Decode64(); // decode data block in rxd_buffer |
switch(rxd_buffer[1] - 'a') |
{ |
case FM_ADDRESS: |
switch(rxd_buffer[2]) |
{ |
default: |
//unsupported command received |
break; |
} // case FC_ADDRESS: |
default: // any Slave Address |
switch(rxd_buffer[2]) |
{ |
case 'a':// request for labels of the analog debug outputs |
Request_DebugLabel = pRxData[0]; |
if(Request_DebugLabel > 31) Request_DebugLabel = 31; |
PcAccess = 255; |
break; |
case 'h':// request for display columns |
PcAccess = 255; |
RemoteKeys |= pRxData[0]; |
if(RemoteKeys) DisplayLine = 0; |
Request_Display = TRUE; |
break; |
case 'l':// request for display columns |
PcAccess = 255; |
MenuItem = pRxData[0]; |
Request_Display1 = TRUE; |
break; |
case 'v': // request for version and board release |
Request_VerInfo = TRUE; |
break; |
case 'd': // request for the debug data |
DebugData_Interval = (uint16_t) pRxData[0] * 10; |
if(DebugData_Interval > 0) Request_DebugData = TRUE; |
break; |
case 'g':// get external control data |
Request_ExternalControl = TRUE; |
break; |
default: |
//unsupported command received |
break; |
} |
break; // default: |
} |
// unlock the rxd buffer after processing |
pRxData = 0; |
RxDataLen = 0; |
rxd_buffer_locked = FALSE; |
} |
//############################################################################ |
//Routine für die Serielle Ausgabe |
int16_t uart_putchar (int8_t c) |
//############################################################################ |
{ |
if (c == '\n') |
uart_putchar('\r'); |
// wait until previous character was send |
loop_until_bit_is_set(UCSR0A, UDRE0); |
// send character |
UDR0 = c; |
return (0); |
} |
//--------------------------------------------------------------------------------------------- |
void USART0_TransmitTxData(void) |
{ |
if(!txd_complete) return; |
if(Request_VerInfo && txd_complete) |
{ |
SendOutData('V', FM_ADDRESS, 1, (uint8_t *) &UART_VersionInfo, sizeof(UART_VersionInfo)); |
Request_VerInfo = FALSE; |
} |
if(Request_Display && txd_complete) |
{ |
LCD_PrintMenu(); |
SendOutData('H', FM_ADDRESS, 2, &DisplayLine, sizeof(DisplayLine), &DisplayBuff[DisplayLine * 20], 20); |
DisplayLine++; |
if(DisplayLine >= 4) DisplayLine = 0; |
Request_Display = FALSE; |
} |
if(Request_Display1 && txd_complete) |
{ |
LCD_PrintMenu(); |
SendOutData('L', FM_ADDRESS, 3, &MenuItem, sizeof(MenuItem), &MaxMenuItem, sizeof(MaxMenuItem), DisplayBuff, sizeof(DisplayBuff)); |
Request_Display1 = FALSE; |
} |
if(Request_DebugLabel != 0xFF) // Texte für die Analogdaten |
{ |
SendOutData('A', FM_ADDRESS, 2, (uint8_t *) &Request_DebugLabel, sizeof(Request_DebugLabel), ANALOG_LABEL[Request_DebugLabel], 16); |
Request_DebugLabel = 0xFF; |
} |
if(Request_ExternalControl && txd_complete) |
{ |
SendOutData('G', FM_ADDRESS, 1,(uint8_t *) &ExternControl, sizeof(ExternControl)); |
Request_ExternalControl = FALSE; |
} |
if( ((DebugData_Interval && CheckDelay(DebugData_Timer)) || Request_DebugData) && txd_complete) |
{ |
SendOutData('D', FM_ADDRESS, 1,(uint8_t *) &DebugOut, sizeof(DebugOut)); |
DebugData_Timer = SetDelay(DebugData_Interval); |
Request_DebugData = FALSE; |
} |
if(Request_SendFollowMe && txd_complete) |
{ |
SendOutData('s', NC_ADDRESS, 1, (uint8_t *)&FollowMe, sizeof(FollowMe)); |
FollowMe.Position.Status = PROCESSED; |
Request_SendFollowMe = FALSE; |
} |
} |
/FollowMe/trunk/uart0.h |
---|
0,0 → 1,75 |
#ifndef _UART0_H |
#define _UART0_H |
#include "ubx.h" |
#define RXD_BUFFER_LEN 150 |
// must be at least 4('#'+Addr+'CmdID'+'\r')+ (80 * 4)/3 = 111 bytes |
#define TXD_BUFFER_LEN 150 |
#define RXD_BUFFER_LEN 150 |
#include <inttypes.h> |
//Baud rate of the USART |
#define USART0_BAUD 57600 |
extern void USART0_Init (void); |
extern void USART0_TransmitTxData(void); |
extern void USART0_ProcessRxData(void); |
extern int16_t uart_putchar(int8_t c); |
extern uint8_t PcAccess; |
extern uint8_t RemotePollDisplayLine; |
extern uint8_t text[100]; // globally used text buffer |
typedef struct |
{ |
uint8_t Digital[2]; |
uint8_t RemoteButtons; |
int8_t Nick; |
int8_t Roll; |
int8_t Yaw; |
uint8_t Gas; |
int8_t Height; |
uint8_t free; |
uint8_t Frame; |
uint8_t Config; |
} __attribute__((packed)) ExternControl_t; |
extern ExternControl_t ExternControl; |
typedef struct |
{ |
uint8_t Digital[2]; |
uint16_t Analog[32]; // Debugvalues |
} __attribute__((packed)) DebugOut_t; |
extern DebugOut_t DebugOut; |
typedef struct |
{ |
uint8_t SWMajor; |
uint8_t SWMinor; |
uint8_t ProtoMajor; |
uint8_t ProtoMinor; |
uint8_t SWPatch; |
uint8_t Reserved[5]; |
} __attribute__((packed)) UART_VersionInfo_t; |
typedef struct |
{ |
GPS_Pos_t Position; // the gps position of the waypoint, see ubx.h for details |
int16_t Heading; // orientation, future implementation |
uint8_t ToleranceRadius; // in meters, if the MK is within that range around the target, then the next target is triggered |
uint8_t HoldTime; // in seconds, if the was once in the tolerance area around a WP, this time defines the delay before the next WP is triggered |
uint8_t Event_Flag; // future implementation |
uint8_t reserve[12]; // reserve |
} __attribute__((packed)) Waypoint_t; |
extern Waypoint_t FollowMe; |
extern uint8_t Request_SendFollowMe; |
#endif //_UART0_H |
/FollowMe/trunk/uart1.c |
---|
0,0 → 1,94 |
#include <avr/io.h> |
#include <avr/interrupt.h> |
#include "main.h" |
#include "uart1.h" |
#include "ubx.h" |
/****************************************************************/ |
/* Initialization of the USART1 */ |
/****************************************************************/ |
void USART1_Init (void) |
{ |
// USART1 Control and Status Register A, B, C and baud rate register |
uint8_t sreg = SREG; |
uint16_t ubrr = (uint16_t) ((uint32_t) SYSCLK/(8 * USART1_BAUD) - 1); |
// disable all interrupts before reconfiguration |
cli(); |
// disable RX-Interrupt |
UCSR1B &= ~(1 << RXCIE1); |
// disable TX-Interrupt |
UCSR1B &= ~(1 << TXCIE1); |
// disable DRE-Interrupt |
UCSR1B &= ~(1 << UDRIE1); |
// set direction of RXD1 and TXD1 pins |
// set RXD1 (PD2) as an input pin |
PORTD |= (1 << PORTD2); |
DDRD &= ~(1 << DDD2); |
// set TXD1 (PD3) as an output pin |
PORTD |= (1 << PORTD3); |
DDRD |= (1 << DDD3); |
// USART0 Baud Rate Register |
// set clock divider |
UBRR1H = (uint8_t)(ubrr>>8); |
UBRR1L = (uint8_t)ubrr; |
// enable double speed operation |
UCSR1A |= (1 << U2X1); |
// enable receiver and transmitter |
UCSR1B = (1 << TXEN1) | (1 << RXEN1); |
// set asynchronous mode |
UCSR1C &= ~(1 << UMSEL11); |
UCSR1C &= ~(1 << UMSEL10); |
// no parity |
UCSR1C &= ~(1 << UPM11); |
UCSR1C &= ~(1 << UPM10); |
// 1 stop bit |
UCSR1C &= ~(1 << USBS1); |
// 8-bit |
UCSR1B &= ~(1 << UCSZ12); |
UCSR1C |= (1 << UCSZ11); |
UCSR1C |= (1 << UCSZ10); |
// flush receive buffer explicit |
while ( UCSR1A & (1<<RXC1) ) UDR1; |
// enable interrupts at the end |
// enable RX-Interrupt |
UCSR1B |= (1 << RXCIE1); |
// enable TX-Interrupt |
UCSR1B |= (1 << TXCIE1); |
// enable DRE interrupt |
//UCSR1B |= (1 << UDRIE1); |
// restore global interrupt flags |
SREG = sreg; |
} |
/****************************************************************/ |
/* USART1 transmitter ISR */ |
/****************************************************************/ |
/*ISR(USART1_TX_vect) |
{ |
} |
*/ |
/****************************************************************/ |
/* USART1 receiver ISR */ |
/****************************************************************/ |
ISR(USART1_RX_vect) |
{ |
uint8_t c; |
c = UDR1; // get data byte |
UBX_Parser(c); // and put it into the ubx protocol parser |
} |
/FollowMe/trunk/uart1.h |
---|
0,0 → 1,25 |
#ifndef _UART1_H |
#define _UART1_H |
#define USART1_BAUD 57600 |
/* |
Initialize the USART und activate the receiver and transmitter |
as well as the receive-interrupt. The IO-FIFOs are initialized. |
The global interrupt-enable-flag (I-Bit in SREG) is not changed |
*/ |
extern void USART1_Init (void); |
/* |
The character c is stored in the output buffer. If the character was pushed sucessfully to |
the output buffer then the return value is 1. In case of an output buffer overflow the return value is 0. |
The isr is activated, which will send the data from the outbut buffer to the UART. |
*/ |
extern int USART1_putc (const uint8_t c); |
/* |
extern uint8_t USART1_getc_wait(void); |
extern int16_t USART1_getc_nowait(void); |
*/ |
#endif //_UART1_H |
/FollowMe/trunk/ubx.c |
---|
0,0 → 1,413 |
#include <inttypes.h> |
#include "ubx.h" |
#include "timer0.h" |
#include "uart0.h" |
// ------------------------------------------------------------------------------------------------ |
// defines |
#define DAYS_FROM_JAN01YEAR0001_TO_JAN6_1980 722819 // the year 0 does not exist! |
#define DAYS_PER_YEAR 365 |
#define DAYS_PER_LEAPYEAR 366 |
#define DAYS_PER_4YEARS 1461 //((3 * DAYS_PER_YEAR) + DAYS_PER_LEAPYEAR) // years dividable by 4 are leap years |
#define DAYS_PER_100YEARS 36524 //((25 * DAYS_PER_4YEARS) - 1) // years dividable by 100 are no leap years |
#define DAYS_PER_400YEARS 146097 //((4 * DAYS_PER_100YEARS) + 1L) // but years dividable by 400 are leap years |
#define SECONDS_PER_MINUTE 60 |
#define MINUTES_PER_HOUR 60 |
#define HOURS_PER_DAY 24 |
#define DAYS_PER_WEEK 7 |
#define SECONDS_PER_HOUR 3600 //(SECONDS_PER_MINUTE * MINUTES_PER_HOUR) |
#define SECONDS_PER_DAY 86400 //(SECONDS_PER_HOUR * HOURS_PER_DAY) |
#define SECONDS_PER_WEEK 604800 //(SECONDS_PER_DAY * DAYS_PER_WEEK) |
// days per month in normal and leap years |
const uint32_t Leap[ 13 ] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; |
const uint32_t Normal[ 13 ] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; |
#define LEAP_SECONDS_FROM_1980 15 // the last one was on the Dec 31th 2008 |
// message sync bytes |
#define UBX_SYNC1_CHAR 0xB5 |
#define UBX_SYNC2_CHAR 0x62 |
// protocoll identifier |
#define UBX_CLASS_NAV 0x01 |
// message id |
#define UBX_ID_POSLLH 0x02 |
#define UBX_ID_SOL 0x06 |
#define UBX_ID_VELNED 0x12 |
// ------------------------------------------------------------------------------------------------ |
// typedefs |
// ubx parser state |
typedef enum |
{ |
UBXSTATE_IDLE, |
UBXSTATE_SYNC1, |
UBXSTATE_SYNC2, |
UBXSTATE_CLASS, |
UBXSTATE_LEN1, |
UBXSTATE_LEN2, |
UBXSTATE_DATA, |
UBXSTATE_CKA, |
UBXSTATE_CKB |
} ubxState_t; |
typedef struct |
{ |
uint32_t itow; // ms GPS Millisecond Time of Week |
int32_t frac; // ns remainder of rounded ms above |
int16_t week; // GPS week |
uint8_t GPSfix; // GPSfix Type, range 0..6 |
uint8_t Flags; // Navigation Status Flags |
int32_t ECEF_X; // cm ECEF X coordinate |
int32_t ECEF_Y; // cm ECEF Y coordinate |
int32_t ECEF_Z; // cm ECEF Z coordinate |
int32_t PAcc; // cm 3D Position Accuracy Estimate |
int32_t ECEFVX; // cm/s ECEF X velocity |
int32_t ECEFVY; // cm/s ECEF Y velocity |
int32_t ECEFVZ; // cm/s ECEF Z velocity |
uint32_t SAcc; // cm/s Speed Accuracy Estimate |
uint16_t PDOP; // 0.01 Position DOP |
uint8_t res1; // reserved |
uint8_t numSV; // Number of SVs used in navigation solution |
uint32_t res2; // reserved |
uint8_t Status; // invalid/newdata/processed |
} __attribute__((packed)) ubx_nav_sol_t; |
typedef struct |
{ |
uint32_t itow; // ms GPS Millisecond Time of Week |
int32_t VEL_N; // cm/s NED north velocity |
int32_t VEL_E; // cm/s NED east velocity |
int32_t VEL_D; // cm/s NED down velocity |
int32_t Speed; // cm/s Speed (3-D) |
int32_t GSpeed; // cm/s Ground Speed (2-D) |
int32_t Heading; // 1e-05 deg Heading 2-D |
uint32_t SAcc; // cm/s Speed Accuracy Estimate |
uint32_t CAcc; // deg Course / Heading Accuracy Estimate |
uint8_t Status; // invalid/newdata/processed |
} __attribute__((packed)) ubx_nav_velned_t; |
typedef struct |
{ |
uint32_t itow; // ms GPS Millisecond Time of Week |
int32_t LON; // 1e-07 deg Longitude |
int32_t LAT; // 1e-07 deg Latitude |
int32_t HEIGHT; // mm Height above Ellipsoid |
int32_t HMSL; // mm Height above mean sea level |
uint32_t Hacc; // mm Horizontal Accuracy Estimate |
uint32_t Vacc; // mm Vertical Accuracy Estimate |
uint8_t Status; // invalid/newdata/processed |
} __attribute__((packed)) ubx_nav_posllh_t; |
//------------------------------------------------------------------------------------ |
// global variables |
// local buffers for the incomming ubx messages |
volatile ubx_nav_sol_t UbxSol = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, INVALID}; |
volatile ubx_nav_posllh_t UbxPosLlh = {0,0,0,0,0,0,0, INVALID}; |
volatile ubx_nav_velned_t UbxVelNed = {0,0,0,0,0,0,0,0,0, INVALID}; |
uint16_t CheckGPSOkay = 0; |
// shared buffer |
gps_data_t GPSData = {{0,0,0,INVALID},0,0,0,0,0,0,0, INVALID}; |
//------------------------------------------------------------------------------------ |
// functions |
uint8_t IsLeapYear(uint16_t year) |
{ |
if((year%400 == 0) || ( (year%4 == 0) && (year%100 != 0) ) ) return 1; |
else return 0; |
} |
/********************************************************/ |
/* Calculates the UTC Time from the GPS week and tow */ |
/********************************************************/ |
void SetGPSTime(DateTime_t * pTimeStruct) |
{ |
uint32_t Days, Seconds, Week; |
uint16_t YearPart; |
uint32_t * MonthDayTab = 0; |
uint8_t i; |
// if GPS data show valid time data |
if((UbxSol.Status != INVALID) && (UbxSol.Flags & FLAG_WKNSET) && (UbxSol.Flags & FLAG_TOWSET) ) |
{ |
Seconds = UbxSol.itow / 1000L; |
Week = (uint32_t)UbxSol.week; |
// correct leap seconds since 1980 |
if(Seconds < LEAP_SECONDS_FROM_1980) |
{ |
Week--; |
Seconds = SECONDS_PER_WEEK - LEAP_SECONDS_FROM_1980 + Seconds; |
} |
else Seconds -= LEAP_SECONDS_FROM_1980; |
Days = DAYS_FROM_JAN01YEAR0001_TO_JAN6_1980; |
Days += (Week * DAYS_PER_WEEK); |
Days += Seconds / SECONDS_PER_DAY; // seperate days from GPS seconds of week |
pTimeStruct->Year = 1; |
YearPart = (uint16_t)(Days / DAYS_PER_400YEARS); |
pTimeStruct->Year += YearPart * 400; |
Days = Days % DAYS_PER_400YEARS; |
YearPart = (uint16_t)(Days / DAYS_PER_100YEARS); |
pTimeStruct->Year += YearPart * 100; |
Days = Days % DAYS_PER_100YEARS; |
YearPart = (uint16_t)(Days / DAYS_PER_4YEARS); |
pTimeStruct->Year += YearPart * 4; |
Days = Days % DAYS_PER_4YEARS; |
if(Days < (3* DAYS_PER_YEAR)) YearPart = (uint16_t)(Days / DAYS_PER_YEAR); |
else YearPart = 3; |
pTimeStruct->Year += YearPart; |
// calculate remaining days of year |
Days -= (uint32_t)(YearPart * DAYS_PER_YEAR); |
Days += 1; |
// check if current year is a leap year |
if(IsLeapYear(pTimeStruct->Year)) MonthDayTab = (uint32_t*)Leap; |
else MonthDayTab = (uint32_t*)Normal; |
// seperate month and day from days of year |
for ( i = 0; i < 12; i++ ) |
{ |
if ( (MonthDayTab[i]< Days) && (Days <= MonthDayTab[i+1]) ) |
{ |
pTimeStruct->Month = i+1; |
pTimeStruct->Day = Days - MonthDayTab[i]; |
i = 12; |
} |
} |
Seconds = Seconds % SECONDS_PER_DAY; // remaining seconds of current day |
pTimeStruct->Hour = (uint8_t)(Seconds / SECONDS_PER_HOUR); |
Seconds = Seconds % SECONDS_PER_HOUR; // remaining seconds of current hour |
pTimeStruct->Min = (uint8_t)(Seconds / SECONDS_PER_MINUTE); |
Seconds = Seconds % SECONDS_PER_MINUTE; // remaining seconds of current minute |
pTimeStruct->Sec = (uint8_t)(Seconds); |
pTimeStruct->mSec = (uint16_t)(UbxSol.itow % 1000L); |
pTimeStruct->Valid = 1; |
} |
else |
{ |
pTimeStruct->Valid = 0; |
} |
} |
/********************************************************/ |
/* Initialize UBX Parser */ |
/********************************************************/ |
void UBX_Init(void) |
{ |
// mark msg buffers invalid |
UbxSol.Status = INVALID; |
UbxPosLlh.Status = INVALID; |
UbxVelNed.Status = INVALID; |
GPSData.Status = INVALID; |
} |
/********************************************************/ |
/* Upate GPS data stcructure */ |
/********************************************************/ |
void Update_GPSData (void) |
{ |
static uint16_t Ubx_Timeout = 0; |
static uint8_t Msg_Count = 0; |
// the timeout is used to detect the delay between two message sets |
// and is used for synchronisation so that always a set is collected |
// that belongs together |
// _______NAVSOL|POSLLH|VELNED|___________________NAVSOL|POSLLH|VELNED|_____________ |
// | 8ms | 8ms | 184 ms | | | |
// msg_count: 0 1 2 0 1 2 |
if(CheckDelay(Ubx_Timeout)) Msg_Count = 0; |
else Msg_Count++; |
Ubx_Timeout = SetDelay(100); // reset ubx msg timeout |
// if a new set of ubx messages was collected |
if((Msg_Count >= 2)) |
{ // if set is complete |
if((UbxSol.Status == NEWDATA) && (UbxPosLlh.Status == NEWDATA) && (UbxVelNed.Status == NEWDATA)) |
{ |
CheckGPSOkay++; |
// update GPS data only if the status is INVALID or PROCESSED and the last ubx message was received within less than 100 ms |
if(GPSData.Status != NEWDATA) // if last data were processed |
{ // wait for new data at all neccesary ubx messages |
GPSData.Status = INVALID; |
// NAV SOL |
GPSData.Flags = UbxSol.Flags; |
GPSData.NumOfSats = UbxSol.numSV; |
GPSData.SatFix = UbxSol.GPSfix; |
GPSData.Position_Accuracy = UbxSol.PAcc; |
GPSData.Speed_Accuracy = UbxSol.SAcc; |
SetGPSTime(&SystemTime); // update system time |
// NAV POSLLH |
GPSData.Position.Status = INVALID; |
GPSData.Position.Longitude = UbxPosLlh.LON; |
GPSData.Position.Latitude = UbxPosLlh.LAT; |
GPSData.Position.Altitude = UbxPosLlh.HMSL; |
GPSData.Position.Status = NEWDATA; |
// NAV VELNED |
GPSData.Speed_East = UbxVelNed.VEL_E; |
GPSData.Speed_North = UbxVelNed.VEL_N; |
GPSData.Speed_Top = -UbxVelNed.VEL_D; |
GPSData.Speed_Ground = UbxVelNed.GSpeed; |
GPSData.Heading = UbxVelNed.Heading; |
GPSData.Status = NEWDATA; // new data available |
} // EOF if(GPSData.Status != NEWDATA) |
} // EOF all ubx messages received |
// set state to collect new data |
UbxSol.Status = PROCESSED; // ready for new data |
UbxPosLlh.Status = PROCESSED; // ready for new data |
UbxVelNed.Status = PROCESSED; // ready for new data |
} |
} |
/********************************************************/ |
/* UBX Parser */ |
/********************************************************/ |
void UBX_Parser(uint8_t c) |
{ |
static ubxState_t ubxState = UBXSTATE_IDLE; |
static uint16_t msglen; |
static uint8_t cka, ckb; |
static uint8_t *ubxP, *ubxEp, *ubxSp; // pointers to data currently transfered |
//state machine |
switch (ubxState) // ubx message parser |
{ |
case UBXSTATE_IDLE: // check 1st sync byte |
if (c == UBX_SYNC1_CHAR) ubxState = UBXSTATE_SYNC1; |
else ubxState = UBXSTATE_IDLE; // out of synchronization |
break; |
case UBXSTATE_SYNC1: // check 2nd sync byte |
if (c == UBX_SYNC2_CHAR) ubxState = UBXSTATE_SYNC2; |
else ubxState = UBXSTATE_IDLE; // out of synchronization |
break; |
case UBXSTATE_SYNC2: // check msg class to be NAV |
if (c == UBX_CLASS_NAV) ubxState = UBXSTATE_CLASS; |
else ubxState = UBXSTATE_IDLE; // unsupported message class |
break; |
case UBXSTATE_CLASS: // check message identifier |
switch(c) |
{ |
case UBX_ID_POSLLH: // geodetic position |
ubxP = (uint8_t *)&UbxPosLlh; // data start pointer |
ubxEp = (uint8_t *)(&UbxPosLlh + 1); // data end pointer |
ubxSp = (uint8_t *)&UbxPosLlh.Status; // status pointer |
break; |
case UBX_ID_SOL: // navigation solution |
ubxP = (uint8_t *)&UbxSol; // data start pointer |
ubxEp = (uint8_t *)(&UbxSol + 1); // data end pointer |
ubxSp = (uint8_t *)&UbxSol.Status; // status pointer |
break; |
case UBX_ID_VELNED: // velocity vector in tangent plane |
ubxP = (uint8_t *)&UbxVelNed; // data start pointer |
ubxEp = (uint8_t *)(&UbxVelNed + 1); // data end pointer |
ubxSp = (uint8_t *)&UbxVelNed.Status; // status pointer |
break; |
default: // unsupported identifier |
ubxState = UBXSTATE_IDLE; |
break; |
} |
if (ubxState != UBXSTATE_IDLE) |
{ |
ubxState = UBXSTATE_LEN1; |
cka = UBX_CLASS_NAV + c; |
ckb = UBX_CLASS_NAV + cka; |
} |
break; |
case UBXSTATE_LEN1: // 1st message length byte |
msglen = (uint16_t)c; // lowbyte first |
cka += c; |
ckb += cka; |
ubxState = UBXSTATE_LEN2; |
break; |
case UBXSTATE_LEN2: // 2nd message length byte |
msglen += ((uint16_t)c)<<8; // high byte last |
cka += c; |
ckb += cka; |
// if the old data are not processed so far then break parsing now |
// to avoid writing new data in ISR during reading by another function |
if ( *ubxSp == NEWDATA ) |
{ |
ubxState = UBXSTATE_IDLE; |
Update_GPSData(); //update GPS info respectively |
} |
else // data invalid or allready processd |
{ |
*ubxSp = INVALID; // mark invalid during buffer filling |
ubxState = UBXSTATE_DATA; |
} |
break; |
case UBXSTATE_DATA: // collecting data |
if (ubxP < ubxEp) |
{ |
*ubxP++ = c; // copy curent data byte if any space is left |
cka += c; |
ckb += cka; |
if (--msglen == 0) ubxState = UBXSTATE_CKA; // switch to next state if all data was read |
} |
else // rx buffer overrun |
{ |
ubxState = UBXSTATE_IDLE; |
} |
break; |
case UBXSTATE_CKA: |
if (c == cka) ubxState = UBXSTATE_CKB; |
else |
{ |
*ubxSp = INVALID; |
ubxState = UBXSTATE_IDLE; |
} |
break; |
case UBXSTATE_CKB: |
if (c == ckb) |
{ |
*ubxSp = NEWDATA; // new data are valid |
Update_GPSData(); //update GPS info respectively |
} |
else |
{ // if checksum not match then set data invalid |
*ubxSp = INVALID; |
} |
ubxState = UBXSTATE_IDLE; // ready to parse new data |
break; |
default: // unknown ubx state |
ubxState = UBXSTATE_IDLE; |
break; |
} |
DebugOut.Analog[9] = GPSData.Speed_North; |
DebugOut.Analog[10] = GPSData.Speed_East; |
DebugOut.Analog[11] = GPSData.Speed_Top; |
DebugOut.Analog[12] = GPSData.NumOfSats; |
DebugOut.Analog[13] = GPSData.Position.Longitude; |
DebugOut.Analog[14] = GPSData.Position.Latitude; |
DebugOut.Analog[15] = GPSData.Position.Altitude; |
} |
/FollowMe/trunk/ubx.h |
---|
0,0 → 1,57 |
#ifndef _UBX_H |
#define _UBX_H |
#include <inttypes.h> |
// Satfix types for GPSData.SatFix |
#define SATFIX_NONE 0x00 |
#define SATFIX_DEADRECKOING 0x01 |
#define SATFIX_2D 0x02 |
#define SATFIX_3D 0x03 |
#define SATFIX_GPS_DEADRECKOING 0x04 |
#define SATFIX_TIMEONLY 0x05 |
// Flags for interpretation of the GPSData.Flags |
#define FLAG_GPSFIXOK 0x01 // (i.e. within DOP & ACC Masks) |
#define FLAG_DIFFSOLN 0x02 // (is DGPS used) |
#define FLAG_WKNSET 0x04 // (is Week Number valid) |
#define FLAG_TOWSET 0x08 // (is Time of Week valid) |
#define INVALID 0x00 |
#define NEWDATA 0x01 |
#define PROCESSED 0x02 |
typedef struct |
{ |
int32_t Longitude; // in 1E-7 deg |
int32_t Latitude; // in 1E-7 deg |
int32_t Altitude; // in mm |
uint8_t Status; // validity of data |
} __attribute__((packed)) GPS_Pos_t; |
typedef struct |
{ |
GPS_Pos_t Position; // Lat/Lon/Alt |
uint8_t Flags; // Status Flags |
uint8_t NumOfSats; // number of satelites |
uint8_t SatFix; // type of satfix |
uint32_t Position_Accuracy; // in cm 3d position accuracy |
int32_t Speed_North; // in cm/s |
int32_t Speed_East; // in cm/s |
int32_t Speed_Top; // in cm/s |
uint32_t Speed_Ground; // 2D ground speed in cm/s |
int32_t Heading; // 1e-05 deg Heading 2-D (curent flight direction) |
uint32_t Speed_Accuracy; // in cm/s 3d velocity accuracy |
uint8_t Status; // status of data |
} __attribute__((packed)) gps_data_t; |
// The data are valid if the GPSData.Status is NEWDATA or PROCESSED. |
// To achieve new data after reading the GPSData.Status should be set to PROCESSED. |
extern gps_data_t GPSData; |
extern uint16_t CheckGPSOkay; |
void UBX_Init(void); |
void UBX_Parser(uint8_t c); |
#endif // _UBX_H |