view Discovery/Src/externLogbookFlash.c @ 426:514e6269256f ImprovmentNVM_2

Added function to analyse the sampel ringbuffer: The function will show 0 for used sectors, 4 for the sector currently in use and 5 for empty sectors. This allows identification of log sample index position and identification of a buffer corruption (more than 2 sectors have state 4) The repair function writes dummy bytes to the end of the active buffer with the lower sector number. This decision is based on the fact that corruption results typically in a reset of index to buffer start address. After repair the writing will be continued using the hugher buffer marked as used.
author ideenmodellierer
date Sat, 15 Feb 2020 20:50:58 +0100
parents 86fcac4cc43a
children b1091e183d52
line wrap: on
line source

/**
  ******************************************************************************
	* @copyright heinrichs weikamp
  * @file    externLogbookFlash.c
  * @author  heinrichs weikamp gmbh
  * @date    07-Aug-2014
  * @version V0.0.4
  * @since   29-Sept-2015
  * @brief   Main File to access the new 1.8 Volt Spansion S25FS256S 256 Mbit (32 Mbyte)
	* @bug
	* @warning
  *
  @verbatim
  ==============================================================================
              ##### Logbook Header (TOC) #####
  ==============================================================================
  [..] Memory useage:
	NEW: Spansion S25FS-S256S
	
			only 8x 4KB and 1x 32KB, remaining is 64KB or 256KB
			Sector Size (kbyte) Sector Count Sector Range Address Range (Byte Address) Notes
			 4   8	SA00  00000000h-00000FFFh 
								:							:
							SA07  00007000h-00007FFFh

			32   1  SA08  00008000h-0000FFFFh

			64 511  SA09  00010000h-0001FFFFh
								: 						:
							SA519 01FF0000h-01FFFFFFh			
	OLD:
				1kB each header
				with predive header at beginning
				and postdive header with 0x400 HEADER2OFFSET
				4kB (one erase) has two dives with 4 headers total
				total of 512 kB (with 256 header ids (8 bit))
				Size is 280 Byte (as of 25.Nov. 2014)

	[..] Output to PC / UART is postdive header

	[..] Block Protection Lock-Down is to erase logbook only

	[..] Timing (see page 137 of LOGBOOK_V3_S25FS-S_00-271247.pdf
				bulk erase is 2 minutes typ., 6 minutes max.

  ==============================================================================
              ##### DEMOMODE #####
  ==============================================================================
	151215: ext_flash_write_settings() is DISABLED!
	
	==============================================================================
              ##### bug fixes #####
  ==============================================================================
	150917:	end in header and length of sample was one byte too long 
					as stated by Jef Driesen email 15.09.2015
					
	@endverbatim
	******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2015 heinrichs weikamp</center></h2>
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "externLogbookFlash.h"
#include "ostc.h"
#include "settings.h"
#include "gfx_engine.h"

#ifndef BOOTLOADER_STANDALONE
#include "logbook.h"
#endif

/* Private types -------------------------------------------------------------*/
#define FLASHSTART 	0x000000
//#define FLASHSTOP 	0x01FFFFFF all 32 MB with 4byte addressing
#define FLASHSTOP 	0x00FFFFFF 
//#define FLASHSTOP 	0x3FFFFF
#define RELEASE		1
#define HOLDCS		0

#define HEADER2OFFSET 0x400

typedef enum{
	EF_HEADER,
	EF_SAMPLE,
	EF_DEVICEDATA,
	EF_VPMDATA,
	EF_SETTINGS,
	EF_FIRMWARE,
	EF_FIRMWARE2,
}which_ring_enum;


typedef struct{
uint8_t IsBusy:1;
uint8_t IsWriteEnabled:1;
uint8_t BlockProtect0:1;
uint8_t BlockProtect1:1;
uint8_t BlockProtect2:1;
uint8_t BlockProtect3:1;
uint8_t IsAutoAddressIncMode:1;
uint8_t BlockProtectL:1;
} extFlashStatusUbit8_t;

typedef union{
extFlashStatusUbit8_t ub;
uint8_t uw;
} extFlashStatusBit8_Type;


/* Exported variables --------------------------------------------------------*/

/* Private variables ---------------------------------------------------------*/
static uint32_t	actualAddress = 0;
static uint32_t	preparedPageAddress = 0;
static uint32_t closeSectorAddress = 0;
static uint32_t	entryPoint = 0;

static uint32_t	actualPointerHeader = 0;
static uint32_t	actualPointerSample = 0;
static uint32_t	LengthLeftSampleRead = 0;
static uint32_t	actualPointerDevicedata = DDSTART;
static uint32_t	actualPointerDevicedata_Read = DDSTART;
static uint32_t	actualPointerVPM = 0;
static uint32_t	actualPointerSettings = 0;
static uint32_t	actualPointerFirmware = 0;
static uint32_t	actualPointerFirmware2 = 0;

/* Private function prototypes -----------------------------------------------*/
static void chip_unselect(void);
static void chip_select(void);
static void error_led_on(void);
static void error_led_off(void);

static void write_spi(uint8_t data, uint8_t unselect_CS_afterwards);
static uint8_t read_spi(uint8_t unselect_CS_afterwards);
static void write_address(uint8_t unselect_CS_afterwards);
static void Error_Handler_extflash(void);
static void wait_chip_not_busy(void);
static void ext_flash_incf_address(uint8_t type);
//void ext_flash_incf_address_ring(void);
static void ext_flash_decf_address_ring(uint8_t type);

static void ext_flash_erase4kB(void);
static void ext_flash_erase32kB(void);
static void ext_flash_erase64kB(void);
static uint8_t ext_flash_erase_if_on_page_start(void);

static void ef_write_block(uint8_t * sendByte, uint32_t length, uint8_t type, uint8_t do_not_erase);

static void ext_flash_read_block(uint8_t *getByte, uint8_t type);
static void ext_flash_read_block_multi(void *getByte, uint32_t size, uint8_t type);
static void ext_flash_read_block_stop(void);

static void ef_hw_rough_delay_us(uint32_t delayUs);
static void ef_erase_64K(uint32_t blocks);

static void ext_flash_overwrite_sample_without_erase(uint8_t *pSample, uint16_t length);

static void ext_flash_disable_protection(void);

static _Bool ext_flash_test_remaining_space_of_page_empty(uint32_t pointer, uint16_t length);
static void ext_flash_set_to_begin_of_next_page(uint32_t *pointer, uint8_t type);
static void ext_flash_find_start(void);


/* Exported functions --------------------------------------------------------*/

void ext_flash_write_firmware(uint8_t *pSample1, uint32_t length1)//, uint8_t *pSample2, uint32_t length2)
{
	general32to8_Type lengthTransform;

	lengthTransform.u32 = length1;
	
	actualPointerFirmware = FWSTART;
	ef_write_block(lengthTransform.u8,4, EF_FIRMWARE, 1);
	ef_write_block(pSample1,length1, EF_FIRMWARE, 1);

//	if(length2)
//		ef_write_block(pSample2,length2, EF_FIRMWARE, 1);
}

uint8_t ext_flash_read_firmware_version(char *text)
{
	uint32_t backup = actualAddress;
	uint8_t buffer[4];
	
	// + 4 for length data, see ext_flash_write_firmware
	actualAddress = FWSTART + 4 + 0x10000;
	ext_flash_read_block_start();
	ext_flash_read_block(&buffer[0], EF_FIRMWARE);
	ext_flash_read_block(&buffer[1], EF_FIRMWARE);
	ext_flash_read_block(&buffer[2], EF_FIRMWARE);
	ext_flash_read_block(&buffer[3], EF_FIRMWARE);

	ext_flash_read_block_stop();
	actualAddress = backup;

	uint8_t ptr = 0;
	text[ptr++] = 'V';
	ptr += gfx_number_to_string(2,0,&text[ptr],buffer[0] & 0x3F);
	text[ptr++] = '.';
	ptr += gfx_number_to_string(2,0,&text[ptr],buffer[1] & 0x3F);
	text[ptr++] = '.';
	ptr += gfx_number_to_string(2,0,&text[ptr],buffer[2] & 0x3F);
	text[ptr++] = ' ';
	if(buffer[3])
	{
		text[ptr++] = 'b';
		text[ptr++] = 'e';
		text[ptr++] = 't';
		text[ptr++] = 'a';
		text[ptr++] = ' ';
	}
	return ptr;
}


uint32_t ext_flash_read_firmware(uint8_t *pSample1, uint32_t max_length, uint8_t *magicByte)
{
	uint32_t backup = actualAddress;
	general32to8_Type lengthTransform;
	
	actualAddress = FWSTART;
	ext_flash_read_block_start();

	ext_flash_read_block(&lengthTransform.u8[0], EF_FIRMWARE);
	ext_flash_read_block(&lengthTransform.u8[1], EF_FIRMWARE);
	ext_flash_read_block(&lengthTransform.u8[2], EF_FIRMWARE);
	ext_flash_read_block(&lengthTransform.u8[3], EF_FIRMWARE);

	
	if(lengthTransform.u32 == 0xFFFFFFFF)
	{
		lengthTransform.u32 = 0xFFFFFFFF;
	}
	else
	if(lengthTransform.u32 > max_length)
	{
		lengthTransform.u32 = 0xFF000000;
	}
	else
	{
		for(uint32_t i = 0; i<lengthTransform.u32; i++)
		{
			ext_flash_read_block(&pSample1[i], EF_FIRMWARE);
		}
		
	}
	
	ext_flash_read_block_stop();
	
	if(magicByte)
	{
		*magicByte = pSample1[0x10000 + 0x3E]; // 0x3E == 62 
	}
	
	actualAddress = backup;
	return lengthTransform.u32;
}


