嵌入式主机通过串口 ISP 升级程序

备注:该升级程序方法是单片机给单片机升级程序,因此,在升级程序前必须在主机当中写入待下载的程序。该方法比较方便无需通过电脑下载,适合户外携带维护和批量下载的情况。

由于使用 ISP 上位机升级程序需要借助电脑,在实际生产过程中会带来不便。因此实现了一种嵌入式 ISP 主机用于对目标 MCU 的程序进行升级。嵌入式主机根据 ISP 协议通过串口与 BootLoader 进行通信,从而实现对目标 MCU 程序的升级。

BOOTLOAD引导模式:

BOOT1接地情况下:

硬件连接:

串口初始化

串口初始化:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105


/*!


 \brief configure COM port


 \param[in] com: COM on the board


 \arg EVAL_COM1: COM1 on the board


 \arg EVAL_COM2: COM2 on the board


 \param[out] none


 \retval none


*/


void gd_eval_com_init(uint32_t com)


{


 uint32_t com_id = 0U;


 if(EVAL_COM1 == com){


 com_id = 0U;


 }else if(EVAL_COM2 == com){


 com_id = 1U;


 }


 /* enable GPIO clock */


 rcu_periph_clock_enable(COM_GPIO_CLK[com_id]);


 /* enable USART clock */


 rcu_periph_clock_enable(COM_CLK[com_id]);


 /* connect port to USARTx_Tx */


 gpio_init(COM_GPIO_PORT[com_id], GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, 


COM_TX_PIN[com_id]);


 /* connect port to USARTx_Rx */


 gpio_init(COM_GPIO_PORT[com_id], GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, 


COM_RX_PIN[com_id]);


 /* USART configure */


 usart_deinit(com);


 usart_parity_config(com, USART_PM_EVEN);


 usart_word_length_set(com, USART_WL_9BIT);


 usart_baudrate_set(com, 57600U);


 usart_receive_config(com, USART_RECEIVE_ENABLE);


 usart_transmit_config(com, USART_TRANSMIT_ENABLE);


 usart_enable(com);


}

控制管脚配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24


/* enable the clock */


rcu_periph_clock_enable(RCU_GPIOA);


/* configure BOOT0_CTL port */


gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0);


gpio_bit_reset(GPIOA, GPIO_PIN_0);


/* configure NRST_CTL port */


gpio_init(GPIOA, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_1);


gpio_bit_set(GPIOA, GPIO_PIN_1);

升级程序

待下载 bin2 在下载之前存储到嵌入式主机 0x08004000 地址处,格式为 bin2=bin 文件大小(4

字节)+bin1 文件。可根据需要将 bin 文件存储在其他位置,bin 格式也可以自由设置。

升级主程序流程图:

升级步骤

1.将目标MCU Boot0拉高

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


void boot0_high(void)


{


 gpio_bit_set(GPIOA, GPIO_PIN_0);


 /* pull up the target reset pin */


 gpio_bit_reset(GPIOA, GPIO_PIN_1);


 gpio_bit_set(GPIOA, GPIO_PIN_1);


 /* wait for the target to reset */


 delay_1ms(300);


}

2.发送握手命令,当嵌入式主机收到0x79,说明握手成功。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54


ErrStatus usart_handshake(void)


{


	 uint16_t count = 0;


	 while(count++<10)


	 {


		 usart_data_transmit(USART0, 0x7F);


		 while(RESET == usart_flag_get(USART0, USART_FLAG_TC));


		 while((ERROR == ack_flag) && (timeout--))


		 {


		 }


		 if(0 != timeout)


		 {


			 ack_flag = ERROR;


			 return SUCCESS;


		 }


	 }


 return ERROR;


}

3.发送页擦除命令

首先根据 GD32F4xx flash 结构以及 bin 文件的大小索引需要擦除的扇区。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75


static uint32_t f4_get_sector_number(uint32_t bin_size)


{


 uint32_t sector_size[32] = {16, 16, 16, 16, 64, 128, 128, 128, 128, 128, 128, 128, 


 16, 16, 16, 16, 64, 128, 128, 128, 128, 128, 128, 128, 


256, 256, 256, 256, 256, 256, 256};


 uint32_t i,j;


 uint32_t sector_number = 0;


uint32_t sector_address;


 sector_address = 0x08000000 + bin_size;


 for(i=0; i<31; i++){


 uint32_t start_address = 0x08000000;


 uint32_t end_address = 0x08000000;


 for(j=0; j<=i; j++){


 start_address += sector_size[j] * 1024;


 end_address += sector_size[j] * 1024;


 }


 start_address -= sector_size[i] * 1024;


 end_address -= 1;


 if(sector_address>=start_address && sector_address<=end_address){


 sector_number = i;


 break;


 }


 }


 return sector_number;


}

4.执行擦除命令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93


ErrStatus usart_send_erase_command(void)


