SPI.py 12.9 KB
Newer Older
juhasch's avatar
juhasch committed
1 2 3 4 5 6 7 8 9
#!/usr/bin/env python
# encoding: utf-8
"""
Created by Sean Nelson on 2009-10-14.
Copyright 2009 Sean Nelson <audiohacked@gmail.com>

Overhauled and edited by Garrett Berg on 2011- 1 - 22
Copyright 2011 Garrett Berg <cloudform511@gmail.com>

10 11 12
Updated and made Python3 compatible by Juergen Hasch, 20160501
Copyright 2016 Juergen Hasch <python@elbonia.de>

juhasch's avatar
juhasch committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
This file is part of pyBusPirate.

pyBusPirate is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

pyBusPirate is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with pyBusPirate.  If not, see <http://www.gnu.org/licenses/>.
"""

29 30 31 32 33 34 35 36 37 38 39
from .BBIO_base import BBIO_base, BPError, ProtocolError

SPI_speed = { '30kHz' : 0b000,
              '125kHz': 0b001,
              '250kHz': 0b010,
              '1MHz'  : 0b011,
              '2MHz'  : 0b100,
              '2.6MHz': 0b101,
              '4MHz'  : 0b110,
              '8MHz'  : 0b111}

Jürgen Hasch's avatar
Jürgen Hasch committed
40 41 42 43 44
CFG_SAMPLE = 0x01
CFG_CLK_EDGE = 0x02
CFG_IDLE = 0x04
CFG_PUSH_PULL = 0x08

45 46 47 48 49
PIN_CS = 1
PIN_AUX = 2
PIN_PULLUP = 4
PIN_POWER = 8

50 51

class SPI(BBIO_base):
Jürgen Hasch's avatar
Jürgen Hasch committed
52
    def __init__(self, portname='', speed=115200, timeout=0.1, connect=True):
53 54
        """ Provide high-speed access to the Bus Pirate SPI hardware

Jürgen Hasch's avatar
Jürgen Hasch committed
55 56 57 58 59 60 61 62 63
        Parameters
        ----------
        portname : str
            Name of comport (/dev/bus_pirate or COM3)
        speed : int
            Communication speed, use default of 115200
        timeout : int
            Timeout in s to wait for reply

64 65 66
        Example
        -------
        >>> spi = SPI()
Jürgen Hasch's avatar
Jürgen Hasch committed
67 68
        >>> spi.config(CFG_PUSH_PULL | CFG_IDLE)
        >>> spi.speed = '1MHz'
69 70 71 72 73
        >>> spi.cs = True
        >>> data = spi.transfer( [0x82, 0x00])
        >>> spi.cs = False
        """
        super().__init__()
74 75 76 77 78 79 80
        if connect is True:       
            self.connect(portname, speed, timeout)       
            self.enter()
        self._config = None
        self._speed = None
        self._cs = None
        self._pins = None
juhasch's avatar
juhasch committed
81

82
    def enter(self):
83
        """ Enter raw SPI mode
juhasch's avatar
juhasch committed
84

85 86
        Once in raw bitbang mode, send 0x01 to enter raw SPI mode. The Bus Pirate responds 'SPIx',
        where x is the raw SPI protocol version (currently 1). Get the version string at any time by sending 0x01 again.
juhasch's avatar
juhasch committed
87

88 89 90 91
        Raises
        ------
        BPError
            Could not enter SPI mode
juhasch's avatar
juhasch committed
92

93
        """
94 95
        if self.mode == 'spi':
            return
Jürgen Hasch's avatar
Jürgen Hasch committed
96 97 98
        if self.mode != 'bb':
           super(SPI, self).enter()

99 100 101 102 103 104
        self.write(0x01)
        if self.response(4) == "SPI1":
            self.mode = 'spi'
            return
        raise BPError('Could not enter SPI mode')

Jürgen Hasch's avatar
Jürgen Hasch committed
105 106 107 108 109 110 111
    @property
    def modestring(self):
        """ Return mode version string """
        self.write(0x01)
        self.timeout(self.minDelay * 10)
        return self.response(4)

112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    @property
    def pins(self):
        return self._pins

    @pins.setter
    def pins(self, cfg):
        """ Configure peripherals

        Parameters
        ----------
        cfg: int
            Pin configuration 0000wxyz
            w=power, x=pull-ups, y=AUX, z=CS

        Notes
        -----
        Enable (1) and disable (0) Bus Pirate peripherals and pins. Bit w enables the power supplies, bit x toggles
        the on-board pull-up resistors, y sets the state of the auxiliary pin, and z sets the chip select pin.
        Features not present in a specific hardware version are ignored. Bus Pirate responds 0x01 on success.

        * CS pin always follows the current HiZ pin configuration.
        * AUX is always a normal pin output (0=GND, 1=3.3volts).
        """
        self.write(0x40 | (cfg & 0x0f))
        if self.response(1, True) != b'\x01':
            raise ValueError("Could not set SPI pins")
        self._pins = cfg

