当前位置: 首页 > news >正文

ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(核心API详解之单个外设管理)

目录

  • 单个外设管理API
    • esp_periph_create
    • esp_periph_set_function
    • esp_periph_start
    • esp_periph_stop
    • esp_periph_set_data
    • esp_periph_get_data
    • esp_periph_get_state
    • esp_periph_get_id
    • esp_periph_set_id
    • esp_periph_init
    • esp_periph_run
    • esp_periph_destroy

单个外设管理API

esp_periph_create

功能描述:创建一个新的外设实例。

函数原型

/*** @brief      创建一个新的外设实例* * @details    分配内存并初始化一个新的外设结构。这个函数仅创建外设的基本结构,*             不会设置外设的功能函数。要完成外设的创建,还需要调用 esp_periph_set_function 设置*             外设的初始化、运行和销毁函数,然后调用 esp_periph_start 将外设添加到外设集合中。* * @param[in]  periph_id  外设 ID,可以是预定义的 ID 或通过 esp_periph_alloc_periph_id 分配的 ID* @param[in]  tag        外设标签,用于调试和识别外设,如果为 NULL,将使用默认标签 "periph"* * @return     成功:返回外设句柄*             失败:返回 NULL(内存分配失败时)*/
esp_periph_handle_t esp_periph_create(int periph_id, const char *tag)
{esp_periph_handle_t new_entry = audio_calloc(1, sizeof(struct esp_periph));AUDIO_MEM_CHECK(TAG, new_entry, return NULL);if (tag) {new_entry->tag = audio_strdup(tag);} else {new_entry->tag = audio_strdup("periph");}AUDIO_MEM_CHECK(TAG, new_entry->tag, {audio_free(new_entry);return NULL;})new_entry->state = PERIPH_STATE_INIT;new_entry->periph_id = periph_id;return new_entry;
}

参数说明

  • periph_id:外设 ID,可以是预定义的 ID 或通过 esp_periph_alloc_periph_id 分配的 ID
  • tag:外设标签,用于调试和识别外设,如果为 NULL,将使用默认标签 “periph”

返回值

  • 成功:返回外设句柄
  • 失败:返回 NULL(内存分配失败时)

调用示例

// 创建外设集合
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);// 使用预定义 ID 创建外设
esp_periph_handle_t gpio_periph = esp_periph_create(PERIPH_ID_GPIO, "gpio");
if (gpio_periph == NULL) {ESP_LOGE(TAG, "Failed to create GPIO peripheral");return;
}// 设置外设函数
esp_periph_set_function(gpio_periph, gpio_init_func, gpio_run_func, gpio_destroy_func);// 启动外设
esp_periph_start(set, gpio_periph);// 或者创建自定义外设
int custom_id;
esp_periph_alloc_periph_id(set, &custom_id);
esp_periph_handle_t custom_periph = esp_periph_create(custom_id, "my_custom_periph");

创建外设时序图

应用程序 esp_periph_create 内存管理 esp_periph_set_function esp_periph_start 调用esp_periph_create(periph_id, tag) 分配外设结构内存(audio_calloc) 返回内存地址 分配并复制标签字符串(audio_strdup) 返回标签内存地址 初始化状态和 ID 返回外设句柄 设置外设函数(esp_periph_set_function) 返回ESP_OK 启动外设(esp_periph_start) 返回ESP_OK 应用程序 esp_periph_create 内存管理 esp_periph_set_function esp_periph_start

这个时序图展示了esp_periph_create函数的执行流程,以及完整的外设创建过程,包括创建外设、设置外设函数和启动外设的步骤。

注意:esp_periph_create函数只是创建外设的第一步,完整的外设创建过程还需要设置外设函数和启动外设。外设创建后的初始状态为PERIPH_STATE_INIT,需要通过esp_periph_start函数将外设添加到外设集合中并启动。

esp_periph_set_function

功能描述:设置外设的功能函数,包括初始化、运行和销毁函数。

函数原型

/*** @brief      设置外设的功能函数* * @details    为外设设置初始化、运行和销毁函数。这些函数定义了外设的行为和生命周期。*             初始化函数在外设启动时调用,运行函数在收到事件时调用,销毁函数在外设停止时调用。*             这些函数应该在调用 esp_periph_start 之前设置。* * @param[in]  periph   外设句柄,通过 esp_periph_create 获得* @param[in]  init     初始化函数,在外设启动时调用* @param[in]  run      运行函数,在收到事件时调用* @param[in]  destroy  销毁函数,在外设停止时调用* * @return     成功:ESP_OK*/
esp_err_t esp_periph_set_function(esp_periph_handle_t periph,esp_periph_func init,esp_periph_run_func run,esp_periph_func destroy)
{periph->init = init;periph->run = run;periph->destroy = destroy;return ESP_OK;
}

参数说明

  • periph:外设句柄,通过 esp_periph_create 获得
  • init:初始化函数,在外设启动时调用
  • run:运行函数,在收到事件时调用
  • destroy:销毁函数,在外设停止时调用

返回值

  • 成功:返回ESP_OK

函数类型定义

// 初始化和销毁函数类型
typedef esp_err_t (*esp_periph_func)(esp_periph_handle_t periph);// 运行函数类型
typedef esp_err_t (*esp_periph_run_func)(esp_periph_handle_t periph, audio_event_iface_msg_t *msg);

调用示例

// 创建外设
esp_periph_handle_t gpio_periph = esp_periph_create(PERIPH_ID_GPIO, "gpio");// 定义外设函数
esp_err_t gpio_init(esp_periph_handle_t periph) {// 初始化 GPIO 外设的代码ESP_LOGI(TAG, "GPIO peripheral initialized");return ESP_OK;
}esp_err_t gpio_run(esp_periph_handle_t periph, audio_event_iface_msg_t *msg) {// 处理 GPIO 事件的代码if (msg->cmd == GPIO_EVENT_BUTTON_PRESSED) {ESP_LOGI(TAG, "Button pressed");}return ESP_OK;
}esp_err_t gpio_destroy(esp_periph_handle_t periph) {// 清理 GPIO 外设资源的代码ESP_LOGI(TAG, "GPIO peripheral destroyed");return ESP_OK;
}// 设置外设函数
esp_periph_set_function(gpio_periph, gpio_init, gpio_run, gpio_destroy);// 启动外设
esp_periph_start(set, gpio_periph);

设置外设函数时序图