void ext_flash_write_firmware2(uint32_t offset, uint8_t *pSample1, uint32_t length1, uint8_t *pSample2, uint32_t length2)
{
	general32to8_Type lengthTransform, offsetTransform;

	lengthTransform.u32 = length1 + length2;
	offsetTransform.u32 = offset;
	
	actualPointerFirmware2 = FWSTART2;
	ef_write_block(lengthTransform.u8,4, EF_FIRMWARE2, 1);
	ef_write_block(offsetTransform.u8,4, EF_FIRMWARE2, 1);
	ef_write_block(pSample1,length1, EF_FIRMWARE2, 1);
	if(length2)
		ef_write_block(pSample2,length2, EF_FIRMWARE2, 1);
}


uint32_t ext_flash_read_firmware2(uint32_t *offset, uint8_t *pSample1, uint32_t max_length1, uint8_t *pSample2, uint32_t max_length2)
{
	uint32_t backup = actualAddress;
	uint32_t length1, length2;
	general32to8_Type lengthTransform, offsetTransform;

	actualAddress = FWSTART2;
	ext_flash_read_block_start();

	ext_flash_read_block(&lengthTransform.u8[0], EF_FIRMWARE2);
	ext_flash_read_block(&lengthTransform.u8[1], EF_FIRMWARE2);
	ext_flash_read_block(&lengthTransform.u8[2], EF_FIRMWARE2);
	ext_flash_read_block(&lengthTransform.u8[3], EF_FIRMWARE2);

	ext_flash_read_block(&offsetTransform.u8[0], EF_FIRMWARE2);
	ext_flash_read_block(&offsetTransform.u8[1], EF_FIRMWARE2);
	ext_flash_read_block(&offsetTransform.u8[2], EF_FIRMWARE2);
	ext_flash_read_block(&offsetTransform.u8[3], EF_FIRMWARE2);
	
	*offset = offsetTransform.u32;
	
	if(lengthTransform.u32 == 0xFFFFFFFF)
	{
		lengthTransform.u32 = 0xFFFFFFFF;
	}
	else
	if(lengthTransform.u32 > max_length1 + max_length2)
	{
		lengthTransform.u32 = 0xFF000000;
	}
	else
	{
		if(lengthTransform.u32 <  max_length1)
		{
			length1 = lengthTransform.u32;
			length2 = 0;
		}
		else
		{
			length1 = max_length1;
			length2 = lengthTransform.u32 - max_length1;
		}
		
		if(pSample1)
		{
			for(uint32_t i = 0; i<length1; i++)
			{
				ext_flash_read_block(&pSample1[i], EF_FIRMWARE2);
			}
			if(pSample2)
			{
				for(uint32_t i = 0; i<length2; i++)
				{
					ext_flash_read_block(&pSample2[i], EF_FIRMWARE2);
				}
			}
		}
		else if(pSample2)
		{
			actualAddress += length1;
			for(uint32_t i = 0; i<length2; i++)
			{
				ext_flash_read_block(&pSample2[i], EF_FIRMWARE2);
			}
		}
	}
	ext_flash_read_block_stop();
	actualAddress = backup;
	return lengthTransform.u32;
}


void ext_flash_read_fixed_16_devicedata_blocks_formated_128byte_total(uint8_t *buffer)
{
	SDeviceLine data[16];
	uint8_t tempLengthIngnore;
	uint16_t count;
	uint8_t transfer;

	RTC_DateTypeDef Sdate;
	RTC_TimeTypeDef Stime;
	
	actualAddress = DDSTART;

	ext_flash_read_block_start();
	ext_flash_read_block(&tempLengthIngnore, EF_DEVICEDATA);
	ext_flash_read_block(&tempLengthIngnore, EF_DEVICEDATA);
	
	ext_flash_read_block_multi((uint8_t *)data,16*3*4, EF_DEVICEDATA);
	ext_flash_read_block_stop();

	count = 0;
	for(int i=0;i<16;i++)
	{
		transfer = (data[i].value_int32 >> 24) & 0xFF;
		buffer[count++] = transfer;
		transfer = (data[i].value_int32 >> 16) & 0xFF;
		buffer[count++] = transfer;
		transfer = (data[i].value_int32 >> 8) & 0xFF;
		buffer[count++] = transfer;
		transfer = (data[i].value_int32) & 0xFF;
		buffer[count++] = transfer;

		translateDate(data[i].date_rtc_dr, &Sdate);
		translateTime(data[i].time_rtc_tr, &Stime);
		buffer[count++] = Sdate.Year;
		buffer[count++] = Sdate.Month;
		buffer[count++] = Sdate.Date;
		buffer[count++] = Stime.Hours;
	}
}


#ifndef BOOTLOADER_STANDALONE

void ext_flash_write_devicedata(uint8_t resetRing)
{
	uint8_t *pData;
	const uint16_t length = sizeof(SDevice);
	uint8_t length_lo, length_hi;
	uint8_t dataLength[2] = { 0 };
	uint32_t tmpBlockStart;

	ext_flash_disable_protection();

	pData = (uint8_t *)stateDeviceGetPointer();

	/* Reset the Ring to the start address if requested (e.g. because we write the default block during shutdown) */
	if((resetRing) || ((actualPointerDevicedata + length) >= DDSTOP))
	{
		actualPointerDevicedata = DDSTART;
	}
	tmpBlockStart = actualPointerDevicedata;

	length_lo = (uint8_t)(length & 0xFF);
	length_hi = (uint8_t)(length >> 8);
	dataLength[0] = length_lo;
	dataLength[1] = length_hi;

	ef_write_block(dataLength,2, EF_DEVICEDATA, 0);
	ef_write_block(pData,length, EF_DEVICEDATA, 0);

	actualPointerDevicedata_Read = tmpBlockStart;

}


uint16_t ext_flash_read_devicedata(uint8_t *buffer, uint16_t max_length)
{
	uint16_t length;
	uint8_t length_lo, length_hi;

	actualAddress = actualPointerDevicedata_Read; 

	length = 0;
	length_lo = 0;
	length_hi = 0;
	ext_flash_read_block_start();


	ext_flash_read_block(&length_lo, EF_DEVICEDATA);
	ext_flash_read_block(&length_hi, EF_DEVICEDATA);
	
	while ((length_lo != 0xFF) && (length_hi != 0xFF))
	{
		length = (length_hi * 256) + length_lo;

		if(length > max_length)
			return 0;

		ext_flash_read_block_multi(buffer,length,EF_DEVICEDATA);

		ext_flash_read_block(&length_lo, EF_DEVICEDATA);	/* check if another devicedata set is available */
		ext_flash_read_block(&length_hi, EF_DEVICEDATA);	/* length will be 0xFFFF if a empty memory is read */
	}
	ext_flash_decf_address_ring(EF_DEVICEDATA);				/* set pointer back to empty address */
	ext_flash_decf_address_ring(EF_DEVICEDATA);
	ext_flash_read_block_stop();

	if(actualAddress > actualPointerDevicedata)				/* the write pointer has not yet been set up probably */
	{
		actualPointerDevicedata = actualAddress;
	}
	return length;
}


void ext_flash_write_vpm(SVpm *vpmInput)
{
	uint8_t *pData;
	const uint16_t length = sizeof(SVpm);

	uint8_t length_lo, length_hi;
	uint8_t dataLength[2] = { 0 };
	
	pData = (uint8_t *)vpmInput;

	actualPointerVPM = VPMSTART;

	length_lo = (uint8_t)(length & 0xFF);
	length_hi = (uint8_t)(length >> 8);
	dataLength[0] = length_lo;
	dataLength[1] = length_hi;

	ef_write_block(dataLength,2, EF_VPMDATA, 0);
	ef_write_block(pData,length, EF_VPMDATA, 0);
}


int ext_flash_read_vpm(SVpm *vpmOutput)
{
	uint8_t *pData;
	const uint16_t length = sizeof(SVpm);
	uint8_t length_lo, length_hi;
	int output;
	
	actualAddress = VPMSTART;

	ext_flash_read_block_start();
	ext_flash_read_block(&length_lo, EF_VPMDATA);
	ext_flash_read_block(&length_hi, EF_VPMDATA);
	if((length_lo == (uint8_t)(length & 0xFF))
		&&(length_hi == (uint8_t)(length >> 8)))
	{
		pData = (uint8_t *)vpmOutput;
		for(uint16_t i = 0; i < length; i++)
			ext_flash_read_block(&pData[i], EF_VPMDATA);
		output = length;
	}
	else
		output = 0;
	
	ext_flash_read_block_stop();
	return output;
}

