From bcd050f33b0ecb28e4ee3af80b02714024a2c6a1 Mon Sep 17 00:00:00 2001
From: Christopher Pepper <p3p@p3psoft.co.uk>
Date: Tue, 25 Jul 2017 17:27:53 +0100
Subject: [PATCH] Initial HAL SPI API

---
 Marlin/src/HAL/HAL_LPC1768/HAL_spi.cpp  |  25 +-
 Marlin/src/HAL/HAL_LPC1768/main.cpp     |   5 +-
 Marlin/src/HAL/HAL_LPC1768/spi_impl.cpp | 402 ++++++++++++++++++++++++
 Marlin/src/HAL/HAL_LPC1768/spi_pins.h   |   7 +-
 Marlin/src/HAL/spi_api.h                |  46 +++
 5 files changed, 465 insertions(+), 20 deletions(-)
 create mode 100644 Marlin/src/HAL/HAL_LPC1768/spi_impl.cpp
 create mode 100644 Marlin/src/HAL/spi_api.h

diff --git a/Marlin/src/HAL/HAL_LPC1768/HAL_spi.cpp b/Marlin/src/HAL/HAL_LPC1768/HAL_spi.cpp
index 55e5d65938..e91f963442 100644
--- a/Marlin/src/HAL/HAL_LPC1768/HAL_spi.cpp
+++ b/Marlin/src/HAL/HAL_LPC1768/HAL_spi.cpp
@@ -37,12 +37,12 @@
 // --------------------------------------------------------------------------
 
 #include "../../../MarlinConfig.h"
+#include "../spi_api.h"
 
 // --------------------------------------------------------------------------
 // Public Variables
 // --------------------------------------------------------------------------
 
-
 // --------------------------------------------------------------------------
 // Public functions
 // --------------------------------------------------------------------------
@@ -138,39 +138,32 @@
   }
 #else
   void spiBegin() {
+    HAL::SPI::initialise(SD_SPI_CHANNEL);
   }
 
   void spiInit(uint8_t spiRate) {
+    uint32_t freq =  8000000 / (1u << spiRate);
+    HAL::SPI::set_frequency(SD_SPI_CHANNEL, freq);
   }
 
   void spiSend(byte b) {
-  }
-
-  void spiSend(const uint8_t* buf, size_t n) {
-  }
-
-  void spiSend(uint32_t chan, byte b) {
-  }
-
-  void spiSend(uint32_t chan, const uint8_t* buf, size_t n) {
-
+    HAL::SPI::write(SD_SPI_CHANNEL, b);
   }
 
   // Read single byte from SPI
   uint8_t spiRec() {
-    return 0;
-  }
-
-  uint8_t spiRec(uint32_t chan) {
-    return 0;
+    return HAL::SPI::read(SD_SPI_CHANNEL);
   }
 
   // Read from SPI into buffer
   void spiRead(uint8_t*buf, uint16_t nbyte) {
+    HAL::SPI::read(SD_SPI_CHANNEL, buf, nbyte);
   }
 
   // Write from buffer to SPI
   void spiSendBlock(uint8_t token, const uint8_t* buf) {
+    HAL::SPI::write(SD_SPI_CHANNEL, token);
+    HAL::SPI::write(SD_SPI_CHANNEL, buf, 512);
   }
 #endif // ENABLED(SOFTWARE_SPI)
 
diff --git a/Marlin/src/HAL/HAL_LPC1768/main.cpp b/Marlin/src/HAL/HAL_LPC1768/main.cpp
index 5b1f8dcfd3..6427340cb4 100644
--- a/Marlin/src/HAL/HAL_LPC1768/main.cpp
+++ b/Marlin/src/HAL/HAL_LPC1768/main.cpp
@@ -43,7 +43,6 @@ static __INLINE uint32_t SysTick_Config(uint32_t ticks) {
   SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
   return (0); /* Function successful */
 }
