diff --git a/Makefile b/Makefile index 9cb2dec..fb02568 100644 --- a/Makefile +++ b/Makefile @@ -215,12 +215,14 @@ endif @md5sum ./build/test.hex tinygo build -size short -o ./build/test.elf -target=wioterminal ./examples/axp192/m5stack-core2-blinky/ @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/xpt2046/main.go + @md5sum ./build/test.uf2 DRIVERS = $(wildcard */) NOTESTS = build examples flash semihosting pcd8544 shiftregister st7789 microphone mcp3008 gps microbitmatrix \ hcsr04 ssd1331 ws2812 thermistor apa102 easystepper ssd1351 ili9341 wifinina shifter hub75 \ hd44780 buzzer ssd1306 espat l9110x st7735 bmi160 l293x dht keypad4x4 max72xx p1am tone tm1637 \ - pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft hts221 lps22hb apds9960 axp192 + pcf8563 mcp2515 servo sdcard rtl8720dn image cmd i2csoft hts221 lps22hb apds9960 axp192 xpt2046 TESTS = $(filter-out $(addsuffix /%,$(NOTESTS)),$(DRIVERS)) unit-test: diff --git a/README.md b/README.md index b77bcb3..a9ea0c7 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ The following 74 devices are supported. | [Waveshare 2.13" e-paper display](https://www.waveshare.com/w/upload/e/e6/2.13inch_e-Paper_Datasheet.pdf) | SPI | | [Waveshare 4.2" e-paper B/W display](https://www.waveshare.com/w/upload/6/6a/4.2inch-e-paper-specification.pdf) | SPI | | [WS2812 RGB LED](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf) | GPIO | +| [XPT2046 touch controller](http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf) | GPIO | ## Contributing diff --git a/examples/xpt2046/main.go b/examples/xpt2046/main.go new file mode 100644 index 0000000..b790458 --- /dev/null +++ b/examples/xpt2046/main.go @@ -0,0 +1,44 @@ +package main + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/xpt2046" +) + +func main() { + + clk := machine.GPIO0 + cs := machine.GPIO1 + din := machine.GPIO2 + dout := machine.GPIO3 + irq := machine.GPIO4 + + touchScreen := xpt2046.New(clk, cs, din, dout, irq) + + touchScreen.Configure(&xpt2046.Config{ + Precision: 10, //Maximum number of samples for a single ReadTouchPoint to improve accuracy. + }) + + for { + + //Wait for a touch + for !touchScreen.Touched() { + time.Sleep(50 * time.Millisecond) + } + + 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 + println("touch:", touch.X, touch.Y, touch.Z) + //Example of scaling for a 240x320 display + println("screen:", (touch.X*240)>>16, (touch.Y*320)>>16) + + //Wait for touch to end + for touchScreen.Touched() { + time.Sleep(50 * time.Millisecond) + } + + } +} diff --git a/xpt2046/xpt2046.go b/xpt2046/xpt2046.go new file mode 100644 index 0000000..d1ef269 --- /dev/null +++ b/xpt2046/xpt2046.go @@ -0,0 +1,188 @@ +// Package xpt2046 implements a driver for the XPT2046 resistive touch controller as packaged on the TFT_320QVT board +// +// Datasheet: http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf +package xpt2046 + +import ( + "machine" + "time" + + "tinygo.org/x/drivers/touch" +) + +type Device struct { + t_clk machine.Pin + t_cs machine.Pin + t_din machine.Pin + t_dout machine.Pin + t_irq machine.Pin + + precision uint8 +} + +type Config struct { + Precision uint8 +} + +func New(t_clk, t_cs, t_din, t_dout, t_irq machine.Pin) Device { + return Device{ + precision: 10, + t_clk: t_clk, + t_cs: t_cs, + t_din: t_din, + t_dout: t_dout, + t_irq: t_irq, + } +} + +func (d *Device) Configure(config *Config) error { + + if config.Precision == 0 { + d.precision = 10 + } else { + d.precision = config.Precision + } + + d.t_clk.Configure(machine.PinConfig{Mode: machine.PinOutput}) + d.t_cs.Configure(machine.PinConfig{Mode: machine.PinOutput}) + d.t_din.Configure(machine.PinConfig{Mode: machine.PinOutput}) + + d.t_dout.Configure(machine.PinConfig{Mode: machine.PinInput}) + d.t_irq.Configure(machine.PinConfig{Mode: machine.PinInput}) + + d.t_clk.Low() + d.t_cs.High() + d.t_din.Low() + + d.readRaw() //Set Powerdown mode to enable T_IRQ + + return nil +} + +func busSleep() { + time.Sleep(5 * time.Nanosecond) +} + +func pulseHigh(p machine.Pin) { + p.High() + busSleep() + p.Low() + busSleep() +} + +func (d *Device) writeCommand(data uint8) { + + for count := uint8(0); count < 8; count++ { + d.t_din.Set((data & 0x80) != 0) + data <<= 1 + pulseHigh(d.t_clk) + } + +} + +func (d *Device) readData() uint16 { + + data := uint16(0) + + for count := uint8(0); count < 12; count++ { + data <<= 1 + pulseHigh(d.t_clk) + if d.t_dout.Get() { + data |= 1 + } + } + pulseHigh(d.t_clk) //13 + pulseHigh(d.t_clk) //14 + pulseHigh(d.t_clk) //15 + pulseHigh(d.t_clk) //16 + + return data +} + +func (d *Device) ReadTouchPoint() touch.Point { + + tx := uint32(0) + ty := uint32(0) + tz := uint32(0) + sampleCount := uint8(0) + + d.t_cs.Low() + + for ; sampleCount < d.precision && d.Touched(); sampleCount++ { + rx, ry, rz := d.readRaw() + tx += uint32(rx) + ty += uint32(ry) + tz += uint32(rz) + } + d.t_cs.High() + + if sampleCount > 0 { + x := int(tx / uint32(sampleCount)) + y := int(ty / uint32(sampleCount)) + z := int(tz / uint32(sampleCount)) + return touch.Point{ + X: x, + Y: y, + Z: z, + } + } else { + return touch.Point{ + X: 0, + Y: 0, + Z: 0, + } + } +} + +func (d *Device) Touched() bool { + avail := !d.t_irq.Get() + return avail +} + +func (d *Device) readRaw() (int32, int32, int32) { + + d.t_cs.Low() + + //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.writeCommand(0x90) + ty := d.readData() + + //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.writeCommand(0xD0) + tx := d.readData() + + //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.writeCommand(0xB0) + tz1 := int32(d.readData()) + + //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.writeCommand(0xC0) + tz2 := int32(d.readData()) + + 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)) + } + + d.t_cs.High() + + //Scale X&Y to 16 bit for consistency across touch drivers + return int32(tx) << 4, int32(4096-ty) << 4, tz +}