Commit 91a56368 authored by Ryan Day's avatar Ryan Day
Browse files

adding v1 from @jacobrosenthal

parent ae0a04e3
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -3,9 +3,8 @@ js-stk500

[STK500](http://www.atmel.com/tools/stk500.aspx) javascript implementation for node and browserify

It's not there yet, but the goals are:

* Use browserify so it can be put into NPM and can be used either via node or browser
* Get it cleaned up and working for any chip, not just the 256RFR2.
* Merge Serial.js and Serial2.js, (Serial.js was for Chrome version <= 33, Serial2.js > 33)
* Ensure both STK500 and STK500v2 work (right now it's only tested with STK500v2)
### working on doc for refactor!


+2 −2
Original line number Diff line number Diff line

//default to v2
module.exports = require('./stk500v2');
module.exports = require('./stk500v2.js');
module.exports.v2 = module.exports;
module.exports.v1 = false;//todo
module.exports.v1 = require('./stk500.js');

stk500.js

0 → 100644
+456 −0
Original line number Diff line number Diff line
//use strict might have screwed up my this context, or might not have.. 
// var serialPort = require("serialport");
var async = require("async");
var bufferEqual = require('buffer-equal');

var Cmnd_STK_GET_SYNC = 0x30;
var Cmnd_STK_SET_DEVICE = 0x42;
var Cmnd_STK_ENTER_PROGMODE = 0x50;
var Cmnd_STK_LOAD_ADDRESS = 0x55;
var Cmnd_STK_PROG_PAGE = 0x64;
var Cmnd_STK_LEAVE_PROGMODE = 0x51;
var Cmnd_STK_READ_SIGN = 0x75;

var Sync_CRC_EOP = 0x20;

var Resp_STK_OK = 0x10;
var Resp_STK_INSYNC = 0x14;

var memtype = 0x46;
var timeout = 200;

var _options = {
  devicecode:0,
  revision:0,
  progtype:0,
  parmode:0,
  polling:0,
  selftimed:0,
  lockbytes:0,
  fusebytes:0,
  flashpollval1:0,
  flashpollval2:0,
  eeprompollval1:0,
  eeprompollval2:0,
  pagesizehigh:0,
  pagesizelow:0,
  eepromsizehigh:0,
  eepromsizelow:0,
  flashsize4:0,
  flashsize3:0,
  flashsize2:0,
  flashsize1:0
};

//todo abstract out chrome and take serial object shim
function stk500(port) {
	if (!(this instanceof stk500)) 
	return new stk500();

	console.log("constructed");

	this.serialPort = port;

	this.received = new Buffer(300);

	this.receivedSize = 0;

	var self = this;

	this.onDataCallback = function(data) {
		console.log("received " + data.toString('hex'));
		data.copy(self.received, self.receivedSize);
		self.receivedSize = self.receivedSize + data.length;
  }

};

stk500.prototype.matchReceive = function(match, timeout, callback){
	console.log("matching");

	var self = this;

	self.getBytes(match.length, timeout, function(error, data){

		if(error)
		{
			callback(error);
		}else{
			var err = null;

			if(bufferEqual(data, match)){
				console.log(match.toString('hex') + "==" + data.toString('hex'));
			}else{
				console.log(match.toString('hex') + "!=" + data.toString('hex'));
				err = new Error("No Match");
				err.name = "INVALID";
			}
			callback(err, data);
		}

	});

};

stk500.prototype.getBytes = function(numberBytes, timeout, callback){
	console.log("getBytes");

	var self = this;

	var interval = 10;
	var elapsed = interval;

	var timer = setInterval(check, interval);

	function check(){
		console.log(elapsed + "ms elapsed");

		if(elapsed>timeout){
			clearInterval(timer);
			self.received = new Buffer(300);
			self.receivedSize = 0;

			var err = new Error("Timed out after " + elapsed + "ms");
			err.name = "TIMEOUT";
			callback(err);
		}

		elapsed = elapsed + interval;

		if(self.receivedSize>=numberBytes){
			clearInterval(timer);
			var received_copy = new Buffer(self.receivedSize);
			self.received.copy(received_copy);
			self.received = new Buffer(300);
			self.receivedSize = 0;
			callback(null, received_copy);

		}
	}
};

//todo use error
stk500.prototype.connect = function(done) {
	console.log("connect");

	var self = this;

	this.serialPort.open(function (error) {

	  if ( error ) {
	    console.log('failed to connect: ' + error);
	    done(error);
	  } else {
	    console.log('connected');

	    self.received = new Buffer(300);
	    self.receivedSize = 0;

	    self.serialPort.on('data', self.onDataCallback);
	    done();
	  }
	});

};

//todo can this timeout? or fail?
stk500.prototype.disconnect = function(done) {
	console.log("disconnect");

	var self = this;

	self.serialPort.removeListener('data', self.onDataCallback);

	self.serialPort.close(function (error) {
	  if ( error ) {
	    console.log('failed to close: ' + error);
	    done(error);
	  } else {
	    console.log('closed');
	    done();
	  }
	});

};

stk500.prototype.reset = function(delay1, delay2, done){
	console.log("reset");

	var self = this;

	async.series([
	  function(cbdone) {
	  	console.log("asserting");
	    self.serialPort.set({rts:true, dtr:true}, function(result){
	    	console.log("asserted");
	    	if(result) cbdone(result);
	    	else cbdone();
	    });
	  },
	  function(cbdone) {
	  	console.log("wait");
	    setTimeout(cbdone, delay1);
	  },
	  function(cbdone) {
	  	console.log("clearing");
	    self.serialPort.set({rts:false, dtr:false}, function(result){
	    	console.log("clear");
	    	if(result) cbdone(result);
	    	else cbdone();
	    });
	  },
	  function(cbdone) {
	  	console.log("wait");
	    setTimeout(cbdone, delay2);
	  }],
		function(error) {
			done(error);
		}
	);
};

stk500.prototype.sync = function(attempts, done) {
	console.log("sync");
	var self = this;
	var tries = 1;

	var cmd = new Buffer([Cmnd_STK_GET_SYNC, Sync_CRC_EOP]);

	attempt();
	function attempt(){
		tries=tries+1;
		console.log(cmd.toString('hex'));
		self.serialPort.write(cmd, function(error, results){
			console.log("confirm sync");
			self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(error){
				if(error) {
					if(tries<=attempts){
						console.log("failed attempt again");
						attempt();
					}else{
						done(error);
					}
				}else{
					console.log("confirmed sync");
					done();
				}
			});
		});
	}
};

stk500.prototype.verifySignature = function(signature, done) {
	console.log("verify signature");
	var self = this;
	var cmd = new Buffer([Cmnd_STK_READ_SIGN, Sync_CRC_EOP]);

	var match = new Buffer([Resp_STK_INSYNC]);
	match = Buffer.concat([match,signature]);
	var end = new Buffer([Resp_STK_OK]);
	match = Buffer.concat([match,end]);

	console.log(cmd.toString('hex'));
	this.serialPort.write(cmd, function(error, results){
		console.log("confirm signature");		
			self.matchReceive(match, timeout, function(error, response){
				if(error){
					if(error.name==="INVALID"){
						done(new Error("signature doesnt match. Found: " + response.toString('hex'), error));
					}else{
						done(error);
					}
				}else{
					done();
				}
		});
	});
};

stk500.prototype.getSignature = function(done) {
	console.log("verify signature");
	var self = this;
	var cmd = new Buffer([Cmnd_STK_READ_SIGN, Sync_CRC_EOP]);

	console.log(cmd.toString('hex'));
	this.serialPort.write(cmd, function(error, results){
		console.log("confirm signature");		
			self.getBytes(5, timeout, function(error, response){
				console.log(response);
				done(error, response);
		});
	});
};

stk500.prototype.setOptions = function(options, done) {
	console.log("set device");
	var self = this;

	options.devicecode = options.devicecode || _options.devicecode;
	options.revision = options.revision || _options.revision;
	options.progtype = options.progtype || _options.progtype;
	options.parmode = options.parmode || _options.parmode;
	options.polling = options.polling || _options.polling;
	options.selftimed = options.selftimed || _options.selftimed;
	options.lockbytes = options.lockbytes || _options.lockbytes;
	options.fusebytes = options.fusebytes || _options.fusebytes;
	options.flashpollval1 = options.flashpollval1 || _options.flashpollval1;
	options.flashpollval2 = options.flashpollval2 || _options.flashpollval2;
	options.eeprompollval1 = options.eeprompollval1 || _options.eeprompollval1;
	options.eeprompollval2 = options.eeprompollval2 || _options.eeprompollval2;
	options.pagesizehigh = options.pagesizehigh || _options.pagesizehigh;
	options.pagesizelow = options.pagesizelow || _options.pagesizelow;
	options.eepromsizehigh = options.eepromsizehigh || _options.eepromsizehigh;
	options.eepromsizelow = options.eepromsizelow || _options.eepromsizelow;
	options.flashsize4 = options.flashsize4 || _options.flashsize4;
	options.flashsize3 = options.flashsize3 || _options.flashsize3;
	options.flashsize2 = options.flashsize2 || _options.flashsize2;
	options.flashsize1 = options.flashsize1 || _options.flashsize1;

	var cmd = new Buffer([Cmnd_STK_SET_DEVICE, options.devicecode, options.revision, options.progtype, options.parmode, options.polling, options.selftimed, options.lockbytes, options.fusebytes, options.flashpollval1, options.flashpollval2, options.eeprompollval1, options.eeprompollval2, options.pagesizehigh, options.pagesizelow, options.eepromsizehigh, options.eepromsizelow, options.flashsize4, options.flashsize3, options.flashsize2, options.flashsize1, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));
	this.serialPort.write(cmd, function(error, results){
		console.log("confirm set device");		
			self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(error){
				done(error);
		});
	});
};

