diff options
author | Ryan <fauxpark@gmail.com> | 2020-04-08 11:04:31 +1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-08 11:04:31 +1000 |
commit | 400ca2d035d7bdb85c077a53b2cd85cdd04882ab (patch) | |
tree | dd6c88d9deee75d52af52ee3d208abfa99072386 /tmk_core | |
parent | e409fb47f27f9cf56479928ed86eb2eb346eec54 (diff) | |
download | qmk_firmware-400ca2d035d7bdb85c077a53b2cd85cdd04882ab.tar.gz qmk_firmware-400ca2d035d7bdb85c077a53b2cd85cdd04882ab.zip |
spi_master for AVR (#8299)
* Change _delay_ms/us() to wait_ms/us()
* Switch to platform-agnostic GPIO macros
* Add AVR spi_master and migrate Adafruit BLE code
* Set verbose back to false
* Add clock divisor, bit order and SPI mode configuration for init
* Add start and stop functions
* Move configuration of mode, endianness and speed to `spi_start()`
* Some breaks here would be good
* Default Adafruit BLE clock divisor to 4 (2MHz on the Feather 32U4)
* Remove mode and divisor enums
* Add some docs
* No hr at EOF
* Add links in sidebar
Diffstat (limited to 'tmk_core')
-rw-r--r-- | tmk_core/protocol/lufa.mk | 1 | ||||
-rw-r--r-- | tmk_core/protocol/lufa/adafruit_ble.cpp | 162 |
2 files changed, 39 insertions, 124 deletions
diff --git a/tmk_core/protocol/lufa.mk b/tmk_core/protocol/lufa.mk index d7d1d9ffc..d87802992 100644 --- a/tmk_core/protocol/lufa.mk +++ b/tmk_core/protocol/lufa.mk | |||
@@ -29,6 +29,7 @@ ifeq ($(strip $(BLUETOOTH_ENABLE)), yes) | |||
29 | endif | 29 | endif |
30 | 30 | ||
31 | ifeq ($(strip $(BLUETOOTH)), AdafruitBLE) | 31 | ifeq ($(strip $(BLUETOOTH)), AdafruitBLE) |
32 | LUFA_SRC += spi_master.c | ||
32 | LUFA_SRC += analog.c | 33 | LUFA_SRC += analog.c |
33 | LUFA_SRC += $(LUFA_DIR)/adafruit_ble.cpp | 34 | LUFA_SRC += $(LUFA_DIR)/adafruit_ble.cpp |
34 | endif | 35 | endif |
diff --git a/tmk_core/protocol/lufa/adafruit_ble.cpp b/tmk_core/protocol/lufa/adafruit_ble.cpp index 7b3ffdef7..f04ab757e 100644 --- a/tmk_core/protocol/lufa/adafruit_ble.cpp +++ b/tmk_core/protocol/lufa/adafruit_ble.cpp | |||
@@ -1,15 +1,15 @@ | |||
1 | #include "adafruit_ble.h" | 1 | #include "adafruit_ble.h" |
2 | |||
2 | #include <stdio.h> | 3 | #include <stdio.h> |
3 | #include <stdlib.h> | 4 | #include <stdlib.h> |
4 | #include <alloca.h> | 5 | #include <alloca.h> |
5 | #include <util/delay.h> | ||
6 | #include <util/atomic.h> | ||
7 | #include "debug.h" | 6 | #include "debug.h" |
8 | #include "pincontrol.h" | ||
9 | #include "timer.h" | 7 | #include "timer.h" |
10 | #include "action_util.h" | 8 | #include "action_util.h" |
11 | #include "ringbuffer.hpp" | 9 | #include "ringbuffer.hpp" |
12 | #include <string.h> | 10 | #include <string.h> |
11 | #include "spi_master.h" | ||
12 | #include "wait.h" | ||
13 | #include "analog.h" | 13 | #include "analog.h" |
14 | 14 | ||
15 | // These are the pin assignments for the 32u4 boards. | 15 | // These are the pin assignments for the 32u4 boards. |
@@ -27,6 +27,12 @@ | |||
27 | # define AdafruitBleIRQPin E6 | 27 | # define AdafruitBleIRQPin E6 |
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | #ifndef AdafruitBleSpiClockSpeed | ||
31 | # define AdafruitBleSpiClockSpeed 4000000UL // SCK frequency | ||
32 | #endif | ||
33 | |||
34 | #define SCK_DIVISOR (F_CPU / AdafruitBleSpiClockSpeed) | ||
35 | |||
30 | #define SAMPLE_BATTERY | 36 | #define SAMPLE_BATTERY |
31 | #define ConnectionUpdateInterval 1000 /* milliseconds */ | 37 | #define ConnectionUpdateInterval 1000 /* milliseconds */ |
32 | 38 | ||
@@ -130,10 +136,6 @@ enum ble_system_event_bits { | |||
130 | BleSystemMidiRx = 10, | 136 | BleSystemMidiRx = 10, |
131 | }; | 137 | }; |
132 | 138 | ||
133 | // The SDEP.md file says 2MHz but the web page and the sample driver | ||
134 | // both use 4MHz | ||
135 | #define SpiBusSpeed 4000000 | ||
136 | |||
137 | #define SdepTimeout 150 /* milliseconds */ | 139 | #define SdepTimeout 150 /* milliseconds */ |
138 | #define SdepShortTimeout 10 /* milliseconds */ | 140 | #define SdepShortTimeout 10 /* milliseconds */ |
139 | #define SdepBackOff 25 /* microseconds */ | 141 | #define SdepBackOff 25 /* microseconds */ |
@@ -142,116 +144,32 @@ enum ble_system_event_bits { | |||
142 | static bool at_command(const char *cmd, char *resp, uint16_t resplen, bool verbose, uint16_t timeout = SdepTimeout); | 144 | static bool at_command(const char *cmd, char *resp, uint16_t resplen, bool verbose, uint16_t timeout = SdepTimeout); |
143 | static bool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool verbose = false); | 145 | static bool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool verbose = false); |
144 | 146 | ||
145 | struct SPI_Settings { | ||
146 | uint8_t spcr, spsr; | ||
147 | }; | ||
148 | |||
149 | static struct SPI_Settings spi; | ||
150 | |||
151 | // Initialize 4Mhz MSBFIRST MODE0 | ||
152 | void SPI_init(struct SPI_Settings *spi) { | ||
153 | spi->spcr = _BV(SPE) | _BV(MSTR); | ||
154 | #if F_CPU == 8000000 | ||
155 | // For MCUs running at 8MHz (such as Feather 32U4, or 3.3V Pro Micros) we set the SPI doublespeed bit | ||
156 | spi->spsr = _BV(SPI2X); | ||
157 | #endif | ||
158 | |||
159 | ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { | ||
160 | // Ensure that SS is OUTPUT High | ||
161 | digitalWrite(B0, PinLevelHigh); | ||
162 | pinMode(B0, PinDirectionOutput); | ||
163 | |||
164 | SPCR |= _BV(MSTR); | ||
165 | SPCR |= _BV(SPE); | ||
166 | pinMode(B1 /* SCK */, PinDirectionOutput); | ||
167 | pinMode(B2 /* MOSI */, PinDirectionOutput); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | static inline void SPI_begin(struct SPI_Settings *spi) { | ||
172 | SPCR = spi->spcr; | ||
173 | SPSR = spi->spsr; | ||
174 | } | ||
175 | |||
176 | static inline uint8_t SPI_TransferByte(uint8_t data) { | ||
177 | SPDR = data; | ||
178 | asm volatile("nop"); | ||
179 | while (!(SPSR & _BV(SPIF))) { | ||
180 | ; // wait | ||
181 | } | ||
182 | return SPDR; | ||
183 | } | ||
184 | |||
185 | static inline void spi_send_bytes(const uint8_t *buf, uint8_t len) { | ||
186 | if (len == 0) return; | ||
187 | const uint8_t *end = buf + len; | ||
188 | while (buf < end) { | ||
189 | SPDR = *buf; | ||
190 | while (!(SPSR & _BV(SPIF))) { | ||
191 | ; // wait | ||
192 | } | ||
193 | ++buf; | ||
194 | } | ||
195 | } | ||
196 | |||
197 | static inline uint16_t spi_read_byte(void) { return SPI_TransferByte(0x00 /* dummy */); } | ||
198 | |||
199 | static inline void spi_recv_bytes(uint8_t *buf, uint8_t len) { | ||
200 | const uint8_t *end = buf + len; | ||
201 | if (len == 0) return; | ||
202 | while (buf < end) { | ||
203 | SPDR = 0; // write a dummy to initiate read | ||
204 | while (!(SPSR & _BV(SPIF))) { | ||
205 | ; // wait | ||
206 | } | ||
207 | *buf = SPDR; | ||
208 | ++buf; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | #if 0 | ||
213 | static void dump_pkt(const struct sdep_msg *msg) { | ||
214 | print("pkt: type="); | ||
215 | print_hex8(msg->type); | ||
216 | print(" cmd="); | ||
217 | print_hex8(msg->cmd_high); | ||
218 | print_hex8(msg->cmd_low); | ||
219 | print(" len="); | ||
220 | print_hex8(msg->len); | ||
221 | print(" more="); | ||
222 | print_hex8(msg->more); | ||
223 | print("\n"); | ||
224 | } | ||
225 | #endif | ||
226 | |||
227 | // Send a single SDEP packet | 147 | // Send a single SDEP packet |
228 | static bool sdep_send_pkt(const struct sdep_msg *msg, uint16_t timeout) { | 148 | static bool sdep_send_pkt(const struct sdep_msg *msg, uint16_t timeout) { |
229 | SPI_begin(&spi); | 149 | spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR); |
230 | |||
231 | digitalWrite(AdafruitBleCSPin, PinLevelLow); | ||
232 | uint16_t timerStart = timer_read(); | 150 | uint16_t timerStart = timer_read(); |
233 | bool success = false; | 151 | bool success = false; |
234 | bool ready = false; | 152 | bool ready = false; |
235 | 153 | ||
236 | do { | 154 | do { |
237 | ready = SPI_TransferByte(msg->type) != SdepSlaveNotReady; | 155 | ready = spi_write(msg->type, 100) != SdepSlaveNotReady; |
238 | if (ready) { | 156 | if (ready) { |
239 | break; | 157 | break; |
240 | } | 158 | } |
241 | 159 | ||
242 | // Release it and let it initialize | 160 | // Release it and let it initialize |
243 | digitalWrite(AdafruitBleCSPin, PinLevelHigh); | 161 | spi_stop(); |
244 | _delay_us(SdepBackOff); | 162 | wait_us(SdepBackOff); |
245 | digitalWrite(AdafruitBleCSPin, PinLevelLow); | 163 | spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR); |
246 | } while (timer_elapsed(timerStart) < timeout); | 164 | } while (timer_elapsed(timerStart) < timeout); |
247 | 165 | ||
248 | if (ready) { | 166 | if (ready) { |
249 | // Slave is ready; send the rest of the packet | 167 | // Slave is ready; send the rest of the packet |
250 | spi_send_bytes(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload)) + msg->len); | 168 | spi_transmit(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload)) + msg->len, 100); |
251 | success = true; | 169 | success = true; |
252 | } | 170 | } |
253 | 171 | ||
254 | digitalWrite(AdafruitBleCSPin, PinLevelHigh); | 172 | spi_stop(); |
255 | 173 | ||
256 | return success; | 174 | return success; |
257 | } | 175 | } |
@@ -275,41 +193,39 @@ static bool sdep_recv_pkt(struct sdep_msg *msg, uint16_t timeout) { | |||
275 | bool ready = false; | 193 | bool ready = false; |
276 | 194 | ||
277 | do { | 195 | do { |
278 | ready = digitalRead(AdafruitBleIRQPin); | 196 | ready = readPin(AdafruitBleIRQPin); |
279 | if (ready) { | 197 | if (ready) { |
280 | break; | 198 | break; |
281 | } | 199 | } |
282 | _delay_us(1); | 200 | wait_us(1); |
283 | } while (timer_elapsed(timerStart) < timeout); | 201 | } while (timer_elapsed(timerStart) < timeout); |
284 | 202 | ||
285 | if (ready) { | 203 | if (ready) { |
286 | SPI_begin(&spi); | 204 | spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR); |
287 | |||
288 | digitalWrite(AdafruitBleCSPin, PinLevelLow); | ||
289 | 205 | ||
290 | do { | 206 | do { |
291 | // Read the command type, waiting for the data to be ready | 207 | // Read the command type, waiting for the data to be ready |
292 | msg->type = spi_read_byte(); | 208 | msg->type = spi_read(100); |
293 | if (msg->type == SdepSlaveNotReady || msg->type == SdepSlaveOverflow) { | 209 | if (msg->type == SdepSlaveNotReady || msg->type == SdepSlaveOverflow) { |
294 | // Release it and let it initialize | 210 | // Release it and let it initialize |
295 | digitalWrite(AdafruitBleCSPin, PinLevelHigh); | 211 | spi_stop(); |
296 | _delay_us(SdepBackOff); | 212 | wait_us(SdepBackOff); |
297 | digitalWrite(AdafruitBleCSPin, PinLevelLow); | 213 | spi_start(AdafruitBleCSPin, false, 0, SCK_DIVISOR); |
298 | continue; | 214 | continue; |
299 | } | 215 | } |
300 | 216 | ||
301 | // Read the rest of the header | 217 | // Read the rest of the header |
302 | spi_recv_bytes(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload))); | 218 | spi_receive(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload)), 100); |
303 | 219 | ||
304 | // and get the payload if there is any | 220 | // and get the payload if there is any |
305 | if (msg->len <= SdepMaxPayload) { | 221 | if (msg->len <= SdepMaxPayload) { |
306 | spi_recv_bytes(msg->payload, msg->len); | 222 | spi_receive(msg->payload, msg->len, 100); |
307 | } | 223 | } |
308 | success = true; | 224 | success = true; |
309 | break; | 225 | break; |
310 | } while (timer_elapsed(timerStart) < timeout); | 226 | } while (timer_elapsed(timerStart) < timeout); |
311 | 227 | ||
312 | digitalWrite(AdafruitBleCSPin, PinLevelHigh); | 228 | spi_stop(); |
313 | } | 229 | } |
314 | return success; | 230 | return success; |
315 | } | 231 | } |
@@ -320,7 +236,7 @@ static void resp_buf_read_one(bool greedy) { | |||
320 | return; | 236 | return; |
321 | } | 237 | } |
322 | 238 | ||
323 | if (digitalRead(AdafruitBleIRQPin)) { | 239 | if (readPin(AdafruitBleIRQPin)) { |
324 | struct sdep_msg msg; | 240 | struct sdep_msg msg; |
325 | 241 | ||
326 | again: | 242 | again: |
@@ -331,7 +247,7 @@ static void resp_buf_read_one(bool greedy) { | |||
331 | dprintf("recv latency %dms\n", TIMER_DIFF_16(timer_read(), last_send)); | 247 | dprintf("recv latency %dms\n", TIMER_DIFF_16(timer_read(), last_send)); |
332 | } | 248 | } |
333 | 249 | ||
334 | if (greedy && resp_buf.peek(last_send) && digitalRead(AdafruitBleIRQPin)) { | 250 | if (greedy && resp_buf.peek(last_send) && readPin(AdafruitBleIRQPin)) { |
335 | goto again; | 251 | goto again; |
336 | } | 252 | } |
337 | } | 253 | } |
@@ -361,7 +277,7 @@ static void send_buf_send_one(uint16_t timeout = SdepTimeout) { | |||
361 | dprintf("send_buf_send_one: have %d remaining\n", (int)send_buf.size()); | 277 | dprintf("send_buf_send_one: have %d remaining\n", (int)send_buf.size()); |
362 | } else { | 278 | } else { |
363 | dprint("failed to send, will retry\n"); | 279 | dprint("failed to send, will retry\n"); |
364 | _delay_ms(SdepTimeout); | 280 | wait_ms(SdepTimeout); |
365 | resp_buf_read_one(true); | 281 | resp_buf_read_one(true); |
366 | } | 282 | } |
367 | } | 283 | } |
@@ -382,20 +298,18 @@ static bool ble_init(void) { | |||
382 | state.configured = false; | 298 | state.configured = false; |
383 | state.is_connected = false; | 299 | state.is_connected = false; |
384 | 300 | ||
385 | pinMode(AdafruitBleIRQPin, PinDirectionInput); | 301 | setPinInput(AdafruitBleIRQPin); |
386 | pinMode(AdafruitBleCSPin, PinDirectionOutput); | ||
387 | digitalWrite(AdafruitBleCSPin, PinLevelHigh); | ||
388 | 302 | ||
389 | SPI_init(&spi); | 303 | spi_init(); |
390 | 304 | ||
391 | // Perform a hardware reset | 305 | // Perform a hardware reset |
392 | pinMode(AdafruitBleResetPin, PinDirectionOutput); | 306 | setPinOutput(AdafruitBleResetPin); |
393 | digitalWrite(AdafruitBleResetPin, PinLevelHigh); | 307 | writePinHigh(AdafruitBleResetPin); |
394 | digitalWrite(AdafruitBleResetPin, PinLevelLow); | 308 | writePinLow(AdafruitBleResetPin); |
395 | _delay_ms(10); | 309 | wait_ms(10); |
396 | digitalWrite(AdafruitBleResetPin, PinLevelHigh); | 310 | writePinHigh(AdafruitBleResetPin); |
397 | 311 | ||
398 | _delay_ms(1000); // Give it a second to initialize | 312 | wait_ms(1000); // Give it a second to initialize |
399 | 313 | ||
400 | state.initialized = true; | 314 | state.initialized = true; |
401 | return state.initialized; | 315 | return state.initialized; |
@@ -596,7 +510,7 @@ void adafruit_ble_task(void) { | |||
596 | resp_buf_read_one(true); | 510 | resp_buf_read_one(true); |
597 | send_buf_send_one(SdepShortTimeout); | 511 | send_buf_send_one(SdepShortTimeout); |
598 | 512 | ||
599 | if (resp_buf.empty() && (state.event_flags & UsingEvents) && digitalRead(AdafruitBleIRQPin)) { | 513 | if (resp_buf.empty() && (state.event_flags & UsingEvents) && readPin(AdafruitBleIRQPin)) { |
600 | // Must be an event update | 514 | // Must be an event update |
601 | if (at_command_P(PSTR("AT+EVENTSTATUS"), resbuf, sizeof(resbuf))) { | 515 | if (at_command_P(PSTR("AT+EVENTSTATUS"), resbuf, sizeof(resbuf))) { |
602 | uint32_t mask = strtoul(resbuf, NULL, 16); | 516 | uint32_t mask = strtoul(resbuf, NULL, 16); |