Работа с флеш памятью Winbond W25Q128

Наконец руки дошли до пришедших мне уже месяц назад микросхем Flash памяти w25q128 от компании Winbond.
Микросхема довольно крутая на свои деньги. В ней куча регистров и команд.
Я рассмотрю только несколько из них.

Я не использовал внешние подтяжки и конденсаторы, а просто собрал схему на макетной плате.

В качестве контроллера я использовал stm32f4 discovery.
SPI был настроен вот так.

void spi2_init() {
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
 
    GPIO_InitTypeDef gpio;
    GPIO_StructInit(&gpio);
 
    gpio.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    gpio.GPIO_Mode = GPIO_Mode_AF;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    gpio.GPIO_OType = GPIO_OType_PP;
    gpio.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOB,&gpio);
    
    gpio.GPIO_Pin = GPIO_Pin_12;
    gpio.GPIO_Mode = GPIO_Mode_OUT;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    gpio.GPIO_OType = GPIO_OType_PP;
    gpio.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOB,&gpio);
 
 
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2);
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_SPI2);
    GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2);
 
    SPI_I2S_DeInit(SPI2);
    SPI_InitTypeDef spi2;
    SPI_StructInit(&spi2);
 
    spi2.SPI_Mode = SPI_Mode_Master;
    spi2.SPI_DataSize = SPI_DataSize_8b;
    spi2.SPI_NSS = SPI_NSS_Soft;
    spi2.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
    SPI_Init(SPI2,&spi2);
    SPI_Cmd(SPI2,ENABLE);
}

u8 w25q128_spi_send(u8 data)
{
    /* Fill output buffer with data */
    SPI2->DR = data;
    /* Wait for transmission to complete */
    while (!SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE));
    /* Wait for received data to complete */
    while (!SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE));
    /* Wait for SPI to be ready */
    while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY));
    /* Return data from buffer */
    return SPI2->DR;
}

Перед использованием микросхему нужно сбросить. Для этого есть отдельная ножка, но мы не будем завязываться на неё. Ибо она отсутствует в некоторых корпусах да и ноги в контроллере обычно в дефиците. Поэтому произведем программный сброс.

Сброс производится в два присеста. Сначала нужно разрешить сброс, а уже только после этого сбрасывать.

void w25q128_reset()
{
    CS_LOW;
    w25q128_spi_send(W25_ENABLE_RESET);
    CS_HIGH;

    CS_LOW;
    w25q128_spi_send(W25_RESET);
    CS_HIGH;
}

Команды можно определить при помощи define или же как я при помощи перечисления.

typedef enum {
    W25_WRITE_DISABLE = 0x04,
    W25_WRITE_ENABLE = 0x06,
    
    W25_READ_STATUS_1 = 0x05,
    W25_READ_STATUS_2 = 0x35,
    W25_READ_STATUS_3 = 0x15,
    
    W25_WRITE_STATUS_1 = 0x01,
    W25_WRITE_STATUS_2 = 0x31,
    W25_WRITE_STATUS_3 = 0x11,
    
    W25_CHIP_ERASE = 0xc7, //0x60
    
    W25_GET_DEV_ID = 0x90,
    W25_GET_JEDEC_ID = 0x9f,
    
    W25_ENABLE_RESET = 0x66,
    W25_RESET = 0x99,
    
    W25_PAGE_PROGRAMM = 0x02,
    W25_READ = 0x03,
} W25_Command_t;  

Перед программированием нужно стереть память. Это можно сделать при помощи команды стирания чипа.

void w25q128_chip_erase()
{
    CS_LOW;
    w25q128_spi_send(W25_CHIP_ERASE);
    CS_HIGH;
}

Или же команд стирания блоков, страниц.
Стирание происходит довольно долго. Поэтому нужно либо ждать, либо отдать управление операционной системе, если вы её используете.

