Browse Source

git-svn-id: https://svn.ti-link.com.cn/svn/G100/PowerManagement/PS100@5232 efa15413-de06-ce4f-a13f-2108acd733b2

liuguo1 5 năm trước cách đây
mục cha
commit
3f9c9acee0

+ 97 - 0
bootloader/bl_common.h

@@ -0,0 +1,97 @@
+/*!
+    \file  gd32f1x0_libopt.h
+    \brief library optional for gd32f1x0
+*/
+
+/*
+    Copyright (C) 2016 GigaDevice
+
+    2016-01-15, V1.0.0, demo for GD32F1x0(x=3,5)
+    2016-05-13, V2.0.0, demo for GD32F1x0(x=3,5)
+*/
+
+#ifndef BL_COMMON_H
+#define BL_COMMON_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdlib.h>
+#include "gd32f10x.h"
+#include "gd32f10x_libopt.h"
+
+
+
+
+#define BOOTLOADER_NVIC_OFFSET                       (0)
+
+
+
+
+
+
+#define USART_BAUND    (38400)
+#define DEBUG_MODE     0
+
+
+
+
+typedef struct
+{
+	uint8_t set;
+	uint16_t count;
+}DELAY_COMMON;
+
+typedef struct
+{
+	uint8_t enable;
+	uint8_t set;
+	uint16_t count;
+}CTR_DELAY_COMMON;
+
+
+
+
+#define NO_EVENT                     0x00000000UL
+#define RS485_RECEIVE_END_EVENT      0x00000001UL
+#define EVENT_CAN_RECEIVE_FINISH	 0x00000002UL
+#define RS485_2_RECEIVE_END_EVENT    0x00000004UL
+#define SUB_BMS_1_RS485_DISC_EVENT   0x00000008UL
+#define SUB_BMS_2_RS485_DISC_EVENT   0x00000010UL
+#define MEASURE_TEMPERATURE_EVENT    0x00000020UL
+#define SUB_BMS_1_SEND_CMD_EVENT     0x00000040UL
+#define SUB_BMS_2_SEND_CMD_EVENT     0x00000080UL
+#define BMS_1_RESEND_CMD_EVENT       0x00000100UL
+#define BMS_2_RESEND_CMD_EVENT       0x00000200UL
+#define END_CTR_RESEND_CMD_EVENT     0x00000400UL
+#define ADAS_RESEND_CMD_EVENT        0x00000800UL
+#define BMS_1_SELF_TIMEOUT_EVENT     0x00001000UL
+#define BMS_2_SELF_TIMEOUT_EVENT     0x00002000UL
+#define END_CTR_SELF_TIMEOUT_EVENT   0x00004000UL
+#define ADAS_SELF_TIMEOUT_EVENT      0x00008000UL
+#define ADAS_PWM_1_TIMEOUT_EVENT     0x00010000UL
+#define ADAS_PWM_2_TIMEOUT_EVENT     0x00020000UL
+#define ADAS_MEAS_1_FINISH_EVENT     0x00040000UL
+#define ADAS_MEAS_2_FINISH_EVENT     0x00080000UL
+#define RE_INITIAL_CAN_EVENT         0x00100000UL
+
+extern uint32_t g_event;
+
+
+// 2017/9/12  19:37:50
+#define UTC_INITIAL   (1505216210)  
+
+
+typedef struct
+{
+	uint8_t set;
+	uint16_t count;
+}UART_TOUT;
+
+
+void WatchDog_Initial(uint8_t period);
+
+
+#endif /* GD32F1X0_LIBOPT_H */
+

+ 187 - 0
bootloader/bl_drv_usart.c

@@ -0,0 +1,187 @@
+#include "bl_common.h" 
+#include "bl_drv_usart.h"
+
+ /* transmit buffer and receive buffer */
+static uint8_t tx_buffer[TX_BUFFER_SIZE];
+static uint8_t rx_buffer[RX_BUFFER_SIZE];
+/* counter of transmit buffer and receive buffer */
+static uint16_t tx_count = 0, rx_count = 0;
+/* size of transmit buffer and receive buffer */
+static uint32_t /*rx_buffer_size ,*/ tx_buffer_size;
+
+DELAY_COMMON uart1_tout;
+
+
+void Enable_Uart1_Timer(uint8_t enable,uint16_t timeout)
+{
+	if(enable)
+	{
+		uart1_tout.set = 1;
+		uart1_tout.count = timeout;
+	}
+	else
+	memset(&uart1_tout,0x00,sizeof(uart1_tout));
+	
+}
+
+static void Reset_RX_Buffer(void)
+{
+	memset(rx_buffer,0x00,sizeof(rx_buffer));
+	rx_count = 0;
+}
+
+uint16_t Get_RS485_Data(uint8_t * dbuf,uint16_t dbuf_len)
+{
+	if(dbuf == NULL || dbuf_len < rx_count)
+		return 0;
+
+	memcpy(dbuf,rx_buffer,rx_count);
+	dbuf_len = rx_count;
+	Reset_RX_Buffer();
+	
+	return dbuf_len;
+}
+
+
+
+int8_t Send_Data_RS485(uint8_t*data,uint16_t size)
+{
+	if(data == NULL || size == 0 || size > sizeof(tx_buffer))
+		return 0;
+
+	
+	memcpy(tx_buffer,data,size);
+	tx_count = 0;
+	tx_buffer_size = size;
+	
+	//
+#if 0
+
+	do
+	{
+		while(RESET == usart_flag_get(USART0, USART_FLAG_TC));
+		usart_data_transmit(USART0,tx_buffer[tx_count++]);
+	}while(tx_count < tx_buffer_size);
+#else
+	//while(RESET == USART_GetFlagStatus(USART1, USART_FLAG_TC));
+	//USART_SendData(USART1,tx_buffer[tx_count++]);
+	usart_interrupt_enable(USART0, USART_INT_TC);
+	usart_interrupt_flag_get(USART0, USART_INT_TC);
+#endif	
+	return 1;
+}
+
+
+
+/*
+ * 函数名:USART1_Config
+ * 描述  :USART1 GPIO 配置,工作模式配置。9600 8-N-1
+ * 输入  :无
+ * 输出  : 无
+ * 调用  :外部调用
+ */
+void Usart1_Initial(void)
+{
+
+	
+	/* enable GPIO clock */
+    rcu_periph_clock_enable(RCU_GPIOA);
+	rcu_periph_clock_enable(RCU_AF);
+	rcu_periph_clock_enable(RCU_USART0);
+    /* connect port to USARTx_Tx */
+    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
+
+    /* connect port to USARTx_Rx */
+    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
+
+    /* USART configure */
+	/* enable USART clock */
+    
+    usart_deinit(USART0);
+    usart_baudrate_set(USART0, USART_0_BAUND);
+    usart_word_length_set(USART0, USART_WL_8BIT);
+    usart_stop_bit_set(USART0, USART_STB_1BIT);
+    usart_parity_config(USART0, USART_PM_NONE);
+    usart_hardware_flow_rts_config(USART0, USART_RTS_DISABLE);
+    usart_hardware_flow_cts_config(USART0, USART_CTS_DISABLE);
+    usart_receive_config(USART0, USART_RECEIVE_ENABLE);
+    usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
+	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
+	nvic_irq_enable(USART0_IRQn, 1, 0);
+	//usart_interrupt_enable(USART0, USART_INT_RBNE);
+
+#if UART_0_IDAR
+	/*USART_ClockInitTypeDef  USART_ClockInitStruct; 
+
+	USART_ClockInitStruct.USART_Clock = USART_Clock_Disable;
+	USART_ClockInitStruct.USART_CPOL = USART_CPOL_Low;
+	USART_ClockInitStruct.USART_CPHA = USART_CPHA_2Edge;
+	USART_ClockInitStruct.USART_LastBit = USART_LastBit_Disable;
+	
+	USART_ClockInit(USART1,  &USART_ClockInitStruct);*/
+
+	
+	usart_enable(USART0); 
+
+	usart_prescaler_config(USART0, 0x01);
+	usart_irda_lowpower_config(USART0, USART_IRLP_NORMAL);
+	usart_irda_mode_enable(USART0);
+#else
+	usart_enable(USART0);
+#endif
+    
+	
+	
+	
+}
+
+
+
+/*******************************************************************************
+* Function Name  : USART1_IRQHandler
+* Description    : This function handles USART1 global interrupt request.
+* Input          : None
+* Output         : None
+* Return         : None
+*******************************************************************************/
+void USART0_IRQHandler(void)
+{
+	uint8_t c;
+	
+	/* 串口接收 */
+	//if(usart_interrupt_flag_get(USART0, USART_FLAG_RBNE) == SET)
+	if(usart_flag_get(USART0, USART_FLAG_RBNE) == SET)	
+	{
+		c = usart_data_receive(USART0);
+		if (rx_count >= sizeof(rx_buffer))
+		{
+		    return;
+		}
+		//USART_ReceiverTimeOutCmd(USART1,DISABLE);
+		Enable_Uart1_Timer(0,0);
+		/* Read one byte from the receive data register */
+		rx_buffer[rx_count++] = c;
+				
+		Enable_Uart1_Timer(1,5);
+	}
+	/* 发送 */
+	//if(usart_interrupt_flag_get(USART0, USART_FLAG_TC) == SET)
+	if(usart_flag_get(USART0, USART_FLAG_TC) == SET)
+	{
+		//USART_ClearFlag(USART1, USART_FLAG_TC);
+		if (tx_count >= tx_buffer_size)
+		{
+			 /* Disable the EVAL_COM1 Transmit interrupt */
+		  	usart_interrupt_disable(USART0,USART_INT_TC);
+			//USART_ClearFlag(USART1, USART_FLAG_TC);
+			return;
+		}
+		/* Write one byte to the transmit data register */
+		usart_data_transmit(USART0,tx_buffer[tx_count++]);
+	}
+
+}
+
+
+
+

