//头文件
#include "Flash.h"

#define Page_Erase   ((unsigned char(*)(unsigned int))(PGM))
#define Program_Byte ((unsigned char(*)(unsigned int, unsigned char))(PGM))
 
//擦除或写入函数的操作代码指令对应的机器码
volatile unsigned char PGM[57]  = { 
0x87,0xc6,0x18,0x25,0xa5,0x10,0x27,0x08,0xc6,0x18,
0x25,0xaa,0x10,0xc7,0x18,0x25,0x9e,0xe6,0x01,0xf7,
0xa6,0x40,0xc7,0x18,0x26,0x45,0x18,0x25,0xf6,0xaa,
0x80,0xf7,0x9d,0x9d,0x9d,0x9d,0xc6,0x18,0x25,0xa5,
0x30,0x27,0x04,0xa6,0x01,0x8a,0x81,0xc6,0x18,0x25,
0xa5,0x40,0x27,0xf9,0x4f,0x8a,0x81  
};

//-------------------------------------------------------------------------*
//函数名: FlashInit                                                        *
//功  能: 初始化Flash时钟分频、禁止保护                                    *
//参  数: busclk: 总线频率(单位Khz)                                        *
//返  回: 无                                                               *
//说  明: (1)请注意入口参数busclk(总线频率)的单位是Khz                     *
//        (2)JM60主频取值:20MHz,10MHz,8MHz,4MHz,2MHz,1MHz,200kHz,15kHz     *
//-------------------------------------------------------------------------*
void FlashInit(uint32 busclk)
{
    uint8 div6;
    
    if(busclk>10*1000)
    {
       FCDIV |= (1<<6);           //置PRDIV8=1 
       div6 = 12;                 //最高主频为20MHz
    }
    else if(busclk>=1000)
    {
        FCDIV &= ~(1<<6);         //清PRDIV8=0 
        div6 = (busclk/1000)*5-1;
    }
    else
    {
        FCDIV &= ~(1<<6);//清PRDIV8=0 
        div6 = 0;
    }  
    FCDIV |= div6;
    //FPROT = 0xFF; //禁止保护 
}
   
//-------------------------------------------------------------------------*
//函数名: Flash_PageErase                                                  *
//功  能: Flash页擦除                                                      *
//参  数: pageNo:页号(从5开始)                                             *
//返  回: 0 - 成功,1 - 失败                                                *
//说  明: 无                                                               *
//-------------------------------------------------------------------------*
uint8 Flash_PageErase(uint8 pageNo)
{
    uint16 pageAddr;
    uint8  rtnValue;
    
    pageAddr= pageNo*512;       //根据页号计算页内地址
    PGM[21] = 0x40;             //设置页擦除指令
    rtnValue = Page_Erase(pageAddr); //擦除addr所在的页           
    return rtnValue;
}

//-------------------------------------------------------------------------*
//函数名: Flash_ByteWrite                                                  *
//功  能: Flash字节写入                                                    *
//参  数:       buff:源数据缓冲区地址                                      *
//            pageNo:要写入的Flash的页号                                   *
//        startSAddr:要写入的Flash页内地址                                 *
//               len:写入字节数                                            *
//返  回: 0 - 成功,1 - 失败                                                *
//说  明: (1) Flash 写入时，未将页内其他位置数据进行保护，原因 RAM一共2KB，*
//            若进行了保护，则需要开辟512B的缓冲区 很容易出现内存溢出      *                                                         *
//-------------------------------------------------------------------------*
uint8 Flash_ByteWrite(uint8 *buff,uint16 pageNo,uint16 startAddr,uint16 len)
{
    uint16 i;
    uint16 flashaddr;
    uint8  rtnValue;
    
    //计算写入地址
    flashaddr = pageNo*512 + startAddr; 
    
    //逐个字节写入数据
    for(i=0; i<len; i++) 
    {
        PGM[21]  = 0x20;                             //字节编程指令 
        rtnValue = Program_Byte(flashaddr+i,buff[i]);//执行写入操作
        if(rtnValue)
            return 1; 
    }
    return 0;
} 

