From 46054f5138617d3a6a7a225262e568959bef907d Mon Sep 17 00:00:00 2001 From: Christophe Diericx Date: Wed, 11 Jan 2017 00:45:09 -0400 Subject: [PATCH] Issue #2 - Add support for Micro (ATMega32U4 / AVR109 protocol) --- Documentation/AVR109.pdf | Bin 0 -> 94446 bytes .../CommandLineOptions.cs | 2 +- Source/ArduinoSketchUploader/NLog.config | 2 +- .../ArduinoUploader/ArduinoSketchUploader.cs | 89 ++----- Source/ArduinoUploader/ArduinoUploader.csproj | 29 ++- .../ArduinoBootloaderProgrammer.cs | 6 +- .../BootloaderProgrammer.cs | 19 +- .../ButterflyBootloaderProgrammer.cs | 219 ++++++++++++++++++ .../IBootloaderProgrammer.cs | 3 +- .../OptibootBootloaderProgrammer.cs | 53 ++--- .../SerialPortBootloaderProgrammer.cs | 58 ++++- .../BootloaderProgrammers/SerialPortConfig.cs | 21 ++ .../WiringBootloaderProgrammer.cs | 42 ++-- Source/ArduinoUploader/Hardware/ATMega32U4.cs | 53 +++++ .../ArduinoUploader/Hardware/ArduinoModel.cs | 1 + Source/ArduinoUploader/Hardware/MCU.cs | 2 + .../Protocols/AVR109/Constants.cs | 22 ++ .../Messages/CheckBlockSupportRequest.cs | 13 ++ .../Messages/CheckBlockSupportResponse.cs | 8 + .../Messages/EnterProgrammingModeRequest.cs | 13 ++ .../AVR109/Messages/ExitBootLoaderRequest.cs | 13 ++ .../Messages/LeaveProgrammingModeRequest.cs | 13 ++ .../Messages/ReadSignatureBytesRequest.cs | 13 ++ .../Messages/ReadSignatureBytesResponse.cs | 10 + .../Messages/ReturnProgrammerTypeRequest.cs | 13 ++ .../Messages/ReturnProgrammerTypeResponse.cs | 7 + .../ReturnSoftwareIdentifierRequest.cs | 13 ++ .../ReturnSoftwareIdentifierResponse.cs | 6 + .../Messages/ReturnSoftwareVersionRequest.cs | 13 ++ .../Messages/ReturnSoftwareVersionResponse.cs | 8 + .../ReturnSupportedDeviceCodesRequest.cs | 13 ++ .../Messages/SelectDeviceTypeRequest.cs | 14 ++ .../AVR109/Messages/SetAddressRequest.cs | 15 ++ .../AVR109/Messages/StartBlockLoadRequest.cs | 18 ++ .../AVR109/Messages/StartBlockReadRequest.cs | 18 ++ .../AVR109/Messages/StartBlockReadResponse.cs | 6 + .../Messages/EnableProgrammingModeRequest.cs | 2 +- .../Messages/ExecuteProgramPageRequest.cs | 2 +- .../Messages/ExecuteReadPageRequest.cs | 2 +- .../STK500v1/Messages/GetParameterRequest.cs | 2 +- .../STK500v1/Messages/GetSyncRequest.cs | 2 +- .../STK500v1/Messages/GetSyncResponse.cs | 2 +- .../Messages/LeaveProgrammingModeRequest.cs | 2 +- .../STK500v1/Messages/LoadAddressRequest.cs | 2 +- .../STK500v1/Messages/ReadSignatureRequest.cs | 2 +- .../Messages/ReadSignatureResponse.cs | 4 +- .../SetDeviceProgrammingParametersRequest.cs | 2 +- .../Messages/EnableProgrammingModeRequest.cs | 2 +- .../Messages/ExecuteProgramPageRequest.cs | 2 +- .../Messages/ExecuteProgramPageResponse.cs | 4 +- .../Messages/ExecuteReadPageRequest.cs | 2 +- .../Messages/ExecuteReadPageResponse.cs | 4 +- .../STK500v2/Messages/GetParameterRequest.cs | 2 +- .../STK500v2/Messages/GetParameterResponse.cs | 4 +- .../STK500v2/Messages/GetSyncRequest.cs | 2 +- .../STK500v2/Messages/GetSyncResponse.cs | 4 +- .../Messages/LeaveProgrammingModeRequest.cs | 2 +- .../Messages/LeaveProgrammingModeResponse.cs | 2 +- .../STK500v2/Messages/LoadAddressRequest.cs | 2 +- .../STK500v2/Messages/LoadAddressResponse.cs | 2 +- Source/ArduinoUploader/UploaderLogger.cs | 5 +- Source/ArduinoUploader/UploaderSerialPort.cs | 12 - Source/ArduinoUploader/packages.config | 1 + 63 files changed, 737 insertions(+), 187 deletions(-) create mode 100644 Documentation/AVR109.pdf create mode 100644 Source/ArduinoUploader/BootloaderProgrammers/ButterflyBootloaderProgrammer.cs create mode 100644 Source/ArduinoUploader/BootloaderProgrammers/SerialPortConfig.cs create mode 100644 Source/ArduinoUploader/Hardware/ATMega32U4.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Constants.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/CheckBlockSupportRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/CheckBlockSupportResponse.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/EnterProgrammingModeRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/ExitBootLoaderRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/LeaveProgrammingModeRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/ReadSignatureBytesRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/ReadSignatureBytesResponse.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnProgrammerTypeRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnProgrammerTypeResponse.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareIdentifierRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareIdentifierResponse.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareVersionRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareVersionResponse.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSupportedDeviceCodesRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/SelectDeviceTypeRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/SetAddressRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockLoadRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockReadRequest.cs create mode 100644 Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockReadResponse.cs delete mode 100644 Source/ArduinoUploader/UploaderSerialPort.cs diff --git a/Documentation/AVR109.pdf b/Documentation/AVR109.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0942f44bcfddecee0875fef751fdb650a7e1fe7b GIT binary patch literal 94446 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 literal 0 HcmV?d00001 diff --git a/Source/ArduinoSketchUploader/CommandLineOptions.cs b/Source/ArduinoSketchUploader/CommandLineOptions.cs index 956bd6a..f8fdc51 100644 --- a/Source/ArduinoSketchUploader/CommandLineOptions.cs +++ b/Source/ArduinoSketchUploader/CommandLineOptions.cs @@ -12,7 +12,7 @@ internal class CommandLineOptions [Option('p', "port", Required = true, HelpText = "Name of the COM port where the Arduino is attached (e.g. 'COM1', 'COM2', 'COM3'...).")] public string PortName { get; set; } - [Option('m', "model", Required = true, HelpText = "Arduino model. Valid parameters are any of the following: [Mega2560, NanoR3, UnoR3].")] + [Option('m', "model", Required = true, HelpText = "Arduino model. Valid parameters are any of the following: [Mega2560, Micro, NanoR3, UnoR3].")] public ArduinoModel ArduinoModel { get; set; } [ParserState] diff --git a/Source/ArduinoSketchUploader/NLog.config b/Source/ArduinoSketchUploader/NLog.config index b5b183d..7d8513e 100644 --- a/Source/ArduinoSketchUploader/NLog.config +++ b/Source/ArduinoSketchUploader/NLog.config @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/Source/ArduinoUploader/ArduinoSketchUploader.cs b/Source/ArduinoUploader/ArduinoSketchUploader.cs index bc79d32..e8abbfa 100644 --- a/Source/ArduinoUploader/ArduinoSketchUploader.cs +++ b/Source/ArduinoUploader/ArduinoSketchUploader.cs @@ -1,13 +1,13 @@ using System; using System.Collections.Generic; using System.IO; -using System.IO.Ports; using System.Linq; using ArduinoUploader.BootloaderProgrammers; using ArduinoUploader.Hardware; using IntelHexFormatReader; using IntelHexFormatReader.Model; using NLog; +using RJCP.IO.Ports; namespace ArduinoUploader { @@ -15,9 +15,6 @@ public class ArduinoSketchUploader { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); private readonly ArduinoSketchUploaderOptions options; - private UploaderSerialPort serialPort; - - private const int SerialPortTimeOut = 1000; public ArduinoSketchUploader(ArduinoSketchUploaderOptions options) { @@ -37,12 +34,12 @@ public void UploadSketch(IEnumerable hexFileContents) { var serialPortName = options.PortName; - var ports = SerialPort.GetPortNames(); + var ports = SerialPortStream.GetPortNames(); if (!ports.Any() || ports.Distinct().SingleOrDefault( x => x.Equals(serialPortName, StringComparison.OrdinalIgnoreCase)) == null) { - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format("Specified COM port name '{0}' is not valid.", serialPortName)); } @@ -50,42 +47,48 @@ public void UploadSketch(IEnumerable hexFileContents) SerialPortBootloaderProgrammer programmer = null; IMCU mcu = null; + SerialPortConfig serialPortConfig; switch (options.ArduinoModel) { case ArduinoModel.Mega2560: { mcu = new ATMega2560(); - serialPort = new UploaderSerialPort(serialPortName, 115200); - programmer = new WiringBootloaderProgrammer(serialPort, mcu); + serialPortConfig = new SerialPortConfig(serialPortName, 115200); + programmer = new WiringBootloaderProgrammer(serialPortConfig, mcu); + break; + } + case ArduinoModel.Micro: + { + mcu = new ATMega32U4(); + serialPortConfig = new SerialPortConfig(serialPortName, 57600); + programmer = new ButterflyBootloaderProgrammer(serialPortConfig, mcu); break; } case ArduinoModel.NanoR3: { mcu = new ATMega328P(); - serialPort = new UploaderSerialPort(serialPortName, 57600); - programmer = new OptibootBootloaderProgrammer(serialPort, mcu); + serialPortConfig = new SerialPortConfig(serialPortName, 57600); + programmer = new OptibootBootloaderProgrammer(serialPortConfig, mcu); break; } case ArduinoModel.UnoR3: { mcu = new ATMega328P(); - serialPort = new UploaderSerialPort(serialPortName, 115200); - programmer = new OptibootBootloaderProgrammer(serialPort, mcu); + serialPortConfig = new SerialPortConfig(serialPortName, 115200); + programmer = new OptibootBootloaderProgrammer(serialPortConfig, mcu); break; } default: { - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format("Unsupported model: {0}!", options.ArduinoModel)); break; } } + try { - TryToOpenSerialPort(); - ConfigureSerialPort(); - programmer.Open(); logger.Info("Establishing sync..."); @@ -108,11 +111,13 @@ public void UploadSketch(IEnumerable hexFileContents) programmer.ProgramDevice(ReadHexFile(hexFileContents, mcu.Flash.Size)); logger.Info("Device programmed."); - programmer.Close(); + logger.Info("Leaving programming mode..."); + programmer.LeaveProgrammingMode(); + logger.Info("Left programming mode!"); } finally { - CloseSerialPort(); + programmer.Close(); } logger.Info("All done, shutting down!"); } @@ -128,57 +133,11 @@ private static MemoryBlock ReadHexFile(IEnumerable hexFileContents, int } catch (Exception ex) { - UploaderLogger.LogAndThrowError(ex.Message); + UploaderLogger.LogErrorAndQuit(ex.Message); } return null; } - private void TryToOpenSerialPort() - { - logger.Trace("Opening serial port..."); - try - { - serialPort.Open(); - } - catch (UnauthorizedAccessException) - { - UploaderLogger.LogAndThrowError( - "Access to the port is denied. This or another process is currently using this port."); - } - catch (ArgumentOutOfRangeException) - { - UploaderLogger.LogAndThrowError( - "The configuration parameters for the port are invalid (e.g. baud rate, parity, databits)."); - } - catch (IOException) - { - UploaderLogger.LogAndThrowError("The port is in an invalid state."); - } - logger.Trace("Opened serial port {0} with baud rate {1}!", serialPort.PortName, serialPort.BaudRate); - } - - private void CloseSerialPort() - { - logger.Info("Closing serial port..."); - serialPort.DtrEnable = false; - serialPort.RtsEnable = false; - try - { - serialPort.Close(); - } - catch (Exception) - { - // Ignore - } - } - - private void ConfigureSerialPort() - { - logger.Trace("Setting Read/Write Timeout on serial port to '{0}'.", SerialPortTimeOut); - serialPort.ReadTimeout = SerialPortTimeOut; - serialPort.WriteTimeout = SerialPortTimeOut; - } - #endregion } } diff --git a/Source/ArduinoUploader/ArduinoUploader.csproj b/Source/ArduinoUploader/ArduinoUploader.csproj index aa68fc4..3cdbb3f 100644 --- a/Source/ArduinoUploader/ArduinoUploader.csproj +++ b/Source/ArduinoUploader/ArduinoUploader.csproj @@ -37,16 +37,22 @@ ..\packages\NLog.4.3.9\lib\net45\NLog.dll + + ..\packages\SerialPortStream.2.0.2\lib\net45\RJCP.SerialPortStream.dll + + + + @@ -55,6 +61,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -86,9 +112,6 @@ - - Component - diff --git a/Source/ArduinoUploader/BootloaderProgrammers/ArduinoBootloaderProgrammer.cs b/Source/ArduinoUploader/BootloaderProgrammers/ArduinoBootloaderProgrammer.cs index a8dd502..4ed6228 100644 --- a/Source/ArduinoUploader/BootloaderProgrammers/ArduinoBootloaderProgrammer.cs +++ b/Source/ArduinoUploader/BootloaderProgrammers/ArduinoBootloaderProgrammer.cs @@ -8,19 +8,21 @@ internal abstract class ArduinoBootloaderProgrammer : SerialPortBootloaderProgra protected abstract void Reset(); - protected ArduinoBootloaderProgrammer(UploaderSerialPort serialPort, IMCU mcu) - : base(serialPort, mcu) + protected ArduinoBootloaderProgrammer(SerialPortConfig serialPortConfig, IMCU mcu) + : base(serialPortConfig, mcu) { } public override void Open() { + base.Open(); Reset(); } public override void Close() { Reset(); + base.Close(); } } } diff --git a/Source/ArduinoUploader/BootloaderProgrammers/BootloaderProgrammer.cs b/Source/ArduinoUploader/BootloaderProgrammers/BootloaderProgrammer.cs index 0ec87c0..43c0785 100644 --- a/Source/ArduinoUploader/BootloaderProgrammers/BootloaderProgrammer.cs +++ b/Source/ArduinoUploader/BootloaderProgrammers/BootloaderProgrammer.cs @@ -1,5 +1,5 @@ -using System.IO; -using System.Linq; +using System.Linq; +using System.Threading; using ArduinoUploader.Hardware; using ArduinoUploader.Hardware.Memory; using IntelHexFormatReader.Model; @@ -25,8 +25,9 @@ protected BootloaderProgrammer(IMCU mcu) public abstract void InitializeDevice(); public abstract void EnableProgrammingMode(); public abstract void LeaveProgrammingMode(); + public abstract void LoadAddress(IMemory memory, int offset); public abstract void ExecuteWritePage(IMemory memory, int offset, byte[] bytes); - public abstract byte[] ExecuteReadPage(IMemory memory, int offset); + public abstract byte[] ExecuteReadPage(IMemory memory); public virtual void ProgramDevice(MemoryBlock memoryBlock) { @@ -52,20 +53,24 @@ public virtual void ProgramDevice(MemoryBlock memoryBlock) var bytesToCopy = memoryBlock.Cells.Skip(offset).Take(pageSize).Select(x => x.Value).ToArray(); logger.Trace("Checking if bytes at offset {0} need to be overwritten...", offset); - var bytesAlreadyPresent = ExecuteReadPage(flashMem, offset); + LoadAddress(flashMem, offset); + var bytesAlreadyPresent = ExecuteReadPage(flashMem); if (bytesAlreadyPresent.SequenceEqual(bytesToCopy)) { logger.Trace("Bytes to be written are identical to bytes already present - skipping actual write!"); continue; } logger.Trace("Writing page at offset {0}.", offset); + LoadAddress(flashMem, offset); ExecuteWritePage(flashMem, offset, bytesToCopy); - logger.Trace("Page written, now verifying..."); - var verify = ExecuteReadPage(flashMem, offset); + logger.Trace("Page written, now verifying..."); + Thread.Sleep(10); + LoadAddress(flashMem, offset); + var verify = ExecuteReadPage(flashMem); var succeeded = verify.SequenceEqual(bytesToCopy); if (!succeeded) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( "Difference encountered during verification, write failed!"); } else diff --git a/Source/ArduinoUploader/BootloaderProgrammers/ButterflyBootloaderProgrammer.cs b/Source/ArduinoUploader/BootloaderProgrammers/ButterflyBootloaderProgrammer.cs new file mode 100644 index 0000000..d9449f0 --- /dev/null +++ b/Source/ArduinoUploader/BootloaderProgrammers/ButterflyBootloaderProgrammer.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using ArduinoUploader.Hardware; +using ArduinoUploader.Hardware.Memory; +using ArduinoUploader.Protocols.AVR109; +using ArduinoUploader.Protocols.AVR109.Messages; +using NLog; +using RJCP.IO.Ports; + +namespace ArduinoUploader.BootloaderProgrammers +{ + internal class ButterflyBootloaderProgrammer : ArduinoBootloaderProgrammer + { + private static readonly Logger logger = LogManager.GetCurrentClassLogger(); + private const string EXPECTED_DEVICE_SIGNATURE = "1e-95-87"; + private const int VIRTUAL_COM_CREATION_TIMEOUT = 1000; + private string[] originalPorts; + + public ButterflyBootloaderProgrammer(SerialPortConfig serialPortConfig, IMCU mcu) + : base(serialPortConfig, mcu) + { + } + + protected override void Reset() + { + logger.Info("Issuing forced 1200bps reset..."); + var currentPortName = SerialPort.PortName; + originalPorts = SerialPortStream.GetPortNames(); + + SerialPort.Close(); + + SerialPort = new SerialPortStream(currentPortName, 1200); + SerialPort.Open(); + SerialPort.Close(); + Thread.Sleep(VIRTUAL_COM_CREATION_TIMEOUT); + } + + public override void Close() + { + try + { + logger.Info("Closing..."); + SerialPort.Close(); + logger.Info("Waiting for virtual port to disappear..."); + Thread.Sleep(VIRTUAL_COM_CREATION_TIMEOUT); + } + catch (Exception ex) + { + UploaderLogger.LogErrorAndQuit( + string.Format("Exception during close of the programmer: '{0}'.", + ex.Message)); + } + } + + public override void EstablishSync() + { + var ports = SerialPortStream.GetPortNames(); + var newPort = ports.Except(originalPorts).SingleOrDefault(); + + if (newPort == null) + UploaderLogger.LogErrorAndQuit( + string.Format( + "No (unambiguous) virtual COM port detected (after {0}ms).", + VIRTUAL_COM_CREATION_TIMEOUT)); + + SerialPort = new SerialPortStream + { + BaudRate = 57600, + PortName = newPort, + DataBits = 8, + Parity = Parity.None, + StopBits = StopBits.One, + Handshake = Handshake.DtrRts + }; + try + { + SerialPort.Open(); + } + catch (Exception ex) + { + UploaderLogger.LogErrorAndQuit( + string.Format("Unable to open serial port - {0}.", ex.Message)); + } + } + + public override void CheckDeviceSignature() + { + logger.Debug("Expecting to find '{0}'...", EXPECTED_DEVICE_SIGNATURE); + Send(new ReadSignatureBytesRequest()); + var response = Receive(3); + if (response == null) + UploaderLogger.LogErrorAndQuit( + "Unable to check device signature!"); + + var signature = response.Signature; + if (signature[0] != 0x1e || signature[1] != 0x95 || signature[2] != 0x87) + UploaderLogger.LogErrorAndQuit( + string.Format( + "Unexpected device signature - found '{0}'- expected '{1}'.", + BitConverter.ToString(signature), + EXPECTED_DEVICE_SIGNATURE)); + } + + public override void InitializeDevice() + { + Send(new ReturnSoftwareIdentifierRequest()); + var softIdResponse = Receive(7); + if (softIdResponse == null) + UploaderLogger.LogErrorAndQuit( + "Unable to retrieve software identifier!"); + + logger.Info("Software identifier: '{0}'", + Encoding.ASCII.GetString(softIdResponse.Bytes)); + + Send(new ReturnSoftwareVersionRequest()); + var softVersionResponse = Receive(2); + if (softVersionResponse == null) + UploaderLogger.LogErrorAndQuit( + "Unable to retrieve software version!"); + + logger.Info("Software Version: {0}.{1}", + softVersionResponse.MajorVersion, softVersionResponse.MinorVersion); + + Send(new ReturnProgrammerTypeRequest()); + var progTypeResponse = Receive(1); + if (progTypeResponse == null) + UploaderLogger.LogErrorAndQuit( + "Unable to retrieve programmer type!"); + + logger.Info("Programmer type: {0}.", progTypeResponse.ProgrammerType); + + Send(new CheckBlockSupportRequest()); + var checkBlockResponse = Receive(3); + if (checkBlockResponse == null) + UploaderLogger.LogErrorAndQuit("Unable to retrieve block support!"); + if (!checkBlockResponse.HasBlockSupport) + UploaderLogger.LogErrorAndQuit("Block support is not supported!"); + + logger.Info("Block support - buffer size {0} bytes.", checkBlockResponse.BufferSize); + + Send(new ReturnSupportedDeviceCodesRequest()); + var devices = new List(); + do + { + var nextByte = (byte) ReceiveNext(); + if (nextByte != Constants.NULL) devices.Add(nextByte); + else break; + } + while (true); + + var supportedDevices = string.Join("-", devices); + logger.Info("Supported devices: {0}.", supportedDevices); + + var devCode = MCU.DeviceCode; + if (!devices.Contains(devCode)) + UploaderLogger.LogErrorAndQuit( + string.Format("Device {0} not in supported list of devices: {1}!", + devCode, supportedDevices)); + + logger.Info("Selecting device type '{0}'...", devCode); + Send(new SelectDeviceTypeRequest(devCode)); + var response = ReceiveNext(); + if (response != Constants.CARRIAGE_RETURN) + UploaderLogger.LogErrorAndQuit("Unable to execute select device type command!"); + } + + public override void EnableProgrammingMode() + { + Send(new EnterProgrammingModeRequest()); + var response = ReceiveNext(); + if (response != Constants.CARRIAGE_RETURN) + UploaderLogger.LogErrorAndQuit("Unable to enter programming mode!"); + } + + public override void LoadAddress(IMemory memory, int offset) + { + logger.Trace("Sending load address request: {0}.", offset); + Send(new SetAddressRequest(offset / 2)); + var response = ReceiveNext(); + if (response != Constants.CARRIAGE_RETURN) + UploaderLogger.LogErrorAndQuit("Unable to execute set address request!"); + } + + public override byte[] ExecuteReadPage(IMemory memory) + { + var type = memory.Type; + var blockSize = memory.PageSize; + Send(new StartBlockReadRequest(type, blockSize)); + var response = Receive(blockSize); + return response.Bytes; + } + + public override void ExecuteWritePage(IMemory memory, int offset, byte[] bytes) + { + var type = memory.Type; + var blockSize = memory.PageSize; + Send(new StartBlockLoadRequest(type, blockSize, bytes)); + var response = ReceiveNext(); + if (response != Constants.CARRIAGE_RETURN) + UploaderLogger.LogErrorAndQuit("Unable to execute write page!"); + } + + public override void LeaveProgrammingMode() + { + Send(new LeaveProgrammingModeRequest()); + var leaveProgModeResp = ReceiveNext(); + if (leaveProgModeResp != Constants.CARRIAGE_RETURN) + UploaderLogger.LogErrorAndQuit("Unable to leave programming mode!"); + + Send(new ExitBootLoaderRequest()); + var exitBootloaderResp = ReceiveNext(); + if (exitBootloaderResp != Constants.CARRIAGE_RETURN) + UploaderLogger.LogErrorAndQuit("Unable to exit boot loader!"); + } + } +} diff --git a/Source/ArduinoUploader/BootloaderProgrammers/IBootloaderProgrammer.cs b/Source/ArduinoUploader/BootloaderProgrammers/IBootloaderProgrammer.cs index d531034..20d11cb 100644 --- a/Source/ArduinoUploader/BootloaderProgrammers/IBootloaderProgrammer.cs +++ b/Source/ArduinoUploader/BootloaderProgrammers/IBootloaderProgrammer.cs @@ -13,7 +13,8 @@ internal interface IBootloaderProgrammer void EnableProgrammingMode(); void LeaveProgrammingMode(); void ProgramDevice(MemoryBlock memoryBlock); + void LoadAddress(IMemory memory, int offset); void ExecuteWritePage(IMemory memory, int offset, byte[] bytes); - byte[] ExecuteReadPage(IMemory memory, int offset); + byte[] ExecuteReadPage(IMemory memory); } } diff --git a/Source/ArduinoUploader/BootloaderProgrammers/OptibootBootloaderProgrammer.cs b/Source/ArduinoUploader/BootloaderProgrammers/OptibootBootloaderProgrammer.cs index dd75ddd..61dadb6 100644 --- a/Source/ArduinoUploader/BootloaderProgrammers/OptibootBootloaderProgrammer.cs +++ b/Source/ArduinoUploader/BootloaderProgrammers/OptibootBootloaderProgrammer.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using ArduinoUploader.Hardware; using ArduinoUploader.Hardware.Memory; using ArduinoUploader.Protocols; @@ -14,8 +13,8 @@ internal class OptibootBootloaderProgrammer : ArduinoBootloaderProgrammer private static readonly Logger logger = LogManager.GetCurrentClassLogger(); private const string EXPECTED_DEVICE_SIGNATURE = "1e-95-0f"; - internal OptibootBootloaderProgrammer(UploaderSerialPort serialPort, IMCU mcu) - : base(serialPort, mcu) + internal OptibootBootloaderProgrammer(SerialPortConfig serialPortConfig, IMCU mcu) + : base(serialPortConfig, mcu) { } @@ -36,27 +35,17 @@ public override void EstablishSync() } if (i == MaxSyncRetries) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format( "Unable to establish sync after {0} retries.", MaxSyncRetries)); var nextByte = ReceiveNext(); if (nextByte != Constants.RESP_STK_OK) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( "Unable to establish sync."); } - protected TResponse Receive(int length = 1) where TResponse : Response - { - var bytes = ReceiveNext(length); - if (bytes == null) return null; - - var result = (TResponse) Activator.CreateInstance(typeof(TResponse)); - result.Bytes = bytes; - return result; - } - protected void SendWithSyncRetry(IRequest request) { byte nextByte; @@ -72,7 +61,7 @@ protected void SendWithSyncRetry(IRequest request) break; } if (nextByte != Constants.RESP_STK_INSYNC) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format( "Unable to aqcuire sync in SendWithSyncRetry for request of type {0}!", request.GetType())); @@ -84,12 +73,12 @@ public override void CheckDeviceSignature() SendWithSyncRetry(new ReadSignatureRequest()); var response = Receive(4); if (response == null || !response.IsCorrectResponse) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( "Unable to check device signature!"); var signature = response.Signature; if (signature[0] != 0x1e || signature[1] != 0x95 || signature[2] != 0x0f) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format( "Unexpected device signature - found '{0}'- expected '{1}'.", BitConverter.ToString(signature), @@ -108,7 +97,7 @@ public override void InitializeDevice() var nextByte = ReceiveNext(); if (nextByte != Constants.RESP_STK_OK) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( "Unable to set device programming parameters!"); } @@ -118,7 +107,7 @@ public override void EnableProgrammingMode() var nextByte = ReceiveNext(); if (nextByte == Constants.RESP_STK_OK) return; if (nextByte == Constants.RESP_STK_NODEVICE || nextByte == Constants.RESP_STK_Failed) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( "Unable to enable programming mode on the device!"); } @@ -128,7 +117,7 @@ public override void LeaveProgrammingMode() var nextByte = ReceiveNext(); if (nextByte == Constants.RESP_STK_OK) return; if (nextByte == Constants.RESP_STK_NODEVICE || nextByte == Constants.RESP_STK_Failed) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( "Unable to leave programming mode on the device!"); } @@ -141,11 +130,11 @@ private uint GetParameterValue(byte param) nextByte = ReceiveNext(); if (nextByte == Constants.RESP_STK_Failed) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format("Retrieving parameter '{0}' failed!", param)); if (nextByte != Constants.RESP_STK_OK) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format( "General protocol error while retrieving parameter '{0}'.", param)); @@ -155,41 +144,35 @@ private uint GetParameterValue(byte param) public override void ExecuteWritePage(IMemory memory, int offset, byte[] bytes) { - LoadAddress(offset); SendWithSyncRetry(new ExecuteProgramPageRequest(memory, bytes)); var nextByte = ReceiveNext(); if (nextByte == Constants.RESP_STK_OK) return; - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format("Write at offset {0} failed!", offset)); } - public override byte[] ExecuteReadPage(IMemory memory, int offset) + public override byte[] ExecuteReadPage(IMemory memory) { var pageSize = memory.PageSize; - LoadAddress(offset); SendWithSyncRetry(new ExecuteReadPageRequest(memory.Type, pageSize)); var bytes = ReceiveNext(pageSize); if (bytes == null) - { - UploaderLogger.LogAndThrowError( - string.Format("Read at offset {0} failed!", offset)); - } + UploaderLogger.LogErrorAndQuit("Execute read page failed!"); var nextByte = ReceiveNext(); if (nextByte == Constants.RESP_STK_OK) return bytes; - UploaderLogger.LogAndThrowError( - string.Format("Read at offset {0} failed!", offset)); + UploaderLogger.LogErrorAndQuit("Execute read page failed!"); return null; } - private void LoadAddress(int addr) + public override void LoadAddress(IMemory memory, int addr) { logger.Trace("Sending load address request: {0}.", addr); addr = addr >> 1; SendWithSyncRetry(new LoadAddressRequest(addr)); var result = ReceiveNext(); if (result == Constants.RESP_STK_OK) return; - UploaderLogger.LogAndThrowError(string.Format("LoadAddress failed with result {0}!", result)); + UploaderLogger.LogErrorAndQuit(string.Format("LoadAddress failed with result {0}!", result)); } } } diff --git a/Source/ArduinoUploader/BootloaderProgrammers/SerialPortBootloaderProgrammer.cs b/Source/ArduinoUploader/BootloaderProgrammers/SerialPortBootloaderProgrammer.cs index 34678a3..cc5a645 100644 --- a/Source/ArduinoUploader/BootloaderProgrammers/SerialPortBootloaderProgrammer.cs +++ b/Source/ArduinoUploader/BootloaderProgrammers/SerialPortBootloaderProgrammer.cs @@ -3,6 +3,7 @@ using ArduinoUploader.Hardware; using ArduinoUploader.Protocols; using NLog; +using RJCP.IO.Ports; namespace ArduinoUploader.BootloaderProgrammers { @@ -10,12 +11,56 @@ internal abstract class SerialPortBootloaderProgrammer : BootloaderProgrammer { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - protected UploaderSerialPort SerialPort { get; private set; } + protected SerialPortConfig serialPortConfig; + protected SerialPortStream SerialPort { get; set; } - protected SerialPortBootloaderProgrammer(UploaderSerialPort serialPort, IMCU mcu) + protected SerialPortBootloaderProgrammer(SerialPortConfig serialPortConfig, IMCU mcu) : base(mcu) { - SerialPort = serialPort; + this.serialPortConfig = serialPortConfig; + } + + public override void Open() + { + var portName = serialPortConfig.PortName; + var baudRate = serialPortConfig.BaudRate; + logger.Info("Opening serial port {0} - baudrate {1}", serialPortConfig.PortName, serialPortConfig.BaudRate); + + SerialPort = new SerialPortStream(portName, baudRate) + { + ReadTimeout = serialPortConfig.ReadTimeOut, + WriteTimeout = serialPortConfig.WriteTimeOut + }; + try + { + SerialPort.Open(); + } + catch (ObjectDisposedException ex) + { + UploaderLogger.LogErrorAndQuit( + string.Format("Unable to open serial port {0} - {1}.", portName, ex.Message)); + } + catch (InvalidOperationException ex) + { + UploaderLogger.LogErrorAndQuit( + string.Format("Unable to open serial port {0} - {1}.", portName, ex.Message)); + } + logger.Trace("Opened serial port {0} with baud rate {1}!", portName, baudRate); + } + + public override void Close() + { + logger.Info("Closing serial port..."); + SerialPort.DtrEnable = false; + SerialPort.RtsEnable = false; + try + { + SerialPort.Close(); + } + catch (Exception) + { + // Ignore + } } protected void ToggleDtrRts(int wait1, int wait2, bool invert = false) @@ -43,6 +88,13 @@ protected virtual void Send(IRequest request) SerialPort.Write(bytes, 0, length); } + protected TResponse Receive(int length = 1) where TResponse : Response, new() + { + var bytes = ReceiveNext(length); + if (bytes == null) return null; + return new TResponse { Bytes = bytes }; + } + protected int ReceiveNext() { var bytes = new byte[1]; diff --git a/Source/ArduinoUploader/BootloaderProgrammers/SerialPortConfig.cs b/Source/ArduinoUploader/BootloaderProgrammers/SerialPortConfig.cs new file mode 100644 index 0000000..1c4e4ff --- /dev/null +++ b/Source/ArduinoUploader/BootloaderProgrammers/SerialPortConfig.cs @@ -0,0 +1,21 @@ +namespace ArduinoUploader.BootloaderProgrammers +{ + internal class SerialPortConfig + { + public string PortName { get; set; } + public int BaudRate { get; set; } + public int ReadTimeOut { get; set; } + public int WriteTimeOut { get; set; } + + private const int DefaultTimeout = 1000; + + public SerialPortConfig(string portName, int baudRate, + int readTimeout = DefaultTimeout, int writeTimeout = DefaultTimeout) + { + PortName = portName; + BaudRate = baudRate; + ReadTimeOut = readTimeout; + WriteTimeOut = writeTimeout; + } + } +} diff --git a/Source/ArduinoUploader/BootloaderProgrammers/WiringBootloaderProgrammer.cs b/Source/ArduinoUploader/BootloaderProgrammers/WiringBootloaderProgrammer.cs index 8542e04..716e84d 100644 --- a/Source/ArduinoUploader/BootloaderProgrammers/WiringBootloaderProgrammer.cs +++ b/Source/ArduinoUploader/BootloaderProgrammers/WiringBootloaderProgrammer.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using ArduinoUploader.Hardware; using ArduinoUploader.Hardware.Memory; using ArduinoUploader.Protocols; @@ -45,8 +44,8 @@ protected static byte SequenceNumber } } - public WiringBootloaderProgrammer(UploaderSerialPort serialPort, IMCU mcu) - : base(serialPort, mcu) + public WiringBootloaderProgrammer(SerialPortConfig serialPortConfig, IMCU mcu) + : base(serialPortConfig, mcu) { } @@ -190,7 +189,7 @@ public override void EstablishSync() } if (i == MaxSyncRetries) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format( "Unable to establish sync after {0} retries.", MaxSyncRetries)); } @@ -200,7 +199,7 @@ public override void CheckDeviceSignature() logger.Debug("Expecting to find '{0}'...", EXPECTED_DEVICE_SIGNATURE); if (!deviceSignature.Equals(EXPECTED_DEVICE_SIGNATURE)) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format("Unexpected device signature - found '{0}'- expected '{1}'.", deviceSignature, EXPECTED_DEVICE_SIGNATURE)); } @@ -220,7 +219,7 @@ public override void EnableProgrammingMode() Send(new EnableProgrammingModeRequest(MCU)); var response = Receive(); if (response == null) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( "Unable to enable programming mode on the device!"); } @@ -229,13 +228,12 @@ public override void LeaveProgrammingMode() Send(new LeaveProgrammingModeRequest()); var response = Receive(); if (response == null) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( "Unable to leave programming mode on the device!"); } public override void ExecuteWritePage(IMemory memory, int offset, byte[] bytes) { - LoadAddress(memory, offset); logger.Trace( "Sending execute write page request for offset {0} ({1} bytes)...", offset, bytes.Length); @@ -247,40 +245,34 @@ public override void ExecuteWritePage(IMemory memory, int offset, byte[] bytes) if (response == null || response.AnswerID != writeCmd || response.Status != Constants.STATUS_CMD_OK) { - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format( "Executing write page request at offset {0} failed!", offset)); } } - public override byte[] ExecuteReadPage(IMemory memory, int offset) + public override byte[] ExecuteReadPage(IMemory memory) { - LoadAddress(memory, offset); - logger.Trace("Sending execute read page request (offset {0})...", offset); var readCmd = readCommands[memory.Type]; Send(new ExecuteReadPageRequest(readCmd, memory)); var response = Receive(); - if (response == null || response.AnswerID != readCmd - || response.Status != Constants.STATUS_CMD_OK) - { - UploaderLogger.LogAndThrowError( - string.Format( - "Executing read page request at offset {0} failed!", offset)); - } + if (response == null || response.AnswerID != readCmd || response.Status != Constants.STATUS_CMD_OK) + UploaderLogger.LogErrorAndQuit("Executing read page request failed!"); + var responseBytes = new byte[memory.PageSize]; Buffer.BlockCopy(response.Bytes, 2, responseBytes, 0, responseBytes.Length); return responseBytes; } - private void LoadAddress(IMemory memory, int addr) + public override void LoadAddress(IMemory memory, int offset) { - logger.Trace("Sending load address request: {0}.", addr); - addr = addr >> 1; - Send(new LoadAddressRequest(memory, addr)); + logger.Trace("Sending load address request: {0}.", offset); + offset = offset >> 1; + Send(new LoadAddressRequest(memory, offset)); var response = Receive(); if (response == null || !response.Succeeded) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( "Unable to execute load address!"); } @@ -290,7 +282,7 @@ private uint GetParameterValue(byte param) Send(new GetParameterRequest(param)); var response = Receive(); if (response == null || !response.IsSuccess) - UploaderLogger.LogAndThrowError( + UploaderLogger.LogErrorAndQuit( string.Format("Retrieving parameter '{0}' failed!", param)); return response.ParameterValue; } diff --git a/Source/ArduinoUploader/Hardware/ATMega32U4.cs b/Source/ArduinoUploader/Hardware/ATMega32U4.cs new file mode 100644 index 0000000..6158a88 --- /dev/null +++ b/Source/ArduinoUploader/Hardware/ATMega32U4.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using ArduinoUploader.Hardware.Memory; + +namespace ArduinoUploader.Hardware +{ + internal class ATMega32U4 : MCU + { + public override byte DeviceCode { get { return 0x44; } } + public override byte DeviceRevision { get { return 0; } } + public override byte ProgType { get { return 0; } } + public override byte ParallelMode { get { return 0; } } + public override byte Polling { get { return 0; } } + public override byte SelfTimed { get { return 0; } } + public override byte LockBytes { get { return 0; } } + public override byte FuseBytes { get { return 0; } } + + public override byte Timeout { get { return 200; } } + public override byte StabDelay { get { return 100; } } + public override byte CmdExeDelay { get { return 25; } } + public override byte SynchLoops { get { return 32; } } + public override byte ByteDelay { get { return 0; } } + public override byte PollIndex { get { return 3; } } + public override byte PollValue { get { return 0x53; } } + + public override IDictionary CommandBytes + { + get { return new Dictionary(); } + } + + public override IList Memory + { + get + { + return new List() + { + new FlashMemory() + { + Size = 32 * 1024, + PageSize = 128, + PollVal1 = 0xff, + PollVal2 = 0xff + }, + new EEPROMMemory() + { + Size = 1024, + PollVal1 = 0xff, + PollVal2 = 0xff + } + }; + } + } + } +} diff --git a/Source/ArduinoUploader/Hardware/ArduinoModel.cs b/Source/ArduinoUploader/Hardware/ArduinoModel.cs index 5707d5e..3f391d2 100644 --- a/Source/ArduinoUploader/Hardware/ArduinoModel.cs +++ b/Source/ArduinoUploader/Hardware/ArduinoModel.cs @@ -3,6 +3,7 @@ public enum ArduinoModel { Mega2560, + Micro, NanoR3, UnoR3 } diff --git a/Source/ArduinoUploader/Hardware/MCU.cs b/Source/ArduinoUploader/Hardware/MCU.cs index 71f0871..0f35fa9 100644 --- a/Source/ArduinoUploader/Hardware/MCU.cs +++ b/Source/ArduinoUploader/Hardware/MCU.cs @@ -6,6 +6,8 @@ namespace ArduinoUploader.Hardware { internal abstract class MCU : IMCU { + // TODO: move properties (both on interface and implementation to correct corresponding places) + // At the moment this is just one giant mixin class. public abstract byte DeviceCode { get; } public abstract byte DeviceRevision { get; } public abstract byte LockBytes { get; } diff --git a/Source/ArduinoUploader/Protocols/AVR109/Constants.cs b/Source/ArduinoUploader/Protocols/AVR109/Constants.cs new file mode 100644 index 0000000..35c56ca --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Constants.cs @@ -0,0 +1,22 @@ +namespace ArduinoUploader.Protocols.AVR109 +{ + internal static class Constants + { + internal const byte NULL = 0x00; + internal const byte CARRIAGE_RETURN = 0x0d; + + internal const byte CMD_SET_ADDRESS = 0x41; + internal const byte CMD_START_BLOCK_LOAD = 0x42; + internal const byte CMD_EXIT_BOOTLOADER = 0x45; + internal const byte CMD_LEAVE_PROGRAMMING_MODE = 0x4c; + internal const byte CMD_ENTER_PROGRAMMING_MODE = 0x50; + internal const byte CMD_RETURN_SOFTWARE_IDENTIFIER = 0x53; + internal const byte CMD_SELECT_DEVICE_TYPE = 0x54; + internal const byte CMD_RETURN_SOFTWARE_VERSION = 0x56; + internal const byte CMD_CHECK_BLOCK_SUPPORT = 0x62; + internal const byte CMD_START_BLOCK_READ = 0x67; + internal const byte CMD_RETURN_PROGRAMMER_TYPE = 0x70; + internal const byte CMD_READ_SIGNATURE_BYTES = 0x73; + internal const byte CMD_RETURN_SUPPORTED_DEVICE_CODES = 0x74; + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/CheckBlockSupportRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/CheckBlockSupportRequest.cs new file mode 100644 index 0000000..8c9fd6b --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/CheckBlockSupportRequest.cs @@ -0,0 +1,13 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class CheckBlockSupportRequest : Request + { + internal CheckBlockSupportRequest() + { + Bytes = new[] + { + Constants.CMD_CHECK_BLOCK_SUPPORT + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/CheckBlockSupportResponse.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/CheckBlockSupportResponse.cs new file mode 100644 index 0000000..65bd019 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/CheckBlockSupportResponse.cs @@ -0,0 +1,8 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class CheckBlockSupportResponse : Response + { + internal bool HasBlockSupport { get { return Bytes[0] == (byte) 'Y'; } } + internal int BufferSize { get { return (Bytes[1] << 8) + Bytes[2]; } } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/EnterProgrammingModeRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/EnterProgrammingModeRequest.cs new file mode 100644 index 0000000..d9ce1b6 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/EnterProgrammingModeRequest.cs @@ -0,0 +1,13 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class EnterProgrammingModeRequest : Request + { + internal EnterProgrammingModeRequest() + { + Bytes = new[] + { + Constants.CMD_ENTER_PROGRAMMING_MODE + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/ExitBootLoaderRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/ExitBootLoaderRequest.cs new file mode 100644 index 0000000..7b89648 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/ExitBootLoaderRequest.cs @@ -0,0 +1,13 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class ExitBootLoaderRequest : Request + { + internal ExitBootLoaderRequest() + { + Bytes = new[] + { + Constants.CMD_EXIT_BOOTLOADER + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/LeaveProgrammingModeRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/LeaveProgrammingModeRequest.cs new file mode 100644 index 0000000..82f5603 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/LeaveProgrammingModeRequest.cs @@ -0,0 +1,13 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class LeaveProgrammingModeRequest : Request + { + internal LeaveProgrammingModeRequest() + { + Bytes = new[] + { + Constants.CMD_LEAVE_PROGRAMMING_MODE + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/ReadSignatureBytesRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReadSignatureBytesRequest.cs new file mode 100644 index 0000000..e4157fb --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReadSignatureBytesRequest.cs @@ -0,0 +1,13 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class ReadSignatureBytesRequest : Request + { + internal ReadSignatureBytesRequest() + { + Bytes = new[] + { + Constants.CMD_READ_SIGNATURE_BYTES + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/ReadSignatureBytesResponse.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReadSignatureBytesResponse.cs new file mode 100644 index 0000000..ffb6dd0 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReadSignatureBytesResponse.cs @@ -0,0 +1,10 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class ReadSignatureBytesResponse : Response + { + internal byte[] Signature + { + get { return new[] { Bytes[2], Bytes[1], Bytes[0] }; } + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnProgrammerTypeRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnProgrammerTypeRequest.cs new file mode 100644 index 0000000..e73b612 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnProgrammerTypeRequest.cs @@ -0,0 +1,13 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class ReturnProgrammerTypeRequest : Request + { + internal ReturnProgrammerTypeRequest() + { + Bytes = new[] + { + Constants.CMD_RETURN_PROGRAMMER_TYPE + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnProgrammerTypeResponse.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnProgrammerTypeResponse.cs new file mode 100644 index 0000000..5ca3b6e --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnProgrammerTypeResponse.cs @@ -0,0 +1,7 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class ReturnProgrammerTypeResponse : Response + { + internal char ProgrammerType { get { return (char) Bytes[0]; } } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareIdentifierRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareIdentifierRequest.cs new file mode 100644 index 0000000..39f5fa7 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareIdentifierRequest.cs @@ -0,0 +1,13 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class ReturnSoftwareIdentifierRequest : Request + { + internal ReturnSoftwareIdentifierRequest() + { + Bytes = new[] + { + Constants.CMD_RETURN_SOFTWARE_IDENTIFIER + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareIdentifierResponse.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareIdentifierResponse.cs new file mode 100644 index 0000000..bda55e1 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareIdentifierResponse.cs @@ -0,0 +1,6 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class ReturnSoftwareIdentifierResponse : Response + { + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareVersionRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareVersionRequest.cs new file mode 100644 index 0000000..adb444c --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareVersionRequest.cs @@ -0,0 +1,13 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class ReturnSoftwareVersionRequest : Request + { + internal ReturnSoftwareVersionRequest() + { + Bytes = new[] + { + Constants.CMD_RETURN_SOFTWARE_VERSION + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareVersionResponse.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareVersionResponse.cs new file mode 100644 index 0000000..efaff59 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSoftwareVersionResponse.cs @@ -0,0 +1,8 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class ReturnSoftwareVersionResponse : Response + { + internal char MajorVersion { get { return (char) Bytes[0]; } } + internal char MinorVersion { get { return (char) Bytes[1]; } } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSupportedDeviceCodesRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSupportedDeviceCodesRequest.cs new file mode 100644 index 0000000..5e717b3 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/ReturnSupportedDeviceCodesRequest.cs @@ -0,0 +1,13 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class ReturnSupportedDeviceCodesRequest : Request + { + internal ReturnSupportedDeviceCodesRequest() + { + Bytes = new[] + { + Constants.CMD_RETURN_SUPPORTED_DEVICE_CODES + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/SelectDeviceTypeRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/SelectDeviceTypeRequest.cs new file mode 100644 index 0000000..2d36df3 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/SelectDeviceTypeRequest.cs @@ -0,0 +1,14 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class SelectDeviceTypeRequest : Request + { + internal SelectDeviceTypeRequest(byte deviceCode) + { + Bytes = new[] + { + Constants.CMD_SELECT_DEVICE_TYPE, + deviceCode + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/SetAddressRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/SetAddressRequest.cs new file mode 100644 index 0000000..a134386 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/SetAddressRequest.cs @@ -0,0 +1,15 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class SetAddressRequest : Request + { + internal SetAddressRequest(int offset) + { + Bytes = new[] + { + Constants.CMD_SET_ADDRESS, + (byte) ((offset >> 8) & 0xff), + (byte) (offset & 0xff) + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockLoadRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockLoadRequest.cs new file mode 100644 index 0000000..0f8f942 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockLoadRequest.cs @@ -0,0 +1,18 @@ +using System; +using ArduinoUploader.Hardware.Memory; + +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class StartBlockLoadRequest : Request + { + internal StartBlockLoadRequest(MemoryType memType, int blockSize, byte[] bytes) + { + Bytes = new byte[blockSize + 4]; + Bytes[0] = Constants.CMD_START_BLOCK_LOAD; + Bytes[1] = (byte) (blockSize >> 8); + Bytes[2] = (byte) (blockSize & 0xff); + Bytes[3] = (byte) (memType == MemoryType.FLASH ? 'F' : 'E'); + Buffer.BlockCopy(bytes, 0, Bytes, 4, blockSize); + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockReadRequest.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockReadRequest.cs new file mode 100644 index 0000000..210f368 --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockReadRequest.cs @@ -0,0 +1,18 @@ +using ArduinoUploader.Hardware.Memory; + +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class StartBlockReadRequest : Request + { + internal StartBlockReadRequest(MemoryType memType, int blockSize) + { + Bytes = new[] + { + Constants.CMD_START_BLOCK_READ, + (byte)(blockSize >> 8), + (byte)(blockSize & 0xff), + (byte)(memType == MemoryType.FLASH ? 'F' : 'E') + }; + } + } +} diff --git a/Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockReadResponse.cs b/Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockReadResponse.cs new file mode 100644 index 0000000..32ccd7e --- /dev/null +++ b/Source/ArduinoUploader/Protocols/AVR109/Messages/StartBlockReadResponse.cs @@ -0,0 +1,6 @@ +namespace ArduinoUploader.Protocols.AVR109.Messages +{ + internal class StartBlockReadResponse : Response + { + } +} diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/EnableProgrammingModeRequest.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/EnableProgrammingModeRequest.cs index df464a2..5e3adb9 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/EnableProgrammingModeRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/EnableProgrammingModeRequest.cs @@ -2,7 +2,7 @@ { internal class EnableProgrammingModeRequest : Request { - public EnableProgrammingModeRequest() + internal EnableProgrammingModeRequest() { Bytes = new[] { diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/ExecuteProgramPageRequest.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/ExecuteProgramPageRequest.cs index 5d451af..4d01354 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/ExecuteProgramPageRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/ExecuteProgramPageRequest.cs @@ -5,7 +5,7 @@ namespace ArduinoUploader.Protocols.STK500v1.Messages { internal class ExecuteProgramPageRequest : Request { - public ExecuteProgramPageRequest(IMemory memory, byte[] bytesToCopy) + internal ExecuteProgramPageRequest(IMemory memory, byte[] bytesToCopy) { var size = bytesToCopy.Length; Bytes = new byte[size + 5]; diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/ExecuteReadPageRequest.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/ExecuteReadPageRequest.cs index 53b0263..7a6ca59 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/ExecuteReadPageRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/ExecuteReadPageRequest.cs @@ -4,7 +4,7 @@ namespace ArduinoUploader.Protocols.STK500v1.Messages { internal class ExecuteReadPageRequest : Request { - public ExecuteReadPageRequest(MemoryType memType, int pageSize) + internal ExecuteReadPageRequest(MemoryType memType, int pageSize) { Bytes = new byte[5]; Bytes[0] = Constants.CMD_STK_READ_PAGE; diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetParameterRequest.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetParameterRequest.cs index a573688..28a047a 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetParameterRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetParameterRequest.cs @@ -2,7 +2,7 @@ { internal class GetParameterRequest : Request { - public GetParameterRequest(byte param) + internal GetParameterRequest(byte param) { Bytes = new[] { diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetSyncRequest.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetSyncRequest.cs index 9e662b8..57c9c78 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetSyncRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetSyncRequest.cs @@ -2,7 +2,7 @@ { internal class GetSyncRequest : Request { - public GetSyncRequest() + internal GetSyncRequest() { Bytes = new[] { diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetSyncResponse.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetSyncResponse.cs index 140602f..13d7c10 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetSyncResponse.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/GetSyncResponse.cs @@ -2,7 +2,7 @@ { internal class GetSyncResponse : Response { - public bool IsInSync + internal bool IsInSync { get { return Bytes.Length > 0 && Bytes[0] == Constants.RESP_STK_INSYNC; } } diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/LeaveProgrammingModeRequest.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/LeaveProgrammingModeRequest.cs index c0678c1..a11689b 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/LeaveProgrammingModeRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/LeaveProgrammingModeRequest.cs @@ -2,7 +2,7 @@ { internal class LeaveProgrammingModeRequest : Request { - public LeaveProgrammingModeRequest() + internal LeaveProgrammingModeRequest() { Bytes = new[] { diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/LoadAddressRequest.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/LoadAddressRequest.cs index 80dfbd8..6717d80 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/LoadAddressRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/LoadAddressRequest.cs @@ -2,7 +2,7 @@ { internal class LoadAddressRequest : Request { - public LoadAddressRequest(int address) + internal LoadAddressRequest(int address) { Bytes = new[] { diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/ReadSignatureRequest.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/ReadSignatureRequest.cs index 394b2ba..f4926a9 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/ReadSignatureRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/ReadSignatureRequest.cs @@ -2,7 +2,7 @@ { internal class ReadSignatureRequest : Request { - public ReadSignatureRequest() + internal ReadSignatureRequest() { Bytes = new[] { diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/ReadSignatureResponse.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/ReadSignatureResponse.cs index b58d976..125c60e 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/ReadSignatureResponse.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/ReadSignatureResponse.cs @@ -2,12 +2,12 @@ { internal class ReadSignatureResponse : Response { - public bool IsCorrectResponse + internal bool IsCorrectResponse { get { return Bytes.Length == 4 && Bytes[3] == Constants.RESP_STK_OK; } } - public byte[] Signature + internal byte[] Signature { get { return new[] { Bytes[0], Bytes[1], Bytes[2] }; } } diff --git a/Source/ArduinoUploader/Protocols/STK500v1/Messages/SetDeviceProgrammingParametersRequest.cs b/Source/ArduinoUploader/Protocols/STK500v1/Messages/SetDeviceProgrammingParametersRequest.cs index 71eb68c..ce593f4 100644 --- a/Source/ArduinoUploader/Protocols/STK500v1/Messages/SetDeviceProgrammingParametersRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v1/Messages/SetDeviceProgrammingParametersRequest.cs @@ -4,7 +4,7 @@ namespace ArduinoUploader.Protocols.STK500v1.Messages { internal class SetDeviceProgrammingParametersRequest : Request { - public SetDeviceProgrammingParametersRequest(IMCU mcu) + internal SetDeviceProgrammingParametersRequest(IMCU mcu) { var flashMem = mcu.Flash; var eepromMem = mcu.EEPROM; diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/EnableProgrammingModeRequest.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/EnableProgrammingModeRequest.cs index 1796fa9..5ff7bd0 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/EnableProgrammingModeRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/EnableProgrammingModeRequest.cs @@ -4,7 +4,7 @@ namespace ArduinoUploader.Protocols.STK500v2.Messages { internal class EnableProgrammingModeRequest : Request { - public EnableProgrammingModeRequest(IMCU mcu) + internal EnableProgrammingModeRequest(IMCU mcu) { var cmdBytes = mcu.CommandBytes[Command.PGM_ENABLE]; Bytes = new[] diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteProgramPageRequest.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteProgramPageRequest.cs index a3064c6..92a108c 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteProgramPageRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteProgramPageRequest.cs @@ -6,7 +6,7 @@ namespace ArduinoUploader.Protocols.STK500v2.Messages { internal class ExecuteProgramPageRequest : Request { - public ExecuteProgramPageRequest(byte writeCmd, IMemory memory, IReadOnlyCollection data) + internal ExecuteProgramPageRequest(byte writeCmd, IMemory memory, IReadOnlyCollection data) { var len = data.Count; const byte mode = 0xc1; diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteProgramPageResponse.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteProgramPageResponse.cs index 12238ac..cb57ea3 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteProgramPageResponse.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteProgramPageResponse.cs @@ -2,7 +2,7 @@ { internal class ExecuteProgramPageResponse : Response { - public byte AnswerID { get { return Bytes[0]; } } - public byte Status { get { return Bytes[1]; } } + internal byte AnswerID { get { return Bytes[0]; } } + internal byte Status { get { return Bytes[1]; } } } } diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteReadPageRequest.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteReadPageRequest.cs index 9892c4e..53ca871 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteReadPageRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteReadPageRequest.cs @@ -4,7 +4,7 @@ namespace ArduinoUploader.Protocols.STK500v2.Messages { internal class ExecuteReadPageRequest : Request { - public ExecuteReadPageRequest(byte readCmd, IMemory memory) + internal ExecuteReadPageRequest(byte readCmd, IMemory memory) { var pageSize = memory.PageSize; var cmdByte = memory.CmdBytesRead[0]; diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteReadPageResponse.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteReadPageResponse.cs index 77099f7..c4b660b 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteReadPageResponse.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/ExecuteReadPageResponse.cs @@ -2,7 +2,7 @@ { internal class ExecuteReadPageResponse : Response { - public byte AnswerID { get { return Bytes[0]; } } - public byte Status { get { return Bytes[1]; } } + internal byte AnswerID { get { return Bytes[0]; } } + internal byte Status { get { return Bytes[1]; } } } } diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetParameterRequest.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetParameterRequest.cs index e336525..2b9bfc3 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetParameterRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetParameterRequest.cs @@ -2,7 +2,7 @@ { internal class GetParameterRequest : Request { - public GetParameterRequest(byte param) + internal GetParameterRequest(byte param) { Bytes = new[] { diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetParameterResponse.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetParameterResponse.cs index 4f988c8..69153c9 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetParameterResponse.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetParameterResponse.cs @@ -2,7 +2,7 @@ { internal class GetParameterResponse : Response { - public bool IsSuccess + internal bool IsSuccess { get { @@ -11,7 +11,7 @@ public bool IsSuccess } } - public byte ParameterValue + internal byte ParameterValue { get { return Bytes[2]; } } diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetSyncRequest.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetSyncRequest.cs index 3e2d840..cb8be08 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetSyncRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetSyncRequest.cs @@ -2,7 +2,7 @@ { internal class GetSyncRequest : Request { - public GetSyncRequest() + internal GetSyncRequest() { Bytes = new[] { diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetSyncResponse.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetSyncResponse.cs index ce9b509..1e5a667 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetSyncResponse.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/GetSyncResponse.cs @@ -5,7 +5,7 @@ namespace ArduinoUploader.Protocols.STK500v2.Messages { internal class GetSyncResponse : Response { - public bool IsInSync + internal bool IsInSync { get { @@ -15,7 +15,7 @@ public bool IsInSync } } - public string Signature + internal string Signature { get { diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/LeaveProgrammingModeRequest.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/LeaveProgrammingModeRequest.cs index 6af0833..a9f3440 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/LeaveProgrammingModeRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/LeaveProgrammingModeRequest.cs @@ -2,7 +2,7 @@ { internal class LeaveProgrammingModeRequest : Request { - public LeaveProgrammingModeRequest() + internal LeaveProgrammingModeRequest() { Bytes = new[] { diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/LeaveProgrammingModeResponse.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/LeaveProgrammingModeResponse.cs index 2271ebb..bcb8f6e 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/LeaveProgrammingModeResponse.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/LeaveProgrammingModeResponse.cs @@ -2,7 +2,7 @@ { internal class LeaveProgrammingModeResponse : Response { - public bool Success + internal bool Success { get { diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/LoadAddressRequest.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/LoadAddressRequest.cs index 8995e88..4d249aa 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/LoadAddressRequest.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/LoadAddressRequest.cs @@ -4,7 +4,7 @@ namespace ArduinoUploader.Protocols.STK500v2.Messages { internal class LoadAddressRequest : Request { - public LoadAddressRequest(IMemory memory, int addr) + internal LoadAddressRequest(IMemory memory, int addr) { var modifier = memory.Type == MemoryType.FLASH ? 0x80 : 0x00; Bytes = new[] diff --git a/Source/ArduinoUploader/Protocols/STK500v2/Messages/LoadAddressResponse.cs b/Source/ArduinoUploader/Protocols/STK500v2/Messages/LoadAddressResponse.cs index c462e11..ff96c59 100644 --- a/Source/ArduinoUploader/Protocols/STK500v2/Messages/LoadAddressResponse.cs +++ b/Source/ArduinoUploader/Protocols/STK500v2/Messages/LoadAddressResponse.cs @@ -2,7 +2,7 @@ { internal class LoadAddressResponse : Response { - public bool Succeeded + internal bool Succeeded { get { diff --git a/Source/ArduinoUploader/UploaderLogger.cs b/Source/ArduinoUploader/UploaderLogger.cs index d8de5fb..8f821ad 100644 --- a/Source/ArduinoUploader/UploaderLogger.cs +++ b/Source/ArduinoUploader/UploaderLogger.cs @@ -7,11 +7,10 @@ internal class UploaderLogger { private static readonly Logger logger = LogManager.GetCurrentClassLogger(); - internal static void LogAndThrowError(string errorMessage) where TException : Exception, new() + internal static void LogErrorAndQuit(string errorMessage) { logger.Error(errorMessage); - var exception = (TException) Activator.CreateInstance(typeof(TException), errorMessage); - throw exception; + Environment.Exit(1); } } } diff --git a/Source/ArduinoUploader/UploaderSerialPort.cs b/Source/ArduinoUploader/UploaderSerialPort.cs deleted file mode 100644 index 0e7784a..0000000 --- a/Source/ArduinoUploader/UploaderSerialPort.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.IO.Ports; - -namespace ArduinoUploader -{ - internal class UploaderSerialPort : SerialPort - { - public UploaderSerialPort(string portName, int baudRate) - : base(portName, baudRate) - { - } - } -} diff --git a/Source/ArduinoUploader/packages.config b/Source/ArduinoUploader/packages.config index 44a115e..100e081 100644 --- a/Source/ArduinoUploader/packages.config +++ b/Source/ArduinoUploader/packages.config @@ -2,4 +2,5 @@ + \ No newline at end of file