|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +from __future__ import print_function |
| 4 | + |
| 5 | +import sys |
| 6 | +import argparse |
| 7 | +import errno |
| 8 | +import serial |
| 9 | +import struct |
| 10 | +import time |
| 11 | + |
| 12 | +__version__ = '0.9.1' |
| 13 | + |
| 14 | +CMD_PEEK = (0x0) |
| 15 | +CMD_POKE = (0x01) |
| 16 | +CMD_MAGIC = (0x02) |
| 17 | +CMD_HW_VER = (0x10) |
| 18 | +CMD_FW_VER = (0x11) |
| 19 | +CMD_PROD_ID = (0x12) |
| 20 | +CMD_SETUP_SLEEP = (0x20) |
| 21 | +CMD_GO_SLEEP = (0x21) |
| 22 | +CMD_CALIBRATE = (0x22) |
| 23 | +CMD_BAUD_CHANGE = (0x30) |
| 24 | +CMD_DFU = (0x31) |
| 25 | + |
| 26 | +ANSELA_ADDR = (0x18C) |
| 27 | +ANSELB_ADDR = (0x18D) |
| 28 | +ANSELC_ADDR = (0x18E) |
| 29 | + |
| 30 | +ADCON0_ADDR = (0x9D) |
| 31 | +ADCON1_ADDR = (0x9E) |
| 32 | + |
| 33 | +IOCAP_ADDR = (0x391) |
| 34 | +IOCAN_ADDR = (0x392) |
| 35 | + |
| 36 | +_ADCON0_CHS_POSN = (0x02) |
| 37 | +_ADCON0_ADON_MASK = (0x01) |
| 38 | +_ADCON1_ADCS_POSN = (0x04) |
| 39 | +_ADCON0_GO_nDONE_MASK = (0x02) |
| 40 | + |
| 41 | +ADRESL_ADDR = (0x09B) |
| 42 | +ADRESH_ADDR = (0x09C) |
| 43 | + |
| 44 | +TRISC_ADDR = (0x08E) |
| 45 | + |
| 46 | +PORTA_ADDR = (0x00C) |
| 47 | +PORTC_ADDR = (0x00E) |
| 48 | + |
| 49 | +WPUA_ADDR = (0x20C) |
| 50 | + |
| 51 | +PCON_ADDR = (0x096) |
| 52 | +STATUS_ADDR = (0x083) |
| 53 | + |
| 54 | + |
| 55 | +# helper functions |
| 56 | +def eprint(*args, **kwargs): |
| 57 | + print(*args, file=sys.stderr, **kwargs) |
| 58 | + |
| 59 | +def log(*args): |
| 60 | + print(' '.join(str(a) for a in args)) |
| 61 | + |
| 62 | +def error(msg): |
| 63 | + eprint('error:', msg) |
| 64 | + |
| 65 | +def exit_with_error(code, msg): |
| 66 | + error(msg) |
| 67 | + sys.exit(code) |
| 68 | + |
| 69 | +def warn(msg): |
| 70 | + eprint('warning:', msg) |
| 71 | + |
| 72 | + |
| 73 | +class Pypic: |
| 74 | + |
| 75 | + def __init__(self, port): |
| 76 | + # we need bytesize to be 5 bits in order for the PIC to process the commands |
| 77 | + self.serial = serial.Serial(port, baudrate=115200, bytesize=serial.FIVEBITS, timeout=0.25) |
| 78 | + self.detected = False |
| 79 | + |
| 80 | + try: |
| 81 | + if self.read_fw_version() < 6: |
| 82 | + raise ValueError('PIC firmware out of date') |
| 83 | + else: |
| 84 | + self.detected = True |
| 85 | + except Exception: |
| 86 | + pass |
| 87 | + |
| 88 | + def _write(self, data, read=True): |
| 89 | + self.serial.write(data) |
| 90 | + if read: |
| 91 | + r_data = self.serial.read(2) |
| 92 | + if not r_data: |
| 93 | + raise Exception('Timeout while waiting for Rx data') |
| 94 | + return struct.unpack('B', r_data[0])[0] |
| 95 | + |
| 96 | + def _send_cmd(self, cmd): |
| 97 | + return self._write(bytearray([cmd])) |
| 98 | + |
| 99 | + def read_hw_version(self): |
| 100 | + return self._send_cmd(CMD_HW_VER) |
| 101 | + |
| 102 | + def read_fw_version(self): |
| 103 | + return self._send_cmd(CMD_FW_VER) |
| 104 | + |
| 105 | + def read_product_id(self): |
| 106 | + return self._send_cmd(CMD_PROD_ID) |
| 107 | + |
| 108 | + def peek_memory(self, addr): |
| 109 | + return self._write(bytearray([CMD_PEEK, addr & 0xFF, (addr >> 8) & 0xFF])) |
| 110 | + |
| 111 | + def poke_memory(self, addr, value): |
| 112 | + self._write(bytearray([CMD_POKE, addr & 0xFF, (addr >> 8) & 0xFF, value & 0xFF]), False) |
| 113 | + |
| 114 | + def magic_write_read(self, addr, _and=0xFF, _or=0, _xor=0): |
| 115 | + return self._write(bytearray([CMD_MAGIC, addr & 0xFF, (addr >> 8) & 0xFF, _and & 0xFF, _or & 0xFF, _xor & 0xFF])) |
| 116 | + |
| 117 | + def magic_write(self, addr, _and=0xFF, _or=0, _xor=0): |
| 118 | + self._write(bytearray([CMD_MAGIC, addr & 0xFF, (addr >> 8) & 0xFF, _and & 0xFF, _or & 0xFF, _xor & 0xFF]), False) |
| 119 | + |
| 120 | + def toggle_bits_in_memory(self, addr, bits): |
| 121 | + self.magic_write(addr, _xor=bits) |
| 122 | + |
| 123 | + def mask_bits_in_memory(self, addr, mask): |
| 124 | + self.magic_write(addr, _and=mask) |
| 125 | + |
| 126 | + def set_bits_in_memory(self, addr, bits): |
| 127 | + self.magic_write(addr, _or=bits) |
| 128 | + |
| 129 | + def reset_pycom_module(self): |
| 130 | + # make RC5 an output |
| 131 | + self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 5)) |
| 132 | + # drive RC5 low |
| 133 | + self.mask_bits_in_memory(PORTC_ADDR, ~(1 << 5)) |
| 134 | + time.sleep(0.25) |
| 135 | + # drive RC5 high |
| 136 | + self.set_bits_in_memory(PORTC_ADDR, 1 << 5) |
| 137 | + time.sleep(0.1) |
| 138 | + |
| 139 | + def enter_pycom_programming_mode(self): |
| 140 | + # make RC0 an output |
| 141 | + self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 0)) |
| 142 | + # set RC0 low |
| 143 | + self.mask_bits_in_memory(PORTC_ADDR, ~(1 << 0)) |
| 144 | + self.reset_pycom_module() |
| 145 | + |
| 146 | + def exit_pycom_programming_mode(self): |
| 147 | + # make RC0 an output |
| 148 | + self.mask_bits_in_memory(TRISC_ADDR, ~(1 << 0)) |
| 149 | + # set RC0 high |
| 150 | + self.set_bits_in_memory(PORTC_ADDR, 1 << 0) |
| 151 | + self.reset_pycom_module() |
| 152 | + |
| 153 | + def isdetected(self): |
| 154 | + return self.detected |
| 155 | + |
| 156 | + def close(self): |
| 157 | + self.serial.close() |
| 158 | + |
| 159 | + |
| 160 | +def main(args): |
| 161 | + parser = argparse.ArgumentParser(description='Sends internal commands to pu the Pycom module in programming mode') |
| 162 | + parser.add_argument('-p', '--port', metavar='PORT', help='the serial port used to communicate with the PIC') |
| 163 | + parser.add_argument('--enter', action='store_true', help='enter programming mode') |
| 164 | + parser.add_argument('--exit', action='store_true', help='exit programming mode') |
| 165 | + args = parser.parse_args() |
| 166 | + |
| 167 | + if not args.port: |
| 168 | + exit_with_error(1, 'no serial port specified') |
| 169 | + |
| 170 | + if (args.enter and args.exit) or (not args.enter and not args.exit): |
| 171 | + exit_with_error(1, 'invalid action requested') |
| 172 | + |
| 173 | + pic = Pypic(args.port) |
| 174 | + |
| 175 | + if pic.isdetected(): |
| 176 | + if args.enter: |
| 177 | + pic.enter_pycom_programming_mode() |
| 178 | + elif args.exit: |
| 179 | + pic.exit_pycom_programming_mode() |
| 180 | + |
| 181 | + pic.close() |
| 182 | + |
| 183 | + |
| 184 | +if __name__ == "__main__": |
| 185 | + sys.exit(main(sys.argv[1:])) |
0 commit comments