//-------------------------------------------------------------------------*
//函数名: Flash_Secure                                                     *	
//功  能: 加密Flash,同时设置加密密码(后门机制)                             *
//返  回: 0=成功                                                           * 
//        1=擦除失败                                                       *
//        2=写入失败                                                       *
//        3=已设置块保护,无法修改                                          *
//说  明: (1)只有芯片复位后,设置才生效                                     *
//内部调用: Flash_PageErase,Flash_ByteWrite                                *
//-------------------------------------------------------------------------*
uint8 Flash_Secure(uint8 key[]) 
{
   	uint8 vecdata[512];
	uint8 * addr;
	uint16 i;
	uint8 state=0;
	
	//1.检查是否已设置块保护
    if((FPROT & 0x01)==0) //已设置块保护
    {
        state = 3;                 //已设置块保护,无法修改
        goto Flash_Secure_Exit;
    }

    //2.复制中断向量表(Flash第127页)至RAM
	addr = (uint8 *)0xFE00;
	for(i=0;i<512;i++)
	{
	    vecdata[i] = addr[i];
	}
	//3. 设置密钥
	for(i=0;i<8;i++)
	{
	    vecdata[432+i] = key[i];  //后门密钥
	}
	//4. 设置保护块
	FPROT = 0xFF;
	//vecdata[445] = 0xFF; 
	vecdata[445] = 0b11111111;//设置块保护寄存器
    //               ||||||||__/FPDIS=1:禁止块保护  
    //               |||||||   \FPDIS=0:保护FPS7~FPS1之后的地址 
    //               |||||||___ FPS1
    //               ||||||____ FPS2
    //               |||||_____ FPS3        
    //               ||||______ FPS4
    //               |||_______ FPS5
    //               ||________ FPS6  
    //               |_________ FPS7                 
    //5. 设置寄存器NVOPT/FOPT
	vecdata[447] = 0b11000001;
    //               ||    ||__SEC00  (SEC01:SEC00=1:0是为非保密状态，
    //               ||    |___SEC01   其他为保密状态)
    //               ||
    //               ||________/FNORED=1:禁止向量重定向;
    //               |         \FNORED=0:允许向量重定向;
    //               |
    //               |_________/KEYEN=1:允许后门密钥;
    //                         \KEYEN=0:禁止后门密钥
    //6. 擦除
    state=Flash_PageErase(127);//擦除最后一页
    if(state!=0)
    {
        state = 1;              //擦除失败
        goto Flash_Secure_Exit;
    }	
	//7. 写入,将配置好的参数写入Flash
	state=Flash_ByteWrite(vecdata,127,0,512);
	if(state!=0)
    {
        state = 2;              //写入失败
        goto Flash_Secure_Exit;
    }
 
Flash_Secure_Exit://函数出口
	return state;
}

