Initial bootloader implementation

Took a publicly available XMODEM bootloader and made it work on HK32F.

Known issues:
- Erase is super slow on this MCU. The upload routine erases all flash before loading, which means the first packet seems to hang. This also erases possibly persistent data stored in unused pages on flash.
This commit is contained in:
true
2023-06-25 01:41:36 -07:00
parent 861caf8bce
commit a47cfa7fa5
83 changed files with 39624 additions and 0 deletions

View File

@@ -0,0 +1,41 @@
/*
* flash.h
*
* Created on: Jun 23, 2023
* Author: true
*/
#ifndef CODE_INC_FLASH_H_
#define CODE_INC_FLASH_H_
#include "hk32f030m.h"
#define FLASH_SIZE 16384 // size of overall flash available on MCU
#define FLASH_ERASE_PAGE_SIZE ((uint16_t)0x80) // erase page size
#define USER_APP_START_ADDR ((uint32_t)FLASH_BASE + 0xa00) // 2560 bytes (20 pages) rsvd for bootloader
#define USER_APP_END_ADDR ((uint32_t)FLASH_BASE + FLASH_SIZE)
/* Status report for the functions. */
typedef enum {
FLASH_OK = 0x00u, /**< The action was successful. */
FLASH_ERROR_SIZE = 0x01u, /**< The binary is too big. */
FLASH_ERROR_WRITE = 0x02u, /**< Writing failed. */
FLASH_ERROR_VERIFY = 0x04u, /**< Writing was successful, but the content of the memory is wrong. */
FLASH_ERROR = 0xFFu /**< Generic error. */
} BL_flash_status;
BL_flash_status flash_erase_user_app();
BL_flash_status flash_write(uint32_t addr, uint8_t *data, uint16_t length);
void jump_to_user_app();
#endif /* CODE_INC_FLASH_H_ */

View File

@@ -0,0 +1,107 @@
/**
******************************************************************************
* @file hk32f030m_conf.h
* @brief configuration file.
******************************************************************************
* @attention
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __HK32F030M_CONF_H
#define __HK32F030M_CONF_H
#ifdef __cplusplus
extern "C" {
#endif
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* ########################## HSE/HSI Values adaptation ##################### */
/**
* @brief Adjust the value of External High Speed oscillator (HSE) used in your application.
* This value is used by the RCC module to compute the system frequency
* (when HSE is used as system clock source, directly or through the PLL).
*/
#define EXTCLK_VALUE ((uint32_t)32000000) /*!< Value of the External oscillator in Hz */
/**
* @brief Internal High Speed oscillator (HSI) value.
* This value is used by the RCC module to compute the system frequency
* (when HSI is used as system clock source, directly or through the PLL).
*/
#define HSI_VALUE ((uint32_t)32000000) /*!< Value of the Internal oscillator in Hz */
/**
* @brief In the following line adjust the Internal High Speed oscillator (HSI) Startup
* Timeout value
*/
#define HSI_STARTUP_TIMEOUT ((uint32_t)0xFFFF) /*!< Time out for start up */
/**
* @brief Internal Low Speed oscillator (LSI) value.
*/
#define LSI_VALUE ((uint32_t)128000)
/*!< Value of the Internal Low Speed oscillator in Hz
The real value may vary depending on the variations */
/* Includes ------------------------------------------------------------------*/
/**
* @brief Include module's header file
*/
#include "hk32f030m_rcc.h"
//#include "hk32f030m_crc.h"
#include "hk32f030m_exti.h"
#include "hk32f030m_flash.h"
#include "hk32f030m_gpio.h"
#include "hk32f030m_misc.h"
//#include "hk32f030m_adc.h"
//#include "hk32f030m_syscfg.h"
//#include "hk32f030m_def.h"
//#include "hk32f030m_i2c.h"
//#include "hk32f030m_iwdg.h"
#include "hk32f030m_pwr.h"
//#include "hk32f030m_spi.h"
//#include "hk32f030m_tim.h"
#include "hk32f030m_usart.h"
//#include "hk32f030m_iwdg.h"
//#include "hk32f030m_wwdg.h"
//#include "hk32f030m_awu.h"
//#include "hk32f030m_beep.h"
/* Exported macro ------------------------------------------------------------*/
/* ########################## Assert Selection ############################## */
/**
* @brief Uncomment the line below to expanse the "assert_param" macro in the
* drivers code
*/
//#define USE_FULL_ASSERT (1U)
#ifdef USE_FULL_ASSERT
/**
* @brief The assert_param macro is used for function's parameters check.
* @param expr: If expr is false, it calls assert_failed function
* which reports the name of the source file and the source
* line number of the call that failed.
* If expr is true, it returns no value.
* @retval None
*/
#define assert_param(expr) ((expr) ? (void)0U : assert_failed((char *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(char* file, uint32_t line);
#else
#define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */
#ifdef __cplusplus
}
#endif
#endif /* __HK32F030M_CONF_H */
/************************ (C) COPYRIGHT MKMcircoChuip *****END OF FILE****/

