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); |