stk500.prototype.enterProgrammingMode = function(done) {
	console.log("send enter programming mode");
	var self = this;
	var cmd = new Buffer([Cmnd_STK_ENTER_PROGMODE, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));
	this.serialPort.write(cmd, function(error, results) {
		console.log("sent enter programming mode");
		self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(error){
	  	done(error);
		});
	});
};


stk500.prototype.loadAddress = function(useaddr, done) {
	console.log("load address");
	var self = this;

	var addr_low = useaddr & 0xff;
	var addr_high = (useaddr >> 8) & 0xff;

	var cmd = new Buffer([Cmnd_STK_LOAD_ADDRESS, addr_low, addr_high, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));

	this.serialPort.write(cmd, function(error, results) {
		console.log("confirm load address");
  	self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(error){
  		done(error);
  	});

	});

};


stk500.prototype.loadPage = function(writeBytes, done) {
	console.log("load page");
	var self = this;

	var bytes_low = writeBytes.length & 0xff;
	var bytes_high = writeBytes.length >> 8;

	var cmd = new Buffer([Cmnd_STK_PROG_PAGE, bytes_high, bytes_low, memtype]);
	cmd = Buffer.concat([cmd,writeBytes]);
	var end = new Buffer([Sync_CRC_EOP]);
	cmd = Buffer.concat([cmd,end]);
	console.log(cmd.toString('hex'));

	this.serialPort.write(cmd, function(error, results) {
		console.log("loaded page");

		self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(error){
			done(error);
		});

	});
};