View File

@@ -0,0 +1,27 @@
/**
******************************************************************************
* @file hk32f030m_it.h
* @brief This file contains the headers of the interrupt handlers.
******************************************************************************
*
******************************************************************************
*/
#ifndef __HK32F030M_IT_H
#define __HK32F030M_IT_H
#ifdef __cplusplus
extern "C" {
#endif
/* Exported functions prototypes ---------------------------------------------*/
void NMI_Handler(void);
void HardFault_Handler(void);
void SVC_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
#ifdef __cplusplus
}
#endif
#endif /* __HK32F030M_IT_H */

View File

@@ -0,0 +1,32 @@
/*
* usart.h
*
* Created on: Jun 23, 2023
* Author: true
*/
#ifndef CODE_INC_USART_H_
#define CODE_INC_USART_H_
#define COMM_TIMEOUT 512000;
/* Status report for the functions. */
typedef enum {
COMM_OK = 0x00, // success
COMM_RX_TIMEOUT = 0xe1, // data did not arrive in time
COMM_ERROR = 0xff // all other errors
} BL_comm_status;
void comm_init();
BL_comm_status comm_rx(uint8_t *data, uint16_t length);
BL_comm_status comm_tx_str(uint8_t *data);
BL_comm_status comm_tx_byte(uint8_t data);
#endif /* CODE_INC_USART_H_ */

View File

@@ -0,0 +1,36 @@
/*
* user_io.h
*
* Created on: Jun 23, 2023
* Author: true
*/
#ifndef CODE_INC_USER_IO_H_
#define CODE_INC_USER_IO_H_
#include "hk32f030m.h"
// bootloader activation button (MODE button)
#define BTN_CLK RCC_AHBPeriph_GPIOA
#define BTN_PORT GPIOA
#define BTN_PIN 1
#define BTN_ACT_DIR 0 // when pushed, is signal high (1) or low (0)?
// bootloader indicator LED
#define LED_CLK RCC_AHBPeriph_GPIOD
#define LED_PORT GPIOD
#define LED_PIN 7
#define LED_ACT_DIR 0 // to light, does signal need to be high (1) or low (0)?
void user_io_init();
void user_led_init();
void user_led_set(uint8_t lit);
#endif /* CODE_INC_USER_IO_H_ */

View File

