forked from betaflight/betaflight-configurator
/
stm32.js
898 lines (740 loc) · 38 KB
/
stm32.js
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
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
/*
STM32 F103 serial bus seems to properly initialize with quite a huge auto-baud range
From 921600 down to 1200, i don't recommend getting any lower then that
Official "specs" are from 115200 to 1200
popular choices - 921600, 460800, 256000, 230400, 153600, 128000, 115200, 57600, 38400, 28800, 19200
*/
import MSPConnectorImpl from "../msp/MSPConnector";
import GUI, { TABS } from "../gui";
import { i18n } from "../localization";
import MSP from "../msp";
import FC from "../fc";
import { bit_check } from "../bit";
import { gui_log } from "../gui_log";
import MSPCodes from "../msp/MSPCodes";
import PortUsage from "../port_usage";
import PortHandler, { usbDevices } from "../port_handler";
import { API_VERSION_1_42 } from "../data_storage";
import serial from "../serial";
import STM32DFU from "./stm32usbdfu";
import semver from "semver";
import $ from 'jquery';
const STM32_protocol = function () {
this.baud = null;
this.options = {};
this.callback = null;
this.hex = null;
this.verify_hex = [];
this.receive_buffer = [];
this.bytesToRead = 0;
this.read_callback = null;
this.upload_time_start = 0;
this.upload_process_alive = false;
this.msp_connector = new MSPConnectorImpl();
this.status = {
ACK: 0x79, // y
NACK: 0x1F,
};
this.command = {
get: 0x00, // Gets the version and the allowed commands supported by the current version of the bootloader
get_ver_r_protect_s: 0x01, // Gets the bootloader version and the Read Protection status of the Flash memory
get_ID: 0x02, // Gets the chip ID
read_memory: 0x11, // Reads up to 256 bytes of memory starting from an address specified by the application
go: 0x21, // Jumps to user application code located in the internal Flash memory or in SRAM
write_memory: 0x31, // Writes up to 256 bytes to the RAM or Flash memory starting from an address specified by the application
erase: 0x43, // Erases from one to all the Flash memory pages
extended_erase: 0x44, // Erases from one to all the Flash memory pages using two byte addressing mode (v3.0+ usart).
write_protect: 0x63, // Enables the write protection for some sectors
write_unprotect: 0x73, // Disables the write protection for all Flash memory sectors
readout_protect: 0x82, // Enables the read protection
readout_unprotect: 0x92, // Disables the read protection
};
// Erase (x043) and Extended Erase (0x44) are exclusive. A device may support either the Erase command or the Extended Erase command but not both.
this.available_flash_size = 0;
this.page_size = 0;
this.useExtendedErase = false;
};
// no input parameters
STM32_protocol.prototype.connect = function (port, baud, hex, options, callback) {
const self = this;
self.hex = hex;
self.port = port;
self.baud = baud;
self.callback = callback;
// we will crunch the options here since doing it inside initialization routine would be too late
self.options = {
no_reboot: false,
reboot_baud: false,
erase_chip: false,
};
if (options.no_reboot) {
self.options.no_reboot = true;
} else {
self.options.reboot_baud = options.reboot_baud;
}
if (options.erase_chip) {
self.options.erase_chip = true;
}
if (self.options.no_reboot) {
serial.connect(port, {bitrate: self.baud, parityBit: 'even', stopBits: 'one'}, function (openInfo) {
if (openInfo) {
// we are connected, disabling connect button in the UI
GUI.connect_lock = true;
self.initialize();
} else {
gui_log(i18n.getMessage('serialPortOpenFail'));
}
});
} else {
let rebootMode = 0; // FIRMWARE
const startFlashing = () => {
if (rebootMode === 0) {
return;
}
// refresh device list
PortHandler.check_usb_devices(function(dfu_available) {
if (dfu_available) {
STM32DFU.connect(usbDevices, hex, options);
} else {
serial.connect(self.port, {bitrate: self.baud, parityBit: 'even', stopBits: 'one'}, function (openInfo) {
if (openInfo) {
self.initialize();
} else {
GUI.connect_lock = false;
gui_log(i18n.getMessage('serialPortOpenFail'));
}
});
}
});
};
const onDisconnect = disconnectionResult => {
if (disconnectionResult) {
// wait until board boots into bootloader mode
// MacOs may need 5 seconds delay
function waitForDfu() {
if (PortHandler.dfu_available) {
console.log(`DFU available after ${failedAttempts / 10} seconds`);
clearInterval(dfuWaitInterval);
startFlashing();
} else {
failedAttempts++;
if (failedAttempts > 100) {
clearInterval(dfuWaitInterval);
console.log(`failed to get DFU connection, gave up after 10 seconds`);
gui_log(i18n.getMessage('serialPortOpenFail'));
GUI.connect_lock = false;
}
}
}
let failedAttempts = 0;
const dfuWaitInterval = setInterval(waitForDfu, 100);
} else {
GUI.connect_lock = false;
}
};
const legacyRebootAndFlash = function() {
serial.connect(self.port, {bitrate: self.options.reboot_baud}, function (openInfo) {
if (!openInfo) {
GUI.connect_lock = false;
gui_log(i18n.getMessage('serialPortOpenFail'));
return;
}
console.log('Using legacy reboot method');
console.log('Sending ascii "R" to reboot');
const bufferOut = new ArrayBuffer(1);
const bufferView = new Uint8Array(bufferOut);
bufferView[0] = 0x52;
serial.send(bufferOut, function () {
serial.disconnect(disconnectionResult => onDisconnect(disconnectionResult));
});
});
};
const onConnectHandler = function () {
gui_log(i18n.getMessage('apiVersionReceived', [FC.CONFIG.apiVersion]));
if (semver.lt(FC.CONFIG.apiVersion, API_VERSION_1_42)) {
self.msp_connector.disconnect(function (disconnectionResult) {
// need some time for the port to be closed, serial port does not open if tried immediately
setTimeout(legacyRebootAndFlash, 500);
});
} else {
console.log('Looking for capabilities via MSP');
MSP.send_message(MSPCodes.MSP_BOARD_INFO, false, false, () => {
if (bit_check(FC.CONFIG.targetCapabilities, FC.TARGET_CAPABILITIES_FLAGS.HAS_FLASH_BOOTLOADER)) {
// Board has flash bootloader
gui_log(i18n.getMessage('deviceRebooting_flashBootloader'));
console.log('flash bootloader detected');
rebootMode = 4; // MSP_REBOOT_BOOTLOADER_FLASH
} else {
gui_log(i18n.getMessage('deviceRebooting_romBootloader'));
console.log('no flash bootloader detected');
rebootMode = 1; // MSP_REBOOT_BOOTLOADER_ROM;
}
const selectedBoard = TABS.firmware_flasher.selectedBoard !== '0' ? TABS.firmware_flasher.selectedBoard : 'NONE';
const connectedBoard = FC.CONFIG.boardName ? FC.CONFIG.boardName : 'UNKNOWN';
function reboot() {
const buffer = [];
buffer.push8(rebootMode);
setTimeout(() => {
MSP.send_message(MSPCodes.MSP_SET_REBOOT, buffer, () => {
// if firmware doesn't flush MSP/serial send buffers and gracefully shutdown VCP connections we won't get a reply, so don't wait for it.
self.msp_connector.disconnect(disconnectionResult => onDisconnect(disconnectionResult));
console.log('Reboot request received by device');
});
}, 100);
}
function onAbort() {
GUI.connect_lock = false;
rebootMode = 0;
console.log('User cancelled because selected target does not match verified board');
reboot();
TABS.firmware_flasher.refresh();
}
if (selectedBoard !== connectedBoard && !TABS.firmware_flasher.localFirmwareLoaded) {
TABS.firmware_flasher.showDialogVerifyBoard(selectedBoard, connectedBoard, onAbort, reboot);
} else {
reboot();
}
});
}
};
const onTimeoutHandler = function() {
GUI.connect_lock = false;
console.log('Looking for capabilities via MSP failed');
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32RebootingToBootloaderFailed'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
};
const onFailureHandler = function() {
GUI.connect_lock = false;
TABS.firmware_flasher.refresh();
};
GUI.connect_lock = true;
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32RebootingToBootloader'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
self.msp_connector.connect(self.port, self.options.reboot_baud, onConnectHandler, onTimeoutHandler, onFailureHandler);
}
};
// initialize certain variables and start timers that oversee the communication
STM32_protocol.prototype.initialize = function () {
const self = this;
// reset and set some variables before we start
self.receive_buffer = [];
self.verify_hex = [];
self.upload_time_start = new Date().getTime();
self.upload_process_alive = false;
// reset progress bar to initial state
TABS.firmware_flasher.flashingMessage(null, TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL)
.flashProgress(0);
// lock some UI elements TODO needs rework
$('select[name="release"]').prop('disabled', true);
serial.onReceive.addListener(function (info) {
self.read(info);
});
GUI.interval_add('STM32_timeout', function () {
if (self.upload_process_alive) { // process is running
self.upload_process_alive = false;
} else {
console.log('STM32 - timed out, programming failed ...');
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32TimedOut'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
// protocol got stuck, clear timer and disconnect
GUI.interval_remove('STM32_timeout');
// exit
self.upload_procedure(99);
}
}, 2000);
self.upload_procedure(1);
};
// no input parameters
// this method should be executed every 1 ms via interval timer
STM32_protocol.prototype.read = function (readInfo) {
// routine that fills the buffer
const data = new Uint8Array(readInfo.data);
for (const instance of data) {
this.receive_buffer.push(instance);
}
// routine that fetches data from buffer if statement is true
if (this.receive_buffer.length >= this.bytesToRead && this.bytesToRead != 0) {
const fetched = this.receive_buffer.slice(0, this.bytesToRead); // bytes requested
this.receive_buffer.splice(0, this.bytesToRead); // remove read bytes
this.bytesToRead = 0; // reset trigger
this.read_callback(fetched);
}
};
// we should always try to consume all "proper" available data while using retrieve
STM32_protocol.prototype.retrieve = function (nBytes, callback) {
if (this.receive_buffer.length >= nBytes) {
// data that we need are there, process immediately
const data = this.receive_buffer.slice(0, nBytes);
this.receive_buffer.splice(0, nBytes); // remove read bytes
callback(data);
} else {
// still waiting for data, add callback
this.bytesToRead = nBytes;
this.read_callback = callback;
}
};
// bytes_to_send = array of bytes that will be send over serial
// bytesToRead = received bytes necessary to trigger read_callback
// callback = function that will be executed after received bytes = bytesToRead
STM32_protocol.prototype.send = function (bytes_to_send, bytesToRead, callback) {
// flip flag
this.upload_process_alive = true;
const bufferOut = new ArrayBuffer(bytes_to_send.length);
const bufferView = new Uint8Array(bufferOut);
// set bytes_to_send values inside bufferView (alternative to for loop)
bufferView.set(bytes_to_send);
// update references
this.bytesToRead = bytesToRead;
this.read_callback = callback;
// empty receive buffer before next command is out
this.receive_buffer = [];
// send over the actual data
serial.send(bufferOut);
};
// val = single byte to be verified
// data = response of n bytes from mcu (array)
// result = true/false
STM32_protocol.prototype.verify_response = function (val, data) {
if (val !== data[0]) {
const message = `STM32 Communication failed, wrong response, expected: ${val} (0x${val.toString(16)}) received: ${data[0]} (0x${data[0].toString(16)})`;
console.error(message);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32WrongResponse',[val, val.toString(16), data[0], data[0].toString(16)]), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
// disconnect
this.upload_procedure(99);
return false;
}
return true;
};
// input = 16 bit value
// result = true/false
STM32_protocol.prototype.verify_chip_signature = function (signature) {
switch (signature) {
case 0x412: // not tested
console.log('Chip recognized as F1 Low-density');
break;
case 0x410:
console.log('Chip recognized as F1 Medium-density');
this.available_flash_size = 131072;
this.page_size = 1024;
break;
case 0x414:
this.available_flash_size = 0x40000;
this.page_size = 2048;
console.log('Chip recognized as F1 High-density');
break;
case 0x418: // not tested
console.log('Chip recognized as F1 Connectivity line');
break;
case 0x420: // not tested
console.log('Chip recognized as F1 Medium-density value line');
break;
case 0x428: // not tested
console.log('Chip recognized as F1 High-density value line');
break;
case 0x430: // not tested
console.log('Chip recognized as F1 XL-density value line');
break;
case 0x416: // not tested
console.log('Chip recognized as L1 Medium-density ultralow power');
break;
case 0x436: // not tested
console.log('Chip recognized as L1 High-density ultralow power');
break;
case 0x427: // not tested
console.log('Chip recognized as L1 Medium-density plus ultralow power');
break;
case 0x411: // not tested
console.log('Chip recognized as F2 STM32F2xxxx');
break;
case 0x440: // not tested
console.log('Chip recognized as F0 STM32F051xx');
break;
case 0x444: // not tested
console.log('Chip recognized as F0 STM32F050xx');
break;
case 0x413: // not tested
console.log('Chip recognized as F4 STM32F40xxx/41xxx');
break;
case 0x419: // not tested
console.log('Chip recognized as F4 STM32F427xx/437xx, STM32F429xx/439xx');
break;
case 0x432: // not tested
console.log('Chip recognized as F3 STM32F37xxx, STM32F38xxx');
break;
case 0x422:
console.log('Chip recognized as F3 STM32F30xxx, STM32F31xxx');
this.available_flash_size = 0x40000;
this.page_size = 2048;
break;
}
if (this.available_flash_size > 0) {
if (this.hex.bytes_total < this.available_flash_size) {
return true;
} else {
console.log(`Supplied hex is bigger then flash available on the chip, HEX: ${this.hex.bytes_total} bytes, limit = ${this.available_flash_size} bytes`);
return false;
}
}
console.log(`Chip NOT recognized: ${signature}`);
return false;
};
// firstArray = usually hex_to_flash array
// secondArray = usually verify_hex array
// result = true/false
STM32_protocol.prototype.verify_flash = function (firstArray, secondArray) {
for (let i = 0; i < firstArray.length; i++) {
if (firstArray[i] !== secondArray[i]) {
console.log(`Verification failed on byte: ${i} expected: 0x${firstArray[i].toString(16)} received: 0x${secondArray[i].toString(16)}`);
return false;
}
}
console.log(`Verification successful, matching: ${firstArray.length} bytes`);
return true;
};
// step = value depending on current state of upload_procedure
STM32_protocol.prototype.upload_procedure = function (step) {
const self = this;
switch (step) {
case 1:
// initialize serial interface on the MCU side, auto baud rate settings
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32ContactingBootloader'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
let sendCounter = 0;
GUI.interval_add('stm32_initialize_mcu', function () { // 200 ms interval (just in case mcu was already initialized), we need to break the 2 bytes command requirement
self.send([0x7F], 1, function (reply) {
if (reply[0] === 0x7F || reply[0] === self.status.ACK || reply[0] === self.status.NACK) {
GUI.interval_remove('stm32_initialize_mcu');
console.log('STM32 - Serial interface initialized on the MCU side');
// proceed to next step
self.upload_procedure(2);
} else {
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32ContactingBootloaderFailed'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
GUI.interval_remove('stm32_initialize_mcu');
// disconnect
self.upload_procedure(99);
}
});
if (sendCounter++ > 3) {
// stop retrying, its too late to get any response from MCU
console.log('STM32 - no response from bootloader, disconnecting');
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32ResponseBootloaderFailed'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
GUI.interval_remove('stm32_initialize_mcu');
GUI.interval_remove('STM32_timeout');
// exit
self.upload_procedure(99);
}
}, 250, true);
break;
case 2:
// get version of the bootloader and supported commands
self.send([self.command.get, 0xFF], 2, function (data) { // 0x00 ^ 0xFF
if (self.verify_response(self.status.ACK, data)) {
self.retrieve(data[1] + 1 + 1, function (data) { // data[1] = number of bytes that will follow [– 1 except current and ACKs]
console.log(`STM32 - Bootloader version: ${(parseInt(data[0].toString(16)) / 10).toFixed(1)}`); // convert dec to hex, hex to dec and add floating point
self.useExtendedErase = (data[7] === self.command.extended_erase);
// proceed to next step
self.upload_procedure(3);
});
}
});
break;
case 3:
// get ID (device signature)
self.send([self.command.get_ID, 0xFD], 2, function (data) { // 0x01 ^ 0xFF
if (self.verify_response(self.status.ACK, data)) {
self.retrieve(data[1] + 1 + 1, function (data) { // data[1] = number of bytes that will follow [– 1 (N = 1 for STM32), except for current byte and ACKs]
const signature = (data[0] << 8) | data[1];
console.log(`STM32 - Signature: 0x${signature.toString(16)}`); // signature in hex representation
if (self.verify_chip_signature(signature)) {
// proceed to next step
self.upload_procedure(4);
} else {
// disconnect
self.upload_procedure(99);
}
});
}
});
break;
case 4:
// erase memory
if (self.useExtendedErase) {
if (self.options.erase_chip) {
const message = 'Executing global chip erase (via extended erase)';
console.log(message);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32GlobalEraseExtended'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
self.send([self.command.extended_erase, 0xBB], 1, function (reply) {
if (self.verify_response(self.status.ACK, reply)) {
self.send( [0xFF, 0xFF, 0x00], 1, function (reply) {
if (self.verify_response(self.status.ACK, reply)) {
console.log('Executing global chip extended erase: done');
self.upload_procedure(5);
}
});
}
});
} else {
const message = 'Executing local erase (via extended erase)';
console.log(message);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32LocalEraseExtended'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
self.send([self.command.extended_erase, 0xBB], 1, function (reply) {
if (self.verify_response(self.status.ACK, reply)) {
// For reference: https://code.google.com/p/stm32flash/source/browse/stm32.c#723
const maxAddress = self.hex.data[self.hex.data.length - 1].address + self.hex.data[self.hex.data.length - 1].bytes - 0x8000000;
const erasePagesN = Math.ceil(maxAddress / self.page_size);
const buff = [];
let checksum = 0;
let pgByte;
pgByte = (erasePagesN - 1) >> 8;
buff.push(pgByte);
checksum ^= pgByte;
pgByte = (erasePagesN - 1) & 0xFF;
buff.push(pgByte);
checksum ^= pgByte;
for (let i = 0; i < erasePagesN; i++) {
pgByte = i >> 8;
buff.push(pgByte);
checksum ^= pgByte;
pgByte = i & 0xFF;
buff.push(pgByte);
checksum ^= pgByte;
}
buff.push(checksum);
console.log(`Erasing. pages: 0x00 - 0x${erasePagesN.toString(16)}, checksum: 0x${checksum.toString(16)}`);
self.send(buff, 1, function (_reply) {
if (self.verify_response(self.status.ACK, _reply)) {
console.log('Erasing: done');
// proceed to next step
self.upload_procedure(5);
}
});
}
});
}
break;
}
if (self.options.erase_chip) {
const message = 'Executing global chip erase' ;
console.log(message);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32GlobalErase'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
self.send([self.command.erase, 0xBC], 1, function (reply) { // 0x43 ^ 0xFF
if (self.verify_response(self.status.ACK, reply)) {
self.send([0xFF, 0x00], 1, function (reply) {
if (self.verify_response(self.status.ACK, reply)) {
console.log('Erasing: done');
// proceed to next step
self.upload_procedure(5);
}
});
}
});
} else {
const message = 'Executing local erase';
console.log(message);
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32LocalErase'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
self.send([self.command.erase, 0xBC], 1, function (reply) { // 0x43 ^ 0xFF
if (self.verify_response(self.status.ACK, reply)) {
// the bootloader receives one byte that contains N, the number of pages to be erased – 1
const maxAddress = self.hex.data[self.hex.data.length - 1].address + self.hex.data[self.hex.data.length - 1].bytes - 0x8000000;
const erasePagesN = Math.ceil(maxAddress / self.page_size);
const buff = [];
let checksum = erasePagesN - 1;
buff.push(erasePagesN - 1);
for (let ii = 0; ii < erasePagesN; ii++) {
buff.push(ii);
checksum ^= ii;
}
buff.push(checksum);
self.send(buff, 1, function (reply) {
if (self.verify_response(self.status.ACK, reply)) {
console.log('Erasing: done');
// proceed to next step
self.upload_procedure(5);
}
});
}
});
}
break;
case 5:
// upload
console.log('Writing data ...');
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32Flashing'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
let blocks = self.hex.data.length - 1,
flashing_block = 0,
address = self.hex.data[flashing_block].address,
bytes_flashed = 0,
bytes_flashed_total = 0; // used for progress bar
const write = function () {
if (bytes_flashed < self.hex.data[flashing_block].bytes) {
const bytesToWrite = ((bytes_flashed + 256) <= self.hex.data[flashing_block].bytes) ? 256 : (self.hex.data[flashing_block].bytes - bytes_flashed);
// console.log('STM32 - Writing to: 0x' + address.toString(16) + ', ' + bytesToWrite + ' bytes');
self.send([self.command.write_memory, 0xCE], 1, function (reply) { // 0x31 ^ 0xFF
if (self.verify_response(self.status.ACK, reply)) {
// address needs to be transmitted as 32 bit integer, we need to bit shift each byte out and then calculate address checksum
const addressArray = [(address >> 24), (address >> 16), (address >> 8), address];
const addressChecksum = addressArray[0] ^ addressArray[1] ^ addressArray[2] ^ addressArray[3];
// write start address + checksum
self.send([addressArray[0], addressArray[1], addressArray[2], addressArray[3], addressChecksum], 1, function (_reply) {
if (self.verify_response(self.status.ACK, _reply)) {
const arrayOut = Array.from(bytesToWrite + 2); // 2 byte overhead [N, ...., checksum]
arrayOut[0] = bytesToWrite - 1; // number of bytes to be written (to write 128 bytes, N must be 127, to write 256 bytes, N must be 255)
let checksum = arrayOut[0];
for (let ii = 0; ii < bytesToWrite; ii++) {
arrayOut[ii + 1] = self.hex.data[flashing_block].data[bytes_flashed]; // + 1 because of the first byte offset
checksum ^= self.hex.data[flashing_block].data[bytes_flashed];
bytes_flashed++;
}
arrayOut[arrayOut.length - 1] = checksum; // checksum (last byte in the arrayOut array)
address += bytesToWrite;
bytes_flashed_total += bytesToWrite;
self.send(arrayOut, 1, function (response) {
if (self.verify_response(self.status.ACK, response)) {
// flash another page
write();
}
});
// update progress bar
TABS.firmware_flasher.flashProgress(Math.round(bytes_flashed_total / (self.hex.bytes_total * 2) * 100));
}
});
}
});
} else {
// move to another block
if (flashing_block < blocks) {
flashing_block++;
address = self.hex.data[flashing_block].address;
bytes_flashed = 0;
write();
} else {
// all blocks flashed
console.log('Writing: done');
// proceed to next step
self.upload_procedure(6);
}
}
};
// start writing
write();
break;
case 6:
// verify
console.log('Verifying data ...');
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32Verifying'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.NEUTRAL);
blocks = self.hex.data.length - 1;
let readingBlock = 0;
address = self.hex.data[readingBlock].address;
let bytesVerified = 0;
let bytesVerifiedTotal = 0; // used for progress bar
// initialize arrays
for (let i = 0; i <= blocks; i++) {
self.verify_hex.push([]);
}
const reading = function () {
if (bytesVerified < self.hex.data[readingBlock].bytes) {
const bytesToRead = ((bytesVerified + 256) <= self.hex.data[readingBlock].bytes) ? 256 : (self.hex.data[readingBlock].bytes - bytesVerified);
// console.log('STM32 - Reading from: 0x' + address.toString(16) + ', ' + bytesToRead + ' bytes');
self.send([self.command.read_memory, 0xEE], 1, function (reply) { // 0x11 ^ 0xFF
if (self.verify_response(self.status.ACK, reply)) {
const addressArray = [(address >> 24), (address >> 16), (address >> 8), address];
const addressChecksum = addressArray[0] ^ addressArray[1] ^ addressArray[2] ^ addressArray[3];
self.send([addressArray[0], addressArray[1], addressArray[2], addressArray[3], addressChecksum], 1, function (_reply) { // read start address + checksum
if (self.verify_response(self.status.ACK, _reply)) {
const bytesToReadN = bytesToRead - 1;
// bytes to be read + checksum XOR(complement of bytesToReadN)
self.send([bytesToReadN, (~bytesToReadN) & 0xFF], 1, function (response) {
if (self.verify_response(self.status.ACK, response)) {
self.retrieve(bytesToRead, function (data) {
for (const instance of data) {
self.verify_hex[readingBlock].push(instance);
}
address += bytesToRead;
bytesVerified += bytesToRead;
bytesVerifiedTotal += bytesToRead;
// verify another page
reading();
});
}
});
// update progress bar
TABS.firmware_flasher.flashProgress(Math.round((self.hex.bytes_total + bytesVerifiedTotal) / (self.hex.bytes_total * 2) * 100));
}
});
}
});
} else {
// move to another block
if (readingBlock < blocks) {
readingBlock++;
address = self.hex.data[readingBlock].address;
bytesVerified = 0;
reading();
} else {
// all blocks read, verify
let verify = true;
for (let i = 0; i <= blocks; i++) {
verify = self.verify_flash(self.hex.data[i].data, self.verify_hex[i]);
if (!verify) break;
}
if (verify) {
console.log('Programming: SUCCESSFUL');
// update progress bar
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32ProgrammingSuccessful'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.VALID);
// proceed to next step
self.upload_procedure(7);
} else {
console.log('Programming: FAILED');
// update progress bar
TABS.firmware_flasher.flashingMessage(i18n.getMessage('stm32ProgrammingFailed'), TABS.firmware_flasher.FLASH_MESSAGE_TYPES.INVALID);
// disconnect
self.upload_procedure(99);
}
}
}
};
// start reading
reading();
break;
case 7:
// go
// memory address = 4 bytes, 1st high byte, 4th low byte, 5th byte = checksum XOR(byte 1, byte 2, byte 3, byte 4)
console.log('Sending GO command: 0x8000000');
self.send([self.command.go, 0xDE], 1, function (reply) { // 0x21 ^ 0xFF
if (self.verify_response(self.status.ACK, reply)) {
const gtAddress = 0x8000000;
address = [(gtAddress >> 24), (gtAddress >> 16), (gtAddress >> 8), gtAddress];
const addressChecksum = address[0] ^ address[1] ^ address[2] ^ address[3];
self.send([address[0], address[1], address[2], address[3], addressChecksum], 1, function (response) {
if (self.verify_response(self.status.ACK, response)) {
// disconnect
self.upload_procedure(99);
}
});
}
});
break;
case 99:
// disconnect
GUI.interval_remove('STM32_timeout'); // stop STM32 timeout timer (everything is finished now)
// close connection
if (serial.connectionId) {
serial.disconnect(self.cleanup);
} else {
self.cleanup();
}
break;
}
};
STM32_protocol.prototype.cleanup = function () {
PortUsage.reset();
// unlocking connect button
GUI.connect_lock = false;
// unlock some UI elements TODO needs rework
$('select[name="release"]').prop('disabled', false);
// handle timing
const timeSpent = new Date().getTime() - self.upload_time_start;
console.log(`Script finished after: ${(timeSpent / 1000)} seconds`);
if (self.callback) {
self.callback();
}
};
// initialize object
const STM32 = new STM32_protocol();
export default STM32;