应用程序 esp_periph_create esp_periph_set_function esp_periph_start 外设结构 外设任务 创建外设(esp_periph_create) 返回外设句柄 调用esp_periph_set_function(periph, init, run, destroy) periph->>init = init periph->>run = run periph->>destroy = destroy 返回ESP_OK 启动外设(esp_periph_start) 在任务中调用 periph->>init(periph) 当收到事件时调用 periph->>run(periph, msg) 停止外设时调用 periph->>destroy(periph) 应用程序 esp_periph_create esp_periph_set_function esp_periph_start 外设结构 外设任务

这个时序图展示了esp_periph_set_function函数的执行流程,以及外设函数在外设生命周期中的调用时机。

注意:外设函数定义了外设的行为和生命周期。初始化函数应该设置外设的初始状态和资源,运行函数应该处理外设的事件,销毁函数应该清理外设的资源。这三个函数都是可选的,可以设置为 NULL,但对于大多数外设,至少应该提供初始化和运行函数。

esp_periph_start

功能描述:启动外设并将其添加到外设集合中。

函数原型

/*** @brief      启动外设并将其添加到外设集合中* * @details    将外设添加到外设集合的链表中,并注册外设的事件处理函数。*             如果外设集合处于非运行状态且配置了任务栈,则创建并启动外设任务。*             如果外设已经存在于外设集合中,则只是启用该外设。* * @param[in]  periph_set_handle  外设集合句柄* @param[in]  periph             要启动的外设句柄* * @return     成功:ESP_OK*             失败:ESP_FAIL(当外设集合无效或创建任务失败时)*/
esp_err_t esp_periph_start(esp_periph_set_handle_t periph_set_handle, esp_periph_handle_t periph)
{if (periph_set_handle == NULL) {AUDIO_ERROR(TAG, "Peripherals have not been initialized");return ESP_FAIL;}if (esp_periph_set_get_by_id(periph_set_handle, periph->periph_id) != NULL) {ESP_LOGI(TAG, "This peripheral has been added");periph->disabled = false;} else {esp_periph_register_on_events(periph, &periph_set_handle->event_handle);STAILQ_INSERT_TAIL(&periph_set_handle->periph_list, periph, entries);}if (periph_set_handle->run == false && periph_set_handle->task_stack > 0) {periph_set_handle->run = true;if (audio_thread_create(&periph_set_handle->audio_thread,"esp_periph",esp_periph_task,periph_set_handle,periph_set_handle->task_stack,periph_set_handle->task_prio,periph_set_handle->ext_stack,periph_set_handle->task_core) != ESP_OK) {ESP_LOGE(TAG, "Create [%s] task failed", periph->tag);return ESP_FAIL;}}return ESP_OK;
}

参数说明

  • periph_set_handle:外设集合句柄,通过 esp_periph_set_init 获得
  • periph:要启动的外设句柄,通过 esp_periph_create 获得

返回值

  • 成功:返回ESP_OK
  • 失败:返回ESP_FAIL(当外设集合无效或创建任务失败时)

调用示例

// 创建外设集合
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);// 创建外设
esp_periph_handle_t button = periph_button_init(&button_cfg);
if (button == NULL) {ESP_LOGE(TAG, "Failed to create button peripheral");return;
}// 启动外设
esp_err_t ret = esp_periph_start(set, button);
if (ret != ESP_OK) {ESP_LOGE(TAG, "Failed to start button peripheral");return;
}// 创建并启动另一个外设
esp_periph_handle_t sdcard = periph_sdcard_init(&sdcard_cfg);
esp_periph_start(set, sdcard);

启动外设时序图

应用程序 esp_periph_start esp_periph_set_get_by_id esp_periph_register_on_events 外设链表 audio_thread_create esp_periph_task 调用esp_periph_start(set, periph) 检查periph_set_handle是否有效 检查外设是否已存在(esp_periph_set_get_by_id) 返回外设句柄 periph->>disabled = false 返回NULL 注册外设事件处理函数 将外设添加到链表尾部(STAILQ_INSERT_TAIL) alt [外设已存在] [外设不存在] periph_set_handle->>run = true 创建外设任务(audio_thread_create) 返回创建结果 启动esp_periph_task alt [外设任务未运行且有任务栈] 返回ESP_OK 应用程序 esp_periph_start esp_periph_set_get_by_id esp_periph_register_on_events 外设链表 audio_thread_create esp_periph_task

这个时序图展示了esp_periph_start函数的执行流程,包括如何检查外设是否已存在、添加外设到链表以及创建外设任务的过程。

注意:esp_periph_start函数是外设创建过程的最后一步,它将外设添加到外设集合中并启动外设任务。如果外设集合使用任务模式(task_stack > 0),则会创建一个任务来处理外设事件;如果使用无任务模式(task_stack = 0),则需要手动调用esp_periph_set_list_initesp_periph_set_list_run函数。

esp_periph_stop

功能描述:停止外设的运行。

函数原型

/*** @brief      停止外设的运行* * @details    通过将外设的 disabled 标志设置为 true 来停止外设的运行。*             这不会从外设集合中移除外设,也不会调用外设的销毁函数,*             只是禁用外设使其不再处理事件。* * @param[in]  periph  要停止的外设句柄* * @return     成功:ESP_OK*/
esp_err_t esp_periph_stop(esp_periph_handle_t periph)
{if (periph) {periph->disabled = true;return ESP_OK;}return ESP_OK;
}

参数说明

  • periph:要停止的外设句柄,通过 esp_periph_create 获得

返回值

  • 成功:返回ESP_OK

调用示例

// 创建并启动外设
esp_periph_handle_t button = periph_button_init(&button_cfg);
esp_periph_start(set, button);// 使用外设...// 当不再需要按钮外设时停止它
esp_periph_stop(button);// 如果需要再次启用外设,可以再次调用 esp_periph_start
// esp_periph_start(set, button);// 如果需要完全移除外设,可以调用 esp_periph_remove_from_set
// esp_periph_remove_from_set(set, button);

停止外设时序图

应用程序 esp_periph_stop 外设结构 外设任务 调用esp_periph_stop(periph) 检查periph是否有效 periph->>disabled = true 返回ESP_OK 外设任务中会检查disabled标志 如果periph->>disabled为true,则跳过该外设 应用程序 esp_periph_stop 外设结构 外设任务

这个时序图展示了esp_periph_stop函数的执行流程,以及外设任务如何处理被禁用的外设。

注意:esp_periph_stop函数只是通过设置外设的 disabled 标志来禁用外设,它不会从外设集合中移除外设,也不会调用外设的销毁函数。如果需要完全移除外设,应该使用esp_periph_remove_from_set函数。如果需要再次启用外设,可以再次调用esp_periph_start函数。

