Created
June 1, 2023 19:59
-
-
Save LingDong-/0c917b3f37e042807e311cd3b99a00d3 to your computer and use it in GitHub Desktop.
(minimal) TI MSP430 μC 1,2,4 Family BSL flasher (slau319af)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
bsl124.py | |
(minimal) TI MSP430 1,2,4 Family BSL flasher | |
using pyserial, works on windows, mac, and linux | |
- implementation of slau319af.pdf | |
- programming MSP430 chips via USB-TTL / FTDI / ... | |
- supports Intel HEX format | |
- supports F1xx, F2xx, F4xx, G2xx3 chips | |
usage: | |
python3 bsl124.py -p /dev/cu.usbserial-110 -i blink.hex | |
hardware connection example: | |
____3V3______ 5V 3V3 | |
| | | +---------+ | | |
+-------+ +-----------+ +--|VI GND VO|--+ | |
+---+ | VCC | | VCC | | +---------+ | | |
| 5V| | TX|---|BSLRX(P1.5)| =10uF | =10uF | |
| D+|---|D+ RX|---|BSLTX(P1.1)| |______GND______| | |
| D-|---|D- DTR|---|RST | | |
|GND| | RTS|---|TEST | RST--[10Kohm]->3V3 | |
+---+ | GND | | GND | TEST-[10Kohm]->GND | |
USB +-------+ +-----------+ 3V3--[100nF ]->GND | |
CH340C MSP430G2553 3V3--[100nF ]->GND | |
- I use CH340C but you should be able to use any | |
USB to serial/UART/TTL/RS-232 chips | |
- FTDI cables often don't have DTR line (only CTS/RTS), | |
so you might need a breakout board / custom PCB | |
- check the datasheet of your chip for assignment | |
of the BSL TX/RX pins, they're different from TXD/RXD | |
example hex file for G2553, for your testing convenience: | |
:10C00000314000040C43B0120AC02183B240805A70 | |
:10C010002001F2432200F2432A005C4221007CE32B | |
:10C020003CF0FF00C24C21005C4229007CE33CF064 | |
:10C03000FF00C24C2900B140102700002C413C53A6 | |
:0CC04000814C00002C410C93F923E73FD9 | |
:02FFFE0000C041 | |
:040000030000C00039 | |
:00000001FF | |
- this code blinks all the pins on PORT1 and PORT2, | |
connect an LED to any. | |
- you can use msp430-elf-gcc and msp430-elf-objcopy | |
(available from TI's website) to make hex files. | |
example output from bsl124.py: | |
====== parsing ihex file ====== | |
block sizes: [16, 16, 16, 16, 12, 2] | |
===== opening serial port ===== | |
/dev/cu.usbserial-110 | |
== sending BSL init sequence == | |
========== mass erase ========== | |
SYNC ACK. CMD ACK. | |
=== sending default password === | |
SYNC ACK. CMD ACK. | |
====== sending RX blocks ====== | |
1 / 6 ... SYNC ACK. CMD ACK. | |
2 / 6 ... SYNC ACK. CMD ACK. | |
3 / 6 ... SYNC ACK. CMD ACK. | |
4 / 6 ... SYNC ACK. CMD ACK. | |
5 / 6 ... SYNC ACK. CMD ACK. | |
6 / 6 ... SYNC ACK. CMD ACK. | |
=== loading program counter === | |
SYNC ACK. CMD ACK. | |
DONE. | |
(c) lingdong 2023, MIT License | |
""" | |
import serial # pyserial.readthedocs.io | |
import time | |
import sys | |
DTRRTS_NOT_INV = 0 | |
ARGS = { | |
'-p':"/dev/cu.usbserial-110" | |
} | |
frame_state = -1 | |
ser = None | |
ckl = 0 | |
ckh = 0 | |
for i in range(1,len(sys.argv),2): | |
try: | |
k = sys.argv[i]; | |
if k == "--help" or k == '-h': | |
print("usage:\n\tpython3 bsl124.py -p [port] -i [file.hex]") | |
exit(0); | |
v = sys.argv[i+1]; | |
ARGS[k] = v | |
except Exception as e: | |
print("ERR: bad arg:", k) | |
exit(1) | |
def parse_ihex(filepath): | |
lines = [x.strip().split(':')[1] for x in open(filepath,'r').read().replace("\r","\n").split('\n') if len(x.strip())]; | |
recs = [] | |
for l in lines: | |
typ = int(l[6:8],16) | |
num = int(l[0:2],16) | |
if (num > 250): | |
print("ERR: ihex record too long (250 max.): ",l) | |
exit(1); | |
if (num & 1): | |
print("ERR: ihex record length not even: ",l) | |
exit(1); | |
if (typ == 0): | |
recs.append({ | |
'bcnt': num, | |
'addr': int(l[2:6],16), | |
'data': [int(l[i:i+2],16) for i in range(8,len(l)-2,2)] | |
}) | |
return recs | |
def serial_open(): | |
global ser | |
ser = serial.Serial( | |
port=ARGS['-p'], | |
baudrate=9600, | |
bytesize=serial.EIGHTBITS, | |
parity=serial.PARITY_EVEN, | |
stopbits=serial.STOPBITS_ONE, | |
#rtscts=True, | |
#dsrdtr=True, | |
timeout=2, | |
) | |
print(ser.name) | |
def set_dtr(v): | |
ser.dtr = not (v ^ DTRRTS_NOT_INV) | |
time.sleep(0.05) | |
def set_rts(v): | |
ser.rts = not (v ^ DTRRTS_NOT_INV) | |
time.sleep(0.05) | |
def send_sync(): | |
send_byte(0x80); | |
ret = read_byte() | |
if (ret == 0x90): | |
print("SYNC ACK.",end=' ',flush=True) | |
else: | |
print("SYNC NOT ACK:",ret); | |
exit(1); | |
def init_seq(): | |
set_dtr(0) | |
set_rts(0) | |
time.sleep(0.1); | |
set_rts(1); | |
set_rts(0); | |
set_rts(1); | |
set_rts(0); | |
set_rts(1); | |
set_dtr(1); | |
set_rts(0); | |
time.sleep(0.2); | |
def send_byte(b): | |
global frame_state, ckl, ckh | |
ser.write(bytes([b])) | |
if frame_state != -1: | |
if frame_state: | |
ckh ^= b | |
else: | |
ckl ^= b | |
frame_state ^= 1 | |
def read_byte(): | |
return int.from_bytes(ser.read(1),'little'); | |
def start_frame(): | |
global frame_state, ckl, ckh | |
send_sync(); | |
ckl = 0 | |
ckh = 0 | |
frame_state = 0 | |
def end_frame(): | |
global frame_state, ckl, ckh | |
frame_state = -1 | |
send_byte(ckl^0xff) | |
send_byte(ckh^0xff) | |
ret = read_byte() | |
if (ret == 0x90): | |
print("CMD ACK.") | |
time.sleep(0.2) | |
else: | |
print("CMD NOT ACK:",ret); | |
exit(1); | |
def cmd_load_pc(): | |
start_frame(); | |
send_byte(0x80); | |
send_byte(0x1A); | |
send_byte(0x04); | |
send_byte(0x04); | |
send_byte(0xFE); | |
send_byte(0xFF); | |
send_byte(0xFF); | |
send_byte(0xFF); | |
end_frame(); | |
def cmd_rx_password0(): | |
start_frame(); | |
send_byte(0x80); | |
send_byte(0x10); | |
send_byte(0x24); | |
send_byte(0x24); | |
send_byte(0xFF); | |
send_byte(0xFF); | |
send_byte(0xFF); | |
send_byte(0xFF); | |
for i in range(0,0x20): | |
send_byte(0xFF); | |
end_frame(); | |
def cmd_mass_erase(): | |
start_frame(); | |
send_byte(0x80); | |
send_byte(0x18); | |
send_byte(0x04); | |
send_byte(0x04); | |
send_byte(0xFF); | |
send_byte(0xFF); | |
send_byte(0x06); | |
send_byte(0xA5); | |
end_frame(); | |
def cmd_rx_data(rec): | |
# print(rec); | |
start_frame(); | |
send_byte(0x80); | |
send_byte(0x12); | |
l1l2 = rec['bcnt']+4; | |
send_byte(l1l2); | |
send_byte(l1l2); | |
send_byte(rec['addr']&0xFF); | |
send_byte((rec['addr']>>8)&0xFF); | |
send_byte(rec['bcnt']); | |
send_byte(0); | |
for i in range(rec['bcnt']): | |
send_byte(rec['data'][i]) | |
end_frame(); | |
print("====== parsing ihex file ======"); | |
recs = parse_ihex(ARGS['-i']) | |
print("block sizes:", [x['bcnt'] for x in recs]) | |
print("===== opening serial port ====="); | |
serial_open(); | |
print("== sending BSL init sequence =="); | |
init_seq(); | |
print("========== mass erase =========="); | |
cmd_mass_erase(); | |
print("=== sending default password ==="); | |
cmd_rx_password0(); | |
print("====== sending RX blocks ======"); | |
for i in range(len(recs)): | |
print(i+1,'/',len(recs),'...',end=' ',flush=True); | |
cmd_rx_data(recs[i]); | |
print("=== loading program counter ==="); | |
cmd_load_pc(); | |
print("DONE.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment