/*---------------------------------------------------------------------*/
/* --- STC MCU Limited ------------------------------------------------*/
/* --- STC 1T Series MCU Demo Programme -------------------------------*/
/* --- Mobile: (86)13922805190 ----------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ------------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ------------------------*/
/* --- Web: www.STCMCU.com --------------------------------------------*/
/* --- Web: www.STCMCUDATA.com  ---------------------------------------*/
/* --- QQ:  800003751 -------------------------------------------------*/
/* 如果要在程序中使用此代码,请在程序中注明使用了STC的资料及程序            */
/*---------------------------------------------------------------------*/


/*************  技术支持与购买说明    **************
产品主页：http://tw51.haohaodada.com
淘宝搜索：天问51，可购买。目前基础版99元，带彩屏标准版149元
技术支持QQ群一：1138055784
******************************************/


/*************  功能说明    **************

通过串口对W25QXX进行读写测试。

对FLASH做扇区擦除、写入、读出的操作，命令指定地址。

默认波特率:  115200,8,N,1. 可以在"串口初始化函数"里修改.

串口命令设置: (字母不区分大小写)
    E 0x001234              --> 对EEPROM进行扇区擦除操作，指定十六进制地址.
    W 0x001234 1234567890   --> 对EEPROM进行扇区写入操作，指定十六进制地址，后面为写入内容.
    R 0x001234 16           --> 对EEPROM进行扇区读出操作，指定十六进制地址. 

注意：为了通用，程序不识别地址是否有效，用户自己根据具体的型号来决定。

下载时, 选择时钟 22.1184MHz (用户可自行修改频率).

******************************************/
#include "sys.h"
#include "intrins.h"
#include "delay.h"
#include "hc595.h"
#include "led8.h"
#include "uart1.h"

#define SPIF    0x80        //SPI传输完成标志。写入1清0。
#define WCOL    0x40        //SPI写冲突标志。写入1清0。

#define     EE_BUF_LENGTH       50          //

#define     W25Q80 	            			0XEF13 	
#define     W25Q16 	            			0XEF14
#define     W25Q32 	            			0XEF15
#define     W25Q64 	            			0XEF16
#define     W25Q128	            			0XEF17

/*************  本地常量声明    **************/

/*************  本地变量声明    **************/
u8  xdata   tmp[EE_BUF_LENGTH];
u8  sst_byte;
u32 Flash_addr;

/*************  FLASH相关变量声明   **************/
sbit    P_W25QXX_CE  = P2^2;     //PIN1
sbit    P_W25QXX_SO  = P2^4;     //PIN2
sbit    P_W25QXX_SI  = P2^3;     //PIN5
sbit    P_W25QXX_SCK = P2^5;     //PIN6

bit B_FlashOK;                                //Flash状态
u16  W25QXX_ID;

extern u8  RX1_TimeOut;
extern u8  TX1_Cnt;    //发送计数
extern u8  RX1_Cnt;    //接收计数
extern u8  xdata RX1_Buffer[UART1_BUF_LENGTH];   //接收缓冲


void    delay_ms(u8 ms);
void    RX1_Check(void);

void SPI_init(void);
u16 FlashCheckID(void);
void W25QXX_Erase_Sector(u32 Dst_Addr);
void SPI_Read_Nbytes(uint32 ReadAddr,uint8* pBuffer,uint16 NumByteToRead);
u8 SPI_Read_Compare(u32 addr, u8 *buffer, u16 size);
void SPI_Write_Nbytes(uint32 WriteAddr,uint8* pBuffer,uint16 NumByteToWrite);

u8  Hex2Ascii(u8 dat)
{
    dat &= 0x0f;
    if(dat < 10)    return (dat+'0');
    return (dat-10+'A');
}

