SPIFI: различия между версиями

Материал из MIK32 микроконтроллер
Нет описания правки
Нет описания правки
Строка 77: Строка 77:


==== Запуск скрипта ====
==== Запуск скрипта ====
Сначала нужно собрать проект Blink чтобы появился файл firmware.hex. В PlatformIO сделать это можно нажав галочку как показано на рисунке 1.
[[Файл:Рисунок 1 - Сборка проекта.png|мини|Рисунок 1 - Сборка проекта]]
[[Файл:Рисунок 1 - Сборка проекта.png|мини|Рисунок 1 - Сборка проекта]]
Сначала нужно собрать проект Blink чтобы появился файл firmware.hex. В PlatformIO сделать это можно нажав галочку как показано на рисунке 1. Blink должен быть активным проектом.  
Blink должен быть активным проектом.  
Скрипт mcu32flash.py нужно поместить в папку, в которой хранится hex файл. По умолчанию путь к hex файлу проекта Blink "C:\Users\%USERNAME%\Documents\PlatformIO\Projects\Blink\.pio\build\mik32". Также сделать это можно, добавив файл в проводнике Visual Studio Code - рисунок 2.
Скрипт mcu32flash.py нужно поместить в папку, в которой хранится hex файл. По умолчанию путь к hex файлу проекта Blink "C:\Users\%USERNAME%\Documents\PlatformIO\Projects\Blink\.pio\build\mik32". Также сделать это можно, добавив файл в проводнике Visual Studio Code - рисунок 2.
[[Файл:Рисунок 1 - Размещение mcu32flash.png|мини|Рисунок 2 - Размещение mcu32flash.py|360x360пкс]]
[[Файл:Рисунок 1 - Размещение mcu32flash.png|мини|Рисунок 2 - Размещение mcu32flash.py|360x360пкс]]
Строка 93: Строка 94:
==== Отправка команд ====
==== Отправка команд ====
Для записи, чтения и стирания данных используются стандартные инструкции - рисунок 5.[[Файл:Рисунок 4 - Фрагмент набора инструкций.png|мини|Рисунок 5 - Фрагмент набора инструкций]]Перед записью следует произвести стирание сектора или блока, в который будет производиться запись. В данном примере проводится стирание всей микросхемы. Перед стиранием должна быть выполнена инструкция "включение записи" ("write enable"). Запись проводится с помощью инструкции "Программирование страниц", отправка которой будет разобрана далее.
Для записи, чтения и стирания данных используются стандартные инструкции - рисунок 5.[[Файл:Рисунок 4 - Фрагмент набора инструкций.png|мини|Рисунок 5 - Фрагмент набора инструкций]]Перед записью следует произвести стирание сектора или блока, в который будет производиться запись. В данном примере проводится стирание всей микросхемы. Перед стиранием должна быть выполнена инструкция "включение записи" ("write enable"). Запись проводится с помощью инструкции "Программирование страниц", отправка которой будет разобрана далее.
Для стирания чипа используется функция erase, которая оправляет инструкции "Включение записи", "Стирание микросхемы" и "Чтение регистра состояния - 1". Стирание микросхемы занимает несколько секунд, поэтому после отправки инструкции стирания читается регистр состояния 1 - рисунок 6. [[Файл:Рисунок 5 - Регистр состояния 1.png|мини|Рисунок 6 - Регистр состояния 1]]
Для стирания чипа используется функция erase, которая оправляет инструкции "Включение записи", "Стирание микросхемы" и "Чтение регистра состояния - 1". Стирание микросхемы занимает несколько секунд, поэтому после отправки инструкции стирания читается регистр состояния 1 - рисунок 6. [[Файл:Рисунок 5 - Регистр состояния 1.png|мини|Рисунок 6 - Регистр состояния 1]]
В регистре состояния есть бит 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 станет раным нулю.
В регистре состояния есть бит 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 станет раным нулю.

Версия от 17:15, 18 августа 2022

Контроллер 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 main.c

#include <mcu32_memory_map.h>
#include <pad_config.h>
#include <gpio.h>
#include <power_manager.h>
#include "common.h"

#define PIN_LED2 7 // LED2 управляется выводом PORT_2_7
#define PIN_button 6 // LED2 управляется выводом PORT_2_6

void initClock() {
	PM->CLK_APB_P_SET |=  PM_CLOCK_GPIO_2_M; // включение тактирования GPIO2
	PM->CLK_APB_M_SET |= PM_CLOCK_PAD_CONFIG_M | PM_CLOCK_WU_M | PM_CLOCK_PM_M; // включение тактирования блока для смены режима выводов
	for (volatile int i = 0; i < 10; i++) ;
}

