SPIFI: различия между версиями
Андрей (обсуждение | вклад) Нет описания правки |
Sh-sergey (обсуждение | вклад) Нет описания правки |
||
(не показано 19 промежуточных версий 2 участников) | |||
Строка 3: | Строка 3: | ||
Контроллер SPIFI c КЭШ обеспечивает работу с микросхемами FLASH – памяти через SPI интерфейс в одном из трех режимов: одноканальный; двухканальный; четырёхканальный. По умолчанию используется одноканальный режим, двухканальный и четырёхканальный режим включается через программное обеспечение. | Контроллер SPIFI c КЭШ обеспечивает работу с микросхемами FLASH – памяти через SPI интерфейс в одном из трех режимов: одноканальный; двухканальный; четырёхканальный. По умолчанию используется одноканальный режим, двухканальный и четырёхканальный режим включается через программное обеспечение. | ||
Микросхема внешней FLASH – памяти, подключаемая к блоку SPI Flash c КЭШ, должна иметь встроенный интерфейс SPI (Single, Dual, Quatro) и поддерживать набор команд управления, определенных в стандарте JEDEC. | Микросхема внешней FLASH – памяти, подключаемая к блоку SPI Flash c КЭШ, должна иметь встроенный интерфейс SPI (Single, Dual, Quatro) и поддерживать набор команд управления, определенных в стандарте JEDEC. | ||
Выводы SPIFI: | |||
Blink main.c <syntaxhighlight lang="c++" line="1"> | Port_2_0 - SPIFI_SCLK - Интерфейс SPIFI, тактовый сигнал - CLK (Serial Clock Input); | ||
Port_2_1 - SPIFI_CS - Интерфейс SPIFI, сигнал CS - CS (Chip Select Input); | |||
Port_2_2 - SPIFI_DATA_0 - Интерфейс SPIFI, шина данных, разряд 0 - DI (Data Input); | |||
Port_2_3 - SPIFI_DATA_1 - Интерфейс SPIFI, шина данных, разряд 1 - DO (Data Output); | |||
Port_2_4 - SPIFI_DATA_2 - Интерфейс SPIFI, шина данных, разряд 2 - WP (Write Protect Input); | |||
Port_2_5 - SPIFI_DATA_3 - Интерфейс SPIFI, шина данных, разряд 3 - HOLD (Hold Input). | |||
== Пример использования SPIFI == | |||
В данном примере используется микросхема памяти GSN2516Y (GS Nanotech) или W25Q64FV (Winbond). В микросхему памяти с помощью MIK32 по SPIFI загружается программа Blink, после чего микроконтроллер загружается из внешней памяти. Для этого требуется получить массив байтов из hex файла firmware.hex проекта Blink с помощью специального скрипта mcu32flash.py, который можно скачать по [https://github.com/MikronMIK32/mik32-examples/tree/main/Парсер%20hex ссылке]. | |||
Проект Blink - [https://github.com/MikronMIK32/mik32-examples/tree/main/Blink ссылка] | |||
Blink | |||
{{#spoiler:show=Показать main.c|hide=Скрыть main.c| | |||
<syntaxhighlight lang="c++" line="1"> | |||
#include <mcu32_memory_map.h> | #include <mcu32_memory_map.h> | ||
#include <pad_config.h> | #include <pad_config.h> | ||
Строка 59: | Строка 78: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
}} | |||
==== Запуск скрипта ==== | ==== Запуск скрипта ==== | ||
Скрипт mcu32flash.py нужно поместить в папку, в которой хранится hex файл. | Сначала нужно собрать проект Blink чтобы появился файл firmware.hex. В PlatformIO сделать это можно нажав галочку как показано на рисунке 1. | ||
[[Файл:Рисунок 1 - Размещение mcu32flash.png|мини|Рисунок | [[Файл:Рисунок 1 - Сборка проекта.png|мини|Рисунок 1 - Сборка проекта]] | ||
Blink должен быть активным проектом. | |||
После этого нажать правой кнопкой мыши по файлу и выбрать "Открыть во встроенном терминале" как на рисунке | Скрипт mcu32flash.py нужно поместить в папку, в которой хранится hex файл. По умолчанию путь к hex файлу проекта Blink "C:\Users\%USERNAME%\Documents\PlatformIO\Projects\Blink\.pio\build\mik32". Также сделать это можно, добавив файл в проводнике Visual Studio Code - рисунок 2. | ||
[[Файл:Рисунок 2 - | [[Файл:Рисунок 1 - Размещение mcu32flash.png|мини|Рисунок 2 - Размещение mcu32flash.py|360x360пкс]] | ||
После этого нажать правой кнопкой мыши по файлу и выбрать "Открыть во встроенном терминале" как на рисунке 3. | |||
Для запуска скрипта нужно в терминале написать "python.exe .\mcu32flash.py" и нажать "Enter" - рисунок | [[Файл:Рисунок 2 - Контекстное меню.png|мини|Рисунок 3 - Контекстное меню|241x241пкс]] | ||
Для запуска скрипта нужно в терминале написать "python.exe .\mcu32flash.py" и нажать "Enter" - рисунок 4.[[Файл:Рисунок 2 - Терминал.png|мини|Рисунок 4 - Терминал]]Скрипт создаст файл "array.h" в папке, в которой расположен hex файл. В "array.h" находится массив "bin_data" с байтами, которые будут загружаться в чип памяти. | |||
Скрипт создаст файл "array.h" в папке, в которой расположен hex файл. В "array.h" находится массив "bin_data" с байтами, которые будут загружаться в чип памяти. | |||
=== Загрузка программы в | === Загрузка программы в микросхему памяти === | ||
Полученный файл "array.h" следует переместить в папку "src" проекта SPIFI. | Полученный файл "array.h" следует переместить в папку "src" проекта SPIFI. | ||
Строка 78: | Строка 97: | ||
==== Отправка команд ==== | ==== Отправка команд ==== | ||
Для записи, чтения и стирания данных используются стандартные инструкции - рисунок 4. | Для записи, чтения и стирания данных используются стандартные инструкции - рисунок 5.[[Файл:Рисунок 4 - Фрагмент набора инструкций.png|мини|Рисунок 5 - Фрагмент набора инструкций]]Перед записью следует произвести стирание сектора или блока, в который будет производиться запись. В данном примере проводится стирание всей микросхемы. Перед стиранием должна быть выполнена инструкция "включение записи" ("write enable"). Запись проводится с помощью инструкции "Программирование страниц", отправка которой будет разобрана далее. | ||
Для стирания чипа используется функция erase, которая оправляет инструкции "Включение записи", "Стирание микросхемы" и "Чтение регистра состояния - 1". Стирание микросхемы занимает несколько секунд, поэтому после отправки инструкции стирания читается регистр состояния 1 - рисунок 6. [[Файл:Рисунок 5 - Регистр состояния 1.png|мини|Рисунок 6 - Регистр состояния 1]] | |||
Перед записью следует произвести стирание сектора или блока, в который будет производиться запись. В данном примере проводится стирание всей микросхемы. Перед стиранием должна быть выполнена инструкция "включение записи" ("write enable"). Запись проводится с помощью инструкции "Программирование страниц", отправка которой будет разобрана далее. | В регистре состояния есть бит BUSY, который устанавливается в состояние 1, когда устройство выполняет инструкцию Page Program, Quad Page Program, Sector Erase, Block Erase, Chip Erase, Write Status Register или Erase/Program Security Register. В течение этого времени устройство будет игнорировать дальнейшие инструкции, за исключением инструкций Read Status Register и Erase/Program Suspend. Когда инструкция по программированию, стиранию или записи состояния/защиты регистра будет завершена, бит BUSY будет установлен в состояние 0, указывающее, что устройство готово к дальнейшим инструкциям. После отправки "Стирание микросхемы" программа ожидает когда бит BUSY станет раным нулю. | ||
Для стирания чипа используется функция erase, которая оправляет инструкции "Включение записи", "Стирание микросхемы" и "Чтение регистра состояния - 1". Стирание микросхемы занимает несколько секунд, поэтому после отправки инструкции стирания читается регистр состояния 1 - рисунок 5. В регистре состояния есть бит BUSY, который устанавливается в состояние 1, когда устройство выполняет инструкцию Page Program, Quad Page Program, Sector Erase, Block Erase, Chip Erase, Write Status Register или Erase/Program Security Register. В течение этого времени устройство будет игнорировать дальнейшие инструкции, за исключением инструкций Read Status Register и Erase/Program Suspend. Когда инструкция по программированию, стиранию или записи состояния/защиты регистра будет завершена, бит BUSY будет установлен в состояние 0, указывающее, что устройство готово к дальнейшим инструкциям. После отправки "Стирание микросхемы" программа ожидает когда бит BUSY станет раным нулю. | |||
функция erase<syntaxhighlight lang="c++" line="1"> | функция erase<syntaxhighlight lang="c++" line="1"> | ||
Строка 94: | Строка 110: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
После стирания начинается запись страниц по 256 байт. Если количество байт массива bin_data не кратно 256, то в конце записывается оставшиеся байты массива. Запись производится с помощью функции write, которая отправляет инструкции "Включение записи", "Программирование страниц" и "Чтение регистра состояния - 1".<syntaxhighlight lang="c++"> | |||
void write(int address, char data[], int data_len); | |||
</syntaxhighlight>Функция принимает следующие аргументы: | |||
- int address - адрес, с которого начинается запись; | |||
- char data[] - массив данных; | |||
- int data_len - количество байт. Не может быть больше 256. | |||
Разберем отправку команд на примере инструкции "Программирование страниц" ("Page Program"). На рисунке 4 видно, что инструкция состоит из первого байта 0x02, трех байтов адреса и не менее одного байта данных, но не более 256. | |||
Инструкция "Программирование страниц" использует адрес, который нужно записать в регистр ADDR. С этого адреса начнется запись. | |||
Для отправки команды следует настроить биты OPCODE, FRAMEFORM и FIELDFORM в регистре CMD. | Для отправки команды следует настроить биты OPCODE, FRAMEFORM и FIELDFORM в регистре CMD. | ||
OPCODE - код инструкции. В данном случае | OPCODE - код инструкции. В данном случае 0x02. | ||
FRAMEFORM - Бит управления полями кода операции и адреса команды: | FRAMEFORM - Бит управления полями кода операции и адреса команды: | ||
Строка 111: | Строка 135: | ||
- 3 – код операции и два младших байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_2ADDR; | - 3 – код операции и два младших байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_2ADDR; | ||
- 4 – код операции и три младших байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR; | - 4 – код операции и три младших байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR; | ||
Строка 132: | Строка 156: | ||
Так как в примере используются стандартные инструкции, то FIELDFORM = 0 - SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL. Инструкция " | DOUT - Бит направления передачи данных: | ||
- 0 – чтение из флэш; | |||
- 1 – запись во флэш | |||
POLL - Бит должен быть установлен при выполнении команды, которая содержит входное поле данных и циклически запрашивает состояние бита входного потока битов из регистра статуса флэш-памяти. | |||
DATALEN - В тех случаях, когда бит POLL равен 0, это поле определяет количество байт данных при выполнении команды. Если это поле равно 0, то выполняемая команда не содержит данных | |||
Так как в примере используются стандартные инструкции, то FIELDFORM = 0 - SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL. | |||
Инструкция "Программирование страниц" требует 3 байта адреса, поэтому FRAMEFORM = 4 - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR. | |||
Направление передачи - запись. DOUT = 1. | |||
Режим поллинга не используется. POLL = 0. | |||
Количество байт зависит от аргумента функции byte_count. DATALEN = byte_count. | |||
После заполнения регистра CMD каждый байт массива bin_data записывается в регистр данных DATA. . Если на микросхему памяти отправлено более 256 байт данных, адресация вернется к началу страницы и перезапишет ранее отправленные данные. | |||
функция page_program<syntaxhighlight lang="c++" line="1"> | |||
void page_program(unsigned int ByteAddress, char data[], int byte_count) { | |||
if(byte_count > 256) | |||
{ | |||
xprintf("Количество байт больше 256\n"); | |||
} | |||
xprintf("Start page program\n"); | |||
//STAT:INTRQ | |||
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; | |||
//ADDRESS | |||
SPIFI_CONFIG->ADDR = ByteAddress; | |||
//IDATA | |||
SPIFI_CONFIG->IDATA = 0x00; | |||
//CLIMIT | |||
SPIFI_CONFIG->CLIMIT = 0x00000000; | |||
//CMD | |||
SPIFI_CONFIG->CMD = (PAGE_PROGRAM_COMMAND << SPIFI_CONFIG_CMD_OPCODE_S) | | |||
(SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR << SPIFI_CONFIG_CMD_FRAMEFORM_S) | | |||
(SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL << SPIFI_CONFIG_CMD_FIELDFORM_S) | | |||
(0 << SPIFI_CONFIG_CMD_INTLEN_S) | | |||
(1 << SPIFI_CONFIG_CMD_DOUT_S) | | |||
(0 << SPIFI_CONFIG_CMD_POLL_S) | | |||
(byte_count << SPIFI_CONFIG_CMD_DATALEN_S); | |||
for(int i = ByteAddress; i < (ByteAddress + byte_count); i++) | |||
{ | |||
SPIFI_CONFIG->DATA8 = data[i]; | |||
} | |||
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; | |||
} | |||
</syntaxhighlight>После записи страницы проводится чтение только что записанных байтов и сравнение с исходным массивом для исключения ошибок при записи. | |||
==== Результат выполнения программы ==== | |||
Результат выполнения программы можно видеть в COM-порте. В примере для передачи данных используется UART_0, который имеет следующие выводы: | |||
- Port_0_5 - UART0_rxd; | |||
- Port_0_6 - UART0_txd. | |||
Установлена скорость 9600 бод. На рисунке 7 изображен вывод программы. | |||
==== Полный код ==== | |||
[[Файл:Рисунок 7 - Вывод программы.jpg|мини|Рисунок 7 - Вывод программы]] | |||
Пример проекта - [https://github.com/MikronMIK32/mik32-examples/tree/main/SPIFI ссылка] | |||
{{#spoiler: | |||
SPIFI main.c<syntaxhighlight lang="c++" line="1"> | |||
#include "common.h" | |||
#include "spifi.h" | |||
#include "array.h" | |||
#define SREG1_BUSY 1 | |||
#define READ_SREG 1 | |||
#define READ_LEN 256 | |||
#define TIMEOUT 100000 | |||
#define CHIP_ERASE_COMMAND 0xC7 | |||
#define WRITE_ENABLE_COMMAND 0x06 | |||
#define WRITE_DISABLE_COMMAND 0x04 | |||
#define MEM_CONFIG_COMMAND 0x61 | |||
#define MEM_CONFIG_VALUE 0x7F | |||
#define READ_DATA_COMMAND 0x03 | |||
#define READ_SREG_COMMAND 0x05 | |||
#define PAGE_PROGRAM_COMMAND 0x02 | |||
void spifi_init() | |||
{ | |||
PM->CLK_AHB_SET |= PM_CLOCK_SPIFI_M; | |||
/* | |||
* | |||
* STAT - регистр статуса | |||
* INTRQ - Запись «1» в бит сбрасывает запрос на прерывание от контроллера SPIFI | |||
* RESET - Бит предназначен для того, чтобы прервать текущую команду периферийного режима или режима памяти | |||
* | |||
* ADDR - Исполнительный адрес команды | |||
* | |||
* IDATA - регистр промежуточных данных | |||
* | |||
* CLIMIT - Верхний предел кэшируемой памяти | |||
* | |||
* CTRL - регистр управления | |||
* INTEN - Бит разрешения прерывания при завершении выполнения команды (если этот бит равен «1», то прерывание разрешено) | |||
* | |||
*/ | |||
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M | | |||
SPIFI_CONFIG_STAT_RESET_M; | |||
SPIFI_CONFIG->ADDR = 0x00; | |||
SPIFI_CONFIG->IDATA = 0x00; | |||
SPIFI_CONFIG->CLIMIT = 0x00000000; | |||
//SPIFI_CONFIG -> CTRL |= SPIFI_CONFIG_CTRL_SCK_DIV(3); // должно быть 2Мгц | |||
// SPIFI_CONFIG->CTRL |= SPIFI_CONFIG_CTRL_INTEN_M; | |||
for (int i = 0; i < 10000000; i++); | |||
xprintf("Finish init SPIFI\n"); | |||
} | |||
int SPIFI_WaitIntrqTimeout(SPIFI_CONFIG_TypeDef* spifi, uint32_t timeout) | |||
{ | |||
while (timeout-- > 0) | |||
{ | |||
if((spifi->STAT & SPIFI_CONFIG_STAT_INTRQ_M) != 0) | |||
{ | |||
return 1; | |||
} | |||
} | |||
return 0; | |||
} | |||
void write_enable() | |||
{ | |||
/* | |||
* | |||
* CMD код операции | |||
* OPCODE - код операции | |||
* FRAMEFORM - Бит управления полями кода операции и адреса команды: | |||
* «0» – резерв; | |||
* «1» – выдается только код операции, адреса нет; (SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_NOADDR) | |||
* «2» – код операции и младший байт адреса; | |||
* «3» – код операции и два младших байта адреса; | |||
* «4» – код операции и три младших байта адреса; | |||
* «5» – код операции и 4 байта адреса; | |||
* «6» – нет кода операции, три младших байта адре-са; | |||
* «7» – нет кода операции, 4 байта адреса | |||
* | |||
* FIELDFORM - Формат вывода полей команды: | |||
* «0» – все поля выводятся в последовательном режиме; (SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL) | |||
* «1» – данные выводятся в четырех или двух битовом режиме, а остальные поля в последовательном режиме; | |||
* «2» – код операции выводится в последовательном режиме, а остальные в четырех или двух битовом; | |||
* «3» – все поля в четырех или двух битовом режиме | |||
* | |||
*/ | |||
xprintf("Start write en\n"); | |||
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; | |||
SPIFI_CONFIG->CMD = (WRITE_ENABLE_COMMAND << SPIFI_CONFIG_CMD_OPCODE_S) | | |||
(SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_NOADDR << SPIFI_CONFIG_CMD_FRAMEFORM_S) | | |||
(SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL << SPIFI_CONFIG_CMD_FIELDFORM_S); | |||
if(SPIFI_WaitIntrqTimeout(SPIFI_CONFIG, TIMEOUT) == 0) | |||
{ | |||
TEST_ERROR("Timeout executing write enable command"); | |||
return; | |||
} | |||
} | |||
uint8_t read_sreg_1() | |||
{ | |||
uint8_t read_sreg = 0; | |||
/* | |||
* | |||
* CMD код операции | |||
* OPCODE - код операции | |||
* FRAMEFORM - Бит управления полями кода операции и адреса команды: | |||
* «0» – резерв; | |||
* «1» – выдается только код операции, адреса нет; (SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_NOADDR) | |||
* «2» – код операции и младший байт адреса; | |||
* «3» – код операции и два младших байта адреса; | |||
* «4» – код операции и три младших байта адреса; | |||
* «5» – код операции и 4 байта адреса; | |||
* «6» – нет кода операции, три младших байта адре-са; | |||
* «7» – нет кода операции, 4 байта адреса | |||
* | |||
* FIELDFORM - Формат вывода полей команды: | |||
* «0» – все поля выводятся в последовательном режиме; (SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL) | |||
* «1» – данные выводятся в четырех или двух битовом режиме, а остальные поля в последовательном режиме; | |||
* «2» – код операции выводится в последовательном режиме, а остальные в четырех или двух битовом; | |||
* «3» – все поля в четырех или двух битовом режиме | |||
* | |||
*/ | |||
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; | |||
SPIFI_CONFIG->CMD = (READ_SREG_COMMAND << SPIFI_CONFIG_CMD_OPCODE_S) | | |||
(SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_NOADDR << SPIFI_CONFIG_CMD_FRAMEFORM_S) | | |||
(SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL << SPIFI_CONFIG_CMD_FIELDFORM_S) | | |||
(READ_SREG << SPIFI_CONFIG_CMD_DATALEN_S); | |||
if(SPIFI_WaitIntrqTimeout(SPIFI_CONFIG, TIMEOUT) == 0) | |||
{ | |||
TEST_ERROR("Timeout executing read sreg1 command"); | |||
return 0; | |||
} | |||
read_sreg = SPIFI_CONFIG->DATA8; | |||
return read_sreg; | |||
} | |||
void wait_busy() | |||
{ | |||
xprintf("Wait\n"); | |||
uint8_t sreg; | |||
while (1) | |||
{ | |||
sreg = read_sreg_1(); | |||
if(!(sreg & SREG1_BUSY)) | |||
break; | |||
} | |||
} | |||
void chip_erase() | |||
{ | |||
xprintf("Start erase\n"); | |||
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; | |||
SPIFI_CONFIG->CMD = (CHIP_ERASE_COMMAND << SPIFI_CONFIG_CMD_OPCODE_S) | | |||
(SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_NOADDR << SPIFI_CONFIG_CMD_FRAMEFORM_S) | | |||
(SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL << SPIFI_CONFIG_CMD_FIELDFORM_S); | |||
if(SPIFI_WaitIntrqTimeout(SPIFI_CONFIG, TIMEOUT) == 0) | |||
{ | |||
TEST_ERROR("Timeout executing chip erase command"); | |||
return; | |||
} | |||
} | |||
void read_data(unsigned int address, int byte_count) | |||
{ | |||
xprintf("read data\n"); | |||
char read_data[byte_count]; | |||
SPIFI_CONFIG->ADDR = address; | |||
/* | |||
* | |||
* CMD код операции | |||
* OPCODE - код операции | |||
* FRAMEFORM - Бит управления полями кода операции и адреса команды: | |||
* «0» – резерв; | |||
* «1» – выдается только код операции, адреса нет; (SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_NOADDR) | |||
* «2» – код операции и младший байт адреса; | |||
* «3» – код операции и два младших байта адреса; | |||
* «4» – код операции и три младших байта адреса; | |||
* «5» – код операции и 4 байта адреса; | |||
* «6» – нет кода операции, три младших байта адре-са; | |||
* «7» – нет кода операции, 4 байта адреса | |||
* | |||
* FIELDFORM - Формат вывода полей команды: | |||
* «0» – все поля выводятся в последовательном режиме; (SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL) | |||
* «1» – данные выводятся в четырех или двух битовом режиме, а остальные поля в последовательном режиме; | |||
* «2» – код операции выводится в последовательном режиме, а остальные в четырех или двух битовом; | |||
* «3» – все поля в четырех или двух битовом режиме | |||
* | |||
*/ | |||
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; | |||
SPIFI_CONFIG->CMD = (READ_DATA_COMMAND << SPIFI_CONFIG_CMD_OPCODE_S) | | |||
(SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR << SPIFI_CONFIG_CMD_FRAMEFORM_S) | | |||
(SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL << SPIFI_CONFIG_CMD_FIELDFORM_S) | | |||
(byte_count << SPIFI_CONFIG_CMD_DATALEN_S); | |||
// if(SPIFI_WaitIntrqTimeout(SPIFI_CONFIG, TIMEOUT) == 0) | |||
// { | |||
// TEST_ERROR("Timeout executing read data command"); | |||
// return; | |||
// } | |||
for (int i = 0; i < byte_count; i++) | |||
{ | |||
read_data[i] = SPIFI_CONFIG->DATA8; | |||
//xprintf("DATA[%d] = 0x%02x\n", address +i, read_data); | |||
} | |||
for (int i = 0; i < byte_count; i++) | |||
{ | |||
if(read_data[i] != bin_data[address + i]) | |||
{ | |||
xprintf("DATA[%d] = 0x%02x - ошибка\n", address + i, read_data[i]); | |||
} | |||
} | |||
} | |||
void page_program(unsigned int ByteAddress, char data[], int byte_count) { | |||
if(byte_count > 256) | |||
{ | |||
xprintf("Количество байт больше 256\n"); | |||
} | |||
xprintf("Start page program\n"); | |||
//STAT:INTRQ | |||
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; | |||
//ADDRESS | |||
SPIFI_CONFIG->ADDR = ByteAddress; | |||
//IDATA | |||
SPIFI_CONFIG->IDATA = 0x00; | |||
//CLIMIT | |||
SPIFI_CONFIG->CLIMIT = 0x00000000; | |||
//CMD | |||
SPIFI_CONFIG->CMD = (PAGE_PROGRAM_COMMAND << SPIFI_CONFIG_CMD_OPCODE_S) | | |||
(SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR << SPIFI_CONFIG_CMD_FRAMEFORM_S) | | |||
(SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL << SPIFI_CONFIG_CMD_FIELDFORM_S) | | |||
(0 << SPIFI_CONFIG_CMD_INTLEN_S) | | |||
(1 << SPIFI_CONFIG_CMD_DOUT_S) | | |||
(0 << SPIFI_CONFIG_CMD_POLL_S) | | |||
(byte_count << SPIFI_CONFIG_CMD_DATALEN_S); | |||
for(int i = ByteAddress; i < (ByteAddress + byte_count); i++) | |||
{ | |||
SPIFI_CONFIG->DATA8 = data[i]; | |||
} | |||
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M; | |||
} | |||
void erase() | |||
{ | |||
write_enable(); | |||
chip_erase(); | |||
wait_busy(); | |||
} | |||
void write(int address, char data[], int data_len) | |||
{ | |||
if(data_len > 256) | |||
{ | |||
xprintf("Количество байт больше 256\n"); | |||
return; | |||
} | |||
write_enable(); | |||
page_program(address, data, data_len); | |||
wait_busy(); | |||
xprintf("written\n"); | |||
} | |||
int main() | |||
{ | |||
spifi_init(); | |||
erase(); | |||
int bin_data_len = sizeof(bin_data); | |||
xprintf("bin_data_len = %d\n", bin_data_len); | |||
int address = 0; | |||
for(address = 0; address < bin_data_len; address += 256) | |||
{ | |||
if(address + 256 > bin_data_len) | |||
{ | |||
break; | |||
} | |||
xprintf("address = %d\n", address); | |||
write(address, bin_data, 256); | |||
read_data(address, 256); | |||
} | |||
if((sizeof(bin_data) % 256) != 0) | |||
{ | |||
xprintf("address = %d, +%d[%d]\n", address, bin_data_len - address-1, address + bin_data_len - address-1); | |||
write(address, bin_data, bin_data_len - address); | |||
read_data(address, bin_data_len - address); | |||
} | |||
xprintf("end\n"); | |||
while (1) | |||
{ | |||
} | |||
} | |||
</syntaxhighlight> | |||
}} | |||
===Старт из внешней памяти с использованием контроллера SPIFI=== | |||
Микросхема MCU32 имеет три различных варианта старта, определяемых состоянием выводов Boot0 и Boot1 в соответствии с таблицей 1. | |||
Для старта из внешней памяти следует установить перемычки на плате так чтобы Boot0 = 0, а Boot1 = 1. Пример установки перемычек на плате DIP-MIK32-BB показан на рисунке 8. | |||
[[Файл:Рисунок 7 - Распиновка DIP-MIK32-BB.png|мини|Рисунок 8 - Распиновка DIP-MIK32-BB]] | |||
{| class="wikitable" | |||
|+Таблица 1 - Варианты старта | |||
!Boot0 | |||
!Boot1 | |||
!Режим | |||
|- | |||
!0 | |||
!0 | |||
!Старт из встроенной памяти EEPROM | |||
|- | |||
!0 | |||
!1 | |||
!Старт из внешней памяти с использованием контроллера SPIFI | |||
|- | |||
!1 | |||
!0 | |||
!Старт из системного ОЗУ | |||
|- | |||
!1 | |||
!1 | |||
!Зарезервировано | |||
|} | |||
Загрузка из внешней памяти: контроллер SPIFI отображается в загрузочную область (0x0000_0000), но по прежнему остается доступной также по адресу 0x8000_0000. | |||
====Результат==== | |||
После старта должен мигать светодиод на выводе Port_2_7. | |||
В COM-порте можно видеть вывод программы Blink. В примере для передачи данных используется UART_0. Установлена скорость 9600 бод. | |||
Программа Blink из внешней памяти выполняется медленнее чем из EEPROM или RAM. Сравнение времени выполнения программы Blink и инструкций приведены в таблице 2. | |||
{| class="wikitable" | |||
|+Таблица 2 - Время выполнения | |||
! colspan="4" |Blink | |||
|- | |||
! | |||
!ram, мс | |||
!eeprom, мс | |||
!GSN2516Y, мс | |||
|- | |||
! Выполнение программы | |||
!109,37 | |||
!249,99 | |||
!4384,99 | |||
|- | |||
! colspan="4" |Делитель частоты SPIFI = SCK_DIV(3), 2 МГц | |||
|- | |||
!Write enable + стирание чипа | |||
!6076,82 | |||
!6082,03 | |||
!- | |||
|- | |||
!Write enable + запись страницы | |||
(256 Байт) | |||
!41,57 | |||
!41,67 | |||
!- | |||
|- | |||
!чтение страницы | |||
(256 Байт) | |||
!1,18 | |||
!1,39 | |||
!- | |||
|- | |||
! colspan="4" |Делитель частоты SPIFI = SCK_DIV(0), 16 МГц | |||
|- | |||
!Write enable + стирание чипа | |||
! 6108,26 | |||
!6075,40 | |||
!- | |||
|- | |||
!Write enable + запись страницы | |||
(256 Байт) | |||
!40,73 | |||
!40,83 | |||
!- | |||
|- | |||
!чтение страницы | |||
(256 Байт) | |||
!0,29 | |||
!0,60 | |||
!- | |||
|} | |||
===Ссылки=== | |||
Проекты были выполнены в среде PlatformIO. | |||
Скрипт mcu32flash.py - [https://github.com/MikronMIK32/mik32-examples/tree/main/Парсер%20hex ссылка] | |||
Проект Blink - [https://github.com/MikronMIK32/mik32-examples/tree/main/Blink ссылка] | |||
Проект SPIFI - [https://github.com/MikronMIK32/mik32-examples/tree/main/SPIFI ссылка] |
Текущая версия от 19:25, 27 марта 2023
Контроллер SPIFI c КЭШ предназначен для организации взаимодействия микропроцессорного ядра с микросхемой внешней FLASH-памяти. Это позволяет обеспечить исполнение кода программы, записанного в микросхеме флеш-памяти, а также при необходимости чтение и запись произвольных данных во внешнюю флэш-память в процессе выполнения программы.
Контроллер SPIFI c КЭШ обеспечивает работу с микросхемами FLASH – памяти через SPI интерфейс в одном из трех режимов: одноканальный; двухканальный; четырёхканальный. По умолчанию используется одноканальный режим, двухканальный и четырёхканальный режим включается через программное обеспечение.
Микросхема внешней FLASH – памяти, подключаемая к блоку SPI Flash c КЭШ, должна иметь встроенный интерфейс SPI (Single, Dual, Quatro) и поддерживать набор команд управления, определенных в стандарте JEDEC.
Выводы SPIFI:
Port_2_0 - SPIFI_SCLK - Интерфейс SPIFI, тактовый сигнал - CLK (Serial Clock Input);
Port_2_1 - SPIFI_CS - Интерфейс SPIFI, сигнал CS - CS (Chip Select Input);
Port_2_2 - SPIFI_DATA_0 - Интерфейс SPIFI, шина данных, разряд 0 - DI (Data Input);
Port_2_3 - SPIFI_DATA_1 - Интерфейс SPIFI, шина данных, разряд 1 - DO (Data Output);
Port_2_4 - SPIFI_DATA_2 - Интерфейс SPIFI, шина данных, разряд 2 - WP (Write Protect Input);
Port_2_5 - SPIFI_DATA_3 - Интерфейс SPIFI, шина данных, разряд 3 - HOLD (Hold Input).
Пример использования SPIFI
В данном примере используется микросхема памяти GSN2516Y (GS Nanotech) или W25Q64FV (Winbond). В микросхему памяти с помощью MIK32 по SPIFI загружается программа Blink, после чего микроконтроллер загружается из внешней памяти. Для этого требуется получить массив байтов из hex файла firmware.hex проекта Blink с помощью специального скрипта mcu32flash.py, который можно скачать по ссылке.
Проект Blink - ссылка
Blink
Запуск скрипта
Сначала нужно собрать проект Blink чтобы появился файл firmware.hex. В PlatformIO сделать это можно нажав галочку как показано на рисунке 1.
Blink должен быть активным проектом. Скрипт mcu32flash.py нужно поместить в папку, в которой хранится hex файл. По умолчанию путь к hex файлу проекта Blink "C:\Users\%USERNAME%\Documents\PlatformIO\Projects\Blink\.pio\build\mik32". Также сделать это можно, добавив файл в проводнике Visual Studio Code - рисунок 2.
После этого нажать правой кнопкой мыши по файлу и выбрать "Открыть во встроенном терминале" как на рисунке 3.
Для запуска скрипта нужно в терминале написать "python.exe .\mcu32flash.py" и нажать "Enter" - рисунок 4.
Скрипт создаст файл "array.h" в папке, в которой расположен hex файл. В "array.h" находится массив "bin_data" с байтами, которые будут загружаться в чип памяти.
Загрузка программы в микросхему памяти
Полученный файл "array.h" следует переместить в папку "src" проекта SPIFI.
Инициализация SPIFI
Инициализация контроллера SPIFI проводится с помощью функции spifi_init, которая включает тактирование контроллера SPIFI на шине AHB.
Отправка команд
Для записи, чтения и стирания данных используются стандартные инструкции - рисунок 5.
Перед записью следует произвести стирание сектора или блока, в который будет производиться запись. В данном примере проводится стирание всей микросхемы. Перед стиранием должна быть выполнена инструкция "включение записи" ("write enable"). Запись проводится с помощью инструкции "Программирование страниц", отправка которой будет разобрана далее. Для стирания чипа используется функция erase, которая оправляет инструкции "Включение записи", "Стирание микросхемы" и "Чтение регистра состояния - 1". Стирание микросхемы занимает несколько секунд, поэтому после отправки инструкции стирания читается регистр состояния 1 - рисунок 6.
В регистре состояния есть бит BUSY, который устанавливается в состояние 1, когда устройство выполняет инструкцию Page Program, Quad Page Program, Sector Erase, Block Erase, Chip Erase, Write Status Register или Erase/Program Security Register. В течение этого времени устройство будет игнорировать дальнейшие инструкции, за исключением инструкций Read Status Register и Erase/Program Suspend. Когда инструкция по программированию, стиранию или записи состояния/защиты регистра будет завершена, бит BUSY будет установлен в состояние 0, указывающее, что устройство готово к дальнейшим инструкциям. После отправки "Стирание микросхемы" программа ожидает когда бит BUSY станет раным нулю.
функция erase
void erase()
{
write_enable();
chip_erase();
wait_busy();
}
После стирания начинается запись страниц по 256 байт. Если количество байт массива bin_data не кратно 256, то в конце записывается оставшиеся байты массива. Запись производится с помощью функции write, которая отправляет инструкции "Включение записи", "Программирование страниц" и "Чтение регистра состояния - 1".
void write(int address, char data[], int data_len);
Функция принимает следующие аргументы:
- int address - адрес, с которого начинается запись;
- char data[] - массив данных;
- int data_len - количество байт. Не может быть больше 256.
Разберем отправку команд на примере инструкции "Программирование страниц" ("Page Program"). На рисунке 4 видно, что инструкция состоит из первого байта 0x02, трех байтов адреса и не менее одного байта данных, но не более 256.
Инструкция "Программирование страниц" использует адрес, который нужно записать в регистр ADDR. С этого адреса начнется запись.
Для отправки команды следует настроить биты OPCODE, FRAMEFORM и FIELDFORM в регистре CMD.
OPCODE - код инструкции. В данном случае 0x02.
FRAMEFORM - Бит управления полями кода операции и адреса команды:
- 1 – выдается только код операции, адреса нет - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_NOADDR;
- 2 – код операции и младший байт адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_1ADDR;
- 3 – код операции и два младших байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_2ADDR;
- 4 – код операции и три младших байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR;
- 5 – код операции и 4 байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_4ADDR;
- 6 – нет кода операции, три младших байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_NOOPCODE_3ADDR;
- 7 – нет кода операции, 4 байта адреса - SPIFI_CONFIG_CMD_FRAMEFORM_NOOPCODE_3ADDR.
FIELDFORM - Формат вывода полей команды:
- 0 – все поля выводятся в последовательном режиме - SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL;
- 1 – данные выводятся в четырех или двух битовом режиме, а остальные поля в последовательном режиме - SPIFI_CONFIG_CMD_FIELDFORM_DATA_PARALLEL;
- 2 – код операции выводится в последовательном режиме, а остальные в четырех или двух битовом - SPIFI_CONFIG_CMD_FIELDFORM_OPCODE_SERIAL;
- 3 – все поля в четырех или двух битовом режиме - SPIFI_CONFIG_CMD_FIELDFORM_ALL_PARALLEL.
DOUT - Бит направления передачи данных:
- 0 – чтение из флэш;
- 1 – запись во флэш
POLL - Бит должен быть установлен при выполнении команды, которая содержит входное поле данных и циклически запрашивает состояние бита входного потока битов из регистра статуса флэш-памяти.
DATALEN - В тех случаях, когда бит POLL равен 0, это поле определяет количество байт данных при выполнении команды. Если это поле равно 0, то выполняемая команда не содержит данных
Так как в примере используются стандартные инструкции, то FIELDFORM = 0 - SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL.
Инструкция "Программирование страниц" требует 3 байта адреса, поэтому FRAMEFORM = 4 - SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR.
Направление передачи - запись. DOUT = 1.
Режим поллинга не используется. POLL = 0.
Количество байт зависит от аргумента функции byte_count. DATALEN = byte_count.
После заполнения регистра CMD каждый байт массива bin_data записывается в регистр данных DATA. . Если на микросхему памяти отправлено более 256 байт данных, адресация вернется к началу страницы и перезапишет ранее отправленные данные.
функция page_program
void page_program(unsigned int ByteAddress, char data[], int byte_count) {
if(byte_count > 256)
{
xprintf("Количество байт больше 256\n");
}
xprintf("Start page program\n");
//STAT:INTRQ
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M;
//ADDRESS
SPIFI_CONFIG->ADDR = ByteAddress;
//IDATA
SPIFI_CONFIG->IDATA = 0x00;
//CLIMIT
SPIFI_CONFIG->CLIMIT = 0x00000000;
//CMD
SPIFI_CONFIG->CMD = (PAGE_PROGRAM_COMMAND << SPIFI_CONFIG_CMD_OPCODE_S) |
(SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR << SPIFI_CONFIG_CMD_FRAMEFORM_S) |
(SPIFI_CONFIG_CMD_FIELDFORM_ALL_SERIAL << SPIFI_CONFIG_CMD_FIELDFORM_S) |
(0 << SPIFI_CONFIG_CMD_INTLEN_S) |
(1 << SPIFI_CONFIG_CMD_DOUT_S) |
(0 << SPIFI_CONFIG_CMD_POLL_S) |
(byte_count << SPIFI_CONFIG_CMD_DATALEN_S);
for(int i = ByteAddress; i < (ByteAddress + byte_count); i++)
{
SPIFI_CONFIG->DATA8 = data[i];
}
SPIFI_CONFIG->STAT |= SPIFI_CONFIG_STAT_INTRQ_M;
}
После записи страницы проводится чтение только что записанных байтов и сравнение с исходным массивом для исключения ошибок при записи.
Результат выполнения программы
Результат выполнения программы можно видеть в COM-порте. В примере для передачи данных используется UART_0, который имеет следующие выводы:
- Port_0_5 - UART0_rxd;
- Port_0_6 - UART0_txd.
Установлена скорость 9600 бод. На рисунке 7 изображен вывод программы.
Полный код
Пример проекта - ссылка
Старт из внешней памяти с использованием контроллера SPIFI
Микросхема MCU32 имеет три различных варианта старта, определяемых состоянием выводов Boot0 и Boot1 в соответствии с таблицей 1.
Для старта из внешней памяти следует установить перемычки на плате так чтобы Boot0 = 0, а Boot1 = 1. Пример установки перемычек на плате DIP-MIK32-BB показан на рисунке 8.
Boot0 | Boot1 | Режим |
---|---|---|
0 | 0 | Старт из встроенной памяти EEPROM |
0 | 1 | Старт из внешней памяти с использованием контроллера SPIFI |
1 | 0 | Старт из системного ОЗУ |
1 | 1 | Зарезервировано |
Загрузка из внешней памяти: контроллер SPIFI отображается в загрузочную область (0x0000_0000), но по прежнему остается доступной также по адресу 0x8000_0000.
Результат
После старта должен мигать светодиод на выводе Port_2_7.
В COM-порте можно видеть вывод программы Blink. В примере для передачи данных используется UART_0. Установлена скорость 9600 бод.
Программа Blink из внешней памяти выполняется медленнее чем из EEPROM или RAM. Сравнение времени выполнения программы Blink и инструкций приведены в таблице 2.
Blink | |||
---|---|---|---|
ram, мс | eeprom, мс | GSN2516Y, мс | |
Выполнение программы | 109,37 | 249,99 | 4384,99 |
Делитель частоты SPIFI = SCK_DIV(3), 2 МГц | |||
Write enable + стирание чипа | 6076,82 | 6082,03 | - |
Write enable + запись страницы
(256 Байт) |
41,57 | 41,67 | - |
чтение страницы
(256 Байт) |
1,18 | 1,39 | - |
Делитель частоты SPIFI = SCK_DIV(0), 16 МГц | |||
Write enable + стирание чипа | 6108,26 | 6075,40 | - |
Write enable + запись страницы
(256 Байт) |
40,73 | 40,83 | - |
чтение страницы
(256 Байт) |
0,29 | 0,60 | - |
Ссылки
Проекты были выполнены в среде PlatformIO.
Скрипт mcu32flash.py - ссылка
Проект Blink - ссылка
Проект SPIFI - ссылка