
#include "stflash.h"
#include "trigger.h"

//--------------------------------------------------------------------

#define	STM32_FLASH_TIMOUT		-1	// 超时
#define STM32_FLASH_OK			0	// 操作完成
#define STM32_FLASH_BUSY		1	// 忙
#define STM32_FLASH_ERROR		2	// 错误

//--------------------------------------------------------------------

#define	MAX_WAIT_TIME	2000	// 最大等待时间2秒

//--------------------------------------------------------------------

// 等待操作完成
// waitms，等待时间(ms)
int FlashWaitNoBusy(int waitms)
{
	int timout = waitms;
	u32 timer;
	u32 tick = GetMsSoftTimer();

	do
	{
		if (READ_BIT(FLASH->SR, FLASH_SR_BSY) != (FLASH_SR_BSY))
		{
			break;
		}
		timer = GetMsSoftTimer();

		if ((timer - tick) > timout)
		{
			return STM32_FLASH_TIMOUT;
		}
	}while (1);

	if ((FLASH->SR & (FLASH_SR_EOP)) != 0)
	{
		CLEAR_BIT(FLASH->SR, FLASH_SR_EOP);
	}
#if (0)
	timer = GetMsSoftTimer();
	printf("FlashWaitNoBusy time=%d\r\n", timer - tick);
#endif

	return STM32_FLASH_OK;
}

// 读取状态
int FLASHGetStatus(void)
{
	u32 temp = FLASH->SR;

	if ((temp & FLASH_SR_BSY) != 0)
	{
		return STM32_FLASH_BUSY;	// 忙
	}
#if (__CORTEX_M == 0 || __CORTEX_M == 3)
	if ((temp & (	FLASH_SR_WRPRTERR |	// 写保护错误
					FLASH_SR_PGERR  |	// 编程错误
					0 )		) != 0)
#elif (__CORTEX_M == 4)
	else if ((temp & (	FLASH_SR_WRPERR |	// 写保护错误
						FLASH_SR_PGAERR |	// 编程对齐错误
						FLASH_SR_PGPERR |	// 并行位数错误
						FLASH_SR_PGSERR |	// 编程顺序错误
				//		FLASH_SR_RDERR  |
						0	)	) != 0)
#endif
	{
		return STM32_FLASH_ERROR;	// 操作错误
	}

	return STM32_FLASH_OK;			// 操作完成
}

