Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Чтение\запись по SPI
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > MSP430
Страницы: 1, 2
rezident
Цитата(d7d1cd @ Mar 6 2013, 22:55) *
Теперь мне необходимо реализовать запись. И тут возник вопрос.

Ответ на ваш вопрос есть в моем сообщении полугодовой давности #44.
d7d1cd
rezident, из Вашего ответа я понял, что перед отправкой любого оп-кода необходимо сигнализировать микросхеме, что ей отправляется новая команда (оп-код). Сигналом служит изменение уровня на CS с высокого на низкий. Спасибо за напоминание!
d7d1cd
Возник такой вопрос: с какой целью создана команда WRDI, то есть включение запрета на запись? Ведь после отправки команды WREN следует либо команда на запись регистра статуса (WRSR), либо запись в память (WRITE) (для этого команда WREN и отправлялась). А после завершения команд WRSR или WRITE запрет на запись включается автоматически.
rezident
Цитата(d7d1cd @ Mar 11 2013, 19:31) *
Ведь после отправки команды WREN следует либо команда на запись регистра статуса (WRSR), либо запись в память (WRITE)

Не следует, а может следовать. А может и не следовать. Это же программный алгоритм, а не аппаратный. Вот для того, чтобы можно было безопасно прочитать после разрешения записи и нужна команда, отменяющая это разрешение.
d7d1cd
Цитата(rezident @ Mar 11 2013, 20:30) *
Не следует, а может следовать. А может и не следовать. Это же программный алгоритм, а не аппаратный. Вот для того, чтобы можно было безопасно прочитать после разрешения записи и нужна команда, отменяющая это разрешение.


Теперь у меня 2 вопроса:

1. Что значит программный алгоритм? Разве не я определяю какая команда за какой будет идти?
2. Если я разрешил запись, то я не могу прочитать память? Обязательно надо отменять разрешение записи?
rezident
Цитата(d7d1cd @ Mar 11 2013, 21:37) *
1. Что значит программный алгоритм? Разве не я определяю какая команда за какой будет идти?
Именно это и означает программный алгоритм. Вот если бы для записи требовалось последовательно передать WREN и WRITE и никак иначе, то это был бы аппаратный алгоритм. А так вслед за WREN вы можете и не передавать WRITE.
Цитата(d7d1cd @ Mar 11 2013, 21:37) *
2. Если я разрешил запись, то я не могу прочитать память? Обязательно надо отменять разрешение записи?
Я же написал - безопасное чтение. Можете не отменять, но возможность неразрушающего чтения не гарантируется.
Я вообще вашей заморочки не понимаю. Ну есть команда отменяющая разрешение записи. Чем она вас не устраивает-то? Это же полностью логично - разрешить запись и отменить разрешение. Что вас смущает? Если устройство выполняет, например, функции логгирования, то вполне возможна ситуация, когда от разных процессов поступают запросы на чтение и на запись одновременно. Причем чтение информации обычно процесс более приоритетный. В частности вот для такого случая и нужна команда отмены разрешения записи.
d7d1cd
Понял вас, rezident! Можете же грамотно пояснить! Спасибо.

Итак, запись... Моя схема подключения микросхем памяти (МП) к микроконтроллеру приведена в начале топика, за исключением того, что выводы МП SI и SO соединены вместе. Функцию чтения из МП я написал. В этой функции через параметры передаются из какой МП читать, по какому адресу, сколько байт, а так же указывается адрес в ОЗУ, куда надо поместить прочитанные байты. Тестирование этой функции показало, что работает она правильно, то есть читает байты из любой МП по любому (доступному в МП) адресу.

Далее я написал функцию записи. По аналогии с функцией чтения в ее параметрах указывается в какую МП писать, по какому адресу, сколько байт и указывается адрес, откуда брать байты для записи. Однако тестирование показало следующее: запись в МП D7 происходит успешно, а вот в D8 и D9 запись не производится вообще.

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

Подскажите, пожалуйста, в чем может быть причина отказа в записи МП D8 и D9.

P.S. D7 - FM25CL64, D8 - M95256, D9 - M95512
P.S.S Все МП исправны: программатором запись и чтение проходят на ура.
rezident
Вы бы для начала выложили наконец корректную схему подключения. Ибо еще в начале топика выяснили, что схема из вашего корневого сообщения не соответствует реальности.
d7d1cd
Цитата(rezident @ Mar 13 2013, 13:56) *
Вы бы для начала выложили наконец корректную схему подключения. Ибо еще в начале топика выяснили, что схема из вашего корневого сообщения не соответствует реальности.