140 141
    @property
    def config(self):
142
        return self._config
143 144 145 146

    @config.setter
    def config(self, cfg):
        """ Set SPI configuration
147

148 149 150 151 152 153 154 155 156 157
        This command configures the SPI settings. Options and start-up defaults are the same as the user terminal
        SPI mode. w= pin output HiZ(0)/3.3v(1), x=CKP clock idle phase (low=0), y=CKE clock edge (active to idle=1),
        z=SMP sample time (middle=0). The Bus Pirate responds 0x01 on success.

        Default raw SPI startup condition is 0010. HiZ mode configuration applies to the SPI pins and the CS pin,
        but not the AUX pin. See the PIC24FJ64GA002 datasheet and the SPI section[PDF] of the PIC24 family manual
        for more about the SPI configuration settings.

        Parameters
        ----------
Jürgen Hasch's avatar
Jürgen Hasch committed
158 159
        cfg : byte
                CFG_SAMPLE: sample time (0 = middle)
160
                CFG_CLK_EDGE: clock edge (1 = active to idle)
Jürgen Hasch's avatar
Jürgen Hasch committed
161 162 163 164 165 166
                CFG_IDLE: clock idle phase (0 = low)
                CFG_PUSH_PULL: pin output (0 = HiZ, 1 = push-pull)

        Examples
        -------
        >>> spi.config = CFG_PUSH_PULL | CFG_IDLE
167 168 169 170 171

        Raises
        ------
        ProtocolError
            If configuration could not be set
172
        """
173
        self.write(0x80 | cfg)
174 175
        self.timeout(self.minDelay)
        if self.response(1, True) != b'\x01':
176
            raise ValueError("Could not set SPI configuration")
177
        self._config = cfg
178 179

    def transfer(self, txdata):
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
        """ Bulk SPI transfer, send/read 1-16 bytes

        Bulk SPI allows direct byte reads and writes. The Bus Pirate expects xxxx+1 data bytes. Up to 16 data bytes
        can be sent at once, each returns a byte read from the SPI bus during the write.

        Note that 0000 indicates 1 byte because there's no reason to send 0. BP replies 0x01 to the bulk SPI command,
        and returns the value read from SPI after each data byte write.

        The way it goes together:

        The upper 4 bit of the command byte are the bulk read command (0001xxxx)
        xxxx = the number of bytes to read. 0000=1, 0001=2, etc, up to 1111=16
        If we want to read (0001) four bytes (0011=3=read 4) the full command is 00010011 (0001 + 0011 ).
        Convert from binary to hex and it is 0x13


        Parameters
        ----------
        txdata: List of bytes
            Data to send (1-16 bytes)

        Returns
        -------
            List containing received data

        Raises
        ------
        ValueError
            If more than 16 bytes are requested to be sent
209 210
        """
        length = len(txdata)
211 212
        if length > 16:
            ValueError('A maximum of 16 bytes can be sent')
213 214 215
        self.write(0x10 + length-1)
        for data in txdata:
            self.write(data)
216
        if self.response(1, True) != b'\x01':
217
            raise ValueError("Could not transfer SPI data")
218 219
        rxdata = self.response(length, True)
        return rxdata
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281

    def write_then_read(self, numtx, numrx, txdata, cs = True):
        """ Write then read

        This command was developed to help speed ROM programming with Flashrom. It might be helpful for a lot of common
        SPI operations. It enables chip select, writes 0-4096 bytes, reads 0-4096 bytes, then disables chip select.

        All data for this command can be sent at once, and it will be buffered in the Bus Pirate. The write and read
        operations happen all at once, and the read data is buffered. At the end of the operation, the read data is
        returned from the buffer. The goal is to meet the stringent timing requirements of some ROM chips by buffering
        everything instead of letting the serial port delay things.

        Write then read command format:
        +---------------|--------------------------------|-------------------------------|-----------------------------+
        |command (1byte)| number of write bytes (2bytes) | number of read bytes (2bytes) | bytes to write (0-4096bytes)|
        +---------------|--------------------------------|-------------------------------|-----------------------------+

        Return data format:
        +----------------------|-----------------------------------+
        | success/0x01 (1byte) | bytes read from SPI (0-4096bytes) |
        +----------------------|-----------------------------------+

        1. First send the write then read command (00000100)
        2. The next two bytes (High8/Low8) set the number of bytes to write (0 to 4096)
        3. The next two bytes (h/l) set the number of bytes to read (0 to 4096)
        4. If the number of bytes to read or write are out of bounds, the Bus Pirate will return 0x00 now
        5. Now send the bytes to write. Bytes are buffered in the Bus Pirate, there is no acknowledgment that a byte is received.
        6. Now the Bus Pirate will write the bytes to SPI and read/return the requsted number of read bytes
        7. CS goes low, all write bytes are sent at once
        8. Read starts immediately, all bytes are put into a buffer at max SPI speed (no waiting for UART)
        9. At the end of the read, CS goes high
        10. The Bus Pirate now returns 0x01, success
        11. Finally, the buffered read bytes are returned via the serial port

        Except as described above, there is no acknowledgment that a byte is received.

        Parameters
        ----------
        numtx : int
            Number of bytes to write
        numrx : int
            Number of bytes to read
        txdata : list
            Data to send
        cs : bool
            Generate CS transitions (default=True)

        Raises
        ------
        ProtocolError
            If data could not be sent
        """
        if cs:
            self.write(0x04)
        else:
            self.write(0x05)
        self.write(numtx>>8 & 0xff)
        self.write(numtx & 0xff)
        self.write(numrx>>8 & 0xff)
        self.write(numrx & 0xff)
        for data in txdata:
            self.write(data)
