diff options
Diffstat (limited to 'tmk_core')
-rw-r--r-- | tmk_core/common/arm_atsam/gpio.h | 8 | ||||
-rw-r--r-- | tmk_core/protocol/arm_atsam.mk | 3 | ||||
-rw-r--r-- | tmk_core/protocol/arm_atsam/arm_atsam_protocol.h | 2 | ||||
-rw-r--r-- | tmk_core/protocol/arm_atsam/shift_register.c | 118 | ||||
-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.c | 92 | ||||
-rw-r--r-- | tmk_core/protocol/arm_atsam/spi_master.c | 109 | ||||
-rw-r--r-- | tmk_core/protocol/arm_atsam/spi_master.h | 48 |
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 c2d5a30889..915ed0ef4f 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; PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \ } while (0) -#define writePin(pin, level) ((level) ? (writePinHigh(pin)) : (writePinLow(pin))) +#define writePin(pin, level) \ + do { \ + if (level) \ + PORT->Group[SAMD_PORT(pin)].OUTSET.reg = SAMD_PIN_MASK(pin); \ + else \ + PORT->Group[SAMD_PORT(pin)].OUTCLR.reg = SAMD_PIN_MASK(pin); \ + } while (0) #define readPin(pin) ((PORT->Group[SAMD_PORT(pin)].IN.reg & SAMD_PIN_MASK(pin)) != 0) diff --git a/tmk_core/protocol/arm_atsam.mk b/tmk_core/protocol/arm_atsam.mk index 5bb45d658e..e3b5505966 100644 --- a/tmk_core/protocol/arm_atsam.mk +++ b/tmk_core/protocol/arm_atsam.mk @@ -9,7 +9,8 @@ ifeq ($(RGB_MATRIX_DRIVER),custom) SRC += $(ARM_ATSAM_DIR)/md_rgb_matrix.c endif SRC += $(ARM_ATSAM_DIR)/main_arm_atsam.c -SRC += $(ARM_ATSAM_DIR)/spi.c +SRC += $(ARM_ATSAM_DIR)/shift_register.c +SRC += $(ARM_ATSAM_DIR)/spi_master.c SRC += $(ARM_ATSAM_DIR)/startup.c SRC += $(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 d126c66e7d..c3eab39fb1 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/>. #include "wait.h" #include "adc.h" #include "i2c_master.h" -#include "spi.h" +#include "shift_register.h" #include "./usb/usb2422.h" 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 0000000000..8d63af1b59 --- /dev/null +++ b/tmk_core/protocol/arm_atsam/shift_register.c @@ -0,0 +1,118 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "arm_atsam_protocol.h" + +#include "spi_master.h" +#include "wait.h" +#include "gpio.h" + +// #define SR_USE_BITBANG + +// Bodge for when spi_master is not available +#ifdef SR_USE_BITBANG +# define CLOCK_DELAY 10 + +void shift_init_impl(void) { + setPinOutput(SR_EXP_RCLK_PIN); + setPinOutput(SPI_DATAOUT_PIN); + setPinOutput(SPI_SCLK_PIN); +} + +void shift_out_impl(const uint8_t *data, uint16_t length) { + writePinLow(SR_EXP_RCLK_PIN); + for (uint16_t i = 0; i < length; i++) { + uint8_t val = data[i]; + + // shift out lsb first + for (uint8_t bit = 0; bit < 8; bit++) { + writePin(SPI_DATAOUT_PIN, !!(val & (1 << bit))); + writePin(SPI_SCLK_PIN, true); + wait_us(CLOCK_DELAY); + + writePin(SPI_SCLK_PIN, false); + wait_us(CLOCK_DELAY); + } + } + writePinHigh(SR_EXP_RCLK_PIN); + return SPI_STATUS_SUCCESS; +} + +#else + +void shift_init_impl(void) { spi_init(); } + +void shift_out_impl(const uint8_t *data, uint16_t length) { + spi_start(SR_EXP_RCLK_PIN, true, 0, 0); + + spi_transmit(data, length); + + spi_stop(); +} +#endif + +// *************************************************************** + +void shift_out(const uint8_t *data, uint16_t length) { shift_out_impl(data, length); } + +void shift_enable(void) { + setPinOutput(SR_EXP_OE_PIN); + writePinLow(SR_EXP_OE_PIN); +} + +void shift_disable(void) { + setPinOutput(SR_EXP_OE_PIN); + writePinHigh(SR_EXP_OE_PIN); +} + +void shift_init(void) { + shift_disable(); + shift_init_impl(); +} + +// *************************************************************** + +sr_exp_t sr_exp_data; + +void SR_EXP_WriteData(void) { + uint8_t data[2] = { + sr_exp_data.reg & 0xFF, // Shift in bits 7-0 + (sr_exp_data.reg >> 8) & 0xFF, // Shift in bits 15-8 + }; + shift_out(data, 2); +} + +void SR_EXP_Init(void) { + shift_init(); + + sr_exp_data.reg = 0; + sr_exp_data.bit.HUB_CONNECT = 0; + sr_exp_data.bit.HUB_RESET_N = 0; + sr_exp_data.bit.S_UP = 0; + sr_exp_data.bit.E_UP_N = 1; + sr_exp_data.bit.S_DN1 = 1; + sr_exp_data.bit.E_DN1_N = 1; + sr_exp_data.bit.E_VBUS_1 = 0; + sr_exp_data.bit.E_VBUS_2 = 0; + sr_exp_data.bit.SRC_1 = 1; + sr_exp_data.bit.SRC_2 = 1; + sr_exp_data.bit.IRST = 1; + sr_exp_data.bit.SDB_N = 0; + SR_EXP_WriteData(); + + shift_enable(); +} diff --git a/tmk_core/protocol/arm_atsam/spi.h b/tmk_core/protocol/arm_atsam/shift_register.h index dcd45f31af..56a8c7f717 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 along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef _SPI_H_ -#define _SPI_H_ +#pragma once -/* Macros for Shift Register control */ -#define SR_EXP_RCLK_LO PORT->Group[SR_EXP_RCLK_PORT].OUTCLR.reg = (1 << SR_EXP_RCLK_PIN) -#define SR_EXP_RCLK_HI PORT->Group[SR_EXP_RCLK_PORT].OUTSET.reg = (1 << SR_EXP_RCLK_PIN) -#define SR_EXP_OE_N_ENA PORT->Group[SR_EXP_OE_N_PORT].OUTCLR.reg = (1 << SR_EXP_OE_N_PIN) -#define SR_EXP_OE_N_DIS PORT->Group[SR_EXP_OE_N_PORT].OUTSET.reg = (1 << SR_EXP_OE_N_PIN) - -/* Determine bits to set for mux selection */ -#if SR_EXP_DATAOUT_PIN % 2 == 0 -# define SR_EXP_DATAOUT_MUX_SEL PMUXE -#else -# define SR_EXP_DATAOUT_MUX_SEL PMUXO -#endif - -/* Determine bits to set for mux selection */ -#if SR_EXP_SCLK_PIN % 2 == 0 -# define SR_EXP_SCLK_MUX_SEL PMUXE -#else -# define SR_EXP_SCLK_MUX_SEL PMUXO -#endif +#include <stdint.h> /* Data structure to define Shift Register output expander hardware */ /* This structure gets shifted into registers LSB first */ @@ -66,5 +47,3 @@ extern sr_exp_t sr_exp_data; void SR_EXP_WriteData(void); void SR_EXP_Init(void); - -#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 3b118bc1f1..0000000000 --- a/tmk_core/protocol/arm_atsam/spi.c +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright 2018 Massdrop Inc. - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "arm_atsam_protocol.h" - -sr_exp_t sr_exp_data; - -void SR_EXP_WriteData(void) { - SR_EXP_RCLK_LO; - - while (!(SR_EXP_SERCOM->SPI.INTFLAG.bit.DRE)) { - DBGC(DC_SPI_WRITE_DRE); - } - - SR_EXP_SERCOM->SPI.DATA.bit.DATA = sr_exp_data.reg & 0xFF; // Shift in bits 7-0 - while (!(SR_EXP_SERCOM->SPI.INTFLAG.bit.TXC)) { - DBGC(DC_SPI_WRITE_TXC_1); - } - - SR_EXP_SERCOM->SPI.DATA.bit.DATA = (sr_exp_data.reg >> 8) & 0xFF; // Shift in bits 15-8 - while (!(SR_EXP_SERCOM->SPI.INTFLAG.bit.TXC)) { - DBGC(DC_SPI_WRITE_TXC_2); - } - - SR_EXP_RCLK_HI; -} - -void SR_EXP_Init(void) { - DBGC(DC_SPI_INIT_BEGIN); - - CLK_set_spi_freq(CHAN_SERCOM_SPI, FREQ_SPI_DEFAULT); - - // Set up MCU Shift Register pins - PORT->Group[SR_EXP_RCLK_PORT].DIRSET.reg = (1 << SR_EXP_RCLK_PIN); - PORT->Group[SR_EXP_OE_N_PORT].DIRSET.reg = (1 << SR_EXP_OE_N_PIN); - - // Set up MCU SPI pins - 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 - 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 - PORT->Group[SR_EXP_DATAOUT_PORT].PINCFG[SR_EXP_DATAOUT_PIN].bit.PMUXEN = 1; // MUX Enable - PORT->Group[SR_EXP_SCLK_PORT].PINCFG[SR_EXP_SCLK_PIN].bit.PMUXEN = 1; // MUX Enable - - // Initialize Shift Register - SR_EXP_OE_N_DIS; - SR_EXP_RCLK_HI; - - SR_EXP_SERCOM->SPI.CTRLA.bit.DORD = 1; // Data Order - LSB is transferred first - SR_EXP_SERCOM->SPI.CTRLA.bit.CPOL = 1; // Clock Polarity - SCK high when idle. Leading edge of cycle is falling. Trailing rising. - SR_EXP_SERCOM->SPI.CTRLA.bit.CPHA = 1; // Clock Phase - Leading Edge Falling, change, Trailing Edge - Rising, sample - 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.) - SR_EXP_SERCOM->SPI.CTRLA.bit.DOPO = 0; // Data Output PAD[0], Serial Clock PAD[1] - SR_EXP_SERCOM->SPI.CTRLA.bit.MODE = 3; // Operating Mode - Master operation - - SR_EXP_SERCOM->SPI.CTRLA.bit.ENABLE = 1; // Enable - Peripheral is enabled or being enabled - while (SR_EXP_SERCOM->SPI.SYNCBUSY.bit.ENABLE) { - DBGC(DC_SPI_SYNC_ENABLING); - } - - sr_exp_data.reg = 0; - sr_exp_data.bit.HUB_CONNECT = 0; - sr_exp_data.bit.HUB_RESET_N = 0; - sr_exp_data.bit.S_UP = 0; - sr_exp_data.bit.E_UP_N = 1; - sr_exp_data.bit.S_DN1 = 1; - sr_exp_data.bit.E_DN1_N = 1; - sr_exp_data.bit.E_VBUS_1 = 0; - sr_exp_data.bit.E_VBUS_2 = 0; - sr_exp_data.bit.SRC_1 = 1; - sr_exp_data.bit.SRC_2 = 1; - sr_exp_data.bit.IRST = 1; - sr_exp_data.bit.SDB_N = 0; - SR_EXP_WriteData(); - - // Enable Shift Register output - SR_EXP_OE_N_ENA; - - DBGC(DC_SPI_INIT_COMPLETE); -} 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 0000000000..9781d45b1e --- /dev/null +++ b/tmk_core/protocol/arm_atsam/spi_master.c @@ -0,0 +1,109 @@ +/* +Copyright 2018 Massdrop Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "arm_atsam_protocol.h" +#include "spi_master.h" +#include "gpio.h" + +/* Determine bits to set for mux selection */ +#if SPI_DATAOUT_PIN % 2 == 0 +# define SPI_DATAOUT_MUX_SEL PMUXE +#else +# define SPI_DATAOUT_MUX_SEL PMUXO +#endif + +/* Determine bits to set for mux selection */ +#if SPI_SCLK_PIN % 2 == 0 +# define SPI_SCLK_MUX_SEL PMUXE +#else +# define SPI_SCLK_MUX_SEL PMUXO +#endif + +static pin_t currentSelectPin = NO_PIN; + +__attribute__((weak)) void spi_init(void) { + static bool is_initialised = false; + if (!is_initialised) { + is_initialised = true; + + DBGC(DC_SPI_INIT_BEGIN); + + CLK_set_spi_freq(CHAN_SERCOM_SPI, FREQ_SPI_DEFAULT); + + // Set up MCU SPI pins + 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 + 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 + PORT->Group[SAMD_PORT(SPI_DATAOUT_PIN)].PINCFG[SAMD_PIN(SPI_DATAOUT_PIN)].bit.PMUXEN = 1; // MUX Enable + PORT->Group[SAMD_PORT(SPI_SCLK_PIN)].PINCFG[SAMD_PIN(SPI_SCLK_PIN)].bit.PMUXEN = 1; // MUX Enable + + DBGC(DC_SPI_INIT_COMPLETE); + } +} + +bool spi_start(pin_t csPin, bool lsbFirst, uint8_t mode, uint16_t divisor) { + if (currentSelectPin != NO_PIN || csPin == NO_PIN) { + return false; + } + + currentSelectPin = csPin; + setPinOutput(currentSelectPin); + writePinLow(currentSelectPin); + + SPI_SERCOM->SPI.CTRLA.bit.DORD = lsbFirst; // Data Order - LSB is transferred first + SPI_SERCOM->SPI.CTRLA.bit.CPOL = 1; // Clock Polarity - SCK high when idle. Leading edge of cycle is falling. Trailing rising. + SPI_SERCOM->SPI.CTRLA.bit.CPHA = 1; // Clock Phase - Leading Edge Falling, change, Trailing Edge - Rising, sample + 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.) + SPI_SERCOM->SPI.CTRLA.bit.DOPO = 0; // Data Output PAD[0], Serial Clock PAD[1] + SPI_SERCOM->SPI.CTRLA.bit.MODE = 3; // Operating Mode - Master operation + + SPI_SERCOM->SPI.CTRLA.bit.ENABLE = 1; // Enable - Peripheral is enabled or being enabled + while (SPI_SERCOM->SPI.SYNCBUSY.bit.ENABLE) { + DBGC(DC_SPI_SYNC_ENABLING); + } + return true; +} + +spi_status_t spi_transmit(const uint8_t *data, uint16_t length) { + while (!(SPI_SERCOM->SPI.INTFLAG.bit.DRE)) { + DBGC(DC_SPI_WRITE_DRE); + } + + for (uint16_t i = 0; i < length; i++) { + SPI_SERCOM->SPI.DATA.bit.DATA = data[i]; + while (!(SPI_SERCOM->SPI.INTFLAG.bit.TXC)) { + DBGC(DC_SPI_WRITE_TXC_1); + } + } + + return SPI_STATUS_SUCCESS; +} + +void spi_stop(void) { + if (currentSelectPin != NO_PIN) { + setPinOutput(currentSelectPin); + writePinHigh(currentSelectPin); + currentSelectPin = NO_PIN; + } +} + +// Not implemented yet.... + +spi_status_t spi_write(uint8_t data); + +spi_status_t spi_read(void); + +spi_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 0000000000..26c55128be --- /dev/null +++ b/tmk_core/protocol/arm_atsam/spi_master.h @@ -0,0 +1,48 @@ +/* Copyright 2021 QMK + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <stdbool.h> + +typedef int16_t spi_status_t; + +#define SPI_STATUS_SUCCESS (0) +#define SPI_STATUS_ERROR (-1) +#define SPI_STATUS_TIMEOUT (-2) + +#define SPI_TIMEOUT_IMMEDIATE (0) +#define SPI_TIMEOUT_INFINITE (0xFFFF) + +#ifdef __cplusplus +extern "C" { +#endif +void spi_init(void); + +bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor); + +spi_status_t spi_write(uint8_t data); + +spi_status_t spi_read(void); + +spi_status_t spi_transmit(const uint8_t *data, uint16_t length); + +spi_status_t spi_receive(uint8_t *data, uint16_t length); + +void spi_stop(void); +#ifdef __cplusplus +} +#endif |