Files
mbed-os/targets/TARGET_Cypress/TARGET_PSOC6/mtb-hal-cat1/source/cyhal_spi.c
Beslan 0ef1717155
Some checks failed
Basic Checks / license-check (push) Has been cancelled
Basic Checks / include-check (push) Has been cancelled
Basic Checks / style-check (push) Has been cancelled
Basic Checks / docs-check (push) Has been cancelled
Basic Checks / python-tests (push) Has been cancelled
Basic Checks / pin-validation (push) Has been cancelled
Basic Checks / cmake-checks (push) Has been cancelled
Basic Checks / frozen-tools-check (push) Has been cancelled
Publish or Update docker image for head of branch / prepare-tags (push) Has been cancelled
Release or update docker image for a released mbed-os version / prepare-tags (push) Has been cancelled
Publish or Update docker image for head of branch / build-container (push) Has been cancelled
Publish or Update docker image for head of branch / test-container (linux/amd64) (push) Has been cancelled
Publish or Update docker image for head of branch / test-container (linux/arm64) (push) Has been cancelled
Publish or Update docker image for head of branch / deploy-container (push) Has been cancelled
Release or update docker image for a released mbed-os version / build-container (push) Has been cancelled
Release or update docker image for a released mbed-os version / test-container (linux/amd64) (push) Has been cancelled
Release or update docker image for a released mbed-os version / test-container (linux/arm64) (push) Has been cancelled
Release or update docker image for a released mbed-os version / deploy-container (push) Has been cancelled
Prune temporary docker images / prune-images (push) Has been cancelled
Mirror mbed-os-6.15.0
2026-07-10 18:42:39 +03:00