#ifdef DEMOMODE
void ext_flash_write_settings(void)
{
	return;
}
#else
void ext_flash_write_settings(void)
{
	uint8_t *pData;
	const uint16_t length = sizeof(SSettings);
	uint8_t length_lo, length_hi;
	uint8_t dataLength[2] = { 0 };

	ext_flash_disable_protection();

	if(stateRealGetPointer()->lastKnownBatteryPercentage)
	{
		settingsGetPointer()->lastKnownBatteryPercentage = stateRealGetPointer()->lastKnownBatteryPercentage;
	}
	settingsGetPointer()->backup_localtime_rtc_tr = stateRealGetPointer()->lifeData.timeBinaryFormat;
	settingsGetPointer()->backup_localtime_rtc_dr = stateRealGetPointer()->lifeData.dateBinaryFormat;

	pData = (uint8_t *)settingsGetPointer();

	actualPointerSettings = SETTINGSSTART;

	length_lo = (uint8_t)(length & 0xFF);
	length_hi = (uint8_t)(length >> 8);
	dataLength[0] = length_lo;
	dataLength[1] = length_hi;

	ef_write_block(dataLength,2, EF_SETTINGS, 0);
	ef_write_block(pData,length, EF_SETTINGS, 0);
//	ext_flash_enable_protection();
}
#endif


/* CHANGES 150929 hw 
 * this now allows to read old settings too
 * but make sure that everything is fixed in 
 * set_new_settings_missing_in_ext_flash
 * new settings should be fine as they are added
 * and loaded before calling this function
 */
uint8_t ext_flash_read_settings(void)
{
	uint8_t returnValue = HAL_BUSY;
	uint8_t *pData;
	const uint16_t lengthStandardNow = sizeof(SSettings);
	uint8_t length_lo, length_hi;
	uint16_t lengthOnEEPROM;
	uint32_t header;
	SSettings *pSettings = settingsGetPointer();

	actualAddress = SETTINGSSTART;

	ext_flash_read_block_start();
	ext_flash_read_block(&length_lo, EF_SETTINGS);
	ext_flash_read_block(&length_hi, EF_SETTINGS);
	
	lengthOnEEPROM = length_hi * 256;
	lengthOnEEPROM += length_lo;
	if(lengthOnEEPROM <= lengthStandardNow)
	{
		ext_flash_read_block_multi(&header, 4, EF_SETTINGS);
		if((header <= pSettings->header) && (header >= pSettings->updateSettingsAllowedFromHeader))
		{
			returnValue = HAL_OK;
			pSettings->header = header;
			pData = (uint8_t *)pSettings + 4; /* header */
			for(uint16_t i = 0; i < (lengthOnEEPROM-4); i++)
				ext_flash_read_block(&pData[i], EF_SETTINGS);
		}
		else
		{
			returnValue = HAL_ERROR;
		}
	}
	ext_flash_read_block_stop();
	return returnValue;
}




	/* ext_flash_start_new_dive_log_and_set_actualPointerSample
	 * prepares the write sample pointer
	 * to be used by ext_flash_write_sample()
	 * to be set in the * pHeaderPreDive
	 * for write with ext_flash_create_new_dive_log() and ext_flash_close_new_dive_log()
	 */
void ext_flash_start_new_dive_log_and_set_actualPointerSample(uint8_t *pHeaderPreDive)
{
	convert_Type data;
	SSettings *settings = settingsGetPointer();

	/* new 5. Jan. 2015 */
	actualPointerSample = settings->logFlashNextSampleStartAddress;

	if(!ext_flash_test_remaining_space_of_page_empty(actualPointerSample, 4))
		ext_flash_set_to_begin_of_next_page(&actualPointerSample, EF_SAMPLE);

	if((actualPointerSample < SAMPLESTART) || (actualPointerSample > SAMPLESTOP))
		actualPointerSample = SAMPLESTART;

	data.u32bit = actualPointerSample;
	pHeaderPreDive[2] = data.u8bit.byteLow;
	pHeaderPreDive[3] = data.u8bit.byteMidLow;
	pHeaderPreDive[4] = data.u8bit.byteMidHigh;
	/* to start sample writing and header etc. pp. */
	ext_flash_disable_protection_for_logbook();
}


	/* ext_flash_create_new_dive_log
	 * uses the first header without HEADER2OFFSET
	 * for the header it is not important to be complete
	 * and can be reconstructed
	 * ext_flash_start_new_dive_log_and_set_actualPointerSample()
	 * has to be called before to set the actualPointerSample
	 * in the header
	 * the following func writes to header to the ext_flash
	 */
void ext_flash_create_new_dive_log(uint8_t *pHeaderPreDive)
{
	SSettings *settings;
	uint8_t id, id_next;
	uint8_t  header1, header2;

	settings = settingsGetPointer();
	id = settings->lastDiveLogId;

	actualAddress = HEADERSTART + (0x800 * id);
	ext_flash_read_block_start();
	ext_flash_read_block(&header1, EF_SAMPLE);
	ext_flash_read_block(&header2, EF_SAMPLE);
	ext_flash_read_block_stop();

	if((header1 == 0xFA) && (header2 == 0xFA))
	{
		id += 1; /* 0-255, auto rollover */
		if(id & 1)
		{
			actualAddress = HEADERSTART + (0x800 * id);
			ext_flash_read_block_start();
			ext_flash_read_block(&header1, EF_SAMPLE);
			ext_flash_read_block(&header2, EF_SAMPLE);
			ext_flash_read_block_stop();
			if((header1 == 0xFA) && (header2 == 0xFA))
				id += 1;
		}
	}
	else
	{
		id = 0;
	}

	/* delete next header */
	id_next = id + 1;
	actualPointerHeader = HEADERSTART + (0x800 * id_next);
	ef_write_block(0,0, EF_HEADER, 0);

	settings->lastDiveLogId = id;
	actualPointerHeader = HEADERSTART + (0x800 * id);

	if(pHeaderPreDive != 0)
		ef_write_block(pHeaderPreDive,HEADERSIZE, EF_HEADER, 0);
}


void ext_flash_close_new_dive_log(uint8_t *pHeaderPostDive )
{
	SSettings *	settings = settingsGetPointer();
	uint8_t id;
	convert_Type startAddress;
	convert_Type data;
	uint32_t backup;

	uint8_t	sampleData[3];
  actualAddress = actualPointerSample;
	sampleData[0] = 0xFD;
	sampleData[1] = 0xFD;
  ext_flash_write_sample(sampleData, 2);
	
	/* end of sample data, pointing to the last sample 0xFD
	*/
  actualAddress = actualPointerSample; // change hw 17.09.2015
	ext_flash_decf_address_ring(EF_SAMPLE); // 17.09.2015: this decf actualAddress only!!
	actualPointerSample = actualAddress; // change hw 17.09.2015
	data.u32bit = actualPointerSample;
	
	pHeaderPostDive[5] = data.u8bit.byteLow;
	pHeaderPostDive[6] = data.u8bit.byteMidLow;
	pHeaderPostDive[7] = data.u8bit.byteMidHigh;

	/* take data written before, calculate length and write
		SLogbookHeader has different order: length (byte# 8,9,10) prior to profile version (byte# 11)
	*/
	startAddress.u8bit.byteLow 			= pHeaderPostDive[2];
	startAddress.u8bit.byteMidLow 	= pHeaderPostDive[3];
	startAddress.u8bit.byteMidHigh 	= pHeaderPostDive[4];
	startAddress.u8bit.byteHigh = 0;

	if(startAddress.u32bit < actualPointerSample)
		data.u32bit = 1 + actualPointerSample - startAddress.u32bit;
	else
		data.u32bit = 2 + (actualPointerSample - SAMPLESTART) + (SAMPLESTOP - startAddress.u32bit);

	pHeaderPostDive[8] = data.u8bit.byteLow;
	pHeaderPostDive[9] = data.u8bit.byteMidLow;
	pHeaderPostDive[10] = data.u8bit.byteMidHigh;

	/* set id and write post-dive-header
	*/
	id = settings->lastDiveLogId;
	actualPointerHeader = HEADERSTART + (0x800 * id) + HEADER2OFFSET;

	ef_write_block(pHeaderPostDive,HEADERSIZE, EF_HEADER, 0);

	/* write length at beginning of sample
		and write proper beginning for next dive to actualPointerSample
	*/
	backup = actualPointerSample;
	actualPointerSample = startAddress.u32bit; // is still 0xFF
	sampleData[0] = data.u8bit.byteLow;
	sampleData[1] = data.u8bit.byteMidLow;
	sampleData[2] = data.u8bit.byteMidHigh;
  ext_flash_overwrite_sample_without_erase(sampleData, 3);

	actualAddress = backup;
	ext_flash_incf_address(EF_SAMPLE);
	actualPointerSample = actualAddress;
	ext_flash_enable_protection();
}


void ext_flash_write_sample(uint8_t *pSample, uint16_t length)
{
	uint32_t actualAdressBackup = 0;

	ef_write_block(pSample,length, EF_SAMPLE, 0);

	SSettings *settings = settingsGetPointer();
	settings->logFlashNextSampleStartAddress = actualPointerSample;

	if(0xFFFF - (actualAddress & 0x0000FFFF) < 255)								/* are we close to a sector border? */
	{
		if (((actualAddress & 0x0000FFFF) != 0) && (preparedPageAddress == 0))		/* only prepare if not already at start of sector */
		{
			actualAdressBackup = actualAddress;
			actualAddress = (actualAddress & 0xFFFF0000) + 0x00010000;	/* Set to start of next 64k sector */
			if(actualAddress >= SAMPLESTOP)
			{
				actualAddress = SAMPLESTART;
			}
			preparedPageAddress = actualAddress;
			ext_flash_erase64kB();
			actualAddress = actualAdressBackup;
		}
	}
}

static void ext_flash_overwrite_sample_without_erase(uint8_t *pSample, uint16_t length)
{
	ef_write_block(pSample,length, EF_SAMPLE, 1);
}