+ 40 - 0
bootloader/bl_drv_usart.h

@@ -0,0 +1,40 @@
+#ifndef DRV_USART1_H
+#define DRV_USART1_H
+
+#define TX_BUFFER_SIZE 128
+#define RX_BUFFER_SIZE 128
+
+#define UART_0_IDAR   (0)
+#define USART_0_BAUND    (USART_BAUND)
+extern DELAY_COMMON uart1_tout;
+
+
+__inline void Uart1_Time_Out(void)
+{
+	//uart1
+	if(uart1_tout.set)
+	{
+		if(uart1_tout.count)
+			uart1_tout.count--;
+		else
+		{
+			g_event |= RS485_RECEIVE_END_EVENT;
+			uart1_tout.set = 0;
+			uart1_tout.count = 0;
+		}
+	}
+}
+
+unsigned char CheckSum(unsigned char *dat, unsigned char num);
+
+void Usart1_Initial(void);
+
+int8_t Send_Data_RS485(uint8_t*data,uint16_t size);
+
+
+uint16_t Get_RS485_Data(uint8_t * dbuf,uint16_t dbuf_len);
+
+
+
+#endif
+

+ 209 - 0
bootloader/bl_drv_usart_2.c

@@ -0,0 +1,209 @@
+#include "bl_common.h" 
+#include "bl_drv_usart_2.h"
+
+ /* transmit buffer and receive buffer */
+static uint8_t tx_buffer[TX_2_BUFFER_SIZE];
+static uint8_t rx_buffer[RX_2_BUFFER_SIZE];
+/* counter of transmit buffer and receive buffer */
+static uint16_t tx_count = 0, rx_count = 0;
+/* size of transmit buffer and receive buffer */
+static uint32_t /*rx_buffer_size ,*/ tx_buffer_size;
+
+DELAY_COMMON uart2_tout;
+
+void Enable_Uart2_Timer(uint8_t enable,uint16_t timeout)
+{
+	if(enable)
+	{
+		uart2_tout.set = 1;
+		uart2_tout.count = timeout;
+	}
+	else
+	memset(&uart2_tout,0x00,sizeof(uart2_tout));
+	
+}
+
+static void Reset_RX_Buffer(void)
+{
+	memset(rx_buffer,0x00,sizeof(rx_buffer));
+	rx_count = 0;
+}
+
+uint16_t Get_RS485_2_Data(uint8_t * dbuf,uint16_t dbuf_len)
+{
+	if(dbuf == NULL || dbuf_len < rx_count)
+		return 0;
+
+	memcpy(dbuf,rx_buffer,rx_count);
+	dbuf_len = rx_count;
+	Reset_RX_Buffer();
+	
+	return dbuf_len;
+}
+
+
+
+int8_t Send_Data_2_RS485(uint8_t*data,uint16_t size)
+{
+	if(data == NULL || size == 0 || size > sizeof(tx_buffer))
+		return 0;
+
+
+	memcpy(tx_buffer,data,size);
+	tx_count = 0;
+	tx_buffer_size = size;
+	
+	//
+#if 0
+	do
+	{
+		while(RESET == usart_flag_get(USART1, USART_FLAG_TC));
+		usart_data_transmit(USART1,tx_buffer[tx_count++]);
+	}while(tx_count < tx_buffer_size);
+#else
+	//while(RESET == USART_GetFlagStatus(USART2, USART_FLAG_TC));
+	//USART_SendData(USART2,tx_buffer[tx_count++]);
+
+	usart_interrupt_enable(USART1, USART_INT_TC);
+#endif	
+	return 1;
+}
+
+
+
+/*
+ * 函数名:USART2_Config
+ * 描述  :USART2 GPIO 配置,工作模式配置。9600 8-N-1
+ * 输入  :无
+ * 输出  : 无
+ * 调用  :外部调用
+ */
+void Usart2_Initial(void)
+{
+
+	
+	/* enable GPIO clock */
+    rcu_periph_clock_enable(RCU_GPIOA);
+	rcu_periph_clock_enable(RCU_AF);
+
+    /* connect port to USARTx_Tx */
+    gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_2);
+
+    /* connect port to USARTx_Rx */
+    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3);
+
+    /* USART configure */
+	/* enable USART clock */
+    rcu_periph_clock_enable(RCU_USART1);
+    usart_deinit(USART1);
+    usart_baudrate_set(USART1, USART_1_BAUND);
+    usart_word_length_set(USART1, USART_WL_8BIT);
+    usart_stop_bit_set(USART1, USART_STB_1BIT);
+    usart_parity_config(USART1, USART_PM_NONE);
+    usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE);
+    usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE);
+    usart_receive_config(USART1, USART_RECEIVE_ENABLE);
+    usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);
+	nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
+	nvic_irq_enable(USART1_IRQn, 1, 0);
+	//usart_interrupt_enable(USART1, USART_INT_RBNE);
+
+	
+#if UART_1_IDAR
+	/*USART_ClockInitTypeDef  USART_ClockInitStruct; 
+
+	USART_ClockInitStruct.USART_Clock = USART_Clock_Disable;
+	USART_ClockInitStruct.USART_CPOL = USART_CPOL_Low;
+	USART_ClockInitStruct.USART_CPHA = USART_CPHA_2Edge;
+	USART_ClockInitStruct.USART_LastBit = USART_LastBit_Disable;
+	
+	USART_ClockInit(USART1,  &USART_ClockInitStruct);*/
+
+	
+	usart_enable(USART1); 
+
+	usart_prescaler_config(USART1, 0x01);
+	usart_irda_lowpower_config(USART1, USART_IRLP_NORMAL);
+	usart_irda_mode_enable(USART1);
+#else
+	usart_enable(USART1);
+#endif
+
+
+}
+
+
+
+/*******************************************************************************
+* Function Name  : USART2_IRQHandler
+* Description    : This function handles USART2 global interrupt request.
+* Input          : None
+* Output         : None
+* Return         : None
+*******************************************************************************/
+void USART1_IRQHandler(void)
+{
+	uint8_t c;
+	
+	/* 串口接收 */
+	if(usart_flag_get(USART1, USART_FLAG_RBNE) == SET)
+	{
+		c = usart_data_receive(USART1);
+		if (rx_count >= sizeof(rx_buffer))
+		{
+		    return;
+		}
+		//USART_ReceiverTimeOutCmd(USART2,DISABLE);
+		Enable_Uart2_Timer(0,0);
+		/* Read one byte from the receive data register */
+		rx_buffer[rx_count++] = c;
+				
+		Enable_Uart2_Timer(1,5);
+	}
+	/* 发送 */
+	if(usart_flag_get(USART1, USART_FLAG_TC) == SET)
+	{
+		//USART_ClearFlag(USART1, USART_FLAG_TC);
+		if (tx_count >= tx_buffer_size)
+		{
+			 /* Disable the EVAL_COM1 Transmit interrupt */
+		  	usart_interrupt_disable(USART1,USART_INT_TC);
+			
+			//USART_ClearFlag(USART1, USART_FLAG_TC);
+			return;
+		}
+		/* Write one byte to the transmit data register */
+		usart_data_transmit(USART1,tx_buffer[tx_count++]);
+	}
+	
+}
+
+#if 0
+// 
+//用于系统调用printf
+//
+#ifdef __GNUC__
+  /* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
+     set to 'Yes') calls __io_putchar() */
+#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
+#else
+#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
+#endif /* __GNUC__ */
+
+PUTCHAR_PROTOTYPE
+{
+	// RS485发送使能
+	gpio_bit_set(GPIOA, GPIO_PIN_4);
+	
+	usart_data_transmit(USART1,(uint8_t) ch);
+	while(!(usart_flag_get(USART1, USART_FLAG_TC) == SET));
+	// RS485接收使能
+
+	gpio_bit_reset(GPIOA, GPIO_PIN_4);
+	
+	return ch;
+}
+#endif
+
+
+

+ 41 - 0
bootloader/bl_drv_usart_2.h