282
        if self.response(1, True) != b'\x01':
283
            raise ProtocolError("Error transmitting data")
284

285 286
    @property
    def cs(self):
287
        """ Return chip select pin status """
288
        return self._cs
289

290 291
    @cs.setter
    def cs(self, value):
292
        """ Set chip select pin
293 294 295 296 297 298 299 300 301 302 303 304 305 306
        Parameters
        ----------
        value: bool
            Set CS high(False) or low(True) (i.e. active low CS)

        Raises
        ------
        ProtocolError
            If CS could not be set
        """
        if value:
            self.write(0x02)
        else:
            self.write(0x03)
307
        if self.response(1, True) != b'\x01':
308
            raise ProtocolError("CS could not be set")
Jürgen Hasch's avatar
Jürgen Hasch committed
309 310
        self._cs = value

311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
    @property
    def speed(self):
        return self._speed

    @speed.setter
    def speed(self, frequency):
        """ Set SPI bus speed

        Parameters
        ----------
        frequency : str
            SPI clock speed (30kHz, 125kHz, 250kHz, 1MHz, 2MHz, 2.6MHz, 4MHz, 8MHz)

        Raises
        ------
        ProtocolError
            If I2C speed could not be set
        """
        try:
            clock = SPI_speed[frequency]
        except KeyError:
            raise ValueError('Clock speed not supported')
        self.write(0x60 | clock)
Jürgen Hasch's avatar
Jürgen Hasch committed
334

335 336 337
        if self.response(1, True) != b'\x01':
            raise ProtocolError('Could not set SPI speed')
        self._speed = frequency
Jürgen Hasch's avatar
Jürgen Hasch committed
338

339 340
    def sniffer(self, cs):
        """ Sniff SPI traffic when CS low(10)/all(01) TODO
Jürgen Hasch's avatar
Jürgen Hasch committed
341

342 343 344 345 346 347 348 349 350 351 352 353 354 355
        Parameters
        ----------
        cs : Bool
            True: Capture when CS is low
            False: Capture all

        Notes
        -----
        0000 11XX
        The SPI sniffer is implemented in hardware and should work up to 10MHz. It follows the configuration settings
        you entered for SPI mode. The sniffer can read all traffic, or filter by the state of the CS pin.

            [/] - CS enable/disable
            xy - escape character (\\) precedes two byte values X (MOSI pin) and Y (MISO pin) (updated in v5.1)
Jürgen Hasch's avatar
Jürgen Hasch committed
356

357 358
        Sniffed traffic is encoded according to the table above. The two data bytes are escaped with the '\' character
        to help locate data in the stream.
Jürgen Hasch's avatar
Jürgen Hasch committed
359

360 361
        Send the SPI sniffer command to start the sniffer, the Bus Pirate responds 0x01 then sniffed data starts to
        flow. Send any byte to exit. Bus Pirate responds 0x01 on exit. (0x01 reply location was changed in v5.8)
Jürgen Hasch's avatar
Jürgen Hasch committed
362

363
        If the sniffer can't keep with the SPI data, the MODE LED turns off and the sniff is aborted. (new in v5.1)
Jürgen Hasch's avatar
Jürgen Hasch committed
364

365 366 367 368 369 370 371 372 373 374 375 376
        The sniffer follows the output clock edge and output polarity settings of the SPI mode, but not the input
        sample phase.

        More detailed notes on the SPI sniffer in the SPI user terminal documentation.
        """
        if cs is True:
            cmd = 0x0e
        else:
            cmd = 0x0d
        self.write(cmd)
        if self.response(1, True) != b'\x01':
            raise ProtocolError('Could not set SPI sniff mode')