esp_periph_set_data

功能描述:设置外设的用户数据。

函数原型

/*** @brief      设置外设的用户数据* * @details    将用户指定的数据指针关联到外设上。这个函数允许开发者将任意类型的数据*             与外设关联,便于在外设的回调函数中访问这些数据。* * @param[in]  periph  外设句柄* @param[in]  data    要设置的用户数据指针* * @return     成功:ESP_OK*/
esp_err_t esp_periph_set_data(esp_periph_handle_t periph, void *data)
{periph->periph_data = data;return ESP_OK;
}

参数说明

  • periph:外设句柄,通过 esp_periph_create 获得
  • data:要设置的用户数据指针,可以是任意类型的数据

返回值

  • 成功:返回ESP_OK

调用示例

// 创建外设
esp_periph_handle_t button = periph_button_init(&button_cfg);// 创建用户数据结构
typedef struct {int button_press_count;bool long_press_detected;void *user_context;
} button_user_data_t;button_user_data_t *user_data = calloc(1, sizeof(button_user_data_t));
if (user_data == NULL) {ESP_LOGE(TAG, "Failed to allocate memory for button user data");return;
}// 初始化用户数据
user_data->button_press_count = 0;
user_data->long_press_detected = false;
user_data->user_context = some_context;// 将用户数据关联到外设
esp_periph_set_data(button, user_data);// 启动外设
esp_periph_start(set, button);// 在外设的运行函数中可以访问用户数据
esp_err_t button_run(esp_periph_handle_t periph, audio_event_iface_msg_t *msg) {button_user_data_t *user_data = (button_user_data_t *)esp_periph_get_data(periph);if (msg->cmd == BUTTON_PRESSED) {user_data->button_press_count++;ESP_LOGI(TAG, "Button pressed %d times", user_data->button_press_count);}return ESP_OK;
}

设置外设数据时序图

应用程序 esp_periph_set_data 外设结构 esp_periph_get_data 外设运行函数 创建并初始化用户数据 调用esp_periph_set_data(periph, data) periph->>periph_data = data 返回ESP_OK 在外设运行期间 调用esp_periph_get_data(periph) 访问 periph->>periph_data 返回用户数据指针 使用用户数据 应用程序 esp_periph_set_data 外设结构 esp_periph_get_data 外设运行函数

这个时序图展示了esp_periph_set_data函数的执行流程,以及如何在外设的运行函数中访问这些数据。

注意:esp_periph_set_data函数允许开发者将任意类型的数据与外设关联,这在实现复杂的外设行为时非常有用。开发者负责管理这些数据的生命周期,包括在不再需要时释放内存。

esp_periph_get_data

功能描述:获取外设的用户数据。

函数原型

/*** @brief      获取外设的用户数据* * @details    返回之前通过 esp_periph_set_data 函数设置的用户数据指针。*             这个函数允许开发者在外设的回调函数中访问与外设关联的数据。* * @param[in]  periph  外设句柄* * @return     外设的用户数据指针,如果未设置则返回 NULL*/
void *esp_periph_get_data(esp_periph_handle_t periph)
{return periph->periph_data;
}

参数说明

  • periph:外设句柄,通过 esp_periph_create 获得

返回值

  • 外设的用户数据指针,如果未设置则返回 NULL

调用示例

// 在外设的运行函数中获取并使用用户数据
esp_err_t button_run(esp_periph_handle_t periph, audio_event_iface_msg_t *msg) {// 获取用户数据button_user_data_t *user_data = (button_user_data_t *)esp_periph_get_data(periph);if (user_data == NULL) {ESP_LOGE(TAG, "No user data associated with this peripheral");return ESP_FAIL;}// 使用用户数据if (msg->cmd == BUTTON_PRESSED) {user_data->button_press_count++;ESP_LOGI(TAG, "Button pressed %d times", user_data->button_press_count);// 检查是否达到特定条件if (user_data->button_press_count >= 10) {ESP_LOGI(TAG, "Button press count reached threshold");// 执行特定操作...}}return ESP_OK;
}

获取外设数据时序图

外设运行函数 esp_periph_get_data 外设结构 调用esp_periph_get_data(periph) 访问 periph->>periph_data 返回用户数据指针 检查数据是否为 NULL 类型转换并使用用户数据 外设运行函数 esp_periph_get_data 外设结构

这个时序图展示了esp_periph_get_data函数的执行流程,以及如何在外设的运行函数中获取并使用用户数据。

注意:esp_periph_get_data函数返回的是void*类型的指针,需要开发者自己进行类型转换才能正确使用。在使用前应该始终检查返回值是否为 NULL,以避免空指针引用错误。

esp_periph_get_state

功能描述:获取外设的当前状态。

函数原型

/*** @brief      获取外设的当前状态* * @details    返回外设的当前状态,可以是初始化、运行中、错误或停止状态。*             这个函数允许开发者检查外设的当前状态,以便进行相应的处理。* * @param[in]  periph  外设句柄* * @return     外设的当前状态(esp_periph_state_t类型)*/
esp_periph_state_t esp_periph_get_state(esp_periph_handle_t periph)
{return periph->state;
}

参数说明

  • periph:外设句柄,通过 esp_periph_create 获得

返回值

  • 外设的当前状态(esp_periph_state_t类型),可能的值包括:
    • PERIPH_STATE_INIT:外设处于初始化状态
    • PERIPH_STATE_RUNNING:外设处于运行状态
    • PERIPH_STATE_ERROR:外设处于错误状态
    • PERIPH_STATE_STOPPED:外设处于停止状态

调用示例

// 创建并启动外设
esp_periph_handle_t button = periph_button_init(&button_cfg);
esp_periph_start(set, button);// 检查外设状态
esp_periph_state_t state = esp_periph_get_state(button);
switch (state) {case PERIPH_STATE_INIT:ESP_LOGI(TAG, "Button peripheral is in initialization state");break;case PERIPH_STATE_RUNNING:ESP_LOGI(TAG, "Button peripheral is running");break;case PERIPH_STATE_ERROR:ESP_LOGE(TAG, "Button peripheral encountered an error");// 处理错误情况...break;case PERIPH_STATE_STOPPED:ESP_LOGI(TAG, "Button peripheral is stopped");break;default:ESP_LOGE(TAG, "Unknown peripheral state: %d", state);break;
}// 在外设的运行函数中也可以检查状态
esp_err_t button_run(esp_periph_handle_t periph, audio_event_iface_msg_t *msg) {if (esp_periph_get_state(periph) != PERIPH_STATE_RUNNING) {ESP_LOGW(TAG, "Button peripheral is not in running state");return ESP_FAIL;}// 正常处理事件...return ESP_OK;
}

