175 lines
4.2 KiB
C
175 lines
4.2 KiB
C
/*
|
|
* main.c
|
|
*
|
|
* Created on: Jun 23, 2023
|
|
* Author: true
|
|
*
|
|
* UART bootloader for HK32F030M MCU. Tested on HK32F030MF4P6.
|
|
* Button or no-code activation. Otherwise instant jump to user code.
|
|
* Should be easy to port to other platforms. Needs less than 2K RAM.
|
|
*
|
|
* Just a couple days before completing this, LCSC removed this part...
|
|
* it likely won't be very common now, so what's the point? =(
|
|
*/
|
|
|
|
#include "hk32f030m.h"
|
|
#include "flash.h"
|
|
#include "usart.h"
|
|
#include "user_io.h"
|
|
#include "xmodem.h"
|
|
|
|
|
|
|
|
__attribute__((section(".noinit"))) uint32_t bootcheck;
|
|
|
|
|
|
void welcome_banner()
|
|
{
|
|
uint8_t i;
|
|
|
|
comm_tx_str("\r\n\r\n");
|
|
|
|
for (i = 0; i < 31; i++) {
|
|
comm_tx_byte('=');
|
|
}
|
|
|
|
comm_tx_str("\r\ntrueControl HK32F Loader v0.0.3\r\n");
|
|
|
|
for (i = 0; i < 31; i++) {
|
|
comm_tx_byte('=');
|
|
}
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
uint32_t x;
|
|
uint32_t btn;
|
|
uint8_t flash_empty = 0;
|
|
|
|
x = *(volatile uint32_t *)USER_APP_START_ADDR; // first byte of user flash
|
|
if ((x == 0x00000000) || (x == 0xffffffff)) {
|
|
flash_empty = 1;
|
|
}
|
|
|
|
// if we software reset, but have contents in flash
|
|
if (!flash_empty && (RCC->CSR & RCC_CSR_SFTRSTF)) {
|
|
// and have the bootload done key loaded,
|
|
if (bootcheck == 0x1337b007) {
|
|
// then forego all of this shit and jump to the user app
|
|
jump_to_user_app();
|
|
}
|
|
}
|
|
|
|
/*
|
|
// set with a debugger to enable this codepath (testing)
|
|
volatile uint8_t do_this = 0;
|
|
// make sure bootloader is write protected before we write anything to main flash
|
|
const uint8_t pages = (uint8_t)(OB_WRP_Pages0to3 | OB_WRP_Pages4to7);
|
|
uint8_t pagecheck = (uint8_t)(~FLASH_OB_GetWRP());
|
|
if (do_this && (pagecheck & pages) != pages) {
|
|
uint32_t flash_cr;
|
|
uint16_t wrp0;
|
|
uint32_t wrp_addr = FLASH_OB_WRP_ADDRESS;
|
|
uint16_t i;
|
|
|
|
wrp0 = (uint16_t)((pages << 8) | (~pages & 0xff));
|
|
|
|
FLASH_Unlock();
|
|
FLASH_OB_Unlock();
|
|
flash_cr = FLASH->CR;
|
|
|
|
for(i = 0; i < 2; i++) {
|
|
FLASH->CR = flash_cr | FLASH_CR_OPTER;
|
|
FLASH->AR = wrp_addr;
|
|
FLASH->CR = flash_cr | FLASH_CR_OPTER | FLASH_CR_STRT;
|
|
|
|
FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
|
|
|
|
wrp_addr++;
|
|
}
|
|
|
|
FLASH->CR = flash_cr | FLASH_CR_OPTPG;
|
|
OB->WRP0 = wrp0;
|
|
FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
|
|
|
|
FLASH->CR = flash_cr;
|
|
|
|
NVIC_SystemReset();
|
|
}
|
|
*/
|
|
|
|
// start clocks used by peripherals
|
|
RCC->AHBENR = RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | \
|
|
RCC_AHBPeriph_GPIOC | RCC_AHBPeriph_GPIOD;
|
|
RCC->APB2ENR = RCC_APB2Periph_USART1;
|
|
|
|
// set up user IO
|
|
user_io_init();
|
|
|
|
// get button state
|
|
btn = BTN_PORT->IDR & (1 << BTN_PIN);
|
|
#if BTN_ACT_DIR == 0
|
|
btn = (btn == 0) ? 1 : 0;
|
|
#endif
|
|
|
|
// bootloader activation methods
|
|
|
|
if (btn || flash_empty) { // is button pushed, or flash empty or null?
|
|
// bootloader is activated
|
|
// this is where you can can blink an LED... at least
|
|
// when you don't fuck up your circuit so the LED doesn't work
|
|
// in our case we start up systick to do the blinker for us
|
|
SysTick_Config(8000000 / 20);
|
|
|
|
// configure serial comms
|
|
comm_init();
|
|
|
|
welcome_banner();
|
|
|
|
// wait for firmware load or reset
|
|
while (1) {
|
|
// announce bootloader start
|
|
comm_tx_str("\r\n\r\nsend firm.bin file using XMODEM\r\n\r\n");
|
|
|
|
// start listening for data and letting user know to send XMODEM data
|
|
x = xmodem_receive();
|
|
|
|
if (x == X_OK) {
|
|
comm_tx_str("\r\nfw updated; starting...\r\n\r\n");
|
|
break;
|
|
}
|
|
|
|
// if we've spammed enough, then reboot so the banner is shown again
|
|
if (x == X_TIMEOUT) {
|
|
welcome_banner();
|
|
} else {
|
|
// if xmodem_receive exits, there was a failure. repeat the process.
|
|
comm_tx_str("\r\nfailed to update. try again.\r\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
// bootloader is either done not activated; jump to user code
|
|
jump_to_user_app();
|
|
}
|
|
|
|
#ifdef USE_FULL_ASSERT
|
|
/**
|
|
* @brief Reports the name of the source file and the source line number
|
|
* where the assert_param error has occurred.
|
|
* @param file: pointer to the source file name
|
|
* @param line: assert_param error line source number
|
|
* @retval None
|
|
*/
|
|
void assert_failed(char* file , uint32_t line)
|
|
{
|
|
/* User can add his own implementation to report the file name and line number,
|
|
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
|
|
/* Infinite loop */
|
|
|
|
while (1)
|
|
{
|
|
}
|
|
}
|
|
#endif /* USE_FULL_ASSERT */
|