uint8_t ext_flash_count_dive_headers(void)
{
	uint8_t id = 0;
	uint8_t counter = 0;
	uint16_t headerStartData = 0x0000;
	
	id = settingsGetPointer()->lastDiveLogId;

	do
	{
		actualAddress = HEADERSTART + (0x800 * id) + HEADER2OFFSET;
		ext_flash_read_block_start();
		ext_flash_read_block_multi((uint8_t *)&headerStartData, 2, EF_HEADER);
		ext_flash_read_block_stop();
		counter++;
		id -=1;
	} while((headerStartData == 0xFAFA) && (counter < 255));
	return (counter - 1);
}


void ext_flash_read_dive_header(uint8_t *pHeaderToFill, uint8_t StepBackwards)
{
	SSettings *settings;
	uint8_t id;
	uint16_t i;

	settings = settingsGetPointer();
	id = settings->lastDiveLogId;
	id -= StepBackwards; /* 0-255, auto rollover */

	actualAddress = HEADERSTART + (0x800 * id) + HEADER2OFFSET;
	ext_flash_read_block_start();
	for(i = 0; i < HEADERSIZE; i++)
		ext_flash_read_block(&pHeaderToFill[i], EF_HEADER);
	ext_flash_read_block_stop();

}

void ext_flash_read_dive_header2(uint8_t *pHeaderToFill, uint8_t id, _Bool bOffset)
{

	uint16_t i;
	actualAddress = HEADERSTART + (0x800 * id) ;

	if(bOffset)
	  actualAddress += HEADER2OFFSET;
	ext_flash_read_block_start();
	for(i = 0; i < HEADERSIZE; i++)
		ext_flash_read_block(&pHeaderToFill[i], EF_HEADER);
	ext_flash_read_block_stop();
}


uint32_t ext_flash_read_dive_raw_with_double_header_1K(uint8_t *data, uint32_t max_size, uint8_t StepBackwards)
{
	if(max_size < 0x800) 
		return 0;
	
	uint8_t id;
	convert_Type dataStart, dataEnd;
	uint32_t LengthAll = 0;
	
	id = settingsGetPointer()->lastDiveLogId;
	id -= StepBackwards; /* 0-255, auto rollover */

	// clear data
	for(int i=0;i<0x800;i++)
		data[i] = 0xFF;
	
	// copy primary/pre-dive
	actualAddress = HEADERSTART + (0x800 * id);
	ext_flash_read_block_start();
	for(int i = 0; i < HEADERSIZE; i++)
		ext_flash_read_block(&data[i], EF_HEADER);
	ext_flash_read_block_stop();

	// copy main/secondary/post-dive
	actualAddress = HEADERSTART + (0x800 * id) + HEADER2OFFSET;
	ext_flash_read_block_start();
	for(int i = 0x400; i < HEADERSIZE+0x400; i++)
		ext_flash_read_block(&data[i], EF_HEADER);
	ext_flash_read_block_stop();
	
	// data

	dataStart.u8bit.byteHigh = 0;
	dataStart.u8bit.byteLow 		= data[0x402];
	dataStart.u8bit.byteMidLow 	= data[0x403];
	dataStart.u8bit.byteMidHigh	= data[0x404];

	dataEnd.u8bit.byteHigh = 0;
	dataEnd.u8bit.byteLow 			= data[0x405];
	dataEnd.u8bit.byteMidLow 		= data[0x406];
	dataEnd.u8bit.byteMidHigh		= data[0x407];
	
	actualPointerSample = dataStart.u32bit;
	if(dataEnd.u32bit >= dataStart.u32bit)
		LengthAll = 1 + dataEnd.u32bit - dataStart.u32bit;
	else
		LengthAll = 2 + (dataStart.u32bit - SAMPLESTART) + (SAMPLESTOP - dataEnd.u32bit);

	LengthAll += 0x800;
	
	if(LengthAll > max_size)
		return 0x800;

	actualAddress = actualPointerSample;
	ext_flash_read_block_start();
	for(uint32_t i = 0x800; i < LengthAll; i++)
		ext_flash_read_block(&data[i], EF_SAMPLE);
	ext_flash_read_block_stop();
	return LengthAll;
}

void ext_flash_write_dive_raw_with_double_header_1K(uint8_t *data, uint32_t length)
{
	convert_Type dataStart, dataEnd;
	SLogbookHeader  headerTemp;

	// set actualPointerSample and get pointer to sample storage and disable flash write protect
	ext_flash_start_new_dive_log_and_set_actualPointerSample((uint8_t *)&headerTemp);
	
	dataStart.u8bit.byteHigh = 0;
	dataStart.u8bit.byteLow 		= headerTemp.pBeginProfileData[0];
	dataStart.u8bit.byteMidLow 	= headerTemp.pBeginProfileData[1];
	dataStart.u8bit.byteMidHigh	= headerTemp.pBeginProfileData[2];

	dataEnd.u32bit = dataStart.u32bit + length - 0x801;
	if(dataEnd.u32bit > SAMPLESTOP)
		dataEnd.u32bit -=  SAMPLESTOP + SAMPLESTART - 1;
	
	data[0x002] = data[0x402] = dataStart.u8bit.byteLow;
	data[0x003] = data[0x403] = dataStart.u8bit.byteMidLow;
	data[0x004] = data[0x404] = dataStart.u8bit.byteMidHigh;
	data[0x005] = data[0x405] = dataEnd.u8bit.byteLow;
	data[0x006] = data[0x406] = dataEnd.u8bit.byteMidLow;
	data[0x007] = data[0x407] = dataEnd.u8bit.byteMidHigh;

	// set actualPointerHeader to next free header and update lastDiveLogId
	ext_flash_create_new_dive_log(0);

	// copy header data
	ef_write_block(data,0x800,EF_HEADER, 1);
	
 // copy sample data
	ef_write_block(&data[0x800], length-0x800, EF_SAMPLE, 1);
	
	// update logFlashNextSampleStartAddress
	settingsGetPointer()->logFlashNextSampleStartAddress = actualPointerSample;
}


//  ===============================================================================
//  ext_flash_read_header_memory
/// @brief		This function returns the entire header space 1:1
///	@date			04-April-2016
///
/// @param	*data 256KB output
//  ===============================================================================
void ext_flash_read_header_memory(uint8_t *data)
{
	actualAddress = HEADERSTART;
	actualPointerHeader = actualAddress;
	ext_flash_read_block_start();
	for(int i=0;i<8;i++)
		ext_flash_read_block_multi(&data[0x8000 * i], 0x8000, EF_HEADER);
	ext_flash_read_block_stop();
}


//  ===============================================================================
//  ext_flash_read_header_memory
/// @brief		This function erases and overwrites the entire logbook header block
///	@date			04-April-2016
///
/// @param	*data 256KB input of header memory 1:1
//  ===============================================================================
void ext_flash_write_header_memory(uint8_t *data)
{
	actualAddress = HEADERSTART;
	actualPointerHeader = actualAddress;
	ef_write_block(data, 0x40000, EF_HEADER, 0);
}


void ext_flash_open_read_sample(uint8_t StepBackwards, uint32_t *totalNumberOfBytes)
{
	SSettings *settings = settingsGetPointer();
	uint8_t id;
	convert_Type dataStart, dataEnd;
	uint8_t header1, header2;

	id = settings->lastDiveLogId;
	id -= StepBackwards; /* 0-255, auto rollover */
#
	actualAddress = HEADERSTART + (0x800 * id) + HEADER2OFFSET;
	actualPointerHeader = actualAddress;

	ext_flash_read_block_start();
	/* little endian */
	ext_flash_read_block(&header1, EF_HEADER);
	ext_flash_read_block(&header2, EF_HEADER);
	dataStart.u8bit.byteHigh = 0;
	ext_flash_read_block(&dataStart.u8bit.byteLow, EF_HEADER);
	ext_flash_read_block(&dataStart.u8bit.byteMidLow, EF_HEADER);
	ext_flash_read_block(&dataStart.u8bit.byteMidHigh, EF_HEADER);
	dataEnd.u8bit.byteHigh = 0;
	ext_flash_read_block(&dataEnd.u8bit.byteLow, EF_HEADER);
	ext_flash_read_block(&dataEnd.u8bit.byteMidLow, EF_HEADER);
	ext_flash_read_block(&dataEnd.u8bit.byteMidHigh, EF_HEADER);
	ext_flash_read_block_stop();

	actualPointerSample = dataStart.u32bit;
	if(dataEnd.u32bit >= dataStart.u32bit)
		LengthLeftSampleRead = 1 + dataEnd.u32bit - dataStart.u32bit;
	else
		LengthLeftSampleRead = 2 + (dataStart.u32bit - SAMPLESTART) + (SAMPLESTOP - dataEnd.u32bit);
	*totalNumberOfBytes = LengthLeftSampleRead;

	actualAddress = actualPointerSample;
	ext_flash_read_block_start();
}


void ext_flash_read_next_sample_part(uint8_t *pSample, uint8_t length)
{
	for(uint16_t i = 0; i < length; i++)
		ext_flash_read_block(&pSample[i], EF_SAMPLE);
}


void ext_flash_close_read_sample(void)
{
  actualPointerSample =	actualAddress;
	ext_flash_read_block_stop();
}


void ext_flash_set_entry_point(void)
{
	entryPoint = actualAddress;
}