@@ -0,0 +1,79 @@
/*
* xmodem.h
*
* Created on: Jun 23, 2023
* Author: true
*
* originally Copyright (c) 2018 Ferenc Nemeth - https://github.com/ferenc-nemeth - 21 Dec 2018
* modified for use with HK32F and my coding style.
*
* under MIT license.
*/
#ifndef CODE_INC_XMODEM_H_
#define CODE_INC_XMODEM_H_
#include <stdbool.h>
#include <stdint.h>
#include "flash.h"
#include "usart.h"
/* Xmodem (128 bytes) packet format
* Byte 0: Header
* Byte 1: Packet number
* Byte 2: Packet number complement
* Bytes 3-130: Data
* Bytes 131-132: CRC
*/
/* Xmodem (1024 bytes) packet format
* Byte 0: Header
* Byte 1: Packet number
* Byte 2: Packet number complement
* Bytes 3-1026: Data
* Bytes 1027-1028: CRC
*/
/* Maximum allowed errors (user defined). */
#define X_MAX_ERRORS ((uint8_t)3u)
/* Sizes of the packets. */
#define X_PACKET_NUMBER_SIZE ((uint16_t)2u)
#define X_PACKET_128_SIZE ((uint16_t)128u)
#define X_PACKET_1024_SIZE ((uint16_t)1024u)
#define X_PACKET_CRC_SIZE ((uint16_t)2u)
/* Indexes inside packets. */
#define X_PACKET_NUMBER_INDEX ((uint16_t)0u)
#define X_PACKET_NUMBER_COMPLEMENT_INDEX ((uint16_t)1u)
#define X_PACKET_CRC_HIGH_INDEX ((uint16_t)0u)
#define X_PACKET_CRC_LOW_INDEX ((uint16_t)1u)
/* Bytes defined by the protocol. */
#define X_SOH ((uint8_t)0x01u) /**< Start Of Header (128 bytes). */
#define X_STX ((uint8_t)0x02u) /**< Start Of Header (1024 bytes). */
#define X_EOT ((uint8_t)0x04u) /**< End Of Transmission. */
#define X_ACK ((uint8_t)0x06u) /**< Acknowledge. */
#define X_NAK ((uint8_t)0x15u) /**< Not Acknowledge. */
#define X_CAN ((uint8_t)0x18u) /**< Cancel. */
#define X_C ((uint8_t)0x43u) /**< ASCII "C" to notify the host we want to use CRC16. */
/* Status report for the functions. */
typedef enum {
X_OK = 0x00u, /**< The action was successful. */
X_ERROR_CRC = 0x01u, /**< CRC calculation error. */
X_ERROR_NUMBER = 0x02u, /**< Packet number mismatch error. */
X_ERROR_UART = 0x04u, /**< UART communication error. */
X_ERROR_FLASH = 0x08u, /**< Flash related error. */
X_ERROR = 0xFFu /**< Generic error. */
} BL_xmodem_status;
void xmodem_receive(void);
#endif /* XMODEM_H_ */

View File

@@ -0,0 +1,131 @@
/*
* flash.c
*
* Created on: Jun 23, 2023
* Author: true
*/
#include "hk32f030m.h"
#include "flash.h"
/**
* @brief This function erases the user application area, from start to end of flash.
* @return status: Report about the success of the erasing.
*/
__attribute__ ((long_call, section(".ramfunc"))) BL_flash_status flash_erase_user_app()
{
// uint32_t timeout;
uint32_t page;
FLASH_Unlock();
for (page = USER_APP_START_ADDR; page < USER_APP_END_ADDR; page += FLASH_ERASE_PAGE_SIZE) {
if (FLASH_ErasePage(page) != FLASH_COMPLETE) {
FLASH_Lock();
return FLASH_ERROR;
}
/*
// clear end of operation bit
FLASH->SR &= ~FLASH_FLAG_EOP;
// start erasing page
FLASH->CR |= FLASH_CR_PER;
FLASH->AR = page;
FLASH->CR |= FLASH_CR_STRT;
// wait wait for operation to complete
timeout = FLASH_ER_PRG_TIMEOUT;
while ((FLASH->SR & FLASH_FLAG_BSY)) {
timeout--;
if (!timeout) {
FLASH_Lock();
return FLASH_ERROR;
}
}
// disable page erase bit
FLASH->CR &= ~FLASH_CR_PER;
*/
}
FLASH_Lock();
return FLASH_OK;
}
/**
* @brief This function flashes the memory.
* @param addr: First address to be written to.
* @param *data: Array of the data that we want to write.
* @param *length: Size of the data array, in 16-bit words.
* @return status: Report about the success of the writing.
*/
__attribute__ ((long_call, section(".ramfunc"))) BL_flash_status flash_write(uint32_t addr, uint8_t *data, uint16_t length)
{
BL_flash_status status = FLASH_OK;
FLASH_Unlock();
/* Loop through the data. */
for (uint32_t i = 0u; (i < length) && (status == FLASH_OK); i++) {
if (addr >= USER_APP_END_ADDR) {
status = FLASH_ERROR_SIZE;
break;
} else {
/* The actual flashing. If there is an error, then report it. */
if (FLASH_ProgramByte(addr, data[i]) != FLASH_COMPLETE) {
status |= FLASH_ERROR_WRITE;
}
/* Read back the content of the memory. If it is wrong, then report an error. */
if (data[i] != (*(volatile uint8_t *)addr)) {
status |= FLASH_ERROR_VERIFY;
}
/* Shift the address by a word. */
addr++;
}
}
FLASH_Lock();
return status;
}
void jump_to_user_app()
{
void (*user_app)(void);
// stop systick interrupt; reset to defaults
SysTick->CTRL = 0;
SysTick->LOAD = 0xffffff;
SysTick->VAL = 0xffffff;
// stop USART; reset to defaults
USART1->CR1 = 0;
USART1->CR1 = 0;
USART1->BRR = 0;
// disable configured interrupts
// NVIC->ICER[0] = 0;
// configure jump location
user_app = (void (*)(void))(*(volatile uint32_t *)(USER_APP_START_ADDR + 4));
// configure stack pointer
__set_MSP(*(volatile uint32_t*)USER_APP_START_ADDR);
// configure interrupt vector table
// SCB->VTOR = USER_APP_START_ADDR;
// cortex m0 doesn't have VTOR to remap vector table.
// to address this, HK MCU has a flash offset thing which
// remaps 256 bytes specified by an offset, to to 0x08000000
FLASH->INT_VEC_OFFSET = USER_APP_START_ADDR & 0x3ffc;
// let's do this
user_app();
}

