天天看点

spi flash驱动代码分析(二)

一、spi flash裸机驱动代码

/*
*W25Q64一共8M容量?
*分为128个快,每块大小为64K字节?
*每块又可以分为16个扇区,每个扇区4K字节?//每次擦除最少一个扇区,也就是4K字节?//25Q64?一共有2048个扇区
*一页256字节
*/
#define W25Q64_PAGE_SIZE    256                          //一页的大小,256字节
#define W25Q64_SECTOR_SIZE (4*1024)                      //扇区大小,字节
#define W25Q64_BLOCK_SIZE  (16*W25Q64_SECTOR_SIZE)
#define W25Q64_SIZE        (128*W25Q64_BLOCK_SIZE)

void W25q64_Init(void)
{
    1.可以用GPIO模拟SPI
    2.也可以用spi控制器<span style="white-space:pre">	</span>
}

/*******************************************************************************
* Function Name  : W25q_ReadWriteByte 
* Description    : W25q芯片SPI读写接口函数
* Input          : None
* Output         : None
* Return         : None
* Date           : 2014-05-14
* Author         : ADT LL
*******************************************************************************/
static uint8_t W25q_ReadWriteByte(uint8_t data)
{
  return SSP_SendData(data);  
}


/*******************************************************************************
* Function Name  : W25q64_WaitForIdle
* Description    : 读状态寄存器
* Input          : None
* Output         : None
* Return         : None
* Date           : 2014-10-24
* Author         : ADT LL
*******************************************************************************/
void W25q64_WaitForIdle(void)
{
  uint8_t status=0;
  CS1_RESET;

  W25q_ReadWriteByte(0x05);

  do
  {
    status = W25q_ReadWriteByte(0xA5);
  }while (status&0x01 == 0x01);

  CS1_SET;
  
}

/*******************************************************************************
* Function Name  : W25q64_WriteEnable
* Description    : 写使能
* Input          : None
* Output         : None
* Return         : None
* Date           : 2014-10-24
* Author         : ADT LL
*******************************************************************************/
void W25q64_WriteEnable(void)
{
  CS1_RESET;

  W25q_ReadWriteByte(0x06);

  CS1_SET; 
}

/*******************************************************************************
* Function Name  : W25q64_4KErase
* Description    : 4K片擦除
* Input          : addr:起始地址
* Output         : None
* Return         : None
* Date           : 2014-10-24
* Author         : ADT LL
*******************************************************************************/
void  W25q64_4KErase(uint32_t addr)
{
  W25q64_WriteEnable();

  CS1_RESET;

  W25q_ReadWriteByte(0x20);
  W25q_ReadWriteByte((addr & 0xFF0000)>>16);
  W25q_ReadWriteByte((addr & 0x00FF00)>>8);
  W25q_ReadWriteByte(addr & 0xFF);

  CS1_SET; 
  
  W25q64_WaitForIdle(); 
}

/*******************************************************************************
* Function Name  : W25q64_32KErase
* Description    : 32K片擦除
* Input          : addr:起始地址
* Output         : None
* Return         : None
* Date           : 2014-10-24
* Author         : ADT LL
*******************************************************************************/
void  W25q64_32KErase(uint32_t addr)
{
  W25q64_WriteEnable();

  CS1_RESET;

  W25q_ReadWriteByte(0x52);
  W25q_ReadWriteByte((addr & 0xFF0000)>>16);
  W25q_ReadWriteByte((addr & 0x00FF00)>>8);
  W25q_ReadWriteByte(addr & 0xFF);

  CS1_SET;
  
  W25q64_WaitForIdle();  
}

/*******************************************************************************
* Function Name  : W25q64_64KErase
* Description    : 64K片擦除
* Input          : addr:起始地址
* Output         : None
* Return         : None
* Date           : 2014-10-24
* Author         : ADT LL
*******************************************************************************/
void  W25q64_64KErase(uint32_t addr)
{
  W25q64_WriteEnable();

  CS1_RESET;

  W25q_ReadWriteByte(0xD8);
  W25q_ReadWriteByte((addr & 0xFF0000)>>16);
  W25q_ReadWriteByte((addr & 0x00FF00)>>8);
  W25q_ReadWriteByte(addr & 0xFF);

  CS1_SET;
  
  W25q64_WaitForIdle();  
}

