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