├── .gitignore ├── LICENSE ├── README.md ├── adc └── adc.go ├── common.go ├── gpio └── gpio.go ├── i2c └── i2c.go ├── pwm ├── pwm.go └── servo.go └── spi └── 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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Erik Unger 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go-embedded 2 | =========== 3 | 4 | Go package for embedded Linux development 5 | -------------------------------------------------------------------------------- /adc/adc.go: -------------------------------------------------------------------------------- 1 | package adc 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/SpaceLeap/go-embedded" 8 | ) 9 | 10 | type Name string 11 | 12 | const ( 13 | AIN0 Name = "AIN0" 14 | AIN1 Name = "AIN1" 15 | AIN2 Name = "AIN2" 16 | AIN3 Name = "AIN3" 17 | AIN4 Name = "AIN4" 18 | AIN5 Name = "AIN5" 19 | AIN6 Name = "AIN6" 20 | ) 21 | 22 | var ( 23 | deviceTree string 24 | prefixDir string 25 | ) 26 | 27 | func Init(deviceTreePrefix string) error { 28 | err := embedded.LoadDeviceTree(deviceTreePrefix) 29 | if err != nil { 30 | return err 31 | } 32 | deviceTree = deviceTreePrefix 33 | 34 | ocpDir, err := embedded.BuildPath("/sys/devices", "ocp") 35 | if err != nil { 36 | return err 37 | } 38 | prefixDir, err = embedded.BuildPath(ocpDir, "helper") 39 | if err != nil { 40 | return err 41 | } 42 | prefixDir += "/AIN" 43 | return nil 44 | } 45 | 46 | func Cleanup() error { 47 | return embedded.UnloadDeviceTree(deviceTree) 48 | } 49 | 50 | type ADC struct { 51 | ain Name 52 | file *os.File 53 | } 54 | 55 | func NewADC(ain Name) (*ADC, error) { 56 | filename := prefixDir + string(ain) 57 | file, err := os.Open(filename) 58 | if err != nil { 59 | return nil, err 60 | } 61 | 62 | return &ADC{ain, file}, nil 63 | } 64 | 65 | func (adc *ADC) Close() error { 66 | return adc.file.Close() 67 | } 68 | 69 | func (adc *ADC) AIn() Name { 70 | return adc.ain 71 | } 72 | 73 | func (adc *ADC) ReadRaw() (value float32) { 74 | adc.file.Seek(0, os.SEEK_SET) 75 | fmt.Fscan(adc.file, &value) 76 | return value 77 | } 78 | 79 | func (adc *ADC) ReadValue() (value float32) { 80 | return adc.ReadRaw() / 1800.0 81 | } 82 | -------------------------------------------------------------------------------- /common.go: -------------------------------------------------------------------------------- 1 | package embedded 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "path" 9 | "strings" 10 | "time" 11 | 12 | "github.com/ungerik/go-dry" 13 | ) 14 | 15 | var ctrlDir string 16 | 17 | func Init(devicesDir string) error { 18 | dir, err := BuildPath("/sys/devices", devicesDir) 19 | if err != nil { 20 | return err 21 | } 22 | ctrlDir = dir 23 | return nil 24 | } 25 | 26 | func BuildPath(partialPath, prefix string) (string, error) { 27 | dirFiles, err := ioutil.ReadDir(partialPath) 28 | if err != nil { 29 | return "", err 30 | } 31 | for _, file := range dirFiles { 32 | if file.IsDir() && strings.HasPrefix(file.Name(), prefix) { 33 | return path.Join(partialPath, file.Name()), nil 34 | } 35 | } 36 | return "", os.ErrNotExist 37 | } 38 | 39 | func IsDeviceTreeLoaded(name string) bool { 40 | data, err := dry.FileGetString(ctrlDir + "/slots") 41 | if err != nil { 42 | return false 43 | } 44 | return strings.Contains(data, name) 45 | } 46 | 47 | func LoadDeviceTree(name string) error { 48 | if IsDeviceTreeLoaded(name) { 49 | return nil 50 | } 51 | 52 | err := dry.FileSetString(ctrlDir+"/slots", name) 53 | if err == nil { 54 | time.Sleep(time.Millisecond * 200) 55 | } 56 | return err 57 | } 58 | 59 | func UnloadDeviceTree(name string) error { 60 | if !IsDeviceTreeLoaded(name) { 61 | return nil 62 | } 63 | 64 | file, err := os.OpenFile(ctrlDir+"/slots", os.O_RDWR, 0660) 65 | if err != nil { 66 | return err 67 | } 68 | defer file.Close() 69 | 70 | reader := bufio.NewReader(file) 71 | line, err := reader.ReadString('\n') 72 | for err != nil { 73 | if strings.Contains(line, name) { 74 | slot := strings.TrimSpace(line[:strings.IndexRune(line, ':')]) 75 | _, err = file.WriteString("-" + slot) 76 | return err 77 | } 78 | line, err = reader.ReadString('\n') 79 | } 80 | if err != io.EOF { 81 | return err 82 | } 83 | return nil 84 | } 85 | -------------------------------------------------------------------------------- /gpio/gpio.go: -------------------------------------------------------------------------------- 1 | package gpio 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "runtime" 7 | "syscall" 8 | "time" 9 | 10 | "github.com/ungerik/go-dry" 11 | ) 12 | 13 | type Value int 14 | 15 | const ( 16 | LOW Value = 0 17 | HIGH Value = 1 18 | 19 | _EPOLLET = 1 << 31 20 | ) 21 | 22 | type Edge string 23 | 24 | const ( 25 | EDGE_NONE Edge = "none" 26 | EDGE_RISING Edge = "rising" 27 | EDGE_FALLING Edge = "falling" 28 | EDGE_BOTH Edge = "both" 29 | ) 30 | 31 | type EdgeEvent struct { 32 | Time time.Time 33 | Value Value 34 | } 35 | 36 | type Direction string 37 | 38 | const ( 39 | DIRECTION_IN Direction = "in" 40 | DIRECTION_OUT Direction = "out" 41 | // ALT0 Direction = 4 42 | ) 43 | 44 | // TODO: How is this configured? 45 | // Maybe see https://github.com/adafruit/PyBBIO/blob/master/bbio/bbio.py 46 | type PullUpDown int 47 | 48 | const ( 49 | PUD_OFF PullUpDown = 0 50 | PUD_DOWN PullUpDown = 1 51 | PUD_UP PullUpDown = 2 52 | ) 53 | 54 | func IsExported(nr int) bool { 55 | return dry.FileExists(fmt.Sprintf("/sys/class/gpio/gpio%d/", nr)) 56 | } 57 | 58 | type GPIO struct { 59 | nr int 60 | valueFile *os.File 61 | epollFd dry.SyncInt 62 | edge Edge 63 | } 64 | 65 | // NewGPIO exports the GPIO pin nr. 66 | func NewGPIO(nr int, direction Direction) (gpio *GPIO, err error) { 67 | if !IsExported(nr) { 68 | err = dry.FilePrintf("/sys/class/gpio/export", "%d", nr) 69 | if err != nil { 70 | return nil, err 71 | } 72 | } 73 | 74 | gpio = &GPIO{nr: nr} 75 | 76 | err = gpio.SetDirection(direction) 77 | if err != nil { 78 | return nil, err 79 | } 80 | 81 | return gpio, nil 82 | } 83 | 84 | // Close unexports the GPIO pin. 85 | func (gpio *GPIO) Close() error { 86 | gpio.DisableEdgeDetection() 87 | 88 | if gpio.valueFile != nil { 89 | gpio.valueFile.Close() 90 | } 91 | 92 | if !IsExported(gpio.nr) { 93 | return nil 94 | } 95 | return dry.FilePrintf("/sys/class/gpio/unexport", "%d", gpio.nr) 96 | } 97 | 98 | func (gpio *GPIO) Direction() (Direction, error) { 99 | filename := fmt.Sprintf("/sys/class/gpio/gpio%d/direction", gpio.nr) 100 | direction, err := dry.FileGetString(filename) 101 | return Direction(direction), err 102 | } 103 | 104 | func (gpio *GPIO) SetDirection(direction Direction) error { 105 | filename := fmt.Sprintf("/sys/class/gpio/gpio%d/direction", gpio.nr) 106 | return dry.FileSetString(filename, string(direction)) 107 | } 108 | 109 | // func (gpio *GPIO) SetPullUpDown(pull PullUpDown) error { 110 | // file, err := os.OpenFile("/sys/kernel/debug/omap_mux/", os.O_WRONLY, 0660) 111 | // if err != nil { 112 | // return err 113 | // } 114 | // defer file.Close() 115 | // _, err = file.Write([]byte(fmt.Sprintf("%X", 0x07|1<<5|pull))) 116 | // return err 117 | // } 118 | 119 | func (gpio *GPIO) ensureValueFileIsOpen() error { 120 | if gpio.valueFile != nil { 121 | return nil 122 | } 123 | filename := fmt.Sprintf("/sys/class/gpio/gpio%d/value", gpio.nr) 124 | file, err := os.OpenFile(filename, os.O_RDWR|syscall.O_NONBLOCK, 0660) 125 | if err == nil { 126 | gpio.valueFile = file 127 | } 128 | return err 129 | } 130 | 131 | func (gpio *GPIO) Value() (Value, error) { 132 | if err := gpio.ensureValueFileIsOpen(); err != nil { 133 | return 0, err 134 | } 135 | val := make([]byte, 1) 136 | _, err := gpio.valueFile.ReadAt(val, 0) 137 | if err != nil { 138 | return 0, err 139 | } 140 | return Value(val[0] - '0'), nil 141 | } 142 | 143 | func (gpio *GPIO) SetValue(value Value) (err error) { 144 | if err = gpio.ensureValueFileIsOpen(); err != nil { 145 | return err 146 | } 147 | gpio.valueFile.Seek(0, os.SEEK_SET) 148 | _, err = gpio.valueFile.Write([]byte{'0' + byte(value)}) 149 | return err 150 | } 151 | 152 | func (gpio *GPIO) setEdge(edge Edge) error { 153 | if edge == gpio.edge { 154 | return nil 155 | } 156 | filename := fmt.Sprintf("/sys/class/gpio/gpio%d/edge", gpio.nr) 157 | err := dry.FileSetString(filename, string(edge)) 158 | if err == nil { 159 | gpio.edge = edge 160 | } 161 | return err 162 | } 163 | 164 | var dummyEpollEvents = make([]syscall.EpollEvent, 1) 165 | 166 | func (gpio *GPIO) WaitForEdge(edge Edge) (value Value, err error) { 167 | if err = gpio.setEdge(edge); err != nil { 168 | return 0, err 169 | } 170 | if err = gpio.ensureValueFileIsOpen(); err != nil { 171 | return 0, err 172 | } 173 | 174 | epollFd := gpio.epollFd.Get() 175 | 176 | if epollFd == 0 { 177 | epollFd, err = syscall.EpollCreate(1) 178 | if err != nil { 179 | return 0, err 180 | } 181 | 182 | event := &syscall.EpollEvent{ 183 | Events: syscall.EPOLLIN | syscall.EPOLLPRI | _EPOLLET, 184 | Fd: int32(gpio.valueFile.Fd()), 185 | } 186 | err = syscall.EpollCtl(epollFd, syscall.EPOLL_CTL_ADD, int(gpio.valueFile.Fd()), event) 187 | if err != nil { 188 | syscall.Close(epollFd) 189 | return 0, err 190 | } 191 | 192 | // first time triggers with current state, so ignore 193 | _, err = syscall.EpollWait(epollFd, dummyEpollEvents, -1) 194 | if err != nil { 195 | syscall.Close(epollFd) 196 | return 0, err 197 | } 198 | 199 | gpio.epollFd.Set(epollFd) 200 | } 201 | 202 | _, err = syscall.EpollWait(epollFd, dummyEpollEvents, -1) 203 | if err != nil { 204 | return 0, err 205 | } 206 | return gpio.Value() 207 | } 208 | 209 | func (gpio *GPIO) IsEdgeDetectionEnabled() bool { 210 | return gpio.epollFd.Get() != 0 211 | } 212 | 213 | func (gpio *GPIO) DisableEdgeDetection() { 214 | epollFd := gpio.epollFd.Swap(0) 215 | if epollFd != 0 { 216 | syscall.EpollCtl(epollFd, syscall.EPOLL_CTL_DEL, int(gpio.valueFile.Fd()), new(syscall.EpollEvent)) 217 | syscall.Close(epollFd) 218 | } 219 | gpio.setEdge(EDGE_NONE) 220 | } 221 | 222 | // StartEdgeDetectCallbacks starts a thread that calls callback for every 223 | // detected edge. An error or DisableEdgeDetection stops the thread. 224 | func (gpio *GPIO) StartEdgeDetectCallbacks(edge Edge, callback func(Value)) { 225 | go func() { 226 | runtime.LockOSThread() 227 | for { 228 | value, err := gpio.WaitForEdge(edge) 229 | if err != nil { 230 | return 231 | } 232 | callback(value) 233 | } 234 | }() 235 | } 236 | 237 | // StartEdgeDetectEvents starts a thread that sends EdgeEvent instances into 238 | // the events channel for every edge. EdgeEvent contains the time of the event, 239 | // to be also useful for buffered channels where the events are read later. 240 | // An error or DisableEdgeDetection stops the thread. 241 | func (gpio *GPIO) StartEdgeDetectEvents(edge Edge, events chan EdgeEvent) { 242 | gpio.StartEdgeDetectCallbacks(edge, func(value Value) { 243 | events <- EdgeEvent{time.Now(), value} 244 | }) 245 | } 246 | -------------------------------------------------------------------------------- /i2c/i2c.go: -------------------------------------------------------------------------------- 1 | package i2c 2 | 3 | // #include 4 | // #include 5 | // #include 6 | import "C" 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | "syscall" 12 | "unsafe" 13 | ) 14 | 15 | func SwapBytes(word uint16) uint16 { 16 | return word<<8 | word>>8 17 | } 18 | 19 | type Err struct { 20 | method string 21 | cause error 22 | } 23 | 24 | func (err Err) Error() string { 25 | return fmt.Sprintf("I2C.%s error: %s", err.method, err.cause) 26 | } 27 | 28 | func wrapErr(method string, err error) error { 29 | if err == nil { 30 | return nil 31 | } 32 | if e, ok := err.(Err); ok { 33 | e.method = method 34 | return e 35 | } 36 | return Err{method, err} 37 | } 38 | 39 | // I2C is a port of https://github.com/bivab/smbus-cffi/ 40 | type I2C struct { 41 | file *os.File 42 | address int 43 | } 44 | 45 | // Connects the object to the specified SMBus. 46 | func NewI2C(bus, address int) (*I2C, error) { 47 | filename := fmt.Sprintf("/dev/i2c-%d", bus) 48 | file, err := os.OpenFile(filename, os.O_RDWR, 0) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | i2c := &I2C{file: file, address: -1} 54 | err = i2c.SetAddress(address) 55 | if err != nil { 56 | file.Close() 57 | return nil, err 58 | } 59 | 60 | return i2c, nil 61 | } 62 | 63 | func (i2c *I2C) Close() error { 64 | return wrapErr("Close", i2c.file.Close()) 65 | } 66 | 67 | func (i2c *I2C) Address() int { 68 | return i2c.address 69 | } 70 | 71 | func (i2c *I2C) SetAddress(address int) error { 72 | if address != i2c.address { 73 | result, _, errno := syscall.Syscall(syscall.SYS_IOCTL, i2c.file.Fd(), C.I2C_SLAVE, uintptr(address)) 74 | if result != 0 { 75 | return Err{"SetAddress", errno} 76 | } 77 | i2c.address = address 78 | } 79 | return nil 80 | } 81 | 82 | func (i2c *I2C) smbusAccess(readWrite, register uint8, size int, data unsafe.Pointer) (uintptr, error) { 83 | args := C.struct_i2c_smbus_ioctl_data{ 84 | read_write: C.char(readWrite), 85 | command: C.__u8(register), 86 | size: C.int(size), 87 | data: (*C.union_i2c_smbus_data)(data), 88 | } 89 | result, _, errno := syscall.Syscall(syscall.SYS_IOCTL, i2c.file.Fd(), C.I2C_SMBUS, uintptr(unsafe.Pointer(&args))) 90 | if int(result) == -1 { 91 | return 0, errno 92 | } 93 | return result, nil 94 | } 95 | 96 | // WriteQuick sends a single bit to the device, at the place of the Rd/Wr bit. 97 | func (i2c *I2C) WriteQuick(value uint8) error { 98 | _, err := i2c.smbusAccess(value, 0, C.I2C_SMBUS_QUICK, nil) 99 | return wrapErr("SetAddress", err) 100 | } 101 | 102 | // ReadUint8 reads a single byte from a device, without specifying a device 103 | // register. Some devices are so simple that this interface is enough; for 104 | // others, it is a shorthand if you want to read the same register as in 105 | // the previous SMBus command. 106 | func (i2c *I2C) ReadUint8() (result uint8, err error) { 107 | _, err = i2c.smbusAccess(C.I2C_SMBUS_READ, 0, C.I2C_SMBUS_BYTE, unsafe.Pointer(&result)) 108 | if err != nil { 109 | return 0, wrapErr("ReadUint8", err) 110 | } 111 | return 0xFF & result, nil 112 | } 113 | 114 | // WriteUint8 sends a single byte to a device. 115 | func (i2c *I2C) WriteUint8(value uint8) error { 116 | _, err := i2c.smbusAccess(C.I2C_SMBUS_WRITE, value, C.I2C_SMBUS_BYTE, nil) 117 | return wrapErr("WriteUint8", err) 118 | } 119 | 120 | // ReadInt8 reads a single byte from a device, without specifying a device 121 | // register. Some devices are so simple that this interface is enough; for 122 | // others, it is a shorthand if you want to read the same register as in 123 | // the previous SMBus command. 124 | func (i2c *I2C) ReadInt8() (int8, error) { 125 | result, err := i2c.ReadUint8() 126 | return int8(result), wrapErr("ReadInt8", err) 127 | } 128 | 129 | // WriteInt8 sends a single byte to a device. 130 | func (i2c *I2C) WriteInt8(value int8) error { 131 | return wrapErr("WriteInt8", i2c.WriteUint8(uint8(value))) 132 | } 133 | 134 | // ReadUint8Reg reads a single byte from a device, from a designated register. 135 | func (i2c *I2C) ReadUint8Reg(register uint8) (result uint8, err error) { 136 | _, err = i2c.smbusAccess(C.I2C_SMBUS_READ, register, C.I2C_SMBUS_BYTE_DATA, unsafe.Pointer(&result)) 137 | if err != nil { 138 | return 0, wrapErr("ReadUint8Reg", err) 139 | } 140 | return 0xFF & result, nil 141 | } 142 | 143 | // WriteUint8Reg writes a single byte to a device, to a designated register. 144 | func (i2c *I2C) WriteUint8Reg(register uint8, value uint8) error { 145 | _, err := i2c.smbusAccess(C.I2C_SMBUS_WRITE, register, C.I2C_SMBUS_BYTE_DATA, unsafe.Pointer(&value)) 146 | return wrapErr("WriteUint8Reg", err) 147 | } 148 | 149 | // ReadInt8Reg reads a single byte from a device, from a designated register. 150 | func (i2c *I2C) ReadInt8Reg(register uint8) (int8, error) { 151 | result, err := i2c.ReadUint8Reg(register) 152 | return int8(result), wrapErr("ReadInt8Reg", err) 153 | } 154 | 155 | // WriteInt8Reg writes a single byte to a device, to a designated register. 156 | func (i2c *I2C) WriteInt8Reg(register uint8, value int8) error { 157 | return wrapErr("WriteInt8Reg", i2c.WriteUint8Reg(register, uint8(value))) 158 | } 159 | 160 | // ReadUint16Reg is very like ReadUint8Reg; again, data is read from a 161 | // device, from a designated register. 162 | // But this time, the data is a complete word (16 bits). 163 | func (i2c *I2C) ReadUint16Reg(register uint8) (result uint16, err error) { 164 | _, err = i2c.smbusAccess(C.I2C_SMBUS_READ, register, C.I2C_SMBUS_WORD_DATA, unsafe.Pointer(&result)) 165 | if err != nil { 166 | return 0, wrapErr("ReadUint16Reg", err) 167 | } 168 | return 0xFFFF & result, nil 169 | } 170 | 171 | // WriteUint16Reg is the opposite of the ReadUint16Reg operation. 16 bits 172 | // of data is written to a device, to the designated register. 173 | func (i2c *I2C) WriteUint16Reg(register uint8, value uint16) error { 174 | _, err := i2c.smbusAccess(C.I2C_SMBUS_WRITE, register, C.I2C_SMBUS_WORD_DATA, unsafe.Pointer(&value)) 175 | return wrapErr("WriteUint16Reg", err) 176 | } 177 | 178 | // ReadUint16RegSwapped is very like ReadUint8Reg; again, data is read from a 179 | // device, from a designated register. But this time, the data is a complete word (16 bits). 180 | // The bytes of the 16 bit value will be swapped. 181 | func (i2c *I2C) ReadUint16RegSwapped(register uint8) (result uint16, err error) { 182 | result, err = i2c.ReadUint16Reg(register) 183 | return SwapBytes(result), wrapErr("ReadUint16Reg", err) 184 | } 185 | 186 | // WriteUint16RegSwapped is the opposite of the ReadUint16RegSwapped operation. 16 bits 187 | // of data is written to a device, to the designated register. 188 | // The bytes of the 16 bit value will be swapped. 189 | func (i2c *I2C) WriteUint16RegSwapped(register uint8, value uint16) error { 190 | return wrapErr("WriteUint16RegSwapped", i2c.WriteUint16Reg(register, SwapBytes(value))) 191 | } 192 | 193 | // ReadInt16Reg is very like ReadInt8Reg; again, data is read from a 194 | // device, from a designated register. But this time, the data is a complete word (16 bits). 195 | func (i2c *I2C) ReadInt16Reg(register uint8) (int16, error) { 196 | result, err := i2c.ReadUint16Reg(register) 197 | return int16(result), wrapErr("ReadInt16Reg", err) 198 | } 199 | 200 | // WriteInt16Reg is the opposite of the ReadInt16Reg operation. 16 bits 201 | // of data is written to a device, to the designated register. 202 | func (i2c *I2C) WriteInt16Reg(register uint8, value int16) error { 203 | return wrapErr("WriteInt16Reg", i2c.WriteUint16Reg(register, uint16(value))) 204 | } 205 | 206 | // ReadInt16RegSwapped is very like ReadInt8RegSwapped; again, data is read from a 207 | // device, from a designated register. But this time, the data is a complete word (16 bits). 208 | // The bytes of the 16 bit value will be swapped. 209 | func (i2c *I2C) ReadInt16RegSwapped(register uint8) (int16, error) { 210 | result, err := i2c.ReadUint16RegSwapped(register) 211 | return int16(result), wrapErr("ReadInt16RegSwapped", err) 212 | } 213 | 214 | // WriteInt16RegSwapped is the opposite of the ReadInt16RegSwapped operation. 16 bits 215 | // of data is written to a device, to the designated register. 216 | // The bytes of the 16 bit value will be swapped. 217 | func (i2c *I2C) WriteInt16RegSwapped(register uint8, value int16) error { 218 | return wrapErr("WriteInt16RegSwapped", i2c.WriteUint16RegSwapped(register, uint16(value))) 219 | } 220 | 221 | // ProcessCall selects a device register (through the register byte), sends 222 | // 16 bits of data to it, and reads 16 bits of data in return. 223 | func (i2c *I2C) ProcessCall(register uint8, value uint16) (uint16, error) { 224 | _, err := i2c.smbusAccess(C.I2C_SMBUS_WRITE, register, C.I2C_SMBUS_PROC_CALL, unsafe.Pointer(&value)) 225 | if err != nil { 226 | return 0, wrapErr("ProcessCall", err) 227 | } 228 | return 0xFFFF & value, nil 229 | } 230 | 231 | // ProcessCallSwapped selects a device register (through the register byte), sends 232 | // 16 bits of data to it, and reads 16 bits of data in return. 233 | // The bytes of the 16 bit value will be swapped. 234 | func (i2c *I2C) ProcessCallSwapped(register uint8, value uint16) (uint16, error) { 235 | result, err := i2c.ProcessCall(register, SwapBytes(value)) 236 | return SwapBytes(result), wrapErr("ProcessCallSwapped", err) 237 | } 238 | 239 | // ProcessCallBlock reads a block of up to 32 bytes from a device, from a 240 | // designated register. 241 | func (i2c *I2C) ProcessCallBlock(register uint8, block []byte) ([]byte, error) { 242 | length := len(block) 243 | if length == 0 || length > C.I2C_SMBUS_BLOCK_MAX { 244 | return nil, wrapErr("ProcessCallBlock", fmt.Errorf("Length of block is %d, but must be in the range 1 to %d", length, C.I2C_SMBUS_BLOCK_MAX)) 245 | } 246 | data := make([]byte, length+1, C.I2C_SMBUS_BLOCK_MAX+2) 247 | data[0] = byte(length) 248 | copy(data[1:], block) 249 | _, err := i2c.smbusAccess(C.I2C_SMBUS_WRITE, register, C.I2C_SMBUS_BLOCK_PROC_CALL, unsafe.Pointer(&data[0])) 250 | if err != nil { 251 | return nil, wrapErr("ProcessCallBlock", err) 252 | } 253 | return data[1 : 1+data[0]], nil 254 | } 255 | 256 | // ReadBlock writes up to 32 bytes to a device, to a designated register. 257 | func (i2c *I2C) ReadBlock(register uint8) ([]byte, error) { 258 | data := make([]byte, C.I2C_SMBUS_BLOCK_MAX+2) 259 | _, err := i2c.smbusAccess(C.I2C_SMBUS_READ, register, C.I2C_SMBUS_BLOCK_DATA, unsafe.Pointer(&data[0])) 260 | if err != nil { 261 | return nil, wrapErr("ReadBlock", err) 262 | } 263 | return data[1 : 1+data[0]], nil 264 | } 265 | 266 | // WriteBlock selects a device register, sends 267 | // 1 to 31 bytes of data to it, and reads 1 to 31 bytes of data in return. 268 | func (i2c *I2C) WriteBlock(register uint8, block []byte) error { 269 | length := len(block) 270 | if length == 0 || length > C.I2C_SMBUS_BLOCK_MAX { 271 | return wrapErr("WriteBlock", fmt.Errorf("Length of block is %d, but must be in the range 1 to %d", length, C.I2C_SMBUS_BLOCK_MAX)) 272 | } 273 | data := make([]byte, length+1) 274 | data[0] = byte(length) 275 | copy(data[1:], block) 276 | _, err := i2c.smbusAccess(C.I2C_SMBUS_WRITE, register, C.I2C_SMBUS_BLOCK_DATA, unsafe.Pointer(&data[0])) 277 | return wrapErr("WriteBlock", err) 278 | } 279 | 280 | // TODO: Perform I2C Block Read transaction. 281 | // With if len == 32 then arg = C.I2C_SMBUS_I2C_BLOCK_BROKEN instead of I2C_SMBUS_I2C_BLOCK_DATA ??? 282 | 283 | func (i2c *I2C) Read(p []byte) (n int, err error) { 284 | n, err = i2c.file.Read(p) 285 | return n, wrapErr("Read", err) 286 | } 287 | 288 | func (i2c *I2C) Write(p []byte) (n int, err error) { 289 | n, err = i2c.file.Write(p) 290 | return n, wrapErr("Write", err) 291 | } 292 | -------------------------------------------------------------------------------- /pwm/pwm.go: -------------------------------------------------------------------------------- 1 | package pwm 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "time" 7 | 8 | "github.com/SpaceLeap/go-embedded" 9 | ) 10 | 11 | type Polarity uint 12 | 13 | const ( 14 | POLARITY_LOW Polarity = 0 15 | POLARITY_HIGH Polarity = 1 16 | ) 17 | 18 | type PWM struct { 19 | key string 20 | period time.Duration 21 | duty time.Duration 22 | polarity Polarity 23 | periodFile *os.File 24 | dutyFile *os.File 25 | polarityFile *os.File 26 | } 27 | 28 | var ( 29 | deviceTree string 30 | devicePrefix string 31 | ) 32 | 33 | func Init(deviceTreePrefix, pwmDevicePrefix string) error { 34 | err := embedded.LoadDeviceTree(deviceTreePrefix) 35 | if err != nil { 36 | return err 37 | } 38 | deviceTree = deviceTreePrefix 39 | devicePrefix = pwmDevicePrefix 40 | return nil 41 | } 42 | 43 | func Cleanup() error { 44 | return embedded.UnloadDeviceTree(deviceTree) 45 | } 46 | 47 | func NewPWM(key string, period, duty time.Duration, polarity Polarity) (*PWM, error) { 48 | err := embedded.LoadDeviceTree(devicePrefix + key) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | ocpDir, err := embedded.BuildPath("/sys/devices", "ocp") 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | //finds and builds the pwmTestPath, as it can be variable... 59 | pwmTestPath, err := embedded.BuildPath(ocpDir, "pwm_test_"+key) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | //create the path for the period and duty 65 | periodPath := pwmTestPath + "/period" 66 | dutyPath := pwmTestPath + "/duty" 67 | polarityPath := pwmTestPath + "/polarity" 68 | 69 | periodFile, err := os.OpenFile(periodPath, os.O_RDWR, 0660) 70 | if err != nil { 71 | return nil, err 72 | } 73 | dutyFile, err := os.OpenFile(dutyPath, os.O_RDWR, 0660) 74 | if err != nil { 75 | periodFile.Close() 76 | return nil, err 77 | } 78 | polarityFile, err := os.OpenFile(polarityPath, os.O_RDWR, 0660) 79 | if err != nil { 80 | periodFile.Close() 81 | dutyFile.Close() 82 | return nil, err 83 | } 84 | 85 | pwm := &PWM{ 86 | key: key, 87 | periodFile: periodFile, 88 | dutyFile: dutyFile, 89 | polarityFile: polarityFile, 90 | } 91 | 92 | err = pwm.SetPolarity(polarity) 93 | if err != nil { 94 | pwm.Close() 95 | return nil, err 96 | } 97 | err = pwm.SetPeriod(period) 98 | if err != nil { 99 | pwm.Close() 100 | return nil, err 101 | } 102 | err = pwm.SetDuty(duty) 103 | if err != nil { 104 | pwm.Close() 105 | return nil, err 106 | } 107 | 108 | return pwm, nil 109 | } 110 | 111 | func (pwm *PWM) Close() error { 112 | pwm.periodFile.Close() 113 | pwm.dutyFile.Close() 114 | pwm.polarityFile.Close() 115 | 116 | return embedded.UnloadDeviceTree(devicePrefix + pwm.key) 117 | } 118 | 119 | func (pwm *PWM) Key() string { 120 | return pwm.key 121 | } 122 | 123 | func (pwm *PWM) Period() time.Duration { 124 | return pwm.period 125 | } 126 | 127 | func (pwm *PWM) SetPeriod(period time.Duration) error { 128 | _, err := fmt.Fprintf(pwm.periodFile, "%d", period) 129 | if err != nil { 130 | return err 131 | } 132 | pwm.period = period 133 | return nil 134 | } 135 | 136 | func (pwm *PWM) Duty() time.Duration { 137 | return pwm.duty 138 | } 139 | 140 | func (pwm *PWM) SetDuty(duty time.Duration) error { 141 | _, err := fmt.Fprintf(pwm.dutyFile, "%d", duty) 142 | if err != nil { 143 | return err 144 | } 145 | pwm.duty = duty 146 | return nil 147 | } 148 | 149 | func (pwm *PWM) Polarity() Polarity { 150 | return pwm.polarity 151 | } 152 | 153 | func (pwm *PWM) SetPolarity(polarity Polarity) error { 154 | _, err := fmt.Fprintf(pwm.polarityFile, "%d", polarity) 155 | if err != nil { 156 | return err 157 | } 158 | pwm.polarity = polarity 159 | return nil 160 | } 161 | -------------------------------------------------------------------------------- /pwm/servo.go: -------------------------------------------------------------------------------- 1 | package pwm 2 | 3 | import "time" 4 | 5 | var ( 6 | ServoCenter time.Duration = 1500 * time.Microsecond 7 | ServoRange time.Duration = 1600 * time.Microsecond 8 | ) 9 | 10 | func servoPositionToDuty(position float32) time.Duration { 11 | return (ServoCenter - ServoRange/2) + time.Duration(float64(position)*float64(ServoRange)+0.5) 12 | } 13 | 14 | type Servo struct { 15 | pwm *PWM 16 | position float32 17 | } 18 | 19 | func NewServo(key string, position float32) (*Servo, error) { 20 | pwm, err := NewPWM(key, 2e7, servoPositionToDuty(position), POLARITY_LOW) 21 | if err != nil { 22 | return nil, err 23 | } 24 | servo := &Servo{ 25 | pwm: pwm, 26 | position: position, 27 | } 28 | return servo, nil 29 | } 30 | 31 | // Position returns the servo position in the range from 0.0 to 1.0 32 | func (servo *Servo) Position() float32 { 33 | return servo.position 34 | } 35 | 36 | // SetPosition sets the servo position in the range from 0.0 to 1.0. 37 | // position will be clamped if outside 0.0 to 1.0 38 | func (servo *Servo) SetPosition(position float32) error { 39 | if position < 0 { 40 | position = 0 41 | } else if position > 1 { 42 | position = 1 43 | } 44 | err := servo.pwm.SetDuty(servoPositionToDuty(position)) 45 | if err != nil { 46 | return err 47 | } 48 | servo.position = position 49 | return nil 50 | } 51 | 52 | // Closes the current servo instance 53 | func (servo *Servo) Close() error { 54 | return servo.pwm.Close() 55 | } 56 | -------------------------------------------------------------------------------- /spi/spi.go: -------------------------------------------------------------------------------- 1 | package spi 2 | 3 | // #include 4 | // #include 5 | import "C" 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "syscall" 11 | "unsafe" 12 | 13 | "github.com/SpaceLeap/go-embedded" 14 | ) 15 | 16 | const ( 17 | CPHA uint8 = 0x01 /* clock phase */ 18 | CPOL uint8 = 0x02 /* clock polarity */ 19 | CS_HIGH uint8 = 0x04 /* chipselect active high? */ 20 | LSB_FIRST uint8 = 0x08 /* per-word bits-on-wire */ 21 | THREE_WIRE uint8 = 0x10 /* SI/SO signals shared */ 22 | LOOP uint8 = 0x20 /* loopback mode */ 23 | ) 24 | 25 | // not used yet 26 | const ( 27 | NO_CS = 0x40 /* 1 dev/bus, no chipselect */ 28 | READY = 0x80 /* slave pulls low to pause */ 29 | TX_DUAL = 0x100 /* transmit with 2 wires */ 30 | TX_QUAD = 0x200 /* transmit with 4 wires */ 31 | RX_DUAL = 0x400 /* receive with 2 wires */ 32 | RX_QUAD = 0x800 /* receive with 4 wires */ 33 | ) 34 | 35 | // SPI mode as two bit pattern of 36 | // Clock Polarity and Phase [CPOL|CPHA] 37 | // min: 0b00 = 0 max: 0b11 = 3 38 | type Mode uint8 39 | 40 | const ( 41 | MODE_0 Mode = 0 /* (original MicroWire) */ 42 | MODE_1 Mode = Mode(CPHA) 43 | MODE_2 Mode = Mode(CPOL) 44 | MODE_3 Mode = Mode(CPOL | CPHA) 45 | ) 46 | 47 | var deviceTreePrefix string 48 | 49 | func Init(deviceTree string) { 50 | deviceTreePrefix = deviceTree 51 | } 52 | 53 | type SPI struct { 54 | bus int 55 | device int 56 | file *os.File /* open file descriptor: /dev/spi-X.Y */ 57 | mode uint8 /* current SPI mode */ 58 | bitsPerWord uint8 /* current SPI bits per word setting */ 59 | maxSpeedHz uint32 /* current SPI max speed setting in Hz */ 60 | } 61 | 62 | // NewSPI returns a new SPI object that is connected to the 63 | // specified SPI device interface. 64 | // 65 | // NewSPI(X,Y) will open /dev/spidev-X.Y 66 | // 67 | // SPI is an object type that allows SPI transactions 68 | // on hosts running the Linux kernel. The host kernel must have SPI 69 | // support and SPI device interface support. 70 | // All of these can be either built-in to the kernel, or loaded from modules. 71 | // 72 | // Because the SPI device interface is opened R/W, users of this 73 | // module usually must have root permissions. 74 | func NewSPI(bus, device int) (spi *SPI, err error) { 75 | err = embedded.LoadDeviceTree(fmt.Sprintf("%s%d", deviceTreePrefix, bus)) 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | spi = &SPI{bus: bus, device: device} 81 | 82 | path := fmt.Sprintf("/dev/spidev%d.%d", bus+1, device) 83 | spi.file, err = os.OpenFile(path, os.O_RDWR, 0) 84 | if err != nil { 85 | return nil, err 86 | } 87 | 88 | r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_MODE, uintptr(unsafe.Pointer(&spi.mode))) 89 | if r != 0 { 90 | return nil, err 91 | } 92 | 93 | r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_BITS_PER_WORD, uintptr(unsafe.Pointer(&spi.bitsPerWord))) 94 | if r != 0 { 95 | return nil, err 96 | } 97 | 98 | r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_MAX_SPEED_HZ, uintptr(unsafe.Pointer(&spi.maxSpeedHz))) 99 | if r != 0 { 100 | return nil, err 101 | } 102 | 103 | return spi, nil 104 | } 105 | 106 | // Disconnects the object from the interface. 107 | func (spi *SPI) Close() error { 108 | return spi.file.Close() 109 | } 110 | 111 | // Read len(data) bytes from SPI device. 112 | func (spi *SPI) Read(data []byte) (n int, err error) { 113 | return spi.file.Read(data) 114 | } 115 | 116 | // Write data to SPI device. 117 | func (spi *SPI) Write(data []byte) (n int, err error) { 118 | return spi.file.Write(data) 119 | } 120 | 121 | type spi_ioc_transfer struct { 122 | tx_buf uintptr 123 | rx_buf uintptr 124 | len uint32 125 | speed_hz uint32 126 | delay_usecs uint16 127 | bits_per_word uint8 128 | cs_change uint8 129 | pad uint32 130 | } 131 | 132 | // Xfer performs a SPI transaction. 133 | // CS will be released and reactivated between blocks. 134 | // delay specifies delay in usec between blocks. 135 | func (spi *SPI) Xfer(txBuf []byte, delay_usecs uint16) (rxBuf []byte, err error) { 136 | length := len(txBuf) 137 | rxBuf = make([]byte, length) 138 | 139 | xfer := make([]spi_ioc_transfer, length) 140 | for i := range xfer { 141 | xfer[i].tx_buf = uintptr(unsafe.Pointer(&txBuf[i])) 142 | xfer[i].rx_buf = uintptr(unsafe.Pointer(&rxBuf[i])) 143 | xfer[i].len = 1 144 | xfer[i].delay_usecs = delay_usecs 145 | } 146 | 147 | SPI_IOC_MESSAGE := C._IOC_WRITE< 16 { 244 | return fmt.Errorf("SPI bits per word %d outside of valid range 8 to 16", bits) 245 | } 246 | 247 | r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_WR_BITS_PER_WORD, uintptr(unsafe.Pointer(&bits))) 248 | if r != 0 { 249 | return err 250 | } 251 | 252 | var test uint8 253 | r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_BITS_PER_WORD, uintptr(unsafe.Pointer(&test))) 254 | if r != 0 { 255 | return err 256 | } 257 | 258 | if test == bits { 259 | spi.bitsPerWord = bits 260 | return nil 261 | } else { 262 | return fmt.Errorf("Could not set SPI bits per word %d", bits) 263 | } 264 | } 265 | 266 | func (spi *SPI) MaxSpeedHz() uint32 { 267 | return spi.maxSpeedHz 268 | } 269 | 270 | func (spi *SPI) SetMaxSpeedHz(maxSpeedHz uint32) error { 271 | r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_WR_MAX_SPEED_HZ, uintptr(unsafe.Pointer(&maxSpeedHz))) 272 | if r != 0 { 273 | return err 274 | } 275 | 276 | var test uint32 277 | r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_MAX_SPEED_HZ, uintptr(unsafe.Pointer(&test))) 278 | if r != 0 { 279 | return err 280 | } 281 | 282 | if test == maxSpeedHz { 283 | spi.maxSpeedHz = maxSpeedHz 284 | return nil 285 | } else { 286 | return fmt.Errorf("Could not set SPI max speed in hz %d", maxSpeedHz) 287 | } 288 | } 289 | 290 | func (spi *SPI) setModeFlag(flag bool, mask uint8) error { 291 | newMode := spi.mode 292 | if flag { 293 | newMode |= mask 294 | } else { 295 | newMode &= ^mask 296 | } 297 | err := spi.setModeInt(newMode) 298 | if err == nil { 299 | spi.mode = newMode 300 | } 301 | return err 302 | } 303 | 304 | func (spi *SPI) setModeInt(mode uint8) error { 305 | r, _, err := syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_WR_MODE, uintptr(unsafe.Pointer(&mode))) 306 | if r != 0 { 307 | return err 308 | } 309 | 310 | var test uint8 311 | r, _, err = syscall.Syscall(syscall.SYS_IOCTL, spi.file.Fd(), C.SPI_IOC_RD_MODE, uintptr(unsafe.Pointer(&test))) 312 | if r != 0 { 313 | return err 314 | } 315 | 316 | if test == mode { 317 | return nil 318 | } else { 319 | return fmt.Errorf("Could not set SPI mode %X", mode) 320 | } 321 | } 322 | --------------------------------------------------------------------------------