├── .gitignore ├── .travis.yml ├── LICENSE ├── MCP23S17 ├── MCP23S17.go ├── MCP23S17Register.go ├── MCP23S17RegisterBit.go ├── MCP23S17RegisterBitNeg.go ├── MCP23S17RegisterNeg.go ├── MCP23S17RegisterNibble.go ├── MCP23S17RegisterNibbleNeg.go └── definitions.go ├── README.md ├── examples ├── blink_MCP23S17 │ └── blink_MCP23S17.go └── blink_piface │ └── blink_piface.go ├── ioctl └── ioctl.go ├── piface └── piface.go ├── rpi.go └── spi ├── SPIDevice.go ├── definitions.go └── spi.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.2 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 luismesas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /MCP23S17/MCP23S17.go: -------------------------------------------------------------------------------- 1 | package MCP23S17 2 | 3 | import ( 4 | "fmt" 5 | "github.com/luismesas/goPi/spi" 6 | ) 7 | 8 | const ( 9 | MCP23S17_MODE = 0 10 | MCP23S17_BPW = 8 11 | MCP23S17_SPEED = 10000000 12 | ) 13 | 14 | // Microchip's MCP23S17: A 16-Bit I/O Expander with Serial Interface. 15 | type MCP23S17 struct { 16 | Device *spi.SPIDevice 17 | HardwareAddress byte 18 | 19 | // Controls the direction of the data I/O. 20 | IODIRa *MCP23S17Register 21 | IODIRb *MCP23S17Register 22 | 23 | // This register allows the user to configure the polarity on the corresponding GPIO port bits. 24 | IPOLa *MCP23S17Register 25 | IPOLb *MCP23S17Register 26 | 27 | // The GPINTEN register controls the interrupt-onchange feature for each pin. 28 | GPINTENa *MCP23S17Register 29 | GPINTENb *MCP23S17Register 30 | 31 | // The default comparison value is configured in the DEFVAL register. 32 | DEFVALa *MCP23S17Register 33 | DEFVALb *MCP23S17Register 34 | 35 | // The INTCON register controls how the associated pin value is compared for the interrupt-on-change feature. 36 | INTCONa *MCP23S17Register 37 | INTCONb *MCP23S17Register 38 | 39 | // The IOCON register contains several bits for configuring the device. 40 | IOCON *MCP23S17Register 41 | 42 | // The GPPU register controls the pull-up esistors for the port pins. 43 | GPPUa *MCP23S17Register 44 | GPPUb *MCP23S17Register 45 | 46 | // The INTF register reflects the interrupt condition on the port pins of any pin that is enabled for interrupts via the GPINTEN register. 47 | INTFa *MCP23S17Register 48 | INTFb *MCP23S17Register 49 | 50 | // The INTCAP register captures the GPIO port value at the time the interrupt occurred. 51 | INTCAPa *MCP23S17Register 52 | INTCAPb *MCP23S17Register 53 | 54 | // The GPIO register reflects the value on the port. 55 | GPIOa *MCP23S17Register 56 | GPIOb *MCP23S17Register 57 | 58 | // The OLAT register provides access to the output latches. 59 | OLATa *MCP23S17Register 60 | OLATb *MCP23S17Register 61 | } 62 | 63 | func NewMCP23S17(hardwareAddress uint8, bus int, chip_select int) *MCP23S17 { 64 | mcp := new(MCP23S17) 65 | mcp.Device = spi.NewSPIDevice(bus, chip_select) 66 | mcp.HardwareAddress = hardwareAddress 67 | 68 | mcp.IODIRa = NewMCP23S17Register(IODIRA, mcp) 69 | mcp.IODIRb = NewMCP23S17Register(IODIRB, mcp) 70 | mcp.IPOLa = NewMCP23S17Register(IPOLA, mcp) 71 | mcp.IPOLb = NewMCP23S17Register(IPOLB, mcp) 72 | mcp.GPINTENa = NewMCP23S17Register(GPINTENA, mcp) 73 | mcp.GPINTENb = NewMCP23S17Register(GPINTENB, mcp) 74 | mcp.DEFVALa = NewMCP23S17Register(DEFVALA, mcp) 75 | mcp.DEFVALb = NewMCP23S17Register(DEFVALB, mcp) 76 | mcp.INTCONa = NewMCP23S17Register(INTCONA, mcp) 77 | mcp.INTCONb = NewMCP23S17Register(INTCONB, mcp) 78 | mcp.IOCON = NewMCP23S17Register(IOCON, mcp) 79 | mcp.GPPUa = NewMCP23S17Register(GPPUA, mcp) 80 | mcp.GPPUb = NewMCP23S17Register(GPPUB, mcp) 81 | mcp.INTFa = NewMCP23S17Register(INTFA, mcp) 82 | mcp.INTFb = NewMCP23S17Register(INTFB, mcp) 83 | mcp.INTCAPa = NewMCP23S17Register(INTCAPA, mcp) 84 | mcp.INTCAPb = NewMCP23S17Register(INTCAPB, mcp) 85 | mcp.GPIOa = NewMCP23S17Register(GPIOA, mcp) 86 | mcp.GPIOb = NewMCP23S17Register(GPIOB, mcp) 87 | mcp.OLATa = NewMCP23S17Register(OLATA, mcp) 88 | mcp.OLATb = NewMCP23S17Register(OLATB, mcp) 89 | 90 | return mcp 91 | } 92 | 93 | func (mcp *MCP23S17) Open() error { 94 | 95 | err := mcp.Device.Open() 96 | if err != nil { 97 | return err 98 | } 99 | 100 | err = mcp.Device.SetMode(MCP23S17_MODE) 101 | if err != nil { 102 | return err 103 | } 104 | 105 | err = mcp.Device.SetBitsPerWord(MCP23S17_BPW) 106 | if err != nil { 107 | return err 108 | } 109 | 110 | err = mcp.Device.SetSpeed(MCP23S17_SPEED) 111 | if err != nil { 112 | return err 113 | } 114 | 115 | return nil 116 | } 117 | 118 | func (mcp *MCP23S17) Close() error { 119 | return mcp.Device.Close() 120 | } 121 | 122 | // Returns an SPI control byte. 123 | // The MCP23S17 is a slave SPI device. The slave address contains 124 | // four fixed bits and three user-defined hardware address bits 125 | // (if enabled via IOCON.HAEN) (pins A2, A1 and A0) with the 126 | // read/write bit filling out the control byte:: 127 | 128 | // +--------------------+ 129 | // |0|1|0|0|A2|A1|A0|R/W| 130 | // +--------------------+ 131 | // 7 6 5 4 3 2 1 0 132 | 133 | // :param read_write_cmd: Read or write command. 134 | // :type read_write_cmd: int 135 | func (mcp *MCP23S17) getSPIControlByte(read_write_cmd uint8) byte { 136 | board_addr_pattern := (mcp.HardwareAddress << 1) & 0xE 137 | rw_cmd_pattern := read_write_cmd & 0x01 // make sure it's just 1 bit long 138 | return 0x40 | board_addr_pattern | rw_cmd_pattern 139 | } 140 | 141 | // Returns the value of the address specified. 142 | func (mcp *MCP23S17) Read(address byte) byte { 143 | ctrl_byte := mcp.getSPIControlByte(READ_CMD) 144 | data, err := mcp.Device.Send([3]byte{ctrl_byte, address, 0}) 145 | if err != nil { 146 | panic(fmt.Sprintf("Error reading from MCP23S17: %s\n", err)) 147 | return 0x00 148 | } 149 | if len(data) == 0 { 150 | return 0x00 151 | } 152 | return data[2] 153 | } 154 | 155 | // Writes data to the address specified. 156 | func (mcp *MCP23S17) Write(data byte, address byte) { 157 | ctrl_byte := mcp.getSPIControlByte(WRITE_CMD) 158 | _, err := mcp.Device.Send([3]byte{ctrl_byte, address, data}) 159 | if err != nil { 160 | panic(fmt.Sprintf("Error writing on MCP23S17: %s\n", err)) 161 | } 162 | } 163 | 164 | // Returns the bit specified from the address. 165 | func (mcp *MCP23S17) ReadBit(bit_num uint, address byte) byte { 166 | value := mcp.Read(address) 167 | return (value >> bit_num) & 1 168 | } 169 | 170 | // Writes the value given to the bit in the address specified. 171 | func (mcp *MCP23S17) WriteBit(data byte, bit_num uint, address byte) { 172 | value := mcp.Read(address) 173 | if data > 0 { 174 | value = value | (1 << bit_num) //set 175 | } else { 176 | value = value & (0xff ^ (1 << bit_num)) //clear 177 | } 178 | mcp.Write(value, address) 179 | } 180 | 181 | // Clears the interrupt flags by reading the capture register. 182 | func (mcp *MCP23S17) ClearInterrupts(port int) { 183 | var address byte 184 | address = INTCAPA 185 | if port == GPIOA { 186 | address = INTCAPB 187 | } 188 | mcp.Read(address) 189 | } 190 | -------------------------------------------------------------------------------- /MCP23S17/MCP23S17Register.go: -------------------------------------------------------------------------------- 1 | package MCP23S17 2 | 3 | // An 8-bit register inside an MCP23S17. 4 | type MCP23S17Register struct { 5 | address byte 6 | chip *MCP23S17 7 | lowerNibble *MCP23S17RegisterNibble 8 | upperNibble *MCP23S17RegisterNibble 9 | bits []*MCP23S17RegisterBit 10 | } 11 | 12 | func NewMCP23S17Register(address byte, chip *MCP23S17) *MCP23S17Register { 13 | register := new(MCP23S17Register) 14 | register.address = address 15 | register.chip = chip 16 | 17 | register.lowerNibble = NewMCP23S17RegisterNibble(LOWER_NIBBLE, register.address, register.chip) 18 | register.upperNibble = NewMCP23S17RegisterNibble(UPPER_NIBBLE, register.address, register.chip) 19 | 20 | register.bits = make([]*MCP23S17RegisterBit, 8) 21 | for i := range register.bits { 22 | register.bits[i] = NewMCP23S17RegisterBit(uint(i), register.address, register.chip) 23 | } 24 | 25 | return register 26 | } 27 | 28 | func (register *MCP23S17Register) Value() byte { 29 | return register.chip.Read(register.address) 30 | } 31 | 32 | func (register *MCP23S17Register) SetValue(value byte) { 33 | register.chip.Write(value, register.address) 34 | } 35 | 36 | func (register *MCP23S17Register) AllHigh() { 37 | register.SetValue(0xff) 38 | } 39 | 40 | func (register *MCP23S17Register) AllLow() { 41 | register.SetValue(0x00) 42 | } 43 | 44 | func (register *MCP23S17Register) AllOn() { 45 | register.SetValue(0xff) 46 | } 47 | 48 | func (register *MCP23S17Register) AllOff() { 49 | register.SetValue(0x00) 50 | } 51 | 52 | func (register *MCP23S17Register) Toggle() { 53 | register.SetValue(0xff ^ register.Value()) 54 | } 55 | -------------------------------------------------------------------------------- /MCP23S17/MCP23S17RegisterBit.go: -------------------------------------------------------------------------------- 1 | package MCP23S17 2 | 3 | // A bit inside register inside an MCP23S17. 4 | type MCP23S17RegisterBit struct { 5 | bit_num uint 6 | address byte 7 | chip *MCP23S17 8 | } 9 | 10 | func NewMCP23S17RegisterBit(bit_num uint, address byte, chip *MCP23S17) *MCP23S17RegisterBit { 11 | bit := new(MCP23S17RegisterBit) 12 | bit.bit_num = bit_num 13 | bit.address = address 14 | bit.chip = chip 15 | return bit 16 | } 17 | 18 | func (register *MCP23S17RegisterBit) Value() byte { 19 | return register.chip.ReadBit(register.bit_num, register.address) 20 | } 21 | 22 | func (register *MCP23S17RegisterBit) SetValue(value byte) { 23 | register.chip.WriteBit(value, register.bit_num, register.address) 24 | } 25 | 26 | func (register *MCP23S17RegisterBit) AllHigh() { 27 | register.SetValue(1) 28 | } 29 | 30 | func (register *MCP23S17RegisterBit) AllLow() { 31 | register.SetValue(0) 32 | } 33 | 34 | func (register *MCP23S17RegisterBit) AllOn() { 35 | register.SetValue(1) 36 | } 37 | 38 | func (register *MCP23S17RegisterBit) AllOff() { 39 | register.SetValue(0) 40 | } 41 | 42 | func (register *MCP23S17RegisterBit) Toggle() { 43 | register.SetValue(1 ^ register.Value()) 44 | } 45 | -------------------------------------------------------------------------------- /MCP23S17/MCP23S17RegisterBitNeg.go: -------------------------------------------------------------------------------- 1 | package MCP23S17 2 | 3 | // A bit inside register inside an MCP23S17. 4 | type MCP23S17RegisterBitNeg struct { 5 | bit_num uint 6 | address byte 7 | chip *MCP23S17 8 | } 9 | 10 | func NewMCP23S17RegisterBitNeg(bit_num uint, address byte, chip *MCP23S17) *MCP23S17RegisterBitNeg { 11 | bit := new(MCP23S17RegisterBitNeg) 12 | bit.bit_num = bit_num 13 | bit.address = address 14 | bit.chip = chip 15 | return bit 16 | } 17 | 18 | func (register *MCP23S17RegisterBitNeg) Value() byte { 19 | return 1 ^ register.chip.ReadBit(register.bit_num, register.address) 20 | } 21 | 22 | func (register *MCP23S17RegisterBitNeg) SetValue(value byte) { 23 | register.chip.WriteBit(value^1, register.bit_num, register.address) 24 | } 25 | 26 | func (register *MCP23S17RegisterBitNeg) AllHigh() { 27 | register.SetValue(1) 28 | } 29 | 30 | func (register *MCP23S17RegisterBitNeg) AllLow() { 31 | register.SetValue(0) 32 | } 33 | 34 | func (register *MCP23S17RegisterBitNeg) AllOn() { 35 | register.SetValue(1) 36 | } 37 | 38 | func (register *MCP23S17RegisterBitNeg) AllOff() { 39 | register.SetValue(0) 40 | } 41 | 42 | func (register *MCP23S17RegisterBitNeg) Toggle() { 43 | register.SetValue(1 ^ register.Value()) 44 | } 45 | -------------------------------------------------------------------------------- /MCP23S17/MCP23S17RegisterNeg.go: -------------------------------------------------------------------------------- 1 | package MCP23S17 2 | 3 | // An 8-bit register inside an MCP23S17. 4 | type MCP23S17RegisterNeg struct { 5 | address byte 6 | chip *MCP23S17 7 | lowerNibble *MCP23S17RegisterNibbleNeg 8 | upperNibble *MCP23S17RegisterNibbleNeg 9 | bits []*MCP23S17RegisterBitNeg 10 | } 11 | 12 | func NewMCP23S17RegisterNeg(address byte, chip *MCP23S17) *MCP23S17RegisterNeg { 13 | register := new(MCP23S17RegisterNeg) 14 | register.address = address 15 | register.chip = chip 16 | 17 | register.lowerNibble = NewMCP23S17RegisterNibbleNeg(LOWER_NIBBLE, register.address, register.chip) 18 | register.upperNibble = NewMCP23S17RegisterNibbleNeg(UPPER_NIBBLE, register.address, register.chip) 19 | 20 | register.bits = make([]*MCP23S17RegisterBitNeg, 8) 21 | for i := range register.bits { 22 | register.bits[i] = NewMCP23S17RegisterBitNeg(uint(i), register.address, register.chip) 23 | } 24 | 25 | return register 26 | } 27 | 28 | func (register *MCP23S17RegisterNeg) Value() byte { 29 | return 0xff ^ register.chip.Read(register.address) 30 | } 31 | 32 | func (register *MCP23S17RegisterNeg) SetValue(value byte) { 33 | register.chip.Write(0xff^value, register.address) 34 | } 35 | 36 | func (register *MCP23S17RegisterNeg) AllHigh() { 37 | register.SetValue(0xff) 38 | } 39 | 40 | func (register *MCP23S17RegisterNeg) AllLow() { 41 | register.SetValue(0x00) 42 | } 43 | 44 | func (register *MCP23S17RegisterNeg) AllOn() { 45 | register.SetValue(0xff) 46 | } 47 | 48 | func (register *MCP23S17RegisterNeg) AllOff() { 49 | register.SetValue(0x00) 50 | } 51 | 52 | func (register *MCP23S17RegisterNeg) Toggle() { 53 | register.SetValue(0xff ^ register.Value()) 54 | } 55 | -------------------------------------------------------------------------------- /MCP23S17/MCP23S17RegisterNibble.go: -------------------------------------------------------------------------------- 1 | package MCP23S17 2 | 3 | // An 4-bit nibble inside a register inside an MCP23S17. 4 | type MCP23S17RegisterNibble struct { 5 | nibble uint 6 | address byte 7 | chip *MCP23S17 8 | bits []*MCP23S17RegisterBit 9 | } 10 | 11 | func NewMCP23S17RegisterNibble(nibble uint, address byte, chip *MCP23S17) *MCP23S17RegisterNibble { 12 | register := new(MCP23S17RegisterNibble) 13 | register.nibble = nibble 14 | register.address = address 15 | register.chip = chip 16 | 17 | range_start := 4 * register.nibble 18 | 19 | register.bits = make([]*MCP23S17RegisterBit, 4) 20 | for i := range register.bits { 21 | register.bits[i] = NewMCP23S17RegisterBit(uint(i)+range_start, register.address, register.chip) 22 | } 23 | 24 | return register 25 | } 26 | 27 | func (register *MCP23S17RegisterNibble) Value() byte { 28 | if register.nibble == LOWER_NIBBLE { 29 | return register.chip.Read(register.address) & 0x0f 30 | } 31 | return register.chip.Read(register.address) & 0xf0 >> 4 32 | } 33 | 34 | func (register *MCP23S17RegisterNibble) SetValue(value byte) { 35 | reg_value := register.chip.Read(register.address) 36 | 37 | if register.nibble == LOWER_NIBBLE { 38 | reg_value = reg_value & 0xf0 // clear 39 | reg_value = reg_value ^ (value & 0x0f) // set 40 | } else { 41 | reg_value = reg_value & 0x0f // clear 42 | reg_value = reg_value ^ ((value << 4) * 0xf0) // set 43 | } 44 | 45 | register.chip.Write(reg_value, register.address) 46 | } 47 | 48 | func (register *MCP23S17RegisterNibble) AllHigh() { 49 | register.SetValue(0xf) 50 | } 51 | 52 | func (register *MCP23S17RegisterNibble) AllLow() { 53 | register.SetValue(0x0) 54 | } 55 | 56 | func (register *MCP23S17RegisterNibble) AllOn() { 57 | register.SetValue(0xf) 58 | } 59 | 60 | func (register *MCP23S17RegisterNibble) AllOff() { 61 | register.SetValue(0x0) 62 | } 63 | 64 | func (register *MCP23S17RegisterNibble) Toggle() { 65 | register.SetValue(0xf ^ register.Value()) 66 | } 67 | -------------------------------------------------------------------------------- /MCP23S17/MCP23S17RegisterNibbleNeg.go: -------------------------------------------------------------------------------- 1 | package MCP23S17 2 | 3 | // A negated 4-bit nibble inside a register inside an MCP23S17. 4 | type MCP23S17RegisterNibbleNeg struct { 5 | nibble uint 6 | address byte 7 | chip *MCP23S17 8 | bits []*MCP23S17RegisterBitNeg 9 | } 10 | 11 | func NewMCP23S17RegisterNibbleNeg(nibble uint, address byte, chip *MCP23S17) *MCP23S17RegisterNibbleNeg { 12 | register := new(MCP23S17RegisterNibbleNeg) 13 | register.nibble = nibble 14 | register.address = address 15 | register.chip = chip 16 | 17 | range_start := 4 * register.nibble 18 | 19 | register.bits = make([]*MCP23S17RegisterBitNeg, 4) 20 | for i := range register.bits { 21 | register.bits[i] = NewMCP23S17RegisterBitNeg(uint(i)+range_start, register.address, register.chip) 22 | } 23 | 24 | return register 25 | } 26 | 27 | func (register *MCP23S17RegisterNibbleNeg) Value() byte { 28 | var value byte 29 | if register.nibble == LOWER_NIBBLE { 30 | value = register.chip.Read(register.address) & 0x0f 31 | } else { 32 | value = register.chip.Read(register.address) & 0xf0 >> 4 33 | } 34 | 35 | return 0xf ^ value 36 | } 37 | 38 | func (register *MCP23S17RegisterNibbleNeg) SetValue(value byte) { 39 | reg_value := register.chip.Read(register.address) 40 | 41 | if register.nibble == LOWER_NIBBLE { 42 | reg_value = reg_value & 0xf0 // clear 43 | reg_value = reg_value ^ (value&0x0f ^ 0x0f) // set 44 | } else { 45 | reg_value = reg_value & 0x0f // clear 46 | reg_value = reg_value ^ ((value<<4)*0xf0 ^ 0xf0) // set 47 | } 48 | 49 | register.chip.Write(reg_value, register.address) 50 | } 51 | 52 | func (register *MCP23S17RegisterNibbleNeg) AllHigh() { 53 | register.SetValue(0xf) 54 | } 55 | 56 | func (register *MCP23S17RegisterNibbleNeg) AllLow() { 57 | register.SetValue(0x0) 58 | } 59 | 60 | func (register *MCP23S17RegisterNibbleNeg) AllOn() { 61 | register.SetValue(0xf) 62 | } 63 | 64 | func (register *MCP23S17RegisterNibbleNeg) AllOff() { 65 | register.SetValue(0x0) 66 | } 67 | 68 | func (register *MCP23S17RegisterNibbleNeg) Toggle() { 69 | register.SetValue(0xf ^ register.Value()) 70 | } 71 | -------------------------------------------------------------------------------- /MCP23S17/definitions.go: -------------------------------------------------------------------------------- 1 | package MCP23S17 2 | 3 | // Register addresses 4 | const ( 5 | IODIRA = 0x0 // I/O direction A 6 | IODIRB = 0x1 // I/O direction B 7 | IPOLA = 0x2 // I/O polarity A 8 | IPOLB = 0x3 // I/O polarity B 9 | GPINTENA = 0x4 // interupt enable A 10 | GPINTENB = 0x5 // interupt enable B 11 | DEFVALA = 0x6 // register default value A (interupts) 12 | DEFVALB = 0x7 // register default value B (interupts) 13 | INTCONA = 0x8 // interupt control A 14 | INTCONB = 0x9 // interupt control B 15 | IOCON = 0xA // I/O config (also 0xB) 16 | GPPUA = 0xC // port A pullups 17 | GPPUB = 0xD // port B pullups 18 | INTFA = 0xE // interupt flag A (where the interupt came from) 19 | INTFB = 0xF // interupt flag B 20 | INTCAPA = 0x10 // interupt capture A (value at interupt is saved here) 21 | INTCAPB = 0x11 // interupt capture B 22 | GPIOA = 0x12 // port A 23 | GPIOB = 0x13 // port B 24 | OLATA = 0x14 // output latch A 25 | OLATB = 0x15 // output latch B 26 | ) 27 | 28 | // I/O config 29 | const ( 30 | BANK_OFF = 0x00 // addressing mode 31 | BANK_ON = 0x80 32 | INT_MIRROR_ON = 0x40 // interupt mirror (INTa|INTb) 33 | INT_MIRROR_OFF = 0x00 34 | SEQOP_OFF = 0x20 // incrementing address pointer 35 | SEQOP_ON = 0x00 36 | DISSLW_ON = 0x10 // slew rate 37 | DISSLW_OFF = 0x00 38 | HAEN_ON = 0x08 // hardware addressing 39 | HAEN_OFF = 0x00 40 | ODR_ON = 0x04 // open drain for interupts 41 | ODR_OFF = 0x00 42 | INTPOL_HIGH = 0x02 // interupt polarity 43 | INTPOL_LOW = 0x00 44 | ) 45 | 46 | // Commands 47 | const ( 48 | WRITE_CMD = 0 49 | READ_CMD = 1 50 | ) 51 | 52 | // Nibbles 53 | const ( 54 | LOWER_NIBBLE = 0 55 | UPPER_NIBBLE = 1 56 | ) 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | goPi 2 | ====== 3 | 4 | A library for using Raspberry Pi with Golang 5 | 6 | [![Build Status](https://travis-ci.org/luismesas/goPi.png)](https://travis-ci.org/luismesas/goPi) 7 | 8 | 9 | Current features 10 | ---------------- 11 | 12 | * ioctl - syscall ioctl needed for sending spi signals 13 | * spi - allows spi communication with connected spi devices 14 | * MCP23S17 - compatibility with Microchip MCP23S17 16-Bit I/O Expander with Serial Interface. 15 | * piface - compatibility with [PiFaceDigital](http://www.piface.org.uk/products/piface_digital/) 16 | 17 | Documentation 18 | ------------- 19 | * http://godoc.org/github.com/luismesas/goPi 20 | 21 | Usage 22 | ----- 23 | * See the [examples/](https://github.com/luismesas/goPi/tree/master/examples) folder 24 | -------------------------------------------------------------------------------- /examples/blink_MCP23S17/blink_MCP23S17.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/luismesas/goPi/MCP23S17" 5 | "github.com/luismesas/goPi/spi" 6 | "log" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | 12 | // creates a new MCP23S17 instance 13 | mcp := MCP23S17.NewMCP23S17(spi.DEFAULT_HARDWARE_ADDR, spi.DEFAULT_BUS, spi.DEFAULT_CHIP) 14 | 15 | // GPIOa outputs 16 | gpioa := make([]*MCP23S17.MCP23S17RegisterBit, 8) 17 | for i := range gpioa { 18 | gpioa[i] = MCP23S17.NewMCP23S17RegisterBit(uint(i), MCP23S17.GPIOA, mcp) 19 | } 20 | 21 | // Connects to chip 22 | err := mcp.Open() 23 | if err != nil { 24 | log.Fatalf("Error connecting to chip: %s\n", err) 25 | } 26 | 27 | // Blinks output 7 28 | for { 29 | gpioa[7].Toggle() 30 | time.Sleep(time.Second) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/blink_piface/blink_piface.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/luismesas/goPi/piface" 6 | "github.com/luismesas/goPi/spi" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | 12 | // creates a new pifacedigital instance 13 | pfd := piface.NewPiFaceDigital(spi.DEFAULT_HARDWARE_ADDR, spi.DEFAULT_BUS, spi.DEFAULT_CHIP) 14 | 15 | // initializes pifacedigital board 16 | err := pfd.InitBoard() 17 | if err != nil { 18 | fmt.Printf("Error on init board: %s", err) 19 | return 20 | } 21 | 22 | // blink time!! 23 | fmt.Println("Blinking led 7 each second") 24 | for { 25 | pfd.Leds[7].Toggle() 26 | time.Sleep(time.Second) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ioctl/ioctl.go: -------------------------------------------------------------------------------- 1 | package ioctl 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | const ( 8 | IOC_NRBITS = 8 9 | IOC_TYPEBITS = 8 10 | 11 | IOC_SIZEBITS = 14 12 | IOC_DIRBITS = 2 13 | 14 | IOC_NRMASK = (1 << IOC_NRBITS) - 1 15 | IOC_TYPEMASK = (1 << IOC_TYPEBITS) - 1 16 | IOC_SIZEMASK = (1 << IOC_SIZEBITS) - 1 17 | IOC_DIRMASK = (1 << IOC_DIRBITS) - 1 18 | 19 | IOC_NRSHIFT = 0 20 | IOC_TYPESHIFT = IOC_NRSHIFT + IOC_NRBITS 21 | IOC_SIZESHIFT = IOC_TYPESHIFT + IOC_TYPEBITS 22 | IOC_DIRSHIFT = IOC_SIZESHIFT + IOC_SIZEBITS 23 | 24 | // Direction bits 25 | IOC_NONE = 0 26 | IOC_WRITE = 1 27 | IOC_READ = 2 28 | ) 29 | 30 | //...and for the drivers/sound files... 31 | const ( 32 | IOC_IN = IOC_WRITE << IOC_DIRSHIFT 33 | IOC_OUT = IOC_READ << IOC_DIRSHIFT 34 | IOC_INOUT = (IOC_WRITE | IOC_READ) << IOC_DIRSHIFT 35 | IOCSIZE_MASK = IOC_SIZEMASK << IOC_SIZESHIFT 36 | IOCSIZE_SHIFT = IOC_SIZESHIFT 37 | ) 38 | 39 | func IOC(dir, t, nr, size uintptr) uintptr { 40 | return (dir << IOC_DIRSHIFT) | (t << IOC_TYPESHIFT) | (nr << IOC_NRSHIFT) | (size << IOC_SIZESHIFT) 41 | } 42 | 43 | // used to create ioctl numbers 44 | 45 | func IO(t, nr uintptr) uintptr { 46 | return IOC(IOC_NONE, t, nr, 0) 47 | } 48 | 49 | func IOR(t, nr, size uintptr) uintptr { 50 | return IOC(IOC_READ, t, nr, size) 51 | } 52 | 53 | func IOW(t, nr, size uintptr) uintptr { 54 | return IOC(IOC_WRITE, t, nr, size) 55 | } 56 | 57 | func IOWR(t, nr, size uintptr) uintptr { 58 | return IOC(IOC_READ|IOC_WRITE, t, nr, size) 59 | } 60 | 61 | func IOR_BAD(t, nr, size uintptr) uintptr { 62 | return IOC(IOC_READ, t, nr, size) 63 | } 64 | 65 | func IOW_BAD(t, nr, size uintptr) uintptr { 66 | return IOC(IOC_WRITE, t, nr, size) 67 | } 68 | 69 | func IOWR_BAD(t, nr, size uintptr) uintptr { 70 | return IOC(IOC_READ|IOC_WRITE, t, nr, size) 71 | } 72 | 73 | func IOCTL(fd, op, arg uintptr) error { 74 | _, _, ep := syscall.Syscall(syscall.SYS_IOCTL, fd, op, arg) 75 | if ep != 0 { 76 | return syscall.Errno(ep) 77 | } 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /piface/piface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package piface is for interfacing with a PiFace Digital IO board. 3 | 4 | - More Info at http://www.piface.org.uk/products/piface_digital/ 5 | - Guides at http://prod.www.piface.org.uk/guides/ 6 | - goPi blink example at https://github.com/luismesas/goPi/blob/master/examples/blink_piface/blink_piface.go 7 | 8 | Its possible to connect up to four PiFace boards to a Raspberry Pi, however some jumper 9 | settings are required to set the hardware address. 10 | 11 | - See http://prod.www.piface.org.uk/guides/howto/PiFace_Digital_Jumper_Settings/ 12 | 13 | */ 14 | package piface 15 | 16 | 17 | 18 | import ( 19 | "fmt" 20 | "github.com/luismesas/goPi/MCP23S17" 21 | ) 22 | 23 | // A PiFace Digital board. 24 | type PiFaceDigital struct { 25 | mcp *MCP23S17.MCP23S17 26 | 27 | InputPins []*MCP23S17.MCP23S17RegisterBitNeg 28 | InputPort *MCP23S17.MCP23S17RegisterNeg 29 | OutputPins []*MCP23S17.MCP23S17RegisterBit 30 | OutputPort *MCP23S17.MCP23S17Register 31 | Leds []*MCP23S17.MCP23S17RegisterBit 32 | Relays []*MCP23S17.MCP23S17RegisterBit 33 | Switches []*MCP23S17.MCP23S17RegisterBit 34 | } 35 | 36 | // Create a new PiFaceDigital Instance 37 | func NewPiFaceDigital(hardware_addr byte, bus int, chip_select int) *PiFaceDigital { 38 | pfd := new(PiFaceDigital) 39 | pfd.mcp = MCP23S17.NewMCP23S17(hardware_addr, bus, chip_select) 40 | // pfd.device = interrupts.NewGPIOInterruptDevice() 41 | 42 | pfd.InputPins = make([]*MCP23S17.MCP23S17RegisterBitNeg, 8) 43 | for i := range pfd.InputPins { 44 | pfd.InputPins[i] = MCP23S17.NewMCP23S17RegisterBitNeg(uint(i), MCP23S17.GPIOB, pfd.mcp) 45 | } 46 | 47 | pfd.InputPort = MCP23S17.NewMCP23S17RegisterNeg(MCP23S17.GPIOB, pfd.mcp) 48 | 49 | pfd.OutputPins = make([]*MCP23S17.MCP23S17RegisterBit, 8) 50 | for i := range pfd.OutputPins { 51 | pfd.OutputPins[i] = MCP23S17.NewMCP23S17RegisterBit(uint(i), MCP23S17.GPIOA, pfd.mcp) 52 | } 53 | 54 | pfd.OutputPort = MCP23S17.NewMCP23S17Register(MCP23S17.GPIOA, pfd.mcp) 55 | 56 | pfd.Leds = make([]*MCP23S17.MCP23S17RegisterBit, 8) 57 | for i := range pfd.Leds { 58 | pfd.Leds[i] = MCP23S17.NewMCP23S17RegisterBit(uint(i), MCP23S17.GPIOA, pfd.mcp) 59 | } 60 | 61 | pfd.Relays = make([]*MCP23S17.MCP23S17RegisterBit, 2) 62 | for i := range pfd.Relays { 63 | pfd.Relays[i] = MCP23S17.NewMCP23S17RegisterBit(uint(i), MCP23S17.GPIOA, pfd.mcp) 64 | } 65 | 66 | pfd.Switches = make([]*MCP23S17.MCP23S17RegisterBit, 4) 67 | for i := range pfd.Switches { 68 | pfd.Switches[i] = MCP23S17.NewMCP23S17RegisterBit(uint(i), MCP23S17.GPIOB, pfd.mcp) 69 | } 70 | 71 | return pfd 72 | } 73 | 74 | // Initialize the board 75 | func (pfd *PiFaceDigital) InitBoard() error { 76 | 77 | err := pfd.mcp.Open() 78 | if err != nil { 79 | return err 80 | } 81 | 82 | var ioconfig byte 83 | ioconfig = (MCP23S17.BANK_OFF | 84 | MCP23S17.INT_MIRROR_OFF | 85 | MCP23S17.SEQOP_OFF | 86 | MCP23S17.DISSLW_OFF | 87 | MCP23S17.HAEN_ON | 88 | MCP23S17.ODR_OFF | 89 | MCP23S17.INTPOL_LOW) 90 | 91 | pfd.mcp.IOCON.SetValue(ioconfig) 92 | if pfd.mcp.IOCON.Value() != ioconfig { 93 | return fmt.Errorf("No PiFace Digital board detected (hardware_addr=%d, bus=%b, chip_select=%b).", pfd.mcp.HardwareAddress, pfd.mcp.Device.Bus, pfd.mcp.Device.Chip) 94 | } 95 | 96 | pfd.mcp.GPIOa.SetValue(0) 97 | pfd.mcp.IODIRa.SetValue(0) // GPIOA as outputs 98 | pfd.mcp.IODIRb.SetValue(0xFF) // GPIOB as inputs 99 | pfd.mcp.GPPUb.SetValue(0xFF) // input pullups on 100 | // pfd.EnableInterrupts() 101 | 102 | return nil 103 | } 104 | 105 | // Not Implemented 106 | func (pfd *PiFaceDigital) EnableInterrupts() error { 107 | return fmt.Errorf("EnableInterrupts() Not implemented") 108 | } 109 | 110 | // Not Implemented 111 | func (pfd *PiFaceDigital) Open() error { 112 | return fmt.Errorf("Open() Not implemented") 113 | } 114 | 115 | // Not Implemented 116 | func (pfd *PiFaceDigital) Close() error { 117 | return fmt.Errorf("Close() Not implemented") 118 | } 119 | -------------------------------------------------------------------------------- /rpi.go: -------------------------------------------------------------------------------- 1 | 2 | // Package rpi is a library for interfacing with Raspberry Pi IO 3 | package rpi 4 | 5 | // This is the wrong url here ??? 6 | import ( 7 | _ "github.com/luismesas/goPi/MCP23S17" 8 | _ "github.com/luismesas/goPi/ioctl" 9 | _ "github.com/luismesas/goPi/spi" 10 | _ "github.com/luismesas/goPi/piface" 11 | ) 12 | -------------------------------------------------------------------------------- /spi/SPIDevice.go: -------------------------------------------------------------------------------- 1 | package spi 2 | 3 | import ( 4 | "fmt" 5 | "github.com/luismesas/goPi/ioctl" 6 | "os" 7 | "unsafe" 8 | ) 9 | 10 | const SPIDEV = "/dev/spidev" 11 | const SPI_HELP_LINK = "http://piface.github.io/pifacecommon/installation.html#enable-the-spi-module" 12 | 13 | // Defaults 14 | const ( 15 | SPI_HARDWARE_ADDR = 0 16 | SPI_BUS = 0 17 | SPI_CHIP = 0 18 | SPI_DELAY = 0 19 | ) 20 | 21 | type SPIDevice struct { 22 | Bus int // 0 23 | Chip int // 0 24 | file *os.File // nil 25 | 26 | mode uint8 27 | bpw uint8 28 | speed uint32 29 | } 30 | 31 | // An SPI Device at /dev/spi.. 32 | func NewSPIDevice(bus int, chipSelect int) *SPIDevice { 33 | spi := new(SPIDevice) 34 | spi.Bus = bus 35 | spi.Chip = chipSelect 36 | 37 | return spi 38 | } 39 | 40 | // Opens SPI device 41 | func (spi *SPIDevice) Open() error { 42 | spiDevice := fmt.Sprintf("%s%d.%d", SPIDEV, spi.Bus, spi.Chip) 43 | 44 | var err error 45 | spi.file, err = os.OpenFile(spiDevice, os.O_RDWR, 0) 46 | // spi.file, err = os.Create(spiDevice) 47 | if err != nil { 48 | return fmt.Errorf("I can't see %s. Have you enabled the SPI module? (%s)", spiDevice, SPI_HELP_LINK) 49 | } 50 | 51 | return nil 52 | } 53 | 54 | // Closes SPI device 55 | func (spi *SPIDevice) Close() error { 56 | err := spi.file.Close() 57 | if err != nil { 58 | return fmt.Errorf("Error closing spi", err) 59 | } 60 | return nil 61 | } 62 | 63 | // Sends bytes over SPI channel and returns []byte response 64 | func (spi *SPIDevice) Send(bytes_to_send [3]byte) ([]byte, error) { 65 | wBuffer := bytes_to_send 66 | rBuffer := [3]byte{} 67 | 68 | // generates message 69 | transfer := SPI_IOC_TRANSFER{} 70 | transfer.txBuf = uint64(uintptr(unsafe.Pointer(&wBuffer))) 71 | transfer.rxBuf = uint64(uintptr(unsafe.Pointer(&rBuffer))) 72 | transfer.length = uint32(unsafe.Sizeof(wBuffer)) 73 | transfer.delayUsecs = SPI_DELAY 74 | transfer.bitsPerWord = spi.bpw 75 | transfer.speedHz = spi.speed 76 | 77 | // sends message over SPI 78 | err := ioctl.IOCTL(spi.file.Fd(), SPI_IOC_MESSAGE(1), uintptr(unsafe.Pointer(&transfer))) 79 | if err != nil { 80 | return nil, fmt.Errorf("Error on sending: %s\n", err) 81 | } 82 | 83 | // generates a valid response 84 | ret := make([]byte, unsafe.Sizeof(rBuffer)) 85 | for i := range ret { 86 | ret[i] = rBuffer[i] 87 | } 88 | 89 | return ret, nil 90 | } 91 | 92 | func (spi *SPIDevice) SetMode(mode uint8) error { 93 | spi.mode = mode 94 | err := ioctl.IOCTL(spi.file.Fd(), SPI_IOC_WR_MODE(), uintptr(unsafe.Pointer(&mode))) 95 | if err != nil { 96 | return fmt.Errorf("Error setting mode: %s\n", err) 97 | } 98 | return nil 99 | } 100 | 101 | func (spi *SPIDevice) SetBitsPerWord(bpw uint8) error { 102 | spi.bpw = bpw 103 | err := ioctl.IOCTL(spi.file.Fd(), SPI_IOC_WR_BITS_PER_WORD(), uintptr(unsafe.Pointer(&bpw))) 104 | if err != nil { 105 | return fmt.Errorf("Error setting bits per word: %s\n", err) 106 | } 107 | return nil 108 | } 109 | 110 | func (spi *SPIDevice) SetSpeed(speed uint32) error { 111 | spi.speed = speed 112 | err := ioctl.IOCTL(spi.file.Fd(), SPI_IOC_WR_MAX_SPEED_HZ(), uintptr(unsafe.Pointer(&speed))) 113 | if err != nil { 114 | return fmt.Errorf("Error setting speed: %s\n", err) 115 | } 116 | return nil 117 | } 118 | -------------------------------------------------------------------------------- /spi/definitions.go: -------------------------------------------------------------------------------- 1 | package spi 2 | 3 | // Defaults 4 | const ( 5 | DEFAULT_HARDWARE_ADDR = 0 6 | DEFAULT_BUS = 0 7 | DEFAULT_CHIP = 0 8 | ) 9 | -------------------------------------------------------------------------------- /spi/spi.go: -------------------------------------------------------------------------------- 1 | package spi 2 | 3 | import ( 4 | "github.com/luismesas/goPi/ioctl" 5 | "unsafe" 6 | ) 7 | 8 | const SPI_IOC_MAGIC = 107 9 | 10 | // Read of SPI mode (SPI_MODE_0..SPI_MODE_3) 11 | func SPI_IOC_RD_MODE() uintptr { 12 | return ioctl.IOR(SPI_IOC_MAGIC, 1, 1) 13 | } 14 | 15 | // Write of SPI mode (SPI_MODE_0..SPI_MODE_3) 16 | func SPI_IOC_WR_MODE() uintptr { 17 | return ioctl.IOW(SPI_IOC_MAGIC, 1, 1) 18 | } 19 | 20 | // Read SPI bit justification 21 | func SPI_IOC_RD_LSB_FIRST() uintptr { 22 | return ioctl.IOR(SPI_IOC_MAGIC, 2, 1) 23 | } 24 | 25 | // Write SPI bit justification 26 | func SPI_IOC_WR_LSB_FIRST() uintptr { 27 | return ioctl.IOW(SPI_IOC_MAGIC, 2, 1) 28 | } 29 | 30 | // Read SPI device word length (1..N) 31 | func SPI_IOC_RD_BITS_PER_WORD() uintptr { 32 | return ioctl.IOR(SPI_IOC_MAGIC, 3, 1) 33 | } 34 | 35 | // Write SPI device word length (1..N) 36 | func SPI_IOC_WR_BITS_PER_WORD() uintptr { 37 | return ioctl.IOW(SPI_IOC_MAGIC, 3, 1) 38 | } 39 | 40 | // Read SPI device default max speed hz 41 | func SPI_IOC_RD_MAX_SPEED_HZ() uintptr { 42 | return ioctl.IOR(SPI_IOC_MAGIC, 4, 4) 43 | } 44 | 45 | // Write SPI device default max speed hz 46 | func SPI_IOC_WR_MAX_SPEED_HZ() uintptr { 47 | return ioctl.IOW(SPI_IOC_MAGIC, 4, 4) 48 | } 49 | 50 | // Write custom SPI message 51 | func SPI_IOC_MESSAGE(n uintptr) uintptr { 52 | return ioctl.IOW(SPI_IOC_MAGIC, 0, uintptr(SPI_MESSAGE_SIZE(n))) 53 | } 54 | 55 | func SPI_MESSAGE_SIZE(n uintptr) uintptr { 56 | if (n * unsafe.Sizeof(SPI_IOC_TRANSFER{})) < (1 << ioctl.IOC_SIZEBITS) { 57 | return (n * unsafe.Sizeof(SPI_IOC_TRANSFER{})) 58 | } 59 | return 0 60 | } 61 | 62 | type SPI_IOC_TRANSFER struct { 63 | txBuf uint64 64 | rxBuf uint64 65 | length uint32 66 | speedHz uint32 67 | delayUsecs uint16 68 | bitsPerWord uint8 69 | csChange uint8 70 | pad uint32 71 | } 72 | --------------------------------------------------------------------------------