//-------------------------------------------------------------------------*
//函数名: Flash_SetBlockProtect                                            *	
//功  能: 设置块保护                                                       *
//参  数: pageNo:页号(4~127)                                               *
//        protectFlag:0=设置块保护;1=禁止块保护                            *                  
//返  回: 0=成功                                                           * 
//        1=擦除失败                                                       *
//        2=写入失败                                                       *
//说  明: (1)只有芯片在加密状态下,才可以进行块保护                         *
//        (2)若上一次操作已经使得芯片块保护,则此次只能将FPROT的地址改小,   *
//           而且不能修改NVPROT的内容                                      *   
//        (3)若FPROT的最后1位为1(即块保护未生效),则可以任意修改块保护的地址*
//内部调用: Page_Erase,Flash_Page_Write                                    *
//-------------------------------------------------------------------------*
uint8 Flash_SetBlockProtect(uint8 pageNo,uint8 protectFlag)
{ 
    uint8 vecdata[512];
	uint8 * addr;
	uint16 i;
	uint8 state=0;

    //1. 复制中断向量表(Flash第127页)至RAM中
	addr = (uint8 *)0xFE00;
	for(i=0;i<512;i++)
	{
	    vecdata[i] = addr[i];
	}
	//2. 若芯片未加密,则需要先加密以后再设置块保护   
	if((FOPT & 0x03)==0x02)//芯片未加密
	{
    	vecdata[447] = 0b11000001;//设置寄存器NVOPT/FOPT
        //               ||    ||__SEC00  (SEC01:SEC00=1:0是为非保密状态，
        //               ||    |___SEC01   其他为保密状态)
        //               ||
        //               ||________/FNORED=1:禁止向量重定向;
        //               |         \FNORED=0:允许向量重定向;
        //               |
        //               |_________/KEYEN=1:允许后门密钥;
        //                         \KEYEN=0:禁止后门密钥
	}
	//3. 设置保护块
    //   根据页号计算详细地址
    //   例如:要保护第9页及其以后的页
    //   第9页的地址范围:0x1200-0x13FF,0x1200的前一个地址为0x11FF,
    //   所以FPS7~FPS1是除去0x11FF低9位后剩余的值,即0001 000	
	vecdata[445] = (pageNo<<1)-1;//根据页号计算详细地址
    if(0 == protectFlag)
    {
        vecdata[445] &= 0xFE;    
    }
    //FOPT = vecdata[445];//该设置无效, FOPT需要将MCU重新复位后,方可改变
    
    //4. 若芯片已经设置块保护，则只能临时修改保护区域
	if((FPROT & 0x01)==0)
    {
        FPROT = vecdata[445];           //设置块保护寄存器
        goto Flash_SetBlockProtect_Exit;
    }
    //5. 擦除
    state=Flash_PageErase(127);
    if(state!=0)
    {
        state = 1;                      //擦除失败
        goto Flash_SetBlockProtect_Exit;
    }	
	//6. 写入
	state=Flash_ByteWrite(vecdata,127,0,512);
	if(state!=0)
    {
        state = 2;                      //写入失败
        goto Flash_SetBlockProtect_Exit;
    }
 
Flash_SetBlockProtect_Exit:             //函数出口
    return state;
}

//-------------------------------------------------------------------------*
//函数名: Flash_KEY_Match                                                  *	
//功  能: 后门密钥匹配                                                     *
//参  数: key[]:8个字节密钥                                                *		
//返  回: 0=成功                                                           * 
//        1=未使能后门机制                                                 *
//        2=密钥错误                                                       *
//内部调用:                                                                *
//-------------------------------------------------------------------------*
uint8 Flash_KEY_Match(uint8 key[])
{
	uint8 * addr;
	uint16 i;
	uint8  state=0;
    
    //1. 判断是否使能后门机制
    //寄存器NVOPT/FOPT的KEYEN位为0时，后门机制无效，
    //但是如果KEYEN位为1时，用户按照如下的方法取消MCU的保密状态
    if((FOPT & (1<<7)) == 0)
    {
        state = 1;                 //未使能后门机制
        goto Flash_KEY_Match_Exit;
    }
    //2. 置FCNFG的KEYACC位为1
    BSET(KEYACC,FCNFG);  
    //3. 修改密钥
    //向NVBACKKEY~NVBACKKEY+7的位置(即$FFB0~$FFB7)依次写入8个字节
    addr = (uint8 *)0xFFB0;//密钥首地址
	for(i=0;i<8;i++)
	{
	    addr[i] = key[i];    //写入密钥
	}
    //4. 将FCNFG的KEYACC位清0    
    BCLR(KEYACC,FCNFG);   
    //延时
    for(i=0;i<20;i++);
    //5.检查密钥是否正确
    //  若用户写入的8字节密钥和NVBACKKEY~NVBACKKEY+7本身的8字节密钥一致,
    //  则MCU会将SEC01:SEC00改写成1:0,从而取消MCU的保密性。 
    if((FOPT & 0x03)!=0x02)
        state = 2;          //密钥错误
        
Flash_KEY_Match_Exit:       //函数出口
	return state;
}

