mbed library sources. Supersedes mbed-src.
Dependents: Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more
targets/TARGET_GigaDevice/TARGET_GD32F30X/spi_api.c
- Committer:
- AnnaBridge
- Date:
- 2019-02-20
- Revision:
- 189:f392fc9709a3
File content as of revision 189:f392fc9709a3:
/* mbed Microcontroller Library * Copyright (c) 2018 GigaDevice Semiconductor Inc. * * SPDX-License-Identifier: Apache-2.0 * * 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 "mbed_assert.h" #include "mbed_error.h" #include "spi_api.h" #if DEVICE_SPI #include "cmsis.h" #include "pinmap.h" #include "PeripheralPins.h" #define SPI_S(obj) (( struct spi_s *)(obj)) /** Get the frequency of SPI clock source * * Configures the pins used by SPI, sets a default format and frequency, and enables the peripheral * @param[out] spi_freq The SPI clock source freguency * @param[in] obj The SPI object */ static int dev_spi_clock_source_frequency_get(spi_t *obj) { int spi_freq = 0; struct spi_s *spiobj = SPI_S(obj); switch ((int)spiobj->spi) { case SPI0: /* clock source is APB2 */ spi_freq = rcu_clock_freq_get(CK_APB2); break; case SPI1: /* clock source is APB1 */ spi_freq = rcu_clock_freq_get(CK_APB1); break; case SPI2: /* clock source is APB1 */ spi_freq = rcu_clock_freq_get(CK_APB1); break; default: error("SPI clock source frequency get error"); break; } return spi_freq; } /** Initialize the SPI structure * * Configures the pins used by SPI, sets a default format and frequency, and enables the peripheral * @param[out] obj The SPI object to initialize */ static void dev_spi_struct_init(spi_t *obj) { struct spi_s *spiobj = SPI_S(obj); spi_disable(spiobj->spi); spi_para_init(spiobj->spi, &obj->spi_struct); spi_enable(spiobj->spi); } /** Initialize the SPI peripheral * * Configures the pins used by SPI, sets a default format and frequency, and enables the peripheral * @param[out] obj The SPI object to initialize * @param[in] mosi The pin to use for MOSI * @param[in] miso The pin to use for MISO * @param[in] sclk The pin to use for SCLK * @param[in] ssel The pin to use for SSEL */ void spi_init(spi_t *obj, PinName mosi, PinName miso, PinName sclk, PinName ssel) { struct spi_s *spiobj = SPI_S(obj); 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); /* return SPIName according to PinName */ SPIName spi_data = (SPIName)pinmap_merge(spi_mosi, spi_miso); SPIName spi_cntl = (SPIName)pinmap_merge(spi_sclk, spi_ssel); spiobj->spi = (SPIName)pinmap_merge(spi_data, spi_cntl); MBED_ASSERT(spiobj->spi != (SPIName)NC); /* Set iqr type */ if (spiobj->spi == SPI0) { rcu_periph_clock_enable(RCU_SPI0); spiobj->spi_irq = SPI0_IRQn; } if (spiobj->spi == SPI1) { rcu_periph_clock_enable(RCU_SPI1); spiobj->spi_irq = SPI1_IRQn; } if (spiobj->spi == SPI2) { rcu_periph_clock_enable(RCU_SPI2); spiobj->spi_irq = SPI2_IRQn; } /* config GPIO mode of SPI pins */ pinmap_pinout(mosi, PinMap_SPI_MOSI); pinmap_pinout(miso, PinMap_SPI_MISO); pinmap_pinout(sclk, PinMap_SPI_SCLK); spiobj->pin_miso = miso; spiobj->pin_mosi = mosi; spiobj->pin_sclk = sclk; spiobj->pin_ssel = ssel; if (ssel != NC) { pinmap_pinout(ssel, PinMap_SPI_SSEL); spiobj->spi_struct.nss = SPI_NSS_HARD; } else { spiobj->spi_struct.nss = SPI_NSS_SOFT; } /* Default values */ spiobj->spi_struct.device_mode = SPI_MASTER; spiobj->spi_struct.prescale = SPI_PSC_256; spiobj->spi_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX; spiobj->spi_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; spiobj->spi_struct.frame_size = SPI_FRAMESIZE_8BIT; spiobj->spi_struct.endian = SPI_ENDIAN_MSB; dev_spi_struct_init(obj); } /** Release a SPI object * * TODO: spi_free is currently unimplemented * This will require reference counting at the C++ level to be safe * * Return the pins owned by the SPI object to their reset state * Disable the SPI peripheral * Disable the SPI clock * @param[in] obj The SPI object to deinitialize */ void spi_free(spi_t *obj) { struct spi_s *spiobj = SPI_S(obj); spi_disable(spiobj->spi); /* Disable and deinit SPI */ if (spiobj->spi == SPI0) { spi_i2s_deinit(SPI0); rcu_periph_clock_disable(RCU_SPI0); } if (spiobj->spi == SPI1) { spi_i2s_deinit(SPI1); rcu_periph_clock_disable(RCU_SPI1); } if (spiobj->spi == SPI2) { spi_i2s_deinit(SPI2); rcu_periph_clock_disable(RCU_SPI2); } /* Deinit GPIO mode of SPI pins */ pin_function(spiobj->pin_miso, MODE_IN_FLOATING); pin_function(spiobj->pin_mosi, MODE_IN_FLOATING); pin_function(spiobj->pin_sclk, MODE_IN_FLOATING); if (spiobj->spi_struct.nss != SPI_NSS_SOFT) { pin_function(spiobj->pin_ssel, MODE_IN_FLOATING); } } /** Configure the SPI format * * Set the number of bits per frame, configure clock polarity and phase, shift order and master/slave mode. * The default bit order is MSB. * @param[in,out] obj The SPI object to configure * @param[in] bits The number of bits per frame * @param[in] mode The SPI mode (clock polarity, phase, and shift direction) * @param[in] slave Zero for master mode or non-zero for slave mode */ void spi_format(spi_t *obj, int bits, int mode, int slave) { struct spi_s *spiobj = SPI_S(obj); spiobj->spi_struct.frame_size = (bits == 16) ? SPI_FRAMESIZE_16BIT : SPI_FRAMESIZE_8BIT; /* Config polarity and phase of SPI */ switch (mode) { case 0: spiobj->spi_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE; break; case 1: spiobj->spi_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE; break; case 2: spiobj->spi_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_1EDGE; break; default: spiobj->spi_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE; break; } if (spiobj->spi_struct.nss != SPI_NSS_SOFT) { spiobj->spi_struct.nss = SPI_NSS_HARD; spi_nss_output_enable(spiobj->spi); } /* Select SPI as master or slave */ spiobj->spi_struct.device_mode = (slave) ? SPI_SLAVE : SPI_MASTER; dev_spi_struct_init(obj); } static const uint16_t baudrate_prescaler_table[] = {SPI_PSC_2, SPI_PSC_4, SPI_PSC_8, SPI_PSC_16, SPI_PSC_32, SPI_PSC_64, SPI_PSC_128, SPI_PSC_256 }; /** Set the SPI baud rate * * Actual frequency may differ from the desired frequency due to available dividers and bus clock * Configures the SPI peripheral's baud rate * @param[in,out] obj The SPI object to configure * @param[in] hz The baud rate in Hz */ void spi_frequency(spi_t *obj, int hz) { struct spi_s *spiobj = SPI_S(obj); int spi_hz = 0; uint8_t prescaler_rank = 0; uint8_t last_index = (sizeof(baudrate_prescaler_table) / sizeof(baudrate_prescaler_table[0])) - 1; spi_hz = dev_spi_clock_source_frequency_get(obj) / 2; /* Config SPI prescaler according to input frequency*/ while ((spi_hz > hz) && (prescaler_rank < last_index)) { spi_hz = spi_hz / 2; prescaler_rank++; } spiobj->spi_struct.prescale = baudrate_prescaler_table[prescaler_rank]; dev_spi_struct_init(obj); } /** Write a block out in master mode and receive a value * * The total number of bytes sent and received will be the maximum of * tx_length and rx_length. The bytes written will be padded with the * value 0xff. * * @param[in] obj The SPI peripheral to use for sending * @param[in] tx_buffer Pointer to the byte-array of data to write to the device * @param[in] tx_length Number of bytes to write, may be zero * @param[in] rx_buffer Pointer to the byte-array of data to read from the device * @param[in] rx_length Number of bytes to read, may be zero * @param[in] write_fill Default data transmitted while performing a read * @returns * The number of bytes written and read from the device. This is * maximum of tx_length and rx_length. */ 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; } /** Write a byte out in master mode and receive a value * * @param[in] obj The SPI peripheral to use for sending * @param[in] value The value to send * @return Returns the value received during send */ int spi_master_write(spi_t *obj, int value) { int count = 0; struct spi_s *spiobj = SPI_S(obj); /* wait the SPI transmit buffer is empty */ while ((RESET == spi_i2s_flag_get(spiobj->spi, SPI_FLAG_TBE)) && (count++ < 1000)); if (count >= 1000) { return -1; } else { spi_i2s_data_transmit(spiobj->spi, value); } count = 0; /* wait the SPI receive buffer is not empty */ while ((RESET == spi_i2s_flag_get(spiobj->spi, SPI_FLAG_RBNE)) && (count++ < 1000)); if (count >= 1000) { return -1; } else { return spi_i2s_data_receive(spiobj->spi); } } /** Check if a value is available to read * * @param[in] obj The SPI peripheral to check * @return non-zero if a value is available */ int spi_slave_receive(spi_t *obj) { int status; struct spi_s *spiobj = SPI_S(obj); /* check whether or not the SPI receive buffer is empty */ status = ((spi_i2s_flag_get(spiobj->spi, SPI_FLAG_RBNE) != RESET) ? 1 : 0); return status; } /** Get a received value out of the SPI receive buffer in slave mode * * Blocks until a value is available * @param[in] obj The SPI peripheral to read * @return The value received */ int spi_slave_read(spi_t *obj) { int count = 0; struct spi_s *spiobj = SPI_S(obj); /* wait the SPI receive buffer is not empty */ while ((RESET == spi_i2s_flag_get(spiobj->spi, SPI_FLAG_RBNE)) && (count++ < 1000)); if (count >= 1000) { return -1; } else { return spi_i2s_data_receive(spiobj->spi); } } /** Write a value to the SPI peripheral in slave mode * * Blocks until the SPI peripheral can be written to * @param[in] obj The SPI peripheral to write * @param[in] value The value to write */ void spi_slave_write(spi_t *obj, int value) { struct spi_s *spiobj = SPI_S(obj); /* wait the SPI transmit buffer is empty */ while (RESET == spi_i2s_flag_get(spiobj->spi, SPI_FLAG_TBE)); spi_i2s_data_transmit(spiobj->spi, value); } /** Checks if the specified SPI peripheral is in use * * @param[in] obj The SPI peripheral to check * @return non-zero if the peripheral is currently transmitting */ int spi_busy(spi_t *obj) { int status; struct spi_s *spiobj = SPI_S(obj); /* check whether or not the SPI is busy */ status = ((spi_i2s_flag_get(spiobj->spi, SPI_FLAG_TRANS) != RESET) ? 1 : 0); return status; } #endif