uart для платы вэп + структурирован чуть проект и описан бутлоадер

Но надо еще его дорабатывать

+ заготовка для протокола приема (не работает скорее всего, просто из чатгпт вставил)
This commit is contained in:
Razvalyaev 2025-09-19 13:32:45 +03:00
parent 0e834dfe3d
commit c61c438b8c
12 changed files with 372 additions and 290 deletions

View File

@ -5,7 +5,16 @@
extern CAN_HandleTypeDef hcan_boot;
extern CAN_TxHeaderTypeDef TxHeaderBoot;
extern CAN_RxHeaderTypeDef RxHeaderBoot;
extern uint32_t TxMailBoxBoot;
extern uint8_t TXDataBoot[8];
/* Инициализация CAN */
void MX_BOOT_CAN_Init(void);
/* Приём команды по CAN по протоколу */
BootloaderCommand_t Bootloader_CAN_Receive(Bootloader_t *bl);
/* Приём CAN: страница + CRC */
void Bootloader_CAN_Receive_Page(Bootloader_t *bl);
#endif //__BOOT_CAN_H

View File

@ -32,20 +32,19 @@
// Макросы для включения тактирования периферии бутлоадера
#define __RCC_LED_BOOT_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() // тактирование UART
#define __RCC_UART_BOOT_CLK_ENABLE() __HAL_RCC_USART3_CLK_ENABLE() // тактирование UART
#define __RCC_UART_PORT_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE() // тактирование порта UART
#define __RCC_UART_BOOT_CLK_ENABLE() __HAL_RCC_UART4_CLK_ENABLE() // тактирование UART
#define __RCC_UART_PORT_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE() // тактирование порта UART
#define __RCC_CAN_BOOT_CLK_ENABLE() __HAL_RCC_CAN1_CLK_ENABLE() // тактирование CAN
#define __RCC_CAN_PORT_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() // тактирование порта CAN
// ======================== UART defines ========================
// Аппаратный UART и скорость передачи
#define UART_BOOT USART3
#define UART_SPEED 256000
#define UART_BOOT_IRQn USART3_IRQn
#define UART_BOOT UART4
#define UART_SPEED 115200
// Порт и пины UART
#define UART_PORT GPIOB
#define UART_PORT GPIOC
#define UART_PIN_TX GPIO_PIN_10
#define UART_PIN_RX GPIO_PIN_11

View File

@ -5,7 +5,11 @@
extern UART_HandleTypeDef huart_boot;
/* Инициализация UART */
void MX_BOOT_UART_Init(void);
/* Приём команды по UART по протоколу */
BootloaderCommand_t Bootloader_UART_Receive(Bootloader_t *bl);
/* Приём UART: страница + CRC */
void Bootloader_UART_Receive_Page(Bootloader_t *bl);
#endif //__BOOT_UART_H

View File