获取外设状态时序图

应用程序 esp_periph_get_state 外设结构 调用esp_periph_get_state(periph) 访问 periph->>state 返回外设状态 根据状态进行相应处理 应用程序 esp_periph_get_state 外设结构

这个时序图展示了esp_periph_get_state函数的执行流程,以及应用程序如何根据外设状态进行相应处理。

注意:外设状态是由外设子系统内部管理的,开发者不应直接修改外设的状态。外设初始创建时处于PERIPH_STATE_INIT状态,在初始化函数成功执行后变为PERIPH_STATE_RUNNING状态,如果初始化失败则变为PERIPH_STATE_ERROR状态。

esp_periph_get_id

功能描述:获取外设的 ID。

函数原型

/*** @brief      获取外设的 ID* * @details    返回外设的唯一标识 ID。每个外设都有一个唯一的 ID,可以是预定义的 ID*             或通过 esp_periph_alloc_periph_id 分配的自定义 ID。* * @param[in]  periph  外设句柄* * @return     外设的 ID(esp_periph_id_t 类型)*/
esp_periph_id_t esp_periph_get_id(esp_periph_handle_t periph)
{return periph->periph_id;
}

参数说明

  • periph:外设句柄,通过 esp_periph_create 获得

返回值

  • 外设的 ID(esp_periph_id_t 类型)

调用示例

// 创建外设
esp_periph_handle_t button = periph_button_init(&button_cfg);
esp_periph_start(set, button);// 获取外设 ID
esp_periph_id_t id = esp_periph_get_id(button);
ESP_LOGI(TAG, "Button peripheral ID: %d", id);// 使用 ID 从外设集合中获取外设
esp_periph_handle_t found_periph = esp_periph_set_get_by_id(set, id);
if (found_periph != NULL) {ESP_LOGI(TAG, "Found peripheral with ID %d", id);
}

获取外设 ID 时序图

应用程序 esp_periph_get_id 外设结构 esp_periph_set_get_by_id 调用esp_periph_get_id(periph) 访问 periph->>periph_id 返回外设 ID 使用 ID 查找外设的示例 调用esp_periph_set_get_by_id(set, id) 返回外设句柄 应用程序 esp_periph_get_id 外设结构 esp_periph_set_get_by_id

这个时序图展示了esp_periph_get_id函数的执行流程,以及如何使用获取到的 ID 来查找外设。

注意:外设 ID 是外设的唯一标识,用于在外设集合中查找和识别外设。预定义的外设 ID 定义在 esp_peripherals.h 文件中,而自定义外设 ID 可以通过 esp_periph_alloc_periph_id 函数分配。

esp_periph_set_id

功能描述:设置外设的 ID。

函数原型

/*** @brief      设置外设的 ID* * @details    修改外设的唯一标识 ID。这个函数允许开发者在运行时更改外设的 ID,*             但应谨慎使用,因为这可能导致 ID 冲突或外设无法通过 ID 被正确识别。* * @param[in]  periph     外设句柄* @param[in]  periph_id  要设置的新 ID* * @return     成功:ESP_OK*/
esp_err_t esp_periph_set_id(esp_periph_handle_t periph, esp_periph_id_t periph_id)
{periph->periph_id = periph_id;return ESP_OK;
}

参数说明

  • periph:外设句柄,通过 esp_periph_create 获得
  • periph_id:要设置的新 ID,可以是预定义的 ID 或通过 esp_periph_alloc_periph_id 分配的 ID

返回值

  • 成功:返回ESP_OK

调用示例

// 创建外设
esp_periph_handle_t custom_periph = esp_periph_create(PERIPH_ID_UNKNOWN, "custom");// 分配新的 ID
int new_id;
esp_periph_alloc_periph_id(set, &new_id);// 设置外设 ID
esp_periph_set_id(custom_periph, new_id);
ESP_LOGI(TAG, "Custom peripheral ID set to: %d", new_id);// 启动外设
esp_periph_start(set, custom_periph);// 现在可以使用新 ID 查找外设
esp_periph_handle_t found_periph = esp_periph_set_get_by_id(set, new_id);

设置外设 ID 时序图

应用程序 esp_periph_alloc_periph_id esp_periph_set_id 外设结构 esp_periph_set_get_by_id 调用esp_periph_alloc_periph_id(set, &new_id) 返回新分配的 ID 调用esp_periph_set_id(periph, new_id) periph->>periph_id = new_id 返回ESP_OK 使用新 ID 查找外设 调用esp_periph_set_get_by_id(set, new_id) 返回外设句柄 应用程序 esp_periph_alloc_periph_id esp_periph_set_id 外设结构 esp_periph_set_get_by_id

这个时序图展示了esp_periph_set_id函数的执行流程,以及如何分配新 ID 并将其设置给外设。

注意:尽管esp_periph_set_id函数允许开发者更改外设的 ID,但应谨慎使用。更改已添加到外设集合中的外设的 ID 可能会导致无法通过 ID 找到该外设。建议在外设添加到外设集合之前设置其 ID。

esp_periph_init

功能描述:手动初始化外设。

函数原型

/*** @brief      手动初始化外设* * @details    直接调用外设的初始化函数。这个函数通常不需要直接调用,*             因为外设的初始化通常在 esp_periph_start 函数中自动完成。*             只有在特殊情况下,如需要手动控制外设初始化时机时,才需要调用此函数。* * @param[in]  periph  外设句柄* * @return     成功:返回外设初始化函数的返回值*             失败:如果外设没有初始化函数,可能会导致程序崩溃*/
esp_err_t esp_periph_init(esp_periph_handle_t periph)
{return periph->init(periph);
}

参数说明

  • periph:外设句柄,通过 esp_periph_create 获得

返回值

  • 成功:返回外设初始化函数的返回值,通常是 ESP_OK
  • 失败:如果外设初始化失败,返回错误代码,如 ESP_FAIL

调用示例

// 创建外设
esp_periph_handle_t button = periph_button_init(&button_cfg);// 设置外设函数
esp_periph_set_function(button, button_init, button_run, button_destroy);// 手动初始化外设
esp_err_t ret = esp_periph_init(button);
if (ret != ESP_OK) {ESP_LOGE(TAG, "Failed to initialize button peripheral");return;
}// 注意:通常不需要手动初始化,因为 esp_periph_start 会自动完成初始化
// esp_periph_start(set, button);