View File

@@ -0,0 +1,148 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file hk32f030m_it.c
* @brief Interrupt Service Routines.
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "../../code/Inc/hk32f030m_it.h"
#include "user_io.h"
#include "hk32f030m_tim.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/******************************************************************************/
/* Cortex-M0 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
/* USER CODE END NonMaskableInt_IRQn 1 */
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
/**
* @brief This function handles System service call via SWI instruction.
*/
void SVC_Handler(void)
{
/* USER CODE BEGIN SVC_IRQn 0 */
/* USER CODE END SVC_IRQn 0 */
/* USER CODE BEGIN SVC_IRQn 1 */
/* USER CODE END SVC_IRQn 1 */
}
/**
* @brief This function handles Pendable request for system service.
*/
void PendSV_Handler(void)
{
/* USER CODE BEGIN PendSV_IRQn 0 */
/* USER CODE END PendSV_IRQn 0 */
/* USER CODE BEGIN PendSV_IRQn 1 */
/* USER CODE END PendSV_IRQn 1 */
}
/**
* @brief This function handles System tick timer.
*/
__attribute__ ((long_call, section(".ramfunc"))) void SysTick_Handler(void)
{
static uint8_t led = 0;
led ^= 1;
#if LED_ACT_DIR == 0
if (led) LED_PORT->BRR = (1 << LED_PIN);
else LED_PORT->BSRR = (1 << LED_PIN);
#else
if (led) LED_PORT->BSRR = (1 << LED_PIN);
else LED_PORT->BRR = (1 << LED_PIN);
#endif
}
/******************************************************************************/
/* hk32f030m Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_hk32f030m.s). */
/******************************************************************************/
/* USER CODE BEGIN 1 */
//void TIM2_IRQHandler(void)
//{
// if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
// {
// TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// }
//}
/* USER CODE END 1 */
/************************ (C) COPYRIGHT HKMicroChip *****END OF FILE****/

View File

