123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- # Copyright 2017 Clayton G. Hobbs
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
-
- from enum import Enum
- import time
-
- import serial
-
-
- class LPC:
- """Interface for LPC in-system programming"""
-
- def __init__(self, serport, baudrate=115200, clock=12000, timeout=0.1):
- self._serport = serport
- self._baudrate = baudrate
- self._clock = clock
- self._timeout = timeout
- self._echo = True
-
- def open(self):
- """Open the serial port to communicate with the microcontroller"""
- self._uart = serial.Serial(self._serport, baudrate=self._baudrate,
- timeout=self._timeout)
-
- def _readline(self):
- """Read a line terminated with b'\r\n'"""
- s = b""
- while True:
- c = self._uart.read(1)
- if not c:
- # If we timed out, give up
- raise RecvTimeout(s)
- s += c
- if s.endswith(b"\r\n"):
- return s
-
- def _writeline(self, line, plain=False):
- """Write a line to the microcontroller and read the echoed response
-
- If plain is True, the command is taken to be raw binary data.
- """
- self._uart.write(line)
- self._uart.flush()
-
- # If echo is disabled, don't try to read back what we sent
- if not self.echo:
- return
-
- # Read the response, raising the exception if there is one
- if plain:
- response = self._uart.read(len(line))
- else:
- response = self._readline()
- # If we got the wrong response, raise an exception
- if response != line:
- raise ISPError("Wrong text echoed: {}".format(response))
-
- def _send_command_raw(self, cmd):
- """Send a command to the microcontroller, returning bytes"""
- self._writeline(cmd)
- return self._readline()
-
- def _send_command(self, cmd):
- """Send a command to the microcontroller, returning the result"""
- rr = self._send_command_raw(cmd)
- lr = [int(n) for n in rr.split()]
- lr[0] = ReturnCode(lr[0])
- return lr
-
- def enter_isp(self, delay=0.01):
- """Enter ISP mode by controlling the DTR (/RST) and RTS (/ISP) lines
-
- This operation is performed synchronously, with delays.
- """
- self._uart.rts = True
- time.sleep(delay)
- self._uart.dtr = True
- time.sleep(delay)
- self._uart.dtr = False
- time.sleep(delay)
- self._uart.rts = False
-
- def synchronize(self, verbose=False):
- """Begin communication with the microcontroller"""
- # Synchronize with the MCU
- while True:
- # Send a ?
- self._uart.write(b"?")
- self._uart.flush()
- if verbose:
- print("?")
- # Receive a response
- try:
- s = self._readline()
- except RecvTimeout:
- continue
- # If we got the right response, break
- if s == b"Synchronized\r\n":
- break
- # Tell the MCU we've synchronized
- s = self._send_command_raw(b"Synchronized\r\n")
- # Next, it should say OK, at which point we're done synchronizing
- if s != b"OK\r\n":
- raise ISPError("Wrong response during synchronization")
-
- # Send clock frequency in kHz
- s = self._send_command_raw("{:d}\r\n".format(self._clock).encode(
- "utf-8"))
- # Next, it should say OK
- if s != b"OK\r\n":
- raise ISPError("Wrong response during synchronization")
-
- def close(self):
- """Close the serial port"""
- self._uart.close()
-
- def unlock(self, code="23130"):
- """Unlock the flash write, erase, and go commands"""
- r = self._send_command("U {}\r\n".format(code).encode("utf-8"))
- if r[0] != ReturnCode.CMD_SUCCESS:
- raise ISPError(r)
-
- @property
- def baudrate(self):
- """The baud rate used for communication"""
- return self._uart.baudrate
-
- @baudrate.setter
- def baudrate(self, br):
- r = self._send_command("B {} {}\r\n".format(br,
- self._uart.stopbits).encode("utf-8"))
- # Update the baud rate for our UART
- if r[0] != ReturnCode.CMD_SUCCESS:
- raise ISPError(r)
- self._uart.baudrate = br
-
- @property
- def stopbits(self):
- """The number of stop bits used for communication"""
- return self._uart.stopbits
-
- @stopbits.setter
- def stopbits(self, sb):
- r = self._send_command("B {} {}\r\n".format(self._uart.baudrate,
- sb).encode("utf-8"))
- # Update the number of stop bits for our UART
- if r[0] != ReturnCode.CMD_SUCCESS:
- raise ISPError(r)
- self._uart.stopbits = sb
-
- @property
- def echo(self):
- """Whether the microcontroller echoes characters back to the host"""
- return self._echo
-
- @echo.setter
- def echo(self, setting):
- setting = bool(setting)
- r = self._send_command("A {}\r\n".format(int(setting)).encode("utf-8"))
- if r[0] != ReturnCode.CMD_SUCCESS:
- raise ISPError(r)
- self._echo = setting
-
- def write_ram(self, start, data, count=None):
- """Write count bytes from data to RAM at the given start address
-
- Start and count must be multiples of four. If count is not specified,
- len(data) is used.
- """
- # Get the length of the data we're writing
- if count is None:
- count = len(data)
- # Ask to write data
- r = self._send_command("W {} {}\r\n".format(start, count).encode(
- "utf-8"))
- if r[0] != ReturnCode.CMD_SUCCESS:
- raise ISPError(r)
- # If the MCU is okay with what we intend to do, send the data
- # NOTE: this is right for LPC8xx chips, not others
- ok = self._writeline(data[:count], plain=True)
- return
-
- def read_memory(self, start, count):
- """Read count bytes starting at the given address
-
- Start and count must be multiples of four.
- """
- r = self._send_command("R {} {}\r\n".format(start, count).encode(
- "utf-8"))
- if r[0] != ReturnCode.CMD_SUCCESS:
- raise ISPError(r)
- return self._uart.read(count)
-
-
- class ReturnCode(Enum):
- """LPC ISP return codes
-
- From UM10800, section 25.6.1.16.
- """
- CMD_SUCCESS = 0
- INVALID_COMMAND = 1
- SRC_ADDR_ERROR = 2
- DST_ADDR_ERROR = 3
- SRC_ADDR_NOT_MAPPED = 4
- DST_ADDR_NOT_MAPPED = 5
- COUNT_ERROR = 6
- INVALID_SECTOR = 7
- SECTOR_NOT_BLANK = 8
- SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION = 9
- COMPARE_ERROR = 10
- BUSY = 11
- PARAM_ERROR = 12
- ADDR_ERROR = 13
- ADDR_NOT_MAPPED = 14
- CMD_LOCKED = 15
- INVALID_CODE = 16
- INVALID_BAUD_RATE = 17
- INVALID_STOP_BIT = 18
- CODE_READ_PROTECTION_ENABLED = 19
-
-
- class ISPError(IOError):
- """Generic error for ISP"""
-
-
- class RecvTimeout(ISPError):
- """Timeout while receiving a command"""
|