docs: Provide CN translation for api-reference/protocols/icmp_echo.rst and...

This commit is contained in:
Ren Pei Ying
2023-09-05 16:00:26 +08:00
parent 1d030b7601
commit c3daa4bf78
4 changed files with 357 additions and 20 deletions
@@ -1 +1,217 @@
.. include:: ../../../en/api-reference/protocols/esp_sdio_slave_protocol.rst
ESP SDIO 主从机通信
========================
:link_to_translation:`en:[English]`
本文档介绍了 ESP 数字输入输出 (Secure Digital Input and OutputSDIO) 从机设备的初始化过程,并提供 ESP SDIO 从机协议的详细信息。该协议为非标准协议,允许 SDIO 主机与 ESP SDIO 从机进行通信。
创建 ESP SDIO 从机协议是为了实现 SDIO 主机和从机之间的通信。 这是因为 SDIO 规范只说明了如何访问卡的自定义区(向功能 1-7 发送 CMD52 和 CMD53),却没有说明底层的硬件实现过程。
.. _esp_sdio_slave_caps:
乐鑫芯片的 SDIO 从机功能
-----------------------------------
{IDF_TARGET_NAME} 芯片的 SDIO 从机提供以下服务:
.. list-table::
:widths: 70 30
:header-rows: 1
* - 服务
- {IDF_TARGET_NAME}
* - SDIO 从机
- Y
* - :ref:`Tohost intr <esp_sdio_slave_interrupts>`
- 8
* - :ref:`Frhost intr <esp_sdio_slave_interrupts>`
- 8
* - :ref:`TX DMA <esp_sdio_slave_send_fifo>`
- Y
* - :ref:`RX DMA <esp_sdio_slave_rcv_fifo>`
- Y
* - :ref:`共享寄存器 <esp_sdio_slave_shared_registers>`
- 56\*
\* 未包括中断寄存器
.. _esp_slave_init:
初始化 ESP SDIO 从机
-------------------------
主机需按照标准的 SDIO 初始化流程,对 {IDF_TARGET_NAME} SDIO 从机进行初始化(参考 `SDIO Simplified Specification <https://www.sdcard.org/downloads/pls/>`_ 3.1.2 章节)。下文将 SDIO 从机简称为 (SD)IO 卡。以下是 ESP SDIO 从机初始化流程的一个简单示例:
1. SDIO 复位
CMD52(写入 0x6=0x8
2. SD 复位
CMD0
3. 检查 IO 卡(可选)
CMD8
4. 发送 SDIO op cond 指令,等待 IO 卡就绪
CMD5 arg = 0x00000000
CMD5 arg = 0x00ff8000(根据以上响应进行轮询,直到 IO 卡就绪)
**示例:**
首次 CMD5 (arg=0x00000000) 后 R4 的 arg 为 0xXXFFFF00。
不断发送 CMD5 arg=0x00FFFF00,直到 R4 显示卡已就绪 (arg 31=1) 为止。
5. 设置地址
CMD3
6. 选择卡
CMD7(根据 CMD3 响应设置 arg
**示例:**
CMD3 后 R6 的 arg 为 0x0001xxxx。
CMD7 的 arg 应为 0x00010000。
7. 选择 4-bit 模式(可选)
CMD52(写入 0x07=0x02
8. 启用 Func1
CMD52(写入 0x02=0x02
9. 启用 SDIO 中断(使用中断线 (DAT1) 时必要)
CMD52(写入 0x04=0x03
10. 设置 Func0 块大小(可选,默认为 512 (0x200) 字节)
CMD52/53(读取 0x10~0x11
CMD52/53(写入 0x10=0x00
CMD52/53(写入 0x11=0x02
CMD52/53(再次读取 0x10~0x11, 检查最终写入的值)
11. 设置 Func1 块大小(可选,默认为 512 (0x200) 字节)
CMD52/53(读取 0x110~0x111
CMD52/53(写入 0x110=0x00
CMD52/53(写入 0x111=0x02
CMD52/53(再次读取 0x110~0x111, 检查最终写入的值)
.. _esp_slave_protocol_layer:
ESP SDIO 从机协议
--------------------------
ESP SDIO 从机协议基于 SDIO 规范的 I/O 读/写命令(即 CMD52 和 CMD53)创建。该协议提供以下服务:
- 发送 FIFO 和接收 FIFO
- 52 个主从机共享的 8 位读写寄存器(要了解详细信息,请参考 *{IDF_TARGET_NAME} 技术参考手册* > *SDIO 从机控制器* > *寄存器列表* > SDIO SLC Host 寄存器 [`PDF <{IDF_TARGET_TRM_CN_URL}#sdioslave-reg-summ>`__]
- 16 个通用中断源,其中 8 个从主机到从机的中断源,8 个自从机到主机的中断源。
开始通信前,主机需启用从机的 I/O Function 1,访问从机的寄存器,如下所示。
代码示例::example:`peripherals/sdio`
ESP32 SDIO 用作主机与 ESP32 SDIO 从机通信时,协议中涉及的逻辑由 :doc:`/api-reference/protocols/esp_serial_slave_link` 组件实现。
.. _esp_sdio_slave_shared_registers:
从机寄存器列表
^^^^^^^^^^^^^^^^^^^^^^
32 位
""""""
- 0x044 (TOKEN_RDATA): 第 27-16 位为接收 buffer 的数量。
- 0x058 (INT_ST): 保存自从机到主机的中断源位。
- 0x060 (PKT_LEN): 保存主机已读取的累计数据长度(以字节为单位),和已复制到缓冲区但尚未读取的数据。
- 0x0D4 (INT_CLR): 写 1 清除 与 INT_ST 对应的中断位。
- 0x0DC (INT_ENA): 从机到主机的中断屏蔽位。
8 位
"""""
共享通用寄存器:
- 0x06C-0x077: 读/写寄存器 0-11,主机和从机都可读写。
- 0x07A-0x07B: 读/写寄存器 14-15,主机和从机都可读写。
- 0x07E-0x07F: 读/写寄存器 18-19,主机和从机都可读写。
- 0x088-0x08B: 读/写寄存器 24-27,主机和从机都可读写。
- 0x09C-0x0BB: 读/写寄存器 32-63,主机和从机都可读写。
中断寄存器:
- 0x08D (SLAVE_INT):主机对从机的中断位,会自动清空。
FIFO(发送和接收)
""""""""""""""""""""""""""""
0x090 - 0x1F7FF 用作 FIFO。
CMD53 的地址与单次传输中从从机读取或写入从机的要求长度 (requested length) 有关。如下式所示:
*要求长度 = 0x1F800 - 地址*
从机返回的数据长度等于 CMD53 中定义的长度。如果数据长度大于 *requested length*,多余的数据会在发送时自动补零,或在接收时被丢弃,这一情况同时适用于 CMD53 的块模式和字节模式。
.. note::
将功能编号设置为 1,将 OP 也设置为 1(适用于 CMD53)。
为了提高以任意长度访问 FIFO 时的效率,可以将 CMD53 的块模式和字节模式结合使用。例如,如果块大小默认设置为 512 字节,则可以通过以下操作从 FIFO 中写入或获取 1031 字节的数据:
1. 在块模式下发送 CMD53block count = 21024 字节)到 0x1F3F9=0x1F800-**1031**
2. 然后在字节模式下发送 CMD53,byte count = 8(如果控制器支持也可为 7)到 0x1F7F9=0x1F800-**7**
.. _esp_sdio_slave_interrupts:
中断
^^^^
SDIO 中断属于电平敏感中断。对于主机中断,从机通过在适当时间拉低 DAT1 线的方式发送中断。当该中断线被拉低后,主机会检测到变化并读取 INT_ST 寄存器,确定中断源。然后,主机写入 INT_CLR 寄存器清除中断位,并处理中断。主机也可以清除 INT_ENA 寄存器中相应位以屏蔽不需要的源。如果所有源都已清除或已屏蔽,DAT1 线状态无效。
{IDF_TARGET_NAME} 上对应的 host_int 位:0 至 7。
对于从机中断,主机发起一次传输,将数据写入 SLAVE_INT 寄存器。一旦某位被写入 1,从机硬件和驱动程序就会检测到变化,通知应用程序。
.. _esp_sdio_slave_rcv_fifo:
接收 FIFO
^^^^^^^^^^^
要写入从机的接收 FIFO,主机应完成以下步骤:
1. **读取 TOKEN_RDATA (0x044) 寄存器的 TOKEN1 字段(27-16 位)。** 剩余的缓冲数量为 TOKEN1 减去主机使用的缓冲数量。
2. **确保有足够的 buffer**,(*buffer_size* x *buffer_num* 应大于要写入数据的长度,*buffer_size* 是主机和从机在开始通信前预定义的值)。如果 buffer 不够,重复步骤 1,直至满足要求为止。
3. **用 CMD53 写入 FIFO 地址**。注意, *requested length* 不应超过步骤 2 中计算出的长度,FIFO 地址与 *requested length* 有关。
4. **计算已使用的缓冲**。 注意,尾部的缓冲即使仅部分使用,也属于已使用的范围。
.. _esp_sdio_slave_send_fifo:
发送 FIFO
^^^^^^^^^^^
要读取从机的发送 FIFO,主机应完成以下步骤:
1. **等待中断线有效**。(可选,默认为低电平)
2. **读取(轮询)INT_ST 寄存器中的中断位**,以监控是否存在新数据包。
3. **如果新数据包已准备就绪,读取 PKT_LEN 寄存器**。在读取数据包之前,确定要读取数据的长度。由于主机会保留已从从机中读取的数据的长度,因此,要从 PKT_LEN 中减去该值,得到可读取的最大数据长度。如果发送 FIFO 中尚未写入过数据,则继续等待并轮询,直到从机准备就绪,然后更新 PKT_LEN。
4. **用 CMD53 从 FIFO 中读取数据**。注意, *要求长度* 应不大于步骤 3 中计算出的长度,FIFO 地址与 *要求长度* 相关。
5. **更新读取长度**
@@ -1 +1,120 @@
.. include:: ../../../en/api-reference/protocols/icmp_echo.rst
ICMP 回显
===========
:link_to_translation:`en:[English]`
概述
----
网际控制报文协议 (ICMP) 通常用于诊断或控制目的,或响应 IP 操作中的错误。常用的网络工具 ``ping`` 的应答,即 ``Echo Reply``,就是基于类型字段值为 0 的 ICMP 数据包实现的。
在 ping 会话中,首先由源主机发出请求包 (ICMP echo request),然后等待应答包 (ICMP echo reply),等待具有超时限制。通过这一过程,还能测量出信息的往返用时。收到有效的应答包后,源主机会生成 IP 链路层的统计数据(如失包率、运行时间等)。
IoT 设备通常需要检查远程服务器是否可用。如果服务器离线,设备应向用户发出警告。通过创建 ping 会话,定期发送或解析 ICMP echo 数据包,就能实现这一功能。
为简化这一过程方便用户操作,ESP-IDF 提供了一些好用的 API。
创建 ping 会话
^^^^^^^^^^^^^^^^^^^^^^^^^^^
要创建 ping 会话,首先需填写 ``esp_ping_config_t``,指定目标芯片 IP 地址、间隔时间等配置。此外,还可以通过 ``esp_ping_callbacks_t`` 注册回调函数。
创建 ping 会话并注册回调函数示例:
.. code-block:: c
static void test_on_ping_success(esp_ping_handle_t hdl, void *args)
{
// optionally, get callback arguments
// const char* str = (const char*) args;
// printf("%s\r\n", str); // "foo"
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
static void test_on_ping_timeout(esp_ping_handle_t hdl, void *args)
{
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
static void test_on_ping_end(esp_ping_handle_t hdl, void *args)
{
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
printf("%d packets transmitted, %d received, time %dms\n", transmitted, received, total_time_ms);
}
void initialize_ping()
{
/* convert URL to IP address */
ip_addr_t target_addr;
struct addrinfo hint;
struct addrinfo *res = NULL;
memset(&hint, 0, sizeof(hint));
memset(&target_addr, 0, sizeof(target_addr));
getaddrinfo("www.espressif.com", NULL, &hint, &res);
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
freeaddrinfo(res);
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
ping_config.target_addr = target_addr; // target IP address
ping_config.count = ESP_PING_COUNT_INFINITE; // ping in infinite mode, esp_ping_stop can stop it
/* set callback functions */
esp_ping_callbacks_t cbs;
cbs.on_ping_success = test_on_ping_success;
cbs.on_ping_timeout = test_on_ping_timeout;
cbs.on_ping_end = test_on_ping_end;
cbs.cb_args = "foo"; // arguments that will feed to all callback functions, can be NULL
cbs.cb_args = eth_event_group;
esp_ping_handle_t ping;
esp_ping_new_session(&ping_config, &cbs, &ping);
}
启动和停止 ping 会话
^^^^^^^^^^^^^^^^^^^^^^^^^^
使用 ``esp_ping_new_session`` 返回的句柄可以启动或停止 ping 会话。注意,ping 会话在创建后不会自动启动。如果 ping 会话停止后重启,ICMP 数据包的序列号会归零重新计数。
删除 ping 会话
^^^^^^^^^^^^^^^^^^^
如果不再使用 ping 会话,可用 ``esp_ping_delete_session`` 将其删除。在删除 ping 会话时,确保该会话已处于停止状态(即已调用了 ``esp_ping_stop`` ,或该会话已完成所有步骤)。
获取运行时间数据
^^^^^^^^^^^^^^^^^^
在回调函数中调用 ``esp_ping_get_profile``,可获取 ping 会话的不同运行时间数据,如上文代码示例所示。
应用示例
----------
ICMP echo 示例: :example:`protocols/icmp_echo`
API 参考
--------------
.. include-build-file:: inc/ping_sock.inc