Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SPI version of driver for xpt2046 #477

Open
wants to merge 18 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
114 changes: 114 additions & 0 deletions examples/xpt2046/spi/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package main

import (
"image/color"
"machine"
"strconv"
"time"
"tinygo.org/x/drivers"
"tinygo.org/x/drivers/st7789"
// "tinygo.org/x/drivers/xpt2046"
"github.com/gregoster/tinygo-drivers/xpt2046"
"tinygo.org/x/tinyfont"
"tinygo.org/x/tinyfont/freemono"
)

func initSPI() drivers.SPI {
machine.SPI1.Configure(machine.SPIConfig{
Frequency: 3000000,
Mode: 0,
})

return machine.SPI1

}

func initDisplay(bus drivers.SPI) (drivers.Displayer, st7789.Device) {

// https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8
display := st7789.New(bus,
machine.GPIO15, // TFT_RESET - reset pin
machine.GPIO8, // TFT_DC - Data/Command
machine.GPIO9, // TFT_CS - Chip Select
machine.GPIO13) // TFT_LITE - Backlite pin

display.Configure(st7789.Config{
Rotation: st7789.ROTATION_270,
RowOffset: 0,
FrameRate: st7789.FRAMERATE_60,
VSyncLines: st7789.MAX_VSYNC_SCANLINES,
Width: 240,
Height: 320,
})
return &display, display
}

func initTouch(bus drivers.SPI) xpt2046.Device {
// clk := machine.GPIO10 // TP_CLK
cs := machine.GPIO16 // TP_CS
// din := machine.GPIO11 // MOSI
// dout := machine.GPIO12 // MISO
irq := machine.GPIO17 // TP_IRQ

touchScreen := xpt2046.New(bus, cs, irq)

touchScreen.Configure(&xpt2046.Config{
Precision: 10, //Maximum number of samples for a single ReadTouchPoint to improve accuracy.
})
return touchScreen

}

func main() {

SPI := initSPI()

touchScreen := initTouch(SPI)

display, _ := initDisplay(SPI)
width, height := display.Size()

//white := color.RGBA{255, 255, 255, 255}

// red := color.RGBA{255, 0, 0, 255}
blue := color.RGBA{0, 0, 255, 255}
green := color.RGBA{0, 255, 0, 255}
black := color.RGBA{0, 0, 0, 255}
// yellow := color.RGBA{255,255,0,255}

tinyfont.WriteLine(display, &freemono.Regular9pt7b, 30,
80, strconv.Itoa(int(width))+"x"+strconv.Itoa(int(height)), green)

prev := ""

// tinyfont.WriteLine(display, &freemono.Regular9pt7b, 0, 160, "HERE!", red)
// tinyfont.WriteLine(display, &freemono.Regular9pt7b, 0, 200, strconv.Itoa(int(machine.SPI1.GetBaudRate())), red)

for {

//Wait for a touch
for !touchScreen.Touched() {
time.Sleep(50 * time.Millisecond)
}

if prev != "" {
tinyfont.WriteLine(display, &freemono.Regular9pt7b, 0, 180, prev, black)
}

touch := touchScreen.ReadTouchPoint()

//X and Y are 16 bit with 12 bit resolution and need to be scaled for the display size
//Z is 24 bit and is typically > 2000 for a touch

//Example of scaling for a 240x320 display

prev = strconv.Itoa(int((touch.X*240)>>16)) + "x" + strconv.Itoa(int(touch.Y*320)>>16)
tinyfont.WriteLine(display, &freemono.Regular9pt7b, 0, 180, prev, blue)

//Wait for touch to end
for touchScreen.Touched() {
time.Sleep(50 * time.Millisecond)
}
}

}
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module tinygo.org/x/drivers
// module tinygo.org/x/drivers
module github.com/gregoster/tinygo-drivers

go 1.15

