Created
July 26, 2012 19:21
-
-
Save ChickenProp/3183960 to your computer and use it in GitHub Desktop.
Revisions
-
ChickenProp revised this gist
Jul 28, 2012 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -56,13 +56,15 @@ Now you can read the value of the button as the most significant bit from `GPIOA You *can* fix this by putting a [pull-up resistor](http://en.wikipedia.org/wiki/Pull-up_resistor) into your circuit; a 10 kΩ resistor between 3v3 and pin 28 will do the trick. But the MCP has pull-up resistors built-in, they just aren't enabled by default. To enable it for `GPA7`, we use register `GPPUA` at address `0x0c`, and turn on the MSB: `i2cset -y 0 0x20 0x0c 0x80`. (Only two of the Pi's GPIO pins have pull-up resistors, so even if you don't need the extra GPIOs, the MCP might make your circuit simpler.) (Update: it appears that in fact all of the Pi's GPIO pins have pull-up resistors, and most have pull-down resistors as well. The `gpio` program from [WiringPi](https://projects.drogon.net/raspberry-pi/wiringpi/) can enable and disable these.) After this, `i2cget -y 0 0x20 0x12` should return `0x00` if the putton is pressed, and `0x80` if the button is released. I found that for a short time after releasing the button, I would read `0xc0`, indicating that `GPA6` was also returning a 1 bit. I assume this is just due to electrical interference or something; pin 27 isn't connected to either power or ground, so its value is unreliable. (When I enabled its pull-up resistor, or connected it to ground, it read the expected value every time.) ### Miscellaneous extras You can read the value of every register using `i2cdump -y 0 0x20`. This actually returns 256 registers; I don't know what happens if you try to write to a register that doesn't exist, but I wouldn't be surprised if it's possible to destroy the chip like that, so I'm not going to try. You can set the address of the device to any value from `0x20` to `0x28` by connecting pins 15, 16 and 17 to a combination of power and ground. These pins are called A0, A1 and A2 respectively; the device address is `0b10cba` where `a` is 1 if `A0` is connected to power and 0 if it's connected to ground; `b` and `c` are the same for `A1` and `A2`. If you don't connect them, I think their values depend on things like electrical interference and can't be relied upon. The device address seems to be important if you have multiple I2C devices on one bus. I assume the protocol here is to connect SCL and SDA to both devices in parallel, and use the device address to only talk to one of them. But until I get a second I2C device, I can't test that. -
ChickenProp revised this gist
Jul 27, 2012 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -30,6 +30,8 @@ At this point, run `sudo i2cdetect -y 0` and you should get the following output This says there's an I2C device with address `0x20` that you can talk to. (From now on I won't bother telling you to use `sudo`. `-y` says "don't ask me for confirmation", and `0` is the I2C bus to use. Bus 1 also exists, but it won't detect anything. I'm not sure what it corresponds to.) (Update: in the comments, Tiersten informs me that bus 1 is on the camera CSI connector, which is the thing in between the ethernet and HDMI ports.) To actually talk to it, use the programs `i2cget` and `i2cdump` for reading, and `i2cset` for writing. The chip has 21 registers at 22 addresses (one register has two addresses), and reading and writing these allows you to read and write to the 16 GPIO pins. (The register addresses have nothing to do with the device address.) ### Writing -
ChickenProp revised this gist
Jul 27, 2012 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -52,9 +52,9 @@ The direction register for bank A is `IODIRA` at address `0x00`. Like `IODIRB`, Now you can read the value of the button as the most significant bit from `GPIOA`, address `0x12`. Except that there's no power being supplied to that pin or to the button, so you'll just read `0x00`. You *can* fix this by putting a [pull-up resistor](http://en.wikipedia.org/wiki/Pull-up_resistor) into your circuit; a 10 kΩ resistor between 3v3 and pin 28 will do the trick. But the MCP has pull-up resistors built-in, they just aren't enabled by default. To enable it for `GPA7`, we use register `GPPUA` at address `0x0c`, and turn on the MSB: `i2cset -y 0 0x20 0x0c 0x80`. (Only two of the Pi's GPIO pins have pull-up resistors, so even if you don't need the extra GPIOs, the MCP might make your circuit simpler.) After this, `i2cget -y 0 0x20 0x12` should return `0x00` if the putton is pressed, and `0x80` if the button is released. I found that for a short time after releasing the button, I would read `0xc0`, indicating that `GPA6` was also returning a 1 bit. I assume this is just due to electrical interference or something; pin 27 isn't connected to either power or ground, so its value is unreliable. (When I enabled its pull-up resistor, or connected it to ground, it read the expected value every time.) ### Miscellaneous extras -
ChickenProp revised this gist
Jul 26, 2012 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ ### Introduction The [MCP23017](http://www.skpang.co.uk/catalog/mcp23017-16bit-i2c-io-expander-for-raspberry-pi-p-1104.html) is an I/O expander chip. It has 16 GPIO pins which you can control using an I2C interface using two pins from a Raspberry Pi, plus a power source and sink (which can also come from the Pi). It's not quite as simple as [directly controlling the Pi's GPIO pins](https://gist.github.com/3050085), but it's not complicated, either. You need to install `i2c-tools`, which is probably in your distribution's package manager. You also need a kernel with I2C support; you might need to `modprobe i2c-dev`. It would presumably be possible to do without either of these things, and bitbang the I2C protocol over GPIO, but I don't understand the protocol well enough to try. -
ChickenProp revised this gist
Jul 26, 2012 . No changes.There are no files selected for viewing
-
ChickenProp revised this gist
Jul 26, 2012 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -71,10 +71,11 @@ Most of the registers have an A version and a B version; the address of the A ve The registers that I understand are (register address given for bank A): * `IODIRx` (`0x00`): set pins in the given bank to be either input or output pins. * `IOPOLx` (`0x02`): invert polarity of input pins. In the example above, `i2cset -y 0 0x20 0x02 0x80` would cause reads of `GPA7` to return `1` when the button is pressed and `0` when it's not. Has no effect when reading output pins. * `GPPUx` (`0x0c`): enable or disable pull-up resistors. * `GPIOx` (`0x12`): read and write the values of pins. If not all pins in a bank have the same direction, you'll sometimes be reading an output pin or writing an input pin. If you read an output pin, you'll be told its current value. If you write an input pin, you'll set the value it takes when it next becomes an output pin. * `OLATx` (`0x14`): for an output pin, reading and writing this will have the same effect as `GPIOx`. For an input pin, its value is the value that the pin will take if it becomes set to output. Writing to `GPIOx` actually modifies this register. ("OLAT" means "output latch".) The other registers are `GPINTENx` (`0x04`), `DEFVALx` (`0x06`), `INTCONx` (`0x08`), `IOCON`, `INTFx` (`0x0e`) and `INTCAPx` (`0x10`). Mostly they seem related to interrupt pins 19 (`INTB`) and 20 (`INTA`), but I haven't looked closely into that. `IOCON` does expose one interesting feature: its most significant bit is called `BANK`. If you set `BANK` to 1 (i.e. `i2cset -y 0 0x20 0x0a 0x80`) it gives almost every register a different address. (Only `IODIRA` stays the same.) The new address is the old one, but with the last five bits rotated one to the right; so a register's bank is now given by its `0x10` bit instead of its `0x01` bit. I'm not sure why this is considered useful; maybe for compatibility with other chips? I don't think you can always tell just by looking which mode the chip is in, because `IOCON` also moves; but I doubt that's a significant problem in the real world. -
ChickenProp revised this gist
Jul 26, 2012 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -46,6 +46,8 @@ Now to turn it on. (Connect the LED first, if you haven't already. Its `+` termi Now we'll read the state of a button, which we'll put on pin 28. This is `GPA7`, i.e. pin 7 on bank A. So connect a button with one terminal connected to pin 28 and the other connected to ground.  The direction register for bank A is `IODIRA` at address `0x00`. Like `IODIRB`, it's probably already set up as `0xff`, but if it doesn't already have the most significant bit set (which is true iff its value is less than `0x80`), you'll need to write to it. Now you can read the value of the button as the most significant bit from `GPIOA`, address `0x12`. Except that there's no power being supplied to that pin or to the button, so you'll just read `0x00`. -
ChickenProp revised this gist
Jul 26, 2012 . 1 changed file with 2 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -40,6 +40,8 @@ To get the current value of this register, run `i2cget -y 0 0x20 0x01`. Here `0` Now to turn it on. (Connect the LED first, if you haven't already. Its `+` terminal connects to pin 1 on the MCP, its `-` terminal connects to ground via a suitable resistor.) The register we use for this is `GPIOB` at address `0x13`. Write a 1 to bit 1 of this register and the LED should turn on: `i2cset -y 0 0x20 0x13 0x01`. Write a 0 again to turn it off: `i2cset -y 0 0x20 0x13 0x00`.  ### Reading Now we'll read the state of a button, which we'll put on pin 28. This is `GPA7`, i.e. pin 7 on bank A. So connect a button with one terminal connected to pin 28 and the other connected to ground. -
ChickenProp revised this gist
Jul 26, 2012 . 1 changed file with 3 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -10,7 +10,9 @@ On the Pi, we'll be using [pins](http://elinux.org/File:GPIOs.png) 3 (SDA) and 5 ### Setup We'll start by connecting the chip. Connect pins 9 and 18 to 3v3, and pin 10 to ground. For now, connect pins 15 through 17 to ground as well. (They configure the I2C address of the chip, which I'll talk about later.) Now to be able to talk to the chip, connect pin 12 to the Pi's pin 5 (SCL) and pin 13 to the Pi's pin 3 (SDA). Here's my first ever attempt at a circuit diagram showing it, although I haven't included the Pi:  At this point, run `sudo i2cdetect -y 0` and you should get the following output: -
ChickenProp renamed this gist
Jul 26, 2012 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
ChickenProp created this gist
Jul 26, 2012 .There are no files selected for viewing
This file contains hidden or 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 charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,74 @@ ### Introduction The [MCP23017](http://www.skpang.co.uk/catalog/mcp23017-16bit-i2c-io-expander-for-raspberry-pi-p-1104.html) is an I/O expander chip. It has 16 GPIO pins which you can control using an I2C interface using two pins from a Raspberry Pi. It's not quite as simple as [directly controlling the Pi's GPIO pins](https://gist.github.com/3050085), but it's not complicated, either. You need to install `i2c-tools`, which is probably in your distribution's package manager. You also need a kernel with I2C support; you might need to `modprobe i2c-dev`. It would presumably be possible to do without either of these things, and bitbang the I2C protocol over GPIO, but I don't understand the protocol well enough to try. On pin numbering: if you like, you can refer to the [datasheet](http://www.adafruit.com/datasheets/mcp23017.pdf) for the MCP23017. There's a small dot in one corner of the chip, with a semi-circular cut-out at that end. The pin nearest the dot is pin 1, with pins 2, 3, ..., 14 along that long side of the chip. On the other side, pins 15 through 28 go in the other direction, so that pin 15 is opposite pin 14 and pin 28 is opposite pin 1. On the Pi, we'll be using [pins](http://elinux.org/File:GPIOs.png) 3 (SDA) and 5 (SCL) to talk to the MCP, and pins 1 (3v3) and 6 (ground) as power source and sink. ### Setup We'll start by connecting the chip. Connect pins 9 and 18 to 3v3, and pin 10 to ground. For now, connect pins 15 through 17 to ground as well. (They configure the I2C address of the chip, which I'll talk about later.) Now to be able to talk to the chip, connect pin 12 to the Pi's pin 5 (SCL) and pin 13 to the Pi's pin 3 (SDA). At this point, run `sudo i2cdetect -y 0` and you should get the following output: ``` 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- ``` This says there's an I2C device with address `0x20` that you can talk to. (From now on I won't bother telling you to use `sudo`. `-y` says "don't ask me for confirmation", and `0` is the I2C bus to use. Bus 1 also exists, but it won't detect anything. I'm not sure what it corresponds to.) To actually talk to it, use the programs `i2cget` and `i2cdump` for reading, and `i2cset` for writing. The chip has 21 registers at 22 addresses (one register has two addresses), and reading and writing these allows you to read and write to the 16 GPIO pins. (The register addresses have nothing to do with the device address.) ### Writing Let's start by activating an LED on pin 1. The 16 GPIO pins are divided into banks 'A' and 'B' of eight pins each, and somewhat counterintuitively, pin 1 is `GPB0` (pin 0 in bank B). We need to set it up for output; the register we need is `IODIRB` at address `0x01`, which controls the direction of each pin in bank B. (They can all be set individually, but they're all done from the same register.) To get the current value of this register, run `i2cget -y 0 0x20 0x01`. Here `0` is the bus again, `0x20` is the device address and `0x01` is the register address. It will probably print `0xff`. This should be read as eight bits rather than as a single byte; the least significant bit corresponds to `GPB0`, and so on. A `1` bit indicates that pin is configured for input, and a `0` indicates output, so we need to turn off bit 1. Run `i2cset -y 0 0x20 0x01 0xfe`, which means "assign value `0xfe` to register `0x01` of device `0x20` on bus `0`". Of course, you could use `0x00` or something else instead of `0xfe`, as long as the final bit is `0`. Now to turn it on. (Connect the LED first, if you haven't already. Its `+` terminal connects to pin 1 on the MCP, its `-` terminal connects to ground via a suitable resistor.) The register we use for this is `GPIOB` at address `0x13`. Write a 1 to bit 1 of this register and the LED should turn on: `i2cset -y 0 0x20 0x13 0x01`. Write a 0 again to turn it off: `i2cset -y 0 0x20 0x13 0x00`. ### Reading Now we'll read the state of a button, which we'll put on pin 28. This is `GPA7`, i.e. pin 7 on bank A. So connect a button with one terminal connected to pin 28 and the other connected to ground. The direction register for bank A is `IODIRA` at address `0x00`. Like `IODIRB`, it's probably already set up as `0xff`, but if it doesn't already have the most significant bit set (which is true iff its value is less than `0x80`), you'll need to write to it. Now you can read the value of the button as the most significant bit from `GPIOA`, address `0x12`. Except that there's no power being supplied to that pin or to the button, so you'll just read `0x00`. You *can* fix this by putting a [pull-up resistor](http://en.wikipedia.org/wiki/Pull-up_resistor) into your circuit; a 10 kΩ resistor between 3v3 and pin 28 will do the trick. But the MCP has pull-up resistors built-in, they just aren't enabled by default. To enable it for `GPA7`, we use register `GPPUA` at address `0x0c`, and turn on the MSB: `i2cset -y 0 0x20 0x0c 0x80`. After this, `i2cget -y 0 0x20 0x12` should return `0x00` if the putton is pressed, and `0x80` if the button is released. I found that for a short time after releasing the button, I would read `0xc0`, indicating that `GPA6` was also returning a 1 bit. I assume this is just due to electrical interference or something; pin 27 isn't connected to either power or ground, so its value is unreliable. (When I enabled its pull-up resistor, or connected it to ground, I didn't have issues.) ### Miscellaneous extras You can read the value of every register using `i2cdump -y 0 0x20`. This actually returns 256 registers; I don't know what happens if you try to write to a register that doesn't exist, but I wouldn't be surprised if it's possible to destroy the chip like that, so I'm not going to try. You can set the address of the device to any value from `0x20` to `0x28` by connecting pins 15, 16 and 17 to a combination of power and ground. These pins are called A0, A1 and A2 respectively; the device address is `0b10cba` where `a` is 1 if `A0` is connected to power and 0 if it's connected to ground; `b` and `c` are the same for `A1` and `A2`. The device address seems to be important if you have multiple I2C devices on one bus. I assume the protocol here is to connect SCL and SDA to both devices in parallel, and use the device address to only talk to one of them. But until I get a second I2C device, I can't test that. If you haven't looked at the datasheet yet, the 16 GPIO pins are pins 1 through 8 (`GPB0` through `GPB7`) and pins 21 through 28 (`GPA0` through `GPA7`). Most of the registers have an A version and a B version; the address of the A version has its least significant bit 0, and the address of the B version has its LSB 1. So `IODIRA` and `IODIRB` are at `0x00` and `0x01`; `GPPUA` and `GPPUB` are at `0x0c` and `0x0d`; etc. The exception is the register `IOCON`, which can be considered shared between the two pin banks; it has addresses `0x0a` and `0x0b`. The registers that I understand are (register address given for bank A): * `IODIRx` (`0x00`): set pins in the given bank to be either input or output pins. * `GPPUx` (`0x0c`): enable or disable pull-up resistors. * `GPIOx` (`0x12`): read and write the values of pins. If not all pins in a bank have the same direction, you'll sometimes be reading an output pin or writing an input pin. If you read an output pin, you'll be told its current value. If you write an input pin, you'll set the value it takes when it next becomes an output pin. * `OLATx` (`0x14`): for an output pin, reading and writing this will have the same effect as `GPIOx`. For an input pin, its value is the value that the pin will take if it becomes set to output. Writing to `GPIOx` actually modifies this register. ("OLAT" means "output latch".) The other registers are `IPOLx` (`0x02`), `GPINTENx` (`0x04`), `DEFVALx` (`0x06`), `INTCONx` (`0x08`), `IOCON`, `INTFx` (`0x0e`) and `INTCAPx` (`0x10`). Mostly they seem related to interrupt pins 19 (`INTB`) and 20 (`INTA`), but I haven't looked closely into that. `IOCON` does expose one interesting feature: its most significant bit is called `BANK`. If you set `BANK` to 1 (i.e. `i2cset -y 0 0x20 0x0a 0x80`) it gives almost every register a different address. (Only `IODIRA` stays the same.) The new address is the old one, but with the last five bits rotated one to the right; so a register's bank is now given by its `0x10` bit instead of its `0x01` bit. I'm not sure why this is considered useful; maybe for compatibility with other chips? I don't think you can always tell just by looking which mode the chip is in, because `IOCON` also moves; but I doubt that's a significant problem in the real world.