summaryrefslogtreecommitdiff
path: root/firmware/drivers/spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/spi.c')
-rw-r--r--firmware/drivers/spi.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/firmware/drivers/spi.c b/firmware/drivers/spi.c
new file mode 100644
index 0000000..6209a03
--- /dev/null
+++ b/firmware/drivers/spi.c
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/***************************************************************************
+ * spi.c
+ *
+ * Sun Jun 9 14:02:49 CEST 2013
+ * Copyright 2013 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of Pedal2Metal.
+ *
+ * Pedal2Metal 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.
+ *
+ * Pedal2Metal 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 Pedal2Metal; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include "spi.h"
+
+#include <LPC17xx.h>
+
+
+#define USE_CMSIS
+
+#ifdef USE_CMSIS
+#include <lpc17xx_spi.h>
+#include <lpc17xx_pinsel.h>
+
+LPC_SPI_TypeDef *spi;
+#else/*USE_CMSIS*/
+
+// SPI power bit in PCONP register (see table 46)
+#define PCSPI_BIT 8
+
+// SPI clock bits (16 and 17) in PCLKSEL0 register (see table 40)
+#define PCLK_SPI_BIT 16
+
+// Clock speed devisors. Values for PCLKSEL0 register (See table 42)
+typedef enum {
+ CCLK_4 = 0x0, // 00: PCLK_peripheral = CCLK/4
+ CCLK_1 = 0x1, // 01: PCLK_peripheral = CCLK
+ CCLK_2 = 0x2, // 10: PCLK_peripheral = CCLK/2
+ CCLK_8 = 0x3, // 11: PCLK_peripheral = CCLK/8
+} ClockDevisor;
+
+// PINSEL0 function value for SCK0 function (see table 79)
+#define SCK0_FUNCTION 0x2
+#define SCK0_BIT 30
+
+// PINSEL1 function value for SSEL0 function (see table 80)
+#define SSEL0_FUNCTION 0x2
+#define SSEL0_BIT 0
+
+// PINSEL1 function value for MISO0 function (see table 80)
+#define MISO0_FUNCTION 0x2
+#define MISO0_BIT 2
+
+// PINSEL1 function value for MOSI0 function (see table 80)
+#define MOSI0_FUNCTION 0x2
+#define MOSI0_BIT 4
+
+#endif/*USE_CMSIS*/
+
+/**
+ * Initialisation as described in "17.1 Basic Configuration"
+ */
+void spi_init()
+{
+#ifdef USE_CMSIS
+ SPI_CFG_Type spi_cfg;
+
+ // 16 bit data transfers
+ spi_cfg.Databit = SPI_DATABIT_8;
+
+ // WM8523 samples bits on rising edges. Mode 0 or 3 will work
+ // http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase
+
+ // Mode 0
+ spi_cfg.CPHA = SPI_CPHA_FIRST; // 0
+ spi_cfg.CPOL = SPI_CPOL_HI; // 0
+ /*
+ // Mode 3
+ spi_cfg.CPHA = SPI_CPHA_SECOND; // 1
+ spi_cfg.CPOL = SPI_CPOL_LO; // 1
+ */
+
+ // We are the master
+ spi_cfg.Mode = SPI_MASTER_MODE;
+
+ // We run on a little endian mcu.
+ spi_cfg.DataOrder = SPI_DATA_MSB_FIRST;
+
+ // Se WM8523 manual page 14. Min clock pulse width 40ns ~= 25MHz
+ spi_cfg.ClockRate = 1000;//1000000000; // 1MHz
+
+ spi = LPC_SPI;
+ SPI_Init(spi, &spi_cfg);
+
+ NVIC_EnableIRQ(SPI_IRQn);
+
+#else/*USE_CMSIS*/
+ // Power up the SPI bus.
+ // Remark: On reset, the SPI is enabled (PCSPI = 1).
+ LPC_SC->PCONP |= (1 << PCSPI_BIT); // set bit 8 in PCONP (PCSPI bit)
+
+ // Set clock speed (indirectly as clock devisor)
+ ClockDevisor clk = CCLK_8;
+ LPC_SC->PCLKSEL0 &= ~(0x3 << PCLK_SPI_BIT);
+ LPC_SC->PCLKSEL0 |= ((clk & 0x3) << PCLK_SPI_BIT); // set bit 16 and 17 in PCLKSEL0
+
+ // Make pin selection for SPI.
+ // SCK0: P1.20
+ // SSEL0: P1.21
+ // MOSI0: P1.23
+ // MISO0: P1.24
+ LPC_PINCON->PINSEL0 &= ~(0x3 << SCK0_BIT);
+ LPC_PINCON->PINSEL0 |= (SCK0_FUNCTION << SCK0_BIT);
+
+ LPC_PINCON->PINSEL1 &= ~(0x3 << SSEL0_BIT);
+ LPC_PINCON->PINSEL1 |= (SSEL0_FUNCTION << SSEL0_BIT);
+
+ LPC_PINCON->PINSEL1 &= ~(0x3 << MISO0_BIT);
+ LPC_PINCON->PINSEL1 |= (MISO0_FUNCTION << MISO0_BIT);
+
+ LPC_PINCON->PINSEL1 &= ~(0x3 << MOSI0_BIT);
+ LPC_PINCON->PINSEL1 |= (MOSI0_FUNCTION << MOSI0_BIT);
+
+ // Enable SPI interrupt:
+ // Set SPI interrupt flag (see table 367)
+ LPC_SPI->SPINT = 1;
+
+ // Enable SPI interrupt in the NVIC (see table 50)
+ NVIC_EnableIRQ(SPI_IRQn);
+#endif/*USE_CMSIS*/
+
+ // Configure SSEL pin:
+ LPC_GPIO1->FIODIR |= 1 << 21; // Set 1.21 in output mode
+
+}
+
+void spi_deinit()
+{
+#ifdef USE_CMSIS
+#else/*USE_CMSIS*/
+ // Power down SPI
+ LPC_SC->PCONP &= ~(1 << PCSPI_BIT); // unset bit 8 in PCONP (PCSPI bit)
+#endif/*USE_CMSIS*/
+}
+
+// S0SPCR register (see table 361)
+#define BENA 0 // Enable more than 8 bit transactions
+#define CPHA 0 // CPHA (Clock Phase Control) \ _ Mode 0
+#define CPOL 0 // CPOL (Clock Polarity Control) /
+#define MSTR 1 // MSTR (Master Mode Select)
+#define LSB 1 // LSB vs MSB
+#define SPIE 1 // Enable interrupts
+#define BITS 0 // Bits to transfer, only use if BENA is set to 1
+void spi_configure()
+{
+#ifdef USE_CMSIS
+#else/*USE_CMSIS*/
+ // S0SPCR register (see table 361)
+ LPC_SPI->SPCR = (BENA << 2) | (CPHA << 3) | (CPOL << 4) | (MSTR << 5) |
+ (LSB << 6) | (SPIE << 7) | (BITS << 8);
+
+ LPC_SPI->SPCCR = 1;
+#endif/*USE_CMSIS*/
+}
+
+static volatile int spi_done = 0;
+static void spi_mark_as_done()
+{
+ spi_done = 1;
+}
+
+volatile int temp;
+void __delay(uint32_t del)
+{
+ uint32_t i;
+ for(i=0;i<del;i++)
+ temp = i;
+}
+
+void spi_read_write(uint8_t *tx_data, uint8_t *rx_data, size_t length)
+{
+#ifdef USE_CMSIS
+
+ SPI_DATA_SETUP_Type dst;
+ dst.tx_data = tx_data;
+ dst.rx_data = rx_data;
+ dst.length = length;
+ dst.counter = 0;
+ dst.status = 0;
+ dst.callback = spi_mark_as_done;
+
+ SPI_ClearIntPending(spi);
+ LPC_GPIO1->FIOPIN &= ~( 1 << 21 ); // make P1.21 low
+
+ spi_done = 0;
+ SPI_ReadWrite(spi, &dst, SPI_TRANSFER_POLLING);//SPI_TRANSFER_INTERRUPT);
+
+ cli_write(">");
+ while (!(SPI_GetStatus(spi) & SPI_SPSR_SPIF)) { }
+ //while(!spi_done) {}
+ cli_write("<");
+
+ LPC_GPIO1->FIOPIN |= 1 << 21; // make P1.29 high
+
+#else/*USE_CMSIS*/
+
+ int i = 0;
+ cli_write(">>");
+
+ LPC_GPIO1->FIOPIN &= ~( 1 << 21 ); // make P1.21 low
+
+ for(; i < length; i++) {
+ /*
+ cli_write("w");
+ LPC_GPIO1->FIOPIN &= ~( 1 << 21 ); // make P1.21 low
+ __delay(1 << 23);
+ */
+
+ //LPC_SPI->SPINT = 1; // Clear interrupt (see section 17.7.7)
+ //if(LPC_SPI->SPSR) {} // Make sure we clear the status register
+
+ LPC_SPI->SPDR = tx_data[i];
+
+ //cli_write("[%d]", tx_data[i]);
+
+ //if(LPC_SPI->SPSR) {} // Make sure we clear the status register
+
+ // Check SPIF bit (see table 362)
+ //while(((LPC_SPI->SPSR << 7) & 0x1) != 1) {}
+ //while(LPC_SPI->SPSR == 0) {} // Wait for status register
+ while(LPC_SPI->SPINT == 0) {} // Wait for interrupt
+ LPC_SPI->SPINT = 1;
+ //__delay(1 << 23);
+
+ // cli_write("{%d}", LPC_SPI->SPSR);
+
+ rx_data[i] = LPC_SPI->SPDR;
+ //cli_write("[%d]", rx_data[i]);
+
+ /*
+ cli_write("r");
+ LPC_GPIO1->FIOPIN |= 1 << 21; // make P1.29 high
+ __delay(1 << 23);
+ */
+ }
+
+ LPC_GPIO1->FIOPIN |= 1 << 21; // make P1.29 high
+
+ cli_write("<<");
+#endif/*USE_CMSIS*/
+}