{


 uint32_t bin_size;


 uint16_t sector_number = 0;


 uint32_t i, temp;


/* extend erase command */


 buffer_cmd[0] = 0x44;


buffer_cmd[1] = 0xFF - buffer_cmd[0];


/* get the bin size */


 bin_size = *(uint32_t *)(0x8004000);


 bin_size = bin_size;


 sector_number = f4_get_sector_number(bin_size);


 usart_buffer_send(buffer_cmd, 2);


 if(SUCCESS != wait_ack()){


 return ERROR;


 }


 buffer_cmd[0] = (sector_number>>8) & 0xff;


 buffer_cmd[1] = sector_number & 0xff;


 for(i=0; i<sector_number+1; i++){


 temp = 2+2*i;


 buffer_cmd[temp] = (i>>8) & 0xff;


 buffer_cmd[temp+1] = i & 0xff;


 }


 


 buffer_cmd[temp+2] = data_xor(buffer_cmd, temp+2);


 usart_buffer_send(buffer_cmd, temp+3);


 if(SUCCESS != wait_ack()){


 return ERROR;


 }


 return SUCCESS;


}

4.发生编程指令

根据bin文件大小发送命令

4.1 bin文件大小等于252字节

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129


/*!


 \brief send the program command


 \param[in] pro_addr: the address of the target to be programmed


 \param[in] bin_addr: the address where stored the bin file


 \param[in] pro_size: the size to be programmed


 \param[out] none


 \retval ErrStatus


*/


ErrStatus usart_send_program_command(uint32_t pro_addr, uint32_t bin_addr, uint32_t pro_size)


{


 uint8_t i;


 


 buffer_cmd[0] = USART_CMD_PROGRAM;


 buffer_cmd[1] = 0xFF - buffer_cmd[0];


 


 usart_buffer_send(buffer_cmd, 2);


 


 if(SUCCESS != wait_ack()){


 return ERROR;


 }


 buffer_cmd[0] = (uint8_t)((pro_addr >> 24) & 0xff);


 buffer_cmd[1] = (uint8_t)((pro_addr >> 16) & 0xff);


 buffer_cmd[2] = (uint8_t)((pro_addr >> 8) & 0xff);


 buffer_cmd[3] = (uint8_t)(pro_addr & 0xff);


 buffer_cmd[4] = data_xor(buffer_cmd, 4);


 


 usart_buffer_send(buffer_cmd, 5);


 if(SUCCESS != wait_ack()){


 return ERROR;


 }


 memset(buffer_cmd, 0, sizeof(buffer_cmd));


 buffer_cmd[0] = pro_size-1;


 for(i=0; i<pro_size; i=i+4){


 *((uint32_t *)&buffer_cmd[1+i]) = *(uint32_t *)(bin_addr+4+i);


 }


 buffer_cmd[pro_size + 1] = data_xor(buffer_cmd, pro_size + 1);


 usart_buffer_send(buffer_cmd, pro_size + 2);


 if(SUCCESS != wait_ack()){


 return ERROR;


 }


 memset(buffer_cmd, 0, sizeof(buffer_cmd));


return SUCCESS;


}

4.2 bin文件大252字节

由于 BootLoader 存储空间有限,当 bin 文件大于 252 字节时,需要对 bin 文件进行拆包。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87


/*!


 \brief send the program command


 \param[in] pro_addr: the address of the target to be programmed


 \param[in] bin_addr: the address where stored the bin file


 \param[out] none


\retval ErrStatus


*/


ErrStatus usart_program_bin(uint32_t pro_addr, uint32_t bin_addr)


{


 uint32_t bin_size;


 uint32_t i;


 uint32_t pack_num, pack_size, res_size;


 /* 4 bytes aligned */


 pack_size = 252;


 


 bin_size = (*(uint32_t *)(bin_addr));


 pack_num = bin_size/pack_size;


 res_size = bin_size % pack_size;


 /* Unpacking the bin and then program to the flash of the target MCU */


 for(i=0; i<pack_num; i++){


 usart_send_program_command(pro_addr + i*pack_size, bin_addr + i*pack_size, 


pack_size);


 }


 if(0 != res_size){


 usart_send_program_command(pro_addr + pack_num*pack_size, bin_addr + 


pack_num*pack_size, res_size);


 }


 return SUCCESS;


}

5.将目标 MCU 的 BOOT0 拉低,并对其进行复位。

将bin文件全部烧入完毕之后,需要将BOOT0拉低,并复位,就可以看到更新之后的功能程序了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27


void boot0_low(void)


{


 gpio_bit_reset(GPIOA, GPIO_PIN_0);


 /* pull down the target reset pin */


 gpio_bit_reset(GPIOA, GPIO_PIN_1);


 gpio_bit_set(GPIOA, GPIO_PIN_1);


 /* wait for the target to reset */


 delay_1ms(300);


}

GD32F4系列ISP 串口下载就完成了。