Skip to content

Commit 8337fcc

Browse files
authored
Some GPIO manipulations in matrix.c change to atomic. (#10491)
* Changed the processing of select_xxx()/unselect_xxx() in quantum/matrix.c to be atomic. * Changed the processing of select_xxx()/unselect_xxx() in quantum/split_common/matrix.c to be atomic. * update matrix.c * add ATOMIC_BLOCK_FORCEON macro to quantum/quantum_atomic_extend.h * quantum_atomic_extend.h's contents move into quantum.h * update ATOMIC_BLOCK_xxx for unknown platform * ATOMIC_BLOCK macro support PROTOCOL_ARM_ATSAM * Add Atomic Operation section in docs/internals_gpio_control.md
1 parent 0fde6c3 commit 8337fcc

File tree

4 files changed

+124
-20
lines changed

4 files changed

+124
-20
lines changed

docs/internals_gpio_control.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,22 @@ The following functions can provide basic control of GPIOs and are found in `qua
2121
## Advanced Settings :id=advanced-settings
2222

2323
Each microcontroller can have multiple advanced settings regarding its GPIO. This abstraction layer does not limit the use of architecture-specific functions. Advanced users should consult the datasheet of their desired device and include any needed libraries. For AVR, the standard avr/io.h library is used; for STM32, the ChibiOS [PAL library](http://chibios.sourceforge.net/docs3/hal/group___p_a_l.html) is used.
24+
25+
## Atomic Operation
26+
27+
The above functions are not always guaranteed to work atomically. Therefore, if you want to prevent interruptions in the middle of operations when using multiple combinations of the above functions, use the following `ATOMIC_BLOCK_FORCEON` macro.
28+
29+
eg.
30+
```c
31+
void some_function() {
32+
// some process
33+
ATOMIC_BLOCK_FORCEON {
34+
// Atomic Processing
35+
}
36+
// some process
37+
}
38+
```
39+
40+
`ATOMIC_BLOCK_FORCEON` forces interrupts to be disabled before the block is executed, without regard to whether they are enabled or disabled. Then, after the block is executed, the interrupt is enabled.
41+
42+
Note that `ATOMIC_BLOCK_FORCEON` can therefore be used if you know that interrupts are enabled before the execution of the block, or if you know that it is OK to enable interrupts at the completion of the block.

quantum/matrix.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
3232
extern matrix_row_t raw_matrix[MATRIX_ROWS]; // raw values
3333
extern matrix_row_t matrix[MATRIX_ROWS]; // debounced values
3434

35+
static inline void setPinOutput_writeLow(pin_t pin) {
36+
ATOMIC_BLOCK_FORCEON {
37+
setPinOutput(pin);
38+
writePinLow(pin);
39+
}
40+
}
41+
42+
static inline void setPinInputHigh_atomic(pin_t pin) {
43+
ATOMIC_BLOCK_FORCEON {
44+
setPinInputHigh(pin);
45+
}
46+
}
47+
3548
// matrix code
3649

3750
#ifdef DIRECT_PINS
@@ -70,22 +83,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
7083
# if (DIODE_DIRECTION == COL2ROW)
7184

7285
static void select_row(uint8_t row) {
73-
setPinOutput(row_pins[row]);
74-
writePinLow(row_pins[row]);
86+
setPinOutput_writeLow(row_pins[row]);
7587
}
7688

77-
static void unselect_row(uint8_t row) { setPinInputHigh(row_pins[row]); }
89+
static void unselect_row(uint8_t row) {
90+
setPinInputHigh_atomic(row_pins[row]);
91+
}
7892

7993
static void unselect_rows(void) {
8094
for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
81-
setPinInputHigh(row_pins[x]);
95+
setPinInputHigh_atomic(row_pins[x]);
8296
}
8397
}
8498

8599
static void init_pins(void) {
86100
unselect_rows();
87101
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
88-
setPinInputHigh(col_pins[x]);
102+
setPinInputHigh_atomic(col_pins[x]);
89103
}
90104
}
91105

@@ -120,22 +134,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
120134
# elif (DIODE_DIRECTION == ROW2COL)
121135

122136
static void select_col(uint8_t col) {
123-
setPinOutput(col_pins[col]);
124-
writePinLow(col_pins[col]);
137+
setPinOutput_writeLow(col_pins[col]);
125138
}
126139

127-
static void unselect_col(uint8_t col) { setPinInputHigh(col_pins[col]); }
140+
static void unselect_col(uint8_t col) {
141+
setPinInputHigh_atomic(col_pins[col]);
142+
}
128143

129144
static void unselect_cols(void) {
130145
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
131-
setPinInputHigh(col_pins[x]);
146+
setPinInputHigh_atomic(col_pins[x]);
132147
}
133148
}
134149

135150
static void init_pins(void) {
136151
unselect_cols();
137152
for (uint8_t x = 0; x < MATRIX_ROWS; x++) {
138-
setPinInputHigh(row_pins[x]);
153+
setPinInputHigh_atomic(row_pins[x]);
139154
}
140155
}
141156

quantum/quantum.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,61 @@ typedef ioline_t pin_t;
220220
# define togglePin(pin) palToggleLine(pin)
221221
#endif
222222

223+
// Atomic macro to help make GPIO and other controls atomic.
224+
#ifdef IGNORE_ATOMIC_BLOCK
225+
/* do nothing atomic macro */
226+
# define ATOMIC_BLOCK for (uint8_t __ToDo = 1; __ToDo; __ToDo = 0)
227+
# define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK
228+
# define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK
229+
230+
#elif defined(__AVR__)
231+
/* atomic macro for AVR */
232+
# include <util/atomic.h>
233+
234+
# define ATOMIC_BLOCK_RESTORESTATE ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
235+
# define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON)
236+
237+
#elif defined(PROTOCOL_CHIBIOS) || defined(PROTOCOL_ARM_ATSAM)
238+
/* atomic macro for ChibiOS / ARM ATSAM */
239+
# if defined(PROTOCOL_ARM_ATSAM)
240+
# include "arm_atsam_protocol.h"
241+
# endif
242+
243+
static __inline__ uint8_t __interrupt_disable__(void) {
244+
# if defined(PROTOCOL_CHIBIOS)
245+
chSysLock();
246+
# endif
247+
# if defined(PROTOCOL_ARM_ATSAM)
248+
__disable_irq();
249+
# endif
250+
return 1;
251+
}
252+
253+
static __inline__ void __interrupt_enable__(const uint8_t *__s) {
254+
# if defined(PROTOCOL_CHIBIOS)
255+
chSysUnlock();
256+
# endif
257+
# if defined(PROTOCOL_ARM_ATSAM)
258+
__enable_irq();
259+
# endif
260+
__asm__ volatile("" ::: "memory");
261+
(void)__s;
262+
}
263+
264+
# define ATOMIC_BLOCK(type) for (type, __ToDo = __interrupt_disable__(); __ToDo; __ToDo = 0)
265+
# define ATOMIC_FORCEON uint8_t sreg_save __attribute__((__cleanup__(__interrupt_enable__))) = 0
266+
267+
# define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE dose not implement")
268+
# define ATOMIC_BLOCK_FORCEON ATOMIC_BLOCK(ATOMIC_FORCEON)
269+
270+
/* Other platform */
271+
#else
272+
273+
# define ATOMIC_BLOCK_RESTORESTATE _Static_assert(0, "ATOMIC_BLOCK_RESTORESTATE dose not implement")
274+
# define ATOMIC_BLOCK_FORCEON _Static_assert(0, "ATOMIC_BLOCK_FORCEON dose not implement")
275+
276+
#endif
277+
223278
#define SEND_STRING(string) send_string_P(PSTR(string))
224279
#define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval)
225280

quantum/split_common/matrix.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ uint8_t thisHand, thatHand;
4545
// user-defined overridable functions
4646
__attribute__((weak)) void matrix_slave_scan_user(void) {}
4747

48+
static inline void setPinOutput_writeLow(pin_t pin) {
49+
ATOMIC_BLOCK_FORCEON {
50+
setPinOutput(pin);
51+
writePinLow(pin);
52+
}
53+
}
54+
55+
static inline void setPinInputHigh_atomic(pin_t pin) {
56+
ATOMIC_BLOCK_FORCEON {
57+
setPinInputHigh(pin);
58+
}
59+
}
60+
4861
// matrix code
4962

5063
#ifdef DIRECT_PINS
@@ -83,22 +96,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
8396
# if (DIODE_DIRECTION == COL2ROW)
8497

8598
static void select_row(uint8_t row) {
86-
setPinOutput(row_pins[row]);
87-
writePinLow(row_pins[row]);
99+
setPinOutput_writeLow(row_pins[row]);
88100
}
89101

90-
static void unselect_row(uint8_t row) { setPinInputHigh(row_pins[row]); }
102+
static void unselect_row(uint8_t row) {
103+
setPinInputHigh_atomic(row_pins[row]);
104+
}
91105

92106
static void unselect_rows(void) {
93107
for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
94-
setPinInputHigh(row_pins[x]);
108+
setPinInputHigh_atomic(row_pins[x]);
95109
}
96110
}
97111

98112
static void init_pins(void) {
99113
unselect_rows();
100114
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
101-
setPinInputHigh(col_pins[x]);
115+
setPinInputHigh_atomic(col_pins[x]);
102116
}
103117
}
104118

@@ -133,22 +147,23 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
133147
# elif (DIODE_DIRECTION == ROW2COL)
134148

135149
static void select_col(uint8_t col) {
136-
setPinOutput(col_pins[col]);
137-
writePinLow(col_pins[col]);
150+
setPinOutput_writeLow(col_pins[col]);
138151
}
139152

140-
static void unselect_col(uint8_t col) { setPinInputHigh(col_pins[col]); }
153+
static void unselect_col(uint8_t col) {
154+
setPinInputHigh_atomic(col_pins[col]);
155+
}
141156

142157
static void unselect_cols(void) {
143158
for (uint8_t x = 0; x < MATRIX_COLS; x++) {
144-
setPinInputHigh(col_pins[x]);
159+
setPinInputHigh_atomic(col_pins[x]);
145160
}
146161
}
147162

148163
static void init_pins(void) {
149164
unselect_cols();
150165
for (uint8_t x = 0; x < ROWS_PER_HAND; x++) {
151-
setPinInputHigh(row_pins[x]);
166+
setPinInputHigh_atomic(row_pins[x]);
152167
}
153168
}
154169

0 commit comments

Comments
 (0)