/* * 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 */