USB FLASH. Введение и часть 1 — Работа с AT45DB161D

Введение

Есть мнение, что интерфейс USB очень сложен, что если его использовать, то, лишь используя готовые библиотеки, а работать с ним на уровне регистров это извращение. В этом цикле статей, я хочу показать, что это не так, и мы создадим самую настоящую USB флешку, правда, всего на 2 мегабайта. Но ведь это не главное.

Для опытов нам понадобится плата с микроконтроллером STM32F0x2, разведенным разъемом USB и интерфейсом SPI, например как показано на рисунке 1:

  
Рисунок 1 — Схема электрическая принципиальная USB FLASH

Сразу оговорюсь, использование кварцевого резонатора не обязательно, на схеме и плате он предусмотрен, но прошивку мы напишем без его использования.


Рисунок 2 — Основная печатная плата


Рисунок 3 — Модульная плата с AT45DB161D

Фото флешки

Часть 1. Работа с USB FLASH AT45DB161D

Для начала разберемся с работой микросхемы SPI FLASH AT45DB161D.

AT45DB161D — микросхема Flash-памяти последовательного доступа объемом 16 МБит или 2 МБайта. Она поддерживает последовательный SPI-подобный интерфейс Atmel RapidS, работающий на скоростях до 66МГц. 16 мегабит памяти сгруппированы в 4096 страницы размером 512/528 байт. В дополнение к основной памяти, AT45DB161D содержит 2 SRAM буфера размером 512/528 байт. Они позволяют принимать данные во время записи в основную память, организовать запись непрерывного потока данных. Встроенная операция чтение-модификация-запись позволяет легко эмулировать EEPROM.


Рисунок 4 – Структурная схема AT45DB161D

Для максимальной гибкости в массиве памяти AT45DB161D реализованы три уровня разбиения: по секторам, по блокам и по страницам. Эти уровни, а также количество страниц в секторе и блоке изображены на рисунке 5. Все операции записи выполняются на страничном уровне, операции стирания на уровне всей микросхемы, сектора, блока или страницы.


Рисунок 5 – Архитектура памяти AT45DB161D