void ext_flash_reopen_read_sample_at_entry_point(void)
{
	error_led_on();
	chip_unselect();
	wait_chip_not_busy();

	actualAddress = entryPoint;
	ext_flash_read_block_start();
	error_led_off();
}

/*
uint8_t ext_flash_point_to_64k_block_in_headerSpace(uint8_t logId)
{
	uint32_t pointerToData = logId * 0x800;
	
	return pointerToData / 0x10000;
}
*/


//  ===============================================================================
//	ext_flash_repair_dive_numbers_starting_count_helper
/// @brief	
///	@date			22-June-2016

//  ===============================================================================
static uint16_t ext_flash_repair_dive_numbers_starting_count_helper(uint8_t *data, uint8_t *change64k, uint16_t startNumber, uint8_t lastLogId)
{
	const uint32_t headerStep = 0x800;
	uint8_t	actualLogId = 0;
	uint16_t oldNumber = 0;
	uint16_t actualNumber = 0;
	SLogbookHeader *ptrLogbookHeader = 0;
	
	if(startNumber == 0)
		return 0;

	actualNumber = startNumber - 1;	

	// where is the oldest dive (Which is now getting startNumber)
	// use first header for ease (without HEADER2OFFSET for end of dive header)
	// compare for lastLogId to prevent endless loop

	if(*(uint16_t*)&data[lastLogId * headerStep] != 0xFAFA)
		return 0;
	
	actualLogId = lastLogId - 1;
	while((*(uint16_t*)&data[actualLogId * headerStep] == 0xFAFA) && (actualLogId != lastLogId))
	{
		actualLogId--;
	}
	
	// now pointing to one behind the last
	while(actualLogId != lastLogId)
	{
		actualLogId++;
		actualNumber++;
		ptrLogbookHeader = (SLogbookHeader *)&data[actualLogId * headerStep];
		
		oldNumber = ptrLogbookHeader->diveNumber;
		if(oldNumber != actualNumber)
		{
//			change64k[ext_flash_point_to_64k_block_in_headerSpace(actualLogId )] = 1;
			change64k[(actualLogId * 0x800)/0x10000] = 1;
			ptrLogbookHeader->diveNumber = actualNumber;
			ptrLogbookHeader = (SLogbookHeader *)(&data[actualLogId * headerStep] + HEADER2OFFSET);
			ptrLogbookHeader->diveNumber = actualNumber;
		}
	}
	
	return actualNumber;
}

	//  ===============================================================================
//	ext_flash_repair_SPECIAL_dive_numbers_starting_count_with
/// @brief	This function
///	@date    04-April-2016
/// problem (160621): 64K blocks (32 dives) in the new flash memory chip
/// This block needs to be deleted
/// these where only 4KB block before
/// @output endCount, last diveNumber

//  ===============================================================================
uint16_t ext_flash_repair_SPECIAL_dive_numbers_starting_count_with(uint16_t startCount)
{
	uint32_t logCopyDataPtr = 0;
	uint8_t *data;
	uint16_t lastCount;
	uint8_t listOfChanged64kBlocks[8]; // 32 dives each 64K
	
	logCopyDataPtr = getFrame(97);
	data = (uint8_t *)logCopyDataPtr;

	for(int i=0;i<8;i++)
		listOfChanged64kBlocks[i] = 0;

	actualAddress = HEADERSTART;
	ext_flash_read_block_start();
	ext_flash_read_block_multi(data,0x100000,EF_HEADER);
	ext_flash_read_block_stop();

	lastCount = ext_flash_repair_dive_numbers_starting_count_helper(data, listOfChanged64kBlocks, startCount, settingsGetPointer()->lastDiveLogId);
	
	for(int i=0;i<8;i++)
	{
		if(listOfChanged64kBlocks[i] != 0)
		{
			actualPointerHeader = HEADERSTART + (i * 0x10000);
			ef_write_block(&data[i * 0x10000], 0x10000, EF_HEADER, 0);
		}
	}

	releaseFrame(97,logCopyDataPtr);
	if(settingsGetPointer()->totalDiveCounter < lastCount)
	{
		settingsGetPointer()->totalDiveCounter = lastCount;
	}
	return lastCount;
}

/*
void OLD_ext_flash_repair_SPECIAL_dive_numbers_starting_count_with(uint16_t startCount)
{
	uint16_t counterStorage[256];
	uint8_t start = 0xFF;
	uint32_t logCopyDataPtr = 0;
	uint8_t *data;
	uint8_t startAbsolute = 0;
	int16_t count = 0;
	_Bool repair = 0;
	uint8_t startBackup = 0;
	
	SLogbookHeader  tempLogbookHeader;
	SLogbookHeader *ptrHeaderInData1a;
	SLogbookHeader *ptrHeaderInData1b;
	SLogbookHeader *ptrHeaderInData2a;
	SLogbookHeader *ptrHeaderInData2b;

	logCopyDataPtr = getFrame(97);
	data = (uint8_t *)logCopyDataPtr;
	ptrHeaderInData1a = (SLogbookHeader *)logCopyDataPtr;
	ptrHeaderInData1b = (SLogbookHeader *)(logCopyDataPtr + HEADER2OFFSET);
	ptrHeaderInData2a = (SLogbookHeader *)(logCopyDataPtr + 0x800);
	ptrHeaderInData2b = (SLogbookHeader *)(logCopyDataPtr + 0x800 + HEADER2OFFSET);
	
	// get data
	for(int StepBackwards = 0; StepBackwards < 255; StepBackwards++)
	{
		logbook_getHeader(StepBackwards, &tempLogbookHeader);
		counterStorage[StepBackwards+1] = tempLogbookHeader.diveNumber;
		if(tempLogbookHeader.diveHeaderStart == 0xFAFA)
			start = StepBackwards;
		else
			break;
	}
	
	if(start == 0xFF)
		return;
	
	count = start + 1;
	startAbsolute = settingsGetPointer()->lastDiveLogId;



	if(start%2)
	{
		if(counterStorage[start] != startCount)
		{
			// clear data
			for(int i=0;i<0x800*2;i++)
				data[i] = 0xFF;

			uint8_t id = settingsGetPointer()->lastDiveLogId;
			id -= start; // 0-255, auto rollover
			
			// copy primary/pre-dive
			actualAddress = HEADERSTART + (0x800 * id);
			ext_flash_read_block_start();
			for(int i = 0; i < HEADERSIZE; i++)
				ext_flash_read_block(&data[i], EF_HEADER);
			ext_flash_read_block_stop();

			// copy main/secondary/post-dive
			actualAddress = HEADERSTART + (0x800 * id) + HEADER2OFFSET;
			ext_flash_read_block_start();
			for(int i = 0x400; i < HEADERSIZE+0x400; i++)
				ext_flash_read_block(&data[i], EF_HEADER);
			ext_flash_read_block_stop();
			
			// repair
			ptrHeaderInData2a->diveNumber = startCount;
			ptrHeaderInData2b->diveNumber = startCount;
			startCount++;

			// write
			actualAddress = HEADERSTART + (0x800 * (id-1));
			ef_write_block(data,0x800*2,EF_HEADER, 0);
		}
		start--;
	}

//	for(int count = start; count > -1; count -= 2)

	while(count > 0)
	{
			// clear data
			for(int i=0;i<0x1000;i++)
				data[i] = 0xFF;

		repair = 0;
		
		startBackup = startAbsolute;
		
		if(startAbsolute%2) // 0x800 to 0x1000
		{
			// copy second pre-dive
			actualAddress = HEADERSTART + (0x800 * startAbsolute);
			ext_flash_read_block_start();
			for(int i = 0x800; i < HEADERSIZE+0x800; i++)
				ext_flash_read_block(&data[i], EF_HEADER);
			ext_flash_read_block_stop();

			// copy second post-dive
			actualAddress = HEADERSTART + HEADER2OFFSET + (0x800 * startAbsolute);
			ext_flash_read_block_start();
			for(int i = 0xC00; i < HEADERSIZE+0xC00; i++)
				ext_flash_read_block(&data[i], EF_HEADER);
			ext_flash_read_block_stop();

			if(counterStorage[count] != startCount)
			{
				ptrHeaderInData2a->diveNumber = startCount;
				ptrHeaderInData2b->diveNumber = startCount;
				repair = 1; 
			}
			startCount += 1;
			
			startAbsolute -= 1;
			count -= 1;
			
			if(count > 0)
			{
				// copy  first pre-dive
				actualAddress = HEADERSTART + (0x800 * startAbsolute);
				ext_flash_read_block_start();
				for(int i = 0; i < HEADERSIZE; i++)
					ext_flash_read_block(&data[i], EF_HEADER);
				ext_flash_read_block_stop();

				// copy first post-dive
				actualAddress = HEADERSTART + (0x800 * startAbsolute);
				ext_flash_read_block_start();
				for(int i = 0x400; i < HEADERSIZE+0x400; i++)
					ext_flash_read_block(&data[i], EF_HEADER);
				ext_flash_read_block_stop();

				if(counterStorage[count] != startCount)
				{
					ptrHeaderInData1a->diveNumber = startCount;
					ptrHeaderInData1b->diveNumber = startCount;
					repair = 1;
				}
				startCount += 1;

				startAbsolute -= 1;
				count -= 1;
			}
		}
		else
		{
			// copy  first pre-dive
			actualAddress = HEADERSTART + (0x800 * startAbsolute);
			ext_flash_read_block_start();
			for(int i = 0; i < HEADERSIZE; i++)
				ext_flash_read_block(&data[i], EF_HEADER);
			ext_flash_read_block_stop();

			// copy first post-dive
			actualAddress = HEADERSTART + (0x800 * startAbsolute);
			ext_flash_read_block_start();
			for(int i = 0x400; i < HEADERSIZE+0x400; i++)
				ext_flash_read_block(&data[i], EF_HEADER);
			ext_flash_read_block_stop();

			if(counterStorage[count] != startCount)
			{
				ptrHeaderInData1a->diveNumber = startCount;
				ptrHeaderInData1b->diveNumber = startCount;
				repair = 1;
			}
			startCount += 1;

			startAbsolute -= 1;
			count -= 1;
		}
			
		// write
		if(repair)
		{
			actualPointerHeader = HEADERSTART + (0x1000 * startBackup%2);
			ef_write_block(data,0x1000,EF_HEADER, 0);
		}
	}
	releaseFrame(97,logCopyDataPtr);
	settingsGetPointer()->totalDiveCounter = startCount;
}
*/