@@ -0,0 +1,41 @@
+#ifndef DRV_USART2_H
+#define DRV_USART2_H
+
+#define TX_2_BUFFER_SIZE 128
+#define RX_2_BUFFER_SIZE 128
+
+#define UART_1_IDAR   (0)
+#define USART_1_BAUND    (USART_BAUND)
+extern DELAY_COMMON uart2_tout;
+
+
+__inline void Uart2_Time_Out(void)
+{
+	//uart2
+	if(uart2_tout.set)
+	{
+		if(uart2_tout.count)
+			uart2_tout.count--;
+		else
+		{
+			g_event |= RS485_2_RECEIVE_END_EVENT;
+			uart2_tout.set = 0;
+			uart2_tout.count = 0;
+		}
+	}
+}
+
+
+
+
+
+void Usart2_Initial(void);
+
+int8_t Send_Data_2_RS485(uint8_t*data,uint16_t size);
+
+uint16_t Get_RS485_2_Data(uint8_t * dbuf,uint16_t dbuf_len);
+
+
+
+#endif
+

+ 155 - 0
bootloader/byte_queue.c

@@ -0,0 +1,155 @@
+#include "byte_queue.h"
+
+static u16 byte_queue_add(const byte_queue_t *queue, u16 value1, u16 value2)
+{
+	return (value1 + value2) % queue->size;
+}
+
+static u16 byte_queue_tail_add(const byte_queue_t *queue, u16 value)
+{
+	return byte_queue_add(queue, queue->tail, value);
+}
+
+static u16 byte_queue_head_add(const byte_queue_t *queue, u16 value)
+{
+	return byte_queue_add(queue, queue->head, value);
+}
+
+bool byte_queue_empty(const byte_queue_t *queue)
+{
+	return (bool) (queue->head == queue->tail);
+}
+
+bool byte_queue_readable(const byte_queue_t *queue)
+{
+	return (bool) (queue->head != queue->tail);
+}
+
+bool byte_queue_full(const byte_queue_t *queue)
+{
+	return (bool) (byte_queue_tail_add(queue, 1) == queue->head);
+}
+
+bool byte_queue_writeable(const byte_queue_t *queue)
+{
+	return (bool) (byte_queue_tail_add(queue, 1) != queue->head);
+}
+
+u16 byte_queue_get_used(const byte_queue_t *queue)
+{
+	if (queue->head <= queue->tail) {
+		return queue->tail - queue->head;
+	}
+
+	return queue->size - (queue->head - queue->tail) - 1;
+}
+
+u16 byte_queue_get_free(const byte_queue_t *queue)
+{
+	if (queue->tail < queue->head) {
+		return queue->head - queue->tail;
+	}
+
+	return queue->size - (queue->tail - queue->head) - 1;
+}
+
+void byte_queue_reset(byte_queue_t *queue)
+{
+	queue->head = queue->tail = 0;
+}
+
+void byte_queue_init(byte_queue_t *queue, u8 *buff, u16 size)
+{
+	queue->buff = buff;
+	queue->size = size;
+	queue->head = queue->tail = 0;
+}
+
+u16 byte_queue_write(byte_queue_t *queue, const u8 *buff, u16 size)
+{
+	const u8 *buff_bak = buff;
+	const u8 *buff_end;
+
+	for (buff_end = buff + size; buff < buff_end; buff++) {
+		u16 tail = byte_queue_tail_add(queue, 1);
+
+		if (tail == queue->head) {
+			return buff - buff_bak;
+		}
+
+		queue->buff[queue->tail] = *buff;
+		queue->tail = tail;
+	}
+
+	return size;
+}
+
+u16 byte_queue_write_byte(byte_queue_t *queue, u8 byte)
+{
+	return byte_queue_write(queue, &byte, 1);
+}
+
+u16 byte_queue_read(byte_queue_t *queue, u8 *buff, u16 size)
+{
+	u8 *buff_bak = buff;
+	u8 *buff_end;
+
+	for (buff_end = buff + size; buff < buff_end; buff++) {
+		if (queue->head == queue->tail) {
+			return buff - buff_bak;
+		}
+
+		*buff = queue->buff[queue->head];
+		queue->head = byte_queue_head_add(queue, 1);
+	}
+
+	return size;
+}
+
+void byte_queue_fill(byte_queue_t *queue, u8 *buff, u16 size)
+{
+	while (size > 0) {
+		u16 length = byte_queue_read(queue, buff, size);
+		size -= length;
+		buff += size;
+	}
+}
+
+// ================================================================================
+
+void byte_queue_alloc_init(byte_queue_t *queue, u8 *buff, u8 size)
+{
+	queue->buff = buff;
+	queue->size = size;
+	byte_queue_alloc_reset(queue);
+}
+
+void byte_queue_alloc_reset(byte_queue_t *queue)
+{
+	u8 *buff = queue->buff;
+	u8 size = queue->size;
+	u8 index;
+
+	for (index = 0; index < size; index++) {
+		buff[index] = index;
+	}
+
+	queue->tail = size - 1;
+	queue->head = 0;
+}
+
+u8 byte_queue_alloc(byte_queue_t *queue)
+{
+	u8 index;
+
+	if (byte_queue_read(queue, &index, 1) > 0) {
+		return index;
+	}
+
+	return 0xFF;
+}
+
+void byte_queue_free(byte_queue_t *queue, u8 index)
+{
+	byte_queue_write(queue, &index, 1);
+}

+ 29 - 0
bootloader/byte_queue.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include "s600.h"
+
+typedef struct {
+	u8 *buff;
+	u16 size;
+	u16 head;
+	u16 tail;
+} byte_queue_t;
+
+bool byte_queue_empty(const byte_queue_t *queue);
+bool byte_queue_readable(const byte_queue_t *queue);
+bool byte_queue_full(const byte_queue_t *queue);
+bool byte_queue_writeable(const byte_queue_t *queue);
+u16 byte_queue_get_used(const byte_queue_t *queue);
+u16 byte_queue_get_free(const byte_queue_t *queue);
+
+void byte_queue_reset(byte_queue_t *queue);
+void byte_queue_init(byte_queue_t *queue, u8 *buff, u16 size);
+u16 byte_queue_write(byte_queue_t *queue, const u8 *buff, u16 size);
+u16 byte_queue_write_byte(byte_queue_t *queue, u8 byte);
+u16 byte_queue_read(byte_queue_t *queue, u8 *buff, u16 size);
+void byte_queue_fill(byte_queue_t *queue, u8 *buff, u16 size);
+
+void byte_queue_alloc_init(byte_queue_t *queue, u8 *buff, u8 size);
+void byte_queue_alloc_reset(byte_queue_t *queue);
+u8 byte_queue_alloc(byte_queue_t *queue);
+void byte_queue_free(byte_queue_t *queue, u8 index);

+ 147 - 0
bootloader/gd32f10x_it.c

@@ -0,0 +1,147 @@
+/*!
+    \file    gd32f10x_it.c
+    \brief   interrupt service routines
+
+    \version 2014-12-26, V1.0.0, firmware for GD32F10x
+    \version 2017-06-20, V2.0.0, firmware for GD32F10x
+    \version 2018-07-31, V2.1.0, firmware for GD32F10x
+*/
+
+/*
+    Copyright (c) 2018, GigaDevice Semiconductor Inc.
+
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this 
+       list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, 
+       this list of conditions and the following disclaimer in the documentation 
+       and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holder nor the names of its contributors 
+       may be used to endorse or promote products derived from this software without 
+       specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+OF SUCH DAMAGE.
+*/
+
+#include "gd32f10x_it.h"
+#include "s600.h"
+
+/*!
+    \brief      this function handles NMI exception
+    \param[in]  none
+    \param[out] none
+    \retval     none
+*/
+void NMI_Handler(void)
+{
+}
+
+/*!
+    \brief      this function handles HardFault exception
+    \param[in]  none
+    \param[out] none
+    \retval     none
+*/
+void HardFault_Handler(void)
+{
+    /* if Hard Fault exception occurs, go to infinite loop */
+    while(1){
+    }
+}
+
+/*!
+    \brief      this function handles MemManage exception
+    \param[in]  none
+    \param[out] none
+    \retval     none
+*/
+void MemManage_Handler(void)
+{
+    /* if Memory Manage exception occurs, go to infinite loop */
+    while(1){
+    }
+}
+
+/*!
+    \brief      this function handles BusFault exception
+    \param[in]  none
+    \param[out] none
+    \retval     none
+*/
+void BusFault_Handler(void)
+{
+    /* if Bus Fault exception occurs, go to infinite loop */
+    while(1){
+    }
+}
+
+/*!
+    \brief      this function handles UsageFault exception
+    \param[in]  none
+    \param[out] none
+    \retval     none
+*/
+void UsageFault_Handler(void)
+{
+    /* if Usage Fault exception occurs, go to infinite loop */
+    while(1){
+    }
+}
+
+/*!
+    \brief      this function handles SVC exception
+    \param[in]  none
+    \param[out] none
+    \retval     none
+*/
+void SVC_Handler(void)
+{
+}
+
+/*!
+    \brief      this function handles DebugMon exception
+    \param[in]  none
+    \param[out] none
+    \retval     none
+*/
+void DebugMon_Handler(void)
+{
+}
+
+/*!
+    \brief      this function handles PendSV exception
+    \param[in]  none
+    \param[out] none
+    \retval     none
+*/
+void PendSV_Handler(void)
+{
+}
+
+/*!
+    \brief      this function handles SysTick exception
+    \param[in]  none
+    \param[out] none
+    \retval     none
+*/
+
+extern void Send_Sub_BMS_CMD_Delay(void);
+void SysTick_Handler(void)
+{
+	s600_iap_100ms++;
+
+	Send_Sub_BMS_CMD_Delay();
+}

