diff options
Diffstat (limited to 'firmware/drivers/spi.c')
-rw-r--r-- | firmware/drivers/spi.c | 264 |
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*/ +} |