@@ -0,0 +1,93 @@
/*
* 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"
int main(void)
{
uint8_t btn;
//volatile uint16_t timeout = 0xffff;
// ensure the bootloader is write protected
// TODO
// configure button and LED
user_io_init();
// need a small delay to ensure the button pullup/down will charge/discharge pin capacitance
//while (--timeout);
// 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 // button pushed
|| (*(volatile uint32_t *)USER_APP_START_ADDR) == 0x00000000 // flash empty
|| (*(volatile uint32_t *)USER_APP_START_ADDR) == 0xFFFFFFFF) { // flash empty
// bootloader is activated, show it
user_led_init();
// configure serial comms and print welcome banner
// todo: implement autobaud?
comm_init();
const uint8_t *banner_bar = (uint8_t *)"\r\n================================================\r\n";
comm_tx_str((uint8_t *)banner_bar);
comm_tx_str((uint8_t *)"trueControl HK32F XMODEM Flash Bootloader v0.0.1");
comm_tx_str((uint8_t *)banner_bar);
// wait for firmware load or reset
while(1) {
comm_tx_str((uint8_t*)"\r\nsend binary file using XMODEM to update firmware");
comm_tx_str((uint8_t*)"\r\nthe first data block will take time. it's normal\r\n\r\n");
xmodem_receive();
// if xmodem exits, there was a failure. repeat the process.
comm_tx_str((uint8_t*)"\r\nfailed to update. try again.\r\n");
}
}
// bootloader is 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 */

View File

@@ -0,0 +1,199 @@
/*
* usart.c
*
* Created on: Jun 23, 2023
* Author: true
*/
#include "hk32f030m.h"
#include "usart.h"
#include <string.h>
#define BAUDRATE 115200
#define USART_DEVICE USART1
#define USART_TX_GPIO_CLK RCC_AHBPeriph_GPIOD
#define USART_TX_PORT GPIOD
#define USART_TX_PIN 1
#define USART_TX_PINSRC GPIO_PinSource1
#define USART_RX_GPIO_CLK RCC_AHBPeriph_GPIOB
#define USART_RX_PORT GPIOB
#define USART_RX_PIN 4
#define USART_RX_PINSRC GPIO_PinSource4
// inefficient, but we have time and space
#define RX_BUF_LEN 1040
uint8_t rxbuf[RX_BUF_LEN];
uint16_t rxptr = 0;
uint16_t rxlen = 0;
/**
* @brief Initializes USART peripheral at fixed baud rate.
*/
void comm_init()
{
// USART_InitTypeDef usart;
// NVIC_InitTypeDef nvic;
// enable GPIO clock
RCC_AHBPeriphClockCmd(USART_TX_GPIO_CLK | USART_RX_GPIO_CLK, ENABLE);
// configure USART GPIO
GPIO_PinAFConfig(USART_TX_PORT, USART_TX_PINSRC, GPIO_AF_1);
GPIO_PinAFConfig(USART_RX_PORT, USART_RX_PINSRC, GPIO_AF_1);
USART_RX_PORT->MODER &= ~(GPIO_MODER_MODER0 << 2 * USART_RX_PIN);
USART_RX_PORT->MODER |= (GPIO_Mode_AF << 2 * USART_RX_PIN); // alt function
USART_TX_PORT->MODER &= ~(GPIO_MODER_MODER0 << 2 * USART_TX_PIN);
USART_TX_PORT->OTYPER &= ~(GPIO_OTYPER_OT_0 << USART_TX_PIN); // push pull
USART_TX_PORT->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEEDR0 << 2 * USART_TX_PIN); // slow
USART_TX_PORT->MODER |= (GPIO_Mode_AF << 2 * USART_TX_PIN); // alt function
// configure NVIC
/*
nvic.NVIC_IRQChannel = USART1_IRQn;
nvic.NVIC_IRQChannelPriority = 1;
nvic.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic);
*/
// enable USART clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
/*
// configure USART peripheral
usart.USART_BaudRate = BAUDRATE;
usart.USART_WordLength = USART_WordLength_8b;
usart.USART_StopBits = USART_StopBits_1;
usart.USART_Parity = USART_Parity_No;
usart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &usart);
*/
// configure USART peripheral manually
// we do this as the library uses division which bloats flash space
USART1->CR1 = 0; // disable USART
USART1->CR1 = USART_Mode_Rx | USART_Mode_Tx; // 8 bits, no parity, tx/rx enabled
USART1->CR2 = 0; // 1 stop bit
USART1->CR3 = 0;
USART1->BRR = 0x45; // 115200 baud rate @8MHz (per datasheet)
// enable USART interrupts
// USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// enable USART
USART1->CR1 |= USART_CR1_UE;
}
/**
* @brief Receives data from USART.
* @param *data: Array to save the received data.
* @param length: Size of the data.
* @return status: Report about the success of the receiving.
*/
BL_comm_status comm_rx(uint8_t *data, uint16_t length)
{
uint16_t i;
uint16_t timeout;
/*
// wait for more data to arrive
timeout = COMM_TIMEOUT;
while (rxlen < length) {
timeout--;
if (!timeout) return COMM_RX_TIMEOUT;
}
// copy data to final buffer
for (i = 0; i < length; i++) {
data[i] = rxbuf[rxptr++];
if (rxptr >= RX_BUF_LEN) rxptr = 0;
rxlen--;
}
*/
for (i = 0; i < length; i++) {
timeout = 0xffff;
while (!(USART1->ISR & USART_ISR_RXNE_Msk) && timeout) {
timeout--;
}
if (timeout) {
// there is new data waiting for us
data[i] = USART1->RDR;
} else {
// there is no data returned in time
return COMM_ERROR;
}
}
return COMM_OK;
}
/**
* @brief Transmits a single char to UART.
* @param *data: The char.
* @return status: Report about the success of the transmission.
*/
BL_comm_status comm_tx_byte(uint8_t data)
{
USART_SendData(USART1, data);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return COMM_OK;
}
/**
* @brief Transmits a null-terminated string over USART.
* @param *data: String array.
* @return status: Report about the success of the transmission.
*/
BL_comm_status comm_tx_str(uint8_t *data)
{
uint8_t i;
uint16_t len = 0;
len = strlen((const char *)data);
for(i = 0; i < len; i++) {
comm_tx_byte(data[i]);
}
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
return COMM_OK;
}
/*
void USART1_IRQHandler()
{
uint8_t data;
if (USART1->ISR & USART_ISR_RXNE_Msk) {
// read data
data = USART1->RDR;
// only add data if there is room
if (rxlen >= RX_BUF_LEN) return;
rxlen++;
// next position
if (++rxptr >= RX_BUF_LEN) rxptr = 0;
// save data
rxbuf[rxptr] = data;
}
}
*/