手动初始化外设时序图

应用程序 esp_periph_init 外设结构 外设初始化函数 调用esp_periph_init(periph) 访问 periph->>init 调用 periph->>init(periph) 返回初始化结果 返回初始化结果 注意:如果 periph->>init 为 NULL,将导致程序崩溃 应用程序 esp_periph_init 外设结构 外设初始化函数

这个时序图展示了esp_periph_init函数的执行流程,以及如何直接调用外设的初始化函数。

注意:esp_periph_init函数直接调用外设的初始化函数,没有进行空指针检查。因此,在调用此函数前,应确保外设的初始化函数已经设置,否则可能导致程序崩溃。通常情况下,应使用esp_periph_start函数来启动外设,该函数会安全地处理外设的初始化。

esp_periph_run

功能描述:手动运行外设的运行函数。

函数原型

/*** @brief      手动运行外设的运行函数* * @details    直接调用外设的运行函数,传入 NULL 消息。这个函数通常不需要直接调用,*             因为外设的运行函数通常在外设任务中或通过 esp_periph_set_list_run 函数自动调用。*             只有在特殊情况下,如需要手动触发外设处理时,才需要调用此函数。* * @param[in]  periph  外设句柄* * @return     成功:返回外设运行函数的返回值*             失败:如果外设没有运行函数,可能会导致程序崩溃*/
esp_err_t esp_periph_run(esp_periph_handle_t periph)
{return periph->run(periph, NULL);
}

参数说明

  • periph:外设句柄,通过 esp_periph_create 获得

返回值

  • 成功:返回外设运行函数的返回值,通常是 ESP_OK
  • 失败:如果外设运行失败,返回错误代码,如 ESP_FAIL

调用示例

// 创建外设
esp_periph_handle_t button = periph_button_init(&button_cfg);// 设置外设函数
esp_periph_set_function(button, button_init, button_run, button_destroy);// 手动初始化外设
esp_periph_init(button);// 手动运行外设
esp_err_t ret = esp_periph_run(button);
if (ret != ESP_OK) {ESP_LOGE(TAG, "Failed to run button peripheral");return;
}// 注意:通常不需要手动运行外设,因为外设任务或 esp_periph_set_list_run 会自动调用运行函数

手动运行外设时序图

应用程序 esp_periph_run 外设结构 外设运行函数 调用esp_periph_run(periph) 访问 periph->>run 调用 periph->>run(periph, NULL) 返回运行结果 返回运行结果 注意:如果 periph->>run 为 NULL,将导致程序崩溃 应用程序 esp_periph_run 外设结构 外设运行函数

这个时序图展示了esp_periph_run函数的执行流程,以及如何直接调用外设的运行函数。

注意:esp_periph_run函数直接调用外设的运行函数,没有进行空指针检查。因此,在调用此函数前,应确保外设的运行函数已经设置,否则可能导致程序崩溃。与正常的外设运行函数调用不同,这里传入的消息参数为 NULL,这意味着外设可能无法正常处理事件。

esp_periph_destroy

功能描述:手动销毁外设。

函数原型

/*** @brief      手动销毁外设* * @details    直接调用外设的销毁函数。这个函数通常不需要直接调用,*             因为外设的销毁通常在 esp_periph_set_destroy 或 esp_periph_set_list_destroy 函数中自动完成。*             只有在特殊情况下,如需要手动控制外设销毁时机时,才需要调用此函数。* * @param[in]  periph  外设句柄* * @return     成功:返回外设销毁函数的返回值*             失败:如果外设没有销毁函数,可能会导致程序崩溃*/
esp_err_t esp_periph_destroy(esp_periph_handle_t periph)
{return periph->destroy(periph);
}

参数说明

  • periph:外设句柄,通过 esp_periph_create 获得

返回值

  • 成功:返回外设销毁函数的返回值,通常是 ESP_OK
  • 失败:如果外设销毁失败,返回错误代码,如 ESP_FAIL

调用示例

// 创建外设
esp_periph_handle_t button = periph_button_init(&button_cfg);// 设置外设函数
esp_periph_set_function(button, button_init, button_run, button_destroy);// 使用外设...// 先从外设集合中移除外设
esp_periph_remove_from_set(set, button);// 手动销毁外设
esp_err_t ret = esp_periph_destroy(button);
if (ret != ESP_OK) {ESP_LOGE(TAG, "Failed to destroy button peripheral");return;
}// 释放外设内存
// free(button);  // 注意:外设的内存释放取决于具体实现// 注意:通常不需要手动销毁外设,因为 esp_periph_set_destroy 会自动完成销毁

手动销毁外设时序图

应用程序 esp_periph_remove_from_set esp_periph_destroy 外设结构 外设销毁函数 调用esp_periph_remove_from_set(set, periph) 返回ESP_OK 调用esp_periph_destroy(periph) 访问 periph->>destroy 调用 periph->>destroy(periph) 返回销毁结果 返回销毁结果 注意:如果 periph->>destroy 为 NULL,将导致程序崩溃 释放外设内存(如果需要) 应用程序 esp_periph_remove_from_set esp_periph_destroy 外设结构 外设销毁函数

这个时序图展示了esp_periph_destroy函数的执行流程,以及如何直接调用外设的销毁函数。

注意:esp_periph_destroy函数直接调用外设的销毁函数,没有进行空指针检查。因此,在调用此函数前,应确保外设的销毁函数已经设置,否则可能导致程序崩溃。这个函数只负责调用外设的销毁函数,不会释放外设本身的内存。如果需要完全释放外设资源,还需要手动释放外设的内存。

相关文章:

ESP-ADF外设子系统深度解析:esp_peripherals组件架构与核心设计(核心API详解之单个外设管理)

目录 单个外设管理APIesp_periph_createesp_periph_set_functionesp_periph_startesp_periph_stopesp_periph_set_dataesp_periph_get_dataesp_periph_get_stateesp_periph_get_idesp_periph_set_idesp_periph_initesp_periph_runesp_periph_destroy 单个外设管理API esp_peri…...

基于vue2+ElementUI的el-tree封装一个带搜索的树形组件

需求 实现一个如图带搜索框的下拉树形组件。 解决方案 利用el-inputel-tree实现自定义带搜索的下拉树形组件。 具体实现步骤 1、创建TreeSelect组件 <template><div class"tree-select-wrapper" v-clickoutside"handleClose"><el-inpu…...

G2学习打卡

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 DCGAN实践 import torch, random, random, os import torch.nn as nn import torch.nn.parallel import torch.optim as optim import torch.utils.data im…...

