From 9f35cd4fb213c4b9e5be994be692205b69c6e639 Mon Sep 17 00:00:00 2001 From: Xingyu Wang Date: Sat, 10 Oct 2020 09:51:48 +0800 Subject: [PATCH 1/8] PRF @MjSeven --- ...08 How to install software with Ansible.md | 82 +++++++++++-------- 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/translated/tech/20200908 How to install software with Ansible.md b/translated/tech/20200908 How to install software with Ansible.md index 3074036eb1..bb806adf17 100644 --- a/translated/tech/20200908 How to install software with Ansible.md +++ b/translated/tech/20200908 How to install software with Ansible.md @@ -1,6 +1,6 @@ [#]: collector: (lujun9972) [#]: translator: (MjSeven) -[#]: reviewer: ( ) +[#]: reviewer: (wxy) [#]: publisher: ( ) [#]: url: ( ) [#]: subject: (How to install software with Ansible) @@ -10,51 +10,55 @@ 如何使用 Ansible 安装软件 ====== -使用 Ansible 剧本自动安装和更新设备上的软件。 -![Puzzle pieces coming together to form a computer screen][1] +> 使用 Ansible 剧本自动安装和更新设备上的软件。 -Ansible 是系统管理员和开发人员用来保持计算机系统处于最佳状态的一种流行的自动化工具。与可扩展框架一样,[Ansible][2] 本身功能有限,它真正的功能体现在许多模块中。在某种程度上,Ansible 模块是 [Linux][3] 系统的命令。它们针对特定问题提供解决方案。维护计算机时的一项常见任务是使所有计算机的更新和一致。 +![](https://img.linux.net.cn/data/attachment/album/202010/10/095024hh65atkh6cc8ntn9.jpg) -我曾经使用软件包的文本列表来保持系统或多或少同步:我会列出笔记本电脑上安装的软件包,然后将其与台式机或另一台服务器之间进行交叉引用,手动弥补差异。当然,在 Linux 机器上安装和维护应用程序是 Ansible 的一项基本功能,这意味着你可以在自己关心的计算机上列出所需的内容。 +Ansible 是系统管理员和开发人员用来保持计算机系统处于最佳状态的一种流行的自动化工具。与可扩展框架一样,[Ansible][2] 本身功能有限,它真正的功能体现在许多模块中。在某种程度上,Ansible 模块就是 [Linux][3] 系统的命令。它们针对特定问题提供解决方案,而维护计算机时的一项常见任务是使所有计算机的更新和一致。 -### 寻找正确的 Ansible +我曾经使用软件包的文本列表来保持系统或多或少的同步:我会列出笔记本电脑上安装的软件包,然后将其与台式机或另一台服务器之间进行交叉参考,手动弥补差异。当然,在 Linux 机器上安装和维护应用程序是 Ansible 的一项基本功能,这意味着你可以在自己关心的计算机上列出所需的内容。 -Ansible 模块的数量非常庞大,如何找到能完成你任务的模块?在 Linux 中,你可以在应用程序菜单或 `/usr/bin` 中查找要运行的应用程序。使用 Ansible 时,参考 [Ansible 模块索引][4]。 +### 寻找正确的 Ansible 模块 + +Ansible 模块的数量非常庞大,如何找到能完成你任务的模块?在 Linux 中,你可以在应用程序菜单或 `/usr/bin` 中查找要运行的应用程序。使用 Ansible 时,你可以参考 [Ansible 模块索引][4]。 这个索引按照类别列出。稍加搜索,你就很可能找到所需的模块。对于包管理,[Packaging 模块][5]几乎适用于所有带包管理器的系统。 ### 动手写一个 Ansible 剧本 -首先,选择本地计算机上的包管理器。例如,如果你打算在运行 Fedora 的笔记本电脑上编写 Ansible 指令(在 Ansible 中称为“剧本”),那么从 dnf 模块开始。如果你在 Elementary OS 上编写,使用 `apt` 模块,以此类推。这样你就可以开始进行测试和验证,并可以在以后扩展到其它计算机。 +首先,选择本地计算机上的包管理器。例如,如果你打算在运行 Fedora 的笔记本电脑上编写 Ansible 指令(在 Ansible 中称为“剧本playbook”),那么从 `dnf` 模块开始。如果你在 Elementary OS 上编写,使用 `apt` 模块,以此类推。这样你就可以开始进行测试和验证,并可以在以后扩展到其它计算机。 + +第一步是创建一个代表你的剧本的目录。这不是绝对必要的,但这是一个好习惯。Ansible 只需要一个配置文件就可以运行在 YAML 中,但是如果你以后想要扩展剧本,你就可以通过改变目录和文件的方式来控制 Ansible。现在,只需创建一个名为 `install_packages` 或类似的目录: -第一步是创建一个代表你剧本的目录。这不是绝对必要的,但这是一个好习惯。Ansible 只需要一个配置文件就可以运行在 YAML 中,但是如果你以后想要扩展剧本,你就可以通过改变目录和文件的方式来控制 Ansible。现在,只需创建一个名为 `install_packages` 或类似的目录: ``` $ mkdir ~/install_packages ``` 你可以根据自己的喜好来命名 Ansible 的剧本,但通常将其命名为 `site.yml`: + ``` $ touch ~/install_packages/site.yml ``` 在你最喜欢的文本编辑器中打开 `site.yml`,添加以下内容: + ``` -\--- -\- hosts: localhost -  tasks: -    - name: install packages -      become: true -      become_user: root -      dnf: -        state: present -        name: -         - tcsh -         - htop +--- +- hosts: localhost + tasks: + - name: install packages + become: true + become_user: root + dnf: + state: present + name: + - tcsh + - htop ``` -你必须调整使用的模块名称来匹配你使用的发行版。在此示例中,我使用 `dnf` 是因为我在 Fedora Linux 上编写剧本。 +你必须调整使用的模块名称以匹配你使用的发行版。在此示例中,我使用 `dnf` 是因为我在 Fedora Linux 上编写剧本。 -就像 Linux 终端中的命令一样,知道 _如何_ 来调用 Ansible 模块就已经成功了一半。这个示例剧本遵循标准剧本格式: +就像 Linux 终端中的命令一样,知道 **如何** 来调用 Ansible 模块就已经成功了一半。这个示例剧本遵循标准剧本格式: * `hosts` 是一台或多台计算机。在本示例中,目标计算机是 `localhost`,即你当前正在使用的计算机(而不是你希望 Ansible 连接的远程系统)。 * `tasks` 是你要在主机上执行的任务列表。 @@ -66,9 +70,8 @@ $ touch ~/install_packages/site.yml `dnf` 下的节点是 `dnf` 模块专用的。这是模块文档的关键所在。就像 Linux 命令的手册页一样,模块文档会告诉你可用的选项和所需的参数。 ![Ansible 文档][6] -Ansible module documentation (Seth Kenlon, [CC BY-SA 4.0][7]) -安装软件包是一个相对简单的任务,仅需要两个元素。`state` 选项指示 Ansible 检查系统上是否存在 _软件包_,而 `name` 选项列出要查找的软件包。Ansible 会处理机器 _状态_,因此模块指令始终意味着更改。假如 Ansible 扫描了系统状态,发现剧本里描述的系统(在本例中,`tcsh` 和 `htop` 存在)与实际状态存在冲突,那么 Ansible 的任务是进行必要的更改来使系统与剧本匹配。Ansible 可以通过 dnf(或 apt 或者其它任何包管理器)模块进行更改。 +安装软件包是一个相对简单的任务,仅需要两个元素。`state` 选项指示 Ansible 检查系统上是否存在 **软件包**,而 `name` 选项列出要查找的软件包。Ansible 会针对机器的 **状态** 进行调整,因此模块指令始终意味着更改。假如 Ansible 扫描了系统状态,发现剧本里描述的系统(在本例中,`tcsh` 和 `htop` 存在)与实际状态存在冲突,那么 Ansible 的任务是进行必要的更改来使系统与剧本匹配。Ansible 可以通过 `dnf`(或 `apt` 或者其它任何包管理器)模块进行更改。 每个模块可能都有一组不同的选项,所以在编写剧本时,要经常参考模块文档。除非你对模块非常熟悉,否则这是期望模块完成工作的唯一合理方法。 @@ -77,16 +80,19 @@ Ansible module documentation (Seth Kenlon, [CC BY-SA 4.0][7]) 剧本是用 YAML 编写的。因为 YAML 遵循严格的语法,所以安装 `yamllint` 来检查剧本是很有帮助的。更妙的是,有一个专门针对 Ansible 的检查工具称为 `ansible-lint`,它专门为剧本而生。在继续之前,安装它。 在 Fedora 或 CentOs 上: + ``` $ sudo dnf ins tall yamllint python3-ansible-lint ``` 在 Debian、Elementary 或 Ubuntu 上,同样的: + ``` $ sudo apt install yamllint ansible-lint ``` 使用 `ansible-link` 来验证你的剧本。如果你无法使用 `ansible-lint`,你可以使用 `yamllint`。 + ``` $ ansible-lint ~/install_packages/site.yml ``` @@ -98,6 +104,7 @@ $ ansible-lint ~/install_packages/site.yml 现在你有了一个可验证的有效剧本,你终于可以在本地计算机上运行它了,因为你碰巧知道该剧本定义的任务需要 root 权限,所以在调用 Ansible 时必须使用 `--ask-become-pass` 选项,因此系统会提示你输入管理员密码。 开始安装: + ``` $ ansible-playbook --ask-become-pass ~/install_packages/site.yml BECOME password: @@ -121,15 +128,15 @@ localhost: ok=0 changed=2 unreachable=0 failed=0 [...] 要连接到远程系统,你必须在 `/etc/ansible/hosts` 文件中定义远程系统,该文件与 Ansible 是一起安装的,所以它已经存在了,但它可能是空的,除了一些解释性注释之外。使用 `sudo` 在你喜欢的文本编辑器中打开它。 -只要主机名可以解析,就可以通过其 IP 地址或主机名定义主机。例如,如果你已经在 `/etc/hosts` 中定义了 `liavara` 并可以成功 ping 通,那么你可以在 `/etc/ansible/hosts` 中将 `liavara` 设置为主机。或者,如果你正在运行一个域名服务器或 Avahi 服务器并且可以 ping 通 `liavara`,那么你就可以在 `/etc/ansible/hosts` 中定义它。否则,你必须使用它的 IP 地址。 +你可以通过其 IP 地址或主机名(只要主机名可以解析)定义主机。例如,如果你已经在 `/etc/hosts` 中定义了 `liavara` 并可以成功 `ping` 通,那么你可以在 `/etc/ansible/hosts` 中将 `liavara` 设置为主机。或者,如果你正在运行一个域名服务器或 Avahi 服务器并且可以 `ping` 通 `liavara`,那么你就可以在 `/etc/ansible/hosts` 中定义它。否则,你必须使用它的 IP 地址。 你还必须成功地建立与目标主机的安全 shell(SSH)连接。最简单的方法是使用 `ssh-copy-id` 命令,但是如果你以前从未与主机建立 SSH 连接,[阅读我关于如何创建自动 SSH 连接的文章][8]。 一旦你在 `/etc/ansible/hosts` 文件中输入了主机名或 IP 地址后,你就可以在剧本中更改 `hosts` 定义: ``` -\--- -\- hosts: all +--- +- hosts: all   tasks:     - name: install packages       become: true @@ -141,7 +148,8 @@ localhost: ok=0 changed=2 unreachable=0 failed=0 [...]          - htop ``` -再次运行 `ansible-playbook`: +再次运行 `ansible-playbook`: + ``` $ ansible-playbook --ask-become-pass ~/install_packages/site.yml ``` @@ -152,29 +160,31 @@ $ ansible-playbook --ask-become-pass ~/install_packages/site.yml ### 适用于混合环境的 Ansible -到目前为止,我们一直假定 Ansible 配置的所有主机都运行相同的操作系统(都是是使用 **dnf** 命令进行程序包管理的操作系统)。那么,如果你要管理不同发行版的主机,例如 Ubuntu(使用 **apt**)或 Arch(使用 **pacman**),或者其它的操作系统时,该怎么办? +到目前为止,我们一直假定 Ansible 配置的所有主机都运行相同的操作系统(都是是使用 `dnf` 命令进行程序包管理的操作系统)。那么,如果你要管理不同发行版的主机,例如 Ubuntu(使用 `apt`)或 Arch(使用 `pacman`),或者其它的操作系统时,该怎么办? 只要目标操作系统具有程序包管理器([MacOs 有 Homebrew][9],[Windows 有 Chocolatey][10]),Ansible 就能派上用场。 -这就是 Ansible 优势最明显的地方。在 shell 脚本中,你必须检查目标主机上有哪些可用的包管理器,即使使用纯 Python,也必须检查操作系统。Ansible 不仅内置了这些功能,而且还具有在剧本中使用命令结果的机制。你可以使用 **action** 关键字来执行由 Ansible 事实收集子系统提供的变量定义的任务,而不是使用 **dnf** 模块。 +这就是 Ansible 优势最明显的地方。在 shell 脚本中,你必须检查目标主机上有哪些可用的包管理器,即使使用纯 Python,也必须检查操作系统。Ansible 不仅内置了这些功能,而且还具有在剧本中使用命令结果的机制。你可以使用 `action` 关键字来执行由 Ansible 事实收集子系统提供的变量定义的任务,而不是使用 `dnf` 模块。 + ``` -\--- -\- hosts: all +--- +- hosts: all   tasks:     - name: install packages       become: true       become_user: root -      action: > +      action: >        {{ ansible_pkg_mgr }} name=htop,transmission state=present update_cache=yes ``` -**action** 关键字会加载目标插件。在本例中,它使用了 **ansible_pkg_mgr** 变量,该变量由 Ansible 在初始 **收集信息** 期间填充。你不需要告诉 Ansible 收集有关其运行操作系统的事实,所以很容易忽略这一点,但是当你运行一个剧本时,你会在默认输出中看到它: +`action` 关键字会加载目标插件。在本例中,它使用了 `ansible_pkg_mgr` 变量,该变量由 Ansible 在初始 **收集信息** 期间填充。你不需要告诉 Ansible 收集有关其运行操作系统的事实,所以很容易忽略这一点,但是当你运行一个剧本时,你会在默认输出中看到它: + ``` TASK [Gathering Facts] ***************************************** ok: [localhost] ``` -**action** 插件使用来自这个探针的信息,使用相关的包管理器命令填充 **ansible_pkg_mgr**,以安装在 **name** 参数之后列出的程序包。使用 8 行代码,你可以克服在其它脚本选项中很少允许的复杂跨平台难题。 +`action` 插件使用来自这个探针的信息,使用相关的包管理器命令填充 `ansible_pkg_mgr`,以安装在 `name` 参数之后列出的程序包。使用 8 行代码,你可以克服在其它脚本选项中很少允许的复杂跨平台难题。 ### 使用 Ansible @@ -187,7 +197,7 @@ via: https://opensource.com/article/20/9/install-packages-ansible 作者:[Seth Kenlon][a] 选题:[lujun9972][b] 译者:[MjSeven](https://github.com/MjSeven) -校对:[校对者ID](https://github.com/校对者ID) +校对:[wxy](https://github.com/wxy) 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 From 99e8b7f3b6e29f45fef82f51b98ec09ce1e8d724 Mon Sep 17 00:00:00 2001 From: Xingyu Wang Date: Sat, 10 Oct 2020 09:52:27 +0800 Subject: [PATCH 2/8] PUB @MjSeven https://linux.cn/article-12703-1.html --- .../20200908 How to install software with Ansible.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename {translated/tech => published}/20200908 How to install software with Ansible.md (99%) diff --git a/translated/tech/20200908 How to install software with Ansible.md b/published/20200908 How to install software with Ansible.md similarity index 99% rename from translated/tech/20200908 How to install software with Ansible.md rename to published/20200908 How to install software with Ansible.md index bb806adf17..54fb204e83 100644 --- a/translated/tech/20200908 How to install software with Ansible.md +++ b/published/20200908 How to install software with Ansible.md @@ -1,8 +1,8 @@ [#]: collector: (lujun9972) [#]: translator: (MjSeven) [#]: reviewer: (wxy) -[#]: publisher: ( ) -[#]: url: ( ) +[#]: publisher: (wxy) +[#]: url: (https://linux.cn/article-12703-1.html) [#]: subject: (How to install software with Ansible) [#]: via: (https://opensource.com/article/20/9/install-packages-ansible) [#]: author: (Seth Kenlon https://opensource.com/users/seth) From fcf1942e172417102d07265883d8d74f7e55e896 Mon Sep 17 00:00:00 2001 From: Xingyu Wang Date: Sat, 10 Oct 2020 09:57:44 +0800 Subject: [PATCH 3/8] PRF --- published/20200908 How to install software with Ansible.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/published/20200908 How to install software with Ansible.md b/published/20200908 How to install software with Ansible.md index 54fb204e83..40a7ea77ad 100644 --- a/published/20200908 How to install software with Ansible.md +++ b/published/20200908 How to install software with Ansible.md @@ -97,7 +97,7 @@ $ sudo apt install yamllint ansible-lint $ ansible-lint ~/install_packages/site.yml ``` -成功则不返回任何内容,但如果文件中有错误,则必须先修复它们,然后再继续。复制和粘贴过程中的常见错误包括在最后一行的末尾省略换行符、使用制表符而不是空格来缩进。在文本编辑器中修复它们,重新运行 `ansible-llint`,重复这个过程,直到 `ansible-lint` 或 `yamllint` 没有返回为止。 +成功则不返回任何内容,但如果文件中有错误,则必须先修复它们,然后再继续。复制和粘贴过程中的常见错误包括在最后一行的末尾省略换行符、使用制表符而不是空格来缩进。在文本编辑器中修复它们,重新运行 `ansible-lint`,重复这个过程,直到 `ansible-lint` 或 `yamllint` 没有返回为止。 ### 使用 Ansible 安装一个应用 From 733c4ea3db245139efb69ba0159e374d59dca6ff Mon Sep 17 00:00:00 2001 From: Xingyu Wang Date: Sat, 10 Oct 2020 10:15:57 +0800 Subject: [PATCH 4/8] PRF @gxlct008 --- ...6 Building a Messenger App- Access Page.md | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/translated/tech/20180716 Building a Messenger App- Access Page.md b/translated/tech/20180716 Building a Messenger App- Access Page.md index 1e195a4b55..eac7e3e314 100644 --- a/translated/tech/20180716 Building a Messenger App- Access Page.md +++ b/translated/tech/20180716 Building a Messenger App- Access Page.md @@ -1,6 +1,6 @@ [#]: collector: (lujun9972) [#]: translator: (gxlct008) -[#]: reviewer: ( ) +[#]: reviewer: (wxy) [#]: publisher: ( ) [#]: url: ( ) [#]: subject: (Building a Messenger App: Access Page) @@ -10,6 +10,8 @@ 构建一个即时消息应用(七):Access 页面 ====== +![](https://img.linux.net.cn/data/attachment/album/202010/10/101345zj7gfybyee2g9x9e.jpg) + 本文是该系列的第七篇。 * [第一篇: 模式][1] @@ -38,7 +40,7 @@ ``` -这个 HTML 文件必须为每个 URL 提供服务,并且将使用 JavaScript 负责呈现正确的页面。 +这个 HTML 文件必须为每个 URL 提供服务,并且使用 JavaScript 负责呈现正确的页面。 因此,让我们将注意力转到 `main.go` 片刻,然后在 `main()` 函数中添加以下路由: @@ -96,11 +98,9 @@ function view(pageName) { } ``` -如果您是这个博客的关注者,您已经知道它是如何工作的了。 该路由器就是在 [这里][7] 显示的那个。 只需从 [@nicolasparada/router][8] 下载并保存到 `static/router.js` 即可。 +如果你是这个博客的关注者,你已经知道它是如何工作的了。 该路由器就是在 [这里][7] 显示的那个。 只需从 [@nicolasparada/router][8] 下载并保存到 `static/router.js` 即可。 -We registered four routes. At the root `/` we show the home or access page whether the user is authenticated. At `/callback` we show the callback page. On `/conversations/{conversationID}` we show the conversation or access page whether the user is authenticated and for every other URL, we show a not found page. - -我们注册了四条路由。 在根路由 `/` 处,我们展示 home 或 access 页面,无论用户是否通过身份验证。 在 `/callback` 中,我们展示 callback 页面。 在 `/conversations/{conversationID}` 上,我们展示对话或 access 页面,无论用户是否通过验证,对于其他 URL,我们展示一个 not found 页面。 +我们注册了四条路由。 在根路由 `/` 处,我们展示 `home` 或 `access` 页面,无论用户是否通过身份验证。 在 `/callback` 中,我们展示 `callback` 页面。 在 `/conversations/{conversationID}` 上,我们展示对话或 `access` 页面,无论用户是否通过验证,对于其他 URL,我们展示一个 `not-found` 页面。 我们告诉路由器将结果渲染为文档主体,并在离开之前向每个页面调度一个 `disconnect` 事件。 @@ -108,8 +108,7 @@ We registered four routes. At the root `/` we show the home or access page wheth ### 身份验证 -`guard()` 是一个函数,给它两个函数作为参数,如果用户通过了身份验证,则执行第一个函数,否则执行第二个。 -它来自 `auth.js`,所以我们创建一个包含以下内容的 `static/auth.js` 文件: +`guard()` 是一个函数,给它两个函数作为参数,如果用户通过了身份验证,则执行第一个函数,否则执行第二个。它来自 `auth.js`,所以我们创建一个包含以下内容的 `static/auth.js` 文件: ```javascript export function isAuthenticated() { @@ -151,15 +150,15 @@ export function getAuthUser() { } ``` -`isAuthenticated()` 检查 localStorage 中的 `token` 和 `expires_at`,以判断用户是否已通过身份验证。`getAuthUser()` 从 localStorage 中获取经过身份验证的用户。 +`isAuthenticated()` 检查 `localStorage` 中的 `token` 和 `expires_at`,以判断用户是否已通过身份验证。`getAuthUser()` 从 `localStorage` 中获取经过身份验证的用户。 -当我们登录时,我们会将所有的数据保存到 localStorage,这样才有意义。 +当我们登录时,我们会将所有的数据保存到 `localStorage`,这样才有意义。 ### Access 页面 ![access page screenshot][9] -让我们从 access 页面开始。 创建一个包含以下内容的文件 `static/pages/access-page.js`: +让我们从 `access` 页面开始。 创建一个包含以下内容的文件 `static/pages/access-page.js`: ```javascript const template = document.createElement('template') @@ -175,7 +174,7 @@ export default function accessPage() { 因为路由器会拦截所有链接点击来进行导航,所以我们必须特别阻止此链接的事件传播。 -单击该链接会将我们重定向到后端,然后重定向到 GitHub,再重定向到后端,然后再次重定向到前端; 到 callback 页面。 +单击该链接会将我们重定向到后端,然后重定向到 GitHub,再重定向到后端,然后再次重定向到前端; 到 `callback` 页面。 ### Callback 页面 @@ -212,7 +211,7 @@ function getAuthUser(token) { } ``` -callback 页面不呈现任何内容。这是一个异步函数,它使用 URL 查询字符串中的 token 向 `/api/auth_user` 发出 GET 请求,并将所有数据保存到 localStorage。 然后重定向到 `/`。 +`callback` 页面不呈现任何内容。这是一个异步函数,它使用 URL 查询字符串中的 token 向 `/api/auth_user` 发出 GET 请求,并将所有数据保存到 `localStorage`。 然后重定向到 `/`。 ### HTTP @@ -304,7 +303,7 @@ export default { ![home page screenshot][12] -因此,当用户登录时,将显示主页。 创建一个具有以下内容的 `static/pages/home-page.js` 文件: +因此,当用户登录时,将显示 `home` 页。 创建一个具有以下内容的 `static/pages/home-page.js` 文件: ```javascript import { getAuthUser } from '../auth.js' @@ -335,9 +334,9 @@ function onLogoutClick() { } ``` -对于这篇文章,这是我们在主页上呈现的唯一内容。我们显示当前经过身份验证的用户和注销按钮。 +对于这篇文章,这是我们在 `home` 页上呈现的唯一内容。我们显示当前经过身份验证的用户和注销按钮。 -当用户单击注销时,我们清除 localStorage 中的所有内容并重新加载页面。 +当用户单击注销时,我们清除 `localStorage` 中的所有内容并重新加载页面。 ### Avatar @@ -351,11 +350,9 @@ export function avatar(user) { } ``` -We use a small figure with the user’s initial in case the avatar URL is null. -如果头像网址为 null,我们将使用用户的姓名首字母作为初始头像。 +如果头像网址为 `null`,我们将使用用户的姓名首字母作为初始头像。 -您可以使用 `attr()` 函数显示带有少量 CSS 样式的首字母。 -You can show the initial with a little of CSS using the `attr()` function. +你可以使用 `attr()` 函数显示带有少量 CSS 样式的首字母。 ```css .avatar[data-initial]::after { @@ -367,7 +364,7 @@ You can show the initial with a little of CSS using the `attr()` function. ![access page with login form screenshot][13] -在上一篇文章中,我们为编写了一个登录代码。让我们在 access 页面中为此添加一个表单。 进入 `static/ages/access-page.js`,稍微修改一下。 +在上一篇文章中,我们为编写了一个登录代码。让我们在 `access` 页面中为此添加一个表单。 进入 `static/ages/access-page.js`,稍微修改一下。 ```javascript import http from '../http.js' @@ -423,7 +420,7 @@ function login(username) { } ``` -我添加了一个登录表单。当用户提交表单时。它使用用户名对 `/api/login` 进行 POST 请求。将所有数据保存到 localStorage 并重新加载页面。 +我添加了一个登录表单。当用户提交表单时。它使用用户名对 `/api/login` 进行 POST 请求。将所有数据保存到 `localStorage` 并重新加载页面。 记住在前端完成后删除此表单。 @@ -431,7 +428,7 @@ function login(username) { 这就是这篇文章的全部内容。在下一篇文章中,我们将继续使用主页添加一个表单来开始对话,并显示包含最新对话的列表。 -[Souce Code][14] +- [源代码][14] -------------------------------------------------------------------------------- @@ -439,19 +436,19 @@ via: https://nicolasparada.netlify.com/posts/go-messenger-access-page/ 作者:[Nicolás Parada][a] 选题:[lujun9972][b] -译者:[译者ID](https://github.com/gxlct008) -校对:[校对者ID](https://github.com/校对者ID) +译者:[gxlct008](https://github.com/gxlct008) +校对:[wxy](https://github.com/wxy) 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 [a]: https://nicolasparada.netlify.com/ [b]: https://github.com/lujun9972 -[1]: https://nicolasparada.netlify.com/posts/go-messenger-schema/ -[2]: https://nicolasparada.netlify.com/posts/go-messenger-oauth/ -[3]: https://nicolasparada.netlify.com/posts/go-messenger-conversations/ -[4]: https://nicolasparada.netlify.com/posts/go-messenger-messages/ -[5]: https://nicolasparada.netlify.com/posts/go-messenger-realtime-messages/ -[6]: https://nicolasparada.netlify.com/posts/go-messenger-dev-login/ +[1]: https://linux.cn/article-11396-1.html +[2]: https://linux.cn/article-11510-1.html +[3]: https://linux.cn/article-12056-1.html +[4]: https://linux.cn/article-12680-1.html +[5]: https://linux.cn/article-12685-1.html +[6]: https://linux.cn/article-12692-1.html [7]: https://nicolasparada.netlify.com/posts/js-router/ [8]: https://unpkg.com/@nicolasparada/router [9]: https://nicolasparada.netlify.com/img/go-messenger-access-page/access-page.png From 6a69ac51b5d6070090390f41f5f1da5de4da8032 Mon Sep 17 00:00:00 2001 From: Xingyu Wang Date: Sat, 10 Oct 2020 10:28:55 +0800 Subject: [PATCH 5/8] PUB @gxlct008 https://linux.cn/article-12704-1.html --- .../20180716 Building a Messenger App- Access Page.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename {translated/tech => published}/20180716 Building a Messenger App- Access Page.md (99%) diff --git a/translated/tech/20180716 Building a Messenger App- Access Page.md b/published/20180716 Building a Messenger App- Access Page.md similarity index 99% rename from translated/tech/20180716 Building a Messenger App- Access Page.md rename to published/20180716 Building a Messenger App- Access Page.md index eac7e3e314..b35c83efdc 100644 --- a/translated/tech/20180716 Building a Messenger App- Access Page.md +++ b/published/20180716 Building a Messenger App- Access Page.md @@ -1,8 +1,8 @@ [#]: collector: (lujun9972) [#]: translator: (gxlct008) [#]: reviewer: (wxy) -[#]: publisher: ( ) -[#]: url: ( ) +[#]: publisher: (wxy) +[#]: url: (https://linux.cn/article-12704-1.html) [#]: subject: (Building a Messenger App: Access Page) [#]: via: (https://nicolasparada.netlify.com/posts/go-messenger-access-page/) [#]: author: (Nicolás Parada https://nicolasparada.netlify.com/) From 48f21e2f80890bec1542719d295b98e6cf8ecb46 Mon Sep 17 00:00:00 2001 From: XianLei Gao <279483350@qq.com> Date: Sat, 10 Oct 2020 12:33:48 +0800 Subject: [PATCH 6/8] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E8=AF=91=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...180414 Go on very small hardware Part 2.md | 697 +++++++++--------- 1 file changed, 333 insertions(+), 364 deletions(-) diff --git a/sources/tech/20180414 Go on very small hardware Part 2.md b/sources/tech/20180414 Go on very small hardware Part 2.md index 38344cf427..ab4f10a9ec 100644 --- a/sources/tech/20180414 Go on very small hardware Part 2.md +++ b/sources/tech/20180414 Go on very small hardware Part 2.md @@ -3,188 +3,186 @@ [#]: reviewer: ( ) [#]: publisher: ( ) [#]: url: ( ) -[#]: subject: (Go on very small hardware (Part 2)) +[#]: subject: (Go on very small hardware Part 2) [#]: via: (https://ziutek.github.io/2018/04/14/go_on_very_small_hardware2.html) [#]: author: (Michał Derkacz https://ziutek.github.io/) -Go on very small hardware (Part 2) +Go 语言在极小硬件上的运用(二) ============================================================ - [![STM32F030F4P6](https://ziutek.github.io/images/mcu/f030-demo-board/board.jpg)][1] + [![STM32F030F4P6](https://ziutek.github.io/images/mcu/f030-demo-board/board.jpg)][1] -At the end of the [first part][2] of this article I promised to write something about  _interfaces_ . I don’t want to write here a complete or even brief lecture about the interfaces. Instead, I’ll show a simple example how to define and use an interface, and then, how to take advantage of ubiquitous  _io.Writer_  interface. There will also be a few words about  _reflection_  and  _semihosting_ . -Interfaces are a crucial part of Go language. If you want to learn more about them, I suggest to read [Effective Go][3] and [Russ Cox article][4]. +在本文的 [第一部分][2] 的结尾,我承诺要写关于 _interfaces_ 的内容。我不想在这里写有关接口的完整甚至简短的讲义。相反,我将展示一个简单的示例,来说明如何定义和使用接口,以及如何利用无处不在的 _io.Writer_ 接口。还有一些关于 _reflection_ 和 _semihosting_ 的内容。 -### Concurrent Blinky – revisited +接口是 Go 语言的重要组成部分。如果您想了解更多有关它们的信息,我建议您阅读 [Effective Go][3] 和 [Russ Cox 的文章][4]。 -When you read the code of previous examples you probably noticed a counterintuitive way to turn the LED on or off. The  _Set_  method was used to turn the LED off and the  _Clear_  method was used to turn the LED on. This is due to driving the LEDs in open-drain configuration. What we can do to make the code less confusing? Let’s define the  _LED_  type with  _On_  and  _Off_  methods: +### 并发 Blinky – 回顾 + +当您阅读前面示例的代码时,您可能会注意到一个违反直觉的方式来打开或关闭 LED。 _Set_ 方法用于关闭 LED,_Clear_ 方法用于打开 LED。这是由于在 漏极开路配置open-drain configuration 下驱动了 LED。我们可以做些什么来减少代码的混乱? 让我们用 _On_ 和 _Off_ 方法来定义 _LED_ 类型: ``` type LED struct { - pin gpio.Pin + pin gpio.Pin } func (led LED) On() { - led.pin.Clear() + led.pin.Clear() } func (led LED) Off() { - led.pin.Set() + led.pin.Set() } ``` -Now we can simply call `led.On()` and `led.Off()` which no longer raises any doubts. +现在我们可以简单地调用 `led.On()` 和 `led.Off()`,这不会再引起任何疑惑了。 -In all previous examples I tried to use the same open-drain configuration to don’t complicate the code. But in the last example, it would be easier for me to connect the third LED between GND and PA3 pins and configure PA3 in push-pull mode. The next example will use a LED connected this way. -But our new  _LED_  type doesn’t support the push-pull configuration. In fact, we should call it  _OpenDrainLED_  and define another  _PushPullLED_  type: +在前面的所有示例中,我都尝试使用相同的 漏极开路配置open-drain configuration来 避免代码复杂化。但是在最后一个示例中,对于我来说,将第三个 LED 连接到 GND 和 PA3 引脚之间并将 PA3 配置为推挽模式push-pull mode会更容易。下一个示例将使用以此方式连接的 LED。 + +但是我们的新 _LED_ 类型不支持推挽配置。实际上,我们应该将其称为 _OpenDrainLED_,并定义另一个类型 _PushPullLED_: ``` type PushPullLED struct { - pin gpio.Pin + pin gpio.Pin } func (led PushPullLED) On() { - led.pin.Set() + led.pin.Set() } func (led PushPullLED) Off() { - led.pin.Clear() + led.pin.Clear() } ``` -Note, that both types have the same methods that work the same. It would be nice if the code that operates on LEDs could use both types, without paying attention to which one it uses at the moment. The  _interface type_  comes to help: +请注意,这两种类型都具有相同的方法,它们的工作方式也相同。如果在 LED 上运行的代码可以同时使用这两种类型,而不必注意当前使用的是哪种类型,那就太好了。 _interface type_ 可以提供帮助: ``` package main import ( - "delay" + "delay" - "stm32/hal/gpio" - "stm32/hal/system" - "stm32/hal/system/timer/systick" + "stm32/hal/gpio" + "stm32/hal/system" + "stm32/hal/system/timer/systick" ) type LED interface { - On() - Off() + On() + Off() } type PushPullLED struct{ pin gpio.Pin } func (led PushPullLED) On() { - led.pin.Set() + led.pin.Set() } func (led PushPullLED) Off() { - led.pin.Clear() + led.pin.Clear() } func MakePushPullLED(pin gpio.Pin) PushPullLED { - pin.Setup(&gpio.Config{Mode: gpio.Out, Driver: gpio.PushPull}) - return PushPullLED{pin} + pin.Setup(&gpio.Config{Mode: gpio.Out, Driver: gpio.PushPull}) + return PushPullLED{pin} } type OpenDrainLED struct{ pin gpio.Pin } func (led OpenDrainLED) On() { - led.pin.Clear() + led.pin.Clear() } func (led OpenDrainLED) Off() { - led.pin.Set() + led.pin.Set() } func MakeOpenDrainLED(pin gpio.Pin) OpenDrainLED { - pin.Setup(&gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain}) - return OpenDrainLED{pin} + pin.Setup(&gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain}) + return OpenDrainLED{pin} } var led1, led2 LED func init() { - system.SetupPLL(8, 1, 48/8) - systick.Setup(2e6) + system.SetupPLL(8, 1, 48/8) + systick.Setup(2e6) - gpio.A.EnableClock(false) - led1 = MakeOpenDrainLED(gpio.A.Pin(4)) - led2 = MakePushPullLED(gpio.A.Pin(3)) + gpio.A.EnableClock(false) + led1 = MakeOpenDrainLED(gpio.A.Pin(4)) + led2 = MakePushPullLED(gpio.A.Pin(3)) } func blinky(led LED, period int) { - for { - led.On() - delay.Millisec(100) - led.Off() - delay.Millisec(period - 100) - } + for { + led.On() + delay.Millisec(100) + led.Off() + delay.Millisec(period - 100) + } } func main() { - go blinky(led1, 500) - blinky(led2, 1000) + go blinky(led1, 500) + blinky(led2, 1000) } ``` -We’ve defined  _LED_  interface that has two methods:  _On_  and  _Off_ . The  _PushPullLED_  and  _OpenDrainLED_ types represent two ways of driving LEDs. We also defined two  _Make_ _*LED_  functions which act as constructors. Both types implement the  _LED_  interface, so the values of these types can be assigned to the variables of type  _LED_ : +我们定义了 _LED_ 接口,它有两个方法: _On_ 和 _Off_。 _PushPullLED_ 和 _OpenDrainLED_ 类型代表两种驱动 LED 的方式。我们还定义了两个用作构造函数的 _Make_ _*LED_ 函数。这两种类型都实现了 _LED_ 接口,因此可以将这些类型的值赋给 _LED_ 类型的变量: ``` led1 = MakeOpenDrainLED(gpio.A.Pin(4)) led2 = MakePushPullLED(gpio.A.Pin(3)) - ``` -In this case the assignability is checked at compile time. After the assignment the  _led1_  variable contains `OpenDrainLED{gpio.A.Pin(4)}` and a pointer to the method set of the  _OpenDrainLED_  type. The `led1.On()` call roughly corresponds to the following C code: +在这种情况下,可赋值性在编译时检查。赋值后,_led1_ 变量包含一个 `OpenDrainLED{gpio.A.Pin(4)}`,以及一个指向 _OpenDainLED_ 类型的方法集的指针。 `led1.On()` 调用大致对应于以下 C 代码: ``` led1.methods->On(led1.value) - ``` -As you can see, this is quite inexpensive abstraction if only consider the function call overhead. +如您所见,如果仅考虑函数调用的开销,这是相当便宜的抽象。 -But any assigment to an interface causes to include a lot of information about the assigned type. There can be a lot information in case of complex type which consists of many other types: + +但是,对接口的任何赋值都会导致包含有关已赋值类型的大量信息。对于由许多其他类型组成的复杂类型,可能会有很多信息: ``` $ egc -$ arm-none-eabi-size cortexm0.elf +$ arm-none-eabi-size cortexm0.elf text data bss dec hex filename 10356 196 212 10764 2a0c cortexm0.elf - ``` -If we don’t use [reflection][5] we can save some bytes by avoid to include the names of types and struct fields: +如果我们不使用 [反射][5],可以通过避免包含类型和结构字段的名称来节省一些字节: ``` $ egc -nf -nt -$ arm-none-eabi-size cortexm0.elf +$ arm-none-eabi-size cortexm0.elf text data bss dec hex filename 10312 196 212 10720 29e0 cortexm0.elf - ``` -The resulted binary still contains some necessary information about types and full information about all exported methods (with names). This information is need for checking assignability at runtime, mainly when you assign one value stored in the interface variable to any other variable. +生成的二进制文件仍然包含一些有关类型的必要信息和关于所有导出方法(带有名称)的完整信息。在运行时,主要是当您将存储在接口变量中的一个值赋值给任何其他变量时,需要此信息来检查可赋值性。 -We can also remove type and field names from imported packages by recompiling them all: +我们还可以通过重新编译所导入的包来删除它们的类型和字段名称: ``` $ cd $HOME/emgo $ ./clean.sh $ cd $HOME/firstemgo $ egc -nf -nt -$ arm-none-eabi-size cortexm0.elf +$ arm-none-eabi-size cortexm0.elf text data bss dec hex filename 10272 196 212 10680 29b8 cortexm0.elf - ``` -Let’s load this program to see does it work as expected. This time we’ll use the [st-flash][6] command: +让我们加载这个程序,看看它是否按预期工作。这一次我们将使用 [st-flash][6] 命令: ``` $ arm-none-eabi-objcopy -O binary cortexm0.elf cortexm0.bin @@ -205,106 +203,105 @@ Flash page at addr: 0x08002800 erased ``` -I didn’t connected the NRST signal to the programmer so the  _—reset_  option can’t be used and the reset button have to be pressed to run the program. +我没有将 NRST 信号连接到编程器,因此无法使用 _-reset_ 选项,必须按下 reset 按钮才能运行程序。 ![Interfaces](https://ziutek.github.io/images/mcu/f030-demo-board/interfaces.png) -It seems that the  _st-flash_  works a bit unreliably with this board (often requires reseting the ST-LINK dongle). Additionally, the current version doesn’t issue the reset command over SWD (uses only NRST signal). The software reset isn’t realiable however it usually works and lack of it introduces inconvenience. For this board-programmer pair the  _OpenOCD_  works much better. +看来,_st-flash_ 与此板配合使用有点不可靠 (通常需要重置 ST-LINK 加密狗)。此外,当前版本不会通过 SWD 发出 reset 命令 (仅使用 NRST 信号)。 软件重置是不现实的,但是它通常是有效的,缺少它会将会带来不便。对于电路板-程序员board-programmer 组合 _OpenOCD_ 工作得更好。 ### UART -UART (Universal Aynchronous Receiver-Transmitter) is still one of the most important peripherals of today’s microcontrollers. Its advantage is unique combination of the following properties: +UART(通用异步收发传输器Universal Aynchronous Receiver-Transmitter)仍然是当今微控制器最重要的外设之一。它的优点是以下属性的独特组合: -* relatively high speed, +* 相对较高的速度, -* only two signal lines (even one in case of half-duplex communication), +* 仅两条信号线(在 半双工half-duplex 通信的情况下甚至一条), -* symmetry of roles, +* 角色对称, -* synchronous in-band signaling about new data (start bit), +* 关于新数据的 同步带内信令synchronous in-band signaling(起始位), -* accurate timing inside transmitted word. +* 在传输 words 内的精确计时。 -This causes that UART, originally intedned to transmit asynchronous messages consisting of 7-9 bit words, is also used to efficiently implement various other phisical protocols such as used by [WS28xx LEDs][7] or [1-wire][8] devices. -However, we will use the UART in its usual role: to printing text messages from our program. +这使得最初用于传输由 7-9 位 words 组成的异步消息的 UART,也被用于有效地实现各种其他物理协议,例如被 [WS28xx LEDs][7] 或 [1-wire][8] 设备使用的协议。 + +但是,我们将以其通常的角色使用 UART:从程序中打印文本消息。 ``` package main import ( - "io" - "rtos" + "io" + "rtos" - "stm32/hal/dma" - "stm32/hal/gpio" - "stm32/hal/irq" - "stm32/hal/system" - "stm32/hal/system/timer/systick" - "stm32/hal/usart" + "stm32/hal/dma" + "stm32/hal/gpio" + "stm32/hal/irq" + "stm32/hal/system" + "stm32/hal/system/timer/systick" + "stm32/hal/usart" ) var tts *usart.Driver func init() { - system.SetupPLL(8, 1, 48/8) - systick.Setup(2e6) + system.SetupPLL(8, 1, 48/8) + systick.Setup(2e6) - gpio.A.EnableClock(true) - tx := gpio.A.Pin(9) + gpio.A.EnableClock(true) + tx := gpio.A.Pin(9) - tx.Setup(&gpio.Config{Mode: gpio.Alt}) - tx.SetAltFunc(gpio.USART1_AF1) - d := dma.DMA1 - d.EnableClock(true) - tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil) - tts.Periph().EnableClock(true) - tts.Periph().SetBaudRate(115200) - tts.Periph().Enable() - tts.EnableTx() + tx.Setup(&gpio.Config{Mode: gpio.Alt}) + tx.SetAltFunc(gpio.USART1_AF1) + d := dma.DMA1 + d.EnableClock(true) + tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil) + tts.Periph().EnableClock(true) + tts.Periph().SetBaudRate(115200) + tts.Periph().Enable() + tts.EnableTx() - rtos.IRQ(irq.USART1).Enable() - rtos.IRQ(irq.DMA1_Channel2_3).Enable() + rtos.IRQ(irq.USART1).Enable() + rtos.IRQ(irq.DMA1_Channel2_3).Enable() } func main() { - io.WriteString(tts, "Hello, World!\r\n") + io.WriteString(tts, "Hello, World!\r\n") } func ttsISR() { - tts.ISR() + tts.ISR() } func ttsDMAISR() { - tts.TxDMAISR() + tts.TxDMAISR() } //c:__attribute__((section(".ISRs"))) var ISRs = [...]func(){ - irq.USART1: ttsISR, - irq.DMA1_Channel2_3: ttsDMAISR, + irq.USART1: ttsISR, + irq.DMA1_Channel2_3: ttsDMAISR, } ``` -You can find this code slightly complicated but for now there is no simpler UART driver in STM32 HAL (simple polling driver will be probably useful in some cases). The  _usart.Driver_  is efficient driver that uses DMA and interrupts to ofload the CPU. +您会发现此代码可能有些复杂,但目前 STM32 HAL 中没有更简单的 UART 驱动程序(在某些情况下,简单的轮询驱动程序可能会很有用)。 _usart.Driver_ 是使用 DMA 和中断来卸载 CPU 的高效驱动程序。 -STM32 USART peripheral provides traditional UART and its synchronous version. To use it as output we have to connect its Tx signal to the right GPIO pin: +STM32 USART 外设提供传统的 UART 及其同步版本。要将其用作输出,我们必须将其 Tx 信号连接到正确的 GPIO 引脚: ``` tx.Setup(&gpio.Config{Mode: gpio.Alt}) tx.SetAltFunc(gpio.USART1_AF1) - ``` -The  _usart.Driver_  is configured in Tx-only mode (rxdma and rxbuf are set to nil): +在 Tx-only 模式下配置 _usart.Driver_ (rxdma 和 rxbuf 设置为 nil): ``` tts = usart.NewDriver(usart.USART1, d.Channel(2, 0), nil, nil) - ``` -We use its  _WriteString_  method to print the famous sentence. Let’s clean everything and compile this program: +我们使用它的 _WriteString_ 方法来打印这句名句。让我们清理所有内容并编译该程序: ``` $ cd $HOME/emgo @@ -312,20 +309,19 @@ $ ./clean.sh $ cd $HOME/firstemgo $ egc $ arm-none-eabi-size cortexm0.elf - text data bss dec hex filename - 12728 236 176 13140 3354 cortexm0.elf - + text data bss dec hex filename + 12728 236 176 13140 3354 cortexm0.elf ``` -To see something you need an UART peripheral in your PC. +要查看某些内容,您需要在 PC 中使用 UART 外设。 -**Do not use RS232 port or USB to RS232 converter!** +**请勿使用 RS232 端口或 USB 转 RS232 转换器!** -The STM32 family uses 3.3 V logic but RS232 can produce from -15 V to +15 V which will probably demage your MCU. You need USB to UART converter that uses 3.3 V logic. Popular converters are based on FT232 or CP2102 chips. +STM32 系列使用 3.3V 逻辑,但是 RS232 可以产生 -15 V ~ +15 V 的电压,这可能会损坏您的 MCU。您需要使用 3.3 V 逻辑的 USB 转 UART 转换器。流行的转换器基于 FT232 或 CP2102 芯片。 ![UART](https://ziutek.github.io/images/mcu/f030-demo-board/uart.jpg) -You also need some terminal emulator program (I prefer [picocom][9]). Flash the new image, run the terminal emulator and press the reset button a few times: +您还需要一些终端仿真程序 (我更喜欢 [picocom][9])。刷新新图像,运行终端仿真器,然后按几次 reset 按钮: ``` $ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; reset run; exit' @@ -338,18 +334,18 @@ adapter speed: 1000 kHz adapter_nsrst_delay: 100 none separate adapter speed: 950 kHz -target halted due to debug-request, current mode: Thread +target halted due to debug-request, current mode: Thread xPSR: 0xc1000000 pc: 0x080016f4 msp: 0x20000a20 adapter speed: 4000 kHz ** Programming Started ** auto erase enabled -target halted due to breakpoint, current mode: Thread +target halted due to breakpoint, current mode: Thread xPSR: 0x61000000 pc: 0x2000003a msp: 0x20000a20 wrote 13312 bytes from file cortexm0.elf in 1.020185s (12.743 KiB/s) ** Programming Finished ** adapter speed: 950 kHz $ -$ picocom -b 115200 /dev/ttyUSB0 +$ picocom -b 115200 /dev/ttyUSB0 picocom v3.1 port is : /dev/ttyUSB0 @@ -366,8 +362,8 @@ hangup is : no nolock is : no send_cmd is : sz -vv receive_cmd is : rz -vv -E -imap is : -omap is : +imap is : +omap is : emap is : crcrlf,delbs, logfile is : none initstring : none @@ -379,69 +375,62 @@ Terminal ready Hello, World! Hello, World! Hello, World! - ``` -Every press of the reset button produces new “Hello, World!” line. Everything works as expected. +每次按下 reset 按钮都会产生新的 “Hello,World!”行。一切都在按预期进行。 -To see bi-directional UART code for this MCU check out [this example][10]. +要查看此 MCU 的 双向bi-directional UART 代码,请查看 [此示例][10]。 -### io.Writer +### io.Writer 接口 -The  _io.Writer_  interface is probably the second most commonly used interface type in Go, right after the  _error_  interface. Its definition looks like this: +_io.Writer_ 接口可能是 Go 中第二种最常用的接口类型,紧接在 _error_ 接口之后。其定义如下所示: ``` type Writer interface { - Write(p []byte) (n int, err error) + Write(p []byte) (n int, err error) } - ``` - _usart.Driver_  implements  _io.Writer_  so we can replace: + _usart.Driver_ 实现了 _io.Writer_ ,因此我们可以替换: ``` tts.WriteString("Hello, World!\r\n") - ``` -with +为 ``` io.WriteString(tts, "Hello, World!\r\n") - ``` -Additionally you need to add the  _io_  package to the  _import_  section. +此外,您需要将 _io_ 包添加到 _import_ 部分。 -The declaration of  _io.WriteString_  function looks as follows: +_io.WriteString_ 函数的声明如下所示: ``` func WriteString(w Writer, s string) (n int, err error) - ``` -As you can see, the  _io.WriteString_  allows to write strings using any type that implements  _io.Writer_ interface. Internally it check does the underlying type has  _WriteString_  method and uses it instead of  _Write_  if available. +如您所见,_io.WriteString_ 允许使用实现了 _io.Writer_ 接口的任何类型来编写字符串。在内部,它检查基础类型是否具有 _WriteString_ 方法,并使用该方法代替 _Write_ (如果可用)。 -Let’s compile the modified program: +让我们编译修改后的程序: ``` $ egc -$ arm-none-eabi-size cortexm0.elf +$ arm-none-eabi-size cortexm0.elf text data bss dec hex filename 15456 320 248 16024 3e98 cortexm0.elf - ``` -As you can see,  _io.WriteString_  causes a significant increase in the size of the binary: 15776 - 12964 = 2812 bytes. There isn’t too much space left on the Flash. What caused such a drastic increase in size? +如您所见,_io.WriteString_ 导致二进制文件的大小显着增加:15776-12964 = 2812字节。 Flash 上没有太多空间了。是什么引起了这么大规模的增长? -Using the command: +使用这个命令: ``` arm-none-eabi-nm --print-size --size-sort --radix=d cortexm0.elf - ``` -we can print all symbols ordered by its size for both cases. By filtering and analyzing the obtained data (awk, diff) we can find about 80 new symbols. The ten largest are: +我们可以打印两种情况下按其大小排序的所有符号。通过过滤和分析获得的数据(awk,diff),我们可以找到大约 80 个新符号。最大的十个如下所示: ``` > 00000062 T stm32$hal$usart$Driver$DisableRx @@ -453,46 +442,44 @@ we can print all symbols ordered by its size for both cases. By filtering and an > 00000100 T stm32$hal$usart$Error$Error > 00000360 T io$WriteString > 00000660 T stm32$hal$usart$Driver$Read - ``` -So, even though we don’t use the  _usart.Driver.Read_  method it was compiled in, same as  _DisableRx_ ,  _RxDMAISR_ ,  _EnableRx_  and other not mentioned above. Unfortunately, if you assign something to the interface, its full method set is required (with all dependences). This isn’t a problem for a large programs that use most of the methods anyway. But for our simple one it’s a huge burden. +因此,即使我们不使用 _usart.Driver.Read_ 方法进行编译,也与 _DisableRx_、_RxDMAISR_、_EnableRx_ 以及上面未提及的其他方法相同。不幸的是,如果您为接口赋值了一些内容,那么它的完整方法集是必需的(包含所有依赖项)。对于使用大多数方法的大型程序来说,这不是问题。但是对于我们这种极简的情况而言,这是一个巨大的负担。 -We’re already close to the limits of our MCU but let’s try to print some numbers (you need to replace  _io_ package with  _strconv_  in  _import_  section): +我们已经接近 MCU 的极限,但让我们尝试打印一些数字(您需要在 _import_ 部分中用 _strconv_ 替换 _io_ 包): ``` func main() { - a := 12 - b := -123 + a := 12 + b := -123 - tts.WriteString("a = ") - strconv.WriteInt(tts, a, 10, 0, 0) - tts.WriteString("\r\n") - tts.WriteString("b = ") - strconv.WriteInt(tts, b, 10, 0, 0) - tts.WriteString("\r\n") + tts.WriteString("a = ") + strconv.WriteInt(tts, a, 10, 0, 0) + tts.WriteString("\r\n") + tts.WriteString("b = ") + strconv.WriteInt(tts, b, 10, 0, 0) + tts.WriteString("\r\n") - tts.WriteString("hex(a) = ") - strconv.WriteInt(tts, a, 16, 0, 0) - tts.WriteString("\r\n") - tts.WriteString("hex(b) = ") - strconv.WriteInt(tts, b, 16, 0, 0) - tts.WriteString("\r\n") + tts.WriteString("hex(a) = ") + strconv.WriteInt(tts, a, 16, 0, 0) + tts.WriteString("\r\n") + tts.WriteString("hex(b) = ") + strconv.WriteInt(tts, b, 16, 0, 0) + tts.WriteString("\r\n") } - ``` -As in the case of  _io.WriteString_  function, the first argument of the  _strconv.WriteInt_  is of type  _io.Writer_ . +与使用 _io.WriteString_ 函数的情况一样,_strconv.WriteInt_ 的第一个参数的类型为 _io.Writer_ 。 + ``` $ egc /usr/local/arm/bin/arm-none-eabi-ld: /home/michal/firstemgo/cortexm0.elf section `.rodata' will not fit in region `Flash' /usr/local/arm/bin/arm-none-eabi-ld: region `Flash' overflowed by 692 bytes exit status 1 - ``` -This time we’ve run out of space. Let’s try to slim down the information about types: +这一次我们的空间用完了。让我们试着精简一下有关类型的信息: ``` $ cd $HOME/emgo @@ -502,391 +489,376 @@ $ egc -nf -nt $ arm-none-eabi-size cortexm0.elf text data bss dec hex filename 15876 316 320 16512 4080 cortexm0.elf - ``` -It was close, but we fit. Let’s load and run this code: +很接近,但很合适。让我们加载并运行此代码: ``` a = 12 b = -123 hex(a) = c hex(b) = -7b - ``` -The  _strconv_  package in Emgo is quite different from its archetype in Go. It is intended for direct use to write formatted numbers and in many cases can replace heavy  _fmt_  package. That’s why the function names start with  _Write_  instead of  _Format_  and have additional two parameters. Below is an example of their use: +Emgo 中的 _strconv_ 包与 Go 中的原型有很大的不同。 它旨在直接用于写入格式化的数字,并且在许多情况下可以替换繁重的 _fmt_ 包。 这就是为什么函数名称以 _Write_ 而不是 _Format_ 开头,并具有额外的两个参数的原因。 以下是其用法示例: ``` func main() { - b := -123 - strconv.WriteInt(tts, b, 10, 0, 0) - tts.WriteString("\r\n") - strconv.WriteInt(tts, b, 10, 6, ' ') - tts.WriteString("\r\n") - strconv.WriteInt(tts, b, 10, 6, '0') - tts.WriteString("\r\n") - strconv.WriteInt(tts, b, 10, 6, '.') - tts.WriteString("\r\n") - strconv.WriteInt(tts, b, 10, -6, ' ') - tts.WriteString("\r\n") - strconv.WriteInt(tts, b, 10, -6, '0') - tts.WriteString("\r\n") - strconv.WriteInt(tts, b, 10, -6, '.') - tts.WriteString("\r\n") + b := -123 + strconv.WriteInt(tts, b, 10, 0, 0) + tts.WriteString("\r\n") + strconv.WriteInt(tts, b, 10, 6, ' ') + tts.WriteString("\r\n") + strconv.WriteInt(tts, b, 10, 6, '0') + tts.WriteString("\r\n") + strconv.WriteInt(tts, b, 10, 6, '.') + tts.WriteString("\r\n") + strconv.WriteInt(tts, b, 10, -6, ' ') + tts.WriteString("\r\n") + strconv.WriteInt(tts, b, 10, -6, '0') + tts.WriteString("\r\n") + strconv.WriteInt(tts, b, 10, -6, '.') + tts.WriteString("\r\n") } - ``` -There is its output: +下面是它的输出: ``` -123 -123 -00123 ..-123 --123 --123 +-123 +-123 -123.. - ``` -### Unix streams and Morse code +### Unix 流 和 莫尔斯电码Morse code -Thanks to the fact that most of the functions that write something use  _io.Writer_  instead of concrete type (eg.  _FILE_  in C) we get a functionality similar to  _Unix streams_ . In Unix we can easily combine simple commands to perform larger tasks. For example, we can write text to the file this way: +得益于事实上大多数写入功能的函数都使用 _io.Writer_ 而不是具体类型(例如 C 中的 _FILE_ ),因此我们获得了类似于 _Unix stream_ 的功能。在 Unix 中,我们可以轻松地组合简单的命令来执行更大的任务。例如,我们可以通过以下方式将文本写入文件: ``` echo "Hello, World!" > file.txt - ``` -The `>` operator writes the output stream of the preceding command to the file. There is also `|`operator that connects output and input streams of adjacent commands. +`>` 操作符将前面命令的输出流写入文件。还有 `|` 操作符,用于连接相邻命令的输出流和输入流。 -Thanks to the streams we can easily convert/filter output of any command. For example, to convert all letters to uppercase we can filter the echo’s output through  _tr_  command: +多亏了流,我们可以轻松地转换/过滤任何命令的输出。例如,要将所有字母转换为大写,我们可以通过 _tr_ 命令过滤 echo 的输出: ``` echo "Hello, World!" | tr a-z A-Z > file.txt - ``` -To show the analogy between  _io.Writer_  and Unix streams let’s write our: +为了显示 _io.Writer_ 和 Unix 流之间的类比,让我们编写以下代码: ``` io.WriteString(tts, "Hello, World!\r\n") - ``` -in the following pseudo-unix form: +采用以下伪 unix 形式: ``` io.WriteString "Hello, World!" | usart.Driver usart.USART1 - ``` -The next example will show how to do this: +下一个示例将显示如何执行此操作: ``` io.WriteString "Hello, World!" | MorseWriter | usart.Driver usart.USART1 - ``` -Let’s create a simple encoder that encodes the text written to it using Morse coding: +让我们来创建一个简单的编码器,它使用莫尔斯电码对写入的文本进行编码: ``` type MorseWriter struct { - W io.Writer + W io.Writer } func (w *MorseWriter) Write(s []byte) (int, error) { - var buf [8]byte - for n, c := range s { - switch { - case c == '\n': - c = ' ' // Replace new lines with spaces. - case 'a' <= c && c <= 'z': - c -= 'a' - 'A' // Convert to upper case. - } - if c < ' ' || 'Z' < c { - continue // c is outside ASCII [' ', 'Z'] - } - var symbol morseSymbol - if c == ' ' { - symbol.length = 1 - buf[0] = ' ' - } else { - symbol = morseSymbols[c-'!'] - for i := uint(0); i < uint(symbol.length); i++ { - if (symbol.code>>i)&1 != 0 { - buf[i] = '-' - } else { - buf[i] = '.' - } - } - } - buf[symbol.length] = ' ' - if _, err := w.W.Write(buf[:symbol.length+1]); err != nil { - return n, err - } - } - return len(s), nil + var buf [8]byte + for n, c := range s { + switch { + case c == '\n': + c = ' ' // Replace new lines with spaces. + case 'a' <= c && c <= 'z': + c -= 'a' - 'A' // Convert to upper case. + } + if c < ' ' || 'Z' < c { + continue // c is outside ASCII [' ', 'Z'] + } + var symbol morseSymbol + if c == ' ' { + symbol.length = 1 + buf[0] = ' ' + } else { + symbol = morseSymbols[c-'!'] + for i := uint(0); i < uint(symbol.length); i++ { + if (symbol.code>>i)&1 != 0 { + buf[i] = '-' + } else { + buf[i] = '.' + } + } + } + buf[symbol.length] = ' ' + if _, err := w.W.Write(buf[:symbol.length+1]); err != nil { + return n, err + } + } + return len(s), nil } type morseSymbol struct { - code, length byte + code, length byte } //emgo:const var morseSymbols = [...]morseSymbol{ - {1<<0 | 1<<1 | 1<<2, 4}, // ! ---. - {1<<1 | 1<<4, 6}, // " .-..-. - {}, // # - {1<<3 | 1<<6, 7}, // $ ...-..- + {1<<0 | 1<<1 | 1<<2, 4}, // ! ---. + {1<<1 | 1<<4, 6}, // " .-..-. + {}, // # + {1<<3 | 1<<6, 7}, // $ ...-..- - // Some code omitted... + // Some code omitted... - {1<<0 | 1<<3, 4}, // X -..- - {1<<0 | 1<<2 | 1<<3, 4}, // Y -.-- - {1<<0 | 1<<1, 4}, // Z --.. + {1<<0 | 1<<3, 4}, // X -..- + {1<<0 | 1<<2 | 1<<3, 4}, // Y -.-- + {1<<0 | 1<<1, 4}, // Z --.. } - ``` -You can find the full  _morseSymbols_  array [here][11]. The `//emgo:const` directive ensures that  _morseSymbols_ array won’t be copied to the RAM. +您可以在 [这里][11] 找到完整的 _morseSymbols_ 数组。 `//emgo:const` 指令确保 _morseSymbols_ 数组不会被复制到 RAM 中。 -Now we can print our sentence in two ways: +现在我们可以通过两种方式打印句子: ``` func main() { - s := "Hello, World!\r\n" - mw := &MorseWriter{tts} + s := "Hello, World!\r\n" + mw := &MorseWriter{tts} - io.WriteString(tts, s) - io.WriteString(mw, s) + io.WriteString(tts, s) + io.WriteString(mw, s) } - ``` -We use the pointer to the  _MorseWriter_  `&MorseWriter{tts}` instead os simple `MorseWriter{tts}` value beacuse the  _MorseWriter_  is to big to fit into an interface variable. +我们使用指向 _MorseWriter_ `&MorseWriter{tts}` 的指针而不是简单的 `MorseWriter{tts}` 值,因为 _MorseWriter_ 太大,不适合接口变量。 -Emgo, unlike Go, doesn’t dynamically allocate memory for value stored in interface variable. The interface type has limited size, equal to the size of three pointers (to fit  _slice_ ) or two  _float64_  (to fit  _complex128_ ), what is bigger. It can directly store values of all basic types and small structs/arrays but for bigger values you must use pointers. -Let’s compile this code and see its output: +与 Go 不同,Emgo 不会为存储在接口变量中的值动态分配内存。接口类型的大小受限制,等于三个指针(适合 _slice_ )或两个 _float64_(适合 _complex128_ )的大小,以较大者为准。它可以直接存储所有基本类型和小型 “结构体/数组” 的值,但是对于较大的值,您必须使用指针。 + +让我们编译此代码并查看其输出: ``` $ egc $ arm-none-eabi-size cortexm0.elf text data bss dec hex filename 15152 324 248 15724 3d6c cortexm0.elf - ``` ``` Hello, World! .... . .-.. .-.. --- --..-- .-- --- .-. .-.. -.. ---. - ``` -### The Ultimate Blinky +### 终极 Blinky -The  _Blinky_  is hardware equivalent of  _Hello, World!_  program. Once we have a Morse encoder we can easly combine both to obtain the  _Ultimate Blinky_  program: +_Blinky_ 等效于 _Hello,World!_ 程序的硬件。一旦有了 Morse 编码器,我们就可以轻松地将两者结合起来以获得 _Ultimate Blinky_ 程序: ``` package main import ( - "delay" - "io" + "delay" + "io" - "stm32/hal/gpio" - "stm32/hal/system" - "stm32/hal/system/timer/systick" + "stm32/hal/gpio" + "stm32/hal/system" + "stm32/hal/system/timer/systick" ) var led gpio.Pin func init() { - system.SetupPLL(8, 1, 48/8) - systick.Setup(2e6) + system.SetupPLL(8, 1, 48/8) + systick.Setup(2e6) - gpio.A.EnableClock(false) - led = gpio.A.Pin(4) + gpio.A.EnableClock(false) + led = gpio.A.Pin(4) - cfg := gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain, Speed: gpio.Low} - led.Setup(&cfg) + cfg := gpio.Config{Mode: gpio.Out, Driver: gpio.OpenDrain, Speed: gpio.Low} + led.Setup(&cfg) } type Telegraph struct { - Pin gpio.Pin - Dotms int // Dot length [ms] + Pin gpio.Pin + Dotms int // Dot length [ms] } func (t Telegraph) Write(s []byte) (int, error) { - for _, c := range s { - switch c { - case '.': - t.Pin.Clear() - delay.Millisec(t.Dotms) - t.Pin.Set() - delay.Millisec(t.Dotms) - case '-': - t.Pin.Clear() - delay.Millisec(3 * t.Dotms) - t.Pin.Set() - delay.Millisec(t.Dotms) - case ' ': - delay.Millisec(3 * t.Dotms) - } - } - return len(s), nil + for _, c := range s { + switch c { + case '.': + t.Pin.Clear() + delay.Millisec(t.Dotms) + t.Pin.Set() + delay.Millisec(t.Dotms) + case '-': + t.Pin.Clear() + delay.Millisec(3 * t.Dotms) + t.Pin.Set() + delay.Millisec(t.Dotms) + case ' ': + delay.Millisec(3 * t.Dotms) + } + } + return len(s), nil } func main() { - telegraph := &MorseWriter{Telegraph{led, 100}} - for { - io.WriteString(telegraph, "Hello, World! ") - } + telegraph := &MorseWriter{Telegraph{led, 100}} + for { + io.WriteString(telegraph, "Hello, World! ") + } } // Some code omitted... ``` -In the above example I omitted the definition of  _MorseWriter_  type because it was shown earlier. The full version is available [here][12]. Let’s compile it and run: +在上面的示例中,我省略了 _MorseWriter_ 类型的定义,因为它已在前面展示过。完整版可通过 [这里][12] 获取。让我们编译它并运行: ``` $ egc $ arm-none-eabi-size cortexm0.elf text data bss dec hex filename 11772 244 244 12260 2fe4 cortexm0.elf - ``` ![Ultimate Blinky](https://ziutek.github.io/images/mcu/f030-demo-board/morse.png) -### Reflection +### 反射 -Yes, Emgo supports [reflection][13]. The  _reflect_  package isn’t complete yet but that what is done is enough to implement  _fmt.Print_  family of functions. Let’s see what can we do on our small MCU. +是的,Emgo 支持 [反射][13]。 _reflect_ 包尚未完成,但是已完成的部分足以实现 _fmt.Print_ 函数族了。来看看我们可以在小型 MCU 上做什么。 -To reduce memory usage we will use [semihosting][14] as standard output. For convenience, we also write simple  _println_  function which to some extent mimics  _fmt.Println_ . +为了减少内存使用,我们将使用 [semihosting][14] 作为标准输出。为了方便起见,我们还编写了简单的 _println_ 函数,它在某种程度上类似于 _fmt.Println_。 ``` package main import ( - "debug/semihosting" - "reflect" - "strconv" + "debug/semihosting" + "reflect" + "strconv" - "stm32/hal/system" - "stm32/hal/system/timer/systick" + "stm32/hal/system" + "stm32/hal/system/timer/systick" ) var stdout semihosting.File func init() { - system.SetupPLL(8, 1, 48/8) - systick.Setup(2e6) + system.SetupPLL(8, 1, 48/8) + systick.Setup(2e6) - var err error - stdout, err = semihosting.OpenFile(":tt", semihosting.W) - for err != nil { - } + var err error + stdout, err = semihosting.OpenFile(":tt", semihosting.W) + for err != nil { + } } type stringer interface { - String() string + String() string } func println(args ...interface{}) { - for i, a := range args { - if i > 0 { - stdout.WriteString(" ") - } - switch v := a.(type) { - case string: - stdout.WriteString(v) - case int: - strconv.WriteInt(stdout, v, 10, 0, 0) - case bool: - strconv.WriteBool(stdout, v, 't', 0, 0) - case stringer: - stdout.WriteString(v.String()) - default: - stdout.WriteString("%unknown") - } - } - stdout.WriteString("\r\n") + for i, a := range args { + if i > 0 { + stdout.WriteString(" ") + } + switch v := a.(type) { + case string: + stdout.WriteString(v) + case int: + strconv.WriteInt(stdout, v, 10, 0, 0) + case bool: + strconv.WriteBool(stdout, v, 't', 0, 0) + case stringer: + stdout.WriteString(v.String()) + default: + stdout.WriteString("%unknown") + } + } + stdout.WriteString("\r\n") } type S struct { - A int - B bool + A int + B bool } func main() { - p := &S{-123, true} + p := &S{-123, true} - v := reflect.ValueOf(p) + v := reflect.ValueOf(p) - println("kind(p) =", v.Kind()) - println("kind(*p) =", v.Elem().Kind()) - println("type(*p) =", v.Elem().Type()) + println("kind(p) =", v.Kind()) + println("kind(*p) =", v.Elem().Kind()) + println("type(*p) =", v.Elem().Type()) - v = v.Elem() + v = v.Elem() - println("*p = {") - for i := 0; i < v.NumField(); i++ { - ft := v.Type().Field(i) - fv := v.Field(i) - println(" ", ft.Name(), ":", fv.Interface()) - } - println("}") + println("*p = {") + for i := 0; i < v.NumField(); i++ { + ft := v.Type().Field(i) + fv := v.Field(i) + println(" ", ft.Name(), ":", fv.Interface()) + } + println("}") } ``` -The  _semihosting.OpenFile_  function allows to open/create file on the host side. The special path  _:tt_ corresponds to host’s standard output. +_semihosting.OpenFile_ 函数允许在主机端 打开/创建 文件。特殊路径 _:tt_ 对应于主机的标准输出。 -The  _println_  function accepts arbitrary number of arguments, each of arbitrary type: +_println_ 函数接受任意数量的参数,每个参数的类型都是任意的: ``` func println(args ...interface{}) - ``` -It’s possible because any type implements the empty interface  _interface{}_ . The  _println_  uses [type switch][15] to print strings, integers and booleans: +可能是因为任何类型都实现了空接口 _interface{}_。 _println_ 使用 [类型开关][15] 打印字符串,整数和布尔值: ``` switch v := a.(type) { case string: - stdout.WriteString(v) + stdout.WriteString(v) case int: - strconv.WriteInt(stdout, v, 10, 0, 0) + strconv.WriteInt(stdout, v, 10, 0, 0) case bool: - strconv.WriteBool(stdout, v, 't', 0, 0) + strconv.WriteBool(stdout, v, 't', 0, 0) case stringer: - stdout.WriteString(v.String()) + stdout.WriteString(v.String()) default: - stdout.WriteString("%unknown") + stdout.WriteString("%unknown") } - ``` -Additionally it supports any type that implements  _stringer_  interface, that is, any type that has  _String()_ method. In any  _case_  clause the  _v_  variable has the right type, same as listed after  _case_  keyword. +此外,它还支持任何实现了 _stringer_ 接口的类型,即任何具有 _String()_ 方法的类型。在任何 _case_ 子句中,_v_ 变量具有正确的类型,与 _case_ 关键字后列出的类型相同。 -The `reflect.ValueOf(p)` returns  _p_  in the form that allows to analyze its type and content programmatically. As you can see, we can even dereference pointers using `v.Elem()` and print all struct fields with their names. -Let’s try to compile this code. For now let’s see what will come out if compiled without type and field names: +reflect.ValueOf(p) 函数以允许以编程方式分析其类型和内容的形式返回 _p_。如您所见,我们甚至可以使用 `v.Elem()` 取消引用指针,并打印所有结构体及其名称。 + +让我们尝试编译这段代码。现在,让我们看看如果不使用类型和字段名进行编译会产生什么结果: ``` $ egc -nt -nf -$ arm-none-eabi-size cortexm0.elf +$ arm-none-eabi-size cortexm0.elf text data bss dec hex filename 16028 216 312 16556 40ac cortexm0.elf - ``` -Only 140 free bytes left on the Flash. Let’s load it using OpenOCD with semihosting enabled: +闪存上只剩下 140 个可用字节。让我们使用启用了 semihosting 的 OpenOCD 加载它: ``` $ openocd -d0 -f interface/stlink.cfg -f target/stm32f0x.cfg -c 'init; program cortexm0.elf; arm semihosting enable; reset run' @@ -899,12 +871,12 @@ adapter speed: 1000 kHz adapter_nsrst_delay: 100 none separate adapter speed: 950 kHz -target halted due to debug-request, current mode: Thread +target halted due to debug-request, current mode: Thread xPSR: 0xc1000000 pc: 0x08002338 msp: 0x20000a20 adapter speed: 4000 kHz ** Programming Started ** auto erase enabled -target halted due to breakpoint, current mode: Thread +target halted due to breakpoint, current mode: Thread xPSR: 0x61000000 pc: 0x2000003a msp: 0x20000a20 wrote 16384 bytes from file cortexm0.elf in 0.700133s (22.853 KiB/s) ** Programming Finished ** @@ -912,27 +884,25 @@ semihosting is enabled adapter speed: 950 kHz kind(p) = ptr kind(*p) = struct -type(*p) = +type(*p) = *p = { X. : -123 X. : true } - ``` -If you’ve actually run this code, you noticed that semihosting is slow, especially if you write a byte after byte (buffering helps). +如果您实际运行过此代码,则会注意到 semihosting 运行缓慢,尤其是在逐字节写入时(缓冲很有用)。 -As you can see, there is no type name for `*p` and all struct fields have the same  _X._  name. Let’s compile this program again, this time without  _-nt -nf_  options: +如您所见,`*p` 没有类型名称,并且所有结构字段都具有相同的 _X._ 名称。让我们再次编译该程序,这次不带 _-nt -nf_ 选项: ``` $ egc -$ arm-none-eabi-size cortexm0.elf +$ arm-none-eabi-size cortexm0.elf text data bss dec hex filename 16052 216 312 16580 40c4 cortexm0.elf - ``` -Now the type and field names have been included but only these defined in  ~~_main.go_  file~~  _main_  package. The output of our program looks as follows: +现在已经包括了类型和字段名称,但仅在 ~~_main.go_ 文件中~~ _main_ 包中定义了它们。该程序的输出如下所示: ``` kind(p) = ptr @@ -942,12 +912,11 @@ type(*p) = S A : -123 B : true } - ``` -Reflection is a crucial part of any easy to use serialization library and serialization ~~algorithms~~ like [JSON][16]gain in importance in the IOT era. +反射是任何易于使用的序列化库的关键部分,而像 [JSON][16] 这样的序列化 ~~算法~~ 在物联网IoT时代也越来越重要。 -This is where I finish the second part of this article. I think there is a chance for the third part, more entertaining, where we connect to this board various interesting devices. If this board won’t carry them, we replace it with something a little bigger. +这些就是我完成的本文的第二部分。我认为有机会进行第三部分,更具娱乐性的部分,在那里我们将各种有趣的设备连接到这块板上。如果这块板装不下,我们就换一块大一点的。 -------------------------------------------------------------------------------- From 904f96a3cbae77058728befd87fb85776cc3206e Mon Sep 17 00:00:00 2001 From: XianLei Gao <279483350@qq.com> Date: Sat, 10 Oct 2020 12:37:17 +0800 Subject: [PATCH 7/8] =?UTF-8?q?=E5=B0=86=E8=AF=91=E6=96=87=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E5=88=B0=20translated/tech/=20=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tech/20180414 Go on very small hardware Part 2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {sources => translated}/tech/20180414 Go on very small hardware Part 2.md (99%) diff --git a/sources/tech/20180414 Go on very small hardware Part 2.md b/translated/tech/20180414 Go on very small hardware Part 2.md similarity index 99% rename from sources/tech/20180414 Go on very small hardware Part 2.md rename to translated/tech/20180414 Go on very small hardware Part 2.md index ab4f10a9ec..4eaf203686 100644 --- a/sources/tech/20180414 Go on very small hardware Part 2.md +++ b/translated/tech/20180414 Go on very small hardware Part 2.md @@ -923,7 +923,7 @@ type(*p) = S via: https://ziutek.github.io/2018/04/14/go_on_very_small_hardware2.html 作者:[Michał Derkacz ][a] -译者:[译者ID](https://github.com/译者ID) +译者:[gxlct008](https://github.com/gxlct008) 校对:[校对者ID](https://github.com/校对者ID) 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 From 575581ff961ebfd93473d0a8a740e4242f163dc8 Mon Sep 17 00:00:00 2001 From: XianLei Gao <279483350@qq.com> Date: Sat, 10 Oct 2020 16:37:54 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E7=94=B3=E9=A2=86=E5=8E=9F=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0201009 How to Install Deepin Desktop on Ubuntu 20.04 LTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/tech/20201009 How to Install Deepin Desktop on Ubuntu 20.04 LTS.md b/sources/tech/20201009 How to Install Deepin Desktop on Ubuntu 20.04 LTS.md index e9d910abdf..5ef25c8ef5 100644 --- a/sources/tech/20201009 How to Install Deepin Desktop on Ubuntu 20.04 LTS.md +++ b/sources/tech/20201009 How to Install Deepin Desktop on Ubuntu 20.04 LTS.md @@ -1,5 +1,5 @@ [#]: collector: (lujun9972) -[#]: translator: ( ) +[#]: translator: (gxlct008) [#]: reviewer: ( ) [#]: publisher: ( ) [#]: url: ( )