загрузчик mik32 для альфа-версии bsp arduino ide

This commit is contained in:
khristolyubov 2024-07-15 16:08:12 +07:00 committed by GitHub
parent dd1e920f05
commit bd225dbabe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 521 additions and 0 deletions

39
include/README Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

46
lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

16
platformio.ini Normal file
View File

@ -0,0 +1,16 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:mik32v2]
platform = MIK32
board = mik32v2
framework = framework-mik32v2-sdk
board_debug.ldscript = eeprom

409
src/bootloader.c Normal file
View File

@ -0,0 +1,409 @@
#include "mik32_hal_pcc.h"
#include "mik32_hal_spifi_w25.h"
#include "power_manager.h"
#include "uart.h"
#include "pad_config.h"
#include "mcu32_memory_map.h"
#include "riscv_csr_encoding.h"
#include "csr.h"
#include "string.h"
#define JALR_TO_SPIFI() \
asm volatile( "la ra, 0x80000000\n\t" \
"jalr ra" \
);
#define ACK 0x0F /* Подтверждение */
#define NACK 0xF0 /* Нет подтверждения */
#define MAX_PACKAGE_SIZE 256 /* максимальный размер пакета */
#define TIMEOUT_VALUE 1000000 /* Время ожидания загрузчика до прыжка по умолчанию в RAM 1000000 */
/* Виды команд */
typedef enum
{
PACKAGE_SIZE = 0x30, /* Команда размера пакета */
SEND_PACKAGE = 0x60, /* Команда отправить пакет */
FULL_ERASE = 0xFE /* Команда стирания spifi*/
} BotloaderComand;
typedef enum
{
SPIFI_ADDRESS = 0x80000000
} AddressMemory;
/* Виды ошибок */
typedef enum
{
ERROR_NONE = 0,
ERROR_TIMEOUT = 1, // Время ожидания истекло
} Bootloader_error;
typedef struct
{
uint8_t* address; // Адрес для записи присылаемых байт
uint16_t size_package; // Размер пакета
uint8_t error;
uint8_t command; // Текущая принятая загрузчиком команда
} Bootloader_attributes;
Bootloader_attributes hBootloader = {(uint8_t*) SPIFI_ADDRESS, MAX_PACKAGE_SIZE, ERROR_NONE, 0};
uint32_t timeout = 0;
/* Инициализация UART */
void Bootloader_UART_Init()
{
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
/*
* Настройки USART:
* Асинхронный режим. Включен RX и TX;
* Кадр: 8 бит данных, бит четности выключен, 1 стоп бит;
* Байт LSB - первый бит нулевой.
*/
UART_0->CONTROL1 = 0;
UART_0->CONTROL2 = 0;
UART_0->CONTROL3 = 0;
UART_0->DIVIDER = 138; /* Baudrate = 230400 */
UART_0->FLAGS = 0xFFFFFFFF;
UART_0->CONTROL1 = UART_CONTROL1_RE_M | UART_CONTROL1_TE_M | UART_CONTROL1_UE_M;
/* Ожидание флагов готовности RX и TX */
while (!(UART_0->FLAGS & (UART_FLAGS_REACK_M | UART_FLAGS_TEACK_M)))
;
}
void Bootloader_UART_Deinit()
{
UART_0->CONTROL1 = 0;
UART_0->CONTROL2 = 0;
UART_0->CONTROL3 = 0;
UART_0->DIVIDER = 0x0000; // сброс бодрейта
UART_0->FLAGS = 0xFFFFFFFF; // сброс всех флагов
UART_0->TXDATA = 0x00;
PAD_CONFIG->PORT_0_CFG &= (0b00 << (5 << 1)) | (0b00 << (6 << 1)); // Настройка выводов PORT0.5 и PORT0.6
PM->CLK_APB_P_SET &= !PM_CLOCK_APB_P_UART_0_M; // Выключение тактирования UART0
}
/* Отправить байт */
void Bootloader_UART_WriteByte(uint16_t Write_Byte)
{
UART_0->TXDATA = Write_Byte;
/* Ожидаем успешную передачу */
while (!(UART_0->FLAGS & UART_FLAGS_TC_M))
;
}
/* Ожидание и считывание байта */
uint16_t Bootloader_UART_ReadByte()
{
timeout = 0;
while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE))
{
timeout++;
}
if (timeout == TIMEOUT_VALUE)
{
hBootloader.error = ERROR_TIMEOUT;
}
return (uint16_t)UART_0->RXDATA;
}
/* Обработчик ошибок */
void Bootloader_ErrorHandler()
{
switch (hBootloader.error)
{
case ERROR_TIMEOUT:
Bootloader_UART_WriteByte(NACK);
if (UART_0->FLAGS & UART_FLAGS_ORE_M)
{
UART_0->FLAGS |= UART_FLAGS_ORE_M;
}
break;
}
hBootloader.error = ERROR_NONE;
}
SPIFI_HandleTypeDef spifi = {.Instance = SPIFI_CONFIG};
uint8_t erase_chip(SPIFI_HandleTypeDef *spifi)
{
const uint32_t cmd_chip_erase =
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(0xC7); //CHIP_ERASE = 0x60 или 0xC7
HAL_SPIFI_W25_WriteEnable(spifi);
uint8_t stat = HAL_SPIFI_SendCommand_LL(spifi, cmd_chip_erase, 0, 0, 0, 0, 0, HAL_SPIFI_TIMEOUT);
stat = HAL_SPIFI_W25_WaitBusy(spifi, 10000000); // SPIFI_W25_PROGRAM_BUSY = 100000
return stat;
}
/* Загрузить данные пакета в RAM */
#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 ADDRES_POS 1 // индекс адреса
#define ADDRES_QTY 2 // количество байт адреса
#define RECORD_TYPE_POS 3 // индекс типа записи
#define DATA_POS 4 // индекс начала данных в команде
// типы записей в хекс-фйле
#define REC_TYPE_DATA 0x00
#define REC_TYPE_EOF 0x01
#define REC_TYPE_EXT_LIN_ADDR 0x04
uint32_t abs_addr = 0; // адрес из хекса
uint32_t rel_addr = 0; // адрес от начала области spifi, по нему определяем, надо ли стирать сектор и какой именно
uint8_t page_data[256] = {0}; // сюда собираем распарсенные данные из хекса
uint16_t page_fill_size = 0; // счетчик, сколько заполнно в page_data. когда page_data заполнена до конца - будем записывать в spifi
void go_to_spifi();
void mem_write()
{
// если адрес дошел до начала нового сектора, стираем новый сектор
rel_addr = (uint32_t)(hBootloader.address) - 0x80000000;
if ((rel_addr % SIZE_4K) == 0)
HAL_SPIFI_W25_SectorErase4K(&spifi, rel_addr);
// записываем страницу в 256 байт в spifi
HAL_SPIFI_W25_PageProgram(&spifi, (uint32_t)hBootloader.address, 256, page_data);
// увеличиваем адреса, по которым писать и стирать
hBootloader.address += 256;
// обнуляем буфер и счетчик заполнения буфера
page_fill_size = 0;
memset(page_data, 0, 256);
}
void Bootloader_parseHexAndLoadInMemory(uint8_t rx_data[])
{
// из принятых данных вытаскиваем тип записи
uint8_t rec_type = rx_data[RECORD_TYPE_POS];
switch (rec_type)
{
case REC_TYPE_EXT_LIN_ADDR:
// если так получилось, что нам слали данные, буфер на 256 не заполнился, а тут прилетела команда смены адреса, то пишем сколько есть
if (page_fill_size != 0)
mem_write();
// собираем адрес, с которого начинаем писать из данных команды смены адреса. нам присылают только 2 старших байта адреса
abs_addr = (rx_data[DATA_POS] << 24) + (rx_data[DATA_POS+1] << 16);
hBootloader.address = (uint8_t*)abs_addr;
break;
case REC_TYPE_DATA:
// перекладываем из приемного буфера данные команды в буфер для записи
memcpy(&page_data[page_fill_size], &rx_data[DATA_POS], rx_data[BYTE_COUNT_POS]);
// указываем, на сколько заполнился буфер
page_fill_size += rx_data[BYTE_COUNT_POS];
// если пора записывать целую страницу - пишемм
if (page_fill_size == 256)
mem_write();
break;
case REC_TYPE_EOF: // конец прошивки
Bootloader_UART_WriteByte(ACK);
// если есть недозаполненная страница, записываем ее как есть
if (page_fill_size != 0)
mem_write();
// и идем в записанную программу
go_to_spifi();
break;
default:
break;
}
}
uint8_t uart_data[MAX_PACKAGE_SIZE] = {0}; // Массив данных из полученного пакета
void Bootloader_UART_ReadPackage()
{
for (uint32_t counter = 0; counter < hBootloader.size_package; counter++)
{
timeout = 0;
while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE)) // Ожидание байта пакета
{
timeout++;
}
if (timeout == TIMEOUT_VALUE)
{
hBootloader.error = ERROR_TIMEOUT;
break;
}
uart_data[counter] = UART_0->RXDATA;
}
if (!hBootloader.error)
{
Bootloader_parseHexAndLoadInMemory(uart_data);
}
}
void Bootloader_Commands()
{
while (1)
{
hBootloader.command = Bootloader_UART_ReadByte(); // Ожидание и считывание команды
if (hBootloader.error)
{
Bootloader_ErrorHandler(); // Обработчик ошибок
}
else
{
switch (hBootloader.command)
{
case PACKAGE_SIZE:
Bootloader_UART_WriteByte(ACK); // Подтвердить команду
hBootloader.size_package = Bootloader_UART_ReadByte() + 1; // Прочитать размер пакета
Bootloader_UART_WriteByte(ACK); // Подтвердить
break;
case SEND_PACKAGE:
Bootloader_UART_WriteByte(ACK); // Подтвердить команду
Bootloader_UART_ReadPackage(); // Получить пакет и скопировать его в RAM
if (hBootloader.error)
{
Bootloader_ErrorHandler(); // Обработчик ошибок
}
else
{
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;
default:
break;
}
}
}
}
void SystemClock_Config();
int main()
{
SystemClock_Config();
HAL_SPIFI_MspInit(&spifi);
HAL_SPIFI_Reset(&spifi);
Bootloader_UART_Init(); // Инициализация UART. НАстройка выводов и тактирования
timeout = 0;
while ((!(UART_0->FLAGS & UART_FLAGS_RXNE_M)) && (timeout != TIMEOUT_VALUE)) // Загрузчик ожидает команду
{
timeout++;
}
if (timeout == TIMEOUT_VALUE)
{
go_to_spifi();
}
else
{
Bootloader_Commands(); // Обработка и ожидания команд
}
while (1)
{
/* code */
}
}
void SystemClock_Config(void)
{
PCC_InitTypeDef PCC_OscInit = {0};
PCC_OscInit.OscillatorEnable = PCC_OSCILLATORTYPE_ALL;
PCC_OscInit.FreqMon.OscillatorSystem = PCC_OSCILLATORTYPE_OSC32M;
PCC_OscInit.FreqMon.ForceOscSys = PCC_FORCE_OSC_SYS_UNFIXED;
PCC_OscInit.FreqMon.Force32KClk = PCC_FREQ_MONITOR_SOURCE_OSC32K;
PCC_OscInit.AHBDivider = 0;
PCC_OscInit.APBMDivider = 0;
PCC_OscInit.APBPDivider = 0;
PCC_OscInit.HSI32MCalibrationValue = 128;
PCC_OscInit.LSI32KCalibrationValue = 128;
PCC_OscInit.RTCClockSelection = PCC_RTC_CLOCK_SOURCE_AUTO;
PCC_OscInit.RTCClockCPUSelection = PCC_CPU_RTC_CLOCK_SOURCE_OSC32K;
HAL_PCC_Config(&PCC_OscInit);
}
void SPIFI_disableDataCache(SPIFI_MemoryModeConfig_HandleTypeDef *spifi)
{
spifi->Instance->CTRL |= SPIFI_CONFIG_CTRL_D_CACHE_DIS_M;
}
void SPIFI_Init()
{
HAL_SPIFI_MspInit(&spifi);
HAL_SPIFI_Reset(&spifi);
uint8_t sreg1 = HAL_SPIFI_W25_ReadSREG(&spifi, W25_SREG1);
uint8_t sreg2 = HAL_SPIFI_W25_ReadSREG(&spifi, W25_SREG2);
/*В Winbond для выставления QE используется команда 0x01 в 1-м бите 2го статус регистра.
Количество промежуточных данных в команде 4READ = 0xEB равно 3 байта (в cmd_mem)*/
HAL_SPIFI_W25_WriteSREG(&spifi, sreg1, sreg2 | (1 << 1)); // ? HAL_SPIFI_W25_QuadEnable(&spifi);
SPIFI_MemoryCommandTypeDef cmd_mem = {
.OpCode = 0xEB,
.FieldForm = SPIFI_CONFIG_CMD_FIELDFORM_OPCODE_SERIAL,
.FrameForm = SPIFI_CONFIG_CMD_FRAMEFORM_OPCODE_3ADDR,
.InterimData = 0,
.InterimLength = 3,
};
SPIFI_MemoryModeConfig_HandleTypeDef spifi_mem = {
.Instance = spifi.Instance,
.CacheEnable = SPIFI_CACHE_ENABLE,
.CacheLimit = 0x00010000,
.Command = cmd_mem,
};
HAL_SPIFI_MemoryMode_Init(&spifi_mem);
// SPIFI_disableDataCache(&spifi_mem);
}
void go_to_spifi()
{
Bootloader_UART_Deinit();
SPIFI_Init();
write_csr(mtvec, 0x80000000);
JALR_TO_SPIFI();
}

11
test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html