Понял. Вот корректная схема:




rezident
Сигналами CS, надеюсь, при записи в разные кристаллы вы разными управляете?
Основное отличие FM25CL64 от M95xxx в том, что первая м/с это FRAM, а остальные это EEPROM. FRAM не требует циклов ожидания, т.к. у памяти FRAM запись происходит при каждом обращении к ней. Даже при чтении содержимого FRAM происходит перезапись считываемой информации. У м/с EEPROM же запись может происходить только небольшими блоками. Т.е. вначале данные попадают в буфер и только после дезактивации CS включается генератор стирания/записи и происходит запись. Для M95256 буфер размером 64 байта, для M95512 буфер - 128 байт.
Поэтому, во-первых, перед записью нужно проверять готовность EEPROM к записи путем чтения регистра статуса и проверки битов WEL (1 - когда выполнена команда WREN и запись разрешена, 0 - запись запрещена) и WIP (1 - когда идет процесс записи и новую запись делать нельзя, 0 - когда запись возможна).
Во-вторых, адресация внутри буфера осуществляется счетчиком ограниченной разрядности (64 байта - 6-битный счетчик, 128 байт - 7-битный счетчик). Поэтому при записи блока нужно выравнивать размер записи на границу размера буфера. Иначе происходит переполнение счетчика и запись в буфер происходит по кольцу. Для вычисления адреса начала буфера достаточно адрес начала записи поделить, а затем умножить на размер буфера (или наложить маску той же размерности, что и размер буфера). Соответственно для вычисления адреса конца буфера нужно прибавить к адресу начала буфера его размер.

Пример.
Пускай буфер у нас будет всего 4 байта. Нам нужно записать 6 байт (0x01, 0x02, 0x03, 0x04, 0x05, 0x06) в "чистую" м/с EEPROM (заполнена 0xFF), начиная с адреса 0x0003.
Предполагаем, что после записи содержимое должно быть таким
0x0000 0xFF
0x0001 0xFF
0x0002 0xFF
0x0003 0x01
0x0004 0x02
0x0005 0x03
0x0006 0x04
0x0007 0x05
0x0008 0x06
0x0009 0xFF
0x000A 0xFF
Если будем писать сразу 6 байт, то получится переполнение буфера и запись будет такая
0x0000 0x06
0x0001 0x03
0x0002 0x04
0x0003 0x05
0x0004 0xFF
0x0005 0xFF
0x0006 0xFF
0x0007 0xFF
0x0008 0xFF
0x0009 0xFF
0x000A 0xFF
Если сократим запись до размера буфера (4 байта), то все равно будет нарушение записи
0x0000 0x02
0x0001 0x03
0x0002 0x04
0x0003 0x01
0x0004 0xFF
0x0005 0xFF
0x0006 0xFF
0x0007 0xFF
0x0008 0xFF
0x0009 0xFF
0x000A 0xFF
Корректная запись будет только, если мы разобьем запись на три блока
первая запись с начального адреса 0x0003 0x01 (1 байт, запись выравнена по концу буфера на его размер, адресация буфера 0x0000-0x0003)
вторая запись с начального адреса 0x0004 0x02 0x03 0x04 0x05 (4 байта, запись выравнена на полный размер буфера, адресация буфера 0x0004-0x0007)
третья запись с начального адреса 0x0008 0x06 (1 байт, запись выравнена по началу буфера, адресация буфера 0x0008-0x000B)
Резюмируя.
1) перед записью нужно проверять готовность м/с к записи.
2) при записи нужно ограничивать количество байт данных так, чтобы не произошел переход адреса (переполнение счетчика буфера) через конец буфера в его начало.
d7d1cd
Какой развернутый ответ... Готовность микросхемы я проверяю. Более того, я делаю запись регистра статуса (все биты 0) для разрешения записи во всю МС. На WP при этом у меня 1.

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


Вот это немного сложно пока. Даже примеры пока не спасают. Давайте представим, что я хочу записать 10 байт в микросхему M95512 по адресу FFF0. Адрес начала буфера - FFF0 \ 80 = 1FF * 80 = FF80. Адрес конца буфера - FF80 + 7F = FFFF. То есть, буфер от FF80 до FFFF.