//  ===============================================================================
//	ext_flash_repair_dive_log
/// @brief	This function 
///					does set
///						logFlashNextSampleStartAddress
///					and
///						lastDiveLogId
///
void ext_flash_repair_dive_log(void)
{
	uint8_t  header1, header2;
  convert_Type dataStart;

  for(int id = 0; id < 255;id++)
  {
    actualAddress = HEADERSTART + (0x800 * id);
    ext_flash_read_block_start();
    ext_flash_read_block(&header1, EF_HEADER);
    ext_flash_read_block(&header2, EF_HEADER);
    dataStart.u8bit.byteHigh = 0;
    ext_flash_read_block(&dataStart.u8bit.byteLow, EF_HEADER);
    ext_flash_read_block(&dataStart.u8bit.byteMidLow, EF_HEADER);
    ext_flash_read_block(&dataStart.u8bit.byteMidHigh, EF_HEADER);
    ext_flash_read_block_stop();
    if((header1 == 0xFA) && (header2 == 0xFA))						/* Header is indicating the start of a dive */
    {
      actualAddress = HEADERSTART + (0x800 * id) + HEADER2OFFSET;
      ext_flash_read_block_start();
      ext_flash_read_block(&header1, EF_HEADER);
      ext_flash_read_block(&header2, EF_HEADER);
      ext_flash_read_block_stop();
      if((header1 != 0xFA) || (header2 != 0xFA))					/* Secondary header was not written at the end of a dive */
      {
        actualPointerSample = dataStart.u32bit;						/* Set datapointer to position stored in header written at beginning of dive */
        actualAddress = actualPointerSample;
        logbook_recover_brokenlog(id);
        SSettings *settings = settingsGetPointer();
        settings->logFlashNextSampleStartAddress = actualPointerSample;
      }
    }
  }
  ext_flash_find_start();
}


static void ext_flash_find_start(void)
{
	uint8_t id;
	uint8_t  header1, header2;
  convert_Type dataStart, dataEnd;

  for(id = 0; id < 255;id++)
  {
    actualAddress = HEADERSTART + (0x800 * id) + HEADER2OFFSET;
    ext_flash_read_block_start();
    ext_flash_read_block(&header1, EF_HEADER);
    ext_flash_read_block(&header2, EF_HEADER);
    dataStart.u8bit.byteHigh = 0;
    ext_flash_read_block(&dataStart.u8bit.byteLow, EF_HEADER);
    ext_flash_read_block(&dataStart.u8bit.byteMidLow, EF_HEADER);
    ext_flash_read_block(&dataStart.u8bit.byteMidHigh, EF_HEADER);
    ext_flash_read_block_stop();
    if((header1 == 0xFF) && (header2 == 0xFF))
    {
      break;
    }
  }
  id--;
  SSettings *settings = settingsGetPointer();
	settings->lastDiveLogId = id;

	actualAddress = HEADERSTART + (0x800 * id) + HEADER2OFFSET;
	actualPointerHeader = actualAddress;

  ext_flash_read_block_start();

  ext_flash_read_block(&header1, EF_HEADER);
  ext_flash_read_block(&header2, EF_HEADER);
  dataStart.u8bit.byteHigh = 0;
  ext_flash_read_block(&dataStart.u8bit.byteLow, EF_HEADER);
  ext_flash_read_block(&dataStart.u8bit.byteMidLow, EF_HEADER);
  ext_flash_read_block(&dataStart.u8bit.byteMidHigh, EF_HEADER);
  dataEnd.u8bit.byteHigh = 0;
  ext_flash_read_block(&dataEnd.u8bit.byteLow, EF_HEADER);
  ext_flash_read_block(&dataEnd.u8bit.byteMidLow, EF_HEADER);
  ext_flash_read_block(&dataEnd.u8bit.byteMidHigh, EF_HEADER);
  ext_flash_read_block_stop();

  //Find free space
  if((header1 == 0xFA) && (header2 == 0xFA))
  {
    uint8_t uiRead = 0;
    int countFF = 0;
    //End of last complete dive
    actualPointerSample = dataEnd.u32bit ;
    actualAddress = actualPointerSample;
    //Check if there are samples of dives with less than half a minute
    while(true)
    {
      ext_flash_read_block_start();
      ext_flash_read_block(&uiRead, EF_SAMPLE);
      if(uiRead == 0xFF)
        countFF++;
      else
        countFF = 0;



      if(countFF == 10)
      {
        actualAddress -= 10;
        break;
      }

      //New page: clear
			if(ext_flash_erase_if_on_page_start())
        break;
    }
    // Set new start address
    actualPointerSample = actualAddress;
    settings->logFlashNextSampleStartAddress = actualPointerSample;
  }
  else
  {
    settings->logFlashNextSampleStartAddress = SAMPLESTART;
  }
}


#endif

static void ext_flash_disable_protection(void)
{
/*	
	extFlashStatusBit8_Type status;

	status.uw = 0;

	wait_chip_not_busy();
	write_spi(0x50,RELEASE); 			// EWSR
	write_spi(0x01,HOLDCS); 			// WRSR
	write_spi(status.uw,RELEASE);	// new status
*/
}


void ext_flash_disable_protection_for_logbook(void)
{
	/*
	extFlashStatusBit8_Type status;

	status.uw = 0;
	status.ub.BlockProtect0 = 1;
	status.ub.BlockProtect1 = 0;
	status.ub.BlockProtect2 = 1;
	status.ub.BlockProtect3 = 0; // not set in OSTC3. Why?

	wait_chip_not_busy();
	write_spi(0x50,RELEASE); 			// EWSR
	write_spi(0x01,HOLDCS); 			// WRSR
	write_spi(status.uw,RELEASE);	// new status
*/	
}


void ext_flash_enable_protection(void)
{
/*	
	extFlashStatusBit8_Type status;

	status.uw = 0;
	status.ub.BlockProtect0 = 1;
	status.ub.BlockProtect1 = 1;
	status.ub.BlockProtect2 = 1;
	status.ub.BlockProtect3 = 1; // not set in OSTC3. Why?

	wait_chip_not_busy();
	write_spi(0x50,RELEASE); 			// EWSR
	write_spi(0x01,HOLDCS); 			// WRSR
	write_spi(status.uw,RELEASE); // new status
*/
}


/*void ext_flash_erase_chip(void)
{
	wait_chip_not_busy();
	write_spi(0x06,RELEASE);
	write_spi(0x60,RELEASE);
	wait_chip_not_busy();
}*/

void ext_flash_erase_firmware(void)
{
	uint32_t size, blocks_64k;

	actualAddress = FWSTART;
	size = 1 + FWSTOP - FWSTART;
	blocks_64k = size / 0x10000;
	ef_erase_64K(blocks_64k);
}

void ext_flash_erase_firmware2(void)
{
	uint32_t size, blocks_64k;

	actualAddress = FWSTART2;
	size = 1 + FWSTOP2 - FWSTART2;
	blocks_64k = size / 0x10000;
	ef_erase_64K(blocks_64k);
}



void ext_flash_erase_logbook(void)
{
	uint32_t size, blocks_64k;

	ext_flash_disable_protection_for_logbook();

	actualAddress = SAMPLESTART;
	size = 1 + SAMPLESTOP - SAMPLESTART;
	blocks_64k = size / 0x10000;
	ef_erase_64K(blocks_64k);

	actualAddress = HEADERSTART;
	size = 1 + HEADERSTOP - HEADERSTART;
	blocks_64k = size / 0x10000;
	ef_erase_64K(blocks_64k);

	ext_flash_enable_protection();
}


static void ext_flash_erase4kB(void)
{
	wait_chip_not_busy();
	write_spi(0x06,RELEASE);/* WREN */
	write_spi(0x20,HOLDCS);/* sector erase cmd */
	write_address(RELEASE);
}

/* be careful - might not work with entire family and other products
 * see page 14 of LOGBOOK_V3_S25FS-S_00-271247.pdf
 */
static void ext_flash_erase32kB(void)
{
	uint32_t actualAddress_backup;
	
	actualAddress_backup = actualAddress;
	actualAddress = 0;
	wait_chip_not_busy();
	write_spi(0x06,RELEASE);/* WREN */
	write_spi(0xD8,HOLDCS);/* sector erase cmd */
	write_address(RELEASE);
	actualAddress = actualAddress_backup;
}


static void ext_flash_erase64kB(void)
{
	wait_chip_not_busy();
	write_spi(0x06,RELEASE);/* WREN */
	write_spi(0xD8,HOLDCS);/* sector erase cmd */
	write_address(RELEASE);
}