/******************** 主函数 **************************/
void main(void)
{
    P0M1 = 0;   P0M0 = 0;   //设置为准双向口
    P1M1 = 0;   P1M0 = 0;   //设置为准双向口
    P2M1 = 0;   P2M0 = 0;   //设置为准双向口
    P3M1 = 0;   P3M0 = 0;   //设置为准双向口
    P4M1 = 0;   P4M0 = 0;   //设置为准双向口
    P5M1 = 0;   P5M0 = 0;   //设置为准双向口
    P6M1 = 0;   P6M0 = 0;   //设置为准双向口
    P7M1 = 0;   P7M0 = 0;   //设置为准双向口

		led8_disable();					//LED Power OFF
    hc595_init(); 
   	hc595_disable();
	
    delay(10);
    UART1_config(1);    // 选择波特率, 2: 使用Timer2做波特率, 其它值: 使用Timer1做波特率.
    EA = 1;     //允许总中断

    PrintString1("命令设置:\r\n");
    PrintString1("E 0x001234            --> 扇区擦除  十六进制地址\r\n");
    PrintString1("W 0x001234 1234567890 --> 写入操作  十六进制地址  写入内容\r\n");
    PrintString1("R 0x001234 10         --> 读出操作  十六进制地址  读出字节数\r\n");
    PrintString1("C                     --> 如果检测不到W25QXX, 发送C强制允许操作.\r\n\r\n");

    SPI_init();
    W25QXX_ID = FlashCheckID();
    if((W25QXX_ID != W25Q16) & (W25QXX_ID != W25Q32)){  
				PrintString1("未检测到W25QXX!\r\n");
				B_FlashOK = 0;
		}
    else            
    {
				B_FlashOK = 1;
        PrintString1("检测到W25QXX!\r\n");
        PrintString1("设备ID = 0x");
				UART1_TxByte(Hex2Ascii(W25QXX_ID >> 12));
				UART1_TxByte(Hex2Ascii(W25QXX_ID >> 8));
				UART1_TxByte(Hex2Ascii(W25QXX_ID >> 4));
				UART1_TxByte(Hex2Ascii(W25QXX_ID));
        PrintString1("\r\n");
    }

    while (1)
    {
        delay(1);
                
        if(RX1_TimeOut > 0)
        {
            if(--RX1_TimeOut == 0)  //超时,则串口接收结束
            {
                if(RX1_Cnt > 0)
                {
                    RX1_Check();    //串口1处理数据
                }
                RX1_Cnt = 0;
            }
        }
    }
}

/**************** ASCII码转BIN ****************************/
u8  CheckData(u8 dat)
{
    if((dat >= '0') && (dat <= '9'))        return (dat-'0');
    if((dat >= 'A') && (dat <= 'F'))        return (dat-'A'+10);
    return 0xff;
}

/**************** 获取写入地址 ****************************/
u32 GetAddress(void)
{
    u32 address;
    u8  i,j;
    
    address = 0;
    if((RX1_Buffer[2] == '0') && (RX1_Buffer[3] == 'X'))
    {
        for(i=4; i<10; i++)
        {
            j = CheckData(RX1_Buffer[i]);
            if(j >= 0x10)   return 0x80000000;  //error
            address = (address << 4) + j;
        }
        return (address);
    }
    return  0x80000000; //error
}

/**************** 获取要读出数据的字节数 ****************************/
u8  GetDataLength(void)
{
    u8  i;
    u8  length;
    
    length = 0;
    for(i=11; i<RX1_Cnt; i++)
    {
        if(CheckData(RX1_Buffer[i]) >= 10)  break;
        length = length * 10 + CheckData(RX1_Buffer[i]);
    }
    return (length);
}


/**************** 串口1处理函数 ****************************/

void RX1_Check(void)
{
    u8  i,j;

    if((RX1_Cnt == 1) && (RX1_Buffer[0] == 'C'))    //发送C强制允许操作
    {
        B_FlashOK = 1;
        PrintString1("强制允许操作FLASH!\r\n");
    }

    if(!B_FlashOK)
    {
        PrintString1("PM25LV040不存在, 不能操作FLASH!\r\n");
        return;
    }
    
    F0 = 0;
    if((RX1_Cnt >= 10) && (RX1_Buffer[1] == ' '))   //最短命令为10个字节
    {
        for(i=0; i<10; i++)
        {
            if((RX1_Buffer[i] >= 'a') && (RX1_Buffer[i] <= 'z'))    RX1_Buffer[i] = RX1_Buffer[i] - 'a' + 'A';//小写转大写
        }
        Flash_addr = GetAddress();
        if(Flash_addr < 0x80000000)
        {
            if(RX1_Buffer[0] == 'E')    //擦除
            {
                W25QXX_Erase_Sector(Flash_addr);
                PrintString1("已擦除一个扇区数据!\r\n");
                F0 = 1;
            }

            else if((RX1_Buffer[0] == 'W') && (RX1_Cnt >= 12) && (RX1_Buffer[10] == ' '))   //写入N个字节
            {
                j = RX1_Cnt - 11;
                for(i=0; i<j; i++)  tmp[i] = 0xff;      //检测要写入的空间是否为空
                i = SPI_Read_Compare(Flash_addr,tmp,j);
                if(i > 0)
                {
                    PrintString1("要写入的地址为非空,不能写入,请先擦除!\r\n");
                }
                else
                {
                    P27 = 0;
                    SPI_Write_Nbytes(Flash_addr,&RX1_Buffer[11],j);     //写N个字节 
                    P27 = 1;
                    i = SPI_Read_Compare(Flash_addr,&RX1_Buffer[11],j); //比较写入的数据
                    if(i == 0)
                    {
                        PrintString1("已写入");
                        if(j >= 100)    {UART1_TxByte(j/100+'0');   j = j % 100;}
                        if(j >= 10)     {UART1_TxByte(j/10+'0');    j = j % 10;}
                        UART1_TxByte(j%10+'0');
                        PrintString1("字节数据!\r\n");
                    }
                    else        PrintString1("写入错误!\r\n");
                }
                F0 = 1;
            }
            else if((RX1_Buffer[0] == 'R') && (RX1_Cnt >= 12) && (RX1_Buffer[10] == ' '))   //读出N个字节
            {
                j = GetDataLength();
                if((j > 0) && (j < EE_BUF_LENGTH))
                {
                    P27 = 0;
                    SPI_Read_Nbytes(Flash_addr,tmp,j);
                    P27 = 1;
                    PrintString1("读出");
                    if(j>=100)  UART1_TxByte(j/100+'0');
                    UART1_TxByte(j%100/10+'0');
                    UART1_TxByte(j%10+'0');
                    PrintString1("个字节数据如下：\r\n");
                    for(i=0; i<j; i++)  UART1_TxByte(tmp[i]);
                    UART1_TxByte(0x0d);
                    UART1_TxByte(0x0a);
                    F0 = 1;
                }
            }
        }
    }
    if(!F0) PrintString1("命令错误!\r\n");
}