@ -71,12 +71,14 @@
* @brief Команды для управления бутлоадером
*/
typedef enum {
NO_CMD = 0x00, ///< Нет комманды
CMD_ERASE = 0x01, ///< Команда на стирание прошивки
CMD_START_RECEIVE, ///< Команда на старт приема прошивки
CMD_WRITE, ///< Команда на запись блока прошивки
CMD_GOTOAPP, ///< Команда на переход в приложение
CMD_RESET, ///< Команда на переход в приложение
CMD_GOTOBOOT, ///< Команда на переход в приложение
CMD_PING = 0xAA, ///< Команда на пинг
}BootloaderCommand_t;
/**
@ -108,6 +110,7 @@ typedef union {
unsigned erase_err:1; ///< Ошибка стирания
unsigned write_err:1; ///< Ошибка записи
unsigned verify_err:1; ///< Ошибка проверки прошивки
unsigned overflow:1; ///< Слишком много данных
unsigned timeout_receive:1; ///< Таймаут приёма
unsigned crc_err:1; ///< Ошибка CRC
} bit;
@ -119,7 +122,7 @@ typedef union {
*/
typedef struct {
BootloaderState_t state; ///< текущее состояние бутлоадера
BootloaderError_t error;
BootloaderError_t error; ///< ошибки бутлоадера
uint32_t addr; ///< текущий адрес прошивки
@ -141,5 +144,7 @@ void Bootloader_Task(Bootloader_t *bl);
void Boot_SystemClock_Config(void);
/* Хендлер ошибки */
void Error_Handler(void);
/* CRC */
uint32_t CRC32_Compute(const uint8_t* data, uint32_t length);
#endif //__BOOTLOADER_H

View File

@ -3,6 +3,12 @@
CAN_HandleTypeDef hcan_boot;
CAN_TxHeaderTypeDef TxHeaderBoot;
CAN_RxHeaderTypeDef RxHeaderBoot;
uint32_t TxMailBoxBoot = 0;
uint8_t TXDataBoot[8] = {0};
/**
* @brief Инициализация CAN для бутлоадера
*/
@ -68,69 +74,89 @@ void MX_BOOT_CAN_Init(void)
}
}
/**
* @brief Приём команды по CAN по протоколу:
* @param bl: указатель на структуру бутлоадера
* @retval BootloaderCommand_t принятая команда или NO_CMD
*/
BootloaderCommand_t Bootloader_CAN_Receive(Bootloader_t *bl)
{
BootloaderCommand_t cmd = NO_CMD;
uint8_t canData[8];
if (HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
{
if (HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &RxHeaderBoot, canData) == HAL_OK)
{
cmd = canData[0]; // предполагаем, что команда в первом байте
}
}
return cmd;
}
/**
* @brief Приём CAN: страница + CRC
* @param bl: указатель на структуру бутлоадера
*/
void Bootloader_CAN_Receive_Page(Bootloader_t *bl)
{
uint16_t bytes_received = 0;
CAN_RxHeaderTypeDef canHeader;
uint8_t canData[8];
uint32_t start_tick = HAL_GetTick();
uint16_t bytes_received = 0;
CAN_RxHeaderTypeDef canHeader;
uint8_t canData[8];
uint32_t start_tick = HAL_GetTick();
// Приём страницы прошивки
while(bytes_received < PAGE_SIZE)
{
if(HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
{
if(HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &canHeader, canData) == HAL_OK)
{
uint8_t len = canHeader.DLC;
if(bytes_received + len > PAGE_SIZE)
len = PAGE_SIZE - bytes_received;
// Приём страницы прошивки
while(bytes_received < PAGE_SIZE)
{
if(HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
{
if(HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &canHeader, canData) == HAL_OK)
{
uint8_t len = canHeader.DLC;
if(bytes_received + len > PAGE_SIZE)
len = PAGE_SIZE - bytes_received;
memcpy(&bl->fw_buffer[bytes_received], canData, len);
bytes_received += len;
start_tick = HAL_GetTick(); // сброс таймаута
LED_BOOT_TOOGLE();
}
}
memcpy(&bl->fw_buffer[bytes_received], canData, len);
bytes_received += len;
start_tick = HAL_GetTick(); // сброс таймаута
LED_BOOT_TOOGLE();
}
}
// проверка таймаута
if(HAL_GetTick() - start_tick >= FW_RECEIVE_TIMEOUT_MS)
{
bl->error.bit.timeout_receive = 1;
bl->state = BL_STATE_ERROR;
return;
}
}
// Приём CRC (4 байта)
start_tick = HAL_GetTick(); // сброс таймаута
while(1)
{
if(HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
{
if(HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &canHeader, canData) == HAL_OK)
{
// CRC в первых 4 байтах пакета
bl->fw_crc = (canData[0] << 24) |
(canData[1] << 16) |
(canData[2] << 8) |
canData[3];
break;
}
}
// проверка таймаута
if(HAL_GetTick() - start_tick >= FW_RECEIVE_TIMEOUT_MS)
{
bl->error.bit.timeout_receive = 1;
bl->state = BL_STATE_ERROR;
return;
}
}
// Приём CRC (4 байта)
start_tick = HAL_GetTick(); // сброс таймаута
while(1)
{
if(HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
{
if(HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &canHeader, canData) == HAL_OK)
{
// CRC в первых 4 байтах пакета
bl->fw_crc = (canData[0] << 24) |
(canData[1] << 16) |
(canData[2] << 8) |
canData[3];
break;
}
}
// Таймаут
if(HAL_GetTick() - start_tick >= FW_RECEIVE_TIMEOUT_MS)
{
bl->error.bit.timeout_receive = 1;
bl->state = BL_STATE_ERROR;
return;
}
}
// Таймаут
if(HAL_GetTick() - start_tick >= FW_RECEIVE_TIMEOUT_MS)
{
bl->error.bit.timeout_receive = 1;
bl->state = BL_STATE_ERROR;
return;
}
}
bl->fw_len = PAGE_SIZE;
bl->state = BL_STATE_IDLE;
bl->fw_len = PAGE_SIZE;
bl->state = BL_STATE_IDLE;
}

View File

