1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
/*
* Copyright 2023 jacqueline <me@jacqueline.id.au>
*
* SPDX-License-Identifier: GPL-3.0-only
*/
#include "touchwheel.hpp"
#include <stdint.h>
#include <cstdint>
#include "assert.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/projdefs.h"
#include "hal/gpio_types.h"
#include "hal/i2c_types.h"
#include "i2c.hpp"
namespace drivers {
// Touch wheel implementation using a Microchip AT42QT2120
static const char* kTag = "TOUCHWHEEL";
static const uint8_t kTouchWheelAddress = 0x1C;
static const gpio_num_t kIntPin = GPIO_NUM_25;
TouchWheel::TouchWheel() {
gpio_config_t int_config{
.pin_bit_mask = 1ULL << kIntPin,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&int_config);
WriteRegister(RESET, 1);
vTaskDelay(pdMS_TO_TICKS(300));
// Configure keys 0, 1, and 2 as a wheel.
WriteRegister(SLIDER_OPTIONS, 0b11000000);
// Configure adjacent key suppression.
// Wheel keys. Set to channel 1.
WriteRegister(Register::KEY_CONTROL_BASE + 0, 0b100);
WriteRegister(Register::KEY_CONTROL_BASE + 1, 0b100);
WriteRegister(Register::KEY_CONTROL_BASE + 2, 0b100);
// Centre button. AKS disabled.
WriteRegister(Register::KEY_CONTROL_BASE + 3, 0b000);
// Touch guard. Set as a guard, in channel 1.
WriteRegister(Register::KEY_CONTROL_BASE + 4, 0b10100);
// It's normal to press the wheel for a long time. Disable auto recalibration
// so that the user's finger isn't calibrated away.
WriteRegister(Register::RECALIBRATION_DELAY, 0);
// Unused extra keys. All disabled.
for (int i = 5; i < 12; i++) {
WriteRegister(Register::KEY_CONTROL_BASE + i, 1);
}
}
TouchWheel::~TouchWheel() {}
void TouchWheel::WriteRegister(uint8_t reg, uint8_t val) {
// Addresses <= 5 are not writeable. Make sure we don't try.
assert(reg > 5);
I2CTransaction transaction;
transaction.start()
.write_addr(kTouchWheelAddress, I2C_MASTER_WRITE)
.write_ack(reg, val)
.stop();
esp_err_t res = transaction.Execute(1);
if (res != ESP_OK) {
ESP_LOGW(kTag, "write failed: %s", esp_err_to_name(res));
}
}
uint8_t TouchWheel::ReadRegister(uint8_t reg) {
uint8_t res;
I2CTransaction transaction;
transaction.start()
.write_addr(kTouchWheelAddress, I2C_MASTER_WRITE)
.write_ack(reg)
.start()
.write_addr(kTouchWheelAddress, I2C_MASTER_READ)
.read(&res, I2C_MASTER_NACK)
.stop();
if (transaction.Execute(1) == ESP_OK) {
return res;
} else {
return 0;
}
}
void TouchWheel::Update() {
// Read data from device into member struct
bool has_data = !gpio_get_level(kIntPin);
if (!has_data) {
return;
}
uint8_t status = ReadRegister(Register::DETECTION_STATUS);
if (status & 0b10000000) {
// Still calibrating.
ESP_LOGW(kTag, "awaiting calibration");
return;
}
if (status & 0b01000000) {
// Probably okay, but we should keep an eye on this for development.
ESP_LOGW(kTag, "touchwheel acquisition >16ms");
}
if (status & 0b10) {
// Slider detect.
uint8_t pos = ReadRegister(Register::SLIDER_POSITION);
data_.wheel_position = pos;
}
if (status & 0b1) {
// Key detect. Note that the touchwheel keys also trigger this.
uint8_t reg = ReadRegister(Register::KEY_STATUS_A);
data_.is_button_touched = reg & 0b1000;
data_.is_wheel_touched = reg & 0b111;
} else {
data_.is_button_touched = false;
data_.is_wheel_touched = false;
}
}
TouchWheelData TouchWheel::GetTouchWheelData() const {
return data_;
}
auto TouchWheel::PowerDown() -> void {
WriteRegister(LOW_POWER, 0);
}
} // namespace drivers
|