View File

@@ -0,0 +1,56 @@
/*
* user_io.c
*
* Created on: Jun 23, 2023
* Author: true
*/
#include "hk32f030m.h"
#include "user_io.h"
void user_io_init()
{
// enable gpio clocks
RCC_AHBPeriphClockCmd(BTN_CLK | LED_CLK, ENABLE);
// configure led
#if LED_ACT_DIR == 0
LED_PORT->BSRR = (1 << LED_PIN); // idle high
#else
LED_PORT->BRR = (1 << LED_PIN); // idle low
#endif
LED_PORT->OTYPER &= ~(GPIO_OTYPER_OT_0 << LED_PIN); // push pull
LED_PORT->OSPEEDR &= ~(GPIO_OSPEEDR_OSPEEDR0 << 2 * LED_PIN); // slow
LED_PORT->MODER &= ~(GPIO_MODER_MODER0 << 2 * LED_PIN);
LED_PORT->MODER |= (GPIO_Mode_OUT << 2 * LED_PIN); // output
// configure button
BTN_PORT->MODER &= ~(GPIO_MODER_MODER0 << 2 * BTN_PIN); // input
BTN_PORT->PUPDR &= ~(GPIO_PUPDR_PUPDR0 << 2 * BTN_PIN);
#if BTN_ACT_DIR == 0
BTN_PORT->PUPDR |= (GPIO_PuPd_UP << 2 * BTN_PIN); // pulled high
#else
BTN_PORT->PUPDR |= (GPIO_PuPd_DOWN << 2 * BTN_PIN); // pulled low
#endif
}
void user_led_init()
{
// led blinking timer; set LED flash rate here
SysTick_Config(8000000 / 20);
}
void user_led_set(uint8_t lit)
{
#if LED_ACT_DIR == 0
if (lit) LED_PORT->BRR = (1 << LED_PIN);
else LED_PORT->BSRR = (1 << LED_PIN);
#else
if (lit) LED_PORT->BSRR = (1 << LED_PIN);
else LED_PORT->BRR = (1 << LED_PIN);
#endif
}