w25q128_chip_erase();
//ждем готовности
do{
    r.all = w25q128_read_status_1();
}while(r.bit.busy);

Для этого нам нужно считать регистр статуса.
Всего их в w25q128 три. Нам нужен первый.

u8 w25q128_read_status_1()
{
    u8 resp;
    CS_LOW;
    w25q128_spi_send(W25_READ_STATUS_1);
    w25q128_read(&resp,1);
    CS_HIGH;
    
    return resp;
}

Я любитель использовать объединения. Поэтому написал тип описывающий этот регистр.

typedef struct {
    u8 busy : 1;
    u8 write_enable : 1;
    u8 block_protect : 3;
    u8 top_bot_ptotect : 1;
    u8 sector_protect : 1;
    u8 status_reg_protect0 : 1;
} STATUS_REG1_STRUCT_t;

typedef union {
    u8 all;
    STATUS_REG1_STRUCT_t bit;
} Status_reg_1_t;

После стирания мы готовы записать тестовый блок данных.

//наполняем буфер
for (i=0;i<255;i++)
{
    page[i] = i;
}
    while(1)
	{
     
        r.all  = w25q128_read_status_1();
        if (!r.bit.busy)
        {
            if (!programmed)
            {
                w25q128_page_programm(page, 0, 10);
                do{
                    r.all = w25q128_read_status_1();
                }while(r.bit.busy);
                
                memset(page, 0, 255);
                
                programmed = 1;
            } else {
                w25q128_read_page(page, 0, 255);
            }
        }
}

Тут мы читаем статусный регистр и анализируем бит готовности. Если еще не проводилось программирование памяти, записываем страницу размером 10 байт с 0 адреса. Ждем готовности, сбрасываем буфер в ноль и переходим к чтению участка с нулевого адреса размером 255 байт.

Функция записи в память выглядит вот так.

u8 w25q128_page_programm(const u8* data, u32 addr, u8 len)
{
    w25q128_write_enable();
    
    CS_LOW;
    w25q128_spi_send(W25_PAGE_PROGRAMM);
    w25q128_spi_send((addr >> 16) & 0xFF);
    w25q128_spi_send((addr >> 8) & 0xFF);
    w25q128_spi_send(addr & 0xFF);
    while(len--)
    {
        w25q128_spi_send(*data++);
    }
    CS_HIGH;
    return 0;
}

Мы просто шлем команду записи и 3 байта адреса, после чего шлем данные.
Чтение производится аналогично, только команда другая.

u8 w25q128_read_page(u8* data, u32 addr, u8 len)
{
    CS_LOW;
    w25q128_spi_send(W25_READ);
    w25q128_spi_send((addr >> 16) & 0xFF);
    w25q128_spi_send((addr >> 8) & 0xFF);
    w25q128_spi_send(addr & 0xFF);
    while(len--)
    {
        *data++ = w25q128_spi_send(0x00);
    }
    CS_HIGH;
    return 0;
}

В отдельных случаях необходимо также проверить с какой микросхемой мы имеем дело.
Это можно сделать при помощи команды запроса идентификационных данных.

u8 w25q128_ident()
{
    const u8 get_id[] = {W25_GET_DEV_ID, 0,0,0 };
    u8 buff[2], ret;
    
    CS_LOW;
    w25q128_write(get_id, 4);
    w25q128_read(buff,2);
    CS_HIGH;
    
    if (buff[0] != 0xef && buff[1] != 0x17)
    {
        ret = W25_IDENT_ERROR;
    } else {
        ret = W25_OK;
    }
    return ret;
}

В ответ нам вернется EFh - идентификатор производителя и 17h - идентификатор микросхемы.

Приятного дня ;)

Просмотров:   5831

Комментарии

Добавить комментарий

Plain text

  • HTML-теги не обрабатываются и показываются как обычный текст
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Строки и параграфы переносятся автоматически.
CAPTCHA
Введи эти символы. Ато роботы одолели!