// 得到扇区编号
int GetFlashSectorIdx(u32 faddr)
{
#if (__CORTEX_M == 0 || __CORTEX_M == 3)
	if (faddr >= STM32_FLASH_BASE)
	{
		faddr -= STM32_FLASH_BASE;
		if (faddr <= STM32_FLASH_SIZE)
		{
			faddr /= STM32_FLASH_SEC_SIZE;
			if (faddr < STM32_FLASH_SEC_NUM)
			{
				return faddr;
			}
		}
	}

#elif (__CORTEX_M == 4)

#define FLS_RGCK(ad)  \
	if (faddr < STM32_FLASH_ADDR_SECTOR_##ad)\
	{\
		return (ad-1);\
	}\
	else \
//------
	FLS_RGCK(0)
	FLS_RGCK(1)
	FLS_RGCK(2)
	FLS_RGCK(3)
	FLS_RGCK(4)
	FLS_RGCK(5)
	FLS_RGCK(6)
	FLS_RGCK(7)

	if (faddr < STM32_FLASH_ADDR_SECTOR_7 + STM32_FLASH_SIZE_SECTOR_7)
	{
		return 7;
	}
#endif
	return -1;
}

u32 GetFlashSectorBegAddr(u32 sidx)
{
	u32 addr = STM32_FLASH_BASE;

#if (__CORTEX_M == 0 || __CORTEX_M == 3)

	if (sidx < STM32_FLASH_SEC_NUM)
	{
		addr += sidx * STM32_FLASH_SEC_SIZE;
	}
	else
	{
		addr += STM32_FLASH_SIZE;
	}

#elif (__CORTEX_M == 4)
	switch (sidx)
	{
	case 0:
		addr = STM32_FLASH_ADDR_SECTOR_0;
		break;
	case 1:
		addr = STM32_FLASH_ADDR_SECTOR_1;
		break;
	case 2:
		addr = STM32_FLASH_ADDR_SECTOR_2;
		break;
	case 3:
		addr = STM32_FLASH_ADDR_SECTOR_3;
		break;
	case 4:
		addr = STM32_FLASH_ADDR_SECTOR_4;
		break;
	case 5:
		addr = STM32_FLASH_ADDR_SECTOR_5;
		break;
	case 6:
		addr = STM32_FLASH_ADDR_SECTOR_6;
		break;
	case 7:
		addr = STM32_FLASH_ADDR_SECTOR_7;
		break;
	default:
		break;
	}
#endif
	return addr;
}

int GetFlashSectorSize(u32 sidx)
{
	int size = 0;

#if (__CORTEX_M == 0 || __CORTEX_M == 3)

	size = STM32_FLASH_SEC_SIZE;
#elif (__CORTEX_M == 4)
	switch (sidx)
	{
	case 0:
		size = STM32_FLASH_SIZE_SECTOR_0;
		break;
	case 1:
		size = STM32_FLASH_SIZE_SECTOR_1;
		break;
	case 2:
		size = STM32_FLASH_SIZE_SECTOR_2;
		break;
	case 3:
		size = STM32_FLASH_SIZE_SECTOR_3;
		break;
	case 4:
		size = STM32_FLASH_SIZE_SECTOR_4;
		break;
	case 5:
		size = STM32_FLASH_SIZE_SECTOR_5;
		break;
	case 6:
		size = STM32_FLASH_SIZE_SECTOR_6;
		break;
	case 7:
		size = STM32_FLASH_SIZE_SECTOR_7;
		break;
	default:
		break;
	}
#endif
	return size;
}

int GetSecRemainSize(u32 faddr)
{
	int sidx = GetFlashSectorIdx(faddr);
	int slen = GetFlashSectorSize(sidx);
	u32 baddr = GetFlashSectorBegAddr(sidx);
	if (sidx < 0 || slen <= 0)
	{
		return -1;
	}

	return (baddr + slen - faddr);
}

//--------------------------------------------------------------------

// 解锁
void FLASHUnlock()
{
//	printf("FLASHUnlock beg\r\n");

	if (READ_BIT(FLASH->CR, FLASH_CR_LOCK) != RESET)
	{
		WRITE_REG(FLASH->KEYR, FLASH_KEY1);
		WRITE_REG(FLASH->KEYR, FLASH_KEY2);
	}

	while((READ_BIT(FLASH->CR, FLASH_CR_LOCK) != RESET))
	{}
//	printf("FLASHUnlock ok\r\n");
}

// 锁定
void FLASHLock()
{
//	printf("FLASHLock\r\n");

	SET_BIT(FLASH->CR, FLASH_CR_LOCK);			// 锁定
}

// STM32 flash 写入代码
// 读取指定地址的半字
// faddr:读地址(此地址必须为2的倍数)
// data: 数据
void FLASHProgramHalfWord(uint32_t faddr, uint16_t data)
{
#if (__CORTEX_M == 4)
	CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);
	SET_BIT(FLASH->CR, FLASH_PSIZE_HALF_WORD);			// 按半字写入
#endif
	SET_BIT(FLASH->CR, FLASH_CR_PG);					// 编程

	*(__IO uint16_t*)faddr = data;
}

// STM32 flash 读出代码
// 读取指定地址的半字(16位数据)
// faddr:读地址(此地址必须为2的倍数)
// 返回值:对应数据
u16 FlashRDHalfWord(u32 faddr)
{
	return *( __IO uint16_t*)faddr;
}

// 擦除扇区
void FLASHEraseSector(uint32_t secIdx)
{
#if (__CORTEX_M == 0 || __CORTEX_M == 3)
	SET_BIT(FLASH->CR, FLASH_CR_PER);
    WRITE_REG(FLASH->AR, secIdx * STM32_FLASH_SEC_SIZE + STM32_FLASH_BASE);
    SET_BIT(FLASH->CR, FLASH_CR_STRT);
#elif (__CORTEX_M == 4)
	CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);
	SET_BIT(FLASH->CR, FLASH_PSIZE_HALF_WORD);		// 按半字写入
	SET_BIT(FLASH->CR, FLASH_CR_SER);				// 设置擦除方式

	CLEAR_BIT(FLASH->CR, FLASH_CR_SNB);				// 清除地址
	FLASH->CR |= (secIdx << FLASH_CR_SNB_Pos);		// 设置地址
	SET_BIT(FLASH->CR, FLASH_CR_STRT);				// 开始擦除
#endif
}

//-----------------------------------------------

// 不检查的写入
// wrAddr: 起始地址
// pBuffer: 数据指针
// hwNumToWrite: 半字(16位)数
int STMFlashWrNoCheck(u32 wrAddr, u16 *pBuffer, u16 hwNumToWrite)
{
	int rslt;
	u16 i;
	for (i = 0; i < hwNumToWrite; i++)
	{
		FLASHProgramHalfWord(wrAddr, *pBuffer);
		pBuffer++;
	    wrAddr += 2;	// 地址增加2

		// 等待写入完成
		rslt = FlashWaitNoBusy(MAX_WAIT_TIME);
		if (rslt != STM32_FLASH_OK)
		{
			rslt = FLASHGetStatus();
			break;
		}
	    FLASH->CR &= (~FLASH_CR_PG);
	}
	return rslt;
}


