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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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 '\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):
  41. """Write a line to the microcontroller and read the echoed response"""
  42. self._uart.write(line)
  43. self._uart.flush()
  44. if not self._echo:
  45. return
  46. # Read the response, raising the exception if there is one
  47. response = self.readline()
  48. # If we got the wrong response, raise an exception
  49. if response != line:
  50. raise ISPError("Wrong text echoed: {}".format(response))
  51. def send_command_raw(self, cmd):
  52. """Send a command to the microcontroller, returning bytes"""
  53. self.writeline(cmd)
  54. return self.readline()
  55. def send_command(self, cmd):
  56. """Send a command to the microcontroller, returning the result"""
  57. rr = self.send_command_raw(cmd)
  58. lr = [int(n) for n in rr.split()]
  59. lr[0] = ReturnCode(lr[0])
  60. return lr
  61. def start_isp(self, delay=0.01):
  62. """Enter ISP mode by controlling the DTR (/RST) and RTS (/ISP) lines
  63. This operation is performed synchronously.
  64. """
  65. self._uart.rts = True
  66. time.sleep(delay)
  67. self._uart.dtr = True
  68. time.sleep(delay)
  69. self._uart.dtr = False
  70. time.sleep(delay)
  71. self._uart.rts = False
  72. def start_comms(self, verbose=False):
  73. """Start communication with the microcontroller"""
  74. # Synchronize with the MCU
  75. while True:
  76. # Send a ?
  77. self._uart.write(b"?")
  78. self._uart.flush()
  79. if verbose:
  80. print("?")
  81. # Receive a response
  82. try:
  83. s = self.readline()
  84. except RecvTimeout:
  85. continue
  86. # If we got the right response, break
  87. if s == b"Synchronized\r\n":
  88. break
  89. # Tell the MCU we've synchronized
  90. s = self.send_command_raw(b"Synchronized\r\n")
  91. # Next, it should say OK, at which point we're done synchronizing
  92. if s != b"OK\r\n":
  93. raise ISPError("Wrong response during synchronization")
  94. # Send clock frequency in kHz
  95. s = self.send_command_raw("{:d}\r\n".format(self.clock).encode("utf-8"))
  96. # Next, it should say OK
  97. if s != b"OK\r\n":
  98. raise ISPError("Wrong response during synchronization")
  99. def close(self):
  100. """Close the serial port"""
  101. self._uart.close()
  102. def unlock(self, code="23130"):
  103. """Unlock the flash write, erase, and go commands"""
  104. return self.send_command("U {}\r\n".format(code).encode("utf-8"))
  105. def setbaudrate(self, baudrate, stopbits):
  106. """Update the baud rate and number of stop bits"""
  107. r = self.send_command("B {} {}\r\n".format(baudrate, stopbits).encode(
  108. "utf-8"))
  109. # Update the baud rate and number of stop bits for our UART connection
  110. if r[0] == ReturnCode.CMD_SUCCESS:
  111. self._uart.baudrate = baudrate
  112. self._uart.stopbits = stopbits
  113. return r
  114. def echo(self, setting):
  115. """Set whether echo is enabled or disabled"""
  116. r = self.send_command("A {}\r\n".format(int(setting)).encode("utf-8"))
  117. self._echo = setting
  118. return r
  119. class ReturnCode(Enum):
  120. """LPC ISP return codes
  121. From UM10800, section 25.6.1.16.
  122. """
  123. CMD_SUCCESS = 0
  124. INVALID_COMMAND = 1
  125. SRC_ADDR_ERROR = 2
  126. DST_ADDR_ERROR = 3
  127. SRC_ADDR_NOT_MAPPED = 4
  128. DST_ADDR_NOT_MAPPED = 5
  129. COUNT_ERROR = 6
  130. INVALID_SECTOR = 7
  131. SECTOR_NOT_BLANK = 8
  132. SECTOR_NOT_PREPARED_FOR_WRITE_OPERATION = 9
  133. COMPARE_ERROR = 10
  134. BUSY = 11
  135. PARAM_ERROR = 12
  136. ADDR_ERROR = 13
  137. ADDR_NOT_MAPPED = 14
  138. CMD_LOCKED = 15
  139. INVALID_CODE = 16
  140. INVALID_BAUD_RATE = 17
  141. INVALID_STOP_BIT = 18
  142. CODE_READ_PROTECTION_ENABLED = 19
  143. class ISPError(IOError):
  144. """Generic error for ISP"""
  145. class RecvTimeout(ISPError):
  146. """Timeout while receiving a command"""