//-------------------------------------------------------------------------*
//函数名: Flash_Key_Delete                                                 *	
//功  能: 加密Flash,同时设置加密密码(后门机制)                             *
//返  回: 0=成功                                                           * 
//        1=擦除失败                                                       *
//        2=写入失败                                                       *
//        3=已设置块保护,无法删除                                          *
//说  明: (1)只有芯片复位后,设置才生效                                     *
//内部调用: Flash_PageErase,Flash_ByteWrite                                *
//-------------------------------------------------------------------------*
uint8 Flash_Key_Delete() 
{
   	uint8 vecdata[512];
	uint8 * addr;
	uint16 i;
	uint8 state=0;

    //1.检查是否已设置块保护
    if((FPROT & 0x01)==0) //已设置块保护
    {
        state = 3;                  //已设置块保护,无法删除密钥
        goto Flash_Key_Delete_Exit;
    }
    
    //2.复制中断向量表(Flash第127页)至RAM
	addr = (uint8 *)0xFE00;
	for(i=0;i<512;i++)
	{
	    vecdata[i] = addr[i];
	}
	//3. 设置密钥
	for(i=0;i<8;i++)
	{
	    vecdata[432+i] = 0xFF;  //后门密钥
	}               
    //4. 设置寄存器NVOPT/FOPT
	vecdata[447] = 0b01000001;
    //               ||    ||__SEC00  (SEC01:SEC00=1:0是为非保密状态，
    //               ||    |___SEC01   其他为保密状态)
    //               ||
    //               ||________/FNORED=1:禁止向量重定向;
    //               |         \FNORED=0:允许向量重定向;
    //               |
    //               |_________/KEYEN=1:允许后门密钥;
    //                         \KEYEN=0:禁止后门密钥
    //5. 擦除
    state=Flash_PageErase(127);//擦除最后一页
    if(state!=0)
    {
        state = 1;              //擦除失败
        goto Flash_Key_Delete_Exit;
    }	
	//6. 写入,将配置好的参数写入Flash
	state=Flash_ByteWrite(vecdata,127,0,512);
	if(state!=0)
    {
        state = 2;              //写入失败
        goto Flash_Key_Delete_Exit;
    }
 
Flash_Key_Delete_Exit://函数出口
	return state;
}

//-------------------------------------------------------------------------*
//函数名: Flash_UnSecure                                                   *	
//功  能: 加密Flash,同时设置加密密码(后门机制)                             *
//返  回: 0=成功                                                           * 
//        1=擦除失败                                                       *
//        2=写入失败                                                       *
//        3=已设置块保护,无法修改                                          *
//说  明: (1)只有芯片复位后,设置才生效                                     *
//内部调用: Flash_PageErase,Flash_ByteWrite                                *
//-------------------------------------------------------------------------*
uint8 Flash_UnSecure() 
{
   	uint8 vecdata[512];
	uint8 * addr;
	uint16 i;
	uint8 state=0;
	
	//1.检查是否已设置块保护
    if((FPROT & 0x01)==0) //已设置块保护
    {
        state = 3;                  //已设置块保护,无法修改
        goto Flash_UnSecure_Exit;
    }

    //2.复制中断向量表(Flash第127页)至RAM
	addr = (uint8 *)0xFE00;
	for(i=0;i<512;i++)
	{
	    vecdata[i] = addr[i];
	}
	//3. 设置密钥
	for(i=0;i<8;i++)
	{
	    //vecdata[432+i] = key[i];  //后门密钥
	}                
    //4. 设置寄存器NVOPT/FOPT
	vecdata[447] = 0b01000010;
    //               ||    ||__SEC00  (SEC01:SEC00=1:0是为非保密状态，
    //               ||    |___SEC01   其他为保密状态)
    //               ||
    //               ||________/FNORED=1:禁止向量重定向;
    //               |         \FNORED=0:允许向量重定向;
    //               |
    //               |_________/KEYEN=1:允许后门密钥;
    //                         \KEYEN=0:禁止后门密钥
    //5. 擦除
    state=Flash_PageErase(127);//擦除最后一页
    if(state!=0)
    {
        state = 1;              //擦除失败
        goto Flash_UnSecure_Exit;
    }	
	//6. 写入,将配置好的参数写入Flash
	state=Flash_ByteWrite(vecdata,127,0,512);
	if(state!=0)
    {
        state = 2;              //写入失败
        goto Flash_UnSecure_Exit;
    }
 
Flash_UnSecure_Exit://函数出口
	return state;
}



                         