//-----------------------------------------------
// 擦除扇区过程

int FLASHEraseSectorProc(uint32_t secIdx)
{
	int rslt;

	if (secIdx < 0 || secIdx >= STM32_FLASH_SEC_NUM)
	{
		return STM32_FLASH_ERROR;
	}

	rslt = FlashWaitNoBusy(MAX_WAIT_TIME);
	if (rslt != STM32_FLASH_OK)
	{
		rslt = FLASHGetStatus();
		return rslt;
	}

	FLASHEraseSector(secIdx);

	rslt = FlashWaitNoBusy(MAX_WAIT_TIME);
	if (rslt != STM32_FLASH_OK)
	{
		rslt = FLASHGetStatus();
		return rslt;
	}
#if (__CORTEX_M == 0 || __CORTEX_M == 3)
    CLEAR_BIT(FLASH->CR, FLASH_CR_PER);
#elif (__CORTEX_M == 4)
    CLEAR_BIT(FLASH->CR, (FLASH_CR_SER | FLASH_CR_SNB));
#endif

	return STM32_FLASH_OK;
}

//-----------------------------------------------


void STMFlashUnlock()
{
	FLASHUnlock();
}

void STMFlashLock()
{
	FLASHLock();
}

int STMFlashErase(u32 secIdx, int secnums)
{
	int rslt = 0;

	FLASHUnlock();

	for (int i = 0; i < secnums; i++)
	{
		rslt = FLASHEraseSectorProc(secIdx);
		if (rslt != STM32_FLASH_OK)
		{
			printf("STMFlashErase error, rslt=%d\r\n", rslt);
			rslt = -1;
			break;
		}
#if (0)
		if (STMFlashCheck(secIdx * STM32_FLASH_SEC_SIZE + STM32_FLASH_BASE, 0, STM32_FLASH_SEC_SIZE) != 0)
		{
			printf("STMFlashErase and STMFlashCheck err, \r\n");
		}
#endif
		secIdx++;
	}

	FLASHLock();		// 上锁

	return rslt;
}
//-----------------------------------------------

// 从指定地址开始读出指定长度的数据
// rdAddr: 起始地址
// pBuffer: 数据指针
// hwNumOfRead: 半字(16位)数
int STMFlashRead(u32 rdAddr, u16 * pBuffer, u16 hwNumOfRead)
{
	u16 i;
	if (pBuffer == NULL ||
		rdAddr < STM32_FLASH_BASE ||
		rdAddr + hwNumOfRead*2 > STM32_FLASH_BASE+STM32_FLASH_SIZE)
	{
		return -1;
	}

	for (i = 0; i < hwNumOfRead; i++)
	{
		*pBuffer = FlashRDHalfWord(rdAddr);		// 读取2个字节
		pBuffer++;
		rdAddr += 2;	// 地址增加2
	}
	return 0;
}

// 从指定地址开始比较指定长度的数据是否一致
// ckAddr: 起始地址
// pBuffer: 数据指针
// hwNumOfChekc: 半字(16位)数
// 返回值: 0, 全部符合
// 		   其他，不符合
int STMFlashCheck(u32 ckAddr, u16 * pBuffer, u16 hwNumOfChekc)
{
	int i;
	u16 tprd, tpdat;
	for(i = 0; i < hwNumOfChekc; i++)
	{
		tprd = FlashRDHalfWord(ckAddr);		// 读取2个字节
		if (pBuffer != NULL)
		{
			tpdat = *pBuffer;
		}
		else
		{
			tpdat = 0xffff;
		}
		if (tpdat != tprd)
		{
			printf("STMFlashCheck error, tpdat=0x%x, tprd=0x%x, i=%d\r\n", tpdat, tprd, i);
			return 1;
		}

		if (pBuffer != NULL)
		{
			pBuffer++;
		}
		ckAddr += 2;	// 地址增加2
	}
	return 0;
}

// 从指定地址开始写入指定长度的数据
// wrAddr: 起始地址(此地址必须为2的倍数)
// pBuffer: 数据指针
// hwNumToWrite: 半字(16位)数(就是要写入的16位数据的个数)
// svwhenera == 0, 擦除时不保留写入区域之外的数据
// svwhenera == 1, 擦除时保留写入区域之前的数据
// svwhenera == 2, 擦除时保留写入区域之后的数据
// svwhenera == 3, 擦除时保留写入区域之外的数据