Получается, что отправив в МС адрес FFF0 реально произойдет запись начиная с адреса FF80 что ли?
rezident
Цитата(d7d1cd @ Mar 13 2013, 21:16) *
Получается, что отправив в МС адрес FFF0 реально произойдет запись начиная с адреса FF80 что ли?
Нет конечно! Посмотрите Figure 4. Block diagram в datasheet M95512. Видите там прямоугольник с надписью Address register and counter? Вот в этот регистр записывается начальный адрес, который вы передаете в команде записи. Младшие 7 бит этого адреса автоматически инкрементируются при последовательной записи данных в буфер (размером 128 байт), начиная от начального адреса. Если побитно рассматривать, то адрес выглядит как XXXX XXXX XCCC CCCC, где биты X, транслированные в м/с EEPROM после кода команды, не меняются, а биты C инкрементируются при записи каждого последующего байта данных. Если вы запишете в буфер больше, чем позволяет разрядность его счетчика, то этот счетчик переполнится и адрес вернется вновь к началу буфера. При побайтной записи (один байт на каждую команду записи) можно писать по какому угодно адресу. Но при блочной записи необходимо выравнивать данные так, чтобы счетчик адреса не переполнился.
d7d1cd
Понял. Со счетчиком все ясно. Вернувшись к моей задаче (о записи 10 байт по адресу FFF0) получается, что все 10 байт, по идее, должны записаться в память. Однако этого не происходит.
Готовность МС к записи я определяю путем чтения регистра статуса и проверки бита WIP (должен быть 0). Далее я отправляю WREN, потом WRITE, адрес и байты. rezident, Вы писали, что надо проверять биты WIP и WEL. Может быть мне сначала проверить бит WIP, отправить команду WREN, проверить бит WEL и только тогда приступать к записи?
rezident
Да, именно так и нужно.
1.Читаете регистр статуса (команда RDSR). Если WIP=1, то повторяете чтение регистра статуса до тех пор, пока WIP не станет = 0.
1а.при необходимости изменения состояния битов BPx в регистре статуса (изменение размеров области памяти, защищенной от записи) переводите вывод WP в неактивное состояние (= 1)
1б.проверяете, что в регистре статуса бит SRWD = 0 (запись в регистр статуса разрешена)
1в.сбрасываете/устанавливаете биты BPx в регистре статуса (производите запись в регистр статуса соответствующей командой WRSR), чтобы снять блокировку записи с требуемых секторов EEPROM
2.Отправляете команду WREN.
3.Читаете регистр статуса, чтобы убедиться, что WEL = 1.
4.Отправляете на запись данные командой WRITE с учетом границы буфера.
5.Читаете регистр статуса, проверяя WIP до тех пор пока он не станет = 0.
Если нужно еще дописать, то переходите к п.2. Если нет, то
6. при необходимости защиты от записи секторов EEPROM устанавливаете биты BPx в регистра статуса (командой WRSR).
6а. WP переводите в активное состояние (= 0) для установки защиты от записи регистра статуса
Если защиту от записи не используете, то пункты манипуляций с WP и BPx (1а, 1б, 1в, 6, 6а) можно пропустить
d7d1cd
А если в пункте 1б SRWD равен 1? В своей реализации записи я делаю так:
1. Устанавливаю WP = 1.
2. Читаю регистр статуса.
2. Если бит WIP равен 1, то перехожу к пункту 2.
3. Отправляю команду WREN.
4. Записываю в регистр статуса байт 0х00 (разрешаю запись в любую область памяти, сбрасываю бит SRWD).
5. Отправляю команду WREN.
6. Отправляю команду WRITE.
7. Отправляю адрес.
8. Отправляю байты для записи.

Я дополнительно не проверяю установился ли бит WEL. Он может не установиться после команды WREN?
rezident
Да, в моем описании алгоритма я почему-то упустил необходимость подачи команды WREN перед записью в регистр статуса и контроль окончания выполнения записи в него (анализ бита WIP).
Цитата(d7d1cd @ Mar 14 2013, 21:46) *
Я дополнительно не проверяю установился ли бит WEL. Он может не установиться после команды WREN?

Может, если, например, вы попытаетесь транслировать команду WREN до окончания выполнения записи. Или, если во время (после) подачи команды WREN произойдет "провал" в питании EEPROM, который она воспримет как условие Power-up. Или CS сняли не вовремя.
d7d1cd
Цитата
необходимость подачи команды WREN перед записью в регистр статуса и контроль окончания выполнения записи в него (анализ бита WIP)

Цитата
...если, например, вы попытаетесь транслировать команду WREN до окончания выполнения записи.