/******************* FLASH相关程序 ************************/
//指令表
#define 		W25X_WriteEnable					0x06 
#define 		W25X_WriteDisable					0x04 
#define 		W25X_ReadStatusReg				0x05 
#define 		W25X_WriteStatusReg				0x01 
#define 		W25X_ReadData							0x03 
#define 		W25X_FastReadData					0x0B 
#define 		W25X_FastReadDual					0x3B 
#define 		W25X_PageProgram					0x02 
#define 		W25X_BlockErase						0xD8 
#define 		W25X_SectorErase					0x20 
#define 		W25X_ChipErase						0xC7 
#define 		W25X_PowerDown						0xB9 
#define 		W25X_ReleasePowerDown			0xAB 
#define 		W25X_DeviceID							0xAB 
#define 		W25X_ManufactDeviceID			0x90 
#define 		W25X_JedecDeviceID				0x9F

#define SPI_CE_High()   P_W25QXX_CE  = 1     // set CE high
#define SPI_CE_Low()    P_W25QXX_CE  = 0     // clear CE low

/************************************************************************/
void SPI_init(void)
{
    SPI_CE_High();
    P_W25QXX_SCK = 0;    // set clock to low initial state
    P_W25QXX_SI = 1;
}

/************************************************************************/
void SPI_WriteByte(u8 out)
{
    u8 i;
    i = 8;
    do{
        out <<= 1;
        P_W25QXX_SI  = CY;
        P_W25QXX_SCK = 1;
        P_W25QXX_SCK = 0;
    }while(--i);
    P_W25QXX_SI = 1;
}

/************************************************************************/
u8 SPI_ReadByte(void)
{
    u8 i, in;
    i = 8;
    do{
        in <<= 1;
        if (P_W25QXX_SO) in++;
        P_W25QXX_SCK = 1;
        P_W25QXX_SCK = 0;
    }while(--i);
    return in;
}

/************************************************
检测Flash是否准备就绪
入口参数: 无
出口参数:
    0 : 没有检测到正确的Flash
    1 : Flash准备就绪
************************************************/
u16 FlashCheckID(void)
{
		u16 Temp = 0;
    SPI_CE_Low();
		SPI_WriteByte(0x90);                   //发送读取ID指令
		SPI_WriteByte(0x00); 	    
		SPI_WriteByte(0x00); 	    
		SPI_WriteByte(0x00); 	 			   
		Temp |= SPI_ReadByte()<<8;  
		Temp |= SPI_ReadByte();
    SPI_CE_High();
	  return Temp;
}

/************************************************
W25QXX读状态寄存器
入口参数: 无
出口参数: 无
************************************************/
u8 W25QXX_Read_SR()   
{  
	u8 byte=0;   
	SPI_CE_Low();                          //选中spiflash
	SPI_WriteByte(W25X_ReadStatusReg);    //发送读取状态寄存器命令
	byte=SPI_ReadByte();                  //读取一个字节 
	SPI_CE_High();                           //取消片选     
	return byte;   
}

/************************************************
W25QXX写状态寄存器
入口参数: 无
出口参数: 无
************************************************/
void W25QXX_Write_SR(uint8 sr)   
{   
	SPI_CE_Low();                          //选中spiflash  
	SPI_WriteByte(W25X_WriteStatusReg);     //发送写状态寄存器命令   
	SPI_WriteByte(sr);                      //写入一个字节
	SPI_CE_High();                           //取消片选 	      
} 

