1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-15 04:15:32 +03:00
betaflight-configurator/src/js/protocols/stm32.js
2021-12-18 08:21:05 +01:00

866 lines
37 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
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
*/
'use strict';
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) {
// delay to allow board to boot in bootloader mode
// required to detect if a DFU device appears
setTimeout(startFlashing, 1000);
} 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);
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'));
}
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();