stk500.prototype.upload = function(hex, pageSize, done) {
	console.log("program");

	var pageaddr = 0;
	var writeBytes;
	var useaddr;

	var self = this;

	// program individual pages
  async.whilst(
    function() { return pageaddr < hex.length; },
    function(pagedone) {
			console.log("program page");
      async.series([
      	function(cbdone){
      		useaddr = pageaddr >> 1;
      		cbdone();
      	},
      	function(cbdone){
      		self.loadAddress(useaddr, cbdone);
      	},
        function(cbdone){

					writeBytes = hex.slice(pageaddr, (hex.length > pageSize ? (pageaddr + pageSize) : hex.length - 1))
        	cbdone();
        },
        function(cbdone){
        	self.loadPage(writeBytes, cbdone);
        },
        function(cbdone){
					console.log("programmed page");
        	pageaddr =  pageaddr + writeBytes.length;
        	setTimeout(cbdone, 4);
        }
      ],
      function(error) {
      	console.log("page done");
      	pagedone(error);
      });
    },
    function(error) {
    	console.log("upload done");
    	done(error);
    }
  );
};

stk500.prototype.exitProgrammingMode = function(done) {
	console.log("send leave programming mode");
	var self = this;
	var cmd = new Buffer([Cmnd_STK_LEAVE_PROGMODE, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));

	this.serialPort.write(cmd, function(error, results) {
		console.log("sent leave programming mode");
		self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(error){
			done(error);
		});
	});
};

stk500.prototype.verify = function(hex, done) {
	// console.log("verify");
	// var self = this;

	// serial.send([Cmnd_STK_LOAD_ADDRESS, addr_low, addr_high, Sync_CRC_EOP]) n times
	// self.matchReceive([Resp_STK_INSYNC, Resp_STK_OK]);
	// serial.send ([Cmnd_STK_READ_PAGE, bytes_high, bytes_low, memtype, Sync_CRC_EOP]) n times
	// self.matchReceive([Resp_STK_INSYNC].concat(writeBytes));
	done();
};

//todo convenience function
stk500.prototype.bootload = function (chip, hex, done){
	done();
};

// export the class
module.exports = stk500;