void ext_flash_read_block_start(void)
{
	wait_chip_not_busy();
	write_spi(0x03,HOLDCS);		/* WREN */
	write_address(HOLDCS);
}

/* 4KB, 32KB, 64 KB, not the upper 16 MB with 4 Byte address at the moment */
static uint8_t ext_flash_erase_if_on_page_start(void)
{
	if(actualAddress < 0x00008000)
	{
		/* 4K Byte is 0x1000 */
		if((actualAddress & 0xFFF) == 0)
		{
			ext_flash_erase4kB();
			return 1;
		}
	}
	else
	if(actualAddress < 0x00010000)
	{
		/* 32K Byte is only one page */
		if(actualAddress == 0x00010000)
		{
			ext_flash_erase32kB();
			return 1;
		}
	}
	else
	{
		/* 64K Byte is 0x10000 */
		if((actualAddress & 0xFFFF) == 0)
		{
			if(preparedPageAddress == actualAddress)	/* has page already been prepared before? (at the moment for samples only) */
			{
				preparedPageAddress = 0;

			}
			else
			{
				ext_flash_erase64kB();
			}
			return 1;
		}
	}

	return 0;
}


static void ext_flash_read_block(uint8_t *getByte, uint8_t type)
{
	*getByte = read_spi(HOLDCS);/* read data */
	ext_flash_incf_address(type);
}


static void ext_flash_read_block_multi(void *getByte, uint32_t size, uint8_t type)
{
	uint8_t  *data;
	data = getByte;

	for(uint32_t i=0;i<size;i++)
	{
		data[i] = read_spi(HOLDCS);/* read data */
		ext_flash_incf_address(type);
	}
}


static void ext_flash_read_block_stop(void)
{
	chip_unselect();
}


/* Private functions ---------------------------------------------------------*/

static void ef_write_block(uint8_t * sendByte, uint32_t length, uint8_t type, uint8_t do_not_erase)
{
	uint32_t remaining_page_size, remaining_length, remaining_space_to_ring_end;
	uint32_t i=0;

	if(!length)
		return;

	uint32_t ringStart, ringStop;

	switch(type)
	{
		case EF_HEADER:
			actualAddress = actualPointerHeader;
			ringStart = HEADERSTART;
			ringStop = HEADERSTOP;
			break;
		case EF_SAMPLE:
			actualAddress = actualPointerSample;
			ringStart = SAMPLESTART;
			ringStop = SAMPLESTOP;
			break;
		case EF_DEVICEDATA:
			actualAddress = actualPointerDevicedata;
			ringStart = DDSTART;
			ringStop = DDSTOP;
			break;
		case EF_VPMDATA:
			actualAddress = actualPointerVPM;
			ringStart = VPMSTART;
			ringStop = VPMSTOP;
			break;
		case EF_SETTINGS:
			actualAddress = actualPointerSettings;
			ringStart = SETTINGSSTART;
			ringStop = SETTINGSSTOP;
			break;
		case EF_FIRMWARE:
			actualAddress = actualPointerFirmware;
			ringStart = FWSTART;
			ringStop = FWSTOP;
			break;
		case EF_FIRMWARE2:
			actualAddress = actualPointerFirmware2;
			ringStart = FWSTART2;
			ringStop = FWSTOP2;
			break;
		default:
			ringStart = FLASHSTART;
			ringStop = FLASHSTOP;
			break;
	}
	/* safety */
	if(actualAddress < ringStart)
		actualAddress = ringStart;

	if(do_not_erase == 0)
		ext_flash_erase_if_on_page_start();
	
	while( i<length)
	{
		ef_hw_rough_delay_us(5);
		wait_chip_not_busy();
		write_spi(0x06,RELEASE);		/* WREN */
		write_spi(0x02,HOLDCS);			/* write cmd */
		write_address(HOLDCS);
		
		remaining_length = length - i;
		remaining_page_size = 0xFF - (uint8_t)(actualAddress & 0xFF) +1;
		remaining_space_to_ring_end = ringStop - actualAddress;
		
		if(remaining_length >= 256)
		{
			remaining_length = 255;	/* up to 256 bytes may be written in one burst. Last byte is written with release */
		}
		else
		{
			remaining_length--;		/* last byte needed for release */
		}
		if(remaining_length >= (remaining_page_size) ) /* use 256 byte page and calculate number of bytes left */
		{
			remaining_length = remaining_page_size - 1;
		}
		if( (remaining_space_to_ring_end >= 256)) 
		{
			for(int j=0; j<remaining_length; j++)
			{
				write_spi(sendByte[i],HOLDCS);/* write data */
				actualAddress++;
				i++;
			}
		}
		/* byte with RELEASE */
		write_spi(sendByte[i],RELEASE);/* write data */
		actualAddress++;
		i++;

		if(actualAddress > ringStop)
			actualAddress = ringStart;

		if(do_not_erase == 0)
			ext_flash_erase_if_on_page_start();
	}
	switch(type)
	{
		case EF_HEADER:
			actualPointerHeader = actualAddress;
			break;
		case EF_SAMPLE:
			actualPointerSample = actualAddress;
			break;
		case EF_DEVICEDATA:
			actualPointerDevicedata = actualAddress;
			break;
		case EF_VPMDATA:
			actualPointerVPM = actualAddress;
			break;
		case EF_SETTINGS:
			actualPointerSettings = actualAddress;
			break;
		case EF_FIRMWARE:
			actualPointerFirmware = actualAddress;
			break;
		case EF_FIRMWARE2:
			actualPointerFirmware2 = actualAddress;
			break;
		default:
			break;
	}
}


static _Bool ext_flash_test_remaining_space_of_page_empty(uint32_t pointer, uint16_t length)
{
	if((pointer & 0xFFF) == 0)
		return 1;

	uint32_t backup = actualAddress;
	uint8_t data;
	uint32_t size_to_page_end;

	size_to_page_end = 0x1000 - (pointer & 0xFFF);
	if(length > size_to_page_end)
		length = size_to_page_end;

	actualAddress = pointer;
	ext_flash_read_block_start();

	for(uint16_t i = 0; i<length; i++)
	{
		ext_flash_read_block(&data, 255); // 255 = ENTIRE FLASH
		if(data != 0xFF)
		{
			ext_flash_read_block_stop();
			actualAddress = backup;
			return 0;
		}
	}
	ext_flash_read_block_stop();
	actualAddress = backup;
	return 1;
}


static void ext_flash_set_to_begin_of_next_page(uint32_t *pointer, uint8_t type)
{
	uint32_t ringStart, ringStop;

	switch(type)
	{
		case EF_HEADER:
			ringStart = HEADERSTART;
			ringStop = HEADERSTOP;
			break;
		case EF_SAMPLE:
			ringStart = SAMPLESTART;
			ringStop = SAMPLESTOP;
			break;
		case EF_DEVICEDATA:
			ringStart = DDSTART;
			ringStop = DDSTOP;
			break;
		case EF_VPMDATA:
			ringStart = VPMSTART;
			ringStop = VPMSTOP;
			break;
		case EF_SETTINGS:
			ringStart = SETTINGSSTART;
			ringStop = SETTINGSSTOP;
			break;
		default:
			ringStart = FLASHSTART;
			ringStop = FLASHSTOP;
			break;
	}

	*pointer = (*pointer & 0xFFF) + 0x1000;

	if((*pointer < ringStart) || (*pointer >= ringStop))
		*pointer = ringStart;
}


static void ef_erase_64K(uint32_t blocks)
{
	for(uint32_t i = 0; i < blocks; i++)
	{
		wait_chip_not_busy();
		write_spi(0x06,RELEASE);/* WREN */
		write_spi(0xD8,HOLDCS);/* 64k erase cmd */
		write_address(RELEASE);
		actualAddress += 0x10000;
		HAL_Delay(25);
	}
}


static void chip_unselect(void)
{
		HAL_GPIO_WritePin(EXTFLASH_CSB_GPIO_PORT,EXTFLASH_CSB_PIN,GPIO_PIN_SET); // chip select
}

static void chip_select(void)
{
	HAL_GPIO_WritePin(EXTFLASH_CSB_GPIO_PORT,EXTFLASH_CSB_PIN,GPIO_PIN_RESET); // chip select
}

static void error_led_on(void)
{
		HAL_GPIO_WritePin(OSCILLOSCOPE_GPIO_PORT,OSCILLOSCOPE_PIN,GPIO_PIN_SET);
}

static void error_led_off(void)
{
		HAL_GPIO_WritePin(OSCILLOSCOPE_GPIO_PORT,OSCILLOSCOPE_PIN,GPIO_PIN_RESET);
}


static uint8_t read_spi(uint8_t unselect_CS_afterwards)
{
	uint8_t byte;

	chip_select();

	if(HAL_SPI_Receive(&hspiDisplay, &byte, 1, 10000) != HAL_OK)
		Error_Handler_extflash();

	while (HAL_SPI_GetState(&hspiDisplay) != HAL_SPI_STATE_READY)
  {
  }
	if(unselect_CS_afterwards)
		chip_unselect();

	return byte;
}


static void write_spi(uint8_t data, uint8_t unselect_CS_afterwards)
{
	chip_select();

	if(HAL_SPI_Transmit(&hspiDisplay, &data, 1, 10000) != HAL_OK)
		Error_Handler_extflash();

	while (HAL_SPI_GetState(&hspiDisplay) != HAL_SPI_STATE_READY)
  {
  }
	if(unselect_CS_afterwards)
		chip_unselect();
}


