// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD // // 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. /******************************************************************************* * NOTICE * The hal is not public api, don't use in application code. * See readme.md in soc/include/hal/readme.md ******************************************************************************/ // The HAL layer for SPI master (common part) // SPI HAL usages: // 1. initialize the bus // 2. initialize the DMA descriptors if DMA used // 3. setup the clock speed (since this takes long time) // 4. call setup_device to update parameters for the specific device // 5. call setup_trans to update parameters for the specific transaction // 6. prepare data to send, and prepare the receiving buffer // 7. trigger user defined SPI transaction to start // 8. wait until the user transaction is done // 9. fetch the received data // Parameter to be updated only during ``setup_device`` will be highlighted in the // field comments. #pragma once #include "hal/spi_ll.h" #include #include "soc/lldesc.h" #include "soc/spi_caps.h" /** * Timing configuration structure that should be calculated by * ``spi_hal_setup_clock`` at initialization and hold. Filled into the * ``timing_conf`` member of the context of HAL before setup a device. */ typedef struct { spi_ll_clock_val_t clock_reg; ///< Register value used by the LL layer int timing_dummy; ///< Extra dummy needed to compensate the timing int timing_miso_delay; ///< Extra miso delay clocks to compensate the timing } spi_hal_timing_conf_t; /** * Context that should be maintained by both the driver and the HAL. */ typedef struct { /* configured by driver at initialization, don't touch */ spi_dev_t *hw; ///< Beginning address of the peripheral registers. /* should be configured by driver at initialization */ lldesc_t *dmadesc_tx; /**< Array of DMA descriptor used by the TX DMA. * The amount should be larger than dmadesc_n. The driver should ensure that * the data to be sent is shorter than the descriptors can hold. */ lldesc_t *dmadesc_rx; /**< Array of DMA descriptor used by the RX DMA. * The amount should be larger than dmadesc_n. The driver should ensure that * the data to be sent is shorter than the descriptors can hold. */ int dmadesc_n; ///< The amount of descriptors of both ``dmadesc_tx`` and ``dmadesc_rx`` that the HAL can use. /* * Device specific, all these parameters will be updated to the peripheral * only when ``spi_hal_setup_device``. They may not get updated when * ``spi_hal_setup_trans``. */ int mode; ///< SPI mode, device specific int cs_setup; ///< Setup time of CS active edge before the first SPI clock, device specific int cs_hold; ///< Hold time of CS inactive edge after the last SPI clock, device specific int cs_pin_id; ///< CS pin to use, 0-2, otherwise all the CS pins are not used. Device specific spi_hal_timing_conf_t *timing_conf; /**< Pointer to an structure holding * the pre-calculated timing configuration for the device at initialization, * device specific */ struct { uint32_t sio : 1; ///< Whether to use SIO mode, device specific uint32_t half_duplex : 1; ///< Whether half duplex mode is used, device specific uint32_t tx_lsbfirst : 1; ///< Whether LSB is sent first for TX data, device specific uint32_t rx_lsbfirst : 1; ///< Whether LSB is received first for RX data, device specific uint32_t dma_enabled : 1; ///< Whether the DMA is enabled, do not update after initialization uint32_t no_compensate : 1; ///< No need to add dummy to compensate the timing, device specific #ifdef SOC_SPI_SUPPORT_AS_CS uint32_t as_cs : 1; ///< Whether to toggle the CS while the clock toggles, device specific #endif uint32_t positive_cs : 1; ///< Whether the postive CS feature is abled, device specific };//boolean configurations /* * Transaction specific (data), all these parameters will be updated to the * peripheral every transaction. */ uint16_t cmd; ///< Command value to be sent int cmd_bits; ///< Length (in bits) of the command phase int addr_bits; ///< Length (in bits) of the address phase int dummy_bits; ///< Base length (in bits) of the dummy phase. Note when the compensation is enabled, some extra dummy bits may be appended. int tx_bitlen; ///< TX length, in bits int rx_bitlen; ///< RX length, in bits uint64_t addr; ///< Address value to be sent uint8_t *send_buffer; ///< Data to be sent uint8_t *rcv_buffer; ///< Buffer to hold the receive data. spi_ll_io_mode_t io_mode; ///< IO mode of the master } spi_hal_context_t; /** * Init the peripheral and the context. * * @param hal Context of the HAL layer. * @param host_id Index of the SPI peripheral. 0 for SPI1, 1 for HSPI (SPI2) and 2 for VSPI (SPI3). */ void spi_hal_init(spi_hal_context_t *hal, int host_id); /** * Deinit the peripheral (and the context if needed). * * @param hal Context of the HAL layer. */ void spi_hal_deinit(spi_hal_context_t *hal); /** * Setup device-related configurations according to the settings in the context. * * @param hal Context of the HAL layer. */ void spi_hal_setup_device(const spi_hal_context_t *hal); /** * Setup transaction related configurations according to the settings in the context. * * @param hal Context of the HAL layer. */ void spi_hal_setup_trans(const spi_hal_context_t *hal); /** * Prepare the data for the current transaction. * * @param hal Context of the HAL layer. */ void spi_hal_prepare_data(const spi_hal_context_t *hal); /** * Trigger start a user-defined transaction. * * @param hal Context of the HAL layer. */ void spi_hal_user_start(const spi_hal_context_t *hal); /** * Check whether the transaction is done (trans_done is set). * * @param hal Context of the HAL layer. */ bool spi_hal_usr_is_done(const spi_hal_context_t *hal); /** * Post transaction operations, mainly fetch data from the buffer. * * @param hal Context of the HAL layer. */ void spi_hal_fetch_result(const spi_hal_context_t *hal); /*---------------------------------------------------------- * Utils * ---------------------------------------------------------*/ /** * Get the configuration of clock and timing. The configuration will be used when ``spi_hal_setup_device``. * * It is highly suggested to do this at initialization, since it takes long time. * * @param hal Context of the HAL layer. * @param speed_hz Desired frequency. * @param duty_cycle Desired duty cycle of SPI clock * @param use_gpio true if the GPIO matrix is used, otherwise false * @param input_delay_ns Maximum delay between SPI launch clock and the data to * be valid. This is used to compensate/calculate the maximum frequency * allowed. Left 0 if not known. * @param out_freq Output of the actual frequency, left NULL if not required. * @param timing_conf Output of the timing configuration. * * @return ESP_OK if desired is available, otherwise fail. */ esp_err_t spi_hal_get_clock_conf(const spi_hal_context_t *hal, int speed_hz, int duty_cycle, bool use_gpio, int input_delay_ns, int *out_freq, spi_hal_timing_conf_t *timing_conf); /** * Get the frequency actual used. * * @param hal Context of the HAL layer. * @param fapb APB clock frequency. * @param hz Desired frequencyc. * @param duty_cycle Desired duty cycle. */ int spi_hal_master_cal_clock(int fapb, int hz, int duty_cycle); /** * Get the timing configuration for given parameters. * * @param eff_clk Actual SPI clock frequency * @param gpio_is_used true if the GPIO matrix is used, otherwise false. * @param input_delay_ns Maximum delay between SPI launch clock and the data to * be valid. This is used to compensate/calculate the maximum frequency * allowed. Left 0 if not known. * @param dummy_n Dummy cycles required to correctly read the data. * @param miso_delay_n suggested delay on the MISO line, in APB clocks. */ void spi_hal_cal_timing(int eff_clk, bool gpio_is_used, int input_delay_ns, int *dummy_n, int *miso_delay_n); /** * Get the maximum frequency allowed to read if no compensation is used. * * @param gpio_is_used true if the GPIO matrix is used, otherwise false. * @param input_delay_ns Maximum delay between SPI launch clock and the data to * be valid. This is used to compensate/calculate the maximum frequency * allowed. Left 0 if not known. */ int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns);