Главная » Микроконтроллеры
Призовой фонд
на август 2017 г.
1. Регулируемый паяльник 60 Вт
Паяльник
2. Тестер компонентов LCR-T4
Паяльник
3. 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 Поиск в FivelВ блокнот
SPI FLASHAT45DB161D1 Поиск в FivelВ блокнот
LDOXC6206P332MR1 Поиск в FivelВ блокнот
Конденсатор 06031мкФ2 Поиск в FivelВ блокнот
Конденсатор 06030.1 мкФ3 Поиск в FivelВ блокнот
Конденсатор 060322 пФ2 Поиск в FivelВ блокнот
Резистор 0603
10 кОм
2 Поиск в FivelВ блокнот
Резистор 0603
220 Ом
1 Поиск в FivelВ блокнот
Кварцевый резонатор8 МГц1 Поиск в FivelВ блокнот
Светодиод 08051 Поиск в FivelВ блокнот
Добавить все

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

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

Теги:

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

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

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

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

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

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

[Автор]
sobs #
Конечно можно, просто цель статей показать принцип, да и микросхема памяти под рукой только на 2 мегабайта была.
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется напряжение?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Pickit 2 - USB-программатор PIC-микроконтроллеров
Pickit 2 - USB-программатор PIC-микроконтроллеров
Конструктор регулируемого преобразователя напряжения LM317 200 Вт усилитель класса D на IRS2092
вверх