@ -15,12 +15,16 @@ void MX_BOOT_UART_Init(void)
__RCC_UART_PORT_CLK_ENABLE();
/* Настройка GPIO TX/RX */
GPIO_InitStruct.Pin = UART_PIN_TX | UART_PIN_RX;
GPIO_InitStruct.Pin = UART_PIN_TX;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(UART_PORT, &GPIO_InitStruct);
GPIO_InitStruct.Pin = UART_PIN_RX;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(UART_PORT, &GPIO_InitStruct);
/* Настройка UART */
huart_boot.Instance = UART_BOOT;
huart_boot.Init.BaudRate = UART_SPEED;
@ -37,9 +41,96 @@ void MX_BOOT_UART_Init(void)
}
}
/**
* @brief Приём команды по UART по протоколу:
* [SOH][CMD][LEN_H][LEN_L][DATA ...][CRC32_H..CRC32_L]
* Idle не блокируется до SOH
* После SOH блокирующий приём всего пакета
* @param bl: указатель на структуру бутлоадера
* @retval BootloaderCommand_t принятая команда или NO_CMD
*/
BootloaderCommand_t Bootloader_UART_Receive(Bootloader_t *bl)
{
BootloaderCommand_t cmd;
uint8_t byte = 0;
HAL_StatusTypeDef res;
// -----------------------------
// 1. Ждём SOH в неблокирующем режиме
res = HAL_UART_Receive(bl->huart, &byte, 1, 1); // 1 ms таймаут
if(res != HAL_OK)
return NO_CMD; // пакета нет
if(byte != 0xAA)
return NO_CMD; // игнорируем мусор
// -----------------------------
// 2. Блокирующий приём CMD + LEN_H + LEN_L
uint8_t header[3];
res = HAL_UART_Receive(bl->huart, header, 3, FW_RECEIVE_TIMEOUT_MS);
if(res != HAL_OK)
{
bl->error.bit.timeout_receive = 1;
bl->state = BL_STATE_ERROR;
return NO_CMD;
}
cmd = (BootloaderCommand_t)header[0];
bl->fw_len = ((uint16_t)header[1] << 8) | header[2];
if(bl->fw_len > PAGE_SIZE)
{
bl->error.bit.overflow = 1;
bl->state = BL_STATE_ERROR;
return NO_CMD;
}
// -----------------------------
// 3. Блокирующий приём DATA
for(uint16_t i = 0; i < bl->fw_len; i++)
{
res = HAL_UART_Receive(bl->huart, &bl->fw_buffer[i], 1, FW_RECEIVE_TIMEOUT_MS);
if(res != HAL_OK)
{
bl->error.bit.timeout_receive = 1;
bl->state = BL_STATE_ERROR;
return NO_CMD;
}
}
// -----------------------------
// 4. Приём CRC32
uint8_t crc_buf[4];
res = HAL_UART_Receive(bl->huart, crc_buf, 4, FW_RECEIVE_TIMEOUT_MS);
if(res != HAL_OK)
{
bl->error.bit.timeout_receive = 1;
bl->state = BL_STATE_ERROR;
return NO_CMD;
}
bl->fw_crc = (crc_buf[0]<<24) | (crc_buf[1]<<16) | (crc_buf[2]<<8) | crc_buf[3];
// -----------------------------
// 5. Проверка CRC по всему пакету (CMD + LEN + DATA)
uint32_t crc_calc = CRC32_Compute(header, 3); // CMD + LEN
crc_calc = CRC32_Compute(bl->fw_buffer, bl->fw_len) ^ crc_calc; // DATA
if(crc_calc != bl->fw_crc)
{
bl->error.bit.crc_err = 1;
bl->state = BL_STATE_ERROR;
return NO_CMD;
}
// -----------------------------
// 6. Всё верно — возвращаем команду
return cmd;
}
/**
* @brief Приём UART: страница + CRC
* @param bl: указатель на структуру бутлоадера
*/
void Bootloader_UART_Receive_Page(Bootloader_t *bl)
{

View File

@ -40,17 +40,12 @@
// Глобальные переменные для HAL-периферии
// -----------------------------------------------------------------------------
HAL_StatusTypeDef res_hal;
CAN_TxHeaderTypeDef TxHeaderBoot;
CAN_RxHeaderTypeDef RxHeaderBoot;
uint32_t TxMailBoxBoot = 0;
uint8_t TXDataBoot[8] = {0};
uint32_t led_err_lasttick = 0;
// -----------------------------------------------------------------------------
// Локальные (static) функции
// -----------------------------------------------------------------------------
static uint8_t Receive_FW_Command(Bootloader_t *bl);
static uint32_t CRC32_Compute(const uint8_t* data, uint32_t length);
/**
* @brief Проверка после сброса MCU.
@ -72,7 +67,8 @@ void Bootloader_StartCheck(Bootloader_t *bl)
// Чтение сохранённого кода ошибки и количества сбоев
ErrCodeBoot = GetErrorCode();
ErrCntBoot = GetErrorCnt();
// Если ошибок было больше 5, фиксируем тип ошибки
// Если ошибок было больше 5, фиксируем тип ошибки и уходим в бутлоадер
// Данные ошибки фиксируются только в прерываниях бутлоадера. Hardfault в прерывании приложения не считается
if(ErrCntBoot > 5)
{
ClearErrorCode();
@ -385,6 +381,8 @@ static uint8_t SetBootState(Bootloader_t *bl, BootloaderCommand_t cmd, uint8_t u
return 0x00;
case CMD_GOTOBOOT: // команда: прыжок в бутлоадер
bl->state = BL_STATE_JUMP_TO_BOOT;
case CMD_PING: // команда: пинг, отправка текущего состояния бутлоадера
bl->prev_state = 0; // обнуляем предыдущее состоояние, чтобы снова отправить комманду с текущим состоянием
return 0x00;
default:
@ -406,8 +404,8 @@ static uint8_t Receive_FW_Command(Bootloader_t *bl)
// ---------------------------
// Чтение команды по UART
// ---------------------------
res = HAL_UART_Receive(bl->huart, &cmd, 1, 10); // таймаут 10 ms
if (res == HAL_OK)
cmd = Bootloader_UART_Receive(bl); // таймаут 10 ms
if (cmd != NO_CMD)
{
ret_val = SetBootState(bl, cmd, 1);
}
@ -415,14 +413,10 @@ static uint8_t Receive_FW_Command(Bootloader_t *bl)
// ---------------------------
// Чтение команды по CAN
// ---------------------------
uint8_t canData[8];
if (HAL_CAN_GetRxFifoFillLevel(bl->hcan, CAN_RX_FIFO0) > 0)
cmd = Bootloader_CAN_Receive(bl);
if (cmd != NO_CMD)
{
if (HAL_CAN_GetRxMessage(bl->hcan, CAN_RX_FIFO0, &RxHeaderBoot, canData) == HAL_OK)
{
cmd = canData[0]; // предполагаем, что команда в первом байте
ret_val = SetBootState(bl, cmd, 0);
}
ret_val = SetBootState(bl, cmd, 0);
}
#ifdef TEST_CAN
@ -451,7 +445,7 @@ static uint8_t Receive_FW_Command(Bootloader_t *bl)
* @param length: длина массива в байтах
* @retval CRC32 вычисленное значение
*/
static uint32_t CRC32_Compute(const uint8_t* data, uint32_t length)
uint32_t CRC32_Compute(const uint8_t* data, uint32_t length)
{
const uint32_t polynomial = 0x04C11DB7;
uint32_t crc = 0xFFFFFFFF;

View File

@ -0,0 +1,16 @@
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x0800C000 0x00040000 { ; load region size_region
ER_IROM1 0x0800C000 0x00040000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
.ANY (+XO)
}
RW_IRAM1 0x20000000 0x0000C000 { ; RW data
.ANY (+RW +ZI)
}
}

View File

@ -28,7 +28,7 @@
<TargetOption>
<CLKADS>8000000</CLKADS>
<OPTTT>
<gFlags>1</gFlags>
<gFlags>0</gFlags>
<BeepAtEnd>1</BeepAtEnd>
<RunSim>0</RunSim>
<RunTarget>1</RunTarget>
@ -103,7 +103,7 @@
<bEvRecOn>1</bEvRecOn>
<bSchkAxf>0</bSchkAxf>
<bTchkAxf>0</bTchkAxf>
<nTsel>4</nTsel>
<nTsel>0</nTsel>
<sDll></sDll>
<sDllPa></sDllPa>
<sDlgDll></sDlgDll>
@ -114,34 +114,9 @@
<tDlgDll></tDlgDll>
<tDlgPa></tDlgPa>
<tIfile></tIfile>
<pMon>Segger\JL2CM3.dll</pMon>
<pMon>BIN\UL2CM3.DLL</pMon>
</DebugOpt>
<TargetDriverDllRegistry>
<SetRegEntry>
<Number>0</Number>
<Key>ARMRTXEVENTFLAGS</Key>
<Name>-L70 -Z18 -C0 -M0 -T1</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>DLGTARM</Key>
<Name>(1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>ARMDBGFLAGS</Key>
<Name></Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>DLGUARM</Key>
<Name></Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>JL2CM3</Key>
<Name>-U11111118 -O14 -S2 -ZTIFSpeedSel5000 -A0 -C0 -JU1 -JI127.0.0.1 -JP0 -RST0 -N00("ARM CoreSight JTAG-DP") -D00(3BA00477) -L00(4) -N01("Unknown JTAG device") -D01(06414041) -L01(5) -TO18 -TC10000000 -TP21 -TDS8007 -TDT0 -TDC1F -TIEFFFFFFFF -TIP8 -TB1 -TFE0 -FO7 -FD20000000 -FC1000 -FN1 -FF0STM32F10x_512.FLM -FS08000000 -FL080000 -FP0($$Device:STM32F103RC$Flash\STM32F10x_512.FLM)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
<Key>UL2CM3</Key>
@ -149,81 +124,18 @@
</SetRegEntry>
</TargetDriverDllRegistry>
<Breakpoint/>
<WatchWindow1>
<Ww>
<count>0</count>
<WinNumber>1</WinNumber>
<ItemText>input</ItemText>
</Ww>
<Ww>
<count>1</count>
<WinNumber>1</WinNumber>
<ItemText>Inputs</ItemText>
</Ww>
<Ww>
<count>2</count>
<WinNumber>1</WinNumber>
<ItemText>duty_cycle,0x0A</ItemText>
</Ww>
<Ww>
<count>3</count>
<WinNumber>1</WinNumber>
<ItemText>frequency,0x0A</ItemText>
</Ww>
<Ww>
<count>4</count>
<WinNumber>1</WinNumber>
<ItemText>uwTick,0x0A</ItemText>
</Ww>
<Ww>
<count>5</count>
<WinNumber>1</WinNumber>
<ItemText>modbus</ItemText>
</Ww>
<Ww>
<count>6</count>
<WinNumber>1</WinNumber>
<ItemText>msgData[0]</ItemText>
</Ww>
<Ww>
<count>7</count>
<WinNumber>1</WinNumber>
<ItemText>CMD_GOTOBOOT</ItemText>
</Ww>
<Ww>
<count>8</count>
<WinNumber>1</WinNumber>
<ItemText>hcan</ItemText>
</Ww>
</WatchWindow1>
<MemoryWindow1>
<Mm>
<WinNumber>1</WinNumber>
<SubType>0</SubType>
<ItemText>0x0800c000</ItemText>
<AccSizeX>0</AccSizeX>
</Mm>
</MemoryWindow1>
<MemoryWindow2>
<Mm>
<WinNumber>2</WinNumber>
<SubType>0</SubType>
<ItemText>0x08009800</ItemText>
<AccSizeX>0</AccSizeX>
</Mm>
</MemoryWindow2>
<Tracepoint>
<THDelay>0</THDelay>
</Tracepoint>
<DebugFlag>
<trace>0</trace>
<periodic>1</periodic>
<aLwin>1</aLwin>
<periodic>0</periodic>
<aLwin>0</aLwin>
<aCover>0</aCover>
<aSer1>0</aSer1>
<aSer2>0</aSer2>
<aPa>0</aPa>
<viewmode>1</viewmode>
<viewmode>0</viewmode>
<vrSel>0</vrSel>
<aSym>0</aSym>
<aTbox>0</aTbox>
@ -254,27 +166,9 @@
<pszMrulep></pszMrulep>
<pSingCmdsp></pSingCmdsp>
<pMultCmdsp></pMultCmdsp>
<SystemViewers>
<Entry>
<Name>System Viewer\BKP</Name>
<WinId>35902</WinId>
</Entry>
<Entry>
<Name>System Viewer\CAN</Name>
<WinId>35904</WinId>
</Entry>
<Entry>
<Name>System Viewer\GPIOB</Name>
<WinId>35905</WinId>
</Entry>
<Entry>
<Name>System Viewer\IWDG</Name>
<WinId>35903</WinId>
</Entry>
</SystemViewers>
<DebugDescription>
<Enable>1</Enable>
<EnableFlashSeq>0</EnableFlashSeq>
<EnableFlashSeq>1</EnableFlashSeq>
<EnableLog>0</EnableLog>
<Protocol>2</Protocol>
<DbgClock>10000000</DbgClock>
@ -386,7 +280,7 @@
<SetRegEntry>
<Number>0</Number>
<Key>DLGTARM</Key>
<Name>(1010=-1605,104,-1155,661,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)</Name>
<Name>(1010=-1,-1,-1,-1,0)(1007=-1,-1,-1,-1,0)(1008=-1,-1,-1,-1,0)(1009=-1,-1,-1,-1,0)</Name>
</SetRegEntry>
<SetRegEntry>
<Number>0</Number>
@ -413,70 +307,25 @@
<Bp>
<Number>0</Number>
<Type>0</Type>
<LineNumber>1300</LineNumber>
<LineNumber>368</LineNumber>
<EnabledFlag>1</EnabledFlag>
<Address>134222082</Address>
<Address>134230816</Address>
<ByteObject>0</ByteObject>
<HtxType>0</HtxType>
<ManyObjects>0</ManyObjects>
<SizeOfObject>0</SizeOfObject>
<BreakByAccess>0</BreakByAccess>
<BreakIfRCount>1</BreakIfRCount>
<Filename>../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_can.c</Filename>
<Filename>..\Core\Bootloader\Src\bootloader.c</Filename>
<ExecCommand></ExecCommand>
<Expression>\\bootloader\../Drivers/STM32F1xx_HAL_Driver/Src/stm32f1xx_hal_can.c\1300</Expression>
<Expression>\\bootloader\../Core/Bootloader/Src/bootloader.c\368</Expression>
</Bp>
</Breakpoint>
<WatchWindow1>
<Ww>
<count>0</count>
<WinNumber>1</WinNumber>
<ItemText>boot,0x10</ItemText>
</Ww>
<Ww>
<count>1</count>
<WinNumber>1</WinNumber>
<ItemText>Inputs</ItemText>
</Ww>
<Ww>
<count>2</count>
<WinNumber>1</WinNumber>
<ItemText>duty_cycle,0x0A</ItemText>
</Ww>
<Ww>
<count>3</count>
<WinNumber>1</WinNumber>
<ItemText>frequency,0x0A</ItemText>
</Ww>
<Ww>
<count>4</count>
<WinNumber>1</WinNumber>
<ItemText>uwTick,0x0A</ItemText>
</Ww>
<Ww>
<count>5</count>
<WinNumber>1</WinNumber>
<ItemText>GoToApp</ItemText>
</Ww>
<Ww>
<count>6</count>
<WinNumber>1</WinNumber>
<ItemText>app_jump_adr</ItemText>
</Ww>
<Ww>
<count>7</count>
<WinNumber>1</WinNumber>
<ItemText>TXDataBoot</ItemText>
</Ww>
<Ww>
<count>8</count>
<WinNumber>1</WinNumber>
<ItemText>\\bootloader\../Core/Bootloader/boot_main.c\boot.error.bit.unknown_cmd</ItemText>
</Ww>
<Ww>
<count>9</count>
<WinNumber>1</WinNumber>
<ItemText>\\bootloader\../Core/Bootloader/boot_main.c\boot.error.bit.write_err</ItemText>
<ItemText>boot</ItemText>
</Ww>
</WatchWindow1>
<MemoryWindow1>
@ -537,15 +386,9 @@
<pszMrulep></pszMrulep>
<pSingCmdsp></pSingCmdsp>
<pMultCmdsp></pMultCmdsp>
<SystemViewers>
<Entry>
<Name>System Viewer\GPIOB</Name>
<WinId>35905</WinId>
</Entry>
</SystemViewers>
<DebugDescription>
<Enable>1</Enable>
<EnableFlashSeq>0</EnableFlashSeq>
<EnableFlashSeq>1</EnableFlashSeq>
<EnableLog>0</EnableLog>
<Protocol>2</Protocol>
<DbgClock>10000000</DbgClock>
@ -555,7 +398,7 @@
<Group>
<GroupName>Application/MDK-ARM</GroupName>
<tvExp>1</tvExp>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
@ -575,7 +418,7 @@
<Group>
<GroupName>Application/User/Core</GroupName>
<tvExp>1</tvExp>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<cbSel>0</cbSel>
<RteFlg>0</RteFlg>
@ -982,8 +825,8 @@
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Core\Bootloader\Src\bootloader.c</PathWithFileName>
<FilenameWithoutPath>bootloader.c</FilenameWithoutPath>
<PathWithFileName>..\Core\Bootloader\Src\boot_main.c</PathWithFileName>
<FilenameWithoutPath>boot_main.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
@ -994,8 +837,8 @@
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Core\Bootloader\Src\boot_main.c</PathWithFileName>
<FilenameWithoutPath>boot_main.c</FilenameWithoutPath>
<PathWithFileName>..\Core\Bootloader\Src\bootloader.c</PathWithFileName>
<FilenameWithoutPath>bootloader.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
@ -1006,6 +849,18 @@
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Core\Bootloader\Src\boot_uart.c</PathWithFileName>
<FilenameWithoutPath>boot_uart.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>36</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Core\Bootloader\Src\boot_can.c</PathWithFileName>
<FilenameWithoutPath>boot_can.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
@ -1013,7 +868,7 @@
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>36</FileNumber>
<FileNumber>37</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1025,7 +880,7 @@
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>37</FileNumber>
<FileNumber>38</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1037,7 +892,7 @@
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>38</FileNumber>
<FileNumber>39</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
@ -1047,18 +902,6 @@
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
<File>
<GroupNumber>5</GroupNumber>
<FileNumber>39</FileNumber>
<FileType>1</FileType>
<tvExp>0</tvExp>
<tvExpOptDlg>0</tvExpOptDlg>
<bDave2>0</bDave2>
<PathWithFileName>..\Core\Bootloader\Src\boot_uart.c</PathWithFileName>
<FilenameWithoutPath>boot_uart.c</FilenameWithoutPath>
<RteFlg>0</RteFlg>
<bShared>0</bShared>
</File>
</Group>
<Group>

View File

@ -667,11 +667,6 @@
<FileType>5</FileType>
<FilePath>..\Core\Bootloader\Inc\boot_project_setup.h</FilePath>
</File>
<File>
<FileName>bootloader.c</FileName>
<FileType>1</FileType>
<FilePath>..\Core\Bootloader\Src\bootloader.c</FilePath>
</File>
<File>
<FileName>boot_main.c</FileName>
<FileType>1</FileType>
@ -728,6 +723,16 @@
</FileArmAds>
</FileOption>
</File>
<File>
<FileName>bootloader.c</FileName>
<FileType>1</FileType>
<FilePath>..\Core\Bootloader\Src\bootloader.c</FilePath>
</File>
<File>
<FileName>boot_uart.c</FileName>
<FileType>1</FileType>
<FilePath>..\Core\Bootloader\Src\boot_uart.c</FilePath>
</File>
<File>
<FileName>boot_can.c</FileName>
<FileType>1</FileType>
@ -799,11 +804,6 @@
</FileArmAds>
</FileOption>
</File>
<File>
<FileName>boot_uart.c</FileName>
<FileType>1</FileType>
<FilePath>..\Core\Bootloader\Src\boot_uart.c</FilePath>
</File>
</Files>
</Group>
<Group>
@ -854,13 +854,13 @@
<NotGenerated>0</NotGenerated>
<InvalidFlash>1</InvalidFlash>
</TargetStatus>
<OutputDirectory>.\</OutputDirectory>
<OutputDirectory>.\bootloader\</OutputDirectory>
<OutputName>bootloader</OutputName>
<CreateExecutable>1</CreateExecutable>
<CreateLib>0</CreateLib>
<CreateHexFile>1</CreateHexFile>
<DebugInformation>1</DebugInformation>
<BrowseInformation>1</BrowseInformation>
<BrowseInformation>0</BrowseInformation>
<ListingPath></ListingPath>
<HexFormatSelection>1</HexFormatSelection>
<Merge32K>0</Merge32K>
@ -1490,15 +1490,20 @@
<FileType>5</FileType>
<FilePath>..\Core\Bootloader\Inc\boot_project_setup.h</FilePath>
</File>
<File>
<FileName>boot_main.c</FileName>
<FileType>1</FileType>
<FilePath>..\Core\Bootloader\Src\boot_main.c</FilePath>
</File>
<File>
<FileName>bootloader.c</FileName>
<FileType>1</FileType>
<FilePath>..\Core\Bootloader\Src\bootloader.c</FilePath>
</File>
<File>
<FileName>boot_main.c</FileName>
<FileName>boot_uart.c</FileName>
<FileType>1</FileType>
<FilePath>..\Core\Bootloader\Src\boot_main.c</FilePath>
<FilePath>..\Core\Bootloader\Src\boot_uart.c</FilePath>
</File>
<File>
<FileName>boot_can.c</FileName>
@ -1520,11 +1525,6 @@
<FileType>1</FileType>
<FilePath>..\Core\Bootloader\Src\boot_jump.c</FilePath>
</File>
<File>
<FileName>boot_uart.c</FileName>
<FileType>1</FileType>
<FilePath>..\Core\Bootloader\Src\boot_uart.c</FilePath>
</File>
</Files>
</Group>
<Group>

95
README.md Normal file
View File

@ -0,0 +1,95 @@
# STM32 Bootloader (Beta)
## Описание
Этот бутлоадер для STM32 реализован как **машина состояний (state machine)**.
Его задача — управлять обновлением прошивки по UART или CAN, а также обеспечивать безопасный переход между приложением и бутлоадером.
---
## Общение с бутлоадером
В [гите](https://git.arktika.cyou/Razvalyaev/boot_terminal) есть терминалка на C# для общения с бутом через SlCan (uart там не сделан).
А вообще реализованы следующие команды
- `0x01` — стирание прошивки во Flash — (`CMD_ERASE`)
- `0x02` — запуск процедуры приёма страницы — (`CMD_START_RECEIVE`)
- `0x03` — запись блока прошивки — (`CMD_WRITE`)
- `0x04` — переход в приложение — (`CMD_GOTOAPP`)
- `0x05`сброс микроконтроллера — (`CMD_RESET`)
- `0x06` — принудительный переход в бутлоадер — (`CMD_GOTOBOOT`)
- `0xAA` — проверка связи (ping) — (`CMD_PING`)
Бутлоадер уже содержит каркас для работы как с **CAN** (и некоторые наработки для **UART**, но не проверенные).
Для расширения функционала и поддержки новых протоколов нужно доопределить следующие функции.
- Приём команды:
```c
BootloaderCommand_t Bootloader_UART_Receive(Bootloader_t *bl); //boot_uart.c
BootloaderCommand_t Bootloader_CAN_Receive(Bootloader_t *bl); //boot_can.c
```
- Читает 1 байт команды и возвращает её (`CMD_ERASE`, `CMD_WRITE`, и т.п.) или, если нет команды, `NO_CMD`.
- Приём страницы прошивки:
```c
void Bootloader_UART_Receive_Page(Bootloader_t *bl); //boot_uart.c
void Bootloader_CAN_Receive_Page(Bootloader_t *bl); //boot_can.c
```
- Принимает блок данных длиной `PAGE_SIZE` байт.
- Принимает CRC32 (4 байта).
- Заполняет поля `bl->fw_buffer`, `bl->fw_len`, `bl->fw_crc`.
---
## Как работает бутлоадер
1. После сброса бутлоадер проверяет сохранённые ошибки и "ключ" прошивки.
- Если приложение корректное — выполняется переход в него.
- Если приложение повреждено или нет ключа — остаётся в бутлоадере.
2. В состоянии `IDLE` бутлоадер слушает команды.
3. Прошивка отправляется страницами, каждая страница проверяется по CRC32.
4. По завершению записи устанавливается "ключ приложения", и возможен переход в основное приложение.
5. При ошибках — возврат в состояние `ERROR`, уведомление хоста и мигание LED. Для сброса ошибок надо перезапустить МК.
---
## Основные особенности бута
- Реализован как конечный автомат со следующими состояниями:
- `INIT` — проверка ключа, и далее переход в приложение или инициализация бута
- `IDLE` — ожидание команды
- `RECEIVE_UART` / `RECEIVE_CAN` — приём страницы прошивки
- `WRITE` — запись принятой страницы во Flash
- `ERASE` — стирание приложения
- `JUMP_TO_APP` — переход к основному приложению
- `JUMP_TO_BOOT` — возврат в бутлоадер
- `RESET` — программный сброс
- `ERROR` — обработка ошибок
- Проверка целостности прошивки с помощью **CRC32**
- Учёт ошибок (hardfault, memmanage, watchdog и т.п.)
- Индикация состояния через LED:
- мигает раз в 500 мс при ошибке
- включается при приеме комады и выключается, когда команда выполнена
---
## Интеграция в проект
Здесь бутлоадер и основное приложение реализованы как отдельные таргеты:
- **Таргет приложения**
Исключает `main()` бутлоадера и компилирует только само приложение.
Адрес загрузки приложения: `0x0800C000`.
- **Таргет бутлоадера**
Исключает код приложения и компилирует только бутлоадер.
Адрес загрузки бутлоадера: `0x08000000`.
Чтобы в основном приложении можно было управлять бутлоадером:
1. Подключить `boot_jump.h` и `boot_jump.c` для взаимодействия с бутлоадером:
```c
#include "boot_jump.h"
```
2. В начале `main()` вызвать `App_Init()`, чтобы установить VTOR на
начало приложения:
```c
App_Init();
```
3. Для перехода в бутлоадер (например, при ошибке или обновлении):
```c
JumpToBootloader();
```