Pull to refresh

Подключение Xmega к FRAM по SPI

Reading time 5 min
Views 5.6K
В одном из приборов, возникла необходимость полного восстановления предыдущего режима работы в случае какого либо сбоя по питанию или кратковременному отключению. Можно было конечно заложить источник резервного питания, но его использование было ограничено, так скажем, конструктивными особенностями прибора. Как результат, было решено записывать ряд необходимых для восстановления значений в память. Так как обновлять значения для восстановления я собирался часто, в связи с ограниченным количеством циклов записи, использование Flash и EEPROM даже не рассматривалось.

На мой взгляд, в данный момент самое оптимальное решение для таких случаев, это FRAM память. Можно было бы записывать необходимые данные циклически во Flash, постоянно инкрементируя адрес для новых значений, но в данном случае возникала необходимость где-то сохранять указатель на самые последние значения, либо полностью считывать Flash и затем уже извлекать «самые свежие» данные.

У Lapis Semiconductor есть три линейки FRAM микросхем, которые обмениваются с ведущим устройством по I2C или SPI, либо по параллельному интерфейсу. Преимущества последовательных интерфейсов перед параллельным очевидны. Что же касается I2C и SPI, то скорость передачи данных по SPI в 4 раза выше чем по I2C, но и потребление в связи с этим выше практически в 16 раз.

Мне же выбирать не приходилось, в наличии была только MR45V256 c 32 Кб памяти и SPI интерфейсом. 32 Кб для моих нужд более чем достаточно, поэтому оставшуюся память я использовал для записи всевозможной технической информации и логирования команд полученных от оператора.

Работа с FRAM памятью очень простая. Любая операция начинается с перевода линии выбора ведомого устройства CS# в низкое состояние. Затем отправляется одна из команд операций, их всего 6:

— Чтение данных(READ)
— Запись данных(WRITE)
— Запись в регистр статуса FRAM(WRSR)
— Чтение из регистра статуса FRAM(RDSR)
— Установка защиты данных от перезаписи(WRDI)
— Снятие защиты данных от перезаписи(WREN)

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

Перед тем как начать запись в память, необходимо всегда устанавливать бит разрешения записи(WREN) в регистре статуса MR45V256. И только после этого передавать команду записи(WRITE).

image

В качестве ведущего устройства использовал микроконтроллер Xmega. SPI микроконтроллеров Xmega практически ничем не отличается от SPI в других микроконтроллерах Atmel. Наиболее заметное отличие это возможность использования DMA, но объемы передаваемой информации в данном случае у меня не большие, поэтому от использования DMA я отказался. Xmega поддерживает все 4 режима работы SPI(Mode 0,1,2,3). Стоит заметить, что MR45V256 поддерживает только Mode 0 и Mode 3.

Модуль SPI в Xmega оснащен одноуровневой буферизацией в канале передачи и двухуровневой в канале приёма. То есть, байты для передачи, нельзя поместить в регистр данных SPI пока полностью не завершится цикл сдвигов. Во время приёма данных, принятую посылку необходимо считать прежде, чем завершится приём следующей посылки. В противном случае, первый байт данных будет потерян. Поэтому работать рекомендуется либо по прерываниям, либо при приёме и передаче контролировать регистр статуса SPI.

// Директивы для SPIE
#define	FRAMPORT	PORTE // FRAM PORT
#define	SPIRESET	0
#define	SPICS		4
#define	SPIMOSI		5
#define	SPIMISO		6
#define SPISCK		7

// Функция инициализации SPIE(FRAM)
void SPIE_init()
{
	FRAMPORT.DIRCLR = (1<<SPIMISO); // Выводы SPI на вход
	FRAMPORT.DIRSET = (1<<SPIMOSI) | (1<<SPISCK) | (1<<SPICS); // Выводы SPI на выход
	FRAMPORT.OUTSET = (1<<SPICS);
	// SPI включен, режим мастера, SPI Mode 0, деление частоты тактирования на 4
	SPIE.CTRL = SPI_ENABLE_bm | SPI_MASTER_bm | SPI_MODE_0_gc | SPI_PRESCALER_DIV4_gc;
}

