烧录工具开发

烧录工具开发

wlwhonest Lv1

代码实现主要用的是node.js。其中async await 语法用的尤为多,用于控制同步异步代码,从而准确发包和收包。其他细节主要在帧格式。

真正的开发还是在实验室学长那拿到了逻辑分析仪后才算真正开始。

image-20250731151656567

烧录工具开发

代码实现主要用的是node.js。其中async await 语法用的尤为多,用于控制同步异步代码,从而准确发包和收包。其他细节主要在帧格式。

looderboot前两块

image-20250724151352624

下载命令

tx

image-20250724151551463

rx

image-20250724151617497

eot

每个分区的最后一块数据,等待设备返回指定包

image-20250724152440685

tx

image-20250724152424236

rx

image-20250724152630357

2025.7.28

开发叙事:

  1. 基于以上分析,重新校对ymodem.js发送数据块的内容,发现每个分区的第零个数据包地址这块少了0x(转成十六进制表示就是【0x30,-x78】)
  2. 然后就是同步异步的逻辑检查,一个await少写了,导致怀疑代码怀疑了快两个小时
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
// ymodem.js

// pymodem.js
const fs = require('fs');
const { calcCrc16 } = require('./crc');
// const ProgressBar = require('cli-progress');
const cliProgress = require('cli-progress');
const { clear } = require('console');
const { rejects } = require('assert');
const { resolve } = require('path');
const path = require('path');
const { finished } = require('stream');

// 控制字符
const SOH = 0x01;
const STX = 0x02;
const EOT = 0x04;
const ACK = 0x06;
const NAK = 0x15;
const CHAR_C = 0x43

// 超时(毫秒)
const YMODEM_C_TIMEOUT = 5000;
const YMODEM_ACK_TIMEOUT = 20000;
const YMODEM_XMIT_TIMEOUT = 30000;

async function sleep(seconds) {
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
}

/**
* 等C
*/

async function waitForC(serial) {
let t0 = Date.now(); // 记录开始时间

return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
console.warn("Timeout while waiting for C");
reject(false); // 如果超时,拒绝
}, YMODEM_C_TIMEOUT); // 设置超时时间

// 定义回调函数以便移除
const onData = (data) => {
// 检查接收到的数据是否为 'C' 字节
if (data[0] === CHAR_C) {
console.log("接收到 C 字节!");
clearTimeout(timeout); // 清除超时定时器
serial.removeListener('data', onData); // 移除事件监听器
resolve(true); // 收到 C 字节后,返回控制权给调用者
}
};

// 监听数据事件
serial.on('data', onData);
});
}

/**
* ymodem_wait_ack
*/

async function ymodem_wait_ack(serial) {
const t0 = Date.now();

return new Promise((resolve, reject) => {
const onData = (data) => {
if (data[0] === ACK) {
clearInterval(interval);
resolve(true); // 收到 ACK,返回 true
serial.removeListener('data', onData); // 清除事件监听器
}

if (data[0] === NAK) {
clearInterval(interval);
resolve(false); // 收到 NAK,返回 false
serial.removeListener('data', onData); // 清除事件监听器
}
};

// 设置监听器
serial.on('data', onData);
// console.log
const interval = setInterval(() => {
if (Date.now() - t0 > YMODEM_ACK_TIMEOUT) {
clearInterval(interval);
console.warn('超时,未收到ack或nak');
reject(false); // 超时未收到 ACK 或 NAK,返回 false
serial.removeListener('data', onData); // 清除事件监听器
}
},10);
});
}

/**
* EOT字符
*/
async function sentEOT(serial) {
return new Promise((resolve,reject)=>{
serial.write(Buffer.from([EOT]),(err)=>{
if(err){
console.error('写入数据:',err);
reject(err);
}else{
resolve(true);
}
})
})
}




/**
* ymodem_blk_timed_xmit
*/