А я в своем алгоритме после записи регистра статуса (байт 0х00), сразе же отправляю команду WREN (почему то я подумал, что регистр статуса пишется "мгновенно" и проверять WIP нет необходимости). Возможно, что ошибка именно тут: регистр статуса еще "пишется", а я снова отправляю команду на разрешение записи. Конечно она не проходит. Это как раз и объясняет почему микросхема FRAM пишется, а EEPROM нет.

Сегодня, если будет время, обязательно это все проверю.

rezident
Циатата из datasheet M95512
Цитата
While the Write Status Register cycle is in progress, the Status Register may still be read to
check the value of the Write In Progress (WIP) bit. The Write In Progress (WIP) bit is 1
during the self-timed Write Status Register cycle
, and is 0 when it is completed. When the
cycle is completed, the Write Enable Latch (WEL) is reset.

Я бы на вашем месте попробовал пока вообще не использовать защиту записи и проверить саму запись. И только после отладки записи добавить процедуры защиты записи.
IVN2013
Доброго времени суток! Начал осваивать работу с FRAM FM25CL64B. Контроллер -AVR mega16, копмилятор Написал тестовую программу записи / считывания (при отправке на mega16 по USART любого байта пишу и считываю). Только вот приходят одни нули. На MISO FRAM - низкий уровень, в то время как осуществляю чтение. Выкладываю код программки, может кто что подскажет. Заранее спасибо!
Код
#include <iom16v.h>
#include <macros.h>
#include <eeprom.h>
#include <string.h>

#pragma interrupt_handler UART_RX_interrupt:12

#define ToggleBit(x, bit) (x^=(1<<bit))

static volatile unsigned char readdata=0x00;
static volatile unsigned char readdata1=0x00;
static volatile unsigned char readdata2=0x00;

void InitUART(void)
{
    UBRRL = 0X03;        // baud rate = 115200
    UBRRH = 0x00;
    UCSRA = 0x00;
    UCSRB = (1<<RXEN)|(1<<TXEN);
    UCSRC = (3<<UCSZ0)|(0<<USBS)|(1<<URSEL);
     /* Set frame format: 8data, 1stop bit */
    UCSRC = (1<<URSEL)|0x06;
}

void SPI_MasterInit(void)
{
/* Set MOSI, chip select and SCK (clock) output, MISO - input */
DDRB |= (1<<PB7)|(0<<PB6)|(1<<PB5)|(1<<PB4);
PORTB |= 0b01000000; // подтягиваем пин входа

/* Enable SPI, Master, set clock rate fck/16 */
SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR0);                    //SPI Enable, SCK frequency = fosc/16

//SPCR |=(0x0C);
}

void SPI_MasterTransmit(unsigned char cData)
{
/* Start transmission */
SPDR = cData;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)));
}

void UART_RX_interrupt( void )
{  
     // отправляю данные  
     PORTB &= ~0b00010000;   // chip select в 0
     SPI_MasterTransmit(0b00000110);//wren
     SPI_MasterTransmit(0b00000010); //write
     SPI_MasterTransmit(0b00000011); //address
     SPI_MasterTransmit(0b00000011); //address
     SPI_MasterTransmit(0b01011100); //data
     SPI_MasterTransmit(0b00001111); //data
     SPI_MasterTransmit(0b11110000); //data
     PORTB |= 0b00010000;    // chip select в 1
     ////////////////////////////////////////////
    
    // считываю
    PORTB &= ~0b00010000;   // chip select в 0
    SPI_MasterTransmit(0b00000110); //wren
    SPI_MasterTransmit(0b00000011); //read
    SPI_MasterTransmit(0b00000011); //address
    SPI_MasterTransmit(0b00000011); //address
        
    //SPCR |=(0x0C);
    SPI_MasterTransmit(0xff);   // бросаем данные чтобы генерировались такты на sck
    readdata= SPDR;//            // считываю данные
    SPI_MasterTransmit(0xff);
    readdata1= SPDR;
    SPI_MasterTransmit(0xff);
    readdata2= SPDR;
    PORTB |= 0b00010000;            // chip select в 1
    /////////////////////////////////////////////////////////////////
    
     while(!( UCSRA & (1 << UDRE)));
        UDR = readdata;                    //передаю на PC по USART
        while(!( UCSRA & (1 << UDRE)));
        UDR = readdata1;
        while(!( UCSRA & (1 << UDRE)));
        UDR = readdata2;
        
    ToggleBit(DDRC,7);        //мигаю диодом
        
}  

void main(void)
{
    SPI_MasterInit();
    InitUART();
    UCSRB |= (1<<RXCIE); // enable UART interrupts
    
     while(1)
        {
                 SEI();    
                
        }

}
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.