static void write_address(uint8_t unselect_CS_afterwards)
{
	uint8_t hi, med ,lo;

	hi = (actualAddress >> 16) & 0xFF;
	med = (actualAddress >> 8) & 0xFF;
	lo = actualAddress & 0xFF;

	write_spi(hi, HOLDCS);
	write_spi(med, HOLDCS);
	write_spi(lo, unselect_CS_afterwards);
}


static void wait_chip_not_busy(void)
{
	uint8_t status;

	chip_unselect();

	write_spi(0x05,HOLDCS);		/* RDSR */
	status = read_spi(HOLDCS);/* read status */
	while(status & 0x01)
	{
		HAL_Delay(1);
		status = read_spi(HOLDCS);/* read status */
	}
	chip_unselect();
}


static void ext_flash_incf_address(uint8_t type)
{
	uint32_t ringStart, ringStop;
	
	actualAddress += 1;
	
	switch(type)
	{
		case EF_HEADER:
			ringStart = HEADERSTART;
			ringStop = HEADERSTOP;
			break;
		case EF_SAMPLE:
			ringStart = SAMPLESTART;
			ringStop = SAMPLESTOP;
			break;
		case EF_DEVICEDATA:
			ringStart = DDSTART;
			ringStop = DDSTOP;
			break;
		case EF_VPMDATA:
			ringStart = VPMSTART;
			ringStop = VPMSTOP;
			break;
		case EF_SETTINGS:
			ringStart = SETTINGSSTART;
			ringStop = SETTINGSSTOP;
			break;
		case EF_FIRMWARE:
			ringStart = FWSTART;
			ringStop = FWSTOP;
			break;
		case EF_FIRMWARE2:
			ringStart = FWSTART2;
			ringStop = FWSTOP2;
			break;
		default:
			ringStart = FLASHSTART;
			ringStop = FLASHSTOP;
			break;
	}
	
	if((actualAddress < ringStart) || (actualAddress > ringStop))
		actualAddress = ringStart;
}


static void ext_flash_decf_address_ring(uint8_t type)
{
	uint32_t ringStart, ringStop;
	
	switch(type)
	{
		case EF_HEADER:
			ringStart = HEADERSTART;
			ringStop = HEADERSTOP;
			break;
		case EF_SAMPLE:
			ringStart = SAMPLESTART;
			ringStop = SAMPLESTOP;
			break;
		case EF_DEVICEDATA:
			ringStart = DDSTART;
			ringStop = DDSTOP;
			break;
		case EF_VPMDATA:
			ringStart = VPMSTART;
			ringStop = VPMSTOP;
			break;
		case EF_SETTINGS:
			ringStart = SETTINGSSTART;
			ringStop = SETTINGSSTOP;
			break;
		case EF_FIRMWARE:
			ringStart = FWSTART;
			ringStop = FWSTOP;
			break;
		case EF_FIRMWARE2:
			ringStart = FWSTART2;
			ringStop = FWSTOP2;
			break;
		default:
			ringStart = FLASHSTART;
			ringStop = FLASHSTOP;
			break;
	}
	
	if((actualAddress <= ringStart) || (actualAddress > ringStop))
		actualAddress = ringStop;
	else
		actualAddress -= 1;
}


static void ef_hw_rough_delay_us(uint32_t delayUs)
{
	if(!delayUs)
		return;
	delayUs*= 12;
	while(delayUs--);
	return;
}

static void Error_Handler_extflash(void)
{
  while(1)
  {
  }
}

void ext_flash_CloseSector(void)
{
	uint32_t actualAddressBackup = actualAddress;
	int i=0;

	if(closeSectorAddress != 0)
	{
	/* write some dummy bytes to the sector which is currently used for storing samples. This is done to "hide" problem if function is calles again */
		actualAddress = closeSectorAddress;

		wait_chip_not_busy();
		write_spi(0x06,RELEASE);		/* WREN */
		write_spi(0x02,HOLDCS);			/* write cmd */
		write_address(HOLDCS);
		for(i = 0; i<8; i++)
		{
			write_spi(0xA5,HOLDCS);/* write data */
			actualAddress++;
		}
		/* byte with RELEASE */
		write_spi(0xA5,RELEASE);/* write data */
		actualAddress = actualAddressBackup;
		closeSectorAddress = 0;
	}
}

uint32_t ext_flash_AnalyseSampleBuffer(char *pstrResult)
{
	uint8_t sectorState[16];	/* samples are stored in 16 sector / 64k each */
	uint32_t curAddress = SAMPLESTART;
	uint8_t curSector = 0;
	uint8_t samplebuffer[10];
	uint32_t actualAddressBackup = actualAddress;
	uint8_t emptyCellCnt = 0;
	uint32_t i = 0;
	uint8_t startedSectors = 0;
	uint8_t	lastSectorInuse = 0;

/* check if a sector is used till its end */
	for(curSector = 0; curSector < 16; curSector++)
	{
		sectorState[curSector] = 0;
		emptyCellCnt = 0;
		curAddress = SAMPLESTART + (curSector * 0x10000);	/* set address to begin of sector and check if it is used */
		actualAddress = curAddress;
		ext_flash_read_block_start();
		for(uint32_t i=0;i<10;i++)
		{
			samplebuffer[i] = read_spi(HOLDCS);/* read data */
			if(samplebuffer[i] == 0xFF)
			{
				emptyCellCnt++;
			}
		}
		ext_flash_read_block_stop();
		if(emptyCellCnt == 10)
		{
			sectorState[curSector] = SECTOR_NOTUSED;
		}
		emptyCellCnt = 0;
		curAddress = SAMPLESTART + (curSector * 0x10000) + 0xFFF5;	/* set address to end of sector and check if it is used */
		actualAddress = curAddress;
		ext_flash_read_block_start();
		for(i=0;i<10;i++)
		{
			samplebuffer[i] = read_spi(HOLDCS);/* read data */
			if(samplebuffer[i] == 0xFF)
			{
				emptyCellCnt++;
			}
		}
		ext_flash_read_block_stop();
		if(emptyCellCnt == 10)
		{
			sectorState[curSector] |= SECTOR_INUSE;		/* will become SECTOR_EMPTY if start is NOTUSED */
		}
	}

	for(i=0;i<16;i++)
	{
		if( sectorState[i] == SECTOR_INUSE)
		{
			startedSectors++;
			lastSectorInuse = i;
		}
		*(pstrResult+i) = sectorState[i] + 48;
	}

	if(startedSectors > 1)	/* more than one sector is in used => ring buffer corrupted */
	{
		if(startedSectors == 2)			/* only fix issue if only two sectors are in used. Otherwise fixing will cause more worries than help */
		{
		/* the logic behind healing of the problem is that the larger address is the oldest one => restore the largest address */
			curAddress = SAMPLESTART + (lastSectorInuse * 0x10000);
			emptyCellCnt = 0;
			actualAddress = curAddress;
			ext_flash_read_block_start();
			while((emptyCellCnt < 10) && (actualAddress < curAddress + 0x10000))
			{
				samplebuffer[0] = read_spi(HOLDCS);/* read data */
				if(samplebuffer[0] == 0xFF)
				{
					emptyCellCnt++;
				}
				else
				{
					emptyCellCnt = 0;
				}
				actualAddress++;
			}
			ext_flash_read_block_stop();
			actualAddress -= 10;	/* step 10 bytes back to the start of free bytes */
			actualPointerSample = actualAddress;

			closeSectorAddress = settingsGetPointer()->logFlashNextSampleStartAddress & 0xFFFF0000;
			closeSectorAddress += 0xFFF5; /* to be used once next time a dive is logged. Needed because NextSampleID is derived at every startup */
			settingsGetPointer()->logFlashNextSampleStartAddress = actualPointerSample;	/* store new position to be used for next dive */
		}
	}
	actualAddress = actualAddressBackup;
	*(pstrResult+i) = 0;
	return startedSectors;
}

/*
uint8_t ext_flash_erase_firmware_if_not_empty(void)
{
	const uint8_t TESTSIZE_FW = 4;
	
	uint8_t data[TESTSIZE_FW];
	uint8_t notEmpty = 0;
	
	actualAddress = FWSTART;
	ext_flash_read_block_start();
	for(int i = 0; i < TESTSIZE_FW; i++)
	{
		ext_flash_read_block(&data[i], EF_FIRMWARE);
		if(data[i] != 0xFF)
			notEmpty = 1;
	}
	ext_flash_read_block_stop();
	
	if(notEmpty)
	{
		ext_flash_erase_firmware();
		return 1;
	}
	else
		return 0;
}

uint8_t ext_flash_erase_firmware2_if_not_empty(void)
{
	const uint8_t TESTSIZE_FW = 4;
	
	uint8_t data[TESTSIZE_FW];
	uint8_t notEmpty = 0;
	
	actualAddress = FWSTART2;
	ext_flash_read_block_start();
	for(int i = 0; i < TESTSIZE_FW; i++)
	{
		ext_flash_read_block(&data[i], EF_FIRMWARE2);
		if(data[i] != 0xFF)
			notEmpty = 1;
	}
	ext_flash_read_block_stop();
	
	if(notEmpty)
	{
		ext_flash_erase_firmware2();
		return 1;
	}
	else
		return 0;
}*/