/*******************************************************************************
* Function Name  : W25q64_ChipErase
* Description    : Chip片擦除
* Input          : addr:起始地址
* Output         : None
* Return         : None
* Date           : 2014-10-24
* Author         : ADT LL
*******************************************************************************/
void  W25q64_ChipErase(uint32_t addr)
{
  W25q64_WriteEnable();

  CS1_RESET;

  W25q_ReadWriteByte(0xC7);
  W25q_ReadWriteByte((addr & 0xFF0000)>>16);
  W25q_ReadWriteByte((addr & 0x00FF00)>>8);
  W25q_ReadWriteByte(addr & 0xFF);

  CS1_SET;
  
  W25q64_WaitForIdle();  
}

/*******************************************************************************
* Function Name  : W25q64_Erase
* Description    : 擦除操作,最小以4K为单位进行,从擦除。
* Input          : sectornum:块数,擦除的空间大小为sectornum*4K
                    --例如:如果sectornum=1,则擦除4K字节,
                      sectornum=3,则擦除12K=4K*3个字节
                   addr:起始地址
* Output         : None
* Return         : 状态码
* Date           : 2014-10-25
* Author         : ADT LL
*******************************************************************************/
int W25q64_Erase(uint32_t base_addr,int sector_count)
{

  if (base_addr % W25Q64_SECTOR_SIZE ) {
  	return -1;
  }
  if (base_addr>=W25Q64_SIZE) return -1;
  
  while(sector_count = sector_count / 16) {
  	 W25q64_64KErase(base_addr);
	 base_addr += 16*W25Q64_SECTOR_SIZE;
  }
  while(sector_count = sector_count / 8) {
  	 W25q64_32KErase(base_addr);
	 base_addr += 8*W25Q64_SECTOR_SIZE;
  }
  while(sector_count--) {
  	 W25q64_4KErase(base_addr);
	 base_addr += W25Q64_SECTOR_SIZE;
  }
	
  return 0; 
}



/*******************************************************************************
* Function Name  : W25q64_PageProgram
* Description    : 页写
* Input          : buf:要写内的数据
                   len:数据长度
                   add:起始地址
* Output         : None
* Return         : 状态码
* Date           : 2014-10-24
* Author         : ADT LL
*******************************************************************************/
static int  W25q64_PageProgram(uint8_t *buf, uint16_t len, uint32_t addr)
{
  if (addr>=W25Q64_SIZE) return -1;
  if (len<=0 || len>256) return -1;//没有数据不操作

  W25q64_WriteEnable();

  CS1_RESET;

  W25q_ReadWriteByte(0x02);
  W25q_ReadWriteByte((addr & 0xFF0000)>>16);
  W25q_ReadWriteByte((addr & 0x00FF00)>>8);
  W25q_ReadWriteByte(addr & 0xFF);

  while (len--)
  {
    W25q_ReadWriteByte(*buf);
    buf++;   
  }

  CS1_SET;
  
  W25q64_WaitForIdle();
  
  return 0;   
}

/*******************************************************************************
* Function Name  : W25q64_Write
* Description    : 数据存储,为防止意外的擦除
* Input          : buf:要写内的数据
                   len:数据长度
                   add:起始地址
* Output         : None
* Return         : None
* Date           : 2014-10-24
* Author         : ADT LL
*******************************************************************************/
void W25q64_Write(uint8_t *buf, uint16_t len, uint32_t addr)
{
  uint8_t pagenum;
  uint8_t addrbyte;//最低八位地址
  
  addrbyte = addr%W25Q64_PAGE_SIZE;
  if (len > (W25Q64_PAGE_SIZE - addrbyte))//跨页了
  {
      W25q64_PageProgram(buf, W25Q64_PAGE_SIZE - addrbyte, addr);//写满本页
      addr += W25Q64_PAGE_SIZE-addrbyte;
      buf += W25Q64_PAGE_SIZE-addrbyte;
      len -= W25Q64_PAGE_SIZE-addrbyte;
      pagenum = len/W25Q64_PAGE_SIZE;
 
      while (pagenum--)
      {
        W25q64_PageProgram(buf, W25Q64_PAGE_SIZE, addr);
        addr += W25Q64_PAGE_SIZE;
        buf += W25Q64_PAGE_SIZE;
        len -= W25Q64_PAGE_SIZE;  
      }
      W25q64_PageProgram(buf, len, addr);
      
  }
  else
  {
    W25q64_PageProgram(buf, len, addr);
   
  } 
}