void ledBlink() {
	GPIO_2->OUTPUT |= 1 << PIN_LED2;   //Установка значения вывода 7 порта 2 в высокий уровень
	xprintf("ON \n");
	for (volatile int i = 0; i < 100000; i++);
	GPIO_2->OUTPUT &= ~(1 << PIN_LED2); //Установка значения вывода 7 порта  в низкий уровень   
	xprintf("OFF \n");
	for (volatile int i = 0; i < 100000; i++);
}

void ledButton() {
	if(GPIO_2->SET == (1 << PIN_button))
	{
		GPIO_2->OUTPUT |= 1 << PIN_LED2;   //Установка значения вывода 7 порта 2 в высокий уровень
	}
	else
	{
		GPIO_2->OUTPUT &= ~(1 << PIN_LED2); //Установка значения вывода 7 порта  в низкий уровень
	}
}

void main() {
	initClock(); //включние тактирования GPIO2

	PAD_CONFIG->PORT_2_CFG |= (1 << (2 * PIN_LED2)); // Установка порта 2 в режим GPIO
	PAD_CONFIG->PORT_2_CFG |= (1 << (2 * PIN_button)); // Установка порта 2 в режим GPIO

	GPIO_2->DIRECTION_OUT = 1 << PIN_LED2; // Установка направления вывода  в выход
	GPIO_2->DIRECTION_IN = 1 << PIN_button; // Установка направления порта  в вход

	while (1) {
		ledBlink();
		//ledButton();
	}
}

Запуск скрипта

Сначала нужно собрать проект Blink чтобы появился файл firmware.hex. В PlatformIO сделать это можно нажав галочку как показано на рисунке 1.

Рисунок 1 - Сборка проекта

Blink должен быть активным проектом. Скрипт mcu32flash.py нужно поместить в папку, в которой хранится hex файл. По умолчанию путь к hex файлу проекта Blink "C:\Users\%USERNAME%\Documents\PlatformIO\Projects\Blink\.pio\build\mik32". Также сделать это можно, добавив файл в проводнике Visual Studio Code - рисунок 2.

Рисунок 2 - Размещение mcu32flash.py

После этого нажать правой кнопкой мыши по файлу и выбрать "Открыть во встроенном терминале" как на рисунке 3.

Рисунок 3 - Контекстное меню

Для запуска скрипта нужно в терминале написать "python.exe .\mcu32flash.py" и нажать "Enter" - рисунок 4.

Рисунок 4 - Терминал

Скрипт создаст файл "array.h" в папке, в которой расположен hex файл. В "array.h" находится массив "bin_data" с байтами, которые будут загружаться в чип памяти.

Загрузка программы в микросхему памяти

Полученный файл "array.h" следует переместить в папку "src" проекта SPIFI.

Инициализация SPIFI

Инициализация контроллера SPIFI проводится с помощью функции spifi_init, которая включает тактирование контроллера SPIFI на шине AHB.

Отправка команд

Для записи, чтения и стирания данных используются стандартные инструкции - рисунок 5.

Рисунок 5 - Фрагмент набора инструкций

Перед записью следует произвести стирание сектора или блока, в который будет производиться запись. В данном примере проводится стирание всей микросхемы. Перед стиранием должна быть выполнена инструкция "включение записи" ("write enable"). Запись проводится с помощью инструкции "Программирование страниц", отправка которой будет разобрана далее. Для стирания чипа используется функция erase, которая оправляет инструкции "Включение записи", "Стирание микросхемы" и "Чтение регистра состояния - 1". Стирание микросхемы занимает несколько секунд, поэтому после отправки инструкции стирания читается регистр состояния 1 - рисунок 6.

Рисунок 6 - Регистр состояния 1

В регистре состояния есть бит 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 main.c

#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)
    {

    }
}

Старт из внешней памяти с использованием контроллера SPIFI

Микросхема MCU32 имеет три различных варианта старта, определяемых состоянием выводов Boot0 и Boot1 в соответствии с таблицей 1.

Для старта из внешней памяти следует установить перемычки на плате так чтобы Boot0 = 0, а Boot1 = 1. Пример установки перемычек на плате DIP-MIK32-BB показан на рисунке 8.

Рисунок 8 - Распиновка DIP-MIK32-BB
Таблица 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.

Таблица 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 - ссылка