view Discovery/Src/externLogbookFlash.c @ 180:9ecc2e60418d Cleanup_Compass_Wireless

Removed not used compass code Code was prepared for usage of LSM303DLHCTR but at the end the OSTC4 was never equipped with this chip
author ideenmodellierer
date Wed, 13 Mar 2019 21:49:25 +0100
parents 5f11787b4f42
children 2bb1db22b5f5
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 ---------------------------------------------------------*/
uint32_t	actualAddress = 0;
uint32_t	entryPoint = 0;

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

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

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

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

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

void ext_flash_read_block(uint8_t *getByte, uint8_t type);
void ext_flash_read_block_multi(void *getByte, uint32_t size, uint8_t type);
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);

void ext_flash_overwrite_sample_without_erase(uint8_t *pSample, uint16_t length);

void ext_flash_disable_protection(void);

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


/* 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(void)
{
	uint8_t *pData;
	const uint16_t length = sizeof(SDevice);
	uint8_t length_lo, length_hi;
	uint8_t dataLength[2] = { 0 };

	ext_flash_disable_protection();

	pData = (uint8_t *)stateDeviceGetPointer();

	actualPointerDevicedata = DDSTART;

	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);
}


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

	actualAddress = DDSTART;

	ext_flash_read_block_start();
	ext_flash_read_block(&length_lo, EF_DEVICEDATA);
	ext_flash_read_block(&length_hi, EF_DEVICEDATA);
	
	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_stop();
	
	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)
{
	ef_write_block(pSample,length, EF_SAMPLE, 0);

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

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

//  ===============================================================================
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;	

	// wo ist der �lteste Tauchgang (der, der startNumber bekommt)
	// 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 Bl�cke (32 Tauchg�nge) weil neuer Flash Chip.
/// Dieser Bereich muss auf einmal gel�scht werden.
/// Vorher waren es 4K Bl�cke.
/// @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 Tauchg�nge pro 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))
    {
      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))
      {
        actualPointerSample = dataStart.u32bit;
        actualAddress = actualPointerSample;
        logbook_recover_brokenlog(id);
        SSettings *settings = settingsGetPointer();
        settings->logFlashNextSampleStartAddress = actualPointerSample;
      }
    }
  }
  ext_flash_find_start();
}


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

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();
}


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 carefull - might not work with entire family and other products
 * see page 14 of LOGBOOK_V3_S25FS-S_00-271247.pdf
 */
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;
}


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 */
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)
		{
			ext_flash_erase64kB();
			return 1;
		}
	}		
	return 0;
}


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


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);
	}
}


void ext_flash_read_block_stop(void)
{
	chip_unselect();
}


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

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;
	
	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();
	
	for(uint32_t i=0;i<length;i++)
	{
		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 = actualAddress & 0xFF;
		remaining_space_to_ring_end = ringStop - actualAddress;
		
		if((remaining_page_size == 0) && (remaining_length >= 256) && (remaining_space_to_ring_end >= 256))
		{
			for(int j=0; j<255; j++)
			{
				write_spi(sendByte[i],HOLDCS);/* write data */
				actualAddress++;
				i++;
			}
		}
		/* byte with RELEASE */
		write_spi(sendByte[i],RELEASE);/* write data */
		actualAddress++;
		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;
	}
}


_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;
}


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);
	}
}


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

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

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

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


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;
}


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();
}


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();
}


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;
}


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)
  {
  }
}

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;
}