SensorLib 0.5.1
Multi-platform sensor driver library for Arduino, PlatformIO, and ESP-IDF
Loading...
Searching...
No Matches
aw9523_keypad_5x7.ino
Go to the documentation of this file.
1
13#include <IoExpanderDrv.hpp>
14
15#ifndef SENSOR_SDA
16#define SENSOR_SDA 17
17#endif
18
19#ifndef SENSOR_SCL
20#define SENSOR_SCL 18
21#endif
22
23#ifndef SENSOR_IRQ
24#define SENSOR_IRQ 10
25#endif
26
27static constexpr uint8_t ROW_COUNT = 5;
28static constexpr uint8_t COLUMN_COUNT = 7;
29static constexpr uint8_t ROW_PINS[ROW_COUNT] = {0, 1, 2, 3, 4};
30static constexpr uint8_t COLUMN_PINS[COLUMN_COUNT] = {5, 6, 7, 8, 9, 10, 11};
31static constexpr uint16_t ROW_MASK = 0x001F;
32static constexpr uint16_t COLUMN_MASK = 0x0FE0;
33
34// T-Deck V2 5x7 maps. The first index is the column and the second is the row.
35static constexpr char KEY_MAP[COLUMN_COUNT][ROW_COUNT] = {
36 {'q', 'e', 'r', 'u', 'o'},
37 {'w', 's', 'g', 'h', 'l'},
38 {'\0', 'd', 't', 'y', 'i'},
39 {'a', 'p', '\0', '\n', '\0'},
40 {'\0', 'x', 'v', 'b', '$'},
41 {' ', 'z', 'c', 'n', 'm'},
42 {'\0', '\0', 'f', 'j', 'k'},
43};
44
45static constexpr char SYMBOL_MAP[COLUMN_COUNT][ROW_COUNT] = {
46 {'#', '2', '3', '_', '+'},
47 {'1', '4', '/', ':', '"'},
48 {'\0', '5', '(', ')', '-'},
49 {'*', '@', '\0', '\0', '\0'},
50 {'\0', '8', '?', '!', '\0'},
51 {'\0', '7', '9', ',', '.'},
52 {'0', '\0', '6', ';', '\''},
53};
54
55static constexpr uint8_t SYMBOL_ROW = 0;
56static constexpr uint8_t SYMBOL_COLUMN = 2;
57
59volatile bool keyboardInterrupt = false;
60bool keyState[ROW_COUNT][COLUMN_COUNT] = {};
61
63{
64 keyboardInterrupt = true;
65}
66
67void scanKeyboard(bool state[ROW_COUNT][COLUMN_COUNT])
68{
69 // In open-drain mode HIGH releases a row and LOW selects it.
70 expander.digitalWritePort(ROW_MASK, ROW_MASK);
71
72 for (uint8_t row = 0; row < ROW_COUNT; ++row) {
73 expander.digitalWrite(ROW_PINS[row], LOW);
74 delayMicroseconds(50);
75
76 const uint16_t levels = expander.digitalReadPort();
77 for (uint8_t column = 0; column < COLUMN_COUNT; ++column) {
78 state[row][column] = (levels & (1UL << COLUMN_PINS[column])) == 0;
79 }
80
81 expander.digitalWrite(ROW_PINS[row], HIGH);
82 }
83
84 // All rows low allows any key press or release to change a column level.
85 expander.digitalWritePort(ROW_MASK, 0);
86 delayMicroseconds(20); // Longer than the AW9523 input debounce time.
87 expander.digitalReadPort(); // Clear interrupts generated while scanning.
88}
89
90const char *specialKeyName(uint8_t row, uint8_t column)
91{
92 if (row == 0 && column == 2) return "Symbol";
93 if (row == 2 && column == 3) return "Right Shift";
94 if (row == 4 && column == 3) return "Backspace";
95 if (row == 0 && column == 4) return "Alt";
96 if (row == 0 && column == 6) return "Mic";
97 if (row == 1 && column == 6) return "Left Shift";
98 return nullptr;
99}
100
101void printKeyName(uint8_t row, uint8_t column, bool symbolLayer)
102{
103 const char *name = specialKeyName(row, column);
104 if (name) {
105 Serial.print(name);
106 return;
107 }
108
109 const char key = symbolLayer ? SYMBOL_MAP[column][row] : KEY_MAP[column][row];
110 if (key == '\n') {
111 Serial.print("Enter");
112 } else if (key == ' ') {
113 Serial.print("Space");
114 } else if (key == '\0') {
115 Serial.print("Unmapped");
116 } else {
117 Serial.print(key);
118 }
119}
120
121void reportChanges(const bool current[ROW_COUNT][COLUMN_COUNT])
122{
123 const bool symbolLayer = current[SYMBOL_ROW][SYMBOL_COLUMN];
124 for (uint8_t row = 0; row < ROW_COUNT; ++row) {
125 for (uint8_t column = 0; column < COLUMN_COUNT; ++column) {
126 if (current[row][column] == keyState[row][column]) {
127 continue;
128 }
129
130 keyState[row][column] = current[row][column];
131 printKeyName(row, column, symbolLayer);
132 Serial.println(current[row][column] ? " pressed" : " released");
133 }
134 }
135}
136
137void setup()
138{
139 Serial.begin(115200);
140
141 pinMode(SENSOR_IRQ, INPUT_PULLUP);
142 if (!expander.begin(Wire, AW9523_DEFAULT_ADDRESS, SENSOR_SDA, SENSOR_SCL)) {
143 while (1) {
144 Serial.println("Failed to find AW9523 - check your wiring and address pins!");
145 delay(1000);
146 }
147 }
148
149 // Open-drain rows avoid driving one row high while another row is low.
151 expander.configPins(ROW_MASK, OUTPUT);
152 expander.digitalWritePort(ROW_MASK, 0);
153 expander.configPins(COLUMN_MASK, INPUT);
154
155 // The chip enables every GPIO interrupt after reset. Only watch columns.
157 expander.enableInterrupts(COLUMN_MASK, true);
158
160 keyboardInterrupt = false;
161 attachInterrupt(digitalPinToInterrupt(SENSOR_IRQ), onKeyboardInterrupt, FALLING);
162}
163
164void loop()
165{
166 if (!keyboardInterrupt) {
167 return;
168 }
169
170 keyboardInterrupt = false;
171 delay(10); // Keyboard switch debounce.
172
173 // Matrix scanning changes column levels, so temporarily ignore MCU IRQ edges.
174 detachInterrupt(digitalPinToInterrupt(SENSOR_IRQ));
175 bool current[ROW_COUNT][COLUMN_COUNT] = {};
176 scanKeyboard(current);
177 reportChanges(current);
178 attachInterrupt(digitalPinToInterrupt(SENSOR_IRQ), onKeyboardInterrupt, FALLING);
179
180 // Catch a state change that arrived between the final clear and re-attach.
181 if (digitalRead(SENSOR_IRQ) == LOW) {
182 keyboardInterrupt = true;
183 }
184}
@license MIT License
IoExpanderAW9523 expander
bool keyState[ROW_COUNT][COLUMN_COUNT]
void scanKeyboard(bool state[ROW_COUNT][COLUMN_COUNT])
void reportChanges(const bool current[ROW_COUNT][COLUMN_COUNT])
void onKeyboardInterrupt()
void setup()
#define SENSOR_SCL
#define SENSOR_SDA
void printKeyName(uint8_t row, uint8_t column, bool symbolLayer)
const char * specialKeyName(uint8_t row, uint8_t column)
volatile bool keyboardInterrupt
#define SENSOR_IRQ
void loop()
bool begin(SensorCommCustom::CustomCallback callback, uint8_t addr)
Initialize the sensor using custom callback interface.
Driver for the AW9523/AW9523B 16-channel GPIO and LED controller.
bool enableInterrupts(uint16_t pinMask, bool enable=true)
Enable or disable input-change interrupts for selected pins.
bool setPort0PushPull(bool pushPull)
Select the P0 GPIO output driver type.
uint16_t digitalReadPort() override
Read both GPIO input ports.
void digitalWritePort(uint16_t mask, uint16_t values) override
Write selected GPIO outputs while preserving unselected pins.
void digitalWrite(uint8_t pin, bool value)
Write a digital value to an output pin.
void configPins(uint16_t pinMask, uint8_t mode)
Configure multiple pins as input or output simultaneously.