In-system programming tool for LPC microcontrollers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__init__.py 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. # Copyright 2017 Clayton G. Hobbs
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. from enum import Enum
  15. import time
  16. import serial
  17. class LPC:
  18. """Interface for LPC in-system programming"""
  19. def __init__(self, serport, baudrate=115200, clock=12000, timeout=0.1):
  20. self._serport = serport
  21. self._baudrate = baudrate
  22. self._clock = clock
  23. self._timeout = timeout
  24. self._echo = True
  25. def open(self):
  26. """Open the serial port to communicate with the microcontroller"""
  27. self._uart = serial.Serial(self._serport, baudrate=self._baudrate,
  28. timeout=self._timeout)
  29. def _readline(self):
  30. """Read a line terminated with b'\r\n'"""
  31. s = b""
  32. while True:
  33. c = self._uart.read(1)
  34. if not c:
  35. # If we timed out, give up
  36. raise RecvTimeout(s)
  37. s += c
  38. if s.endswith(b"\r\n"):
  39. return s
  40. def _writeline(self, line, plain=False):
  41. """Write a line to the microcontroller and read the echoed response
  42. If plain is True, the command is taken to be raw binary data.
  43. """
  44. self._uart.write(line)
  45. self._uart.flush()
  46. # If echo is disabled, don't try to read back what we sent
  47. if not self.echo:
  48. return
  49. # Read the response, raising the exception if there is one
  50. if plain:
  51. response = self._uart.read(len(line))
  52. else:
  53. response = self._readline()
  54. # If we got the wrong response, raise an exception
  55. if response != line:
  56. raise ISPError("Wrong text echoed: {}".format(response))
  57. def _send_command_raw(self, cmd):
  58. """Send a command to the microcontroller, returning bytes"""
  59. self._writeline(cmd)
  60. return self._readline()
  61. def _send_command(self, cmd):
  62. """Send a command to the microcontroller, returning the result"""
  63. rr = self._send_command_raw(cmd)
  64. lr = [int(n) for n in rr.split()]
  65. lr[0] = ReturnCode(lr[0])
  66. return lr
  67. def enter_isp(self, delay=0.01):
  68. """Enter ISP mode by controlling the DTR (/RST) and RTS (/ISP) lines
  69. This operation is performed synchronously, with delays.
  70. """
  71. self._uart.rts = True
  72. time.sleep(delay)
  73. self._uart.dtr = True
  74. time.sleep(delay)
  75. self._uart.dtr = False
  76. time.sleep(delay)
  77. self._uart.rts = False
  78. def synchronize(self, verbose=False):
  79. """Begin communication with the microcontroller"""
  80. # Synchronize with the MCU
  81. while True:
  82. # Send a ?
  83. self._uart.write(b"?")
  84. self._uart.flush()
  85. if verbose:
  86. print("?")
  87. # Receive a response
  88. try:
  89. s = self._readline()
  90. except RecvTimeout:
  91. continue
  92. # If we got the right response, break
  93. if s == b"Synchronized\r\n":
  94. break
  95. # Tell the MCU we've synchronized
  96. s = self._send_command_raw(b"Synchronized\r\n")
  97. # Next, it should say OK, at which point we're done synchronizing
  98. if s != b"OK\r\n":
  99. raise ISPError("Wrong response during synchronization")
  100. # Send clock frequency in kHz
  101. s = self._send_command_raw("{:d}\r\n".format(self._clock).encode(
  102. "utf-8"))
  103. # Next, it should say OK
  104. if s != b"OK\r\n":
  105. raise ISPError("Wrong response during synchronization")
  106. def close(self):
  107. """Close the serial port"""
  108. self._uart.close()
  109. def unlock(self, code="23130"):
  110. """Unlock the flash write, erase, and go commands"""
  111. r = self._send_command("U {}\r\n".format(code).encode("utf-8"))
  112. if r[0] != ReturnCode.CMD_SUCCESS:
  113. raise ISPError(r)
  114. @property
  115. def baudrate(self):
  116. """The baud rate used for communication"""
  117. return self._uart.baudrate
  118. @baudrate.setter
  119. def baudrate(self, br):
  120. r = self._send_command("B {} {}\r\n".format(br,
  121. self._uart.stopbits).encode("utf-8"))
  122. # Update the baud rate for our UART
  123. if r[0] != ReturnCode.CMD_SUCCESS:
  124. raise ISPError(r)
  125. self._uart.baudrate = br
  126. @property
  127. def stopbits(self):
  128. """The number of stop bits used for communication"""
  129. return self._uart.stopbits
  130. @stopbits.setter
  131. def stopbits(self, sb):
  132. r = self._send_command("B {} {}\r\n".format(self._uart.baudrate,
  133. sb).encode("utf-8"))
  134. # Update the number of stop bits for our UART
  135. if r[0] != ReturnCode.CMD_SUCCESS:
  136. raise ISPError(r)
  137. self._uart.stopbits = sb
  138. @property
  139. def echo(self):
  140. """Whether the microcontroller echoes characters back to the host"""
  141. return self._echo
  142. @echo.setter
  143. def echo(self, setting):
  144. setting = bool(setting)
  145. r = self._send_command("A {}\r\n".format(int(setting)).encode("utf-8"))
  146. if r[0] != ReturnCode.CMD_SUCCESS:
  147. raise ISPError(r)
  148. self._echo = setting
  149. def write_ram(self, start, data, count=None):
  150. """Write count bytes from data to RAM at the given start address
  151. Start and count must be multiples of four. If count is not specified,
  152. len(data) is used.
  153. """
  154. # Get the length of the data we're writing
  155. if count is None:
  156. count = len(data)
  157. # Ask to write data
  158. r = self._send_command("W {} {}\r\n".format(start, count).encode(
  159. "utf-8"))
  160. if r[0] != ReturnCode.CMD_SUCCESS:
  161. raise ISPError(r)
  162. # If the MCU is okay with what we intend to do, send the data
  163. # NOTE: this is right for LPC8xx chips, not others
  164. ok = self._writeline(data[:count], plain=True)
  165. return
  166. def read_memory(self, start, count):
  167. """Read count bytes starting at the given address
  168. Start and count must be multiples of four.
  169. """
  170. r = self._send_command("R {} {}\r\n".format(start, count).encode(
  171. "utf-8"))
  172. if r[0] != ReturnCode.CMD_SUCCESS:
  173. raise ISPError(r)
  174. return self._uart.read(count)
  175. class ReturnCode(Enum):
  176. """LPC ISP return codes
  177. From UM10800, section 25.6.1.16.
  178. """
  179. CMD_SUCCESS = 0
  180. INVALID_COMMAND = 1
  181. SRC_ADDR_ERROR = 2
  182. DST_ADDR_ERROR = 3
  183. SRC_ADDR_NOT_MAPPED = 4
  184. DST_ADDR_NOT_MAPPED = 5
  185. COUNT_ERROR = 6
  186. INVALID_SECTOR = 7
  187. SECTOR_NOT_BLANK = 8
  188. SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION = 9
  189. COMPARE_ERROR = 10
  190. BUSY = 11
  191. PARAM_ERROR = 12
  192. ADDR_ERROR = 13
  193. ADDR_NOT_MAPPED = 14
  194. CMD_LOCKED = 15
  195. INVALID_CODE = 16
  196. INVALID_BAUD_RATE = 17
  197. INVALID_STOP_BIT = 18
  198. CODE_READ_PROTECTION_ENABLED = 19
  199. class ISPError(IOError):
  200. """Generic error for ISP"""
  201. class RecvTimeout(ISPError):
  202. """Timeout while receiving a command"""