Skip to content

Commit c7a0e45

Browse files
committed
Merge branch 'feat/spi_master_dynamic_freq' into 'master'
feat(spi_master): add transaction config for device multi freq point Closes IDFGH-12774 See merge request espressif/esp-idf!28579
2 parents ee4cf1b + 39c8526 commit c7a0e45

File tree

20 files changed

+211
-128
lines changed

20 files changed

+211
-128
lines changed

components/esp_driver_spi/include/driver/spi_master.h

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ struct spi_transaction_t {
137137
*/
138138
size_t length; ///< Total data length, in bits
139139
size_t rxlength; ///< Total data length received, should be not greater than ``length`` in full-duplex mode (0 defaults this to the value of ``length``).
140+
uint32_t override_freq_hz; ///< New freq speed value will override for current device, remain `0` to skip update. Need about 30us each time
140141
void *user; ///< User-defined variable. Can be used to store eg transaction ID.
141142
union {
142143
const void *tx_buffer; ///< Pointer to transmit buffer, or NULL for no MOSI phase
@@ -205,7 +206,7 @@ esp_err_t spi_bus_remove_device(spi_device_handle_t handle);
205206
* @note Normally a device cannot start (queue) polling and interrupt
206207
* transactions simultaneously.
207208
*
208-
* @param handle Device handle obtained using spi_host_add_dev
209+
* @param handle Device handle obtained using spi_bus_add_device()
209210
* @param trans_desc Description of transaction to execute
210211
* @param ticks_to_wait Ticks to wait until there's room in the queue; use portMAX_DELAY to
211212
* never time out.
@@ -228,7 +229,7 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *
228229
* completed transaction so software can inspect the result and e.g. free the memory or
229230
* reuse the buffers.
230231
*
231-
* @param handle Device handle obtained using spi_host_add_dev
232+
* @param handle Device handle obtained using spi_bus_add_device()
232233
* @param trans_desc Pointer to variable able to contain a pointer to the description of the transaction
233234
that is executed. The descriptor should not be modified until the descriptor is returned by
234235
spi_device_get_trans_result.
@@ -252,7 +253,7 @@ esp_err_t spi_device_get_trans_result(spi_device_handle_t handle, spi_transactio
252253
* Normally a device cannot start (queue) polling and interrupt
253254
* transactions simutanuously.
254255
*
255-
* @param handle Device handle obtained using spi_host_add_dev
256+
* @param handle Device handle obtained using spi_bus_add_device()
256257
* @param trans_desc Description of transaction to execute
257258
* @return
258259
* - ESP_ERR_INVALID_ARG if parameter is invalid
@@ -267,7 +268,7 @@ esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *tra
267268
* transactions simutanuously. Moreover, a device cannot start a new polling
268269
* transaction if another polling transaction is not finished.
269270
*
270-
* @param handle Device handle obtained using spi_host_add_dev
271+
* @param handle Device handle obtained using spi_bus_add_device()
271272
* @param trans_desc Description of transaction to execute
272273
* @param ticks_to_wait Ticks to wait until there's room in the queue;
273274
* currently only portMAX_DELAY is supported.
@@ -290,7 +291,7 @@ esp_err_t spi_device_polling_start(spi_device_handle_t handle, spi_transaction_t
290291
* successfully completed. The task is not blocked, but actively busy-spins for
291292
* the transaction to be completed.
292293
*
293-
* @param handle Device handle obtained using spi_host_add_dev
294+
* @param handle Device handle obtained using spi_bus_add_device()
294295
* @param ticks_to_wait Ticks to wait until there's a returned item; use portMAX_DELAY to never time
295296
out.
296297
* @return
@@ -310,7 +311,7 @@ esp_err_t spi_device_polling_end(spi_device_handle_t handle, TickType_t ticks_to
310311
* Normally a device cannot start (queue) polling and interrupt
311312
* transactions simutanuously.
312313
*
313-
* @param handle Device handle obtained using spi_host_add_dev
314+
* @param handle Device handle obtained using spi_bus_add_device()
314315
* @param trans_desc Description of transaction to execute
315316
* @return
316317
* - ESP_ERR_INVALID_ARG if parameter is invalid
@@ -368,19 +369,19 @@ esp_err_t spi_device_get_actual_freq(spi_device_handle_t handle, int *freq_khz);
368369
int spi_get_actual_clock(int fapb, int hz, int duty_cycle) __attribute__((deprecated("Please use spi_device_get_actual_freq instead")));
369370

370371
/**
371-
* @brief Calculate the timing settings of specified frequency and settings.
372-
*
373-
* @param gpio_is_used True if using GPIO matrix, or False if iomux pins are used.
374-
* @param input_delay_ns Input delay from SCLK launch edge to MISO data valid.
375-
* @param eff_clk Effective clock frequency (in Hz) from `spi_get_actual_clock()`.
376-
* @param dummy_o Address of dummy bits used output. Set to NULL if not needed.
377-
* @param cycles_remain_o Address of cycles remaining (after dummy bits are used) output.
378-
* - -1 If too many cycles remaining, suggest to compensate half a clock.
379-
* - 0 If no remaining cycles or dummy bits are not used.
380-
* - positive value: cycles suggest to compensate.
381-
*
382-
* @note If **dummy_o* is not zero, it means dummy bits should be applied in half duplex mode, and full duplex mode may not work.
383-
*/
372+
* @brief Calculate the timing settings of specified frequency and settings.
373+
*
374+
* @param gpio_is_used True if using GPIO matrix, or False if iomux pins are used.
375+
* @param input_delay_ns Input delay from SCLK launch edge to MISO data valid.
376+
* @param eff_clk Effective clock frequency (in Hz) from `spi_get_actual_clock()`.
377+
* @param dummy_o Address of dummy bits used output. Set to NULL if not needed.
378+
* @param cycles_remain_o Address of cycles remaining (after dummy bits are used) output.
379+
* - -1 If too many cycles remaining, suggest to compensate half a clock.
380+
* - 0 If no remaining cycles or dummy bits are not used.
381+
* - positive value: cycles suggest to compensate.
382+
*
383+
* @note If **dummy_o* is not zero, it means dummy bits should be applied in half duplex mode, and full duplex mode may not work.
384+
*/
384385
void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int *dummy_o, int *cycles_remain_o);
385386

386387
/**

components/esp_driver_spi/src/gpspi/spi_master.c

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ esp_err_t spi_bus_add_device(spi_host_device_t host_id, const spi_device_interfa
487487
SPI_CHECK(ret == ESP_OK, "assigned clock speed not supported", ret);
488488
temp_timing_conf.clock_source = clk_src;
489489
temp_timing_conf.source_pre_div = clock_source_div;
490+
temp_timing_conf.source_real_freq = clock_source_hz;
490491
temp_timing_conf.rx_sample_point = dev_config->sample_point;
491492
if (temp_timing_conf.rx_sample_point == SPI_SAMPLING_POINT_PHASE_1) {
492493
SPI_CHECK(spi_ll_master_is_rx_std_sample_supported(), "SPI_SAMPLING_POINT_PHASE_1 is not supported on this chip", ESP_ERR_NOT_SUPPORTED);
@@ -635,14 +636,33 @@ int spi_get_actual_clock(int fapb, int hz, int duty_cycle)
635636

636637
// Setup the device-specified configuration registers. Called every time a new
637638
// transaction is to be sent, but only apply new configurations when the device
638-
// changes.
639-
static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev)
639+
// changes or timing change is required.
640+
static SPI_MASTER_ISR_ATTR void spi_setup_device(spi_device_t *dev, spi_trans_priv_t *trans_buf)
640641
{
641642
spi_bus_lock_dev_handle_t dev_lock = dev->dev_lock;
642643
spi_hal_context_t *hal = &dev->host->hal;
643644
spi_hal_dev_config_t *hal_dev = &(dev->hal_dev);
644645

645-
if (spi_bus_lock_touch(dev_lock)) {
646+
bool clock_changed = false;
647+
// check if timing config update is required
648+
if (trans_buf && (trans_buf->trans->override_freq_hz > 0) && (hal_dev->timing_conf.expect_freq != trans_buf->trans->override_freq_hz)) {
649+
spi_hal_timing_param_t timing_param = {
650+
.expected_freq = trans_buf->trans->override_freq_hz,
651+
.clk_src_hz = dev->hal_dev.timing_conf.source_real_freq,
652+
.duty_cycle = dev->cfg.duty_cycle_pos,
653+
.input_delay_ns = dev->cfg.input_delay_ns,
654+
.half_duplex = dev->hal_dev.half_duplex,
655+
.use_gpio = !(dev->host->bus_attr->flags | SPICOMMON_BUSFLAG_IOMUX_PINS),
656+
};
657+
658+
if (ESP_OK == spi_hal_cal_clock_conf(&timing_param, &dev->hal_dev.timing_conf)) {
659+
clock_changed = true;
660+
} else {
661+
ESP_EARLY_LOGW(SPI_TAG, "assigned clock speed %d not supported", trans_buf->trans->override_freq_hz);
662+
}
663+
}
664+
665+
if (spi_bus_lock_touch(dev_lock) || clock_changed) {
646666
/* Configuration has not been applied yet. */
647667
spi_hal_setup_device(hal, hal_dev);
648668
SPI_MASTER_PERI_CLOCK_ATOMIC() {
@@ -790,7 +810,7 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_
790810
dev->host->cur_cs = dev->id;
791811

792812
//Reconfigure according to device settings, the function only has effect when the dev_id is changed.
793-
spi_setup_device(dev);
813+
spi_setup_device(dev, trans_buf);
794814

795815
//set the transaction specific configuration each time before a transaction setup
796816
spi_hal_trans_config_t hal_trans = {};
@@ -865,7 +885,7 @@ static void SPI_MASTER_ISR_ATTR spi_new_sct_trans(spi_device_t *dev, spi_sct_tra
865885
dev->host->cur_cs = dev->id;
866886

867887
//Reconfigure according to device settings, the function only has effect when the dev_id is changed.
868-
spi_setup_device(dev);
888+
spi_setup_device(dev, NULL);
869889

870890
#if !CONFIG_IDF_TARGET_ESP32S2
871891
// s2 update this seg_gap_clock_len by dma from conf_buffer
@@ -1319,7 +1339,7 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_acquire_bus(spi_device_t *device, TickT
13191339
esp_pm_lock_acquire(host->bus_attr->pm_lock);
13201340
#endif
13211341
//configure the device ahead so that we don't need to do it again in the following transactions
1322-
spi_setup_device(host->device[device->id]);
1342+
spi_setup_device(host->device[device->id], NULL);
13231343
//the DMA is also occupied by the device, all the slave devices that using DMA should wait until bus released.
13241344

13251345
#if CONFIG_IDF_TARGET_ESP32

components/esp_driver_spi/test_apps/components/spi_bench_mark/include/spi_performance.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,21 @@
5454
#endif
5555

5656
#elif CONFIG_IDF_TARGET_ESP32C6
57-
#define IDF_PERFORMANCE_MAX_SPI_CLK_FREQ 26*1000*1000
57+
#define IDF_PERFORMANCE_MAX_SPI_CLK_FREQ 26666*1000
5858
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING 35 //TODO: IDF-9551, check perform
5959
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING 17
6060
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING_NO_DMA 32
6161
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING_NO_DMA 15
6262

6363
#elif CONFIG_IDF_TARGET_ESP32H2
64-
#define IDF_PERFORMANCE_MAX_SPI_CLK_FREQ 26*1000*1000
64+
#define IDF_PERFORMANCE_MAX_SPI_CLK_FREQ 24*1000*1000
6565
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING 32
6666
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING_NO_DMA 25
6767
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING 61
6868
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING_NO_DMA 54
6969

7070
#elif CONFIG_IDF_TARGET_ESP32P4
71-
#define IDF_PERFORMANCE_MAX_SPI_CLK_FREQ 26*1000*1000
71+
#define IDF_PERFORMANCE_MAX_SPI_CLK_FREQ 20*1000*1000
7272
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING 44
7373
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_POLLING 28
7474
#define IDF_PERFORMANCE_MAX_SPI_PER_TRANS_NO_POLLING_NO_DMA 26

components/esp_driver_spi/test_apps/master/main/test_spi_master.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,44 @@ TEST_CASE("SPI Master clk_source and divider accuracy", "[spi]")
181181
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
182182
}
183183

184+
TEST_CASE("test_device_dynamic_freq_update", "[spi]")
185+
{
186+
spi_device_handle_t dev0;
187+
int master_send;
188+
189+
spi_bus_config_t buscfg = SPI_BUS_TEST_DEFAULT_CONFIG();
190+
spi_device_interface_config_t devcfg = SPI_DEVICE_TEST_DEFAULT_CONFIG();
191+
devcfg.flags |= SPI_DEVICE_HALFDUPLEX;
192+
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
193+
TEST_ESP_OK(spi_bus_add_device(TEST_SPI_HOST, &devcfg, &dev0));
194+
195+
spi_transaction_t trans_cfg = {
196+
.tx_buffer = &master_send,
197+
.length = sizeof(master_send) * 8,
198+
};
199+
200+
trans_cfg.override_freq_hz = IDF_PERFORMANCE_MAX_SPI_CLK_FREQ;
201+
for (int i = 1; i < 15; i++) {
202+
TEST_ESP_OK(spi_device_transmit(dev0, &trans_cfg));
203+
spi_device_get_actual_freq(dev0, &master_send);
204+
printf("override %5ld k real freq %5d k\n", trans_cfg.override_freq_hz / 1000, master_send);
205+
if (trans_cfg.override_freq_hz) {
206+
TEST_ASSERT_EQUAL_UINT32(trans_cfg.override_freq_hz / 1000, master_send);
207+
}
208+
if (i > 10) {
209+
//test without override freq
210+
trans_cfg.override_freq_hz = 0;
211+
continue;
212+
}
213+
if (!(i % 2)) {
214+
//test override freq every 2 trans
215+
trans_cfg.override_freq_hz /= 2;
216+
}
217+
}
218+
TEST_ESP_OK(spi_bus_remove_device(dev0));
219+
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST));
220+
}
221+
184222
static spi_device_handle_t setup_spi_bus_loopback(int clkspeed, bool dma)
185223
{
186224
spi_bus_config_t buscfg = {

components/hal/esp32/include/hal/spi_ll.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_cl
652652
*
653653
* @return Frequency of given dividers.
654654
*/
655+
__attribute__((always_inline))
655656
static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
656657
{
657658
return (fapb / (pre * n));
@@ -667,6 +668,7 @@ static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
667668
*
668669
* @return Actual (nearest) frequency.
669670
*/
671+
__attribute__((always_inline))
670672
static inline int spi_ll_master_cal_clock(int fapb, int hz, int duty_cycle, spi_ll_clock_val_t *out_reg)
671673
{
672674
typeof(SPI1.clock) reg = {.val = 0};

components/hal/esp32c2/include/hal/spi_ll.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,7 @@ static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_cl
735735
*
736736
* @return Frequency of given dividers.
737737
*/
738+
__attribute__((always_inline))
738739
static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
739740
{
740741
return (fapb / (pre * n));
@@ -750,6 +751,7 @@ static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
750751
*
751752
* @return Actual (nearest) frequency.
752753
*/
754+
__attribute__((always_inline))
753755
static inline int spi_ll_master_cal_clock(int fapb, int hz, int duty_cycle, spi_ll_clock_val_t *out_reg)
754756
{
755757
typeof(GPSPI2.clock) reg = {.val = 0};

components/hal/esp32c3/include/hal/spi_ll.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,7 @@ static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_cl
737737
*
738738
* @return Frequency of given dividers.
739739
*/
740+
__attribute__((always_inline))
740741
static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
741742
{
742743
return (fapb / (pre * n));
@@ -752,6 +753,7 @@ static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
752753
*
753754
* @return Actual (nearest) frequency.
754755
*/
756+
__attribute__((always_inline))
755757
static inline int spi_ll_master_cal_clock(int fapb, int hz, int duty_cycle, spi_ll_clock_val_t *out_reg)
756758
{
757759
typeof(GPSPI2.clock) reg = {.val = 0};

components/hal/esp32c5/include/hal/spi_ll.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,7 @@ static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_cl
735735
*
736736
* @return Frequency of given dividers.
737737
*/
738+
__attribute__((always_inline))
738739
static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
739740
{
740741
return (fapb / (pre * n));
@@ -750,6 +751,7 @@ static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
750751
*
751752
* @return Actual (nearest) frequency.
752753
*/
754+
__attribute__((always_inline))
753755
static inline int spi_ll_master_cal_clock(int fapb, int hz, int duty_cycle, spi_ll_clock_val_t *out_reg)
754756
{
755757
typeof(GPSPI2.clock) reg = {.val = 0};

components/hal/esp32c6/include/hal/spi_ll.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,7 @@ static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_cl
728728
*
729729
* @return Frequency of given dividers.
730730
*/
731+
__attribute__((always_inline))
731732
static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
732733
{
733734
return (fapb / (pre * n));
@@ -743,6 +744,7 @@ static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
743744
*
744745
* @return Actual (nearest) frequency.
745746
*/
747+
__attribute__((always_inline))
746748
static inline int spi_ll_master_cal_clock(int fapb, int hz, int duty_cycle, spi_ll_clock_val_t *out_reg)
747749
{
748750
typeof(GPSPI2.clock) reg = {.val = 0};

components/hal/esp32c61/include/hal/spi_ll.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,7 @@ static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, const spi_ll_cl
748748
*
749749
* @return Frequency of given dividers.
750750
*/
751+
__attribute__((always_inline))
751752
static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
752753
{
753754
return (fapb / (pre * n));
@@ -763,6 +764,7 @@ static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
763764
*
764765
* @return Actual (nearest) frequency.
765766
*/
767+
__attribute__((always_inline))
766768
static inline int spi_ll_master_cal_clock(int fapb, int hz, int duty_cycle, spi_ll_clock_val_t *out_reg)
767769
{
768770
typeof(GPSPI2.clock) reg = {.val = 0};

0 commit comments

Comments
 (0)