Микросхема управляется командами от ведущего процессора. Команда начинается спадающим фронтом сигнала CS, следом идут 8-битный код команды, номер буфера или адрес в массиве памяти. Подача тактового сигнала на SCK при активном CS приводит к чтению с вывода SI кода команды, номера буфера или адреса в массиве памяти. Коды команд, адреса и данные передаются старшим битом вперед (MSB). Микросхема поддерживает множество команд, но из всего разнообразия мы будем использовать следующие:

  • Чтение регистра статуса (D7h);
  • Непрерывное чтение массива (высокая скорость) (0Bh);
  • Запись в буфер 1 (84h);
  • Запись содержимого буфера 1 в страницу основной памяти с авто-стиранием (83h).
  • Подготовительный этап. Функции для работы со SPI.

    Инициализация:

    #define CS_HIGH GPIOB -> BSRR = GPIO_BSRR_BS_1
    #define CS_LOW GPIOB -> BSRR = GPIO_BSRR_BR_1

    void AT45DB161_Init(){
    //Включаем тактирование SPI1, GPIOA, GPIOB
    RCC -> APB2ENR |= RCC_APB2ENR_SPI1EN;
    RCC -> AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN;
    //Настраиваем PA5, PA6, PA7 как AF
    GPIOA -> MODER |= GPIO_MODER_MODER5_1 | GPIO_MODER_MODER6_1 | GPIO_MODER_MODER7_1;
    //High Speed
    GPIOA -> OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5 | GPIO_OSPEEDER_OSPEEDR6 | GPIO_OSPEEDER_OSPEEDR7;
    //Настраиваем PB1 как GP OUTPUT
    GPIOB -> MODER |= GPIO_MODER_MODER1_0;
    //High Speed
    GPIOB -> OSPEEDR |= GPIO_OSPEEDER_OSPEEDR1;
    //CS = 1
    CS_HIGH;
    /*
    SPI_CR1_SSM – программное управление выводом NSS (внутри микросхемы)
    SPI_CR1_SSI – активный уровень на NSS
    SPI_CR1_MSTR – режим SPI – мастер
    SPI_BR = 0 – частота SPI = fclk / 2 = 24МГц
    */
    SPI1 -> CR1 = SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_MSTR;
    /*
    SPI_CR2_DS = 7 – режим 8 бит
    SPI_CR2_FRXTH – событие RXNE будет генерироваться, когда FIFO будет заполнено на ¼ — то есть когда придет 1 байт
    */
    SPI1 -> CR2 = SPI_CR2_DS_0 | SPI_CR2_DS_1 | SPI_CR2_DS_2 | SPI_CR2_FRXTH;
    //SPI_CR1_SPE — включаем SPI
    SPI1 -> CR1 |= SPI_CR1_SPE;
    }

    Функция отправки и приема байта:

    //Регистр SPI_DR c 8-битным доступом
    #define SPI_DR8 *(uint8_t *)0x4001300C

    uint8_t SPI_SendByte (uint8_t byte){
    //Ждем пока передающий буфер не пуст
    while(!(SPI1-> SR & SPI_SR_TXE));
    //Передаем байт
    SPI_DR8 = byte;
    //Ждем пока приемный буфер пуст
    while(!(SPI1-> SR & SPI_SR_RXNE));
    //Возвращаем принятое значение
    return SPI_DR8;
    }

    Изначально микросхемы поставляются с размером страниц 528 байт. Программно можно установить размер 512 байт, но лишь один раз, после чего изменить его будет нельзя.

    Для того, чтобы активировать режим страниц размером 512 байт, необходимо выполнить следующие шаги:

    1. Запрограммировать регистр конфигурации:

    •    CS = 0;
    •    Послать в строгом порядке последовательность 3Dh + 2Ah + 80h + A6h;
    •    CS = 1;

    Программирование должно закончится максимум за время tp (максимум 6 мс), в течении которого регистр статуса будет информировать о занятости микросхемы.

    2. Сбросить и подать питание на микросхему, если сброс питания произойдет до завершения цикла программирования, то правильная запись не гарантируется;

    3. Теперь запись страниц производится по 512 байт.

    Чтобы выяснить установлен размер страницы в 512 байт или нет, необходимо проверить бит 0 в Регистре Статуса. Если он не установлен, команду можно послать повторно.

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

    1. Чтение регистра статуса (D7h)

    Из регистра статуса можно узнать готовность/занятость микросхемы, результат сравнения страницы основной памяти с буфером, статус защиты сектора, емкость микросхемы.

    Бит 7 RDY/BUSY – бит готовности/занятости микросхемы. Если бит равен 1, то микросхема не занята и готова к приему следующей команды. Если бит равен 0 – микросхема занята.

    Бит 6 COMP – результат последней команды «Сравнение страницы основной памяти с буфером X». Если бит равен 0 – данные в буфере соответствуют данным в основной памяти. Если бит равен 1 – данные отличаются хотя бы 1 битом.

    Биты 5..2 – объем микросхемы. Для микросхемы AT45DB161D – 1011.

    Бит 1 – PROTECT – бит защиты секторов. Значение 1 соответствует включенной защите секторов, 0 – выключенной защите.

    Бит 0 — PAGE SIZE – показывает размер страниц в основной памяти. Значение 1 соответствует размеру страниц в 512 байт, значение 0 – 528 байт.

    Регистр статуса можно прочесть в любой момент времени, даже во время выполнения самотактируемых операций записи и стирания. Чтобы прочесть регистр статуса необходимо активировать сигнал CS, после чего загрузить код команды D7h. После загрузки кода команды, с последующими тактовыми импульсами, на выводе SO будет выдан 1-байтовый регистр статуса. После передачи одного байта регистра статуса, в случае активного CS и последующих тактовых импульсах, выдача повторится. Данные в регистре статуса постоянно обновляются, поэтому все повторные выдачи будут содержать новые данные.


    Рисунок 7 – Диаграмма чтения регистра статуса

    Напишем функцию чтения регистра статуса:

    uint8_t AT45DB161_Read_Status(){
    uint8_t temp;
    //CS = 0
    CS_LOW;
    //Отправляем код команды
    SPI_SendByte(0xD7);
    //Читаем байт статуса
    temp = SPI_SendByte(0x00);
    //Ждем пока завершится работа SPI
    while(SPI1 -> SR & SPI_SR_BSY);
    //CS = 1
    CS_HIGH;
    //Возвращаем байт статуса
    return temp;
    }

    2. Непрерывное чтение массива (высокая скорость) (0Bh)

    Эта команда применяется при последовательном чтении массива основной памяти последовательным интерфейсом с частотой тактового сигнала вплоть до максимальной, определяемой параметром fcar1.

    Для чтения страниц стандартного размера (528 байт) необходимо сначала активировать сигнал CS, потом подать команду 0B, после которой следуют 3 адресных байта (формируют 24 бита адреса страницы и байта) и 1 произвольный. Первые 12 бит (PA11-PA0) 22-битной адресной последовательности задают номер читаемой страницы в массиве, а последние 10 бит(BA9-BA0) стартовый байт внутри страницы.

       
    Рисунок 8 – Последовательность для команд чтения/записи страниц размером 528 байт

    Для чтения страниц размером 512 байт необходимо подать команду 0Bh, после которой следуют 3 адресных байта (биты A20-A0) и 1 произвольный. Тактовые импульсы на выводе SCK, следующие после произвольного байта, приведут к выдаче данных на выводе SO.


    Рисунок 9 – Последовательность для команд чтения/записи страниц размером 512 байт

    Вывод CS должен оставаться в низком состоянии во время загрузки кода команды, адресных байт, произвольного байта и чтения данных. Если во время чтения будет достигнут конец страницы, микросхема продолжит чтение с начала следующей страницы без каких-либо задержек связанных с пересечением границ. Если будет прочтен последний байт массива памяти, чтение продолжиться с начала первой страницы, задержек так же не будет.

    Переход вывода CS из низкого состояния в высокое завершит операцию чтения и переведет вывод SO в третье состояние. Максимальная частота SCK для непрерывного чтения определяется параметром fcar1. Команда непрерывного чтения не меняет содержимое внутренних SRAM-буферов.


    Рисунок 10 – Диаграмма выполнения команды 0Bh

    Напишем функцию чтения массива байт:

    /* uint16_t page – номер читаемой страницы (0 – 4095)
    uint16_t addr – адрес байта внутри страницы (0 – 511)
    uint32_t length – количество читаемых байт
    uint8_t *out – указатель, куда ложить прочитанные байты
    */
    void AT45DB161_Read_Data(uint16_t page, uint16_t addr, uint32_t length, uint8_t *out){
    uint32_t i;
    uint8_t temp;
    //Ожидаем готовности микросхемы
    do {
    temp = AT45DB161_Read_Status();
    } while (!(temp & 0x80));
    //Исходя из размера страницы формируем адрес
    if (temp & 0x01){ //512
    i = ((page << 9) | (addr & 0x1FF));
    } else { //528
    i = ((page << 10) | (addr & 0x3FF)) ;
    }
    //CS = 0
    CS_LOW;
    //Отправляем код команды
    SPI_SendByte(0x0B);
    //Отправляем адрес
    SPI_SendByte(i >> 16);
    SPI_SendByte(i >> 8);
    SPI_SendByte(i);
    //Отправляем произвольный байт
    SPI_SendByte(0x00);
    //Читаем последующие байты в out[]
    for (i = 0; i < length; i++){
    out[i] = SPI_SendByte(0xFF);
    }
    //Ждем завершения работы SPI
    while(SPI1 -> SR & SPI_SR_BSY);
    //CS = 1
    CS_HIGH;
    }

    3. Запись в буфер 1 (84h)

    Данные, поступающие с входного сигнала SI, могут быть записаны либо в буфер 1, либо в буфер 2.

    Для записи данных в стандартный буфер (размером 528 байт) необходимо подать однобайтовый код команды 84h для буфера 1 и 87h для буфера 2. После чего подать 3 адресных байта, состоящих из 14 произвольных бит и 10 бит адреса в буфере (BFA1-BFA0). Адресные биты задают первый байт в буфере в который будет выполнена запись.

    Для записи данных в буфер размером 512 байт, необходимо подать команду 84h для буфера 1 и 87h для буфера 2. После команды подают 3 адресных байта, в которых 15 произвольных и 9 бит адреса в буфере (BFA9-BFA0). Адресные биты задают первый байт в буфере, в который будет выполнена запись.

    Сразу после адресных байт, с последующими тактовыми импульсами, подают байты данных. Если запись дойдет до границы буфера, микросхема автоматически продолжит запись с его начала. Данные будут загружаться до тех пор, пока не произойдет переход сигнала CS из низкого уровня в высокий.

    4. Запись содержимого буфера 1 в страницу основной памяти с авто-стиранием (83h)

    Данные, находящиеся в буфере 1 или буфере 2, могут быть записаны в основную память микросхемы. Для этого необходимо подать команду 83h для буфера 1 и 86h для буфера 2.

    При записи в страницы стандартного размера (528 байт), после команды подаются 3 адресных байта в которых 2 произвольных бита, 12 бит (PA11-PA0) задают адрес программируемой страницы в основной памяти , 10 произвольных бит.

    Чтобы записать в страницы размером 512 байт необходимо подать команду 83h для буфера 1 и 86h для буфера 2. После команды подаются 3 адресных байта в которых 3 произвольных бита, 12 бит (A20-A9) задают адрес программируемой страницы в основной памяти, 9 произвольных бит.

    Переход сигнала CS из низкого состояния в высокое сначала сотрёт страницу в основной памяти, после чего запишет данные из буфера в указанную страницу основной памяти. Операция стирания и записи страницы самотактируемая и выполняется за максимальное время tEP. В течение этого времени регистр статуса и вывод RDY/BUSY будут информировать о занятости микросхемы.

    Напишем функцию записи страницы:

    void AT45DB161_PageProgram(uint16_t page, uint8_t *data, uint16_t length){
    uint16_t i;
    uint8_t temp;
    //Читаем регистр статуса, чтобы определить размер страницы
    temp = AT45DB161_Read_Status();
    CS_LOW;
    //Пишем данные в буфер 1 с 0 адреса
    SPI_SendByte(0x84);
    SPI_SendByte(0x00);
    SPI_SendByte(0x00);
    SPI_SendByte(0x00);
    for (i = 0; i < length; i++){
    SPI_SendByte(data[i]);
    }
    while(SPI1 -> SR & SPI_SR_BSY);
    CS_HIGH;
    CS_LOW;
    //Посылаем команду 83h
    SPI_SendByte(0x83);
    //Исходя из размера страницы формируем адрес
    if (temp & 0x01){ //512
    SPI_SendByte((uint8_t)(page >> 7));
    SPI_SendByte((uint8_t)((page & 0x7F) << 1));
    SPI_SendByte(0x00);
    } else { //528
    SPI_SendByte((uint8_t)(page >> 6));
    SPI_SendByte((uint8_t)((page & 0x3F) << 2));
    SPI_SendByte(0x00);
    }
    while(SPI1 -> SR & SPI_SR_BSY);
    CS_HIGH;
    //Ожидаем готовности микросхемы
    while (!(AT45DB161_Read_Status() & 0x80));
    }

    На этом все, теперь мы умеем работать с флеш-памятью и можем переходить к USB.

    Список радиоэлементовОбозначение
    Тип
    Номинал
    Количество
    ПримечаниеМагазинМой блокнот

    МК STM32STM32F042F61

    SPI FLASHAT45DB161D1

    LDOXC6206P332MR1

    Конденсатор 06031мкФ2

    Конденсатор 06030.1 мкФ3

    Конденсатор 060322 пФ2

    Резистор 060310 кОм2

    Резистор 0603220 Ом1

    Кварцевый резонатор8 МГц1

    Светодиод 08051
    Добавить все

    Скачать список элементов (PDF)

    Прикрепленные файлы:

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

    Ваш адрес email не будет опубликован.