// Функция записи во FRAM
void FRAM_WREN()
{
	SPIE_init();
	FRAMPORT.OUTCLR = (1<<SPICS); // Включаю ChipSelect
	SPIE.DATA = 0x06; // Установка бита разрешения записи
	while( !(SPIE_STATUS & SPI_IF_bm) ); // Жду отчета об отправке
	FRAMPORT.OUTSET = (1<<SPICS); // Выключаю ChipSelect

	PORTE.OUTCLR = (1<<SPICS); // Включаю ChipSelect
	SPIE.DATA = 0x02; // Отправляю команду на запись
	while( !(SPIE_STATUS & SPI_IF_bm) );
	SPIE.DATA = 0x00; // Старший байт адреса
	while( !(SPIE_STATUS & SPI_IF_bm) );
	SPIE.DATA = 0x10; // Младший байт адреса
	while( !(SPIE_STATUS & SPI_IF_bm) );
	
	SPIE.DATA = Data1; // Записываю данные
	while( !(SPIE_STATUS & SPI_IF_bm) );
	SPIE.DATA = Data2;
	while( !(SPIE_STATUS & SPI_IF_bm) );
	SPIE.DATA = Data3;
	while( !(SPIE_STATUS & SPI_IF_bm) );
	
	FRAMPORT.OUTSET = (1<<SPICS); // Выключаю ChipSelect
	SPIE.CTRL = 0x00;
	FRAMPORT.OUTCLR = (1<<SPIMOSI) | (1<<SPISCK);
	
}
// Функция чтения FRAM
void FRAM_RD()
{
	SPIE_init();
	FRAMPORT.OUTCLR = (1<<SPICS); // Включаю ChipSelect
	SPIE.DATA = 0x03;
	while( !(SPIE_STATUS & SPI_IF_bm) );
	SPIE.DATA = 0x00; // Старший байт адреса
	while( !(SPIE_STATUS & SPI_IF_bm) );
	SPIE.DATA = 0x10; // Младший байт адреса
	while( !(SPIE_STATUS & SPI_IF_bm) );
	
	SPIE.DATA = 0x00; // Считываю данные
	while( !(SPIE_STATUS & SPI_IF_bm) );
	Data1 = SPIE.DATA;
	SPIE.DATA = 0x00;
	while( !(SPIE_STATUS & SPI_IF_bm) );
	Data2 = SPIE.DATA;
	SPIE.DATA = 0x00;
	while( !(SPIE_STATUS & SPI_IF_bm) );
	Data3 = SPIE.DATA;
	
	FRAMPORT.OUTSET = (1<<SPICS); // Выключаем ChipSelect
	SPIE.CTRL = 0x00;
	FRAMPORT.OUTCLR = (1<<SPIMOSI) | (1<<SPISCK); // Порты SPI на выход
}

Команды для стирания во FRAM не предусмотрено, так как данные можно перезаписывать без предварительного стирания, как например в других видах памяти. Но всё же иногда возникает необходимость полного «стирания» всей FRAM:

void FramErase(void)
{
	SPIE_init();
	FRAMPORT.OUTCLR = (1<<SPICS); // Включаю ChipSelect
	SPIE.DATA = 0x06; // Отправляю команду WREN
	while( !(SPIE_STATUS & SPI_IF_bm) ); // Жду отчета об отправке
	FRAMPORT.OUTSET = (1<<SPICS); // Выключаю ChipSelect

	PORTE.OUTCLR = (1<<SPICS); // Включаю ChipSelect
	SPIE.DATA = 0x02; // Отправляю команду на запись
	while( !(SPIE_STATUS & SPI_IF_bm) );
	SPIE.DATA = 0x00; // Старший байт адреса
	while( !(SPIE_STATUS & SPI_IF_bm) );
	SPIE.DATA = 0x00; // Младший байт адреса
	while( !(SPIE_STATUS & SPI_IF_bm) );
	
	while(FramCounter <= 0x7FFF)
	{	
		SPIE.DATA = 0x00;
		while( !(SPIE_STATUS & SPI_IF_bm) );
		FramCounter++;
	}
	FRAMPORT.OUTSET = (1<<SPICS); // Выключаю ChipSelect
	FramCounter = 0;
	SPIE.CTRL = 0x00;
	// Порты SPI на выход
	FRAMPORT.OUTCLR = (1<<SPIMOSI) | (1<<SPISCK);	
}

Если говорить о преимуществах использования FRAM, то в первую очередь это надёжность. Количество циклов записи 10 в 12 степени. Производитель обещает до 10 лет хранения информации без её потери. По сравнению с той же FLASH память FRAM обладает большим быстродействием и куда меньшим потреблением. Из минусов можно отметить если только не большой объём памяти, но с учетом тех задач для которых обычно используют FRAM, большие объемы не так важны.
Tags:
Hubs:
+19
Comments 13
Comments Comments 13

Articles