+ 64 - 0
bootloader/gd32f10x_it.h

@@ -0,0 +1,64 @@
+/*!
+    \file    gd32f10x_it.h
+    \brief   the header file of the ISR
+
+    \version 2014-12-26, V1.0.0, firmware for GD32F10x
+    \version 2017-06-20, V2.0.0, firmware for GD32F10x
+    \version 2018-07-31, V2.1.0, firmware for GD32F10x
+*/
+
+/*
+    Copyright (c) 2018, GigaDevice Semiconductor Inc.
+
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this 
+       list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, 
+       this list of conditions and the following disclaimer in the documentation 
+       and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holder nor the names of its contributors 
+       may be used to endorse or promote products derived from this software without 
+       specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+OF SUCH DAMAGE.
+*/
+
+#ifndef GD32F10X_IT_H
+#define GD32F10X_IT_H
+
+#include "gd32f10x.h"
+
+/* function declarations */
+/* this function handles NMI exception */
+void NMI_Handler(void);
+/* this function handles HardFault exception */
+void HardFault_Handler(void);
+/* this function handles MemManage exception */
+void MemManage_Handler(void);
+/* this function handles BusFault exception */
+void BusFault_Handler(void);
+/* this function handles UsageFault exception */
+void UsageFault_Handler(void);
+/* this function handles SVC exception */
+void SVC_Handler(void);
+/* this function handles DebugMon exception */
+void DebugMon_Handler(void);
+/* this function handles PendSV exception */
+void PendSV_Handler(void);
+/* this function handles SysTick exception */
+void SysTick_Handler(void);
+
+#endif /* GD32F10X_IT_H */

+ 66 - 0
bootloader/gd32f10x_libopt.h

@@ -0,0 +1,66 @@
+/*!
+    \file    gd32f10x_libopt.h
+    \brief   library optional for gd32f10x
+
+    \version 2014-12-26, V1.0.0, firmware for GD32F10x
+    \version 2017-06-20, V2.0.0, firmware for GD32F10x
+    \version 2018-07-31, V2.1.0, firmware for GD32F10x
+*/
+
+/*
+    Copyright (c) 2018, GigaDevice Semiconductor Inc.
+
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this 
+       list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, 
+       this list of conditions and the following disclaimer in the documentation 
+       and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holder nor the names of its contributors 
+       may be used to endorse or promote products derived from this software without 
+       specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+OF SUCH DAMAGE.
+*/
+
+#ifndef GD32F10X_LIBOPT_H
+#define GD32F10X_LIBOPT_H
+
+#include "gd32f10x_fmc.h"
+#include "gd32f10x_pmu.h"
+#include "gd32f10x_bkp.h"
+#include "gd32f10x_rcu.h"
+#include "gd32f10x_exti.h"
+#include "gd32f10x_gpio.h"
+#include "gd32f10x_crc.h"
+#include "gd32f10x_dma.h"
+#include "gd32f10x_dbg.h"
+#include "gd32f10x_adc.h"
+#include "gd32f10x_dac.h"
+#include "gd32f10x_fwdgt.h"
+#include "gd32f10x_wwdgt.h"
+#include "gd32f10x_rtc.h"
+#include "gd32f10x_timer.h"
+#include "gd32f10x_usart.h"
+#include "gd32f10x_i2c.h"
+#include "gd32f10x_spi.h"
+#include "gd32f10x_sdio.h"
+#include "gd32f10x_exmc.h"
+#include "gd32f10x_can.h"
+#include "gd32f10x_enet.h"
+#include "gd32f10x_misc.h"
+
+#endif /* GD32F10X_LIBOPT_H */

+ 149 - 0
bootloader/main.c