/*******************************************************************************
* Function Name  : W25q64_WriteStatus
* Description    : 写状态寄存器
* Input          : status:状态值
* Output         : None
* Return         : None
* Date           : 2014-10-24
* Author         : ADT LL
*******************************************************************************/
void W25q64_WriteStatus(uint16_t status)
{
  W25q64_WriteEnable();

  CS1_RESET;

  W25q_ReadWriteByte(0x01);
  W25q_ReadWriteByte(status & 0xFF);
  W25q_ReadWriteByte((status>>8) & 0xFF);

  CS1_SET;

  W25q64_WaitForIdle();  
  
}

/*******************************************************************************
* Function Name  : W25q64_ReadStatus
* Description    : 读状态寄存器
* Input          : None
* Output         : None
* Return         : 状态寄存器值
* Date           : 2014-10-24
* Author         : ADT LL
*******************************************************************************/
uint16_t W25q64_ReadStatus(void)
{
  uint16_t status=0;
  CS1_RESET;

  W25q_ReadWriteByte(0x05);
  status = W25q_ReadWriteByte(0xFF);

  CS1_SET;

  CS1_RESET;
  W25q_ReadWriteByte(0x35);
  status |= W25q_ReadWriteByte(0xFF)<<8;
  CS1_SET;

  
  return status;  
}

/*******************************************************************************
* Function Name  : W25q64_ReadDeviceID
* Description    : 读字库芯片ID
* Input          : None
* Output         : None
* Return         : None
* Date           : 2014-10-20
* Author         : ADT LL
*******************************************************************************/

uint16_t W25q64_ReadDeviceID(void)
{
  uint16_t DeviceID=0;

  CS1_RESET;

  W25q_ReadWriteByte(0x90);
  W25q_ReadWriteByte(0x00);
  W25q_ReadWriteByte(0x00);
  W25q_ReadWriteByte(0x00);

  DeviceID = W25q_ReadWriteByte(0xFF)<<8;
  DeviceID |= W25q_ReadWriteByte(0xFF);

  CS1_SET;

  return DeviceID;
}

/*******************************************************************************
* Function Name  : W25q64_Read
* Description    : 读数据
* Input          : buf:数据缓冲区
                   len:长度
                   addr:起始地址
* Output         : None
* Return         : 返回状态,暂无意义
* Date           : 2014-10-20
* Author         : ADT LL
*******************************************************************************/
uint8_t W25q64_Read(uint8_t *buf, uint32_t len, uint32_t addr)
{
  uint32_t i;
  uint8_t status;

  //if (addr>=W25Q64_TOPADDR) return W25Q64_ADDRERROR;
  CS1_RESET;
    
  status = W25q_ReadWriteByte(CODE_READDATA);
  status = W25q_ReadWriteByte((addr & 0xFF0000)>>16);
  status = W25q_ReadWriteByte((addr & 0x00FF00)>>8);
  status = W25q_ReadWriteByte(addr & 0xFF);
  for (i=0; i<len; i++)
  {
    buf[i] = W25q_ReadWriteByte(0xFF);
  }

  status = 0;

  CS1_SET;

  return status;
}
           

二、SPI 接口驱动代码

1.如果是GPIO模拟SPI

/*GPIO模拟SPI协议读写函数*/
uint8_t SPI_RW(uint8_t data)
{  
	char buf[24];
	uint8_t bit_ctr;
   	for(bit_ctr=0;bit_ctr<8;bit_ctr++)   // output 8-bit
   	{
		if((data & 0x80)) {
			MOSI_H;
		} else {
			MOSI_L;
		}
		data = (data << 1); 

		SCK_H;
		delayUs(1);
		data |= MISO ? 1 : 0;
		SCK_L;
		delayUs(1);
   	}

    return(data);    
}
           

2.如果是spi控制器读写spi

spi flash的CS片选如果接spi接口的SSEL要注意,由于 spi控制器的SSEL端不受代码控制的,这里我单独用了一个gpio来做CS片选。

uint8_t  SSP_SendData(uint8_t data)
{  
#ifndef GPIO_SPI
    LPC_SSP1->DR = data;
    while( (LPC_SSP1->SR & 0x01) == 0 );   /* 等待TFE置位,即发送FIFO空    */
    while((LPC_SSP1->SR & 0x04) == 0);    /*等待接收fifo不为空*/
    return (LPC_SSP1->DR);
#else
    return SPI_RW(data);
#endif
}