// 使用sdram 的一段空间（最大128K）

int STMFlashWrite(u32 wrAddr, u16 * pBuffer, u16 hwNumToWrite, int svwhenera)
{
	int rslt;
	u16 * thiswrbuff;

	u32 addr, addrEnd, thiswraddr;
	u32 wrlen, thiswrlen;
	int thissidx, thisslen;
	u32 thisaddr, thislen;

#if (NO_BUFF_WHEN_WT == 0)
	u32 i;

	u16 tempbuf[STM32_FLASH_SEC_SIZE/2];

#endif

	addr = wrAddr;
	wrlen = hwNumToWrite * 2;
	addrEnd = wrAddr + wrlen;

	if (wrAddr < STM32_FLASH_BASE || (addrEnd  > (STM32_FLASH_BASE + STM32_FLASH_SIZE)))
	{
		return -1;	// 非法地址
	}

	FLASHUnlock();									// 解锁flash

	//
	while (addr < addrEnd)
	{
		thisaddr = addr;							// 起始地址

		thissidx = GetFlashSectorIdx(thisaddr);
		thisslen = GetFlashSectorSize(thissidx);
		if (thissidx < 0 || thisslen <= 0)
		{
			return -1;
		}

		thislen = GetSecRemainSize(thisaddr);		// 剩余长度
		if (thislen > wrlen)
		{
			thislen = wrlen;
		}

		rslt = FlashWaitNoBusy(MAX_WAIT_TIME);
		if (rslt != STM32_FLASH_OK)
		{
			rslt = FLASHGetStatus();
			printf("STMFlashWrite error, FlashWaitNoBusy=%d\r\n", rslt);
			return rslt;
		}

#if (NO_BUFF_WHEN_WT == 0)
		// 判断是否需要擦除
		STMFlashRead(thisaddr, tempbuf, thislen/2);	// 读出内容
		for (i = 0; i < thislen/2; i++)				// 检验数据是否需要擦除
		{
		#if (1)		// st 内部 flash 不支持写过后的flash把某些1位变成0位，外置 flash支持，
			if (tempbuf[i] != 0xffff)	// 只要不是全1, 就擦除
		#else
			if ((tempbuf[i] & pBuffer[i]) != pBuffer[i])	// 与之后，和要写入数据相同，可以不擦除
		#endif
			{
				break;	// 需要擦除
			}
		}

		if (i < thislen/2)	// 需要擦除
		{
			u32 secbegaddr;
			u8* wrbufaddr;

		//	printf("\r\nErase sector %d\r\n", thissidx);
			secbegaddr = GetFlashSectorBegAddr(thissidx);

			// 读取扇区所有数据
			if (thisaddr != secbegaddr || thislen != thisslen)
			{
				STMFlashRead(secbegaddr, tempbuf, thisslen/2);
			}

			// 擦除扇区
			if (FLASHEraseSectorProc(thissidx) != STM32_FLASH_OK)		// 擦除这个扇区
			{
				printf("\r\nErase sector %d error\r\n", thissidx);
				break;
			}

			// 复制要写入的数据到缓冲区
			wrbufaddr = (u8*)tempbuf;
			wrbufaddr += thisaddr-secbegaddr;
			memcpy(wrbufaddr, pBuffer, thislen);

			if (svwhenera == 0)				// 擦除时不保留写入区域之外的数据
			{
				thiswraddr = thisaddr;
				thiswrbuff = pBuffer;
				thiswrlen = thislen;
			}
			else if (svwhenera == 1)		// 擦除时保留写入区域之前的数据
			{
				thiswraddr = secbegaddr;
				thiswrbuff = tempbuf;
				thiswrlen = thislen + (thisaddr - secbegaddr);
			}
			else if (svwhenera == 2)		// 擦除时保留写入区域之后的数据
			{
				thiswraddr = thisaddr;
				thiswrbuff = (u16*)wrbufaddr;
				thiswrlen = thisslen - (thisaddr - secbegaddr);
			}
			else // if (svwhenera == 3)		// 擦除时保留写入区域之外的数据
			{
				thiswraddr = secbegaddr;
				thiswrbuff = tempbuf;
				thiswrlen = thisslen;	// 扇区长度
			}
		}
		else
#endif
		{
			thiswraddr = thisaddr;
			thiswrbuff = pBuffer;
			thiswrlen = thislen;
		}

		// 写入数据
		STMFlashWrNoCheck(thiswraddr, thiswrbuff, thiswrlen / 2);	// 写入数据
		pBuffer += thislen;
		addr += thislen;
		wrlen -= thislen;
	}

	FLASHLock();		// 上锁

	return 0;
}
