Реклама ⓘ
Главная » Микроконтроллеры
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Реклама ⓘ

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

Введение

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

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

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

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

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

Модульная плата с AT45DB161D
Рисунок 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.

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

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

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

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

  1. Чтение регистра статуса (D7h);
  2. Непрерывное чтение массива (высокая скорость) (0Bh);
  3. Запись в буфер 1 (84h);
  4. Запись содержимого буфера 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) стартовый байт внутри страницы.

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

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

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

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

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

Диаграмма выполнения команды 0Bh
Рисунок 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.

Список радиоэлементов

Обозначение Тип Номинал Количество ПримечаниеМагазинМой блокнот
МК STM32
STM32F042F6
1 Поиск в магазине ОтронВ блокнот
SPI FLASHAT45DB161D1 Поиск в магазине ОтронВ блокнот
LDOXC6206P332MR1 Поиск в магазине ОтронВ блокнот
Конденсатор 06031мкФ2 Поиск в магазине ОтронВ блокнот
Конденсатор 06030.1 мкФ3 Поиск в магазине ОтронВ блокнот
Конденсатор 060322 пФ2 Поиск в магазине ОтронВ блокнот
Резистор 0603
10 кОм
2 Поиск в магазине ОтронВ блокнот
Резистор 0603
220 Ом
1 Поиск в магазине ОтронВ блокнот
Кварцевый резонатор8 МГц1 Поиск в магазине ОтронВ блокнот
Светодиод 08051 Поиск в магазине ОтронВ блокнот
Добавить все

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

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

Теги:

Опубликована: 0 2
Я собрал 0 3
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 5 Проголосовало: 3 чел.

Комментарии (15) | Я собрал (0) | Подписаться

0
Публикатор #
На форуме автоматически создана тема для обсуждения статьи.
Ответить
0
Дмитрий #
А больше чем на 2Мбайта нельзя сделать?
Ответить
0

[Автор]
sobs #
Конечно можно, просто цель статей показать принцип, да и микросхема памяти под рукой только на 2 мегабайта была.
Ответить
0
Андрей #
А на atmega8 можно переделать вашу библиотеку, чтобы этот контроллер мог с памятью работать?
Ответить
0

[Автор]
sobs #
Если речь о работе с флеш, то можно, достаточно легко.
Если речь о работе с usb, то мою библиотеку портировать не удастся, для этих целей там есть V-USB.
Ответить
0
Андрей #
Нет, я имею ввиду чтобы был AVR контроллер, например мега8 чтобы она работала с этой микросхемой памяти. А не могли вы бы переделать и выложить библиотеку для AVR контроллеров?
Ответить
0

[Автор]
sobs #
Вроде так, проверить не на чем, так что сами.
Ссылка
Ответить
0
Андрей #
А не могли бы вы переделать хотя бы не тестируя. Хотя бы функцию инициализации. Так как предполагаете, а я уже сам потом буду тестить
Ответить
0

[Автор]
sobs #
Так выше в ссылке, уже переделал 2 дня назад.
Ответить
0
Андрей #
Так не могли бы вы переделать библиотеку для работы с памятью под любой AVR контроллер?
Ответить
0

[Автор]
sobs #
Под ЛЮБОЙ - нет, у каждого свои особенности, выше писали - мега8. Я переделал и выложил. Если нужно на другой контроллер, меняйте следующие функции с учетом особенностей вашего контроллера:
void AT45DB161_Init(). - Тут должна быть настройка портов SPI и настройка самого SPI, и настройка вывода CS.
uint8_t SPI_SendByte(uint8_t byte) - функция отправки и приема байта по SPI.
Дефайны CS_HIGH и CS_LOW - установить вывод CS в 1 и 0 соответственно.
Ну и смотря в каком компиляторе будете использовать, может понадобиться подключение заголовочных файлов с описанием регистров.
Ответить
0
Андрей #
USB не нужен, очень нужна только библиотека для работы с этой памятью
Ответить
0
Егерь #
Интересует вопрос. Какое число циклов записи в такую микросхему?
Ответить
0
Егерь #
Отвечаю сам себе, почитав даташит... Минимум 100 000 циклов программирования/стирания на странице. ну.... Как обычная флэшка по ходу дела.... Трудно сказать на сколько хватит микросхемы, если в нее будет что то записываться постоянно...
Ответить
0
Kostya #
А если у меня 4 45db161d?
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется напряжение?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Программатор Pickit3
Программатор Pickit3
200 Вт усилитель класса D на IRS2092 Мультиметр DT9205A
вверх