@@ -0,0 +1,149 @@
+/*!
+    \file    main.c
+    \brief   led spark with systick, USART print and key example
+    
+    \version 2017-12-26, V1.0.0, firmware for GD32E10x
+*/
+
+/*
+    Copyright (c) 2017, GigaDevice Semiconductor Inc.
+
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this 
+       list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, 
+       this list of conditions and the following disclaimer in the documentation 
+       and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holder nor the names of its contributors 
+       may be used to endorse or promote products derived from this software without 
+       specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+OF SUCH DAMAGE.
+*/
+
+#include "s600.h"
+#include "main.h"
+#include "s600_can.h"
+#include "s600_iap.h"
+
+#ifdef CONFIG_BOARD_PS100
+#include "bl_common.h"
+#include "bl_drv_usart.h"
+#include "bl_drv_usart_2.h"
+
+DELAY_COMMON send_delay;
+uint32_t g_event = NO_EVENT;
+
+static uint8_t app_rs485_buf[TX_BUFFER_SIZE] = { 0x17, 0x31, 0x42, 0x10, 0x8B, 0xBE, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+__STATIC_INLINE void Send_Sub_BMS_CMD_Delay(void)
+{
+	if (send_delay.set) {
+		++send_delay.count;
+
+		if(send_delay.count >= 4) {
+			send_delay.count = 0;
+			g_event |= SUB_BMS_2_SEND_CMD_EVENT;
+		} else if(send_delay.count == 2) {
+			g_event |= SUB_BMS_1_SEND_CMD_EVENT;
+		}
+	}
+}
+
+__STATIC_INLINE void BMS_Com_Initial(void)
+{
+	Usart1_Initial();
+	Usart2_Initial();
+
+	send_delay.set = 1;
+	send_delay.count = 4;
+
+	rcu_periph_clock_enable(RCU_GPIOA);
+	gpio_init(GPIOA,GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_12);
+	gpio_bit_reset(GPIOA, GPIO_PIN_12);
+
+	rcu_periph_clock_enable(RCU_GPIOA);
+	gpio_init(GPIOA,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_7);
+
+	gpio_bit_reset(GPIOA, GPIO_PIN_7);
+}
+
+__STATIC_INLINE int8_t Send_Sub_BMS_CMD_1(void)
+{
+	return Send_Data_RS485(app_rs485_buf, app_rs485_buf[0]);
+}
+
+__STATIC_INLINE int8_t Send_Sub_BMS_CMD_2(void)
+{
+	return Send_Data_2_RS485(app_rs485_buf, app_rs485_buf[0]);
+}
+#endif
+
+__STATIC_INLINE void systick_config(void)
+{
+	/* setup systick timer for 10Hz interrupts */
+	SysTick_Config(SystemCoreClock / 10);
+	/* configure the systick handler priority */
+	NVIC_SetPriority(SysTick_IRQn, 0x00U);
+}
+
+int main(void)
+{
+	u8 command[] = { 0xF0, CONFIG_CAN_ID, 0x00 };
+
+	s600_iap_boot(true);
+
+	systick_config();
+
+#ifdef CONFIG_BOARD_PS100
+	BMS_Com_Initial();
+#endif
+
+	s600_can_device_config();
+
+	s600_can_send_command(S600_CAN_ALL_ID, command, sizeof(command));
+
+	while (1) {
+		s600_can_poll();
+
+		if (s600_iap_100ms > 600) {
+			s600_iap_boot(false);
+			s600_iap_100ms = 0;
+			s600_can_send_command(S600_CAN_ALL_ID, command, sizeof(command));
+		}
+
+#ifdef CONFIG_BOARD_PS100
+		if (g_event & SUB_BMS_1_SEND_CMD_EVENT) {
+			Send_Sub_BMS_CMD_1();
+			g_event &= ~SUB_BMS_1_SEND_CMD_EVENT;
+		}
+
+		if (g_event & SUB_BMS_2_SEND_CMD_EVENT) {
+			Send_Sub_BMS_CMD_2();
+			g_event &= ~SUB_BMS_2_SEND_CMD_EVENT;
+		}
+#endif
+	}
+}
+
+void SysTick_Handler(void)
+{
+	s600_iap_100ms++;
+
+#ifdef CONFIG_BOARD_PS100
+	Send_Sub_BMS_CMD_Delay();
+#endif
+}

+ 40 - 0
bootloader/main.h

@@ -0,0 +1,40 @@
+/*!
+    \file    main.h
+    \brief   the header file of main
+    
+    \version 2017-12-26, V1.0.0, firmware for GD32E10x
+*/
+
+/*
+    Copyright (c) 2017, GigaDevice Semiconductor Inc.
+
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright notice, this 
+       list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright notice, 
+       this list of conditions and the following disclaimer in the documentation 
+       and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holder nor the names of its contributors 
+       may be used to endorse or promote products derived from this software without 
+       specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 
+OF SUCH DAMAGE.
+*/
+
+#ifndef MAIN_H
+#define MAIN_H
+
+#endif /* MAIN_H */

+ 143 - 0
bootloader/s600.c

@@ -0,0 +1,143 @@
+#include "s600.h"
+
+u32 s600_iap_100ms;
+
+const u8 s600_value_char_map[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+u16 s600_get_avg_value(const u16 *values, u16 size)
+{
+	u32 sum = values[0];
+	u16 i;
+
+	for (i = 1; i < size; i++) {
+		sum += values[i];
+	}
+
+	return sum / size;
+}
+
+u32 s600_checksum_put(u32 checksum, const u8 *data, u16 size)
+{
+	const u8 *data_end;
+
+	for (data_end = data + size - 1; data < data_end; data += 2) {
+		checksum += *(u16 *) data;
+	}
+
+	if (data == data_end) {
+		checksum += *data;
+	}
+
+	return checksum;
+}
+
+u32 s600_checksum_put_value(u32 checksum, u16 value)
+{
+	return checksum + value;
+}
+
+u16 s600_checksum_finish(u32 checksum)
+{
+	while (checksum & 0xFFFF0000) {
+		checksum = (checksum >> 16) + (checksum & 0xFFFF);
+	}
+
+	return ~checksum;
+}
+
+u8 s600_char2value(char ch)
+{
+	if (ch >= '0' && ch <= '9') {
+		return ch - '0';
+	}
+
+	if (ch >= 'a' && ch <= 'z') {
+		return ch - 'a' + 10;
+	}
+
+	if (ch >= 'A' && ch <= 'Z') {
+		return ch - 'A' + 10;
+	}
+
+	return 0xFF;
+}
+
+// "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+u8 s600_month2value(const char *text)
+{
+	switch (text[0]) {
+	case 'J':
+		if (text[1] == 'a') {
+			return 1;
+		} else if (text[2] == 'n') {
+			return 6;
+		} else {
+			return 7;
+		}
+
+	case 'F':
+		return 2;
+
+	case 'M':
+		if (text[2] == 'r') {
+			return 3;
+		} else {
+			return 5;
+		}
+
+	case 'A':
+		if (text[1] == 'p') {
+			return 4;
+		} else {
+			return 8;
+		}
+
+	case 'S':
+		return 9;
+
+	case 'O':
+		return 10;
+
+	case 'N':
+		return 11;
+
+	case 'D':
+		return 12;
+
+	default:
+		return 0;
+	}
+}
+
+u16 s600_decode_u16(const u8 *buff)
+{
+	return DECODE_U16(buff);
+}
+
+u32 s600_decode_u24(const u8 *buff)
+{
+	return DECODE_U24(buff);
+}
+
+u32 s600_decode_u32(const u8 *buff)
+{
+	return DECODE_U32(buff);
+}
+
+void s600_encode_u16(u8 *buff, u16 value)
+{
+	buff[0] = value;
+	buff[1] = value >> 8;
+}
+
+void s600_encode_u24(u8 *buff, u32 value)
+{
+	s600_encode_u16(buff, value);
+	buff[2] = value >> 16;
+}
+
+void s600_encode_u32(u8 *buff, u32 value)
+{
+	s600_encode_u24(buff, value);
+	buff[3] = value >> 24;
+}

+ 143 - 0
bootloader/s600.h

@@ -0,0 +1,143 @@
+#pragma once
+
+#include "s600_config.h"
+#include "stdio.h"
+
+#define CAN_PIN_MAP_PA11_PA12		1
+#define CAN_PIN_MAP_PB8_PB9			2
+#define CAN_PIN_MAP_PD0_PD1			3
+
+#ifdef CONFIG_BOARD_S600
+#define CONFIG_BOARD_NAME			"S600"
+#define CONFIG_CAN_ID				0x41
+#define CONFIG_CAN_PIN_MAP			CAN_PIN_MAP_PA11_PA12
+#elif defined(CONFIG_BOARD_C200)
+#define CONFIG_BOARD_NAME			"C200"
+#define CONFIG_CAN_ID				0x43
+#define CONFIG_CAN_PIN_MAP			CAN_PIN_MAP_PB8_PB9
+#elif defined(CONFIG_BOARD_G580)
+#define CONFIG_BOARD_NAME			"G580"
+#define CONFIG_CAN_ID				0x50
+#define CONFIG_CAN_PIN_MAP			CAN_PIN_MAP_PB8_PB9
+#elif defined(CONFIG_BOARD_PS300)
+#define CONFIG_BOARD_NAME			"PS300"
+#define CONFIG_CAN_ID				0x43
+#define CONFIG_CAN_PIN_MAP			CAN_PIN_MAP_PB8_PB9
+#elif defined(CONFIG_BOARD_PS100)
+#define CONFIG_BOARD_NAME			"PS100"
+#define CONFIG_CAN_ID				0x42
+#define CONFIG_CAN_PIN_MAP			CAN_PIN_MAP_PB8_PB9
+#else
+#error "Invalid Board!"
+#endif
+
+#ifdef GD32E10X_H
+#define CONFIG_GD32E10X			1
+#define FMC_PAGE_SIZE			1024
+#endif
+
+#ifdef GD32F10X_H
+#define CONFIG_GD32F10X			1
+
+#ifdef GD32F10X_MD
+#define FMC_PAGE_SIZE			1024
+#else
+#define FMC_PAGE_SIZE			2048
+#endif
+#endif
+
+#define NELEM(a) \
+	(sizeof(a) / sizeof((a)[0]))
+
+#define LAST_ELEM(a) \
+	((a)[NELEM(a) - 1])
+
+#define KB(value) \
+	((value) << 10)
+
+#define MB(value) \
+	((value) << 20)
+
+#define SYSTICK_SECONDS(value) \
+	(SystemCoreClock * (value))
+
+#define SYSTICK_MSECONDS(value) \
+	(SYSTICK_SECONDS(value) / 1000)
+
+#if S600_UART_PRINT
+#define println(fmt, args ...) \
+	printf(fmt "\r\n", ##args)
+
+#define pr_pos_info() \
+	println("%s:%d", __FILE__, __LINE__)
+#else
+#define println(fmt, args ...)
+#define pr_pos_info()
+#endif
+
+#define U32(value) \
+	((u32) (value))
+
+#define PU32(value) \
+	((u32 *) (value))
+
+#define U16(value) \
+	((u16) (value))
+
+#define PU16(value) \
+	((u16 *) (value))
+
+#define U8(value) \
+	((u8) (value))
+
+#define PU8(value) \
+	((u8 *) (value))
+
+#define DECODE_U16(buff) \
+	(U16(buff[1]) << 8 | buff[0])
+
+#define DECODE_U24(buff) \
+	(U32(buff[2]) << 16 | DECODE_U16(buff))
+
+#define DECODE_U32(buff) \
+	(U32(buff[3]) << 24 | DECODE_U24(buff))
+
+typedef uint32_t u32;
+typedef int32_t s32;
+
+typedef uint16_t u16;
+typedef int16_t s16;
+
+typedef uint8_t u8;
+typedef int8_t s8;
+
+typedef void (*s600_command_t)(void);
+
+#ifdef CONFIG_GD32E10X
+typedef enum {
+	false,
+	true,
+} bool;
+#else
+#define true TRUE
+#define false FALSE
+#endif
+
+extern uint32_t SystemCoreClock;
+extern const u8 s600_value_char_map[];
+extern u32 s600_iap_100ms;
+
+u16 s600_get_avg_value(const u16 *values, u16 size);
+u32 s600_checksum_put(u32 checksum, const u8 *data, u16 size);
+u32 s600_checksum_put_value(u32 checksum, u16 value);
+u16 s600_checksum_finish(u32 checksum);
+u8 s600_char2value(char ch);
+u8 s600_month2value(const char *text);
+
+u16 s600_decode_u16(const u8 *buff);
+u32 s600_decode_u24(const u8 *buff);
+u32 s600_decode_u32(const u8 *buff);
+
+void s600_encode_u16(u8 *buff, u16 value);
+void s600_encode_u24(u8 *buff, u32 value);
+void s600_encode_u32(u8 *buff, u32 value);

+ 584 - 0
bootloader/s600_can.c

@@ -0,0 +1,584 @@
+#include "s600_can.h"
+#include "s600_iap.h"
+#include "string.h"
+#include "stdlib.h"
+
+#ifndef CONFIG_CAN_ID
+#error CONFIG_CAN_ID not set
+#endif
+
+#define S600_CAN_LOOPBACK			0
+
+static s600_can_packet_t s600_can_tx_packets[5];
+static s600_can_packet_t *s600_can_tx_front;
+
+static s600_can_packet_t s600_can_rx_packets[10];
+static s600_can_packet_t *s600_can_rx_fronts[10];
+static u8 s600_can_rx_front_len;
+
+static u8 s600_can_tx_alloc_buff[NELEM(s600_can_tx_packets) + 1];
+static byte_queue_t s600_can_tx_allocator;
+
+static u8 s600_can_rx_alloc_buff[NELEM(s600_can_rx_packets) + 1];
+static byte_queue_t s600_can_rx_allocator;
+
+static u8 s600_can_tx_buff[NELEM(s600_can_tx_packets) + 1];
+static byte_queue_t s600_can_tx_queue = { s600_can_tx_buff, NELEM(s600_can_tx_buff), 0, 0 };
+
+static u8 s600_can_rx_buff[NELEM(s600_can_rx_packets) + 1];
+static byte_queue_t s600_can_rx_queue = { s600_can_rx_buff, NELEM(s600_can_rx_buff), 0, 0 };
+
+// ================================================================================
+
+static u32 s600_can_write_position;
+
+__STATIC_INLINE int s600_can_process_cmd_write_begin(const u8 *buff, u16 length)
+{
+	s600_can_write_position = 0;
+	s600_iap_write_begin();
+	return 0;
+}
+
+__STATIC_INLINE int s600_can_process_cmd_write_end(const u8 *buff, u16 length)
+{
+	u32 size, checksum;
+
+	if (length < 7) {
+		return -1;
+	}
+
+	size = s600_decode_u24(buff);
+	checksum = s600_decode_u32(buff + 3);
+
+	if (s600_iap_write_end(size, checksum)) {
+		return 0;
+	}
+
+	return -1;
+}
+
+__STATIC_INLINE int s600_can_process_cmd_write_slow(u8 can, u8 *buff, u16 length)
+{
+	u32 position;
+
+	if (length < 3) {
+		return -1;
+	}
+
+	position = s600_decode_u24(buff);
+
+	if (s600_can_write_position == position) {
+		length -= 3;
+		s600_iap_write(buff + 3, length);
+		s600_can_write_position += length;
+	}
+
+	s600_encode_u24(buff + 1, s600_can_write_position);
+
+	return 3;
+}
+
+__STATIC_INLINE void s600_can_process_command(u8 can, u8 *command, u16 length)
+{
+	int rsplen;
+	u8 *args;
+	u16 key;
+
+	if (length < 2) {
+		return;
+	}
+
+	key = *(u16 *) command;
+
+	if ((key & 0xFFF0) != S600_CAN_BUILD_CMD(0xF0)) {
+		return;
+	}
+
+	s600_iap_100ms = 0;
+	args = command + 2;
+	length -= 2;
+
+	switch (key)
+	{
+	case S600_CAN_BUILD_CMD(0xF0):
+		rsplen = 0;
+		break;
+
+	case S600_CAN_BUILD_CMD(0xF1):
+		rsplen = s600_can_process_cmd_write_begin(args, length);
+		break;
+
+	case S600_CAN_BUILD_CMD(0xF2):
+		rsplen = s600_can_process_cmd_write_end(args, length);
+		break;
+
+	case S600_CAN_BUILD_CMD(0xF3):
+		s600_iap_write(args, length);
+		return;
+
+	case S600_CAN_BUILD_CMD(0xF4):
+		rsplen = s600_can_process_cmd_write_slow(can, args, length);
+		break;
+
+	case S600_CAN_BUILD_CMD(0xF5):
+		s600_iap_boot(false);
+		rsplen = -1;
+		break;
+
+	default:
+		rsplen = -1;
+		break;
+	}
+
+	if (rsplen < 0) {
+		rsplen = 0;
+		args[0] = 0x01;
+	} else {
+		args[0] = 0x00;
+	}
+
+	s600_can_send_response(can, command, rsplen + 3);
+}
+
+// ================================================================================
+
+__STATIC_INLINE void s600_can_rcu_config(void)
+{
+	byte_queue_alloc_init(&s600_can_tx_allocator, s600_can_tx_alloc_buff, sizeof(s600_can_tx_alloc_buff));
+	byte_queue_alloc_init(&s600_can_rx_allocator, s600_can_rx_alloc_buff, sizeof(s600_can_rx_alloc_buff));
+
+#if CONFIG_CAN_PIN_MAP == CAN_PIN_MAP_PA11_PA12
+	rcu_periph_clock_enable(RCU_GPIOA);
+#elif CONFIG_CAN_PIN_MAP == CAN_PIN_MAP_PB8_PB9
+	rcu_periph_clock_enable(RCU_GPIOB);
+#elif CONFIG_CAN_PIN_MAP == CAN_PIN_MAP_PD0_PD1
+	rcu_periph_clock_enable(RCU_GPIOD);
+#else
+#error "Invalid CONFIG_CAN_PIN_MAP"
+#endif
+
+	rcu_periph_clock_enable(RCU_AF);
+    rcu_periph_clock_enable(RCU_CAN0);
+}
+
+__STATIC_INLINE void s600_can_gpio_config(void)
+{
+#if CONFIG_CAN_PIN_MAP == CAN_PIN_MAP_PA11_PA12
+	gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
+	gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_12);
+#elif CONFIG_CAN_PIN_MAP == CAN_PIN_MAP_PB8_PB9
+	gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
+	gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
+#ifdef GPIO_CAN0_PARTIAL_REMAP
+	gpio_pin_remap_config(GPIO_CAN0_PARTIAL_REMAP, ENABLE);
+#else
+	gpio_pin_remap_config(GPIO_CAN_PARTIAL_REMAP, ENABLE);
+#endif
+#elif CONFIG_CAN_PIN_MAP == CAN_PIN_MAP_PD0_PD1
+	gpio_init(GPIOD, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_0);
+	gpio_init(GPIOD, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);
+	gpio_pin_remap_config(GPIO_CAN_FULL_REMAP, ENABLE);
+#else
+#error "Invalid CONFIG_CAN_PIN_MAP"
+#endif
+}
+
+void s600_can_device_config(void)
+{
+	can_parameter_struct can_parameter;
+	can_filter_parameter_struct can_filter;
+
+	s600_can_rcu_config();
+	s600_can_gpio_config();
+
+	// can_struct_para_init(CAN_INIT_STRUCT, &can_parameter);
+	// can_struct_para_init(CAN_FILTER_STRUCT, &can_filter);
+
+	can_deinit(CAN0);
+
+	/* initialize CAN parameters */
+	can_parameter.time_triggered = DISABLE;
+	can_parameter.auto_bus_off_recovery = DISABLE;
+	can_parameter.auto_wake_up = DISABLE;
+	can_parameter.auto_retrans = DISABLE;
+	can_parameter.rec_fifo_overwrite = DISABLE;
+	can_parameter.trans_fifo_order = ENABLE;
+	can_parameter.resync_jump_width = CAN_BT_SJW_1TQ;
+	can_parameter.time_segment_1 = CAN_BT_BS1_5TQ;
+	can_parameter.time_segment_2 = CAN_BT_BS2_3TQ;
+
+#if S600_CAN_LOOPBACK
+	can_parameter.working_mode = CAN_LOOPBACK_MODE;
+#else
+	can_parameter.working_mode = CAN_NORMAL_MODE;
+#endif
+
+#if S600_CAN_BAUDRATE == 1000
+		can_parameter.prescaler = 6;
+#elif S600_CAN_BAUDRATE == 500
+		can_parameter.prescaler = 12;
+#elif S600_CAN_BAUDRATE == 250
+		can_parameter.prescaler = 24;
+#elif S600_CAN_BAUDRATE == 125
+		can_parameter.prescaler = 48;
+#elif S600_CAN_BAUDRATE == 100
+		can_parameter.prescaler = 60;
+#elif S600_CAN_BAUDRATE == 50
+		can_parameter.prescaler = 120;
+#elif S600_CAN_BAUDRATE == 20
+		can_parameter.prescaler = 300;
+#else
+	#error "Invalid Baudrate"
+#endif
+
+	/* initialize CAN */
+	can_init(CAN0, &can_parameter);
+
+#ifdef CONFIG_GD32E10X
+	can_frequency_set(CAN0, S600_CAN_BAUDRATE * 1000);
+#endif
+
+	/* initialize filter */
+	can_filter.filter_number = 0;
+	can_filter.filter_mode = CAN_FILTERMODE_MASK;
+	can_filter.filter_bits = CAN_FILTERBITS_32BIT;
+	can_filter.filter_list_high = 0x0000;
+	can_filter.filter_list_low = S600_CAN_DEST_ID(CONFIG_CAN_ID) << 3 | 1 << 2;
+	can_filter.filter_mask_high = 0x0000;
+	can_filter.filter_mask_low = S600_CAN_DEST_ID(S600_CAN_ALL_ID) << 3 | 1 << 2;
+	can_filter.filter_fifo_number = CAN_FIFO1;
+	can_filter.filter_enable = ENABLE;
+	can_filter_init(&can_filter);
+
+	nvic_irq_enable(CAN0_RX1_IRQn, 0, 0);
+	can_interrupt_enable(CAN0, CAN_INTEN_RFNEIE1);
+}
+
+void s600_can_device_deinit(void)
+{
+	nvic_irq_disable(CAN0_RX1_IRQn);
+	can_deinit(CAN0);
+}
+
+u8 s600_can_find_mailbox(void)
+{
+	u32 value = CAN_TSTAT(CAN0);
+
+	if ((value & CAN_TSTAT_TME0) != 0) {
+		return CAN_MAILBOX0;
+	}
+
+	if ((value & CAN_TSTAT_TME1) != 0) {
+		return CAN_MAILBOX1;
+	}
+
+	if ((value & CAN_TSTAT_TME2) != 0) {
+		return CAN_MAILBOX2;
+	}
+
+	return CAN_NOMAILBOX;
+}
+
+u8 s600_can_wait_mailbox(void)
+{
+	u32 count;
+
+	for (count = 0; count < 1000000; count++) {
+		u8 mailbox = s600_can_find_mailbox();
+
+		if (mailbox != CAN_NOMAILBOX) {
+			return mailbox;
+		}
+	}
+
+	return CAN_NOMAILBOX;
+}
+
+void s600_can_send_frame(u8 mailbox, u32 efid, const u8 *buff, u8 length)
+{
+	/* set the data length */
+	CAN_TMP(CAN0, mailbox) &= ~CAN_TMP_DLENC;
+	CAN_TMP(CAN0, mailbox) |= length;
+
+	/* set the data */
+	CAN_TMDATA0(CAN0, mailbox) = ((const u32 *) buff)[0];
+
+	if (length > 4) {
+		CAN_TMDATA1(CAN0, mailbox) = ((const u32 *) buff)[1];
+	}
+
+	/* enable transmission */
+	CAN_TMI(CAN0, mailbox) = TMI_EFID(efid) | CAN_FF_EXTENDED | CAN_FT_DATA | CAN_TMI_TEN;
+}
+
+bool s600_can_tx_packet(s600_can_packet_t *packet)
+{
+	u8 total, index;
+	u8 mailbox;
+	u16 length;
+	u32 efid;
+
+	mailbox = s600_can_find_mailbox();
+	if (mailbox == CAN_NOMAILBOX) {
+		return false;
+	}
+
+	total = (packet->length + 7) / 8;
+	index = packet->offset / 8 + 1;
+
+	efid = 6 << 26 | U32(packet->type) << 24
+		| U32(total & 0x1F) << 19 | U32(index & 0x1F) << 14
+		| S600_CAN_DEST_ID(packet->dest) | S600_CAN_SRC_ID(packet->src);
+
+	length = packet->length - packet->offset;
+	if (length > 8) {
+		length = 8;
+	}
+
+	s600_can_send_frame(mailbox, efid, packet->data + packet->offset, length);
+	packet->offset += length;
+
+	return true;
+}
+
+__STATIC_INLINE s600_can_packet_t *s600_can_rx_front_get(u8 addr)
+{
+	u8 index;
+
+	for (index = 0; index < s600_can_rx_front_len; index++) {
+		s600_can_packet_t *packet = s600_can_rx_fronts[index];
+
+		if (packet->src == addr) {
+			return packet;
+		}
+	}
+
+	if (s600_can_rx_front_len < NELEM(s600_can_rx_fronts)) {
+		u8 index = byte_queue_alloc(&s600_can_rx_allocator);
+		s600_can_packet_t *packet;
+
+		if (index == 0xFF) {
+			return NULL;
+		}
+
+		packet = s600_can_rx_packets + index;
+		packet->src = addr;
+
+#if S600_CAN_USE_MASK
+		packet->mask = 0;
+#else
+		packet->index = 0xFF;
+#endif
+
+		s600_can_rx_fronts[s600_can_rx_front_len] = packet;
+		s600_can_rx_front_len++;
+		return packet;
+	}
+
+	return NULL;
+}
+
+__STATIC_INLINE bool s600_can_rx_front_put(s600_can_packet_t *packet)
+{
+	u8 index;
+
+	for (index = 0; index < s600_can_rx_front_len; index++) {
+		if (s600_can_rx_fronts[index] == packet) {
+			s600_can_rx_front_len--;
+
+			while (index < s600_can_rx_front_len) {
+				s600_can_rx_fronts[index] = s600_can_rx_fronts[index + 1];
+				index++;
+			}
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
+bool s600_can_rx_packet(u8 fifo)
+{
+	s600_can_packet_t *packet;
+	u8 index, total;
+	u32 *buff;
+	u32 value;
+	u32 efid;
+
+	value = CAN_RFIFOMI(CAN0, fifo);
+	if ((value & (CAN_RFIFOMI_FT | CAN_RFIFOMI_FF)) != (CAN_FT_DATA | CAN_FF_EXTENDED)) {
+		return true;
+	}
+
+	efid = GET_RFIFOMI_EFID(value);
+
+	value = CAN_RFIFOMP(CAN0, fifo);
+
+#ifdef CAN_RFIFOMP_FDF
+	if ((value & CAN_RFIFOMP_FDF) != CAN_FDF_CLASSIC) {
+		return true;
+	}
+#endif
+
+	packet = s600_can_rx_front_get((efid >> 7) & 0x7F);
+	if (packet == NULL) {
+		return false;
+	}
+
+	index = (efid >> 14) & 0x1F;
+	total = (efid >> 19) & 0x1F;
+
+#if S600_CAN_USE_MASK
+	if (index == total) {
+		packet->dest = efid & 0x7F;
+		packet->type = (efid >> 24) & 0x03;
+		packet->length = ((total - 1) & 0x1F) << 3;
+		packet->length += GET_RFIFOMP_DLENC(value);
+	}
+
+	index = (index - 1) & 0x1F;
+
+	if (index == 0) {
+		packet->mask = 1;
+	} else {
+		packet->mask |= 1 << index;
+	}
+
+	buff = (u32 *) packet->datas[index];
+	buff[0] = CAN_RFIFOMDATA0(CAN0, fifo);
+	buff[1] = CAN_RFIFOMDATA1(CAN0, fifo);
+
+	if (total == 0) {
+		value = 0xFFFFFFFF;
+	} else {
+		value = (1 << total) - 1;
+	}
+
+	if ((packet->mask & value) == value) {
+		byte_queue_write_byte(&s600_can_rx_queue, packet - s600_can_rx_packets);
+		s600_can_rx_front_put(packet);
+	}
+#else
+	if (index != packet->index) {
+		if (index != 1) {
+			return true;
+		}
+
+		packet->type = (efid >> 24) & 0x03;
+		packet->dest = efid & 0x7F;
+		packet->offset = 0;
+		packet->index = 2;
+	} else {
+		packet->index = (index + 1) & 0x1F;
+	}
+
+	buff = (u32 *) (packet->data + packet->offset);
+	packet->offset += GET_RFIFOMP_DLENC(value);
+	buff[0] = CAN_RFIFOMDATA0(CAN0, fifo);
+	buff[1] = CAN_RFIFOMDATA1(CAN0, fifo);
+
+	if (total == index) {
+		packet->length = packet->offset;
+		byte_queue_write_byte(&s600_can_rx_queue, packet - s600_can_rx_packets);
+		s600_can_rx_front_put(packet);
+	}
+#endif
+
+	return true;
+}
+
+void s600_can_transmit(void)
+{
+	while (1) {
+		u8 index;
+
+		if (s600_can_tx_front) {
+			if (s600_can_tx_front->offset < s600_can_tx_front->length) {
+				s600_can_tx_packet(s600_can_tx_front);
+				break;
+			}
+
+			byte_queue_free(&s600_can_tx_allocator, s600_can_tx_front - s600_can_tx_packets);
+		}
+
+		if (byte_queue_read(&s600_can_tx_queue, &index, 1) > 0) {
+			s600_can_tx_front = s600_can_tx_packets + index;
+		} else {
+			s600_can_tx_front = NULL;
+			break;
+		}
+	}
+}
+
+__STATIC_INLINE void s600_can_process_packet(s600_can_packet_t *packet)
+{
+	s600_can_process_command(packet->src, packet->data, packet->length);
+}
+
+void s600_can_poll(void)
+{
+	u8 index;
+
+	s600_can_transmit();
+
+	while (byte_queue_read(&s600_can_rx_queue, &index, 1) > 0) {
+		s600_can_process_packet(s600_can_rx_packets + index);
+		byte_queue_free(&s600_can_rx_allocator, index);
+	}
+}
+
+bool s600_can_send_packet(u8 dest, u8 src, u8 type, const void *data, u16 length)
+{
+	s600_can_packet_t *packet;
+	u8 index;
+
+	index = byte_queue_alloc(&s600_can_tx_allocator);
+	if (index == 0xFF) {
+		return false;
+	}
+
+	packet = s600_can_tx_packets + index;
+
+	packet->offset = 0;
+	packet->type = type;
+	packet->dest = dest;
+	packet->src = src;
+	packet->length = length;
+	memcpy(packet->data, data, length);
+
+	byte_queue_write_byte(&s600_can_tx_queue, index);
+	s600_can_transmit();
+
+	return true;
+}
+
+void s600_can_send_packet_block(u8 dest, u8 src, u8 type, const void *data, u16 length)
+{
+	while (!s600_can_send_packet(dest, src, type, data, length)) {
+		s600_can_transmit();
+	}
+}
+
+bool s600_can_send_command(u8 addr, const void *command, u16 length)
+{
+	return s600_can_send_packet(addr, CONFIG_CAN_ID, 1, command, length);
+}
+
+bool s600_can_send_response(u8 addr, const void *response, u16 length)
+{
+	return s600_can_send_packet(addr, CONFIG_CAN_ID, 2, response, length);
+}
+
+bool s600_can_send_event(u8 addr, const void *event, u16 length)
+{
+	return s600_can_send_packet(addr, CONFIG_CAN_ID, 3, event, length);
+}
+
+// ================================================================================
+
+void CAN0_RX1_IRQHandler(void)
+{
+	s600_can_rx_packet(CAN_FIFO1);
+	CAN_RFIFO1(CAN0) |= CAN_RFIFO1_RFD1;
+}

+ 63 - 0
bootloader/s600_can.h

@@ -0,0 +1,63 @@
+#pragma once
+
+#include "s600.h"
+#include "byte_queue.h"
+
+#define S600_CAN_BUILD_CMD(cmd) \
+	(CONFIG_CAN_ID << 8 | (cmd))
+
+#define S600_CAN_DEST_OFFSET		0
+#define S600_CAN_SRC_OFFSET			7
+
+#define S600_CAN_ALL_ID				0x7F
+#define S600_CAN_BAUDRATE			250
+
+#define S600_CAN_USE_MASK			0
+
+#define S600_CAN_DEST_ID(id) \
+	(U32(id) << S600_CAN_DEST_OFFSET)
+
+#define S600_CAN_SRC_ID(id) \
+	(U32(id) << S600_CAN_SRC_OFFSET)
+
+typedef struct {
+	u32 efid;
+	u8 length;
+	u8 data[8];
+} s600_can_frame_t;
+
+#pragma anon_unions
+
+typedef struct {
+	u8 dest;
+	u8 src;
+	u8 type;
+	u8 index;
+	u16 offset;
+	u16 length;
+
+#if S600_CAN_USE_MASK
+	u32 mask;
+#endif
+
+	union {
+		u8 data[256];
+		u8 datas[32][8];
+	};
+} s600_can_packet_t;
+
+void s600_can_device_config(void);
+void s600_can_device_deinit(void);
+
+u8 s600_can_find_mailbox(void);
+u8 s600_can_wait_mailbox(void);
+void s600_can_send_frame(u8 mailbox, u32 efid, const u8 *buff, u8 length);
+void s600_can_transmit(void);
+void s600_can_poll(void);
+
+bool s600_can_send_packet(u8 dest, u8 src, u8 type, const void *data, u16 length);
+void s600_can_send_packet_block(u8 dest, u8 src, u8 type, const void *data, u16 length);
+bool s600_can_send_command(u8 addr, const void *command, u16 length);
+bool s600_can_send_command_read_status(void);
+bool s600_can_send_response(u8 addr, const void *response, u16 length);
+bool s600_can_send_event(u8 addr, const void *event, u16 length);

+ 3 - 0
bootloader/s600_config.h

@@ -0,0 +1,3 @@
+#pragma once
+
+#include "gd32f10x.h"

+ 206 - 0
bootloader/s600_iap.c

@@ -0,0 +1,206 @@
+#include "s600_iap.h"
+#include "s600_can.h"
+#include "string.h"
+
+static u8 s600_iap_page[FMC_PAGE_SIZE];
+static u32 s600_iap_address;
+static u16 s600_iap_length;
+
+static void s600_iap_flag_clear(void)
+{
+#ifdef CONFIG_GD32E10X
+    fmc_flag_clear(FMC_FLAG_PGERR | FMC_FLAG_PGAERR | FMC_FLAG_WPERR | FMC_FLAG_END);
+#else
+	fmc_flag_clear(FMC_FLAG_BANK0_PGERR | FMC_FLAG_BANK0_WPERR | FMC_FLAG_BANK0_END);
+#endif
+}
+
+static u32 s600_iap_capacity(void)
+{
+	return (REG32(0x1FFFF7E0) & 0xFFFF) << 10;
+}
+
+static u32 s600_iap_last_page(void)
+{
+	u32 capacity = s600_iap_capacity();
+	return 0x08000000 + (capacity - FMC_PAGE_SIZE);
+}
+
+void s600_iap_write_begin(void)
+{
+	s600_iap_address = PART_APP;
+	s600_iap_length = 0;
+}
+
+bool s600_iap_write_end(u32 length, u32 checksum)
+{
+	u32 buff[2];
+
+	if (!s600_iap_flush(s600_iap_length)) {
+		return false;
+	}
+
+	if (checksum != s600_iap_checksum(PART_APP, length)) {
+		return false;
+	}
+
+	buff[0] = length;
+	buff[1] = checksum;
+
+	return s600_iap_write_page(s600_iap_last_page(), buff, sizeof(buff), true);
+}
+
+bool s600_iap_write_page(u32 addr, const void *buff, u16 length, bool erase)
+{
+	const u32 *p, *p_end;
+
+	fmc_unlock();
+
+	if (erase) {
+		s600_iap_flag_clear();
+
+		if (FMC_READY != fmc_page_erase(addr)) {
+			return false;
+		}
+	}
+
+	for (p = (const u32 *) buff, p_end = p + ((length + 3) / 4); p < p_end; p++, addr += 4) {
+		s600_iap_flag_clear();
+
+		if (FMC_READY != fmc_word_program(addr, *p)) {
+			return false;
+		}
+	}
+
+	fmc_lock();
+
+	return true;
+}
+
+bool s600_iap_flush(u32 length)
+{
+	if (!s600_iap_write_page(s600_iap_address, s600_iap_page, length, true)) {
+		return false;
+	}
+
+	s600_iap_address += length;
+	s600_iap_length = 0;
+
+	return true;
+}
+
+bool s600_iap_write(const u8 *buff, u16 length)
+{
+	while (1) {
+		u16 remain = sizeof(s600_iap_page) - s600_iap_length;
+
+		if (length < remain) {
+			memcpy(s600_iap_page + s600_iap_length, buff, length);
+			s600_iap_length += length;
+			break;
+		}
+
+		memcpy(s600_iap_page + s600_iap_length, buff, remain);
+		if (!s600_iap_flush(sizeof(s600_iap_page))) {
+			return false;
+		}
+
+		buff += remain;
+		length -= remain;
+	}
+
+	return true;
+}
+
+bool s600_iap_write_u32(u32 value)
+{
+	return s600_iap_write((u8 *) &value, sizeof(value));
+}
+
+#if S600_IAP_CRC32
+u32 s600_iap_checksum(u32 addr, u32 length)
+{
+	u32 crc = 0xFFFFFFFFU;
+	const u8 *p, *p_end;
+
+	for (p = (u8 *) addr, p_end = p + length; p < p_end; p++) {
+		u8 value = *p;
+		u8 i;
+
+		for (i = 0; i < 8; i++) {
+			if (((crc ^ value) & 1) != 0) {
+				crc = (crc >> 1) ^ 0xEDB88320U;
+			} else {
+				crc >>= 1;
+			}
+
+			value >>= 1;
+		}
+	}
+
+	return crc ^ 0xFFFFFFFFU;
+}
+#else
+u32 s600_iap_checksum(u32 addr, u32 length)
+{
+	const u8 *p, *p_end;
+	u32 checksum = 0;
+
+	for (p = (u8 *) addr, p_end = p + length; p < p_end; p++) {
+		checksum += *p;
+	}
+
+	return checksum;
+}
+#endif
+
+bool s600_iap_boot(bool normal)
+{
+	const s600_iap_info_t *info = (s600_iap_info_t *) PART_FACTORY;
+	const char *board_name = (const char *) PART_NAME;
+
+	if (info->success == 0xFFFFFFFF) {
+		u32 value = APP_BOOT_OK;
+		s600_iap_write_page((u32) &info->success, &value, sizeof(value), false);
+		s600_iap_write_page(s600_iap_last_page(), info, sizeof(*info), true);
+	}
+
+	if (strcmp(board_name, CONFIG_BOARD_NAME) != 0) {
+		return false;
+	}
+
+	info = (s600_iap_info_t *) s600_iap_last_page();
+
+	if (normal) {
+		if (info->success != APP_BOOT_OK) {
+			return false;
+		}
+	} else if (info->length > s600_iap_capacity() - 0x2000) {
+		return false;
+	} else if (info->checksum != s600_iap_checksum(PART_APP, info->length)) {
+		return false;
+	}
+
+#if 0
+	if ((REG32(PART_APP) & 0x2FFE0000) != 0x20000000) {
+		return false;
+	}
+#endif
+
+#ifdef CONFIG_BOARD_PS100
+	nvic_irq_disable(USART0_IRQn);
+	nvic_irq_disable(USART1_IRQn);
+#endif
+
+	s600_can_device_deinit();
+	SysTick->CTRL = 0;
+
+	/* initialize user application's stack pointer */
+	__set_MSP(REG32(PART_APP));
+	nvic_vector_table_set(PART_APP, 0);
+
+	/* jump to user application */
+	((void (*)(void)) REG32(PART_APP + 4))();
+
+	return false;
+}

+ 27 - 0
bootloader/s600_iap.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#include "s600.h"
+
+#define APP_BOOT_OK			0x11223344
+
+#define PART_FACTORY		0x08001FF0
+#define PART_APP			0x08002000
+#define PART_NAME			0x08002800
+#define PART_VERSION		0x08002808
+
+#define S600_IAP_CRC32		1
+
+typedef struct {
+	u32 length;
+	u32 checksum;
+	u32 success;
+} s600_iap_info_t;
+
+void s600_iap_write_begin(void);
+bool s600_iap_write_end(u32 length, u32 checksum);
+bool s600_iap_write_page(u32 addr, const void *buff, u16 length, bool erase);
+bool s600_iap_flush(u32 length);
+bool s600_iap_write(const u8 *buff, u16 length);
+bool s600_iap_write_u32(u32 value);
+bool s600_iap_boot(bool normal);
+u32 s600_iap_checksum(u32 addr, u32 length);