964 lines
33 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/***************************************************************************//**
* \file cyhal_spi.c
*
* \brief
* Provides a high level interface for interacting with the Cypress SPI. This is
* a wrapper around the lower level PDL API.
*
********************************************************************************
* \copyright
* Copyright 2018-2021 Cypress Semiconductor Corporation
* 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 <stdlib.h>
#include <string.h>
#include "cyhal_spi.h"
#include "cyhal_scb_common.h"
#include "cyhal_gpio.h"
#include "cyhal_system_impl.h"
#include "cyhal_hwmgr.h"
#include "cyhal_system.h"
#include "cyhal_syspm.h"
#include "cyhal_clock.h"
#if defined(CY_IP_MXSCB) || defined(CY_IP_M0S8SCB)
#if defined(__cplusplus)
extern "C"
{
#endif
#define _CYHAL_SPI_DEFAULT_SPEED 100000
#define _CYHAL_SPI_OVERSAMPLE_MIN 4
#define _CYHAL_SPI_OVERSAMPLE_MAX 16
#define _CYHAL_SPI_SSEL_NUM 4
#define _CYHAL_SPI_PENDING_NONE 0
#define _CYHAL_SPI_PENDING_RX 1
#define _CYHAL_SPI_PENDING_TX 2
#define _CYHAL_SPI_PENDING_TX_RX 3
#define _CYHAL_SPI_SSEL_ACTIVATE true
#define _CYHAL_SPI_SSEL_DEACTIVATE false
/* Default SPI configuration */
static const cy_stc_scb_spi_config_t _cyhal_spi_default_config =
{
.spiMode = CY_SCB_SPI_MASTER,
.subMode = CY_SCB_SPI_MOTOROLA,
.sclkMode = CY_SCB_SPI_CPHA0_CPOL0,
.oversample = _CYHAL_SPI_OVERSAMPLE_MIN,
.rxDataWidth = 8,
.txDataWidth = 8,
.enableMsbFirst = true,
.enableFreeRunSclk = false,
.enableInputFilter = false,
.enableMisoLateSample = true,
.enableTransferSeperation = false,
.enableWakeFromSleep = false,
.ssPolarity = CY_SCB_SPI_ACTIVE_LOW,
.rxFifoTriggerLevel = 0,
.rxFifoIntEnableMask = 0,
.txFifoTriggerLevel = 0,
.txFifoIntEnableMask = 0,
.masterSlaveIntEnableMask = 0
};
static void _cyhal_ssel_switch_state(cyhal_spi_t *obj, uint8_t ssel_idx, bool ssel_activate);
static cy_rslt_t _cyhal_spi_int_frequency(cyhal_spi_t *obj, uint32_t hz, uint8_t *over_sample_val)
{
CY_ASSERT(NULL != obj);
cy_rslt_t result = CY_RSLT_SUCCESS;
uint8_t oversample_value;
uint32_t divider_value;
uint32_t last_diff = 0xFFFFFFFFU;
uint8_t last_ovrsmpl_val = 0;
uint32_t last_dvdr_val = 0;
uint32_t oversampled_freq = 0;
uint32_t divided_freq = 0;
uint32_t diff = 0;
Cy_SysClk_PeriphDisableDivider((cy_en_divider_types_t)obj->clock.block, obj->clock.channel);
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
uint32_t peri_freq = Cy_SysClk_ClkPeriGetFrequency();
#elif defined(COMPONENT_CAT2)
uint32_t peri_freq = Cy_SysClk_ClkSysGetFrequency();
#endif
if (!obj->is_slave)
{
for (oversample_value = _CYHAL_SPI_OVERSAMPLE_MIN; oversample_value <= _CYHAL_SPI_OVERSAMPLE_MAX; oversample_value++)
{
oversampled_freq = hz * oversample_value;
if ((hz * oversample_value > peri_freq) && (_CYHAL_SPI_OVERSAMPLE_MIN == oversample_value))
{
return CYHAL_SPI_RSLT_CLOCK_ERROR;
}
else if (hz * oversample_value > peri_freq)
{
continue;
}
divider_value = _cyhal_utils_divider_value(hz * oversample_value, 0);
divided_freq = peri_freq /divider_value;
diff = (oversampled_freq > divided_freq)
? oversampled_freq - divided_freq
: divided_freq - oversampled_freq;
if (diff < last_diff)
{
last_diff = diff;
last_ovrsmpl_val = oversample_value;
last_dvdr_val = divider_value;
if (0 == diff)
{
break;
}
}
}
*over_sample_val = last_ovrsmpl_val;
}
else
{
/* Slave requires such frequency: required_frequency = N / ((0.5 * desired_period) 20 nsec - tDSI,
* N is 3 when "Enable Input Glitch Filter" is false and 4 when true.
* tDSI Is external master delay which is assumed to be 16.66 nsec */
/* Divided by 2 desired period to avoid dividing in required_frequency formula */
float desired_period_us_divided = 5e5f * (1 / (float)hz);
uint32_t required_frequency = (uint32_t)(3e6f / (desired_period_us_divided - 36.66f / 1e3f));
if (required_frequency > peri_freq)
{
return CYHAL_SPI_RSLT_CLOCK_ERROR;
}
/* Use maximum available clock for slave to make it able to work with any master environment */
last_dvdr_val = 1;
}
result = Cy_SysClk_PeriphSetDivider((cy_en_divider_types_t)obj->clock.block, obj->clock.channel, last_dvdr_val - 1);
if (CY_RSLT_SUCCESS == result)
{
Cy_SysClk_PeriphEnableDivider((cy_en_divider_types_t)obj->clock.block, obj->clock.channel);
}
return result;
}
static inline cyhal_spi_event_t _cyhal_spi_convert_interrupt_cause(uint32_t pdl_cause)
{
static const uint32_t status_map[] =
{
(uint32_t)CYHAL_SPI_IRQ_ERROR, // Default error if unknown value is set
(uint32_t)CYHAL_SPI_IRQ_DATA_IN_FIFO, // CY_SCB_SPI_TRANSFER_IN_FIFO_EVENT
(uint32_t)CYHAL_SPI_IRQ_DONE, // CY_SCB_SPI_TRANSFER_CMPLT_EVENT
(uint32_t)CYHAL_SPI_IRQ_ERROR, // CY_SCB_SPI_TRANSFER_ERR_EVENT
};
return (cyhal_spi_event_t)_cyhal_utils_convert_flags(status_map, sizeof(status_map) / sizeof(uint32_t), pdl_cause);
}
static void _cyhal_spi_irq_handler(void)
{
cyhal_spi_t *obj = (cyhal_spi_t*) _cyhal_scb_get_irq_obj();
if (NULL == obj)
{
return;
}
Cy_SCB_SPI_Interrupt(obj->base, &(obj->context));
if (!obj->is_async)
{
return;
}
if (0 == (Cy_SCB_SPI_GetTransferStatus(obj->base, &obj->context) & CY_SCB_SPI_TRANSFER_ACTIVE))
{
if (obj->tx_buffer)
{
/* Start TX Transfer */
obj->pending = _CYHAL_SPI_PENDING_TX;
const uint8_t *buf = obj->tx_buffer;
obj->tx_buffer = NULL;
Cy_SCB_SPI_Transfer(obj->base, (uint8_t *)buf, NULL, obj->tx_buffer_size, &obj->context);
}
else if (obj->rx_buffer)
{
/* Start RX Transfer */
obj->pending = _CYHAL_SPI_PENDING_RX;
uint8_t *rx_buf = obj->rx_buffer;
uint8_t *tx_buf;
size_t trx_size = obj->rx_buffer_size;
if (obj->rx_buffer_size > 1)
{
/* In this case we don't have a transmit buffer; we only have a receive buffer. While the PDL
* is fine with passing NULL for transmit, we don't get to control what data it is sending in
* that case, which we allowed the user to set. To honor the user's request, we reuse the rx
* buffer as the tx buffer too. We set all bytes beyond the one we will start filling in with
* the user provided 'write_fill'. This means the tx buffer is 1 element smaller than the rx
* buffer. As a result, we must therefore transfer 1 less element then we really want to in
* this transfer. When this transfer is complete, it will call into this again to receive the
* final element.
*/
trx_size -= 1; // Transfer everything left except for the last byte
uint8_t **rx_buffer_p = (uint8_t **) &obj->rx_buffer;
tx_buf = *rx_buffer_p + 1; // Start at second byte to avoid trying to transmit and receive the same byte
memset(tx_buf, obj->write_fill, trx_size);
*rx_buffer_p += trx_size; // Move to 1 byte before end
obj->rx_buffer_size = 1; // Transfer the last byte on the next interrupt
}
else
{
tx_buf = &obj->write_fill;
obj->rx_buffer = NULL;
}
Cy_SCB_SPI_Transfer(obj->base, tx_buf, rx_buf, trx_size, &obj->context);
}
else
{
/* Finish Async Transfer */
obj->pending = _CYHAL_SPI_PENDING_NONE;
obj->is_async = false;
_cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_DEACTIVATE);
}
}
}
static void _cyhal_spi_cb_wrapper(uint32_t event)
{
cyhal_spi_t *obj = (cyhal_spi_t*) _cyhal_scb_get_irq_obj();
cyhal_spi_irq_event_t anded_events = (cyhal_spi_irq_event_t) (obj->irq_cause & (uint32_t) _cyhal_spi_convert_interrupt_cause(event));
// Don't call the callback until the final transfer has put everything in the FIFO/completed
if ((anded_events & (CYHAL_SPI_IRQ_DATA_IN_FIFO | CYHAL_SPI_IRQ_DONE)) && !(obj->rx_buffer == NULL && obj->tx_buffer == NULL))
{
return;
}
if (anded_events)
{
cyhal_spi_event_callback_t callback = (cyhal_spi_event_callback_t) obj->callback_data.callback;
callback(obj->callback_data.callback_arg, anded_events);
}
}
static cy_en_scb_spi_sclk_mode_t _cyhal_convert_mode_sclk(cyhal_spi_mode_t mode)
{
uint8_t sclk_mode = (mode & (CYHAL_SPI_MODE_FLAG_CPOL | CYHAL_SPI_MODE_FLAG_CPHA));
switch (sclk_mode)
{
case CYHAL_SPI_MODE_FLAG_CPOL | CYHAL_SPI_MODE_FLAG_CPHA:
return (CY_SCB_SPI_CPHA1_CPOL1);
case CYHAL_SPI_MODE_FLAG_CPOL:
return (CY_SCB_SPI_CPHA0_CPOL1);
case CYHAL_SPI_MODE_FLAG_CPHA:
return (CY_SCB_SPI_CPHA1_CPOL0);
default:
return (CY_SCB_SPI_CPHA0_CPOL0);
}
}
static inline bool _is_cyhal_mode_msb(cyhal_spi_mode_t mode)
{
return ((mode & CYHAL_SPI_MODE_FLAG_LSB) != CYHAL_SPI_MODE_FLAG_LSB);
}
static bool _cyhal_spi_pm_callback_instance(void *obj_ptr, cyhal_syspm_callback_state_t state, cy_en_syspm_callback_mode_t pdl_mode)
{
cyhal_spi_t *obj = (cyhal_spi_t *)obj_ptr;
bool allow = true;
cy_stc_syspm_callback_params_t spi_callback_params = {
.base = (void *) (obj->base),
.context = (void *) &(obj->context)
};
if (CYHAL_SYSPM_CB_CPU_DEEPSLEEP == state)
allow = (CY_SYSPM_SUCCESS == Cy_SCB_SPI_DeepSleepCallback(&spi_callback_params, pdl_mode));
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
else if (CYHAL_SYSPM_CB_SYSTEM_HIBERNATE == state)
allow = (CY_SYSPM_SUCCESS == Cy_SCB_SPI_HibernateCallback(&spi_callback_params, pdl_mode));
#endif
return allow;
}
static cy_rslt_t _cyhal_spi_get_ssel_map_idx(cyhal_spi_t *obj, cyhal_gpio_t ssel,
const cyhal_resource_pin_mapping_t **ssel_map, uint8_t *idx)
{
#ifdef CY_IP_M0S8SCB
/* SSEL0 is only available for Slave on M0S8SCB */
static const cyhal_resource_pin_mapping_t *ssel_s_pin_maps[] = { cyhal_pin_map_scb_spi_s_select0 };
static const size_t ssel_s_pin_maps_sizes_bytes[] = { sizeof(cyhal_pin_map_scb_spi_s_select0) };
#else
static const cyhal_resource_pin_mapping_t *ssel_s_pin_maps[] = {
cyhal_pin_map_scb_spi_s_select0, cyhal_pin_map_scb_spi_s_select1,
cyhal_pin_map_scb_spi_s_select2, cyhal_pin_map_scb_spi_s_select3 };
static const size_t ssel_s_pin_maps_sizes_bytes[] = {
sizeof(cyhal_pin_map_scb_spi_s_select0), sizeof(cyhal_pin_map_scb_spi_s_select1),
sizeof(cyhal_pin_map_scb_spi_s_select2), sizeof(cyhal_pin_map_scb_spi_s_select3) };
#endif /* M0S8 version of SCB or other */
for (uint8_t i = 0; i < sizeof(ssel_s_pin_maps) / sizeof(ssel_s_pin_maps[0]); i++)
{
*ssel_map = _cyhal_scb_find_map(ssel, ssel_s_pin_maps[i],
ssel_s_pin_maps_sizes_bytes[i] / sizeof(cyhal_resource_pin_mapping_t), &(obj->resource));
if (NULL != *ssel_map)
{
*idx = i;
return CY_RSLT_SUCCESS;
}
}
return CYHAL_SPI_RSLT_ERR_CANNOT_CONFIG_SSEL;
}
cy_rslt_t cyhal_spi_init(cyhal_spi_t *obj, cyhal_gpio_t mosi, cyhal_gpio_t miso, cyhal_gpio_t sclk, cyhal_gpio_t ssel,
const cyhal_clock_t *clk, uint8_t bits, cyhal_spi_mode_t mode, bool is_slave)
{
CY_ASSERT(NULL != obj);
memset(obj, 0, sizeof(cyhal_spi_t));
cy_rslt_t result = CY_RSLT_SUCCESS;
uint8_t ovr_sample_val = _CYHAL_SPI_OVERSAMPLE_MIN;
obj->pending = _CYHAL_SPI_PENDING_NONE;
// Explicitly marked not allocated resources as invalid to prevent freeing them.
obj->resource.type = CYHAL_RSC_INVALID;
obj->pin_miso = NC;
obj->pin_mosi = NC;
obj->pin_sclk = NC;
for (uint8_t i = 0; i < _CYHAL_SPI_SSEL_NUM; i++)
{
obj->pin_ssel[i] = NC;
obj->ssel_pol[i] = CY_SCB_SPI_ACTIVE_LOW;
}
obj->write_fill = (uint8_t) CY_SCB_SPI_DEFAULT_TX;
obj->active_ssel = 0;
/* Validate pins configuration. Mandatory pins:*/
/* Master mode: MOSI pin used, MISO unused: SCLK, SSEL are optional */
/* Master mode: MISO pin used, MOSI unused: SCLK is mandatory, MOSI, SSEL are optional */
/* Slave mode: MOSI or MISO are used: SCLK and SSEL are mandatory */
/* Slave */
if (is_slave)
{
if ((NC == sclk) || (NC == ssel) || ((NC == mosi) && (NC == miso)))
{
return CYHAL_SPI_RSLT_PIN_CONFIG_NOT_SUPPORTED;
}
}
/* Master */
else
{
if ((NC != miso && NC == sclk) || (NC == mosi && NC == miso))
{
return CYHAL_SPI_RSLT_PIN_CONFIG_NOT_SUPPORTED;
}
}
obj->is_slave = is_slave;
/* Get pin configurations */
const cyhal_resource_pin_mapping_t *mosi_map = NULL;
const cyhal_resource_pin_mapping_t *miso_map = NULL;
const cyhal_resource_pin_mapping_t *sclk_map = NULL;
const cyhal_resource_pin_mapping_t *ssel_map = NULL;
uint8_t active_ssel = 0;
if (is_slave)
{
mosi_map = _CYHAL_SCB_FIND_MAP(mosi, cyhal_pin_map_scb_spi_s_mosi);
miso_map = _CYHAL_SCB_FIND_MAP(miso, cyhal_pin_map_scb_spi_s_miso);
sclk_map = _CYHAL_SCB_FIND_MAP(sclk, cyhal_pin_map_scb_spi_s_clk);
result = _cyhal_spi_get_ssel_map_idx(obj, ssel, &ssel_map, &active_ssel);
}
else
{
mosi_map = _CYHAL_SCB_FIND_MAP(mosi, cyhal_pin_map_scb_spi_m_mosi);
miso_map = _CYHAL_SCB_FIND_MAP(miso, cyhal_pin_map_scb_spi_m_miso);
sclk_map = _CYHAL_SCB_FIND_MAP(sclk, cyhal_pin_map_scb_spi_m_clk);
/* No need to find maps for ssel pins, as GPIO used */
}
const cyhal_resource_inst_t *spi_inst = (NC != mosi)
? (mosi_map != NULL ? mosi_map->inst : NULL)
: (miso_map != NULL ? miso_map->inst : NULL);
/* Validate pins mapping */
if (NULL == spi_inst ||
((NC != mosi) && ((NULL == mosi_map) || !_cyhal_utils_resources_equal(spi_inst, mosi_map->inst))) ||
((NC != miso) && ((NULL == miso_map) || !_cyhal_utils_resources_equal(spi_inst, miso_map->inst))) ||
((NC != sclk) && ((NULL == sclk_map) || !_cyhal_utils_resources_equal(spi_inst, sclk_map->inst))) ||
((is_slave) && ((NC != ssel) && ((NULL == ssel_map) || !_cyhal_utils_resources_equal(spi_inst, ssel_map->inst)))))
{
return CYHAL_SPI_RSLT_ERR_INVALID_PIN;
}
if (CY_RSLT_SUCCESS != (result = cyhal_hwmgr_reserve(spi_inst)))
{
return result;
}
obj->resource = *spi_inst;
obj->base = _CYHAL_SCB_BASE_ADDRESSES[obj->resource.block_num];
// reserve the MOSI pin
if ((result == CY_RSLT_SUCCESS) && (NC != mosi))
{
result = _cyhal_utils_reserve_and_connect(mosi, mosi_map);
if (result == CY_RSLT_SUCCESS)
{
obj->pin_mosi = mosi;
}
}
// reserve the MISO pin
if ((result == CY_RSLT_SUCCESS) && (NC != miso))
{
result = _cyhal_utils_reserve_and_connect(miso, miso_map);
if (result == CY_RSLT_SUCCESS)
{
obj->pin_miso = miso;
}
}
// reserve the SCLK pin
if (result == CY_RSLT_SUCCESS && (NC != sclk))
{
result = _cyhal_utils_reserve_and_connect(sclk, sclk_map);
if (result == CY_RSLT_SUCCESS)
{
obj->pin_sclk = sclk;
}
}
// reserve and configure the SSEL pin
if ((result == CY_RSLT_SUCCESS) && (NC != ssel))
{
result = cyhal_spi_slave_select_config(obj, ssel, CYHAL_SPI_SSEL_ACTIVE_LOW);
}
if (result == CY_RSLT_SUCCESS)
{
if (clk == NULL)
{
result = cyhal_clock_allocate(&(obj->clock), CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT);
obj->alloc_clock = true;
}
else
{
obj->clock = *clk;
obj->alloc_clock = false;
_cyhal_utils_update_clock_format(&(obj->clock));
/* Per CDT 315848 and 002-20730 Rev. *E:
* For SPI, an integer clock divider must be used for both master and slave. */
if ((obj->clock.block == CYHAL_CLOCK_BLOCK_PERIPHERAL_16_5BIT) || (obj->clock.block == CYHAL_CLOCK_BLOCK_PERIPHERAL_24_5BIT))
{
result = CYHAL_SPI_RSLT_CLOCK_NOT_SUPPORTED;
}
}
}
if (result == CY_RSLT_SUCCESS)
{
result = (cy_rslt_t)Cy_SysClk_PeriphAssignDivider(
_cyhal_scb_get_clock_index(obj->resource.block_num),
(cy_en_divider_types_t)obj->clock.block, obj->clock.channel);
if (result == CY_RSLT_SUCCESS)
{
result = _cyhal_spi_int_frequency(obj, _CYHAL_SPI_DEFAULT_SPEED, &ovr_sample_val);
}
}
if (result == CY_RSLT_SUCCESS)
{
_cyhal_scb_update_instance_data(obj->resource.block_num, (void*)obj, &_cyhal_spi_pm_callback_instance);
cy_stc_scb_spi_config_t config_structure = _cyhal_spi_default_config;
config_structure.spiMode = is_slave == 0
? CY_SCB_SPI_MASTER
: CY_SCB_SPI_SLAVE;
obj->msb_first = _is_cyhal_mode_msb(mode);
config_structure.enableMsbFirst = obj->msb_first;
obj->clk_mode = _cyhal_convert_mode_sclk(mode);
config_structure.sclkMode = obj->clk_mode;
config_structure.rxDataWidth = bits;
config_structure.txDataWidth = bits;
config_structure.oversample = ovr_sample_val;
obj->data_bits = bits;
obj->mode = (uint8_t) mode;
obj->oversample_value = ovr_sample_val;
Cy_SCB_SPI_Init(obj->base, &config_structure, &(obj->context));
/* Activating specified by user ssel after init */
if (NC != ssel)
{
result = cyhal_spi_select_active_ssel(obj, ssel);
}
obj->callback_data.callback = NULL;
obj->callback_data.callback_arg = NULL;
obj->irq_cause = 0;
cy_stc_sysint_t irqCfg = { _CYHAL_SCB_IRQ_N[obj->resource.block_num], CYHAL_ISR_PRIORITY_DEFAULT };
Cy_SysInt_Init(&irqCfg, _cyhal_spi_irq_handler);
NVIC_EnableIRQ(_CYHAL_SCB_IRQ_N[obj->resource.block_num]);
Cy_SCB_SPI_Enable(obj->base);
}
else
{
cyhal_spi_free(obj);
}
return result;
}
void cyhal_spi_free(cyhal_spi_t *obj)
{
if (NULL != obj->base)
{
_cyhal_scb_update_instance_data(obj->resource.block_num, NULL, NULL);
Cy_SCB_SPI_Disable(obj->base, NULL);
Cy_SCB_SPI_DeInit(obj->base);
obj->base = NULL;
}
if (obj->resource.type != CYHAL_RSC_INVALID)
{
IRQn_Type irqn = _CYHAL_SCB_IRQ_N[obj->resource.block_num];
NVIC_DisableIRQ(irqn);
cyhal_hwmgr_free(&(obj->resource));
obj->resource.type = CYHAL_RSC_INVALID;
}
_cyhal_utils_release_if_used(&(obj->pin_miso));
_cyhal_utils_release_if_used(&(obj->pin_mosi));
_cyhal_utils_release_if_used(&(obj->pin_sclk));
for (uint8_t i = 0; i < _CYHAL_SPI_SSEL_NUM; i++)
{
_cyhal_utils_release_if_used(&(obj->pin_ssel[i]));
}
if (obj->alloc_clock)
{
cyhal_clock_free(&(obj->clock));
obj->alloc_clock = false;
}
}
static void _cyhal_ssel_switch_state(cyhal_spi_t *obj, uint8_t ssel_idx, bool ssel_activate)
{
if ((!obj->is_slave) && (CYHAL_NC_PIN_VALUE != obj->pin_ssel[ssel_idx]))
{
/* Situations described:
* ssel_activate = true (need to set SSEL into active state)
* CY_SCB_SPI_ACTIVE_LOW - writing 0 to ssel pin
* CY_SCB_SPI_ACTIVE_HIGH - writing 1 to ssel pin
* ssel_activate = false (need to set SSEL into inactive state)
* CY_SCB_SPI_ACTIVE_LOW - writing 1 to ssel pin
* CY_SCB_SPI_ACTIVE_HIGH - writing 0 to ssel pin */
bool ssel_state = (CY_SCB_SPI_ACTIVE_LOW == obj->ssel_pol[ssel_idx]) ? !ssel_activate : ssel_activate;
cyhal_gpio_write(obj->pin_ssel[ssel_idx], ssel_state);
}
}
cy_rslt_t cyhal_spi_set_frequency(cyhal_spi_t *obj, uint32_t hz)
{
cy_rslt_t result = CY_RSLT_SUCCESS;
uint8_t ovr_sample_val;
if (NULL == obj)
{
return CYHAL_SPI_RSLT_BAD_ARGUMENT;
}
Cy_SCB_SPI_Disable(obj->base, &obj->context);
result = _cyhal_spi_int_frequency(obj, hz, &ovr_sample_val);
/* No need to reconfigure slave since oversample value, that was changed in _cyhal_spi_int_frequency, in slave is ignored */
if ((CY_RSLT_SUCCESS == result) && !obj->is_slave && (obj->oversample_value != ovr_sample_val))
{
cy_stc_scb_spi_config_t config_structure = _cyhal_spi_default_config;
Cy_SCB_SPI_DeInit(obj->base);
config_structure.spiMode = obj->is_slave == false
? CY_SCB_SPI_MASTER
: CY_SCB_SPI_SLAVE;
config_structure.enableMsbFirst = obj->msb_first;
config_structure.sclkMode = obj->clk_mode;
config_structure.rxDataWidth = obj->data_bits;
config_structure.txDataWidth = obj->data_bits;
config_structure.oversample = ovr_sample_val;
obj->oversample_value = ovr_sample_val;
Cy_SCB_SPI_Init(obj->base, &config_structure, &(obj->context));
}
Cy_SCB_SPI_Enable(obj->base);
return result;
}
cy_rslt_t cyhal_spi_select_active_ssel(cyhal_spi_t *obj, cyhal_gpio_t ssel)
{
CY_ASSERT(NULL != obj);
CY_ASSERT(NULL != obj->base);
if ((NC != ssel) && (_CYHAL_SPI_PENDING_NONE == obj->pending))
{
for (uint8_t i = 0; i < _CYHAL_SPI_SSEL_NUM; i++)
{
if(obj->pin_ssel[i] == ssel)
{
Cy_SCB_SPI_SetActiveSlaveSelect(obj->base, (cy_en_scb_spi_slave_select_t)i);
obj->active_ssel = i;
return CY_RSLT_SUCCESS;
}
}
}
return CYHAL_SPI_RSLT_ERR_CANNOT_SWITCH_SSEL;
}
static inline cy_en_scb_spi_polarity_t _cyhal_spi_pol_from_hal_to_pdl(cyhal_spi_ssel_polarity_t hal_polarity)
{
return (hal_polarity == CYHAL_SPI_SSEL_ACTIVE_HIGH) ? CY_SCB_SPI_ACTIVE_HIGH : CY_SCB_SPI_ACTIVE_LOW;
}
cy_rslt_t cyhal_spi_slave_select_config(cyhal_spi_t *obj, cyhal_gpio_t ssel, cyhal_spi_ssel_polarity_t polarity)
{
CY_ASSERT(NULL != obj);
cy_rslt_t result = CYHAL_SPI_RSLT_ERR_CANNOT_CONFIG_SSEL;
uint8_t found_idx = 0;
bool configuring_existing = false;
if ((NC != ssel) && (_CYHAL_SPI_PENDING_NONE == obj->pending))
{
for (uint8_t i = 0; i < _CYHAL_SPI_SSEL_NUM; i++)
{
if ((configuring_existing = (ssel == obj->pin_ssel[i])))
{
result = CY_RSLT_SUCCESS;
found_idx = i;
break;
}
if (!obj->is_slave)
{
/* Looking for first available ssel slot */
if ((NC == obj->pin_ssel[i]))
{
result = cyhal_gpio_init(ssel, CYHAL_GPIO_DIR_OUTPUT, CYHAL_GPIO_DRIVE_STRONG,
(polarity == CYHAL_SPI_SSEL_ACTIVE_LOW) ? true : false);
found_idx = i;
break;
}
}
}
if (!configuring_existing && (obj->is_slave))
{
const cyhal_resource_pin_mapping_t *ssel_map = NULL;
if (CY_RSLT_SUCCESS == _cyhal_spi_get_ssel_map_idx(obj, ssel, &ssel_map, &found_idx))
{
/* Either mosi or miso should present. Will take one of them as instance SCB instance index source */
const cyhal_resource_pin_mapping_t *data_pin_map = (NC != obj->pin_mosi) ?
_CYHAL_SCB_FIND_MAP_BLOCK(obj->pin_mosi, cyhal_pin_map_scb_spi_s_mosi, &(obj->resource)) :
_CYHAL_SCB_FIND_MAP_BLOCK(obj->pin_miso, cyhal_pin_map_scb_spi_s_miso, &(obj->resource));
if ((NULL != ssel_map) && (NC == obj->pin_ssel[found_idx]) &&
_cyhal_utils_resources_equal(data_pin_map->inst, ssel_map->inst))
{
result = _cyhal_utils_reserve_and_connect(ssel, ssel_map);
}
}
}
if (CY_RSLT_SUCCESS == result)
{
if (!configuring_existing)
obj->pin_ssel[found_idx] = ssel;
obj->ssel_pol[found_idx] = _cyhal_spi_pol_from_hal_to_pdl(polarity);
/* Immediatelly apply updated slave select polarity */
Cy_SCB_SPI_SetActiveSlaveSelectPolarity(obj->base, (cy_en_scb_spi_slave_select_t)found_idx, obj->ssel_pol[found_idx]);
if (!obj->is_slave)
_cyhal_ssel_switch_state(obj, found_idx, _CYHAL_SPI_SSEL_DEACTIVATE);
}
}
return result;
}
cy_rslt_t cyhal_spi_recv(cyhal_spi_t *obj, uint32_t *value)
{
if (NULL == obj)
return CYHAL_SPI_RSLT_BAD_ARGUMENT;
if (_cyhal_scb_pm_transition_pending())
return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
uint32_t read_value = CY_SCB_SPI_RX_NO_DATA;
const uint32_t fill_in = 0x0000ffffUL; /* PDL Fill in value */
uint32_t count = 0;
if ((obj->is_slave) && (CYHAL_NC_PIN_VALUE == obj->pin_mosi))
{
return CYHAL_SPI_RSLT_INVALID_PIN_API_NOT_SUPPORTED;
}
if ((!obj->is_slave) && (CYHAL_NC_PIN_VALUE == obj->pin_miso))
{
return CYHAL_SPI_RSLT_INVALID_PIN_API_NOT_SUPPORTED;
}
if (!obj->is_slave)
{
_cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_ACTIVATE);
/* Clear FIFOs */
Cy_SCB_SPI_ClearTxFifo(obj->base);
Cy_SCB_SPI_ClearRxFifo(obj->base);
while (count == 0)
{
count = Cy_SCB_SPI_Write(obj->base, fill_in);
}
while (Cy_SCB_SPI_IsTxComplete(obj->base) == false) { }
while (Cy_SCB_SPI_GetNumInRxFifo(obj->base) == 0) { } /* Wait for RX FIFO not empty */
_cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_DEACTIVATE);
}
while (read_value == CY_SCB_SPI_RX_NO_DATA)
{
read_value = Cy_SCB_SPI_Read(obj->base);
}
*value = read_value;
return CY_RSLT_SUCCESS;
}
cy_rslt_t cyhal_spi_send(cyhal_spi_t *obj, uint32_t value)
{
if (NULL == obj)
return CYHAL_SPI_RSLT_BAD_ARGUMENT;
if (_cyhal_scb_pm_transition_pending())
return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
uint32_t count = 0;
cy_rslt_t result = CY_RSLT_SUCCESS;
if ((obj->is_slave) && (CYHAL_NC_PIN_VALUE == obj->pin_miso))
{
return CYHAL_SPI_RSLT_INVALID_PIN_API_NOT_SUPPORTED;
}
if ((!obj->is_slave) && (CYHAL_NC_PIN_VALUE == obj->pin_mosi))
{
return CYHAL_SPI_RSLT_INVALID_PIN_API_NOT_SUPPORTED;
}
if (!obj->is_slave)
{
_cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_ACTIVATE);
/* Clear FIFOs */
Cy_SCB_SPI_ClearTxFifo(obj->base);
Cy_SCB_SPI_ClearRxFifo(obj->base);
}
while (count == 0)
{
count = Cy_SCB_SPI_Write(obj->base, value);
}
if (!obj->is_slave)
{
while (Cy_SCB_SPI_IsTxComplete(obj->base) == false) { }
while (Cy_SCB_SPI_GetNumInRxFifo(obj->base) == 0) { } /* Wait for RX FIFO not empty */
_cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_DEACTIVATE);
(void)Cy_SCB_SPI_Read(obj->base);
}
return result;
}
cy_rslt_t cyhal_spi_transfer(cyhal_spi_t *obj, const uint8_t *tx, size_t tx_length, uint8_t *rx, size_t rx_length, uint8_t write_fill)
{
if (NULL == obj)
return CYHAL_SPI_RSLT_BAD_ARGUMENT;
if (_cyhal_scb_pm_transition_pending())
return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
obj->write_fill = write_fill;
cy_rslt_t rslt = cyhal_spi_transfer_async(obj, tx, tx_length, rx, rx_length);
if (rslt == CY_RSLT_SUCCESS)
{
while (obj->pending != _CYHAL_SPI_PENDING_NONE) { } /* Wait for async transfer to complete */
}
obj->write_fill = (uint8_t) CY_SCB_SPI_DEFAULT_TX;
return rslt;
}
cy_rslt_t cyhal_spi_transfer_async(cyhal_spi_t *obj, const uint8_t *tx, size_t tx_length, uint8_t *rx, size_t rx_length)
{
if (NULL == obj)
return CYHAL_SPI_RSLT_BAD_ARGUMENT;
if (_cyhal_scb_pm_transition_pending())
return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
cy_en_scb_spi_status_t spi_status;
if ((CYHAL_NC_PIN_VALUE == obj->pin_mosi) || (CYHAL_NC_PIN_VALUE == obj->pin_miso))
{
return CYHAL_SPI_RSLT_INVALID_PIN_API_NOT_SUPPORTED;
}
_cyhal_ssel_switch_state(obj, obj->active_ssel, _CYHAL_SPI_SSEL_ACTIVATE);
obj->is_async = true;
/* Setup transfer */
if (tx_length > rx_length)
{
if (rx_length > 0)
{
/* I) write + read, II) write only */
obj->pending = _CYHAL_SPI_PENDING_TX_RX;
obj->rx_buffer = NULL;
obj->tx_buffer = tx + (obj->data_bits <= 8 ? rx_length : (rx_length * 2));
obj->tx_buffer_size = tx_length - rx_length;
tx_length = rx_length; // Use tx_length to store entire transfer length
}
else
{
/* I) write only */
obj->pending = _CYHAL_SPI_PENDING_TX;
obj->rx_buffer = NULL;
obj->tx_buffer = NULL;
rx = NULL;
}
}
else if (rx_length > tx_length)
{
if (tx_length > 0)
{
/* I) write + read, II) read only */
obj->pending = _CYHAL_SPI_PENDING_TX_RX;
obj->tx_buffer = NULL;
obj->rx_buffer = rx + (obj->data_bits <= 8 ? tx_length : (tx_length * 2));
obj->rx_buffer_size = rx_length - tx_length;
}
else
{
/* I) read only. */
obj->pending = _CYHAL_SPI_PENDING_RX;
obj->tx_buffer = NULL;
obj->rx_buffer = rx_length > 1 ? rx + 1 : NULL;
obj->rx_buffer_size = rx_length - 1;
tx = &obj->write_fill;
tx_length = 1;
}
}
else
{
/* RX and TX of the same size: I) write + read. */
obj->pending = _CYHAL_SPI_PENDING_TX_RX;
obj->rx_buffer = NULL;
obj->tx_buffer = NULL;
}
spi_status = Cy_SCB_SPI_Transfer(obj->base, (void *)tx, rx, tx_length, &obj->context);
return spi_status == CY_SCB_SPI_SUCCESS
? CY_RSLT_SUCCESS
: CYHAL_SPI_RSLT_TRANSFER_ERROR;
}
bool cyhal_spi_is_busy(cyhal_spi_t *obj)
{
return Cy_SCB_SPI_IsBusBusy(obj->base) || (_CYHAL_SPI_PENDING_NONE != obj->pending);
}
cy_rslt_t cyhal_spi_abort_async(cyhal_spi_t *obj)
{
if (NULL == obj)
{
return CYHAL_SPI_RSLT_BAD_ARGUMENT;
}
Cy_SCB_SPI_AbortTransfer(obj->base, &(obj->context));
obj->pending = _CYHAL_SPI_PENDING_NONE;
return CY_RSLT_SUCCESS;
}
void cyhal_spi_register_callback(cyhal_spi_t *obj, cyhal_spi_event_callback_t callback, void *callback_arg)
{
uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
obj->callback_data.callback = (cy_israddress) callback;
obj->callback_data.callback_arg = callback_arg;
cyhal_system_critical_section_exit(savedIntrStatus);
Cy_SCB_SPI_RegisterCallback(obj->base, _cyhal_spi_cb_wrapper, &(obj->context));
obj->irq_cause = 0;
}
void cyhal_spi_enable_event(cyhal_spi_t *obj, cyhal_spi_event_t event, uint8_t intr_priority, bool enable)
{
if (enable)
{
obj->irq_cause |= event;
}
else
{
obj->irq_cause &= ~event;
}
IRQn_Type irqn = _CYHAL_SCB_IRQ_N[obj->resource.block_num];
NVIC_SetPriority(irqn, intr_priority);
}
cy_rslt_t cyhal_spi_set_fifo_level(cyhal_spi_t *obj, cyhal_spi_fifo_type_t type, uint16_t level)
{
return _cyhal_scb_set_fifo_level(obj->base, (cyhal_scb_fifo_type_t)type, level);
}
cy_rslt_t cyhal_spi_enable_output(cyhal_spi_t *obj, cyhal_spi_output_t output, cyhal_source_t *source)
{
return _cyhal_scb_enable_output(obj->base, obj->resource, (cyhal_scb_output_t)output, source);
}
cy_rslt_t cyhal_spi_disable_output(cyhal_spi_t *obj, cyhal_spi_output_t output)
{
return _cyhal_scb_disable_output(obj->base, obj->resource, (cyhal_scb_output_t)output);
}
#if defined(__cplusplus)
}
#endif
#endif /* CY_IP_MXSCB */