Expand All @@ -7,6 +8,7 @@ require (
github.com/frankban/quicktest v1.10.2
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
tinygo.org/x/drivers v0.19.0
tinygo.org/x/tinyfont v0.3.0
tinygo.org/x/tinyterm v0.1.0
)
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
tinygo.org/x/drivers v0.14.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
tinygo.org/x/drivers v0.15.1/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
tinygo.org/x/drivers v0.16.0/go.mod h1:uT2svMq3EpBZpKkGO+NQHjxjGf1f42ra4OnMMwQL2aI=
tinygo.org/x/drivers v0.19.0 h1:x/TIC8SFWeGViJvcYO1ATjgwSNF7hHN2ouAyjMUXI2Q=
tinygo.org/x/drivers v0.19.0/go.mod h1:uJD/l1qWzxzLx+vcxaW0eY464N5RAgFi1zTVzASFdqI=
tinygo.org/x/tinyfont v0.2.1/go.mod h1:eLqnYSrFRjt5STxWaMeOWJTzrKhXqpWw7nU3bPfKOAM=
tinygo.org/x/tinyfont v0.3.0 h1:HIRLQoI3oc+2CMhPcfv+Ig88EcTImE/5npjqOnMD4lM=
Expand Down
151 changes: 146 additions & 5 deletions xpt2046/xpt2046.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
// Package xpt2046 implements a driver for the XPT2046 resistive touch controller as packaged on the TFT_320QVT board
// Package xpt2046 implements a driver for the XPT2046 resistive touch controller
// as packaged on the TFT_320QVT board or a Waveshare Pico-ResTouch-LCD-2.8 board.
//
// Datasheet: http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf
package xpt2046

import (
"machine"
"time"

"tinygo.org/x/drivers"
"tinygo.org/x/drivers/touch"
)