-
 extern "C" {
 extern void disk_timerproc(void);
 volatile uint32_t _millis;
@@ -70,6 +69,7 @@ extern "C" void SystemPostInit() {
 extern uint32_t MSC_SD_Init(uint8_t pdrv);
 extern HalSerial usb_serial;
 int main(void) {
+  debug_frmwrk_init();
 
   (void)MSC_SD_Init(0);
   USB_Init();                               // USB Initialization
@@ -81,8 +81,7 @@ int main(void) {
     TOGGLE(13);     // Flash fast while USB initialisation completes
   }
 
-  debug_frmwrk_init();
-  usb_serial.printf("\n\nRe-ARM (LPC1768 @ %dMhz) UART0 Initialised\n", SystemCoreClock / 1000000);
+  usb_serial.printf("\n\nRe-ARM (LPC1768 @ %dMhz) USB Initialised\n", SystemCoreClock / 1000000);
 
   HAL_timer_init();
 
diff --git a/Marlin/src/HAL/HAL_LPC1768/spi_impl.cpp b/Marlin/src/HAL/HAL_LPC1768/spi_impl.cpp
new file mode 100644
index 0000000000..5c21ed788b
--- /dev/null
+++ b/Marlin/src/HAL/HAL_LPC1768/spi_impl.cpp
@@ -0,0 +1,402 @@
+#ifdef TARGET_LPC1768
+
+#include "../spi_api.h"
+
+#include <lpc17xx_ssp.h>
+#include <lpc17xx_pinsel.h>
+#include <lpc17xx_gpio.h>
+#include "lpc17xx_clkpwr.h"
+
+extern "C" void SSP0_IRQHandler(void);
+extern "C" void SSP1_IRQHandler(void);
+
+namespace HAL {
+namespace SPI {
+
+enum class SignalPolarity : uint8_t {
+  ACTIVE_LOW = 0,
+  ACTIVE_HIGH
+};
+
+/* Hardware channels :
+ * 0: clk(0_7),  mosi(0_9),  miso(0_8),  SSP1
+ * 1: clk(0_15), mosi(0_28), miso(0_17), SSP0
+
+ * Logical channels:
+ * 0: hwchannel: 1, ssel(0_6)
+ * 1: hwchannel: 0, ssel(0_16)
+ * 2: hwchannel: 0, ssel(1_23)
+ */
+
+/*
+ * Defines the Hardware setup for an SPI channel
+ * The pins and (if applicable) the Hardware Peripheral
+ *
+ */
+struct LogicalChannel; // who doesn't like circular dependencies
+
+struct HardwareChannel {
+  LPC_SSP_TypeDef *peripheral;
+  IRQn_Type IRQn;
+  uint8_t clk_port;
+  uint8_t clk_pin;
+  uint8_t mosi_port;
+  uint8_t mosi_pin;
+  uint8_t miso_port;
+  uint8_t miso_pin;
+  SSP_DATA_SETUP_Type xfer_config;
+  volatile FlagStatus xfer_complete;
+  bool initialised;
+  volatile bool in_use;
+  LogicalChannel* active_channel;
+} hardware_channels[2] = {
+    {LPC_SSP0, SSP0_IRQn, 0, 15, 0, 18, 0, 17, { nullptr, 0, nullptr, 0, 0, SSP_STAT_DONE }, RESET, false, false, nullptr},
+    {LPC_SSP1, SSP1_IRQn, 0, 7,  0, 9,  0, 8 , { nullptr, 0, nullptr, 0, 0, SSP_STAT_DONE }, RESET, false, false, nullptr}
+};
+
+/*
+ * Define all available logical SPI ports
+ */
+struct LogicalChannel {
+  HardwareChannel& hw_channel;
+  uint8_t ssel_port;
+  uint8_t ssel_pin;
+  SignalPolarity ssel_polarity;
+  bool ssel_override;
+  SSP_CFG_Type config;
+  uint32_t CR0;
+  uint32_t CPSR;
+} logical_channels[3] = {
+    { hardware_channels[1], 0, 6,  SignalPolarity::ACTIVE_LOW, false, { SSP_DATABIT_8, SSP_CPHA_FIRST, SSP_CPOL_HI, SSP_MASTER_MODE, SSP_FRAME_SPI, 1000000 }, 0, 0 },
+    { hardware_channels[0], 0, 16, SignalPolarity::ACTIVE_HIGH, true, { SSP_DATABIT_8, SSP_CPHA_FIRST, SSP_CPOL_HI, SSP_MASTER_MODE, SSP_FRAME_SPI, 1000000 }, 0, 0 },
+    { hardware_channels[0], 1, 23, SignalPolarity::ACTIVE_LOW, true, { SSP_DATABIT_8, SSP_CPHA_FIRST, SSP_CPOL_HI, SSP_MASTER_MODE, SSP_FRAME_SPI, 1000000 }, 0, 0 }
+};
+
+//Internal functions
+extern "C" void ssp_irq_handler(uint8_t hw_channel);
+LogicalChannel* get_logical_channel(uint8_t channel);
+bool set_ssel(LogicalChannel* logical_channel);
+void clear_ssel(LogicalChannel* logical_channel);
+void restore_frequency(LogicalChannel* logical_channel);
+
+LogicalChannel* get_logical_channel(uint8_t channel) {
+  if(channel > sizeof(logical_channels) - 1) {
+    return nullptr;
+  }
+  return &logical_channels[channel];
+}
+
+bool set_ssel(LogicalChannel* logical_channel) {
+  if(logical_channel->hw_channel.in_use == true) {
+    return false;
+  }
+
+  if(logical_channel->ssel_polarity == SignalPolarity::ACTIVE_HIGH) {
+    GPIO_SetValue(logical_channel->ssel_port, (1 << logical_channel->ssel_pin));
+  } else {
+    GPIO_ClearValue(logical_channel->ssel_port, (1 << logical_channel->ssel_pin));
+  }
+  logical_channel->hw_channel.in_use = true;
+
+  return true;
+}
+
+void clear_ssel(LogicalChannel* logical_channel) {
+  if(logical_channel->ssel_polarity == SignalPolarity::ACTIVE_HIGH) {
+    GPIO_ClearValue(logical_channel->ssel_port, (1 << logical_channel->ssel_pin));
+  } else {
+    GPIO_SetValue(logical_channel->ssel_port, (1 << logical_channel->ssel_pin));
+  }
+  logical_channel->hw_channel.in_use = false;
+}
+
+void restore_frequency(LogicalChannel* logical_channel) {
+  logical_channel->hw_channel.peripheral->CR0 = logical_channel->CR0;
+  logical_channel->hw_channel.peripheral->CPSR = logical_channel->CPSR;
+}
+
+/*
+ * SPI API Implementation
+ */
+bool initialise(uint8_t channel) {
+  LogicalChannel* logical_channel = get_logical_channel(channel);
+  if(logical_channel == nullptr) return false;
+  HardwareChannel& hw_channel = logical_channel->hw_channel;
+
+  PINSEL_CFG_Type pin_cfg;
+  pin_cfg.OpenDrain = PINSEL_PINMODE_NORMAL;
+  pin_cfg.Pinmode = PINSEL_PINMODE_PULLUP;
+
+  if(hw_channel.initialised == false) {
+    pin_cfg.Funcnum = 2; //ssp (spi) function
+    pin_cfg.Portnum = hw_channel.clk_port;
+    pin_cfg.Pinnum = hw_channel.clk_pin;
+    PINSEL_ConfigPin(&pin_cfg); //clk
+
+    pin_cfg.Portnum = hw_channel.miso_port;
+    pin_cfg.Pinnum = hw_channel.miso_pin;
+    PINSEL_ConfigPin(&pin_cfg); //miso
+
+    pin_cfg.Portnum = hw_channel.mosi_port;
+    pin_cfg.Pinnum = hw_channel.mosi_pin;
+    PINSEL_ConfigPin(&pin_cfg); //mosi
+
+    SSP_Init(hw_channel.peripheral, &logical_channel->config);
+    logical_channel->CR0 = logical_channel->hw_channel.peripheral->CR0; // preserve for restore
+    logical_channel->CPSR = logical_channel->hw_channel.peripheral->CPSR; // preserve for restore
+    SSP_Cmd(hw_channel.peripheral, ENABLE);
+
+    hw_channel.initialised = true;
+    hw_channel.active_channel = logical_channel;
+
+    //NVIC_SetPriority(hw_channel.IRQn, NVIC_EncodePriority(0, 3, 0)); //Very Low priority
+    //NVIC_EnableIRQ(hw_channel.IRQn);
+  }
+
+  pin_cfg.Portnum = logical_channel->ssel_port;
+  pin_cfg.Pinnum = logical_channel->ssel_pin;
+  pin_cfg.Pinmode = logical_channel->ssel_polarity ==  SignalPolarity::ACTIVE_LOW ? PINSEL_PINMODE_PULLUP : PINSEL_PINMODE_PULLDOWN;
+  pin_cfg.Funcnum = 0; //gpio function
+  PINSEL_ConfigPin(&pin_cfg); //ssel
+
+  GPIO_SetDir(logical_channel->ssel_port, (1 << logical_channel->ssel_pin), 1);
+  GPIO_SetValue(logical_channel->ssel_port, (1 << logical_channel->ssel_pin));
+  return true;
+}
+
+bool enable_cs(uint8_t channel) {
+  LogicalChannel* logical_channel = get_logical_channel(channel);
+  if(logical_channel == nullptr) return false;
+  return set_ssel(logical_channel);
+}
+
+void disable_cs(uint8_t channel) {
+  LogicalChannel* logical_channel = get_logical_channel(channel);
+  if(logical_channel == nullptr) return;
+  if(logical_channel->hw_channel.in_use && !logical_channel->ssel_override) return; //automatic SSel wasn't overridden
+
+  clear_ssel(logical_channel);
+}
+
+void set_frequency(uint8_t channel, uint32_t frequency) {
+  LogicalChannel* logical_channel = get_logical_channel(channel);
+  if(logical_channel == nullptr) return;
+
+  SSP_Cmd(logical_channel->hw_channel.peripheral, DISABLE);
+  uint32_t prescale, cr0_div, cmp_clk, ssp_clk;
+
+  if (logical_channel->hw_channel.peripheral == LPC_SSP0){
+    ssp_clk = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_SSP0);
+  } else if (logical_channel->hw_channel.peripheral == LPC_SSP1) {
+    ssp_clk = CLKPWR_GetPCLK (CLKPWR_PCLKSEL_SSP1);
+  } else {
+    return;
+  }
+  //find the closest clock divider / prescaler
+  cr0_div = 0;
+  cmp_clk = 0xFFFFFFFF;
+  prescale = 2;
+  while (cmp_clk > frequency) {
+    cmp_clk = ssp_clk / ((cr0_div + 1) * prescale);
+    if (cmp_clk > frequency) {
+      cr0_div++;
+      if (cr0_div > 0xFF) {
+        cr0_div = 0;
+        prescale += 2;
+      }
+    }
+  }
+
+  logical_channel->hw_channel.peripheral->CR0 &= (~SSP_CR0_SCR(0xFF)) & SSP_CR0_BITMASK;
+  logical_channel->hw_channel.peripheral->CR0 |= (SSP_CR0_SCR(cr0_div)) & SSP_CR0_BITMASK;
+  logical_channel->CR0 = logical_channel->hw_channel.peripheral->CR0; // preserve for restore
+
+  logical_channel->hw_channel.peripheral->CPSR = prescale & SSP_CPSR_BITMASK;
+  logical_channel->CPSR = logical_channel->hw_channel.peripheral->CPSR; // preserve for restore
+
+  logical_channel->config.ClockRate = ssp_clk / ((cr0_div + 1) * prescale);
+
+  SSP_Cmd(logical_channel->hw_channel.peripheral, ENABLE);
+}
+
+void read(uint8_t channel, uint8_t *buffer, uint32_t length) {
+  transfer(channel, nullptr, buffer, length);
+}
+
+uint8_t read(uint8_t channel) {
+  uint8_t buffer;
+  transfer(channel, nullptr, &buffer, 1);
+  return buffer;
+}
+
+void write(uint8_t channel, const uint8_t *buffer, uint32_t length) {
+  transfer(channel, buffer, nullptr, length);
+}
+
+void write(uint8_t channel, uint8_t value) {
+  transfer(channel, &value, nullptr, 1);
+}
+
+void transfer(uint8_t channel, const uint8_t *buffer_write, uint8_t *buffer_read, uint32_t length) {
+  LogicalChannel* logical_channel = get_logical_channel(channel);
+  if(logical_channel == nullptr) return;
+
+  if((logical_channel->hw_channel.in_use && !logical_channel->ssel_override) || !logical_channel->hw_channel.initialised) return;
+  if(!logical_channel->ssel_override) {
+    if(!set_ssel(logical_channel)) return;
+  }
+
+  if(logical_channel != logical_channel->hw_channel.active_channel) {
+    restore_frequency(logical_channel);
+    logical_channel->hw_channel.active_channel = logical_channel;
+  }
+
+  logical_channel->hw_channel.xfer_config.tx_data = (void *)buffer_write;
+  logical_channel->hw_channel.xfer_config.rx_data = (void *)buffer_read;
+  logical_channel->hw_channel.xfer_config.length = length;
+
+  (void)SSP_ReadWrite(logical_channel->hw_channel.peripheral, &logical_channel->hw_channel.xfer_config, SSP_TRANSFER_POLLING); //SSP_TRANSFER_INTERRUPT
+
+  if(!logical_channel->ssel_override) {
+    clear_ssel(logical_channel->hw_channel.active_channel);
+  }
+}
+
+uint8_t transfer(uint8_t channel, uint8_t value) {
+  uint8_t buffer;
+  transfer(channel, &value, &buffer, 1);
+  return buffer;
+}
+
+/*
+ *  Interrupt Handlers
+ */
+extern "C" void ssp_irq_handler(uint8_t hw_channel) {
+
+  SSP_DATA_SETUP_Type *xf_setup;
+  uint32_t tmp;
+  uint8_t dataword;
+
+  // Disable all SSP interrupts
+  SSP_IntConfig(hardware_channels[hw_channel].peripheral, SSP_INTCFG_ROR | SSP_INTCFG_RT | SSP_INTCFG_RX | SSP_INTCFG_TX, DISABLE);
+
+  dataword = (SSP_GetDataSize(hardware_channels[hw_channel].peripheral) > 8) ? 1 : 0;
+
+  xf_setup = &hardware_channels[hw_channel].xfer_config;
+  // save status
+  tmp = SSP_GetRawIntStatusReg(hardware_channels[hw_channel].peripheral);
+  xf_setup->status = tmp;
+
+  // Check overrun error
+  if (tmp & SSP_RIS_ROR) {
+    // Clear interrupt
+    SSP_ClearIntPending(hardware_channels[hw_channel].peripheral, SSP_INTCLR_ROR);
+    // update status
+    xf_setup->status |= SSP_STAT_ERROR;
+    // Set Complete Flag
+    hardware_channels[hw_channel].xfer_complete = SET;
+    if(!hardware_channels[hw_channel].active_channel->ssel_override) clear_ssel(hardware_channels[hw_channel].active_channel);
+    return;
+  }
+
+  if ((xf_setup->tx_cnt != xf_setup->length) || (xf_setup->rx_cnt != xf_setup->length)) {
+    /* check if RX FIFO contains data */
+    while ((SSP_GetStatus(hardware_channels[hw_channel].peripheral, SSP_STAT_RXFIFO_NOTEMPTY)) && (xf_setup->rx_cnt != xf_setup->length)) {
+      // Read data from SSP data
+      tmp = SSP_ReceiveData(hardware_channels[hw_channel].peripheral);
+
+      // Store data to destination
+      if (xf_setup->rx_data != nullptr) {
+        if (dataword == 0) {
+          *(uint8_t *) ((uint32_t) xf_setup->rx_data + xf_setup->rx_cnt) = (uint8_t) tmp;
+        } else {
+          *(uint16_t *) ((uint32_t) xf_setup->rx_data + xf_setup->rx_cnt) = (uint16_t) tmp;
+        }
+      }
+      // Increase counter
+      if (dataword == 0) {
+        xf_setup->rx_cnt++;
+      } else {
+        xf_setup->rx_cnt += 2;
+      }
+    }
+
+    while ((SSP_GetStatus(hardware_channels[hw_channel].peripheral, SSP_STAT_TXFIFO_NOTFULL)) && (xf_setup->tx_cnt != xf_setup->length)) {
+      // Write data to buffer
+      if (xf_setup->tx_data == nullptr) {
+        if (dataword == 0) {
+          SSP_SendData(hardware_channels[hw_channel].peripheral, 0xFF);
+          xf_setup->tx_cnt++;
+        } else {
+          SSP_SendData(hardware_channels[hw_channel].peripheral, 0xFFFF);
+          xf_setup->tx_cnt += 2;
+        }
+      } else {
+        if (dataword == 0) {
+          SSP_SendData(hardware_channels[hw_channel].peripheral, (*(uint8_t *) ((uint32_t) xf_setup->tx_data + xf_setup->tx_cnt)));
+          xf_setup->tx_cnt++;
+        } else {
+          SSP_SendData(hardware_channels[hw_channel].peripheral, (*(uint16_t *) ((uint32_t) xf_setup->tx_data + xf_setup->tx_cnt)));
+          xf_setup->tx_cnt += 2;
+        }
+      }
+
+      // Check overrun error
+      if (SSP_GetRawIntStatus(hardware_channels[hw_channel].peripheral, SSP_INTSTAT_RAW_ROR)) {
+        // update status
+        xf_setup->status |= SSP_STAT_ERROR;
+        // Set Complete Flag
+        hardware_channels[hw_channel].xfer_complete = SET;
+        if(!hardware_channels[hw_channel].active_channel->ssel_override) clear_ssel(hardware_channels[hw_channel].active_channel);
+        return;
+      }
+
+      // Check for any data available in RX FIFO
+      while ((SSP_GetStatus(hardware_channels[hw_channel].peripheral, SSP_STAT_RXFIFO_NOTEMPTY)) && (xf_setup->rx_cnt != xf_setup->length)) {
+        // Read data from SSP data
+        tmp = SSP_ReceiveData(hardware_channels[hw_channel].peripheral);
+
+        // Store data to destination
+        if (xf_setup->rx_data != nullptr) {
+          if (dataword == 0) {
+            *(uint8_t *) ((uint32_t) xf_setup->rx_data + xf_setup->rx_cnt) = (uint8_t) tmp;
+          } else {
+            *(uint16_t *) ((uint32_t) xf_setup->rx_data + xf_setup->rx_cnt) = (uint16_t) tmp;
+          }
+        }
+        // Increase counter
+        if (dataword == 0) {
+          xf_setup->rx_cnt++;
+        } else {
+          xf_setup->rx_cnt += 2;
+        }
+      }
+    }
+  }
+
+  // If there more data to sent or receive
+  if ((xf_setup->rx_cnt != xf_setup->length) || (xf_setup->tx_cnt != xf_setup->length)) {
+    // Enable all interrupt
+    SSP_IntConfig(hardware_channels[hw_channel].peripheral, SSP_INTCFG_ROR | SSP_INTCFG_RT | SSP_INTCFG_RX | SSP_INTCFG_TX, ENABLE);
+  } else {
+    // Save status
+    xf_setup->status = SSP_STAT_DONE;
+    // Set Complete Flag
+    hardware_channels[hw_channel].xfer_complete = SET;
+    if(!hardware_channels[hw_channel].active_channel->ssel_override) clear_ssel(hardware_channels[hw_channel].active_channel);
+  }
+}
+
+}
+}
+
+extern "C" void SSP0_IRQHandler(void) {
+  HAL::SPI::ssp_irq_handler(0);
+}
+
+extern "C" void SSP1_IRQHandler(void) {
+  HAL::SPI::ssp_irq_handler(1);
+}
+
+
+#endif
diff --git a/Marlin/src/HAL/HAL_LPC1768/spi_pins.h b/Marlin/src/HAL/HAL_LPC1768/spi_pins.h
index f3997838a3..09c3d90079 100644
--- a/Marlin/src/HAL/HAL_LPC1768/spi_pins.h
+++ b/Marlin/src/HAL/HAL_LPC1768/spi_pins.h
@@ -21,7 +21,12 @@
 #ifndef SPI_PINS_LPC1768_H
 #define SPI_PINS_LPC1768_H
 
-#define SOFTWARE_SPI
+//new config options
+#define SD_SPI_CHANNEL (HAL::SPI::CHANNEL_2)
+#define LCD_SPI_FREQUENCY 4000000
+#define LCD_SPI_CHANNEL (HAL::SPI::CHANNEL_1)
+
+//#define SOFTWARE_SPI
 /** onboard SD card */
 //#define SCK_PIN           P0_7
 //#define MISO_PIN          P0_8
diff --git a/Marlin/src/HAL/spi_api.h b/Marlin/src/HAL/spi_api.h
new file mode 100644
index 0000000000..5de40733ab
--- /dev/null
+++ b/Marlin/src/HAL/spi_api.h
@@ -0,0 +1,46 @@
+#ifndef _SPI_API_H_
+#define _SPI_API_H_
+
+#include <stdint.h>
+#include "HAL_spi_pins.h"
+
+namespace HAL {
+namespace SPI {
+
+enum SPI_CHANNELS {
+  CHANNEL_0 = 0,
+  CHANNEL_1,
+  CHANNEL_2,
+  CHANNEL_3,
+  CHANNEL_4,
+  CHANNEL_5
+};
+
+/*
+ * Initialise the hardware layer (pins and peripheral)
+ */
+bool initialise(uint8_t channel);
+
+/*
+ * Allow override of automatic Chip Select
+ */
+bool enable_cs(uint8_t channel);
+void disable_cs(uint8_t channel);
+
+
+void set_frequency(uint8_t channel, uint32_t frequency);
+
+void read(uint8_t channel, uint8_t *buffer, uint32_t length);
+uint8_t read(uint8_t channel);
+
+void write(uint8_t channel, const uint8_t *buffer, uint32_t length);
+void write(uint8_t channel, uint8_t value);
+
+void transfer(uint8_t channel, const uint8_t *buffer_write, uint8_t *buffer_read, uint32_t length);
+uint8_t transfer(uint8_t channel, uint8_t value);
+
+}
+}
+
+
+#endif /* _SPI_API_H_ */