aboutsummaryrefslogtreecommitdiff
path: root/tmk_core
diff options
context:
space:
mode:
authorJoel Challis <git@zvecr.com>2021-10-22 00:49:38 +0100
committerGitHub <noreply@github.com>2021-10-22 00:49:38 +0100
commit1b1f3ec68ee1e7abe436a46bcfedf30f21330aef (patch)
treebbb7e0dd548773b6c1ead6275a4bafc1910c444a /tmk_core
parent1b93d576f84822d0f4947179ee49f614ba345f12 (diff)
downloadqmk_firmware-1b1f3ec68ee1e7abe436a46bcfedf30f21330aef.tar.gz
qmk_firmware-1b1f3ec68ee1e7abe436a46bcfedf30f21330aef.zip
Split out arm_atsam shift register logic (#14848)
Diffstat (limited to 'tmk_core')
-rw-r--r--tmk_core/common/arm_atsam/gpio.h8
-rw-r--r--tmk_core/protocol/arm_atsam.mk3
-rw-r--r--tmk_core/protocol/arm_atsam/arm_atsam_protocol.h2
-rw-r--r--tmk_core/protocol/arm_atsam/shift_register.c118
-rw-r--r--tmk_core/protocol/arm_atsam/shift_register.h (renamed from tmk_core/protocol/arm_atsam/spi.h)25
-rw-r--r--tmk_core/protocol/arm_atsam/spi.c92
-rw-r--r--tmk_core/protocol/arm_atsam/spi_master.c109
-rw-r--r--tmk_core/protocol/arm_atsam/spi_master.h48
8 files changed, 287 insertions, 118 deletions
diff --git a/tmk_core/common/arm_atsam/gpio.h b/tmk_core/common/arm_atsam/gpio.h
index c2d5a3088..915ed0ef4 100644
--- a/tmk_core/common/arm_atsam/gpio.h
+++ b/tmk_core/common/arm_atsam/gpio.h
@@ -64,7 +64,13 @@ typedef uint8_t pin_t;
64 PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \ 64 PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \
65 } while (0) 65 } while (0)
66 66
67#define writePin(pin, level) ((level) ? (writePinHigh(pin)) : (writePinLow(pin))) 67#define writePin(pin, level) \
68 do { \
69 if (level) \
70 PORT->Group[SAMD_PORT(pin)].OUTSET.reg = SAMD_PIN_MASK(pin); \
71 else \
72 PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \
73 } while (0)
68 74
69#define readPin(pin) ((PORT->Group[SAMD_PORT(pin)].IN.reg & SAMD_PIN_MASK(pin)) != 0) 75#define readPin(pin) ((PORT->Group[SAMD_PORT(pin)].IN.reg & SAMD_PIN_MASK(pin)) != 0)
70 76
diff --git a/tmk_core/protocol/arm_atsam.mk b/tmk_core/protocol/arm_atsam.mk
index 5bb45d658..e3b550596 100644
--- a/tmk_core/protocol/arm_atsam.mk
+++ b/tmk_core/protocol/arm_atsam.mk
@@ -9,7 +9,8 @@ ifeq ($(RGB_MATRIX_DRIVER),custom)
9 SRC += $(ARM_ATSAM_DIR)/md_rgb_matrix.c 9 SRC += $(ARM_ATSAM_DIR)/md_rgb_matrix.c
10endif 10endif
11SRC += $(ARM_ATSAM_DIR)/main_arm_atsam.c 11SRC += $(ARM_ATSAM_DIR)/main_arm_atsam.c
12SRC += $(ARM_ATSAM_DIR)/spi.c 12SRC += $(ARM_ATSAM_DIR)/shift_register.c
13SRC += $(ARM_ATSAM_DIR)/spi_master.c
13SRC += $(ARM_ATSAM_DIR)/startup.c 14SRC += $(ARM_ATSAM_DIR)/startup.c
14 15
15SRC += $(ARM_ATSAM_DIR)/usb/main_usb.c 16SRC += $(ARM_ATSAM_DIR)/usb/main_usb.c
diff --git a/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h b/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h
index d126c66e7..c3eab39fb 100644
--- a/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h
+++ b/tmk_core/protocol/arm_atsam/arm_atsam_protocol.h
@@ -27,7 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
27#include "wait.h" 27#include "wait.h"
28#include "adc.h" 28#include "adc.h"
29#include "i2c_master.h" 29#include "i2c_master.h"
30#include "spi.h" 30#include "shift_register.h"
31 31
32#include "./usb/usb2422.h" 32#include "./usb/usb2422.h"
33 33
diff --git a/tmk_core/protocol/arm_atsam/shift_register.c b/tmk_core/protocol/arm_atsam/shift_register.c
new file mode 100644
index 000000000..8d63af1b5
--- /dev/null
+++ b/tmk_core/protocol/arm_atsam/shift_register.c
@@ -0,0 +1,118 @@
1/*
2Copyright 2018 Massdrop Inc.
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include "arm_atsam_protocol.h"
19
20#include "spi_master.h"
21#include "wait.h"
22#include "gpio.h"
23
24// #define SR_USE_BITBANG
25
26// Bodge for when spi_master is not available
27#ifdef SR_USE_BITBANG
28# define CLOCK_DELAY 10
29
30void shift_init_impl(void) {
31 setPinOutput(SR_EXP_RCLK_PIN);
32 setPinOutput(SPI_DATAOUT_PIN);
33 setPinOutput(SPI_SCLK_PIN);
34}
35
36void shift_out_impl(const uint8_t *data, uint16_t length) {
37 writePinLow(SR_EXP_RCLK_PIN);
38 for (uint16_t i = 0; i < length; i++) {
39 uint8_t val = data[i];
40
41 // shift out lsb first
42 for (uint8_t bit = 0; bit < 8; bit++) {
43 writePin(SPI_DATAOUT_PIN, !!(val & (1 << bit)));
44 writePin(SPI_SCLK_PIN, true);
45 wait_us(CLOCK_DELAY);
46
47 writePin(SPI_SCLK_PIN, false);
48 wait_us(CLOCK_DELAY);
49 }
50 }
51 writePinHigh(SR_EXP_RCLK_PIN);
52 return SPI_STATUS_SUCCESS;
53}
54
55#else
56
57void shift_init_impl(void) { spi_init(); }
58
59void shift_out_impl(const uint8_t *data, uint16_t length) {
60 spi_start(SR_EXP_RCLK_PIN, true, 0, 0);
61
62 spi_transmit(data, length);
63
64 spi_stop();
65}
66#endif
67
68// ***************************************************************
69
70void shift_out(const uint8_t *data, uint16_t length) { shift_out_impl(data, length); }
71
72void shift_enable(void) {
73 setPinOutput(SR_EXP_OE_PIN);
74 writePinLow(SR_EXP_OE_PIN);
75}
76
77void shift_disable(void) {
78 setPinOutput(SR_EXP_OE_PIN);
79 writePinHigh(SR_EXP_OE_PIN);
80}
81
82void shift_init(void) {
83 shift_disable();
84 shift_init_impl();
85}
86
87// ***************************************************************
88
89sr_exp_t sr_exp_data;
90
91void SR_EXP_WriteData(void) {
92 uint8_t data[2] = {
93 sr_exp_data.reg & 0xFF, // Shift in bits 7-0
94 (sr_exp_data.reg >> 8) & 0xFF, // Shift in bits 15-8
95 };
96 shift_out(data, 2);
97}
98
99void SR_EXP_Init(void) {
100 shift_init();
101
102 sr_exp_data.reg = 0;
103 sr_exp_data.bit.HUB_CONNECT = 0;
104 sr_exp_data.bit.HUB_RESET_N = 0;
105 sr_exp_data.bit.S_UP = 0;
106 sr_exp_data.bit.E_UP_N = 1;
107 sr_exp_data.bit.S_DN1 = 1;
108 sr_exp_data.bit.E_DN1_N = 1;
109 sr_exp_data.bit.E_VBUS_1 = 0;
110 sr_exp_data.bit.E_VBUS_2 = 0;
111 sr_exp_data.bit.SRC_1 = 1;
112 sr_exp_data.bit.SRC_2 = 1;
113 sr_exp_data.bit.IRST = 1;
114 sr_exp_data.bit.SDB_N = 0;
115 SR_EXP_WriteData();
116
117 shift_enable();
118}
diff --git a/tmk_core/protocol/arm_atsam/spi.h b/tmk_core/protocol/arm_atsam/shift_register.h
index dcd45f31a..56a8c7f71 100644
--- a/tmk_core/protocol/arm_atsam/spi.h
+++ b/tmk_core/protocol/arm_atsam/shift_register.h
@@ -15,28 +15,9 @@ You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>. 15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/ 16*/
17 17
18#ifndef _SPI_H_ 18#pragma once
19#define _SPI_H_
20 19
21/* Macros for Shift Register control */ 20#include <stdint.h>
22#define SR_EXP_RCLK_LO PORT->Group[SR_EXP_RCLK_PORT].OUTCLR.reg = (1 << SR_EXP_RCLK_PIN)
23#define SR_EXP_RCLK_HI PORT->Group[SR_EXP_RCLK_PORT].OUTSET.reg = (1 << SR_EXP_RCLK_PIN)
24#define SR_EXP_OE_N_ENA PORT->Group[SR_EXP_OE_N_PORT].OUTCLR.reg = (1 << SR_EXP_OE_N_PIN)
25#define SR_EXP_OE_N_DIS PORT->Group[SR_EXP_OE_N_PORT].OUTSET.reg = (1 << SR_EXP_OE_N_PIN)
26
27/* Determine bits to set for mux selection */
28#if SR_EXP_DATAOUT_PIN % 2 == 0
29# define SR_EXP_DATAOUT_MUX_SEL PMUXE
30#else
31# define SR_EXP_DATAOUT_MUX_SEL PMUXO
32#endif
33
34/* Determine bits to set for mux selection */
35#if SR_EXP_SCLK_PIN % 2 == 0
36# define SR_EXP_SCLK_MUX_SEL PMUXE
37#else
38# define SR_EXP_SCLK_MUX_SEL PMUXO
39#endif
40 21
41/* Data structure to define Shift Register output expander hardware */ 22/* Data structure to define Shift Register output expander hardware */
42/* This structure gets shifted into registers LSB first */ 23/* This structure gets shifted into registers LSB first */
@@ -66,5 +47,3 @@ extern sr_exp_t sr_exp_data;
66 47
67void SR_EXP_WriteData(void); 48void SR_EXP_WriteData(void);
68void SR_EXP_Init(void); 49void SR_EXP_Init(void);
69
70#endif //_SPI_H_
diff --git a/tmk_core/protocol/arm_atsam/spi.c b/tmk_core/protocol/arm_atsam/spi.c
deleted file mode 100644
index 3b118bc1f..000000000
--- a/tmk_core/protocol/arm_atsam/spi.c
+++ /dev/null
@@ -1,92 +0,0 @@
1/*
2Copyright 2018 Massdrop Inc.
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include "arm_atsam_protocol.h"
19
20sr_exp_t sr_exp_data;
21
22void SR_EXP_WriteData(void) {
23 SR_EXP_RCLK_LO;
24
25 while (!(SR_EXP_SERCOM->SPI.INTFLAG.bit.DRE)) {
26 DBGC(DC_SPI_WRITE_DRE);
27 }
28
29 SR_EXP_SERCOM->SPI.DATA.bit.DATA = sr_exp_data.reg & 0xFF; // Shift in bits 7-0
30 while (!(SR_EXP_SERCOM->SPI.INTFLAG.bit.TXC)) {
31 DBGC(DC_SPI_WRITE_TXC_1);
32 }
33
34 SR_EXP_SERCOM->SPI.DATA.bit.DATA = (sr_exp_data.reg >> 8) & 0xFF; // Shift in bits 15-8
35 while (!(SR_EXP_SERCOM->SPI.INTFLAG.bit.TXC)) {
36 DBGC(DC_SPI_WRITE_TXC_2);
37 }
38
39 SR_EXP_RCLK_HI;
40}
41
42void SR_EXP_Init(void) {
43 DBGC(DC_SPI_INIT_BEGIN);
44
45 CLK_set_spi_freq(CHAN_SERCOM_SPI, FREQ_SPI_DEFAULT);
46
47 // Set up MCU Shift Register pins
48 PORT->Group[SR_EXP_RCLK_PORT].DIRSET.reg = (1 << SR_EXP_RCLK_PIN);
49 PORT->Group[SR_EXP_OE_N_PORT].DIRSET.reg = (1 << SR_EXP_OE_N_PIN);
50
51 // Set up MCU SPI pins
52 PORT->Group[SR_EXP_DATAOUT_PORT].PMUX[SR_EXP_DATAOUT_PIN / 2].bit.SR_EXP_DATAOUT_MUX_SEL = SR_EXP_DATAOUT_MUX; // MUX select for sercom
53 PORT->Group[SR_EXP_SCLK_PORT].PMUX[SR_EXP_SCLK_PIN / 2].bit.SR_EXP_SCLK_MUX_SEL = SR_EXP_SCLK_MUX; // MUX select for sercom
54 PORT->Group[SR_EXP_DATAOUT_PORT].PINCFG[SR_EXP_DATAOUT_PIN].bit.PMUXEN = 1; // MUX Enable
55 PORT->Group[SR_EXP_SCLK_PORT].PINCFG[SR_EXP_SCLK_PIN].bit.PMUXEN = 1; // MUX Enable
56
57 // Initialize Shift Register
58 SR_EXP_OE_N_DIS;
59 SR_EXP_RCLK_HI;
60
61 SR_EXP_SERCOM->SPI.CTRLA.bit.DORD = 1; // Data Order - LSB is transferred first
62 SR_EXP_SERCOM->SPI.CTRLA.bit.CPOL = 1; // Clock Polarity - SCK high when idle. Leading edge of cycle is falling. Trailing rising.
63 SR_EXP_SERCOM->SPI.CTRLA.bit.CPHA = 1; // Clock Phase - Leading Edge Falling, change, Trailing Edge - Rising, sample
64 SR_EXP_SERCOM->SPI.CTRLA.bit.DIPO = 3; // Data In Pinout - SERCOM PAD[3] is used as data input (Configure away from DOPO. Not using input.)
65 SR_EXP_SERCOM->SPI.CTRLA.bit.DOPO = 0; // Data Output PAD[0], Serial Clock PAD[1]
66 SR_EXP_SERCOM->SPI.CTRLA.bit.MODE = 3; // Operating Mode - Master operation
67
68 SR_EXP_SERCOM->SPI.CTRLA.bit.ENABLE = 1; // Enable - Peripheral is enabled or being enabled
69 while (SR_EXP_SERCOM->SPI.SYNCBUSY.bit.ENABLE) {
70 DBGC(DC_SPI_SYNC_ENABLING);
71 }
72
73 sr_exp_data.reg = 0;
74 sr_exp_data.bit.HUB_CONNECT = 0;
75 sr_exp_data.bit.HUB_RESET_N = 0;
76 sr_exp_data.bit.S_UP = 0;
77 sr_exp_data.bit.E_UP_N = 1;
78 sr_exp_data.bit.S_DN1 = 1;
79 sr_exp_data.bit.E_DN1_N = 1;
80 sr_exp_data.bit.E_VBUS_1 = 0;
81 sr_exp_data.bit.E_VBUS_2 = 0;
82 sr_exp_data.bit.SRC_1 = 1;
83 sr_exp_data.bit.SRC_2 = 1;
84 sr_exp_data.bit.IRST = 1;
85 sr_exp_data.bit.SDB_N = 0;
86 SR_EXP_WriteData();
87
88 // Enable Shift Register output
89 SR_EXP_OE_N_ENA;
90
91 DBGC(DC_SPI_INIT_COMPLETE);
92}
diff --git a/tmk_core/protocol/arm_atsam/spi_master.c b/tmk_core/protocol/arm_atsam/spi_master.c
new file mode 100644
index 000000000..9781d45b1
--- /dev/null
+++ b/tmk_core/protocol/arm_atsam/spi_master.c
@@ -0,0 +1,109 @@
1/*
2Copyright 2018 Massdrop Inc.
3
4This program is free software: you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation, either version 2 of the License, or
7(at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18#include "arm_atsam_protocol.h"
19#include "spi_master.h"
20#include "gpio.h"
21
22/* Determine bits to set for mux selection */
23#if SPI_DATAOUT_PIN % 2 == 0
24# define SPI_DATAOUT_MUX_SEL PMUXE
25#else
26# define SPI_DATAOUT_MUX_SEL PMUXO
27#endif
28
29/* Determine bits to set for mux selection */
30#if SPI_SCLK_PIN % 2 == 0
31# define SPI_SCLK_MUX_SEL PMUXE
32#else
33# define SPI_SCLK_MUX_SEL PMUXO
34#endif
35
36static pin_t currentSelectPin = NO_PIN;
37
38__attribute__((weak)) void spi_init(void) {
39 static bool is_initialised = false;
40 if (!is_initialised) {
41 is_initialised = true;
42
43 DBGC(DC_SPI_INIT_BEGIN);
44
45 CLK_set_spi_freq(CHAN_SERCOM_SPI, FREQ_SPI_DEFAULT);
46
47 // Set up MCU SPI pins
48 PORT->Group[SAMD_PORT(SPI_DATAOUT_PIN)].PMUX[SAMD_PIN(SPI_DATAOUT_PIN) / 2].bit.SPI_DATAOUT_MUX_SEL = SPI_DATAOUT_MUX; // MUX select for sercom
49 PORT->Group[SAMD_PORT(SPI_SCLK_PIN)].PMUX[SAMD_PIN(SPI_SCLK_PIN) / 2].bit.SPI_SCLK_MUX_SEL = SPI_SCLK_MUX; // MUX select for sercom
50 PORT->Group[SAMD_PORT(SPI_DATAOUT_PIN)].PINCFG[SAMD_PIN(SPI_DATAOUT_PIN)].bit.PMUXEN = 1; // MUX Enable
51 PORT->Group[SAMD_PORT(SPI_SCLK_PIN)].PINCFG[SAMD_PIN(SPI_SCLK_PIN)].bit.PMUXEN = 1; // MUX Enable
52
53 DBGC(DC_SPI_INIT_COMPLETE);
54 }
55}
56
57bool spi_start(pin_t csPin, bool lsbFirst, uint8_t mode, uint16_t divisor) {
58 if (currentSelectPin != NO_PIN || csPin == NO_PIN) {
59 return false;
60 }
61
62 currentSelectPin = csPin;
63 setPinOutput(currentSelectPin);
64 writePinLow(currentSelectPin);
65
66 SPI_SERCOM->SPI.CTRLA.bit.DORD = lsbFirst; // Data Order - LSB is transferred first
67 SPI_SERCOM->SPI.CTRLA.bit.CPOL = 1; // Clock Polarity - SCK high when idle. Leading edge of cycle is falling. Trailing rising.
68 SPI_SERCOM->SPI.CTRLA.bit.CPHA = 1; // Clock Phase - Leading Edge Falling, change, Trailing Edge - Rising, sample
69 SPI_SERCOM->SPI.CTRLA.bit.DIPO = 3; // Data In Pinout - SERCOM PAD[3] is used as data input (Configure away from DOPO. Not using input.)
70 SPI_SERCOM->SPI.CTRLA.bit.DOPO = 0; // Data Output PAD[0], Serial Clock PAD[1]
71 SPI_SERCOM->SPI.CTRLA.bit.MODE = 3; // Operating Mode - Master operation
72
73 SPI_SERCOM->SPI.CTRLA.bit.ENABLE = 1; // Enable - Peripheral is enabled or being enabled
74 while (SPI_SERCOM->SPI.SYNCBUSY.bit.ENABLE) {
75 DBGC(DC_SPI_SYNC_ENABLING);
76 }
77 return true;
78}
79
80spi_status_t spi_transmit(const uint8_t *data, uint16_t length) {
81 while (!(SPI_SERCOM->SPI.INTFLAG.bit.DRE)) {
82 DBGC(DC_SPI_WRITE_DRE);
83 }
84
85 for (uint16_t i = 0; i < length; i++) {
86 SPI_SERCOM->SPI.DATA.bit.DATA = data[i];
87 while (!(SPI_SERCOM->SPI.INTFLAG.bit.TXC)) {
88 DBGC(DC_SPI_WRITE_TXC_1);
89 }
90 }
91
92 return SPI_STATUS_SUCCESS;
93}
94
95void spi_stop(void) {
96 if (currentSelectPin != NO_PIN) {
97 setPinOutput(currentSelectPin);
98 writePinHigh(currentSelectPin);
99 currentSelectPin = NO_PIN;
100 }
101}
102
103// Not implemented yet....
104
105spi_status_t spi_write(uint8_t data);
106
107spi_status_t spi_read(void);
108
109spi_status_t spi_receive(uint8_t *data, uint16_t length);
diff --git a/tmk_core/protocol/arm_atsam/spi_master.h b/tmk_core/protocol/arm_atsam/spi_master.h
new file mode 100644
index 000000000..26c55128b
--- /dev/null
+++ b/tmk_core/protocol/arm_atsam/spi_master.h
@@ -0,0 +1,48 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include <stdbool.h>
20
21typedef int16_t spi_status_t;
22
23#define SPI_STATUS_SUCCESS (0)
24#define SPI_STATUS_ERROR (-1)
25#define SPI_STATUS_TIMEOUT (-2)
26
27#define SPI_TIMEOUT_IMMEDIATE (0)
28#define SPI_TIMEOUT_INFINITE (0xFFFF)
29
30#ifdef __cplusplus
31extern "C" {
32#endif
33void spi_init(void);
34
35bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor);
36
37spi_status_t spi_write(uint8_t data);
38
39spi_status_t spi_read(void);
40
41spi_status_t spi_transmit(const uint8_t *data, uint16_t length);
42
43spi_status_t spi_receive(uint8_t *data, uint16_t length);
44
45void spi_stop(void);
46#ifdef __cplusplus
47}
48#endif