// This driver supports both GPIO and SPI interfaces for a given touch controller,
// but not at the same time. GPIO works fine unless t_clk, t_din, and t_dout
// are shared with another device and that device is expecting to use SPI over
// those pins.
type Device struct {
bus drivers.SPI
t_clk machine.Pin
t_cs machine.Pin
t_din machine.Pin
Expand All @@ -20,12 +26,16 @@ type Device struct {
precision uint8
}

// Simple configuration -- Precision indicates the number of samples
// averaged to produce X, Y, and Z (pressure) coordinates.
type Config struct {
Precision uint8
}

// Create a new GPIO-based device. SPI bus not used in this setup.
func New(t_clk, t_cs, t_din, t_dout, t_irq machine.Pin) Device {
return Device{
bus: nil,
precision: 10,
t_clk: t_clk,
t_cs: t_cs,
Expand All @@ -35,6 +45,19 @@ func New(t_clk, t_cs, t_din, t_dout, t_irq machine.Pin) Device {
}
}

// Create a new SPI-based device. GPIO not available for this instance
// when SPI is used.
func NewSPI(bus drivers.SPI, t_cs, t_irq machine.Pin) Device {
return Device{
bus: bus,
precision: 10,
t_cs: t_cs,
t_irq: t_irq,
}
}

// Configure a GPIO-based device. Sets up the Precision of the device
// and initializes the GPIO pins.
func (d *Device) Configure(config *Config) error {

if config.Precision == 0 {
Expand All @@ -59,17 +82,63 @@ func (d *Device) Configure(config *Config) error {
return nil
}

// Configure a SPI-based device. Sets up the Precision of the device.
// Also initializes t_cs and t_irq. All of the other pins in Device are
// used by SPI.
func (d *Device) ConfigureSPI(config *Config) error {

if config.Precision == 0 {
d.precision = 10
} else {
d.precision = config.Precision
}

d.t_cs.Configure(machine.PinConfig{Mode: machine.PinOutput})
d.t_irq.Configure(machine.PinConfig{Mode: machine.PinInput})

d.t_cs.High()

//S = 1 --> Required Control bit
//A2-A0 = 000 --> nothing
//MODE = 0 --> 12 bit conversion
//SER/DFR = 0 --> Differential preferred for pressure
//PD1-PD0 = 10 --> Powerdown and enable PEN_IRQ
d.tx([]byte{0x80}) // make sure PD1 is cleared on start
return nil
}

// tx and rx pulled from st7789, modified for xps2046

// tx sends data to the touchpad.
func (d *Device) tx(data []byte) {
d.t_cs.Low()
d.bus.Tx(data, nil)
d.t_cs.High()
}

// rx reads data from the touchpad.
func (d *Device) rx(command uint8, data []byte) {
cmd := make([]byte, len(data))
cmd[0] = command
d.t_cs.Low()
d.bus.Tx(cmd, data)
d.t_cs.High()
}

// Very short sleep for GPIO pulsing.
func busSleep() {
time.Sleep(5 * time.Nanosecond)
}

// Pulse the given pin p high and then low.
func pulseHigh(p machine.Pin) {
p.High()
busSleep()
p.Low()
busSleep()
}

// Write a command to the touchscreen using GPIO.
func (d *Device) writeCommand(data uint8) {

for count := uint8(0); count < 8; count++ {
Expand All @@ -80,6 +149,7 @@ func (d *Device) writeCommand(data uint8) {

}

// Read whatever data is waiting on t_dout using GPIO.
func (d *Device) readData() uint16 {

data := uint16(0)
Expand All @@ -99,22 +169,40 @@ func (d *Device) readData() uint16 {
return data
}

// Read the X, Y, and Z (pressure) coordinates of the point currently being
// touched on the screen. Works for both GPIO and SPI by calling the associated
// raw read routines. The device is queried at most d.precision times, with
// the resulting touch.Point having the average values for each of X, Y, and Z.
func (d *Device) ReadTouchPoint() touch.Point {

tx := uint32(0)
ty := uint32(0)
tz := uint32(0)
rx := int32(0)
ry := int32(0)
rz := int32(0)
sampleCount := uint8(0)

d.t_cs.Low()
if d.bus == nil {
d.t_cs.Low()
}

for ; sampleCount < d.precision && d.Touched(); sampleCount++ {
rx, ry, rz := d.readRaw()
if d.bus == nil {
rx, ry, rz = d.readRaw()
} else {
rx, ry, rz = d.readRawSPI()
}
tx += uint32(rx)
ty += uint32(ry)
tz += uint32(rz)
if d.bus != nil {
time.Sleep(200 * time.Microsecond)
}
}
if d.bus == nil {
d.t_cs.High()
}
d.t_cs.High()

if sampleCount > 0 {
x := int(tx / uint32(sampleCount))
Expand All @@ -134,11 +222,13 @@ func (d *Device) ReadTouchPoint() touch.Point {
}
}

// Touched() is true if the touch device senses a touch at the current moment.
func (d *Device) Touched() bool {
avail := !d.t_irq.Get()
return avail
}

// Read the current X, Y, and Z values using the GPIO interface.
func (d *Device) readRaw() (int32, int32, int32) {

d.t_cs.Low()
Expand Down Expand Up @@ -186,3 +276,54 @@ func (d *Device) readRaw() (int32, int32, int32) {
//Scale X&Y to 16 bit for consistency across touch drivers
return int32(tx) << 4, int32(4096-ty) << 4, tz
}

// Read the current X, Y, and Z values using the SPI interface.
func (d *Device) readRawSPI() (int32, int32, int32) {

data := make([]byte, 4)

//S = 1 --> Required Control bit
//A2-A0 = 101 --> X-Position
//MODE = 0 --> 12 bit conversion
//SER/DFR = 0 --> Differential preferred for X,Y position
//PD1-PD0 = 00 --> Powerdown and enable PEN_IRQ

d.rx(0xD0, data)
tx := int32((uint16(data[1])<<8 | uint16(data[2])) >> 3) // 7 bits come from data[1], remaining 5 from top of data[2]

//S = 1 --> Required Control bit
//A2-A0 = 001 --> Y-Position
//MODE = 0 --> 12 bit conversion
//SER/DFR = 0 --> Differential preferred for X,Y position
//PD1-PD0 = 00 --> Powerdown and enable PEN_IRQ

d.rx(0x90, data)
ty := int32((uint16(data[1])<<8 | uint16(data[2])) >> 3) // 7 bits come from data[0], remaining 5 from top of data[1]

//S = 1 --> Required Control bit
//A2-A0 = 011 --> Z1-position (pressure)
//MODE = 0 --> 12 bit conversion
//SER/DFR = 0 --> Differential preferred for pressure
//PD1-PD0 = 00 --> Powerdown and enable PEN_IRQ

d.rx(0xB0, data)
tz1 := int32((uint16(data[1])<<8 | uint16(data[2])) >> 3) // 7 bits come from data[0], remaining 5 from top of data[1]

//S = 1 --> Required Control bit
//A2-A0 = 100 --> Z2-position (pressure)
//MODE = 0 --> 12 bit conversion
//SER/DFR = 0 --> Differential preferred for pressure
//PD1-PD0 = 00 --> Powerdown and enable PEN_IRQ

d.rx(0xC0, data)
tz2 := int32((uint16(data[1])<<8 | uint16(data[2])) >> 3) // 7 bits come from data[0], remaining 5 from top of data[1]

tz := int32(0)
if tz1 != 0 {
//Touch pressure is proportional to the ratio of z2 to z1 and the x position.
tz = int32(tx) * ((tz2 << 12) / (tz1 << 12))
}

//Scale X&Y to 16 bit for consistency across touch drivers
return int32(tx) << 4, int32(4096-ty) << 4, tz
}