Skip to content

Commit 3dfe723

Browse files
authored
Add GT911 driver (#81)
* Add develop branch Signed-off-by: Andy Liu <[email protected]> * Modify_ci_schedule Signed-off-by: Andy Liu <[email protected]> * Mearge to main Signed-off-by: Andy Liu <[email protected]> * Add release workflow Signed-off-by: Andy Liu <[email protected]> * Development branch Signed-off-by: Andy Liu <[email protected]> * Modify action Signed-off-by: Andy Liu <[email protected]> * Modify CI Signed-off-by: Andy Liu <[email protected]> * Sync main Signed-off-by: Andy Liu <[email protected]> * Add GT911 (#80) Signed-off-by: Andy Liu <[email protected]> * Add GT911 Signed-off-by: Andy Liu <[email protected]> --------- Signed-off-by: Andy Liu <[email protected]>
1 parent 7d533c7 commit 3dfe723

File tree

5 files changed

+356
-2
lines changed

5 files changed

+356
-2
lines changed

.github/workflows/build.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ name: build
44
on:
55
push:
66
branches:
7-
- develop
87
- 'feature/**'
98
- 'fix/**'
109
paths:

.github/workflows/host_test.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ on:
66
branches:
77
- 'feature/**'
88
- 'fix/**'
9-
- develop
109
- main
1110
- 'release/**'
1211
paths:

Package.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ let package = Package(
2323
"DHTxx",
2424
"DS3231",
2525
"ESP32ATClient",
26+
"GT911",
2627
"HCSR04",
2728
"HTU21D",
2829
"IS31FL3731",
@@ -63,6 +64,7 @@ let package = Package(
6364
.library(name: "DHTxx", targets: ["DHTxx"]),
6465
.library(name: "DS3231", targets: ["DS3231"]),
6566
.library(name: "ESP32ATClient", targets: ["ESP32ATClient"]),
67+
.library(name: "GT911", targets: ["GT911"]),
6668
.library(name: "HCSR04", targets: ["HCSR04"]),
6769
.library(name: "HTU21D", targets: ["HTU21D"]),
6870
.library(name: "IS31FL3731", targets: ["IS31FL3731"]),
@@ -142,6 +144,9 @@ let package = Package(
142144
.target(
143145
name: "ESP32ATClient",
144146
dependencies: ["SwiftIO"]),
147+
.target(
148+
name: "GT911",
149+
dependencies: ["SwiftIO"]),
145150
.target(
146151
name: "HCSR04",
147152
dependencies: ["SwiftIO"]),

Sources/GT911/GT911.swift

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
//=== GT911.swift ---------------------------------------------------------===//
2+
//
3+
// Copyright (c) MadMachine Limited
4+
// Licensed under MIT License
5+
//
6+
// Authors: Andy Liu
7+
// Created: 12/19/2024
8+
//
9+
// See https://madmachine.io for more information
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftIO
14+
15+
/// A driver for the GT911 capacitive touch sensor, communicating via I2C.
16+
public final class GT911 {
17+
private let i2c: I2C
18+
private let address: UInt8
19+
private var readBuffer = [UInt8](repeating: 0, count: 40)
20+
21+
// MARK: - Public Types
22+
23+
/// Enum representing the interrupt modes for the GT911 touch sensor.
24+
public enum InterruptMode: UInt8 {
25+
case rising = 0x00
26+
case falling = 0x01
27+
case lowLevel = 0x02
28+
case highLevel = 0x03
29+
}
30+
31+
/// Initializes the GT911 touch sensor with the specified I2C instance and address.
32+
///
33+
/// - Parameters:
34+
/// - i2c: The I2C instance for communication.
35+
/// - address: The I2C address of the GT911 sensor. Default value is `0xBA`.
36+
/// - Precondition: The I2C speed must be `.standard` (100kHz) or `.fast` (400kHz).
37+
public init(_ i2c: I2C, address: UInt8 = 0xBA) {
38+
let speed = i2c.getSpeed()
39+
guard speed == .standard || speed == .fast else {
40+
print(#function + ": GT911 only supports 100kHz (standard) and 400kHz (fast) I2C speed")
41+
fatalError()
42+
}
43+
44+
self.i2c = i2c
45+
self.address = address >> 1
46+
47+
softReset()
48+
sleep(ms: 20)
49+
clearStatus()
50+
}
51+
52+
// MARK: - Public Methods
53+
54+
/// Reads the product ID of the GT911 touch sensor.
55+
///
56+
/// - Returns: A string representing the product ID, or `nil` if reading fails.
57+
public func readProductID() -> String? {
58+
var str: String?
59+
60+
do {
61+
try readRegister(.productID, into: &readBuffer, count: 4)
62+
readBuffer.withUnsafeBufferPointer { buffer in
63+
str = String(cString: buffer.baseAddress!)
64+
}
65+
} catch {
66+
return nil
67+
}
68+
69+
return str
70+
}
71+
72+
/// Resets the touch sensor by issuing a soft reset command.
73+
public func softReset() {
74+
let data: UInt8 = 0x04
75+
76+
try? writeRegister(.command, data)
77+
}
78+
79+
/// Sets the number of touch points the sensor can detect.
80+
///
81+
/// - Parameter number: The number of touch points (1 to 5).
82+
public func setTouchNumber(_ number: UInt8) {
83+
guard number >= 1, number <= 5 else {
84+
return
85+
}
86+
87+
try? writeRegister(.touchNumber, number)
88+
}
89+
90+
/// Reads the maximum X and Y output resolution supported by the touch sensor.
91+
///
92+
/// - Returns: A tuple containing the X and Y maximum values.
93+
public func readOutputMax() -> (x: UInt16, y: UInt16) {
94+
var x: UInt16 = 0, y: UInt16 = 0
95+
96+
try? readRegister(.xOutputMax, into: &x)
97+
try? readRegister(.yOutputMax, into: &y)
98+
99+
return (x, y)
100+
}
101+
102+
/// Sets the maximum X and Y output resolution.
103+
///
104+
/// - Parameters:
105+
/// - x: The maximum X value to set (optional).
106+
/// - y: The maximum Y value to set (optional).
107+
public func setOutputMax(x: UInt16? = nil, y: UInt16? = nil) {
108+
if let x {
109+
try? writeRegister(.xOutputMax, x)
110+
}
111+
if let y {
112+
try? writeRegister(.yOutputMax, y)
113+
}
114+
}
115+
116+
/// Reverses the X and Y axes for the touch coordinates.
117+
public func reverseXY() {
118+
var oldValue: UInt8 = 0
119+
try? readRegister(.moduleSwitch1, into: &oldValue)
120+
121+
oldValue ^= 0x08
122+
try? writeRegister(.moduleSwitch1, oldValue)
123+
}
124+
125+
/// Sets the interrupt mode for the touch sensor.
126+
///
127+
/// - Parameter mode: The interrupt mode to set (`InterruptMode` enum).
128+
public func setInterruptMode(_ mode: InterruptMode) {
129+
var oldValue: UInt8 = 0
130+
try? readRegister(.moduleSwitch1, into: &oldValue)
131+
132+
oldValue &= 0xFC
133+
oldValue |= mode.rawValue
134+
try? writeRegister(.moduleSwitch1, oldValue)
135+
}
136+
137+
/// Reads the status register of the touch sensor.
138+
///
139+
/// - Returns: The status register value.
140+
public func readStatus() -> UInt8 {
141+
var value: UInt8 = 0
142+
try? readRegister(.status, into: &value)
143+
144+
return value
145+
}
146+
147+
/// Clears the status register of the touch sensor.
148+
public func clearStatus() {
149+
try? writeRegister(.status, UInt8(0))
150+
}
151+
152+
/// Reads the touch information and returns an array of touch points.
153+
///
154+
/// - Returns: An array of `TouchInfo` structures containing touch point data.
155+
public func readTouchInfo() -> [TouchInfo] {
156+
var touchInfo = [TouchInfo]()
157+
158+
let status = readStatus()
159+
160+
if (status & 0x80) == 0 {
161+
return touchInfo
162+
} else if (status & 0x0F) == 0 {
163+
clearStatus()
164+
return touchInfo
165+
}
166+
167+
let touchPointCount = Int(status & 0x0F)
168+
try! readRegister(.bufferStart, into: &readBuffer, count: touchPointCount * 8)
169+
170+
for i in 0 ..< touchPointCount {
171+
touchInfo.append(getTouchPosition(at: i))
172+
}
173+
174+
clearStatus()
175+
176+
return touchInfo
177+
}
178+
}
179+
180+
extension GT911 {
181+
private enum Register: UInt16 {
182+
case command = 0x8040
183+
case xOutputMax = 0x8048
184+
case yOutputMax = 0x804A
185+
case touchNumber = 0x804C
186+
case moduleSwitch1 = 0x804D
187+
case touchLevel = 0x8053
188+
case leaveLevel = 0x8054
189+
190+
case productID = 0x8140
191+
case firmwareVer = 0x8144
192+
case status = 0x814E
193+
case bufferStart = 0x814F
194+
195+
func getRawData() -> [UInt8] {
196+
let highByte = UInt8(rawValue >> 8)
197+
let lowByte = UInt8(rawValue & 0xFF)
198+
199+
return [highByte, lowByte]
200+
}
201+
}
202+
203+
private func getTouchPosition(at index: Int) -> TouchInfo {
204+
var index = index * 8
205+
206+
let id = readBuffer[index]
207+
208+
index += 1
209+
let x = 320 - (UInt16(readBuffer[index]) | (UInt16(readBuffer[index + 1]) << 8))
210+
211+
index += 2
212+
let y = UInt16(readBuffer[index]) | (UInt16(readBuffer[index + 1]) << 8)
213+
214+
index += 2
215+
let size = UInt16(readBuffer[index]) | (UInt16(readBuffer[index + 1]) << 8)
216+
217+
return TouchInfo(id: id, x: x, y: y, size: size)
218+
}
219+
220+
private func writeRegister(_ register: Register, _ value: UInt8) throws(Errno) {
221+
var data = register.getRawData()
222+
data.append(value)
223+
224+
let result = i2c.write(data, to: address)
225+
if case let .failure(err) = result {
226+
throw err
227+
}
228+
}
229+
230+
private func writeRegister(_ register: Register, _ value: UInt16) throws(Errno) {
231+
var data = register.getRawData()
232+
233+
let lowByte = UInt8(value & 0xFF)
234+
let highByte = UInt8(value >> 8)
235+
data.append(lowByte)
236+
data.append(highByte)
237+
238+
let result = i2c.write(data, to: address)
239+
if case let .failure(err) = result {
240+
throw err
241+
}
242+
}
243+
244+
private func readRegister(
245+
_ register: Register, into byte: inout UInt8
246+
) throws(Errno) {
247+
var result = i2c.write(register.getRawData(), to: address)
248+
if case let .failure(err) = result {
249+
throw err
250+
}
251+
252+
result = i2c.read(into: &byte, from: address)
253+
if case let .failure(err) = result {
254+
throw err
255+
}
256+
}
257+
258+
private func readRegister(
259+
_ register: Register, into value: inout UInt16
260+
) throws(Errno) {
261+
for i in readBuffer.indices {
262+
readBuffer[i] = 0
263+
}
264+
265+
var result = i2c.write(register.getRawData(), to: address)
266+
if case let .failure(err) = result {
267+
throw err
268+
}
269+
270+
result = i2c.read(into: &readBuffer, count: 2, from: address)
271+
if case let .failure(err) = result {
272+
throw err
273+
}
274+
275+
value = UInt16(readBuffer[0]) | (UInt16(readBuffer[1]) << 8)
276+
}
277+
278+
private func readRegister(
279+
_ register: Register, into buffer: inout [UInt8], count: Int
280+
) throws(Errno) {
281+
for i in buffer.indices {
282+
buffer[i] = 0
283+
}
284+
285+
var result = i2c.write(register.getRawData(), to: address)
286+
if case let .failure(err) = result {
287+
throw err
288+
}
289+
290+
result = i2c.read(into: &buffer, count: count, from: address)
291+
if case let .failure(err) = result {
292+
throw err
293+
}
294+
}
295+
}

Sources/GT911/TouchInfo.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//=== TouchInfo.swift -----------------------------------------------------===//
2+
//
3+
// Copyright (c) MadMachine Limited
4+
// Licensed under MIT License
5+
//
6+
// Authors: Andy Liu
7+
// Created: 12/19/2024
8+
//
9+
// See https://madmachine.io for more information
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
/// Represents information for a single touch point on a capacitive touchscreen.
14+
///
15+
/// The `TouchInfo` structure contains the essential details about a touch event,
16+
/// including an identifier, the x and y coordinates, and the size of the touch area.
17+
///
18+
/// ## Overview
19+
/// This structure is commonly used to handle touch input data from multi-touch
20+
/// capacitive screens, providing necessary information for touch tracking and
21+
/// gesture recognition.
22+
///
23+
/// ## Example
24+
///
25+
/// ```swift
26+
/// let touch = TouchInfo(id: 1, x: 150, y: 300, size: 12)
27+
/// print("Touch ID: \(touch.id), Position: (\(touch.x), \(touch.y)), Size: \(touch.size)")
28+
/// ```
29+
///
30+
/// ## Topics
31+
/// - ``id``
32+
/// - ``x``
33+
/// - ``y``
34+
/// - ``size``
35+
public struct TouchInfo {
36+
/// The unique identifier for the touch point.
37+
///
38+
/// This ID is used to differentiate between multiple touch points during a multi-touch event.
39+
public let id: UInt8
40+
41+
/// The x-coordinate of the touch point on the screen.
42+
///
43+
/// Represents the horizontal position of the touch point, typically in pixels.
44+
public let x: UInt16
45+
46+
/// The y-coordinate of the touch point on the screen.
47+
///
48+
/// Represents the vertical position of the touch point, typically in pixels.
49+
public let y: UInt16
50+
51+
/// The size of the touch point contact area.
52+
///
53+
/// This value typically indicates the diameter or magnitude of the touch area,
54+
/// reflecting how large the contact is (e.g., larger for more pressure).
55+
public let size: UInt16
56+
}

0 commit comments

Comments
 (0)