` 这样的路径。然后像这样使用这个路径:
``` console
ls -l /dev/bus/usb/001/018
diff --git a/src/intro/install/verify.md b/src/intro/install/verify.md
index 037e4df3..ef812823 100644
--- a/src/intro/install/verify.md
+++ b/src/intro/install/verify.md
@@ -4,7 +4,7 @@
使用一个micro USB线缆将你的笔记本/个人电脑连接到discovery开发板上。discovery开发板有两个USB连接器;使用标记着"USB ST-LINK"的那个,它位于开发板边缘的中间位置。
-也要检查下ST-LINK的短路帽被安装了。看下面的图;ST-LINK短路帽用红色圈起来了。
+也要检查下ST-LINK的短路帽是否被安装了。看下面的图;ST-LINK短路帽用红色圈起来了。
@@ -19,7 +19,7 @@ openocd -f interface/stlink.cfg -f target/stm32f3x.cfg
> **注意**: 旧版的openocd, 包括从2017发布的0.10.0, 不包含新的(且更适合的)`interface/stlink.cfg`文件; 你需要使用`interface/stlink-v2.cfg` 或者 `interface/stlink-v2-1.cfg`。
-你应该看到下列的输出,且程序应该阻塞住了控制台:
+你应该看到了下面的输出,且程序应该阻塞住了控制台:
``` text
Open On-Chip Debugger 0.10.0
@@ -44,7 +44,7 @@ Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
[下个章节]: ../../start/index.md
-如果你没看到"breakpoints"这行,尝试下下列命令中的某一个。
+如果你没看到"breakpoints"这行,尝试下下列命令中的某一个命令。
``` console
openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg
@@ -54,9 +54,9 @@ openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg
openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
```
-如果这些命令的某条起作用了,那意味着你使用的discovery开发板是一个旧的版本。那也不成问题,但是你要记住这件事,因为随后你可能需要点不同的配置。你可以移到[下个章节]了。
+如果这些命令的某条起作用了,那意味着你使用的discovery开发板是一个旧的版本。那也不成问题,但是你要记住这件事,因为随后你的配置可能有点不同。你可以移到[下个章节]了。
-如果这些命令在normal user模式下都没用,尝试下使用root模式运行它们(e.g. `sudo openocd ..`)。如果命令在root模式下起作用,要检查下[udev rules]是否被正确地设置了。
+如果这些命令在normal user模式下都没用,尝试下使用root模式运行它们(e.g. `sudo openocd ..`)。如果命令在root模式下起作用,需要检查下[udev rules]是否被正确地设置了。
[udev rules]: linux.md#udev-rules
diff --git a/src/intro/tooling.md b/src/intro/tooling.md
index 1a5b2f98..3e819275 100644
--- a/src/intro/tooling.md
+++ b/src/intro/tooling.md
@@ -7,7 +7,7 @@
- 有ARM支持的GDB。强烈建议7.12或者更新的版本。测试版本: 7.10, 7.11 和 8.1
- [`cargo-generate`](/~https://github.com/ashleygwilliams/cargo-generate) 或者 `git`
-这些工具都是可选的,但是跟着书来使用它们,会更容易。下面的文档将解释我们为什么使用这些工具。安装指令可以在下一页找到。
+这些工具都是可选的,但是跟着书一起来使用它们,会更容易。下面的文档将解释我们为什么使用这些工具。安装指令可以在下一页找到。
## `cargo-generate` 或者 `git`
裸机编程是非标准 Rust 编程,为了得到正确的程序的内存布局,需要对链接过程进行一些调整,这要求添加一些额外的文件(比如linker scripts)和配置(比如linker flags)。我们已经为你把这些打包进了一个模板里,你只需要补充缺失的信息(比如项目名和你的目标硬件的特性)。
@@ -15,14 +15,14 @@
## `cargo-binutils`
`cargo-binutils`是一个Cargo命令的子集,它让我们能轻松使用Rust工具链带来的LLVM工具。这些工具包括LLVM版本的`objdump`,`nm`和`size`,用来查看二进制文件。
-在GNU binutils之上使用这些工具的好处是,(a)无论你的操作系统是什么,安装这些LLVM工具是相同的一条命令(`rustup component add llvm-tools-preview`)。(b)像是`objdump`这样的工具支持所有`rustc`支持的架构--从ARM到x86_64--因为它们都有一样的LLVM后端。
+在GNU binutils之上使用这些工具的好处是,(a)无论你的操作系统是什么,安装这些LLVM工具是相同的一条命令(`rustup component add llvm-tools-preview`)。(b)像是`objdump`这样的工具,支持所有`rustc`支持的架构--从ARM到x86_64--因为它们都有一样的LLVM后端。
## `qemu-system-arm`
-QEMU是一个仿真器。在这个例子里,我们使用能完全仿真ARM系统的改良版。我们使用QEMU在主机上运行嵌入式程序。多亏了它,你可以在没有任何硬件的情况下,尝试这本书的部分示例。
+QEMU是一个仿真器。在这个例子里,我们使用能完全仿真ARM系统的改良版QEMU。我们使用QEMU在主机上运行嵌入式程序。多亏了它,你可以在没有任何硬件的情况下,尝试这本书的部分示例。
## GDB
-调试器是嵌入式开发的一个非常重要的组件,你不可能总是有机会将内容记录到主机控制台上。在某些情况里,你可能甚至没有LEDs去点亮你的硬件!
+调试器是嵌入式开发的一个非常重要的组件,你不可能总是可以将内容记录到主机控制台上。在某些情况里,你甚至可能都没法用LEDs去点亮你的硬件!
通常,提到调试,LLDB和GDB的功能一样,但是我们还没找到与GDB的`load`命令对应的LLDB命令,这条命令负责将程序上传到目标硬件,因此我们现在推荐你使用GDB。
## OpenOCD
diff --git a/src/start/hardware.md b/src/start/hardware.md
index addd5ebf..4751d9f9 100644
--- a/src/start/hardware.md
+++ b/src/start/hardware.md
@@ -1,14 +1,14 @@
# 硬件
-现在你应该有点熟悉工具和开发过程了。这部分我们将转换到真正的硬件上;步骤非常相似。让我们深入进去。
+现在你应该有点熟悉工具和开发过程了。在这部分我们将切换到真正的硬件上;步骤非常相似。让我们深入进去。
## 认识你的硬件
-在我们开始之前,你需要了解下你的目标设备的一些特性,因为它们将被用于配置项目:
-- ARM 内核。e.g. Cortex-M3 。
+在我们开始之前,你需要了解下你的目标设备的一些特性,因为你将用它们来配置项目:
+- ARM 内核。比如 Cortex-M3 。
- ARM 内核包括一个FPU吗?Cortex-M4**F**和Cortex-M7**F**有。
-- 目标设备有多少Flash和RAM?e.g. 256KiB的Flash和32KiB的RAM。
-- Flash和RAM映射在地址空间什么位置?e.g. RAM通常位于 `0x2000_0000` 地址处。
+- 目标设备有多少Flash和RAM?比如 256KiB的Flash和32KiB的RAM。
+- Flash和RAM映射在地址空间的什么位置?比如 RAM通常位于 `0x2000_0000` 地址处。
你可以在你的设备的数据手册和参考手册上找到这些信息。
@@ -46,7 +46,7 @@ tail -n5 .cargo/config.toml
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
```
-我们将使用`thumbv7em-none-eabihf`,因为它覆盖Cortex-M4F核。
+我们将使用`thumbv7em-none-eabihf`,因为它涉及到Cortex-M4F核。
第二步是将存储区域信息(memory region information)输入`memory.x`。
@@ -60,7 +60,7 @@ MEMORY
RAM : ORIGIN = 0x20000000, LENGTH = 40K
}
```
-> **注意**:如果你因为一些理由,在对某个编译目标第一次编译后,改变了`memory.x`文件,需要在`cargo build`之前执行`cargo clean`。因为`cargo build`可能不会追踪`memory.x`的更新。
+> **注意**:如果你因为一些理由,在对某个编译目标首次编译后,改变了`memory.x`文件,需要在`cargo build`之前执行`cargo clean`。因为`cargo build`可能不会追踪`memory.x`的更新。
我们将使用hello示例再次开始,但是首先我们必须做一个小改变。
@@ -71,15 +71,15 @@ MEMORY
fn main() -> ! {
hprintln!("Hello, world!").unwrap();
- // exit QEMU
- // NOTE do not run this on hardware; it can corrupt OpenOCD state
+ // 退出 QEMU
+ // 注意 不要在硬件上运行这个;它会打破OpenOCD的状态
// debug::exit(debug::EXIT_SUCCESS);
loop {}
}
```
-你能像你之前做的一样,使用`cargo build`检查编译程序,使用`cargo-binutils`观察二进制文件。`cortex-m-rt`库可以处理所有运行芯片所需的魔法,几乎所有的Cortex-M CPUs都按同样的方式启动,这同样有用。
+你可以像你之前做的一样,使用`cargo build`检查编译程序,使用`cargo-binutils`观察二进制项。`cortex-m-rt`库可以处理所有运行芯片所需的魔法,几乎所有的Cortex-M CPUs都按同样的方式启动,这同样有用。
``` console
cargo build --example hello
@@ -87,7 +87,7 @@ cargo build --example hello
## 调试
-调试将看起来有点不同。事实上,取决于不同的目标设备第一步可能看起来不一样。在这个章节里,我们将展示,调试一个在STM32F3DISCOVERY上运行的程序,所需要的步骤。这作为一个参考。对于设备,关于调试的特定的信息,可以看[the Debugonomicon](/~https://github.com/rust-embedded/debugonomicon)。
+调试将看起来有点不同。事实上,取决于不同的目标设备,第一步可能看起来不一样。在这个章节里,我们将展示,调试一个在STM32F3DISCOVERY上运行的程序,所需要的步骤。这作为一个参考。对于设备,关于调试有关的信息,可以看[the Debugonomicon](/~https://github.com/rust-embedded/debugonomicon)。
像之前一样,我们将进行远程调试,客户端将是一个GDB进程。不同的是,OpenOCD将是服务器。
diff --git a/src/start/index.md b/src/start/index.md
index 18de458d..70ab15d8 100644
--- a/src/start/index.md
+++ b/src/start/index.md
@@ -1,4 +1,4 @@
# 开始
-在这部分里,你将会经历编写,编译,烧录和调试嵌入式程序的过程。大多数的例子都不需要特定的硬件,你就可以试试,因为我们将要向你展示一个开源硬件仿真器,QEMU的基本使用。唯一需要硬件的部分,自然是,[硬件](./hardware.md)章节,我们会使用OpenOCD去编程一个[STM32F3DISCOVERY]。
+在这部分里,你将会经历编写,编译,烧录和调试嵌入式程序。大多数的例子都不需要特定的硬件就可以试试,因为我们将要向你展示一个开源硬件仿真器,QEMU的基本使用。唯一需要硬件的部分,那就是,[硬件](./hardware.md)那一章,我们会使用OpenOCD去编程一个[STM32F3DISCOVERY]。
[STM32F3DISCOVERY]: http://www.st.com/en/evaluation-tools/stm32f3discovery.html
diff --git a/src/start/qemu.md b/src/start/qemu.md
index 9b2148a5..b2da8c65 100644
--- a/src/start/qemu.md
+++ b/src/start/qemu.md
@@ -7,7 +7,7 @@
在这个引导里,我们将使用"app"这个名字来代指项目名。无论何时你看到单词"app",你应该用你选择的项目名来替代"app"。或者你也可以选择把你的项目命名为"app",避免替代。
## 生成一个非标准的 Rust program
-我们将使用[`cortex-m-quickstart`]项目模板来生成一个新项目。生成的项目将包含一个最基本的应用:对于一个新的嵌入式rust应用来说,是一个很好的起点。另外,项目将包含一个`example`文件夹,文件夹中有许多独立的应用,突出了一些关键的嵌入式rust的功能。
+我们将使用[`cortex-m-quickstart`]项目模板来生成一个新项目。生成的项目将包含一个最基本的应用:对于一个新的嵌入式rust应用来说,是一个很好的开始。另外,项目将包含一个`example`文件夹,文件夹中有许多独立的应用,突出了一些关键的嵌入式rust的功能。
[`cortex-m-quickstart`]: /~https://github.com/rust-embedded/cortex-m-quickstart
@@ -96,7 +96,7 @@ fn main() -> ! {
`#![no_std]`指出这个程序将 *不会* 链接标准crate`std`。反而它将会链接到它的子集: `core` crate。
-`#![no_main]`指出这个程序将不会使用标准的且被大多数Rust程序使用的`main`接口。使用`no_main`的主要理由是,因为在`no_std`上下文中使用`main`接口要求rust nightly(译者注:`main`接口对程序的运行环境有要求,比如,它假设命令行参数存在,这不适合`no_std`环境)。
+`#![no_main]`指出这个程序将不会使用标准的且被大多数Rust程序使用的`main`接口。使用`no_main`的主要理由是,因为在`no_std`上下文中使用`main`接口要求开发版的rust 。
`use panic_halt as _;`。这个crate提供了一个`panic_handler`,它定义了程序陷入`panic`时的行为。我们将会在这本书的[运行时恐慌(Panicking)](panicking.md)章节中覆盖更多的细节。
@@ -121,7 +121,7 @@ target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
```
-为了交叉编译Cortex-M3架构我们不得不使用`thumbv7m-none-eabi`。当安装Rust工具时,target不会自动被安装,如果你还没有做,现在是个好时机,去添加那个target到工具链上。
+为了交叉编译Cortex-M3架构我们不得不使用`thumbv7m-none-eabi`。当安装Rust工具时,target不会自动被安装,如果你还没有做,现在可以去添加那个target到工具链上。
``` console
rustup target add thumbv7m-none-eabi
```
@@ -143,7 +143,7 @@ cargo readobj --bin app -- --file-headers
注意:
* `--bin app` 是一个用来检查`target/$TRIPLE/debug/app`的语法糖
-* `--bin app` 如果需要,将也会重新编译二进制。
+* `--bin app` 如果有需要,也会重新编译二进制项。
``` text
ELF Header:
@@ -168,7 +168,7 @@ ELF Header:
Section header string table index: 18
```
-`cargo-size` 能打印二进制文件的linker部分的大小。
+`cargo-size` 能打印二进制项的linker部分的大小。
```console
cargo size --bin app --release -- -A
@@ -207,9 +207,9 @@ Total 14570
> - `.vector_table` 是一个我们用来存储向量(中断)表的*非*标准的section
> - `.ARM.attributes` 和 `.debug_*` sections包含元数据,当烧录二进制文件时,它们不会被加载到目标上的。
-**重要**: ELF文件包含像是调试信息这样的元数据,因此它们在*硬盘上的尺寸*没有正确地反应了程序被烧录到设备上时将占据的空间的大小。*一直*使用`cargo-size`检查一个二进制文件有多大。
+**重要**: ELF文件包含像是调试信息这样的元数据,因此它们在*硬盘上的尺寸*没有正确地反应了程序被烧录到设备上时将占据的空间的大小。要*一直*使用`cargo-size`检查一个二进制项的大小。
-`cargo-objdump` 能用来反编译二进制文件。
+`cargo-objdump` 能用来反编译二进制项。
```console
cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-imm-hex
@@ -280,14 +280,14 @@ fn main() -> ! {
hprintln!("Hello, world!").unwrap();
// 退出 QEMU
- // NOTE 不要在硬件上运行这个;它会打破OpenOCD状态
+ // NOTE 不要在硬件上运行这个;它会打破OpenOCD的状态
debug::exit(debug::EXIT_SUCCESS);
loop {}
}
```
-这个程序使用一些被叫做semihosting的东西去打印文本到主机调试台上。当使用真实的硬件时,这要求一个调试对话,但是当使用QEMU时这样就可以工作了。
+这个程序使用一些被叫做semihosting的东西去打印文本到主机调试台上。当使用的是真实的硬件时,这需要一个调试对话,但是当使用的是QEMU时这就可以工作了。
通过编译示例,让我们开始
@@ -295,9 +295,9 @@ fn main() -> ! {
cargo build --example hello
```
-输出的二进制文件将位于`target/thumbv7m-none-eabi/debug/examples/hello`。
+输出的二进制项将位于`target/thumbv7m-none-eabi/debug/examples/hello`。
-为了在QEMU上运行这个二进制文件,执行下列的命令:
+为了在QEMU上运行这个二进制项,执行下列的命令:
```console
qemu-system-arm \
@@ -312,7 +312,7 @@ qemu-system-arm \
Hello, world!
```
-这个命令应该打印文本之后成功地退出 (exit code = 0)。你能使用下列的指令检查下:
+这个命令应该在打印文本之后成功地退出 (exit code = 0)。你可以使用下列的指令检查下:
```console
echo $?
@@ -324,13 +324,13 @@ echo $?
让我们看看QEMU命令:
-+ `qemu-system-arm`。这是QEMU仿真器。这些QEMU有一些改良版的二进制文件;这个仿真器能做ARM机器的全系统仿真。
++ `qemu-system-arm`。这是QEMU仿真器。这些QEMU有一些改良版的二进制项;这个仿真器能做ARM机器的全系统仿真。
+ `-cpu cortex-m3`。这告诉QEMU去仿真一个Cortex-M3 CPU。指定CPU模型会让我们捕捉到一些误编译错误:比如,运行一个为Cortex-M4F编译的程序,它具有一个硬件FPU,在执行时将会使QEMU报错。
+ `-machine lm3s6965evb`。这告诉QEMU去仿真 LM3S6965EVB,一个包含LM3S6965微控制器的评估板。
+ `-nographic`。这告诉QEMU不要启动它的GUI。
+ `-semihosting-config (..)`。这告诉QEMU使能半主机模式。半主机模式允许被仿真的设备,使用主机的stdout,stderr,和stdin,并在主机上创建文件。
-+ `-kernel $file`。这告诉QEMU在仿真机器上加载和运行哪个二进制文件。
++ `-kernel $file`。这告诉QEMU在仿真机器上加载和运行哪个二进制项。
输入这么长的QEMU命令太费功夫了!我们可以设置一个自定义运行器(runner)简化步骤。`.cargo/config.toml` 有一个被注释掉的,可以调用QEMU的运行器。让我们去掉注释。
```console
@@ -376,7 +376,7 @@ qemu-system-arm \
这个命令将不打印任何东西到调试台上,且将会阻塞住终端。此刻我们还传递了两个额外的标志。
+ `-gdb tcp::3333`。这告诉QEMU在3333的TCP端口上等待一个GDB连接。
-+ `-S`。这告诉QEMU在启动时,冻结机器。没有这个,在我们有机会启动调试器之前,程序可能已经到达了主程序的底部了!
++ `-S`。这告诉QEMU在启动时,冻结机器。没有这个,在我们有机会启动调试器之前,程序有可能已经到达了主程序的底部了!
接下来我们在另一个终端启动GDB,且告诉它去加载示例的调试符号。
```console
From e08766e36dbece6788d5313d43e4237a1cd59a5a Mon Sep 17 00:00:00 2001
From: XxChang
Date: Thu, 13 Oct 2022 23:45:43 +0800
Subject: [PATCH 108/137] minor
---
src/start/registers.md | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/start/registers.md b/src/start/registers.md
index 2a48d1c4..08a31e83 100644
--- a/src/start/registers.md
+++ b/src/start/registers.md
@@ -1,17 +1,17 @@
# 存储映射的寄存器
-嵌入式系统只能通过执行普通的Rust代码和在RAM间移动数据来运行下去。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。
+嵌入式系统运行下去只能通过执行常规的Rust代码并在RAM间移动数据来进行。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。
-你可能发现,访问你的微控制器外设所需要的代码,已经被写进了下面的某个抽象层中。
+你可能发现,访问你的微控制器外设所需要的代码,已经被写进了下面的某个抽象层中了。
-* Micro-architecture Crate(微架构Crate) - 这个Crate提供那些对你正在使用的微控制器的处理器内核和任何使用这个处理器内核的微控制器的那些常见外设,有用的程序。比如 [cortex-m] crate提供给你可以使能和关闭中断的函数,其对于所有的Cortex-M微控制器都是一样的。它也提供你访问'SysTick'外设的能力,在所有的Cortex-M微控制器中都包括了这个能力。
+* Micro-architecture Crate(微架构Crate) - 这个库拥有任何对于你正用着的微控制器的处理器内核来说经常会用到的程序,也包括对于这些微控制器中通用的外设有用的程序。比如 [cortex-m] crate提供给你可以使能和关闭中断的函数,其对于所有的Cortex-M微控制器都是一样的。它也提供你访问'SysTick'外设的能力,在所有的Cortex-M微控制器中都包括了这个能力。
* Peripheral Access Crate(PAC)(外设访问Crate) - 这种crate是在被不同存储器封装的寄存器上再进行的一次封装,其由你正在使用的微控制器的产品号定义的。比如,[tm4c123x]针对TI的Tiva-C TM4C123系列,[stm32f30x]针对ST的STM32F30x系列。这块,你将根据你的微控制器的技术手册写的每个外设操作指令,直接和寄存器交互。
* HAL Crate - 这些crates为你的处理器提供了一个更友好的API,通常是通过实现在[embedded-hal]中定义的一些常用的traits来实现的。比如,这个crate可能提供一个`Serial`结构体,它的构造函数需要一组合适的GPIO端口和一个波特率,它为了发送数据提供一些 `write_byte` 函数。查看 [可移植性] 可以看到更多关于 [embedded-hal] 的信息。
-* Board Crate(开发板crate) - 这个通过预先配置的不同的外设和GPIO管脚再进行了一层抽象去适配你正在使用的特定的开发者工具或者开发板,比如对于STM32F3DISCOVERY开发板来说,是[stm32f3-discovery]
+* Board Crate(开发板crate) - 这些Crate通过预配置不同的外设和GPIO管脚再进行了一层抽象以适配你正在使用的特定的开发者工具或者开发板,比如对于STM32F3DISCOVERY开发板来说,是[stm32f3-discovery]
[cortex-m]: https://crates.io/crates/cortex-m
[tm4c123x]: https://crates.io/crates/tm4c123x
@@ -23,7 +23,7 @@
## 开发板Crate (Board Crate)
-如果你是嵌入式Rust新手,board crate是一个完美的起点。它们很好地抽象出了,在开始学习这个项目时,需要耗费心力了解的硬件细节,使得标准工作变得简单,像是打开或者关闭LED。不同的板子间,它们提供的功能变化很大。因为这本书是不假设我们使用的是何种板子,所以board crate不会被这本书涉及。
+如果你是嵌入式Rust新手,board crate是一个完美的开始。它们很好地抽象出了,在开始学习这个项目时,需要耗费心力了解的硬件细节,使得标准工作变得简单,像是打开或者关闭LED。不同的板子间,它们提供的功能变化很大。因为这本书是不假设我们使用的是何种板子,所以这本书不会提到board crate。
如果你想要用STM32F3DISCOVERY开发板做实验,强烈建议看一下[stm32f3-discovery]开发板crate,它提供了点亮LEDs,访问它的指南针,蓝牙和其它的功能。[Discovery]书对于一个board crate的用法提供一个很好的介绍。
@@ -55,7 +55,7 @@ fn main() -> ! {
loop {}
}
```
-`SYST`结构体上的功能,相当接近ARM技术手册为这个外设定义的功能。在这个API中没有关于 '延迟X毫秒' 的功能 - 我们不得不使用一个 `while` 循环自己粗略地实现它。注意,我们调用了`Peripherals::take()`才能访问我们的`SYST`结构体 - 这是一个特别的程序,保障了在我们的整个程序中只存在一个`SYST`结构体实例,更多的信息可以看[外设]部分。
+`SYST`结构体上的功能,相当接近ARM技术手册为这个外设定义的功能。在这个API中没有关于 '延迟X毫秒' 的功能 - 我们不得不通过使用一个 `while` 循环来粗略地实现它。注意,我们调用了`Peripherals::take()`才能访问我们的`SYST`结构体 - 这是一个特别的程序,保障了在我们的整个程序中只存在一个`SYST`结构体实例,更多的信息可以看[外设]部分。
[外设]: ../peripherals/index.md
@@ -90,7 +90,7 @@ pub fn init() -> (Delay, Leds) {
```
-我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` 之外。因为这个crate是使用[svd2rust]自动生成的,访问我们寄存器位域的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是更多代码了,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数,其所有可能的参数都有效(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊含义),则该函数被标记为 `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。
+我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` 之外。因为这个crate是使用[svd2rust]自动生成的,访问我们寄存器字段的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是有了更多的代码了,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数,其所有可能的参数都有效(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊含义),则该函数被标记为 `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。
### 读取
From a3722357040e577f117105aa0908ad542c60c55b Mon Sep 17 00:00:00 2001
From: XxChang
Date: Thu, 13 Oct 2022 23:50:38 +0800
Subject: [PATCH 109/137] minor
---
src/peripherals/a-first-attempt.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/peripherals/a-first-attempt.md b/src/peripherals/a-first-attempt.md
index a835b8cb..dc574efb 100644
--- a/src/peripherals/a-first-attempt.md
+++ b/src/peripherals/a-first-attempt.md
@@ -75,7 +75,7 @@ fn get_time() -> u32 {
现在通过`read`和`write`方法,volatile accesses可以被自动执行。执行写操作仍然是 `unsafe` 的,但是公平地讲,硬件有一堆可变的状态,对于编译器来说没有方法去知道是否这些写操作是真正安全的,因此默认就这样是个不错的选择。
-## Rusty封装
+## Rust风格的封装
我们需要把这个`struct`封装进一个更高抽象的API中,这个API对于我们用户来说,可以安全地被调用。作为驱动的作者,我们手动地验证不安全的代码是否正确,然后为我们的用户提供一个safe的API,因此他们不必担心它(让他们相信我们做对了!)。
From f27f2c0cabffe6592fd42c65d2164ecf620733b3 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Fri, 14 Oct 2022 11:14:05 +0800
Subject: [PATCH 110/137] minor
---
src/peripherals/borrowck.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/peripherals/borrowck.md b/src/peripherals/borrowck.md
index 6eb49179..f8436bf7 100644
--- a/src/peripherals/borrowck.md
+++ b/src/peripherals/borrowck.md
@@ -1,6 +1,6 @@
## 可变的全局状态
-不幸地是,硬件本质上是个可变的全局状态,对于Rust开发者来说可能感到很害怕。因为硬件独立于我们所写的代码的结构,能被真实世界在任何时候改变。
+不幸地是,硬件本质上是个可变的全局状态,Rust开发者可能对此感到很害怕。因为硬件独立于我们所写的代码的结构,能被真实世界在任何时候改变。
## 我们应该遵循什么规则?
From 27d370571181e78b7cd07acd364a46621f5f3381 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Fri, 14 Oct 2022 11:22:02 +0800
Subject: [PATCH 111/137] minor
---
src/intro/no-std.md | 4 ++--
src/intro/tooling.md | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/intro/no-std.md b/src/intro/no-std.md
index 040d6ad0..a28ef35e 100644
--- a/src/intro/no-std.md
+++ b/src/intro/no-std.md
@@ -30,9 +30,9 @@
| libcore available | ✓ | ✓ |
| writing firmware, kernel, or bootloader code | ✓ | ✘ |
-\* 只有在你使用了 `alloc` crate 和一个适合的分配器,比如[alloc-cortex-m]时有效。
+\* 只有在你使用了 `alloc` crate 和设置了一个适合的分配器,比如[alloc-cortex-m]后有效。
-\** 只有在你使用了 `collections` crate 和配置了一个全局默认的分配器时有效。
+\** 只有在你使用了 `collections` crate 并配置了一个全局默认的分配器后有效。
[alloc-cortex-m]: /~https://github.com/rust-embedded/alloc-cortex-m
diff --git a/src/intro/tooling.md b/src/intro/tooling.md
index 1a5b2f98..54ab00fb 100644
--- a/src/intro/tooling.md
+++ b/src/intro/tooling.md
@@ -1,5 +1,5 @@
# 工具
-与微控制器打交道需要使用几种不同的工具,因为我们要处理的架构与笔记本电脑不同,我们必须在 *远程* 设备上运行和调试程序。我们将使用下面列举出来的工具。当没有指定一个最小版本时,最新的版本应该可以工作,但是我们已经列出了我们已经测过的那些版本。
+与微控制器打交道需要使用几种不同的工具,因为我们要处理的架构与笔记本电脑不同,我们必须在 *远程* 设备上运行和调试程序。我们将使用下面列举出来的工具。当没有指定一个最小版本时,最新的版本应该也可以用,但是我们还是列出了我们已经测过的那些版本。
- Rust 1.31, 1.31-beta, 或者一个更新的,支持ARM Cortex-M编译的工具链。
- [`cargo-binutils`](/~https://github.com/rust-embedded/cargo-binutils) ~0.1.4
- [`qemu-system-arm`](https://www.qemu.org/). 测试的版本: 3.0.0
@@ -27,7 +27,7 @@ QEMU是一个仿真器。在这个例子里,我们使用能完全仿真ARM系
## OpenOCD
GDB不能直接和你的STM32F3DISCOVERY开发板上的ST-Link调试硬件通信。它需要一个转译者,the Open On-Chip Debugger,OpenOCD就是这样的转译者。OpenOCD是一个运行在你的笔记本/个人电脑上的程序,它在基于远程调试的TCP/IP协议和ST-Link的USB协议间进行转译。
-OpenOCD也执行其它一些转译中的重要的工作,用于调试你的STM32FDISCOVERY开发板上的ARM Cortex-M微控制器:
+OpenOCD也执行其它一些转译中的重要的工作,这些工作用于调试你的STM32FDISCOVERY开发板上的ARM Cortex-M微控制器:
* 它知道如何与ARM CoreSight调试外设使用的内存映射的寄存器进行交互。这些CoreSight寄存器的功能有:
* 断点操作
* 读取和写入CPU寄存器
From 0bf60da40c2df673076a354381fd0ec3c7d6b970 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Fri, 14 Oct 2022 15:04:54 +0800
Subject: [PATCH 112/137] minor
---
src/start/panicking.md | 8 ++++----
src/start/registers.md | 14 +++++++-------
src/start/semihosting.md | 2 +-
3 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/start/panicking.md b/src/start/panicking.md
index 2c118d2d..e6b60e50 100644
--- a/src/start/panicking.md
+++ b/src/start/panicking.md
@@ -2,15 +2,15 @@
运行时恐慌是Rust语言的一个核心部分。像是索引这样的內建的操作为了存储安全性是运行时检查的。当尝试越界索引时,这会导致运行时恐慌(panic)。
-在标准库中,运行时恐慌有一个被定义了的行为:它展开(unwinds)恐慌的线程的栈,除非用户选择在恐慌时终止程序。
+在标准库中,运行时恐慌的行为被定义了:它展开(unwinds)恐慌的线程的栈,除非用户选择在恐慌时终止程序。
然而在没有标准库的程序中,运行时恐慌的行为是未被定义了的。通过声明一个 `#[painc_handler]` 函数可以选择一个运行时恐慌的行为。
-这个函数必须在一个程序的依赖图中只出现一次,且必须有这样的签名: `fn(&PanicInfo) -> !`,`PanicInfo`是一个包含关于运行时恐慌发生位置的信息的结构体。
+这个函数在一个程序的依赖图中必须只出现一次,且必须有这样的签名: `fn(&PanicInfo) -> !`,`PanicInfo`是一个包含关于发生运行时恐慌的位置信息的结构体。
[`PanicInfo`]: https://doc.rust-lang.org/core/panic/struct.PanicInfo.html
-鉴于嵌入式系统的范围从面向用户的系统到安全关键系统,没有一个运行时恐慌行为能满足所有场景,但是有许多常用的行为。这些常用的行为已经被打包进了一些crates中,这些crates定义了 `#[panic_handler]`函数。比如:
+鉴于嵌入式系统的范围从面向用户的系统到安全关键系统,没有一个运行时恐慌行为能满足所有场景,但是有许多常用的行为。这些常用的行为已经被打包进了一些crates中,这些crates中定义了 `#[panic_handler]`函数。比如:
- [`panic-abort`]. 这个运行时恐慌会导致终止指令被执行。
- [`panic-halt`]. 这个运行时恐慌会导致程序,或者现在的线程,通过进入一个无限循环中而挂起。
@@ -26,7 +26,7 @@
[`panic-handler`]: https://crates.io/keywords/panic-handler
-仅仅通过连接到相关的crate,一个程序就可以从这些行为中选择一个行为。将运行时恐慌的行为作为一行代码放进一个应用的源码中,不仅仅是可以作为文档使用,而且能根据编译配置改变运行时恐慌的行为。比如:
+仅仅通过链接到相关的crate中,一个程序就可以从这些行为中选择一个运行时恐慌行为。将运行时恐慌的行为作为一行代码放进一个应用的源码中,不仅仅是可以作为文档使用,而且能根据编译配置改变运行时恐慌的行为。比如:
``` rust,ignore
#![no_main]
diff --git a/src/start/registers.md b/src/start/registers.md
index 08a31e83..0535afcd 100644
--- a/src/start/registers.md
+++ b/src/start/registers.md
@@ -1,15 +1,15 @@
# 存储映射的寄存器
-嵌入式系统运行下去只能通过执行常规的Rust代码并在RAM间移动数据来进行。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。
+只有通过执行常规的Rust代码并在RAM间移动数据,嵌入式系统才能继续运行下去。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。
-你可能发现,访问你的微控制器外设所需要的代码,已经被写进了下面的某个抽象层中了。
+你可能会发现,访问你的微控制器外设所需要的代码,已经被写进了下面的某个抽象层中了。
-* Micro-architecture Crate(微架构Crate) - 这个库拥有任何对于你正用着的微控制器的处理器内核来说经常会用到的程序,也包括对于这些微控制器中通用的外设有用的程序。比如 [cortex-m] crate提供给你可以使能和关闭中断的函数,其对于所有的Cortex-M微控制器都是一样的。它也提供你访问'SysTick'外设的能力,在所有的Cortex-M微控制器中都包括了这个能力。
-* Peripheral Access Crate(PAC)(外设访问Crate) - 这种crate是在被不同存储器封装的寄存器上再进行的一次封装,其由你正在使用的微控制器的产品号定义的。比如,[tm4c123x]针对TI的Tiva-C TM4C123系列,[stm32f30x]针对ST的STM32F30x系列。这块,你将根据你的微控制器的技术手册写的每个外设操作指令,直接和寄存器交互。
+* Micro-architecture Crate(微架构Crate) - 这个库拥有任何对于微控制器的处理器内核来说经常会用到的程序,也包括对于在这些微控制器中的通用外设来说有用的程序。比如 [cortex-m] crate提供给你可以使能和关闭中断的函数,其对于所有的Cortex-M微控制器都是一样的。它也提供你访问'SysTick'外设的能力,在所有的Cortex-M微控制器中都包括了这个外设功能。
+* Peripheral Access Crate(PAC)(外设访问Crate) - 这种crate是在被不同存储器封装的寄存器上再进行的一次封装,寄存器由你正在使用的微控制器的产品号定义的。比如,[tm4c123x]针对TI的Tiva-C TM4C123系列,[stm32f30x]针对ST的STM32F30x系列。这块,你将根据你的微控制器的技术手册写的每个外设操作指令,直接和寄存器交互。
* HAL Crate - 这些crates为你的处理器提供了一个更友好的API,通常是通过实现在[embedded-hal]中定义的一些常用的traits来实现的。比如,这个crate可能提供一个`Serial`结构体,它的构造函数需要一组合适的GPIO端口和一个波特率,它为了发送数据提供一些 `write_byte` 函数。查看 [可移植性] 可以看到更多关于 [embedded-hal] 的信息。
* Board Crate(开发板crate) - 这些Crate通过预配置不同的外设和GPIO管脚再进行了一层抽象以适配你正在使用的特定的开发者工具或者开发板,比如对于STM32F3DISCOVERY开发板来说,是[stm32f3-discovery]
@@ -31,7 +31,7 @@
## Micro-architecture crate
-让我们看一下对于所有的Cortex-M微控制器来说很常见的SysTick外设。我们能在[cortex-m] crate中找到一个相当底层的API,我们能像这样使用它:
+让我们看一下SysTick外设,SysTick外设存在于所有的Cortex-M微控制器中。我们能在[cortex-m] crate中找到一个相当底层的API,我们能像这样使用它:
```rust,ignore
#![no_std]
@@ -90,7 +90,7 @@ pub fn init() -> (Delay, Leds) {
```
-我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` 之外。因为这个crate是使用[svd2rust]自动生成的,访问我们寄存器字段的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是有了更多的代码了,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数,其所有可能的参数都有效(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊含义),则该函数被标记为 `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。
+我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` 之外。因为这个crate是使用[svd2rust]自动生成的,访问我们寄存器字段的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是有了更多的代码了,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数其所有可能的参数都能发挥作用(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊含义),则该函数被标记为 `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。
### 读取
@@ -136,7 +136,7 @@ pwm0.enable.write(temp); // 哦 不! 错误的变量!
## 使用一个HAL crate
-一个芯片的HAL crate通常通过为PAC暴露的基础结构体们实现一个自定义Trait来发挥作用。经常这个trait将会为某个外设定义一个被称作 `constrain()` 的函数或者为像是有多个管脚的GPIO端口定义一个`split()`函数。这个函数将会使用基础外设结构体,然后返回一个具有更高抽象的API的新对象。这个API还可以做一些事,像是让Serial port的 `new` 函数变成需要某些`Clock`结构体的函数,这个结构体只能通过调用配置PLLs和设置所有的时钟频率的函数来生成。在这时,生成一个Serial port对象而不先配置时钟速率是不可能的,对于Serial port对象来说错误地将波特率转换为时钟滴答数也是不可能发生的。一些crates甚至为每个GPIO管脚的状态定义了特定的 traits,在把管脚传递进外设前,要求用户去把一个管脚设置成正确的状态(通过选择Alternate Function模式) 。所有这些都没有运行时开销的!
+一个芯片的HAL crate通常通过为PAC暴露的基础结构体们实现一个自定义Trait来发挥作用。经常这个trait将会为某个外设定义一个被称作 `constrain()` 的函数或者为像是有多个管脚的GPIO端口这类东西定义一个`split()`函数。这个函数将会使用基础外设结构体,然后返回一个具有更高抽象的API的新对象。这个API还可以做一些事,像是让Serial port的 `new` 函数变成需要某些`Clock`结构体的函数,这个结构体只能通过调用配置PLLs并设置所有的时钟频率的函数来生成。在这时,生成一个Serial port对象而不先配置时钟速率是不可能的,对于Serial port对象来说错误地将波特率转换为时钟滴答数也是不可能发生的。一些crates甚至为每个GPIO管脚的状态定义了特定的 traits,在把管脚传递进外设前,要求用户去把一个管脚设置成正确的状态(通过选择Alternate Function模式) 。所有这些都没有运行时开销的!
让我们看一个例子:
diff --git a/src/start/semihosting.md b/src/start/semihosting.md
index e9d86deb..2de73235 100644
--- a/src/start/semihosting.md
+++ b/src/start/semihosting.md
@@ -1,6 +1,6 @@
# 半主机模式
-半主机模式是一种让嵌入式设备在主机上进行I/O操作的的机制,主要被用来记录信息到主机控制台上。半主机模式需要一个debug会话,除此之外几乎没有其它要求了,因此它非常易于使用。缺点是它非常慢:每个写操作需要几毫秒的时间,其取决于你的硬件调试器(e.g. ST-LINK)。
+半主机模式是一种可以让嵌入式设备在主机上进行I/O操作的的机制,主要被用来记录信息到主机控制台上。半主机模式需要一个debug会话,除此之外几乎没有其它要求了,因此它非常易于使用。缺点是它非常慢:每个写操作需要几毫秒的时间,其取决于你的硬件调试器(e.g. ST-LINK)。
[`cortex-m-semihosting`] crate 提供了一个API去在Cortex-M设备上执行半主机操作。下面的程序是"Hello, world!"的半主机版本。
From 2e99bc815edd7073e287dc287feb4c86f047ac7c Mon Sep 17 00:00:00 2001
From: XxChang
Date: Fri, 14 Oct 2022 15:56:53 +0800
Subject: [PATCH 113/137] minor
---
src/SUMMARY.md | 2 +-
src/peripherals/a-first-attempt.md | 20 ++++++++++----------
src/start/exceptions.md | 8 ++++----
src/start/interrupts.md | 6 +++---
src/start/registers.md | 2 +-
5 files changed, 19 insertions(+), 19 deletions(-)
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index c5a302fa..f7499fe7 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -28,7 +28,7 @@ more information and coordination
- [中断](./start/interrupts.md)
- [IO](./start/io.md)
- [外设](./peripherals/index.md)
- - [首次尝试](./peripherals/a-first-attempt.md)
+ - [Rust的首次尝试](./peripherals/a-first-attempt.md)
- [借用检查器](./peripherals/borrowck.md)
- [单例](./peripherals/singletons.md)
- [静态保障(static guarantees)](./static-guarantees/index.md)
diff --git a/src/peripherals/a-first-attempt.md b/src/peripherals/a-first-attempt.md
index dc574efb..36007d86 100644
--- a/src/peripherals/a-first-attempt.md
+++ b/src/peripherals/a-first-attempt.md
@@ -1,4 +1,4 @@
-# 首次尝试
+# Rust的首次尝试
## 寄存器
@@ -8,10 +8,10 @@
| Offset | Name | Description | Width |
|--------|-------------|-----------------------------|--------|
-| 0x00 | SYST_CSR | Control and Status Register | 32 bits|
-| 0x04 | SYST_RVR | Reload Value Register | 32 bits|
-| 0x08 | SYST_CVR | Current Value Register | 32 bits|
-| 0x0C | SYST_CALIB | Calibration Value Register | 32 bits|
+| 0x00 | SYST_CSR | 控制和状态寄存器 | 32 bits|
+| 0x04 | SYST_RVR | 重装载值寄存器 | 32 bits|
+| 0x08 | SYST_CVR | 当前值寄存器 | 32 bits|
+| 0x0C | SYST_CALIB | 校准值寄存器 | 32 bits|
## C语言风格的方法(The C Approach)
@@ -26,14 +26,14 @@ struct SysTick {
pub calib: u32,
}
```
-限定符 `#[repr(C)]` 告诉Rust编译器像C编译器一样去布局这个结构体。那是非常重要的,因为Rust允许结构体字段被重新排序,而C语言不允许。你可以想象下如果这些字段被编译器悄悄地重新排序,在调试时会给我们带来什么麻烦!有了这个限定符,我们就有了与上表对应的四个32位的字段。但当然,这个 `struct` 本身没什么用处 - 我们需要一个变量。
+限定符 `#[repr(C)]` 告诉Rust编译器像C编译器一样去布局这个结构体。那是非常重要的,因为Rust允许结构体字段被重新排序,而C语言不允许。你可以想象下如果这些字段被编译器悄悄地重新排了序,在调试时会给我们带来多大的麻烦!有了这个限定符,我们就有了与上表对应的四个32位的字段。但当然,这个 `struct` 本身没什么用处 - 我们需要一个变量。
```rust,ignore
let systick = 0xE000_E010 as *mut SysTick;
let time = unsafe { (*systick).cvr };
```
-## 易变的访问(Volatile Accesses)
+## volatile访问(Volatile Accesses)
现在,上面的方法有一堆问题。
@@ -42,7 +42,7 @@ let time = unsafe { (*systick).cvr };
3. 你程序中任何地方的任何一段代码都可以通过这个结构体访问硬件。
4. 最重要的是,实际上它并不能工作。
-现在的问题是编译器很聪明。如果你往RAM同个地方写两次,一个接着一个,编译器会注意到这个行为,且完全跳过第一个写入操作。在C语言中,我们能标记变量为`volatile`去确保每个读或写操作按预期发生。在Rust中,我们将*访问* 标记为易变的(volatile),而不是将变量标记为volatile。
+现在的问题是编译器很聪明。如果你往RAM同个地方写两次,一个接着一个,编译器会注意到这个行为,且完全跳过第一个写入操作。在C语言中,我们能标记变量为`volatile`去确保每个读或写操作按预期发生。在Rust中,我们将*访问*操作标记为易变的(volatile),而不是将变量标记为volatile。
```rust,ignore
let systick = unsafe { &mut *(0xE000_E010 as *mut SysTick) };
@@ -77,7 +77,7 @@ fn get_time() -> u32 {
## Rust风格的封装
-我们需要把这个`struct`封装进一个更高抽象的API中,这个API对于我们用户来说,可以安全地被调用。作为驱动的作者,我们手动地验证不安全的代码是否正确,然后为我们的用户提供一个safe的API,因此他们不必担心它(让他们相信我们做对了!)。
+我们需要把这个`struct`封装进一个更高抽象的API中,这个API对于我们用户来说,可以安全地被调用。作为驱动的作者,我们亲手验证不安全的代码是否正确,然后为我们的用户提供一个safe的API,因此用户们不必担心它(让他们相信我们不会出错!)。
有可能有这样的例子:
@@ -133,4 +133,4 @@ fn thread2() {
}
```
-虽然 `set_reload` 函数的 `&mut self` 参数保证了对某个`SystemTimer`结构体的引用只有一个,但是他们不能阻止用户去创造第二个`SystemTimer`,其指向同个外设!如果作者足够尽力,能发现所有这些'重复的'驱动实例,那么按这种方式写的代码将可以工作,但是一旦代码被散播几天,散播到多个模块,驱动,开发者,它会越来越容易犯此类错误。
+虽然 `set_reload` 函数的 `&mut self` 参数保证了对某个`SystemTimer`结构体的引用只有一个,但是他们不能阻止用户去创造第二个`SystemTimer`,其指向同个外设!如果作者足够尽力,他能发现所有这些'重复的'驱动实例,那么按这种方式写的代码将可以工作,但是一旦代码被散播几天,散播到多个模块,驱动,开发者,它会越来越容易犯此类错误。
diff --git a/src/start/exceptions.md b/src/start/exceptions.md
index 0c11c86b..29751b2d 100644
--- a/src/start/exceptions.md
+++ b/src/start/exceptions.md
@@ -1,6 +1,6 @@
# 异常
-异常和中断,是处理器用来处理异步事件和致命错误(e.g. 执行一个无效的指令)的一种硬件机制。异常意味着抢占且涉及到异常处理程序,即响应触发事件的信号的子例程。
+异常和中断,是处理器用来处理异步事件和致命错误(e.g. 执行一个无效的指令)的一种硬件机制。异常意味着抢占并涉及到异常处理程序,即响应触发事件的信号的子程序。
`cortex-m-rt` crate提供了一个 [`exception`] 属性去声明异常处理程序。
@@ -16,7 +16,7 @@ fn SysTick() {
除了 `exception` 属性,异常处理函数看起来和普通函数一样,但是有一个很大的不同: `exception` 处理函数 *不能* 被软件调用。在先前的例子中,语句 `SysTick();` 将会导致一个编译错误。
-这么做是有目的的,因为异常处理函数必须具有一个特性: 在异常处理函数中被声明`static mut`的变量能被安全(safe)地使用。
+这么做是故意的,因为异常处理函数必须具有一个特性: 在异常处理函数中被声明为`static mut`的变量能被安全(safe)地使用。
``` rust,ignore
#[exception]
@@ -28,7 +28,7 @@ fn SysTick() {
}
```
-就像你可能已经知道的那样,在一个函数里使用`static mut`变量,会让函数变成[*非可重入函数(non-reentrancy)*](https://en.wikipedia.org/wiki/Reentrancy_(computing))。从多个异常/中断处理函数,或者从`main`函数同多个异常/中断处理函数中,直接或者间接地调用一个非可重入(non-reentrancy)函数是未定义的行为。
+就像你可能已经知道的那样,在一个函数里使用`static mut`变量,会让函数变成[*非可重入函数(non-reentrancy)*](https://en.wikipedia.org/wiki/Reentrancy_(computing))。从多个异常/中断处理函数,或者从`main`函数和多个异常/中断处理函数中,直接或者间接地调用一个非可重入(non-reentrancy)函数是未定义的行为。
安全的Rust不能导致未定义的行为出现,所以非可重入函数必须被标记为 `unsafe`。然而,我刚说了`exception`处理函数能安全地使用`static mut`变量。这怎么可能?因为`exception`处理函数 *不* 能被软件调用因此重入(reentrancy)不会发生,所以这才变得可能。
@@ -127,7 +127,7 @@ fn DefaultHandler() {
}
```
-这个函数是 `cortex-m-rt` crate提供的,且被标记为 `#[no_mangle]` 因此你能在 "DefaultHandler" 上放置一个断点和捕获 *unhandled* 异常。
+这个函数是 `cortex-m-rt` crate提供的,且被标记为 `#[no_mangle]` 因此你能在 "DefaultHandler" 上放置一个断点并捕获 *unhandled* 异常。
可以使用 `exception` 属性覆盖这个 `DefaultHandler`:
diff --git a/src/start/interrupts.md b/src/start/interrupts.md
index 5f220001..dc22b8b7 100644
--- a/src/start/interrupts.md
+++ b/src/start/interrupts.md
@@ -2,10 +2,10 @@
虽然中断和异常在很多方面都不一样,但是它们的操作和使用几乎一样,且它们也能被同一个中断控制器处理。然而异常是由Cortex-M微架构定义的,中断在命名和功能上总是由特定厂商(经常甚至是芯片)实现的。
-中断提供了更多的灵活性,当尝试用一种高级的方法使用它们时,我们需要对这种灵活性进行解释。但我们将不会在这本书里涵盖那些内容,把下面的东西记在心里是个不错的想法:
+中断提供了更多的灵活性,当尝试用一种高级的方法使用它们时,我们需要对这种灵活性进行解释。但我们将不会在这本书里涵盖这些内容,最好把下面的东西记在心里:
* 中断有可以编程的优先级,其决定了它们的处理函数的执行顺序。
* 中断能嵌套且抢占,i.e. 一个中断处理函数的执行可以被其它更高优先级的中断打断。
-* 通常导致中断被触发的因素需要被清除,避免无限地再次进入中断处理函数。
+* 通常需要清除掉导致中断被触发的原因,避免无限地再次进入中断处理函数。
运行时的常规初始化步骤始终相同:
* 设置外设在遇到想要的事故发生的时候产生中断请求
@@ -39,6 +39,6 @@ fn TIM2() {
}
```
-关于这里说的机制的更多细节描述,请参考[异常章节]。
+关于这里所说的机制的更多细节描述,请参考[异常章节]。
[异常章节]: ./exceptions.md
diff --git a/src/start/registers.md b/src/start/registers.md
index 0535afcd..0ec67807 100644
--- a/src/start/registers.md
+++ b/src/start/registers.md
@@ -90,7 +90,7 @@ pub fn init() -> (Delay, Leds) {
```
-我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` 之外。因为这个crate是使用[svd2rust]自动生成的,访问我们寄存器字段的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是有了更多的代码了,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数其所有可能的参数都能发挥作用(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊含义),则该函数被标记为 `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。
+我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` 之外。因为这个crate是使用[svd2rust]自动生成的,访问我们寄存器位段的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是有了更多的代码了,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数其所有可能的参数都能发挥作用(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊含义),则该函数被标记为 `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。
### 读取
From e44135e202fb0b6d349a1c2870ee5a8362f0d37d Mon Sep 17 00:00:00 2001
From: XxChang
Date: Fri, 14 Oct 2022 16:09:24 +0800
Subject: [PATCH 114/137] minor
---
src/peripherals/borrowck.md | 2 +-
src/peripherals/singletons.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/peripherals/borrowck.md b/src/peripherals/borrowck.md
index f8436bf7..2f3a21c7 100644
--- a/src/peripherals/borrowck.md
+++ b/src/peripherals/borrowck.md
@@ -16,4 +16,4 @@
思考一下,我们是否可以分发这些外设的所有权,或者引用它们?
-我们当然可以,但是对于借用检查器来说,每个外设我们都只需要一个实例的话,Rust才可以正确地处理这个实例。在硬件中幸运的是,任何给定的外设,只有一个实例,但是我们该如何将它暴露在我们代码的结构中呢?
+我们当然可以,但是对于借用检查器来说,每个外设我们都只需要一个实例,因此Rust可以正确地处理这个实例。幸运的是,在硬件中,任何给定的外设,只有一个实例,但是我们该如何将它暴露在我们代码的结构中呢?
diff --git a/src/peripherals/singletons.md b/src/peripherals/singletons.md
index 1a7a87f9..906f9c78 100644
--- a/src/peripherals/singletons.md
+++ b/src/peripherals/singletons.md
@@ -21,7 +21,7 @@ fn main() {
}
```
-但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你的整个程序间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有者。
+但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你的整个程序之间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有者。
## 我们在Rust中要怎么做?
From a9c13cfd47c5a0758b0b7d08acfd93b03b058f64 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Sat, 15 Oct 2022 21:57:53 +0800
Subject: [PATCH 115/137] minor
---
src/peripherals/singletons.md | 6 +++---
src/static-guarantees/index.md | 4 ++--
src/static-guarantees/typestate-programming.md | 6 +++---
3 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/peripherals/singletons.md b/src/peripherals/singletons.md
index 906f9c78..bd0e48f4 100644
--- a/src/peripherals/singletons.md
+++ b/src/peripherals/singletons.md
@@ -21,11 +21,11 @@ fn main() {
}
```
-但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你的整个程序之间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有者。
+但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你所有的程序之间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有权。
## 我们在Rust中要怎么做?
-与其只是让我们的外设变成一个全局变量,我们不如决定使用一个全局变量,在这个例子里其被叫做 `PERIPHERALS`,这个全局变量对于我们的每个外设,它都有一个与之对应的 `Option` 。
+与其只是让我们的外设变成一个全局变量,我们不如决定创造一个全局变量,在这个例子里其被叫做 `PERIPHERALS`,这个全局变量对于我们的每个外设,它都有一个与之对应的 `Option` 。
```rust,ignore
struct Peripherals {
@@ -54,7 +54,7 @@ fn main() {
虽然与这个结构体交互是`unsafe`,然而一旦我们获得了它包含的 `SerialPort`,我们将不再需要使用`unsafe`,或者`PERIPHERALS`结构体。
-这个带来了少量的运行时消耗,因为我们必须打包 `SerialPort` 结构体进一个option中,且我们将需要调用一次 `take_serial()`,但是这种少量的前期成本,能使我们在接下来的程序中使用借用检查器(borrow checker) 。
+这个带来了少量的运行时开销,因为我们必须打包 `SerialPort` 结构体进一个option中,且我们将需要调用一次 `take_serial()`,但是这种少量的前期成本,能使我们在接下来的程序中使用借用检查器(borrow checker) 。
## 已存在的库支持
diff --git a/src/static-guarantees/index.md b/src/static-guarantees/index.md
index cc12d035..ee10a4f0 100644
--- a/src/static-guarantees/index.md
+++ b/src/static-guarantees/index.md
@@ -1,11 +1,11 @@
# 静态保障
-Rust的类型系统可防止编译时的数据竞争(看[`Send`]和[`Sync`]特性(traits))。类型系统也能被用来在编译时检查其它属性;减少某些样例中运行时检查的需要。
+Rust的类型系统可防止编译时的数据竞争(看[`Send`]和[`Sync`]特性(traits))。类型系统也能被用来在编译时检查其它属性;减少某些案例中运行时检查的需要。
[`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
[`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html
-当应用到嵌入式程序时,这些*静态检查*能被用来,比如,强制I/O接口被恰当地配置。例如,可以设计一个API来初始化一个串行接口,这个API只能通过先配置接口需要的管脚才能被正确地使用。
+当应用到嵌入式程序时,这些*静态检查*能被用来,比如,强制I/O接口的配置被恰当地执行了。例如,可以设计一个API来初始化一个串行接口,这个API只能通过先配置接口需要的管脚后才能被正确地使用。
也可以静态检查操作是否被执行在正确配置了的外设上,像是拉低一个管脚。例如尝试修改一个被配置成浮空输入模式的管脚的输出状态时,将会出发一个编译时错误。
diff --git a/src/static-guarantees/typestate-programming.md b/src/static-guarantees/typestate-programming.md
index 161f7de3..1469dee3 100644
--- a/src/static-guarantees/typestate-programming.md
+++ b/src/static-guarantees/typestate-programming.md
@@ -1,6 +1,6 @@
# 类型状态编程(Typestate Programming)
-[typestates]的概念描述了将有关对象当前状态的信息编码进该对象的类型中。虽然这听起来有点神秘,如果你在Rust中使用了[建造者模式],你就已经开始使用类型状态编程了!
+[typestates]的概念是指将有关对象当前状态的信息编码进该对象的类型中。虽然这听起来有点神秘,如果你在Rust中使用了[建造者模式],你就已经开始使用类型状态编程了!
[typestates]: https://en.wikipedia.org/wiki/Typestate_analysis
[建造者模式]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
@@ -49,7 +49,7 @@ fn main() {
}
```
-在这个例子里,不能直接生成一个`Foo`对象。我们必须创造一个`FooBuilder`,在我们获取我们需要的`Foo`对象之前恰当地初始化`FooBuilder`。
+在这个例子里,不能直接生成一个`Foo`对象。我们必须创造一个`FooBuilder`,且我们恰当地初始化`FooBuilder`后才能获取到我们需要的`Foo`对象。
这个最小的例子编码了两个状态:
@@ -58,7 +58,7 @@ fn main() {
## 强类型
-因为Rust有一个[强类型系统],没有方法简单的,魔法般地创造一个`Foo`实例或者不用调用`into_foo()`方法把一个`FooBuilder`变成一个`Foo`。另外,调用`into_foo()`方法消费了最初的`FooBuilder`结构体,意味着不创造一个新的实例它就不能被再次使用。
+因为Rust有一个[强类型系统],没有简单的方法可以奇迹般地创造一个`Foo`实例或者不用调用`into_foo()`方法把一个`FooBuilder`变成一个`Foo`。另外,调用`into_foo()`方法消费了最初的`FooBuilder`结构体,意味着不创造一个新的实例它就不能被再次使用。
[强类型系统]: https://en.wikipedia.org/wiki/Strong_and_weak_typing
From af9b018e3f64128f3e5bd19b4f39f9213e0f6b9f Mon Sep 17 00:00:00 2001
From: XxChang
Date: Thu, 27 Oct 2022 16:45:56 +0800
Subject: [PATCH 116/137] minor
---
src/SUMMARY.md | 2 +-
src/intro/hardware.md | 4 +--
src/intro/index.md | 12 ++++----
src/intro/no-std.md | 4 +--
src/peripherals/singletons.md | 4 +--
src/static-guarantees/state-machines.md | 2 +-
.../zero-cost-abstractions.md | 6 ++--
src/unsorted/math.md | 6 ++--
src/unsorted/speed-vs-size.md | 30 +++++++++----------
9 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index f7499fe7..f42f0f78 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -52,7 +52,7 @@ more information and coordination
- [给Rust配上一点C](./interoperability/c-with-rust.md)
- [给C配上一点Rust](./interoperability/rust-with-c.md)
- [没有排序的主题](./unsorted/index.md)
- - [优化: 速度与大小间的均衡](./unsorted/speed-vs-size.md)
+ - [优化: 速度与大小间的博弈](./unsorted/speed-vs-size.md)
- [执行数学运算](./unsorted/math.md)
---
diff --git a/src/intro/hardware.md b/src/intro/hardware.md
index 5e022b25..8035ffb6 100644
--- a/src/intro/hardware.md
+++ b/src/intro/hardware.md
@@ -15,7 +15,7 @@
+ 256 KiB的"Flash"存储。
+ 48 KiB的RAM
+ 多种多样的外设,比如计时器,I2C,SPI和USART
- + 通用GPIO和板子两侧的其它类型引脚
+ + 通用GPIO和在板子两侧的其它类型的引脚
+ 一个写着“USB USER”的USB接口
+ 一个位于[LSM303DLHC](https://www.st.com/en/mems-and-sensors/lsm303dlhc.html)芯片上的[加速度计](https://en.wikipedia.org/wiki/Accelerometer)。
@@ -23,7 +23,7 @@
+ 一个位于[L3GD20](https://www.pololu.com/file/0J563/L3GD20.pdf)芯片上的[陀螺仪](https://en.wikipedia.org/wiki/Gyroscope).
-+ 8个摆得像一个指南针形状的用户LEDs。
++ 8个摆得像一个指南针形状的user LEDs。
+ 一个二级微控制器: [STM32F103](https://www.st.com/en/microcontrollers/stm32f103cb.html)。这个微控制器实际上是一个板载编程器/调试器的一部分,与名为“USB ST-LINK”的USB端口相连。
diff --git a/src/intro/index.md b/src/intro/index.md
index 676dfb73..052a1338 100644
--- a/src/intro/index.md
+++ b/src/intro/index.md
@@ -10,7 +10,7 @@ Embedded Rust是为了那些即想要进行嵌入式编程,又想要使用Rust
+ 分享那些关于使用Rust进行嵌入式开发的,现存的,最好的实践经验,比如,如何最大程度上地利用好Rust语言的特性去写更正确的嵌入式软件
+ 某种程度下作为工具书,比如,如何在一个项目里将C和Rust混合在一起使用
-虽然这本书尝试尽可能地可以适用于大多数场景,但是为了使读者和作者更容易理解,在所有的案例中它都使用了ARM Cortex-M架构。然而,这本书并不需要读者熟悉这个架构,书中会在需要时对这个架构的特定细节进行解释。
+虽然尽可能地尝试让这本书可以用于大多数场景,但是为了使读者和作者更容易理解,在所有的示例中这本书都使用了ARM Cortex-M架构。然而,这本书并不需要读者熟悉这个架构,书中会在需要时对这个架构的特定细节进行解释。
## 这本书是为谁准备的
@@ -44,7 +44,7 @@ Embedded Rust是为了那些即想要进行嵌入式编程,又想要使用Rust
### 翻译
-这本书是已经被一些慷慨的志愿者翻译了。如果你想要将你的翻译列在这里,请打开一个PR去添加它。
+这本书是已经被一些慷慨的志愿者们翻译了。如果你想要将你的翻译列在这里,请打开一个PR去添加它。
* [Japanese](https://tomoyuki-nakabayashi.github.io/book/)
([repository](/~https://github.com/tomoyuki-nakabayashi/book))
@@ -53,9 +53,9 @@ Embedded Rust是为了那些即想要进行嵌入式编程,又想要使用Rust
([repository](/~https://github.com/xxchang/book))
## 如何使用这本书
-这本书通常假设你是前后阅读的。之后章节是建立在先前章节中提到的概念之上的,先前章节可能不会深入一个主题的细节,因为在随后的章节将会再次重温这个主题。
-在大多数案例中这本书将使用[STM32F3DISCOVERY]开发板。这个板子是基于ARM Cortex-M架构的,且基本功能与大多数基于这个架构的CPUs功能相似。微处理器的外设和其它实现细节在不同的厂家之间是不同的,甚至来自同一个厂家,不同处理器系列之间也是不同的。
-因此我们建议购买[STM32F3DISCOVERY]开发板来尝试这本书中的例子。(译者注:我使用[renode](https://renode.io/about/)来测试大多数例子)
+这本书通常假设你是前后阅读的。之后的章节是建立在先前的章节中提到的概念之上的,先前章节可能不会深入一个主题的细节,因为在随后的章节将会再次重温这个主题。
+在大多数示例中这本书将使用[STM32F3DISCOVERY]开发板。这个板子是基于ARM Cortex-M架构的,且基本功能与大多数基于这个架构的CPUs功能相似。微处理器的外设和其它实现细节在不同的厂家之间是不同的,甚至来自同一个厂家,不同处理器系列之间也是不同的。
+因此我们建议购买[STM32F3DISCOVERY]开发板来尝试这本书中的例子。
[STM32F3DISCOVERY]: http://www.st.com/en/evaluation-tools/stm32f3discovery.html
@@ -66,7 +66,7 @@ Embedded Rust是为了那些即想要进行嵌入式编程,又想要使用Rust
[这个仓库]: /~https://github.com/rust-embedded/book
[resouces team]: /~https://github.com/rust-embedded/wg#the-resources-team
-如果你按着这本书的操作遇到了什么麻烦,或者这本书的一些部分不够清楚,或者很难进行下去,那这本书就是有个bug,它应该被报道给这本书的[the issue tracker] 。
+如果你按着这本书的操作遇到了什么麻烦,或者这本书的一些部分不够清楚,或者很难进行下去,那这本书就是有个bug,这个bug应该被报道给这本书的[the issue tracker] 。
[the issue tracker]: /~https://github.com/rust-embedded/book/issues/
diff --git a/src/intro/no-std.md b/src/intro/no-std.md
index a28ef35e..f612131a 100644
--- a/src/intro/no-std.md
+++ b/src/intro/no-std.md
@@ -1,11 +1,11 @@
# 一个 `no_std` Rust环境
-嵌入式编程这个词被广泛用于许多不同的编程场景中。小到RAM和ROM只有KB的8位机(像是[ST72325xx](https://www.st.com/resource/en/datasheet/st72325j6.pdf)),大到一个具有32/64位4核Cortex-A53和1GB的RAM的系统,比如树莓派([Model B 3+](https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications))。当编写代码时,取决于你的目标环境和用例,将会有不同的限制和局限。
+嵌入式编程这个词被广泛用于许多不同的编程场景中。小到RAM和ROM只有KB的8位机(像是[ST72325xx](https://www.st.com/resource/en/datasheet/st72325j6.pdf)),大到一个具有32/64位4核Cortex-A53和1GB RAM的系统,比如树莓派([Model B 3+](https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications))。当编写代码时,取决于你的目标环境和用例,将会有不同的限制和局限。
通常嵌入式编程有两类:
## 主机环境
-这类环境类似一个常见的PC环境。意味着向你提供了一个系统接口[E.G. POSIX](https://en.wikipedia.org/wiki/POSIX),使你能和不同的系统进行交互,比如文件系统,网络,内存管理,进程,等等。标准库相应地依赖这些接口去实现它们的功能。sysroot可能限制你使用RAM/ROM,可能还有一些特别的硬件或者I/O。总之感觉像是在专用的PC环境上编程一样。
+这类环境与一个常见的PC环境类似。意味着向你提供了一个系统接口[E.G. POSIX](https://en.wikipedia.org/wiki/POSIX),使你能和不同的系统进行交互,比如文件系统,网络,内存管理,进程,等等。标准库相应地依赖这些接口去实现它们的功能。sysroot可能限制你使用RAM/ROM,可能还有一些特别的硬件或者I/O。总之感觉像是在专用的PC环境上编程一样。
## 裸机环境
diff --git a/src/peripherals/singletons.md b/src/peripherals/singletons.md
index bd0e48f4..38eedcb3 100644
--- a/src/peripherals/singletons.md
+++ b/src/peripherals/singletons.md
@@ -58,7 +58,7 @@ fn main() {
## 已存在的库支持
-虽然我们在上面创造了我们自己的 `Peripherals` 结构体,但这并不是必须的。`cortex_m` crate 包含一个被叫做 `singleton!()` 的宏,其将为你执行这个任务。
+虽然我们在上面创造了我们自己的 `Peripherals` 结构体,但这并不是必须的。`cortex_m` crate 包含一个被叫做 `singleton!()` 的宏,其将为你完成这个任务。
```rust,ignore
#[macro_use(singleton)]
@@ -73,7 +73,7 @@ fn main() {
[cortex_m docs](https://docs.rs/cortex-m/latest/cortex_m/macro.singleton.html)
-另外,如果你使用 [`cortex-m-rtic`](/~https://github.com/rtic-rs/cortex-m-rtic),其为你抽象了获取和定义这些外设的整个过程,你获得了一个`Peripherals`结构体,其包含了一个所有你定义了的项的非 `Option` 的版本。
+另外,如果你使用 [`cortex-m-rtic`](/~https://github.com/rtic-rs/cortex-m-rtic),它将获取和定义这些外设的整个过程抽象了出来,你将获得一个`Peripherals`结构体,其包含了一个所有你定义了的项的非 `Option` 的版本。
```rust,ignore
// cortex-m-rtic v0.5.x
diff --git a/src/static-guarantees/state-machines.md b/src/static-guarantees/state-machines.md
index 4ddad371..24933c1a 100644
--- a/src/static-guarantees/state-machines.md
+++ b/src/static-guarantees/state-machines.md
@@ -94,4 +94,4 @@ impl GpioConfig {
正常使用这个结构体将会允许我们访问我们上面的状态机没有定义的状态: e.g. 一个被上拉的输出,或者一个被拉高的输入。对于一些硬件,这并没有关系。对另外一些硬件来说,这将会导致不可预期或者没有定义的行为!
-虽然这个接口很方便写入,但是它没有强制我们遵守硬件的实现所规定的设计协约。
+虽然这个接口很方便写入,但是它没有强制我们遵守为硬件的实现所规定的设计协约。
diff --git a/src/static-guarantees/zero-cost-abstractions.md b/src/static-guarantees/zero-cost-abstractions.md
index 44252bd1..aaf4126c 100644
--- a/src/static-guarantees/zero-cost-abstractions.md
+++ b/src/static-guarantees/zero-cost-abstractions.md
@@ -33,10 +33,10 @@ pub fn into_input_high_z(self) -> GpioConfig {
}
```
-我们返回的GpioConfig在运行时并不存在。调用这个函数通常将会被总结为一个单一的汇编指令 - 保存一个常量寄存器值进一个寄存器里。这意味着我们开发的类型状态接口是一个零成本抽象 - 它不会用更多的CPU,RAM,或者代码空间去跟踪`GpioConfig`的状态,会渲染成和直接访问寄存器一样的机器码。
+我们返回的GpioConfig在运行时并不存在。对这个函数的调用通常会被归纳为一条汇编指令 - 保存一个常量寄存器值进一个寄存器里。这意味着我们开发的类型状态接口是一个零成本抽象 - 它不会用更多的CPU,RAM,或者代码空间去跟踪`GpioConfig`的状态,会渲染成和直接访问寄存器一样的机器码。
## 嵌套
-通常,你可能会把这些抽象深深地嵌套起来。一旦所有的被使用的组件是零大小类型,整个结构体将不会在运行时存在。
+通常,你可能会把这些抽象深深地嵌套起来。一旦结构体使用的所有的组件是零大小类型的,整个结构体将不会在运行时存在。
-对于复杂或者深度嵌套的结构体,定义所有可能的状态组合可能很乏味。在这些案例中,宏可能能被用来产生所有的实现。
+对于复杂或者深度嵌套的结构体,定义所有可能的状态组合可能很乏味。在这些例子中,宏能被用来生成所有的实现。
diff --git a/src/unsorted/math.md b/src/unsorted/math.md
index 60101c78..63dccb75 100644
--- a/src/unsorted/math.md
+++ b/src/unsorted/math.md
@@ -1,6 +1,6 @@
# 在`#[no_std]`下执行数学运算
-如果你想要执行数学相关的功能,像是计算平方根或者一个数的指数且你有完整的标准库支持,你的代码可能看起来像这个:
+如果你想要执行数学相关的函数,像是计算平方根或者一个数的指数且你有完整的标准库支持,你的代码可能看起来像这个:
```rs
//! 可用一些标准支持的数学函数
@@ -24,7 +24,7 @@ fn main() {
}
```
-没有标准库支持的时候,这些函数不可用。反而像是[`libm`](https://crates.io/crates/libm)这样一个外部库可以被使用。示例的代码将会看起来像这个:
+没有标准库支持的时候,这些函数不可用。反而可以使用像是[`libm`](https://crates.io/crates/libm)这样一个外部库。示例的代码将会看起来像这样:
```rs
#![no_main]
@@ -62,7 +62,7 @@ fn main() -> ! {
}
```
-如果你需要在你的MCU上执行更复杂的操作,像是DSP信号处理或者更高级的线性代数,下列的crates可能可以帮助你
+如果你需要在你的MCU上执行更复杂的操作,像是DSP信号处理或者更高级的线性代数,下列的crates可能可以帮到你
- [CMSIS DSP library binding](/~https://github.com/jacobrosenthal/cmsis-dsp-sys)
- [`micromath`](/~https://github.com/tarcieri/micromath)
diff --git a/src/unsorted/speed-vs-size.md b/src/unsorted/speed-vs-size.md
index f06bcfdf..c56c4640 100644
--- a/src/unsorted/speed-vs-size.md
+++ b/src/unsorted/speed-vs-size.md
@@ -1,8 +1,8 @@
-# 优化: 速度与大小之间的均衡
+# 优化: 速度与大小之间的博弈
-每个人都想要它们的程序变得超级快且超级小,但是同时满足这两个条件是不可能的。这部分讨论`rustc`提供的不同的优化等级,和它们是如何影响执行时间和一个程序的二进制文件大小。
+每个人都想要它们的程序变得超级快且超级小,但是同时满足这两个条件是不可能的。这部分讨论`rustc`提供的不同的优化等级,和它们是如何影响执行时间和一个程序的二进制项的大小。
-## 不优化
+## 无优化
这是默认的。当你调用`cargo build`时,你使用的是development(又叫`dev`)配置。这个配置优化的目的是为了调试,因此它使能了调试信息且*关闭*了所有优化,i.e. 它使用 `-C opt-level = 0` 。
@@ -10,15 +10,15 @@
``` toml
[profile.release]
-# 符号很好且它们不会增加Flash上的大小
+# 调试符号很好且它们不会增加Flash上的大小
debug = true
```
-不优化对于调试来说是最好的,因为单步调试代码感觉像是你正在逐条语句地执行程序,且你能在GDB中`print`栈变量和函数参数。当代码被优化了,尝试打印变量会导致`$0 = `被打印出来。
+无优化对于调试来说是最好的选择,因为单步调试代码感觉像是你正在逐条语句地执行程序,且你能在GDB中`print`栈变量和函数参数。当代码被优化了,尝试打印变量会导致`$0 = `被打印出来。
-`dev`配置最大的缺点就是最终的二进制文件将会变得巨大且缓慢。大小通常是一个更大的问题,因为未优化的二进制文件会占据大量KiB的Flash,你的目标设备可能没这么多Flash -- 结果: 你未优化的二进制文件无法烧录进你的设备中!
+`dev`配置最大的缺点就是最终的二进制项将会变得巨大且缓慢。大小通常是一个更大的问题,因为未优化的二进制项会占据大量KiB的Flash,你的目标设备可能没这么多Flash -- 结果: 你未优化的二进制项无法烧录进你的设备中!
-我们可以有更小的,调试友好的二进制文件吗?是的,这里有一个技巧。
+我们可以有更小的,调试友好的二进制项吗?是的,这里有一个技巧。
### 优化依赖
@@ -85,15 +85,15 @@ opt-level = "z"
自2018-09-18开始 `rustc` 支持三个 "优化速度" 的等级: `opt-level = 1`, `2` 和 `3` 。当你运行 `cargo build --release` 时,你正在使用的是release配置,其默认是 `opt-level = 3` 。
-`opt-level = 2` 和 `3` 都以二进制文件大小为代价优化速度,但是等级`3`比等级`2`做了更多的向量化和内联。特别是,你将看到在`opt-level`等于或者大于`2`时LLVM将展开循环。循环展开在 Flash / ROM 方面的成本相当高(e.g. from 26 bytes to 194 for a zero this array loop)但是如果条件合适(迭代次数足够大),也可以将执行时间减半。
+`opt-level = 2` 和 `3` 都以二进制项大小为代价优化速度,但是等级`3`比等级`2`做了更多的向量化和内联。特别是,你将看到在`opt-level`等于或者大于`2`时LLVM将展开循环。循环展开在 Flash / ROM 方面的成本相当高(e.g. from 26 bytes to 194 for a zero this array loop)但是如果条件合适(迭代次数足够大),也可以将执行时间减半。
-现在还没有办法在`opt-level = 2`和`3`的情况下关闭循环展开,因此如果你不能接受它的开销,你应该优化你程序的尺寸。
+现在还没有办法在`opt-level = 2`和`3`的情况下关闭循环展开,因此如果你不能接受它的开销,你应该选择优化你的程序的大小。
-## 优化尺寸
+## 优化大小
-自2018-09-18开始`rustc`支持两个"优化尺寸"的等级: `opt-level = "s"` 和 `"z"` 。这些名字传承自 clang / LLVM 且不具有描述性,但是`"z"`意味着它产生的二进制文件比`"s"`更小。
+自2018-09-18开始`rustc`支持两个"优化大小"的等级: `opt-level = "s"` 和 `"z"` 。这些名字传承自 clang / LLVM 且不具有描述性,但是`"z"`意味着它产生的二进制文件比`"s"`更小。
-如果你想要发布一个优化了尺寸的二进制文件,那么改变下面展示的`Cargo.toml`中的`profile.release.opt-level`配置。
+如果你想要发布一个优化了大小的二进制项,那么改变下面展示的`Cargo.toml`中的`profile.release.opt-level`配置。
``` toml
[profile.release]
@@ -101,9 +101,9 @@ opt-level = "z"
opt-level = "s"
```
-这两个优化等级极大地减少了LLVM的内联阈值,一个用来决定是否内联或者不内联一个函数的度量。Rust其中一个概念是零成本抽象;这些抽象趋向于去使用许多新类型和小函数去保持不变量(e.g. 像是`deref`,`as_ref`这样借用内部值的函数)因此一个低内联阈值会使LLVM失去优化的机会(e.g. 去掉死分支(dead branches),内联对闭包的调用)。
+这两个优化等级极大地减小了LLVM的内联阈值,一个用来决定是否内联或者不内联一个函数的度量。Rust其中一个概念是零成本抽象;这些抽象趋向于去使用许多新类型和小函数去保持不变量(e.g. 像是`deref`,`as_ref`这样借用内部值的函数)因此一个低内联阈值会使LLVM失去优化的机会(e.g. 去掉死分支(dead branches),内联对闭包的调用)。
-当优化尺寸时,你可能想要尝试增加增加内联阈值去观察是否会对你的二进制文件的大小有影响。推荐的改变内联阈值的方法是在`.cargo/config.toml`中往其它rustflags后插入`-C inline-threshold` 。
+当优化大小时,你可能想要尝试增加内联阈值去观察是否会对你的二进制项的大小有影响。推荐的改变内联阈值的方法是在`.cargo/config.toml`中往其它rustflags后插入`-C inline-threshold` 。
``` toml
# .cargo/config.toml
@@ -124,4 +124,4 @@ rustflags = [
- `opt-level = "s"` 使用 75
- `opt-level = "z"` 使用 25
-当优化尺寸时,你应该尝试`225`和`275` 。
+当优化大小时,你应该尝试`225`和`275` 。
From 44643af0325773706361ef44d31d3d03bbdb6e36 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Sat, 19 Nov 2022 08:47:02 +0800
Subject: [PATCH 117/137] minor
---
src/intro/hardware.md | 6 +++---
src/intro/index.md | 20 ++++++++++----------
src/intro/install/linux.md | 2 +-
src/intro/no-std.md | 12 ++++++------
src/intro/tooling.md | 18 +++++++++---------
src/start/hardware.md | 18 +++++++++---------
src/start/qemu.md | 32 ++++++++++++++++----------------
7 files changed, 54 insertions(+), 54 deletions(-)
diff --git a/src/intro/hardware.md b/src/intro/hardware.md
index 8035ffb6..47ad66a0 100644
--- a/src/intro/hardware.md
+++ b/src/intro/hardware.md
@@ -1,6 +1,6 @@
# 熟悉你的硬件
-让我们来熟悉下我们将使用的硬件。
+先来熟悉下我们要用的硬件。
## STM32F3DISCOVERY (the "F3")
@@ -27,6 +27,6 @@
+ 一个二级微控制器: [STM32F103](https://www.st.com/en/microcontrollers/stm32f103cb.html)。这个微控制器实际上是一个板载编程器/调试器的一部分,与名为“USB ST-LINK”的USB端口相连。
-关于所列举的功能的更多细节和开发板的更多规格请看向[STMicroelectronics](https://www.st.com/en/evaluation-tools/stm32f3discovery.html)网站。
+关于所列举的功能的更多细节和开发板的更多规格请查阅[STMicroelectronics](https://www.st.com/en/evaluation-tools/stm32f3discovery.html)网站。
-提醒一句: 如果你想要为板子提供外部信号,请小心。微控制器STM32F303VCT6管脚的标称电压是3.3伏。更多信息请查看[6.2 Absolute maximum ratings section in the manual](https://www.st.com/resource/en/datasheet/stm32f303vc.pdf)。
+提醒一句: 如果想要为板子提供外部信号,请小心。微控制器STM32F303VCT6管脚的标称电压是3.3伏。更多信息请查看[6.2 Absolute maximum ratings section in the manual](https://www.st.com/resource/en/datasheet/stm32f303vc.pdf)。
diff --git a/src/intro/index.md b/src/intro/index.md
index 052a1338..f9513634 100644
--- a/src/intro/index.md
+++ b/src/intro/index.md
@@ -1,31 +1,31 @@
# 引言
-欢迎阅读The Embedded Rust Book:一本介绍如何在裸机(比如,微处理器)上使用Rust编程语言的书籍。
+欢迎阅读嵌入式Rust:一本关于如何在裸机(比如,微处理器)上使用Rust编程语言的入门书籍。
-## Embedded Rust 是为谁准备的
-Embedded Rust是为了那些即想要进行嵌入式编程,又想要使用Rust语言所提供的高级语言概念和安全保障的人们而准备的(也可以看 [Who Rust Is For](https://doc.rust-lang.org/book/ch00-00-introduction.html))
+## 嵌入式Rust是为谁准备的
+嵌入式Rust是为了那些即想要进行嵌入式编程,又想要使用Rust语言所提供的高级概念和安全保障的人们而准备的(参见[Who Rust Is For](https://doc.rust-lang.org/book/ch00-00-introduction.html))
-## 范围
+## 本书范围
这本书的目的是:
+ 让开发者快速上手Rust嵌入式开发,比如,如何设置一个开发环境。
+ 分享那些关于使用Rust进行嵌入式开发的,现存的,最好的实践经验,比如,如何最大程度上地利用好Rust语言的特性去写更正确的嵌入式软件
+ 某种程度下作为工具书,比如,如何在一个项目里将C和Rust混合在一起使用
-虽然尽可能地尝试让这本书可以用于大多数场景,但是为了使读者和作者更容易理解,在所有的示例中这本书都使用了ARM Cortex-M架构。然而,这本书并不需要读者熟悉这个架构,书中会在需要时对这个架构的特定细节进行解释。
+虽然尽可能地尝试让这本书可以用于大多数场景,但是为了使读者和作者更容易理解,在所有的示例中,这本书都使用了ARM Cortex-M架构。然而,这本书并不需要读者熟悉这个架构,书中会在需要时对这个架构的特定细节进行解释。
## 这本书是为谁准备的
-这本书适合那些有一些嵌入式背景或者有Rust背景的人,然而我相信每一个对Rust嵌入式编程好奇的人都能从这本书中得到某些收获。对于那些先前没有任何经验的人,我们建议你读一下“要求和预备知识”部分。从其它资料中获取、补充缺失的知识,这样能提高你的阅读体验。你可以看看“其它资源”部分,以找到你感兴趣的那些主题的资源。
+这本书适合那些有一些嵌入式背景或者有Rust背景的人,然而我相信每一个对Rust嵌入式编程好奇的人都能从这本书中获得某些收获。对于那些先前没有任何经验的人,我们建议你读一下“要求和预备知识”部分。从其它资料中获取、补充缺失的知识,这样能提高你的阅读体验。你可以看看“其它资源”部分,以找到你感兴趣的那些主题的资源。
### 要求和预备知识
-+ 你可以轻松地使用Rust编程语言,且在一个桌面环境上写过,运行过,调试过Rust应用。你也应该要熟悉[2018 edition]的术语,因为这本书是面向Rust 2018的。
++ 你可以轻松地使用Rust编程语言,且在一个桌面环境上写过,运行过,调试过Rust应用。你应该也要熟悉[2018 edition]的术语,因为这本书是面向Rust 2018的。
[2018 edition]: https://doc.rust-lang.org/edition-guide/
+ 你可以轻松地使用其它语言,比如C,C++或者Ada,开发和调试嵌入式系统,且熟悉如下的概念:
+ 交叉编译
+ 存储映射的外设(Memory Mapped Peripherals)
+ 中断
- + I2C,SPI,串口等等常见接口
+ + I2C,SPI,串口等等常见的接口
### 其它资源
@@ -46,10 +46,10 @@ Embedded Rust是为了那些即想要进行嵌入式编程,又想要使用Rust
这本书是已经被一些慷慨的志愿者们翻译了。如果你想要将你的翻译列在这里,请打开一个PR去添加它。
-* [Japanese](https://tomoyuki-nakabayashi.github.io/book/)
+* [日文](https://tomoyuki-nakabayashi.github.io/book/)
([repository](/~https://github.com/tomoyuki-nakabayashi/book))
-* [Chinese](https://xxchang.github.io/book/)
+* [中文](https://xxchang.github.io/book/)
([repository](/~https://github.com/xxchang/book))
## 如何使用这本书
diff --git a/src/intro/install/linux.md b/src/intro/install/linux.md
index 52ab8f0a..abf61319 100644
--- a/src/intro/install/linux.md
+++ b/src/intro/install/linux.md
@@ -112,7 +112,7 @@ user::rw-
user:you:rw-
```
-权限后的 `+` 指出存在一个扩展权限。`getfacl` 命令显示,`user`也就是`你`能使用这个设备。
+权限后的 `+` 指出存在一个扩展权限。`getfacl` 命令显示,`user`也就是`你`,可以使用这个设备。
现在,去往[下个章节].
diff --git a/src/intro/no-std.md b/src/intro/no-std.md
index f612131a..298c32b2 100644
--- a/src/intro/no-std.md
+++ b/src/intro/no-std.md
@@ -3,13 +3,13 @@
嵌入式编程这个词被广泛用于许多不同的编程场景中。小到RAM和ROM只有KB的8位机(像是[ST72325xx](https://www.st.com/resource/en/datasheet/st72325j6.pdf)),大到一个具有32/64位4核Cortex-A53和1GB RAM的系统,比如树莓派([Model B 3+](https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications))。当编写代码时,取决于你的目标环境和用例,将会有不同的限制和局限。
通常嵌入式编程有两类:
-## 主机环境
+## 主机环境下
-这类环境与一个常见的PC环境类似。意味着向你提供了一个系统接口[E.G. POSIX](https://en.wikipedia.org/wiki/POSIX),使你能和不同的系统进行交互,比如文件系统,网络,内存管理,进程,等等。标准库相应地依赖这些接口去实现它们的功能。sysroot可能限制你使用RAM/ROM,可能还有一些特别的硬件或者I/O。总之感觉像是在专用的PC环境上编程一样。
+这类环境与一个常见的PC环境类似。意味着向你提供了一个系统接口[比如 POSIX](https://en.wikipedia.org/wiki/POSIX),使你能和不同的系统进行交互,比如文件系统,网络,内存管理,进程,等等。标准库相应地依赖这些接口去实现了它们的功能。可能有某种sysroot并限制了对RAM/ROM的使用,可能还有一些特别的硬件或者I/O。总之感觉像是在专用的PC环境上编程一样。
-## 裸机环境
+## 裸机环境下
-在一个裸机环境中,你的程序被加载前,不存在代码。没有系统提供的软件,我们不能加载标准库。相反地,该程序,以及它使用的crates只能使用硬件(裸机)去运行。使用`no-std`可以防止rust读取标准库。标准库中与平台无关的部分在[libcore](https://doc.rust-lang.org/core/)中。libcore剔除了那些在一个嵌入式环境中非必要的东西。比如用于动态分配的内存分配器。如果你需要这些或者其它的某些功能,通常会有提供这些功能的crates。
+在一个裸机环境中,程序被加载前,环境中不存在代码。没有系统提供的软件,我们不能加载标准库。相反地,程序和它使用的crates只能使用硬件(裸机)去运行。使用`no-std`可以防止rust读取标准库。标准库中与平台无关的部分在[libcore](https://doc.rust-lang.org/core/)中。libcore剔除了那些在一个嵌入式环境中非必要的东西。比如用于动态分配的内存分配器。如果你需要这些或者其它的某些功能,通常会有提供这些功能的crates。
### libstd运行时
@@ -30,11 +30,11 @@
| libcore available | ✓ | ✓ |
| writing firmware, kernel, or bootloader code | ✓ | ✘ |
-\* 只有在你使用了 `alloc` crate 和设置了一个适合的分配器,比如[alloc-cortex-m]后有效。
+\* 只有在你使用了 `alloc` crate 并设置了一个适合的分配器后,比如[alloc-cortex-m]后有效。
\** 只有在你使用了 `collections` crate 并配置了一个全局默认的分配器后有效。
[alloc-cortex-m]: /~https://github.com/rust-embedded/alloc-cortex-m
-## See Also
+## 参见
* [RFC-1184](/~https://github.com/rust-lang/rfcs/blob/master/text/1184-stabilize-no_std.md)
diff --git a/src/intro/tooling.md b/src/intro/tooling.md
index f1e346af..a402e2f3 100644
--- a/src/intro/tooling.md
+++ b/src/intro/tooling.md
@@ -5,29 +5,29 @@
- [`qemu-system-arm`](https://www.qemu.org/). 测试的版本: 3.0.0
- OpenOCD >=0.8. 测试的版本: v0.9.0 and v0.10.0
- 有ARM支持的GDB。强烈建议7.12或者更新的版本。测试版本: 7.10, 7.11 和 8.1
-- [`cargo-generate`](/~https://github.com/ashleygwilliams/cargo-generate) 或者 `git`
-
-这些工具都是可选的,但是跟着书一起来使用它们,会更容易。下面的文档将解释我们为什么使用这些工具。安装指令可以在下一页找到。
+- [`cargo-generate`](/~https://github.com/ashleygwilliams/cargo-generate) 或者 `git`。这些工具都是可选的,但是跟着这本书来使用它们,会更容易。
+-
+- 下面的文档将解释我们为什么使用这些工具。安装指令可以在下一页找到。
## `cargo-generate` 或者 `git`
-裸机编程是非标准 Rust 编程,为了得到正确的程序的内存布局,需要对链接过程进行一些调整,这要求添加一些额外的文件(比如linker scripts)和配置(比如linker flags)。我们已经为你把这些打包进了一个模板里,你只需要补充缺失的信息(比如项目名和你的目标硬件的特性)。
+裸机编程是非标准Rust编程,为了得到正确的程序的内存布局,需要对链接过程进行一些调整,这要求添加一些额外的文件(比如linker scripts)和配置(比如linker flags)。我们已经为你把这些打包进了一个模板里了,你只需要补充缺失的信息(比如项目名和目标硬件的特性)。
我们的模板兼容`cargo-generate`:一个用来从模板生成新的Cargo项目的Cargo子命令。你也能使用`git`,`curl`,`wget`,或者你的网页浏览器下载模板。
## `cargo-binutils`
`cargo-binutils`是一个Cargo命令的子集,它让我们能轻松使用Rust工具链带来的LLVM工具。这些工具包括LLVM版本的`objdump`,`nm`和`size`,用来查看二进制文件。
-在GNU binutils之上使用这些工具的好处是,(a)无论你的操作系统是什么,安装这些LLVM工具是相同的一条命令(`rustup component add llvm-tools-preview`)。(b)像是`objdump`这样的工具,支持所有`rustc`支持的架构--从ARM到x86_64--因为它们都有一样的LLVM后端。
+在GNU binutils之上使用这些工具的好处是,(a)无论你的操作系统是什么,安装这些LLVM工都可以用同一条命令(`rustup component add llvm-tools-preview`)。(b)像是`objdump`这样的工具,支持所有`rustc`支持的架构--从ARM到x86_64--因为它们都有一样的LLVM后端。
## `qemu-system-arm`
QEMU是一个仿真器。在这个例子里,我们使用能完全仿真ARM系统的改良版QEMU。我们使用QEMU在主机上运行嵌入式程序。多亏了它,你可以在没有任何硬件的情况下,尝试这本书的部分示例。
## GDB
-调试器是嵌入式开发的一个非常重要的组件,你不可能总是可以将内容记录到主机控制台上。在某些情况里,你甚至可能都没法用LEDs去点亮你的硬件!
-通常,提到调试,LLDB和GDB的功能一样,但是我们还没找到与GDB的`load`命令对应的LLDB命令,这条命令负责将程序上传到目标硬件,因此我们现在推荐你使用GDB。
+调试器是嵌入式开发的一个非常重要的组件,你不可能总是可以将内容打印到主机控制台上。在某些情况里,你甚至可能都没法用LEDs去点亮你的硬件!
+通常,提到调试,LLDB和GDB的功能一样,但是我们还没找到与GDB的`load`命令对应的LLDB命令,这条命令负责将程序上传到目标硬件,因此目前我们推荐使用GDB。
## OpenOCD
-GDB不能直接和你的STM32F3DISCOVERY开发板上的ST-Link调试硬件通信。它需要一个转译者,the Open On-Chip Debugger,OpenOCD就是这样的转译者。OpenOCD是一个运行在你的笔记本/个人电脑上的程序,它在基于远程调试的TCP/IP协议和ST-Link的USB协议间进行转译。
-OpenOCD也执行其它一些转译中的重要的工作,这些工作用于调试你的STM32FDISCOVERY开发板上的ARM Cortex-M微控制器:
+GDB不能直接和你的STM32F3DISCOVERY开发板上的ST-Link调试硬件通信。它需要一个转译者,the Open On-Chip Debugger,OpenOCD就是这样的转译者。OpenOCD是一个运行在笔记本/个人电脑上的程序,它在基于远程调试的TCP/IP协议和ST-Link的USB协议间进行转译。
+OpenOCD也负责转译中一些其它的重要的工作,这些工作用于调试你的STM32FDISCOVERY开发板上的ARM Cortex-M微控制器:
* 它知道如何与ARM CoreSight调试外设使用的内存映射的寄存器进行交互。这些CoreSight寄存器的功能有:
* 断点操作
* 读取和写入CPU寄存器
diff --git a/src/start/hardware.md b/src/start/hardware.md
index 4751d9f9..5894f4a3 100644
--- a/src/start/hardware.md
+++ b/src/start/hardware.md
@@ -1,6 +1,6 @@
# 硬件
-现在你应该有点熟悉工具和开发过程了。在这部分我们将切换到真正的硬件上;步骤非常相似。让我们深入进去。
+现在你应该有点熟悉工具和开发过程了。在这部分我们将切换到真正的硬件上;步骤非常相似。让我们深入下去。
## 认识你的硬件
@@ -12,14 +12,14 @@
你可以在你的设备的数据手册和参考手册上找到这些信息。
-这部分,我们会用我们的参考硬件,STM32F3DISCOVERY。这个板子包含一个STM32F303VCT6微控制器。这个微控制器拥有:
+这部分,我们将使用我们的参考硬件,STM32F3DISCOVERY。这个板子包含一个STM32F303VCT6微控制器。这个微控制器拥有:
- 一个Cortex-M4F核心,它包含一个单精度FPU。
- 位于 0x0800_0000 地址的256KiB的Flash。
- 位于 0x2000_0000 地址的40KiB的RAM。(这里还有其它的RAM区域,但是为了方便起见,我们将忽略它)。
## 配置
-我们将从一个新的模板实例开始。参考[先前的QEMU]章节,了解如何在没有`cargo-generate`的情况下完成它。
+我们将使用一个新的模板实例从零开始。对于新手参考[先前的QEMU]章节,了解如何在没有`cargo-generate`的情况下完成它。
[先前的QEMU]: qemu.md
@@ -60,11 +60,11 @@ MEMORY
RAM : ORIGIN = 0x20000000, LENGTH = 40K
}
```
-> **注意**:如果你因为一些理由,在对某个编译目标首次编译后,改变了`memory.x`文件,需要在`cargo build`之前执行`cargo clean`。因为`cargo build`可能不会追踪`memory.x`的更新。
+> **注意**:如果你因为某些理由,在对某个编译目标首次编译后,改变了`memory.x`文件,需要在`cargo build`之前执行`cargo clean`。因为`cargo build`可能不会跟踪`memory.x`的更新。
-我们将使用hello示例再次开始,但是首先我们必须做一个小改变。
+我们将再次使用hello示例作为开始,但是首先我们必须做一个小改变。
-在`examples/hello.rs`中,确保`debug::exit()`调用被注释掉了或者移除。它只能用于在QEMU中运行的情况。
+在`examples/hello.rs`中,确保`debug::exit()`调用被注释掉了或者移除掉了。它只能用于在QEMU中运行的情况。
```rust,ignore
#[entry]
@@ -79,7 +79,7 @@ fn main() -> ! {
}
```
-你可以像你之前做的一样,使用`cargo build`检查编译程序,使用`cargo-binutils`观察二进制项。`cortex-m-rt`库可以处理所有运行芯片所需的魔法,几乎所有的Cortex-M CPUs都按同样的方式启动,这同样有用。
+你可以像你之前做的一样,使用`cargo build`检查编译程序,使用`cargo-binutils`观察二进制项。`cortex-m-rt`库可以处理所有让芯片运行起来所需的魔法,几乎所有的Cortex-M CPUs都按同样的方式启动。
``` console
cargo build --example hello
@@ -87,7 +87,7 @@ cargo build --example hello
## 调试
-调试将看起来有点不同。事实上,取决于不同的目标设备,第一步可能看起来不一样。在这个章节里,我们将展示,调试一个在STM32F3DISCOVERY上运行的程序,所需要的步骤。这作为一个参考。对于设备,关于调试有关的信息,可以看[the Debugonomicon](/~https://github.com/rust-embedded/debugonomicon)。
+调试会看起来有点不一样。事实上,取决于不同的目标设备,第一步可能看起来不一样。在这个章节里,我们将展示,调试一个在STM32F3DISCOVERY上运行的程序,所需要的步骤。这作为一个参考。关于调试有关的设备特定的信息,可以看[the Debugonomicon](/~https://github.com/rust-embedded/debugonomicon)。
像之前一样,我们将进行远程调试,客户端将是一个GDB进程。不同的是,OpenOCD将是服务器。
@@ -116,7 +116,7 @@ source [find interface/stlink.cfg]
source [find target/stm32f3x.cfg]
```
-> **注意** 如果你在[安装验证]章节中,发现你的discovery开发板是一个更旧的版本,那么你应该修改你的 `openocd.cfg` 文件,让它去使用 `interface/stlink-v2.cfg` 。注释掉 `interface/stlink.cfg`
+> **注意** 如果你在[安装验证]章节中,发现你的discovery开发板是一个更旧的版本,那么你应该修改你的 `openocd.cfg` 文件,注释掉 `interface/stlink.cfg`,让它去使用 `interface/stlink-v2.cfg` 。
``` text
$ openocd
diff --git a/src/start/qemu.md b/src/start/qemu.md
index b2da8c65..1c3b08c5 100644
--- a/src/start/qemu.md
+++ b/src/start/qemu.md
@@ -1,10 +1,10 @@
# QEMU
-我们将开始为[LM3S6965]编写程序,一个Cortex-M3微控制器。我们选择这个作为我们的第一个目标,因为它能使用[QEMU仿真](https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm),因此本节中,你不需要摆弄硬件,我们注意力可以集中在工具和开发过程上。
+我们将开始为[LM3S6965]编写程序,一个Cortex-M3微控制器。因为它能使用[QEMU仿真](https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm),所以我们选择它作为我们的第一个目标,本节中,不需要使用硬件,我们注意力可以集中在工具和开发过程上。
[LM3S6965]: http://www.ti.com/product/LM3S6965
**重要**
-在这个引导里,我们将使用"app"这个名字来代指项目名。无论何时你看到单词"app",你应该用你选择的项目名来替代"app"。或者你也可以选择把你的项目命名为"app",避免替代。
+在这个引导里,我们将使用"app"这个名字来代指项目名。无论何时你看到单词"app",你应该用你选择的项目名来替代"app"。或者你也可以选择把你的项目命名为"app",避免要替换掉。
## 生成一个非标准的 Rust program
我们将使用[`cortex-m-quickstart`]项目模板来生成一个新项目。生成的项目将包含一个最基本的应用:对于一个新的嵌入式rust应用来说,是一个很好的开始。另外,项目将包含一个`example`文件夹,文件夹中有许多独立的应用,突出了一些关键的嵌入式rust的功能。
@@ -74,7 +74,7 @@ cd app
## 项目概览
-为了便利,这是`src/main.rs`中源码最重要的部分。
+这是`src/main.rs`中源码最重要的部分。
```rust,ignore
#![no_std]
@@ -92,7 +92,7 @@ fn main() -> ! {
}
```
-这个程序与标准Rust程序有一点不同,因此让我们走近点看看。
+这个程序与标准Rust程序有一点不同,让我们走近点看看。
`#![no_std]`指出这个程序将 *不会* 链接标准crate`std`。反而它将会链接到它的子集: `core` crate。
@@ -109,7 +109,7 @@ fn main() -> ! {
## 交叉编译
-下一步是为Cortex-M3架构*交叉*编译程序。如果你知道编译目标(`$TRIPLE`)应该是什么,那就和运行`cargo build --target $TRIPLE`一样简单。幸运地,模板中的`.cargo/config.toml`有这个答案:
+下一步是为Cortex-M3架构*交叉*编译程序。如果你知道编译目标(`$TRIPLE`)应该是什么,运行`cargo build --target $TRIPLE`就可以了。幸运地,模板中的`.cargo/config.toml`有这个答案:
```console
tail -n6 .cargo/config.toml
```
@@ -121,7 +121,7 @@ target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
```
-为了交叉编译Cortex-M3架构我们不得不使用`thumbv7m-none-eabi`。当安装Rust工具时,target不会自动被安装,如果你还没有做,现在可以去添加那个target到工具链上。
+为了交叉编译Cortex-M3架构我们不得不使用`thumbv7m-none-eabi`。当安装Rust工具时,target不会自动被安装,如果还没有添加,现在可以去添加那个target到工具链上。
``` console
rustup target add thumbv7m-none-eabi
```
@@ -133,7 +133,7 @@ cargo build
## 检查
-现在我们在`target/thumbv7m-none-eabi/debug/app`中有一个非主机环境的ELF二进制文件。我们能使用`cargo-binutils`检查它。
+现在在`target/thumbv7m-none-eabi/debug/app`中有一个非主机环境的ELF二进制文件。我们能使用`cargo-binutils`检查它。
使用`cargo-readobj`我们能打印ELF头,确认这是一个ARM二进制。
@@ -142,8 +142,8 @@ cargo readobj --bin app -- --file-headers
```
注意:
-* `--bin app` 是一个用来检查`target/$TRIPLE/debug/app`的语法糖
-* `--bin app` 如果有需要,也会重新编译二进制项。
+* `--bin app` 是一个用来查看二进制项`target/$TRIPLE/debug/app`的语法糖
+* `--bin app` 需要时也会重新编译二进制项。
``` text
ELF Header:
@@ -174,7 +174,7 @@ ELF Header:
cargo size --bin app --release -- -A
```
-我们使用`--release`检查优化的版本
+我们使用`--release`查看优化后的版本
``` text
app :
@@ -207,7 +207,7 @@ Total 14570
> - `.vector_table` 是一个我们用来存储向量(中断)表的*非*标准的section
> - `.ARM.attributes` 和 `.debug_*` sections包含元数据,当烧录二进制文件时,它们不会被加载到目标上的。
-**重要**: ELF文件包含像是调试信息这样的元数据,因此它们在*硬盘上的尺寸*没有正确地反应了程序被烧录到设备上时将占据的空间的大小。要*一直*使用`cargo-size`检查一个二进制项的大小。
+**重要**: ELF文件包含像是调试信息这样的元数据,因此它们在*硬盘上的尺寸*没有正确地反应处程序被烧录到设备上时将占据的空间的大小。要*一直*使用`cargo-size`检查一个二进制项的大小。
`cargo-objdump` 能用来反编译二进制项。
@@ -287,9 +287,9 @@ fn main() -> ! {
}
```
-这个程序使用一些被叫做semihosting的东西去打印文本到主机调试台上。当使用的是真实的硬件时,这需要一个调试对话,但是当使用的是QEMU时这就可以工作了。
+这个程序使用被叫做semihosting的东西去打印文本到主机调试台上。当使用的是真实的硬件时,需要一个调试对话这个程序才能工作,但是当使用的是QEMU时这就可以工作了。
-通过编译示例,让我们开始
+让我们开始编译示例
```console
cargo build --example hello
@@ -353,13 +353,13 @@ Hello, world!
```
## 调试
-对于嵌入式开发来说,调试非常重要。让我们来看下如何完成它。
+对于嵌入式开发来说,调试非常重要。让我们来看下如何调试它。
因为我们想要调试的程序所运行的机器上并没有运行一个调试器程序(GDB或者LLDB),所以调试一个嵌入式设备就涉及到了 *远程* 调试
远程调试涉及一个客户端和一个服务器。在QEMU的情况中,客户端将是一个GDB(或者LLDM)进程且服务器将会是运行着嵌入式程序的QEMU进程。
-在这部分,我们将使用我们已经编译的 `hello` 示例。
+在这部分,我们要使用我们已经编译的 `hello` 示例。
调试的第一步是在调试模式中启动QEMU:
@@ -403,7 +403,7 @@ Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473
>`core::num::bignum::Big32x40::mul_small () at src/libcore/num/bignum.rs:254`
> ` src/libcore/num/bignum.rs: No such file or directory.`
>
-> 那是一个已知的小bug,你可以安全地忽略这些警告,你非常大可能已经Reset()了。
+> 那是一个已知的小bug,你可以安全地忽略这些警告,你非常大可能已经进入Reset()了。
这个reset句柄最终将调用我们的主函数,让我们使用一个断点和`continue`命令跳过所有的步骤。为了设置断点,让我们首先看下我们想要在我们代码哪里打断点,使用`list`指令
From 1ea1d3ac8c976b838764dbdbbdb31efc6afa48de Mon Sep 17 00:00:00 2001
From: XxChang
Date: Sat, 19 Nov 2022 10:53:35 +0800
Subject: [PATCH 118/137] minor
---
src/start/exceptions.md | 14 +++++++-------
src/start/hardware.md | 2 +-
src/start/interrupts.md | 8 ++++----
src/start/panicking.md | 8 ++++----
src/start/registers.md | 28 ++++++++++++++--------------
src/start/semihosting.md | 4 ++--
6 files changed, 32 insertions(+), 32 deletions(-)
diff --git a/src/start/exceptions.md b/src/start/exceptions.md
index 29751b2d..653640f9 100644
--- a/src/start/exceptions.md
+++ b/src/start/exceptions.md
@@ -16,7 +16,7 @@ fn SysTick() {
除了 `exception` 属性,异常处理函数看起来和普通函数一样,但是有一个很大的不同: `exception` 处理函数 *不能* 被软件调用。在先前的例子中,语句 `SysTick();` 将会导致一个编译错误。
-这么做是故意的,因为异常处理函数必须具有一个特性: 在异常处理函数中被声明为`static mut`的变量能被安全(safe)地使用。
+这么做是有目的的,因为异常处理函数必须具有一个特性: 在异常处理函数中被声明为`static mut`的变量能被安全(safe)地使用。
``` rust,ignore
#[exception]
@@ -32,11 +32,11 @@ fn SysTick() {
安全的Rust不能导致未定义的行为出现,所以非可重入函数必须被标记为 `unsafe`。然而,我刚说了`exception`处理函数能安全地使用`static mut`变量。这怎么可能?因为`exception`处理函数 *不* 能被软件调用因此重入(reentrancy)不会发生,所以这才变得可能。
-> 注意,`exception`属性,通过将静态变量封装进`unsafe`块中且为我们提供了名字相同的,类型为 `&mut` 的,新的合适的变量,转换了函数中静态变量的定义。因此我们可以通过 `*` 解引用访问变量的值而不需要将它们打包进一个 `unsafe` 块中。
+> 注意,`exception`属性,通过将静态变量封装进`unsafe`块中并为我们提供了名字相同的,类型为 `&mut` 的,新的合适的变量,转换了函数中静态变量的定义。因此我们可以通过 `*` 解引用访问变量的值而不需要将它们打包进一个 `unsafe` 块中。
-## 一个复杂的例子
+## 一个完整的例子
-这里有个例子,使用系统计时器大概每秒会抛出一个 `SysTick` 异常。异常处理函数使用 `COUNT` 变量追踪它自己被调用了多少次,然后使用半主机模式(semihosting)打印 `COUNT` 的值到主机控制台上。
+这里有个例子,使用系统计时器大概每秒抛出一个 `SysTick` 异常。异常处理函数使用 `COUNT` 变量追踪它自己被调用了多少次,然后使用半主机模式(semihosting)打印 `COUNT` 的值到主机控制台上。
> **注意**: 你能在任何Cortex-M设备上运行这个例子;你也能在QEMU运行它。
@@ -89,7 +89,7 @@ fn SysTick() {
}
// 重要信息 如果运行在真正的硬件上,去掉这个 `if` 块,
- // 否则你的调试器将会以一种不一样的状态结束
+ // 否则你的调试器将会以一种不一致的状态结束
if *COUNT == 9 {
// 这将终结QEMU进程
debug::exit(debug::EXIT_SUCCESS);
@@ -142,13 +142,13 @@ fn DefaultHandler(irqn: i16) {
## 硬错误(Hard Fault)处理函数
-`HardFault`异常有点特别。当程序进入一个无法工作的状态时,这个异常被触发,因此它的处理函数 *不能* 返回,因为这么做可能导致一个未定义的行为。在用户定义的 `HardFault` 处理函数被调用之前,运行时crate还做了一些工作去提供可调试性。
+`HardFault`异常有点特别。当程序进入一个无法工作的状态时,这个异常被触发,因此它的处理函数 *不能* 返回,因为这么做可能导致一个未定义的行为。在用户定义的 `HardFault` 处理函数被调用之前,运行时crate还做了一些工作以改进调试功能。
结果是,`HardFault`处理函数必须有下列的签名: `fn(&ExceptionFrame) -> !` 。处理函数的参数是一个指针,它指向被异常推入栈中的寄存器。这些寄存器是异常被触发那刻,处理器状态的一个记录,能被用来分析一个硬错误。
这里有个执行不合法操作的案例: 读取一个不存在的存储位置。
-> **注意**: 这个程序在QEMU上将不会工作,i.e. 它将不会崩溃,因为 `qemu-system-arm -machine lm3s6965evb` 不对读取存储的操作进行检查,且读取无效存储时将会开心地返回 `0`。
+> **注意**: 这个程序在QEMU上不能起作用,i.e. 它不会崩溃,因为 `qemu-system-arm -machine lm3s6965evb` 不对读取存储的操作进行检查,且读取无效存储时将会开心地返回 `0`。
```rust,ignore
#![no_main]
diff --git a/src/start/hardware.md b/src/start/hardware.md
index 5894f4a3..3e978040 100644
--- a/src/start/hardware.md
+++ b/src/start/hardware.md
@@ -12,7 +12,7 @@
你可以在你的设备的数据手册和参考手册上找到这些信息。
-这部分,我们将使用我们的参考硬件,STM32F3DISCOVERY。这个板子包含一个STM32F303VCT6微控制器。这个微控制器拥有:
+这部分,要使用我们的参考硬件,STM32F3DISCOVERY。这个板子包含一个STM32F303VCT6微控制器。这个微控制器拥有:
- 一个Cortex-M4F核心,它包含一个单精度FPU。
- 位于 0x0800_0000 地址的256KiB的Flash。
- 位于 0x2000_0000 地址的40KiB的RAM。(这里还有其它的RAM区域,但是为了方便起见,我们将忽略它)。
diff --git a/src/start/interrupts.md b/src/start/interrupts.md
index dc22b8b7..b10b66e8 100644
--- a/src/start/interrupts.md
+++ b/src/start/interrupts.md
@@ -1,13 +1,13 @@
# 中断
-虽然中断和异常在很多方面都不一样,但是它们的操作和使用几乎一样,且它们也能被同一个中断控制器处理。然而异常是由Cortex-M微架构定义的,中断在命名和功能上总是由特定厂商(经常甚至是芯片)实现的。
+虽然中断和异常在很多方面都不一样,但是它们的操作和使用几乎是一样的,且它们也能被同一个中断控制器处理。然而异常是由Cortex-M微架构定义的,中断在命名和功能上总是由特定厂商(经常甚至是芯片)实现的。
中断提供了更多的灵活性,当尝试用一种高级的方法使用它们时,我们需要对这种灵活性进行解释。但我们将不会在这本书里涵盖这些内容,最好把下面的东西记在心里:
* 中断有可以编程的优先级,其决定了它们的处理函数的执行顺序。
* 中断能嵌套且抢占,i.e. 一个中断处理函数的执行可以被其它更高优先级的中断打断。
* 通常需要清除掉导致中断被触发的原因,避免无限地再次进入中断处理函数。
-运行时的常规初始化步骤始终相同:
+运行时的初始化步骤总是相同的:
* 设置外设在遇到想要的事故发生的时候产生中断请求
* 在中断控制器中设置需要的中断处理函数的优先级
* 在中断控制器中使能中断处理函数
@@ -25,9 +25,9 @@ fn TIM2() {
}
```
-中断处理函数和异常处理函数一样看起来像是普通的函数(除了没有入参)。然而由于特殊的调用规定,它不能被固件的其它部分直接调用。但是,可以在软件中生成中断请求,触发一个控制权的转移。
+中断处理函数和异常处理函数一样看起来像是普通的函数(除了没有入参)。然而由于特殊的调用规定,它不能被固件的其它部分直接调用。然而,可以在软件中生成中断请求,转移到中断处理函数中。
-与异常处理函数一样,它也能在中断处理函数中声明`static mut`变量且保持 *safe* 状态。
+与异常处理函数一样,也能在中断处理函数中声明`static mut`变量且保持 *safe* 状态。
``` rust,ignore
#[interrupt]
diff --git a/src/start/panicking.md b/src/start/panicking.md
index e6b60e50..d2decf60 100644
--- a/src/start/panicking.md
+++ b/src/start/panicking.md
@@ -2,7 +2,7 @@
运行时恐慌是Rust语言的一个核心部分。像是索引这样的內建的操作为了存储安全性是运行时检查的。当尝试越界索引时,这会导致运行时恐慌(panic)。
-在标准库中,运行时恐慌的行为被定义了:它展开(unwinds)恐慌的线程的栈,除非用户选择在恐慌时终止程序。
+在标准库中,运行时恐慌的行为被定义成:展开(unwinds)恐慌的线程的栈,除非用户选择在恐慌时终止程序。
然而在没有标准库的程序中,运行时恐慌的行为是未被定义了的。通过声明一个 `#[painc_handler]` 函数可以选择一个运行时恐慌的行为。
@@ -22,11 +22,11 @@
[`panic-itm`]: https://crates.io/crates/panic-itm
[`panic-semihosting`]: https://crates.io/crates/panic-semihosting
-在 crates.io 上搜索 [`panic-handler`],你甚至能找到更多的crates。
+在crates.io上搜索 [`panic-handler`],你甚至可以找到更多的crates。
[`panic-handler`]: https://crates.io/keywords/panic-handler
-仅仅通过链接到相关的crate中,一个程序就可以从这些行为中选择一个运行时恐慌行为。将运行时恐慌的行为作为一行代码放进一个应用的源码中,不仅仅是可以作为文档使用,而且能根据编译配置改变运行时恐慌的行为。比如:
+仅仅通过链接到相关的crate中,一个程序就可以简单地从这些行为中选择一个运行时恐慌行为。将运行时恐慌的行为作为一行代码放进一个应用的源码中,不仅仅是因为可以作为文档使用,而且能根据编译配置改变运行时恐慌的行为。比如:
``` rust,ignore
#![no_main]
@@ -45,7 +45,7 @@ use panic_abort as _;
在这个例子里,当使用dev配置编译的时候(`cargo build`),crate链接到 `panic-halt` crate上,但是当使用release配置编译时(`cargo build --release`),crate链接到`panic-abort` crate上。
-> `use panic_abort as _` 形式的 `use` 语句被用来确保 `panic_abort` 运行时恐慌函数被包含进我们最终的可执行程序里,同时让编译器清楚地知道我们不会从这个crate显式地使用任何东西。没有 `_` 重命名,编译器将会警告我们有一个未使用的导入。有时候你可能会看到 `extern crate panic_abort`,这是Rust 2018之前的版本使用的更旧的写法,现在应该只被用于 "sysroot" crates (与Rust一起发布的crates),比如 `proc_macro`,`alloc`,`std` 和 `test` 。
+> `use panic_abort as _` 形式的 `use` 语句,被用来确保 `panic_abort` 运行时恐慌函数被包含进我们最终的可执行程序里,同时让编译器清楚地知道我们不会从这个crate显式地使用任何东西。没有 `_` 重命名,编译器将会警告我们有一个未使用的导入。有时候你可能会看到 `extern crate panic_abort`,这是Rust 2018之前的版本使用的更旧的写法,现在应该只被用于 "sysroot" crates (与Rust一起发布的crates),比如 `proc_macro`,`alloc`,`std` 和 `test` 。
## 一个例子
diff --git a/src/start/registers.md b/src/start/registers.md
index 0ec67807..e46f5f8d 100644
--- a/src/start/registers.md
+++ b/src/start/registers.md
@@ -1,17 +1,17 @@
# 存储映射的寄存器
-只有通过执行常规的Rust代码并在RAM间移动数据,嵌入式系统才能继续运行下去。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。
+嵌入式系统想要继续执行下去,只有通过执行常规的Rust代码并在RAM间移动数据才行。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。
-你可能会发现,访问你的微控制器外设所需要的代码,已经被写进了下面的某个抽象层中了。
+你可能会发现,访问你的微控制器外设所需要的代码,已经存在于下面的某个抽象层中了。
-* Micro-architecture Crate(微架构Crate) - 这个库拥有任何对于微控制器的处理器内核来说经常会用到的程序,也包括对于在这些微控制器中的通用外设来说有用的程序。比如 [cortex-m] crate提供给你可以使能和关闭中断的函数,其对于所有的Cortex-M微控制器都是一样的。它也提供你访问'SysTick'外设的能力,在所有的Cortex-M微控制器中都包括了这个外设功能。
-* Peripheral Access Crate(PAC)(外设访问Crate) - 这种crate是在被不同存储器封装的寄存器上再进行的一次封装,寄存器由你正在使用的微控制器的产品号定义的。比如,[tm4c123x]针对TI的Tiva-C TM4C123系列,[stm32f30x]针对ST的STM32F30x系列。这块,你将根据你的微控制器的技术手册写的每个外设操作指令,直接和寄存器交互。
-* HAL Crate - 这些crates为你的处理器提供了一个更友好的API,通常是通过实现在[embedded-hal]中定义的一些常用的traits来实现的。比如,这个crate可能提供一个`Serial`结构体,它的构造函数需要一组合适的GPIO端口和一个波特率,它为了发送数据提供一些 `write_byte` 函数。查看 [可移植性] 可以看到更多关于 [embedded-hal] 的信息。
-* Board Crate(开发板crate) - 这些Crate通过预配置不同的外设和GPIO管脚再进行了一层抽象以适配你正在使用的特定的开发者工具或者开发板,比如对于STM32F3DISCOVERY开发板来说,是[stm32f3-discovery]
+* Micro-architecture Crate(微架构库) - 这个库拥有任何对于微控制器的处理器内核来说经常会用到的程序,也包括在这些微控制器中的通用外设。比如 [cortex-m] crate提供给你可以使能和关闭中断的函数,其对于所有的Cortex-M微控制器都是一样的。它也提供你访问'SysTick'外设的能力,在所有的Cortex-M微控制器中都包括了这个外设功能。
+* Peripheral Access Crate(PAC)(外设访问库) - 这个库是对各种存储器封装的寄存器再进行的一次浅陋封装,特定于所使用的微控制器的产品号。比如,[tm4c123x]针对TI的Tiva-C TM4C123系列,[stm32f30x]针对ST的STM32F30x系列。这块,根据微控制器的技术手册写的每个外设操作指令,直接和寄存器交互。
+* HAL Crate - 这些crates为你的处理器提供了一个更友好的API,通常是通过实现在[embedded-hal]中定义的一些常用的traits来实现的。比如,这个crate可能提供一个`Serial`结构体,它的构造函数需要一组合适的GPIO端口和一个波特率,它为发送数据提供了 `write_byte` 函数。查看 [可移植性] 可以看到更多关于 [embedded-hal] 的信息。
+* Board Crate(开发板库) - 这些Crate通过预配置不同的外设和GPIO管脚再进行了一层抽象以适配你正在使用的特定的开发者工具或者开发板,比如对于STM32F3DISCOVERY开发板来说,是[stm32f3-discovery]
[cortex-m]: https://crates.io/crates/cortex-m
[tm4c123x]: https://crates.io/crates/tm4c123x
@@ -23,7 +23,7 @@
## 开发板Crate (Board Crate)
-如果你是嵌入式Rust新手,board crate是一个完美的开始。它们很好地抽象出了,在开始学习这个项目时,需要耗费心力了解的硬件细节,使得标准工作变得简单,像是打开或者关闭LED。不同的板子间,它们提供的功能变化很大。因为这本书是不假设我们使用的是何种板子,所以这本书不会提到board crate。
+如果你是嵌入式Rust新手,board crate是一个完美的开始。它们很好地抽象出了,在开始学习这个项目时,需要耗费心力了解的硬件细节,使得标准工作,像是打开或者关闭LED,变得简单。不同的板子间,它们提供的功能变化很大。因为这本书是不假设我们使用的是何种板子,所以这本书不会提到board crate。
如果你想要用STM32F3DISCOVERY开发板做实验,强烈建议看一下[stm32f3-discovery]开发板crate,它提供了点亮LEDs,访问它的指南针,蓝牙和其它的功能。[Discovery]书对于一个board crate的用法提供一个很好的介绍。
@@ -90,9 +90,9 @@ pub fn init() -> (Delay, Leds) {
```
-我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` 之外。因为这个crate是使用[svd2rust]自动生成的,访问我们寄存器位段的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是有了更多的代码了,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数其所有可能的参数都能发挥作用(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊含义),则该函数被标记为 `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。
+我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` 之外。因为这个crate是使用[svd2rust]自动生成的,访问我们寄存器位段的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是有了更多的代码,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数其所有可能的参数都能发挥作用(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊作用),那么该函数需要被标记为 `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。
-### 读取
+### Reading
`read()` 函数返回一个对象,这个对象提供了对这个寄存器中不同子域的只读访问,由厂商提供的这个芯片的SVD文件定义。在 [tm4c123x documentation][tm4c123x documentation R] 中你能找到在这个特别的返回类型 `R` 上所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。
@@ -102,16 +102,16 @@ if pwm.ctl.read().globalsync0().is_set() {
}
```
-### 写入
+### Writing
-`write()`函数使用一个只有一个参数的闭包。通常我们把这个参数叫做 `w`。然后这个参数提供对这个寄存器中不同的子域的读写访问,由厂商关于这个芯片的SVD文件提供。再一次,在 [tm4c123x documentation][tm4c123x documentation W] 中你能找到 `W` 所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。注意所有我们没有设置的子域将会被设置一个默认值 - 任何在这个寄存器中的现存的内容将会丢失。
+`write()`函数使用一个只有一个参数的闭包。通常我们把这个参数叫做 `w`。然后这个参数提供对这个寄存器中不同的子域的读写访问,由厂商关于这个芯片的SVD文件提供。再一次,在 [tm4c123x documentation][tm4c123x documentation W] 中你能找到 `W` 所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。注意所有我们没有设置的子域将会被设置成一个默认值 - 任何在这个寄存器中的现存的内容将会丢失。
```rust,ignore
pwm.ctl.write(|w| w.globalsync0().clear_bit());
```
-### 修改
+### Modifying
如果我们希望只改变这个寄存器中某个特定的子域而让其它子域不变,我们能使用`modify`函数。这个函数使用一个具有两个参数的闭包 - 一个用来读取,一个用来写入。通常我们分别称它们为 `r` 和 `w` 。 `r` 参数能被用来查看这个寄存器现在的内容,`w` 参数能被用来修改寄存器的内容。
@@ -119,7 +119,7 @@ pwm.ctl.write(|w| w.globalsync0().clear_bit());
pwm.ctl.modify(|r, w| w.globalsync0().clear_bit());
```
-`modify` 函数在这里真正展示了闭包的能量。在C中,我们不得不读取一些临时值,修改成正确的比特,然后再把值写回。这意味着出现错误的范围非常大。
+`modify` 函数在这里真正展示了闭包的能量。在C中,我们经常需要读取一些临时值,修改成正确的比特,然后再把值写回。这意味着出现错误的范围非常大。
```C
uint32_t temp = pwm0.ctl.read();
@@ -136,7 +136,7 @@ pwm0.enable.write(temp); // 哦 不! 错误的变量!
## 使用一个HAL crate
-一个芯片的HAL crate通常通过为PAC暴露的基础结构体们实现一个自定义Trait来发挥作用。经常这个trait将会为某个外设定义一个被称作 `constrain()` 的函数或者为像是有多个管脚的GPIO端口这类东西定义一个`split()`函数。这个函数将会使用基础外设结构体,然后返回一个具有更高抽象的API的新对象。这个API还可以做一些事,像是让Serial port的 `new` 函数变成需要某些`Clock`结构体的函数,这个结构体只能通过调用配置PLLs并设置所有的时钟频率的函数来生成。在这时,生成一个Serial port对象而不先配置时钟速率是不可能的,对于Serial port对象来说错误地将波特率转换为时钟滴答数也是不可能发生的。一些crates甚至为每个GPIO管脚的状态定义了特定的 traits,在把管脚传递进外设前,要求用户去把一个管脚设置成正确的状态(通过选择Alternate Function模式) 。所有这些都没有运行时开销的!
+一个芯片的HAL crate是通过为PAC暴露的基础结构体们实现一个自定义Trait来发挥作用的。经常这个trait将会为某个外设定义一个被称作 `constrain()` 的函数,或者为像是有多个管脚的GPIO端口这类东西定义一个`split()`函数。这个函数将会使用基础的外设结构体,然后返回一个具有更高抽象的API的新对象。这个API还可以做一些事,比如让Serial port的 `new` 函数变成需要某个`Clock`结构体的函数,这个结构体只能通过调用配置PLLs并设置所有的时钟频率的函数来生成。在这时,生成一个Serial port对象而不先配置时钟速率是不可能的,对于Serial port对象来说错误地将波特率转换为时钟滴答数也是不会发生的。一些crates甚至为每个GPIO管脚的状态定义了特定的 traits,在把管脚传递进外设前,要求用户去把一个管脚设置成正确的状态(通过选择Alternate Function模式) 。所有这些都没有运行时开销的!
让我们看一个例子:
diff --git a/src/start/semihosting.md b/src/start/semihosting.md
index 2de73235..57ea2d24 100644
--- a/src/start/semihosting.md
+++ b/src/start/semihosting.md
@@ -47,7 +47,7 @@ $ cargo run
Hello, world!
```
-`exit`半主机操作也能被用于终止QEMU进程。重要:**不要**在硬件上使用`debug::exit`;这个函数会关闭你的OpenOCD对话,这样你将不能执行其它程序调试操作,除了重启它。
+`exit`半主机操作也能被用于终止QEMU进程。重要:**不要**在硬件上使用`debug::exit`;这个函数会关闭你的OpenOCD对话,这样你就不能执行其它的程序调试操作了,除了重启它。
```rust,ignore
#![no_main]
@@ -80,7 +80,7 @@ $ echo $?
1
```
-最后一个提示:你可以将运行时恐慌(panicking)的行为设置成 `exit(EXIT_FAILURE)`。这将允许你编写可以在QEMU上运行通过的 `no_std` 测试。
+最后一个提示:你可以将运行时恐慌(panicking)的行为设置成 `exit(EXIT_FAILURE)`。这会允许你编写可以在QEMU上运行通过的 `no_std` 测试。
为了方便,`panic-semihosting` crate有一个 "exit" 特性。当它使能的时候,在主机stderr上打印恐慌(painc)信息后会调用 `exit(EXIT_FAILURE)` 。
From 99eecc2174d7e3a9fdcd0c560e793ac373398dad Mon Sep 17 00:00:00 2001
From: XxChang
Date: Sat, 19 Nov 2022 21:23:03 +0800
Subject: [PATCH 119/137] minor
---
src/SUMMARY.md | 2 +-
src/peripherals/a-first-attempt.md | 26 +++++++++++++-------------
src/peripherals/index.md | 18 +++++++++---------
src/start/registers.md | 2 +-
4 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index f42f0f78..fd5ccef9 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -28,7 +28,7 @@ more information and coordination
- [中断](./start/interrupts.md)
- [IO](./start/io.md)
- [外设](./peripherals/index.md)
- - [Rust的首次尝试](./peripherals/a-first-attempt.md)
+ - [Rust尝鲜](./peripherals/a-first-attempt.md)
- [借用检查器](./peripherals/borrowck.md)
- [单例](./peripherals/singletons.md)
- [静态保障(static guarantees)](./static-guarantees/index.md)
diff --git a/src/peripherals/a-first-attempt.md b/src/peripherals/a-first-attempt.md
index 36007d86..fc8daf8c 100644
--- a/src/peripherals/a-first-attempt.md
+++ b/src/peripherals/a-first-attempt.md
@@ -1,8 +1,8 @@
-# Rust的首次尝试
+# Rust尝鲜
## 寄存器
-让我们看向 'SysTick' 外设 - 一个简单的计时器,其在每个Cortex-M处理器内核中都有。通常你能在芯片厂商的数据手册或者*技术参考手册*中看到它们,但是下面的例子对所有ARM Cortex-M核心都是通用的,让我们看下[ARM参考手册]。我们能看到这里有四个寄存器:
+让我们看下 'SysTick' 外设 - 一个简单的计时器,它存在于每个Cortex-M处理器内核中。通常你能在芯片厂商的数据手册或者*技术参考手册*中看到它们,但是下面的例子对所有ARM Cortex-M核心都是通用的,让我们看下[ARM参考手册]。我们能看到这里有四个寄存器:
[ARM参考手册]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/Babieigh.html
@@ -15,7 +15,7 @@
## C语言风格的方法(The C Approach)
-在Rust中,我们可以像我们在C语言中做的那样,用一个 `struct` 表示一组寄存器。
+在Rust中,我们可以像C语言一样,用一个 `struct` 表示一组寄存器。
```rust,ignore
#[repr(C)]
@@ -26,7 +26,7 @@ struct SysTick {
pub calib: u32,
}
```
-限定符 `#[repr(C)]` 告诉Rust编译器像C编译器一样去布局这个结构体。那是非常重要的,因为Rust允许结构体字段被重新排序,而C语言不允许。你可以想象下如果这些字段被编译器悄悄地重新排了序,在调试时会给我们带来多大的麻烦!有了这个限定符,我们就有了与上表对应的四个32位的字段。但当然,这个 `struct` 本身没什么用处 - 我们需要一个变量。
+限定符 `#[repr(C)]` 告诉Rust编译器像C编译器一样去布局这个结构体。这非常重要,因为Rust允许结构体字段被重新排序,而C语言不允许。你可以想象下如果这些字段被编译器悄悄地重新排了序,在调试时会给我们带来多大的麻烦!有了这个限定符,我们就有了与上表对应的四个32位的字段。但当然,这个 `struct` 本身没什么用处 - 我们需要一个变量。
```rust,ignore
let systick = 0xE000_E010 as *mut SysTick;
@@ -37,18 +37,18 @@ let time = unsafe { (*systick).cvr };
现在,上面的方法有一堆问题。
-1. 每次我们想要访问我们的外设,我们不得不使用unsafe 。
-2. 我们无法指定哪个寄存器是只读的或者读写的。
-3. 你程序中任何地方的任何一段代码都可以通过这个结构体访问硬件。
+1. 每次想要访问外设,不得不使用unsafe 。
+2. 无法指定哪个寄存器是只读的或者读写的。
+3. 程序中任何地方的任何一段代码都可以通过这个结构体访问硬件。
4. 最重要的是,实际上它并不能工作。
-现在的问题是编译器很聪明。如果你往RAM同个地方写两次,一个接着一个,编译器会注意到这个行为,且完全跳过第一个写入操作。在C语言中,我们能标记变量为`volatile`去确保每个读或写操作按预期发生。在Rust中,我们将*访问*操作标记为易变的(volatile),而不是将变量标记为volatile。
+现在的问题是编译器很聪明。如果你往RAM同个地方写两次,一个接着一个,编译器会注意到这个行为,且完全跳过第一个写入操作。在C语言中,我们能标记变量为`volatile`去确保每个读或写操作按所想的那样发生。在Rust中,我们将*访问*操作标记为易变的(volatile),而不是将变量标记为volatile。
```rust,ignore
let systick = unsafe { &mut *(0xE000_E010 as *mut SysTick) };
let time = unsafe { core::ptr::read_volatile(&mut systick.cvr) };
```
-因此,我们已经修复了我们四个问题中的一个,但是现在我们有了更多的 `unsafe` 代码!幸运的是,有个第三方的crate可以帮助到我们 - [`volatile_register`]
+这样,我们已经修复了一个问题,但是现在我们有了更多的 `unsafe` 代码!幸运的是,有个第三方的crate可以帮助到我们 - [`volatile_register`]
[`volatile_register`]: https://crates.io/crates/volatile_register
@@ -73,13 +73,13 @@ fn get_time() -> u32 {
}
```
-现在通过`read`和`write`方法,volatile accesses可以被自动执行。执行写操作仍然是 `unsafe` 的,但是公平地讲,硬件有一堆可变的状态,对于编译器来说没有方法去知道是否这些写操作是真正安全的,因此默认就这样是个不错的选择。
+现在通过`read`和`write`方法,volatile accesses可以被自动执行。执行写操作仍然是 `unsafe` 的,但是公平地讲,硬件有一堆可变的状态,对于编译器来说没有办法知道是否这些写操作是真正安全的,因此默认就这样是个不错的选择。
## Rust风格的封装
-我们需要把这个`struct`封装进一个更高抽象的API中,这个API对于我们用户来说,可以安全地被调用。作为驱动的作者,我们亲手验证不安全的代码是否正确,然后为我们的用户提供一个safe的API,因此用户们不必担心它(让他们相信我们不会出错!)。
+我们需要把这个`struct`封装进一个更高抽象的API中,这个API对于用户来说,可以安全地调用。作为驱动的作者,我们亲手验证不安全的代码是否正确,然后为我们的用户提供一个safe的API,因此用户们不必担心它(让他们相信我们不会出错!)。
-有可能有这样的例子:
+有可能可以这样写:
```rust,ignore
use volatile_register::{RW, RO};
@@ -133,4 +133,4 @@ fn thread2() {
}
```
-虽然 `set_reload` 函数的 `&mut self` 参数保证了对某个`SystemTimer`结构体的引用只有一个,但是他们不能阻止用户去创造第二个`SystemTimer`,其指向同个外设!如果作者足够尽力,他能发现所有这些'重复的'驱动实例,那么按这种方式写的代码将可以工作,但是一旦代码被散播几天,散播到多个模块,驱动,开发者,它会越来越容易犯此类错误。
+虽然 `set_reload` 函数的 `&mut self` 参数保证了没有引用到其它的`SystemTimer`结构体,但是不能阻止用户去创造第二个`SystemTimer`,其指向同个外设!如果作者足够努力的话,他能发现所有这些'重复的'驱动实例,那么按这种方式写的代码就可以工作,但是一旦代码被散播一段时间,散播给多个模块,驱动,开发者,它会越来越容易触发此类错误。
diff --git a/src/peripherals/index.md b/src/peripherals/index.md
index 179d6d25..2a4530d8 100644
--- a/src/peripherals/index.md
+++ b/src/peripherals/index.md
@@ -2,28 +2,28 @@
## 什么是外设?
-大多数微处理器不仅有一个CPU,RAM,或者Flash存储器 - 它们包含部分被用来与微处理器的外部系统交互的硅片,通过传感器,电机控制器,或者人机接口比如一个显示器或者键盘直接和间接地与它们周围的世界交互。这些组件统称为外设。
+大多数微处理器不仅仅有一个CPU,RAM,或者Flash存储器 - 它们还包含被用来与微处理器的外部系统进行交互的硅片部分,通过传感器,电机控制器,或者人机接口比如一个显示器或者键盘直接和间接地与周遭世界交互。这些组件统称为外设。
这些外设很有用,因为它们允许一个开发者将处理工作交给它们来做,避免了必须在软件中处理每件事。就像一个桌面开发者如何将图形处理工作让给一个显卡那样,嵌入式开发者能将一些任务让给外设去做,让CPU可以把时间放在做其它更重要的事上,或者为了省电啥事也不做。
-如果你看向从1970s或者1980s的旧型号的家庭电脑的主板(其实,昨日的桌面PCs与今日的嵌入式系统没太大区别),你将看到:
+如果你看向来自1970s或者1980s的旧型号的家庭电脑的主板(其实,昨日的桌面PCs与今日的嵌入式系统没太大区别),你将看到:
* 一个处理器
* 一个RAM芯片
* 一个ROM芯片
* 一个I/O控制器
-RAM芯片,处理器将会通过一系列并行的迹(traces)又被称为一个"总线"将ROM芯片和I/O控制器(这个系统中的外设)加进来。这个总线搬运地址信息,其用来选择处理器希望跟总线上哪个设备通信,还有一个用来搬运实际数据的数据总线。在我们的嵌入式微控制器中,应用了相同的概念 - 只是所有的东西被打包到一片硅片上。
+RAM芯片,ROM芯片和I/O控制器(这个系统中的外设)会通过一系列并行的迹(traces)又被称为一个"总线"被加进处理器中。地址总线搬运地址信息,其用来选择处理器希望跟总线上哪个设备通信,还有一个用来搬运实际数据的数据总线。在我们的嵌入式微控制器中,应用了相同的概念 - 只是所有的东西被打包到一片硅片上。
然而,不像显卡,显卡通常有像是Vulkan,Metal,或者OpenGL这样的一个软件API。外设暴露给微控制器的是一个硬件接口,其被映射到一块存储区域。
-## 线性的真实存储空间
+## 线性的物理存储空间
-在一个微控制器上,往一些别的任意地址写一些数据,比如 `0x4000_0000` 或者 `0x0000_0000`,可能也是一个完全有效的动作。
+在一个微控制器上,随便往一些地址写一些数据,比如 `0x4000_0000` 或者 `0x0000_0000`,可能也是一个完全有效的动作。
-在一个桌面系统上,访问内存被MMU,或者内存管理单元紧紧地控制着。这个组件有两个主要责任: 对部分内存加入访问权限(防止一个进程读取或者修改另一个进程的内存);重映射物理内存的段到软件中使用的虚拟内存范围上。微控制器通常没有一个MMU,反而在软件中只使用真实的物理地址。
+在一个桌面系统上,访问内存被MMU,或者内存管理单元紧紧地控制着。这个组件有两个主要责任: 对部分内存加入访问权限(防止一个进程读取或者修改另一个进程的内存);重映射物理内存的段到软件中使用的虚拟内存范围上。微控制器通常没有一个MMU,反而在软件中只使用真实的物理地址。
-虽然32位微控制器有一个从`0x0000_0000`到`0xFFFF_FFFF`的真实的线性的地址空间,但是它们通常只使用几百KiB的实际内存。有相当大部分的地址空间遗留。在早期的章节中,我们说到RAM被放置在地址`0x2000_0000`处。如果我们的RAM是64KiB大小(i.e. 最大地址为0xFFFF),那么地址 `0x2000_0000`到`0x2000_FFFF`与我们的RAM有关。当我们写入一个位于地址`0x2000_1234`的变量时,内部发生的是,一些逻辑发现了地址的上部(这个例子里是0x2000),然后激活RAM,因此它能对地址的下部(这个例子里是0x1234)起作用。在一个Cortex-M上,我们也把我们的Flash ROM映射进地址 `0x000_0000` 到地址 `0x0007_FFFF` 上 (如果我们有一个512KiB Flash ROM)。微控制器设计者没有忽略这两个区域间的所有剩余空间,反而将外设的接口映射到某些地址上。最后看起来像这样:
+虽然32位微控制器有一个从`0x0000_0000`到`0xFFFF_FFFF`的线性的物理地址空间,但是它们通常只使用几百KiB的实际内存。有相当大部分的地址空间保留着。在早期的章节中,我们说到RAM被放置在地址`0x2000_0000`处。如果我们的RAM是64KiB大小(i.e. 最大地址为0xFFFF),那么地址 `0x2000_0000`到`0x2000_FFFF`与我们的RAM有关。当我们写入一个位于地址`0x2000_1234`的变量时,内部发生的是,一些逻辑发现了地址的上部(这个例子里是0x2000),然后激活RAM,以便能操作地址的下部(这个例子里是0x1234)。在一个Cortex-M上,我们也也会把Flash ROM映射进地址 `0x000_0000` 到地址 `0x0007_FFFF` 上 (如果我们有一个512KiB Flash ROM)。微控制器设计者没有忽略这两个区域间的所有剩余空间,反而将外设的接口映射到这些地址上。最后看起来像这样:
![](../assets/nrf52-memory-map.png)
@@ -31,9 +31,9 @@ RAM芯片,处理器将会通过一系列并行的迹(traces)又被称为一个
## 存储映射的外设
-乍一看,与这些外设交互很简单 - 将正确的数据写入正确的地址。比如,在一个串行端口上发送一个32位字(32 bit word),可以像把那个32位字写入某些存储地址一样直接。串行端口外设然后能自动获取和发出数据。
+乍一看,与这些外设交互很简单 - 将正确的数据写入正确的地址。比如,在一个串行端口上发送一个32位字,可以直接把那个32位字写入某个存储地址。串行端口外设然后能自动获取和发出数据。
-这些外设的配置工作相似。不是调用一个函数去配置一个外设,而是暴露一块地址空间作为硬件API。向一个SPI频率控制寄存器写入 `0x8000_0000`,SPI端口将会按照每秒8MB的速度发送数据。像同个地址写入 `0x0200_0000`,SPI端口将会按照每秒125KiB的速度发送数据。这些配置寄存器看起来有点像这个:
+这些外设的配置工作相似。不是调用一个函数去配置一个外设,而是暴露一块地址空间作为硬件API。向一个SPI频率控制寄存器写入 `0x8000_0000`,SPI端口将会按照每秒8MB的速度发送数据。向同个地址写入 `0x0200_0000`,SPI端口将会按照每秒125KiB的速度发送数据。这些配置寄存器看起来有点像这个:
![](../assets/nrf52-spi-frequency-register.png)
diff --git a/src/start/registers.md b/src/start/registers.md
index e46f5f8d..a5584ffb 100644
--- a/src/start/registers.md
+++ b/src/start/registers.md
@@ -1,4 +1,4 @@
-# 存储映射的寄存器
+# 存储映射的寄存器(Memory-Mapped Registers)
嵌入式系统想要继续执行下去,只有通过执行常规的Rust代码并在RAM间移动数据才行。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。
From a7801330deb0ffda1c114cbf23e109f8c8b32e14 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Sat, 19 Nov 2022 21:54:17 +0800
Subject: [PATCH 120/137] minor
---
src/peripherals/borrowck.md | 12 ++++++------
src/peripherals/singletons.md | 18 +++++++++---------
2 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/src/peripherals/borrowck.md b/src/peripherals/borrowck.md
index 2f3a21c7..027faa76 100644
--- a/src/peripherals/borrowck.md
+++ b/src/peripherals/borrowck.md
@@ -1,19 +1,19 @@
## 可变的全局状态
-不幸地是,硬件本质上是个可变的全局状态,Rust开发者可能对此感到很害怕。因为硬件独立于我们所写的代码的结构,能被真实世界在任何时候改变。
+不幸的是,硬件本质上是个可变的全局状态,Rust开发者可能会对此感到很害怕。因为硬件独立于我们所写的代码的结构,能被真实世界在任何时候改变。
## 我们应该遵循什么规则?
我们如何才能做到可靠地与这些外设交互?
-1. 总是使用 `volatile` 方法去读或者写外设存储器。因为它可以随时改变。
+1. 总是使用 `volatile` 方法去读或者写外设存储器。因为它随时会改变。
2. 在软件中,我们应该能共享任何数量的关于这些外设的只读访问
-3. 如果某些软件对一个外设应该可以读写访问,它应该保有对那个外设的唯一引用。
+3. 如果某个软件可以读写一个外设,它应该保有对那个外设的唯一引用。
## 借用检查器
-这些规则最后两个听起来与借用检查器已经做的事相似。
+这些规则最后两个听起来与借用检查器在做的事情很像!
-思考一下,我们是否可以分发这些外设的所有权,或者引用它们?
+思考一下,我们是否可以传递这些外设的所有权,或者提供对它们的可变或者不可变的引用?
-我们当然可以,但是对于借用检查器来说,每个外设我们都只需要一个实例,因此Rust可以正确地处理这个实例。幸运的是,在硬件中,任何给定的外设,只有一个实例,但是我们该如何将它暴露在我们代码的结构中呢?
+我们当然可以,但是对于借用检查器来说,每个外设只有一个实例的话,Rust才可以正确地处理这件事。幸运的是,在硬件中,任何给定的外设,只有一个实例,但是我们该如何将它暴露在代码的结构中呢?
diff --git a/src/peripherals/singletons.md b/src/peripherals/singletons.md
index 38eedcb3..0782c25e 100644
--- a/src/peripherals/singletons.md
+++ b/src/peripherals/singletons.md
@@ -21,11 +21,11 @@ fn main() {
}
```
-但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你所有的程序之间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有权。
+但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你所有的程序间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有权。
## 我们在Rust中要怎么做?
-与其只是让我们的外设变成一个全局变量,我们不如决定创造一个全局变量,在这个例子里其被叫做 `PERIPHERALS`,这个全局变量对于我们的每个外设,它都有一个与之对应的 `Option` 。
+与其只是让我们的外设变成一个全局变量,我们不如生成一个全局变量,在这个例子里其被叫做 `PERIPHERALS`,这个全局变量对于我们的每个外设,它都有一个与之对应的 `Option` 。
```rust,ignore
struct Peripherals {
@@ -42,7 +42,7 @@ static mut PERIPHERALS: Peripherals = Peripherals {
};
```
-这个结构体允许我们获得外设的一个实例。如果我们尝试调用`take_serial()`获得多个实例,我们的代码将会抛出运行时恐慌(panic)!
+这个结构体允许我们获得一个外设的实例。如果我们尝试调用`take_serial()`获得多个实例,我们的代码将会抛出运行时恐慌(panic)!
```rust,ignore
fn main() {
@@ -58,7 +58,7 @@ fn main() {
## 已存在的库支持
-虽然我们在上面创造了我们自己的 `Peripherals` 结构体,但这并不是必须的。`cortex_m` crate 包含一个被叫做 `singleton!()` 的宏,其将为你完成这个任务。
+虽然我们在上面生成了我们自己的 `Peripherals` 结构体,但这并不是必须的。`cortex_m` crate 包含一个被叫做 `singleton!()` 的宏,它可以为你完成这个任务。
```rust,ignore
#[macro_use(singleton)]
@@ -73,7 +73,7 @@ fn main() {
[cortex_m docs](https://docs.rs/cortex-m/latest/cortex_m/macro.singleton.html)
-另外,如果你使用 [`cortex-m-rtic`](/~https://github.com/rtic-rs/cortex-m-rtic),它将获取和定义这些外设的整个过程抽象了出来,你将获得一个`Peripherals`结构体,其包含了一个所有你定义了的项的非 `Option` 的版本。
+另外,如果你使用 [`cortex-m-rtic`](/~https://github.com/rtic-rs/cortex-m-rtic),它将获取和定义这些外设的整个过程抽象了出来,你将获得一个`Peripherals`结构体,其包含了所有你定义了的项的一个非 `Option` 的版本。
```rust,ignore
// cortex-m-rtic v0.5.x
@@ -116,7 +116,7 @@ impl SerialPort {
* 因为我们正在使用一个单例模式,所以我们只有一种方法或者地方去获得一个 `SerialPort` 结构体。
* 为了调用 `read_speed()` 方法,我们必须拥有一个 `SerialPort` 结构体的所有权或者一个引用。
-这两个因素放在一起意味着,只有当我们满足了借用检查器的条件时,我们才有可能访问硬件,也意味着我们在任何时候不可能有多个对同一个硬件的可变引用(&mut)!
+这两个因素放在一起意味着,只有当我们满足了借用检查器的条件时,我们才有可能访问硬件,也意味着在任何时候不可能存在多个对同一个硬件的可变引用(&mut)!
```rust,ignore
fn main() {
@@ -132,9 +132,9 @@ fn main() {
## 像数据一样对待你的硬件
-另外,因为一些引用是可变的,一些是不可变的,观察一个函数或者方法是否会潜在修改硬件的状态变得可能了。比如,
+另外,因为一些引用是可变的,一些是不可变的,就可以知道一个函数或者方法是否有能力修改硬件的状态。比如,
-这个函数允许改变硬件的配置:
+这个函数可以改变硬件的配置:
```rust,ignore
fn setup_spi_port(
@@ -153,5 +153,5 @@ fn read_button(gpio: &GpioPin) -> bool {
}
```
-这允许我们在**编译时**而不是运行时强制代码是否应该或者不应该对硬件进行改变,这通常在只有一个应用的情况下起作用,但是对于裸机系统来说,我们的软件将被编译进一个单一应用中,因此它通常是不受限的。
+这允许我们在**编译时**而不是运行时强制代码是否应该或者不应该对硬件进行修改。要注意,这通常在只有一个应用的情况下起作用,但是对于裸机系统来说,我们的软件将被编译进一个单一应用中,因此这通常不是一个限制。
From 96072231537eee7dded14b6e89ce026323f5d7d2 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Mon, 21 Nov 2022 08:37:47 +0800
Subject: [PATCH 121/137] minor
---
src/peripherals/singletons.md | 8 ++++----
src/static-guarantees/index.md | 8 ++++----
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/peripherals/singletons.md b/src/peripherals/singletons.md
index 0782c25e..e820dabc 100644
--- a/src/peripherals/singletons.md
+++ b/src/peripherals/singletons.md
@@ -7,7 +7,7 @@
[Singleton Pattern]: https://en.wikipedia.org/wiki/Singleton_pattern
-## 但是为什么我们不可以使用全局变量呢?
+## 为什么不可以使用全局变量?
可以像这样,我们可以使每个东西都变成公共静态的(public static):
@@ -23,7 +23,7 @@ fn main() {
但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你所有的程序间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有权。
-## 我们在Rust中要怎么做?
+## 在Rust中要怎么做?
与其只是让我们的外设变成一个全局变量,我们不如生成一个全局变量,在这个例子里其被叫做 `PERIPHERALS`,这个全局变量对于我们的每个外设,它都有一个与之对应的 `Option` 。
@@ -92,7 +92,7 @@ const APP: () = {
}
```
-## 但是为什么?
+## 为什么?
但是这些单例模式是如何使我们的Rust代码在工作方式上产生很大不同的?
@@ -130,7 +130,7 @@ fn main() {
}
```
-## 像数据一样对待你的硬件
+## 像对待数据一样对待硬件
另外,因为一些引用是可变的,一些是不可变的,就可以知道一个函数或者方法是否有能力修改硬件的状态。比如,
diff --git a/src/static-guarantees/index.md b/src/static-guarantees/index.md
index ee10a4f0..15ff4dd8 100644
--- a/src/static-guarantees/index.md
+++ b/src/static-guarantees/index.md
@@ -1,13 +1,13 @@
# 静态保障
-Rust的类型系统可防止编译时的数据竞争(看[`Send`]和[`Sync`]特性(traits))。类型系统也能被用来在编译时检查其它属性;减少某些案例中运行时检查的需要。
+Rust的类型系统可以在编译时防止数据竞争(看[`Send`]和[`Sync`]特性(traits))。也可以在编译时使用类型系统来完成一些检查工作;减少某些案例中运行时检查的需要。
[`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
[`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html
-当应用到嵌入式程序时,这些*静态检查*能被用来,比如,强制I/O接口的配置被恰当地执行了。例如,可以设计一个API来初始化一个串行接口,这个API只能通过先配置接口需要的管脚后才能被正确地使用。
+当应用到嵌入式程序时,这些*静态检查*能被用来,比如,强制I/O接口的配置被恰当地执行了。例如,可以设计一个API来初始化一个串行接口,这个API只能通过先配置接口需要的管脚后才可以正确地使用。
-也可以静态检查操作是否被执行在正确配置了的外设上,像是拉低一个管脚。例如尝试修改一个被配置成浮空输入模式的管脚的输出状态时,将会出发一个编译时错误。
+也可以静态检查操作是否被执行在正确配置了的外设上,像是拉低一个管脚。例如尝试修改一个被配置成浮空输入模式的管脚的输出状态时,会触发一个编译时错误。
-且,像是在前面章节看到的,所有权的概念能被应用到外设上确保只有一个程序的某些部分可以修改一个外设。与将这个外设当做全局可变的状态相比,*访问控制*(assess control)使得软件更容易推理。
+并且,像是在前面章节看到的,所有权的概念能被应用到外设上确保只有一个程序的某些部分可以修改一个外设。与将这个外设当做全局可变的状态相比,*访问控制*(assess control)使得软件更容易推理。
From c8a3b2692c949ff524e5bf58f90938cd42c4d417 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Fri, 25 Nov 2022 14:04:24 +0800
Subject: [PATCH 122/137] minor
---
src/SUMMARY.md | 2 +-
src/concurrency/index.md | 28 ++++++++---------
src/portability/index.md | 30 +++++++++----------
src/start/registers.md | 2 +-
src/static-guarantees/design-contracts.md | 22 +++++++-------
src/static-guarantees/index.md | 8 ++---
src/static-guarantees/state-machines.md | 28 ++++++++---------
.../typestate-programming.md | 10 +++----
.../zero-cost-abstractions.md | 8 ++---
9 files changed, 69 insertions(+), 69 deletions(-)
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index fd5ccef9..1c372239 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -34,7 +34,7 @@ more information and coordination
- [静态保障(static guarantees)](./static-guarantees/index.md)
- [类型状态编程](./static-guarantees/typestate-programming.md)
- [作为状态机的外设](./static-guarantees/state-machines.md)
- - [设计协约](./static-guarantees/design-contracts.md)
+ - [设计约定](./static-guarantees/design-contracts.md)
- [零成本抽象](./static-guarantees/zero-cost-abstractions.md)
- [可移植性](./portability/index.md)
- [并发](./concurrency/index.md)
diff --git a/src/concurrency/index.md b/src/concurrency/index.md
index 5337f388..b986b46f 100644
--- a/src/concurrency/index.md
+++ b/src/concurrency/index.md
@@ -1,16 +1,16 @@
# 并发
-无论何时你程序有可能会在不同时刻执行或者不按顺序执行不同的部分,那并发就出现了。在一个嵌入式环境中,这包括:
+无论何时程序有可能会在不同的时刻执行或者不按顺序执行不同的部分,那并发就出现了。在一个嵌入式环境中,这包括:
* 中断处理函数,一旦相关的中断发生时,中断处理函数就会运行,
-* 不同的多线程形式,在这块,你的微处理器通常会在你的程序的不同部分间进行切换,
-* 在一些多核微处理器系统中,每个核可以同时独立地运行你的程序的不同部分。
+* 不同的多线程形式,在这块,微处理器通常会在程序的不同部分间进行切换,
+* 在一些多核微处理器系统中,每个核可以同时独立地运行程序的不同部分。
因为许多嵌入式程序需要处理中断,因此并发迟早会出现,这也是许多微妙和困难的bugs会出现的地方。幸运地是,Rust提供了许多抽象和安全保障去帮助我们写正确的代码。
## 没有并发
-对于一个嵌入式程序来说最简单的并发是没有并发: 你的软件由单个保持运行的main循环组成,一点中断也没有。有时候这非常适合手边的问题! 通常你的循环将会读取一些输入,执行一些处理,且写入一些输出。
+对于一个嵌入式程序来说最简单的并发是没有并发: 你的软件由一个保持运行的main循环组成,一点中断也没有。有时候这非常适合手边的问题! 通常你的循环将会读取一些输入,执行一些处理,且写入一些输出。
```rust,ignore
#[entry]
@@ -34,7 +34,7 @@ fn main() {
[`static mut`]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
-为了举例这种行为如何在你的代码中导致了微妙的错误,思考一个嵌入式程序,这个程序在每一秒的周期内计数一些输入信号的上升沿(一个频率计数器):
+为了举例这种行为如何在代码中导致了微妙的错误,思考一个嵌入式程序,这个程序在每一秒的周期内计数一些输入信号的上升沿(一个频率计数器):
```rust,ignore
static mut COUNTER: u32 = 0;
@@ -59,7 +59,7 @@ fn timer() {
}
```
-每秒计时器中断会把计数器设置回0。这期间,main循环连续地测量信号,且当它看到从低电平到高电平的变化时,增加计数器的值。因为它是`static mut`,我们不得不使用`unsafe`去访问`COUNTER`,意思是我们向编译器保证我们的操作不会导致任何未定义的行为。你能发现竞态条件吗?`COUNTER`上的增加并不一定是原子的 - 事实上,在大多数嵌入式平台上,它将被分开成一个读取操作,然后是增加,然后是写回。如果中断在读取之后但是在写回之前被激活,在中断返回后,重置回0的操作会被忽略 - 那期间某些变化我们会计算两次。
+每秒计时器中断会把计数器设置回0。这期间,main循环连续地测量信号,且当看到从低电平到高电平的变化时,增加计数器的值。因为它是`static mut`的,我们不得不使用`unsafe`去访问`COUNTER`,意思是我们向编译器保证我们的操作不会导致任何未定义的行为。你能发现竞态条件吗?`COUNTER`上的增加并不一定是原子的 - 事实上,在大多数嵌入式平台上,它将被分开成一个读取操作,然后是增加,然后是写回。如果中断在计数器被读取之后但是在被写回之前被激活,在中断返回后,重置回0的操作会被忽略 - 那期间,我们会算出两倍的转换次数。
## 临界区(Critical Sections)
@@ -92,20 +92,20 @@ fn timer() {
在这个例子里,我们使用 `cortex_m::interrupt::free`,但是其它平台将会有更简单的机制在一个临界区中执行代码。它们都有一样的逻辑,关闭中断,运行一些代码,然后重新使能中断。
-注意,有两个理由,我们不需要把一个临界区放进计时器中断中:
+注意,有两个理由,不需要把一个临界区放进计时器中断中:
* 向`COUNTER`写入0不会被一个竞争影响,因为我们不需要读取它
* 无论如何,它永远不会被`main`线程中断
如果`COUNTER`被多个可能相互 _抢占_ 的中断处理函数共享,那么每一个也需要一个临界区。
-这解决了我们的眼前问题,但是我们仍然要编写许多unsafe的代码,我们需要仔细推敲这些代码,有些我们可能不需要使用临界区。因为每个临界区暂时地暂停了中断处理,就会出现一些消耗,其与某些代码尺寸,更高的中断延迟和抖动有关(中断可能花费很长时间去处理,等待被处理的时间变化非常大)。这是否是个问题取决于你的系统,但是通常,我们想要避免它。
+这解决了我们眼前的问题,但是我们仍然要编写许多unsafe的代码,我们需要仔细推敲这些代码,有些我们可能不需要使用临界区。因为每个临界区暂时暂停了中断处理,就会带来一些相关的成本,一些额外的代码大小,更高的中断延迟和抖动(中断可能花费很长时间去处理,等待被处理的时间变化非常大)。这是否是个问题取决于你的系统,但是通常,我们想要避免它。
-值得注意的是,虽然一个临界区保障了没有中断将会发生,但是它在多核系统上不提供一个排他性保证(exclusivity guarantee)!其它核可能很开心访问与你的核一样的内存区域,即使没有中断。如果你正在使用多核,你将需要更强的同步原语(synchronisation primitives)。
+值得注意的是,虽然一个临界区保障了不会发生中断,但是它在多核系统上不提供一个排他性保证(exclusivity guarantee)!其它核可能很开心访问与你的核一样的内存区域,即使没有中断。如果你正在使用多核,你将需要更强的同步原语(synchronisation primitives)。
## 原子访问
-在一些平台上,可以使用特定的原子指令,它保障了读取-修改-写回操作。针对Cortex-M: `thumbv6`(Cortex-M0,Cortex-M0+)只提供原子读取和存取指令,而`thumv7`(Cortex-M3和其上)提供完全的比较和交换(CAS)指令。这些CAS指令提供了一种替代方法,替代禁用所有中断: 我们可以尝试执行增加操作,它在大多数情况下都会成功,但是如果它被中断了它将会自动重试完整的增加操作。这些原子操作甚至在多核间也是安全的。
+在一些平台上,可以使用特定的原子指令,它保障了读取-修改-写回操作。针对Cortex-M: `thumbv6`(Cortex-M0,Cortex-M0+)只提供原子读取和存取指令,而`thumv7`(Cortex-M3和其上)提供完整的比较和交换(CAS)指令。这些CAS指令可以替代过重的禁用所有中断的方法: 我们可以尝试执行加法操作,它在大多数情况下都会成功,但是如果它被中断了它将会自动重试完整的加法操作。这些原子操作甚至在多核间也是安全的。
```rust,ignore
use core::sync::atomic::{AtomicUsize, Ordering};
@@ -133,9 +133,9 @@ fn timer() {
}
```
-这时,`COUNTER`是一个safe的`static`变量。多亏了`AtomicUsize`类型,不需要禁用中断,`COUNTER`能从中断处理函数和main线程被安全地修改。当可以这么做时,这是一个更好的解决方案 - 然而你的平台上可能不支持。
+这时,`COUNTER`是一个safe的`static`变量。多亏了`AtomicUsize`类型,不需要禁用中断,`COUNTER`能从中断处理函数和main线程被安全地修改。当可以这么做时,这是一个更好的解决方案 - 然而平台上可能不支持这么做。
-一个关于[`Ordering`]的提醒: 它可能影响编译器和硬件如何重新排序指令,也会影响缓存可见性。假设目标是个单核平台,在这个案例里`Relaxed`是充足和最有效的选择。更严格的排序将导致编译器在原子操作周围产生内存屏障(Memory Barriers);取决于你做什么原子操作,你可能需要或者不需要这个排序!原子模型的精确细节是复杂的,细节最好写在其它地方。
+关于[`Ordering`]的提醒: 它可能影响编译器和硬件如何重新排序指令,也会影响缓存可见性。假设目标是个单核平台,在这个案例里`Relaxed`是充足的和最有效的选择。更严格的排序将导致编译器在原子操作周围产生内存屏障(Memory Barriers);取决于你做什么原子操作,你可能需要或者不需要这个排序!原子模型的精确细节是复杂的,最好写在其它地方。
关于原子操作和排序的更多细节,可以看这里[nomicon]。
@@ -145,9 +145,9 @@ fn timer() {
## 抽象,Send和Sync
-上面的解决方案都不是特别令人满意。它们需要`unsafe`块,`unsafe`块必须要被十分小心地检查且要符合人体工程学。确实,我们在Rust中可以做得更好!
+上面的解决方案都不是特别令人满意。它们需要`unsafe`块,`unsafe`块必须要被十分小心地检查且不符合人体工程学。确实,我们在Rust中可以做得更好!
-我们可以把我们的计数器抽象进一个安全的接口,其可以在我们代码的其它地方被安全地使用。在这个例子里,我们将使用临界区的(cirtical-section)计数器,但是你可以用原子操作做一些非常类似的事情。
+我们可以把我们的计数器抽象进一个安全的接口中,它可以在代码的其它地方被安全地使用。在这个例子里,我们将使用临界区的(cirtical-section)计数器,但是你可以用原子操作做一些非常类似的事情。
```rust,ignore
use core::cell::UnsafeCell;
diff --git a/src/portability/index.md b/src/portability/index.md
index f148fd26..6f527465 100644
--- a/src/portability/index.md
+++ b/src/portability/index.md
@@ -1,6 +1,6 @@
# 可移植性
-在嵌入式环境中,可移植性是一个非常重要的主题: 每个供应商甚至同个制造商的每个系列,都提供了不同的外设和功能。同样地,与外设交互的方式也将会不一样。
+在嵌入式环境中,可移植性是一个非常重要的主题: 每个供应商甚至同个制造商的不同系列,都提供了不同的外设和功能。同样地,与外设交互的方式也将会不一样。
通过一个被叫做硬件抽象层或者**HAL**的层去均等化这种差异是一种常见的方法。
@@ -11,13 +11,13 @@
[Hardware Abstraction Layer]: https://en.wikipedia.org/wiki/Hardware_abstraction
-在这方面嵌入式系统有点特别,因为我们通常没有操作系统和用户可安装的软件,而是只有固件镜像,其作为一个整体被编译且伴着许多约束。因此虽然维基百科定义的传统方法可能有效,但是它不是确保可移植性最有效的方法。
+在这方面嵌入式系统有点特别,因为我们通常没有操作系统和用户可安装的软件,而是只有固件镜像,其作为一个整体被编译且伴着许多约束。因此虽然维基百科定义的传统方法可能有用,但是它不是确保可移植性最有效的方法。
在Rust中我们要怎么实现这个目标呢?让我们进入**embedded-hal**...
## 什么是embedded-hal?
-简而言之,它是一组traits,其定义了**HAL implementations**,**驱动**,**应用(或者固件)** 之间的实现约定(implementation contracts)。这些约定包括功能(即约定,如果为某个类型实现了某个trait,**HAL implementation**就提供了某个功能)和方法(即,如果你要构造一个实现了某个trait的类型,约定保障你肯定有在trait中指定的方法)。
+简而言之,它是一组traits,其定义了**HAL implementations**,**驱动**,**应用(或者固件)** 之间的实现约定(implementation contracts)。这些约定包括功能(即约定,如果为某个类型实现了某个trait,**HAL implementation**就提供了某个功能)和方法(即,如果构造一个实现了某个trait的类型,约定保障类型肯定有在trait中指定的方法)。
典型的分层可能如下所示:
@@ -32,32 +32,32 @@
* Timers/Countdowns
* Analog Digital Conversion
-出现 **embedded-hal** traits和依赖它们的crates的主要原因是为了控制复杂性。如果你认为一个应用程序可能必须要在硬件中实现外设的使用,以及应用程序和其它硬件组件潜在的驱动,那么其应该很容易被看作是可复用性有限的。用数学语言来说就是,如果**M**是外设HAL implementations的数量,**N**是驱动的数量,那么如果我们要为每个应用重新发明轮子我们最终会有**M*N**个实现,然而通过使用**embedded-hal**的traits提供的 *API* 将会使实现复杂性变成**M+N** 。当然还有其它好处,比如由于API定义良好,开箱即用,导致试错减少。
+使用**embedded-hal** traits和依赖**embedded-hal**的crates的主要原因是为了控制复杂性。如果发现一个应用可能必须要实现对硬件外设的使用,以及需要实现应用程序和其它硬件组件间潜在的驱动,那么其应该很容易被看作是可复用性有限的。用数学语言来说就是,如果**M**是外设HAL implementations的数量,**N**是驱动的数量,那么如果我们要为每个应用重新发明轮子我们最终会有**M*N**个实现,然而通过使用**embedded-hal**的traits提供的 *API* 将会使实现复杂性变成**M+N** 。当然还有其它好处,比如由于API定义良好,开箱即用,导致试错减少。
## embedded-hal的用户
-像上面说的,HAL有三个主要用户:
+像上面所说的,HAL有三个主要用户:
### HAL implementation
-一个HAL implentation提供硬件和HAL traits的用户之间的接口。典型的实现由三部分组成:
+HAL implentation提供硬件和HAL traits的用户之间的接口。典型的实现由三部分组成:
-* 一个或者多个特定于硬件的类型
-* 生成和初始化这个类型的函数,其经常提供不同的配置选项(速度,操作模式,使用的管脚,etc 。)
-* 与那个类型有关的一个或者多个 **embedded-hal** traits 的 `trait` `impl`
+* 一个或者多个硬件特定的类型
+* 生成和初始化这个类型的函数,函数经常提供不同的配置选项(速度,操作模式,使用的管脚,etc 。)
+* 与这个类型有关的一个或者多个 **embedded-hal** traits 的 `trait` `impl`
-这样的一个 **HAL implementation** 可以有各种类型:
-* 通过低级硬件访问,e.g. 通过寄存器。
-* 通过操作系统,e.g. 通过使用Linux下的 `sysfs`
-* 通过适配器,e.g. 一个与单元测试有关的类型的模仿
+这样的一个 **HAL implementation** 可以有多个方法来实现:
+* 通过低级硬件访问,比如通过寄存器。
+* 通过操作系统,比如通过使用Linux下的 `sysfs`
+* 通过适配器,比如一个与单元测试有关的类型的仿真
* 通过相关硬件适配器的驱动,e.g. I2C多路复用器或者GPIO扩展器(I2C multiplexer or GPIO expander)
### 驱动
-一个驱动为一个外部或者内部组件实现了一组自定义的功能,被连接到一个实现了embedded-hal traits的外设上。这种驱动的典型的例子包括多个传感器(温度计,磁力计,加速度计,光照计),显示设备(LED阵列,LCD显示屏)和执行器(电机,发送器)。
+驱动为一个外部或者内部组件实现了一组自定义的功能,被连接到一个实现了embedded-hal traits的外设上。这种驱动的典型的例子包括多个传感器(温度计,磁力计,加速度计,光照计),显示设备(LED阵列,LCD显示屏)和执行器(电机,发送器)。
-必须使用实现了embedded-hal的某个`trait`的类型的实例来初始化一个驱动,这是通过trait bound来确保的,驱动也提供了它自己的类型实例,这个实例具有一组自定义的方法,这些方法允许与被驱动的设备交互。
+必须使用实现了embedded-hal的某个`trait`的类型的实例来初始化驱动,这是通过trait bound来确保的,驱动也提供了它自己的类型实例,这个实例具有一组自定义的方法,这些方法允许与被驱动的设备交互。
### 应用
diff --git a/src/start/registers.md b/src/start/registers.md
index a5584ffb..21876810 100644
--- a/src/start/registers.md
+++ b/src/start/registers.md
@@ -104,7 +104,7 @@ if pwm.ctl.read().globalsync0().is_set() {
### Writing
-`write()`函数使用一个只有一个参数的闭包。通常我们把这个参数叫做 `w`。然后这个参数提供对这个寄存器中不同的子域的读写访问,由厂商关于这个芯片的SVD文件提供。再一次,在 [tm4c123x documentation][tm4c123x documentation W] 中你能找到 `W` 所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。注意所有我们没有设置的子域将会被设置成一个默认值 - 任何在这个寄存器中的现存的内容将会丢失。
+`write()`函数使用一个只有一个参数的闭包。通常我们把这个参数叫做 `w`。然后这个参数提供对这个寄存器中不同的子域的读写访问,由厂商关于这个芯片的SVD文件提供。再一次,在 [tm4c123x documentation][tm4c123x documentation W] 中你能找到 `W` 所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。注意,所有我们没有设置的子域将会被设置成一个默认值 - 将会丢失任何在这个寄存器中的现存的内容。
```rust,ignore
diff --git a/src/static-guarantees/design-contracts.md b/src/static-guarantees/design-contracts.md
index c7a68ad0..1438e52e 100644
--- a/src/static-guarantees/design-contracts.md
+++ b/src/static-guarantees/design-contracts.md
@@ -1,6 +1,6 @@
-# 设计协约
+# 设计约定(design contracts)
-在我们的上个章节中,我们写了一个接口,其没有强制执行设计协约。让我们再看下我们假想的GPIO配置寄存器:
+在我们的上个章节中,我们写了一个接口,但没有强制遵守设计约定。让我们再看下我们假想的GPIO配置寄存器:
| 名字 | 位数(s) | 值 | 含义 | 注释 |
@@ -9,16 +9,16 @@
| | | 1 | 使能 | 使能GPIO |
| 方向 | 1 | 0 | 输入 | 方向设置成输入 |
| | | 1 | 输出 | 方向设置成输出 |
-| 输入模式 | 2..3 | 00 | hi-z | 输入设置为高阻抗 |
+| 输入模式 | 2..3 | 00 | hi-z | 输入设置为高阻态 |
| | | 01 | 下拉 | 下拉输入管脚 |
| | | 10 | 上拉 | 上拉输入管脚 |
-| | | 11 | n/a | 无效模式。不要设置 |
-| 输出模式 | 4 | 0 | 拉低 | 拉低输出管脚 |
-| | | 1 | 拉高 | 拉高输出管脚 |
+| | | 11 | n/a | 无效状态。不要设置 |
+| 输出模式 | 4 | 0 | 拉低 | 把管脚设置成低电平 |
+| | | 1 | 拉高 | 把管脚设置成高电平 |
| 输入状态 | 5 | x | in-val | 如果输入 < 1.5v 为0,如果输入 >= 1.5v 为1 |
-如果我们在使用底层硬件之前检查状态,在运行时强制遵守我们的设计协约,我们写的代码可能像这一样:
+如果在使用底层硬件之前检查状态,在运行时强制遵守设计约定,代码可能像这一样:
```rust,ignore
/// GPIO接口
@@ -99,11 +99,11 @@ impl GpioConfig {
}
```
-因为我们不需要强制遵守硬件上的限制,所以我们最后做了很多运行时检查,它浪费了我们很多时间和资源,对于开发者来说,这个代码用起来就没那么愉快了。
+因为需要强制遵守硬件上的限制,所以最后做了很多运行时检查,它浪费了我们很多时间和资源,对于开发者来说,这个代码用起来就没那么愉快了。
## 类型状态(Type states)
-但是,如果我们使用Rust的类型系统去强制状态转换的规则会怎样?看下这个例子:
+但是,如果我们使用Rust的类型系统去强制遵守状态转换的规则会怎样?看下这个例子:
```rust,ignore
/// GPIO接口
@@ -251,6 +251,6 @@ output_pin.set_bit(true);
## 编译时功能安全(Functional Safety)
-因为我们在编译时完全强制我们遵守我们的设计约定,这造成了没有运行时开销。当你有一个在输入模式的管脚时,是不可能去设置一个输出模式的。你必须先把它设置成一个输出管脚,然后再设置输出模式。因为在执行一个函数前会检查现在的状态,因此没有运行时消耗。
+因为我们在编译时完全强制遵守设计约定,这造成了没有运行时开销。当管脚处于输入模式时时,是不可能设置输出模式的。必须先把它设置成一个输出管脚,然后再设置输出模式。因为在执行一个函数前会检查现在的状态,因此没有运行时消耗。
-也因为这些状态被类型系统强制遵守,因此没有为这个接口的使用者留太多的犯错空间。如果它们尝试执行一个非法的状态转换,代码将不会编译!
+也因为这些状态被类型系统强制遵守,因此没有为这个接口的使用者留太多的犯错余地。如果它们尝试执行一个非法的状态转换,代码将不会编译!
diff --git a/src/static-guarantees/index.md b/src/static-guarantees/index.md
index 15ff4dd8..5dcfe15b 100644
--- a/src/static-guarantees/index.md
+++ b/src/static-guarantees/index.md
@@ -1,13 +1,13 @@
# 静态保障
-Rust的类型系统可以在编译时防止数据竞争(看[`Send`]和[`Sync`]特性(traits))。也可以在编译时使用类型系统来完成一些检查工作;减少某些案例中运行时检查的需要。
+Rust的类型系统可以在编译时防止数据竞争(看[`Send`]和[`Sync`]特性(traits))。也可以在编译时使用类型系统来完成一些检查工作;减少某些例子中对运行时检查的需要。
[`Send`]: https://doc.rust-lang.org/core/marker/trait.Send.html
[`Sync`]: https://doc.rust-lang.org/core/marker/trait.Sync.html
-当应用到嵌入式程序时,这些*静态检查*能被用来,比如,强制I/O接口的配置被恰当地执行了。例如,可以设计一个API来初始化一个串行接口,这个API只能通过先配置接口需要的管脚后才可以正确地使用。
+当应用到嵌入式程序时,这些*静态检查*能被用来,比如,强制按需配置I/O接口。例如,可以设计一个初始化串行接口的API,这个API只有在配置好接口需要的管脚后才可以被正确地使用。
-也可以静态检查操作是否被执行在正确配置了的外设上,像是拉低一个管脚。例如尝试修改一个被配置成浮空输入模式的管脚的输出状态时,会触发一个编译时错误。
+也可以静态检查,是否是在正确配置了的外设上执行的操作,像是拉低一个管脚这种操作。比如尝试修改一个被配置成浮空输入模式的管脚的输出状态时,会触发一个编译时错误。
-并且,像是在前面章节看到的,所有权的概念能被应用到外设上确保只有一个程序的某些部分可以修改一个外设。与将这个外设当做全局可变的状态相比,*访问控制*(assess control)使得软件更容易推理。
+并且,像是在前面章节看到的,所有权的概念能被应用到外设上确保一个程序只有某些部分可以修改一个外设。与将这个外设当做全局可变的状态相比,*访问控制*(assess control)使得软件更容易推理。
diff --git a/src/static-guarantees/state-machines.md b/src/static-guarantees/state-machines.md
index 24933c1a..44f3d7bd 100644
--- a/src/static-guarantees/state-machines.md
+++ b/src/static-guarantees/state-machines.md
@@ -10,18 +10,18 @@
* 输出: 高
* 输出: 低
* 配置成输入
- * 输入: 高阻抗
+ * 输入: 高阻态
* 输入: 下拉
* 输入: 上拉
-如果外设开始于`关闭`模式,切换到`输入: 高阻抗`模式,我们必须执行下面的步骤:
+如果外设开始于`关闭`模式,切换到`输入: 高阻态`模式,我们必须执行下面的步骤:
1. 关闭
2. 使能
3. 配置成输入
-4. 输入: 高阻抗
+4. 输入: 高阻态
-如果我们想要从`输入: 高阻抗`切换到`输入: 下拉`,我们必须执行下列的步骤:
+如果我们想要从`输入: 高阻态`切换到`输入: 下拉`,我们必须执行下列的步骤:
1. 输入: 高阻抗
2. 输入: 下拉
@@ -34,7 +34,7 @@
## 硬件表征(Hardware Representation)
-通常,上面列的状态通过向指定的映射到一个GPIO外设的寄存器中写入值来配置。让我们定义一个假想的GPIO配置寄存器来解释下它:
+通常,通过向映射到GPIO外设上的指定的寄存器中写入值可以配置上面列出的状态。让我们定义一个假想的GPIO配置寄存器来解释下它:
| 名字 | 位数(s) | 值 | 含义 | 注释 |
| ---: | ------------: | ----: | ------: | ----: |
@@ -42,15 +42,15 @@
| | | 1 | 使能 | 使能GPIO |
| 方向 | 1 | 0 | 输入 | 方向设置成输入 |
| | | 1 | 输出 | 方向设置成输出 |
-| 输入模式 | 2..3 | 00 | hi-z | 输入设置为高阻抗 |
+| 输入模式 | 2..3 | 00 | hi-z | 输入设置为高阻态 |
| | | 01 | 下拉 | 下拉输入管脚 |
| | | 10 | 上拉 | 上拉输入管脚 |
-| | | 11 | n/a | 无效模式。不要设置 |
-| 输出模式 | 4 | 0 | 拉低 | 拉低输出管脚 |
-| | | 1 | 拉高 | 拉高输出管脚 |
-| 输入状态 | 5 | x | in-val | 如果输入 < 1.5v 为0,如果输入 >= 1.5v 为1 |
+| | | 11 | n/a | 无效状态。不要设置 |
+| 输出模式 | 4 | 0 | 拉低 | 输出管脚变成地电平 |
+| | | 1 | 拉高 | 输出管脚变成高电平 |
+| 输入状态 | 5 | x | in-val | 如果输入 < 1.5v为0,如果输入 >= 1.5v为1 |
-我们 _能_ 在Rust中暴露下列的结构体来控制这个GPIO:
+_可以_ 在Rust中暴露下列的结构体来控制这个GPIO:
```rust,ignore
/// GPIO接口
@@ -90,8 +90,8 @@ impl GpioConfig {
}
```
-然而,这将会允许我们修改某些没有意义的寄存器。比如,如果当我们的GPIO被配置为输入时我们设置`output_mode`字段,将会发生什么?
+然而,这会允许我们修改某些没有意义的寄存器。比如,如果当我们的GPIO被配置为输入时我们设置`output_mode`字段,将会发生什么?
-正常使用这个结构体将会允许我们访问我们上面的状态机没有定义的状态: e.g. 一个被上拉的输出,或者一个被拉高的输入。对于一些硬件,这并没有关系。对另外一些硬件来说,这将会导致不可预期或者没有定义的行为!
+通常使用这个结构体会允许我们访问到上面的状态机没有定义的状态:比如,一个被上拉的输出,或者一个被拉高的输入。对于一些硬件,这并没有关系。对另外一些硬件来说,这将会导致不可预期或者没有定义的行为!
-虽然这个接口很方便写入,但是它没有强制我们遵守为硬件的实现所规定的设计协约。
+虽然这个接口很方便写入,但是它没有强制我们遵守硬件实现所设的设计约定。
diff --git a/src/static-guarantees/typestate-programming.md b/src/static-guarantees/typestate-programming.md
index 1469dee3..10f5af6d 100644
--- a/src/static-guarantees/typestate-programming.md
+++ b/src/static-guarantees/typestate-programming.md
@@ -1,6 +1,6 @@
# 类型状态编程(Typestate Programming)
-[typestates]的概念是指将有关对象当前状态的信息编码进该对象的类型中。虽然这听起来有点神秘,如果你在Rust中使用了[建造者模式],你就已经开始使用类型状态编程了!
+[typestates]的概念是指将有关对象当前状态的信息编码进该对象的类型中。虽然这听起来有点神秘,如果你在Rust中用过[建造者模式],你就已经开始使用类型状态编程了!
[typestates]: https://en.wikipedia.org/wiki/Typestate_analysis
[建造者模式]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
@@ -49,18 +49,18 @@ fn main() {
}
```
-在这个例子里,不能直接生成一个`Foo`对象。我们必须创造一个`FooBuilder`,且我们恰当地初始化`FooBuilder`后才能获取到我们需要的`Foo`对象。
+在这个例子里,不能直接生成一个`Foo`对象。必须先生成一个`FooBuilder`,并且恰当地初始化`FooBuilder`后,才能获取到需要的`Foo`对象。
这个最小的例子编码了两个状态:
-* `FooBuilder`,其表示了一个"没有被配置",或者"正在配置"状态
+* `FooBuilder`,其表示一个"没有被配置",或者"正在配置"状态
* `Foo`,其表示了一个"被配置",或者"可以使用"状态。
## 强类型
-因为Rust有一个[强类型系统],没有简单的方法可以奇迹般地创造一个`Foo`实例或者不用调用`into_foo()`方法把一个`FooBuilder`变成一个`Foo`。另外,调用`into_foo()`方法消费了最初的`FooBuilder`结构体,意味着不创造一个新的实例它就不能被再次使用。
+因为Rust有一个[强类型系统],没有什么简单的方法可以奇迹般地生成一个`Foo`实例,也没有简单的方法可以不用调用`into_foo()`方法而把一个`FooBuilder`变成一个`Foo`。另外,调用`into_foo()`方法消费了最初的`FooBuilder`结构体,意味着不生成一个新的实例就不能被再次使用它。
[强类型系统]: https://en.wikipedia.org/wiki/Strong_and_weak_typing
-这允许我们去将我们系统的状态表示成类型,把状态转换必须的动作包括进交换两个类型的方法中。通过创造一个 `FooBuilder`,与一个 `Foo` 对象交换,我们已经使用了一个基本的状态机。
+这允许我们可以将系统的状态表示成类型,把状态转换必须的动作包括进转换两个类型的方法中。通过生成一个 `FooBuilder`,转换成一个 `Foo` 对象,我们已经使用了一个基本的状态机。
diff --git a/src/static-guarantees/zero-cost-abstractions.md b/src/static-guarantees/zero-cost-abstractions.md
index aaf4126c..ef88f021 100644
--- a/src/static-guarantees/zero-cost-abstractions.md
+++ b/src/static-guarantees/zero-cost-abstractions.md
@@ -17,7 +17,7 @@ let _ = size_of::>(); // == 0
struct Enabled;
```
-像这样定义的结构体被称为零大小的类型,因为它们不包含实际数据。虽然这些类型在编译时像是"真的"(real) - 你可以拷贝它们,移动它们,引用它们,etc.,然而优化器将会完全跳过它们。
+像这样定义的结构体被称为零大小的类型,因为它们不包含实际数据。虽然这些类型在编译时像是"真实的"(real) - 你可以拷贝它们,移动它们,引用它们,等等,然而优化器将会完全跳过它们。
在这个代码片段里:
@@ -33,10 +33,10 @@ pub fn into_input_high_z(self) -> GpioConfig {
}
```
-我们返回的GpioConfig在运行时并不存在。对这个函数的调用通常会被归纳为一条汇编指令 - 保存一个常量寄存器值进一个寄存器里。这意味着我们开发的类型状态接口是一个零成本抽象 - 它不会用更多的CPU,RAM,或者代码空间去跟踪`GpioConfig`的状态,会渲染成和直接访问寄存器一样的机器码。
+我们返回的GpioConfig在运行时并不存在。对这个函数的调用通常会被归纳为一条汇编指令 - 把一个常量寄存器值存进一个寄存器里。这意味着我们开发的类型状态接口是一个零成本抽象 - 它不会用更多的CPU,RAM,或者代码空间去跟踪`GpioConfig`的状态,会被渲染成和直接访问寄存器一样的机器码。
## 嵌套
-通常,你可能会把这些抽象深深地嵌套起来。一旦结构体使用的所有的组件是零大小类型的,整个结构体将不会在运行时存在。
+通常,这些抽象可能会被深深地嵌套起来。一旦结构体使用的所有的组件是零大小类型的,整个结构体将不会在运行时存在。
-对于复杂或者深度嵌套的结构体,定义所有可能的状态组合可能很乏味。在这些例子中,宏能被用来生成所有的实现。
+对于复杂或者深度嵌套的结构体,定义所有可能的状态组合可能很乏味。在这些例子中,宏可能可以被用来生成所有的实现。
From 67a90023e4771631a410b588ecbb88bdc58bf88d Mon Sep 17 00:00:00 2001
From: XxChang
Date: Fri, 25 Nov 2022 14:56:37 +0800
Subject: [PATCH 123/137] minor
---
src/SUMMARY.md | 4 ++--
src/appendix/glossary.md | 8 ++++----
src/interoperability/c-with-rust.md | 2 +-
src/interoperability/index.md | 12 ++++++------
src/interoperability/rust-with-c.md | 20 ++++++++++----------
src/unsorted/math.md | 6 +++---
src/unsorted/speed-vs-size.md | 2 +-
7 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index 1c372239..c3251e12 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -49,8 +49,8 @@ more information and coordination
- [给嵌入式C开发者的贴士](./c-tips/index.md)
- [互用性](./interoperability/index.md)
- - [给Rust配上一点C](./interoperability/c-with-rust.md)
- - [给C配上一点Rust](./interoperability/rust-with-c.md)
+ - [使用C的Rust](./interoperability/c-with-rust.md)
+ - [使用Rust的C](./interoperability/rust-with-c.md)
- [没有排序的主题](./unsorted/index.md)
- [优化: 速度与大小间的博弈](./unsorted/speed-vs-size.md)
- [执行数学运算](./unsorted/math.md)
diff --git a/src/appendix/glossary.md b/src/appendix/glossary.md
index a18d9f3b..fdbb26a1 100644
--- a/src/appendix/glossary.md
+++ b/src/appendix/glossary.md
@@ -1,10 +1,10 @@
# 附录A: 词汇表
-嵌入式生态系统充满了不同的协议,硬件组件,还有许多与生产商相关的东西,它们都使用自己的缩写和项目名。这个词汇表尝试列出它们以便更高理解它们。
+嵌入式生态系统中充满了不同的协议,硬件组件,还有许多与生产商相关的东西,它们都使用自己的缩写和项目名。这个词汇表尝试列出它们以便更好理解它们。
### BSP
-一个板级支持Crate(Board Support Crate)提供一个为某个特定板子配置的高级接口。它通常依赖一个[HAL](#hal) crate 。在[存储映射的寄存器那页](../start/registers.md)有更多细节的描述或者看[这个视频](https://youtu.be/vLYit_HHPaY)来获取一个更广泛的概述。
+板级支持的Crate(Board Support Crate)提供为某个特定板子配置的高级接口。它通常依赖一个[HAL](#hal) crate 。在[存储映射的寄存器那页](../start/registers.md)有更多细节的描述或者看[这个视频](https://youtu.be/vLYit_HHPaY)来获取一个更广泛的概述。
### FPU
@@ -12,7 +12,7 @@
### HAL
-一个硬件抽象层(Hardware Abstraction Layer) crate为一个微控制器的功能和外设提供一个开发者友好的接口。它通常在[Peripheral Access Crate (PAC)](#pac)之上被实现。它可能也会实现来自[`embedded-hal`](https://crates.io/crates/embedded-hal) crate的traits 。在[存储映射的寄存器那页](../start/registers.md)上有更多的细节或者看[这个视频](https://youtu.be/vLYit_HHPaY)获取一个更广泛的概述。
+硬件抽象层(Hardware Abstraction Layer) crate为微控制器的功能和外设提供一个开发者友好的接口。它通常在[Peripheral Access Crate (PAC)](#pac)之上被实现。它可能也会实现来自[`embedded-hal`](https://crates.io/crates/embedded-hal) crate的traits 。在[存储映射的寄存器那页](../start/registers.md)上有更多的细节或者看[这个视频](https://youtu.be/vLYit_HHPaY)获取一个更广泛的概述。
### I2C
@@ -32,7 +32,7 @@
### SVD
-系统视图描述文件(System View Description)是一个XML文件格式,被用来描述一个微控制器设备的程序员视角。你能在[the ARM CMSIS documentation site](https://www.keil.com/pack/doc/CMSIS/SVD/html/index.html)上获取更多信息。
+系统视图描述文件(System View Description)是一个XML文件格式,以程序员视角来描述一个微控制器设备。你能在[the ARM CMSIS documentation site](https://www.keil.com/pack/doc/CMSIS/SVD/html/index.html)上获取更多信息。
### UART
diff --git a/src/interoperability/c-with-rust.md b/src/interoperability/c-with-rust.md
index 51dc695a..ec204419 100644
--- a/src/interoperability/c-with-rust.md
+++ b/src/interoperability/c-with-rust.md
@@ -1,4 +1,4 @@
-# Rust配点C
+# 使用C的Rust
在一个Rust项目中使用C或者C++,由两个主要部分组成:
diff --git a/src/interoperability/index.md b/src/interoperability/index.md
index a002b60b..6431a155 100644
--- a/src/interoperability/index.md
+++ b/src/interoperability/index.md
@@ -1,8 +1,8 @@
# 互用性
-Rust和C代码间的互用性始终取决于两种语言间的数据转换。为了实现它,在`stdlib`中,有两个专用模块,叫做[`std::ffi`](https://doc.rust-lang.org/std/ffi/index.html)和[`std::os::raw`](https://doc.rust-lang.org/std/os/raw/index.html) 。
+Rust和C代码间的互用性始终取决于两种语言间的数据转换。为了实现互操性,在`stdlib`中,有两个专用模块,叫做[`std::ffi`](https://doc.rust-lang.org/std/ffi/index.html)和[`std::os::raw`](https://doc.rust-lang.org/std/os/raw/index.html) 。
-`std::os::raw`处理底层基本类型,这些类型可以被编译器隐式地转换,因为Rust和C之间的内存布局足够相似或相同。
+`std::os::raw`处理底层的基本类型,这些类型可以被编译器隐式地转换,因为Rust和C之间的内存布局足够相似或相同。
`std::ffi`提供了一些工具去转换更复杂的类型,比如Strings,将`&str`和`String`映射成更容易和安全处理的C类型。
@@ -19,7 +19,7 @@ Rust和C代码间的互用性始终取决于两种语言间的数据转换。为
| u32 or u64 | c_uint | unsigned int |
| etc | ... | ... |
-像上面提到的基本类型能被编译器隐式地转换。
+像上面提到的基本类型都能被编译器隐式地转换。
```rust,ignore
unsafe fn foo(num: u32) {
@@ -30,9 +30,9 @@ unsafe fn foo(num: u32) {
## 与其它编译系统的互用性
-把Rust包含进你的嵌入式项目的一个常见需求是,把Cargo结合进你现存的编译系统中,比如make或者cmake。
+在嵌入式项目中引入Rust的一个常见需求是,把Cargo结合进你现存的编译系统中,比如make或者cmake。
-在[issue #61]中我们的issue tracker上,我们正在为这个需求收集例子和用例。
+在[issue #61]的issue tracker上,我们正在为这个需求收集例子和用例。
[issue #61]: /~https://github.com/rust-embedded/book/issues/61
@@ -41,6 +41,6 @@ unsafe fn foo(num: u32) {
将Rust和一个RTOS集成在一起,比如FreeRTOS或者ChibiOS仍然在进行中; 尤其是从Rust调用RTOS函数可能很棘手。
-在[issue #62]中我们的issue tracker上,我们正为这件事收集例子和用例。
+在[issue #62]的issue tracker上,我们正为这件事收集例子和用例。
[issue #62]: /~https://github.com/rust-embedded/book/issues/62
diff --git a/src/interoperability/rust-with-c.md b/src/interoperability/rust-with-c.md
index 39375d88..c9a49c1f 100644
--- a/src/interoperability/rust-with-c.md
+++ b/src/interoperability/rust-with-c.md
@@ -1,15 +1,15 @@
-# 你的C配点Rust
+# 使用Rust的C
在C或者C++中使用Rust代码通常由两部分组成。
-- 用Rust创造一个C友好的API
+- 用Rust生成一个C友好的API
- 将你的Rust项目嵌入一个外部的编译系统
除了`cargo`和`meson`,大多数编译系统没有原生Rust支持。因此你最好只用`cargo`编译你的crate和依赖。
## 设置一个项目
-像往常一样创建一个新的`cargo`项目。有一些标志可以告诉`cargo`去生成一个系统库,而不是常规的rust目标文件。如果你想要它与你的crate的其它部分不一样,这也允许你为你的库设置一个不同的输出名。
+像往常一样创建一个新的`cargo`项目。有一些标志可以告诉`cargo`去生成一个系统库,而不是常规的rust目标文件。如果你想要它与crate的其它部分不一样,你也可以为你的库设置一个不同的输出名。
```toml
[lib]
@@ -20,17 +20,17 @@ crate-type = ["cdylib"] # 生成动态链接库
## 构建一个`C` API
-因为对于Rust编译器来说,C++没有稳定的ABI,因此我们使用`C`表示不同语言间的互用性。在C和C++代码的内部使用Rust时也不例外。
+因为对于Rust编译器来说,C++没有稳定的ABI,因此对于不同语言间的互操性我们使用`C`。在C和C++代码的内部使用Rust时也不例外。
### `#[no_mangle]`
-Rust对符号名的修饰与本机的代码链接器所期望的不同。因此,需要告知任何被Rust导出到Rust外部去使用的函数不要被编译器修饰。
+Rust对符号名的修饰与主机的代码链接器所期望的不同。因此,需要告知任何被Rust导出到Rust外部去使用的函数不要被编译器修饰。
### `extern "C"`
-默认,任何用Rust写的函数将使用Rust ABI(这也不稳定)。相反,当编译面向外部的FFI APIs时,我们需要告诉编译器去使用系统ABI 。
+默认,任何用Rust写的函数将使用Rust ABI(这也不稳定)。当编译面向外部的FFI APIs时,我们需要告诉编译器去使用系统ABI 。
-取决于你的平台,你可能需要针对一个特定的ABI版本,其记录在[这里](https://doc.rust-lang.org/reference/items/external-blocks.html)。
+取决于你的平台,你可能想要针对一个特定的ABI版本,其记录在[这里](https://doc.rust-lang.org/reference/items/external-blocks.html)。
---
@@ -43,7 +43,7 @@ pub extern "C" fn rust_function() {
}
```
-就像在你的Rust项目中使用`C`代码时那样,你现在将需要把数据转换为应用其它部分可以理解的形式。
+就像在Rust项目中使用`C`代码时那样,现在需要把数据转换为应用中其它部分可以理解的形式。
## 链接和更大的项目上下文
@@ -57,7 +57,7 @@ pub extern "C" fn rust_function() {
然而,从C调用一个Rust函数要求一个头文件去声明函数的签名。
-在你的Rust-ffi API中的每个函数需要有一个相关的头文件函数。
+在Rust-ffi API中的每个函数需要有一个相关的头文件函数。
```rust,ignore
#[no_mangle]
@@ -72,7 +72,7 @@ void rust_function();
等等。
-这里有个工具可以自动化这个过程,被叫做[cbindgen],其会分析你的Rust代码然后从它为你的C和C++项目生成头文件。
+这里有个工具可以自动化这个过程,被叫做[cbindgen],其会分析你的Rust代码然后从它为C和C++项目生成头文件。
[cbindgen]: /~https://github.com/eqrion/cbindgen
diff --git a/src/unsorted/math.md b/src/unsorted/math.md
index 63dccb75..0b9eec8d 100644
--- a/src/unsorted/math.md
+++ b/src/unsorted/math.md
@@ -1,6 +1,6 @@
# 在`#[no_std]`下执行数学运算
-如果你想要执行数学相关的函数,像是计算平方根或者一个数的指数且你有完整的标准库支持,你的代码可能看起来像这个:
+如果你想要执行数学相关的函数,像是计算平方根或者一个数的指数并有完整的标准库支持,代码可能看起来像这样:
```rs
//! 可用一些标准支持的数学函数
@@ -55,14 +55,14 @@ fn main() -> ! {
)
.unwrap();
// 退出QEMU
- // 注意不要在硬件上允许这个; 它能破坏OpenOCD的状态
+ // 注意不要在硬件上使用这个; 它能破坏OpenOCD的状态
// debug::exit(debug::EXIT_SUCCESS);
loop {}
}
```
-如果你需要在你的MCU上执行更复杂的操作,像是DSP信号处理或者更高级的线性代数,下列的crates可能可以帮到你
+如果需要在MCU上执行更复杂的操作,像是DSP信号处理或者更高级的线性代数,下列的crates可能可以帮到你
- [CMSIS DSP library binding](/~https://github.com/jacobrosenthal/cmsis-dsp-sys)
- [`micromath`](/~https://github.com/tarcieri/micromath)
diff --git a/src/unsorted/speed-vs-size.md b/src/unsorted/speed-vs-size.md
index c56c4640..899f79c2 100644
--- a/src/unsorted/speed-vs-size.md
+++ b/src/unsorted/speed-vs-size.md
@@ -1,6 +1,6 @@
# 优化: 速度与大小之间的博弈
-每个人都想要它们的程序变得超级快且超级小,但是同时满足这两个条件是不可能的。这部分讨论`rustc`提供的不同的优化等级,和它们是如何影响执行时间和一个程序的二进制项的大小。
+每个人都想要程序变得即快又小,但是同时满足这两个条件是不可能的。这部分讨论`rustc`提供的不同的优化等级,和它们是如何影响执行时间和一个程序的二进制项的大小。
## 无优化
From bf8d03fbcf01fc02d5aa55bf63a0708eaf19d9b1 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Tue, 29 Nov 2022 15:55:53 +0800
Subject: [PATCH 124/137] minor
---
src/interoperability/rust-with-c.md | 2 +-
src/start/hardware.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/interoperability/rust-with-c.md b/src/interoperability/rust-with-c.md
index c9a49c1f..a6f4b804 100644
--- a/src/interoperability/rust-with-c.md
+++ b/src/interoperability/rust-with-c.md
@@ -72,7 +72,7 @@ void rust_function();
等等。
-这里有个工具可以自动化这个过程,被叫做[cbindgen],其会分析你的Rust代码然后从它为C和C++项目生成头文件。
+这里有个工具可以自动化这个过程,叫做[cbindgen],其会分析你的Rust代码然后为C和C++项目生成头文件。
[cbindgen]: /~https://github.com/eqrion/cbindgen
diff --git a/src/start/hardware.md b/src/start/hardware.md
index 3e978040..f427818d 100644
--- a/src/start/hardware.md
+++ b/src/start/hardware.md
@@ -19,7 +19,7 @@
## 配置
-我们将使用一个新的模板实例从零开始。对于新手参考[先前的QEMU]章节,了解如何在没有`cargo-generate`的情况下完成它。
+我们将使用一个新的模板实例从零开始。对于新手,请参考[先前的QEMU]章节,了解如何在没有`cargo-generate`的情况下完成配置。
[先前的QEMU]: qemu.md
From cb1a02d7af9400182f89703d74c5fe574ef0c498 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Mon, 12 Dec 2022 15:26:42 +0800
Subject: [PATCH 125/137] minor
---
src/collections/index.md | 22 ++++++++++-----------
src/concurrency/index.md | 12 +++++------
src/design-patterns/hal/checklist.md | 10 +++++-----
src/design-patterns/hal/gpio.md | 2 +-
src/design-patterns/hal/index.md | 4 ++--
src/design-patterns/hal/interoperability.md | 8 ++++----
src/design-patterns/hal/naming.md | 4 ++--
src/design-patterns/hal/predictability.md | 6 +++---
src/portability/index.md | 6 +++---
9 files changed, 37 insertions(+), 37 deletions(-)
diff --git a/src/collections/index.md b/src/collections/index.md
index 032e75c6..b2902e2b 100644
--- a/src/collections/index.md
+++ b/src/collections/index.md
@@ -1,14 +1,14 @@
# 集合
-最后,你还希望在你的程序里使用动态数据结构(也称为集合)。`std` 提供了一组常见的集合: [`Vec`],[`String`],[`HashMap`],等等。所有这些在`std`中被实现的集合都使用一个全局动态分配器(也称为堆)。
+最后,还希望在程序里使用动态数据结构(也称为集合)。`std` 提供了一组常见的集合: [`Vec`],[`String`],[`HashMap`],等等。所有这些在`std`中被实现的集合都使用一个全局动态分配器(也称为堆)。
[`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html
[`String`]: https://doc.rust-lang.org/std/string/struct.String.html
[`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
-因为`core`的定义是没有内存分配的,所以这些实现在`core`中是没有的,但是我们可以在编译器附带的`alloc` crate中找到。
+因为`core`的定义中是没有内存分配的,所以这些实现在`core`中是没有的,但是我们可以在编译器附带的`alloc` crate中找到。
-如果你需要集合,一个堆分配的实现不是你唯一的选择。你也可以使用 *fixed capacity* 集合; 一个这样的实现可以在 [`heapless`] crate中被找到。
+如果需要集合,一个基于堆分配的实现不是唯一的选择。也可以使用 *fixed capacity* 集合; 其实现可以在 [`heapless`] crate中被找到。
[`heapless`]: https://crates.io/crates/heapless
@@ -16,7 +16,7 @@
## 使用 `alloc`
-`alloc` crate与标准的Rust发行版在一起。为了导入这个crate,你可以直接 `use` 它而不需要在你的`Cargo.toml`文件中把它声明为一个依赖。
+`alloc` crate与标准的Rust发行版在一起。你可以直接 `use` 导入这个crate,而不需要在`Cargo.toml`文件中把它声明为一个依赖。
``` rust,ignore
#![feature(alloc)]
@@ -26,7 +26,7 @@ extern crate alloc;
use alloc::vec::Vec;
```
-为了能使用集合,你首先需要使用`global_allocator`属性去声明你程序将使用的全局的分配器。它要求你选择的分配器实现了[`GlobalAlloc`] trait 。
+为了能使用集合,首先需要使用`global_allocator`属性去声明程序将使用的全局分配器。它要求选择的分配器实现了[`GlobalAlloc`] trait 。
[`GlobalAlloc`]: https://doc.rust-lang.org/core/alloc/trait.GlobalAlloc.html
@@ -87,7 +87,7 @@ static HEAP: BumpPointerAlloc = BumpPointerAlloc {
};
```
-除了选择一个全局分配器,用户也将必须定义如何使用*不稳定的*`alloc_error_handler`属性来处理内存溢出错误。
+除了选择一个全局分配器,用户也必须要定义如何使用*不稳定的*`alloc_error_handler`属性来处理内存溢出错误。
``` rust,ignore
#![feature(alloc_error_handler)]
@@ -102,7 +102,7 @@ fn on_oom(_layout: Layout) -> ! {
}
```
-一旦一切都满足了,用户最终可以在`alloc`中使用集合。
+一旦一切都完成了,用户最后就可以在`alloc`中使用集合。
```rust,ignore
#[entry]
@@ -122,7 +122,7 @@ fn main() -> ! {
## 使用 `heapless`
-`heapless`无需设置因为它的集合不依赖一个全局内存分配器。只是`use`它的集合然后实例化它们:
+`heapless`无需设置,因为它的集合不依赖一个全局内存分配器。只是`use`它的集合然后实例化它们:
```rust,ignore
extern crate heapless; // v0.4.x
@@ -145,9 +145,9 @@ fn main() -> ! {
[`typenum`]: https://crates.io/crates/typenum
-第二,`push`方法和许多其它方法返回的是一个`Result`。因为`heapless`集合有一个固定的容量,所以所有插入的操作都可能会失败。通过返回一个`Result`,API反应了这个问题,指出操作是否成功还是失败。相反,`alloc`集合自己将会在堆上重新分配去增加它的容量。
+第二,`push`方法和另外一些方法返回的是一个`Result`。因为`heapless`集合有一个固定的容量,所以所有插入的操作都可能会失败。通过返回一个`Result`,API反应了这个问题,指出操作是否成功还是失败。相反,`alloc`集合自己将会在堆上重新分配去增加它的容量。
-自v0.4.x版本起,所有的`heapless`集合将它们所有的元素内联地存储起来了。这意味着像是`let x = heapless::Vec::new()`这样的一个操作将会在栈上分配集合,但是它也能够在一个`static`变量上分配集合,或者甚至在堆上(`Box>`)。
+自v0.4.x版本起,所有的`heapless`集合将所有的元素内联地存储起来了。这意味着像是`let x = heapless::Vec::new()`这样的一个操作将会在栈上分配集合,但是它也能够在一个`static`变量上分配集合,或者甚至在堆上(`Box>`)。
## 取舍
@@ -157,7 +157,7 @@ fn main() -> ! {
使用堆分配,内存溢出总是有可能出现的且会发生在任何一个集合需要增长的地方: 比如,所有的 `alloc::Vec.push` 调用会潜在地产生一个OOM(Out of Memory)条件。因此一些操作可能会*隐式地*失败。一些`alloc`集合暴露了`try_reserve`方法,可以当增加集合时让你检查潜在的OOM条件,但是你需要主动地使用它们。
-如果你只使用`heapless`集合,而不使用内存分配器,那么一个OOM条件不可能出现。反而,你必须逐个处理容量不足的集合。也就是你必须处理*所有*的`Result`,其由像是`Vec.push`这样的方法返回的。
+如果你只使用`heapless`集合,而不使用内存分配器,那么一个OOM条件不可能出现。反而,你必须逐个处理容量不足的集合。也就是必须处理*所有*的`Result`,`Result`由像是`Vec.push`这样的方法返回的。
与在所有由`heapless::Vec.push`返回的`Result`上调用`unwrap`相比,OOM错误更难调试,因为错误被发现的位置可能与导致问题的位置*不*一致。比如,甚至如果分配器接近消耗完`vec.reserve(1)`都能触发一个OOM,因为一些其它的集合正在泄露内存(内存泄露在安全的Rust是会发生的)。
diff --git a/src/concurrency/index.md b/src/concurrency/index.md
index b986b46f..099f865b 100644
--- a/src/concurrency/index.md
+++ b/src/concurrency/index.md
@@ -1,6 +1,6 @@
# 并发
-无论何时程序有可能会在不同的时刻执行或者不按顺序执行不同的部分,那并发就出现了。在一个嵌入式环境中,这包括:
+当程序的不同部分有可能会在不同的时刻被执行或者不按顺序地被执行时,那并发就出现了。在一个嵌入式环境中,这包括:
* 中断处理函数,一旦相关的中断发生时,中断处理函数就会运行,
* 不同的多线程形式,在这块,微处理器通常会在程序的不同部分间进行切换,
@@ -10,7 +10,7 @@
## 没有并发
-对于一个嵌入式程序来说最简单的并发是没有并发: 你的软件由一个保持运行的main循环组成,一点中断也没有。有时候这非常适合手边的问题! 通常你的循环将会读取一些输入,执行一些处理,且写入一些输出。
+对于一个嵌入式程序来说最简单的并发是没有并发: 软件由一个保持运行的main循环组成,一点中断也没有。有时候这非常适合手边的问题! 通常你的循环将会读取一些输入,执行一些处理,且写入一些输出。
```rust,ignore
#[entry]
@@ -24,13 +24,13 @@ fn main() {
}
```
-因为这里没有并发,因此不需要担心程序不同部分间的共享数据或者同步对外设的访问。如果你可以使用一个简单的方法来解决问题,这种方法是个不错的选择。
+因为这里没有并发,因此不需要担心程序不同部分间的共享数据或者同步对外设的访问。如果可以使用一个简单的方法来解决问题,这种方法是个不错的选择。
## 全局可变数据
不像非嵌入式Rust,我们通常不能分配堆和将对那个数据的引用传递进一个新创造的线程中。反而,我们的中断处理函数可能在任何时间被调用,且必须知道如何访问我们正在使用的共享内存。从最底层看来,这意味着我们必须有 _静态分配的_ 可变的内存,中断处理函数和main代码都可以引用这块内存。
-在Rust中,[`static mut`]这样的变量读取或者写入总是unsafe的,因为不特别关注它们的话,你可能会触发一个竞态条件,你对变量的访问在中途就被一个也访问那个变量的中断打断了。
+在Rust中,[`static mut`]这样的变量读取或者写入总是unsafe的,因为不特别关注它们的话,可能会触发一个竞态条件,对变量的访问在中途就被一个也访问那个变量的中断打断了。
[`static mut`]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
@@ -59,11 +59,11 @@ fn timer() {
}
```
-每秒计时器中断会把计数器设置回0。这期间,main循环连续地测量信号,且当看到从低电平到高电平的变化时,增加计数器的值。因为它是`static mut`的,我们不得不使用`unsafe`去访问`COUNTER`,意思是我们向编译器保证我们的操作不会导致任何未定义的行为。你能发现竞态条件吗?`COUNTER`上的增加并不一定是原子的 - 事实上,在大多数嵌入式平台上,它将被分开成一个读取操作,然后是增加,然后是写回。如果中断在计数器被读取之后但是在被写回之前被激活,在中断返回后,重置回0的操作会被忽略 - 那期间,我们会算出两倍的转换次数。
+每秒计时器中断会把计数器设置回0。这期间,main循环连续地测量信号,且当看到从低电平到高电平的变化时,增加计数器的值。因为它是`static mut`的,我们不得不使用`unsafe`去访问`COUNTER`,意思是我们向编译器保证我们的操作不会导致任何未定义的行为。你能发现竞态条件吗?`COUNTER`上的增加并不一定是原子的 - 事实上,在大多数嵌入式平台上,它将被分开成一个读取操作,然后是增加,然后是写回。如果中断在计数器被读取之后但是在被写回之前被激活,在中断返回后,重置回0的操作会被忽略掉 - 那期间,我们会算出两倍的转换次数。
## 临界区(Critical Sections)
-因此,关于数据竞争我们能做些什么?一个简单的方法是使用 _临界区(critical sections)_ ,在临界区的上下文中中断被关闭了。通过把对`main`中的`COUNTER`访问封装进一个临界区,我们能确保计时器中断将不会激活,直到我们完成了增加`COUNTER`的操作:
+因此,关于数据竞争可以做些什么?一个简单的方法是使用 _临界区(critical sections)_ ,在临界区的上下文中中断被关闭了。通过把对`main`中的`COUNTER`访问封装进一个临界区,我们能确保计时器中断将不会激活,直到我们完成了增加`COUNTER`的操作:
```rust,ignore
static mut COUNTER: u32 = 0;
diff --git a/src/design-patterns/hal/checklist.md b/src/design-patterns/hal/checklist.md
index 316d9ac9..10b842de 100644
--- a/src/design-patterns/hal/checklist.md
+++ b/src/design-patterns/hal/checklist.md
@@ -1,14 +1,14 @@
-# HAL设计模式清单
+# HAL设计检查清单
-- **命名** *(crate符合Rust命名规则)*
+- **命名** *(crate要符合Rust命名规则)*
- [ ] crate被恰当地命名 ([C-CRATE-NAME])
-- **互用性** *(crate很好地与其它的库功能交互)*
+- **互用性** *(crate要很好地与其它的库功能交互)*
- [ ] 封装类型提供一种析构方法 ([C-FREE])
- [ ] HALs重新导出了它们的寄存器访问crate ([C-REEXPORT-PAC])
- [ ] 类型实现了 `embedded-hal` traits ([C-HAL-TRAITS])
-- **可预见性** *(crate使清晰的代码工作起来像它们看起来一样)*
+- **可预见性** *(crate的代码清晰可读,行为和看起来的一样)*
- [ ] 使用构造函数而不是扩展traits ([C-CTOR])
-- **GPIO接口** *(GPIO接口遵循一个常见的模式)*
+- **GPIO接口** *(GPIO接口要遵循一个公共的模式)*
- [ ] Pin类型默认是零大小类型 ([C-ZST-PIN])
- [ ] Pin类型提供擦除管脚和端口的方法 ([C-ERASED-PIN])
- [ ] Pin状态应该被编码为类型参数 ([C-PIN-STATE])
diff --git a/src/design-patterns/hal/gpio.md b/src/design-patterns/hal/gpio.md
index 83c3fb50..38c96c22 100644
--- a/src/design-patterns/hal/gpio.md
+++ b/src/design-patterns/hal/gpio.md
@@ -36,7 +36,7 @@ pub struct PortAPins {
## 管脚类型提供方法去擦除管脚和端口(C-ERASED-PIN)
-管脚应该提供类型擦除方法去将它们的属性从编译时移到运行时,允许在应用中有更多的灵活性。
+管脚应该提供类型擦除方法,将其属性从编译时移到运行时,允许在应用中有更多的灵活性。
案例:
diff --git a/src/design-patterns/hal/index.md b/src/design-patterns/hal/index.md
index d15d1fe0..af0a1917 100644
--- a/src/design-patterns/hal/index.md
+++ b/src/design-patterns/hal/index.md
@@ -1,10 +1,10 @@
# HAL设计模式
-这是一组关于使用Rust为微控制器写硬件抽象层的常见的和推荐的模式。当为微控制器编写HALs时,除了现有的 [Rust API 指南] 外,还打算使用这些模式。
+这是一组关于使用Rust为微控制器写硬件抽象层的常见的和推荐的模式。当为微控制器编写HALs时,除了现有的 [Rust API 指南] 外,这些模式也会被使用。
[Rust API 指南]: https://rust-lang.github.io/api-guidelines/
-[清单](checklist.md)
+[检查清单](checklist.md)
- [命名](naming.md)
- [互用性](interoperability.md)
diff --git a/src/design-patterns/hal/interoperability.md b/src/design-patterns/hal/interoperability.md
index 320b6f83..9263a278 100644
--- a/src/design-patterns/hal/interoperability.md
+++ b/src/design-patterns/hal/interoperability.md
@@ -6,9 +6,9 @@
任何由HAL提供的非`Copy`封装类型应该提供一个`free`方法,这个方法消费封装类且返回最初生成它的外设(可能是其它对象)。
-如果有必要方法应该关闭和重置外设。使用由`free`返回的原始外设调用`new`不应该由于设备的意外状态而失败,
+如果有必要,方法应该关闭和重置外设。使用由`free`返回的原始外设去调用`new`不应该由于设备的意外状态而失败,
-如果HAL类型要求构造其它的非`Copy`对象(比如 I/O 管脚),任何这样的对象应该也由`free`返回和释放。在那个案例中`free`应该返回一个元组。
+如果HAL类型要求构造其它的非`Copy`对象(比如 I/O 管脚),任何这样的对象应该也由`free`返回和释放。在这种情况下`free`应该返回一个元组。
比如:
@@ -30,9 +30,9 @@ impl Timer {
## HALs重新导出它们的寄存器访问crate(C-REEXPORT-PAC)
-HALs能被编写在[svd2rust]生成的PACs之上,或在其它纯寄存器访问的crates之上。HALs应该总是能在它们的crate root中重新导出它们所基于的寄存器访问crate
+可以在[svd2rust]生成的PACs之上,或在其它纯寄存器访问的crates之上编写HALs。HALs需要在crate root中重新导出它们所基于的寄存器访问crate
-一个PAC应该被重新导出在名字`pac`下,无论这个crate实际的名字是什么,因为HAL的名字应该已经明确了正被访问的是什么PAC 。
+一个PAC应该被重新导出在`pac`名下,无论这个crate实际的名字是什么,因为HAL的名字应该已经明确了正被访问的是什么PAC 。
[svd2rust]: /~https://github.com/rust-embedded/svd2rust
diff --git a/src/design-patterns/hal/naming.md b/src/design-patterns/hal/naming.md
index 28b20aa7..5b77b488 100644
--- a/src/design-patterns/hal/naming.md
+++ b/src/design-patterns/hal/naming.md
@@ -2,6 +2,6 @@
-## crate被恰当地命名(C-CRATE-NAME)
+## crate要被恰当地命名(C-CRATE-NAME)
-HAL crates应该被命名在它目标支持的芯片或者芯片系列之后。它们的名字应该以`-hal`结尾,为了将它们与寄存器访问crates区分开来。名字不应该包含下划线(请改用破折号)。
+HAL crates应该在目标支持的芯片或者芯片系列之后被命名。它们的名字应该以`-hal`结尾,为了将它们与PAC区分开来。名字不应该包含下划线(请改用破折号)。
diff --git a/src/design-patterns/hal/predictability.md b/src/design-patterns/hal/predictability.md
index 1b57f59a..5cfddb7e 100644
--- a/src/design-patterns/hal/predictability.md
+++ b/src/design-patterns/hal/predictability.md
@@ -6,11 +6,11 @@
所有由HAL添加功能的外设应该被封装进一个新类型,即使该功能不需要额外的字段。
-应该避免为原始外设扩展traits。
+应该避免为基本外设扩展traits。
## 方法在适当的地方用`#[inline]`修饰
Rust编译器默认不会越过crate边界执行完全内联。因为嵌入式应用对于不可预期的代码大小的增加很敏感,`#[inline]`应该如下所示用来指导编译器:
-* 所有的"小"函数应该被标记`#[inline]`。什么是"小"是主观的,但是通常所有有希望被编译成一位数的指令序列(single-digit instruction sequences)都可以被视为"小"。
-* 非常有可能把一个常量数值作为你参数的函数应该被标记为`#[inline]`。这让编译器在编译时就可以进行计算甚至是复杂的初始化逻辑,前提是函数输入是已知的。
+* 所有的"小"函数应该被标记`#[inline]`。什么是"小"是主观的,但是通常所有有可能被编译成一位数的指令序列(single-digit instruction sequences)都可以被视为"小"。
+* 非常有可能把一个常量数值作为参数的函数应该被标记为`#[inline]`。这让编译器在编译时就可以进行计算甚至是复杂的初始化逻辑,前提是函数输入是已知的。
diff --git a/src/portability/index.md b/src/portability/index.md
index 6f527465..1a928e3b 100644
--- a/src/portability/index.md
+++ b/src/portability/index.md
@@ -1,6 +1,6 @@
# 可移植性
-在嵌入式环境中,可移植性是一个非常重要的主题: 每个供应商甚至同个制造商的不同系列,都提供了不同的外设和功能。同样地,与外设交互的方式也将会不一样。
+在嵌入式环境中,可移植性是一个非常重要的主题: 每个供应商甚至同个制造商的不同系列间,都提供了不同的外设和功能。同样地,与外设交互的方式也将会不一样。
通过一个被叫做硬件抽象层或者**HAL**的层去均等化这种差异是一种常见的方法。
@@ -11,7 +11,7 @@
[Hardware Abstraction Layer]: https://en.wikipedia.org/wiki/Hardware_abstraction
-在这方面嵌入式系统有点特别,因为我们通常没有操作系统和用户可安装的软件,而是只有固件镜像,其作为一个整体被编译且伴着许多约束。因此虽然维基百科定义的传统方法可能有用,但是它不是确保可移植性最有效的方法。
+在这方面,嵌入式系统有点特别,因为通常没有操作系统和用户可安装的软件,而只有固件镜像,其作为一个整体被编译且伴着许多约束。因此虽然维基百科定义的传统方法可能有用,但是它不是确保可移植性最有效的方法。
在Rust中我们要怎么实现这个目标呢?让我们进入**embedded-hal**...
@@ -55,7 +55,7 @@ HAL implentation提供硬件和HAL traits的用户之间的接口。典型的实
### 驱动
-驱动为一个外部或者内部组件实现了一组自定义的功能,被连接到一个实现了embedded-hal traits的外设上。这种驱动的典型的例子包括多个传感器(温度计,磁力计,加速度计,光照计),显示设备(LED阵列,LCD显示屏)和执行器(电机,发送器)。
+驱动为一个外部或者内部组件实现了一组自定义的功能,被连接到一个实现了embedded-hal traits的外设上。这种驱动的典型的例子包括多种传感器(温度计,磁力计,加速度计,光照计),显示设备(LED阵列,LCD显示屏)和执行器(电机,发送器)。
必须使用实现了embedded-hal的某个`trait`的类型的实例来初始化驱动,这是通过trait bound来确保的,驱动也提供了它自己的类型实例,这个实例具有一组自定义的方法,这些方法允许与被驱动的设备交互。
From 3e171244cb6dd9aa40b8c65b54cce12e146f2a18 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Tue, 13 Dec 2022 10:18:08 +0800
Subject: [PATCH 126/137] minor
---
src/c-tips/index.md | 42 ++++++++++++++++-----------------
src/design-patterns/hal/gpio.md | 10 ++++----
src/interoperability/index.md | 2 +-
3 files changed, 27 insertions(+), 27 deletions(-)
diff --git a/src/c-tips/index.md b/src/c-tips/index.md
index d215dbc4..cb718800 100644
--- a/src/c-tips/index.md
+++ b/src/c-tips/index.md
@@ -1,6 +1,6 @@
# 给嵌入式C开发者的贴士
-这个章节收集了可能对于正在寻求开始编写Rust有经验的嵌入式C开发者有用的各种各样的贴士。它将解释你在C中可能已经用到的那些东西与Rust中有多不同。
+这个章节收集了可能对于刚开始编写Rust的,有经验的嵌入式C开发者来说,有用的各种各样的贴士。它将解释你在C中可能已经用到的那些东西与Rust中的有何不同。
## 预处理器
@@ -8,17 +8,17 @@
* 使用`#ifdef`编译时选择代码块
* 编译时的数组大小和计算
-* 用来简化常见的模式的宏(避免函数调用的开销)
+* 用来简化常见的模式的宏(避免调用函数的开销)
-在Rust中没有预处理器,所以许多用例有不同的处理方法。本章节剩下的部分,我们将介绍使用预处理器的各种替代方法。
+在Rust中没有预处理器,所以许多案例有不同的处理方法。本章节剩下的部分,我们将介绍各种替代预处理器的方法。
### 编译时的代码选择
-Rust中最接近`#ifdef ... #endif`的是[Cargo features]。这些比C预处理器更正式一点: 每个crate显式列举的所有可能的features只能是关了的或者打开了的。当你把一个crate列为依赖项时,Features被打开,且是可添加的: 如果你依赖树中的任何crate为另一个crate打开了一个feature,那么这个feature将为所有那个crate的用户而打开。
+Rust中最接近`#ifdef ... #endif`的是[Cargo features]。这些比C预处理器更正式一点: 每个crate显式列举的,所有可能的features只能是关了的或者打开了的。当你把一个crate列为依赖项时,Features被打开,且是可添加的:如果你依赖树中的任何crate为另一个crate打开了一个feature,那么这个feature将为所有使用那个crate的用户而打开。
[Cargo features]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
-比如,你可能有一个crate,其提供一个信号处理的基本类型库(library of signal processing primitives)。每个基本类型可能带来一些额外的时间去编译大量的常量,你想要躲开这些常量。你可以为你的`Cargo.toml`中每个组件声明一个Cargo feature。
+比如,你可能有一个crate,其提供一个信号处理的基本类型库(library of signal processing primitives)。每个基本类型可能带来一些额外的时间去编译大量的常量,你想要避开这些常量。你可以为你的`Cargo.toml`中每个组件声明一个Cargo feature。
```toml
[features]
@@ -26,7 +26,7 @@ FIR = []
IIR = []
```
-然后,在你的代码中,使用`#[cfg(feature="FIR")]`去控制什么东西应该被包含。
+然后,在你的代码中,使用`#[cfg(feature="FIR")]`去控制要包含什么东西。
```rust
/// 在你的顶层的lib.rs中
@@ -39,7 +39,7 @@ pub mod iir;
同样地,你可以控制,只有当某个feature _没有_ 被打开时,包含代码块,或者某些features的组合被打开或者被关闭时。
-另外,Rust提供许多你可以使用的自动配置了的条件,比如`target_arch`用来选择不同的代码所基于的架构。对于条件编译的全部细节,可以参看the Rust reference的[conditional compilation]章节。
+另外,Rust提供了许多可以使用的自动配置了的条件,比如`target_arch`用来选择不同的代码所基于的架构。对于条件编译的全部细节,可以参看the Rust reference的[conditional compilation]章节。
[conditional compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
@@ -64,13 +64,13 @@ static BUF: [u32; array_size()] = [0u32; array_size()];
### 宏
-Rust提供一个极度强大的[宏系统]。虽然C预处理器几乎直接在你的源代码之上进行操作,但是Rust宏系统可以在一个更高的级别上操作。存在两种Rust宏: _声明宏_ 和 _过程宏_ 。前者更简单也最常见; 它们看起来像是函数调用,且能扩展成一个完整的表达式,语句,项目,或者模式。过程宏更复杂但是却能让Rust更强大: 它们可以把任一条Rust语法变成一个新的Rust语法。
+Rust提供一个极度强大的[宏系统]。虽然C预处理器几乎直接在你的源代码之上进行操作,但是Rust宏系统可以在一个更高的级别上操作。存在两种Rust宏: _声明宏_ 和 _过程宏_ 。前者更简单也最常见; 它们看起来像是函数调用,且能扩展成一个完整的表达式,语句,项,或者模式。过程宏更复杂但是却能让Rust更强大: 它们可以把任一条Rust语法变成一个新的Rust语法。
[宏系统]: https://doc.rust-lang.org/book/ch19-06-macros.html
-通常,你可能想知道在那些你可能使用一个C预处理器宏的地方,能否使用一个声明宏做同样的工作。你能在你的crate中定义它们,且在你的crate中轻松使用它们或者导出给其他人用。但是请注意,因为它们必须扩展成完整的表达式,语句,项或者模式,因此C预处理器的某些用例将无法工作,比如扩展成一个变量名的一部分或者一个列表中不完整的项目集。
+通常,你可能想知道在那些使用一个C预处理器宏的地方,能否使用一个声明宏做同样的工作。你可以在crate中定义它们,且在你的crate中轻松使用它们或者导出给其他人用。但是请注意,因为它们必须扩展成完整的表达式,语句,项或者模式,因此C预处理器宏的某些用例没法用,比如可以扩展成一个变量名的一部分的宏或者可以把列表中的项扩展成不完整的集合的宏。
-和Cargo features一样,值得考虑下你是否真的需要宏。在一些例子中一个常规的函数更容易被理解,它也能被内联成和一个和宏一样的代码。`#[inline]`和`#[inline(always)]` [attributes] 能让你更深入控制这个过程,这里也要小心 - 编译器将自动地从同一个crate的合适的的地方内联函数,因此不恰当地强迫它内联函数实际可能会导致性能下降。
+和Cargo features一样,值得考虑下你是否真的需要宏。在一些例子中一个常规的函数更容易被理解,它也能被内联成和一个和宏一样的代码。`#[inline]`和`#[inline(always)]` [attributes] 能让你更深入控制这个过程,这里也要小心 - 编译器会从同一个crate的恰当的地方自动地内联函数,因此不恰当地强迫它内联函数实际可能会导致性能下降。
[attributes]: https://doc.rust-lang.org/reference/attributes.html#inline-attribute
@@ -89,13 +89,13 @@ Rust提供一个极度强大的[宏系统]。虽然C预处理器几乎直接在
* 改变Cargo的编译配置
* 添加额外的静态链接库以进行链接
-现在还不支持post-build脚本,你通常将它用于像是从编译的对象自动生生成二进制文件或者打印编译信息这类任务。
+现在还不支持post-build脚本,通常将它用于像是从编译的对象自动生生成二进制文件或者打印编译信息这类任务中。
### 交叉编译
-为你的编译系统使用Cargo也能简化交叉编译。在大多数例子里,告诉Cargo `--target thumbv6m-none-eabi`就行了,它会在`target/thumbv6m-none-eabi/debug/myapp`找到一个合适的可执行文件。
+为你的编译系统使用Cargo也能简化交叉编译。在大多数例子里,告诉Cargo `--target thumbv6m-none-eabi`就行了,可以在`target/thumbv6m-none-eabi/debug/myapp`中找到一个合适的可执行文件。
-对于那些并不是Rust原生支持的平台,你将需要自己为那个目标平台编译`libcore`。遇到这样的平台,[Xargo]可以作为Cargo的替代来使用,它可以自动地为你编译`libcore`。
+对于那些并不是Rust原生支持的平台,将需要自己为那个目标平台编译`libcore`。遇到这样的平台,[Xargo]可以作为Cargo的替代来使用,它可以自动地为你编译`libcore`。
[Xargo]: /~https://github.com/japaric/xargo
@@ -111,7 +111,7 @@ for(i=0; i
## 管脚类型提供方法去擦除管脚和端口(C-ERASED-PIN)
-管脚应该提供类型擦除方法,将其属性从编译时移到运行时,允许在应用中有更多的灵活性。
+从编译时到运行时,管脚都应该提供可以改变属性的类型擦出方法,允许在应用中有更多的灵活性。
案例:
@@ -83,9 +83,9 @@ enum Port {
## 管脚状态应该被编码成类型参数 (C-PIN-STATE)
-取决于芯片或者芯片系列,管脚可能被配置为具有不同特性的输出或者输入。这个状态应该在类型系统中被编码去避免在错误的状态中使用管脚。
+取决于芯片或者芯片系列,管脚可能被配置为具有不同特性的输出或者输入。这个状态应该编码进类型系统中以避免在错误的状态中使用管脚。
-另外,芯片特定的状态(eg. 驱动强度)可能也用这个办法被编码,使用额外的类型参数。
+另外,也可以用这个方法使用额外的类型参数编码芯片特定的状态(eg. 驱动强度)。
用来改变管脚状态的方法应该被实现成`into_input`和`into_output`方法。
@@ -110,7 +110,7 @@ enum Port {
) -> R
```
-管脚状态应该用sealed traits来绑定。HAL的用户应该不需要添加它们自己的状态。这个traits能提供HAL特定的方法,实现管脚状态API需要这些方法。
+管脚状态应该用sealed traits来绑定。HAL的用户不必添加他们自己的状态。这个traits能提供HAL特定的方法,实现管脚状态API需要这些方法。
案例:
diff --git a/src/interoperability/index.md b/src/interoperability/index.md
index 6431a155..8de43fad 100644
--- a/src/interoperability/index.md
+++ b/src/interoperability/index.md
@@ -1,6 +1,6 @@
# 互用性
-Rust和C代码间的互用性始终取决于两种语言间的数据转换。为了实现互操性,在`stdlib`中,有两个专用模块,叫做[`std::ffi`](https://doc.rust-lang.org/std/ffi/index.html)和[`std::os::raw`](https://doc.rust-lang.org/std/os/raw/index.html) 。
+Rust和C代码间的互用性始终取决于两种语言间的数据转换。为了实现互用性,在`stdlib`中,有两个专用模块,叫做[`std::ffi`](https://doc.rust-lang.org/std/ffi/index.html)和[`std::os::raw`](https://doc.rust-lang.org/std/os/raw/index.html) 。
`std::os::raw`处理底层的基本类型,这些类型可以被编译器隐式地转换,因为Rust和C之间的内存布局足够相似或相同。
From 8eb9ff4090d25edce0929c88832baf3dda90ba51 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Fri, 6 Jan 2023 10:43:24 +0800
Subject: [PATCH 127/137] minor
---
README.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index a25146c6..b2da75c0 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# The Embedded Rust Book
-> 如何使用Rust编程语言在裸板上开发固件的中文文档
+> 一本关于如何使用Rust编程语言在裸板上开发固件的中文文档
-This project is developed and maintained by the [Resources team][team].
+这个项目由 [Resources team][team] 开发和维护。
See [the issue tracker] for more details. This book is a living document, and is updated continuously.
@@ -9,7 +9,7 @@ See [the issue tracker] for more details. This book is a living document, and is
## Online Copies of this Book
-This book is located at https://docs.rust-embedded.org/book/
+This book is located at https://xxchang.github.io/book/
## License
From 7cdec7401f05d14f45d09d72a9f79b72526e7521 Mon Sep 17 00:00:00 2001
From: Narukara
Date: Fri, 13 Jan 2023 23:50:09 +0800
Subject: [PATCH 128/137] update intro
---
src/intro/index.md | 18 +++++++++---------
src/intro/install.md | 5 +++--
src/intro/install/macos.md | 8 ++++----
src/intro/install/verify.md | 2 +-
src/intro/no-std.md | 16 ++++++++--------
src/intro/tooling.md | 6 +++---
6 files changed, 28 insertions(+), 27 deletions(-)
diff --git a/src/intro/index.md b/src/intro/index.md
index f9513634..f223f6e9 100644
--- a/src/intro/index.md
+++ b/src/intro/index.md
@@ -2,7 +2,7 @@
欢迎阅读嵌入式Rust:一本关于如何在裸机(比如,微处理器)上使用Rust编程语言的入门书籍。
## 嵌入式Rust是为谁准备的
-嵌入式Rust是为了那些即想要进行嵌入式编程,又想要使用Rust语言所提供的高级概念和安全保障的人们而准备的(参见[Who Rust Is For](https://doc.rust-lang.org/book/ch00-00-introduction.html))
+嵌入式Rust是为了那些既想要进行嵌入式编程,又想要使用Rust语言所提供的高级概念和安全保障的人们而准备的(参见[Who Rust Is For](https://doc.rust-lang.org/book/ch00-00-introduction.html))
## 本书范围
这本书的目的是:
@@ -31,15 +31,15 @@
如果你不熟悉上面提到的东西或者你对这本书中提到的某个特定主题感兴趣,你也许能从这些资源中找到有用的信息。
-| Topic | Resource | Description |
+| 主题 | 资源 | 描述 |
|--------------|----------|-------------|
| Rust | [Rust Book](https://doc.rust-lang.org/book/) | 如果你还不能轻松地使用Rust,我们强烈地建议读这本书。|
-| Rust, Embedded | [Discovery Book](https://docs.rust-embedded.org/discovery/) | 如果你从来没有做过嵌入式编程,这本书可能是个更好的开始。 |
-| Rust, Embedded | [Embedded Rust Bookshelf](https://docs.rust-embedded.org) | 这里你能找到许多Rust's Embedded Working Group提供的额外资源。|
-| Rust, Embedded | [Embedonomicon](https://docs.rust-embedded.org/embedonomicon/) | 在Rust中进行嵌入式编程的细节。 |
-| Rust, Embedded | [embedded FAQ](https://docs.rust-embedded.org/faq.html) | 关于嵌入式环境下的Rust会遇到的常见问题。|
-| Interrupts | [Interrupt](https://en.wikipedia.org/wiki/Interrupt) | - |
-| Memory-mapped IO/Peripherals | [Memory-mapped I/O](https://en.wikipedia.org/wiki/Memory-mapped_I/O) | - |
+| Rust, 嵌入式 | [Discovery Book](https://docs.rust-embedded.org/discovery/) | 如果你从来没有做过嵌入式编程,这本书可能是个更好的开始。 |
+| Rust, 嵌入式 | [Embedded Rust Bookshelf](https://docs.rust-embedded.org) | 这里你能找到许多Rust's Embedded Working Group提供的额外资源。|
+| Rust, 嵌入式 | [Embedonomicon](https://docs.rust-embedded.org/embedonomicon/) | 在Rust中进行嵌入式编程的细节。 |
+| Rust, 嵌入式 | [embedded FAQ](https://docs.rust-embedded.org/faq.html) | 关于嵌入式环境下的Rust会遇到的常见问题。|
+| 中断 | [Interrupt](https://en.wikipedia.org/wiki/Interrupt) | - |
+| 存储映射的IO/外设 | [Memory-mapped I/O](https://en.wikipedia.org/wiki/Memory-mapped_I/O) | - |
| SPI, UART, RS232, USB, I2C, TTL | [Stack Exchange about SPI, UART, and other interfaces](https://electronics.stackexchange.com/questions/37814/usart-uart-rs232-usb-spi-i2c-ttl-etc-what-are-all-of-these-and-how-do-th) | - |
### 翻译
@@ -53,7 +53,7 @@
([repository](/~https://github.com/xxchang/book))
## 如何使用这本书
-这本书通常假设你是前后阅读的。之后的章节是建立在先前的章节中提到的概念之上的,先前章节可能不会深入一个主题的细节,因为在随后的章节将会再次重温这个主题。
+这本书通常假设你是按顺序阅读的。之后的章节是建立在先前的章节中提到的概念之上的,先前章节可能不会深入一个主题的细节,因为在随后的章节将会再次重温这个主题。
在大多数示例中这本书将使用[STM32F3DISCOVERY]开发板。这个板子是基于ARM Cortex-M架构的,且基本功能与大多数基于这个架构的CPUs功能相似。微处理器的外设和其它实现细节在不同的厂家之间是不同的,甚至来自同一个厂家,不同处理器系列之间也是不同的。
因此我们建议购买[STM32F3DISCOVERY]开发板来尝试这本书中的例子。
diff --git a/src/intro/install.md b/src/intro/install.md
index 2e004a83..4ba6ce6c 100644
--- a/src/intro/install.md
+++ b/src/intro/install.md
@@ -12,7 +12,7 @@ rustc 1.31.1 (b6c32da9b 2018-12-18)
```
考虑到带宽和磁盘的使用量,默认的安装只支持主机环境的编译。为了添加对ARM Cortex-M架构交叉编译的支持,从下列编译目标中选择一个。对于这本书里使用的STM32F3DISCOVERY板子,使用`thumbv7em-none-eabihf`作为目标。
-Cortex-M0, M0+, and M1 (ARMv6-M 架构):
+Cortex-M0, M0+, 和 M1 (ARMv6-M 架构):
``` console
rustup target add thumbv6m-none-eabi
```
@@ -55,7 +55,8 @@ cargo install cargo-binutils
rustup component add llvm-tools-preview
```
-WINDOWS: prerequisite C++ Build Tools for Visual Studio 2019 is installed. https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16
+WINDOWS: 需要预先安装 C++ Build Tools for Visual Studio 2019。https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16
+
### `cargo-generate`
我们随后将使用这个来从模板生成一个项目。
diff --git a/src/intro/install/macos.md b/src/intro/install/macos.md
index b3e51bcf..c5ee5dc6 100644
--- a/src/intro/install/macos.md
+++ b/src/intro/install/macos.md
@@ -1,6 +1,6 @@
# macOS
-All the tools can be install using [Homebrew]:
+所有工具都可以通过 [Homebrew] 安装:
[Homebrew]: http://brew.sh/
@@ -15,11 +15,11 @@ $ # QEMU
$ brew install qemu
```
-> **NOTE** If OpenOCD crashes you may need to install the latest version using:
+> **注意** 如果 OpenOCD 崩溃,你可能需要使用如下命令安装最新版本:
```text
$ brew install --HEAD openocd
```
-That's all! Go to the [next section].
+就这些! 去往 [下个章节].
-[next section]: verify.md
+[下个章节]: verify.md
diff --git a/src/intro/install/verify.md b/src/intro/install/verify.md
index ef812823..26b2262a 100644
--- a/src/intro/install/verify.md
+++ b/src/intro/install/verify.md
@@ -56,7 +56,7 @@ openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
如果这些命令的某条起作用了,那意味着你使用的discovery开发板是一个旧的版本。那也不成问题,但是你要记住这件事,因为随后你的配置可能有点不同。你可以移到[下个章节]了。
-如果这些命令在normal user模式下都没用,尝试下使用root模式运行它们(e.g. `sudo openocd ..`)。如果命令在root模式下起作用,需要检查下[udev rules]是否被正确地设置了。
+如果这些命令在普通用户模式下都没用,尝试下使用root模式运行它们(e.g. `sudo openocd ..`)。如果命令在root模式下起作用,需要检查下[udev rules]是否被正确地设置了。
[udev rules]: linux.md#udev-rules
diff --git a/src/intro/no-std.md b/src/intro/no-std.md
index 298c32b2..5f27c33c 100644
--- a/src/intro/no-std.md
+++ b/src/intro/no-std.md
@@ -20,15 +20,15 @@
### 概述
-| feature | no\_std | std |
+| 特性 | no\_std | std |
|-----------------------------------------------------------|--------|-----|
-| heap (dynamic memory) | * | ✓ |
-| collections (Vec, HashMap, etc) | ** | ✓ |
-| stack overflow protection | ✘ | ✓ |
-| runs init code before main | ✘ | ✓ |
-| libstd available | ✘ | ✓ |
-| libcore available | ✓ | ✓ |
-| writing firmware, kernel, or bootloader code | ✓ | ✘ |
+| 堆 (动态内存) | * | ✓ |
+| collections (Vec, HashMap, 等) | ** | ✓ |
+| 堆栈溢出保护 | ✘ | ✓ |
+| 在 main 之前运行初始化代码 | ✘ | ✓ |
+| libstd 可用 | ✘ | ✓ |
+| libcore 可用 | ✓ | ✓ |
+| 编写固件、内核或 bootloader 代码 | ✓ | ✘ |
\* 只有在你使用了 `alloc` crate 并设置了一个适合的分配器后,比如[alloc-cortex-m]后有效。
diff --git a/src/intro/tooling.md b/src/intro/tooling.md
index a402e2f3..85786641 100644
--- a/src/intro/tooling.md
+++ b/src/intro/tooling.md
@@ -6,8 +6,8 @@
- OpenOCD >=0.8. 测试的版本: v0.9.0 and v0.10.0
- 有ARM支持的GDB。强烈建议7.12或者更新的版本。测试版本: 7.10, 7.11 和 8.1
- [`cargo-generate`](/~https://github.com/ashleygwilliams/cargo-generate) 或者 `git`。这些工具都是可选的,但是跟着这本书来使用它们,会更容易。
--
-- 下面的文档将解释我们为什么使用这些工具。安装指令可以在下一页找到。
+
+下面的文档将解释我们为什么使用这些工具。安装指令可以在下一页找到。
## `cargo-generate` 或者 `git`
裸机编程是非标准Rust编程,为了得到正确的程序的内存布局,需要对链接过程进行一些调整,这要求添加一些额外的文件(比如linker scripts)和配置(比如linker flags)。我们已经为你把这些打包进了一个模板里了,你只需要补充缺失的信息(比如项目名和目标硬件的特性)。
@@ -15,7 +15,7 @@
## `cargo-binutils`
`cargo-binutils`是一个Cargo命令的子集,它让我们能轻松使用Rust工具链带来的LLVM工具。这些工具包括LLVM版本的`objdump`,`nm`和`size`,用来查看二进制文件。
-在GNU binutils之上使用这些工具的好处是,(a)无论你的操作系统是什么,安装这些LLVM工都可以用同一条命令(`rustup component add llvm-tools-preview`)。(b)像是`objdump`这样的工具,支持所有`rustc`支持的架构--从ARM到x86_64--因为它们都有一样的LLVM后端。
+在GNU binutils之上使用这些工具的好处是,(a)无论你的操作系统是什么,安装这些LLVM工具都可以用同一条命令(`rustup component add llvm-tools-preview`)。(b)像是`objdump`这样的工具,支持所有`rustc`支持的架构--从ARM到x86_64--因为它们都有一样的LLVM后端。
## `qemu-system-arm`
From 0c2d76bc7e60d384c97d17913040a944ee813eae Mon Sep 17 00:00:00 2001
From: Narukara
Date: Sat, 14 Jan 2023 22:47:47 +0800
Subject: [PATCH 129/137] modified start
---
src/start/interrupts.md | 2 +-
src/start/panicking.md | 2 +-
src/start/qemu.md | 14 +++++++-------
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/src/start/interrupts.md b/src/start/interrupts.md
index b10b66e8..2cd047d7 100644
--- a/src/start/interrupts.md
+++ b/src/start/interrupts.md
@@ -8,7 +8,7 @@
* 通常需要清除掉导致中断被触发的原因,避免无限地再次进入中断处理函数。
运行时的初始化步骤总是相同的:
-* 设置外设在遇到想要的事故发生的时候产生中断请求
+* 设置外设在想要的事件发生时产生中断请求
* 在中断控制器中设置需要的中断处理函数的优先级
* 在中断控制器中使能中断处理函数
diff --git a/src/start/panicking.md b/src/start/panicking.md
index d2decf60..2f11f74c 100644
--- a/src/start/panicking.md
+++ b/src/start/panicking.md
@@ -1,6 +1,6 @@
# 运行时恐慌(Panicking)
-运行时恐慌是Rust语言的一个核心部分。像是索引这样的內建的操作为了存储安全性是运行时检查的。当尝试越界索引时,这会导致运行时恐慌(panic)。
+运行时恐慌是Rust语言的一个核心部分。像是索引这样的内建的操作为了存储安全性是运行时检查的。当尝试越界索引时,这会导致运行时恐慌(panic)。
在标准库中,运行时恐慌的行为被定义成:展开(unwinds)恐慌的线程的栈,除非用户选择在恐慌时终止程序。
diff --git a/src/start/qemu.md b/src/start/qemu.md
index 1c3b08c5..209c853a 100644
--- a/src/start/qemu.md
+++ b/src/start/qemu.md
@@ -96,7 +96,7 @@ fn main() -> ! {
`#![no_std]`指出这个程序将 *不会* 链接标准crate`std`。反而它将会链接到它的子集: `core` crate。
-`#![no_main]`指出这个程序将不会使用标准的且被大多数Rust程序使用的`main`接口。使用`no_main`的主要理由是,因为在`no_std`上下文中使用`main`接口要求开发版的rust 。
+`#![no_main]`指出这个程序将不会使用标准的且被大多数Rust程序使用的`main`接口。使用`no_main`的主要理由是,在`no_std`上下文中使用`main`接口需要 nightly 版的 Rust。
`use panic_halt as _;`。这个crate提供了一个`panic_handler`,它定义了程序陷入`panic`时的行为。我们将会在这本书的[运行时恐慌(Panicking)](panicking.md)章节中覆盖更多的细节。
@@ -105,7 +105,7 @@ fn main() -> ! {
[entry]: https://docs.rs/cortex-m-rt-macros/latest/cortex_m_rt_macros/attr.entry.html
[`cortex-m-rt`]: https://crates.io/crates/cortex-m-rt
-`fn main() -> !`。我们的程序将会是运行在目标板子上的 *唯一* 的进程,因此我们不想要它结束!我们使用一个[divergent function](https://doc.rust-lang.org/rust-by-example/fn/diverging.html) (函数签名中的 `-> !` )来确保在编译时就是这么回事儿。
+`fn main() -> !`。我们的程序将会是运行在目标板子上的 *唯一* 的进程,因此我们不想要它结束!我们使用一个[发散函数](https://doc.rust-lang.org/rust-by-example/fn/diverging.html) (函数签名中的 `-> !` )来确保在编译时就是这么回事儿。
## 交叉编译
@@ -168,7 +168,7 @@ ELF Header:
Section header string table index: 18
```
-`cargo-size` 能打印二进制项的linker部分的大小。
+`cargo-size` 能打印二进制项的linker section的大小。
```console
cargo size --bin app --release -- -A
@@ -198,14 +198,14 @@ section size addr
Total 14570
```
-> ELF linker sections的新手
+> ELF linker sections的复习
>
> - `.text` 包含程序指令
> - `.rodata` 包含像是字符串这样的常量
> - `.data` 包含静态分配的初始值*非*零的变量
> - `.bss` 也包含静态分配的初始值*是*零的变量
> - `.vector_table` 是一个我们用来存储向量(中断)表的*非*标准的section
-> - `.ARM.attributes` 和 `.debug_*` sections包含元数据,当烧录二进制文件时,它们不会被加载到目标上的。
+> - `.ARM.attributes` 和 `.debug_*` sections包含元数据,当烧录二进制文件时,它们不会被加载到目标上。
**重要**: ELF文件包含像是调试信息这样的元数据,因此它们在*硬盘上的尺寸*没有正确地反应处程序被烧录到设备上时将占据的空间的大小。要*一直*使用`cargo-size`检查一个二进制项的大小。
@@ -230,7 +230,7 @@ main:
Reset:
406: bl #0x24e
40a: movw r0, #0x0
- < .. truncated any more instructions .. >
+ < .. 截断了更多的指令 .. >
DefaultHandler_:
656: b #-0x4
@@ -324,7 +324,7 @@ echo $?
让我们看看QEMU命令:
-+ `qemu-system-arm`。这是QEMU仿真器。这些QEMU有一些改良版的二进制项;这个仿真器能做ARM机器的全系统仿真。
++ `qemu-system-arm`。这是QEMU仿真器。这些QEMU二进制项有一些变体,这个仿真器能做ARM机器的全系统仿真。
+ `-cpu cortex-m3`。这告诉QEMU去仿真一个Cortex-M3 CPU。指定CPU模型会让我们捕捉到一些误编译错误:比如,运行一个为Cortex-M4F编译的程序,它具有一个硬件FPU,在执行时将会使QEMU报错。
+ `-machine lm3s6965evb`。这告诉QEMU去仿真 LM3S6965EVB,一个包含LM3S6965微控制器的评估板。
From 58d1d03828d892c353a2d0901efa73de2139625f Mon Sep 17 00:00:00 2001
From: Narukara
Date: Tue, 17 Jan 2023 20:08:44 +0800
Subject: [PATCH 130/137] modified concurrency & design-contracts
---
src/concurrency/index.md | 10 +++++-----
src/static-guarantees/design-contracts.md | 12 ++++++------
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/concurrency/index.md b/src/concurrency/index.md
index 099f865b..f450a0cd 100644
--- a/src/concurrency/index.md
+++ b/src/concurrency/index.md
@@ -28,7 +28,7 @@ fn main() {
## 全局可变数据
-不像非嵌入式Rust,我们通常不能分配堆和将对那个数据的引用传递进一个新创造的线程中。反而,我们的中断处理函数可能在任何时间被调用,且必须知道如何访问我们正在使用的共享内存。从最底层看来,这意味着我们必须有 _静态分配的_ 可变的内存,中断处理函数和main代码都可以引用这块内存。
+不像非嵌入式Rust,我们通常不会奢侈地在堆上分配数据,并将对该数据的引用传递到新创建的线程中。相反,我们的中断处理函数随时可能被调用,且必须知道如何访问我们正在使用的共享内存。从最底层看来,这意味着我们必须有 _静态分配的_ 可变的内存,中断处理函数和main代码都可以引用这块内存。
在Rust中,[`static mut`]这样的变量读取或者写入总是unsafe的,因为不特别关注它们的话,可能会触发一个竞态条件,对变量的访问在中途就被一个也访问那个变量的中断打断了。
@@ -101,11 +101,11 @@ fn timer() {
这解决了我们眼前的问题,但是我们仍然要编写许多unsafe的代码,我们需要仔细推敲这些代码,有些我们可能不需要使用临界区。因为每个临界区暂时暂停了中断处理,就会带来一些相关的成本,一些额外的代码大小,更高的中断延迟和抖动(中断可能花费很长时间去处理,等待被处理的时间变化非常大)。这是否是个问题取决于你的系统,但是通常,我们想要避免它。
-值得注意的是,虽然一个临界区保障了不会发生中断,但是它在多核系统上不提供一个排他性保证(exclusivity guarantee)!其它核可能很开心访问与你的核一样的内存区域,即使没有中断。如果你正在使用多核,你将需要更强的同步原语(synchronisation primitives)。
+值得注意的是,虽然一个临界区保障了不会发生中断,但是它在多核系统上不提供一个排他性保证(exclusivity guarantee)!其它核可能很开心地访问与你的核一样的内存区域,即使没有中断。如果你正在使用多核,你将需要更强的同步原语(synchronisation primitives)。
## 原子访问
-在一些平台上,可以使用特定的原子指令,它保障了读取-修改-写回操作。针对Cortex-M: `thumbv6`(Cortex-M0,Cortex-M0+)只提供原子读取和存取指令,而`thumv7`(Cortex-M3和其上)提供完整的比较和交换(CAS)指令。这些CAS指令可以替代过重的禁用所有中断的方法: 我们可以尝试执行加法操作,它在大多数情况下都会成功,但是如果它被中断了它将会自动重试完整的加法操作。这些原子操作甚至在多核间也是安全的。
+在一些平台上,可以使用特定的原子指令,它保障了读取-修改-写回操作。针对Cortex-M: `thumbv6`(Cortex-M0,Cortex-M0+)只提供原子读取和存取指令,而`thumv7`(Cortex-M3及以上)提供完整的比较和交换(CAS)指令。这些CAS指令可以替代过重的禁用所有中断的方法: 我们可以尝试执行加法操作,它在大多数情况下都会成功,但是如果它被中断了它将会自动重试完整的加法操作。这些原子操作甚至在多核间也是安全的。
```rust,ignore
use core::sync::atomic::{AtomicUsize, Ordering};
@@ -119,7 +119,7 @@ fn main() -> ! {
loop {
let state = read_signal_level();
if state && !last_state {
- // Use `fetch_add` to atomically add 1 to COUNTER
+ // 使用 `fetch_add` 原子性地给 COUNTER 加一
COUNTER.fetch_add(1, Ordering::Relaxed);
}
last_state = state;
@@ -128,7 +128,7 @@ fn main() -> ! {
#[interrupt]
fn timer() {
- // Use `store` to write 0 directly to COUNTER
+ // 使用 `store` 将 0 直接写入 COUNTER
COUNTER.store(0, Ordering::Relaxed)
}
```
diff --git a/src/static-guarantees/design-contracts.md b/src/static-guarantees/design-contracts.md
index 1438e52e..61a87697 100644
--- a/src/static-guarantees/design-contracts.md
+++ b/src/static-guarantees/design-contracts.md
@@ -9,13 +9,13 @@
| | | 1 | 使能 | 使能GPIO |
| 方向 | 1 | 0 | 输入 | 方向设置成输入 |
| | | 1 | 输出 | 方向设置成输出 |
-| 输入模式 | 2..3 | 00 | hi-z | 输入设置为高阻态 |
+| 输入模式 | 2..3 | 00 | 高阻态 | 输入设置为高阻态 |
| | | 01 | 下拉 | 下拉输入管脚 |
| | | 10 | 上拉 | 上拉输入管脚 |
| | | 11 | n/a | 无效状态。不要设置 |
| 输出模式 | 4 | 0 | 拉低 | 把管脚设置成低电平 |
| | | 1 | 拉高 | 把管脚设置成高电平 |
-| 输入状态 | 5 | x | in-val | 如果输入 < 1.5v 为0,如果输入 >= 1.5v 为1 |
+| 输入状态 | 5 | x | 输入电平 | 如果输入 < 1.5v 为0,如果输入 >= 1.5v 为1 |
如果在使用底层硬件之前检查状态,在运行时强制遵守设计约定,代码可能像这一样:
@@ -173,7 +173,7 @@ impl GpioConfig {
}
}
-/// 这些方法可能被用于使能一个输入GPIO
+/// 这些方法可能被用于任意一个使能的输入GPIO
impl GpioConfig {
pub fn bit_is_set(&self) -> bool {
self.periph.read().input_status.bit_is_set()
@@ -215,14 +215,14 @@ impl GpioConfig {
```rust,ignore
/*
- * 案例 1: Unconfigured to High-Z input
+ * 案例 1: 从未配置到高阻输入
*/
let pin: GpioConfig = get_gpio();
// 不能这么做,pin没有被使能
// pin.into_input_pull_down();
-// 现在从unconfigured to a high-z input打开管脚
+// 现在把管脚从未配置变为高阻输入
let input_pin = pin.into_enabled_input();
// 从管脚读取
@@ -232,7 +232,7 @@ let pin_state = input_pin.bit_is_set();
// input_pin.set_bit(true);
/*
- * 案例 2: High-Z 输入到下拉输入
+ * 案例 2: 高阻输入到下拉输入
*/
let pulled_low = input_pin.into_input_pull_down();
let pin_state = pulled_low.bit_is_set();
From 1cd5be83ce5de3dae473679ce1de47f61df2d450 Mon Sep 17 00:00:00 2001
From: AgainstWar
Date: Wed, 18 Jan 2023 23:48:34 +0800
Subject: [PATCH 131/137] =?UTF-8?q?Windows=20=E5=AE=89=E8=A3=85=E9=83=A8?=
=?UTF-8?q?=E5=88=86=E6=B1=89=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/intro/install/windows.md | 26 +++++++++++---------------
1 file changed, 11 insertions(+), 15 deletions(-)
diff --git a/src/intro/install/windows.md b/src/intro/install/windows.md
index e0074035..86d60474 100644
--- a/src/intro/install/windows.md
+++ b/src/intro/install/windows.md
@@ -2,9 +2,9 @@
## `arm-none-eabi-gdb`
-ARM provides `.exe` installers for Windows. Grab one from [here][gcc], and follow the instructions.
-Just before the installation process finishes tick/select the "Add path to environment variable"
-option. Then verify that the tools are in your `%PATH%`:
+ARM提供了用于Windows的`.exe`安装程序。从[这里][gcc]获取, 然后按照说明操作。
+在完成安装之前,勾选/选择"Add path to environment variable"选项。
+然后验证环境变量是否添加到 `%PATH%`中:
``` text
$ arm-none-eabi-gdb -v
@@ -16,15 +16,12 @@ GNU gdb (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 8.1.0.20180315-
## OpenOCD
-There's no official binary release of OpenOCD for Windows but if you're not in the mood to compile
-it yourself, the xPack project provides a binary distribution, [here][openocd]. Follow the
-provided installation instructions. Then update your `%PATH%` environment variable to
-include the path where the binaries were installed. (`C:\Users\USERNAME\AppData\Roaming\xPacks\@xpack-dev-tools\openocd\0.10.0-13.1\.content\bin\`,
-if you've been using the easy install)
+OpenOCD 官方没有提供Windows的二进制版本, 若你没有心情去折腾编译,[这里][openocd]有xPack提供的一个二进制发布.。按照说明进行安装。然后更新你的`%PATH%` 环境变量,将安装目录包括进去。 (`C:\Users\USERNAME\AppData\Roaming\xPacks\@xpack-dev-tools\openocd\0.10.0-13.1\.content\bin\`,
+如果使用简易安装)
[openocd]: https://xpack.github.io/openocd/
-Verify that OpenOCD is in your `%PATH%` with:
+使用以下命令验证OpenOCD是否在你的`%PATH%`环境变量中 :
``` text
$ openocd -v
@@ -34,17 +31,16 @@ Open On-Chip Debugger 0.10.0
## QEMU
-Grab QEMU from [the official website][qemu].
+从[官网][qemu]获取QEMU。
[qemu]: https://www.qemu.org/download/#windows
## ST-LINK USB driver
-You'll also need to install [this USB driver] or OpenOCD won't work. Follow the installer
-instructions and make sure you install the right version (32-bit or 64-bit) of the driver.
+你还需要安装这个 [USB驱动] 否则OpenOCD将无法工作。按照安装程序的说明,确保你安装了正确版本(32位或64位)的驱动程序。
-[this USB driver]: http://www.st.com/en/embedded-software/stsw-link009.html
+[USB驱动]: http://www.st.com/en/embedded-software/stsw-link009.html
-That's all! Go to the [next section].
+以上是全部内容!转到 [下一节]。
-[next section]: verify.md
+[下一节]: verify.md
From 16792ae1cd937c1e6879149b0674387ef4fdd70f Mon Sep 17 00:00:00 2001
From: AgainstWar
Date: Thu, 19 Jan 2023 10:56:11 +0800
Subject: [PATCH 132/137] =?UTF-8?q?MacOS=20=E5=AE=89=E8=A3=85=E9=83=A8?=
=?UTF-8?q?=E5=88=86=E6=B1=89=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/intro/install/macos.md | 8 ++++----
src/intro/install/windows.md | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/intro/install/macos.md b/src/intro/install/macos.md
index b3e51bcf..d3bf20a3 100644
--- a/src/intro/install/macos.md
+++ b/src/intro/install/macos.md
@@ -1,6 +1,6 @@
# macOS
-All the tools can be install using [Homebrew]:
+所有的工具都可以用[Homebrew]来安装:
[Homebrew]: http://brew.sh/
@@ -15,11 +15,11 @@ $ # QEMU
$ brew install qemu
```
-> **NOTE** If OpenOCD crashes you may need to install the latest version using:
+> **注意** 如果OpenOCD崩溃了,你可能需要用以下方法安装最新版本:
```text
$ brew install --HEAD openocd
```
-That's all! Go to the [next section].
+以上是全部内容!转到 [下个章节]。
-[next section]: verify.md
+[下个章节]: verify.md
diff --git a/src/intro/install/windows.md b/src/intro/install/windows.md
index 86d60474..c1264395 100644
--- a/src/intro/install/windows.md
+++ b/src/intro/install/windows.md
@@ -41,6 +41,6 @@ Open On-Chip Debugger 0.10.0
[USB驱动]: http://www.st.com/en/embedded-software/stsw-link009.html
-以上是全部内容!转到 [下一节]。
+以上是全部内容!转到 [下个章节]。
-[下一节]: verify.md
+[下个章节]: verify.md
From 6e7b145c67056da1111ab919fb4d5cc19d97c4c3 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Fri, 27 Jan 2023 01:16:08 +0800
Subject: [PATCH 133/137] fix typo
---
src/start/panicking.md | 2 +-
src/static-guarantees/design-contracts.md | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/start/panicking.md b/src/start/panicking.md
index d2decf60..05dcd09e 100644
--- a/src/start/panicking.md
+++ b/src/start/panicking.md
@@ -2,7 +2,7 @@
运行时恐慌是Rust语言的一个核心部分。像是索引这样的內建的操作为了存储安全性是运行时检查的。当尝试越界索引时,这会导致运行时恐慌(panic)。
-在标准库中,运行时恐慌的行为被定义成:展开(unwinds)恐慌的线程的栈,除非用户选择在恐慌时终止程序。
+在标准库中,运行时恐慌的行为被定义成:展开(unwinds)恐慌的线程的栈,除非用户自己选择在恐慌时终止程序。
然而在没有标准库的程序中,运行时恐慌的行为是未被定义了的。通过声明一个 `#[painc_handler]` 函数可以选择一个运行时恐慌的行为。
diff --git a/src/static-guarantees/design-contracts.md b/src/static-guarantees/design-contracts.md
index 1438e52e..58c84e7c 100644
--- a/src/static-guarantees/design-contracts.md
+++ b/src/static-guarantees/design-contracts.md
@@ -18,7 +18,7 @@
| 输入状态 | 5 | x | in-val | 如果输入 < 1.5v 为0,如果输入 >= 1.5v 为1 |
-如果在使用底层硬件之前检查状态,在运行时强制遵守设计约定,代码可能像这一样:
+如果在使用底层硬件之前检查硬件的状态,在运行时强制用户遵守设计约定,代码可能像这一样:
```rust,ignore
/// GPIO接口
@@ -103,7 +103,7 @@ impl GpioConfig {
## 类型状态(Type states)
-但是,如果我们使用Rust的类型系统去强制遵守状态转换的规则会怎样?看下这个例子:
+但是,如果我们让Rust的类型系统去强制遵守状态转换的规则会怎样?看下这个例子:
```rust,ignore
/// GPIO接口
@@ -253,4 +253,4 @@ output_pin.set_bit(true);
因为我们在编译时完全强制遵守设计约定,这造成了没有运行时开销。当管脚处于输入模式时时,是不可能设置输出模式的。必须先把它设置成一个输出管脚,然后再设置输出模式。因为在执行一个函数前会检查现在的状态,因此没有运行时消耗。
-也因为这些状态被类型系统强制遵守,因此没有为这个接口的使用者留太多的犯错余地。如果它们尝试执行一个非法的状态转换,代码将不会编译!
+也因为这些状态被类型系统强制遵守,因此没有为这个接口的使用者留太多的犯错余地。如果它们尝试执行一个非法的状态转换,代码将不会编译成功!
From 0d8c55c49403d5eb75376398e5f813b0e0dbaf49 Mon Sep 17 00:00:00 2001
From: Narukara
Date: Fri, 10 Feb 2023 15:26:29 +0800
Subject: [PATCH 134/137] add language = "zh-CN"
---
book.toml | 1 +
1 file changed, 1 insertion(+)
diff --git a/book.toml b/book.toml
index 4ed695de..37e67e54 100644
--- a/book.toml
+++ b/book.toml
@@ -1,5 +1,6 @@
[book]
authors = ["James Munns"]
+language = "zh-CN"
multilingual = false
src = "src"
title = "The Embedded Rust Book"
From 28b868d27226f28814792826c083017b7a3bb5f3 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Thu, 31 Aug 2023 21:59:45 +0800
Subject: [PATCH 135/137] minor
---
src/design-patterns/hal/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/design-patterns/hal/index.md b/src/design-patterns/hal/index.md
index af0a1917..eded1f29 100644
--- a/src/design-patterns/hal/index.md
+++ b/src/design-patterns/hal/index.md
@@ -1,6 +1,6 @@
# HAL设计模式
-这是一组关于使用Rust为微控制器写硬件抽象层的常见的和推荐的模式。当为微控制器编写HALs时,除了现有的 [Rust API 指南] 外,这些模式也会被使用。
+这是一组关于使用Rust为微控制器写硬件抽象层的常见的和推荐的模式。当为微控制器编写HALs时,除了现有的 [Rust API 指南] 外,也可以使用这些模式。
[Rust API 指南]: https://rust-lang.github.io/api-guidelines/
From 8dc5568ff48bed35c90175bda1ad4294c2dbb4d6 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Thu, 31 Aug 2023 22:10:04 +0800
Subject: [PATCH 136/137] minor
---
src/intro/no-std.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/intro/no-std.md b/src/intro/no-std.md
index 5f27c33c..dd403fb2 100644
--- a/src/intro/no-std.md
+++ b/src/intro/no-std.md
@@ -26,13 +26,13 @@
| collections (Vec, HashMap, 等) | ** | ✓ |
| 堆栈溢出保护 | ✘ | ✓ |
| 在 main 之前运行初始化代码 | ✘ | ✓ |
-| libstd 可用 | ✘ | ✓ |
-| libcore 可用 | ✓ | ✓ |
+| 能用 libstd | ✘ | ✓ |
+| 能用 libcore | ✓ | ✓ |
| 编写固件、内核或 bootloader 代码 | ✓ | ✘ |
-\* 只有在你使用了 `alloc` crate 并设置了一个适合的分配器后,比如[alloc-cortex-m]后有效。
+\* 只有在你使用了 `alloc` crate 并设置了一个适合的分配器后,比如[alloc-cortex-m]后可用。
-\** 只有在你使用了 `collections` crate 并配置了一个全局默认的分配器后有效。
+\** 只有在你使用了 `collections` crate 并配置了一个全局默认的分配器后可用。
[alloc-cortex-m]: /~https://github.com/rust-embedded/alloc-cortex-m
From d7ec79385c7bb84e1095030d045d19af6bfc9b57 Mon Sep 17 00:00:00 2001
From: XxChang
Date: Sun, 10 Sep 2023 00:11:59 +0800
Subject: [PATCH 137/137] minor
---
src/interoperability/c-with-rust.md | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/interoperability/c-with-rust.md b/src/interoperability/c-with-rust.md
index ec204419..ae965edf 100644
--- a/src/interoperability/c-with-rust.md
+++ b/src/interoperability/c-with-rust.md
@@ -1,21 +1,21 @@
# 使用C的Rust
-在一个Rust项目中使用C或者C++,由两个主要部分组成:
+要在一个Rust项目中使用C或者C++,主要有两个步骤:
-+ 使用Rust封装要暴露的C API来用
-+ 编译要和Rust代码集成的C或者C++代码
++ 用Rust封装要暴露出来使用的C API
++ 编译要和Rust代码集成在一起的C或者C++代码
-因为对于目标的Rust编译器,C++没有一个稳定的ABI,当将Rust和C或者C++结合时,建议使用`C`。
+因为对于Rust编译器来说,C++没有一个稳定的ABI,当要将Rust和C或者C++结合时,建议优先选择`C`。
## 定义接口
-从Rust消费C或者C++代码之前,必须定义(在Rust中)在被链接的代码中存在什么数据类型和函数签名。在C或者C++中,你要包括一个头文件(`.h`或者`.hpp`),其定义了这个数据。在Rsut中,必须手动地将这些定义翻译成Rust,或者使用一个工具去生成这些定义。
+在Rust消费C或者C++代码之前,必须定义(在Rust中定义),在要被链接的代码中存在什么数据类型和函数签名。在C或者C++中,你要包含一个头文件(`.h`或者`.hpp`),其定义了这个数据。而在Rust中,必须手动地将这些定义翻译成Rust,或者使用一个工具去生成这些定义。
-首先,我们将介绍如何将这些定义从C/C++手动转换为Rust。
+首先,我们将介绍如何将这些定义从C/C++手动地转换为Rust。
### 封装C函数和数据类型
-通常,用C或者C++写的库会提供一个头文件,头文件定义了所有的类型和用于公共接口的函数。一个示例文件可能如下所示:
+通常,用C或者C++写的库会提供一个头文件,头文件定义了所有的类型和用于公共接口的函数。如下是一个示例文件:
```C
/* 文件: cool.h */
@@ -53,7 +53,7 @@ extern "C" {
pub struct CoolStruct { ... }
```
-默认,Rust不会保证包含在一个`struct`中的数据的大小,填充,或者顺序。为了保证与C代码兼容,我们使用`#[repr(C)]`属性,它指示Rust编译器总是使用和C一样的规则去组织一个结构体中的数据。
+默认,Rust不会保证包含在`struct`中的数据的大小,填充,或者顺序。为了保证与C代码兼容,我们使用`#[repr(C)]`属性,它指示Rust编译器总是使用和C一样的规则去组织一个结构体中的数据。
```rust,ignore
pub x: cty::c_int,
@@ -66,7 +66,7 @@ pub y: cty::c_int,
extern "C" { pub fn cool_function( ... ); }
```
-这个语句定义了一个使用C ABI的函数的签名,被叫做`cool_function`。通过定义签名而不定义函数的主体,这个函数的定义将需要在其它地方定义,或者从一个静态库链接进最终的库或者一个二进制文件中。
+这个语句定义了一个使用C ABI的函数的签名,叫做`cool_function`。因为只定义了签名而没有定义函数的主体,所以这个函数的定义将需要在其它地方定义,或者从一个静态库链接进最终的库或者一个二进制文件中。
```rust,ignore
i: cty::c_int,
@@ -76,13 +76,13 @@ extern "C" { pub fn cool_function( ... ); }
与我们上面的数据类型一样,我们使用C兼容的定义去定义函数参数的数据类型。为了清晰可见,我们还保留了相同的参数名。
-这里我们有个新类型,`*mut CoolStruct` 。因为C没有Rust中的引用的概念,其看起来像是这个: `&mut CoolStruct`,所以我们使用一个裸指针。因为解引用这个指针是`unsafe`的,且实际上指针可能是一个`null`指针,因此当与C或者C++代码交互时必须要小心对待那些Rust做出的安全保证。
+这里我们有了一个新类型,`*mut CoolStruct` 。因为C没有Rust中像 `&mut CoolStruct` 这样的引用,替代的是一个裸指针。所以解引用这个指针是`unsafe`的,因为这个指针实际上可能是一个`null`指针,因此当与C或者C++代码交互时必须要小心对待那些Rust做出的安全保证。
### 自动产生接口
-有一个叫做[bindgen]的工具,它可以自动执行这些转换,而不用手动生成这些接口那么繁琐且容易出错。关于[bindgen]使用的指令,请参考[bindgen user's manual],然而经典的过程有下面几步:
+有一个叫做[bindgen]的工具,它可以自动执行这些转换,而不用手动生成这些接口,手动进行这样的操作非常繁琐且容易出错。关于[bindgen]的使用指令,可以参考[bindgen user's manual],常用的步骤大致如下:
-1. 收集所有定义了你可能在Rust中用到的数据类型或者接口的C或者C++头文件。
+1. 收集所有定义了你可能在Rust中会用到的数据类型或者接口的C或者C++头文件。
2. 写一个`bindings.h`文件,其`#include "..."`每一个你在步骤一中收集的文件。
3. 将这个`bindings.h`文件和任何用来编译你代码的编译标识发给`bindgen`。贴士: 使用`Builder.ctypes_prefix("cty")` / `--ctypes-prefix=cty` 和 `Builder.use_core()` / `--use-core` 去使生成的代码兼容`#![no_std]`
4. `bindgen`将会在终端窗口输出生成的Rust代码。这个文件可能会被通过管道发送给你项目中的一个文件,比如`bindings.rs` 。你可能要在你的Rust项目中使用这个文件来与被编译和链接成一个外部库的C/C++代码交互。贴士: 如果你的类型在生成的绑定中被前缀了`cty`,不要忘记使用[`cty`](https://crates.io/crates/cty) crate 。