View File

@@ -0,0 +1,267 @@
/*
* xmodem.c
*
* Created on: Jun 23, 2023
* Author: true
*
* originally Copyright (c) 2018 Ferenc Nemeth - https://github.com/ferenc-nemeth - 21 Dec 2018
* modified for use with HK32F and my coding style.
*
* under MIT license.
*/
#include "xmodem.h"
#include "flash.h"
#include "usart.h"
/* Global variables. */
static uint8_t xmodem_packet_number = 1u; /**< Packet number counter. */
static uint32_t xmodem_flash_w_addr = 0u; /**< Address where we have to write. */
static uint8_t x_first_packet_rcvd = 0; /**< First packet or not. */
/* Local functions. */
static uint16_t xmodem_calc_crc(uint8_t *data, uint16_t length);
static BL_xmodem_status xmodem_handle_packet(uint8_t size);
static BL_xmodem_status xmodem_error_handler(uint8_t *error_number, uint8_t max_error_number);
/**
* @brief This function is the base of the Xmodem protocol.
* When we receive a header from UART, it decides what action it shall take.
* @param void
* @return void
*/
void xmodem_receive(void)
{
volatile BL_xmodem_status status = X_OK;
uint8_t error_number = 0u;
x_first_packet_rcvd = 0;
xmodem_packet_number = 1u;
xmodem_flash_w_addr = USER_APP_START_ADDR;
/* Loop until there isn't any error (or until we jump to the user application). */
while (status == X_OK) {
uint8_t header = 0x00u;
/* Get the header from UART. */
BL_comm_status status_c = comm_rx(&header, 1);
/* Spam the host (until we receive something) with ACSII "C", to notify it, we want to use CRC-16. */
if ((status_c != COMM_OK) && (x_first_packet_rcvd == false)) {
comm_tx_byte(X_C);
}
/* Uart timeout or any other errors. */
else if ((status_c != COMM_OK) && (x_first_packet_rcvd)) {
status = xmodem_error_handler(&error_number, X_MAX_ERRORS);
}
/* Do nothing. */
else {
}
/* The header can be: SOH, STX, EOT and CAN. */
BL_xmodem_status packet_status = X_ERROR;
switch(header) {
/* 128 or 1024 bytes of data. */
case X_SOH:
case X_STX: {
/* If the handling was successful, then send an ACK. */
packet_status = xmodem_handle_packet(header);
if (packet_status == X_OK) {
comm_tx_byte(X_ACK);
}
/* If the error was flash related, then immediately set the error counter to max (graceful abort). */
else if (packet_status == X_ERROR_FLASH) {
error_number = X_MAX_ERRORS;
status = xmodem_error_handler(&error_number, X_MAX_ERRORS);
}
/* Error while processing the packet, either send a NAK or do graceful abort. */
else {
status = xmodem_error_handler(&error_number, X_MAX_ERRORS);
}
break;
}
/* End of Transmission. */
case X_EOT: {
/* ACK, feedback to user (as a text), then jump to user application. */
comm_tx_byte(X_ACK);
comm_tx_str((uint8_t*)"\r\nfirmware updated.\r\nstarting firmware.\r\n\r\n");
jump_to_user_app();
break;
}
/* Abort from host. */
case X_CAN: {
status = X_ERROR;
break;
}
/* Wrong header. */
default: {
if (status_c == COMM_OK) {
status = xmodem_error_handler(&error_number, X_MAX_ERRORS);
}
break;
}
}
}
}
/**
* @brief Calculates the CRC-16 for the input package.
* @param *data: Array of the data which we want to calculate.
* @param length: Size of the data, either 128 or 1024 bytes.
* @return status: The calculated CRC.
*/
static uint16_t xmodem_calc_crc(uint8_t *data, uint16_t length)
{
uint16_t crc = 0u;
while (length)
{
length--;
crc = crc ^ ((uint16_t)*data++ << 8u);
for (uint8_t i = 0u; i < 8u; i++)
{
if (crc & 0x8000u)
{
crc = (crc << 1u) ^ 0x1021u;
}
else
{
crc = crc << 1u;
}
}
}
return crc;
}
/**
* @brief This function handles the data packet we get from the xmodem protocol.
* @param header: SOH or STX.
* @return status: Report about the packet.
*/
static BL_xmodem_status xmodem_handle_packet(uint8_t header)
{
BL_xmodem_status status = X_OK;
uint16_t size = 0u;
/* 2 bytes for packet number, 1024 for data, 2 for CRC*/
uint8_t rcvd_packet_number[X_PACKET_NUMBER_SIZE];
uint8_t rcvd_packet_data[X_PACKET_1024_SIZE];
uint8_t rcvd_packet_crc[X_PACKET_CRC_SIZE];
/* Get the size of the data. */
if (header == X_SOH) {
size = X_PACKET_128_SIZE;
} else if (header == X_STX) {
size = X_PACKET_1024_SIZE;
} else {
/* Wrong header type. This shoudn't be possible... */
status |= X_ERROR;
}
BL_comm_status status_c = COMM_OK;
/* Get the packet number, data and CRC from UART. */
status_c |= comm_rx(&rcvd_packet_number[0u], X_PACKET_NUMBER_SIZE);
status_c |= comm_rx(&rcvd_packet_data[0u], size);
status_c |= comm_rx(&rcvd_packet_crc[0u], X_PACKET_CRC_SIZE);
/* Merge the two bytes of CRC. */
uint16_t crc_rcvd = ((uint16_t)rcvd_packet_crc[X_PACKET_CRC_HIGH_INDEX] << 8u)
| ((uint16_t)rcvd_packet_crc[X_PACKET_CRC_LOW_INDEX]);
/* We calculate it too. */
uint16_t crc_calc = xmodem_calc_crc(&rcvd_packet_data[0u], size);
/* Communication error. */
if (status_c != COMM_OK) {
status |= X_ERROR_UART;
}
/* If it is the first packet, then erase the memory. */
if ((status == X_OK) && (x_first_packet_rcvd == false)) {
if (flash_erase_user_app() == FLASH_OK) {
x_first_packet_rcvd = true;
} else {
status |= X_ERROR_FLASH;
}
}
/* Error handling and flashing. */
if (status == X_OK) {
if (xmodem_packet_number != rcvd_packet_number[0u]) {
/* Packet number counter mismatch. */
status |= X_ERROR_NUMBER;
}
if ((rcvd_packet_number[X_PACKET_NUMBER_INDEX] + rcvd_packet_number[X_PACKET_NUMBER_COMPLEMENT_INDEX]) != 255) {
/* The sum of the packet number and packet number complement aren't 255. */
/* The sum always has to be 255. */
status |= X_ERROR_NUMBER;
}
if (crc_calc != crc_rcvd) {
/* The calculated and received CRC are different. */
status |= X_ERROR_CRC;
}
}
/* Do the actual flashing if there weren't any errors. */
if ((status == X_OK) &&
(flash_write(xmodem_flash_w_addr, rcvd_packet_data, size)) != FLASH_OK) {
/* Flashing error. */
status |= X_ERROR_FLASH;
}
/* Raise the packet number and the address counters (if there weren't any errors). */
if (status == X_OK) {
xmodem_packet_number++;
xmodem_flash_w_addr += size;
}
return status;
}
/**
* @brief Handles the xmodem error.
* Raises the error counter, then if the number of the errors reached critical, do a graceful abort, otherwise send a NAK.
* @param *error_number: Number of current errors (passed as a pointer).
* @param max_error_number: Maximal allowed number of errors.
* @return status: X_ERROR in case of too many errors, X_OK otherwise.
*/
static BL_xmodem_status xmodem_error_handler(uint8_t *error_number, uint8_t max_error_number)
{
BL_xmodem_status status = X_OK;
/* Raise the error counter. */
(*error_number)++;
/* If the counter reached the max value, then abort. */
if ((*error_number) >= max_error_number) {
/* Graceful abort. */
comm_tx_byte(X_CAN);
comm_tx_byte(X_CAN);
status = X_ERROR;
}
/* Otherwise send a NAK for a repeat. */
else {
comm_tx_byte(X_NAK);
status = X_OK;
}
return status;
}