Compare commits

...

19 Commits
r0.0.1 ... main

Author SHA1 Message Date
5012c9a0f0 v0.2.0
- добавлен сброс бита CMP в статус-регистре 2 flash-памяти перед переходом в основную программу
- добавлена проверка успешности записи страницы во flash-память
- добавлена команда на получение версии начального загрузчика
Co-authored-by: KLASSENTS <klassen@elron.tech>
Co-committed-by: KLASSENTS <klassen@elron.tech>
2025-07-24 06:52:37 +03:00
85b74eb004 исправила косяк 2024-10-07 16:52:56 +07:00
c4e8171e25 Merge branch 'main' of https://git.lrnrpst.ru/Elron_dev/elbear_fw_bootloader 2024-10-07 16:00:43 +07:00
32608935ec добавлен расчет контрольной суммы при получении новой строки. сли crc не сходится - выход из загрузчика 2024-10-07 16:00:14 +07:00
a10a08d898 Merged with main 2024-09-28 06:47:37 +03:00
Ogneyar
aff7a7ebce снятие защиты от записи 2024-09-27 14:01:45 +03:00
2452db9096 Merged with main 2024-09-21 05:07:31 +03:00
95e86f9ead Так будет верно 2024-09-20 12:40:26 +03:00
eb7ef97139 оставим немного места для пользовательского eeprom 2024-09-10 20:40:56 +07:00
0140ff7dfc ARD-84 (#1)
- Притяжка к питанию на Rx
- Новая команда для full erase
- При получении "мусорных" байтов не реагируем и отключаемся по таймауту
2024-09-10 11:43:01 +03:00
bb5a94e9ab
обработка строк hex менее 16 байт
исправлен баг с загрузкой hex файла, в котором не все Data-записи по 16 байт
2024-08-19 13:05:50 +07:00
64825e628e
уменьшение тактов ожидание CS
"Уменьшение количества дополнительных тактов задержки сигнала высокого уровня SPIFI_CS между операциями чтения из внешней памяти с 15 по умолчанию (итого 16,5) до 0 (итого 1,5)."

https://vk.com/mik32_amur?w=wall-226627855_63%2Fall

Проверено, работает
2024-08-16 17:55:04 +07:00
5b077eb2b6
Merge pull request #3 from spam-receiver/main
XIP-режим
2024-08-16 17:44:45 +07:00
spam-receiver
643671674b
Замена "mcu32" на "mik32" 2024-07-23 03:48:34 +03:00
spam-receiver
e1f09ff8b6
Выбор режима QSPI или QPI 2024-07-21 05:05:51 +03:00
spam-receiver
c3ac6fe729
XIP-режим
Работа c внешней флеш-памятью через SPIFI без передачи команд (XIP-режим — execute-in-place). Передаётся только адрес с тремя промежуточными байтами, первый из которых "0x20". Таким образом достигнута максимльная производительность с флеш-памятью типа W25Q64JV. Быстрее могут работать версии с поддержкой режима QPI, типа W25Q64FV, потому что у них в таком режиме один промежуточный байт, а не три.
2024-07-19 19:48:04 +03:00
0338620acb
Merge pull request #2 from spam-receiver/main
QE=1 если QE==0
2024-07-19 15:51:01 +07:00
spam-receiver
355ecdfc61
Update bootloader.c 2024-07-17 01:52:35 +03:00
spam-receiver
0d28ae7d78
QE=1 если QE==0
Не перезаписывать энергонезависимую ячейку QE, если там уже записана «1».
2024-07-17 01:51:35 +03:00
3 changed files with 235 additions and 99 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch
.vscode/

View File

@ -14,3 +14,4 @@ platform = MIK32
board = mik32v2 board = mik32v2
framework = framework-mik32v2-sdk framework = framework-mik32v2-sdk
board_debug.ldscript = eeprom board_debug.ldscript = eeprom
board_upload.maximum_size=5000

View File

@ -4,18 +4,24 @@
#include "power_manager.h" #include "power_manager.h"
#include "uart.h" #include "uart.h"
#include "pad_config.h" #include "pad_config.h"
#include "mcu32_memory_map.h" #include "mik32_memory_map.h"
#include "riscv_csr_encoding.h" #include "riscv_csr_encoding.h"
#include "csr.h" #include "csr.h"
#include "string.h" #include "string.h"
// версия начального загрузчика
#define MAJOR_VER 0
#define MINOR_VER 2
#define BUGFIX_VER 0
#define JALR_TO_SPIFI() \ #define JALR_TO_SPIFI() \
asm volatile( "la ra, 0x80000000\n\t" \ asm volatile( "la ra, 0x80000000\n\t" \
"jalr ra" \ "jalr ra" \
); );
#define CHIP_MODE 1 /* Режим работы МФП (SPIFI Memory Mode): 0 = QSPI или 1 = QPI */
#define ACK 0x0F /* Подтверждение */ #define ACK 0x0F /* Подтверждение */
#define NACK 0xF0 /* Нет подтверждения */ #define NACK 0xF0 /* Нет подтверждения */
@ -23,11 +29,13 @@
#define TIMEOUT_VALUE 1000000 /* Время ожидания загрузчика до прыжка по умолчанию в RAM 1000000 */ #define TIMEOUT_VALUE 1000000 /* Время ожидания загрузчика до прыжка по умолчанию в RAM 1000000 */
/* Виды команд */ /* Виды команд */
#define FULL_ERASE_CMD_LEN 4
typedef enum typedef enum
{ {
PACKAGE_SIZE = 0x30, /* Команда размера пакета */ PACKAGE_SIZE = 0x30, /* Команда размера пакета */
SEND_PACKAGE = 0x60, /* Команда отправить пакет */ SEND_PACKAGE = 0x60, /* Команда отправить пакет */
FULL_ERASE = 0xFE /* Команда стирания spifi*/ VERSION = 0x90, /* Команда получения версии начального загрузчика */
FULL_ERASE = 0xBADC0FEE /* Команда стирания spifi */
} BotloaderComand; } BotloaderComand;
typedef enum typedef enum
@ -39,7 +47,8 @@ typedef enum
typedef enum typedef enum
{ {
ERROR_NONE = 0, ERROR_NONE = 0,
ERROR_TIMEOUT = 1, // Время ожидания истекло ERROR_TIMEOUT = 1, // Время ожидания истекло
ERROR_CRC, // Ошибка при получении строки hex от ПК или при записи страницы данных на flash
} Bootloader_error; } Bootloader_error;
typedef struct typedef struct
@ -50,8 +59,11 @@ typedef struct
uint8_t command; // Текущая принятая загрузчиком команда uint8_t command; // Текущая принятая загрузчиком команда
} Bootloader_attributes; } Bootloader_attributes;
Bootloader_attributes hBootloader = {(uint8_t*) SPIFI_ADDRESS, MAX_PACKAGE_SIZE, ERROR_NONE, 0}; Bootloader_attributes hBootloader = {(uint8_t*) SPIFI_ADDRESS, 0, ERROR_NONE, 0};
uint32_t timeout = 0; uint32_t timeout = 0;
uint32_t validCommandsTimeout = 0;
void go_to_spifi();
/* Инициализация UART */ /* Инициализация UART */
@ -59,7 +71,10 @@ void Bootloader_UART_Init()
{ {
PM->CLK_APB_P_SET = PM_CLOCK_APB_P_UART_0_M; // Включение тактирования UART0 PM->CLK_APB_P_SET = PM_CLOCK_APB_P_UART_0_M; // Включение тактирования UART0
PAD_CONFIG->PORT_0_CFG |= (0b01 << (5 << 1)) | (0b01 << (6 << 1)); // Настройка выводов PORT0.5 и PORT0.6 // Настройка выводов PORT0.5 и PORT0.6 на последовательный интерфейс
PAD_CONFIG->PORT_0_CFG |= (0b01 << (5 << 1)) | (0b01 << (6 << 1));
// Включение притяжки к питанию на линии rx
PAD_CONFIG->PORT_0_PUPD |= (0b01 << (5 << 1));
/* /*
* Настройки USART: * Настройки USART:
@ -88,9 +103,12 @@ void Bootloader_UART_Deinit()
UART_0->FLAGS = 0xFFFFFFFF; // сброс всех флагов UART_0->FLAGS = 0xFFFFFFFF; // сброс всех флагов
UART_0->TXDATA = 0x00; UART_0->TXDATA = 0x00;
PAD_CONFIG->PORT_0_CFG &= ~((0b11 << (5 << 1)) | (0b11 << (6 << 1))); // Настройка выводов PORT0.5 и PORT0.6 // Настройка выводов PORT0.5 и PORT0.6 на порт общего назначения
PAD_CONFIG->PORT_0_CFG &= ~((0b11 << (5 << 1)) | (0b11 << (6 << 1)));
// Отключение притяжки на линии rx
PAD_CONFIG->PORT_0_PUPD &= ~(0b01 << (5 << 1));
PM->CLK_APB_P_SET &= !PM_CLOCK_APB_P_UART_0_M; // Выключение тактирования UART0 PM->CLK_APB_P_CLEAR = PM_CLOCK_APB_P_UART_0_M; // Выключение тактирования UART0
} }
/* Отправить байт */ /* Отправить байт */
@ -109,6 +127,7 @@ uint16_t Bootloader_UART_ReadByte()
while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE)) while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE))
{ {
timeout++; timeout++;
validCommandsTimeout++; // Увеличить таймаут валидных команд
} }
if (timeout == TIMEOUT_VALUE) if (timeout == TIMEOUT_VALUE)
@ -122,19 +141,25 @@ uint16_t Bootloader_UART_ReadByte()
/* Обработчик ошибок */ /* Обработчик ошибок */
void Bootloader_ErrorHandler() void Bootloader_ErrorHandler()
{ {
switch (hBootloader.error)
{
case ERROR_TIMEOUT:
if (UART_0->FLAGS & UART_FLAGS_ORE_M)
UART_0->FLAGS |= UART_FLAGS_ORE_M;
switch (hBootloader.error) go_to_spifi(); // переход в основную программу, если в течение TIMEOUT_VALUE нет принятых данных
{ break;
case ERROR_TIMEOUT: case ERROR_CRC:
Bootloader_UART_WriteByte(NACK); // отправить nack и перейти в основную программу, если приняли некорректные данные
if (UART_0->FLAGS & UART_FLAGS_ORE_M) Bootloader_UART_WriteByte(NACK);
{
UART_0->FLAGS |= UART_FLAGS_ORE_M;
}
break;
}
hBootloader.error = ERROR_NONE; if (UART_0->FLAGS & UART_FLAGS_ORE_M)
UART_0->FLAGS |= UART_FLAGS_ORE_M;
go_to_spifi();
break;
}
hBootloader.error = ERROR_NONE;
} }
SPIFI_HandleTypeDef spifi = {.Instance = SPIFI_CONFIG}; SPIFI_HandleTypeDef spifi = {.Instance = SPIFI_CONFIG};
@ -155,36 +180,40 @@ uint8_t erase_chip(SPIFI_HandleTypeDef *spifi)
/* Загрузить данные пакета в RAM */ /* Загрузить данные пакета в RAM */
#define SIZE_4K 4096 #define SIZE_4K 4096
void Bootloader_LoadArrayInRam(uint8_t uart_data[])
{
static uint32_t relative_write_address;
if ((relative_write_address % SIZE_4K) == 0)
HAL_SPIFI_W25_SectorErase4K(&spifi, relative_write_address);
HAL_SPIFI_W25_PageProgram(&spifi, (uint32_t)hBootloader.address, hBootloader.size_package, uart_data);
hBootloader.address += hBootloader.size_package;
relative_write_address += hBootloader.size_package;
}
// разметка строки в хекс-файле // разметка строки в хекс-файле
#define BYTE_COUNT_POS 0 // индекс счетчика байт данных #define BYTE_COUNT_POS 0 // индекс счетчика байт данных
#define ADDRES_POS 1 // индекс адреса #define ADDRESS_POS 1 // индекс адреса
#define ADDRES_QTY 2 // количество байт адреса #define ADDRESS_QTY 2 // количество байт адреса
#define RECORD_TYPE_POS 3 // индекс типа записи #define RECORD_TYPE_POS 3 // индекс типа записи
#define DATA_POS 4 // индекс начала данных в команде #define DATA_POS 4 // индекс начала данных в команде
// типы записей в хекс-фйле // типы записей в хекс-файле
#define REC_TYPE_DATA 0x00 #define REC_TYPE_DATA 0x00
#define REC_TYPE_EOF 0x01 #define REC_TYPE_EOF 0x01
#define REC_TYPE_EXT_LIN_ADDR 0x04 #define REC_TYPE_EXT_LIN_ADDR 0x04
uint32_t abs_addr = 0; // адрес из хекса uint32_t abs_addr = 0; // адрес из хекса
uint32_t rel_addr = 0; // адрес от начала области spifi, по нему определяем, надо ли стирать сектор и какой именно uint32_t rel_addr = 0; // адрес от начала области spifi, по нему определяем, надо ли стирать сектор и какой именно
uint8_t page_data[256] = {0}; // сюда собираем распарсенные данные из хекса #define TAIL_SIZE 15 // если попадутся строки хекса, в которых не 16 байт, то мы рискуем записать данные уарта мимо буфера package_data. а если больше 16 байт, то это проблема завтрашнего дня
uint8_t page_data[MAX_PACKAGE_SIZE + TAIL_SIZE] = {0}; // сюда собираем распарсенные данные из хекса
uint16_t page_fill_size = 0; // счетчик, сколько заполнно в page_data. когда page_data заполнена до конца - будем записывать в spifi uint16_t page_fill_size = 0; // счетчик, сколько заполнно в page_data. когда page_data заполнена до конца - будем записывать в spifi
uint8_t read_data[MAX_PACKAGE_SIZE] = {0};
void go_to_spifi(); bool page_data_is_written(void)
{
// читаем ту же страницу в другой буфер
memset(read_data, 0xFF, MAX_PACKAGE_SIZE);
HAL_SPIFI_W25_ReadData(&spifi, (uint32_t)hBootloader.address, MAX_PACKAGE_SIZE, read_data);
// побайтово сравниваем содержимое двух буферов
for (uint16_t i = 0; i < MAX_PACKAGE_SIZE; i++)
{
if (read_data[i] != page_data[i])
// если не сходится хоть один байт, прерываем проверку
return false;
}
return true;
}
void mem_write() void mem_write()
{ {
@ -194,12 +223,28 @@ void mem_write()
HAL_SPIFI_W25_SectorErase4K(&spifi, rel_addr); HAL_SPIFI_W25_SectorErase4K(&spifi, rel_addr);
// записываем страницу в 256 байт в spifi // записываем страницу в 256 байт в spifi
HAL_SPIFI_W25_PageProgram(&spifi, (uint32_t)hBootloader.address, 256, page_data); HAL_SPIFI_W25_PageProgram(&spifi, (uint32_t)hBootloader.address, MAX_PACKAGE_SIZE, page_data);
// проверить, записалось ли действительно содержимое буфера
if (!page_data_is_written())
{
// если не записалось, выставим ошибку по crc и выходим
hBootloader.error = ERROR_CRC;
return;
}
// увеличиваем адреса, по которым писать и стирать // увеличиваем адреса, по которым писать и стирать
hBootloader.address += 256; hBootloader.address += MAX_PACKAGE_SIZE;
// обнуляем буфер и счетчик заполнения буфера // очищаем часть буфера, которая была записана в память и уменьшаем счетчик заполнения буфера на столько, сколько было записано
page_fill_size = 0; if (page_fill_size <= MAX_PACKAGE_SIZE)
memset(page_data, 0, 256); page_fill_size = 0;
else
page_fill_size -= MAX_PACKAGE_SIZE;
memset(&page_data[0], 0xFF, MAX_PACKAGE_SIZE);
// хвост копируем в начало буфера, чтобы записать его в следующий раз
memcpy(&page_data[0], &page_data[MAX_PACKAGE_SIZE], TAIL_SIZE);
// а сам хвост очищаем
memset(&page_data[MAX_PACKAGE_SIZE], 0xFF, TAIL_SIZE);
} }
void Bootloader_parseHexAndLoadInMemory(uint8_t rx_data[]) void Bootloader_parseHexAndLoadInMemory(uint8_t rx_data[])
{ {
@ -208,7 +253,7 @@ void Bootloader_parseHexAndLoadInMemory(uint8_t rx_data[])
switch (rec_type) switch (rec_type)
{ {
case REC_TYPE_EXT_LIN_ADDR: case REC_TYPE_EXT_LIN_ADDR:
// если так получилось, что нам слали данные, буфер на 256 не заполнился, а тут прилетела команда смены адреса, то пишем сколько есть // если так получилось, что нам слали данные, буфер на 256 не заполнился, а тут прилетела команда смены адреса, то пишем сколько есть
if (page_fill_size != 0) if (page_fill_size != 0)
mem_write(); mem_write();
// собираем адрес, с которого начинаем писать из данных команды смены адреса. нам присылают только 2 старших байта адреса // собираем адрес, с которого начинаем писать из данных команды смены адреса. нам присылают только 2 старших байта адреса
@ -221,8 +266,8 @@ void Bootloader_parseHexAndLoadInMemory(uint8_t rx_data[])
memcpy(&page_data[page_fill_size], &rx_data[DATA_POS], rx_data[BYTE_COUNT_POS]); memcpy(&page_data[page_fill_size], &rx_data[DATA_POS], rx_data[BYTE_COUNT_POS]);
// указываем, на сколько заполнился буфер // указываем, на сколько заполнился буфер
page_fill_size += rx_data[BYTE_COUNT_POS]; page_fill_size += rx_data[BYTE_COUNT_POS];
// если пора записывать целую страницу - пишемм // если пора записывать целую страницу - пишем
if (page_fill_size == 256) if (page_fill_size >= 256)
mem_write(); mem_write();
break; break;
@ -230,7 +275,12 @@ void Bootloader_parseHexAndLoadInMemory(uint8_t rx_data[])
Bootloader_UART_WriteByte(ACK); Bootloader_UART_WriteByte(ACK);
// если есть недозаполненная страница, записываем ее как есть // если есть недозаполненная страница, записываем ее как есть
if (page_fill_size != 0) if (page_fill_size != 0)
{
mem_write(); mem_write();
// если при записи остатков возникли ошибки, здесь в основную программу не будем переходить
if (hBootloader.error)
return;
}
// и идем в записанную программу // и идем в записанную программу
go_to_spifi(); go_to_spifi();
break; break;
@ -240,72 +290,106 @@ void Bootloader_parseHexAndLoadInMemory(uint8_t rx_data[])
} }
} }
uint8_t uart_data[MAX_PACKAGE_SIZE] = {0}; // Массив данных из полученного пакета uint8_t uart_data[MAX_PACKAGE_SIZE] = {0}; // Массив данных из полученного пакета
uint32_t crc_acc = 0;
uint8_t crc = 0;
void Bootloader_UART_ReadPackage() void Bootloader_UART_ReadPackage()
{ {
crc_acc = 0;
for (uint32_t counter = 0; counter < hBootloader.size_package; counter++) for (uint32_t counter = 0; counter < hBootloader.size_package; counter++)
{ {
timeout = 0; timeout = 0;
while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE)) // Ожидание байта пакета while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE)) // Ожидание байта пакета
{
timeout++; timeout++;
}
if (timeout == TIMEOUT_VALUE) if (timeout == TIMEOUT_VALUE)
{ {
hBootloader.error = ERROR_TIMEOUT; hBootloader.error = ERROR_TIMEOUT;
break; break;
} }
uart_data[counter] = UART_0->RXDATA; uart_data[counter] = UART_0->RXDATA;
// контрольная сумма идет последним байтом в пакете. Суммируем всё, кроме нее
if (counter < (hBootloader.size_package-1))
crc_acc += uart_data[counter];
else
{
// посчитать контрольную сумму и сравнить
crc = (256 - crc_acc % 256) % 256;
if (crc != uart_data[counter])
hBootloader.error = ERROR_CRC;
}
} }
if (!hBootloader.error) if (hBootloader.error == ERROR_NONE)
{
Bootloader_parseHexAndLoadInMemory(uart_data); Bootloader_parseHexAndLoadInMemory(uart_data);
}
} }
uint8_t eraseChipBufferIndex = 0; // Индекс для накопления команды erase chip
void Bootloader_Commands() void Bootloader_Commands()
{ {
while (1) while (1)
{ {
hBootloader.command = Bootloader_UART_ReadByte(); // Ожидание и считывание команды hBootloader.command = Bootloader_UART_ReadByte(); // Ожидание и считывание команды
// если долго не приходили валидные команды, переход в основную программу
if (validCommandsTimeout >= TIMEOUT_VALUE)
go_to_spifi();
if (hBootloader.error) if (hBootloader.error)
{
Bootloader_ErrorHandler(); // Обработчик ошибок Bootloader_ErrorHandler(); // Обработчик ошибок
}
else else
{ {
switch (hBootloader.command) switch (hBootloader.command)
{ {
case VERSION:
validCommandsTimeout = 0; // Сброс таймаута валидных команд
eraseChipBufferIndex = 0;
Bootloader_UART_WriteByte(ACK); // Подтвердить команду
// отправить все составляющие версии загрузчика
Bootloader_UART_WriteByte((uint16_t)MAJOR_VER);
Bootloader_UART_WriteByte((uint16_t)MINOR_VER);
Bootloader_UART_WriteByte((uint16_t)BUGFIX_VER);
break;
case PACKAGE_SIZE: case PACKAGE_SIZE:
Bootloader_UART_WriteByte(ACK); // Подтвердить команду validCommandsTimeout = 0; // Сброс таймаута валидных команд
eraseChipBufferIndex = 0;
Bootloader_UART_WriteByte(ACK); // Подтвердить команду
hBootloader.size_package = Bootloader_UART_ReadByte() + 1; // Прочитать размер пакета hBootloader.size_package = Bootloader_UART_ReadByte() + 1; // Прочитать размер пакета
Bootloader_UART_WriteByte(ACK); // Подтвердить Bootloader_UART_WriteByte(ACK); // Подтвердить
break; break;
case SEND_PACKAGE: case SEND_PACKAGE:
Bootloader_UART_WriteByte(ACK); // Подтвердить команду validCommandsTimeout = 0; // Сброс таймаута валидных команд
Bootloader_UART_ReadPackage(); // Получить пакет и скопировать его в RAM eraseChipBufferIndex = 0;
Bootloader_UART_WriteByte(ACK); // Подтвердить команду
Bootloader_UART_ReadPackage(); // Получить пакет и скопировать его в RAM
if (hBootloader.error) if (hBootloader.error)
{ Bootloader_ErrorHandler(); // Обработчик ошибок
Bootloader_ErrorHandler(); // Обработчик ошибок
}
else else
{
Bootloader_UART_WriteByte(ACK); // Подтвердить считывание и копирования пакета в RAM Bootloader_UART_WriteByte(ACK); // Подтвердить считывание и копирования пакета в RAM
}
break;
case FULL_ERASE:
if (erase_chip(&spifi) != HAL_OK)
Bootloader_UART_WriteByte(NACK);
else
Bootloader_UART_WriteByte(ACK);
break; break;
default: default:
// Вычислить ожидаемый байт команды FULL_ERASE на основе текущего индекса
uint8_t expectedByte = (FULL_ERASE >> ((FULL_ERASE_CMD_LEN - eraseChipBufferIndex - 1) * 8)) & 0xFF;
// Если полученный байт совпадает с ожидаемым байтом команды FULL_ERASE
if (hBootloader.command == expectedByte)
{
// Переход к следующему байту
eraseChipBufferIndex++;
// Если команда FULL_ERASE полностью получена
if (eraseChipBufferIndex == FULL_ERASE_CMD_LEN)
{
Bootloader_UART_WriteByte(ACK); // Подтвердить получение команды
// Очистка чипа
if (erase_chip(&spifi) != HAL_OK)
Bootloader_UART_WriteByte(NACK);
else
Bootloader_UART_WriteByte(ACK); // Подтвердить успешную очистку
eraseChipBufferIndex = 0; // Сброс индекса для следующего приема
}
validCommandsTimeout = 0; // Сброс таймаута валидных команд
}
else // Если байт не совпал ни с какими валидными командами
eraseChipBufferIndex = 0; // Сброс индекса команды FULL_ERASE
break; break;
} }
} }
@ -315,31 +399,40 @@ void Bootloader_Commands()
void SystemClock_Config(); void SystemClock_Config();
int main() int main()
{ {
SystemClock_Config(); SystemClock_Config();
HAL_SPIFI_MspInit(&spifi); HAL_SPIFI_MspInit(&spifi);
HAL_SPIFI_Reset(&spifi); HAL_SPIFI_Reset(&spifi);
/* Переключение флеш-памяти в нормальный режим с командами, передав ей "0" в промежуточном байте */
const uint32_t cmd_chip_read_xip_init =
SPIFI_DIRECTION_INPUT |
#if CHIP_MODE == 1
SPIFI_CONFIG_CMD_INTLEN(1) |
#else
SPIFI_CONFIG_CMD_INTLEN(3) |
#endif
SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_PARALLEL) |
SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_3ADDR) |
SPIFI_CONFIG_CMD_OPCODE(0xEB);
uint8_t tmp_byte_xip_init[1] = {0};
HAL_SPIFI_SendCommand_LL(&spifi, cmd_chip_read_xip_init, 0, 1, tmp_byte_xip_init, 0, 0, HAL_SPIFI_TIMEOUT);
Bootloader_UART_Init(); // Инициализация UART. НАстройка выводов и тактирования #if CHIP_MODE == 1
/* Переключение флеш-памяти из режима QPI в обычный режим SPI */
const uint32_t cmd_qpi_disable =
SPIFI_DIRECTION_INPUT |
SPIFI_CONFIG_CMD_INTLEN(0) |
SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_PARALLEL) |
SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_OPCODE) |
SPIFI_CONFIG_CMD_OPCODE(0xFF);
HAL_SPIFI_SendCommand_LL(&spifi, cmd_qpi_disable, 0, 0, 0, 0, 0, HAL_SPIFI_TIMEOUT);
#endif
timeout = 0; Bootloader_UART_Init(); // Инициализация UART. Настройка выводов и тактирования
while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE)) // Загрузчик ожидает команду
{
timeout++;
}
if (timeout == TIMEOUT_VALUE) Bootloader_Commands(); // Обработка и ожидание команд
{
go_to_spifi();
}
else
{
Bootloader_Commands(); // Обработка и ожидания команд
}
while (1) while (1)
{ {
@ -373,29 +466,65 @@ void SPIFI_Init()
HAL_SPIFI_MspInit(&spifi); HAL_SPIFI_MspInit(&spifi);
HAL_SPIFI_Reset(&spifi); HAL_SPIFI_Reset(&spifi);
/* В Winbond для выставления QE используется команда 0x01 в 1-м бите 2го статус регистра. */
uint8_t sreg1 = HAL_SPIFI_W25_ReadSREG(&spifi, W25_SREG1); uint8_t sreg1 = HAL_SPIFI_W25_ReadSREG(&spifi, W25_SREG1);
if (sreg1 > 0x03) sreg1 = 0; // снятие защиты от записи (protection bits)
uint8_t sreg2 = HAL_SPIFI_W25_ReadSREG(&spifi, W25_SREG2); uint8_t sreg2 = HAL_SPIFI_W25_ReadSREG(&spifi, W25_SREG2);
if (!(sreg2 & 0x02)) sreg2 |= 0x02; // установка бита QE (quad enable)
if (sreg2 & 0x40) sreg2 &= ~0x40; // сброс бита CMP. Если вдруг он выставился по какой-то причине, часть или вся память можеть быть заблокирована для записи
HAL_SPIFI_W25_WriteSREG(&spifi, sreg1, sreg2);
#if CHIP_MODE == 1
/* Переключение флеш-памяти в режим QPI, когда весь обмен четырёхпроводной */
const uint32_t cmd_qpi_enable =
SPIFI_DIRECTION_INPUT |
SPIFI_CONFIG_CMD_INTLEN(0) |
SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_SERIAL) |
SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_OPCODE) |
SPIFI_CONFIG_CMD_OPCODE(0x38);
HAL_SPIFI_SendCommand_LL(&spifi, cmd_qpi_enable, 0, 0, 0, 0, 0, HAL_SPIFI_TIMEOUT);
/*В Winbond для выставления QE используется команда 0x01 в 1-м бите 2го статус регистра. /* Переключение флеш-памяти в режим без последующих команд чтения, передав ей "0x20" в промежуточном байте */
Количество промежуточных данных в команде 4READ = 0xEB равно 3 байта (в cmd_mem)*/ const uint32_t cmd_chip_read_qpi_xip_init =
HAL_SPIFI_W25_WriteSREG(&spifi, sreg1, sreg2 | (1 << 1)); // ? HAL_SPIFI_W25_QuadEnable(&spifi); SPIFI_DIRECTION_INPUT |
SPIFI_CONFIG_CMD_INTLEN(1) |
SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_ALL_PARALLEL) |
SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_OPCODE_3ADDR) |
SPIFI_CONFIG_CMD_OPCODE(0xEB);
uint8_t tmp_byte_xip_init[1] = {0};
HAL_SPIFI_SendCommand_LL(&spifi, cmd_chip_read_qpi_xip_init, 0, 1, tmp_byte_xip_init, 0, 0x20, HAL_SPIFI_TIMEOUT);
#else
/* Переключение флеш-памяти в режим без последующих команд чтения, передав ей "0x20" в первом промежуточном байте */
const uint32_t cmd_chip_read_xip_init =
SPIFI_DIRECTION_INPUT |
SPIFI_CONFIG_CMD_INTLEN(3) |
SPIFI_CONFIG_CMD_FIELDFORM(SPIFI_FIELDFORM_OPCODE_SERIAL) |
SPIFI_CONFIG_CMD_FRAMEFORM(SPIFI_FRAMEFORM_OPCODE_3ADDR) |
SPIFI_CONFIG_CMD_OPCODE(0xEB);
uint8_t tmp_byte_xip_init[1] = {0};
HAL_SPIFI_SendCommand_LL(&spifi, cmd_chip_read_xip_init, 0, 1, tmp_byte_xip_init, 0, 0x20, HAL_SPIFI_TIMEOUT);
#endif
/* Режим SPIFI без передачи команд, но с "0x20" в первых промежуточных байтах. */
SPIFI_MemoryCommandTypeDef cmd_mem = { SPIFI_MemoryCommandTypeDef cmd_mem = {
.OpCode = 0xEB, .OpCode = 0xEB,
.FieldForm = SPIFI_CONFIG_CMD_FIELDFORM_OPCODE_SERIAL, .FieldForm = SPIFI_CONFIG_CMD_FIELDFORM_ALL_PARALLEL,
.FrameForm = SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR, .FrameForm = SPIFI_CONFIG_CMD_FRAMEFORM_NOOPCODE_3ADDR,
.InterimData = 0, .InterimData = 0x20,
.InterimLength = 3, #if CHIP_MODE == 1
.InterimLength = 1 /* Количество промежуточных данных в команде 0xEB режима QPI равно 1 байт. */
#else
.InterimLength = 3 /* Количество промежуточных данных в команде 0xEB режима QSPI равно 3 байта. */
#endif
}; };
SPIFI_MemoryModeConfig_HandleTypeDef spifi_mem = { SPIFI_MemoryModeConfig_HandleTypeDef spifi_mem = {
.Instance = spifi.Instance, .Instance = spifi.Instance,
.CacheEnable = SPIFI_CACHE_ENABLE, .CacheEnable = SPIFI_CACHE_ENABLE,
.CacheLimit = 0x00010000, .CacheLimit = 0x00010000,
.Command = cmd_mem, .Command = cmd_mem
}; };
SPIFI_CONFIG->CTRL &= 0xFFF0FFFF;
HAL_SPIFI_MemoryMode_Init(&spifi_mem); HAL_SPIFI_MemoryMode_Init(&spifi_mem);
// SPIFI_disableDataCache(&spifi_mem); // SPIFI_disableDataCache(&spifi_mem);
} }