【广州华锐互动】汽车生产引入数字孪生系统,优化生产流程,提升汽车产品质量

数字孪生系统的应用为企业带来了生产流程的革命性变革。以汽车制造企业为例&#xff0c;该企业在生产过程中引入数字孪生系统&#xff0c;实现了生产流程的全面优化和产品质量的显著提升 。​ 在生产流程优化方面&#xff0c;数字孪生系统对汽车生产线进行了全方位的模拟和实时…...

从Gradio App创建Discord Bot/Slack Bot/Website Widget(2)——从Gradio App创建Slack Bot

从Gradio App创建Discord Bot/Slack Bot/Website Widget&#xff08;2&#xff09;——从Gradio App创建Slack Bot 本篇摘要18. 从Gradio App创建Discord Bot/Slack Bot/Website Widget18.2 从Gradio App创建Slack Bot18.2.1 运作原理及前置条件1. 运作原理2. 前置条件 18.2.2 …...

基于STM32G474的SPI获取MT6816编码器绝对角度配置指南

前言&#xff1a;最近上手使用了一款编码器芯片&#xff0c;也是先艰难阅读了一下全英文版本的编码器的规格手册&#xff0c;然后通过SPI读取了一下绝对值角度。虽然发现使用起来还是挺简单的&#xff0c;但使用后还是会产生一个对其原理层面的好奇&#xff0c;比如磁编码器内部…...

深入学习ReentrantLock

ReentrantLock 0. 前言&#xff1a;为什么需要 ReentrantLock&#xff1f;1. 基础概念与核心特性1.1 什么是 ReentrantLock&#xff1f;1.2 ReentrantLock vs. synchronized1.3 核心特性详解1.3.1 可重入性 (Reentrancy)1.3.2 公平性选择 (Fairness Choice)1.3.3 可中断获取锁 …...

Spring Boot 集成金蝶 API 演示

✨ Spring Boot 集成金蝶 API 演示&#xff1a;登录 / 注销 Cookie 保存 本文将通过 Spring Boot 完整实现一套金蝶接口集成模型&#xff0c;包括&#xff1a; ✅ 普通登录✅ AppSecret 登录✅ 注销✅ Cookie 保存与复用 &#x1f4c5; 项目结构 src/ ├── controller/ │…...

适用于 HAL 的 AIDL

目录 设计初衷 注意 编写AIDLHAL接口 查找AIDLHAL接口 扩展接口 将现有HAL从HIDL转换为AIDL AIDL与HIDL之间的主要差异 针对HAL的供应商测试套件(VTS)测试 Android 11 中引入了在 Android 中使用 AIDL 实现 HAL 的功能, 从而可以在不使用 HIDL 的情况下实现 Android 的部分…...

49、Spring Boot 详细讲义(六)(SpringBoot2.x整合Mybatis实现CURD操作和分页查询详细项目文档)

