/* mbed Microcontroller Library * Copyright (c) 2015 ARM Limited * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "spi_api.h" #include "spi_def.h" #include "cmsis.h" #include "pinmap.h" #include "mbed_error.h" #include "mbed_wait_api.h" /* * Driver private data structure that should not be shared by multiple * instances of the driver (same driver for multiple instances of the IP) */ typedef struct { uint32_t size; /* size of an SPI frame in bits: can be 8 or 16 */ } private_spi_t; static const PinMap PinMap_SPI_SCLK[] = { {SHIELD_SPI_SCK , SPI_0, 0}, {ADC_SPI_SCK , SPI_1, 0}, {NC, NC, 0} }; static const PinMap PinMap_SPI_MOSI[] = { {SHIELD_SPI_MOSI, SPI_0, 0}, {ADC_SPI_MOSI, SPI_1, 0}, {NC, NC, 0} }; static const PinMap PinMap_SPI_MISO[] = { {SHIELD_SPI_MISO, SPI_0, 0}, {ADC_SPI_MISO, SPI_1, 0}, {NC, NC, 0} }; static const PinMap PinMap_SPI_SSEL[] = { {SHIELD_SPI_nCS, SPI_0, 0}, {ADC_SPI_nCS, SPI_1, 0}, {NC, NC, 0} }; /* * Retrieve the private data of the instance related to a given IP */ static private_spi_t* get_spi_private(spi_t *obj) { static private_spi_t data0, data1; /* * Select which instance to give using the base * address of registers */ switch ((intptr_t)obj->spi) { case SPI0_BASE: return &data0; case SPI1_BASE: return &data1; default: error("SPI driver private data structure not found for this registers base address"); return (void*)0; } } void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel) { // determine the SPI to use SPIName spi_mosi = (SPIName)pinmap_peripheral(mosi, PinMap_SPI_MOSI); SPIName spi_miso = (SPIName)pinmap_peripheral(miso, PinMap_SPI_MISO); SPIName spi_sclk = (SPIName)pinmap_peripheral(sclk, PinMap_SPI_SCLK); SPIName spi_ssel = (SPIName)pinmap_peripheral(ssel, PinMap_SPI_SSEL); SPIName spi_data = (SPIName)pinmap_merge(spi_mosi, spi_miso); SPIName spi_cntl = (SPIName)pinmap_merge(spi_sclk, spi_ssel); obj->spi = (SPI_TypeDef*)pinmap_merge(spi_data, spi_cntl); if ((int)obj->spi == NC) { error("SPI pinout mapping failed"); } /* Set default format and frequency */ if (ssel == NC) { spi_format(obj, 8, 0, 0); // 8 bits, mode SPI_MSB, master } else { spi_format(obj, 8, 0, 1); // 8 bits, mode SPI_LSB, slave } spi_frequency(obj, 1562500); /* Pin out the spi pins */ pinmap_pinout(mosi, PinMap_SPI_MOSI); pinmap_pinout(miso, PinMap_SPI_MISO); pinmap_pinout(sclk, PinMap_SPI_SCLK); if (ssel != NC) { pinmap_pinout(ssel, PinMap_SPI_SSEL); } /* * Set desired enabled IRQs: * MF: Mode Fail * TF: TX FIFO Full * TNF: TX FIFO Not Full * RNE: RX FIFO Not Empty */ uint32_t irqs = (IRQ_ENABLE_MFE | IRQ_ENABLE_TFE | IRQ_ENABLE_TNFE | IRQ_ENABLE_RNEE); /* * Enable: * - Master mode * - Manual start mode * - Manual chip select * - Peripheral select decode */ obj->spi->CONFIG |= (CONFIG_MSEL | CONFIG_MSE /*| CONFIG_MCSE | CONFIG_PSD*/); /* Set all peripheral select lines high - these should be unused */ obj->spi->CONFIG |= 0x00000; //CONFIG_PCSL; obj->spi->IRQ_ENABLE = irqs; obj->spi->IRQ_DISABLE = ~irqs; obj->spi->SPI_ENABLE |= SPI_ENABLE_SPIE; } void spi_free(spi_t *obj) { } void spi_format(spi_t *obj, int bits, int mode, int slave) { private_spi_t *private_spi = get_spi_private(obj); obj->spi->SPI_ENABLE &= ~SPI_ENABLE_SPIE; /* * The mbed API specifies 'bits' as being 4-16 per frame. This * controller supports only 8 or 16 bit frames. Therefore we will * assume 8 bits and, if anything larger is specified, we will use * 16 bits. */ obj->spi->CONFIG &= ~CONFIG_TWS; /* 00 = 8 bit frame */ private_spi->size = 8; if (bits > 8) { switch (bits) { case 16: private_spi->size = 16; break; default: obj->spi->CONFIG |= CONFIG_TWS_1; /* 01 = 16 bit frame */ break; } } switch (mode) { default: case 0: obj->spi->CONFIG &= ~CONFIG_CPOL; obj->spi->CONFIG &= ~CONFIG_CPHA; break; case 1: obj->spi->CONFIG &= ~CONFIG_CPOL; obj->spi->CONFIG |= CONFIG_CPHA; break; case 2: obj->spi->CONFIG |= CONFIG_CPOL; obj->spi->CONFIG &= ~CONFIG_CPHA; break; case 3: obj->spi->CONFIG |= CONFIG_CPOL; obj->spi->CONFIG |= CONFIG_CPHA; break; } obj->spi->SPI_ENABLE |= SPI_ENABLE_SPIE; } void spi_frequency(spi_t *obj, int hz) { /* * Valid frequencies are derived from a 25MHz peripheral clock. * Frequency | Divisor | MBRD Value | Hz * 12.0 MHz 2 000 12000000 * 6.0 MHz 4 001 6000000 * 3.0 MHz 8 010 3000000 * 1.5 MHz 16 011 1500000 * 750.0 KHz 32 100 750000 * 375.0 KHz 64 101 375000 * 187.500 KHz 128 110 187500 * 93.750 KHz 256 111 93750 */ int valid_frequencies[] = {12000000, 6000000, 3000000, 1500000, 750000, 375000, 187500, 93750}; uint16_t mbrd_value = 0; uint32_t config = (obj->spi->CONFIG & ~CONFIG_MBRD); /* Store the index of the minimum supported frequency */ uint32_t index = 7; for (int i = 0; i < 8; i++) { if (hz >= valid_frequencies[i]) { /* * Store the index of the closest lower or equal supported * frequency. */ index = i; break; } mbrd_value++; } /* * Set the selected frequency. If the frequency is below the minimum * supported the driver sets the minumum. */ config |= index << CONFIG_MBRD_SHIFT; /* * If the specified frequency didn't match any of the valid frequencies * then leave CONFIG_MBRD to the closest lower frequency supported. */ obj->spi->CONFIG = config; } int spi_master_write(spi_t *obj, int value) { private_spi_t *private_spi = get_spi_private(obj); int data = 0; if(private_spi->size == 16) { obj->spi->TX_DATA = (uint8_t)((value >> 8) & TX_DATA_TDATA); obj->spi->TX_DATA = (uint8_t)(value & TX_DATA_TDATA); /* Manually trigger start */ obj->spi->CONFIG |= CONFIG_MSC; while(!(obj->spi->IRQ_STATUS & IRQ_STATUS_TNF)) continue; data = (obj->spi->RX_DATA & RX_DATA_RDATA) << 8; data = data | (obj->spi->RX_DATA & RX_DATA_RDATA); } else { obj->spi->TX_DATA = (uint16_t)(value & TX_DATA_TDATA); /* Manually trigger start */ obj->spi->CONFIG |= CONFIG_MSC; while(!(obj->spi->IRQ_STATUS & IRQ_STATUS_TNF)) continue; data = obj->spi->RX_DATA & RX_DATA_RDATA; } return data; } int spi_master_block_write(spi_t *obj, const char *tx_buffer, int tx_length, char *rx_buffer, int rx_length, char write_fill) { int total = (tx_length > rx_length) ? tx_length : rx_length; for (int i = 0; i < total; i++) { char out = (i < tx_length) ? tx_buffer[i] : write_fill; char in = spi_master_write(obj, out); if (i < rx_length) { rx_buffer[i] = in; } } return total; } uint8_t spi_get_module(spi_t *obj) { return obj->spi->MID; } int spi_busy(spi_t *obj) { return 0; }