diff --git a/README.md b/README.md index 66ac8b5ff..40846a9cf 100644 --- a/README.md +++ b/README.md @@ -273,6 +273,7 @@ platforms are currently supported: - [Parrot Bebop](http://www.parrot.com/usa/products/bebop-drone/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/parrot/bebop) - [Parrot Minidrone](https://www.parrot.com/us/minidrones) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/parrot/minidrone) - [Pebble](https://www.getpebble.com/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/pebble) +- [PINE64 ROCK64](https://pine64.org/documentation/ROCK64/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/pine64/rock64) - [Radxa Rock Pi 4](https://wiki.radxa.com/Rock4/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/rockpi) - [Raspberry Pi](http://www.raspberrypi.org/) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/raspi) - [Serial Port](https://en.wikipedia.org/wiki/Serial_port) <=> [Package](https://github.com/hybridgroup/gobot/blob/release/platforms/serialport) diff --git a/examples/rock64_direct_pin.go b/examples/rock64_direct_pin.go new file mode 100644 index 000000000..96ed764dd --- /dev/null +++ b/examples/rock64_direct_pin.go @@ -0,0 +1,84 @@ +//go:build example +// +build example + +// +// Do not build by default. + +package main + +import ( + "fmt" + "time" + + "gobot.io/x/gobot/v2" + "gobot.io/x/gobot/v2/drivers/gpio" + "gobot.io/x/gobot/v2/platforms/adaptors" + "gobot.io/x/gobot/v2/platforms/pine64/rock64" +) + +// Wiring +// PWR ROCK64: 1, P5_1 (+3.3V, VCC); 2, 4, P5_2 (+5V, VDD); 6, 9, 14, 20, P5_7, P5_8, P5_15, P5_16 (GND) +// GPIO ROCK64: second header P5+BUS pin 3 is input, pin 4 is normal output, pin 5 is inverted output +// Button: the input pin is wired with a button to GND, the internal pull up resistor is used +// LED's: the output pins are wired to the cathode of the LED, the anode is wired with a resistor (70-130Ohm for 20mA) +// to VCC +// Expected behavior: always one LED is on, the other in opposite state, if button is pressed for >2 seconds the state +// changes +func main() { + const ( + inPinNum = "P5_3" + outPinNum = "P5_4" + outPinInvertedNum = "P5_5" + debounceTime = 2 * time.Second + ) + // note: WithGpiosOpenDrain() is optional, if using WithGpiosOpenSource() the LED's will not light up + board := rock64.NewAdaptor(adaptors.WithGpiosActiveLow(outPinInvertedNum), + adaptors.WithGpiosOpenDrain(outPinNum, outPinInvertedNum), + adaptors.WithGpiosPullUp(inPinNum), + adaptors.WithGpioDebounce(inPinNum, debounceTime)) + + inPin := gpio.NewDirectPinDriver(board, inPinNum) + outPin := gpio.NewDirectPinDriver(board, outPinNum) + outPinInverted := gpio.NewDirectPinDriver(board, outPinInvertedNum) + + work := func() { + level := byte(1) + + gobot.Every(500*time.Millisecond, func() { + read, err := inPin.DigitalRead() + fmt.Printf("pin %s state is %d\n", inPinNum, read) + if err != nil { + fmt.Println(err) + if level == 1 { + level = 0 + } else { + level = 1 + } + } else { + level = byte(read) + } + + err = outPin.DigitalWrite(level) + fmt.Printf("pin %s is now %d\n", outPinNum, level) + if err != nil { + fmt.Println(err) + } + + err = outPinInverted.DigitalWrite(level) + fmt.Printf("pin %s is now not %d\n", outPinInvertedNum, level) + if err != nil { + fmt.Println(err) + } + }) + } + + robot := gobot.NewRobot("pinBot", + []gobot.Connection{board}, + []gobot.Device{inPin, outPin, outPinInverted}, + work, + ) + + if err := robot.Start(); err != nil { + panic(err) + } +} diff --git a/examples/rock64_yl40.go b/examples/rock64_yl40.go new file mode 100644 index 000000000..e755c1ff9 --- /dev/null +++ b/examples/rock64_yl40.go @@ -0,0 +1,81 @@ +//go:build example +// +build example + +// +// Do not build by default. + +package main + +import ( + "fmt" + "log" + "time" + + "gobot.io/x/gobot/v2" + "gobot.io/x/gobot/v2/drivers/i2c" + "gobot.io/x/gobot/v2/platforms/pine64/rock64" +) + +func main() { + // Wiring + // PWR ROCK64: 1, P5_1 (+3.3V, VCC), 6, 9, 14, 20, P5_7, P5_8, P5_15, P5_16 (GND) + // I2C0 ROCK64: 3 (SDA), 5 (SCL) + // I2C1 ROCK64: 27 (SDA), 28 (SCL) + // YL-40 module: wire AOUT --> AIN2 for this example + // + // Note: temperature measurement is often buggy, because sensor is not properly grounded + // fix it by soldering a small bridge to the adjacent ground pin of brightness sensor + board := rock64.NewAdaptor() + yl := i2c.NewYL40Driver(board, i2c.WithBus(1)) + + work := func() { + // the LED light is visible above ~1.7V + writeVal, _ := yl.AOUT() + + gobot.Every(1000*time.Millisecond, func() { + if err := yl.Write(writeVal); err != nil { + fmt.Println(err) + } else { + log.Printf(" %.1f V written", writeVal) + writeVal = writeVal + 0.1 + if writeVal > 3.3 { + writeVal = 0 + } + } + + if brightness, err := yl.ReadBrightness(); err != nil { + fmt.Println(err) + } else { + log.Printf("Brightness: %.0f [0..1000]", brightness) + } + + if temperature, err := yl.ReadTemperature(); err != nil { + fmt.Println(err) + } else { + log.Printf("Temperature: %.1f °C", temperature) + } + + if ain2, err := yl.ReadAIN2(); err != nil { + fmt.Println(err) + } else { + log.Printf("Read back AOUT: %.1f [0..3.3]", ain2) + } + + if potiState, err := yl.ReadPotentiometer(); err != nil { + fmt.Println(err) + } else { + log.Printf("Resistor: %.0f %% [-100..+100]", potiState) + } + }) + } + + robot := gobot.NewRobot("yl40Bot", + []gobot.Connection{board}, + []gobot.Device{yl}, + work, + ) + + if err := robot.Start(); err != nil { + panic(err) + } +} diff --git a/platforms/pine64/rock64/LICENSE b/platforms/pine64/rock64/LICENSE new file mode 100644 index 000000000..f4448d985 --- /dev/null +++ b/platforms/pine64/rock64/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2025 The Hybrid Group + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/platforms/pine64/rock64/README.md b/platforms/pine64/rock64/README.md new file mode 100644 index 000000000..3a7305528 --- /dev/null +++ b/platforms/pine64/rock64/README.md @@ -0,0 +1,110 @@ +# Pine ROCK64 + +The Pine ROCK64 is a single board SoC computer based on the Rockchip RK3328 arm64 processor. It has built-in GPIO and +I2C interfaces. SPI is most likely not usable (not tested), because in use by the SPI FLASH 128M memory chip. + +For more info about the Pine ROCK64, go to [https://pine64.org/documentation/ROCK64/](https://pine64.org/documentation/ROCK64/). + +## How to Install + +Please refer to the main [README.md](https://github.com/hybridgroup/gobot/blob/release/README.md) + +Tested OS: + +* [armbian](https://www.armbian.com/rock64/) with Debian + +## Configuration steps for the OS + +### System access and configuration basics + +Please follow the instructions of the OS provider. A ssh access is used in this guide. + +```sh +ssh @192.168.1.xxx +``` + +### Enabling hardware drivers + +Not all drivers are enabled by default. You can have a look at the configuration file, to find out what is enabled at +your system: + +```sh +cat /boot/armbianEnv.txt +``` + +```sh +sudo apt install armbian-config +sudo armbian-config +``` + +## How to Use + +The pin numbering used by your Gobot program should match the way your board is labeled right on the board itself. + +```go +r := rock64.NewAdaptor() +led := gpio.NewLedDriver(r, "7") +``` + +## How to Connect + +### Compiling + +Compile your Gobot program on your workstation like this: + +```sh +GOARCH=arm64 GOOS=linux go build -o output/ examples/rock64_blink.go +``` + +Once you have compiled your code, you can upload your program and execute it on the board from your workstation +using the `scp` and `ssh` commands like this: + +```sh +scp rock64_blink @192.168.1.xxx:~ +ssh -t @192.168.1.xxx "./rock64_blink" +``` + +## Troubleshooting + +### I2C-0 overlay + +With the armbian-config sometimes the overlays can not properly applied (different open Bugs). To ensure your overlay +is applied have a look into your /boot/boot.cmd and search for the name of the used shell variable(s). This name needs +to be used in your /boot/armbianEnv.txt. + +```sh cat /boot/boot.cmd | grep overlay_file +for overlay_file in ${overlays}; do + if load ${devtype} ${devnum}:${distro_bootpart} ${load_addr} ${prefix}dtb/rockchip/overlay/${overlay_prefix}-${overlay_file}.dtbo; then + echo "Applying kernel provided DT overlay ${overlay_prefix}-${overlay_file}.dtbo" +for overlay_file in ${user_overlays}; do + if load ${devtype} ${devnum}:${distro_bootpart} ${load_addr} ${prefix}overlay-user/${overlay_file}.dtbo; then + echo "Applying user provided DT overlay ${overlay_file}.dtbo" +``` + +In the example above the variable is named `overlays`. So your /boot/armbianEnv.txt must contain this variable. + +```sh cat /boot/armbianEnv.txt | grep overlay +overlay_prefix=rockchip +overlays=rk3328-i2c0 +``` + +In some buggy versions the variable is named "fdt_overlays", just rename the variable in your "armbianEnv.txt" to match +the boot script. + +As you can see in the boot script, the real file name is a concatenate of different variables `${overlay_prefix}-${overlay_file}.dtbo`. +This file must exist in the folder `${prefix}dtb/rockchip/overlay` (prefix="/boot/"). So for the i2c-0 overlay: + +```sh ls -la /boot/dtb/rockchip/overlay/ | grep i2c0 +-rw-r--r-- 1 root root 218 Nov 25 19:15 rockchip-rk3328-i2c0.dtbo +-rw-r--r-- 1 root root 223 Nov 25 19:15 rockchip-rk3568-hk-i2c0.dtbo +``` + +...means the entry in the armbianEnv.txt sould be set to "overlays=rk3328-i2c0". + +The variable can contain a space separated list. + +### PWM + +There are 3 PWMs on the chip (pwm0, pwm1, pwm2). Unfortunately all pins are shared with the PMIC, so i2c-1 (pwm0, pwm1) +can not be deactivated, because it is mandatory for the i2c communication to PMIC address 0x18. Simply an activation of +pwm0 or pwm1 with an overlay leads to the Kernel can not be loaded anymore. \ No newline at end of file diff --git a/platforms/pine64/rock64/adaptor.go b/platforms/pine64/rock64/adaptor.go new file mode 100644 index 000000000..d6c33db17 --- /dev/null +++ b/platforms/pine64/rock64/adaptor.go @@ -0,0 +1,131 @@ +package rock64 + +import ( + "fmt" + "sync" + + multierror "github.com/hashicorp/go-multierror" + + "gobot.io/x/gobot/v2" + "gobot.io/x/gobot/v2/platforms/adaptors" + "gobot.io/x/gobot/v2/system" +) + +const ( + defaultI2cBusNumber = 1 + + defaultSpiBusNumber = 0 + defaultSpiChipNumber = 0 + defaultSpiMode = 0 + defaultSpiBitsNumber = 8 + defaultSpiMaxSpeed = 500000 +) + +// Adaptor represents a Gobot Adaptor for the PINE64 ROCK64 +type Adaptor struct { + name string + sys *system.Accesser // used for unit tests only + mutex *sync.Mutex + *adaptors.AnalogPinsAdaptor + *adaptors.DigitalPinsAdaptor + *adaptors.I2cBusAdaptor + *adaptors.SpiBusAdaptor +} + +// NewAdaptor creates a ROCK64 Adaptor +// +// Optional parameters: +// +// adaptors.WithGpioSysfsAccess(): use legacy sysfs driver instead of default character device driver +// adaptors.WithSpiGpioAccess(sclk, ncs, sdo, sdi): use GPIO's instead of /dev/spidev#.# +// adaptors.WithGpiosActiveLow(pin's): invert the pin behavior +// adaptors.WithGpiosPullUp/Down(pin's): sets the internal pull resistor +// adaptors.WithGpiosOpenDrain/Source(pin's): sets the output behavior +// adaptors.WithGpioDebounce(pin, period): sets the input debouncer +// adaptors.WithGpioEventOnFallingEdge/RaisingEdge/BothEdges(pin, handler): activate edge detection +func NewAdaptor(opts ...interface{}) *Adaptor { + sys := system.NewAccesser() + a := &Adaptor{ + name: gobot.DefaultName("ROCK64"), + sys: sys, + mutex: &sync.Mutex{}, + } + + var digitalPinsOpts []adaptors.DigitalPinsOptionApplier + var spiBusOpts []adaptors.SpiBusOptionApplier + for _, opt := range opts { + switch o := opt.(type) { + case adaptors.DigitalPinsOptionApplier: + digitalPinsOpts = append(digitalPinsOpts, o) + case adaptors.SpiBusOptionApplier: + spiBusOpts = append(spiBusOpts, o) + default: + panic(fmt.Sprintf("'%s' can not be applied on adaptor '%s'", opt, a.name)) + } + } + + analogPinTranslator := adaptors.NewAnalogPinTranslator(sys, analogPinDefinitions) + digitalPinTranslator := adaptors.NewDigitalPinTranslator(sys, gpioPinDefinitions) + // Valid bus numbers are [0,1] which corresponds to /dev/i2c-0, /dev/i2c-1. + // We don't support "/dev/i2c-4 DesignWare HDMI". + i2cBusNumberValidator := adaptors.NewBusNumberValidator([]int{0, 1}) + // Valid bus number is 0 which corresponds to /dev/spidev0.x + // x is the chip number <255 + spiBusNumberValidator := adaptors.NewBusNumberValidator([]int{0}) + + a.AnalogPinsAdaptor = adaptors.NewAnalogPinsAdaptor(sys, analogPinTranslator.Translate) + a.DigitalPinsAdaptor = adaptors.NewDigitalPinsAdaptor(sys, digitalPinTranslator.Translate, digitalPinsOpts...) + a.I2cBusAdaptor = adaptors.NewI2cBusAdaptor(sys, i2cBusNumberValidator.Validate, defaultI2cBusNumber) + a.SpiBusAdaptor = adaptors.NewSpiBusAdaptor(sys, spiBusNumberValidator.Validate, defaultSpiBusNumber, + defaultSpiChipNumber, defaultSpiMode, defaultSpiBitsNumber, defaultSpiMaxSpeed, a.DigitalPinsAdaptor, spiBusOpts...) + + return a +} + +// Name returns the name of the Adaptor +func (a *Adaptor) Name() string { return a.name } + +// SetName sets the name of the Adaptor +func (a *Adaptor) SetName(n string) { a.name = n } + +// Connect create new connection to board and pins. +func (a *Adaptor) Connect() error { + a.mutex.Lock() + defer a.mutex.Unlock() + + if err := a.SpiBusAdaptor.Connect(); err != nil { + return err + } + + if err := a.I2cBusAdaptor.Connect(); err != nil { + return err + } + + if err := a.AnalogPinsAdaptor.Connect(); err != nil { + return err + } + + return a.DigitalPinsAdaptor.Connect() +} + +// Finalize closes connection to board, pins and bus +func (a *Adaptor) Finalize() error { + a.mutex.Lock() + defer a.mutex.Unlock() + + err := a.DigitalPinsAdaptor.Finalize() + + if e := a.AnalogPinsAdaptor.Finalize(); e != nil { + err = multierror.Append(err, e) + } + + if e := a.I2cBusAdaptor.Finalize(); e != nil { + err = multierror.Append(err, e) + } + + if e := a.SpiBusAdaptor.Finalize(); e != nil { + err = multierror.Append(err, e) + } + + return err +} diff --git a/platforms/pine64/rock64/adaptor_test.go b/platforms/pine64/rock64/adaptor_test.go new file mode 100644 index 000000000..faca9e244 --- /dev/null +++ b/platforms/pine64/rock64/adaptor_test.go @@ -0,0 +1,180 @@ +package rock64 + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "gobot.io/x/gobot/v2" + "gobot.io/x/gobot/v2/drivers/aio" + "gobot.io/x/gobot/v2/drivers/gpio" + "gobot.io/x/gobot/v2/drivers/i2c" + "gobot.io/x/gobot/v2/platforms/adaptors" + "gobot.io/x/gobot/v2/system" +) + +// make sure that this Adaptor fulfills all the required interfaces +var ( + _ gobot.Adaptor = (*Adaptor)(nil) + _ gobot.DigitalPinnerProvider = (*Adaptor)(nil) + _ gpio.DigitalReader = (*Adaptor)(nil) + _ gpio.DigitalWriter = (*Adaptor)(nil) + _ aio.AnalogReader = (*Adaptor)(nil) + _ i2c.Connector = (*Adaptor)(nil) +) + +func initConnectedTestAdaptorWithMockedFilesystem(mockPaths []string) (*Adaptor, *system.MockFilesystem) { + a := initConnectedTestAdaptor() + fs := a.sys.UseMockFilesystem(mockPaths) + return a, fs +} + +func initConnectedTestAdaptor() *Adaptor { + a := NewAdaptor() + if err := a.Connect(); err != nil { + panic(err) + } + return a +} + +func TestNewAdaptor(t *testing.T) { + // arrange & act + a := NewAdaptor() + // assert + assert.IsType(t, &Adaptor{}, a) + assert.True(t, strings.HasPrefix(a.Name(), "ROCK64")) + assert.NotNil(t, a.sys) + assert.NotNil(t, a.mutex) + assert.NotNil(t, a.AnalogPinsAdaptor) + assert.NotNil(t, a.DigitalPinsAdaptor) + assert.NotNil(t, a.I2cBusAdaptor) + assert.NotNil(t, a.SpiBusAdaptor) + assert.True(t, a.sys.HasDigitalPinCdevAccess()) + // act & assert + a.SetName("NewName") + assert.Equal(t, "NewName", a.Name()) +} + +func TestNewAdaptorWithOption(t *testing.T) { + // arrange & act + a := NewAdaptor(adaptors.WithGpiosActiveLow("1"), adaptors.WithGpioSysfsAccess()) + // assert + require.NoError(t, a.Connect()) + assert.True(t, a.sys.HasDigitalPinSysfsAccess()) +} + +func TestDigitalIO(t *testing.T) { + // some basic tests, further tests are done in "digitalpinsadaptor.go" + // arrange + a := initConnectedTestAdaptor() + dpa := a.sys.UseMockDigitalPinAccess() + require.True(t, a.sys.HasDigitalPinCdevAccess()) + // act & assert write + err := a.DigitalWrite("7", 1) + require.NoError(t, err) + assert.Equal(t, []int{1}, dpa.Written("gpiochip1", "28")) + // arrange, act & assert read + dpa.UseValues("gpiochip2", "1", []int{3}) + i, err := a.DigitalRead("10") + require.NoError(t, err) + assert.Equal(t, 3, i) + // act and assert unknown pin + require.ErrorContains(t, a.DigitalWrite("99", 1), "'99' is not a valid id for a digital pin") + // act and assert finalize + require.NoError(t, a.Finalize()) + assert.Equal(t, 0, dpa.Exported("gpiochip1", "28")) + assert.Equal(t, 0, dpa.Exported("gpiochip2", "1")) +} + +func TestDigitalIOSysfs(t *testing.T) { + // some basic tests, further tests are done in "digitalpinsadaptor.go" + // arrange + a := NewAdaptor(adaptors.WithGpioSysfsAccess()) + require.NoError(t, a.Connect()) + dpa := a.sys.UseMockDigitalPinAccess() + require.True(t, a.sys.HasDigitalPinSysfsAccess()) + // act & assert write + err := a.DigitalWrite("7", 1) + require.NoError(t, err) + assert.Equal(t, []int{1}, dpa.Written("", "60")) + // arrange, act & assert read + dpa.UseValues("", "65", []int{4}) + i, err := a.DigitalRead("10") + require.NoError(t, err) + assert.Equal(t, 4, i) + // act and assert unknown pin + require.ErrorContains(t, a.DigitalWrite("99", 1), "'99' is not a valid id for a digital pin") + // act and assert finalize + require.NoError(t, a.Finalize()) + assert.Equal(t, 0, dpa.Exported("", "60")) + assert.Equal(t, 0, dpa.Exported("", "65")) +} + +func TestAnalogRead(t *testing.T) { + mockPaths := []string{ + "/sys/class/thermal/thermal_zone0/temp", + } + + a, fs := initConnectedTestAdaptorWithMockedFilesystem(mockPaths) + + fs.Files["/sys/class/thermal/thermal_zone0/temp"].Contents = "567\n" + got, err := a.AnalogRead("thermal_zone0") + require.NoError(t, err) + assert.Equal(t, 567, got) + + _, err = a.AnalogRead("thermal_zone10") + require.ErrorContains(t, err, "'thermal_zone10' is not a valid id for an analog pin") + + fs.WithReadError = true + _, err = a.AnalogRead("thermal_zone0") + require.ErrorContains(t, err, "read error") + fs.WithReadError = false + + require.NoError(t, a.Finalize()) +} + +func TestFinalizeErrorAfterGPIO(t *testing.T) { + // arrange + a := initConnectedTestAdaptor() + dpa := a.sys.UseMockDigitalPinAccess() + require.True(t, a.sys.HasDigitalPinCdevAccess()) + require.NoError(t, a.DigitalWrite("7", 1)) + dpa.UseUnexportError("gpiochip1", "28") + // act + err := a.Finalize() + // assert + require.ErrorContains(t, err, "unexport error") +} + +func TestSpiDefaultValues(t *testing.T) { + a := NewAdaptor() + + assert.Equal(t, 0, a.SpiDefaultBusNumber()) + assert.Equal(t, 0, a.SpiDefaultChipNumber()) + assert.Equal(t, 0, a.SpiDefaultMode()) + assert.Equal(t, 8, a.SpiDefaultBitCount()) + assert.Equal(t, int64(500000), a.SpiDefaultMaxSpeed()) +} + +func TestI2cDefaultBus(t *testing.T) { + a := NewAdaptor() + assert.Equal(t, 1, a.DefaultI2cBus()) +} + +func TestI2cFinalizeWithErrors(t *testing.T) { + // arrange + a := initConnectedTestAdaptor() + a.sys.UseMockSyscall() + fs := a.sys.UseMockFilesystem([]string{"/dev/i2c-1"}) + con, err := a.GetI2cConnection(0xff, 1) + require.NoError(t, err) + _, err = con.Write([]byte{0xbf}) + require.NoError(t, err) + fs.WithCloseError = true + // act + err = a.Finalize() + // assert + require.ErrorContains(t, err, "close error") +} diff --git a/platforms/pine64/rock64/doc.go b/platforms/pine64/rock64/doc.go new file mode 100644 index 000000000..226c888b0 --- /dev/null +++ b/platforms/pine64/rock64/doc.go @@ -0,0 +1,7 @@ +/* +Package rock64 contains the Gobot adaptor for the PINE64 ROCK64. + +For further information refer to the boards README: +https://github.com/hybridgroup/gobot/blob/release/platforms/pine64/rock64/README.md +*/ +package rock64 // import "gobot.io/x/gobot/v2/platforms/pin64/rock64" diff --git a/platforms/pine64/rock64/pin_map.go b/platforms/pine64/rock64/pin_map.go new file mode 100644 index 000000000..ac4c9ddba --- /dev/null +++ b/platforms/pine64/rock64/pin_map.go @@ -0,0 +1,59 @@ +package rock64 + +import "gobot.io/x/gobot/v2/platforms/adaptors" + +// notes for character device +// sysfs: Chip*32 + (A=0, B=8, C=16) + Nr +// tested with cdev on a ROCK64 V2.0 board: armbian Linux, OK: works, ?: unknown, NOK: not working +// IN: works only as input, PU: if used as input, external pullup resistor needed +// PM: pins are shared with the PMIC i2c communication (address 0x13), GPIO seems to work but problems can occur +// +// pin suffix: +// "P5": those pins are located on the second pin header "P5+BUS" +// "SD": those pins should be used with caution and only if eMMC is used and no SD card is inserted +// "M2": those pins are used by the SPI FLASH 128M memory chip, but not blocked and maybe work, if unsure don't use it +// "SDA/SCL": see PM above, should only used for i2c-1 +var gpioPinDefinitions = adaptors.DigitalPinDefinitions{ + "13": {Sysfs: 0, Cdev: adaptors.CdevPin{Chip: 0, Line: 0}}, // GPIO0_A0 - OK + "P5_13": {Sysfs: 27, Cdev: adaptors.CdevPin{Chip: 0, Line: 27}}, // GPIO0_D3_SPDIF_TX_M0 - OK + "33_SD": {Sysfs: 32, Cdev: adaptors.CdevPin{Chip: 1, Line: 0}}, // GPIO1_A0_SDMMC0_D0 - ? + "35_SD": {Sysfs: 33, Cdev: adaptors.CdevPin{Chip: 1, Line: 1}}, // GPIO1_A1_SDMMC0_D1 - ? + "37_SD": {Sysfs: 34, Cdev: adaptors.CdevPin{Chip: 1, Line: 2}}, // GPIO1_A2_SDMMC0_D2_JTAG_TCK - ? + "40_SD": {Sysfs: 35, Cdev: adaptors.CdevPin{Chip: 1, Line: 3}}, // GPIO1_A3_SDMMC0_D3_JTAG_TMS - ? + "38_SD": {Sysfs: 36, Cdev: adaptors.CdevPin{Chip: 1, Line: 4}}, // GPIO1_A4_SDMMC0_CMD - ? + "36_SD": {Sysfs: 37, Cdev: adaptors.CdevPin{Chip: 1, Line: 5}}, // GPIO1_A5_SDMMC0_DET - ? + "32_SD": {Sysfs: 38, Cdev: adaptors.CdevPin{Chip: 1, Line: 6}}, // GPIO1_A6_SDMMC0_CLK - ? + "7": {Sysfs: 60, Cdev: adaptors.CdevPin{Chip: 1, Line: 28}}, // GPIO1_D4_CLK32KOUT_M1 - OK (PU) + "8": {Sysfs: 64, Cdev: adaptors.CdevPin{Chip: 2, Line: 0}}, // GPIO2_A0_UART2_TX_M1 - OK (PU) + "10": {Sysfs: 65, Cdev: adaptors.CdevPin{Chip: 2, Line: 1}}, // GPIO2_A1_UART2_RX_M1 - OK + "12": {Sysfs: 67, Cdev: adaptors.CdevPin{Chip: 2, Line: 3}}, // GPIO2_A3 - IN + "27_SDA": {Sysfs: 68, Cdev: adaptors.CdevPin{Chip: 2, Line: 4}}, // GPIO2_A4_I2C1_SDA - PM + "28_SCL": {Sysfs: 69, Cdev: adaptors.CdevPin{Chip: 2, Line: 5}}, // GPIO2_A5_I2C1_SCL - PM + "26": {Sysfs: 76, Cdev: adaptors.CdevPin{Chip: 2, Line: 12}}, // GPIO2_B4_SPI_CSN1_M0 - OK + "P5_10": {Sysfs: 79, Cdev: adaptors.CdevPin{Chip: 2, Line: 15}}, // GPIO2_B7_I2S1_MCLK - OK (PU) + "P5_9": {Sysfs: 80, Cdev: adaptors.CdevPin{Chip: 2, Line: 16}}, // GPIO2_C0_I2S1_LRCKRX - OK + "P5_3": {Sysfs: 81, Cdev: adaptors.CdevPin{Chip: 2, Line: 17}}, // GPIO2_C1_I2S1_LRCKTX - OK + "P5_4": {Sysfs: 82, Cdev: adaptors.CdevPin{Chip: 2, Line: 18}}, // GPIO2_C2_I2S1_SCLK - OK (PU) + "P5_6": {Sysfs: 83, Cdev: adaptors.CdevPin{Chip: 2, Line: 19}}, // GPIO2_C3_I2S1_SDI - OK + "P5_12": {Sysfs: 84, Cdev: adaptors.CdevPin{Chip: 2, Line: 20}}, // GPIO2_C4_I2S1_SDIO1 - OK + "P5_11": {Sysfs: 85, Cdev: adaptors.CdevPin{Chip: 2, Line: 21}}, // GPIO2_C5_I2S1_SDIO2 - OK + "P5_14": {Sysfs: 86, Cdev: adaptors.CdevPin{Chip: 2, Line: 22}}, // GPIO2_C6_I2S1_SDIO3 - OK + "P5_5": {Sysfs: 87, Cdev: adaptors.CdevPin{Chip: 2, Line: 23}}, // GPIO2_C7_I2S1_SDO - OK + "5": {Sysfs: 88, Cdev: adaptors.CdevPin{Chip: 2, Line: 24}}, // GPIO2_D0_I2C0_SCL_EthernetLink (P5_22) - OK + "P5_22": {Sysfs: 88, Cdev: adaptors.CdevPin{Chip: 2, Line: 24}}, // GPIO2_D0_I2C0_SCL_EthernetLink (P5_22) - OK + "3": {Sysfs: 89, Cdev: adaptors.CdevPin{Chip: 2, Line: 25}}, // GPIO2_D1_I2C0_SDA_EthernetSpeed (P5_21) - OK + "P5_21": {Sysfs: 89, Cdev: adaptors.CdevPin{Chip: 2, Line: 25}}, // GPIO2_D1_I2C0_SDA_EthernetSpeed (P5_21) - OK + "23_M2": {Sysfs: 96, Cdev: adaptors.CdevPin{Chip: 3, Line: 0}}, // GPIO3_A0_SPI_CLK_M2 - NOK + "19_M2": {Sysfs: 97, Cdev: adaptors.CdevPin{Chip: 3, Line: 1}}, // GPIO3_A1_SPI_TXD_M2 - OK + "21_M2": {Sysfs: 98, Cdev: adaptors.CdevPin{Chip: 3, Line: 2}}, // GPIO3_A2_SPI_RXD_M2 - OK + "15": {Sysfs: 100, Cdev: adaptors.CdevPin{Chip: 3, Line: 4}}, // GPIO3_A4 - OK + "16": {Sysfs: 101, Cdev: adaptors.CdevPin{Chip: 3, Line: 5}}, // GPIO3_A5 - OK + "18": {Sysfs: 102, Cdev: adaptors.CdevPin{Chip: 3, Line: 6}}, // GPIO3_A6 - OK + "22": {Sysfs: 103, Cdev: adaptors.CdevPin{Chip: 3, Line: 7}}, // GPIO3_A7 - OK + "24_M2": {Sysfs: 104, Cdev: adaptors.CdevPin{Chip: 3, Line: 8}}, // GPIO3_B0_SPI_CSN0_M2 - NOK +} + +var analogPinDefinitions = adaptors.AnalogPinDefinitions{ + // +/-273.200 °C need >=7 characters to read: +/-273200 millidegree Celsius + "thermal_zone0": {Path: "/sys/class/thermal/thermal_zone0/temp", W: false, ReadBufLen: 7}, +}