对于小白的我来说,当时写这个真的是一头雾水。擦除的跨页处理就很恶心。
你想删除一段,这时就还得考虑把这一段额,说不清楚,画个图吧
当时因为这块,抓耳挠腮了很久,为什么网上的驱动都没有考虑这些呢?
gd25q64.h
#ifndef __GD25Q64_H
#define __GD25Q64_H
#include "gd32f30x.h"
//********************************引脚初始化*************************************************//
#define gd25q64RCU_SPIx RCU_SPI0
#define gd25q64RCU_GPIOx RCU_GPIOA
#define gd25q64GPIOx GPIOA
#define gd25q64GPIO_PIN_x GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7
#define gd25q64SPI_CS_PIN_x GPIO_PIN_4
//********************************引脚初始化*************************************************//
//**********************************************************************************************//
//********************************数组的长度*************************************************//
#define gd25q64DataLen 4096
//********************************数组的长度*************************************************//
//**********************************************************************************************//
//********************************GD25存储芯片的指令************************************//
#define GD25Q64_WriteEnable 0x06
#define GD25Q64_WriteDisable 0x04
#define GD25Q64_ReadStatusRegister1 0x05 //S7-S0
#define GD25Q64_ReadStatusRegister2 0x35 //S15-S8
#define GD25Q64_ReadStatusRegister3 0x15 //S23-S16
#define GD25Q64_WriteStatusRegister1 0x01
#define GD25Q64_WriteStatusRegister2 0x31
#define GD25Q64_WriteStatusRegister3 0x11
#define GD25Q64_ReadData 0x03
#define GD25Q64_FastRead 0x0B
#define GD25Q64_PageProgram 0x02
#define GD25Q64_QuadPageProgram 0x32
#define GD25Q64_SectorErase 0x20
#define GD25Q64_BlockErase32 0x52
#define GD25Q64_BlockErase64 0xD8
#define GD25Q64_ChipErase 0xC7
#define GD25Q64_ReadIdentification 0x9F
#define GD25Q64_DeepPowerDown 0xB9
#define GD25Q64_ManufacturerDeviceID 0x90
#define GD25Q64_ReleaseFromDeepPowerDown 0xAB
#define GD25Q64_ReadIdentification 0x9F
//********************************GD25存储芯片的指令************************************//
//**********************************************************************************************//
//********************************GD25存储芯片的典型速度******************************//
//Program/Erase Speed
#define PageProgramTime 0.6 //ms
#define SectorEraseTime 50
#define BlockEraseTime 200
#define ChipEraseTime 25000
//********************************GD25存储芯片的典型速度******************************//
//**********************************************************************************************//
//********************************GD25存储芯片的片区大小******************************//
//Flexible Architecture 1M=1024K 1K=1024Byte 1Byte=8bit
#define GD25Q64_Size (8*1024*1024)
#define GD25Q64_PageSize 256 //Byte
#define GD25Q64_SectorSize (4*1024) //Byte
#define GD25Q64_BlockSize (32*1024) //Byte
//********************************GD25存储芯片的片区大小******************************//
//我自己当时写远程升级时的一些位置定义
#define GD25Q64_ADDRESS_DEVINFO ((uint32_t)0x00)
#define GD25Q64_ADDRESS_TASK ((uint32_t)0x1000)
#define GD25Q64_ADDRESS_GLOBALTASK ((uint32_t)0x41000)
#define GD25Q64_ADDRESS_BOOTLOADER_INFO ((uint32_t)0x81000)
#define GD25Q64_ADDRESS_BOOTLOADER_APP1 ((uint32_t)0x82000)
#define GD25Q64_ADDRESS_BOOTLOADER_BACKUP ((uint32_t)0xA2000)
//********************************片选引脚的宏定义******************************//
#define GD25Q64_CS_LOW() {gpio_bit_write(GPIOA, GPIO_PIN_4,RESET);}
#define GD25Q64_CS_HIGH() {gpio_bit_write(GPIOA, GPIO_PIN_4,SET);}
//********************************片选引脚的宏定义******************************//
//********************************推荐常用函数******************************//
//SPI初始化
void vgd25q64SPIInit(void);
//刷新写入,先删除再写入(自动处理删除,和写入跨页)
void vgd25q64FlushWrite(uint8_t *sourcedata,uint32_t address,uint32_t length);
//读数据
void vgd25q64ReadData(uint8_t *sourcedata, uint32_t address, uint32_t length);
//删除(删除指定地址到指定长度,自动处理跨页)
void vgd25q64FlushDel(uint32_t address,uint32_t length);
//********************************推荐常用函数******************************//
//********************************不常用函数******************************//
//擦除片
void vgd25q64EraseSector(uint32_t eraseAddr);
//擦除范围片
void vgd25q64EraseAppointSector(uint32_t addstart,uint32_t addend);
//擦除整块芯片
void Flash_EraseChip(void);
//写数据
void vgd25q64WriteData(uint8_t *sourcedata, uint32_t address, uint32_t length);
//获取位置信息(用于变地址存储信息,减少芯片的擦写次数)
uint32_t vgd25q64GetDataPos(uint32_t Startaddress,uint32_t length);
//读状态寄存器(用于配置外挂FLASH的一些功能)
void vgd25q64WriteStatusRegisterx(uint8_t sourcedata, uint8_t GD25Q64_ReadStatusRegisterx);
//********************************不常用函数******************************//
#endif
gd25q64.c
#include "stdio.h"
#include "stdlib.h"
#include "systick.h"
#include "../User/BSP/gd25q64/gd25q64.h"
//#define DEBUG
uint8_t gd25q64Data[gd25q64DataLen];
extern bool vdatastorageCheckArraySame(uint8_t *sourcearray, uint8_t *targetarray, uint32_t len);
/***********************************************************
*@fuction :vgd25q64SPIInit
*@brief :
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
void vgd25q64SPIInit(void)
{
//打开时钟
rcu_periph_clock_enable(RCU_AF);
rcu_periph_clock_enable(gd25q64RCU_GPIOx);
rcu_periph_clock_enable(gd25q64RCU_SPIx);
//初始化引脚
gpio_init(gd25q64GPIOx, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, gd25q64GPIO_PIN_x);
gpio_init(gd25q64GPIOx, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, gd25q64SPI_CS_PIN_x);
gpio_bit_write(gd25q64GPIOx, gd25q64SPI_CS_PIN_x, SET);
//初始化SPI
spi_parameter_struct spi_init_struct;
spi_i2s_deinit(SPI0);
spi_struct_para_init(&spi_init_struct);
spi_init_struct.device_mode = SPI_MASTER;
spi_init_struct.prescale = SPI_PSC_32;
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
spi_init_struct.endian = SPI_ENDIAN_MSB;
spi_init_struct.nss = SPI_NSS_SOFT;
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
spi_init(SPI0, &spi_init_struct);
spi_enable(SPI0);
}
/***********************************************************
*@fuction :spi_master_send_recv_byte
*@brief :
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
uint8_t spi_master_send_recv_byte(uint8_t sourcedata)
{
static uint8_t ByteRecv;
while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE))
;
spi_i2s_data_transmit(SPI0, sourcedata);
while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE))
;
ByteRecv = spi_i2s_data_receive(SPI0);
return ByteRecv;
}
/***********************************************************
*@fuction :spi_master_recv_some_bytes
*@brief :
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
void spi_master_recv_some_bytes( uint8_t *sourcedata, uint16_t length)
{
uint8_t *temp_data = sourcedata;
while (length--)
{
*temp_data = spi_master_send_recv_byte(0xFF); //发送 0xff 为从设备提供时钟
temp_data++;
}
}
/***********************************************************
*@fuction :vgd25q64WriteEnable
*@brief :
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
void vgd25q64WriteEnable(void)
{
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_WriteEnable);
GD25Q64_CS_HIGH();
}
/***********************************************************
*@fuction :ucgd25q64ReadStatusRegister
*@brief :
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
uint8_t ucgd25q64ReadStatusRegister(uint8_t GD25Q64_ReadStatusRegisterx)
{
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_ReadStatusRegisterx);
uint8_t StatusRegisterx = spi_master_send_recv_byte(0xFF);
GD25Q64_CS_HIGH();
return StatusRegisterx;
}
/***********************************************************
*@fuction :vgd25q64WriteStatusRegisterx
*@brief : 写状态寄存器
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
void vgd25q64WriteStatusRegisterx(uint8_t sourcedata, uint8_t GD25Q64_ReadStatusRegisterx)
{
vgd25q64WriteEnable();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_ReadStatusRegisterx);
spi_master_send_recv_byte(sourcedata);
GD25Q64_CS_HIGH();
}
/***********************************************************
*@fuction :vgd25q64EraseSector
*@brief : 擦除sector
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
void vgd25q64EraseSector(uint32_t eraseAddr)
{
vgd25q64WriteEnable();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_SectorErase);
spi_master_send_recv_byte((uint8_t)(eraseAddr >> 16));
spi_master_send_recv_byte((uint8_t)(eraseAddr >> 8));
spi_master_send_recv_byte((uint8_t)(eraseAddr));
GD25Q64_CS_HIGH();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
}
/***********************************************************
*@fuction :vgd25q64EraseAppointSector
*@brief : 擦除指定范围的sector
*@param :起始地址到结束地址
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
void vgd25q64EraseAppointSector(uint32_t addstart, uint32_t addend)
{
uint32_t startsector = addstart / GD25Q64_SectorSize * GD25Q64_SectorSize;
uint32_t endsector = addend / GD25Q64_SectorSize * GD25Q64_SectorSize;
uint32_t erasesectornum = (endsector / GD25Q64_SectorSize) - (startsector / GD25Q64_SectorSize);
for(uint8_t i = 0; i <= erasesectornum; i++)
{
vgd25q64EraseSector(startsector + i * GD25Q64_SectorSize);
}
}
/***********************************************************
*@fuction :Flash_EraseChip
*@brief :整片擦除
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
void Flash_EraseChip(void)
{
vgd25q64WriteEnable();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_ChipErase);
GD25Q64_CS_HIGH();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
}
/***********************************************************
*@fuction :uigd25q64ReadIdentification
*@brief :
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
unsigned int uigd25q64ReadIdentification(void)
{
uint32_t Identification = 0;
uint8_t recv_buff[3] = {0};
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_ReadIdentification); //9fh
uint8_t recvLen = sizeof(recv_buff);
for(uint8_t i = 0 ; i < recvLen; i++)
{
recv_buff[i] = spi_master_send_recv_byte(0xFF); //发送 0xff 为从设备提供时钟
}
Identification = (recv_buff[0] << 16) | (recv_buff[1] << 8) | (recv_buff[2]);
GD25Q64_CS_HIGH();
return Identification;
}
/***********************************************************
*@fuction :usgd25q64ReadID
*@brief :
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
unsigned short usgd25q64ReadID(void)
{
uint16_t ID = 0;
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_ManufacturerDeviceID);
spi_master_send_recv_byte(0x00);
spi_master_send_recv_byte(0x00);
spi_master_send_recv_byte(0x00);
ID |= spi_master_send_recv_byte(0xFF) << 8;
ID |= spi_master_send_recv_byte(0xFF);
GD25Q64_CS_HIGH();
return ID;
}
/***********************************************************
*@fuction :vgd25q64WritePage
*@brief : 页编程,需要考虑 先删除,后写入,写入时要考虑跨页机制
*@param :--
*@return :void
*@author :flechazo
*@date :2023-06-13
***********************************************************/
void vgd25q64WritePage(uint8_t *sourcedata, uint32_t address, uint32_t length)
{
vgd25q64WriteEnable();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_PageProgram);
spi_master_send_recv_byte(address >> 16);
spi_master_send_recv_byte(address >> 8);
spi_master_send_recv_byte(address >> 0);
for(uint32_t i = 0; i < length; i++)
{
spi_master_send_recv_byte(sourcedata[i]);
}
GD25Q64_CS_HIGH();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
}
/***********************************************************
*@fuction :vgd25q64WriteData
*@brief :
*@param :--
*@return :void
*@author :flechazo更多例程请访问(flechazo.mba)
*@date :2023-06-22
***********************************************************/
void vgd25q64WriteData(uint8_t *sourcedata, uint32_t address, uint32_t length)
{
uint32_t currentGroup = 0;
uint32_t DataAdd = address;
uint32_t currentLen = length, residuelength = length;
uint32_t shiftingadd = address % GD25Q64_PageSize;
uint32_t firstwritelength = 0;
uint8_t status1 = ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1);
uint8_t status2 = ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister2);
uint8_t status3 = ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister3);
//我用得模式为CMP = 0 ,status1 = 0,status2=0;所对应的为没有数据保护
//vgd25q64WriteStatusRegisterx(0x04, GD25Q64_ReadStatusRegister1);//GD25Q64_WriteStatusRegister1这一块似乎存在问题
//status1 = ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1);
//write first page
if(shiftingadd && ((shiftingadd + length) > GD25Q64_PageSize))
{
firstwritelength = GD25Q64_PageSize - shiftingadd;
vgd25q64WriteEnable();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_PageProgram);
spi_master_send_recv_byte(DataAdd >> 16);
spi_master_send_recv_byte(DataAdd >> 8);
spi_master_send_recv_byte(DataAdd >> 0);
for(uint32_t i = 0; i < firstwritelength; i++)
{
spi_master_send_recv_byte(sourcedata[i]);
}
GD25Q64_CS_HIGH();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
DataAdd += firstwritelength;
residuelength -= firstwritelength;
currentLen = residuelength;
}
do
{
//vgd25q64ReadData(gd25q64Data,PosGD25Q64_TaskInfo_num_I,GD25Q64_SectorSize);
if(currentLen > (GD25Q64_PageSize))
{
currentLen = GD25Q64_PageSize;
}
vgd25q64WriteEnable();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_PageProgram);
spi_master_send_recv_byte(DataAdd >> 16);
spi_master_send_recv_byte(DataAdd >> 8);
spi_master_send_recv_byte(DataAdd >> 0);
for(uint32_t i = 0; i < currentLen; i++)
{
uint32_t shifting = firstwritelength + i + (currentGroup * GD25Q64_PageSize);
spi_master_send_recv_byte(sourcedata[shifting]);
}
GD25Q64_CS_HIGH();
while(((ucgd25q64ReadStatusRegister(GD25Q64_ReadStatusRegister1)) & 0x01) == 0x01)
;
if(residuelength >= currentLen)
{
residuelength -= currentLen;
}
DataAdd += currentLen;
currentLen = residuelength;
}
while((currentGroup++) < (length / (GD25Q64_PageSize)));
}
/*!
\brief vgd25q64ReadData
\param[in] *sourcedata
\param[in] address
\param[in] length
\param[out] none
\retval none
*/
void vgd25q64ReadData(uint8_t *sourcedata, uint32_t address, uint32_t length)
{
uint8_t *temp_data = sourcedata;
GD25Q64_CS_LOW();
spi_master_send_recv_byte(GD25Q64_ReadData);
spi_master_send_recv_byte((uint8_t)(address >> 16));
spi_master_send_recv_byte((uint8_t)(address >> 8));
spi_master_send_recv_byte((uint8_t)(address >> 0));
while (length--)
{
*temp_data = spi_master_send_recv_byte(0xFF); //发送 0xff 为从设备提供时钟
temp_data++;
}
GD25Q64_CS_HIGH();
}
/*!
\brief vgd25q64FlushWrite erase -> write
\param[in] *data
\param[in] add
\param[in] length
\param[out] none
\retval none
*/
void vgd25q64FlushWrite(uint8_t *sourcedata, uint32_t address, uint32_t length)
{
bool falg = false;
uint32_t startSectorAddress = ((address / GD25Q64_SectorSize) * GD25Q64_SectorSize);
uint32_t endSectorAddress = (((address + length) / GD25Q64_SectorSize) * GD25Q64_SectorSize);
uint32_t startAddress = address % GD25Q64_SectorSize;
vgd25q64ReadData(gd25q64Data, startSectorAddress, GD25Q64_SectorSize);
for(uint32_t i = 0; i < length; i++)
{
if(gd25q64Data[startAddress + i] != 0xFF)
{
falg = true;
}
}
//no need to erase
if(!falg)
{
vgd25q64WriteData(sourcedata, address, length);
}
else
{
//need to erase
if(startSectorAddress == endSectorAddress)
{
for(uint32_t i = 0; i < length; i++)
{
gd25q64Data[i + startAddress] = sourcedata[i];
}
vgd25q64EraseSector(address);
vgd25q64WriteData(gd25q64Data, startSectorAddress, GD25Q64_SectorSize);
}
else
{
for(uint32_t i = 0; i < (GD25Q64_SectorSize - startAddress); i++)
{
gd25q64Data[i + startAddress] = sourcedata[i];
}
vgd25q64EraseSector(startSectorAddress);
vgd25q64WriteData(gd25q64Data, startSectorAddress, GD25Q64_SectorSize);
uint32_t enddatalen = length - (GD25Q64_SectorSize - startAddress);
vgd25q64ReadData(gd25q64Data, endSectorAddress, GD25Q64_SectorSize);
for(uint32_t i = 0; i < enddatalen; i++)
{
gd25q64Data[i] = sourcedata[(GD25Q64_SectorSize - startAddress) + i];
}
vgd25q64EraseSector(endSectorAddress);
vgd25q64WriteData(gd25q64Data, endSectorAddress, GD25Q64_SectorSize);
}
}
}
/***********************************************************
*@fuction :vgd25q64FlushDel
*@brief :
*@param :--
*@return :void
*@author :--
*@date :2023-04-23
***********************************************************/
void vgd25q64FlushDel(uint32_t address, uint32_t length)
{
uint32_t startSectorAddress = ((address / GD25Q64_SectorSize) * GD25Q64_SectorSize);
uint32_t endSectorAddress = (((address + length) / GD25Q64_SectorSize) * GD25Q64_SectorSize);
uint32_t SectorinterstartAddress = (address - startSectorAddress);
if(startSectorAddress == endSectorAddress)
{
vgd25q64ReadData(gd25q64Data, startSectorAddress, GD25Q64_SectorSize);
vgd25q64EraseSector(startSectorAddress);
//write Previous
vgd25q64WriteData(gd25q64Data, startSectorAddress, SectorinterstartAddress);
//write subsequent
vgd25q64WriteData(gd25q64Data + SectorinterstartAddress + length, startSectorAddress + SectorinterstartAddress + length, GD25Q64_SectorSize - SectorinterstartAddress - length);
}
else
{
//startSectorAddress
vgd25q64ReadData(gd25q64Data, startSectorAddress, GD25Q64_SectorSize);
vgd25q64EraseSector(startSectorAddress);
vgd25q64WriteData(gd25q64Data, startSectorAddress, SectorinterstartAddress);
//endSectorAddress
vgd25q64ReadData(gd25q64Data, endSectorAddress, GD25Q64_SectorSize);
vgd25q64EraseSector(endSectorAddress);
uint32_t endSectorWriteAdd = (length + SectorinterstartAddress) % GD25Q64_SectorSize ;
vgd25q64WriteData(&gd25q64Data[endSectorWriteAdd], endSectorAddress + endSectorWriteAdd, GD25Q64_SectorSize - endSectorWriteAdd);
//middleSector
uint32_t middleSector = address + GD25Q64_SectorSize;
for( ; middleSector < endSectorAddress; middleSector += GD25Q64_SectorSize)
{
vgd25q64EraseSector(middleSector);
}
}
}
/***********************************************************
*@fuction :vgd25q64GetDataPos
*@brief :input address then output this data storage position
*@param :uint32_t Startaddress uint32_t length
*@return :uint32_t position
*@author :flechazo
*@date :2023-04-23
***********************************************************/
uint32_t vgd25q64GetDataPos(uint32_t Startaddress, uint32_t length)
{
uint32_t position = 0;
uint32_t tempPos;
uint32_t count = 0;
bool direction = false;
vgd25q64ReadData(gd25q64Data, Startaddress, length);
tempPos = length;
uint8_t checkdata[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
do
{
tempPos /= 2;
if(direction)
{
position -= tempPos;
}
else
{
position += tempPos;
}
count++;
if(vdatastorageCheckArraySame(checkdata, &gd25q64Data[position], 8))
{
//data is front
direction = true;
}
else
{
//data is back
direction = false;
}
}
while(tempPos > 0);
position++;
while(gd25q64Data[position] == 0xFF)
{
position--;
}
position++;
return (position + Startaddress);
}