/************************************************
W25QXX等待空闲
入口参数: 无
出口参数: 无
************************************************/
void W25QXX_Wait_Busy()   
{   
	while((W25QXX_Read_SR()&0x01)==0x01);   //等待busy位清空
}

/************************************************
使能Flash写命令
入口参数: 无
出口参数: 无
************************************************/
void W25QXX_Write_Enable()   
{
		SPI_CE_Low();                           //选中spiflash   
    SPI_WriteByte(W25X_WriteEnable);         //发送写使能
		SPI_CE_High();                               //取消片选   	      
}

/************************************************
擦除整片Flash
入口参数: 无
出口参数: 无
************************************************/
/*
void W25QXX_Erase_Chip()   
{                                   
    W25QXX_Write_Enable();                  //写使能 
    W25QXX_Wait_Busy();   
  	SPI_CE_Low();                           //片选使能   
    SPI_WriteByte(W25X_ChipErase);     	//发送片擦除命令 
		SPI_CE_High();                            //取消片选     	      
		W25QXX_Wait_Busy();   				    //等待擦除完成
}  
*/

/************************************************
擦除扇区, 一个扇区4KB
入口参数: Dst_Addr:扇区地址
出口参数: 无
************************************************/
void W25QXX_Erase_Sector(u32 Dst_Addr)   
{  	  
    W25QXX_Write_Enable();                 		//写使能	 
    W25QXX_Wait_Busy();   
  	SPI_CE_Low();                           	//片选使能
    SPI_WriteByte(W25X_SectorErase);      		//发送扇区擦除指令
    SPI_WriteByte((uint8)((Dst_Addr)>>16));  		//发送24位地址 
    SPI_WriteByte((uint8)((Dst_Addr)>>8));   
    SPI_WriteByte((uint8)Dst_Addr);  
		SPI_CE_High();                            	//取消片选   	      
    W25QXX_Wait_Busy();     				   		//等待擦除完成
}


/************************************************
从Flash中读取数据
入口参数:
    addr   : 地址参数
    buffer : 缓冲从Flash中读取的数据
    size   : 数据块大小
出口参数:
    无
************************************************/
void SPI_Read_Nbytes(uint32 ReadAddr,uint8* pBuffer,uint16 NumByteToRead)   
{ 
		uint16 i;   										    
		SPI_CE_Low();                          //选中spiflash      
    SPI_WriteByte(W25X_ReadData);         //发送读取命令
    SPI_WriteByte((uint8)((ReadAddr)>>16));  //发送24位地址    
    SPI_WriteByte((uint8)((ReadAddr)>>8));   
    SPI_WriteByte((uint8)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)
		{ 
        pBuffer[i]=SPI_ReadByte();   //循环读数 
    }
		SPI_CE_High();  				    	      
} 

/************************************************************************
读出n个字节,跟指定的数据进行比较, 错误返回1,正确返回0
************************************************************************/
u8 SPI_Read_Compare(u32 ReadAddr, u8 *buffer, u16 size)
{
    u8  j;
    if(size == 0)   return 2;
    if(!B_FlashOK)  return 2;
    W25QXX_Wait_Busy();            //Flash忙检测

    j = 0;
    SPI_CE_Low();                            //enable device
    SPI_WriteByte(W25X_ReadData);            //read command
    SPI_WriteByte((uint8)((ReadAddr)>>16));  //发送24位地址    
    SPI_WriteByte((uint8)((ReadAddr)>>8));   
    SPI_WriteByte((uint8)ReadAddr); 
    do
    {
        if(*buffer != SPI_ReadByte())       //receive byte and store at buffer
        {
            j = 1;
            break;
        }
        buffer++;
    }while(--size);         //read until no_bytes is reached
    SPI_CE_High();          //disable device
    return j;
}


/************************************************
写数据到Flash中
入口参数:
    addr   : 地址参数
    buffer : 缓冲需要写入Flash的数据
    size   : 数据块大小
出口参数: 无
************************************************/
void SPI_Write_Nbytes(uint32 WriteAddr,uint8* pBuffer,uint16 NumByteToWrite)
{
	 	uint16 i; 
    if(NumByteToWrite == 0)   return;
    if(!B_FlashOK)  return;
    W25QXX_Write_Enable();                  					//写使能
		SPI_CE_Low();                            					//选中spiflash   
    SPI_WriteByte(W25X_PageProgram);      					//发送写页命令   
    SPI_WriteByte((uint8)((WriteAddr)>>16)); 					//发送24位地址   
    SPI_WriteByte((uint8)((WriteAddr)>>8));   
    SPI_WriteByte((uint8)WriteAddr);   
    for(i=0;i<NumByteToWrite;i++)SPI_WriteByte(pBuffer[i]);//循环写数
		SPI_CE_High();                           					//取消片选
		W25QXX_Wait_Busy();				  						 //等待写入结束
}

