mx25l1606e SPI FLASH驅動代碼
1. yc_drv_mx25l1606e.c
#include <stdint.h>
#include <string.h>
#include "nrf_drv_spi.h"
#include "yc_drv_mx25l1606e.h"
#include "nrf_delay.h"
#include "yc_ssd1306.h"
extern void nrf_delay_us(uint32_t volatile number_of_us);
extern nrf_drv_spi_t SPI_0;
#define flash_delay_us(n) nrf_delay_us(n);
#define flash_delay_ms(n) nrf_delay_ms(n);
#define FLASH_CS_LOW() {nrf_drv_gpiote_out_clear(FLASH_SPI_CS);\
nrf_drv_gpiote_out_set(OLED_SPI_CS);}
#define FLASH_CS_HIGH() nrf_drv_gpiote_out_set(FLASH_SPI_CS)
static uint32_t flash_spi_write(uint8_t byte);
static uint32_t flash_spi_transfer(uint8_t * p_tx_buf,
uint8_t tx_buf_len,
uint8_t * p_rx_buf,
uint8_t rx_buf_len);
static uint32_t flash_read_status(void);
static uint32_t wait_for_ready(void);
static uint32_t flash_write_enable(bool enable);
static uint32_t get_manufacture_info(uint8_t *manuf_id, uint8_t *device_id);
static uint32_t flash_power(bool up_or_own);
static uint32_t flash_spi_write(uint8_t byte)
{
uint32_t err_code=0;
err_code = nrf_drv_spi_transfer(&SPI_0, &byte, 1, NULL, 0);
if(err_code)
{
DRV_printf("!!!flash_spi_write ERROR(0x%02X)\n",err_code);
}
return err_code;
}
static uint32_t flash_spi_read(uint8_t *rx_buf, uint8_t rx_len)
{
uint32_t err_code=0;
err_code = nrf_drv_spi_transfer(&SPI_0, NULL, NULL, rx_buf, rx_len);
if(err_code)
{
DRV_printf("!!!flash_spi_read ERROR(0x%02X)\n",err_code);
}
return err_code;
}
static uint32_t flash_spi_transfer(uint8_t * p_tx_buf,\
uint8_t tx_buf_len,\
uint8_t * p_rx_buf,\
uint8_t rx_buf_len)
{
uint32_t err_code=0;
err_code = nrf_drv_spi_transfer(&SPI_0, p_tx_buf, tx_buf_len, NULL, 0);
err_code += nrf_drv_spi_transfer(&SPI_0, NULL, 0, p_rx_buf, rx_buf_len);
if(err_code)
{
DRV_printf("!!!flash_spi_transfer ERROR(0x%02X)\n",err_code);
}
return err_code;
}
static uint32_t flash_power(bool up_or_down)
{
uint32_t err_code = FLASH_SUCCESS;
flash_delay_us(100);
FLASH_CS_LOW();
if(!up_or_down)
{
err_code = flash_spi_write(MX25L1606E_PWR_DOWN_CMD);
}
else
{
err_code = flash_spi_write(MX25L1606E_PWR_UP_CMD);
}
FLASH_CS_HIGH();
nrf_delay_us(9);
return err_code;
}
#if 0
/************************************************************************************/
static uint32_t flash_write_status(uint8_t flash_status)
{
uint32_t err_code = FLASH_SUCCESS;
uint8_t buf[2];
buf[0] = MX25L1606E_WRSR_CMD;
buf[1] = flash_status;
FLASH_CS_LOW();
err_code = flash_spi_transfer(buf,2,NULL,0);
if(err_code)
{
DRV_printf("!!!flash_write_status ERROR(0x%02X)\n",err_code);
}
FLASH_CS_HIGH();
return err_code;
}
#endif
/**************************************************************************/
/**************************************************************************/
static uint32_t flash_read_status(void)
{
uint32_t err_code = FLASH_SUCCESS;
uint8_t buf[2],status;
FLASH_CS_LOW();
err_code = flash_spi_write(MX25L1606E_READ_STATUS_CMD);
err_code += flash_spi_read(buf, 2);
if(err_code)
{
DRV_printf("!!!flash_read_status ERROR(0x%02X)\n",err_code);
}
FLASH_CS_HIGH();
status = buf[0];
return status & (FLASH_STAT_BUSY | FLASH_STAT_WRTEN);
}
/**************************************************************************/
/**************************************************************************/
static uint32_t wait_for_ready(void)
{
uint32_t timeout = 0,err_code = FLASH_SUCCESS;
uint8_t status;
while ( timeout < 1000 )
{
status = flash_read_status() & FLASH_STAT_BUSY;
if (status == 0)
{
break;
}
timeout++;
}
if ( timeout == 1000 )
{
err_code = FLASH_ERROR_TIMEOUT_READY;
DRV_printf("!!!wait_for_ready FLASH_ERROR_TIMEOUT_READY(0x%02X)\n",err_code);
return err_code;
}
return err_code;
}
/**************************************************************************/
/**************************************************************************/
uint32_t get_unique_id(uint32_t *device_unique_id)
{
uint32_t err_code = FLASH_SUCCESS;
uint8_t buf[3];
flash_power(up);
memset(buf,0,3);
if (wait_for_ready())
{
DRV_printf("!!!get_unique_id FLASH_ERROR_TIMEOUT_READY\n");
return FLASH_ERROR_TIMEOUT_READY;
}
FLASH_CS_LOW();
err_code = flash_spi_write(MX25L1606E_RDID_CMD);
err_code += flash_spi_read(buf, 3);
FLASH_CS_HIGH();
flash_power(down);
*device_unique_id=0;
*device_unique_id = (((uint32_t)buf[0]<<16) + ((uint32_t)buf[1]<<8) + buf[2]);
return err_code;
}
/**************************************************************************/
/**************************************************************************/
static uint32_t get_manufacture_info(uint8_t *manuf_id, uint8_t *device_id)
{
uint32_t err_code = FLASH_SUCCESS;
uint8_t buf[4];
flash_power(up);
memset(buf,0,4);
if (wait_for_ready())
{
DRV_printf("!!!get_manufacture_info FLASH_ERROR_TIMEOUT_READY\n");
return FLASH_ERROR_TIMEOUT_READY;
}
FLASH_CS_LOW();
buf[0] = MX25L1606E_REMS_CMD;
buf[1] = 0x00;
buf[2] = 0x00;
buf[3] = 0x00;
err_code = flash_spi_transfer(buf,4,NULL,0);
memset(buf,0,4);
err_code += flash_spi_read(buf, 4);
FLASH_CS_HIGH();
flash_power(down);
*manuf_id = buf[0];
*device_id = buf[1];
return err_code;
}
/**************************************************************************/
/**************************************************************************/
static uint32_t flash_write_enable(bool enable)
{
uint32_t err_code = FLASH_SUCCESS;
FLASH_CS_LOW();
err_code = flash_spi_write(enable?MX25L1606E_WREN_CMD:MX25L1606E_WRDI_CMD);
if(err_code)
{
DRV_printf("!!!flash_write_enable ERROR(0x%02X)\n",err_code);
}
FLASH_CS_HIGH();
return err_code;
}
/***********************************************************************************************/
/**************************************************************************/
/**************************************************************************/
uint32_t flash_read_buffer(uint32_t address, uint8_t *buffer, uint32_t len)
{
uint8_t buf[4],size=0;
uint32_t err_code = FLASH_SUCCESS,n = 0,spi_transfer_times=0;
flash_power(up);
if (address >= MX25L1606E_TOTAL_SIZE)
{
flash_power(down);
DRV_printf("!!!flash_read_buffer FLASH_ERROR_LENGTH\n");
return FLASH_ERROR_LENGTH;
}
if (wait_for_ready())
{
flash_power(down);
DRV_printf("!!!flash_read_buffer FLASH_ERROR_TIMEOUT_READY\n");
return FLASH_ERROR_TIMEOUT_READY;
}
len = ((address + len) > MX25L1606E_TOTAL_SIZE)?(MX25L1606E_TOTAL_SIZE - address):len;
FLASH_CS_LOW();
buf[0] = MX25L1606E_READ_CMD;
spi_transfer_times = len/FLASH_SPI_SINGLE_TRANSFER_MAX_SIZE + 1;
buf[1] = (uint8_t)((address >> 16) & 0x000000FF);
buf[2] = (uint8_t)((address >> 8) & 0x000000FF);
buf[3] = (uint8_t)(address & 0x000000FF);
err_code = flash_spi_transfer(buf, 4, NULL, 0);
for (uint8_t i=0; i<spi_transfer_times; i++ )
{
size = (len < 256 ? len : 255);
err_code +=flash_spi_transfer(NULL, 0 , &buffer[n], size);
n += size;
len -=size;
}
FLASH_CS_HIGH();
flash_power(down);
if(!err_code)
{
return n;
}
else
{
DRV_printf("!!!flash_read_buffer FLASH_ERROR_SPI_TRANSFER\n");
return 0;
}
}
/**************************************************************************/
/**************************************************************************/
uint32_t flash_erase_sector (uint32_t sector_number)
{
uint8_t buf[4];
flash_power(up);
if (sector_number >= MX25L1606E_SECTORS)
{
DRV_printf("!!!flash_read_buffer FLASH_PARAM\n");
return FLASH_PARAM;
}
if (wait_for_ready())
{
flash_power(down);
DRV_printf("!!!flash_erase_sector FLASH_ERROR_TIMEOUT_READY\n");
return FLASH_ERROR_TIMEOUT_READY;
}
flash_write_enable (true);
uint8_t status;
status = flash_read_status();
if (!(status & FLASH_STAT_WRTEN))
{
DRV_printf("!!!flash_read_buffer FLASH_ERROR_STATUS\n");
flash_power(down);
return FLASH_ERROR_STATUS;
}
uint32_t address = sector_number * MX25L1606E_SECTOR_SIZE;
FLASH_CS_LOW();
buf[0] = MX25L1606E_SECTOR_ERASE_CMD;
buf[1] = (uint8_t)((address >> 16) & 0x000000FF);
buf[2] = (uint8_t)((address >> 8) & 0x000000FF);
buf[3] = (uint8_t)(address & 0x000000FF);
flash_spi_transfer(buf, 4,NULL, 0);
FLASH_CS_HIGH();
while(flash_read_status() & FLASH_STAT_BUSY);
flash_power(down);
return FLASH_SUCCESS;
}
/**************************************************************************/
/**************************************************************************/
uint32_t flash_erase_chip (void)
{
uint8_t status;
DRV_printf("A");
flash_power(up);
if (wait_for_ready())
{
flash_power(down);
return FLASH_ERROR_TIMEOUT_READY;
}
DRV_printf("B");
status = flash_read_status();
DRV_printf("Stat: 0x%02X\n",status);
flash_write_enable(true);
DRV_printf("C");
status = flash_read_status();
DRV_printf("Stat: 0x%02X\n",flash_read_status());
if (!(status & FLASH_STAT_WRTEN))
{
flash_power(down);
return FLASH_ERROR_STATUS;
}
DRV_printf("D");
FLASH_CS_LOW();
flash_spi_write(MX25L1606E_CHIP_ERASE_CMD);
FLASH_CS_HIGH();
DRV_printf("Stat: 0x%02X\n",flash_read_status());
DRV_printf("E");
while (flash_read_status() & FLASH_STAT_BUSY)
{
DRV_printf("Stat: 0x%02X\n",flash_read_status());
}
flash_power(down);
return FLASH_SUCCESS;
}
/**************************************************************************/
/**************************************************************************/
uint32_t flash_write_page(uint32_t address,const uint8_t *buffer, uint32_t len)
{
uint32_t err_code = FLASH_SUCCESS;
uint8_t status,buf[5];
if (address >= MX25L1606E_MAX_ADDRESS)
{
DRV_printf("!!!flash_read_buffer FLASH_PARAM\n");
return FLASH_PARAM;
}
if (len > MX25L1606E_PAGE_SIZE)
{
DRV_printf("!!!flash_read_buffer FLASH_ERROR_LENGTH\n");
return FLASH_ERROR_LENGTH;
}
if ((address % MX25L1606E_PAGE_SIZE) + len > MX25L1606E_PAGE_SIZE)
{
DRV_printf("!!!flash_read_buffer FLASH_ERROR_LENGTH\n");
return FLASH_ERROR_LENGTH;
}
flash_power(up);
if (wait_for_ready())
{
flash_power(down);
DRV_printf("!!!flash_erase_sector FLASH_ERROR_TIMEOUT_READY\n");
return FLASH_ERROR_STATUS;
}
flash_write_enable(true);
status = flash_read_status();
if (!(status & FLASH_STAT_WRTEN))
{
flash_power(down);
DRV_printf("!!!flash_write_page FLASH_ERROR_STATUS(0x%02X)\n",status);
return FLASH_ERROR_STATUS;
}
FLASH_CS_LOW();
buf[0] = MX25L1606E_PAGE_WRITE_CMD;
buf[1] = (uint8_t)((address >> 16) & 0x000000FF);
buf[2] = (uint8_t)((address >> 8) & 0x000000FF);
buf[3] = (uint8_t)(address & 0x000000FF);
if(len)
{
buf[4] = buffer[0];
err_code = flash_spi_transfer(buf, 5, NULL, 0);
}
err_code += flash_spi_transfer((uint8_t *)&buffer[1], len-1, NULL, 0);
FLASH_CS_HIGH();
if (wait_for_ready())
{
flash_power(down);
DRV_printf("!!!flash_write_page FLASH_ERROR_STATUS\n");
return FLASH_ERROR_STATUS;
}
flash_power(down);
if(!err_code)
{
return len;
}
else
{
DRV_printf("!!!flash_read_buffer FLASH_ERROR_SPI_TRANSFER\n");
return 0;
}
}
/**************************************************************************/
/**************************************************************************/
uint32_t flash_write_buffer(uint32_t address,const uint8_t *buffer, uint32_t len)
{
uint32_t bytes_to_write;
uint32_t buffer_offset;
uint32_t results;
uint32_t bytes_written;
if ((address % MX25L1606E_PAGE_SIZE) + len <= MX25L1606E_PAGE_SIZE)
{
return flash_write_page(address, buffer, len);
}
bytes_written = 0;
buffer_offset = 0;
while(len)
{
bytes_to_write = MX25L1606E_PAGE_SIZE - (address % MX25L1606E_PAGE_SIZE);
results = flash_write_page(address, buffer+buffer_offset, bytes_to_write);
bytes_written += results;
if (!results)
{
DRV_printf("!!!flash_read_buffer READ PAGE ERROR\n");
return bytes_written;
}
address += bytes_to_write;
len -= bytes_to_write;
buffer_offset += bytes_to_write;
if (len <= MX25L1606E_PAGE_SIZE)
{
results = flash_write_page(address, buffer+buffer_offset, len);
bytes_written += results;
if (!results)
{
DRV_printf("!!!flash_read_buffer READ PAGE ERROR\n");
return bytes_written;
}
len = 0;
}
}
return bytes_written;
}
/**************************************************************************/
/**************************************************************************/
uint32_t find_first_empty_addr(void)
{
uint32_t address, latestUsedAddr;
uint8_t b,buf[4];
flash_power(up);
memset(buf,0,4);
if (wait_for_ready())
{
flash_power(down);
return FLASH_ERROR_TIMEOUT_READY;
}
for (int16_t page=0; page < MX25L1606E_PAGES; page++)
{
address = page * MX25L1606E_PAGE_SIZE;
FLASH_CS_LOW();
buf[0] = MX25L1606E_READ_CMD;
buf[1] = (uint8_t)((address >> 16) & 0x000000FF);
buf[2] = (uint8_t)((address >> 8) & 0x000000FF);
buf[3] = (uint8_t)(address & 0x000000FF);
flash_spi_transfer(buf, 4, NULL, 0);
for (int16_t i=0; i<MX25L1606E_PAGE_SIZE; i++)
{
flash_spi_read(&b,1);
if (b == 0xFF)
{
flash_power(down);
DRV_printf("Found an empty byte at 0x%02X",latestUsedAddr);
return address + i;
}
}
FLASH_CS_HIGH();
}
return FLASH_SUCCESS;
}
/***********************************************************************************************/
uint32_t flash_init(void)
{
uint32_t err_code = FLASH_SUCCESS;
uint32_t device_unique_id = 0;
uint8_t manufacture_id = 0,device_id = 0;
err_code = get_unique_id(&device_unique_id);
err_code += get_manufacture_info(&manufacture_id, &device_id);
DRV_printf("%s(0x%02X)------>flash_init()\n", (device_unique_id==MX25L1606E_UNIQUE_ID)?"Success":"Failure",err_code);
if(!err_code)
{
DRV_printf(" FLASH device_unique_id:0x%02X\n",device_unique_id);
DRV_printf(" FLASH manufacture id :0x%02X\n",manufacture_id);
DRV_printf(" FLASH device_id :0x%02X\n",device_id);
}
return err_code;
}
/***********************************************************************************************/
uint32_t FLASH_disk_status(void)
{
uint32_t err_code = FLASH_SUCCESS, flash_id;
if(!get_unique_id(&flash_id))
{
if(flash_id != MX25L1606E_UNIQUE_ID)
{
DRV_printf("!!!FLASH_disk_status FLASH_ERROR\n");
err_code = FLASH_ERROR;
}
}
else
{
DRV_printf("!!!FLASH_disk_status FLASH_ERROR\n");
err_code = FLASH_ERROR;
}
return err_code;
}
uint32_t FLASH_disk_initialize(void)
{
uint32_t err_code = FLASH_SUCCESS;
if(flash_init())
{
DRV_printf("!!!FLASH_disk_initialize FLASH_ERROR\n");
return FLASH_ERROR;
}
DRV_printf("FLASH_disk_initialize FLASH_SUCCESS\n");
return err_code;
}
uint32_t FLASH_disk_read(uint8_t *buff,uint32_t sector, uint32_t count)
{
uint32_t err_code = FLASH_SUCCESS, n=0;
DRV_printf("!!!FLASH_disk_read sector=%02d, count=%02d\n",sector ,count);
if ((sector >= MX25L1606E_SECTORS)||(count>(MX25L1606E_TOTAL_SIZE-(sector<<12))))
{
DRV_printf("!!!FLASH_disk_read FLASH_ERROR_LENGTH\n");
return FLASH_PARAM;
}
n = flash_read_buffer(sector<<12,buff,count<<12);
if(n!=(count<<12))
{
DRV_printf("!!!!!FLASH_disk_read FLASH_ERROR\n");
return FLASH_PARAM;
}
return err_code;
}
uint32_t FLASH_disk_write(const uint8_t *buff, uint32_t sector, uint32_t count)
{
uint32_t err_code = FLASH_SUCCESS, n = 0,erase_sector=0;
if ((sector >= MX25L1606E_SECTORS)||(count>(MX25L1606E_TOTAL_SIZE-(sector<<12))))
{
DRV_printf("!!!FLASH_disk_read FLASH_ERROR_LENGTH\n");
return FLASH_PARAM;
}
erase_sector = sector;
for(uint8_t i=0;i<count;i++)
{
flash_erase_sector(erase_sector++);
}
DRV_printf("!!!FLASH_disk_write sector=%02d, count=%02d\n",sector ,count);
n = flash_write_buffer(sector<<12,buff,count<<12);
if(n!=(count<<12))
{
DRV_printf("!!!!!FLASH_disk_read FLASH_ERROR\n");
return FLASH_PARAM;
}
return err_code;
}
2. yc_drv_mx25l1606e.h
/********************************************************************************************************************/
/***************************************** Debug macro ***************************************************/
/********************************************************************************************************************/
// Flash status bits
/***********************************************************************************************************/
// SPI Flash Characteristics (MX25L1606E Specific) size=2M
//#define MX25L1606E_BLOCK_SIZE 65536
typedef enum
{
FLASH_SUCCESS = 0,
FLASH_ERROR,
FLASH_PARAM,
FLASH_ERROR_TIMEOUT_READY,
FLASH_ERROR_LENGTH,
FLASH_ERROR_STATUS,
FLASH_ERROR_SPI_TRANSFER,
}flash_error_t;
typedef enum
{
up = true,
down = false,
}flash_power_t;
typedef struct
{
uint8_t manufacturer_id;
uint16_t device_id;
}flashInfoTypedef;
uint32_t get_unique_id(uint32_t *device_unique_id);
uint32_t flash_read_buffer(uint32_t address, uint8_t *buffer, uint32_t len);
uint32_t flash_erase_sector (uint32_t sector_number);
uint32_t flash_erase_chip (void);
uint32_t flash_write_page(uint32_t address, const uint8_t *buffer, uint32_t len);
uint32_t flash_write_buffer(uint32_t address, const uint8_t *buffer, uint32_t len);
uint32_t find_first_empty_addr(void);
uint32_t flash_init(void);
/****************************************************************************/
uint32_t FLASH_disk_status(void);
uint32_t FLASH_disk_initialize(void);
uint32_t FLASH_disk_read(uint8_t *buff,uint32_t sector, uint32_t count);
uint32_t FLASH_disk_write(const uint8_t *buff, uint32_t sector, uint32_t count);