diff --git a/.travis.yml b/.travis.yml index 14c3702..f7153e1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,11 +5,10 @@ branches: - go-rpi go: - - 1.2.2 - - 1.3.3 - - 1.4 - - tip + - 1.6 + - 1.7 script: - go test -bench=. -v ./... | grep -v 'no test files' ; test ${PIPESTATUS[0]} -eq 0 - cd samples; find . -name "*.go" -print0 | xargs -0 -n1 go build + - cd ../embd; go build . diff --git a/CONTRIBUTORS b/CONTRIBUTORS index f44d830..e3ca3e1 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,8 +1,17 @@ -Karan Misra +adeschamps +alsm +Al S-M +Ben Delarre +Ben Schwartz +Gavin Cabbage +gotang Kashyap Kopparam Kunal Powar Marco P. Monteiro +Matthew Dale Nikesh Vora +SjB Steve Beaulac -Al S-M -Ben Delarre +Thorsten von Eicken +wiless +Wu Jiang diff --git a/README.md b/README.md index f37b5b5..93896f8 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,24 @@ **embd** is a hardware abstraction layer (HAL) for embedded systems. -It allows you to start your hardware hack on easily available hobby boards (like the Raspberry Pi, BeagleBone Black, etc.) by giving you staight forward access to the board's capabilities as well as a plethora of **sensors** (like accelerometers, gyroscopes, thermometers, etc.) and **controllers** (PWM generators, digital-to-analog convertors) for which we have written drivers. And when things get serious, you dont have to throw away the code. You carry forward the effort onto more custom designed boards where the HAL abstraction of EMBD will save you precious time. +It allows you to start your hardware hack on easily available hobby boards +(like the Raspberry Pi, BeagleBone Black, C.H.I.P., etc.) by giving you +straight-forward access to the board's capabilities as well as a plethora of +**sensors** (like accelerometers, gyroscopes, thermometers, etc.) and +**controllers** (PWM generators, digital-to-analog convertors) for +which it includes drivers. If you move to custom designed boards +you have to throw away your code: you carry forward the effort +where the HAL abstraction of EMBD will save you precious time. -Development supported and sponsored by [**SoStronk**](https://www.sostronk.com) and [**ThoughtWorks**](http://www.thoughtworks.com/) +The overall strategy used in embd is to use Linux device drivers to access gpio pins, +SPI and I2C buses, as well as interrupts. This makes it easy to port from one platform +to another and it enables kernel code to handle the devices as efficiently as possible. +What embd then adds is first a Golang library interface on top of the various Linux +devices and then another layer of user-level drivers for specific sensors and controllers +that are connected to gpio pins or one of the buses. + +Development supported and sponsored by [**SoStronk**](https://www.sostronk.com) and +[**ThoughtWorks**](http://www.thoughtworks.com/). Also, you might be interested in: [Why Golang?](https://github.com/kidoman/embd/wiki/Why-Go) @@ -12,7 +27,9 @@ Also, you might be interested in: [Why Golang?](https://github.com/kidoman/embd/ ## Getting Started -After installing Go* and setting up your [GOPATH](http://golang.org/doc/code.html#GOPATH), create your first .go file. We'll call it ```simpleblinker.go```. +Install Go version 1.6 or later to make compiling for ARM easy. +The set up your [GOPATH](http://golang.org/doc/code.html#GOPATH), +and create your first .go file. We'll call it `simpleblinker.go`. ```go package main @@ -32,11 +49,11 @@ func main() { } ``` -Then install the EMBD package (go1.2 and greater is required): +Then install the EMBD package: $ go get github.com/kidoman/embd -Build the binary*: +Build the binary for linux/ARM: $ export GOOS=linux $ export GOARCH=arm @@ -46,7 +63,7 @@ Copy the cross-compiled binary to your RaspberryPi*: $ scp simpleblinker pi@192.168.2.2:~ -Then run the program with ```sudo```*: +Then on the rPi run the program with ```sudo```*: $ sudo ./simpleblinker @@ -54,25 +71,21 @@ Then run the program with ```sudo```*: *** Notes** -* Please install the cross compilers. Mac users: ```brew install go --cross-compile-common```. [goxc](https://github.com/laher/goxc) can be a big help as well -* We are instructing the ```go``` compiler to create a binary which will run on the RaspberryPi processor * Assuming your RaspberryPi has an IP address of ```192.168.2.2```. Substitute as necessary -* ```sudo``` (root) permission is required as we are controlling the hardware by writing to special files -* This sample program is optimized for brevity and does not clean up after itself. Click here to see the [full version](https://github.com/kidoman/embd/blob/master/samples/fullblinker.go) +* `sudo` (root) permission is required as we are controlling the hardware by writing to special files +* This sample program is optimized for brevity and does not clean up after itself. Click here to + see the [full version](https://github.com/kidoman/embd/blob/master/samples/fullblinker.go) ## Getting Help -Join the [mailing list](https://groups.google.com/forum/#!forum/go-embd) +Join the [slack channel](https://gophers.slack.com/archives/embd) ## Platforms Supported * [RaspberryPi](http://www.raspberrypi.org/) (including [A+](http://www.raspberrypi.org/products/model-a-plus/) and [B+](http://www.raspberrypi.org/products/model-b-plus/)) * [RaspberryPi 2](http://www.raspberrypi.org/) +* [NextThing C.H.I.P](https://www.nextthing.co/pages/chip) * [BeagleBone Black](http://beagleboard.org/Products/BeagleBone%20Black) -* [Intel Edison](http://www.intel.com/content/www/us/en/do-it-yourself/galileo-maker-quark-board.html) **coming soon** -* [Radxa](http://radxa.com/) **coming soon** -* [Cubietruck](http://www.cubietruck.com/) **coming soon** -* Bring Your Own **coming soon** ## The command line tool @@ -80,8 +93,6 @@ Join the [mailing list](https://groups.google.com/forum/#!forum/go-embd) will install a command line utility ```embd``` which will allow you to quickly get started with prototyping. The binary should be available in your ```$GOPATH/bin```. However, to be able to run this on a ARM based device, you will need to build it with ```GOOS=linux``` and ```GOARCH=arm``` environment variables set. -But, since I am feeling so generous, a prebuilt/tested version is available for direct download and deployment [here](https://dl.dropboxusercontent.com/u/6727135/Binaries/embd/linux-arm/embd). - For example, if you run ```embd detect``` on a **BeagleBone Black**: root@beaglebone:~# embd detect @@ -214,17 +225,11 @@ platforms. ## Sensors Supported * **TMP006** Thermopile sensor [Documentation](http://godoc.org/github.com/kidoman/embd/sensor/tmp006), [Datasheet](http://www.adafruit.com/datasheets/tmp006.pdf), [Userguide](http://www.adafruit.com/datasheets/tmp006ug.pdf) - * **BMP085** Barometric pressure sensor [Documentation](http://godoc.org/github.com/kidoman/embd/sensor/bmp085), [Datasheet](https://www.sparkfun.com/datasheets/Components/General/BST-BMP085-DS000-05.pdf) - * **BMP180** Barometric pressure sensor [Documentation](http://godoc.org/github.com/kidoman/embd/sensor/bmp180), [Datasheet](http://www.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf) - * **LSM303** Accelerometer and magnetometer [Documentation](http://godoc.org/github.com/kidoman/embd/sensor/lsm303), [Datasheet](https://www.sparkfun.com/datasheets/Sensors/Magneto/LSM303%20Datasheet.pdf) - * **L3GD20** Gyroscope [Documentation](http://godoc.org/github.com/kidoman/embd/sensor/l3gd20), [Datasheet](http://www.adafruit.com/datasheets/L3GD20.pdf) - * **US020** Ultrasonic proximity sensor [Documentation](http://godoc.org/github.com/kidoman/embd/sensor/us020), [Product Page](http://www.digibay.in/sensor/object-detection-and-proximity?product_id=239) - * **BH1750FVI** Luminosity sensor [Documentation](http://godoc.org/github.com/kidoman/embd/sensor/bh1750fvi), [Datasheet](http://www.elechouse.com/elechouse/images/product/Digital%20light%20Sensor/bh1750fvi-e.pdf) ## Interfaces @@ -234,9 +239,7 @@ platforms. ## Controllers * **PCA9685** 16-channel, 12-bit PWM Controller with I2C protocol [Documentation](http://godoc.org/github.com/kidoman/embd/controller/pca9685), [Datasheet](http://www.adafruit.com/datasheets/PCA9685.pdf), [Product Page](http://www.adafruit.com/products/815) - * **MCP4725** 12-bit DAC [Documentation](http://godoc.org/github.com/kidoman/embd/controller/mcp4725), [Datasheet](http://www.adafruit.com/datasheets/mcp4725.pdf), [Product Page](http://www.adafruit.com/products/935) - * **ServoBlaster** RPi PWM/PCM based PWM controller [Documentation](http://godoc.org/github.com/kidoman/embd/controller/servoblaster), [Product Page](https://github.com/richardghirst/PiBits/tree/master/ServoBlaster) ## Convertors @@ -245,9 +248,11 @@ platforms. ## Contributing -We look forward to your pull requests, but contributions which abide by the [guidelines](https://github.com/kidoman/embd/blob/master/CONTRIBUTING.md) will get a free beer! - -File an [issue](https://github.com/kidoman/embd/issues), open a [pull request](https://github.com/kidoman/embd/pulls). We are waiting. +[Pull requests](https://github.com/kidoman/embd/pulls) that follow the +[guidelines](https://github.com/kidoman/embd/blob/master/CONTRIBUTING.md) are very appreciated. +If you find a problem but are not up to coding a fix please file an +[issue](https://github.com/kidoman/embd/issues). +Thank you! ## About diff --git a/controller/doc.go b/controller/doc.go new file mode 100644 index 0000000..01256a5 --- /dev/null +++ b/controller/doc.go @@ -0,0 +1,2 @@ +// Package controller is a container for the various device controllers supported by EMBD. +package controller diff --git a/controller/hd44780/hd44780_test.go b/controller/hd44780/hd44780_test.go index b05dddc..eee375d 100644 --- a/controller/hd44780/hd44780_test.go +++ b/controller/hd44780/hd44780_test.go @@ -131,6 +131,7 @@ type mockI2CBus struct { closed bool } +func (bus *mockI2CBus) ReadBytes(addr byte, num int) ([]byte, error) { return []byte{0x00}, nil } func (bus *mockI2CBus) ReadByte(addr byte) (byte, error) { return 0x00, nil } func (bus *mockI2CBus) WriteBytes(addr byte, value []byte) error { return nil } func (bus *mockI2CBus) ReadFromReg(addr, reg byte, value []byte) error { return nil } diff --git a/detect.go b/detect.go index a92ff4e..93f4500 100644 --- a/detect.go +++ b/detect.go @@ -31,6 +31,9 @@ const ( // HostRadxa represents the Radxa board. HostRadxa = "Radxa" + + // HostCHIP represents the NextThing C.H.I.P. + HostCHIP = "CHIP" ) func execOutput(name string, arg ...string) (output string, err error) { @@ -92,7 +95,7 @@ func cpuInfo() (model, hardware string, revision int, err error) { } revision = int(rev) case strings.HasPrefix(fields[0], "Hardware"): - hardware = fields[1] + hardware = strings.TrimSpace(fields[1]) case strings.HasPrefix(fields[0], "model name"): model = fields[1] } @@ -108,7 +111,9 @@ func DetectHost() (host Host, rev int, err error) { } if major < 3 || (major == 3 && minor < 8) { - return HostNull, 0, fmt.Errorf("embd: linux kernel versions lower than 3.8 are not supported. you have %v.%v.%v", major, minor, patch) + return HostNull, 0, fmt.Errorf( + "embd: linux kernel versions lower than 3.8 are not supported, "+ + "you have %v.%v.%v", major, minor, patch) } model, hardware, rev, err := cpuInfo() @@ -121,6 +126,13 @@ func DetectHost() (host Host, rev int, err error) { return HostBBB, rev, nil case strings.Contains(hardware, "BCM2708") || strings.Contains(hardware, "BCM2709"): return HostRPi, rev, nil + case hardware == "Allwinner sun4i/sun5i Families": + if major < 4 || (major == 4 && minor < 4) { + return HostNull, 0, fmt.Errorf( + "embd: linux kernel version 4.4+ required, you have %v.%v", + major, minor) + } + return HostCHIP, rev, nil default: return HostNull, 0, fmt.Errorf(`embd: your host "%v:%v" is not supported at this moment. request support at https://github.com/kidoman/embd/issues`, host, model) } diff --git a/doc.go b/doc.go index 80118d4..a31cab1 100644 --- a/doc.go +++ b/doc.go @@ -1,88 +1,119 @@ /* - Package embd provides a hardware abstraction layer for doing embedded programming - on supported platforms like the Raspberry Pi and BeagleBone Black. Most of the examples below - will work without change (i.e. the same binary) on all supported platforms. How cool is that? +Package embd provides a hardware abstraction layer for doing embedded programming +on supported platforms like the Raspberry Pi, BeagleBone Black and CHIP. Most of the examples below +will work without change (i.e. the same binary) on all supported platforms. - Although samples are all present in the samples folder, we will show a few choice examples here. +== Overall structure - Use the LED driver to toggle LEDs on the BBB: +It's best to think of the top-level embd package as a switchboard that doesn't implement anything +on its own but rather relies on sub-packages for hosts drivers and devices and stitches them +together. The exports in the top-level package serve a number of different purposes, +which can be confusing at first: +- it defines a number of driver interfaces, such as the GPIODriver, this is the interface that +the driver for each specific platform must implement and is not something of concern to the +typical user. +- it defines the main low-level hardware interface types: analog pins, digital pins, +interrupt pins, I2Cbuses, SPI buses, PWM pins and LEDs. Each type has a New function to +instantiate one of these pins or buses. +- it defines a number of InitXXX functions that initialize the various drivers, however, these +are called by the coresponding NewXXX functions, so can be ignored. +- it defines a number of top-level convenience functions, such as DigitalWrite, that can be +called as 1-liners instead of first instantiating a DigitalPin and then writing to it - import "github.com/kidoman/embd" - ... - embd.InitLED() - defer embd.CloseLED() - ... - led, err := embd.NewLED("USR3") - ... - led.Toggle() +To get started a host driver needs to be registered with the top-level embd package. This is +most easily accomplished by doing an "underscore import" on of the sub-packages of embd/host, +e.g., `import _ "github.com/kidoman/embd/host/chip"`. An `Init()` function in the host driver +registers all the individual drivers with embd. - Even shorter while prototyping: +After getting the host driver the next step might be to instantiate a GPIO pin using +`NewDigitalPin` or an I2CBus using `NewI2CBus`. Such a pin or bus can be used directly but +often it is passed into the initializer of a sensor, controller or other user-level driver +which provides a high-level interface to some device. For example, the New function +for the BMP180 type in the `embd/sensor/bmp180` package takes an I2CBus as argument, which +it will use to reach the sensor. - import "github.com/kidoman/embd" - ... - embd.InitLED() - defer embd.CloseLED() - ... - embd.ToggleLED(3) +== Samples - BBB + PWM: +This section shows a few choice samples, more are available in the samples folder. - import "github.com/kidoman/embd" - ... - embd.InitGPIO() - defer embd.CloseGPIO() - ... - pwm, _ := embd.NewPWMPin("P9_14") - defer pwm.Close() - ... - pwm.SetDuty(1000) +Use the LED driver to toggle LEDs on the BBB: - Control GPIO pins on the RaspberryPi / BeagleBone Black: + import "github.com/kidoman/embd" + ... + embd.InitLED() + defer embd.CloseLED() + ... + led, err := embd.NewLED("USR3") + ... + led.Toggle() - import "github.com/kidoman/embd" - ... - embd.InitGPIO() - defer embd.CloseGPIO() - ... - embd.SetDirection(10, embd.Out) - embd.DigitalWrite(10, embd.High) +Even shorter while prototyping: - Could also do: + import "github.com/kidoman/embd" + ... + embd.InitLED() + defer embd.CloseLED() + ... + embd.ToggleLED(3) - import "github.com/kidoman/embd" - ... - embd.InitGPIO() - defer embd.CloseGPIO() - ... - pin, err := embd.NewDigitalPin(10) - ... - pin.SetDirection(embd.Out) - pin.Write(embd.High) +BBB + PWM: - Or read data from the Bosch BMP085 barometric sensor: + import "github.com/kidoman/embd" + ... + embd.InitGPIO() + defer embd.CloseGPIO() + ... + pwm, _ := embd.NewPWMPin("P9_14") + defer pwm.Close() + ... + pwm.SetDuty(1000) - import "github.com/kidoman/embd" - import "github.com/kidoman/embd/sensor/bmp085" - ... - bus := embd.NewI2CBus(1) - ... - baro := bmp085.New(bus) - ... - temp, err := baro.Temperature() - altitude, err := baro.Altitude() +Control GPIO pins on the RaspberryPi / BeagleBone Black: - Even find out the heading from the LSM303 magnetometer: + import "github.com/kidoman/embd" + ... + embd.InitGPIO() + defer embd.CloseGPIO() + ... + embd.SetDirection(10, embd.Out) + embd.DigitalWrite(10, embd.High) - import "github.com/kidoman/embd" - import "github.com/kidoman/embd/sensor/lsm303" - ... - bus := embd.NewI2CBus(1) - ... - mag := lsm303.New(bus) - ... - heading, err := mag.Heading() +Could also do: - The above two examples depend on I2C and therefore will work without change on almost all - platforms. + import "github.com/kidoman/embd" + ... + embd.InitGPIO() + defer embd.CloseGPIO() + ... + pin, err := embd.NewDigitalPin(10) + ... + pin.SetDirection(embd.Out) + pin.Write(embd.High) + +Or read data from the Bosch BMP085 barometric sensor: + + import "github.com/kidoman/embd" + import "github.com/kidoman/embd/sensor/bmp085" + ... + bus := embd.NewI2CBus(1) + ... + baro := bmp085.New(bus) + ... + temp, err := baro.Temperature() + altitude, err := baro.Altitude() + +Even find out the heading from the LSM303 magnetometer: + + import "github.com/kidoman/embd" + import "github.com/kidoman/embd/sensor/lsm303" + ... + bus := embd.NewI2CBus(1) + ... + mag := lsm303.New(bus) + ... + heading, err := mag.Heading() + +The above two examples depend on I2C and therefore will work without change on almost all +platforms. */ package embd diff --git a/gpio.go b/gpio.go index 26bd866..3d7baf7 100644 --- a/gpio.go +++ b/gpio.go @@ -33,7 +33,11 @@ const ( EdgeBoth Edge = "both" ) -// InterruptPin implements access to a Interruptable capable GPIO pin. +// InterruptPin implements access to an interrupt capable GPIO pin. +// The basic capability provided is to watch for a transition on the pin and +// generate a callback to a handler when a transition occurs. +// On Linux the underlying implementation generally uses epoll to receive the +// interrupts at user-level. type InterruptPin interface { // Start watching this pin for interrupt diff --git a/host/bbb/bbb.go b/host/bbb/bbb.go index f909627..1f42d7d 100644 --- a/host/bbb/bbb.go +++ b/host/bbb/bbb.go @@ -95,7 +95,7 @@ var ledMap = embd.LEDMap{ "beaglebone:green:usr3": []string{"3", "USR3", "usr3"}, } -var spiDeviceMinor byte = 1 +var spiDeviceMinor int = 1 func ensureFeatureEnabled(id string) error { glog.V(3).Infof("bbb: enabling feature %v", id) diff --git a/host/chip/README.md b/host/chip/README.md new file mode 100644 index 0000000..51fd49e --- /dev/null +++ b/host/chip/README.md @@ -0,0 +1,34 @@ +# Using embd on CHIP + +The CHIP drivers support gpio, I2C, SPI, and pin interrupts. Not supported are PWM or LED. +The names of the pins on chip have multiple aliases. The official CHIP pin names are supported, +for example XIO-P1 or LCD-D2 and the pin number are also supported, such as U14-14 (same as XIO-P1) +or U13-17. Some of the alternate function names are also supported, like "SPI2_MOSI", and the +linux 4.4 kernel gpio pin numbers as well, e.g., 1017 for XIO-P1. Finally, the official GPIO pins +(XIO-P0 thru XIO-P7) can be addressed as gpio0-gpio7. + +A simple demo to blink an LED connected with a small resistor between XIO-P6 and 3.3V is + +``` +package main +import ( + "time" + "github.com/kidoman/embd" + _ "github.com/kidoman/embd/host/chip" +) + +func main() { + embd.InitGPIO() + defer embd.CloseGPIO() + + embd.SetDirection("gpio6", embd.Out) + on := 0 + for { + embd.DigitalWrite("gpio6", on) + on = 1 - on + time.Sleep(250 * time.Millisecond) + } +} +``` +Run it as root: `sudo ./blinky` + diff --git a/host/chip/chip.go b/host/chip/chip.go new file mode 100644 index 0000000..d849df4 --- /dev/null +++ b/host/chip/chip.go @@ -0,0 +1,99 @@ +// Copyright 2016 by Thorsten von Eicken, see LICENSE file + +// Package chip provides NextThing C.H.I.P. support. +// References: +// http://docs.getchip.com/chip.html#chip-hardware +// http://www.chip-community.org/index.php/Hardware_Information +// +// The following features are supported on Linux kernel 4.4+ +// GPIO (digital (rw)) +// I²C +// SPI +// Could add LED support by following https://bbs.nextthing.co/t/pwr-and-stat-leds/748/5 + +package chip + +import ( + "github.com/kidoman/embd" + "github.com/kidoman/embd/host/generic" +) + +var spiDeviceMinor = 32766 + +var chipPins = embd.PinMap{ + // official GPIO pins (U14 connector) using the pcf8574a + &embd.PinDesc{"XIO-P0", []string{"1016", "0", "U14-13", "gpio0"}, embd.CapDigital, 1016, 0}, + &embd.PinDesc{"XIO-P1", []string{"1017", "1", "U14-14", "gpio1"}, embd.CapDigital, 1017, 0}, + &embd.PinDesc{"XIO-P2", []string{"1018", "2", "U14-15", "gpio2"}, embd.CapDigital, 1018, 0}, + &embd.PinDesc{"XIO-P3", []string{"1019", "3", "U14-16", "gpio3"}, embd.CapDigital, 1019, 0}, + &embd.PinDesc{"XIO-P4", []string{"1020", "4", "U14-17", "gpio4"}, embd.CapDigital, 1020, 0}, + &embd.PinDesc{"XIO-P5", []string{"1021", "5", "U14-18", "gpio5"}, embd.CapDigital, 1021, 0}, + &embd.PinDesc{"XIO-P6", []string{"1022", "6", "U14-19", "gpio6"}, embd.CapDigital, 1022, 0}, + &embd.PinDesc{"XIO-P7", []string{"1023", "7", "U14-20", "gpio7"}, embd.CapDigital, 1023, 0}, + + // pins usable on the U13 connector + &embd.PinDesc{"TWI1-SDA", []string{"48", "U13-9", "I2C0_SDA"}, embd.CapDigital | embd.CapI2C, 48, 0}, + &embd.PinDesc{"TWI1-SCK", []string{"47", "U13-11", "I2C0_SCK"}, embd.CapDigital | embd.CapI2C, 47, 0}, + &embd.PinDesc{"PWM0", []string{"34", "U13-18"}, embd.CapDigital | embd.CapPWM, 34, 0}, + &embd.PinDesc{"LCD-D2", []string{"98", "U13-17"}, embd.CapDigital, 98, 0}, + &embd.PinDesc{"LCD-D3", []string{"99", "U13-20"}, embd.CapDigital, 99, 0}, + &embd.PinDesc{"LCD-D4", []string{"100", "U13-19"}, embd.CapDigital, 100, 0}, + &embd.PinDesc{"LCD-D5", []string{"101", "U13-22"}, embd.CapDigital, 101, 0}, + &embd.PinDesc{"LCD-D6", []string{"102", "U13-21"}, embd.CapDigital, 102, 0}, + &embd.PinDesc{"LCD-D7", []string{"103", "U13-24"}, embd.CapDigital, 103, 0}, + &embd.PinDesc{"LCD-D10", []string{"106", "U13-23"}, embd.CapDigital, 106, 0}, + &embd.PinDesc{"LCD-D11", []string{"107", "U13-26"}, embd.CapDigital, 107, 0}, + &embd.PinDesc{"LCD-D12", []string{"108", "U13-25"}, embd.CapDigital, 108, 0}, + &embd.PinDesc{"LCD-D13", []string{"109", "U13-28"}, embd.CapDigital, 109, 0}, + &embd.PinDesc{"LCD-D14", []string{"110", "U13-27"}, embd.CapDigital, 110, 0}, + &embd.PinDesc{"LCD-D15", []string{"111", "U13-30"}, embd.CapDigital, 111, 0}, + &embd.PinDesc{"LCD-D18", []string{"114", "U13-29"}, embd.CapDigital, 114, 0}, + &embd.PinDesc{"LCD-D19", []string{"115", "U13-32"}, embd.CapDigital, 115, 0}, + &embd.PinDesc{"LCD-D20", []string{"116", "U13-31"}, embd.CapDigital, 116, 0}, + &embd.PinDesc{"LCD-D21", []string{"117", "U13-34"}, embd.CapDigital, 117, 0}, + &embd.PinDesc{"LCD-D22", []string{"118", "U13-33"}, embd.CapDigital, 118, 0}, + &embd.PinDesc{"LCD-D23", []string{"119", "U13-36"}, embd.CapDigital, 119, 0}, + &embd.PinDesc{"LCD-CLK", []string{"120", "U13-35"}, embd.CapDigital, 120, 0}, + &embd.PinDesc{"LCD-VSYNC", []string{"123", "U13-37"}, embd.CapDigital, 123, 0}, + &embd.PinDesc{"LCD-HSYNC", []string{"122", "U13-38"}, embd.CapDigital, 122, 0}, + &embd.PinDesc{"LCD-DE", []string{"121", "U13-40"}, embd.CapDigital, 121, 0}, + + // pins usable on the U14 connector + &embd.PinDesc{"UART1-TX", []string{"195", "U14-3", "EINT3"}, embd.CapDigital | embd.CapUART, 195, 0}, + &embd.PinDesc{"UART1-RX", []string{"196", "U14-5", "EINT4"}, embd.CapDigital | embd.CapUART, 196, 0}, + &embd.PinDesc{"AP-EINT1", []string{"193", "U14-23", "EINT1"}, embd.CapDigital, 193, 0}, + &embd.PinDesc{"AP-EINT3", []string{"35", "U14-24", "EINT3"}, embd.CapDigital, 35, 0}, + &embd.PinDesc{"TWI2-SDA", []string{"50", "U14-25", "I2C2_SDA"}, embd.CapDigital | embd.CapI2C, 50, 0}, + &embd.PinDesc{"TWI2-SCK", []string{"49", "U14-26", "I2C2_SCK"}, embd.CapDigital | embd.CapI2C, 49, 0}, + &embd.PinDesc{"CSIPCK", []string{"128", "U14-27", "SPI2_SCO", "SPI2_CS0"}, embd.CapDigital | embd.CapSPI, 128, 0}, + &embd.PinDesc{"CSICK", []string{"129", "U14-28", "SPI2_CLK"}, embd.CapDigital | embd.CapSPI, 129, 0}, + &embd.PinDesc{"CSIHSYNC", []string{"130", "U14-29", "SPI2_MOSI"}, embd.CapDigital | embd.CapSPI, 130, 0}, + &embd.PinDesc{"CSIVSYNC", []string{"131", "U14-30", "SPI2_MISO"}, embd.CapDigital | embd.CapSPI, 131, 0}, + &embd.PinDesc{"CSID0", []string{"132", "U14-31"}, embd.CapDigital, 132, 0}, + &embd.PinDesc{"CSID1", []string{"133", "U14-32"}, embd.CapDigital, 133, 0}, + &embd.PinDesc{"CSID2", []string{"134", "U14-33"}, embd.CapDigital, 134, 0}, + &embd.PinDesc{"CSID3", []string{"135", "U14-34"}, embd.CapDigital, 135, 0}, + &embd.PinDesc{"CSID4", []string{"136", "U14-35"}, embd.CapDigital, 136, 0}, + &embd.PinDesc{"CSID5", []string{"137", "U14-36"}, embd.CapDigital, 137, 0}, + &embd.PinDesc{"CSID6", []string{"138", "U14-37", "UART1_TX"}, embd.CapDigital | embd.CapUART, 138, 0}, + &embd.PinDesc{"CSID7", []string{"139", "U14-38", "UART1_RX"}, embd.CapDigital | embd.CapUART, 139, 0}, +} + +func init() { + embd.Register(embd.HostCHIP, func(rev int) *embd.Descriptor { + return &embd.Descriptor{ + GPIODriver: func() embd.GPIODriver { + return embd.NewGPIODriver(chipPins, generic.NewDigitalPin, nil, nil) + }, + I2CDriver: func() embd.I2CDriver { + return embd.NewI2CDriver(generic.NewI2CBus) + }, + //LEDDriver: func() embd.LEDDriver { + // return embd.NewLEDDriver(ledMap, generic.NewLED) + //}, + SPIDriver: func() embd.SPIDriver { + return embd.NewSPIDriver(spiDeviceMinor, generic.NewSPIBus, nil) + }, + } + }) +} diff --git a/host/generic/digitalpin.go b/host/generic/digitalpin.go index 80a81ea..525eeb4 100644 --- a/host/generic/digitalpin.go +++ b/host/generic/digitalpin.go @@ -10,6 +10,7 @@ import ( "os" "path" "strconv" + "syscall" "time" "github.com/kidoman/embd" @@ -69,6 +70,9 @@ func (p *digitalPin) export() error { } defer exporter.Close() _, err = exporter.WriteString(strconv.Itoa(p.n)) + if e, ok := err.(*os.PathError); ok && e.Err == syscall.EBUSY { + return nil // EBUSY -> the pin has already been exported + } return err } diff --git a/host/generic/i2cbus.go b/host/generic/i2cbus.go index dc8e74c..fbe6f55 100644 --- a/host/generic/i2cbus.go +++ b/host/generic/i2cbus.go @@ -101,6 +101,28 @@ func (b *i2cBus) ReadByte(addr byte) (byte, error) { return bytes[0], nil } +func (b *i2cBus) ReadBytes(addr byte, num int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + + if err := b.init(); err != nil { + return []byte{0}, err + } + + if err := b.setAddress(addr); err != nil { + return []byte{0}, err + } + + bytes := make([]byte, num) + n, _ := b.file.Read(bytes) + + if n != num { + return []byte{0}, fmt.Errorf("i2c: Unexpected number (%v) of bytes read", n) + } + + return bytes, nil +} + func (b *i2cBus) WriteByte(addr, value byte) error { b.mu.Lock() defer b.mu.Unlock() @@ -223,7 +245,7 @@ func (b *i2cBus) WriteToReg(addr, reg byte, value []byte) error { message.addr = uint16(addr) message.flags = 0 message.len = uint16(len(outbuf)) - message.buf = uintptr(unsafe.Pointer(&hdrp.Data)) + message.buf = uintptr(unsafe.Pointer(hdrp.Data)) var packets i2c_rdwr_ioctl_data diff --git a/host/generic/interrupt.go b/host/generic/interrupt.go index 4052283..7f9b766 100644 --- a/host/generic/interrupt.go +++ b/host/generic/interrupt.go @@ -61,11 +61,13 @@ func initEpollListener() *epollListener { if err != nil { panic(fmt.Sprintf("EpollWait error: %v", err)) } + listener.mu.Lock() for i := 0; i < n; i++ { if irq, ok := listener.interruptablePins[int(epollEvents[i].Fd)]; ok { irq.Signal() } } + listener.mu.Unlock() } }() return listener diff --git a/host/generic/spibus.go b/host/generic/spibus.go index ad3bcec..a33576f 100644 --- a/host/generic/spibus.go +++ b/host/generic/spibus.go @@ -36,12 +36,14 @@ type spiIOCTransfer struct { speedHz uint32 delayus uint16 bitsPerWord uint8 + csChange uint8 + pad uint32 } type spiBus struct { file *os.File - spiDevMinor byte + spiDevMinor int channel byte mode byte @@ -61,7 +63,7 @@ func spiIOCMessageN(n uint32) uint32 { return (spiIOCMessage0 + (n * spiIOCIncrementor)) } -func NewSPIBus(spiDevMinor, mode, channel byte, speed, bpw, delay int, i func() error) embd.SPIBus { +func NewSPIBus(spiDevMinor int, mode, channel byte, speed, bpw, delay int, i func() error) embd.SPIBus { return &spiBus{ spiDevMinor: spiDevMinor, mode: mode, diff --git a/host/rpi/rpi.go b/host/rpi/rpi.go index 0a82a4b..9279ca8 100644 --- a/host/rpi/rpi.go +++ b/host/rpi/rpi.go @@ -13,7 +13,7 @@ import ( "github.com/kidoman/embd/host/generic" ) -var spiDeviceMinor = byte(0) +var spiDeviceMinor = 0 var rev1Pins = embd.PinMap{ &embd.PinDesc{ID: "P1_3", Aliases: []string{"0", "GPIO_0", "SDA", "I2C0_SDA"}, Caps: embd.CapDigital | embd.CapI2C, DigitalLogical: 0}, diff --git a/i2c.go b/i2c.go index 8bdb876..6a5d1ec 100644 --- a/i2c.go +++ b/i2c.go @@ -6,6 +6,8 @@ package embd type I2CBus interface { // ReadByte reads a byte from the given address. ReadByte(addr byte) (value byte, err error) + // ReadBytes reads a slice of bytes from the given address. + ReadBytes(addr byte, num int) (value []byte, err error) // WriteByte writes a byte to the given address. WriteByte(addr, value byte) error // WriteBytes writes a slice bytes to the given address. diff --git a/samples/spi.go b/samples/spi.go index 298acc4..20d606c 100644 --- a/samples/spi.go +++ b/samples/spi.go @@ -24,25 +24,25 @@ func main() { panic(err) } - fmt.Println("received data is: %v", dataBuf) + fmt.Println("received data is:", dataBuf) dataReceived, err := spiBus.ReceiveData(3) if err != nil { panic(err) } - fmt.Println("received data is: %v", dataReceived) + fmt.Println("received data is:", dataReceived) dataByte := byte(1) receivedByte, err := spiBus.TransferAndReceiveByte(dataByte) if err != nil { panic(err) } - fmt.Println("received byte is: %v", receivedByte) + fmt.Println("received byte is:", receivedByte) receivedByte, err = spiBus.ReceiveByte() if err != nil { panic(err) } - fmt.Println("received byte is: %v", receivedByte) + fmt.Println("received byte is:", receivedByte) } diff --git a/spidriver.go b/spidriver.go index cecc19d..a145155 100644 --- a/spidriver.go +++ b/spidriver.go @@ -2,10 +2,10 @@ package embd import "sync" -type spiBusFactory func(byte, byte, byte, int, int, int, func() error) SPIBus +type spiBusFactory func(int, byte, byte, int, int, int, func() error) SPIBus type spiDriver struct { - spiDevMinor byte + spiDevMinor int initializer func() error busMap map[byte]SPIBus @@ -16,7 +16,7 @@ type spiDriver struct { // NewSPIDriver returns a SPIDriver interface which allows control // over the SPI bus. -func NewSPIDriver(spiDevMinor byte, sbf spiBusFactory, i func() error) SPIDriver { +func NewSPIDriver(spiDevMinor int, sbf spiBusFactory, i func() error) SPIDriver { return &spiDriver{ spiDevMinor: spiDevMinor, sbf: sbf, diff --git a/update_contibutors.sh b/update_contibutors.sh deleted file mode 100755 index adde090..0000000 --- a/update_contibutors.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -git log --all --format='%cN <%cE>' | sort -u | grep -v karan.misra@gmail.com > CONTRIBUTORS diff --git a/update_contributors.sh b/update_contributors.sh new file mode 100755 index 0000000..1446108 --- /dev/null +++ b/update_contributors.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +git log --all --format='%cN <%cE>' | sort -u | grep -v karan.misra@gmail.com |\ + grep -v noreply@ | cat CONTRIBUTORS - | sort | uniq > CONTRIBUTORS.new +mv CONTRIBUTORS.new CONTRIBUTORS