1
6
7
8
9
15
16
17
18
19
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
50
51
59
60
61
62
63
64
65
66
67
68
69
70
78
79
80
81
82
83
84
85
86
87
88
89
90
91
99
100
101
102
105
106
107
108
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
129
130
131
132
133
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
163
164
165
166
/* ... */
#include "hal/spi_hal.h"
#include "hal/log.h"
#include "hal/assert.h"
#include "hal/gpio_ll.h"
#include "soc/soc_caps.h"
#include "soc/clk_tree_defs.h"6 includes
static const __attribute__((unused)) char SPI_HAL_TAG[] = "spi_hal";
#define SPI_HAL_CHECK(a, str, ret_val, ...) \
if (!(a)) { \
HAL_LOGE(SPI_HAL_TAG,"%s(%d): "str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
return (ret_val); \
}{...}
...
void spi_hal_init(spi_hal_context_t *hal, uint32_t host_id)
{
memset(hal, 0, sizeof(spi_hal_context_t));
spi_dev_t *hw = SPI_LL_GET_HW(host_id);
hal->hw = hw;
spi_ll_master_init(hw);
spi_ll_enable_int(hw);
spi_ll_set_int_stat(hw);
spi_ll_set_mosi_delay(hw, 0, 0);
spi_ll_apply_config(hw);
}{ ... }
void spi_hal_config_io_default_level(spi_hal_context_t *hal, bool level)
{
#if SPI_LL_MOSI_FREE_LEVEL
spi_ll_set_mosi_free_level(hal->hw, level);
spi_ll_apply_config(hal->hw);/* ... */
#endif
}{ ... }
void spi_hal_deinit(spi_hal_context_t *hal)
{
spi_dev_t *hw = hal->hw;
if (hw) {
spi_ll_disable_int(hw);
spi_ll_clear_int_stat(hw);
}{...}
}{ ... }
#if SOC_SPI_SCT_SUPPORTED
void spi_hal_sct_init(spi_hal_context_t *hal)
{
spi_ll_conf_state_enable(hal->hw, true);
spi_ll_set_magic_number(hal->hw, SPI_LL_SCT_MAGIC_NUMBER);
spi_ll_disable_int(hal->hw);
spi_ll_enable_intr(hal->hw, SPI_LL_INTR_SEG_DONE);
spi_ll_set_intr(hal->hw, SPI_LL_INTR_SEG_DONE);
}{...}
void spi_hal_sct_deinit(spi_hal_context_t *hal)
{
spi_ll_conf_state_enable(hal->hw, false);
spi_ll_disable_intr(hal->hw, SPI_LL_INTR_SEG_DONE);
spi_ll_clear_intr(hal->hw, SPI_LL_INTR_SEG_DONE);
spi_ll_clear_int_stat(hal->hw);
spi_ll_enable_int(hal->hw);
}{...}
/* ... */#endif
esp_err_t spi_hal_cal_clock_conf(const spi_hal_timing_param_t *timing_param, spi_hal_timing_conf_t *timing_conf)
{
spi_hal_timing_conf_t temp_conf = {};
int eff_clk_n = spi_ll_master_cal_clock(timing_param->clk_src_hz, timing_param->expected_freq, timing_param->duty_cycle, &temp_conf.clock_reg);
spi_hal_cal_timing(timing_param->clk_src_hz, eff_clk_n, timing_param->use_gpio, timing_param->input_delay_ns, &temp_conf.timing_dummy, &temp_conf.timing_miso_delay);
#ifdef CONFIG_IDF_TARGET_ESP32
const int freq_limit = spi_hal_get_freq_limit(timing_param->use_gpio, timing_param->input_delay_ns);
SPI_HAL_CHECK(timing_param->half_duplex || temp_conf.timing_dummy == 0 || timing_param->no_compensate,
"When work in full-duplex mode at frequency > %.1fMHz, device cannot read correct data.\n\
Try to use IOMUX pins to increase the frequency limit, or use the half duplex mode.\n\
Please note the SPI master can only work at divisors of 80MHz, and the driver always tries to find the closest frequency to your configuration.\n\
Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output data at higher speed, or read data at your own risk.",
ESP_ERR_NOT_SUPPORTED, freq_limit / 1000. / 1000 );/* ... */
#endif
temp_conf.real_freq = eff_clk_n;
if (timing_conf) {
*timing_conf = temp_conf;
}{...}
return ESP_OK;
}{ ... }
int spi_hal_master_cal_clock(int fapb, int hz, int duty_cycle)
{
return spi_ll_master_cal_clock(fapb, hz, duty_cycle, NULL);
}{ ... }
void spi_hal_cal_timing(int source_freq_hz, int eff_clk, bool gpio_is_used, int input_delay_ns, int *dummy_n, int *miso_delay_n)
{
const int apbclk_kHz = source_freq_hz / 1000;
const int spiclk_apb_n = source_freq_hz / eff_clk;
int gpio_delay_ns = 0;
#if GPIO_LL_MATRIX_DELAY_NS
gpio_delay_ns = gpio_is_used ? GPIO_LL_MATRIX_DELAY_NS : 0;
#endif
int delay_apb_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000;
if (delay_apb_n < 0) {
delay_apb_n = 0;
}{...}
int dummy_required = delay_apb_n / spiclk_apb_n;
int miso_delay = 0;
if (dummy_required > 0) {
miso_delay = (dummy_required + 1) * spiclk_apb_n - delay_apb_n - 1;
}{...} else {
if (delay_apb_n * 4 <= spiclk_apb_n) {
miso_delay = -1;
}{...}
}{...}
*dummy_n = dummy_required;
*miso_delay_n = miso_delay;
HAL_LOGD(SPI_HAL_TAG, "eff: %d, limit: %dk(/%d), %d dummy, %d delay", eff_clk / 1000, apbclk_kHz / (delay_apb_n + 1), delay_apb_n, dummy_required, miso_delay);
}{ ... }
#ifdef CONFIG_IDF_TARGET_ESP32
int spi_hal_get_freq_limit(bool gpio_is_used, int input_delay_ns)
{
const int apbclk_kHz = APB_CLK_FREQ / 1000;
int gpio_delay_ns = 0;
#if GPIO_LL_MATRIX_DELAY_NS
gpio_delay_ns = gpio_is_used ? GPIO_LL_MATRIX_DELAY_NS : 0;
#endif
int delay_apb_n = (1 + input_delay_ns + gpio_delay_ns) * apbclk_kHz / 1000 / 1000;
if (delay_apb_n < 0) {
delay_apb_n = 0;
}{...}
return APB_CLK_FREQ / (delay_apb_n + 1);
}{ ... }
#endif/* ... */