diff --git a/js/stm32.js b/js/stm32.js new file mode 100644 index 0000000000..b82592aab6 --- /dev/null +++ b/js/stm32.js @@ -0,0 +1,455 @@ +var STM32_protocol = function() { + this.hex_to_flash; // data to flash + + this.receive_buffer; + + this.bytes_to_read = 0; // ref + this.read_callback; // ref + + this.flashing_memory_address; + this.verify_memory_address; + + this.bytes_flashed; + this.bytes_verified; + + this.verify_hex = new Array(); + + this.upload_time_start; + + this.steps_executed; + this.steps_executed_last; + + this.status = { + ACK: 0x79, + 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. +}; + +// no input parameters +STM32_protocol.prototype.connect = function() { + var self = this; + + selected_port = String($('div#port-picker .port select').val()); + + if (selected_port != '0') { + // parity and stopbit properties should be in chrome v30 or v31 + chrome.serial.open(selected_port, {bitrate: 115200, parityBit: 'evenparity', stopBit: 'onestopbit'}, function(openInfo) { + connectionId = openInfo.connectionId; + + if (connectionId != -1) { + console.log('Connection was opened with ID: ' + connectionId); + + // we are connected, disabling connect button in the UI + GUI.connect_lock = true; + + // start the upload procedure + self.initialize(); + } + }); + } else { + console.log('Please select valid serial port'); + } +}; + +// initialize certain variables and start timers that oversee the communication +STM32_protocol.prototype.initialize = function() { + var self = this; + + // reset and set some variables before we start + self.receive_buffer = []; + + self.flashing_memory_address = 0x08000000; + self.verify_memory_address = 0x08000000; + + self.bytes_flashed = 0; + self.bytes_verified = 0; + + self.verify_hex = []; + + self.upload_time_start = microtime(); + + self.steps_executed = 0; + self.steps_executed_last = 0; + + GUI.interval_add('firmware_uploader_read', function() { + self.read(); + }, 1, true); + + GUI.interval_add('STM32_timeout', function() { + if (self.steps_executed > self.steps_executed_last) { // process is running + self.steps_executed_last = self.steps_executed; + } else { + console.log('STM32 - timed out, programming failed ...'); + + // protocol got stuck, clear timer and disconnect + GUI.interval_remove('STM32_timeout'); + + // exit + self.upload_procedure(99); + } + }, 1000); + + // there seems to be 2 unwanted bytes in the parsed array, we will drop them now (WHY ???) + this.hex_to_flash.shift(); + this.hex_to_flash.shift(); + + // first step + self.upload_procedure(1); +}; + +// no input parameters +// this method should be executed every 1 ms via interval timer +STM32_protocol.prototype.read = function() { + var self = this; + + // routine that fills the buffer + chrome.serial.read(connectionId, 128, function(readInfo) { + if (readInfo && readInfo.bytesRead > 0) { + var data = new Uint8Array(readInfo.data); + + for (var i = 0; i < data.length; i++) { + self.receive_buffer.push(data[i]); + } + } + }); + + // routine that fetches data from buffer if statement is true + if (self.receive_buffer.length >= self.bytes_to_read && self.bytes_to_read != 0) { + var data = self.receive_buffer.slice(0, self.bytes_to_read); // bytes requested + self.receive_buffer.splice(0, self.bytes_to_read); // remove read bytes + + self.bytes_to_read = 0; // reset trigger + + self.read_callback(data); + } +}; + +// Array = array of bytes that will be send over serial +// bytes_to_read = received bytes necessary to trigger read_callback +// callback = function that will be executed after received bytes = bytes_to_read +STM32_protocol.prototype.send = function(Array, bytes_to_read, callback) { + var bufferOut = new ArrayBuffer(Array.length); + var bufferView = new Uint8Array(bufferOut); + + // set Array values inside bufferView (alternative to for loop) + bufferView.set(Array); + + // update references + this.bytes_to_read = bytes_to_read; + this.read_callback = callback; + + // send over the actual data + chrome.serial.write(connectionId, bufferOut, function(writeInfo) {}); +}; + +// 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]) { + console.log('STM32 Communication failed, wrong response, expected: ' + val + ' received: ' + data[0]); + + // 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: + // low density + return false; + break; + case 0x410: + // medium density + console.log('Chip recognized as F1 Medium-density'); + + return true; + break; + case 0x414: + // high density + return false + break; + case 0x418: + // connectivity line + return false; + break; + case 0x420: + // medium density value line + return false; + break; + case 0x428: + // high density value line + return false; + break; + case 0x430: + // XL density + return false; + break; + default: + return false; + }; +}; + +// first_array = usually hex_to_flash array +// second_array = usually verify_hex array +// result = true/false +STM32_protocol.prototype.verify_flash = function(first_array, second_array) { + for (var i = 0; i < first_array.length; i++) { + if (first_array[i] != second_array[i]) { + console.log('Verification failed on byte: ' + i + ' expected: 0x' + first_array[i].toString(16) + ' received: 0x' + second_array[i].toString(16)); + return false; + } + } + + console.log('Verification successful, matching: ' + first_array.length + ' bytes'); + + return true; +}; + +// step = value depending on current state of upload_procedure +STM32_protocol.prototype.upload_procedure = function(step) { + var self = this; + self.steps_executed++; + + switch (step) { + case 1: + // initialize serial interface on the MCU side, auto baud rate settings + self.send([0x7F], 1, function(reply) { + if (self.verify_response(self.status.ACK, reply)) { + console.log('STM32 - Serial interface initialized on the MCU side'); + + // proceed to next step + self.upload_procedure(2); + } + }); + 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.send([], data[1] + 2, function(data) { // data[1] = number of bytes that will follow (should be 12 + ack) + console.log('STM32 - Bootloader version: ' + (parseInt(data[0].toString(16)) / 10).toFixed(1)); // convert dec to hex, hex to dec and add floating point + + // 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.send([], data[1] + 2, function(data) { // data[1] = number of bytes that will follow (should be 1 + ack), its 2 + ack, WHY ??? + var 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 { + console.log('Chip not supported, sorry :-('); + + // disconnect + self.upload_procedure(99); + } + }); + } + }); + break; + case 4: + // erase memory + console.log('Executing global chip erase'); + + 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'); + console.log('Writing data ...'); + + // proceed to next step + self.upload_procedure(5); + } + }); + } + }); + break; + case 5: + // upload + if (self.bytes_flashed < self.hex_to_flash.length) { + if ((self.bytes_flashed + 256) <= self.hex_to_flash.length) { + var data_length = 256; + } else { + var data_length = self.hex_to_flash.length - self.bytes_flashed; + } + + console.log('STM32 - Writing to: 0x' + self.flashing_memory_address.toString(16) + ', ' + data_length + ' 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 + var address = [(self.flashing_memory_address >> 24), (self.flashing_memory_address >> 16) & 0xFF, (self.flashing_memory_address >> 8) & 0xFF, (self.flashing_memory_address & 0xFF)]; + var address_checksum = address[0] ^ address[1] ^ address[2] ^ address[3]; + + self.send([address[0], address[1], address[2], address[3], address_checksum], 1, function(reply) { // write start address + checksum + if (self.verify_response(self.status.ACK, reply)) { + var array_out = new Array(data_length + 2); // 2 byte overhead [N, ...., checksum] + array_out[0] = data_length - 1; // number of bytes to be written (to write 128 bytes, N must be 127, to write 256 bytes, N must be 255) + + var checksum = array_out[0]; + for (var i = 0; i < data_length; i++) { + array_out[i + 1] = self.hex_to_flash[self.bytes_flashed]; // + 1 because of the first byte offset + checksum ^= self.hex_to_flash[self.bytes_flashed]; + + self.bytes_flashed++; + self.flashing_memory_address++; + } + + array_out[array_out.length - 1] = checksum; // checksum (last byte in the array_out array) + + self.send(array_out, 1, function(reply) { + if (self.verify_response(self.status.ACK, reply)) { + // flash another page + self.upload_procedure(5); + } + }); + } + }); + } + }); + + } else { + console.log('Writing: done'); + console.log('Verifying data ...'); + + // proceed to next step + self.upload_procedure(6); + } + break; + case 6: + // verify + if (self.bytes_verified < self.hex_to_flash.length) { + if ((self.bytes_verified + 256) <= self.hex_to_flash.length) { + var data_length = 256; + } else { + var data_length = self.hex_to_flash.length - self.bytes_verified; + } + + console.log('STM32 - Reading from: 0x' + self.verify_memory_address.toString(16) + ', ' + data_length + ' bytes'); + + self.send([self.command.read_memory, 0xEE], 1, function(reply) { // 0x11 ^ 0xFF + if (self.verify_response(self.status.ACK, reply)) { + var address = [(self.verify_memory_address >> 24), (self.verify_memory_address >> 16) & 0x00FF, (self.verify_memory_address >> 8) & 0x00FF, (self.verify_memory_address & 0x00FF)]; + var address_checksum = address[0] ^ address[1] ^ address[2] ^ address[3]; + + self.send([address[0], address[1], address[2], address[3], address_checksum], 1, function(reply) { // read start address + checksum + if (self.verify_response(self.status.ACK, reply)) { + var bytes_to_read_n = data_length - 1; + + self.send([bytes_to_read_n, (~bytes_to_read_n) & 0xFF], 1, function(reply) { // bytes to be read + checksum XOR(complement of bytes_to_read_n) + if (self.verify_response(self.status.ACK, reply)) { + self.send([], data_length, function(data) { + for (var i = 0; i < data.length; i++) { + self.verify_hex.push(data[i]); + self.bytes_verified++; + } + + self.verify_memory_address += data_length; + + // verify another page + self.upload_procedure(6); + }); + } + }); + } + }); + } + }); + } else { + var result = self.verify_flash(self.hex_to_flash, self.verify_hex); + + if (result) { + console.log('Verifying: done'); + console.log('Programming: SUCCESSFUL'); + + // proceed to next step + self.upload_procedure(7); + } else { + console.log('Verifying: failed'); + console.log('Programming: FAILED'); + + // disconnect + self.upload_procedure(99); + } + } + 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'); + + self.send([self.command.go, 0xDE], 1, function(reply) { // 0x21 ^ 0xFF + if (self.verify_response(self.status.ACK, reply)) { + self.send([0x08, 0x00, 0x00, 0x00, 0x08], 1, function(reply) { + if (self.verify_response(self.status.ACK, reply)) { + // disconnect + self.upload_procedure(99); + } + }); + } + }); + break; + case 99: + // disconnect + GUI.interval_remove('firmware_uploader_read'); // stop reading serial + GUI.interval_remove('STM32_timeout'); // stop STM32 timeout timer (everything is finished now) + + console.log('Script finished after: ' + (microtime() - self.upload_time_start).toFixed(4) + ' seconds'); + console.log('Script finished after: ' + self.steps_executed + ' steps'); + + // close connection + chrome.serial.close(connectionId, function(result) { + if (result) { // All went as expected + console.log('Connection closed successfully.'); + + connectionId = -1; // reset connection id + } else { // Something went wrong + if (connectionId > 0) { + console.log('There was an error that happened during "connection-close" procedure'); + } + } + + // unlocking connect button + GUI.connect_lock = false; + }); + break; + } +}; + +// initialize object +var STM32 = new STM32_protocol(); \ No newline at end of file diff --git a/main.html b/main.html index 715da9e672..d65bb3624d 100644 --- a/main.html +++ b/main.html @@ -17,6 +17,7 @@ + diff --git a/tabs/default.js b/tabs/default.js index 3452e6e363..c8866dd453 100644 --- a/tabs/default.js +++ b/tabs/default.js @@ -9,5 +9,8 @@ function tab_initialize_default() { $('a.firmware_flasher').click(function() { tab_initialize_firmware_flasher(); }); + + // temporary + tab_initialize_firmware_flasher(); }); } \ No newline at end of file diff --git a/tabs/firmware_flasher.js b/tabs/firmware_flasher.js index e3635fa6dc..e26625bff3 100644 --- a/tabs/firmware_flasher.js +++ b/tabs/firmware_flasher.js @@ -43,7 +43,9 @@ function tab_initialize_firmware_flasher() { $('a.flash_firmware').click(function() { if (raw_hex != false) { + STM32.hex_to_flash = raw_hex.slice(0); + STM32.connect(); } }); });