async function ymodem_blk_timed_xmit(serial, blk) {
const t0 = Date.now();

while (Date.now() - t0 < YMODEM_XMIT_TIMEOUT) {
try {
// 写入数据块
const writePromise = new Promise((resolve, reject) => {
serial.write(blk, (err) => {
if (err) {
console.error('写入错误:', err);
reject(err);
} else {
resolve(true);
}
});
});

await writePromise;

// 等待 ACK 或 NAK
const ret = await ymodem_wait_ack(serial);
if (ret === true) {
return true; // 收到 ACK,传输成功
}

console.warn("收到 NAK,重新发送数据块");

} catch (err) {
console.error('写入失败:', err);
return false;
}
}

console.warn("超时,未收到ACK 或 NAK");
return false; // 超时,返回失败
}

async function sendBlock0(serialPort) {
let blkbuf = new Uint8Array(133); // 创建长度为 133 的字节数组

blkbuf[0] = SOH; // 设置 Block 0 的标志
blkbuf[1] = 0x00; // 第一个字节
blkbuf[2] = 0xff; // 第二个字节

// 计算 CRC16 校验(使用 calcCrc16 函数)
const crc = calcCrc16(blkbuf.slice(3, 131)); // 计算第 3 到 131 字节的 CRC16
const crcBuffer = new Uint8Array(2); // 创建一个长度为 2 的字节数组用于存储 CRC
const dataView = new DataView(crcBuffer.buffer);
dataView.setUint16(0, crc, false); // 使用大端字节序设置 CRC 值

// 将 CRC 写入 blkbuf 的最后两个字节
blkbuf.set(crcBuffer, 131);

// 发送数据块并等待 ACK
const ret = await ymodem_blk_timed_xmit(serialPort, blkbuf);
if (!ret) {
console.error('Block 0 传输失败');
return false; // 如果发送失败,返回 false
}

console.log('Block 0 传输成功');
return true; // 如果成功,返回 true
}

/**
* YModem 传输
*/
async function ymodemXfer(serial, filePath, part) {
console.info('>> ymodemXfer received part:', part);

const file_name = part.name;
const file_size = part.length;
const offset = part.partOff
const total_blk = Math.floor((file_size + 1023) / 1024);
const last_blk = file_size % 1024 !== 0 ? (file_size%1024):1024;

// waiting for C
try{
const result = await waitForC(serial); // 等待 C 字节

if(result===true){
console.log("收到 C 字节,接下来的代码继续执行...");
}else{
console.log("未收到 C 字节");
return false
}
}catch(err){
console.error("发生错误:", err);
}

console.debug(`Xfer ${file_name} (${file_size} B, ${total_blk} BLK)`);

// block 0: File Info
let blkbuf = new Uint8Array(133);
blkbuf[0]= SOH
blkbuf[1] = 0x00
blkbuf[2] =0xff
const file_name_bytes = new TextEncoder().encode(file_name);
blkbuf.set(file_name_bytes, 3);

let file_size_hex = file_size.toString(16).padStart(4, '0');
// console.log(`文件大小(hex格式): ${file_size_hex}`);
let byteArray = [0x30, 0x78];
let fl=0;
for (let i = 0; i < file_size_hex.length; i++) {
// 将每个字符转换为 ASCII 编码的字节
if(file_size_hex[i]!=='0'&&fl===0){
fl=1;
}
if(fl===1){
byteArray.push(file_size_hex.charCodeAt(i));
}
}

// console.log("字节数组(十六进制格式): ", byteArray.map(byte => `0x${byte.toString(16).padStart(2, '0')}`).join(' '));

blkbuf.set(byteArray, 3 + file_name_bytes.length+1);
// 计算 CRC
const crc = calcCrc16(blkbuf.slice(3, 131)); // 计算从索引 3 到索引 130 之间的 CRC
// console.log(`JS CRC16(ymodem header)=0x${crc.toString(16).padStart(4, '0')}`);

// 将 CRC 放入 blkbuf 中
const crcBuffer = new Uint8Array(2);
const dataView = new DataView(crcBuffer.buffer);
dataView.setUint16(0, crc, false); // 写入 CRC,false 表示大端字节序
blkbuf.set(crcBuffer, 131); // 将 CRC 设置到 blkbuf 中

// const blkbuf_hex_string = Array.from(blkbuf)
// .map(byte => byte.toString(16).padStart(2, '0'))
// .join(' ');

// console.log(`整个 blkbuf(hex格式): ${blkbuf_hex_string}`);

const res = ymodem_blk_timed_xmit(serial, blkbuf)
if(res===false){
return false;
}

await sleep(0.001)

// Data Blocks: File Data
const progress = new cliProgress.Bar({
format: '[{bar}]{percentage}% | {value}/{total} Transferred',
total: total_blk,
clearOnComplete: true,
});

const fileBuffer = fs.readFileSync(filePath);
let fileIndex = offset
// console.log(total_blk)


for(let i_blk = 1; i_blk <= total_blk; i_blk++){
let blkbuf = new Uint8Array(1029);

blkbuf[0]= STX;
blkbuf[1] = i_blk % 0x100;
blkbuf[2] = 0xff - blkbuf[1];

const rlen = (i_blk === total_blk) ? last_blk : 1024;
blkbuf.set(fileBuffer.slice(fileIndex,fileIndex+rlen),3)
fileIndex += rlen;
// console.log("blkbuf (bytes from index 3):", Array.from(blkbuf.slice(3, 3 + rlen))
// .map(byte => byte.toString(16).padStart(2, '0'))
// .join(' '));
const crc = calcCrc16(blkbuf.slice(3, 1027));
const crcBuffer = new Uint8Array(2);
const dataView = new DataView(crcBuffer.buffer);
dataView.setUint16(0, crc, false);
blkbuf.set(crcBuffer, 1027);
// console.log("blkbuf (hex format):", Array.from(blkbuf)
// .map(byte => byte.toString(16).padStart(2, '0'))
// .join(' '));
const ret = await ymodem_blk_timed_xmit(serial, blkbuf);
if(!ret){
console.error('数据块传输失败');
return false;
}
progress.increment();
await sleep(0.001);
}
console.log('文件传输完成');

// EOT
const eotRes = await sentEOT(serial);
if(!eotRes){
console.error('发送EOT时发生错误');
return false;
}

while(!await ymodem_wait_ack(serial)){
eotRes = await sentEOT(serial);
if(!eotRes){
console.error('发送EOT时发生错误');
return false;
}
}

// block 0 :finish Xmit
const blk0Res = sendBlock0(serial);
if(!blk0Res){
return false
}
return true
}