项目文档:银行借据信息CURD操作和分页查询 一、项目概述 1. 项目简介 本项目旨在使用Spring Boot框架整合MyBatis连接Mysql数据库实现借据信息的增加、删除、修改和查询功能,同时支持分页查询,并提供对应的Restful风格的接口。 2.环境准备 2.1.工具和软件准备 JDK(建议…...

C# 运行web项目

1、web项目直接点击顶部运行...

GPU服务器声音很响可以怎么处理

当GPU服务器运行时噪音过大&#xff0c;通常是由于高负载下散热风扇高速运转所致。以下是分步骤的解决方案&#xff0c;帮助您有效降低噪音并保持设备稳定运行&#xff1a; 一、排查噪音来源 定位声源 • 使用 声级计 或手机分贝检测APP&#xff0c;确定最大噪音位置&#xff0…...

Java如何选择ojdbc驱动

如何选择ojdbc驱动&#xff1f; 取决于短板。 如果JDK版本高&#xff0c;数据库版本低&#xff0c;根据Oracle数据库版本选择。如果JDK版本低&#xff0c;数据库版本高&#xff0c;根据Java版本选择。 Oracle官网OJDBC驱动和受支持的JDK版本 23ai 21c 19c 驱动类型选择 oj…...

【微思就业推荐 】T岗位-北京,福州,厦门等地

到微思学习&#xff0c;免费推荐就业&#xff01;学员内推&#xff01; 原创 厦门微思网络 2025年04月 有哪些大公司在招OCP认证人才&#xff1f; 有哪些大公司在招聘拥有HCIE认证的人才 ① 委托单位&#xff1a;润欣商业管理(厦门)有限公司 央企-华润资产的子公司 岗位&am…...

Linux 命令全解析:从零开始掌握 Linux 命令行

Linux 作为一款强大的开源操作系统&#xff0c;广泛应用于服务器、嵌入式系统以及超级计算机领域。掌握 Linux 命令行技能&#xff0c;是每一位开发者和系统管理员的必备能力。本文将从基础开始&#xff0c;为你详细介绍常用的 Linux 命令&#xff0c;以及它们的使用场景和示例…...

2025年4月份生活有感

今天在5000B培训的下午&#xff0c;一起入所来的小伙伴&#xff0c;有个申请了深圳大学的博士&#xff0c;已录取。哎&#xff0c;想起了当年申博时候信心和决心不足&#xff0c;导致后面匆匆的拿了offer去工作。看到同事的选择还是非常羡慕&#xff0c;想到自己5月份的婚礼&am…...

鸿蒙系统开发状态更新字段区别对比

在鸿蒙系统开发中&#xff0c;状态管理是构建响应式UI的核心机制&#xff0c;主要通过装饰器&#xff08;Decorators&#xff09;实现字段的状态观测与更新。根据鸿蒙的版本&#xff08;V1稳定版和V2试用版&#xff09;&#xff0c;支持的装饰器及其特性有所不同。以下是主要状…...

CEPH OSD_SLOW_PING_TIME_FRONT/BACK 警告处理

ceph config set mgr mon_warn_on_slow_ping_time 2000说明&#xff1a;mon_warn_on_slow_ping_time 该值默认为0&#xff0c;那么只要 osd 心跳超过 mon_warn_on_slow_ping_ratio of osd_heartbeat_grace. 也就是超过 mon_warn_on_slow_ping_ratio和mon_warn_on_slow_ping_rat…...

HTML应用指南:利用POST请求获取全国小菜园门店位置信息

小菜园作为一家以徽菜为主的快餐品牌&#xff0c;自2013年成立以来&#xff0c;凭借其独特的烹饪理念和精致的东方口味菜品&#xff0c;在中国市场上迅速崛起。该品牌强调少油少盐、减少调味品使用&#xff0c;旨在传承并发扬徽州风味的独特魅力。这种健康且不失美味的烹饪方式…...

Python在去中心化物联网中的应用:数据安全、智能合约与边缘计算的融合

Python在去中心化物联网中的应用:数据安全、智能合约与边缘计算的融合 在万物互联的时代,传统物联网(IoT)架构依赖于集中式服务器来管理数据、设备互联与身份认证。然而,随着设备数量激增,中心化架构的可扩展性、安全性和隐私问题逐渐暴露。去中心化物联网(DeIoT)通过…...

CEPH配置优化建议

一、硬件配置优化 磁盘选择&#xff1a; SSD 与 HDD 搭配&#xff1a;使用 SSD 作为 OSD 日志盘&#xff08;Journal&#xff09;或元数据存储&#xff0c;HDD 作为数据盘。推荐 SSD 与 HDD 的比例为 1:3~5&#xff0c;具体根据业务负载调整。 RAID 禁用&#xff1a;避免使用硬…...

深度学习入门:神经网络的学习

目录 1 从数据中学习1.1 数据驱动1.2 训练数据和测试数据 2损失函数2.1 均方误差2.2 交叉熵误差2.3 mini-batch学习2.4 mini-batch版交叉熵误差的实现2.5 为何要设定损失函数 3 数值微分3.1 数值微分3.3 偏导数 4 梯度4.1 梯度法4.2 神经网络的梯度 5 学习算法的实现5.1 2层神经…...

机器学习_决策树

决策树的特点 可以处理非线性的问题可解释强&#xff0c;没有θ模型简单&#xff0c;模型预测效率高 if else不容易显示的使用函数表达&#xff0c;不可微 决策树的生成和预测 生成&#xff1a;通过大量数据生成一颗非常好的树&#xff0c;用这棵树来预测新来的数据。 预测&…...

深入理解UML动态图:系统行为建模全景指南

目录 前言1. 动态图概述2. 用例图&#xff08;Use Case Diagram&#xff09;2.1 定义与作用2.2 应用价值2.3 实践建议 3. 顺序图&#xff08;Sequence Diagram&#xff09;3.1 定义与特征3.2 应用优势3.3 建模建议 4. 活动图&#xff08;Activity Diagram&#xff09;4.1 定义与…...

Linux驱动开发进阶(九)- SPI子系统BSP驱动

文章目录 1、前言2、SPI总线注册3、SPI设备注册4、SPI驱动注册5、SPI BSP驱动 1、前言 学习参考书籍以及本文涉及的示例程序&#xff1a;李山文的《Linux驱动开发进阶》本文属于个人学习后的总结&#xff0c;不太具备教学功能。 2、SPI总线注册 驱动源码文件&#xff1a;dri…...

wabpack学习记录

wabpack学习记录 前言 项目写了不少 对webpack了解甚少 只记住一些 必要的概念以及指令 所以像深究一下具体是什么 可以做什么 如何做等 package.json 文件详解 name: 项目的名称。 version: 项目的版本号。 description: 项目的描述。 author: 项目的作者或维护者信息。 l…...

计算机视觉——基于 Yolov8 目标检测与 OpenCV 光流实现目标追踪

1. 概述 目标检测&#xff08;Object Detection&#xff09;和目标追踪&#xff08;Object Tracking&#xff09;是计算机视觉中的两个关键技术&#xff0c;它们在多种实际应用场景中发挥着重要作用。 目标检测指的是在静态图像或视频帧中识别出特定类别的目标对象&#xff0…...

React 更新 state 中的数组

更新 state 中的数组 数组是另外一种可以存储在 state 中的 JavaScript 对象&#xff0c;它虽然是可变的&#xff0c;但是却应该被视为不可变。同对象一样&#xff0c;当你想要更新存储于 state 中的数组时&#xff0c;你需要创建一个新的数组&#xff08;或者创建一份已有数组…...

[250415] OpenAI 推出 GPT-4.1 系列,支持 1M token

目录 OpenAI 推出 GPT-4.1 系列 OpenAI 推出 GPT-4.1 系列 OpenAI 宣布&#xff0c;新一代 GPT-4.1 模型系列正式发布&#xff0c;包括 GPT-4.1, GPT-4.1 mini 和 GPT-4.1 nano 三款模型&#xff0c;该系列模型在各项性能指标上全面超越 GPT-4o 和 GPT-4o mini&#xff0c;尤其…...

分布式锁+秒杀异步优化

文章目录 问题思路setnx实现锁误删问题和解决方案Redis Lua脚本问题引出解决方案 setnx实现的问题Redission快速入门redission可重入锁原理 秒杀优化(异步优化)异步秒杀思路秒杀资格判断Redis消息队列 问题 比如我们两个机器都部署了我们项目&#xff0c;这里nginx使用轮询的方…...

数据服务化 VS 数据中台:战略演进中的价值重构

在企业数据战略的演进历程中&#xff0c;数据中台曾被视为解决数据孤岛的 “万能钥匙”&#xff0c;而数据服务化的兴起则标志着企业从 “数据资源囤积” 向 “数据价值释放” 的深刻转型。两者的核心差异不仅在于技术架构&#xff0c;更在于对数据资产的定位与使用理念的根本分…...

PL/SQL登录慢,程序连接Oracle 提示无法连接或无监听

PL/SQL登录慢&#xff0c;程序连接Oracle 提示无法连接或无监听 错误提示&#xff1a;ORA-12541: TNS: 无监听程序 的解决办法&#xff0c; 现象&#xff1a;PL/SQL登录慢&#xff0c;程序连接Oracle 提示无法连接或无监听 监听已经正常开起&#xff0c;但还是PL/SQL登录慢或…...

【JAVAFX】自定义FXML 文件存放的位置以及使用

情况 1&#xff1a;FXML 文件与调用类在同一个包中&#xff08;推荐&#xff09; 假设类 MainApp 的包是 com.example&#xff0c;且 FXML 文件放在 resources/com/example 下&#xff1a; 项目根目录 ├── src │ └── sample │ └── Main.java ├── src/s…...

DDoS(分布式拒绝服务)攻击

DDoS(分布式拒绝服务)攻击 这是一份全面系统的 DDoS&#xff08;分布式拒绝服务攻击&#xff09;知识总结&#xff0c;适合用于学习、报告、讲稿或者面试准备。内容涵盖定义、原理、危害、利用、工具、防护策略等。 一、什么是DDoS DDoS&#xff08;Distributed Denial of Se…...

scikit-learn初探

KFold k交叉验证&#xff0c;k-1个作为训练集&#xff0c;剩下的作为测试集 split split(X, yNone, groupsNone)X&#xff1a; (n_samples, n_features)的矩阵&#xff0c;行数为n_samples&#xff0c;列数为n_features y&#xff1a;(n_samples,)为列向量&#xff0c;表示监…...

深入解析 sklearn 中的多种特征编码方式:功能、适用场景与选择建议

标题&#xff1a;深入解析 sklearn 中的多种特征编码方式&#xff1a;功能、适用场景与选择建议 摘要&#xff1a; 在机器学习中&#xff0c;特征编码是数据预处理的重要环节&#xff0c;直接影响模型的性能和效果。本文详细介绍了 sklearn 及其生态中&#xff08;含第三方库…...

windows10 wsl2 安装ubuntu和docker

见 弃用Docker Desktop&#xff1a;在WSL2中玩转Docker之Docker Engine 部署与WSL入门-阿里云开发者社区 如果启动docker时报下面这个错&#xff0c; 那是因为systemctl没有启用 sudo systemctl start docker System has not been booted with systemd as init system (PID 1)…...

一文读懂WPF系列之依赖属性与附加属性

依赖属性与附加属性 依赖属性对比C#属性WPF依赖属性&#xff08;Dependency Properties&#xff09;优先级计算与值决策​​回调与验证机制​​WPF 自带的依赖属性自定义依赖属性 附加属性本质与定义​​与依赖属性的区别​​附加属性的典型应用场景自定义附加属性注意事项 属性…...

1×1卷积与GoogleNet

11卷积 卷积核的尺寸等于1的卷积核 11卷积有什么用 1. 通道混合与特征转换 背景&#xff1a;在卷积神经网络中&#xff0c;输入数据通常有多个通道&#xff08;例如RGB图像有3个通道&#xff0c;经过卷积层后通道数可能会增加&#xff09;。不同通道的特征图可能包含了不同的…...

Handsontable 表格组件的使用

文章目录 1. 安装 Handsontable2. 创建一个基本表格3. 主要配置3.1、 data 数据3.2、 columns 指定列配置 4. Handsontable 高级功能4.1、 添加排序4.2、 过滤数据4.3、 选中行高亮4.4、 只读单元格4.5、 校验数据 5. Handsontable 与 Vue结合6. 总结 Handsontable 是一个强大的…...

消息中间件面试题

前言 本章内容来自B站黑马程序员java大厂面试题与小林coding 如有侵权立即删除 博主学习笔记&#xff0c;如果有不对的地方&#xff0c;海涵。 如果这篇文章对你有帮助&#xff0c;可以点点关注&#xff0c;点点赞&#xff0c;谢谢你&#xff01; 1.通用篇 1.1 什么是消息…...

数据结构与算法--1.判断数组中元素是否有重复

在C语言中&#xff0c;我们可以使用类似的方法来实现判断数组中是否有重复值的功能。由于C语言没有内置的哈希集合&#xff08;如Python的set或C的unordered_set&#xff09;&#xff0c;我们需要自己实现一个简单的哈希表或使用其他方法。 方法一&#xff1a;暴力法&#xff…...

硬件工程师面试常见问题(1)

第一问&#xff1a;单片机上电后没有运转&#xff0c;首先要检查什么&#xff1f; &#xff08;1&#xff09;单片机供电是否正常& 电路焊接检查 用万用表测量对应引脚的供电电压&#xff0c;检查对不对。 &#xff08;2&#xff09;单片机复位是否释放 用万用表测量复位引…...

测试100问:web测试和APP测试的区别

哈喽&#xff0c;大家好&#xff0c;我是十二&#xff0c;那今天要为大家分享的是高频面试题&#xff1a;web测试和 App测试的区别。 从功能测试方面来讲&#xff0c;web测试和 App测试在测试的流程以及测试用例的设计上是没有区别的&#xff0c;那主要的区别包含以下三个方面&…...

Leetcode 3518. Smallest Palindromic Rearrangement II

Leetcode 3518. Smallest Palindromic Rearrangement II 1. 解题思路2. 代码实现 题目链接&#xff1a;Leetcode 3518. Smallest Palindromic Rearrangement II 1. 解题思路 这一题是题目Leetcode 3517. Smallest Palindromic Rearrangement I的升级版本&#xff0c;其主要的…...

Golang|订单相关

文章目录 秒杀写库策略确保缓存的订单数据不丢失 秒杀写库策略 在我们的抽奖函数中&#xff0c;抽中奖品、减库存成功返回给前端后就应该生成订单写入数据库 但是这里有问题&#xff0c;我们的抽奖函数是支持高并发的&#xff0c;并发量大的情况下mysql无法支持这么大并发量的写…...

Python+Playwright:编写自动化测试的避坑策略

PythonPlaywright&#xff1a;编写自动化测试的避坑策略 前言一、告别 time.sleep()&#xff0c;拥抱 Playwright 的智能等待二、选择健壮、面向用户的选择器&#xff0c;优先使用 data-testid三、严格管理环境与依赖&#xff0c;确保一致性四、分离测试数据与逻辑&#xff0c;…...

P12130 [蓝桥杯 2025 省 B] 移动距离

P12130 [蓝桥杯 2025 省 B] 移动距离 - 洛谷 题目描述 小明初始在二维平面的原点&#xff0c;他想前往坐标 (233, 666)。在移动过程中&#xff0c;他只能采用以下两种移动方式&#xff0c;并且这两种移动方式可以交替、不限次数地使用&#xff1a; 水平向右移动&#xff0c;…...

关于 人工智能(AI)发展简史 的详细梳理,按时间阶段划分,涵盖关键里程碑、技术突破、重要人物及挑战

以下是关于 人工智能&#xff08;AI&#xff09;发展简史 的详细梳理&#xff0c;按时间阶段划分&#xff0c;涵盖关键里程碑、技术突破、重要人物及挑战&#xff1a; 字数&#xff1a;约2500字 逻辑结构&#xff1a;时间线清晰&#xff0c;分阶段描述技术突破、关键事件与挑战…...

Formality:Bug记录

相关阅读 Formalityhttps://blog.csdn.net/weixin_45791458/category_12841971.html?spm1001.2014.3001.5482 本文记录博主在使用Synopsys的形式验证工具Formality中遇到的几个Bug。 Bug复现 情况一 // 例1 module dff (input clk, input d_in, output d_out …...