Bus Pirate
2018-01-06 (updated 2019-05-04)The Bus Pirate is a small USB device that can speak several bus protocols, like I2C and SPI.
The specific version I'm using below is TOL-12942 (v3.6a), with the following info blurb:
1 2 3 4 | Bus Pirate v3b
Firmware v5.10 (r559) Bootloader v4.4
DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
http://dangerousprototypes.com
|
Intial setup
To have the device created with the appropriate permissions and also get an automatic /dev/buspirate
symlink, create /etc/udev/rules.d/98-buspirate.rules
with the contents
1 | SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666", SYMLINK+="buspirate"
|
To connect to it once it's plugged in, just run
1 | screen -c /dev/null /dev/buspirate 115200
|
Typing ?
followed by the enter key will output the help message, followed by a prompt.
There's support for some readline-like features. The left and right arrow keys move within the currently entered command. The up and down arrow keys move through command history.
Surprisingly, backspace acts like the delete key instead of backspace. That is, it deletes the character you're currently on, not the one to the left. To really backspace, press the left arrow key and then backspace.
When the device is unplugged, the screen session will automatically close.
You can also close it by killing the window in the usual way (^A k
), but this will not reset the device.
Some useful reading:
Pull-up resistors
The way that the on-board pull-up resistors are handled on the Bus Pirate is a tad unintuitive.
Some buses need pull-ups on their various lines, and it's very convenient for the Bus Pirate to be able to provide them, but it's not enough to hit P
to turn them on.
There's also the matter of the VPU
pin, which doesn't provide the pull-up voltage!
Because the Bus Pirate doesn't know what voltage your circuit is operating on (and maybe you need the lines to be pulled to something weird, like 1 V), you're expected to feed the pull-up voltage into the VPU
pin, and that will then be distributed to the appropriate lines.
PWM
It's not possible to generate a PWM signal from the default HiZ
mode, but it's easy to change to the 1-WIRE
mode using m
, since it has no options.
Connect brown (GND
) and blue (AUX
) to whatever needs the signal.
Then hit g
to turn PWM on:
1 2 3 4 5 6 7 | 1-WIRE>g
1KHz-4,000KHz PWM
Frequency in KHz
(50)>
Duty cycle in %
(50)>
PWM active
|
and again to turn it off:
1 2 | 1-WIRE>g
PWM disabled
|
It's only possible to enter integer values for both the frequency and the duty cycle. The default is 50 kHz at 50%.
I2C
I2C is a 2-wire protocol, with a clock line (SCL
) and a data line (SDA
).
The only option is the clock speed:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | HiZ>m
1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. LCD
9. DIO
x. exit(without change)
(1)>4
Set speed:
1. ~5KHz
2. ~50KHz
3. ~100KHz
4. ~400KHz
(1)>
Ready
I2C>v
Pinstates:
1.(BR) 2.(RD) 3.(OR) 4.(YW) 5.(GN) 6.(BL) 7.(PU) 8.(GR) 9.(WT) 0.(Blk)
GND 3.3V 5.0V ADC VPU AUX SCL SDA - -
P P P I I I I I I I
GND 0.00V 0.00V 0.00V 0.00V L L L L L
|
To connect a device, hook up brown (GND
) to GND
, orange/red (5.0V
/3.3V
) to VCC
, purple (SCL
) to SCL
, and grey (SDA
) to SDA
.
Then power it up:
1 2 | I2C>W
Power supplies ON
|
Unless your device has the required pull-up resistors on both lines, you'll need to connect green (VPU
) to the power source and turn on the internal pull-ups:
1 2 | I2C>P
Pull-up resistors ON
|
The first byte that the master sends is the 7-bit slave address, followed by a single bit indicating write (0) or read (1) mode. The former is the address multiplied by 2, and the latter is the same plus 1. Instead of doing the multiplication ourselves, we can use the Bus Pirate to figure out what the special byte should be for a given address and mode. Suppose we want to talk to the device with address 0x12:
1 2 | I2C>=0x12
0x12 = 18 = 0b00010010
|
Since
1 2 3 4 | I2C>=0b000100100
0x24 = 36 = 0b00100100
I2C>=0b000100101
0x25 = 37 = 0b00100101
|
we should send 0x24 to write and 0x25 to read. It's also possible to use the address search macro:
1 2 3 | I2C>(1)
Searching I2C address space. Found devices at:
0x24(0x12 W) 0x25(0x12 R)
|
LCD character display
Here we play with a generic LCD character display module with an I2C backpack.
It's a 5V device and has its own pull-up resistors.
The documented address is 0x27, so to write to it we need to send 0x4e. We can check that this works:
1 2 3 4 | I2C>[0x4e]
I2C START BIT
WRITE: 0x4E ACK
I2C STOP BIT
|
Note that the response is an ACK and not a NACK. We can also send 0x4f to read:
1 2 3 4 5 6 | I2C>[0x4f r]
I2C START BIT
WRITE: 0x4F ACK
READ: 0xF7
NACK
I2C STOP BIT
|
In this case, we got back 0xf7.
Now the fun begins. Everybody seems to insist on using a library to talk to the display, but I want to speak to it in raw I2C. I'm pretty sure the display itself is HD44780-compatible (datasheet). As far as I can tell, the "I2C backpack" is little more than a PCF8574T 8-bit port expander that reads in 8 bits over I2C and outputs them on 8 pins. The question is which pins of the display those are connected to, and in which order.
According to a tutorial, the bits are mapped according to
1 2 3 4 5 6 7 8 | 7654
||||/---- backlight
|||||/--- E
||||||/-- RW
|||||||/- RS
||||||||
vvvvvvvv
76543210
|
Clearly, we'll be using the 4-bit mode. We want the backlight to always be on, and we're not interested in reading, so those bits can be fixed. To send 4 bits, we need to set the enable bit high, and then low. Thus, to send a nibble to the command register, we must send
1 2 | 0bXXXX1100 0xXc
0bXXXX1000 0xX8
|
and to send a nibble to the data register, we must send
1 2 | 0bXXXX1101 0xXd
0bXXXX1001 0xX9
|
The display initializes itself in 8-bit mode, and it looks like the 4 low bits are wired high in this backpack. Until we switch to 4-bit mode, this limits us to sending command and data bytes of the form 0xXf. For example, we can send 0x0f to turn on the display with a blinking cursor, and then 0x3f to write a question mark:
1 | [0x4e 0x0c 0x08 0x3d 0x39]
|
To change to 4-bit mode, we need to send 0x2X, which in our case will have to be 0x2f:
1 | [0x4e 0x2c 0x28]
|
This has the side effect of requesting the number of lines to be 2 and the symbol size to be 10 dots high, which reduces the duty cycle and makes the text look very dim. (I think this combination is technically invalid, so it doesn't actually make the symbols bigger, as if we had sent 0x2b instead of 0x2f.) The display gets so dim, in fact, that I wasted several hours trying to figure out why it refused to work after switching to 4-bit mode. The next step is then to send 0x20, which is what we really want:
1 | [0x4e 0x2c 0x28 0x0c 0x08]
|
Now we can do as we wish.
For example, we can clear the display with
1 | [0x4e 0x0c 0x08 0x1c 0x18]
|
and then write some digits with
1 | [0x4e 0x3d 0x39 0x0d 0x09 0x3d 0x39 0x1d 0x19 0x3d 0x39 0x2d 0x29 0x3d 0x39 0x4d 0x49]
|
Thus, we have arrived at the initialization sequence
1 | [0x4e 0x2c 0x28 0x2c 0x28 0x0c 0x08 0x0c 0x08 0xfc 0xf8]
|
This isn't very robust, probably because we're being sloppy with the timing.
Nevertheless, we can now do this:
1 | [0x4e 0x5d 0x59 0x9d 0x99 0x6d 0x69 0x1d 0x19 0x7d 0x79 0x2d 0x29 0x7d 0x79 0x2d 0x29 0x2d 0x29 0x1d 0x19]
|
OLED screen
Here we try to get a tiny Kuman OLED display to light up.
It claims to be OK with 5 V, but one of the Amazon comments says it needs 3.3 V. It appears to provide its own pull-ups.
On the back, it looks like the address 0x78 is selected. The address is actually 0x3C, and the first byte will be 0x78 when writing.
1 2 3 4 | I2C>[0x78]
I2C START BIT
WRITE: 0x78 ACK
I2C STOP BIT
|
I think this board has an SSD1306 (datasheet). It's really easy to communicate with it over I2C. We have 3 options for sending commands and data:
- send 0x80, followed by a command byte;
- send 0xc0, followed by a data byte; or
- send 0x40, followed by data bytes until the end of the transmission.
Before the display will show anything, we need to turn on the charge pump:
1 | [0x78 0x80 0x8d 0x80 0x14]
|
Then we can turn on the display:
1 | [0x78 0x80 0xaf]
|
The board appears to retain the displayed data for a while, so at this point you may see whatever was displayed on the screen last. However, it's more likely that you'll see random noise.
Some useful commands are:
Sequence | Effect |
---|---|
0x8d 0x14 | Enable charge pump |
0x8d 0x10 | Disable charge pump |
0xaf | Turn on display |
0xae | Turn off display |
0xa7 | Inverted display |
0xa6 | Regular (non-inverted) display |
0xa5 | Turn on all pixels |
0xa4 | Don't turn on all pixels |
0x81 0xXX | Set brightness |
For example, you can flash the screen by turning on all the pixels:
1 | [0x78 0x80 0xa5 %:200 0x80 0xa4]
|
or by inverting all the pixels
1 | [0x78 0x80 0xa7 %:200 0x80 0xa6]
|
The display is 128 pixels wide and 64 pixels tall, for a total of 8192 pixels. In this model, the top 16 pixels are yellow and the bottom 48 are blue, with a 2-pixel gap between them. The pixels are split into 128 vertical columns and 8 horizontal pages. Each page is 8 pixels tall, and every byte that is sent is written to the current column in the current page. As far as I can tell, the display is upside-down, so data is written bottom-to-top, right-to-left. (Conversely, the display is right side up, but the yellow stripe is actually at the bottom.)
By default, page addressing is used, so when we reach the last column, we wrap back around to the first column, but stay in the same page. We can set the current page by using the commands 0xb0 to 0xb7. Changing the page doesn't reset the column. We can also set the current column by using the commands 0x00 to 0x0f for the lower nibble and 0x10 to 0x1f for the upper nibble. For example, to go to the middle of the second-last page, we can use
1 | [0x78 0x80 0xb6 0x80 0x00 0x80 0x14]
|
Go back to the beginning with
1 | [0x78 0x80 0xb0 0x80 0x00 0x80 0x10]
|
Writing data at the current position is straightforward:
1 | [0x78 0x40 ...]
|
Just fill in the desired bit patterns. For example, to clear the current page, use
1 | [0x78 0x40 0x00:128]
|
To clear the whole screen, use the unwieldy sequence
1 2 3 4 5 6 7 8 | [0x78 0x80 0xb0] [0x78 0x40 0x00:128]
[0x78 0x80 0xb1] [0x78 0x40 0x00:128]
[0x78 0x80 0xb2] [0x78 0x40 0x00:128]
[0x78 0x80 0xb3] [0x78 0x40 0x00:128]
[0x78 0x80 0xb4] [0x78 0x40 0x00:128]
[0x78 0x80 0xb5] [0x78 0x40 0x00:128]
[0x78 0x80 0xb6] [0x78 0x40 0x00:128]
[0x78 0x80 0xb7] [0x78 0x40 0x00:128]
|
Starting from a stock image, we make a low-resolution version. Then (assuming Pillow and pyserial are installed), this script will use the binary mode of the Bus Pirate to write the picture straight to the display:
RTC module
I have a generic RTC module based on the DS1307 (datasheet). Not sure why the module has two sets of contacts, or what the purpose of the AT24C32 EEPROM chip is.
DS1307
The DS1307 wants 5 V, so we use orange (5.0V
).
It seems to have its own pull-up resistors.
The data sheet says it's only rated for 100 kHz.
The slave address is 0x68, so we'll try reading using the magic number 0xd1. There are 64 bytes of RAM ("registers": 3 time, 4 date, 1 control, 56 general purpose), and the reads should wrap back to the beginning (0x00) after the last one (0x3f). We have no idea what the pointer is set to, but if we do 64 reads, we should get back everything:
1 | [0xd1 r:64]
|
gets us
1 2 3 4 5 6 7 8 | 0x99 0x00 0x00 0x01 0x01 0x01 0x00 0xB3
0xFD 0xEB 0xFD 0x99 0xFD 0xA9 0xDC 0xE7
0xF2 0x86 0x30 0x86 0xEF 0x45 0xD8 0xD7
0xEF 0x60 0xFC 0xBA 0x3E 0x99 0xB8 0x95
0xF5 0xBE 0xFD 0xD8 0xE7 0xCC 0xD4 0xB5
0xD3 0x1F 0xE3 0xF3 0xEF 0x07 0x24 0xC1
0x2D 0xB9 0xF9 0xB4 0xF2 0x8E 0xEF 0x3A
0xBA 0xBC 0x70 0x8D 0xE3 0xF3 0x5B 0x45
|
(xx00-01-01 Mon 00:00:19). Confusingly, if we read 32 more bytes and then another 32, we get
1 2 3 4 5 6 7 8 9 | 0x25 0x00 0x01 0x01 0x01 0x00 0xB3
0xFD 0xEB 0xFD 0x99 0xFD 0xA9 0xDC 0xE7
0xF2 0x86 0x30 0x86 0xEF 0x00 0xD8 0xD7
0xEF 0x60 0xFC 0xBA 0x3E 0x99 0xB8 0x95
0xF5 0xFD 0xD8 0xE7 0xCC 0xD4 0xB5
0xD3 0x1F 0xE3 0xF3 0xEF 0x07 0x24 0xC1
0x2D 0xB9 0xF9 0xB4 0xF2 0x8E 0xEF 0x3A
0xBA 0xBC 0x70 0x8D 0xE3 0xF3 0x5B 0x45
0x98 0x26
|
(xx00-01-01 Mon 00:25:??, then ??:26:18). The next 64 are then
1 2 3 4 5 6 7 8 9 | 0x01 0x01 0x01 0x00 0xB3
0xFD 0xEB 0xFD 0x99 0xFD 0xA9 0xDC 0xE7
0xF2 0x86 0x30 0x86 0xEF 0x45 0xD8 0xD7
0xEF 0x60 0xFC 0xBA 0x3E 0x99 0xB8 0x95
0xF5 0xBE 0xFD 0xD8 0xE7 0xCC 0xD4 0xB5
0xD3 0x1F 0xE3 0xF3 0xEF 0x07 0x24 0xC1
0x2D 0xB9 0xF9 0xB4 0xF2 0x8E 0xEF 0x3A
0xBA 0xBC 0x70 0x8D 0xE3 0xF3 0x5B 0x45
0x87 0x21 0x01
|
(xx00-01-01 Mon 01:21:07). It looks like a single byte goes missing after each read sequence.
We can also rewind to the beginning by writing zero:
1 | [0xd0 0x00]
|
Then the first 8 bytes are
1 | 0xC8 0x54 0x03 0x01 0x01 0x01 0x00 0xB3
|
(xx00-01-01 Mon 03:54:48).
The battery was successfully keeping the time going while the device was unplugged.
The fact that the time's been going up doesn't make sense at all, since the CH
(clock halt) bit has been set all along!
When power is first applied, the timekeeping registers should be set to
1 | 0x80 0x00 0x00 0x01 0x01 0x01 0x00 0x03
|
This is almost consistent with what I had, except:
- The
CH
bit is clearly set in the first (seconds) byte, but the clock is going. - The upper nibble of the control bit is B instead of 0. That shouldn't even be possible, since bits 5 and 6 should only be zero.
I found an older (2006) datasheet that says:
Note that the initial power-on state of all registers is not defined. Therefore, it is important to enable the oscillator (CH bit = 0) during initial configuration.
I suppose anything is possible in that case.
Let's start from a clean slate.
1 | [0xd0 0x00 0x88 0x07 0x06 0x05 0x04 0x03 0x12 0x00]
|
(xx12-03-04 Fri 06:07:08). Then we read back
1 | 0xA9 0x07 0x06 0x05 0x04 0x03 0x12 0x20
|
(xx12-03-04 Fri 06:07:41). Looks like bit 5 of the control register is just stuck at 1, even though both datasheets (10 years apart) say "Always reads back as 0.". Except if I do
1 | [0xd0 0x00 0x08 0x07 0x06 0x05 0x04 0x03 0x12 0x00]
|
to clear the CH
bit, I get back
1 | 0x21 0x07 0x06 0x05 0x04 0x03 0x12 0x00
|
where the CH
bit is indeed cleared (and the time is still ticking), but bit 5 of the control register is also cleared.
This is repeatable: the CH
bit always sets bit 5 of the control register, but doesn't stop the clock.
Let's time travel to the end of a century to check that everything rolls over as it should:
1 | [0xd0 0x00 0x59 0x59 0x23 0x07 0x31 0x12 0x99 0x00]
|
(xx99-12-31 Sun 23:59:59). Moments later, I get back
1 | 0x08 0x59 0x23 0x07 0x31 0x12 0x99 0x00
|
(xx99-12-31 Sun 23:59:08). What? If I let it go for a bit, it does eventually get to
1 | 0x12 0x01 0x00 0x01 0x01 0x01 0x00 0x00
|
(xx00-01-01 Mon 00:01:12), as it should.
Keeping a closer eye on it, I see that it counts from 59 seconds up to 63 seconds, after which it resets to 00 seconds, counts up to 59 seconds, and then finally does what I expect.
Setting CH
doesn't change this behaviour.
However, all of
1 | [0xd0 0x00 0x58 0x59 0x23 0x07 0x31 0x12 0x99 0x00]
|
(xx99-12-31 Sun 23:59:58),
1 | [0xd0 0x00 0x59 0x58 0x23 0x07 0x31 0x12 0x99 0x00]
|
(xx99-12-31 Sun 23:58:59) and even
1 | [0xd0 0x00 0x59 0x59 0x23 0x07 0x31 0x12 0x98 0x00]
|
(xx98-12-31 Sun 23:59:59) have the correct behaviour. As a final check, I'll try
1 | [0xd0 0x00 0x00 0x59 0x23 0x07 0x31 0x12 0x99 0x00]
|
(xx99-12-31 Sun 23:59:00) followed by
1 | [0xd0 0x00 0x59]
|
(:59). It still bugs out.
As a final test of the timekeeping, we'll set the date and time to right now:
1 | [0xd0 0x00 0x40 0x49 0x22 0x07 0x14 0x01 0x18 0x00]
|
(xx18-01-14 Sun 22:49:40). Then we let it run with power for a couple of minutes, without power for a bit longer, and with power again for a couple of minutes. The result is
1 | 0x25 0x03 0x23 0x07 0x14 0x01 0x18 0x00
|
(xx18-01-14 Sun 23:03:25), which is correct.
What happens if I remove all power from the module, including the backup battery? I'll first set it to a known state with
1 | [0xd0 0x00 0x08 0x07 0x06 0x05 0x04 0x03 0x12 0x00]
|
As expected, it reset back to the initial conditions:
1 | 0xB9 0x00 0x00 0x01 0x01 0x01 0x00 0xB3
|
The pins on the other side of the module seem to give me access to the same I2C bus.
We also have the pins BAT
, DS
, and SQ
.
The first of these is at the same potential as the battery pin of the DS1307, so that an external circuit can keep track of its charge.
The module I'm using is probably related to this RTC module, so DS
is supposed to be the output of the temperature sensor, but it's just pulled high to VCC
.
Finally, SQ
is supposed to be controlled by the SQW/OUT
pin of the DS1307.
The simple mode of operation for the SQW/OUT
pin is to set it to bit 7 of the control register when bit 4 is zero.
Indeed, with the control register set to 0x00, SQ
is nearly GND
.
If we set it to 0x80, it's pulled up to VCC
.
The pin on the chip is open drain, so the module must be pulling it up for us.
Finally, 0x10 through 0x13 successfully set it to square waves of varying frequency.
For example,
1 | [0xd0 0x07 0x12]
|
24C32
This module also has a 24C32 EEPROM chip (datasheet), presumably for us to use as we desire. Thankfully, this also runs at 5 V, or else it would've been fried by now. This should also run at 400 kHz, but to be nice to the DS1307, I'll run it at 100 kHz. It's not clear from the module what the address of this chip is wired to, but the address search macro reveals that it's
1 | 0xA0(0x50 W) 0xA1(0x50 R)
|
That means that the addressing bits (A0
, A1
, and A2
) are all set to zero.
Let's dive in and read some data!
1 | [0xa1 r:32]
|
Not very exciting:
1 2 3 4 | 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
|
There are supposed to be 4 KB of addressable data. The first 512 bytes are high endurance, and the remaining 3584 bytes are not. Apparently the address won't roll over from the end (0x07ff; should be 0x0fff?) to the beginning (0x0000) automatically, so we have to watch out for that.
Writing a single byte is easy. Let's put something at the beginning, then rewind, and read it back:
1 | [0xa0 0x00 0x00 0x12 [0xa0 0x00 0x00 [0xa1 r:8]
|
That didn't work. Maybe it doesn't like that repeated start. Let's keep the one for the read:
1 | [0xa0 0x00 0x00 0x12] [0xa0 0x00 0x00 [0xa1 r:8]
|
That's better:
1 | 0x12 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
|
It seems that there's a 64-byte cache that is used as a ring buffer, so it's not possible to write more than 64 bytes in one go. Sounds easy:
1 | [0xa0 0x00 0x01 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10]
|
Then
1 | [0xa0 0x00 0x00 [0xa1 r:18]
|
gives us
1 | 0x12 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F 0x10 0xFF 0xFF
|
Excellent!
I2C sniffing
Trying to sniff an I2C bus that just has [0xa0+0x00+0x25+]
being sent (between an ATmega328P micro and a 24C32 EEPROM) once per second.
At 100 kHz, I get
1 2 3 4 | I2C>(2)
Sniffer
Any key to exit
[0xA0+0x00+][0xA0+0x00+][0xA0+0x00+][0xA0+0x00+]
|
which is missing the last byte. At 50 kHz, I get the full sequence
1 | [0xA0+0x00+0x25+][0xA0+0x00+0x25+][0xA0+0x00+0x25+][0xA0+0x00+0x25+]
|
This is independent of the speed selected for the I2C mode. It seems to work fairly reliably (for this small test case) up to around 70 kHz, and starts to get garbled at around 80 kHz. At 400 kHz, it's hopeless:
1 2 3 4 | I2C>(2)
Sniffer
Any key to exit
][][0x80-[][][][]][][]][][][0x80-[]][
|
Interestingly, the binary sniffer is robust up to about 125 kHz (but not beyond), which makes it suitable for sniffing 100 kHz I2C traffic.
SPI
SPI is a 4-wire protocol, with a clock line (SCLK
), two data lines (MOSI
, MISO
), and a slave select line (SS
).
There are many different ways to do SPI, so the Bus Pirate offers quite a few options:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | HiZ>m
1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. LCD
9. DIO
x. exit(without change)
(1)>5
Set speed:
1. 30KHz
2. 125KHz
3. 250KHz
4. 1MHz
(1)>
Clock polarity:
1. Idle low *default
2. Idle high
(1)>
Output clock edge:
1. Idle to active
2. Active to idle *default
(2)>
Input sample phase:
1. Middle *default
2. End
(1)>
CS:
1. CS
2. /CS *default
(2)>
Select output type:
1. Open drain (H=Hi-Z, L=GND)
2. Normal (H=3.3V, L=GND)
(1)>
Ready
SPI>v
Pinstates:
1.(BR) 2.(RD) 3.(OR) 4.(YW) 5.(GN) 6.(BL) 7.(PU) 8.(GR) 9.(WT) 0.(Blk)
GND 3.3V 5.0V ADC VPU AUX CLK MOSI CS MISO
P P P I I I O O O I
GND 0.00V 0.00V 0.00V 0.00V L L L L L
|
To connect a device, hook up brown (GND
) to GND
, orange/red (5.0V
/3.3V
) to VCC
, purple (CLK
) to SCLK
, white (CS
) to SS
, grey (MOSI
) to MOSI
, and black (MISO
) to MISO
.
Then power it up:
1 2 | I2C>W
Power supplies ON
|
SPI can be run without pull-up or pull-down resistors (though it seems to be common to pull MISO
a little so that it's not floating when no slaves are writing).
I'm not sure why the default is open drain or why there's no 5 V option.
SN74HC595
The SN74HC595 (datasheet) is a shift register.
It isn't really an SPI device, but we can still pretend it's one.
If we treat SRCLK
as SCLK
and SER
as MOSI
, then we can write bits as usual.
The only tricky part is RCLK
, which actually propagates the written bits to the outputs.
Ideally, I'd just send the opposite of SRCLK
to it to update after each bit, but I'm too lazy to set that up, so I'll simply toggle SRCLK
manually.
I'll pull OE
low and SRCLR
high permanently.
On the Bus Pirate side, I've chosen all the defaults, except "Normal" instead of "Open drain" operation.
If I do a simple
1 2 | SPI>0xff
WRITE: 0xFF
|
and then toggle RCLK
, all 8 outputs are high.
Then
1 2 | SPI>0x00
WRITE: 0x00
|
and another toggle of RCLK
sets them all low.
I can get fancy with
1 2 | SPI>0x55
WRITE: 0x55
|
in which case the odd outputs (A, C, E, G) are on and the even ones (B, D, F, H) are off. Well that's pretty boring.
25X40AL
The 25X40AL (datasheet) is a 4 Mb flash memory chip.
That's 512 kB.
This is a proper SPI device, although it supports a non-standard mode where MOSI
is used to send data to the master.
The one I'm using was taken from a hard drive controller board and presumably has data on it, so we'll see how that goes. This thing wants no more than 3.6 V, so we'll give it 3.3 V. The write protect and hold pins will be pulled high permanently.
For MISO
(DO
on the chip), we have:
Data is shifted out on the falling edge of the Serial Clock (CLK) input pin.
For MOSI
(DIO
on the chip), we have:
Data is latched on the rising edge of the Serial Clock (CLK) input pin.
It therefore makes sense that it doesn't care about clock polarity:
Both SPI bus operation Modes 0 (0,0) and 3 (1,1) are supported.
If we choose mode 0, the clock idles low. I think that means that we want the defaults "Active to idle" for "Output clock edge" and "Middle" for "Input sample phase". The default chip select option is fine (/CS), but we need to choose "Normal" for the output type instead of "Open drain".
It looks like we get something if we ask for the JEDEC ID:
1 2 | SPI>[0x9f r:3]
READ: 0xEF 0x30 0x13
|
According to the spec sheet, the first byte indicates "Winbond Serial Flash" and the last two indicate "W25X40AL", which is indeed the correct model.
The last byte seems to encode the capacity, as well: 2^19 = 512 * 1024
.
I can also get it to power down and then come back to life:
1 2 3 4 5 6 | SPI>[0xb9]
SPI>[0x9f r:3]
READ: 0x00 0x00 0x00
SPI>[0xab]
SPI>[0x9f r:3]
READ: 0xEF 0x30 0x13
|
It readily tells us its manufacturer and device IDs:
1 2 3 4 | SPI>[0x90 0x00:3 r:2]
READ: 0xEF 0x12
SPI>[0xab 0x00:3 r:1]
READ: 0x12
|
Here, 0xef means "Winbond Serial Flash" and 0x12 means "W25X40AL".
Now we can try reading some data, starting from the start:
1 | [0x03 0x00 0x00 0x00 r:64]
|
gives us
1 2 3 4 5 6 7 8 | 0x5A 0x04 0x00 0x00 0x89 0x0F 0x00 0x00
0x88 0x0F 0x00 0x00 0x20 0x00 0x00 0x00
0x00 0x10 0x00 0x00 0x00 0x10 0x00 0x00
0x02 0x0A 0x00 0x00 0x00 0x00 0x00 0xD9
0x6D 0x00 0x00 0xFB 0x03 0x1C 0x10 0xB5
0x02 0xE0 0x10 0xC9 0x04 0x3A 0x10 0xC3
0x04 0x2A 0xFA 0xD2 0x10 0xBD 0xF8 0xB5
0x0E 0x1C 0x05 0x1C 0x17 0x1C 0x00 0x24
|
Looks like a bunch of 32-bit little-endian integers, which seems sensible if it's storing parameters. As expected,
1 | [0x03 0x00 0x00 0x10 r:16]
|
gives us
1 2 | 0x00 0x10 0x00 0x00 0x00 0x10 0x00 0x00
0x02 0x0A 0x00 0x00 0x00 0x00 0x00 0xD9
|
In order to write, we need to set the WEL
flag:
1 2 3 4 5 | SPI>[0x05 r]
READ: 0x00
SPI>[0x06]
SPI>[0x05 r]
READ: 0x02
|
It looks like the 4 kB sector at 0x070000 is unprogrammed (all 0xff). In particular,
1 2 | SPI>[0x03 0x07 0x01 0x00 r:8]
READ: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
|
We can write into it:
1 | SPI>[0x02 0x07 0x01 0x03 0x01 0x02 0x03 0x04]
|
Then
1 2 | SPI>[0x03 0x07 0x01 0x00 r:8]
READ: 0xFF 0xFF 0xFF 0x01 0x02 0x03 0x04 0xFF
|
We can undo our work by erasing the entire sector:
1 2 3 4 | SPI>[0x06]
SPI>[0x20 0x07 0x00 0x00]
SPI>[0x03 0x07 0x01 0x00 r:8]
READ: 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
|
1-Wire
The 1-Wire bus is extremely simple. As its name suggests, it requires only a single data wire (in addition to a ground wire), but it allows for bidirectional communication. It can even have one device power another over the data wire by charging a small capacitor.
It's so simple that is has no settings:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | HiZ>m
1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. LCD
9. DIO
x. exit(without change)
(1)>2
1WIRE routines (C) 2000 Michael Pearce GNU GPL
Ready
1-WIRE>v
Pinstates:
1.(BR) 2.(RD) 3.(OR) 4.(YW) 5.(GN) 6.(BL) 7.(PU) 8.(GR) 9.(WT) 0.(Blk)
GND 3.3V 5.0V ADC VPU AUX - OWD - -
P P P I I I I I I I
GND 0.00V 0.00V 0.00V 0.00V L L L L L
|
Let's power it up:
1 2 3 4 | 1-WIRE>W
Power supplies ON
1-WIRE>P
Pull-up resistors ON
|
MagSafe 2 charger (85 W)
Thanks to some detective work by Ken Shirriff, I don't need to muck about trying to figure out how to talk to the charger. It supposedly has a DS2413 chip (datasheet) inside, which can be powered over the data line, so the charger doesn't need to be plugged into the wall for this. It does need a pull-up resistor, however. The chip is OK with 5 V, so I'll be using that.
The physical connection is not fun to make. The best solution I've found is to tape two jumper wires very near the charger pins to keep them from going all over the place, but without attempting to make contact. Then each time I've typed out a command, I can touch the pins using both hands, then hold that steady with one hand and use the other to send the command. Occasionally, it would get stuck in a working position on its own, but it refused to do so unless it was by its own initiative.
When there's no connection, we see
1 2 | 1-WIRE>[
BUS RESET Warning: *No device detected
|
When we accidentally short the pins, we see
1 2 | 1-WIRE>[
BUS RESET Warning: *Short or no pull-up
|
When things are good, we can send a read command to extract the contents of the 64-bit ROM:
1 2 3 4 | 1-WIRE>[ 0x33 r:8
BUS RESET OK
WRITE: 0x33
READ: 0x85 0xF4 0xC7 0xA7 0xC0 0x13 0xAA 0x97
|
Apparently the ID is interpreted backwards, so the bytes are
1 | 0x97 0xaa 0x13 0xc0 0xa7 0xc7 0xf4 0x85
|
In this case, the contents are as follows (verified against the System Report on a Mac):
Field | Data | Meaning | System Report value |
---|---|---|---|
CRC checksum | 0x97 | ||
Customer ID | 0xaa1 | 85 W | 0x0aa1 |
Customer data | 0x3c0 | ||
Serial number | 0xa7c7f4 | 0x00a7c7f4 | |
Family code | 0x85 | 0x0085 |
To access the PIO commands, we need to issue a skip ROM command (0xcc) first. We can check that both LEDs (green and orange) are off:
1 2 3 4 5 | 1-WIRE>[ 0xcc 0xf5 r
BUS RESET OK
WRITE: 0xCC
WRITE: 0xF5
READ: 0x5A
|
The first nibble is the complement of the second, so we can ignore it. The second nibble tells us the following:
Bit | Value |
---|---|
PIOA Pin State | 0 |
PIOA Output Latch State | 1 |
PIOB Pin State | 0 |
PIOB Output Latch State | 1 |
That is, both LEDs should be off.
We can try to turn on the first LED. We don't expect the 1-Wire bus to power the LEDs, so we plug the charger in, now being very careful not to touch the power pins. Then we ask politely:
1 2 3 4 5 6 7 | 1-WIRE>[ 0xcc 0x5a 0xfe 0x01 r:2
BUS RESET OK
WRITE: 0xCC
WRITE: 0x5A
WRITE: 0xFE
WRITE: 0x01
READ: 0xAA 0x78
|
The reason this is so complicated is that we must send the desired state in normal form (all ones except the two least significant bits), and then also in inverted form. Then we read a confirmation byte (0xaa) and the new state:
Bit | Value |
---|---|
PIOA Pin State | 0 |
PIOA Output Latch State | 0 |
PIOB Pin State | 0 |
PIOB Output Latch State | 1 |
Looks like we've successfully told the chip to turn on the LED, since the latch has changed state, but the pin is still low. Indeed, the LED is still off. The same thing happens if I try to turn on the other LED (or both of them), and another charger (also MagSafe 2, but 60 W) behaves the same way. I suspect that this is because the charger only outputs a small voltage with very limited current until it senses the correct load, but I haven't had the chance to test this.