module.exports = { ymodemXfer };

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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
// flash.js

const sp = require('serialport');
const SerialPort = sp.SerialPort || sp;
const { promisify } = require('util');
const { calcCrc16 } = require('./crc');
const { ymodemXfer } = require('./ymodem');
const { Fwpkg } = require('./fwpkg');
const cliProgress = require('cli-progress');

// 超时 & 命令常量
const RESET_TIMEOUT = 10 * 1000;
const UART_TIMEOUT = 5 * 1000;
const CMD_HANDSHAKE = 0, CMD_DOWNLOAD = 1, CMD_RST = 2;

const WS63E_FLASHINFO = [
{ cmd:0xF0, length:8, data:Buffer.from([0x00,0xC2,0x01,0x00,0x08,0x01,0x00,0x00]) },
{ cmd:0xD2, length:14, data:Buffer.from([0,0,0,0, 0,0,0,0, 0xFF,0xFF,0xFF,0xFF, 0x00,0xFF]) },
{ cmd:0x87, length:2, data:Buffer.from([0x00,0x00]) }
];

class Ws63BurnTools {
constructor(com, baud) {
this.com = com; this.baudrate = baud;
this.ser = null; this.fwpkg = null;
this.isBaudRateSet = false;
this.isRts = false;
}

setBaudrate(b) {
if (this.ser?.isOpen && !this.isBaudRateSet) {
this.ser.update({ baudRate: b });
this.isBaudRateSet = true; // 设置过波特率后,标记为 true
}
}

async sleep(seconds){
return new Promise(resolve => setTimeout(resolve, seconds * 1000));
}

async writeToSerialPort(chunk){
return new Promise((resolve, reject) => {
this.ser.write(chunk, (err) => {
if (err) {
reject(err); // Reject the promise if there's an error
} else {
resolve(chunk.length); // Resolve with the number of bytes written
}
});
});
}

// 打包命令
ws63Send(cmdDef) {
const tot = cmdDef.length + 10;
const buf = Buffer.alloc(tot+12);

buf.writeUInt32LE(0xDEADBEEF, 0);

buf.writeUInt16LE(tot, 4);

buf[6] = cmdDef.cmd;
buf[7] = cmdDef.cmd ^ 0xFF;

cmdDef.data.copy(buf, 8);

const crc = calcCrc16(buf.slice(0, tot - 2));
buf.writeUInt16LE(crc, tot - 2);
// console.info('> Sent frame:', buf.toString('hex').match(/.{1,2}/g).join(' '));

// this.ser.write(buf);
//重写(1)
// 增强检错,提高鲁棒性。
let written = 0;
const totalBytes = buf.length; // totalBytes 表示 buf 中的总字节数

while (written < totalBytes) {
const wrote = this.ser.write(buf.slice(written, totalBytes));

if (wrote <= 0) {
throw new Error("Error while writing to fd");
}
written += wrote;
}

// console.debug("> " + buf.toString('hex').match(/.{1,2}/g).join(' '));
}

async uart_read_until_magic() {
const MAGIC = Buffer.from([0xEF, 0xBE, 0xAD, 0xDE]); // 魔术帧
const buf = Buffer.alloc(1024 + 12); // 1024字节 + 一些额外空间
const t0 = Date.now();
let i = 0;
let framelen = 0;
let st = 0;

while (true) {
// 超时检测
if (Date.now() - t0 > UART_TIMEOUT) {
console.error("uart_read_until_magic: Timeout");
return -1; // 超时错误
}

try {
// Read one byte
const len_read = await new Promise((resolve, reject) => {
this.ser.read(1, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});

if (!len_read || len_read.length === 0) {
continue;
}

// Update last valid char timer
t0 = Date.now();

const byte = len_read[0];
buf[i] = byte;

// FSM states
if (st === 0) {
// Look for magic sequence
if (MAGIC[i] === buf[i]) {
i += 1;
if (i >= 4) {
st = 1;
}
continue;
} else {
i = 0;
}
} else if (st === 1) {
if (i === 5) { // Bytes 4:5 define framelen
framelen = (buf[4] << 8) | buf[5]; // Little-endian
} else if (i === framelen - 1) { // Reached end of frame
break;
}
i += 1;
continue;
}
} catch (err) {
console.error(`uartReadUntilMagic: ${err}`);
return -1;
}
}

// Check CRC
console.debug("< " + Array.from(buf.slice(0, i + 1)).map(x => x.toString(16).padStart(2, '0')).join(' '));

const crc_received = (buf[framelen - 2] << 8) | buf[framelen - 1];
const crc_calculated = calcCrc16(buf.slice(0, framelen - 2));

if (crc_received !== crc_calculated) {
console.warn("Warning: bad CRC from frame!");
return -1;
}

return 0;
}



async ws63SendCmdDef(cmddef){
const totalBytes = cmddef.length + 10;
let buf = new Uint8Array(totalBytes + 12);
let dataView = new DataView(buf.buffer);
dataView.setUint32(0, 0xdeadbeef, true);
dataView.setUint16(4, totalBytes, true);

buf[6] = cmddef.cmd;
buf[7] = cmddef.cmd ^ 0xFF;
buf.set(cmddef.data, 8);
// const cmddata = Array.from(cmddef.data)
// .map(byte => byte.toString(16).padStart(2, '0')) // 转换为两位十六进制数
// .join(' '); // 用空格连接字节
// console.log(`cmddef.data(hex格式): ${cmddata}`);

// const bufHexString = Array.from(buf)
// .map(byte => byte.toString(16).padStart(2, '0')) // 转换为 2 位的十六进制数
// .join(' '); // 用空格连接各个字节

// console.log(`buf(hex格式): ${bufHexString}`);

const crc = calcCrc16(buf.slice(0, totalBytes - 2));
dataView.setUint16(8 + cmddef.length, crc, true);
// console.log(`JS CRC16(header) = 0x${crc.toString(16).padStart(4, '0')}`);

let written = 0;
while(written < totalBytes){
const chunk = buf.slice(written,totalBytes);
const wrote = await this.writeToSerialPort( chunk);

if (wrote <= 0) {
throw new Error("Error while writing to fd");
}
written += wrote;
}
// console.debug("> " + Array.from(buf).map(x => x.toString(16).padStart(2, '0')).join(' '));
}

async setRTS(value) {
return new Promise((resolve, reject) => {
this.ser.set({ rts: value }, (err) => {
if (err) {
reject(new Error('设置 RTS 失败: ' + err.message));
} else {
console.log(`RTS 信号已设置为 ${value ? '高电平' : '低电平'}`);
resolve();
}
});
});
}

async openSerialPortAndSetRTS() {
try {
// 打开串口
await new Promise((resolve, reject) => {
this.ser.open((err) => {
if (err) {
reject(new Error('串口打开失败: ' + err.message));
} else {
console.log('串口已打开');
resolve();
}
});
});

// 设置 RTS 信号为高电平
await this.setRTS(true);
// 等待 100ms
await new Promise(resolve => setTimeout(resolve, 100));

// 设置 RTS 信号为低电平
await this.setRTS(false);
// 等待 100ms
await new Promise(resolve => setTimeout(resolve, 100));

// 再次设置 RTS 信号为高电平
// await this.setRTS(true);

} catch (err) {
console.error(err.message);
}
}

async updateBaudRate(newBaudRate) {
try {
// 更新波特率
await new Promise((resolve, reject) => {
this.ser.update({ baudRate: newBaudRate }, (err) => {
if (err) {
reject(new Error('更新波特率失败: ' + err.message));
} else {
console.log('波特率已更新为:', newBaudRate);
resolve();
}
});
});
} catch (err) {
console.error(err.message);
}
}

async waitForAck(){
let isAck = 0;
const t0 = Date.now();
const ack = Buffer.from([0xEF, 0xBE, 0xAD, 0xDE, 0x0C, 0x00, 0xE1, 0x1E]);
let isBaudRateSet = false; // 设定一个标志来确保波特率只设置一次

const onData = (data) => {
// 检查接收到的数据是否包含预期的 ack 字节模式
if (data.includes(ack)) {
if (!isBaudRateSet) {
// 仅当波特率尚未设置时,才进行设置
this.updateBaudRate(this.baudrate);
console.info("Establishing ymodem session...");
isBaudRateSet = true; // 设置波特率标志
}
isAck = 1; // 设置 isAck 为 1,跳出循环
this.ser.removeListener('data', onData); // 清除 'data' 事件监听器
}
};

// 异步操作
while (true) {
if (isAck === 1) break;
if (Date.now() - t0 > RESET_TIMEOUT) {
console.warn("Timeout while waiting for device reset");
return;
}

// 写入数据并发送命令
WS63E_FLASHINFO[CMD_HANDSHAKE].data.writeUInt32LE(this.baudrate, 0);
await this.ws63SendCmdDef(WS63E_FLASHINFO[CMD_HANDSHAKE]);
await this.sleep(0.01)

// 添加事件监听器
this.ser.on('data', onData);

if (isAck === 1) break;
}
}

async establishYModemSession(){
let isAck = 0;
const t0 = Date.now();
const ack = Buffer.from([0xEF, 0xBE, 0xAD, 0xDE, 0x0C, 0x00, 0xE1, 0x1E]);

// 异步操作
while (true) {
if (isAck === 1) break;
if (Date.now() - t0 > RESET_TIMEOUT) {
console.warn("Timeout while waiting for device reset");
return;
}

// 写入数据并发送命令
WS63E_FLASHINFO[CMD_HANDSHAKE].data.writeUInt32LE(this.baudrate, 0);
await this.ws63SendCmdDef(WS63E_FLASHINFO[CMD_HANDSHAKE]);

// 监听串口数据
this.ser.once('data', (data) => {
// 检查接收到的数据是否包含预期的 ack 字节模式
if (data.includes(ack)) {
// 设置波特率
this.updateBaudRate(this.baudrate);
console.info("Establishing ymodem session...");
isAck = 1; // 设置 isAck 为 1,跳出循环
}
});

if (isAck === 1) break;
}
}


// 完整烧录
async flash(pkgPath) {
console.info(`开始烧录 ${pkgPath}${this.com}`);
this.ser = new SerialPort({ path:this.com, baudRate:115200, autoOpen:false });
// await promisify(this.ser.open).bind(this.ser)();
// this.ser.RtsEnable = true
// this.ser.set({ rts: true }, err => {
// if (err) {
// console.error('设置 RTS 失败:', err);
// } else {
// console.info('RTS 已设为 false');
// return
// }
// });
this.openSerialPortAndSetRTS()


this.fwpkg = new Fwpkg(pkgPath);
// this.fwpkg.show();
let loaderboot = null
for(let bin_info of this.fwpkg.binInfos){
if(bin_info.type === 0){
loaderboot = bin_info;
console.log(loaderboot)
break;
}
}
if(!loaderboot){
console.error("Required loaderboot not found in fwpkg!");
return ;
}

this.fwpkg.show();

// stage 1: Flash loaderboot
console.info('Waiting for device reset... 请按硬件复位键');

await this.waitForAck()
// await this.establishYModemSession()

await this.sleep(0.5)

// Entered YModem mode, transfer loaderboot
console.info(`Transferring ${loaderboot.name}....`);
const ret =await ymodemXfer(this.ser,pkgPath,loaderboot)
if(ret === false){
console.error(`Error transferring${loaderboot.name}`)
return
}
// await this.sleep(0.5)
// await this.uart_read_until_magic()
// .then(res=>{
// if(res===0){
// console.log('数据接受成功')
// }else{
// console.log('数据接收失败');
// }
// }).catch(error => {
// console.error('错误:', error);
// });


for(let bin_info of this.fwpkg.binInfos){
if(bin_info['type']!==1){
continue;
}
console.log(`Transferring ${bin_info['name']}...`);

const eras_size = Math.ceil(bin_info['length'] / 8192.0) * 0x2000;

WS63E_FLASHINFO[CMD_DOWNLOAD]["data"].set(new Uint8Array([bin_info['burnAddr'] & 0xff, (bin_info['burnAddr'] >> 8) & 0xff, (bin_info['burnAddr'] >> 16) & 0xff, (bin_info['burnAddr'] >> 24) & 0xff]), 0);
WS63E_FLASHINFO[CMD_DOWNLOAD]["data"].set(new Uint8Array([bin_info['length'] & 0xff, (bin_info['length'] >> 8) & 0xff, (bin_info['length'] >> 16) & 0xff, (bin_info['length'] >> 24) & 0xff]), 4);
WS63E_FLASHINFO[CMD_DOWNLOAD]["data"].set(new Uint8Array([eras_size & 0xff, (eras_size >> 8) & 0xff, (eras_size >> 16) & 0xff, (eras_size >> 24) & 0xff]), 8);

await this.ws63SendCmdDef(WS63E_FLASHINFO[CMD_DOWNLOAD]);
await this.sleep(0.5);
// this.uart_read_until_magic()
// .then(res=>{
// if(res===0){
// console.log('数据接受成功')
// }else{
// console.log('数据接收失败');
// }
// }).catch(error => {
// console.error('错误:', error);
// });

const ret = await ymodemXfer(this.ser, pkgPath, bin_info);
if (!ret) {
console.error(`Error transferring ${bin_info['name']}`);
return;
}
await new Promise(resolve => setTimeout(resolve, 100));
}

console.log("Done. Resetting device...");

await this.ws63SendCmdDef(WS63E_FLASHINFO[CMD_RST]);
this.uart_read_until_magic()
.then(res=>{
if(res===0){
console.log('数据接受成功')
}else{
console.log('数据接收失败');
}
}).catch(error => {
console.error('错误:', error);
});

bar.stop();
this.ser.close();

}
}

module.exports = Ws63BurnTools;

  • Title: 烧录工具开发
  • Author: wlwhonest
  • Created at : 2025-07-31 15:29:10
  • Updated at : 2025-07-31 15:47:56
  • Link: https://blog.wlwhonest.top/2025/07/31/烧录工具开发/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments