diff --git a/README.md b/README.md
index 2ed54e3b..b2da75c0 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,7 @@
# The Embedded Rust Book
+> 一本关于如何使用Rust编程语言在裸板上开发固件的中文文档
-> Documentation on how to use the Rust Programming Language to develop firmware for bare metal (microcontroller) devices
-
-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.
@@ -10,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
diff --git a/book.toml b/book.toml
index fa5b7e3a..37e67e54 100644
--- a/book.toml
+++ b/book.toml
@@ -1,8 +1,10 @@
[book]
authors = ["James Munns"]
+language = "zh-CN"
multilingual = false
src = "src"
title = "The Embedded Rust Book"
[output.html]
-git-repository-url = "/~https://github.com/rust-embedded/book"
+# git-repository-url = "/~https://github.com/rust-embedded/book"
+git-repository-url = "/~https://github.com/xxchang/book"
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 00000000..c4192631
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 00000000..38ececf0
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,37 @@
+## Welcome to GitHub Pages
+
+You can use the [editor on GitHub](/~https://github.com/XxChang/book/edit/master/docs/index.md) to maintain and preview the content for your website in Markdown files.
+
+Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files.
+
+### Markdown
+
+Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for
+
+```markdown
+Syntax highlighted code block
+
+# Header 1
+## Header 2
+### Header 3
+
+- Bulleted
+- List
+
+1. Numbered
+2. List
+
+**Bold** and _Italic_ and `Code` text
+
+[Link](url) and ![Image](src)
+```
+
+For more details see [Basic writing and formatting syntax](https://docs.github.com/en/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax).
+
+### Jekyll Themes
+
+Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](/~https://github.com/XxChang/book/settings/pages). The name of this theme is saved in the Jekyll `_config.yml` configuration file.
+
+### Support or Contact
+
+Having trouble with Pages? Check out our [documentation](https://docs.github.com/categories/github-pages-basics/) or [contact support](https://support.github.com/contact) and we’ll help you sort it out.
diff --git a/src/SUMMARY.md b/src/SUMMARY.md
index f62fa633..13b05abe 100644
--- a/src/SUMMARY.md
+++ b/src/SUMMARY.md
@@ -9,52 +9,52 @@ more information and coordination
-->
-- [Introduction](./intro/index.md)
- - [Hardware](./intro/hardware.md)
+- [引言](./intro/index.md)
+ - [硬件](./intro/hardware.md)
- [`no_std`](./intro/no-std.md)
- - [Tooling](./intro/tooling.md)
- - [Installation](./intro/install.md)
+ - [工具](./intro/tooling.md)
+ - [安装](./intro/install.md)
- [Linux](./intro/install/linux.md)
- [MacOS](./intro/install/macos.md)
- [Windows](./intro/install/windows.md)
- - [Verify Installation](./intro/install/verify.md)
-- [Getting started](./start/index.md)
+ - [验证工具链的安装](./intro/install/verify.md)
+- [开始](./start/index.md)
- [QEMU](./start/qemu.md)
- - [Hardware](./start/hardware.md)
- - [Memory-mapped Registers](./start/registers.md)
- - [Semihosting](./start/semihosting.md)
- - [Panicking](./start/panicking.md)
- - [Exceptions](./start/exceptions.md)
- - [Interrupts](./start/interrupts.md)
+ - [硬件](./start/hardware.md)
+ - [存储映射的寄存器](./start/registers.md)
+ - [半主机模式](./start/semihosting.md)
+ - [运行时恐慌(Panicking)](./start/panicking.md)
+ - [异常](./start/exceptions.md)
+ - [中断](./start/interrupts.md)
- [IO](./start/io.md)
-- [Peripherals](./peripherals/index.md)
- - [A first attempt in Rust](./peripherals/a-first-attempt.md)
- - [The Borrow Checker](./peripherals/borrowck.md)
- - [Singletons](./peripherals/singletons.md)
-- [Static Guarantees](./static-guarantees/index.md)
- - [Typestate Programming](./static-guarantees/typestate-programming.md)
- - [Peripherals as State Machines](./static-guarantees/state-machines.md)
- - [Design Contracts](./static-guarantees/design-contracts.md)
- - [Zero Cost Abstractions](./static-guarantees/zero-cost-abstractions.md)
-- [Portability](./portability/index.md)
-- [Concurrency](./concurrency/index.md)
-- [Collections](./collections/index.md)
-- [Design Patterns](./design-patterns/index.md)
+- [外设](./peripherals/index.md)
+ - [Rust尝鲜](./peripherals/a-first-attempt.md)
+ - [借用检查器](./peripherals/borrowck.md)
+ - [单例](./peripherals/singletons.md)
+- [静态保障(static guarantees)](./static-guarantees/index.md)
+ - [类型状态编程](./static-guarantees/typestate-programming.md)
+ - [把外设当作状态机](./static-guarantees/state-machines.md)
+ - [设计约定](./static-guarantees/design-contracts.md)
+ - [零成本抽象](./static-guarantees/zero-cost-abstractions.md)
+- [可移植性](./portability/index.md)
+- [并发](./concurrency/index.md)
+- [容器](./collections/index.md)
+- [设计模式](./design-patterns/index.md)
- [HALs](./design-patterns/hal/index.md)
- - [Checklist](./design-patterns/hal/checklist.md)
- - [Naming](./design-patterns/hal/naming.md)
- - [Interoperability](./design-patterns/hal/interoperability.md)
- - [Predictability](./design-patterns/hal/predictability.md)
+ - [列表](./design-patterns/hal/checklist.md)
+ - [命名](./design-patterns/hal/naming.md)
+ - [互操性](./design-patterns/hal/interoperability.md)
+ - [可预见性](./design-patterns/hal/predictability.md)
- [GPIO](./design-patterns/hal/gpio.md)
-- [Tips for embedded C developers](./c-tips/index.md)
+- [给嵌入式C开发者的贴士](./c-tips/index.md)
-- [Interoperability](./interoperability/index.md)
- - [A little C with your Rust](./interoperability/c-with-rust.md)
- - [A little Rust with your C](./interoperability/rust-with-c.md)
-- [Unsorted topics](./unsorted/index.md)
- - [Optimizations: The speed size tradeoff](./unsorted/speed-vs-size.md)
- - [Performing Math Functionality](./unsorted/math.md)
+- [互操性](./interoperability/index.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)
---
-[Appendix A: Glossary](./appendix/glossary.md)
+[附录A: 词汇表](./appendix/glossary.md)
diff --git a/src/appendix/glossary.md b/src/appendix/glossary.md
index 6a898b49..fdbb26a1 100644
--- a/src/appendix/glossary.md
+++ b/src/appendix/glossary.md
@@ -1,64 +1,47 @@
-# Appendix A: Glossary
+# 附录A: 词汇表
-The embedded ecosystem is full of different protocols, hardware components and
-vendor-specific things that use their own terms and abbreviations. This Glossary
-attempts to list them with pointers for understanding them better.
+嵌入式生态系统中充满了不同的协议,硬件组件,还有许多与生产商相关的东西,它们都使用自己的缩写和项目名。这个词汇表尝试列出它们以便更好理解它们。
### BSP
-A Board Support Crate provides a high level interface configured for a specific
-board. It usually depends on a [HAL](#hal) crate.
-There is a more detailed description on the [memory-mapped registers page](../start/registers.md)
-or for a broader overview see [this video](https://youtu.be/vLYit_HHPaY).
+板级支持的Crate(Board Support Crate)提供为某个特定板子配置的高级接口。它通常依赖一个[HAL](#hal) crate 。在[存储映射的寄存器那页](../start/registers.md)有更多细节的描述或者看[这个视频](https://youtu.be/vLYit_HHPaY)来获取一个更广泛的概述。
### FPU
-Floating-point Unit. A 'math processor' running only operations on floating-point numbers.
+浮点单元(Floating-Point Unit)。一个只运行在浮点数上的'数学处理器'。
### HAL
-A Hardware Abstraction Layer crate provides a developer friendly interface to a microcontroller's
-features and peripherals. It is usually implemented on top of a [Peripheral Access Crate (PAC)](#pac).
-It may also implement traits from the [`embedded-hal`](https://crates.io/crates/embedded-hal) crate.
-There is a more detailed description on the [memory-mapped registers page](../start/registers.md)
-or for a broader overview see [this video](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
-Sometimes referred to as `I²C` or Inter-IC. It is a protocol meant for hardware communication
-within a single integrated circuit. See [here][i2c] for more details
+有时又被称为 `I²C` 或者 Intere-IC 。它是一种用于在单个集成电路中进行硬件通信的协议。看[这里][i2c]来获取更多细节。
[i2c]: https://en.wikipedia.org/wiki/I2c
### PAC
-A Peripheral Access Crate provides access to a microcontroller's peripherals. It is one of
-the lower level crates and is usually generated directly from the provided [SVD](#svd), often
-using [svd2rust](/~https://github.com/rust-embedded/svd2rust/). The [Hardware Abstraction Layer](#hal)
-would usually depend on this crate.
-There is a more detailed description on the [memory-mapped registers page](../start/registers.md)
-or for a broader overview see [this video](https://youtu.be/vLYit_HHPaY).
+一个外设访问 Crate (Peripheral Access Crate)提供了对一个微控制器的外设的访问。它是一个底层的crates且通常从提供的[SVD](#svd)被直接生成,经常使用[svd2rust](/~https://github.com/rust-embedded/svd2rust/)。[硬件抽象层](#hal)应该依赖这个crate。在[存储映射的寄存器那页](../start/registers.md)有更细节的描述或者看[这个视频](https://youtu.be/vLYit_HHPaY)获取一个更广泛的概述。
### SPI
-Serial Peripheral Interface. See [here][spi] for more information.
+串行外设接口。看[这里][spi]获取更多信息。
[spi]: https://en.wikipedia.org/wiki/Serial_peripheral_interface
### SVD
-System View Description is an XML file format used to describe the programmers view of a
-microcontroller device. You can read more about it on
-[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
-Universal asynchronous receiver-transmitter. See [here][uart] for more information.
+通用异步收发器。看[这里][uart]获取更多信息。
[uart]: https://en.wikipedia.org/wiki/Universal_asynchronous_receiver-transmitter
### USART
-Universal synchronous and asynchronous receiver-transmitter. See [here][usart] for more information.
+通用同步异步收发器。看[这里][usart]获取更多信息。
[usart]: https://en.wikipedia.org/wiki/Universal_synchronous_and_asynchronous_receiver-transmitter
diff --git a/src/c-tips/index.md b/src/c-tips/index.md
index 0c67108e..cb718800 100644
--- a/src/c-tips/index.md
+++ b/src/c-tips/index.md
@@ -1,37 +1,24 @@
-# Tips for embedded C developers
+# 给嵌入式C开发者的贴士
-This chapter collects a variety of tips that might be useful to experienced
-embedded C developers looking to start writing Rust. It will especially
-highlight how things you might already be used to in C are different in Rust.
+这个章节收集了可能对于刚开始编写Rust的,有经验的嵌入式C开发者来说,有用的各种各样的贴士。它将解释你在C中可能已经用到的那些东西与Rust中的有何不同。
-## Preprocessor
+## 预处理器
-In embedded C it is very common to use the preprocessor for a variety of
-purposes, such as:
+在嵌入式C中,为了各种各样的目的使用预处理器是很常见的,比如:
-* Compile-time selection of code blocks with `#ifdef`
-* Compile-time array sizes and computations
-* Macros to simplify common patterns (to avoid function call overhead)
+* 使用`#ifdef`编译时选择代码块
+* 编译时的数组大小和计算
+* 用来简化常见的模式的宏(避免调用函数的开销)
-In Rust there is no preprocessor, and so many of these use cases are addressed
-differently. In the rest of this section we cover various alternatives to
-using the preprocessor.
+在Rust中没有预处理器,所以许多案例有不同的处理方法。本章节剩下的部分,我们将介绍各种替代预处理器的方法。
-### Compile-Time Code Selection
+### 编译时的代码选择
-The closest match to `#ifdef ... #endif` in Rust are [Cargo features]. These
-are a little more formal than the C preprocessor: all possible features are
-explicitly listed per crate, and can only be either on or off. Features are
-turned on when you list a crate as a dependency, and are additive: if any crate
-in your dependency tree enables a feature for another crate, that feature will
-be enabled for all users of that 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
-For example, you might have a crate which provides a library of signal
-processing primitives. Each one might take some extra time to compile or
-declare some large table of constants which you'd like to avoid. You could
-declare a Cargo feature for each component in your `Cargo.toml`:
+比如,你可能有一个crate,其提供一个信号处理的基本类型库(library of signal processing primitives)。每个基本类型可能带来一些额外的时间去编译大量的常量,你想要避开这些常量。你可以为你的`Cargo.toml`中每个组件声明一个Cargo feature。
```toml
[features]
@@ -39,11 +26,10 @@ FIR = []
IIR = []
```
-Then, in your code, use `#[cfg(feature="FIR")]` to control what is included.
+然后,在你的代码中,使用`#[cfg(feature="FIR")]`去控制要包含什么东西。
```rust
-/// In your top-level lib.rs
-
+/// 在你的顶层的lib.rs中
#[cfg(feature="FIR")]
pub mod fir;
@@ -51,29 +37,17 @@ pub mod fir;
pub mod iir;
```
-You can similarly include code blocks only if a feature is _not_ enabled, or if
-any combination of features are or are not enabled.
+同样地,你可以控制,只有当某个feature _没有_ 被打开时,包含代码块,或者某些features的组合被打开或者被关闭时。
-Additionally, Rust provides a number of automatically-set conditions you can
-use, such as `target_arch` to select different code based on architecture. For
-full details of the conditional compilation support, refer to the
-[conditional compilation] chapter of the Rust reference.
+另外,Rust提供了许多可以使用的自动配置了的条件,比如`target_arch`用来选择不同的代码所基于的架构。对于条件编译的全部细节,可以参看the Rust reference的[conditional compilation]章节。
[conditional compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
-The conditional compilation will only apply to the next statement or block. If
-a block can not be used in the current scope then the `cfg` attribute will
-need to be used multiple times. It's worth noting that most of the time it is
-better to simply include all the code and allow the compiler to remove dead
-code when optimising: it's simpler for you and your users, and in general the
-compiler will do a good job of removing unused code.
+条件编译将只应用于下一条语句或者块。如果一个块不能在现在的作用域中被使用,那么`cfg`属性将需要被多次使用。值得注意的是大多数时间,仅是包含所有的代码而让编译器在优化时去删除死代码(dead code)更好,通常,在移除不使用的代码方面的工作,编译器做得很好。
-### Compile-Time Sizes and Computation
+### 编译时大小和计算
-Rust supports `const fn`, functions which are guaranteed to be evaluable at
-compile-time and can therefore be used where constants are required, such as
-in the size of arrays. This can be used alongside features mentioned above,
-for example:
+Rust支持`const fn`,`const fn`是在编译时可以被计算的函数,因此可以被用在需要常量的地方,比如在数组的大小中。这个能与上述的features一起使用,比如:
```rust
const fn array_size() -> usize {
@@ -86,83 +60,48 @@ const fn array_size() -> usize {
static BUF: [u32; array_size()] = [0u32; array_size()];
```
-These are new to stable Rust as of 1.31, so documentation is still sparse. The
-functionality available to `const fn` is also very limited at the time of
-writing; in future Rust releases it is expected to expand on what is permitted
-in a `const fn`.
-
-### Macros
-
-Rust provides an extremely powerful [macro system]. While the C preprocessor
-operates almost directly on the text of your source code, the Rust macro system
-operates at a higher level. There are two varieties of Rust macro: _macros by
-example_ and _procedural macros_. The former are simpler and most common; they
-look like function calls and can expand to a complete expression, statement,
-item, or pattern. Procedural macros are more complex but permit extremely
-powerful additions to the Rust language: they can transform arbitrary Rust
-syntax into new Rust syntax.
-
-[macro system]: https://doc.rust-lang.org/book/ch19-06-macros.html
-
-In general, where you might have used a C preprocessor macro, you probably want
-to see if a macro-by-example can do the job instead. They can be defined in
-your crate and easily used by your own crate or exported for other users. Be
-aware that since they must expand to complete expressions, statements, items,
-or patterns, some use cases of C preprocessor macros will not work, for example
-a macro that expands to part of a variable name or an incomplete set of items
-in a list.
-
-As with Cargo features, it is worth considering if you even need the macro. In
-many cases a regular function is easier to understand and will be inlined to
-the same code as a macro. The `#[inline]` and `#[inline(always)]` [attributes]
-give you further control over this process, although care should be taken here
-as well — the compiler will automatically inline functions from the same crate
-where appropriate, so forcing it to do so inappropriately might actually lead
-to decreased performance.
+这些对于stable版本的Rust来说是新的特性,从1.31开始引入,因此文档依然很少。在写这篇文章的时候`const fn`可用的功能也非常有限; 在未来的Rust release版本中,我们可以期望`const fn`将带来更多的功能。
+
+### 宏
+
+Rust提供一个极度强大的[宏系统]。虽然C预处理器几乎直接在你的源代码之上进行操作,但是Rust宏系统可以在一个更高的级别上操作。存在两种Rust宏: _声明宏_ 和 _过程宏_ 。前者更简单也最常见; 它们看起来像是函数调用,且能扩展成一个完整的表达式,语句,项,或者模式。过程宏更复杂但是却能让Rust更强大: 它们可以把任一条Rust语法变成一个新的Rust语法。
+
+[宏系统]: https://doc.rust-lang.org/book/ch19-06-macros.html
+
+通常,你可能想知道在那些使用一个C预处理器宏的地方,能否使用一个声明宏做同样的工作。你可以在crate中定义它们,且在你的crate中轻松使用它们或者导出给其他人用。但是请注意,因为它们必须扩展成完整的表达式,语句,项或者模式,因此C预处理器宏的某些用例没法用,比如可以扩展成一个变量名的一部分的宏或者可以把列表中的项扩展成不完整的集合的宏。
+
+和Cargo features一样,值得考虑下你是否真的需要宏。在一些例子中一个常规的函数更容易被理解,它也能被内联成和一个和宏一样的代码。`#[inline]`和`#[inline(always)]` [attributes] 能让你更深入控制这个过程,这里也要小心 - 编译器会从同一个crate的恰当的地方自动地内联函数,因此不恰当地强迫它内联函数实际可能会导致性能下降。
[attributes]: https://doc.rust-lang.org/reference/attributes.html#inline-attribute
-Explaining the entire Rust macro system is out of scope for this tips page, so
-you are encouraged to consult the Rust documentation for full details.
+研究完整的Rust宏系统超出了本节内容,因此我们鼓励你去查阅Rust文档了解完整的细节。
-## Build System
+## 编译系统
-Most Rust crates are built using Cargo (although it is not required). This
-takes care of many difficult problems with traditional build systems. However,
-you may wish to customise the build process. Cargo provides [`build.rs`
-scripts] for this purpose. They are Rust scripts which can interact with the
-Cargo build system as required.
+大多数Rust crates使用Cargo编译 (即使这不是必须的)。这解决了传统编译系统带来的许多难题。然而,你可能希望自定义编译过程。为了实现这个目的,Cargo提供了[`build.rs`脚本]。它们是可以根据需要与Cargo编译系统进行交互的Rust脚本。
-[`build.rs` scripts]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
+[`build.rs`脚本]: https://doc.rust-lang.org/cargo/reference/build-scripts.html
-Common use cases for build scripts include:
+与编译脚本有关的常见用例包括:
-* provide build-time information, for example statically embedding the build
- date or Git commit hash into your executable
-* generate linker scripts at build time depending on selected features or other
- logic
-* change the Cargo build configuration
-* add extra static libraries to link against
+* 提供编译时信息,比如静态嵌入编译日期或者Git commit hash进你的可执行文件中
+* 根据被选择的features或者其它逻辑在编译时生成链接脚本
+* 改变Cargo的编译配置
+* 添加额外的静态链接库以进行链接
-At present there is no support for post-build scripts, which you might
-traditionally have used for tasks like automatic generation of binaries from
-the build objects or printing build information.
+现在还不支持post-build脚本,通常将它用于像是从编译的对象自动生生成二进制文件或者打印编译信息这类任务中。
-### Cross-Compiling
+### 交叉编译
-Using Cargo for your build system also simplifies cross-compiling. In most
-cases it suffices to tell Cargo `--target thumbv6m-none-eabi` and find a
-suitable executable in `target/thumbv6m-none-eabi/debug/myapp`.
+为你的编译系统使用Cargo也能简化交叉编译。在大多数例子里,告诉Cargo `--target thumbv6m-none-eabi`就行了,可以在`target/thumbv6m-none-eabi/debug/myapp`中找到一个合适的可执行文件。
-For platforms not natively supported by Rust, you will need to build `libcore`
-for that target yourself. On such platforms, [Xargo] can be used as a stand-in
-for Cargo which automatically builds `libcore` for you.
+对于那些并不是Rust原生支持的平台,将需要自己为那个目标平台编译`libcore`。遇到这样的平台,[Xargo]可以作为Cargo的替代来使用,它可以自动地为你编译`libcore`。
[Xargo]: /~https://github.com/japaric/xargo
-## Iterators vs Array Access
+## 迭代器与数组访问
-In C you are probably used to accessing arrays directly by their index:
+在C中,你可能习惯于通过索引直接访问数组:
```c
int16_t arr[16];
@@ -172,13 +111,9 @@ for(i=0; i,
end: usize,
@@ -65,19 +51,18 @@ unsafe impl Sync for BumpPointerAlloc {}
unsafe impl GlobalAlloc for BumpPointerAlloc {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
- // `interrupt::free` is a critical section that makes our allocator safe
- // to use from within interrupts
+ // `interrupt::free`是一个临界区,临界区让我们的分配器在中断中用起来安全
interrupt::free(|_| {
let head = self.head.get();
let size = layout.size();
let align = layout.align();
let align_mask = !(align - 1);
- // move start up to the next alignment boundary
+ // 将start移至下一个对齐边界。
let start = (*head + align - 1) & align_mask;
if start + size > self.end {
- // a null pointer signal an Out Of Memory condition
+ // 一个空指针通知内存不足
ptr::null_mut()
} else {
*head = start + size;
@@ -87,13 +72,13 @@ unsafe impl GlobalAlloc for BumpPointerAlloc {
}
unsafe fn dealloc(&self, _: *mut u8, _: Layout) {
- // this allocator never deallocates memory
+ // 这个分配器从不释放内存
}
}
-// Declaration of the global memory allocator
-// NOTE the user must ensure that the memory region `[0x2000_0100, 0x2000_0200]`
-// is not used by other parts of the program
+// 全局内存分配器的声明
+// 注意 用户必须确保`[0x2000_0100, 0x2000_0200]`内存区域
+// 没有被程序的其它部分使用
#[global_allocator]
static HEAP: BumpPointerAlloc = BumpPointerAlloc {
head: UnsafeCell::new(0x2000_0100),
@@ -101,9 +86,7 @@ static HEAP: BumpPointerAlloc = BumpPointerAlloc {
};
```
-Apart from selecting a global allocator the user will also have to define how
-Out Of Memory (OOM) errors are handled using the *unstable*
-`alloc_error_handler` attribute.
+除了选择一个全局分配器,用户也必须要定义如何使用*不稳定的*`alloc_error_handler`属性来处理内存溢出错误。
``` rust,ignore
#![feature(alloc_error_handler)]
@@ -118,7 +101,7 @@ fn on_oom(_layout: Layout) -> ! {
}
```
-Once all that is in place, the user can finally use the collections in `alloc`.
+一旦一切都完成了,用户最后就可以在`alloc`中使用集合。
```rust,ignore
#[entry]
@@ -134,13 +117,11 @@ fn main() -> ! {
}
```
-If you have used the collections in the `std` crate then these will be familiar
-as they are exact same implementation.
+如果你已经使用了`std` crate中的集合,那么这些对你来说将非常熟悉,因为他们的实现一样。
-## Using `heapless`
+## 使用 `heapless`
-`heapless` requires no setup as its collections don't depend on a global memory
-allocator. Just `use` its collections and proceed to instantiate them:
+`heapless`无需设置,因为它的集合不依赖一个全局内存分配器。只是`use`它的集合然后实例化它们:
```rust,ignore
// heapless version: v0.4.x
@@ -157,105 +138,51 @@ fn main() -> ! {
}
```
-You'll note two differences between these collections and the ones in `alloc`.
+你会注意到这些集合与`alloc`中的集合有两个不一样的地方。
-First, you have to declare upfront the capacity of the collection. `heapless`
-collections never reallocate and have fixed capacities; this capacity is part of
-the type signature of the collection. In this case we have declared that `xs`
-has a capacity of 8 elements that is the vector can, at most, hold 8 elements.
-This is indicated by the `U8` (see [`typenum`]) in the type signature.
+第一,你必须预先声明集合的容量。`heapless`集合从来不会发生重分配且具有固定的容量;这个容量是集合的类型签名的一部分。在这个例子里,我们已经声明了`xs`的容量为8个元素,也就是说,这个vector最多只能有八个元素。这是通过类型签名中的`U8` (看[`typenum`])来指定的。
[`typenum`]: https://crates.io/crates/typenum
-Second, the `push` method, and many other methods, return a `Result`. Since the
-`heapless` collections have fixed capacity all operations that insert elements
-into the collection can potentially fail. The API reflects this problem by
-returning a `Result` indicating whether the operation succeeded or not. In
-contrast, `alloc` collections will reallocate themselves on the heap to increase
-their capacity.
-
-As of version v0.4.x all `heapless` collections store all their elements inline.
-This means that an operation like `let x = heapless::Vec::new();` will allocate
-the collection on the stack, but it's also possible to allocate the collection
-on a `static` variable, or even on the heap (`Box>`).
-
-## Trade-offs
-
-Keep these in mind when choosing between heap allocated, relocatable collections
-and fixed capacity collections.
-
-### Out Of Memory and error handling
-
-With heap allocations Out Of Memory is always a possibility and can occur in
-any place where a collection may need to grow: for example, all
-`alloc::Vec.push` invocations can potentially generate an OOM condition. Thus
-some operations can *implicitly* fail. Some `alloc` collections expose
-`try_reserve` methods that let you check for potential OOM conditions when
-growing the collection but you need be proactive about using them.
-
-If you exclusively use `heapless` collections and you don't use a memory
-allocator for anything else then an OOM condition is impossible. Instead, you'll
-have to deal with collections running out of capacity on a case by case basis.
-That is you'll have deal with *all* the `Result`s returned by methods like
-`Vec.push`.
-
-OOM failures can be harder to debug than say `unwrap`-ing on all `Result`s
-returned by `heapless::Vec.push` because the observed location of failure may
-*not* match with the location of the cause of the problem. For example, even
-`vec.reserve(1)` can trigger an OOM if the allocator is nearly exhausted because
-some other collection was leaking memory (memory leaks are possible in safe
-Rust).
-
-### Memory usage
-
-Reasoning about memory usage of heap allocated collections is hard because the
-capacity of long lived collections can change at runtime. Some operations may
-implicitly reallocate the collection increasing its memory usage, and some
-collections expose methods like `shrink_to_fit` that can potentially reduce the
-memory used by the collection -- ultimately, it's up to the allocator to decide
-whether to actually shrink the memory allocation or not. Additionally, the
-allocator may have to deal with memory fragmentation which can increase the
-*apparent* memory usage.
-
-On the other hand if you exclusively use fixed capacity collections, store
-most of them in `static` variables and set a maximum size for the call stack
-then the linker will detect if you try to use more memory than what's physically
-available.
-
-Furthermore, fixed capacity collections allocated on the stack will be reported
-by [`-Z emit-stack-sizes`] flag which means that tools that analyze stack usage
-(like [`stack-sizes`]) will include them in their analysis.
+第二,`push`方法和另外一些方法返回的是一个`Result`。因为`heapless`集合有一个固定的容量,所以所有插入的操作都可能会失败。通过返回一个`Result`,API反应了这个问题,指出操作是否成功还是失败。相反,`alloc`集合自己将会在堆上重新分配去增加它的容量。
+
+自v0.4.x版本起,所有的`heapless`集合将所有的元素内联地存储起来了。这意味着像是`let x = heapless::Vec::new()`这样的一个操作将会在栈上分配集合,但是它也能够在一个`static`变量上分配集合,或者甚至在堆上(`Box>`)。
+
+## 取舍
+
+当在堆分配的可重定位的集合和固定容量的集合间进行选择的时候,记住这些内容。
+
+### 内存溢出和错误处理
+
+使用堆分配,内存溢出总是有可能出现的且会发生在任何一个集合需要增长的地方: 比如,所有的 `alloc::Vec.push` 调用会潜在地产生一个OOM(Out of Memory)条件。因此一些操作可能会*隐式地*失败。一些`alloc`集合暴露了`try_reserve`方法,可以当增加集合时让你检查潜在的OOM条件,但是你需要主动地使用它们。
+
+如果你只使用`heapless`集合,而不使用内存分配器,那么一个OOM条件不可能出现。反而,你必须逐个处理容量不足的集合。也就是必须处理*所有*的`Result`,`Result`由像是`Vec.push`这样的方法返回的。
+
+与在所有由`heapless::Vec.push`返回的`Result`上调用`unwrap`相比,OOM错误更难调试,因为错误被发现的位置可能与导致问题的位置*不*一致。比如,甚至如果分配器接近消耗完`vec.reserve(1)`都能触发一个OOM,因为一些其它的集合正在泄露内存(内存泄露在安全的Rust是会发生的)。
+
+### 内存使用
+
+推理堆分配集合的内存使用是很难的因为长期使用的集合的大小会在运行时改变。一些操作可能隐式地重分配集合,增加了它的内存使用,一些集合暴露的方法,像是`shrink_to_fit`,会潜在地减少集合使用的内存 -- 最终,它由分配器去决定是否确定减小内存的分配或者不。另外,分配器可能不得不处理内存碎片,它会*明显*增加内存的使用。
+
+另一方面,如果你只使用固定容量的集合,请把大多数的数据保存在`static`变量中,并为调用栈设置一个最大尺寸,随后如果你尝试使用大于可用的物理内存的内存大小时,链接器会发现它。
+
+另外,在栈上分配的固定容量的集合可以通过[`-Z emit-stack-sizes`]标识来报告,其意味着用来分析栈使用的工具(像是[`stack-sizes`])将会把在栈上分配的集合包含进它们的分析中。
[`-Z emit-stack-sizes`]: https://doc.rust-lang.org/beta/unstable-book/compiler-flags/emit-stack-sizes.html
[`stack-sizes`]: https://crates.io/crates/stack-sizes
-However, fixed capacity collections can *not* be shrunk which can result in
-lower load factors (the ratio between the size of the collection and its
-capacity) than what relocatable collections can achieve.
+然而,固定容量的集合*不*能被减少,与可重定位集合所能达到的负载系数(集合的大小和它的容量之间的比值)相比,它能产生更低的负载系数。
-### Worst Case Execution Time (WCET)
+### 最坏执行时间 (WCET)
-If you are building time sensitive applications or hard real time applications
-then you care, maybe a lot, about the worst case execution time of the different
-parts of your program.
+如果你正在搭建时间敏感型应用或者硬实时应用,那么你可能更关心你程序的不同部分的最坏执行时间。
-The `alloc` collections can reallocate so the WCET of operations that may grow
-the collection will also include the time it takes to reallocate the collection,
-which itself depends on the *runtime* capacity of the collection. This makes it
-hard to determine the WCET of, for example, the `alloc::Vec.push` operation as
-it depends on both the allocator being used and its runtime capacity.
+`alloc`集合能重分配,所以操作的WCET可能会增加,集合也将包括它用来重分配集合所需的时间,它取决于集合的*运行时*容量。这使得它更难去决定操作,比如`alloc::Vec.push`,的WCET,因为它依赖被使用的分配器和它的运行时容量。
-On the other hand fixed capacity collections never reallocate so all operations
-have a predictable execution time. For example, `heapless::Vec.push` executes in
-constant time.
+另一方面固定容量的集合不会重分配,因此所有的操作有个可预期的执行时间。比如,`heapless::Vec.push`以固定时间执行。
-### Ease of use
+### 易用性
-`alloc` requires setting up a global allocator whereas `heapless` does not.
-However, `heapless` requires you to pick the capacity of each collection that
-you instantiate.
+`alloc`要求配置一个全局分配器而`heapless`不需要。然而,`heapless`要求你去选择你要实例化的每一个集合的容量。
-The `alloc` API will be familiar to virtually every Rust developer. The
-`heapless` API tries to closely mimic the `alloc` API but it will never be
-exactly the same due to its explicit error handling -- some developers may feel
-the explicit error handling is excessive or too cumbersome.
+`alloc` API几乎为每一个Rust开发者所熟知。`heapless` API尝试模仿`alloc` API,但是因为`heapless`的显式错误处理,它们不可能会一模一样 -- 一些开发者可能会觉得显式的错误处理过多或太麻烦。
diff --git a/src/concurrency/index.md b/src/concurrency/index.md
index fe30e235..f450a0cd 100644
--- a/src/concurrency/index.md
+++ b/src/concurrency/index.md
@@ -1,26 +1,16 @@
-# Concurrency
+# 并发
-Concurrency happens whenever different parts of your program might execute
-at different times or out of order. In an embedded context, this includes:
+当程序的不同部分有可能会在不同的时刻被执行或者不按顺序地被执行时,那并发就出现了。在一个嵌入式环境中,这包括:
-* interrupt handlers, which run whenever the associated interrupt happens,
-* various forms of multithreading, where your microprocessor regularly swaps
- between parts of your program,
-* and in some systems, multiple-core microprocessors, where each core can be
- independently running a different part of your program at the same time.
+* 中断处理函数,一旦相关的中断发生时,中断处理函数就会运行,
+* 不同的多线程形式,在这块,微处理器通常会在程序的不同部分间进行切换,
+* 在一些多核微处理器系统中,每个核可以同时独立地运行程序的不同部分。
-Since many embedded programs need to deal with interrupts, concurrency will
-usually come up sooner or later, and it's also where many subtle and difficult
-bugs can occur. Luckily, Rust provides a number of abstractions and safety
-guarantees to help us write correct code.
+因为许多嵌入式程序需要处理中断,因此并发迟早会出现,这也是许多微妙和困难的bugs会出现的地方。幸运地是,Rust提供了许多抽象和安全保障去帮助我们写正确的代码。
-## No Concurrency
+## 没有并发
-The simplest concurrency for an embedded program is no concurrency: your
-software consists of a single main loop which just keeps running, and there
-are no interrupts at all. Sometimes this is perfectly suited to the problem
-at hand! Typically your loop will read some inputs, perform some processing,
-and write some outputs.
+对于一个嵌入式程序来说最简单的并发是没有并发: 软件由一个保持运行的main循环组成,一点中断也没有。有时候这非常适合手边的问题! 通常你的循环将会读取一些输入,执行一些处理,且写入一些输出。
```rust,ignore
#[entry]
@@ -34,29 +24,17 @@ fn main() {
}
```
-Since there's no concurrency, there's no need to worry about sharing data
-between parts of your program or synchronising access to peripherals. If
-you can get away with such a simple approach this can be a great solution.
+因为这里没有并发,因此不需要担心程序不同部分间的共享数据或者同步对外设的访问。如果可以使用一个简单的方法来解决问题,这种方法是个不错的选择。
-## Global Mutable Data
+## 全局可变数据
-Unlike non-embedded Rust, we will not usually have the luxury of creating
-heap allocations and passing references to that data into a newly-created
-thread. Instead, our interrupt handlers might be called at any time and must
-know how to access whatever shared memory we are using. At the lowest level,
-this means we must have _statically allocated_ mutable memory, which
-both the interrupt handler and the main code can refer to.
+不像非嵌入式Rust,我们通常不会奢侈地在堆上分配数据,并将对该数据的引用传递到新创建的线程中。相反,我们的中断处理函数随时可能被调用,且必须知道如何访问我们正在使用的共享内存。从最底层看来,这意味着我们必须有 _静态分配的_ 可变的内存,中断处理函数和main代码都可以引用这块内存。
-In Rust, such [`static mut`] variables are always unsafe to read or write,
-because without taking special care, you might trigger a race condition,
-where your access to the variable is interrupted halfway through by an
-interrupt which also accesses that variable.
+在Rust中,[`static mut`]这样的变量读取或者写入总是unsafe的,因为不特别关注它们的话,可能会触发一个竞态条件,对变量的访问在中途就被一个也访问那个变量的中断打断了。
[`static mut`]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
-For an example of how this behaviour can cause subtle errors in your code,
-consider an embedded program which counts rising edges of some input signal
-in each one-second period (a frequency counter):
+为了举例这种行为如何在代码中导致了微妙的错误,思考一个嵌入式程序,这个程序在每一秒的周期内计数一些输入信号的上升沿(一个频率计数器):
```rust,ignore
static mut COUNTER: u32 = 0;
@@ -68,7 +46,7 @@ fn main() -> ! {
loop {
let state = read_signal_level();
if state && !last_state {
- // DANGER - Not actually safe! Could cause data races.
+ // 危险 - 实际不安全! 可能导致数据竞争。
unsafe { COUNTER += 1 };
}
last_state = state;
@@ -81,23 +59,11 @@ fn timer() {
}
```
-Each second, the timer interrupt sets the counter back to 0. Meanwhile, the
-main loop continually measures the signal, and incremements the counter when
-it sees a change from low to high. We've had to use `unsafe` to access
-`COUNTER`, as it's `static mut`, and that means we're promising the compiler
-we won't cause any undefined behaviour. Can you spot the race condition? The
-increment on `COUNTER` is _not_ guaranteed to be atomic — in fact, on most
-embedded platforms, it will be split into a load, then the increment, then
-a store. If the interrupt fired after the load but before the store, the
-reset back to 0 would be ignored after the interrupt returns — and we would
-count twice as many transitions for that period.
+每秒计时器中断会把计数器设置回0。这期间,main循环连续地测量信号,且当看到从低电平到高电平的变化时,增加计数器的值。因为它是`static mut`的,我们不得不使用`unsafe`去访问`COUNTER`,意思是我们向编译器保证我们的操作不会导致任何未定义的行为。你能发现竞态条件吗?`COUNTER`上的增加并不一定是原子的 - 事实上,在大多数嵌入式平台上,它将被分开成一个读取操作,然后是增加,然后是写回。如果中断在计数器被读取之后但是在被写回之前被激活,在中断返回后,重置回0的操作会被忽略掉 - 那期间,我们会算出两倍的转换次数。
-## Critical Sections
+## 临界区(Critical Sections)
-So, what can we do about data races? A simple approach is to use _critical
-sections_, a context where interrupts are disabled. By wrapping the access to
-`COUNTER` in `main` in a critical section, we can be sure the timer interrupt
-will not fire until we're finished incrementing `COUNTER`:
+因此,关于数据竞争可以做些什么?一个简单的方法是使用 _临界区(critical sections)_ ,在临界区的上下文中中断被关闭了。通过把对`main`中的`COUNTER`访问封装进一个临界区,我们能确保计时器中断将不会激活,直到我们完成了增加`COUNTER`的操作:
```rust,ignore
static mut COUNTER: u32 = 0;
@@ -109,7 +75,7 @@ fn main() -> ! {
loop {
let state = read_signal_level();
if state && !last_state {
- // New critical section ensures synchronised access to COUNTER
+ // 新的临界区确保对COUNTER的同步访问
cortex_m::interrupt::free(|_| {
unsafe { COUNTER += 1 };
});
@@ -124,39 +90,22 @@ fn timer() {
}
```
-In this example, we use `cortex_m::interrupt::free`, but other platforms will
-have similar mechanisms for executing code in a critical section. This is also
-the same as disabling interrupts, running some code, and then re-enabling
-interrupts.
+在这个例子里,我们使用 `cortex_m::interrupt::free`,但是其它平台将会有更简单的机制在一个临界区中执行代码。它们都有一样的逻辑,关闭中断,运行一些代码,然后重新使能中断。
-Note we didn't need to put a critical section inside the timer interrupt,
-for two reasons:
+注意,有两个理由,不需要把一个临界区放进计时器中断中:
- * Writing 0 to `COUNTER` can't be affected by a race since we don't read it
- * It will never be interrupted by the `main` thread anyway
+ * 向`COUNTER`写入0不会被一个竞争影响,因为我们不需要读取它
+ * 无论如何,它永远不会被`main`线程中断
-If `COUNTER` was being shared by multiple interrupt handlers that might
-_preempt_ each other, then each one might require a critical section as well.
+如果`COUNTER`被多个可能相互 _抢占_ 的中断处理函数共享,那么每一个也需要一个临界区。
-This solves our immediate problem, but we're still left writing a lot of unsafe code which we need to carefully reason about, and we might be using critical sections needlessly. Since each critical section temporarily pauses interrupt processing, there is an associated cost of some extra code size and higher interrupt latency and jitter (interrupts may take longer to be processed, and the time until they are processed will be more variable). Whether this is a problem depends on your system, but in general, we'd like to avoid it.
+这解决了我们眼前的问题,但是我们仍然要编写许多unsafe的代码,我们需要仔细推敲这些代码,有些我们可能不需要使用临界区。因为每个临界区暂时暂停了中断处理,就会带来一些相关的成本,一些额外的代码大小,更高的中断延迟和抖动(中断可能花费很长时间去处理,等待被处理的时间变化非常大)。这是否是个问题取决于你的系统,但是通常,我们想要避免它。
-It's worth noting that while a critical section guarantees no interrupts will
-fire, it does not provide an exclusivity guarantee on multi-core systems! The
-other core could be happily accessing the same memory as your core, even
-without interrupts. You will need stronger synchronisation primitives if you
-are using multiple cores.
+值得注意的是,虽然一个临界区保障了不会发生中断,但是它在多核系统上不提供一个排他性保证(exclusivity guarantee)!其它核可能很开心地访问与你的核一样的内存区域,即使没有中断。如果你正在使用多核,你将需要更强的同步原语(synchronisation primitives)。
-## Atomic Access
+## 原子访问
-On some platforms, special atomic instructions are available, which provide
-guarantees about read-modify-write operations. Specifically for Cortex-M: `thumbv6`
-(Cortex-M0, Cortex-M0+) only provide atomic load and store instructions,
-while `thumbv7` (Cortex-M3 and above) provide full Compare and Swap (CAS)
-instructions. These CAS instructions give an alternative to the heavy-handed
-disabling of all interrupts: we can attempt the increment, it will succeed most
-of the time, but if it was interrupted it will automatically retry the entire
-increment operation. These atomic operations are safe even across multiple
-cores.
+在一些平台上,可以使用特定的原子指令,它保障了读取-修改-写回操作。针对Cortex-M: `thumbv6`(Cortex-M0,Cortex-M0+)只提供原子读取和存取指令,而`thumv7`(Cortex-M3及以上)提供完整的比较和交换(CAS)指令。这些CAS指令可以替代过重的禁用所有中断的方法: 我们可以尝试执行加法操作,它在大多数情况下都会成功,但是如果它被中断了它将会自动重试完整的加法操作。这些原子操作甚至在多核间也是安全的。
```rust,ignore
use core::sync::atomic::{AtomicUsize, Ordering};
@@ -170,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;
@@ -179,57 +128,42 @@ fn main() -> ! {
#[interrupt]
fn timer() {
- // Use `store` to write 0 directly to COUNTER
+ // 使用 `store` 将 0 直接写入 COUNTER
COUNTER.store(0, Ordering::Relaxed)
}
```
-This time `COUNTER` is a safe `static` variable. Thanks to the `AtomicUsize`
-type `COUNTER` can be safely modified from both the interrupt handler and the
-main thread without disabling interrupts. When possible, this is a better
-solution — but it may not be supported on your platform.
+这时,`COUNTER`是一个safe的`static`变量。多亏了`AtomicUsize`类型,不需要禁用中断,`COUNTER`能从中断处理函数和main线程被安全地修改。当可以这么做时,这是一个更好的解决方案 - 然而平台上可能不支持这么做。
-A note on [`Ordering`]: this affects how the compiler and hardware may reorder
-instructions, and also has consequences on cache visibility. Assuming that the
-target is a single core platform `Relaxed` is sufficient and the most efficient
-choice in this particular case. Stricter ordering will cause the compiler to
-emit memory barriers around the atomic operations; depending on what you're
-using atomics for you may or may not need this! The precise details of the
-atomic model are complicated and best described elsewhere.
+关于[`Ordering`]的提醒: 它可能影响编译器和硬件如何重新排序指令,也会影响缓存可见性。假设目标是个单核平台,在这个案例里`Relaxed`是充足的和最有效的选择。更严格的排序将导致编译器在原子操作周围产生内存屏障(Memory Barriers);取决于你做什么原子操作,你可能需要或者不需要这个排序!原子模型的精确细节是复杂的,最好写在其它地方。
-For more details on atomics and ordering, see the [nomicon].
+关于原子操作和排序的更多细节,可以看这里[nomicon]。
[`Ordering`]: https://doc.rust-lang.org/core/sync/atomic/enum.Ordering.html
[nomicon]: https://doc.rust-lang.org/nomicon/atomics.html
-## Abstractions, Send, and Sync
+## 抽象,Send和Sync
-None of the above solutions are especially satisfactory. They require `unsafe`
-blocks which must be very carefully checked and are not ergonomic. Surely we
-can do better in Rust!
+上面的解决方案都不是特别令人满意。它们需要`unsafe`块,`unsafe`块必须要被十分小心地检查且不符合人体工程学。确实,我们在Rust中可以做得更好!
-We can abstract our counter into a safe interface which can be safely used
-anywhere else in our code. For this example, we'll use the critical-section
-counter, but you could do something very similar with atomics.
+我们可以把我们的计数器抽象进一个安全的接口中,它可以在代码的其它地方被安全地使用。在这个例子里,我们将使用临界区的(cirtical-section)计数器,但是你可以用原子操作做一些非常类似的事情。
```rust,ignore
use core::cell::UnsafeCell;
use cortex_m::interrupt;
-// Our counter is just a wrapper around UnsafeCell, which is the heart
-// of interior mutability in Rust. By using interior mutability, we can have
-// COUNTER be `static` instead of `static mut`, but still able to mutate
-// its counter value.
+// 我们的计数器只是包围UnsafeCell的一个封装,它是Rust中内部可变性
+// (interior mutability)的关键。通过使用内部可变性,我们能让COUNTER
+// 变成`static`而不是`static mut`,但是仍能改变它的计数器值。
struct CSCounter(UnsafeCell);
const CS_COUNTER_INIT: CSCounter = CSCounter(UnsafeCell::new(0));
impl CSCounter {
pub fn reset(&self, _cs: &interrupt::CriticalSection) {
- // By requiring a CriticalSection be passed in, we know we must
- // be operating inside a CriticalSection, and so can confidently
- // use this unsafe block (required to call UnsafeCell::get).
+ // 通过要求一个CriticalSection被传递进来,我们知道我们肯定正在一个
+ // CriticalSection中操作,且因此可以自信地使用这个unsafe块(调用UnsafeCell::get的前提)。
unsafe { *self.0.get() = 0 };
}
@@ -238,11 +172,11 @@ impl CSCounter {
}
}
-// Required to allow static CSCounter. See explanation below.
+// 允许静态CSCounter的前提。看下面的解释。
unsafe impl Sync for CSCounter {}
-// COUNTER is no longer `mut` as it uses interior mutability;
-// therefore it also no longer requires unsafe blocks to access.
+// COUNTER不再是`mut`的因为它使用内部可变性;
+// 因此访问它也不再需要unsafe块。
static COUNTER: CSCounter = CS_COUNTER_INIT;
#[entry]
@@ -252,7 +186,7 @@ fn main() -> ! {
loop {
let state = read_signal_level();
if state && !last_state {
- // No unsafe here!
+ // 这里不用unsafe!
interrupt::free(|cs| COUNTER.increment(cs));
}
last_state = state;
@@ -261,82 +195,43 @@ fn main() -> ! {
#[interrupt]
fn timer() {
- // We do need to enter a critical section here just to obtain a valid
- // cs token, even though we know no other interrupt could pre-empt
- // this one.
+ // 这里我们需要进入一个临界区,只是为了传递进一个有效的cs token,尽管我们知道
+ // 没有其它中断可以抢占这个中断。
interrupt::free(|cs| COUNTER.reset(cs));
- // We could use unsafe code to generate a fake CriticalSection if we
- // really wanted to, avoiding the overhead:
+ // 如果我们真的需要,我们可以使用unsafe代码去生成一个假CriticalSection,
+ // 避免开销:
// let cs = unsafe { interrupt::CriticalSection::new() };
}
```
-We've moved our `unsafe` code to inside our carefully-planned abstraction,
-and now our application code does not contain any `unsafe` blocks.
-
-This design requires that the application pass a `CriticalSection` token in:
-these tokens are only safely generated by `interrupt::free`, so by requiring
-one be passed in, we ensure we are operating inside a critical section, without
-having to actually do the lock ourselves. This guarantee is provided statically
-by the compiler: there won't be any runtime overhead associated with `cs`.
-If we had multiple counters, they could all be given the same `cs`, without
-requiring multiple nested critical sections.
-
-This also brings up an important topic for concurrency in Rust: the
-[`Send` and `Sync`] traits. To summarise the Rust book, a type is Send
-when it can safely be moved to another thread, while it is Sync when
-it can be safely shared between multiple threads. In an embedded context,
-we consider interrupts to be executing in a separate thread to the application
-code, so variables accessed by both an interrupt and the main code must be
-Sync.
+我们已经把我们的`unsafe`代码移进了精心安排的抽象中,现在我们的应用代码不包含任何`unsafe`块。
+
+这个设计要求应用传递一个`CriticalSection` token进来: 这些tokens仅由`interrupt::free`安全地产生,因此通过要求传递进一个`CriticalSection` token,我们确保我们正在一个临界区中操作,不用自己动手锁起来。这个保障由编译器静态地提供: 这将不会带来任何与`cs`有关的运行时消耗。如果我们有多个计数器,它们都可以被指定同一个`cs`,而不用要求多个嵌套的临界区。
+
+这也带来了Rust中关于并发的一个重要主题: [`Send` and `Sync`] traits。总结一下Rust book,当一个类型能够安全地被移动到另一个线程,它是Send,当一个类型能被安全地在多个线程间共享的时候,它是Sync。在一个嵌入式上下文中,我们认为中断是在应用代码的一个独立线程中执行的,因此在一个中断和main代码中都能被访问的变量必须是Sync。
[`Send` and `Sync`]: https://doc.rust-lang.org/nomicon/send-and-sync.html
-For most types in Rust, both of these traits are automatically derived for you
-by the compiler. However, because `CSCounter` contains an [`UnsafeCell`], it is
-not Sync, and therefore we could not make a `static CSCounter`: `static`
-variables _must_ be Sync, since they can be accessed by multiple threads.
+在Rust中的大多数类型,这两个traits都会由你的编译器为你自动地产生。然而,因为`CSCounter`包含了一个[`UnsafeCell`],它不是Sync,因此我们不能使用一个`static CSCounter`: `static` 变量 _必须_ 是Sync,因此它们能被多个线程访问。
[`UnsafeCell`]: https://doc.rust-lang.org/core/cell/struct.UnsafeCell.html
-To tell the compiler we have taken care that the `CSCounter` is in fact safe
-to share between threads, we implement the Sync trait explicitly. As with the
-previous use of critical sections, this is only safe on single-core platforms:
-with multiple cores, you would need to go to greater lengths to ensure safety.
+为了告诉编译器我们已经注意到`CSCounter`事实上在线程间共享是安全的,我们显式地实现了Sync trait。与之前使用的临界区一样,这只在单核平台上是安全的: 对于多核,你需要做更多的事来确保安全。
-## Mutexes
+## 互斥量(Mutexs)
-We've created a useful abstraction specific to our counter problem, but
-there are many common abstractions used for concurrency.
+我们已经为我们的计数器问题创造了一个有用的抽象,但是关于并发这里还存在许多通用的抽象。
-One such _synchronisation primitive_ is a mutex, short for mutual exclusion.
-These constructs ensure exclusive access to a variable, such as our counter. A
-thread can attempt to _lock_ (or _acquire_) the mutex, and either succeeds
-immediately, or blocks waiting for the lock to be acquired, or returns an error
-that the mutex could not be locked. While that thread holds the lock, it is
-granted access to the protected data. When the thread is done, it _unlocks_ (or
-_releases_) the mutex, allowing another thread to lock it. In Rust, we would
-usually implement the unlock using the [`Drop`] trait to ensure it is always
-released when the mutex goes out of scope.
+一个互斥量(mutex),互斥(mutual exclusion)的缩写,就是这样的一个 _同步原语_ 。这些构造确保了对一个变量的排他访问,比如我们的计数器。一个线程会尝试 _lock_ (或者 _acquire_) 互斥量,或者当互斥量不能被锁住时返回一个错误。当线程持有锁时,它有权访问被保护的数据,当线程工作完成了,它 _unlocks_ (或者 _releases_) 互斥量,允许其它线程锁住它。在Rust中,我们通常使用[`Drop`] trait实现unlock去确保当互斥量超出作用域时它总是被释放。
[`Drop`]: https://doc.rust-lang.org/core/ops/trait.Drop.html
-Using a mutex with interrupt handlers can be tricky: it is not normally
-acceptable for the interrupt handler to block, and it would be especially
-disastrous for it to block waiting for the main thread to release a lock,
-since we would then _deadlock_ (the main thread will never release the lock
-because execution stays in the interrupt handler). Deadlocking is not
-considered unsafe: it is possible even in safe Rust.
+将中断处理函数与一个互斥量一起使用可能有点棘手: 阻塞中断处理函数通常是不可接受的,如果它阻塞等待main线程去释放一个锁,那将是一场灾难。因为我们会 _死锁_ (因为执行停留在中断处理函数中,主线程将永远不会释放锁)。死锁被认为是不安全的: 即使在安全的Rust中这也是可能发生的。
-To avoid this behaviour entirely, we could implement a mutex which requires
-a critical section to lock, just like our counter example. So long as the
-critical section must last as long as the lock, we can be sure we have
-exclusive access to the wrapped variable without even needing to track
-the lock/unlock state of the mutex.
+为了完全避免这个行为,我们可以实现一个要求临界区的互斥量去锁住,就像我们的计数器例子一样。临界区的存在时间必须和锁存在的时间一样长,我们能确保我们对被封装的变量有排他式访问,甚至不需要跟踪互斥量的 lock/unlock 状态。
-This is in fact done for us in the `cortex_m` crate! We could have written
-our counter using it:
+实际上我们在 `cortex_m` crate中就是这么做的!我们可以用它来写入我们的计数器:
```rust,ignore
use core::cell::Cell;
@@ -360,53 +255,28 @@ fn main() -> ! {
#[interrupt]
fn timer() {
- // We still need to enter a critical section here to satisfy the Mutex.
+ // 这里我们仍然需要进入一个临界区去满足互斥量。
interrupt::free(|cs| COUNTER.borrow(cs).set(0));
}
```
-We're now using [`Cell`], which along with its sibling `RefCell` is used to
-provide safe interior mutability. We've already seen `UnsafeCell` which is
-the bottom layer of interior mutability in Rust: it allows you to obtain
-multiple mutable references to its value, but only with unsafe code. A `Cell`
-is like an `UnsafeCell` but it provides a safe interface: it only permits
-taking a copy of the current value or replacing it, not taking a reference,
-and since it is not Sync, it cannot be shared between threads. These
-constraints mean it's safe to use, but we couldn't use it directly in a
-`static` variable as a `static` must be Sync.
+我们现在使用了[`Cell`],它与它的兄弟`RefCell`一起被用于提供safe的内部可变性。我们已经见过`UnsafeCell`了,在Rust中它是内部可变性的底层: 它允许你去获得对某个值的多个可变引用,但是只能与不安全的代码一起工作。一个`Cell`像一个`UnsafeCell`一样但是它提供了一个安全的接口: 它只允许拷贝现在的值或者替换它,不允许获取一个引用,因此它不是Sync,它不能被在线程间共享。这些限制意味着它用起来是safe的,但是我们不能直接将它用于`static`变量因为一个`static`必须是Sync。
[`Cell`]: https://doc.rust-lang.org/core/cell/struct.Cell.html
-So why does the example above work? The `Mutex` implements Sync for any
-`T` which is Send — such as a `Cell`. It can do this safely because it only
-gives access to its contents during a critical section. We're therefore able
-to get a safe counter with no unsafe code at all!
+因此为什么上面的例子可以工作?`Mutex`对于任何是Send的`T`实现了Sync - 比如一个`Cell`。因为它只能在临界区对它的内容进行访问,所以它这么做是safe的。因此我们可以即使没有一点unsafe的代码我们也能获取一个safe的计数器!
-This is great for simple types like the `u32` of our counter, but what about
-more complex types which are not Copy? An extremely common example in an
-embedded context is a peripheral struct, which generally is not Copy.
-For that, we can turn to `RefCell`.
+对于我们的简单类型,像是我们的计数器的`u32`来说是很棒的,但是对于更复杂的不能拷贝的类型呢?在一个嵌入式上下文中一个极度常见的例子是一个外设结构体,通常它们不是Copy。针对那种情况,我们可以使用`RefCell`。
-## Sharing Peripherals
+## 共享外设
-Device crates generated using `svd2rust` and similar abstractions provide
-safe access to peripherals by enforcing that only one instance of the
-peripheral struct can exist at a time. This ensures safety, but makes it
-difficult to access a peripheral from both the main thread and an interrupt
-handler.
+使用`svd2rust`生成的设备crates和相似的抽象,通过强制要求同时只能存在一个外设结构体的实例,提供了对外设的安全的访问。这个确保了安全性,但是使得它很难从main线程和一个中断处理函数一起访问一个外设。
-To safely share peripheral access, we can use the `Mutex` we saw before. We'll
-also need to use [`RefCell`], which uses a runtime check to ensure only one
-reference to a peripheral is given out at a time. This has more overhead than
-the plain `Cell`, but since we are giving out references rather than copies,
-we must be sure only one exists at a time.
+为了安全地共享对外设的访问,我们能使用我们之前看到的`Mutex`。我们也将需要使用[`RefCell`],它使用一个运行时检查去确保对一个外设每次只有一个引用被给出。这个比纯`Cell`消耗更多,但是因为我们正给出引用而不是拷贝,我们必须确保每次只有一个引用存在。
[`RefCell`]: https://doc.rust-lang.org/core/cell/struct.RefCell.html
-Finally, we'll also have to account for somehow moving the peripheral into
-the shared variable after it has been initialised in the main code. To do
-this we can use the `Option` type, initialised to `None` and later set to
-the instance of the peripheral.
+最终,我们也必须考虑在main代码中初始化外设后,如何将外设移到共享变量中。为了做这个,我们使用`Option`类型,初始成`None`,之后设置成外设的实例。
```rust,ignore
use core::cell::RefCell;
@@ -418,35 +288,31 @@ static MY_GPIO: Mutex>> =
#[entry]
fn main() -> ! {
- // Obtain the peripheral singletons and configure it.
- // This example is from an svd2rust-generated crate, but
- // most embedded device crates will be similar.
+ // 获得外设的单例并配置它。这个例子来自一个svd2rust生成的crate,
+ // 但是大多数的嵌入式设备crates都相似。
let dp = stm32f405::Peripherals::take().unwrap();
let gpioa = &dp.GPIOA;
- // Some sort of configuration function.
- // Assume it sets PA0 to an input and PA1 to an output.
+ // 某个配置函数。假设它把PA0设置成一个输入和把PA1设置成一个输出。
configure_gpio(gpioa);
- // Store the GPIOA in the mutex, moving it.
+ // 把GPIOA存进互斥量中,移动它。
interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA)));
- // We can no longer use `gpioa` or `dp.GPIOA`, and instead have to
- // access it via the mutex.
+ // 我可以不再用`gpioa`或者`dp.GPIOA`,反而必须通过互斥量访问它。
- // Be careful to enable the interrupt only after setting MY_GPIO:
- // otherwise the interrupt might fire while it still contains None,
- // and as-written (with `unwrap()`), it would panic.
+ // 请注意,只有在设置MY_GPIO后才能使能中断: 要不然当MY_GPIO还是包含None的时候,
+ // 中断可能会发生,然后像上面写的那样操作(使用`unwrap()`),它将发生运行时恐慌。
set_timer_1hz();
let mut last_state = false;
loop {
- // We'll now read state as a digital input, via the mutex
+ // 我们现在将通过互斥量,读取其作为数字输入时的状态。
let state = interrupt::free(|cs| {
let gpioa = MY_GPIO.borrow(cs).borrow();
gpioa.as_ref().unwrap().idr.read().idr0().bit_is_set()
});
if state && !last_state {
- // Set PA1 high if we've seen a rising edge on PA0.
+ // 如果我们在PA0上已经看到了一个上升沿,拉高PA1。
interrupt::free(|cs| {
let gpioa = MY_GPIO.borrow(cs).borrow();
gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().set_bit());
@@ -458,40 +324,30 @@ fn main() -> ! {
#[interrupt]
fn timer() {
- // This time in the interrupt we'll just clear PA0.
+ // 这次在中断中,我们将清除PA0。
interrupt::free(|cs| {
- // We can use `unwrap()` because we know the interrupt wasn't enabled
- // until after MY_GPIO was set; otherwise we should handle the potential
- // for a None value.
+ // 我们可以使用`unwrap()` 因为我们知道直到MY_GPIO被设置后,中断都是禁用的;
+ // 否则我应该处理会出现一个None值的潜在可能
let gpioa = MY_GPIO.borrow(cs).borrow();
gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().clear_bit());
});
}
```
-That's quite a lot to take in, so let's break down the important lines.
+这需要理解的内容很多,所以让我们把重要的内容分解一下。
```rust,ignore
static MY_GPIO: Mutex>> =
Mutex::new(RefCell::new(None));
```
-Our shared variable is now a `Mutex` around a `RefCell` which contains an
-`Option`. The `Mutex` ensures we only have access during a critical section,
-and therefore makes the variable Sync, even though a plain `RefCell` would not
-be Sync. The `RefCell` gives us interior mutability with references, which
-we'll need to use our `GPIOA`. The `Option` lets us initialise this variable
-to something empty, and only later actually move the variable in. We cannot
-access the peripheral singleton statically, only at runtime, so this is
-required.
+我们的共享变量现在是一个包围了一个`RefCell`的`Mutex`,`RefCell`包含一个`Option`。`Mutex`确保只在一个临界区中的时候可以访问,因此使变量变成了Sync,甚至即使一个纯`RefCell`不是Sync。`RefCell`赋予了我们引用的内部可变性,我们将需要使用我们的`GPIOA`。`Option`让我们可以初始化这个变量成空的东西,只在随后实际移动变量进来。只有在运行时,我们才能静态地访问外设单例,因此这是必须的。
```rust,ignore
interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA)));
```
-Inside a critical section we can call `borrow()` on the mutex, which gives us
-a reference to the `RefCell`. We then call `replace()` to move our new value
-into the `RefCell`.
+在一个临界区中,我们可以在互斥量上调用`borrow()`,其给了我们一个指向`RefCell`的引用。然后我们调用`replace()`去移动我们的新值进来`RefCell`。
```rust,ignore
interrupt::free(|cs| {
@@ -500,18 +356,11 @@ interrupt::free(|cs| {
});
```
-Finally, we use `MY_GPIO` in a safe and concurrent fashion. The critical section
-prevents the interrupt firing as usual, and lets us borrow the mutex. The
-`RefCell` then gives us an `&Option`, and tracks how long it remains
-borrowed - once that reference goes out of scope, the `RefCell` will be updated
-to indicate it is no longer borrowed.
+最终,我们用一种安全和并发的方式使用`MY_GPIO`。临界区禁止了中断像往常一样发生,让我们借用互斥量。`RefCell`然后给了我们一个`&Option`并追踪它还要借用多久 - 一旦引用超出作用域,`RefCell`将会被更新去指出引用不再被借用。
-Since we can't move the `GPIOA` out of the `&Option`, we need to convert it to
-an `&Option<&GPIOA>` with `as_ref()`, which we can finally `unwrap()` to obtain
-the `&GPIOA` which lets us modify the peripheral.
+因为我不能把`GPIOA`移出`&Option`,我们需要用`as_ref()`将它转换成一个`&Option<&GPIOA>`,最终我们能使用`unwrap()`获得`&GPIOA`,其让我们可以修改外设。
-If we need a mutable reference to a shared resource, then `borrow_mut` and `deref_mut`
-should be used instead. The following code shows an example using the TIM2 timer.
+如果我们需要一个共享的资源的可变引用,那么`borrow_mut`和`deref_mut`应该被使用。下面的代码展示了一个使用TIM2计时器的例子。
```rust,ignore
use core::cell::RefCell;
@@ -528,9 +377,8 @@ fn main() -> ! {
let mut cp = cm::Peripherals::take().unwrap();
let dp = stm32f405::Peripherals::take().unwrap();
- // Some sort of timer configuration function.
- // Assume it configures the TIM2 timer, its NVIC interrupt,
- // and finally starts the timer.
+ // 某个计时器配置函数。假设它配置了TIM2计时器和它的NVIC中断,
+ // 最终启动计时器。
let tim = configure_timer_interrupt(&mut cp, dp);
interrupt::free(|cs| {
@@ -545,7 +393,7 @@ fn main() -> ! {
#[interrupt]
fn timer() {
interrupt::free(|cs| {
- if let Some(ref mut tim)) = G_TIM.borrow(cs).borrow_mut().deref_mut() {
+ if let Some(ref mut tim) = G_TIM.borrow(cs).borrow_mut().deref_mut() {
tim.start(1.hz());
}
});
@@ -553,55 +401,31 @@ fn timer() {
```
-Whew! This is safe, but it is also a little unwieldy. Is there anything else
-we can do?
+呼!这是安全的,但也有点笨拙。我们还能做些什么吗?
## RTIC
-One alternative is the [RTIC framework], short for Real Time Interrupt-driven Concurrency. It
-enforces static priorities and tracks accesses to `static mut` variables
-("resources") to statically ensure that shared resources are always accessed
-safely, without requiring the overhead of always entering critical sections and
-using reference counting (as in `RefCell`). This has a number of advantages such
-as guaranteeing no deadlocks and giving extremely low time and memory overhead.
+另一个方法是使用[RTIC框架],Real Time Interrupt-driven Concurrency的缩写。它强制执行静态优先级并追踪对`static mut`变量("资源")的访问去确保共享资源总是能被安全地访问,而不需要总是进入临界区和使用引用计数带来的消耗(如`RefCell`中所示)。这有许多好处,比如保证没有死锁且时间和内存的消耗极度低。
-[RTIC framework]: /~https://github.com/rtic-rs/cortex-m-rtic
+[RTIC框架]: /~https://github.com/rtic-rs/cortex-m-rtic
-The framework also includes other features like message passing, which reduces
-the need for explicit shared state, and the ability to schedule tasks to run at
-a given time, which can be used to implement periodic tasks. Check out [the
-documentation] for more information!
+这个框架也包括了其它的特性,像是消息传递(message passing),消息传递减少了对显式共享状态的需要,还提供了在一个给定时间调度任务去运行的功能,这功能能被用来实现周期性的任务。看下[文档]可以知道更多的信息!
-[the documentation]: https://rtic.rs
+[文档]: https://rtic.rs
-## Real Time Operating Systems
+## 实时操作系统
-Another common model for embedded concurrency is the real-time operating system
-(RTOS). While currently less well explored in Rust, they are widely used in
-traditional embedded development. Open source examples include [FreeRTOS] and
-[ChibiOS]. These RTOSs provide support for running multiple application threads
-which the CPU swaps between, either when the threads yield control (called
-cooperative multitasking) or based on a regular timer or interrupts (preemptive
-multitasking). The RTOS typically provide mutexes and other synchronisation
-primitives, and often interoperate with hardware features such as DMA engines.
+与嵌入式并发有关的另一个模型是实时操作系统(RTOS)。虽然现在在Rust中的研究较少,但是它们被广泛用于传统的嵌入式开发。开源的例子包括[FreeRTOS]和[ChibiOS](译者注: 目前有个纯Rust实现的[Tock](https://www.tockos.org/))。这些RTOSs提供对运行多个应用线程的支持,CPU在这些线程间进行切换,切换要么发生在当线程让出控制权的时候(被称为非抢占式多任务),要么是基于一个常规计时器或者中断(抢占式多任务)。RTOS通常提供互斥量或者其它的同步原语,经常与硬件功能相互使用,比如DMA引擎。
[FreeRTOS]: https://freertos.org/
[ChibiOS]: http://chibios.org/
-At the time of writing, there are not many Rust RTOS examples to point to,
-but it's an interesting area so watch this space!
+在撰写本文时,没有太多的Rust RTOS示例可供参考,但这是一个有趣的领域,所以请关注这块!
-## Multiple Cores
+## 多个核心
-It is becoming more common to have two or more cores in embedded processors,
-which adds an extra layer of complexity to concurrency. All the examples using
-a critical section (including the `cortex_m::interrupt::Mutex`) assume the only
-other execution thread is the interrupt thread, but on a multi-core system
-that's no longer true. Instead, we'll need synchronisation primitives designed
-for multiple cores (also called SMP, for symmetric multi-processing).
+在嵌入式处理器中有两个或者多个核心很正常,其为并发添加了额外一层复杂性。所有使用临界区的例子(包括`cortex_m::interrupt::Mutex`)都假设了另一个执行的线程仅是中断线程,但是在一个多核系统中,这不再是正确的假设。反而,我们将需要为多核设计的同步原语(也被叫做SMP,symmetric multi-processing的缩写)。
-These typically use the atomic instructions we saw earlier, since the
-processing system will ensure that atomicity is maintained over all cores.
+我们之前看到的,这些通常使用原子指令,因为处理系统将确保原子性在所有的核中都保持着。
-Covering these topics in detail is currently beyond the scope of this book,
-but the general patterns are the same as for the single-core case.
+覆盖这些主题的细节已经超出了本书的范围,但是常规的模式与单核的相似。
diff --git a/src/design-patterns/hal/checklist.md b/src/design-patterns/hal/checklist.md
index 6fdf98b7..10b842de 100644
--- a/src/design-patterns/hal/checklist.md
+++ b/src/design-patterns/hal/checklist.md
@@ -1,17 +1,17 @@
-# HAL Design Patterns Checklist
+# HAL设计检查清单
-- **Naming** *(crate aligns with Rust naming conventions)*
- - [ ] The crate is named appropriately ([C-CRATE-NAME])
-- **Interoperability** *(crate interacts nicely with other library functionality)*
- - [ ] Wrapper types provide a destructor method ([C-FREE])
- - [ ] HALs reexport their register access crate ([C-REEXPORT-PAC])
- - [ ] Types implement the `embedded-hal` traits ([C-HAL-TRAITS])
-- **Predictability** *(crate enables legible code that acts how it looks)*
- - [ ] Constructors are used instead of extension traits ([C-CTOR])
-- **GPIO Interfaces** *(GPIO Interfaces follow a common pattern)*
- - [ ] Pin types are zero-sized by default ([C-ZST-PIN])
- - [ ] Pin types provide methods to erase pin and port ([C-ERASED-PIN])
- - [ ] Pin state should be encoded as type parameters ([C-PIN-STATE])
+- **命名** *(crate要符合Rust命名规则)*
+ - [ ] crate被恰当地命名 ([C-CRATE-NAME])
+- **互用性** *(crate要很好地与其它的库功能交互)*
+ - [ ] 封装类型提供一种析构方法 ([C-FREE])
+ - [ ] HALs重新导出了它们的寄存器访问crate ([C-REEXPORT-PAC])
+ - [ ] 类型实现了 `embedded-hal` traits ([C-HAL-TRAITS])
+- **可预见性** *(crate的代码清晰可读,行为和看起来的一样)*
+ - [ ] 使用构造函数而不是扩展traits ([C-CTOR])
+- **GPIO接口** *(GPIO接口要遵循一个公共的模式)*
+ - [ ] Pin类型默认是零大小类型 ([C-ZST-PIN])
+ - [ ] Pin类型提供擦除管脚和端口的方法 ([C-ERASED-PIN])
+ - [ ] Pin状态应该被编码为类型参数 ([C-PIN-STATE])
[C-CRATE-NAME]: naming.html#c-crate-name
diff --git a/src/design-patterns/hal/gpio.md b/src/design-patterns/hal/gpio.md
index 6582070c..221cf999 100644
--- a/src/design-patterns/hal/gpio.md
+++ b/src/design-patterns/hal/gpio.md
@@ -1,16 +1,13 @@
-# Recommendations for GPIO Interfaces
+# 关于GPIO接口的建议
-## Pin types are zero-sized by default (C-ZST-PIN)
+## Pin类型默认是零大小的(C-ZST-PIN)
-GPIO Interfaces exposed by the HAL should provide dedicated zero-sized types for
-each pin on every interface or port, resulting in a zero-cost GPIO abstraction
-when all pin assignments are statically known.
+由HAL暴露的GPIO接口应该为所有接口或者端口上的每一个管脚提供一个专用的零大小类型,从而当所有的管脚分配静态已知时,提供一个零开销抽象。
-Each GPIO Interface or Port should implement a `split` method returning a
-struct with every pin.
+每个GPIO接口或者端口应该实现一个`split`方法,它返回一个拥有所有管脚的结构体。
-Example:
+案例:
```rust
pub struct PA0;
@@ -37,15 +34,14 @@ pub struct PortAPins {
```
-## Pin types provide methods to erase pin and port (C-ERASED-PIN)
+## 管脚类型提供方法去擦除管脚和端口(C-ERASED-PIN)
-Pins should provide type erasure methods that move their properties from
-compile time to runtime, and allow more flexibility in applications.
+从编译时到运行时,管脚都应该提供可以改变属性的类型擦出方法,允许在应用中有更多的灵活性。
-Example:
+案例:
```rust
-/// Port A, pin 0.
+/// 端口 A, 管脚 0。
pub struct PA0;
impl PA0 {
@@ -54,9 +50,9 @@ impl PA0 {
}
}
-/// A pin on port A.
+/// 端口A上的A管脚。
pub struct PA {
- /// The pin number.
+ /// 管脚号。
pin: u8,
}
@@ -72,7 +68,8 @@ impl PA {
pub struct Pin {
port: Port,
pin: u8,
- // (these fields can be packed to reduce the memory footprint)
+ // (这些字段)
+ // (这些字段可以打包以减少内存占用)
}
enum Port {
@@ -84,23 +81,17 @@ enum Port {
```
-## Pin state should be encoded as type parameters (C-PIN-STATE)
+## 管脚状态应该被编码成类型参数 (C-PIN-STATE)
-Pins may be configured as input or output with different characteristics
-depending on the chip or family. This state should be encoded in the type system
-to prevent use of pins in incorrect states.
+取决于芯片或者芯片系列,管脚可能被配置为具有不同特性的输出或者输入。这个状态应该编码进类型系统中以避免在错误的状态中使用管脚。
-Additional, chip-specific state (eg. drive strength) may also be encoded in this
-way, using additional type parameters.
+另外,也可以用这个方法使用额外的类型参数编码芯片特定的状态(eg. 驱动强度)。
-Methods for changing the pin state should be provided as `into_input` and
-`into_output` methods.
+用来改变管脚状态的方法应该被实现成`into_input`和`into_output`方法。
-Additionally, `with_{input,output}_state` methods should be provided that
-temporarily reconfigure a pin in a different state without moving it.
+另外,应该提供`with_{input,output}_state`方法,在一个不同的状态中临时配置一个管脚而不是移动它。
-The following methods should be provided for every pin type (that is, both
-erased and non-erased pin types should provide the same API):
+应该为每个的管脚类型提供下列的方法(也就是说,已擦除和未擦除的管脚类型应该提供一样的API):
* `pub fn into_input(self, input: N) -> Pin`
* `pub fn into_output(self, output: N) -> Pin`
@@ -119,12 +110,9 @@ erased and non-erased pin types should provide the same API):
) -> R
```
+管脚状态应该用sealed traits来绑定。HAL的用户不必添加他们自己的状态。这个traits能提供HAL特定的方法,实现管脚状态API需要这些方法。
-Pin state should be bounded by sealed traits. Users of the HAL should have no
-need to add their own state. The traits can provide HAL-specific methods
-required to implement the pin state API.
-
-Example:
+案例:
```rust
# use std::marker::PhantomData;
@@ -201,5 +189,5 @@ impl PA1 {
}
}
-// Same for `PA` and `Pin`, and other pin types.
+// 对于`PA`和`Pin`一样的,对于其它管脚类型来说也是。
```
diff --git a/src/design-patterns/hal/index.md b/src/design-patterns/hal/index.md
index c493a873..eded1f29 100644
--- a/src/design-patterns/hal/index.md
+++ b/src/design-patterns/hal/index.md
@@ -1,15 +1,12 @@
-# HAL Design Patterns
+# HAL设计模式
-This is a set of common and recommended patterns for writing hardware
-abstraction layers (HALs) for microcontrollers in Rust. These patterns are
-intended to be used in addition to the existing [Rust API Guidelines] when
-writing HALs for microcontrollers.
+这是一组关于使用Rust为微控制器写硬件抽象层的常见的和推荐的模式。当为微控制器编写HALs时,除了现有的 [Rust API 指南] 外,也可以使用这些模式。
-[Rust API Guidelines]: https://rust-lang.github.io/api-guidelines/
+[Rust API 指南]: https://rust-lang.github.io/api-guidelines/
-[Checklist](checklist.md)
+[检查清单](checklist.md)
-- [Naming](naming.md)
-- [Interoperability](interoperability.md)
-- [Predictability](predictability.md)
+- [命名](naming.md)
+- [互用性](interoperability.md)
+- [可预见性](predictability.md)
- [GPIO](gpio.md)
diff --git a/src/design-patterns/hal/interoperability.md b/src/design-patterns/hal/interoperability.md
index 12049020..9263a278 100644
--- a/src/design-patterns/hal/interoperability.md
+++ b/src/design-patterns/hal/interoperability.md
@@ -1,22 +1,16 @@
-# Interoperability
+# 互用性
-## Wrapper types provide a destructor method (C-FREE)
+## 封装类型提供一个析构方法 (C-FREE)
-Any non-`Copy` wrapper type provided by the HAL should provide a `free` method
-that consumes the wrapper and returns back the raw peripheral (and possibly
-other objects) it was created from.
+任何由HAL提供的非`Copy`封装类型应该提供一个`free`方法,这个方法消费封装类且返回最初生成它的外设(可能是其它对象)。
-The method should shut down and reset the peripheral if necessary. Calling `new`
-with the raw peripheral returned by `free` should not fail due to an unexpected
-state of the peripheral.
+如果有必要,方法应该关闭和重置外设。使用由`free`返回的原始外设去调用`new`不应该由于设备的意外状态而失败,
-If the HAL type requires other non-`Copy` objects to be constructed (for example
-I/O pins), any such object should be released and returned by `free` as well.
-`free` should return a tuple in that case.
+如果HAL类型要求构造其它的非`Copy`对象(比如 I/O 管脚),任何这样的对象应该也由`free`返回和释放。在这种情况下`free`应该返回一个元组。
-For example:
+比如:
```rust
# pub struct TIMER0;
@@ -34,24 +28,19 @@ impl Timer {
```
-## HALs reexport their register access crate (C-REEXPORT-PAC)
+## HALs重新导出它们的寄存器访问crate(C-REEXPORT-PAC)
-HALs can be written on top of [svd2rust]-generated PACs, or on top of other
-crates that provide raw register access. HALs should always reexport the
-register access crate they are based on in their crate root.
+可以在[svd2rust]生成的PACs之上,或在其它纯寄存器访问的crates之上编写HALs。HALs需要在crate root中重新导出它们所基于的寄存器访问crate
-A PAC should be reexported under the name `pac`, regardless of the actual name
-of the crate, as the name of the HAL should already make it clear what PAC is
-being accessed.
+一个PAC应该被重新导出在`pac`名下,无论这个crate实际的名字是什么,因为HAL的名字应该已经明确了正被访问的是什么PAC 。
[svd2rust]: /~https://github.com/rust-embedded/svd2rust
-## Types implement the `embedded-hal` traits (C-HAL-TRAITS)
+## 类型实现`embedded-hal` traits (C-HAL-TRAITS)
-Types provided by the HAL should implement all applicable traits provided by the
-[`embedded-hal`] crate.
+HAL提供的类型应该实现所有的由[`embedded-hal`] crate提供的能用的traits。
-Multiple traits may be implemented for the same type.
+同个类型可能实现多个traits。
[`embedded-hal`]: /~https://github.com/rust-embedded/embedded-hal
diff --git a/src/design-patterns/hal/naming.md b/src/design-patterns/hal/naming.md
index 65877d3e..5b77b488 100644
--- a/src/design-patterns/hal/naming.md
+++ b/src/design-patterns/hal/naming.md
@@ -1,9 +1,7 @@
-# Naming
+# 命名
-## The crate is named appropriately (C-CRATE-NAME)
+## crate要被恰当地命名(C-CRATE-NAME)
-HAL crates should be named after the chip or family of chips they aim to
-support. Their name should end with `-hal` to distinguish them from register
-access crates. The name should not contain underscores (use dashes instead).
+HAL crates应该在目标支持的芯片或者芯片系列之后被命名。它们的名字应该以`-hal`结尾,为了将它们与PAC区分开来。名字不应该包含下划线(请改用破折号)。
diff --git a/src/design-patterns/hal/predictability.md b/src/design-patterns/hal/predictability.md
index e3181856..5cfddb7e 100644
--- a/src/design-patterns/hal/predictability.md
+++ b/src/design-patterns/hal/predictability.md
@@ -1,24 +1,16 @@
-# Predictability
+# 可预见性
-## Constructors are used instead of extension traits (C-CTOR)
+## 使用构造函数而不是扩展traits
-All peripherals to which the HAL adds functionality should be wrapped in a new
-type, even if no additional fields are required for that functionality.
+所有由HAL添加功能的外设应该被封装进一个新类型,即使该功能不需要额外的字段。
-Extension traits implemented for the raw peripheral should be avoided.
+应该避免为基本外设扩展traits。
-## Methods are decorated with `#[inline]` where appropriate (C-INLINE)
+## 方法在适当的地方用`#[inline]`修饰
-The Rust compiler does not by default perform full inlining across crate
-boundaries. As embedded applications are sensitive to unexpected code size
-increases, `#[inline]` should be used to guide the compiler as follows:
-
-* All "small" functions should be marked `#[inline]`. What qualifies as "small"
- is subjective, but generally all functions that are expected to compile down
- to single-digit instruction sequences qualify as small.
-* Functions that are very likely to take constant values as parameters should be
- marked as `#[inline]`. This enables the compiler to compute even complicated
- initialization logic at compile time, provided the function inputs are known.
+Rust编译器默认不会越过crate边界执行完全内联。因为嵌入式应用对于不可预期的代码大小的增加很敏感,`#[inline]`应该如下所示用来指导编译器:
+* 所有的"小"函数应该被标记`#[inline]`。什么是"小"是主观的,但是通常所有有可能被编译成一位数的指令序列(single-digit instruction sequences)都可以被视为"小"。
+* 非常有可能把一个常量数值作为参数的函数应该被标记为`#[inline]`。这让编译器在编译时就可以进行计算甚至是复杂的初始化逻辑,前提是函数输入是已知的。
diff --git a/src/design-patterns/index.md b/src/design-patterns/index.md
index 32153faf..1222d89d 100644
--- a/src/design-patterns/index.md
+++ b/src/design-patterns/index.md
@@ -1,3 +1,3 @@
-# Design Patterns
+# 设计模式
-This chapter aims to collect various useful design patterns for embedded Rust.
+这个章节的目标是为嵌入式Rust收集不同的有用的设计模式。
diff --git a/src/interoperability/c-with-rust.md b/src/interoperability/c-with-rust.md
index 9c56c6aa..00343719 100644
--- a/src/interoperability/c-with-rust.md
+++ b/src/interoperability/c-with-rust.md
@@ -1,24 +1,24 @@
-# A little C with your Rust
+# 使用C的Rust
-Using C or C++ inside of a Rust project consists of two major parts:
+要在一个Rust项目中使用C或者C++,主要有两个步骤:
-- Wrapping the exposed C API for use with Rust
-- Building your C or C++ code to be integrated with the Rust code
++ 用Rust封装要暴露出来使用的C API
++ 编译要和Rust代码集成在一起的C或者C++代码
-As C++ does not have a stable ABI for the Rust compiler to target, it is recommended to use the `C` ABI when combining Rust with C or C++.
+因为对于Rust编译器来说,C++没有一个稳定的ABI,当要将Rust和C或者C++结合时,建议优先选择`C`。
-## Defining the interface
+## 定义接口
-Before consuming C or C++ code from Rust, it is necessary to define (in Rust) what data types and function signatures exist in the linked code. In C or C++, you would include a header (`.h` or `.hpp`) file which defines this data. In Rust, it is necessary to either manually translate these definitions to Rust, or use a tool to generate these definitions.
+在Rust消费C或者C++代码之前,必须定义(在Rust中定义),在要被链接的代码中存在什么数据类型和函数签名。在C或者C++中,你要包含一个头文件(`.h`或者`.hpp`),其定义了这个数据。而在Rust中,必须手动地将这些定义翻译成Rust,或者使用一个工具去生成这些定义。
-First, we will cover manually translating these definitions from C/C++ to Rust.
+首先,我们将介绍如何将这些定义从C/C++手动地转换为Rust。
-### Wrapping C functions and Datatypes
+### 封装C函数和数据类型
-Typically, libraries written in C or C++ will provide a header file defining all types and functions used in public interfaces. An example file may look like this:
+通常,用C或者C++写的库会提供一个头文件,头文件定义了所有的类型和用于公共接口的函数。如下是一个示例文件:
```C
-/* File: cool.h */
+/* 文件: cool.h */
typedef struct CoolStruct {
int x;
int y;
@@ -27,7 +27,7 @@ typedef struct CoolStruct {
void cool_function(int i, char c, CoolStruct* cs);
```
-When translated to Rust, this interface would look as such:
+当翻译成Rust时,这个接口将看起来像是:
```rust,ignore
/* File: cool_bindings.rs */
@@ -46,27 +46,27 @@ extern "C" {
}
```
-Let's take a look at this definition one piece at a time, to explain each of the parts.
+让我们一次看一个语句,来解释每个部分。
```rust,ignore
#[repr(C)]
pub struct CoolStruct { ... }
```
-By default, Rust does not guarantee order, padding, or the size of data included in a `struct`. In order to guarantee compatibility with C code, we include the `#[repr(C)]` attribute, which instructs the Rust compiler to always use the same rules C does for organizing data within a struct.
+默认,Rust不会保证包含在`struct`中的数据的大小,填充,或者顺序。为了保证与C代码兼容,我们使用`#[repr(C)]`属性,它指示Rust编译器总是使用和C一样的规则去组织一个结构体中的数据。
```rust,ignore
pub x: cty::c_int,
pub y: cty::c_int,
```
-Due to the flexibility of how C or C++ defines an `int` or `char`, it is recommended to use primitive data types defined in `cty`, which will map types from C to types in Rust.
+由于C或者C++定义一个`int`或者`char`的方式很灵活,所以建议使用在`cty`中定义的基础类型,它将类型从C映射到Rust中的类型。
```rust,ignore
extern "C" { pub fn cool_function( ... ); }
```
-This statement defines the signature of a function that uses the C ABI, called `cool_function`. By defining the signature without defining the body of the function, the definition of this function will need to be provided elsewhere, or linked into the final library or binary from a static library.
+这个语句定义了一个使用C ABI的函数的签名,叫做`cool_function`。因为只定义了签名而没有定义函数的主体,所以这个函数的定义将需要在其它地方定义,或者从一个静态库链接进最终的库或者一个二进制文件中。
```rust,ignore
i: cty::c_int,
@@ -74,55 +74,53 @@ This statement defines the signature of a function that uses the C ABI, called `
cs: *mut CoolStruct
```
-Similar to our datatype above, we define the datatypes of the function arguments using C-compatible definitions. We also retain the same argument names, for clarity.
+与我们上面的数据类型一样,我们使用C兼容的定义去定义函数参数的数据类型。为了清晰可见,我们还保留了相同的参数名。
-We have one new type here, `*mut CoolStruct`. As C does not have a concept of Rust's references, which would look like this: `&mut CoolStruct`, we instead have a raw pointer. As dereferencing this pointer is `unsafe`, and the pointer may in fact be a `null` pointer, care must be taken to ensure the guarantees typical of Rust when interacting with C or C++ code.
+这里我们有了一个新类型,`*mut CoolStruct` 。因为C没有Rust中像 `&mut CoolStruct` 这样的引用,替代的是一个裸指针。所以解引用这个指针是`unsafe`的,因为这个指针实际上可能是一个`null`指针,因此当与C或者C++代码交互时必须要小心对待那些Rust做出的安全保证。
-### Automatically generating the interface
+### 自动产生接口
-Rather than manually generating these interfaces, which may be tedious and error prone, there is a tool called [bindgen] which will perform these conversions automatically. For instructions of the usage of [bindgen], please refer to the [bindgen user's manual], however the typical process consists of the following:
+有一个叫做[bindgen]的工具,它可以自动执行这些转换,而不用手动生成这些接口,手动进行这样的操作非常繁琐且容易出错。关于[bindgen]的使用指令,可以参考[bindgen user's manual],常用的步骤大致如下:
-1. Gather all C or C++ headers defining interfaces or datatypes you would like to use with Rust.
-2. Write a `bindings.h` file, which `#include "..."`'s each of the files you gathered in step one.
-3. Feed this `bindings.h` file, along with any compilation flags used to compile
- your code into `bindgen`. Tip: use `Builder.ctypes_prefix("cty")` /
- `--ctypes-prefix=cty` and `Builder.use_core()` / `--use-core` to make the generated code `#![no_std]` compatible.
-4. `bindgen` will produce the generated Rust code to the output of the terminal window. This file may be piped to a file in your project, such as `bindings.rs`. You may use this file in your Rust project to interact with C/C++ code compiled and linked as an external library. Tip: don't forget to use the [`cty`](https://crates.io/crates/cty) crate if your types in the generated bindings are prefixed with `cty`.
+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 。
[bindgen]: /~https://github.com/rust-lang/rust-bindgen
[bindgen user's manual]: https://rust-lang.github.io/rust-bindgen/
-## Building your C/C++ code
+## 编译你的 C/C++ 代码
-As the Rust compiler does not directly know how to compile C or C++ code (or code from any other language, which presents a C interface), it is necessary to compile your non-Rust code ahead of time.
+因为Rust编译器并不直接知道如何编译C或者C++代码(或者从其它语言来的代码,其提供了一个C接口),所以必须要静态编译你的非Rust代码。
-For embedded projects, this most commonly means compiling the C/C++ code to a static archive (such as `cool-library.a`), which can then be combined with your Rust code at the final linking step.
+对于嵌入式项目,这通常意味着把C/C++代码编译成一个静态库文档(比如 `cool-library.a`),然后其能在最后链接阶段与你的Rust代码组合起来。
-If the library you would like to use is already distributed as a static archive, it is not necessary to rebuild your code. Just convert the provided interface header file as described above, and include the static archive at compile/link time.
+如果你要使用的库已经作为一个静态库文档被发布,那就没必要重新编译你的代码。只需按照上面所述转换提供的接口头文件,且在编译/链接时包含静态库文档。
-If your code exists as a source project, it will be necessary to compile your C/C++ code to a static library, either by triggering your existing build system (such as `make`, `CMake`, etc.), or by porting the necessary compilation steps to use a tool called the `cc` crate. For both of these steps, it is necessary to use a `build.rs` script.
+如果你的代码作为一个源项目(source project)存在,将你的C/C++代码编译成一个静态库将是必须的,要么通过使用你现存的编译系统(比如 `make`,`CMake`,等等),要么通过使用一个被叫做`cc` crate的工具移植必要的编译步骤。关于这两个,都必须使用一个`build.rs`脚本。
-### Rust `build.rs` build scripts
+### Rust的 `build.rs` 编译脚本
-A `build.rs` script is a file written in Rust syntax, that is executed on your compilation machine, AFTER dependencies of your project have been built, but BEFORE your project is built.
+一个 `build.rs` 脚本是一个用Rust语法编写的文件,它被运行在你的编译机器上,发生在你项目的依赖项被编译**之后**,但是在你的项目被编译**之前** 。
-The full reference may be found [here](https://doc.rust-lang.org/cargo/reference/build-scripts.html). `build.rs` scripts are useful for generating code (such as via [bindgen]), calling out to external build systems such as `Make`, or directly compiling C/C++ through use of the `cc` crate.
+可能能在[这里](https://doc.rust-lang.org/cargo/reference/build-scripts.html)发现完整的参考。`build.rs` 脚本能用来生成代码(比如通过[bindgen]),调用外部编译系统,比如`Make`,或者直接通过使用`cc` crate来直接编译C/C++ 。
-### Triggering external build systems
+### 使用外部编译系统
-For projects with complex external projects or build systems, it may be easiest to use [`std::process::Command`] to "shell out" to your other build systems by traversing relative paths, calling a fixed command (such as `make library`), and then copying the resulting static library to the proper location in the `target` build directory.
+对于有复杂的外部项或者编译系统的项目,使用[`std::process::Command`]通过遍历相对路径来向其它编译系统"输出",调用一个固定的命令(比如 `make library`),然后拷贝最终的静态库到`target`编译文件夹中恰当的位置,可能是最简单的方法。
-While your crate may be targeting a `no_std` embedded platform, your `build.rs` executes only on machines compiling your crate. This means you may use any Rust crates which will run on your compilation host.
+虽然你的crate目标可能是一个`no_std`嵌入式平台,但你的`build.rs`只运行在负责编译你的crate的机器上。这意味着你能使用任何Rust crates,其将运行在你的编译主机上。
[`std::process::Command`]: https://doc.rust-lang.org/std/process/struct.Command.html
-### Building C/C++ code with the `cc` crate
+### 使用`cc` crate构建C/C++代码
-For projects with limited dependencies or complexity, or for projects where it is difficult to modify the build system to produce a static library (rather than a final binary or executable), it may be easier to instead utilize the [`cc` crate], which provides an idiomatic Rust interface to the compiler provided by the host.
+对于具有有限的依赖项或者复杂度的项目,或者对于那些难以修改编译系统去生成一个静态库(而不是一个二进制文件或者可执行文件)的项目,使用[`cc` crate]可能更容易,它提供了一个符合Rust语法的接口,这个接口是关于主机提供的编译器的。
[`cc` crate]: /~https://github.com/alexcrichton/cc-rs
-In the simplest case of compiling a single C file as a dependency to a static library, an example `build.rs` script using the [`cc` crate] would look like this:
+在把一个C文件编译成一个静态库的依赖项的最简单的场景下,可以使用[`cc` crate],示例`build.rs`脚本看起来像这样:
```rust,ignore
fn main() {
@@ -132,4 +130,4 @@ fn main() {
}
```
-The `build.rs` is placed at the root of the package. Then `cargo build` will compile and execute it before the build of the package. A static archive named `libfoo.a` is generated and placed in the `target` directory.
+要把`build.rs`放在包的根目录下.然后`cargo build`会在构建包之前编译和执行它.一个静态的名为`libfoo.a`的归档文件会生成并被放在`target`文件夹中.
diff --git a/src/interoperability/index.md b/src/interoperability/index.md
index 267bd678..7978c134 100644
--- a/src/interoperability/index.md
+++ b/src/interoperability/index.md
@@ -1,28 +1,18 @@
-# Interoperability
+# 互操性
-Interoperability between Rust and C code is always dependent
-on transforming data between the two languages.
-For this purpose, there is a dedicated module
-in the `stdlib` called
+Rust和C代码之间的互操性始终依赖于数据在两个语言间的转换.为了互操性,在`stdlib`中有一个专用的模块,叫作
[`std::ffi`](https://doc.rust-lang.org/std/ffi/index.html).
-`std::ffi` provides type definitions for C primitive types,
-such as `char`, `int`, and `long`.
-It also provides some utility for converting more complex
-types such as strings, mapping both `&str` and `String`
-to C types that are easier and safer to handle.
+`std::ffi`提供了与C基础类型对应的类型定义,比如`char`, `int`,和`long`.
+它也提供了一些工具用于更复杂的类型之间的转换,比如字符串,可以把`&str`和`String`映射成更容易和安全处理的C类型.
-As of Rust 1.30,
-functionalities of `std::ffi` are available
-in either `core::ffi` or `alloc::ffi`
-depending on whether or not memory allocation is involved.
-The [`cty`] crate and the [`cstr_core`] crate
-also offer similar functionalities.
+从Rust 1.30以来,`std::ffi`的功能也出现在`core::ffi`或者`alloc::ffi`中,取决于是否涉及到内存分配.
+[`cty`]库和[`cstr_core`]库也提供了相同的功能.
[`cstr_core`]: https://crates.io/crates/cstr_core
[`cty`]: https://crates.io/crates/cty
-| Rust type | Intermediate | C type |
+| Rust类型 | 间接 | C类型 |
|----------------|--------------|----------------|
| `String` | `CString` | `char *` |
| `&str` | `CStr` | `const char *` |
@@ -30,11 +20,8 @@ also offer similar functionalities.
| `u32` or `u64` | `c_uint` | `unsigned int` |
| etc | ... | ... |
-A value of a C primitive type can be used
-as one of the corresponding Rust type and vice versa,
-since the former is simply a type alias of the latter.
-For example, the following code compiles on platforms
-where `unsigned int` is 32-bit long.
+一个C基本类型的值可以被用来作为相关的Rust类型的值,反之亦然,因此前者仅仅是后者的一个类型伪名.
+比如,下列的代码可以在`unsigned int`是32位宽的平台上编译.
```rust,ignore
fn foo(num: u32) {
@@ -43,22 +30,19 @@ fn foo(num: u32) {
}
```
-## Interoperability with other build systems
+## 与其它编译系统的互用性
-A common requirement for including Rust in your embedded project is combining
-Cargo with your existing build system, such as make or cmake.
+在嵌入式项目中引入Rust的一个常见需求是,把Cargo结合进你现存的编译系统中,比如make或者cmake。
-We are collecting examples and use cases for this on our issue tracker in
-[issue #61].
+在[issue #61]的issue tracker上,我们正在为这个需求收集例子和用例。
[issue #61]: /~https://github.com/rust-embedded/book/issues/61
-## Interoperability with RTOSs
-Integrating Rust with an RTOS such as FreeRTOS or ChibiOS is still a work in
-progress; especially calling RTOS functions from Rust can be tricky.
+## 与RTOSs的互操性
-We are collecting examples and use cases for this on our issue tracker in
-[issue #62].
+将Rust和一个RTOS集成在一起,比如FreeRTOS或者ChibiOS仍然在进行中; 尤其是从Rust调用RTOS函数可能很棘手。
+
+在[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 5e596f02..a6f4b804 100644
--- a/src/interoperability/rust-with-c.md
+++ b/src/interoperability/rust-with-c.md
@@ -1,55 +1,40 @@
-# A little Rust with your C
+# 使用Rust的C
-Using Rust code inside a C or C++ project mostly consists of two parts.
+在C或者C++中使用Rust代码通常由两部分组成。
-- Creating a C-friendly API in Rust
-- Embedding your Rust project into an external build system
+- 用Rust生成一个C友好的API
+- 将你的Rust项目嵌入一个外部的编译系统
-Apart from `cargo` and `meson`, most build systems don't have native Rust support.
-So you're most likely best off just using `cargo` for compiling your crate and
-any dependencies.
+除了`cargo`和`meson`,大多数编译系统没有原生Rust支持。因此你最好只用`cargo`编译你的crate和依赖。
-## Setting up a project
+## 设置一个项目
-Create a new `cargo` project as usual.
-
-There are flags to tell `cargo` to emit a systems library, instead of
-its regular rust target.
-This also allows you to set a different output name for your library,
-if you want it to differ from the rest of your crate.
+像往常一样创建一个新的`cargo`项目。有一些标志可以告诉`cargo`去生成一个系统库,而不是常规的rust目标文件。如果你想要它与crate的其它部分不一样,你也可以为你的库设置一个不同的输出名。
```toml
[lib]
name = "your_crate"
-crate-type = ["cdylib"] # Creates dynamic lib
-# crate-type = ["staticlib"] # Creates static lib
+crate-type = ["cdylib"] # 生成动态链接库
+# crate-type = ["staticlib"] # 生成静态链接库
```
-## Building a `C` API
+## 构建一个`C` API
-Because C++ has no stable ABI for the Rust compiler to target, we use `C` for
-any interoperability between different languages. This is no exception when using Rust
-inside of C and C++ code.
+因为对于Rust编译器来说,C++没有稳定的ABI,因此对于不同语言间的互操性我们使用`C`。在C和C++代码的内部使用Rust时也不例外。
### `#[no_mangle]`
-The Rust compiler mangles symbol names differently than native code linkers expect.
-As such, any function that Rust exports to be used outside of Rust needs to be told
-not to be mangled by the compiler.
+Rust对符号名的修饰与主机的代码链接器所期望的不同。因此,需要告知任何被Rust导出到Rust外部去使用的函数不要被编译器修饰。
### `extern "C"`
-By default, any function you write in Rust will use the
-Rust ABI (which is also not stabilized).
-Instead, when building outwards facing FFI APIs we need to
-tell the compiler to use the system ABI.
+默认,任何用Rust写的函数将使用Rust ABI(这也不稳定)。当编译面向外部的FFI APIs时,我们需要告诉编译器去使用系统ABI 。
-Depending on your platform, you might want to target a specific ABI version, which are
-documented [here](https://doc.rust-lang.org/reference/items/external-blocks.html).
+取决于你的平台,你可能想要针对一个特定的ABI版本,其记录在[这里](https://doc.rust-lang.org/reference/items/external-blocks.html)。
---
-Putting these parts together, you get a function that looks roughly like this.
+把这些部分放在一起,你得到一个函数,其粗略看起来像是这个。
```rust,ignore
#[no_mangle]
@@ -58,46 +43,40 @@ pub extern "C" fn rust_function() {
}
```
-Just as when using `C` code in your Rust project you now need to transform data
-from and to a form that the rest of the application will understand.
+就像在Rust项目中使用`C`代码时那样,现在需要把数据转换为应用中其它部分可以理解的形式。
+
+## 链接和更大的项目上下文
-## Linking and greater project context.
+问题只解决了一半。
-So then, that's one half of the problem solved.
-How do you use this now?
+你现在要如何使用它?
-**This very much depends on your project and/or build system**
+**这很大程度上取决于你的项目或者编译系统**
-`cargo` will create a `my_lib.so`/`my_lib.dll` or `my_lib.a` file,
-depending on your platform and settings. This library can simply be linked
-by your build system.
+`cargo`将生成一个`my_lib.so`/`my_lib.dll`或者`my_lib.a`文件,取决于你的平台和配置。可以通过编译系统简单地链接这个库。
-However, calling a Rust function from C requires a header file to declare
-the function signatures.
+然而,从C调用一个Rust函数要求一个头文件去声明函数的签名。
-Every function in your Rust-ffi API needs to have a corresponding header function.
+在Rust-ffi API中的每个函数需要有一个相关的头文件函数。
```rust,ignore
#[no_mangle]
pub extern "C" fn rust_function() {}
```
-would then become
+将会变成
```C
void rust_function();
```
-etc.
+等等。
-There is a tool to automate this process,
-called [cbindgen] which analyses your Rust code
-and then generates headers for your C and C++ projects from it.
+这里有个工具可以自动化这个过程,叫做[cbindgen],其会分析你的Rust代码然后为C和C++项目生成头文件。
[cbindgen]: /~https://github.com/eqrion/cbindgen
-At this point, using the Rust functions from C
-is as simple as including the header and calling them!
+此时从C中使用Rust函数非常简单,只需包含头文件和调用它们!
```C
#include "my-rust-project.h"
diff --git a/src/intro/hardware.md b/src/intro/hardware.md
index b458aa19..47ad66a0 100644
--- a/src/intro/hardware.md
+++ b/src/intro/hardware.md
@@ -1,6 +1,6 @@
-# Meet Your Hardware
+# 熟悉你的硬件
-Let's get familiar with the hardware we'll be working with.
+先来熟悉下我们要用的硬件。
## STM32F3DISCOVERY (the "F3")
@@ -8,32 +8,25 @@ Let's get familiar with the hardware we'll be working with.
-What does this board contain?
+这个板子有什么?
-- A [STM32F303VCT6](https://www.st.com/en/microcontrollers/stm32f303vc.html) microcontroller. This microcontroller has
- - A single-core ARM Cortex-M4F processor with hardware support for single-precision floating point
- operations and a maximum clock frequency of 72 MHz.
++ 一个[STM32F303VCT6](https://www.st.com/en/microcontrollers/stm32f303vc.html)微控制器。这个微控制器包含
+ + 一个单核的ARM Cortex-M4F 处理器,支持单精度浮点运算,72MHz的最大时钟频率。
+ + 256 KiB的"Flash"存储。
+ + 48 KiB的RAM
+ + 多种多样的外设,比如计时器,I2C,SPI和USART
+ + 通用GPIO和在板子两侧的其它类型的引脚
+ + 一个写着“USB USER”的USB接口
++ 一个位于[LSM303DLHC](https://www.st.com/en/mems-and-sensors/lsm303dlhc.html)芯片上的[加速度计](https://en.wikipedia.org/wiki/Accelerometer)。
- - 256 KiB of "Flash" memory. (1 KiB = 10**24** bytes)
++ 一个位于[LSM303DLHC](https://www.st.com/en/mems-and-sensors/lsm303dlhc.html)芯片上的[磁力计](https://en.wikipedia.org/wiki/Magnetometer)。
- - 48 KiB of RAM.
++ 一个位于[L3GD20](https://www.pololu.com/file/0J563/L3GD20.pdf)芯片上的[陀螺仪](https://en.wikipedia.org/wiki/Gyroscope).
- - A variety of integrated peripherals such as timers, I2C, SPI and USART.
++ 8个摆得像一个指南针形状的user LEDs。
- - General purpose Input Output (GPIO) and other types of pins accessible through the two rows of headers along side the board.
-
- - A USB interface accessible through the USB port labeled "USB USER".
++ 一个二级微控制器: [STM32F103](https://www.st.com/en/microcontrollers/stm32f103cb.html)。这个微控制器实际上是一个板载编程器/调试器的一部分,与名为“USB ST-LINK”的USB端口相连。
-- An [accelerometer](https://en.wikipedia.org/wiki/Accelerometer) as part of the [LSM303DLHC](https://www.st.com/en/mems-and-sensors/lsm303dlhc.html) chip.
+关于所列举的功能的更多细节和开发板的更多规格请查阅[STMicroelectronics](https://www.st.com/en/evaluation-tools/stm32f3discovery.html)网站。
-- A [magnetometer](https://en.wikipedia.org/wiki/Magnetometer) as part of the [LSM303DLHC](https://www.st.com/en/mems-and-sensors/lsm303dlhc.html) chip.
-
-- A [gyroscope](https://en.wikipedia.org/wiki/Gyroscope) as part of the [L3GD20](https://www.pololu.com/file/0J563/L3GD20.pdf) chip.
-
-- 8 user LEDs arranged in the shape of a compass.
-
-- A second microcontroller: a [STM32F103](https://www.st.com/en/microcontrollers/stm32f103cb.html). This microcontroller is actually part of an on-board programmer / debugger and is connected to the USB port named "USB ST-LINK".
-
-For a more detailed list of features and further specifications of the board take a look at the [STMicroelectronics](https://www.st.com/en/evaluation-tools/stm32f3discovery.html) website.
-
-A word of caution: be careful if you want to apply external signals to the board. The microcontroller STM32F303VCT6 pins take a nominal voltage of 3.3 volts. For further information consult the [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 272c2428..caa42bd1 100644
--- a/src/intro/index.md
+++ b/src/intro/index.md
@@ -1,131 +1,94 @@
-# Introduction
+# 引言
+欢迎阅读嵌入式Rust:一本关于如何在裸机(比如,微处理器)上使用Rust编程语言的入门书籍。
-Welcome to The Embedded Rust Book: An introductory book about using the Rust
-Programming Language on "Bare Metal" embedded systems, such as Microcontrollers.
+## 嵌入式Rust是为谁准备的
+嵌入式Rust是为了那些既想要进行嵌入式编程,又想要使用Rust语言所提供的高级概念和安全保障的人们而准备的(参见[Who Rust Is For](https://doc.rust-lang.org/book/ch00-00-introduction.html))
-## Who Embedded Rust is For
-Embedded Rust is for everyone who wants to do embedded programming while taking advantage of the higher-level concepts and safety guarantees the Rust language provides.
-(See also [Who Rust Is For](https://doc.rust-lang.org/book/ch00-00-introduction.html))
+## 本书范围
+这本书的目的是:
++ 让开发者快速上手Rust嵌入式开发,比如,如何设置一个开发环境。
++ 分享那些关于使用Rust进行嵌入式开发的,现存的,最好的实践经验,比如,如何最大程度上地利用好Rust语言的特性去写更正确的嵌入式软件
++ 某种程度下作为工具书,比如,如何在一个项目里将C和Rust混合在一起使用
-## Scope
+虽然尽可能地尝试让这本书可以用于大多数场景,但是为了使读者和作者更容易理解,在所有的示例中,这本书都使用了ARM Cortex-M架构。然而,这本书并不需要读者熟悉这个架构,书中会在需要时对这个架构的特定细节进行解释。
-The goals of this book are:
+## 这本书是为谁准备的
-* Get developers up to speed with embedded Rust development. i.e. How to set
- up a development environment.
+这本书适合那些有一些嵌入式背景或者有Rust背景的人,然而我相信每一个对Rust嵌入式编程好奇的人都能从这本书中获得某些收获。对于那些先前没有任何经验的人,我们建议你读一下“要求和预备知识”部分。从其它资料中获取、补充缺失的知识,这样能提高你的阅读体验。你可以看看“其它资源”部分,以找到你感兴趣的那些主题的资源。
-* Share *current* best practices about using Rust for embedded development. i.e.
- How to best use Rust language features to write more correct embedded
- software.
+### 要求和预备知识
-* Serve as a cookbook in some cases. e.g. How do I mix C and Rust in a single
- project?
-
-This book tries to be as general as possible but to make things easier for both
-the readers and the writers it uses the ARM Cortex-M architecture in all its
-examples. However, the book doesn't assume that the reader is familiar with this
-particular architecture and explains details particular to this architecture
-where required.
-
-## Who This Book is For
-This book caters towards people with either some embedded background or some Rust background, however we believe
-everybody curious about embedded Rust programming can get something out of this book. For those without any prior knowledge
-we suggest you read the "Assumptions and Prerequisites" section and catch up on missing knowledge to get more out of the book
-and improve your reading experience. You can check out the "Other Resources" section to find resources on topics
-you might want to catch up on.
-
-### Assumptions and Prerequisites
-
-* You are comfortable using the Rust Programming Language, and have written,
- run, and debugged Rust applications on a desktop environment. You should also
- be familiar with the idioms of the [2018 edition] as this book targets
- 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,串口等等常见的接口
-* You are comfortable developing and debugging embedded systems in another
- language such as C, C++, or Ada, and are familiar with concepts such as:
- * Cross Compilation
- * Memory Mapped Peripherals
- * Interrupts
- * Common interfaces such as I2C, SPI, Serial, etc.
+### 其它资源
-### Other Resources
-If you are unfamiliar with anything mentioned above or if you want more information about a specific topic mentioned in this book you might find some of these resources helpful.
+如果你还不熟悉上面提到的东西或者你对这本书中提到的某个特定主题感兴趣,你也许能从这些资源中找到有用的信息。
-| Topic | Resource | Description |
+| 主题 | 资源 | 描述 |
|--------------|----------|-------------|
-| Rust | [Rust Book](https://doc.rust-lang.org/book/) | If you are not yet comfortable with Rust, we highly suggest reading this book. |
-| Rust, Embedded | [Discovery Book](https://docs.rust-embedded.org/discovery/) | If you have never done any embedded programming, this book might be a better start |
-| Rust, Embedded | [Embedded Rust Bookshelf](https://docs.rust-embedded.org) | Here you can find several other resources provided by Rust's Embedded Working Group. |
-| Rust, Embedded | [Embedonomicon](https://docs.rust-embedded.org/embedonomicon/) | The nitty gritty details when doing embedded programming in Rust. |
-| Rust, Embedded | [embedded FAQ](https://docs.rust-embedded.org/faq.html) | Frequently asked questions about Rust in an embedded context. |
-| Rust, Embedded | [Comprehensive Rust 🦀: Bare Metal](https://google.github.io/comprehensive-rust/bare-metal.html) | Teaching material for a 1-day class on bare-metal Rust development |
+| 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的嵌入式工作组提供的许多其它资源.|
+| Rust, Embedded | [Embedonomicon](https://docs.rust-embedded.org/embedonomicon/) | 用Rust进行嵌入式编程的细节.|
+| Rust, Embedded | [embedded FAQ](https://docs.rust-embedded.org/faq.html) | Rust在嵌入式上下文中遇到的常见问题.|
+| Rust, Embedded | [Comprehensive Rust 🦀: Bare Metal](https://google.github.io/comprehensive-rust/bare-metal.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) | - |
| 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) | - |
-### Translations
+### 翻译
-This book has been translated by generous volunteers. If you would like your
-translation listed here, please open a PR to add it.
+这本书是已经被一些慷慨的志愿者们翻译了。如果你想要将你的翻译列在这里,请打开一个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/)
- ([repository](/~https://github.com/XxChang/book))
+* [中文](https://xxchang.github.io/book/)
+ ([repository](/~https://github.com/xxchang/book))
-## How to Use This Book
-
-This book generally assumes that you’re reading it front-to-back. Later
-chapters build on concepts in earlier chapters, and earlier chapters may
-not dig into details on a topic, revisiting the topic in a later chapter.
-
-This book will be using the [STM32F3DISCOVERY] development board from
-STMicroelectronics for the majority of the examples contained within. This board
-is based on the ARM Cortex-M architecture, and while basic functionality is
-the same across most CPUs based on this architecture, peripherals and other
-implementation details of Microcontrollers are different between different
-vendors, and often even different between Microcontroller families from the same
-vendor.
-
-For this reason, we suggest purchasing the [STM32F3DISCOVERY] development board
-for the purpose of following the examples in this book.
+## 如何使用这本书
+这本书通常假设你是按顺序阅读的。之后的章节是建立在先前的章节中提到的概念之上的,先前章节可能不会深入一个主题的细节,因为在随后的章节将会再次重温这个主题。
+在大多数示例中这本书将使用[STM32F3DISCOVERY]开发板。这个板子是基于ARM Cortex-M架构的,且基本功能与大多数基于这个架构的CPUs功能相似。微处理器的外设和其它实现细节在不同的厂家之间是不同的,甚至来自同一个厂家,不同处理器系列之间也是不同的。
+因此我们建议购买[STM32F3DISCOVERY]开发板来尝试这本书中的例子。
[STM32F3DISCOVERY]: http://www.st.com/en/evaluation-tools/stm32f3discovery.html
-## Contributing to This Book
+## 贡献
-The work on this book is coordinated in [this repository] and is mainly
-developed by the [resources team].
+这本书的工作主要在[这个仓库]里管理,且主要由[resouces team]开发。
-[this repository]: /~https://github.com/rust-embedded/book
-[resources team]: /~https://github.com/rust-embedded/wg#the-resources-team
+[这个仓库]: /~https://github.com/rust-embedded/book
+[resouces team]: /~https://github.com/rust-embedded/wg#the-resources-team
-If you have trouble following the instructions in this book or find that some
-section of the book is not clear enough or hard to follow then that's a bug and
-it should be reported in [the issue tracker] of this book.
+如果你按着这本书的操作遇到了什么麻烦,或者这本书的一些部分不够清楚,或者很难进行下去,那这本书就是有个bug,这个bug应该被报道给这本书的[the issue tracker] 。
[the issue tracker]: /~https://github.com/rust-embedded/book/issues/
-Pull requests fixing typos and adding new content are very welcome!
+修改拼写错误和添加新内容的Pull requests非常欢迎!
-## Re-using this material
+## 二次使用这个材料
-This book is distributed under the following licenses:
+这本书根据以下许可证发布:
-* The code samples and free-standing Cargo projects contained within this book are licensed under the terms of both the [MIT License] and the [Apache License v2.0].
-* The written prose, pictures and diagrams contained within this book are licensed under the terms of the Creative Commons [CC-BY-SA v4.0] license.
+* 本书中包含的代码示例和独立的Cargo项目均根据[MIT License]和[Apache License v2.0]发放许可的。
+* 本书中包含的文档,图片和表格均根据[CC-BY-SA v4.0]发放许可的。
[MIT License]: https://opensource.org/licenses/MIT
[Apache License v2.0]: http://www.apache.org/licenses/LICENSE-2.0
[CC-BY-SA v4.0]: https://creativecommons.org/licenses/by-sa/4.0/legalcode
-TL;DR: If you want to use our text or images in your work, you need to:
+总之:如果你想在你的工作中使用我们的文档或者图片,你需要:
+
++ 提供合适的授信 (i.e. 在你的幻灯片中提到本书,提供相关页面的连接)
++ 提供[CC-BY-SA v4.0]的许可证的链接
++ 指出你是否改变了材料的内容,在同一个许可证下,可以对材料进行任何改变
-* Give the appropriate credit (i.e. mention this book on your slide, and provide a link to the relevant page)
-* Provide a link to the [CC-BY-SA v4.0] licence
-* Indicate if you have changed the material in any way, and make any changes to our material available under the same licence
+也请告诉我这本书对你是否有帮助!
-Also, please do let us know if you find this book useful!
diff --git a/src/intro/install.md b/src/intro/install.md
index f8c4aa4e..4ba6ce6c 100644
--- a/src/intro/install.md
+++ b/src/intro/install.md
@@ -1,55 +1,48 @@
-# Installing the tools
+# 安装工具
+这一页包含的工具安装指令与操作系统无关:
-This page contains OS-agnostic installation instructions for a few of the tools:
+### Rust 工具链
+跟着[https://rustup.rs](https://rustup.rs)的指令安装rustup。
-### Rust Toolchain
-
-Install rustup by following the instructions at [https://rustup.rs](https://rustup.rs).
-
-**NOTE** Make sure you have a compiler version equal to or newer than `1.31`. `rustc
--V` should return a date newer than the one shown below.
+**注意** 确保你的编译器版本等于或者大于`1.31`版本。`rustc -V`应该返回一个比下列日期更新的日期。
``` text
$ rustc -V
rustc 1.31.1 (b6c32da9b 2018-12-18)
```
+考虑到带宽和磁盘的使用量,默认的安装只支持主机环境的编译。为了添加对ARM Cortex-M架构交叉编译的支持,从下列编译目标中选择一个。对于这本书里使用的STM32F3DISCOVERY板子,使用`thumbv7em-none-eabihf`作为目标。
-For bandwidth and disk usage concerns the default installation only supports
-native compilation. To add cross compilation support for the ARM Cortex-M
-architectures choose one of the following compilation targets. For the STM32F3DISCOVERY
-board used for the examples in this book, use the `thumbv7em-none-eabihf` target.
-
-Cortex-M0, M0+, and M1 (ARMv6-M architecture):
+Cortex-M0, M0+, 和 M1 (ARMv6-M 架构):
``` console
rustup target add thumbv6m-none-eabi
```
-Cortex-M3 (ARMv7-M architecture):
+Cortex-M3 (ARMv7-M 架构):
``` console
rustup target add thumbv7m-none-eabi
```
-Cortex-M4 and M7 without hardware floating point (ARMv7E-M architecture):
+没有硬件浮点单元的Cortex-M4和M7 (ARMv7E-M架构)
``` console
rustup target add thumbv7em-none-eabi
```
-Cortex-M4F and M7F with hardware floating point (ARMv7E-M architecture):
+具有硬件浮点单元的Cortex-M4F和M7F (ARMv7E-M架构)
``` console
rustup target add thumbv7em-none-eabihf
```
-Cortex-M23 (ARMv8-M architecture):
+Cortex-M23 (ARMv8-M架构):
``` console
rustup target add thumbv8m.base-none-eabi
```
-Cortex-M33 and M35P (ARMv8-M architecture):
+Cortex-M33和M35P (ARMv8-M架构):
``` console
rustup target add thumbv8m.main-none-eabi
```
-Cortex-M33F and M35PF with hardware floating point (ARMv8-M architecture):
+具有硬件浮点单元的Cortex-M33F和M35PF (ARMv8-M架构):
``` console
rustup target add thumbv8m.main-none-eabihf
```
@@ -62,20 +55,19 @@ 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
-### `cargo-generate`
+WINDOWS: 需要预先安装 C++ Build Tools for Visual Studio 2019。https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16
-We'll use this later to generate a project from a template.
+### `cargo-generate`
+我们随后将使用这个来从模板生成一个项目。
``` console
cargo install cargo-generate
```
+注意:在某些Linux发行版上(e.g. Ubuntu) 在安装cargo-generate之前,你可能需要安装`libssl-dev`和`pkg-config`
-Note: on some Linux distros (e.g. Ubuntu) you may need to install the packages `libssl-dev` and `pkg-config` prior to installing cargo-generate.
-
-### OS-Specific Instructions
+### 特定于操作系统的指令
-Now follow the instructions specific to the OS you are using:
+现在根据你使用的操作系统,来执行对应的指令:
- [Linux](install/linux.md)
- [Windows](install/windows.md)
diff --git a/src/intro/install/linux.md b/src/intro/install/linux.md
index 41027b8d..849c1c5f 100644
--- a/src/intro/install/linux.md
+++ b/src/intro/install/linux.md
@@ -1,13 +1,12 @@
# Linux
-Here are the installation commands for a few Linux distributions.
+这部分是在某些Linux发行版下的安装指令。
-## Packages
+## 依赖包
-- Ubuntu 18.04 or newer / Debian stretch or newer
+- Ubuntu 18.04 或者更新的版本 / Debian stretch 或者更新的版本
-> **NOTE** `gdb-multiarch` is the GDB command you'll use to debug your ARM
-> Cortex-M programs
+> **注意** `gdb-multiarch` 是你将用来调试你的ARM Cortex-M程序的GDB命令
@@ -19,14 +18,14 @@ Here are the installation commands for a few Linux distributions.
+
``` console
sudo apt install gdb-multiarch openocd qemu-system-arm
```
- Ubuntu 14.04 and 16.04
-> **NOTE** `arm-none-eabi-gdb` is the GDB command you'll use to debug your ARM
-> Cortex-M programs
+> **注意** `arm-none-eabi-gdb` 是你将用来调试你的ARM Cortex-M程序的GDB命令
@@ -37,7 +36,7 @@ sudo apt install gdb-multiarch openocd qemu-system-arm
sudo apt install gdb-arm-none-eabi openocd qemu-system-arm
```
-- Fedora 27 or newer
+- Fedora 27 或者更新的版本
@@ -50,18 +49,17 @@ sudo dnf install gdb openocd qemu-system-arm
- Arch Linux
-> **NOTE** `arm-none-eabi-gdb` is the GDB command you'll use to debug ARM
-> Cortex-M programs
+> **注意** `arm-none-eabi-gdb` 是你将用来调试你的ARM Cortex-M程序的GDB命令
``` console
sudo pacman -S arm-none-eabi-gdb qemu-system-arm openocd
```
-## udev rules
+## udev 规则
-This rule lets you use OpenOCD with the Discovery board without root privilege.
+这个规则可以让你在不使用超级用户权限的情况下,使用OpenOCD和Discovery开发板。
-Create the file `/etc/udev/rules.d/70-st-link.rules` with the contents shown below.
+生成包含下列内容的 `/etc/udev/rules.d/70-st-link.rules` 文件
``` text
# STM32F3DISCOVERY rev A/B - ST-LINK/V2
@@ -71,21 +69,21 @@ ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", TAG+="uaccess"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", TAG+="uaccess"
```
-Then reload all the udev rules with:
+然后重新加载所有的udev规则
``` console
sudo udevadm control --reload-rules
```
-If you had the board plugged to your laptop, unplug it and then plug it again.
+如果你已经把开发板插入到笔记本中了,请拔下它然后再插上它。
-You can check the permissions by running this command:
+你可以通过运行这个命令检查权限:
``` console
lsusb
```
-Which should show something like
+终端可能有如下显示
```text
(..)
@@ -93,8 +91,7 @@ Bus 001 Device 018: ID 0483:374b STMicroelectronics ST-LINK/V2.1
(..)
```
-Take note of the bus and device numbers. Use those numbers to create a path like
-`/dev/bus/usb//`. Then use this path like so:
+记住bus和device号,使用这些数字组合成一个像是 `/dev/bus/usb//` 这样的路径。然后像这样使用这个路径:
``` console
ls -l /dev/bus/usb/001/018
@@ -113,10 +110,8 @@ user::rw-
user:you:rw-
```
-The `+` appended to permissions indicates the existence of an extended
-permission. The `getfacl` command tells the user `you` can make use of
-this device.
+权限后的 `+` 指出存在一个扩展权限。`getfacl` 命令显示,`user`也就是`你`,可以使用这个设备。
-Now, go to the [next section].
+现在,去往[下个章节].
-[next section]: verify.md
+[下个章节]: verify.md
diff --git a/src/intro/install/macos.md b/src/intro/install/macos.md
index 9a797563..ebe805ed 100644
--- a/src/intro/install/macos.md
+++ b/src/intro/install/macos.md
@@ -1,11 +1,11 @@
# macOS
-All the tools can be installed using [Homebrew] or [MacPorts]:
+所有的工具都可以使用[Homebrew]或者[MacPorts]来安装:
[Homebrew]: http://brew.sh/
[MacPorts]: https://www.macports.org/
-## Install tools with [Homebrew]
+## 使用[Homebrew]安装工具
``` text
$ # GDB
@@ -18,12 +18,13 @@ $ # QEMU
$ brew install qemu
```
-> **NOTE** If OpenOCD crashes you may need to install the latest version using:
+> **注意** 如果OpenOCD崩溃了,你可能需要用以下方法安装最新版本:
+
```text
$ brew install --HEAD openocd
```
-## Install tools with [MacPorts]
+## 使用[MacPorts]安装工具
``` text
$ # GDB
@@ -37,7 +38,6 @@ $ sudo port install qemu
```
+这是全部内容,请转入[下个章节].
-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 45caec90..26b2262a 100644
--- a/src/intro/install/verify.md
+++ b/src/intro/install/verify.md
@@ -1,30 +1,25 @@
-# Verify Installation
+# 安装验证
-In this section we check that some of the required tools / drivers have been
-correctly installed and configured.
+在这个章节中我们将检查工具和驱动是否已经被正确地安装和配置了。
-Connect your laptop / PC to the discovery board using a Mini-USB USB cable. The
-discovery board has two USB connectors; use the one labeled "USB ST-LINK" that
-sits on the center of the edge of the board.
+使用一个micro USB线缆将你的笔记本/个人电脑连接到discovery开发板上。discovery开发板有两个USB连接器;使用标记着"USB ST-LINK"的那个,它位于开发板边缘的中间位置。
-Also check that the ST-LINK header is populated. See the picture below; the
-ST-LINK header is highlighted.
+也要检查下ST-LINK的短路帽是否被安装了。看下面的图;ST-LINK短路帽用红色圈起来了。
-Now run the following command:
+现在运行下面的命令:
``` console
openocd -f interface/stlink.cfg -f target/stm32f3x.cfg
```
-> **NOTE**: Old versions of openocd, including the 0.10.0 release from 2017, do
-> not contain the new (and preferable) `interface/stlink.cfg` file; instead you
-> may need to use `interface/stlink-v2.cfg` or `interface/stlink-v2-1.cfg`.
+> **注意**: 旧版的openocd, 包括从2017发布的0.10.0, 不包含新的(且更适合的)`interface/stlink.cfg`文件; 你需要使用`interface/stlink-v2.cfg` 或者 `interface/stlink-v2-1.cfg`。
+
-You should get the following output and the program should block the console:
+你应该看到了下面的输出,且程序应该阻塞住了控制台:
``` text
Open On-Chip Debugger 0.10.0
@@ -45,13 +40,11 @@ Info : Target voltage: 2.919881
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
```
-The contents may not match exactly but you should get the last line about
-breakpoints and watchpoints. If you got it then terminate the OpenOCD process
-and move to the [next section].
+内容可能并不是一模一样,但是在最后一行,你应该看到了breakpoints和watchpoints,如果你看到了,那就终止OpenOCD进程然后进入[下个章节]
-[next section]: ../../start/index.md
+[下个章节]: ../../start/index.md
-If you didn't get the "breakpoints" line then try one of the following commands.
+如果你没看到"breakpoints"这行,尝试下下列命令中的某一个命令。
``` console
openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg
@@ -61,18 +54,12 @@ openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg
openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
```
-If one of those commands works it means you got an old hardware revision of the
-discovery board. That won't be a problem but commit that fact to memory as
-you'll need to configure things a bit differently later on. You can move to the
-[next section].
+如果这些命令的某条起作用了,那意味着你使用的discovery开发板是一个旧的版本。那也不成问题,但是你要记住这件事,因为随后你的配置可能有点不同。你可以移到[下个章节]了。
-If none of the commands work as a normal user then try to run them with root
-permission (e.g. `sudo openocd ..`). If the commands do work with root
-permission then check that the [udev rules] have been correctly set.
+如果这些命令在普通用户模式下都没用,尝试下使用root模式运行它们(e.g. `sudo openocd ..`)。如果命令在root模式下起作用,需要检查下[udev rules]是否被正确地设置了。
[udev rules]: linux.md#udev-rules
-If you have reached this point and OpenOCD is not working please open [an issue]
-and we'll help you out!
+如果这些都试了,OpenOCD还不工作,请打开一个[issue],我们将帮助你!
-[an issue]: /~https://github.com/rust-embedded/book/issues
+[issue]: /~https://github.com/rust-embedded/book/issues
diff --git a/src/intro/install/windows.md b/src/intro/install/windows.md
index e0074035..c1264395 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
diff --git a/src/intro/no-std.md b/src/intro/no-std.md
index 93aadf65..537ea70d 100644
--- a/src/intro/no-std.md
+++ b/src/intro/no-std.md
@@ -1,66 +1,42 @@
-# A `no_std` Rust Environment
+# 一个 `no_std` Rust环境
-The term Embedded Programming is used for a wide range of different classes of programming.
-Ranging from programming 8-Bit MCUs (like the [ST72325xx](https://www.st.com/resource/en/datasheet/st72325j6.pdf))
-with just a few KB of RAM and ROM, up to systems like the Raspberry Pi
-([Model B 3+](https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications)) which has a 32/64-bit
-4-core Cortex-A53 @ 1.4 GHz and 1GB of RAM. Different restrictions/limitations will apply when writing code
-depending on what kind of target and use case you have.
+嵌入式编程这个词被广泛用于许多不同的编程场景中。小到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))。当编写代码时,取决于你的目标环境和用例,将会有不同的限制和局限。
+通常嵌入式编程有两类:
-There are two general Embedded Programming classifications:
+## 主机环境下
-## Hosted Environments
-These kinds of environments are close to a normal PC environment.
-What this means is that you are provided with a System Interface [E.G. POSIX](https://en.wikipedia.org/wiki/POSIX)
-that provides you with primitives to interact with various systems, such as file systems, networking, memory management, threads, etc.
-Standard libraries in turn usually depend on these primitives to implement their functionality.
-You may also have some sort of sysroot and restrictions on RAM/ROM-usage, and perhaps some
-special HW or I/Os. Overall it feels like coding on a special-purpose PC environment.
+这类环境与一个常见的PC环境类似。意味着向你提供了一个系统接口[比如 POSIX](https://en.wikipedia.org/wiki/POSIX),使你能和不同的系统进行交互,比如文件系统,网络,内存管理,进程,等等。标准库相应地依赖这些接口去实现了它们的功能。可能有某种sysroot并限制了对RAM/ROM的使用,可能还有一些特别的硬件或者I/O。总之感觉像是在专用的PC环境上编程一样。
-## Bare Metal Environments
-In a bare metal environment no code has been loaded before your program.
-Without the software provided by an OS we can not load the standard library.
-Instead the program, along with the crates it uses, can only use the hardware (bare metal) to run.
-To prevent rust from loading the standard library use `no_std`.
-The platform-agnostic parts of the standard library are available through [libcore](https://doc.rust-lang.org/core/).
-libcore also excludes things which are not always desirable in an embedded environment.
-One of these things is a memory allocator for dynamic memory allocation.
-If you require this or any other functionalities there are often crates which provide these.
+## 裸机环境下
-### The libstd Runtime
-As mentioned before using [libstd](https://doc.rust-lang.org/std/) requires some sort of system integration, but this is not only because
-[libstd](https://doc.rust-lang.org/std/) is just providing a common way of accessing OS abstractions, it also provides a runtime.
-This runtime, among other things, takes care of setting up stack overflow protection, processing command line arguments,
-and spawning the main thread before a program's main function is invoked. This runtime also won't be available in a `no_std` environment.
+在一个裸机环境中,程序被加载前,环境中不存在代码。没有系统提供的软件,我们不能加载标准库。相反地,程序和它使用的crates只能使用硬件(裸机)去运行。使用`no-std`可以防止rust读取标准库。标准库中与平台无关的部分在[libcore](https://doc.rust-lang.org/core/)中。libcore剔除了那些在一个嵌入式环境中非必要的东西。比如用于动态分配的内存分配器。如果你需要这些或者其它的某些功能,通常会有提供这些功能的crates。
-## Summary
-`#![no_std]` is a crate-level attribute that indicates that the crate will link to the core-crate instead of the std-crate.
-The [libcore](https://doc.rust-lang.org/core/) crate in turn is a platform-agnostic subset of the std crate
-which makes no assumptions about the system the program will run on.
-As such, it provides APIs for language primitives like floats, strings and slices, as well as APIs that expose processor features
-like atomic operations and SIMD instructions. However it lacks APIs for anything that involves platform integration.
-Because of these properties no\_std and [libcore](https://doc.rust-lang.org/core/) code can be used for any kind of
-bootstrapping (stage 0) code like bootloaders, firmware or kernels.
+### libstd运行时
-### Overview
+就像之前提到的,使用[libstd](https://doc.rust-lang.org/std/)需要一些系统集成,这不仅仅是因为[libstd](https://doc.rust-lang.org/std/)使用了一个公共的方法访问操作系统,它也提供了一个运行时环境。这个运行时环境,负责设置堆栈溢出保护,处理命令行参数,并在一个程序的主函数被激活前启动一个主线程。在一个`no_std`环境中,这个运行时环境也是不可用的。
-| feature | no\_std | std |
+## 总结
+`#![no_std]`是一个crate层级的属性,它说明crate将连接至core-crate而不是std-crate。[libcore](https://doc.rust-lang.org/core/) crate是std crate的一个的子集,其与平台无关,它对程序将要运行的系统没有做要求。比如,它提供了像是floats,strings和切片的APIs,暴露了像是与原子操作和SIMD指令相关的处理器功能的APIs。然而,它缺少涉及到平台集成的那些APIs。由于这些特性,no_std和[libcore](https://doc.rust-lang.org/core/)代码可以用于任何引导程序(stage 0)像是bootloaders,固件或者内核。
+
+### 概述
+
+| 特性 | no\_std | std |
|-----------------------------------------------------------|--------|-----|
-| heap (dynamic memory) | * | ✓ |
-| collections (Vec, BTreeMap, etc) | ** | ✓ |
-| stack overflow protection | ✘ | ✓ |
-| runs init code before main | ✘ | ✓ |
+| 堆 (dynamic memory) | * | ✓ |
+| 容器 (Vec, BTreeMap, etc) | ** | ✓ |
+| 栈溢出保护 | ✘ | ✓ |
+| 在进入main之前运行的初始化代码 | ✘ | ✓ |
| libstd available | ✘ | ✓ |
| libcore available | ✓ | ✓ |
-| writing firmware, kernel, or bootloader code | ✓ | ✘ |
+| 编写固件,内核,或者引导程序 | ✓ | ✘ |
-\* Only if you use the `alloc` crate and use a suitable allocator like [alloc-cortex-m].
+\* 只有在你使用了 `alloc` crate 并设置了一个适合的分配器后,比如[alloc-cortex-m]后可用.
-\** Only if you use the `collections` crate and configure a global default allocator.
+\** 只有在你使用了 `collections` crate 并配置了一个全局默认的分配器后可用.
-\** HashMap and HashSet are not available due to a lack of a secure random number generator.
+\** 由于缺少安全的随机数产生器,所以无法使用HashMap和HashSet.
[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 68f3674c..5eff26d8 100644
--- a/src/intro/tooling.md
+++ b/src/intro/tooling.md
@@ -1,112 +1,81 @@
-# Tooling
-
-Dealing with microcontrollers involves using several different tools as we'll be
-dealing with an architecture different than your laptop's and we'll have to run
-and debug programs on a *remote* device.
-
-We'll use all the tools listed below. Any recent version should work when a
-minimum version is not specified, but we have listed the versions we have
-tested.
-
-- Rust 1.31, 1.31-beta, or a newer toolchain PLUS ARM Cortex-M compilation
- support.
+# 工具
+与微控制器打交道需要使用几种不同的工具,因为我们要处理的架构与笔记本电脑不同,我们必须在 *远程* 设备上运行和调试程序。我们将使用下面列举出来的工具。当没有指定一个最小版本时,最新的版本应该也可以用,但是我们还是列出了我们已经测过的那些版本。
+- 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/). Tested versions: 3.0.0
-- OpenOCD >=0.8. Tested versions: v0.9.0 and v0.10.0
-- GDB with ARM support. Version 7.12 or newer highly recommended. Tested
- versions: 7.10, 7.11, 7.12 and 8.1
-- [`cargo-generate`](/~https://github.com/ashleygwilliams/cargo-generate) or `git`.
- These tools are optional but will make it easier to follow along with the book.
-
-The text below explains why we are using these tools. Installation instructions
-can be found on the next page.
+- [`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` OR `git`
+下面的文档将解释我们为什么使用这些工具。安装指令可以在下一页找到。
-Bare metal programs are non-standard (`no_std`) Rust programs that require some
-adjustments to the linking process in order to get the memory layout of the program
-right. This requires some additional files (like linker scripts) and
-settings (like linker flags). We have packaged those for you in a template
-such that you only need to fill in the missing information (such as the project name and the
-characteristics of your target hardware).
-
-Our template is compatible with `cargo-generate`: a Cargo subcommand for
-creating new Cargo projects from templates. You can also download the
-template using `git`, `curl`, `wget`, or your web browser.
+## `cargo-generate` 或者 `git`
+裸机编程是非标准Rust编程,为了得到正确的程序的内存布局,需要对链接过程进行一些调整,这要求添加一些额外的文件(比如linker scripts)和配置(比如linker flags)。我们已经为你把这些打包进了一个模板里了,你只需要补充缺失的信息(比如项目名和目标硬件的特性)。
+我们的模板兼容`cargo-generate`:一个用来从模板生成新的Cargo项目的Cargo子命令。你也能使用`git`,`curl`,`wget`,或者你的网页浏览器下载模板。
## `cargo-binutils`
-
-`cargo-binutils` is a collection of Cargo subcommands that make it easy to use
-the LLVM tools that are shipped with the Rust toolchain. These tools include the
-LLVM versions of `objdump`, `nm` and `size` and are used for inspecting
-binaries.
-
-The advantage of using these tools over GNU binutils is that (a) installing the
-LLVM tools is the same one-command installation (`rustup component add
-llvm-tools-preview`) regardless of your OS and (b) tools like `objdump` support
-all the architectures that `rustc` supports -- from ARM to x86_64 -- because
-they both share the same LLVM backend.
+`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后端。
## `qemu-system-arm`
-QEMU is an emulator. In this case we use the variant that can fully emulate ARM
-systems. We use QEMU to run embedded programs on the host. Thanks to this you
-can follow some parts of this book even if you don't have any hardware with you!
+QEMU是一个仿真器。在这个例子里,我们使用能完全仿真ARM系统的改良版QEMU。我们使用QEMU在主机上运行嵌入式程序。多亏了它,你可以在没有任何硬件的情况下,尝试这本书的部分示例。
-# Tooling for Embedded Rust Debugging
+# 用于调试嵌入式Rust的工具
-## Overview
+## 概述
-Debugging embedded systems in Rust requires specialized tools including software to manage the debugging process, debuggers to inspect and control program execution, and hardware probes to facilitate interaction between the host and the embedded device. This document outlines essential software tools like Probe-rs and OpenOCD, which simplify and support the debugging process, alongside prominent debuggers such as GDB and the Probe-rs Visual Studio Code extension. Additionally, it covers key hardware probes such as Rusty-probe, ST-Link, J-Link, and MCU-Link, which are integral for effective debugging and programming of embedded devices.
+在Rust中调试嵌入式系统需要用到专业的工具,这包括用于管理调试进程的软件,用于观察和控制程序执行的调试器,和用于便捷主机和嵌入式设备之间进行交互的硬件探测器.这个文档会介绍像是Probe-rs和OpenOCD这样的基础软件,以及像是GDB和Probe-rs Visual Studio Code扩展这样常见的调试器.另外,该文档会覆盖像是Rusty-probe,ST-Link,J-Link,和MCU-Link这样的硬件探测器,它们整合在一起可以高效地对嵌入式设备进行调试和编程.
-## Software that drives debugging tools
+## 驱动调试工具的软件
### Probe-rs
-Probe-rs is a modern, Rust-focused software designed to work with debuggers in embedded systems. Unlike OpenOCD, Probe-rs is built with simplicity in mind and aims to reduce the configuration burden often found in other debugging solutions. It supports various probes and targets, providing a high-level interface for interacting with embedded hardware. Probe-rs integrates directly with Rust tooling,and integrates with Visual Studio Code through its extension, allowing developers to streamline their debugging workflow.
+Probe-rs是一个现代化的,以Rust开发的软件,被设计用来配合嵌入式系统中的调试器一起工作.不像OpenOCD,Probe-rs设计的时候就考虑到了简单性,目标是减少在其它调试解决方案中常见的配置重担.
+它支持不同的探测器和目标架构,提供一个用于与嵌入式硬件交互的高层接口.Probe-rs直接集成了Rust工具链,并且通过扩展集成进了Visual Studio Code中,允许开发者精简它们的调试工作流程.
### OpenOCD (Open On-Chip Debugger)
-OpenOCD is an open-source software tool used for debugging, testing, and programming embedded systems. It provides an interface between the host system and embedded hardware, supporting various transport layers like JTAG and SWD (Serial Wire Debug). OpenOCD integrates with GDB, which is a debugger. OpenOCD is widely supported, with extensive documentation and a large community, but may require complex configuration, especially for custom embedded setups.
+OpenOCD是一个用于调试,测试,和编程嵌入式系统的开源软件工具.它提供了一个主机系统和嵌入式硬件之间的接口,支持不同的传输层,比如JTAG和SWD(Serial Wire Debug).OpenOCD集成了GDB,其是一个调试器.OpenOCD受到了广泛的支持,拥有大量的文档和一个庞大的社区,但是配置可能会很复杂,特别是对于自定义的嵌入式设置.
## Debuggers
-A debugger allows developers to inspect and control the execution of a program in order to identify and correct errors or bugs. It provides functionalities such as setting breakpoints, stepping through code line by line, and examining the values of variables and memory states. Debuggers are essential for thorough software development and maintenance, enabling developers to ensure that their code behaves as intended under various conditions.
+调试器允许开发者观察和控制一个程序的执行,以辨别和纠正错误或者bugs.它提供像是设置断点,一行一行地步进代码,和研究变量的值以及内存的状态等功能.调试器本质上是为了通过软件开发和维护,使得开发者可以确保他们的代码的行为在不同环境下就像他们预期的那样运行.
-Debuggers know how to:
- * Interact with the memory mapped registers.
- * Set Breakpoints/Watchpoints.
- * Read and write to the memory mapped registers.
- * Detect when the MCU has been halted for a debug event.
- * Continue MCU execution after a debug event has been encountered.
- * Erase and write to the microcontroller's FLASH.
+调试器可以知道如何:
+ * 与映射到存储上的寄存器交互.
+ * 设置断点.
+ * 读取和写入映射到存储上的寄存器.
+ * 检测什么时候MCU因为一个调试时间被挂了起来.
+ * 在遇到一个调试事件后继续MCU的执行.
+ * 擦出和写入微控制器的FLASH.
### Probe-rs Visual Studio Code Extension
-Probe-rs has a Visual Studio Code extension, providing a seamless debugging experience without extensive setup. Through this connection, developers can use Rust-specific features like pretty printing and detailed error messages, ensuring that their debugging process aligns with the Rust ecosystem.
+Probe-rs有一个Visual Studio Code的扩展,提供了不需要额外设置的无缝的调试体验.通过它的帮助,开发者可以使用Rust特定的特性,像是漂亮的打印和详细的错误信息,确保它们的调试过程可以与Rust的生态对齐.
### GDB (GNU Debugger)
-GDB is a versatile debugging tool that allows developers to examine the state of programs while they run or after they crash. For embedded Rust, GDB connects to the target system via OpenOCD or other debugging servers to interact with the embedded code. GDB is highly configurable and supports features like remote debugging, variable inspection, and conditional breakpoints. It can be used on a variety of platforms, and has extensive support for Rust-specific debugging needs, such as pretty printing and integration with IDEs.
+GDB是一个多用途的调试工具,其允许开发者研究程序的状态,无论其正在运行中还是程序崩溃后.对于嵌入式Rust,GDB通过OpenOCD或者其它的调试服务器链接到目标系统上去和嵌入式代码交互.GDB是高度可配置的,并且支持像是远程调试,变量检测,和条件断点.它可以被用于多个平台,并对Rust特定的调试需求有广泛的支持,比如好看的打印和与IDEs集成.
-## Probes
+## 探测器
-A hardware probe is a device used in the development and debugging of embedded systems to facilitate communication between a host computer and the target embedded device. It typically supports protocols like JTAG or SWD, enabling it to program, debug, and analyze the microcontroller or microprocessor on the embedded system. Hardware probes are crucial for developers to set breakpoints, step through code, and inspect memory and processor registers, effectively allowing them to diagnose and fix issues in real-time.
+硬件探头是一个被用于嵌入式系统的开发和调试的设备,其可以使得主机和目标嵌入式设备间的通信变得简单.它通常支持像是JTAG或者SWD这样的协议,可以编程,调试和分析嵌入式系统上的微控制器或者微处理器.硬件探头对于要设置断点,步进代码,和观察内存与处理器的寄存器的开发者来说很重要,可以让开发者们高效地实时地分析和修复问题.
### Rusty-probe
-Rusty-probe is an open-sourced USB-based hardware debugging probe designed to work with probe-rs. The combination of Rusty-Probe and probe-rs provides an easy-to-use, cost-effective solution for developers working with embedded Rust applications.
+Rusty-probe是一个开源的基于USB的硬件调试探测器,被设计用来辅助probe-rs一起工作.Rusy-Probe和probe-rs的结合为嵌入式Rust应用的开发者提供了一个易用的,成本高效的解决方案.
### ST-Link
-The ST-Link is a popular debugging and programming probe developed by STMicroelectronics primarily for their STM32 and STM8 microcontroller series. It supports both debugging and programming via JTAG or SWD (Serial Wire Debug) interfaces. ST-Link is widely used due to its direct support from STMicroelectronics' extensive range of development boards and its integration into major IDEs, making it a convenient choice for developers working with STM microcontrollers.
+ST-Link是一个由STMicroelectronics开发的常见的调试和编程探测器,其主要用于它们的STM32和STM8微控制器系列.它支持通过JTAG或者SWD接口进行调试和编程.因为STMicroelectronics的大量的开发板对其直接支持并且它集成进了主流的IDEs中,所以使得它成为使用STM微控制器的开发者的首选.
### J-Link
-J-Link, developed by SEGGER Microcontroller, is a robust and versatile debugger supporting a wide range of CPU cores and devices beyond just ARM, such as RISC-V. Known for its high performance and reliability, J-Link supports various communication interfaces, including JTAG, SWD, and fine-pitch JTAG interfaces. It is favored for its advanced features like unlimited breakpoints in flash memory and its compatibility with a multitude of development environments.
+J-Link是由SEGGER微控制器开发的,它是一个鲁棒和功能丰富的调试器,其支持大量的CPU内核和设备,不仅仅是ARM,比如RISC-V.因其高性能和可读性而闻名,J-Link支持不同的通信接口,包括JTAG,SWD,和fine-pitch JTAG接口.它因其高级的特性而受到欢迎,比如在flash存储中的无限的断点和它与多种开发环境的兼容性.
### MCU-Link
-MCU-Link is a debugging probe that also functions as a programmer, provided by NXP Semiconductors. It supports a variety of ARM Cortex microcontrollers and interfaces seamlessly with development tools like MCUXpresso IDE. MCU-Link is particularly notable for its versatility and affordability, making it an accessible option for hobbyists, educators, and professional developers alike.
\ No newline at end of file
+MCU-Link是一个调试探测器,也可以作为编程器使用,由NXP Semiconductors提供.它支持不同的ARM Cortex微控制器且可以与像是MCUXpresso IDE这样的开发工具进行无缝地交互.MCU-Link因其丰富的功能和易使用而闻名,使它成为像是爱好者,教育者,和专业的开发者们的可行的选项.
diff --git a/src/peripherals/a-first-attempt.md b/src/peripherals/a-first-attempt.md
index 59b65608..fc8daf8c 100644
--- a/src/peripherals/a-first-attempt.md
+++ b/src/peripherals/a-first-attempt.md
@@ -1,21 +1,21 @@
-# A First Attempt
+# Rust尝鲜
-## The Registers
+## 寄存器
-Let's look at the 'SysTick' peripheral - a simple timer which comes with every Cortex-M processor core. Typically you'll be looking these up in the chip manufacturer's data sheet or *Technical Reference Manual*, but this example is common to all ARM Cortex-M cores, let's look in the [ARM reference manual]. We see there are four registers:
+让我们看下 'SysTick' 外设 - 一个简单的计时器,它存在于每个Cortex-M处理器内核中。通常你能在芯片厂商的数据手册或者*技术参考手册*中看到它们,但是下面的例子对所有ARM Cortex-M核心都是通用的,让我们看下[ARM参考手册]。我们能看到这里有四个寄存器:
-[ARM reference manual]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/Babieigh.html
+[ARM参考手册]: http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/Babieigh.html
| 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|
-## The C Approach
+## C语言风格的方法(The C Approach)
-In Rust, we can represent a collection of registers in exactly the same way as we do in C - with a `struct`.
+在Rust中,我们可以像C语言一样,用一个 `struct` 表示一组寄存器。
```rust,ignore
#[repr(C)]
@@ -26,32 +26,29 @@ struct SysTick {
pub calib: u32,
}
```
-
-The qualifier `#[repr(C)]` tells the Rust compiler to lay this structure out like a C compiler would. That's very important, as Rust allows structure fields to be re-ordered, while C does not. You can imagine the debugging we'd have to do if these fields were silently re-arranged by the compiler! With this qualifier in place, we have our four 32-bit fields which correspond to the table above. But of course, this `struct` is of no use by itself - we need a variable.
+限定符 `#[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)
-Now, there are a couple of problems with the approach above.
+现在,上面的方法有一堆问题。
-1. We have to use unsafe every time we want to access our Peripheral.
-2. We've got no way of specifying which registers are read-only or read-write.
-3. Any piece of code anywhere in your program could access the hardware
- through this structure.
-4. Most importantly, it doesn't actually work...
+1. 每次想要访问外设,不得不使用unsafe 。
+2. 无法指定哪个寄存器是只读的或者读写的。
+3. 程序中任何地方的任何一段代码都可以通过这个结构体访问硬件。
+4. 最重要的是,实际上它并不能工作。
-Now, the problem is that compilers are clever. If you make two writes to the same piece of RAM, one after the other, the compiler can notice this and just skip the first write entirely. In C, we can mark variables as `volatile` to ensure that every read or write occurs as intended. In Rust, we instead mark the *accesses* as volatile, not the variable.
+现在的问题是编译器很聪明。如果你往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) };
```
-
-So, we've fixed one of our four problems, but now we have even more `unsafe` code! Fortunately, there's a third party crate which can help - [`volatile_register`].
+这样,我们已经修复了一个问题,但是现在我们有了更多的 `unsafe` 代码!幸运的是,有个第三方的crate可以帮助到我们 - [`volatile_register`]
[`volatile_register`]: https://crates.io/crates/volatile_register
@@ -76,13 +73,13 @@ fn get_time() -> u32 {
}
```
-Now, the volatile accesses are performed automatically through the `read` and `write` methods. It's still `unsafe` to perform writes, but to be fair, hardware is a bunch of mutable state and there's no way for the compiler to know whether these writes are actually safe, so this is a good default position.
+现在通过`read`和`write`方法,volatile accesses可以被自动执行。执行写操作仍然是 `unsafe` 的,但是公平地讲,硬件有一堆可变的状态,对于编译器来说没有办法知道是否这些写操作是真正安全的,因此默认就这样是个不错的选择。
-## The Rusty Wrapper
+## Rust风格的封装
-We need to wrap this `struct` up into a higher-layer API that is safe for our users to call. As the driver author, we manually verify the unsafe code is correct, and then present a safe API for our users so they don't have to worry about it (provided they trust us to get it right!).
+我们需要把这个`struct`封装进一个更高抽象的API中,这个API对于用户来说,可以安全地调用。作为驱动的作者,我们亲手验证不安全的代码是否正确,然后为我们的用户提供一个safe的API,因此用户们不必担心它(让他们相信我们不会出错!)。
-One example might be:
+有可能可以这样写:
```rust,ignore
use volatile_register::{RW, RO};
@@ -122,7 +119,7 @@ pub fn example_usage() -> String {
}
```
-Now, the problem with this approach is that the following code is perfectly acceptable to the compiler:
+现在,这种方法带来的问题是,下列的代码完全可以被编译器接受:
```rust,ignore
fn thread1() {
@@ -136,4 +133,4 @@ fn thread2() {
}
```
-Our `&mut self` argument to the `set_reload` function checks that there are no other references to *that* particular `SystemTimer` struct, but they don't stop the user creating a second `SystemTimer` which points to the exact same peripheral! Code written in this fashion will work if the author is diligent enough to spot all of these 'duplicate' driver instances, but once the code is spread out over multiple modules, drivers, developers, and days, it gets easier and easier to make these kinds of mistakes.
+虽然 `set_reload` 函数的 `&mut self` 参数保证了没有引用到其它的`SystemTimer`结构体,但是不能阻止用户去创造第二个`SystemTimer`,其指向同个外设!如果作者足够努力的话,他能发现所有这些'重复的'驱动实例,那么按这种方式写的代码就可以工作,但是一旦代码被散播一段时间,散播给多个模块,驱动,开发者,它会越来越容易触发此类错误。
diff --git a/src/peripherals/borrowck.md b/src/peripherals/borrowck.md
index fb817b2d..027faa76 100644
--- a/src/peripherals/borrowck.md
+++ b/src/peripherals/borrowck.md
@@ -1,19 +1,19 @@
-## Mutable Global State
+## 可变的全局状态
-Unfortunately, hardware is basically nothing but mutable global state, which can feel very frightening for a Rust developer. Hardware exists independently from the structures of the code we write, and can be modified at any time by the real world.
+不幸的是,硬件本质上是个可变的全局状态,Rust开发者可能会对此感到很害怕。因为硬件独立于我们所写的代码的结构,能被真实世界在任何时候改变。
-## What should our rules be?
+## 我们应该遵循什么规则?
-How can we reliably interact with these peripherals?
+我们如何才能做到可靠地与这些外设交互?
-1. Always use `volatile` methods to read or write to peripheral memory, as it can change at any time
-2. In software, we should be able to share any number of read-only accesses to these peripherals
-3. If some software should have read-write access to a peripheral, it should hold the only reference to that peripheral
+1. 总是使用 `volatile` 方法去读或者写外设存储器。因为它随时会改变。
+2. 在软件中,我们应该能共享任何数量的关于这些外设的只读访问
+3. 如果某个软件可以读写一个外设,它应该保有对那个外设的唯一引用。
-## The Borrow Checker
+## 借用检查器
-The last two of these rules sound suspiciously similar to what the Borrow Checker does already!
+这些规则最后两个听起来与借用检查器在做的事情很像!
-Imagine if we could pass around ownership of these peripherals, or offer immutable or mutable references to them?
+思考一下,我们是否可以传递这些外设的所有权,或者提供对它们的可变或者不可变的引用?
-Well, we can, but for the Borrow Checker, we need to have exactly one instance of each peripheral, so Rust can handle this correctly. Well, luckily in the hardware, there is only one instance of any given peripheral, but how can we expose that in the structure of our code?
+我们当然可以,但是对于借用检查器来说,每个外设只有一个实例的话,Rust才可以正确地处理这件事。幸运的是,在硬件中,任何给定的外设,只有一个实例,但是我们该如何将它暴露在代码的结构中呢?
diff --git a/src/peripherals/index.md b/src/peripherals/index.md
index 23731c56..2a4530d8 100644
--- a/src/peripherals/index.md
+++ b/src/peripherals/index.md
@@ -1,44 +1,44 @@
-# Peripherals
+# 外设
-## What are Peripherals?
+## 什么是外设?
-Most Microcontrollers have more than just a CPU, RAM, or Flash Memory - they contain sections of silicon which are used for interacting with systems outside of the microcontroller, as well as directly and indirectly interacting with their surroundings in the world via sensors, motor controllers, or human interfaces such as a display or keyboard. These components are collectively known as Peripherals.
+大多数微处理器不仅仅有一个CPU,RAM,或者Flash存储器 - 它们还包含被用来与微处理器的外部系统进行交互的硅片部分,通过传感器,电机控制器,或者人机接口比如一个显示器或者键盘直接和间接地与周遭世界交互。这些组件统称为外设。
-These peripherals are useful because they allow a developer to offload processing to them, avoiding having to handle everything in software. Similar to how a desktop developer would offload graphics processing to a video card, embedded developers can offload some tasks to peripherals allowing the CPU to spend its time doing something else important, or doing nothing in order to save power.
+这些外设很有用,因为它们允许一个开发者将处理工作交给它们来做,避免了必须在软件中处理每件事。就像一个桌面开发者如何将图形处理工作让给一个显卡那样,嵌入式开发者能将一些任务让给外设去做,让CPU可以把时间放在做其它更重要的事上,或者为了省电啥事也不做。
-If you look at the main circuit board in an old-fashioned home computer from the 1970s or 1980s (and actually, the desktop PCs of yesterday are not so far removed from the embedded systems of today) you would expect to see:
+如果你看向来自1970s或者1980s的旧型号的家庭电脑的主板(其实,昨日的桌面PCs与今日的嵌入式系统没太大区别),你将看到:
-* A processor
-* A RAM chip
-* A ROM chip
-* An I/O controller
+* 一个处理器
+* 一个RAM芯片
+* 一个ROM芯片
+* 一个I/O控制器
-The RAM chip, ROM chip and I/O controller (the peripheral in this system) would be joined to the processor through a series of parallel traces known as a 'bus'. This bus carries address information, which selects which device on the bus the processor wishes to communicate with, and a data bus which carries the actual data. In our embedded microcontrollers, the same principles apply - it's just that everything is packed on to a single piece of silicon.
+RAM芯片,ROM芯片和I/O控制器(这个系统中的外设)会通过一系列并行的迹(traces)又被称为一个"总线"被加进处理器中。地址总线搬运地址信息,其用来选择处理器希望跟总线上哪个设备通信,还有一个用来搬运实际数据的数据总线。在我们的嵌入式微控制器中,应用了相同的概念 - 只是所有的东西被打包到一片硅片上。
-However, unlike graphics cards, which typically have a Software API like Vulkan, Metal, or OpenGL, peripherals are exposed to our Microcontroller with a hardware interface, which is mapped to a chunk of the memory.
+然而,不像显卡,显卡通常有像是Vulkan,Metal,或者OpenGL这样的一个软件API。外设暴露给微控制器的是一个硬件接口,其被映射到一块存储区域。
-## Linear and Real Memory Space
+## 线性的物理存储空间
-On a microcontroller, writing some data to some other arbitrary address, such as `0x4000_0000` or `0x0000_0000`, may also be a completely valid action.
+在一个微控制器上,随便往一些地址写一些数据,比如 `0x4000_0000` 或者 `0x0000_0000`,可能也是一个完全有效的动作。
-On a desktop system, access to memory is tightly controlled by the MMU, or Memory Management Unit. This component has two major responsibilities: enforcing access permission to sections of memory (preventing one process from reading or modifying the memory of another process); and re-mapping segments of the physical memory to virtual memory ranges used in software. Microcontrollers do not typically have an MMU, and instead only use real physical addresses in software.
+在一个桌面系统上,访问内存被MMU,或者内存管理单元紧紧地控制着。这个组件有两个主要责任: 对部分内存加入访问权限(防止一个进程读取或者修改另一个进程的内存);重映射物理内存的段到软件中使用的虚拟内存范围上。微控制器通常没有一个MMU,反而在软件中只使用真实的物理地址。
-Although 32 bit microcontrollers have a real and linear address space from `0x0000_0000`, and `0xFFFF_FFFF`, they generally only use a few hundred kilobytes of that range for actual memory. This leaves a significant amount of address space remaining. In earlier chapters, we were talking about RAM being located at address `0x2000_0000`. If our RAM was 64 KiB long (i.e. with a maximum address of 0xFFFF) then addresses `0x2000_0000` to `0x2000_FFFF` would correspond to our RAM. When we write to a variable which lives at address `0x2000_1234`, what happens internally is that some logic detects the upper portion of the address (0x2000 in this example) and then activates the RAM so that it can act upon the lower portion of the address (0x1234 in this case). On a Cortex-M we also have our Flash ROM mapped in at address `0x0000_0000` up to, say, address `0x0007_FFFF` (if we have a 512 KiB Flash ROM). Rather than ignore all remaining space between these two regions, Microcontroller designers instead mapped the interface for peripherals in certain memory locations. This ends up looking something like this:
+虽然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)
[Nordic nRF52832 Datasheet (pdf)]
-## Memory Mapped Peripherals
+## 存储映射的外设
-Interaction with these peripherals is simple at a first glance - write the right data to the correct address. For example, sending a 32 bit word over a serial port could be as direct as writing that 32 bit word to a certain memory address. The Serial Port Peripheral would then take over and send out the data automatically.
+乍一看,与这些外设交互很简单 - 将正确的数据写入正确的地址。比如,在一个串行端口上发送一个32位字,可以直接把那个32位字写入某个存储地址。串行端口外设然后能自动获取和发出数据。
-Configuration of these peripherals works similarly. Instead of calling a function to configure a peripheral, a chunk of memory is exposed which serves as the hardware API. Write `0x8000_0000` to a SPI Frequency Configuration Register, and the SPI port will send data at 8 Megabits per second. Write `0x0200_0000` to the same address, and the SPI port will send data at 125 Kilobits per second. These configuration registers look a little bit like this:
+这些外设的配置工作相似。不是调用一个函数去配置一个外设,而是暴露一块地址空间作为硬件API。向一个SPI频率控制寄存器写入 `0x8000_0000`,SPI端口将会按照每秒8MB的速度发送数据。向同个地址写入 `0x0200_0000`,SPI端口将会按照每秒125KiB的速度发送数据。这些配置寄存器看起来有点像这个:
![](../assets/nrf52-spi-frequency-register.png)
[Nordic nRF52832 Datasheet (pdf)]
-This interface is how interactions with the hardware are made, no matter what language is used, whether that language is Assembly, C, or Rust.
+这个接口是关于如何与硬件交互的,其与被使用的语言无关,无论这个语言是汇编,C,或者Rust。
[Nordic nRF52832 Datasheet (pdf)]: http://infocenter.nordicsemi.com/pdf/nRF52832_PS_v1.1.pdf
diff --git a/src/peripherals/singletons.md b/src/peripherals/singletons.md
index 1f3a556e..bcdae720 100644
--- a/src/peripherals/singletons.md
+++ b/src/peripherals/singletons.md
@@ -1,15 +1,15 @@
-# Singletons
+# 单例
-> In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object.
+> 在软件工程中,单例模式是一个软件设计模式,其限制了一个类到一个对象的实例化。
>
> *Wikipedia: [Singleton Pattern]*
[Singleton Pattern]: https://en.wikipedia.org/wiki/Singleton_pattern
-## But why can't we just use global variable(s)?
+## 为什么不可以使用全局变量?
-We could make everything a public static, like this
+可以像这样,我们可以使每个东西都变成公共静态的(public static):
```rust,ignore
static mut THE_SERIAL_PORT: SerialPort = SerialPort;
@@ -21,11 +21,11 @@ fn main() {
}
```
-But this has a few problems. It is a mutable global variable, and in Rust, these are always unsafe to interact with. These variables are also visible across your whole program, which means the borrow checker is unable to help you track references and ownership of these variables.
+但是这个带来了一些问题。它是一个可变的全局变量,在Rust,与这些变量交互总是unsafe的。这些变量在你所有的程序间也是可见的,意味着借用检查器不能帮你跟踪这些变量的引用和所有权。
-## How do we do this in Rust?
+## 在Rust中要怎么做?
-Instead of just making our peripheral a global variable, we might instead decide to make a structure, in this case called `PERIPHERALS`, which contains an `Option` for each of our peripherals.
+与其只是让我们的外设变成一个全局变量,我们不如创造一个结构体,在这个例子里其被叫做 `PERIPHERALS`,这个全局变量对于我们的每个外设,它都有一个与之对应的 `Option` .
```rust,ignore
struct Peripherals {
@@ -42,29 +42,29 @@ static mut PERIPHERALS: Peripherals = Peripherals {
};
```
-This structure allows us to obtain a single instance of our peripheral. If we try to call `take_serial()` more than once, our code will panic!
+这个结构体允许我们获得一个外设的实例。如果我们尝试调用`take_serial()`获得多个实例,我们的代码将会抛出运行时恐慌(panic)!
```rust,ignore
fn main() {
let serial_1 = unsafe { PERIPHERALS.take_serial() };
- // This panics!
+ // 这里造成运行时恐慌!
// let serial_2 = unsafe { PERIPHERALS.take_serial() };
}
```
-Although interacting with this structure is `unsafe`, once we have the `SerialPort` it contained, we no longer need to use `unsafe`, or the `PERIPHERALS` structure at all.
+虽然与这个结构体交互是`unsafe`,然而一旦我们获得了它包含的 `SerialPort`,我们将不再需要使用`unsafe`,或者`PERIPHERALS`结构体。
-This has a small runtime overhead because we must wrap the `SerialPort` structure in an option, and we'll need to call `take_serial()` once, however this small up-front cost allows us to leverage the borrow checker throughout the rest of our program.
+这个带来了少量的运行时开销,因为我们必须打包 `SerialPort` 结构体进一个option中,且我们将需要调用一次 `take_serial()`,但是这种少量的前期成本,能使我们在接下来的程序中使用借用检查器(borrow checker) 。
-## Existing library support
+## 已存在的库支持
-Although we created our own `Peripherals` structure above, it is not necessary to do this for your code. the `cortex_m` crate contains a macro called `singleton!()` that will perform this action for you.
+虽然我们在上面生成了我们自己的 `Peripherals` 结构体,但这并不是必须的。`cortex_m` crate 包含一个被叫做 `singleton!()` 的宏,它可以为你完成这个任务。
```rust,ignore
use cortex_m::singleton;
fn main() {
- // OK if `main` is executed only once
+ // OK 如果 `main` 只被执行一次
let x: &'static mut bool =
singleton!(: bool = false).unwrap();
}
@@ -72,7 +72,7 @@ fn main() {
[cortex_m docs](https://docs.rs/cortex-m/latest/cortex_m/macro.singleton.html)
-Additionally, if you use [`cortex-m-rtic`](/~https://github.com/rtic-rs/cortex-m-rtic), the entire process of defining and obtaining these peripherals are abstracted for you, and you are instead handed a `Peripherals` structure that contains a non-`Option` version of all of the items you define.
+另外,如果你使用 [`cortex-m-rtic`](/~https://github.com/rtic-rs/cortex-m-rtic),它将获取和定义这些外设的整个过程抽象了出来,你将获得一个`Peripherals`结构体,其包含了所有你定义了的项的一个非 `Option` 的版本。
```rust,ignore
// cortex-m-rtic v0.5.x
@@ -82,25 +82,25 @@ const APP: () = {
fn init(cx: init::Context) {
static mut X: u32 = 0;
- // Cortex-M peripherals
+ // Cortex-M外设
let core: cortex_m::Peripherals = cx.core;
- // Device specific peripherals
+ // 设备特定的外设
let device: lm3s6965::Peripherals = cx.device;
}
}
```
-## But why?
+## 为什么?
-But how do these Singletons make a noticeable difference in how our Rust code works?
+但是这些单例模式是如何使我们的Rust代码在工作方式上产生很大不同的?
```rust,ignore
impl SerialPort {
const SER_PORT_SPEED_REG: *mut u32 = 0x4000_1000 as _;
fn read_speed(
- &self // <------ This is really, really important
+ &self // <------ 这个真的真的很重要
) -> u32 {
unsafe {
ptr::read_volatile(Self::SER_PORT_SPEED_REG)
@@ -109,30 +109,31 @@ impl SerialPort {
}
```
-There are two important factors in play here:
-* Because we are using a singleton, there is only one way or place to obtain a `SerialPort` structure
-* To call the `read_speed()` method, we must have ownership or a reference to a `SerialPort` structure
+这里有两个重要因素:
-These two factors put together means that it is only possible to access the hardware if we have appropriately satisfied the borrow checker, meaning that at no point do we have multiple mutable references to the same hardware!
+* 因为我们正在使用一个单例模式,所以我们只有一种方法或者地方去获得一个 `SerialPort` 结构体。
+* 为了调用 `read_speed()` 方法,我们必须拥有一个 `SerialPort` 结构体的所有权或者一个引用。
+
+这两个因素放在一起意味着,只有当我们满足了借用检查器的条件时,我们才有可能访问硬件,也意味着在任何时候不可能存在多个对同一个硬件的可变引用(&mut)!
```rust,ignore
fn main() {
- // missing reference to `self`! Won't work.
+ // 缺少对`self`的引用!将不会工作。
// SerialPort::read_speed();
let serial_1 = unsafe { PERIPHERALS.take_serial() };
- // you can only read what you have access to
+ // 你只能读取你有权访问的内容
let _ = serial_1.read_speed();
}
```
-## Treat your hardware like data
+## 像对待数据一样对待硬件
-Additionally, because some references are mutable, and some are immutable, it becomes possible to see whether a function or method could potentially modify the state of the hardware. For example,
+另外,因为一些引用是可变的,一些是不可变的,就可以知道一个函数或者方法是否有能力修改硬件的状态。比如,
-This is allowed to change hardware settings:
+这个函数可以改变硬件的配置:
```rust,ignore
fn setup_spi_port(
@@ -143,7 +144,7 @@ fn setup_spi_port(
}
```
-This isn't:
+这个不行:
```rust,ignore
fn read_button(gpio: &GpioPin) -> bool {
@@ -151,4 +152,5 @@ fn read_button(gpio: &GpioPin) -> bool {
}
```
-This allows us to enforce whether code should or should not make changes to hardware at **compile time**, rather than at runtime. As a note, this generally only works across one application, but for bare metal systems, our software will be compiled into a single application, so this is not usually a restriction.
+这允许我们在**编译时**而不是运行时强制代码是否应该或者不应该对硬件进行修改。要注意,这通常在只有一个应用的情况下起作用,但是对于裸机系统来说,我们的软件将被编译进一个单一应用中,因此这通常不是一个限制。
+
diff --git a/src/portability/index.md b/src/portability/index.md
index 1e1f03bc..1a928e3b 100644
--- a/src/portability/index.md
+++ b/src/portability/index.md
@@ -1,30 +1,30 @@
-# Portability
+# 可移植性
-In embedded environments portability is a very important topic: Every vendor and even each family from a single manufacturer offers different peripherals and capabilities and similarly the ways to interact with the peripherals will vary.
+在嵌入式环境中,可移植性是一个非常重要的主题: 每个供应商甚至同个制造商的不同系列间,都提供了不同的外设和功能。同样地,与外设交互的方式也将会不一样。
-A common way to equalize such differences is via a layer called Hardware Abstraction layer or **HAL**.
+通过一个被叫做硬件抽象层或者**HAL**的层去均等化这种差异是一种常见的方法。
-> Hardware abstractions are sets of routines in software that emulate some platform-specific details, giving programs direct access to the hardware resources.
->
-> They often allow programmers to write device-independent, high performance applications by providing standard operating system (OS) calls to hardware.
+> 在软件中硬件抽象是一组函数,其模仿了一些平台特定的细节,让程序可以直接访问硬件资源。
+> 通过向硬件提供标准的操作系统(OS)调用,它可以让程序员编写独立于设备的高性能应用。
>
> *Wikipedia: [Hardware Abstraction Layer]*
[Hardware Abstraction Layer]: https://en.wikipedia.org/wiki/Hardware_abstraction
-Embedded systems are a bit special in this regard since we typically do not have operating systems and user installable software but firmware images which are compiled as a whole as well as a number of other constraints. So while the traditional approach as defined by Wikipedia could potentially work it is likely not the most productive approach to ensure portability.
+在这方面,嵌入式系统有点特别,因为通常没有操作系统和用户可安装的软件,而只有固件镜像,其作为一个整体被编译且伴着许多约束。因此虽然维基百科定义的传统方法可能有用,但是它不是确保可移植性最有效的方法。
+
+在Rust中我们要怎么实现这个目标呢?让我们进入**embedded-hal**...
-How do we do this in Rust? Enter **embedded-hal**...
+## 什么是embedded-hal?
-## What is embedded-hal?
+简而言之,它是一组traits,其定义了**HAL implementations**,**驱动**,**应用(或者固件)** 之间的实现约定(implementation contracts)。这些约定包括功能(即约定,如果为某个类型实现了某个trait,**HAL implementation**就提供了某个功能)和方法(即,如果构造一个实现了某个trait的类型,约定保障类型肯定有在trait中指定的方法)。
-In a nutshell it is a set of traits which define implementation contracts between **HAL implementations**, **drivers** and **applications (or firmwares)**. Those contracts include both capabilities (i.e. if a trait is implemented for a certain type, the **HAL implementation** provides a certain capability) and methods (i.e. if you can construct a type implementing a trait it is guaranteed that you have the methods specified in the trait available).
-A typical layering might look like this:
+典型的分层可能如下所示:
![](../assets/rust_layers.svg)
-Some of the defined traits in **embedded-hal** are:
+一些在**embedded-hal**中被定义的traits:
* GPIO (input and output pins)
* Serial communication
* I2C
@@ -32,31 +32,33 @@ Some of the defined traits in **embedded-hal** are:
* Timers/Countdowns
* Analog Digital Conversion
-The main reason for having the **embedded-hal** traits and crates implementing and using them is to keep complexity in check. If you consider that an application might have to implement the use of the peripheral in the hardware as well as the application and potentially drivers for additional hardware components, then it should be easy to see that the re-usability is very limited. Expressed mathematically, if **M** is the number of peripheral HAL implementations and **N** the number of drivers then if we were to reinvent the wheel for every application then we would end up with **M*N** implementations while by using the *API* provided by the **embedded-hal** traits will make the implementation complexity approach **M+N**. Of course there're additional benefits to be had, such as less trial-and-error due to a well-defined and ready-to-use APIs.
+使用**embedded-hal** traits和依赖**embedded-hal**的crates的主要原因是为了控制复杂性。如果发现一个应用可能必须要实现对硬件外设的使用,以及需要实现应用程序和其它硬件组件间潜在的驱动,那么其应该很容易被看作是可复用性有限的。用数学语言来说就是,如果**M**是外设HAL implementations的数量,**N**是驱动的数量,那么如果我们要为每个应用重新发明轮子我们最终会有**M*N**个实现,然而通过使用**embedded-hal**的traits提供的 *API* 将会使实现复杂性变成**M+N** 。当然还有其它好处,比如由于API定义良好,开箱即用,导致试错减少。
-## Users of the embedded-hal
-As said above there are three main users of the HAL:
+## embedded-hal的用户
+
+像上面所说的,HAL有三个主要用户:
### HAL implementation
-A HAL implementation provides the interfacing between the hardware and the users of the HAL traits. Typical implementations consist of three parts:
-* One or more hardware specific types
-* Functions to create and initialize such a type, often providing various configuration options (speed, operation mode, use pins, etc.)
-* one or more `trait` `impl` of **embedded-hal** traits for that type
+HAL implentation提供硬件和HAL traits的用户之间的接口。典型的实现由三部分组成:
+
+* 一个或者多个硬件特定的类型
+* 生成和初始化这个类型的函数,函数经常提供不同的配置选项(速度,操作模式,使用的管脚,etc 。)
+* 与这个类型有关的一个或者多个 **embedded-hal** traits 的 `trait` `impl`
-Such a **HAL implementation** can come in various flavours:
-* Via low-level hardware access, e.g. via registers
-* Via operating system, e.g. by using the `sysfs` under Linux
-* Via adapter, e.g. a mock of types for unit testing
-* Via driver for hardware adapters, e.g. I2C multiplexer or GPIO expander
+这样的一个 **HAL implementation** 可以有多个方法来实现:
+* 通过低级硬件访问,比如通过寄存器。
+* 通过操作系统,比如通过使用Linux下的 `sysfs`
+* 通过适配器,比如一个与单元测试有关的类型的仿真
+* 通过相关硬件适配器的驱动,e.g. I2C多路复用器或者GPIO扩展器(I2C multiplexer or GPIO expander)
-### Driver
+### 驱动
-A driver implements a set of custom functionality for an internal or external component, connected to a peripheral implementing the embedded-hal traits. Typical examples for such drivers include various sensors (temperature, magnetometer, accelerometer, light), display devices (LED arrays, LCD displays) and actuators (motors, transmitters).
+驱动为一个外部或者内部组件实现了一组自定义的功能,被连接到一个实现了embedded-hal traits的外设上。这种驱动的典型的例子包括多种传感器(温度计,磁力计,加速度计,光照计),显示设备(LED阵列,LCD显示屏)和执行器(电机,发送器)。
-A driver has to be initialized with an instance of type that implements a certain `trait` of the embedded-hal which is ensured via trait bound and provides its own type instance with a custom set of methods allowing to interact with the driven device.
+必须使用实现了embedded-hal的某个`trait`的类型的实例来初始化驱动,这是通过trait bound来确保的,驱动也提供了它自己的类型实例,这个实例具有一组自定义的方法,这些方法允许与被驱动的设备交互。
-### Application
+### 应用
-The application binds the various parts together and ensures that the desired functionality is achieved. When porting between different systems, this is the part which requires the most adaptation efforts, since the application needs to correctly initialize the real hardware via the HAL implementation and the initialisation of different hardware differs, sometimes drastically so. Also the user choice often plays a big role, since components can be physically connected to different terminals, hardware buses sometimes need external hardware to match the configuration or there are different trade-offs to be made in the use of internal peripherals (e.g. multiple timers with different capabilities are available or peripherals conflict with others).
+应用把多个部分结合在一起并确保需要的功能被实现。当在不同的系统间移植时,这部分的适配是花费最多精力的地方,因为应用需要通过HAL implementation正确地初始化真实的硬件,而且不同硬件的初始化也不相同,甚至有时候差别非常大。用户的选择也在其中扮演了非常重大的角色,因为组件能被物理连接到不同的端口,硬件总线有时候需要外部硬件去匹配配置,或者用户在内部外设的使用上有不同的考量。
diff --git a/src/start/exceptions.md b/src/start/exceptions.md
index bd9b85b7..c5afc97f 100644
--- a/src/start/exceptions.md
+++ b/src/start/exceptions.md
@@ -1,67 +1,44 @@
-# Exceptions
+# 异常
-Exceptions, and interrupts, are a hardware mechanism by which the processor
-handles asynchronous events and fatal errors (e.g. executing an invalid
-instruction). Exceptions imply preemption and involve exception handlers,
-subroutines executed in response to the signal that triggered the event.
+异常和中断,是处理器用来处理异步事件和致命错误(e.g. 执行一个无效的指令)的一种硬件机制。异常意味着抢占并涉及到异常处理程序,即响应触发事件的信号的子程序。
-The `cortex-m-rt` crate provides an [`exception`] attribute to declare exception
-handlers.
+`cortex-m-rt` crate提供了一个 [`exception`] 属性去声明异常处理程序。
[`exception`]: https://docs.rs/cortex-m-rt-macros/latest/cortex_m_rt_macros/attr.exception.html
``` rust,ignore
-// Exception handler for the SysTick (System Timer) exception
+// SysTick (System计时器)异常的异常处理函数
#[exception]
fn SysTick() {
// ..
}
```
-Other than the `exception` attribute exception handlers look like plain
-functions but there's one more difference: `exception` handlers can *not* be
-called by software. Following the previous example, the statement `SysTick();`
-would result in a compilation error.
+除了 `exception` 属性,异常处理函数看起来和普通函数一样,但是有一个很大的不同: `exception` 处理函数 *不能* 被软件调用。在先前的例子中,语句 `SysTick();` 将会导致一个编译错误。
-This behavior is pretty much intended and it's required to provide a feature:
-`static mut` variables declared *inside* `exception` handlers are *safe* to use.
+这么做是有目的的,因为异常处理函数必须具有一个特性: 在异常处理函数中被声明为`static mut`的变量能被安全(safe)地使用。
``` rust,ignore
#[exception]
fn SysTick() {
static mut COUNT: u32 = 0;
- // `COUNT` has transformed to type `&mut u32` and it's safe to use
+ // `COUNT` 被转换到了 `&mut u32` 类型且它用起来是安全的
*COUNT += 1;
}
```
-As you may know, using `static mut` variables in a function makes it
-[*non-reentrant*](https://en.wikipedia.org/wiki/Reentrancy_(computing)). It's undefined behavior to call a non-reentrant function,
-directly or indirectly, from more than one exception / interrupt handler or from
-`main` and one or more exception / interrupt handlers.
+就像你可能已经知道的那样,在一个函数里使用`static mut`变量,会让函数变成[*非可重入函数(non-reentrancy)*](https://en.wikipedia.org/wiki/Reentrancy_(computing))。从多个异常/中断处理函数,或者从`main`函数和多个异常/中断处理函数中,直接或者间接地调用一个非可重入(non-reentrancy)函数是未定义的行为。
-Safe Rust must never result in undefined behavior so non-reentrant functions
-must be marked as `unsafe`. Yet I just told that `exception` handlers can safely
-use `static mut` variables. How is this possible? This is possible because
-`exception` handlers can *not* be called by software thus reentrancy is not
-possible.
+安全的Rust不能导致未定义的行为出现,所以非可重入函数必须被标记为 `unsafe`。然而,我刚说了`exception`处理函数能安全地使用`static mut`变量。这怎么可能?因为`exception`处理函数 *不* 能被软件调用因此重入(reentrancy)不会发生,所以这才变得可能。
-> Note that the `exception` attribute transforms definitions of static variables
-> inside the function by wrapping them into `unsafe` blocks and providing us
-> with new appropriate variables of type `&mut` of the same name.
-> Thus we can dereference the reference via `*` to access the values of the variables without
-> needing to wrap them in an `unsafe` block.
+> 注意,`exception`属性,通过将静态变量封装进`unsafe`块中并为我们提供了名字相同的,类型为 `&mut` 的,合适的新变量,转换了函数中静态变量的定义。因此我们可以通过 `*` 解引用访问变量的值而不需要将它们打包进一个 `unsafe` 块中。
-## A complete example
+## 一个完整的例子
-Here's an example that uses the system timer to raise a `SysTick` exception
-roughly every second. The `SysTick` exception handler keeps track of how many
-times it has been called in the `COUNT` variable and then prints the value of
-`COUNT` to the host console using semihosting.
+这里有个例子,使用系统计时器大概每秒抛出一个 `SysTick` 异常。异常处理函数使用 `COUNT` 变量追踪它自己被调用了多少次,然后使用半主机模式(semihosting)打印 `COUNT` 的值到主机控制台上。
-> **NOTE**: You can run this example on any Cortex-M device; you can also run it
-> on QEMU
+> **注意**: 你能在任何Cortex-M设备上运行这个例子;你也能在QEMU运行它。
```rust,ignore
#![deny(unsafe_code)]
@@ -84,9 +61,9 @@ fn main() -> ! {
let p = cortex_m::Peripherals::take().unwrap();
let mut syst = p.SYST;
- // configures the system timer to trigger a SysTick exception every second
+ // 配置系统的计时器每秒去触发一个SysTick异常
syst.set_clock_source(SystClkSource::Core);
- // this is configured for the LM3S6965 which has a default CPU clock of 12 MHz
+ // 这是关于LM3S6965的配置,其有一个12MHz的默认CPU时钟
syst.set_reload(12_000_000);
syst.clear_current();
syst.enable_counter();
@@ -102,7 +79,7 @@ fn SysTick() {
*COUNT += 1;
- // Lazy initialization
+ // 惰性初始化(Lazy initialization)
if STDOUT.is_none() {
*STDOUT = hio::hstdout().ok();
}
@@ -111,10 +88,10 @@ fn SysTick() {
write!(hstdout, "{}", *COUNT).ok();
}
- // IMPORTANT omit this `if` block if running on real hardware or your
- // debugger will end in an inconsistent state
+ // 重要信息 如果运行在真正的硬件上,去掉这个 `if` 块,
+ // 否则你的调试器将会以一种不一致的状态结束
if *COUNT == 9 {
- // This will terminate the QEMU process
+ // 这将终结QEMU进程
debug::exit(debug::EXIT_SUCCESS);
}
}
@@ -138,15 +115,11 @@ $ cargo run --release
123456789
```
-If you run this on the Discovery board you'll see the output on the OpenOCD
-console. Also, the program will *not* stop when the count reaches 9.
+如果你在Discovery开发板上运行这个例子,你将会在OpenOCD控制台上看到输出。还有,当计数到达9的时候,程序将 *会* 停止。
-## The default exception handler
+## 默认异常处理函数
-What the `exception` attribute actually does is *override* the default exception
-handler for a specific exception. If you don't override the handler for a
-particular exception it will be handled by the `DefaultHandler` function, which
-defaults to:
+`exception` 属性真正做的是,*覆盖* 了一个特定异常的默认异常处理函数。如果你不覆盖一个特定异常的处理函数,它将会被 `DefaultHandler` 函数处理,其默认的是:
``` rust,ignore
fn DefaultHandler() {
@@ -154,43 +127,28 @@ fn DefaultHandler() {
}
```
-This function is provided by the `cortex-m-rt` crate and marked as
-`#[no_mangle]` so you can put a breakpoint on "DefaultHandler" and catch
-*unhandled* exceptions.
+这个函数是 `cortex-m-rt` crate提供的,且被标记为 `#[no_mangle]` 因此你能在 "DefaultHandler" 上放置一个断点并捕获 *unhandled* 异常。
-It's possible to override this `DefaultHandler` using the `exception` attribute:
+可以使用 `exception` 属性覆盖这个 `DefaultHandler`:
``` rust,ignore
#[exception]
fn DefaultHandler(irqn: i16) {
- // custom default handler
+ // 自定义默认处理函数
}
```
-The `irqn` argument indicates which exception is being serviced. A negative
-value indicates that a Cortex-M exception is being serviced; and zero or a
-positive value indicate that a device specific exception, AKA interrupt, is
-being serviced.
+`irqn` 参数指出了被服务的是哪个异常。一个负数值指出了被服务的是一个Cortex-M异常;0或者一个正数值指出了被服务的是一个设备特定的异常,也就是中断。
-## The hard fault handler
+## 硬错误(Hard Fault)处理函数
-The `HardFault` exception is a bit special. This exception is fired when the
-program enters an invalid state so its handler can *not* return as that could
-result in undefined behavior. Also, the runtime crate does a bit of work before
-the user defined `HardFault` handler is invoked to improve debuggability.
+`HardFault`异常有点特别。当程序进入一个无法工作的状态时,这个异常被触发,因此它的处理函数 *不能* 返回,因为这么做可能导致一个未定义的行为。在用户定义的 `HardFault` 处理函数被调用之前,运行时crate还做了一些工作以改进调试功能。
-The result is that the `HardFault` handler must have the following signature:
-`fn(&ExceptionFrame) -> !`. The argument of the handler is a pointer to
-registers that were pushed into the stack by the exception. These registers are
-a snapshot of the processor state at the moment the exception was triggered and
-are useful to diagnose a hard fault.
+结果是,`HardFault`处理函数必须有下列的签名: `fn(&ExceptionFrame) -> !` 。处理函数的参数是一个指针,它指向被异常推入栈中的寄存器。这些寄存器是异常被触发那刻,处理器状态的一个记录,能被用来分析一个硬错误。
-Here's an example that performs an illegal operation: a read to a nonexistent
-memory location.
+这里有个执行不合法操作的案例: 读取一个不存在的存储位置。
-> **NOTE**: This program won't work, i.e. it won't crash, on QEMU because
-> `qemu-system-arm -machine lm3s6965evb` doesn't check memory loads and will
-> happily return `0 `on reads to invalid memory.
+> **注意**: 这个程序在QEMU上不能起作用,i.e. 它不会崩溃,因为 `qemu-system-arm -machine lm3s6965evb` 不对读取存储的操作进行检查,且读取无效存储时将会开心地返回 `0`。
```rust,ignore
#![no_main]
@@ -206,7 +164,7 @@ use cortex_m_semihosting::hio;
#[entry]
fn main() -> ! {
- // read a nonexistent memory location
+ // 读取一个无效的存储位置
unsafe {
ptr::read_volatile(0x3FFF_FFFE as *const u32);
}
@@ -224,8 +182,7 @@ fn HardFault(ef: &ExceptionFrame) -> ! {
}
```
-The `HardFault` handler prints the `ExceptionFrame` value. If you run this
-you'll see something like this on the OpenOCD console.
+`HardFault`处理函数打印了`ExceptionFrame`值。如果你运行这个,你将会看到下面的东西打印到OpenOCD控制台上。
``` text
$ openocd
@@ -242,11 +199,9 @@ ExceptionFrame {
}
```
-The `pc` value is the value of the Program Counter at the time of the exception
-and it points to the instruction that triggered the exception.
-
-If you look at the disassembly of the program:
+`pc`值是异常时程序计数器(Program Counter)的值,它指向触发了异常的指令。
+如果你看向程序的反汇编:
``` text
$ cargo objdump --bin app --release -- -d --no-show-raw-insn --print-imm-hex
@@ -258,7 +213,4 @@ ResetTrampoline:
800094c: b #-0x4
```
-You can lookup the value of the program counter `0x0800094a` in the disassembly.
-You'll see that a load operation (`ldr r0, [r0]` ) caused the exception.
-The `r0` field of `ExceptionFrame` will tell you the value of register `r0`
-was `0x3fff_fffe` at that time.
+你可以在反汇编中搜索程序计数器`0x0800094a`的值。你将会看到一个读取操作(`ldr r0, [r0]`)导致了异常。`ExceptionFrame`的`r0`字段将告诉你,那时寄存器`r0`的值是`0x3fff_fffe` 。
diff --git a/src/start/hardware.md b/src/start/hardware.md
index ba9fe6e7..c3148f60 100644
--- a/src/start/hardware.md
+++ b/src/start/hardware.md
@@ -1,44 +1,27 @@
-# Hardware
+# 硬件
-By now you should be somewhat familiar with the tooling and the development
-process. In this section we'll switch to real hardware; the process will remain
-largely the same. Let's dive in.
+现在你应该有点熟悉工具和开发过程了。在这部分我们将切换到真正的硬件上;步骤非常相似。让我们深入下去。
-## Know your hardware
+## 认识你的硬件
-Before we begin you need to identify some characteristics of the target device
-as these will be used to configure the project:
+在我们开始之前,你需要了解下你的目标设备的一些特性,因为你将用它们来配置项目:
+- ARM 内核。比如 Cortex-M3 。
+- ARM 内核包括一个FPU吗?Cortex-M4**F**和Cortex-M7**F**有。
+- 目标设备有多少Flash和RAM?比如 256KiB的Flash和32KiB的RAM。
+- Flash和RAM映射在地址空间的什么位置?比如 RAM通常位于 `0x2000_0000` 地址处。
-- The ARM core. e.g. Cortex-M3.
+你可以在你的设备的数据手册和参考手册上找到这些信息。
-- Does the ARM core include an FPU? Cortex-M4**F** and Cortex-M7**F** cores do.
+这部分,要使用我们的参考硬件,STM32F3DISCOVERY。这个板子包含一个STM32F303VCT6微控制器。这个微控制器拥有:
+- 一个Cortex-M4F核心,它包含一个单精度FPU。
+- 位于 0x0800_0000 地址的256KiB的Flash。
+- 位于 0x2000_0000 地址的40KiB的RAM。(这里还有其它的RAM区域,但是为了方便起见,我们将忽略它)。
-- How much Flash memory and RAM does the target device have? e.g. 256 KiB of
- Flash and 32 KiB of RAM.
+## 配置
-- Where are Flash memory and RAM mapped in the address space? e.g. RAM is
- commonly located at address `0x2000_0000`.
+我们将使用一个新的模板实例从零开始。对于新手,请参考[先前的QEMU]章节,了解如何在没有`cargo-generate`的情况下完成配置。
-You can find this information in the data sheet or the reference manual of your
-device.
-
-In this section we'll be using our reference hardware, the STM32F3DISCOVERY.
-This board contains an STM32F303VCT6 microcontroller. This microcontroller has:
-
-- A Cortex-M4F core that includes a single precision FPU
-
-- 256 KiB of Flash located at address 0x0800_0000.
-
-- 40 KiB of RAM located at address 0x2000_0000. (There's another RAM region but
- for simplicity we'll ignore it).
-
-## Configuring
-
-We'll start from scratch with a fresh template instance. Refer to the
-[previous section on QEMU] for a refresher on how to do this without
-`cargo-generate`.
-
-[previous section on QEMU]: qemu.md
+[先前的QEMU]: qemu.md
``` text
$ cargo generate --git /~https://github.com/rust-embedded/cortex-m-quickstart
@@ -49,7 +32,7 @@ $ cargo generate --git /~https://github.com/rust-embedded/cortex-m-quickstart
$ cd app
```
-Step number one is to set a default compilation target in `.cargo/config.toml`.
+第一步是在`.cargo/config.toml`中设置一个默认编译目标。
``` console
tail -n5 .cargo/config.toml
@@ -63,13 +46,11 @@ tail -n5 .cargo/config.toml
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
```
-We'll use `thumbv7em-none-eabihf` as that covers the Cortex-M4F core.
-> **NOTE**: As you may remember from the previous chapter, we have to install
-> all targets and this is a new one. So don't forget to run the installation
-> process `rustup target add thumbv7em-none-eabihf` for this target.
+我们将使用 `thumbv7em-none-eabihf`,因为它包括了Cortex-M4F内核.
+> **注意**:你可能还记得先前的章节,我们必须要安装所有的目标平台,这个平台是一个新的.
+> 所以,不要忘了为这个平台运行安装步骤 `rustup target add thumbv7em-none-eabihf` .
-The second step is to enter the memory region information into the `memory.x`
-file.
+第二步是将存储区域信息(memory region information)输入`memory.x`。
``` text
$ cat memory.x
@@ -81,57 +62,42 @@ MEMORY
RAM : ORIGIN = 0x20000000, LENGTH = 40K
}
```
-> **NOTE**: If you for some reason changed the `memory.x` file after you had made
-> the first build of a specific build target, then do `cargo clean` before
-> `cargo build`, because `cargo build` may not track updates of `memory.x`.
+> **注意**:如果你因为某些理由,在对某个编译目标首次编译后,改变了`memory.x`文件,需要在`cargo build`之前执行`cargo clean`。因为`cargo build`可能不会跟踪`memory.x`的更新。
-We'll start with the hello example again, but first we have to make a small
-change.
+我们将再次使用hello示例作为开始,但是首先我们必须做一个小改变。
-In `examples/hello.rs`, make sure the `debug::exit()` call is commented out or
-removed. It is used only for running in QEMU.
+在`examples/hello.rs`中,确保`debug::exit()`调用被注释掉了或者移除掉了。它只能用于在QEMU中运行的情况。
```rust,ignore
#[entry]
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 {}
}
```
-You can now cross compile programs using `cargo build`
-and inspect the binaries using `cargo-binutils` as you did before. The
-`cortex-m-rt` crate handles all the magic required to get your chip running,
-as helpfully, pretty much all Cortex-M CPUs boot in the same fashion.
+你可以像你之前做的一样,使用`cargo build`检查编译程序,使用`cargo-binutils`观察二进制项。`cortex-m-rt`库可以处理所有让芯片运行起来所需的魔法,几乎所有的Cortex-M CPUs都按同样的方式启动。
``` console
cargo build --example hello
```
-## Debugging
+## 调试
-Debugging will look a bit different. In fact, the first steps can look different
-depending on the target device. In this section we'll show the steps required to
-debug a program running on the STM32F3DISCOVERY. This is meant to serve as a
-reference; for device specific information about debugging check out [the
-Debugonomicon](/~https://github.com/rust-embedded/debugonomicon).
+调试会看起来有点不一样。事实上,取决于不同的目标设备,第一步可能看起来不一样。在这个章节里,我们将展示,调试一个在STM32F3DISCOVERY上运行的程序,所需要的步骤。这作为一个参考。关于调试有关的设备特定的信息,可以看[the Debugonomicon](/~https://github.com/rust-embedded/debugonomicon)。
-As before we'll do remote debugging and the client will be a GDB process. This
-time, however, the server will be OpenOCD.
+像之前一样,我们将进行远程调试,客户端将是一个GDB进程。不同的是,OpenOCD将是服务器。
-As done during the [verify] section connect the discovery board to your laptop /
-PC and check that the ST-LINK header is populated.
+像是在[安装验证]中做的那样,把你的笔记本/个人电脑和discovery开发板连接起来,检查ST-LINK的短路帽是否被安装了。
-[verify]: ../intro/install/verify.md
+[安装验证]: ../intro/install/verify.md
-On a terminal run `openocd` to connect to the ST-LINK on the discovery board.
-Run this command from the root of the template; `openocd` will pick up the
-`openocd.cfg` file which indicates which interface file and target file to use.
+在一个终端上运行 `openocd` 连接到你的开发板上的 ST-LINK 。从模板的根目录运行这个命令;`openocd` 将会选择 `openocd.cfg` 文件,它指出了所使用的接口文件(interface file)和目标文件(target file)。
``` console
cat openocd.cfg
@@ -152,9 +118,7 @@ source [find interface/stlink.cfg]
source [find target/stm32f3x.cfg]
```
-> **NOTE** If you found out that you have an older revision of the discovery
-> board during the [verify] section then you should modify the `openocd.cfg`
-> file at this point to use `interface/stlink-v2.cfg`.
+> **注意** 如果你在[安装验证]章节中,发现你的discovery开发板是一个更旧的版本,那么你应该修改你的 `openocd.cfg` 文件,注释掉 `interface/stlink.cfg`,让它去使用 `interface/stlink-v2.cfg` 。
``` text
$ openocd
@@ -176,17 +140,15 @@ Info : Target voltage: 2.913879
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
```
-On another terminal run GDB, also from the root of the template.
+在另一个终端,也是从模板的根目录,运行GDB。
``` text
gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello
```
-**NOTE**: like before you might need another version of gdb instead of `gdb-multiarch` depending
-on which one you installed in the installation chapter. This could also be
-`arm-none-eabi-gdb` or just `gdb`.
+**注意**: 像之前一样,你可能需要另一个版本的gdb而不是`gdb-multiarch`,取决于你在之前的章节安装了什么工具。这也可能使用的是`arm-none-eabi-gdb`或者只是`gdb` 。
-Next connect GDB to OpenOCD, which is waiting for a TCP connection on port 3333.
+接下来把GDB连接到OpenOCD,它正在等待一个在端口3333上的TCP链接。
``` console
(gdb) target remote :3333
@@ -194,8 +156,7 @@ Remote debugging using :3333
0x00000000 in ?? ()
```
-Now proceed to *flash* (load) the program onto the microcontroller using the
-`load` command.
+接下来使用`load`命令,继续 *flash*(加载) 程序到微控制器上。
``` console
(gdb) load
@@ -206,19 +167,16 @@ Start address 0x08000400, load size 7468
Transfer rate: 13 KB/sec, 2489 bytes/write.
```
-The program is now loaded. This program uses semihosting so before we do any
-semihosting call we have to tell OpenOCD to enable semihosting. You can send
-commands to OpenOCD using the `monitor` command.
+程序现在被加载了。这个程序使用半主机模式,因此在我们调用半主机模式之前,我们必须告诉OpenOCD使能半主机。你可以使用 `monitor` 命令,发送命令给OpenOCD 。
``` console
(gdb) monitor arm semihosting enable
semihosting is enabled
```
-> You can see all the OpenOCD commands by invoking the `monitor help` command.
+> 通过调用 `monitor help` 命令,你能看到所有的OpenOCD命令。
-Like before we can skip all the way to `main` using a breakpoint and the
-`continue` command.
+像我们之前一样,使用一个断点和 `continue` 命令我们可以跳过所有的步骤到 `main` 。
``` console
(gdb) break main
@@ -232,12 +190,9 @@ Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11
11 #[entry]
```
-> **NOTE** If GDB blocks the terminal instead of hitting the breakpoint after
-> you issue the `continue` command above, you might want to double check that
-> the memory region information in the `memory.x` file is correctly set up
-> for your device (both the starts *and* lengths).
+> **注意** 如果在你使用了上面的`continue`命令后,GDB阻塞住了终端而不是停在了断点处,你可能需要检查下`memory.x`文件中的存储分区的信息,对于你的设备来说是否被正确的设置了起始位置**和**大小 。
-Step into the main function with `step`.
+使用`step`步进main函数里。
``` console
(gdb) step
@@ -246,24 +201,24 @@ hello::__cortex_m_rt_main () at examples/hello.rs:13
13 hprintln!("Hello, world!").unwrap();
```
-After advancing the program with `next` you should see "Hello, world!" printed on the OpenOCD console,
-among other stuff.
+在使用了`next`让函数继续执行之后,你应该看到 "Hello, world!" 被打印到了OpenOCD控制台上。
-``` console
+``` text
$ openocd
(..)
-Info : halted: PC: 0x08000502
+Info : halted: PC: 0x08000e6c
Hello, world!
-Info : halted: PC: 0x080004ac
-Info : halted: PC: 0x080004ae
-Info : halted: PC: 0x080004b0
-Info : halted: PC: 0x080004b4
-Info : halted: PC: 0x080004b8
-Info : halted: PC: 0x080004bc
+Info : halted: PC: 0x08000d62
+Info : halted: PC: 0x08000d64
+Info : halted: PC: 0x08000d66
+Info : halted: PC: 0x08000d6a
+Info : halted: PC: 0x08000a0c
+Info : halted: PC: 0x08000d70
+Info : halted: PC: 0x08000d72
```
-The message is only displayed once as the program is about to enter the infinite loop defined in line 19: `loop {}`
+消息只打印一次,然后进入定义在19行的无限循环中: `loop {}`
-You can now exit GDB using the `quit` command.
+使用 `quit` 命令,你现在可以退出 GDB 了。
``` console
(gdb) quit
@@ -274,8 +229,7 @@ A debugging session is active.
Quit anyway? (y or n)
```
-Debugging now requires a few more steps so we have packed all those steps into a
-single GDB script named `openocd.gdb`. The file was created during the `cargo generate` step, and should work without any modifications. Let's have a peek:
+现在调试比之前多了点步骤,因此我们要把所有步骤打包进一个名为 `openocd.gdb` 的GDB脚本中。这个文件在 `cargo generate` 步骤中被生成,因此不需要任何修改了。让我们看一下:
``` console
cat openocd.gdb
@@ -300,12 +254,9 @@ load
stepi
```
-Now running ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` will immediately connect GDB to
-OpenOCD, enable semihosting, load the program and start the process.
+现在运行 ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello` 将会立即把GDB和OpenOCD连接起来,使能半主机,加载程序和启动进程。
-Alternatively, you can turn ` -x openocd.gdb` into a custom runner to make
-`cargo run` build a program *and* start a GDB session. This runner is included
-in `.cargo/config.toml` but it's commented out.
+另外,你能将 ` -x openocd.gdb` 放进一个自定义的 runner 中,使 `cargo run` 能编译程序并启动一个GDB会话。这个 runner 在 `.cargo/config.toml` 中,但是它被注释掉了。
``` console
head -n10 .cargo/config.toml
diff --git a/src/start/index.md b/src/start/index.md
index 2684955d..70ab15d8 100644
--- a/src/start/index.md
+++ b/src/start/index.md
@@ -1,10 +1,4 @@
-# Getting Started
-
-In this section we'll walk you through the process of writing, building,
-flashing and debugging embedded programs. You will be able to try most of the
-examples without any special hardware as we will show you the basics using
-QEMU, a popular open-source hardware emulator. The only section where hardware
-is required is, naturally enough, the [Hardware](./hardware.md) section,
-where we use OpenOCD to program an [STM32F3DISCOVERY].
+# 开始
+在这部分里,你将会经历编写,编译,烧录和调试嵌入式程序。大多数的例子都不需要特定的硬件就可以试试,因为我们将要向你展示一个开源硬件仿真器,QEMU的基本使用。唯一需要硬件的部分,那就是,[硬件](./hardware.md)那一章,我们会使用OpenOCD去编程一个[STM32F3DISCOVERY]。
[STM32F3DISCOVERY]: http://www.st.com/en/evaluation-tools/stm32f3discovery.html
diff --git a/src/start/interrupts.md b/src/start/interrupts.md
index a898708b..2cd047d7 100644
--- a/src/start/interrupts.md
+++ b/src/start/interrupts.md
@@ -1,60 +1,44 @@
-# Interrupts
+# 中断
-Interrupts differ from exceptions in a variety of ways but their operation and
-use is largely similar and they are also handled by the same interrupt
-controller. Whereas exceptions are defined by the Cortex-M architecture,
-interrupts are always vendor (and often even chip) specific implementations,
-both in naming and functionality.
+虽然中断和异常在很多方面都不一样,但是它们的操作和使用几乎是一样的,且它们也能被同一个中断控制器处理。然而异常是由Cortex-M微架构定义的,中断在命名和功能上总是由特定厂商(经常甚至是芯片)实现的。
-Interrupts do allow for a lot of flexibility which needs to be accounted for
-when attempting to use them in an advanced way. We will not cover those uses in
-this book, however it is a good idea to keep the following in mind:
+中断提供了更多的灵活性,当尝试用一种高级的方法使用它们时,我们需要对这种灵活性进行解释。但我们将不会在这本书里涵盖这些内容,最好把下面的东西记在心里:
+* 中断有可以编程的优先级,其决定了它们的处理函数的执行顺序。
+* 中断能嵌套且抢占,i.e. 一个中断处理函数的执行可以被其它更高优先级的中断打断。
+* 通常需要清除掉导致中断被触发的原因,避免无限地再次进入中断处理函数。
-* Interrupts have programmable priorities which determine their handlers' execution order
-* Interrupts can nest and preempt, i.e. execution of an interrupt handler might be interrupted by another higher-priority interrupt
-* In general the reason causing the interrupt to trigger needs to be cleared to prevent re-entering the interrupt handler endlessly
+运行时的初始化步骤总是相同的:
+* 设置外设在想要的事件发生时产生中断请求
+* 在中断控制器中设置需要的中断处理函数的优先级
+* 在中断控制器中使能中断处理函数
-The general initialization steps at runtime are always the same:
-* Setup the peripheral(s) to generate interrupts requests at the desired occasions
-* Set the desired priority of the interrupt handler in the interrupt controller
-* Enable the interrupt handler in the interrupt controller
-
-Similarly to exceptions, the `cortex-m-rt` crate provides an [`interrupt`]
-attribute to declare interrupt handlers. The available interrupts (and
-their position in the interrupt handler table) are usually automatically
-generated via `svd2rust` from a SVD description.
+与异常相似,`cortex-m-rt` crate提供了一个[`interrupt`]属性去声明中断处理函数。可用的中断(及它们在中断向量表中的位置)通常由`svd2rust`从一个SVD描述文件自动地生成。
[`interrupt`]: https://docs.rs/cortex-m-rt-macros/0.1.5/cortex_m_rt_macros/attr.interrupt.html
``` rust,ignore
-// Interrupt handler for the Timer2 interrupt
+// Timer2中断的中断处理函数
#[interrupt]
fn TIM2() {
// ..
- // Clear reason for the generated interrupt request
+ // 清除生成中断请求的原因
}
```
-Interrupt handlers look like plain functions (except for the lack of arguments)
-similar to exception handlers. However they can not be called directly by other
-parts of the firmware due to the special calling conventions. It is however
-possible to generate interrupt requests in software to trigger a diversion to
-the interrupt handler.
+中断处理函数和异常处理函数一样看起来像是普通的函数(除了没有入参)。然而由于特殊的调用规定,它不能被固件的其它部分直接调用。然而,可以在软件中生成中断请求,转移到中断处理函数中。
-Similar to exception handlers it is also possible to declare `static mut`
-variables inside the interrupt handlers for *safe* state keeping.
+与异常处理函数一样,也能在中断处理函数中声明`static mut`变量且保持 *safe* 状态。
``` rust,ignore
#[interrupt]
fn TIM2() {
static mut COUNT: u32 = 0;
- // `COUNT` has type `&mut u32` and it's safe to use
+ // `COUNT` 的类型是 `&mut u32` 且它用起来安全
*COUNT += 1;
}
```
-For a more detailed description about the mechanisms demonstrated here please
-refer to the [exceptions section].
+关于这里所说的机制的更多细节描述,请参考[异常章节]。
-[exceptions section]: ./exceptions.md
+[异常章节]: ./exceptions.md
diff --git a/src/start/panicking.md b/src/start/panicking.md
index a1365fa8..5927173a 100644
--- a/src/start/panicking.md
+++ b/src/start/panicking.md
@@ -1,80 +1,55 @@
-# Panicking
+# 运行时恐慌(Panicking)
-Panicking is a core part of the Rust language. Built-in operations like indexing
-are runtime checked for memory safety. When out of bounds indexing is attempted
-this results in a panic.
+运行时恐慌是Rust语言的一个核心部分。像是索引这样的内建的操作为了存储安全性是运行时检查的。当尝试越界索引时,这会导致运行时恐慌(panic)。
-In the standard library panicking has a defined behavior: it unwinds the stack
-of the panicking thread, unless the user opted for aborting the program on
-panics.
+在标准库中,运行时恐慌的行为被定义成:展开(unwinds)恐慌的线程的栈,除非用户自己选择在恐慌时终止程序。
-In programs without standard library, however, the panicking behavior is left
-undefined. A behavior can be chosen by declaring a `#[panic_handler]` function.
-This function must appear exactly *once* in the dependency graph of a program,
-and must have the following signature: `fn(&PanicInfo) -> !`, where [`PanicInfo`]
-is a struct containing information about the location of the panic.
+然而在没有标准库的程序中,运行时恐慌的行为是未被定义了的。通过声明一个 `#[painc_handler]` 函数可以选择一个运行时恐慌的行为。
+
+这个函数在一个程序的依赖图中必须只出现一次,且必须有这样的签名: `fn(&PanicInfo) -> !`,`PanicInfo`是一个包含关于发生运行时恐慌的位置信息的结构体。
[`PanicInfo`]: https://doc.rust-lang.org/core/panic/struct.PanicInfo.html
-Given that embedded systems range from user facing to safety critical (cannot
-crash) there's no one size fits all panicking behavior but there are plenty of
-commonly used behaviors. These common behaviors have been packaged into crates
-that define the `#[panic_handler]` function. Some examples include:
+鉴于嵌入式系统的范围从面向用户的系统到安全关键系统,没有一个运行时恐慌行为能满足所有场景,但是有许多常用的行为。这些常用的行为已经被打包进了一些crates中,这些crates中定义了 `#[panic_handler]`函数。比如:
-- [`panic-abort`]. A panic causes the abort instruction to be executed.
-- [`panic-halt`]. A panic causes the program, or the current thread, to halt by
- entering an infinite loop.
-- [`panic-itm`]. The panicking message is logged using the ITM, an ARM Cortex-M
- specific peripheral.
-- [`panic-semihosting`]. The panicking message is logged to the host using the
- semihosting technique.
+- [`panic-abort`]. 这个运行时恐慌会导致终止指令被执行。
+- [`panic-halt`]. 这个运行时恐慌会导致程序,或者现在的线程,通过进入一个无限循环中而挂起。
+- [`panic-itm`]. 运行时恐慌的信息会被ITM记录,ITM是一个ARM Cortex-M的特殊的外设。
+- [`panic-semihosting`]. 使用半主机技术,运行时恐慌的信息被记录到主机上。
[`panic-abort`]: https://crates.io/crates/panic-abort
[`panic-halt`]: https://crates.io/crates/panic-halt
[`panic-itm`]: https://crates.io/crates/panic-itm
[`panic-semihosting`]: https://crates.io/crates/panic-semihosting
-You may be able to find even more crates searching for the [`panic-handler`]
-keyword on crates.io.
+在crates.io上搜索 [`panic-handler`],你甚至可以找到更多的crates。
[`panic-handler`]: https://crates.io/keywords/panic-handler
-A program can pick one of these behaviors simply by linking to the corresponding
-crate. The fact that the panicking behavior is expressed in the source of
-an application as a single line of code is not only useful as documentation but
-can also be used to change the panicking behavior according to the compilation
-profile. For example:
+仅仅通过链接到相关的crate中,一个程序就可以简单地从这些行为中选择一个运行时恐慌行为。将运行时恐慌的行为作为一行代码放进一个应用的源码中,不仅仅是因为可以作为文档使用,而且能根据编译配置改变运行时恐慌的行为。比如:
``` rust,ignore
#![no_main]
#![no_std]
-// dev profile: easier to debug panics; can put a breakpoint on `rust_begin_unwind`
+// dev配置: 更容易调试运行时恐慌; 可以在 `rust_begin_unwind` 上放一个断点
#[cfg(debug_assertions)]
use panic_halt as _;
-// release profile: minimize the binary size of the application
+// release配置: 最小化应用的二进制文件的大小
#[cfg(not(debug_assertions))]
use panic_abort as _;
// ..
```
-In this example the crate links to the `panic-halt` crate when built with the
-dev profile (`cargo build`), but links to the `panic-abort` crate when built
-with the release profile (`cargo build --release`).
+在这个例子里,当使用dev配置编译的时候(`cargo build`),crate链接到 `panic-halt` crate上,但是当使用release配置编译时(`cargo build --release`),crate链接到`panic-abort` crate上。
-> The `use panic_abort as _;` form of the `use` statement is used to ensure the `panic_abort` panic handler is
-> included in our final executable while making it clear to the compiler that we won't explicitly use anything from
-> the crate. Without the `as _` rename, the compiler would warn that we have an unused import.
-> Sometimes you might see `extern crate panic_abort` instead, which is an older style used before the
-> 2018 edition of Rust, and should now only be used for "sysroot" crates (those distributed with Rust itself) such
-> as `proc_macro`, `alloc`, `std`, and `test`.
+> `use panic_abort as _` 形式的 `use` 语句,被用来确保 `panic_abort` 运行时恐慌函数被包含进我们最终的可执行程序里,同时让编译器清楚地知道我们不会从这个crate显式地使用任何东西。没有 `_` 重命名,编译器将会警告我们有一个未使用的导入。有时候你可能会看到 `extern crate panic_abort`,这是Rust 2018之前的版本使用的更旧的写法,现在应该只被用于 "sysroot" crates (与Rust一起发布的crates),比如 `proc_macro`,`alloc`,`std` 和 `test` 。
-## An example
+## 一个例子
-Here's an example that tries to index an array beyond its length. The operation
-results in a panic.
+这里有一个尝试越界访问数组的例子。操作的结果导致了一个运行时恐慌(panic)。
```rust,ignore
#![no_main]
@@ -94,8 +69,7 @@ fn main() -> ! {
}
```
-This example chose the `panic-semihosting` behavior which prints the panic
-message to the host console using semihosting.
+这个例子选择了`panic-semihosting`行为,运行时恐慌的信息会被打印至使用了半主机模式的主机控制台上。
``` text
$ cargo run
@@ -103,5 +77,4 @@ $ cargo run
panicked at 'index out of bounds: the len is 3 but the index is 4', src/main.rs:12:13
```
-You can try changing the behavior to `panic-halt` and confirm that no message is
-printed in that case.
+你可以尝试将行为改成`panic-halt`,确保在这个案例里没有信息被打印。
diff --git a/src/start/qemu.md b/src/start/qemu.md
index f7241b87..209c853a 100644
--- a/src/start/qemu.md
+++ b/src/start/qemu.md
@@ -1,34 +1,22 @@
# QEMU
-
-We'll start writing a program for the [LM3S6965], a Cortex-M3 microcontroller.
-We have chosen this as our initial target because it [can be emulated](https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm) using QEMU
-so you don't need to fiddle with hardware in this section and we can focus on
-the tooling and the development process.
+我们将开始为[LM3S6965]编写程序,一个Cortex-M3微控制器。因为它能使用[QEMU仿真](https://wiki.qemu.org/Documentation/Platforms/ARM#Supported_in_qemu-system-arm),所以我们选择它作为我们的第一个目标,本节中,不需要使用硬件,我们注意力可以集中在工具和开发过程上。
[LM3S6965]: http://www.ti.com/product/LM3S6965
-**IMPORTANT**
-We'll use the name "app" for the project name in this tutorial.
-Whenever you see the word "app" you should replace it with the name you selected
-for your project. Or, you could also name your project "app" and avoid the
-substitutions.
-
-## Creating a non standard Rust program
+**重要**
+在这个引导里,我们将使用"app"这个名字来代指项目名。无论何时你看到单词"app",你应该用你选择的项目名来替代"app"。或者你也可以选择把你的项目命名为"app",避免要替换掉。
-We'll use the [`cortex-m-quickstart`] project template to generate a new
-project from it. The created project will contain a barebone application: a good
-starting point for a new embedded rust application. In addition, the project will
-contain an `examples` directory, with several separate applications, highlighting
-some of the key embedded rust functionality.
+## 生成一个非标准的 Rust program
+我们将使用[`cortex-m-quickstart`]项目模板来生成一个新项目。生成的项目将包含一个最基本的应用:对于一个新的嵌入式rust应用来说,是一个很好的开始。另外,项目将包含一个`example`文件夹,文件夹中有许多独立的应用,突出了一些关键的嵌入式rust的功能。
[`cortex-m-quickstart`]: /~https://github.com/rust-embedded/cortex-m-quickstart
-### Using `cargo-generate`
-First install cargo-generate
+### 使用 `cargo-generate`
+首先安装 cargo-generate
```console
cargo install cargo-generate
```
-Then generate a new project
+然后生成一个新项目
```console
cargo generate --git /~https://github.com/rust-embedded/cortex-m-quickstart
```
@@ -43,16 +31,16 @@ cargo generate --git /~https://github.com/rust-embedded/cortex-m-quickstart
cd app
```
-### Using `git`
+### 使用 `git`
-Clone the repository
+克隆仓库
```console
git clone /~https://github.com/rust-embedded/cortex-m-quickstart app
cd app
```
-And then fill in the placeholders in the `Cargo.toml` file
+然后补充`Cargo.toml`文件中的占位符
```toml
[package]
@@ -69,9 +57,9 @@ test = false
bench = false
```
-### Using neither
+### 要么使用
-Grab the latest snapshot of the `cortex-m-quickstart` template and extract it.
+抓取最新的 `cortex-m-quickstart` 模板,解压它。
```console
curl -LO /~https://github.com/rust-embedded/cortex-m-quickstart/archive/master.zip
@@ -80,15 +68,13 @@ mv cortex-m-quickstart-master app
cd app
```
-Or you can browse to [`cortex-m-quickstart`], click the green "Clone or
-download" button and then click "Download ZIP".
+或者你可以浏览[`cortex-m-quickstart`],点击绿色的 "Clone or download" 按钮,然后点击 "Download ZIP" 。
-Then fill in the placeholders in the `Cargo.toml` file as done in the second
-part of the "Using `git`" version.
+然后像在 “使用 `git`” 那里的第二部分写的那样填充 `Cargo.toml` 。
-## Program Overview
+## 项目概览
-For convenience here are the most important parts of the source code in `src/main.rs`:
+这是`src/main.rs`中源码最重要的部分。
```rust,ignore
#![no_std]
@@ -106,44 +92,27 @@ fn main() -> ! {
}
```
-This program is a bit different from a standard Rust program so let's take a
-closer look.
+这个程序与标准Rust程序有一点不同,让我们走近点看看。
-`#![no_std]` indicates that this program will *not* link to the standard crate,
-`std`. Instead it will link to its subset: the `core` crate.
+`#![no_std]`指出这个程序将 *不会* 链接标准crate`std`。反而它将会链接到它的子集: `core` crate。
-`#![no_main]` indicates that this program won't use the standard `main`
-interface that most Rust programs use. The main (no pun intended) reason to go
-with `no_main` is that using the `main` interface in `no_std` context requires
-nightly.
+`#![no_main]`指出这个程序将不会使用标准的且被大多数Rust程序使用的`main`接口。使用`no_main`的主要理由是,在`no_std`上下文中使用`main`接口需要 nightly 版的 Rust。
-`use panic_halt as _;`. This crate provides a `panic_handler` that defines
-the panicking behavior of the program. We will cover this in more detail in the
-[Panicking](panicking.md) chapter of the book.
+`use panic_halt as _;`。这个crate提供了一个`panic_handler`,它定义了程序陷入`panic`时的行为。我们将会在这本书的[运行时恐慌(Panicking)](panicking.md)章节中覆盖更多的细节。
-[`#[entry]`][entry] is an attribute provided by the [`cortex-m-rt`] crate that's used
-to mark the entry point of the program. As we are not using the standard `main`
-interface we need another way to indicate the entry point of the program and
-that'd be `#[entry]`.
+[`#[entry]`][entry] 是一个由[`cortex-m-rt`]提供的属性,它用来标记程序的入口。当我们不使用标准的`main`接口时,我们需要其它方法来指示程序的入口,那就是`#[entry]`。
[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() -> !`. Our program will be the *only* process running on the target
-hardware so we don't want it to end! We use a [divergent function](https://doc.rust-lang.org/rust-by-example/fn/diverging.html) (the `-> !`
-bit in the function signature) to ensure at compile time that'll be the case.
+`fn main() -> !`。我们的程序将会是运行在目标板子上的 *唯一* 的进程,因此我们不想要它结束!我们使用一个[发散函数](https://doc.rust-lang.org/rust-by-example/fn/diverging.html) (函数签名中的 `-> !` )来确保在编译时就是这么回事儿。
-## Cross compiling
-
-The next step is to *cross* compile the program for the Cortex-M3 architecture.
-That's as simple as running `cargo build --target $TRIPLE` if you know what the
-compilation target (`$TRIPLE`) should be. Luckily, the `.cargo/config.toml` in the
-template has the answer:
+## 交叉编译
+下一步是为Cortex-M3架构*交叉*编译程序。如果你知道编译目标(`$TRIPLE`)应该是什么,运行`cargo build --target $TRIPLE`就可以了。幸运地,模板中的`.cargo/config.toml`有这个答案:
```console
tail -n6 .cargo/config.toml
```
-
```toml
[build]
# Pick ONE of these compilation targets
@@ -152,38 +121,29 @@ 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)
```
-
-To cross compile for the Cortex-M3 architecture we have to use
-`thumbv7m-none-eabi`. That target is not automatically installed when installing
-the Rust toolchain, it would now be a good time to add that target to the toolchain,
-if you haven't done it yet:
+为了交叉编译Cortex-M3架构我们不得不使用`thumbv7m-none-eabi`。当安装Rust工具时,target不会自动被安装,如果还没有添加,现在可以去添加那个target到工具链上。
``` console
rustup target add thumbv7m-none-eabi
```
- Since the `thumbv7m-none-eabi` compilation target has been set as the default in
- your `.cargo/config.toml` file, the two commands below do the same:
-
+因为`thumbv7m-none-eabi`编译目标在你的`.cargo/config.toml`中被设置成默认值,下面的两个命令是一样的效果:
```console
cargo build --target thumbv7m-none-eabi
cargo build
```
-## Inspecting
+## 检查
-Now we have a non-native ELF binary in `target/thumbv7m-none-eabi/debug/app`. We
-can inspect it using `cargo-binutils`.
+现在在`target/thumbv7m-none-eabi/debug/app`中有一个非主机环境的ELF二进制文件。我们能使用`cargo-binutils`检查它。
-With `cargo-readobj` we can print the ELF headers to confirm that this is an ARM
-binary.
+使用`cargo-readobj`我们能打印ELF头,确认这是一个ARM二进制。
-``` console
+```console
cargo readobj --bin app -- --file-headers
```
-Note that:
-* `--bin app` is sugar for inspect the binary at `target/$TRIPLE/debug/app`
-* `--bin app` will also (re)compile the binary, if necessary
-
+注意:
+* `--bin app` 是一个用来查看二进制项`target/$TRIPLE/debug/app`的语法糖
+* `--bin app` 需要时也会重新编译二进制项。
``` text
ELF Header:
@@ -208,13 +168,13 @@ ELF Header:
Section header string table index: 18
```
-`cargo-size` can print the size of the linker sections of the binary.
-
+`cargo-size` 能打印二进制项的linker section的大小。
```console
cargo size --bin app --release -- -A
```
-we use `--release` to inspect the optimized version
+
+我们使用`--release`查看优化后的版本
``` text
app :
@@ -238,36 +198,26 @@ section size addr
Total 14570
```
-> A refresher on ELF linker sections
+> ELF linker sections的复习
>
-> - `.text` contains the program instructions
-> - `.rodata` contains constant values like strings
-> - `.data` contains statically allocated variables whose initial values are
-> *not* zero
-> - `.bss` also contains statically allocated variables whose initial values
-> *are* zero
-> - `.vector_table` is a *non*-standard section that we use to store the vector
-> (interrupt) table
-> - `.ARM.attributes` and the `.debug_*` sections contain metadata and will
-> *not* be loaded onto the target when flashing the binary.
-
-**IMPORTANT**: ELF files contain metadata like debug information so their *size
-on disk* does *not* accurately reflect the space the program will occupy when
-flashed on a device. *Always* use `cargo-size` to check how big a binary really
-is.
-
-`cargo-objdump` can be used to disassemble the binary.
+> - `.text` 包含程序指令
+> - `.rodata` 包含像是字符串这样的常量
+> - `.data` 包含静态分配的初始值*非*零的变量
+> - `.bss` 也包含静态分配的初始值*是*零的变量
+> - `.vector_table` 是一个我们用来存储向量(中断)表的*非*标准的section
+> - `.ARM.attributes` 和 `.debug_*` sections包含元数据,当烧录二进制文件时,它们不会被加载到目标上。
+
+**重要**: ELF文件包含像是调试信息这样的元数据,因此它们在*硬盘上的尺寸*没有正确地反应处程序被烧录到设备上时将占据的空间的大小。要*一直*使用`cargo-size`检查一个二进制项的大小。
+
+`cargo-objdump` 能用来反编译二进制项。
```console
cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --print-imm-hex
```
-> **NOTE** if the above command complains about `Unknown command line argument` see
-> the following bug report: /~https://github.com/rust-embedded/book/issues/269
+> **注意** 如果上面的命令抱怨 `Unknown command line argument` 看下面的bug报告:/~https://github.com/rust-embedded/book/issues/269
-> **NOTE** this output can differ on your system. New versions of rustc, LLVM
-> and libraries can generate different assembly. We truncated some of the instructions
-> to keep the snippet small.
+> **注意** 在你的系统上这个输出可能不一样。rustc, LLVM 和库的新版本能产出不同的汇编。我们截取了一些指令
```text
app: file format ELF32-arm-little
@@ -280,7 +230,7 @@ main:
Reset:
406: bl #0x24e
40a: movw r0, #0x0
- < .. truncated any more instructions .. >
+ < .. 截断了更多的指令 .. >
DefaultHandler_:
656: b #-0x4
@@ -308,15 +258,14 @@ HardFault:
663:
```
-## Running
+## 运行
-Next, let's see how to run an embedded program on QEMU! This time we'll use the
-`hello` example which actually does something.
+接下来,让我们看一个嵌入式程序是如何在QEMU上运行的!此刻我们将使用 `hello` 示例,来做些真正的事。
-For convenience here's the source code of `examples/hello.rs`:
+为了方便起见,这是`examples/hello.rs`的源码:
```rust,ignore
-//! Prints "Hello, world!" on the host console using semihosting
+//! 使用semihosting在主机调试台上打印 "Hello, world!"
#![no_main]
#![no_std]
@@ -330,28 +279,25 @@ use cortex_m_semihosting::{debug, hprintln};
fn main() -> ! {
hprintln!("Hello, world!").unwrap();
- // exit QEMU
- // NOTE do not run this on hardware; it can corrupt OpenOCD state
+ // 退出 QEMU
+ // NOTE 不要在硬件上运行这个;它会打破OpenOCD的状态
debug::exit(debug::EXIT_SUCCESS);
loop {}
}
```
-This program uses something called semihosting to print text to the *host*
-console. When using real hardware this requires a debug session but when using
-QEMU this Just Works.
+这个程序使用被叫做semihosting的东西去打印文本到主机调试台上。当使用的是真实的硬件时,需要一个调试对话这个程序才能工作,但是当使用的是QEMU时这就可以工作了。
-Let's start by compiling the example:
+让我们开始编译示例
```console
cargo build --example hello
```
-The output binary will be located at
-`target/thumbv7m-none-eabi/debug/examples/hello`.
+输出的二进制项将位于`target/thumbv7m-none-eabi/debug/examples/hello`。
-To run this binary on QEMU run the following command:
+为了在QEMU上运行这个二进制项,执行下列的命令:
```console
qemu-system-arm \
@@ -366,8 +312,7 @@ qemu-system-arm \
Hello, world!
```
-The command should successfully exit (exit code = 0) after printing the text. On
-*nix you can check that with the following command:
+这个命令应该在打印文本之后成功地退出 (exit code = 0)。你可以使用下列的指令检查下:
```console
echo $?
@@ -377,51 +322,29 @@ echo $?
0
```
-Let's break down that QEMU command:
-
-- `qemu-system-arm`. This is the QEMU emulator. There are a few variants of
- these QEMU binaries; this one does full *system* emulation of *ARM* machines
- hence the name.
-
-- `-cpu cortex-m3`. This tells QEMU to emulate a Cortex-M3 CPU. Specifying the
- CPU model lets us catch some miscompilation errors: for example, running a
- program compiled for the Cortex-M4F, which has a hardware FPU, will make QEMU
- error during its execution.
-
-- `-machine lm3s6965evb`. This tells QEMU to emulate the LM3S6965EVB, an
- evaluation board that contains a LM3S6965 microcontroller.
+让我们看看QEMU命令:
-- `-nographic`. This tells QEMU to not launch its GUI.
++ `qemu-system-arm`。这是QEMU仿真器。这些QEMU二进制项有一些变体,这个仿真器能做ARM机器的全系统仿真。
-- `-semihosting-config (..)`. This tells QEMU to enable semihosting. Semihosting
- lets the emulated device, among other things, use the host stdout, stderr and
- stdin and create files on the host.
-
-- `-kernel $file`. This tells QEMU which binary to load and run on the emulated
- machine.
-
-Typing out that long QEMU command is too much work! We can set a custom runner
-to simplify the process. `.cargo/config.toml` has a commented out runner that invokes
-QEMU; let's uncomment it:
++ `-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在仿真机器上加载和运行哪个二进制项。
+输入这么长的QEMU命令太费功夫了!我们可以设置一个自定义运行器(runner)简化步骤。`.cargo/config.toml` 有一个被注释掉的,可以调用QEMU的运行器。让我们去掉注释。
```console
head -n3 .cargo/config.toml
```
-
```toml
[target.thumbv7m-none-eabi]
# uncomment this to make `cargo run` execute programs on QEMU
runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
```
-
-This runner only applies to the `thumbv7m-none-eabi` target, which is our
-default compilation target. Now `cargo run` will compile the program and run it
-on QEMU:
-
+这个运行器只会应用于 `thumbv7m-none-eabi` 目标,它是我们的默认编译目标。现在 `cargo run` 将会编译程序且在QEMU上运行它。
```console
cargo run --example hello --release
```
-
```text
Compiling app v0.1.0 (file:///tmp/app)
Finished release [optimized + debuginfo] target(s) in 0.26s
@@ -429,21 +352,16 @@ cargo run --example hello --release
Hello, world!
```
-## Debugging
+## 调试
+对于嵌入式开发来说,调试非常重要。让我们来看下如何调试它。
-Debugging is critical to embedded development. Let's see how it's done.
+因为我们想要调试的程序所运行的机器上并没有运行一个调试器程序(GDB或者LLDB),所以调试一个嵌入式设备就涉及到了 *远程* 调试
-Debugging an embedded device involves *remote* debugging as the program that we
-want to debug won't be running on the machine that's running the debugger
-program (GDB or LLDB).
+远程调试涉及一个客户端和一个服务器。在QEMU的情况中,客户端将是一个GDB(或者LLDM)进程且服务器将会是运行着嵌入式程序的QEMU进程。
-Remote debugging involves a client and a server. In a QEMU setup, the client
-will be a GDB (or LLDB) process and the server will be the QEMU process that's
-also running the embedded program.
+在这部分,我们要使用我们已经编译的 `hello` 示例。
-In this section we'll use the `hello` example we already compiled.
-
-The first debugging step is to launch QEMU in debugging mode:
+调试的第一步是在调试模式中启动QEMU:
```console
qemu-system-arm \
@@ -456,30 +374,18 @@ qemu-system-arm \
-kernel target/thumbv7m-none-eabi/debug/examples/hello
```
-This command won't print anything to the console and will block the terminal. We
-have passed two extra flags this time:
-
-- `-gdb tcp::3333`. This tells QEMU to wait for a GDB connection on TCP
- port 3333.
-
-- `-S`. This tells QEMU to freeze the machine at startup. Without this the
- program would have reached the end of main before we had a chance to launch
- the debugger!
-
-Next we launch GDB in another terminal and tell it to load the debug symbols of
-the example:
+这个命令将不打印任何东西到调试台上,且将会阻塞住终端。此刻我们还传递了两个额外的标志。
++ `-gdb tcp::3333`。这告诉QEMU在3333的TCP端口上等待一个GDB连接。
++ `-S`。这告诉QEMU在启动时,冻结机器。没有这个,在我们有机会启动调试器之前,程序有可能已经到达了主程序的底部了!
+接下来我们在另一个终端启动GDB,且告诉它去加载示例的调试符号。
```console
gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello
```
-**NOTE**: you might need another version of gdb instead of `gdb-multiarch` depending
-on which one you installed in the installation chapter. This could also be
-`arm-none-eabi-gdb` or just `gdb`.
-
-Then within the GDB shell we connect to QEMU, which is waiting for a connection
-on TCP port 3333.
+**注意**: 你可能需要另一个gdb版本而不是 `gdb-multiarch`,取决于你在安装章节中安装了哪个。这个可能是 `arm-none-eabi-gdb` 或者只是 `gdb`。
+然后在GDB shell中,我们连接QEMU,QEMU正在等待一个在3333 TCP端口上的连接。
```console
target remote :3333
```
@@ -490,27 +396,21 @@ Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473
473 pub unsafe extern "C" fn Reset() -> ! {
```
+你将看到,进程被挂起了,程序计数器正指向一个名为 `Reset` 的函数。那是 reset 句柄:Cortex-M 内核在启动时执行的中断函数。
-You'll see that the process is halted and that the program counter is pointing
-to a function named `Reset`. That is the reset handler: what Cortex-M cores
-execute upon booting.
-
-> Note that on some setup, instead of displaying the line `Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473` as shown above, gdb may print some warnings like :
+> 注意在一些配置中,可能不会像上面一样,显示`Reset() at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473`,gdb可能打印一些警告,比如:
>
>`core::num::bignum::Big32x40::mul_small () at src/libcore/num/bignum.rs:254`
> ` src/libcore/num/bignum.rs: No such file or directory.`
->
-> That's a known glitch. You can safely ignore those warnings, you're most likely at Reset().
-
+>
+> 那是一个已知的小bug,你可以安全地忽略这些警告,你非常大可能已经进入Reset()了。
-This reset handler will eventually call our main function. Let's skip all the
-way there using a breakpoint and the `continue` command. To set the breakpoint, let's first take a look where we would like to break in our code, with the `list` command.
+这个reset句柄最终将调用我们的主函数,让我们使用一个断点和`continue`命令跳过所有的步骤。为了设置断点,让我们首先看下我们想要在我们代码哪里打断点,使用`list`指令
```console
list main
```
-This will show the source code, from the file examples/hello.rs.
-
+这将显示从examples/hello.rs文件来的源代码。
```text
6 use panic_halt as _;
7
@@ -523,12 +423,13 @@ This will show the source code, from the file examples/hello.rs.
14
15 // exit QEMU
```
-We would like to add a breakpoint just before the "Hello, world!", which is on line 13. We do that with the `break` command:
+我们想要在"Hello, world!"之前添加一个断点,在13行那里。我们可以使用`break`命令
```console
break 13
```
-We can now instruct gdb to run up to our main function, with the `continue` command:
+
+我们现在能使用`continue`命令指示gdb运行到我们的主函数。
```console
continue
@@ -541,8 +442,7 @@ Breakpoint 1, hello::__cortex_m_rt_main () at examples\hello.rs:13
13 hprintln!("Hello, world!").unwrap();
```
-We are now close to the code that prints "Hello, world!". Let's move forward
-using the `next` command.
+我们现在靠近打印"Hello, world!"的代码。让我们使用`next`命令继续前进。
``` console
next
@@ -552,15 +452,14 @@ next
16 debug::exit(debug::EXIT_SUCCESS);
```
-At this point you should see "Hello, world!" printed on the terminal that's
-running `qemu-system-arm`.
+在这里,你应该看到 "Hello, world!" 被打印到正在运行 `qemu-system-arm` 的终端上。
```text
$ qemu-system-arm (..)
Hello, world!
```
-Calling `next` again will terminate the QEMU process.
+再次调用`next`将会终止QEMU进程。
```console
next
@@ -570,7 +469,7 @@ next
[Inferior 1 (Remote target) exited normally]
```
-You can now exit the GDB session.
+你现在能退出GDB的会话了。
``` console
quit
diff --git a/src/start/registers.md b/src/start/registers.md
index 2d4a8e85..2f76a0ca 100644
--- a/src/start/registers.md
+++ b/src/start/registers.md
@@ -1,36 +1,43 @@
-# Memory Mapped Registers
+# 存储映射的寄存器(Memory-Mapped Registers)
-Embedded systems can only get so far by executing normal Rust code and moving data around in RAM. If we want to get any information into or out of our system (be that blinking an LED, detecting a button press or communicating with an off-chip peripheral on some sort of bus) we're going to have to dip into the world of Peripherals and their 'memory mapped registers'.
+嵌入式系统想要继续执行下去,只有通过执行常规的Rust代码并在RAM间移动数据才行。如果我们想要获取或者发出信息(点亮一个LED,发现一个按钮按下或者在总线上与芯片外设通信),我们不得不深入了解外设和它们的"存储映射的寄存器"。
+
+你可能会发现,访问你的微控制器外设所需要的代码,已经存在于下面的某个抽象层中了。
-You may well find that the code you need to access the peripherals in your micro-controller has already been written, at one of the following levels:
-* Micro-architecture Crate - This sort of crate handles any useful routines common to the processor core your microcontroller is using, as well as any peripherals that are common to all micro-controllers that use that particular type of processor core. For example the [cortex-m] crate gives you functions to enable and disable interrupts, which are the same for all Cortex-M based micro-controllers. It also gives you access to the 'SysTick' peripheral included with all Cortex-M based micro-controllers.
-* Peripheral Access Crate (PAC) - This sort of crate is a thin wrapper over the various memory-wrapper registers defined for your particular part-number of micro-controller you are using. For example, [tm4c123x] for the Texas Instruments Tiva-C TM4C123 series, or [stm32f30x] for the ST-Micro STM32F30x series. Here, you'll be interacting with the registers directly, following each peripheral's operating instructions given in your micro-controller's Technical Reference Manual.
-* HAL Crate - These crates offer a more user-friendly API for your particular processor, often by implementing some common traits defined in [embedded-hal]. For example, this crate might offer a `Serial` struct, with a constructor that takes an appropriate set of GPIO pins and a baud rate, and offers some sort of `write_byte` function for sending data. See the chapter on [Portability] for more information on [embedded-hal].
-* Board Crate - These crates go one step further than a HAL Crate by pre-configuring various peripherals and GPIO pins to suit the specific developer kit or board you are using, such as [stm32f3-discovery] for the STM32F3DISCOVERY board.
+* 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
[stm32f30x]: https://crates.io/crates/stm32f30x
[embedded-hal]: https://crates.io/crates/embedded-hal
-[Portability]: ../portability/index.md
+[可移植性]: ../portability/index.md
[stm32f3-discovery]: https://crates.io/crates/stm32f3-discovery
[Discovery]: https://rust-embedded.github.io/discovery/
-## Board Crate
+## 开发板Crate (Board Crate)
+
+<<<<<<< HEAD
+如果你是嵌入式Rust新手,board crate是一个完美的开始。它们很好地抽象出了,在开始学习这个项目时,需要耗费心力了解的硬件细节,使得标准工作,像是打开或者关闭LED,变得简单。不同的板子间,它们提供的功能变化很大。因为这本书是不假设我们使用的是何种板子,所以这本书不会提到board crate。
+如果你想要用STM32F3DISCOVERY开发板做实验,强烈建议看一下[stm32f3-discovery]开发板crate,它提供了点亮LEDs,访问它的指南针,蓝牙和其它的功能。[Discovery]书对于一个board crate的用法提供一个很好的介绍。
+=======
A board crate is the perfect starting point, if you're new to embedded Rust. They nicely abstract the HW details that might be overwhelming when starting studying this subject, and makes standard tasks easy, like turning a LED on or off. The functionality it exposes varies a lot between boards. Since this book aims at staying hardware agnostic, the board crates won't be covered by this book.
If you want to experiment with the STM32F3DISCOVERY board, it is highly recommended to take a look at the [stm32f3-discovery] board crate, which provides functionality to blink the board LEDs, access its compass, bluetooth and more. The [Discovery] book offers a great introduction to the use of a board crate.
+>>>>>>> 17842ebb050f62e40a4618edeb8e8ee86e758707
-But if you're working on a system that doesn't yet have dedicated board crate, or you need functionality not provided by existing crates, read on as we start from the bottom, with the micro-architecture crates.
+但是如果你正在使用一个还没有提供专用的board crate的系统,或者你需要的一些功能,现存的crates不提供,那我们需要从底层的微架构crates开始。
## Micro-architecture crate
-Let's look at the SysTick peripheral that's common to all Cortex-M based micro-controllers. We can find a pretty low-level API in the [cortex-m] crate, and we can use it like this:
+让我们看一下SysTick外设,SysTick外设存在于所有的Cortex-M微控制器中。我们能在[cortex-m] crate中找到一个相当底层的API,我们能像这样使用它:
```rust,ignore
#![no_std]
@@ -54,13 +61,13 @@ fn main() -> ! {
loop {}
}
```
-The functions on the `SYST` struct map pretty closely to the functionality defined by the ARM Technical Reference Manual for this peripheral. There's nothing in this API about 'delaying for X milliseconds' - we have to crudely implement that ourselves using a `while` loop. Note that we can't access our `SYST` struct until we have called `Peripherals::take()` - this is a special routine that guarantees that there is only one `SYST` structure in our entire program. For more on that, see the [Peripherals] section.
+`SYST`结构体上的功能,相当接近ARM技术手册为这个外设定义的功能。在这个API中没有关于 '延迟X毫秒' 的功能 - 我们不得不通过使用一个 `while` 循环来粗略地实现它。注意,我们调用了`Peripherals::take()`才能访问我们的`SYST`结构体 - 这是一个特别的程序,保障了在我们的整个程序中只存在一个`SYST`结构体实例,更多的信息可以看[外设]部分。
-[Peripherals]: ../peripherals/index.md
+[外设]: ../peripherals/index.md
-## Using a Peripheral Access Crate (PAC)
+## 使用一个外设访问Crate (PAC)
-We won't get very far with our embedded software development if we restrict ourselves to only the basic peripherals included with every Cortex-M. At some point, we're going to need to write some code that's specific to the particular micro-controller we're using. In this example, let's assume we have an Texas Instruments TM4C123 - a middling 80MHz Cortex-M4 with 256 KiB of Flash. We're going to pull in the [tm4c123x] crate to make use of this chip.
+如果我们把自己只局限于每个Cortex-M拥有的基本外设,那我们的嵌入式软件开发将不会走得太远。我们准备需要写一些特定于我们正在使用的微控制器的代码。在这个例子里,让我们假设我们有一个TI的TM4C123 - 一个有256KiB Flash的中等规模的80MHz的Cortex-M4。我们用[tm4c123x] crate去使用这个芯片。
```rust,ignore
#![no_std]
@@ -89,11 +96,11 @@ pub fn init() -> (Delay, Leds) {
```
-We've accessed the `PWM0` peripheral in exactly the same way as we accessed the `SYST` peripheral earlier, except we called `tm4c123x::Peripherals::take()`. As this crate was auto-generated using [svd2rust], the access functions for our register fields take a closure, rather than a numeric argument. While this looks like a lot of code, the Rust compiler can use it to perform a bunch of checks for us, but then generate machine-code which is pretty close to hand-written assembler! Where the auto-generated code isn't able to determine that all possible arguments to a particular accessor function are valid (for example, if the SVD defines the register as 32-bit but doesn't say if some of those 32-bit values have a special meaning), then the function is marked as `unsafe`. We can see this in the example above when setting the `load` and `compa` sub-fields using the `bits()` function.
+我们访问 `PWM0` 外设的方法和我们之前访问 `SYST` 的方法一样,除了我们调用的是 `tm4c123x::Peripherals::take()` 之外。因为这个crate是使用[svd2rust]自动生成的,访问我们寄存器位段的函数的参数是一个闭包,而不是一个数值参数。虽然这看起来像是有了更多的代码,但是Rust编译器能使用这个闭包为我们执行一系列检查,且产生的机器码十分接近手写的汇编码!如果自动生成的代码不能确保某个访问函数其所有可能的参数都能发挥作用(比如,如果寄存器被SVD定义为32位,但是没有说明某些32位值是否有特殊作用),那么该函数需要被标记为 `unsafe` 。我们能在上面看到这样的例子,我们使用 `bits()` 函数设置 `load` 和 `compa` 子域。
### Reading
-The `read()` function returns an object which gives read-only access to the various sub-fields within this register, as defined by the manufacturer's SVD file for this chip. You can find all the functions available on special `R` return type for this particular register, in this particular peripheral, on this particular chip, in the [tm4c123x documentation][tm4c123x documentation R].
+`read()` 函数返回一个对象,这个对象提供了对这个寄存器中不同子域的只读访问,由厂商提供的这个芯片的SVD文件定义。在 [tm4c123x documentation][tm4c123x documentation R] 中你能找到在这个特别的返回类型 `R` 上所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。
```rust,ignore
if pwm.ctl.read().globalsync0().is_set() {
@@ -103,7 +110,8 @@ if pwm.ctl.read().globalsync0().is_set() {
### Writing
-The `write()` function takes a closure with a single argument. Typically we call this `w`. This argument then gives read-write access to the various sub-fields within this register, as defined by the manufacturer's SVD file for this chip. Again, you can find all the functions available on the 'w' for this particular register, in this particular peripheral, on this particular chip, in the [tm4c123x documentation][tm4c123x Documentation W]. Note that all of the sub-fields that we do not set will be set to a default value for us - any existing content in the register will be lost.
+`write()`函数使用一个只有一个参数的闭包。通常我们把这个参数叫做 `w`。然后这个参数提供对这个寄存器中不同的子域的读写访问,由厂商关于这个芯片的SVD文件提供。再一次,在 [tm4c123x documentation][tm4c123x documentation W] 中你能找到 `W` 所有可用的函数,其与特定芯片中的特定外设的特定寄存器有关。注意,所有我们没有设置的子域将会被设置成一个默认值 - 将会丢失任何在这个寄存器中的现存的内容。
+
```rust,ignore
pwm.ctl.write(|w| w.globalsync0().clear_bit());
@@ -111,13 +119,13 @@ pwm.ctl.write(|w| w.globalsync0().clear_bit());
### Modifying
-If we wish to change only one particular sub-field in this register and leave the other sub-fields unchanged, we can use the `modify` function. This function takes a closure with two arguments - one for reading and one for writing. Typically we call these `r` and `w` respectively. The `r` argument can be used to inspect the current contents of the register, and the `w` argument can be used to modify the register contents.
+如果我们希望只改变这个寄存器中某个特定的子域而让其它子域不变,我们能使用`modify`函数。这个函数使用一个具有两个参数的闭包 - 一个用来读取,一个用来写入。通常我们分别称它们为 `r` 和 `w` 。 `r` 参数能被用来查看这个寄存器现在的内容,`w` 参数能被用来修改寄存器的内容。
```rust,ignore
pwm.ctl.modify(|r, w| w.globalsync0().clear_bit());
```
-The `modify` function really shows the power of closures here. In C, we'd have to read into some temporary value, modify the correct bits and then write the value back. This means there's considerable scope for error:
+`modify` 函数在这里真正展示了闭包的能量。在C中,我们经常需要读取一些临时值,修改成正确的比特,然后再把值写回。这意味着出现错误的范围非常大。
```C
uint32_t temp = pwm0.ctl.read();
@@ -125,18 +133,18 @@ temp |= PWM0_CTL_GLOBALSYNC0;
pwm0.ctl.write(temp);
uint32_t temp2 = pwm0.enable.read();
temp2 |= PWM0_ENABLE_PWM4EN;
-pwm0.enable.write(temp); // Uh oh! Wrong variable!
+pwm0.enable.write(temp); // 哦 不! 错误的变量!
```
[svd2rust]: https://crates.io/crates/svd2rust
[tm4c123x documentation R]: https://docs.rs/tm4c123x/0.7.0/tm4c123x/pwm0/ctl/struct.R.html
[tm4c123x documentation W]: https://docs.rs/tm4c123x/0.7.0/tm4c123x/pwm0/ctl/struct.W.html
-## Using a HAL crate
+## 使用一个HAL crate
-The HAL crate for a chip typically works by implementing a custom Trait for the raw structures exposed by the PAC. Often this trait will define a function called `constrain()` for single peripherals or `split()` for things like GPIO ports with multiple pins. This function will consume the underlying raw peripheral structure and return a new object with a higher-level API. This API may also do things like have the Serial port `new` function require a borrow on some `Clock` structure, which can only be generated by calling the function which configures the PLLs and sets up all the clock frequencies. In this way, it is statically impossible to create a Serial port object without first having configured the clock rates, or for the Serial port object to misconvert the baud rate into clock ticks. Some crates even define special traits for the states each GPIO pin can be in, requiring the user to put a pin into the correct state (say, by selecting the appropriate Alternate Function Mode) before passing the pin into Peripheral. All with no run-time cost!
+一个芯片的HAL crate是通过为PAC暴露的基础结构体们实现一个自定义Trait来发挥作用的。经常这个trait将会为某个外设定义一个被称作 `constrain()` 的函数,或者为像是有多个管脚的GPIO端口这类东西定义一个`split()`函数。这个函数将会使用基础的外设结构体,然后返回一个具有更高抽象的API的新对象。这个API还可以做一些事,比如让Serial port的 `new` 函数变成需要某个`Clock`结构体的函数,这个结构体只能通过调用配置PLLs并设置所有的时钟频率的函数来生成。在这时,生成一个Serial port对象而不先配置时钟速率是不可能的,对于Serial port对象来说错误地将波特率转换为时钟滴答数也是不会发生的。一些crates甚至为每个GPIO管脚的状态定义了特定的 traits,在把管脚传递进外设前,要求用户去把一个管脚设置成正确的状态(通过选择Alternate Function模式) 。所有这些都没有运行时开销的!
-Let's see an example:
+让我们看一个例子:
```rust,ignore
#![no_std]
@@ -155,42 +163,41 @@ fn main() -> ! {
let p = hal::Peripherals::take().unwrap();
let cp = hal::CorePeripherals::take().unwrap();
- // Wrap up the SYSCTL struct into an object with a higher-layer API
+ // 将SYSCTL结构体封装成一个有更高抽象API的对象
let mut sc = p.SYSCTL.constrain();
- // Pick our oscillation settings
+ // 选择我们的晶振配置
sc.clock_setup.oscillator = sysctl::Oscillator::Main(
sysctl::CrystalFrequency::_16mhz,
sysctl::SystemClock::UsePll(sysctl::PllOutputFrequency::_80_00mhz),
);
- // Configure the PLL with those settings
+ // 设置PLL
let clocks = sc.clock_setup.freeze();
- // Wrap up the GPIO_PORTA struct into an object with a higher-layer API.
- // Note it needs to borrow `sc.power_control` so it can power up the GPIO
- // peripheral automatically.
+ // 把GPIO_PORTA结构体封装成一个有更高抽象API的对象
+ // 注意它需要借用 `sc.power_control` 因此它能自动开启GPIO外设。
let mut porta = p.GPIO_PORTA.split(&sc.power_control);
- // Activate the UART.
+ // 激活UART
let uart = Serial::uart0(
p.UART0,
- // The transmit pin
+ // 传送管脚
porta
.pa1
.into_af_push_pull::(&mut porta.control),
- // The receive pin
+ // 接收管脚
porta
.pa0
.into_af_push_pull::(&mut porta.control),
- // No RTS or CTS required
+ // 不需要RTS或者CTS
(),
(),
- // The baud rate
+ // 波特率
115200_u32.bps(),
- // Output handling
+ // 输出处理
NewlineMode::SwapLFtoCRLF,
- // We need the clock rates to calculate the baud rate divisors
+ // 我们需要时钟频率去计算波特率除法器(divisors)
&clocks,
- // We need this to power up the UART peripheral
+ // 我们需要这个去启动UART外设
&sc.power_control,
);
diff --git a/src/start/semihosting.md b/src/start/semihosting.md
index cf4626f4..57ea2d24 100644
--- a/src/start/semihosting.md
+++ b/src/start/semihosting.md
@@ -1,14 +1,8 @@
-# Semihosting
+# 半主机模式
-Semihosting is a mechanism that lets embedded devices do I/O on the host and is
-mainly used to log messages to the host console. Semihosting requires a debug
-session and pretty much nothing else (no extra wires!) so it's super convenient
-to use. The downside is that it's super slow: each write operation can take
-several milliseconds depending on the hardware debugger (e.g. ST-Link) you use.
+半主机模式是一种可以让嵌入式设备在主机上进行I/O操作的的机制,主要被用来记录信息到主机控制台上。半主机模式需要一个debug会话,除此之外几乎没有其它要求了,因此它非常易于使用。缺点是它非常慢:每个写操作需要几毫秒的时间,其取决于你的硬件调试器(e.g. ST-LINK)。
-The [`cortex-m-semihosting`] crate provides an API to do semihosting operations
-on Cortex-M devices. The program below is the semihosting version of "Hello,
-world!":
+[`cortex-m-semihosting`] crate 提供了一个API去在Cortex-M设备上执行半主机操作。下面的程序是"Hello, world!"的半主机版本。
[`cortex-m-semihosting`]: https://crates.io/crates/cortex-m-semihosting
@@ -29,8 +23,7 @@ fn main() -> ! {
}
```
-If you run this program on hardware you'll see the "Hello, world!" message
-within the OpenOCD logs.
+如果你在硬件上运行这个程序,你将会在OpenOCD的logs中看到"Hello, world!"信息。
``` text
$ openocd
@@ -39,17 +32,13 @@ Hello, world!
(..)
```
-You do need to enable semihosting in OpenOCD from GDB first:
+你首先需要从GDB使能OpenOCD中的半主机模式。
``` console
(gdb) monitor arm semihosting enable
semihosting is enabled
```
-QEMU understands semihosting operations so the above program will also work with
-`qemu-system-arm` without having to start a debug session. Note that you'll
-need to pass the `-semihosting-config` flag to QEMU to enable semihosting
-support; these flags are already included in the `.cargo/config.toml` file of the
-template.
+QEMU理解半主机操作,因此上面的程序不需要启动一个debug会话,也能在`qemu-system-arm`中工作。注意你需要传递`-semihosting-config`标志给QEMU去使能支持半主机模式;这些标识已经被包括在模板的`.cargo/config.toml`文件中了。
``` text
$ # this program will block the terminal
@@ -58,10 +47,7 @@ $ cargo run
Hello, world!
```
-There's also an `exit` semihosting operation that can be used to terminate the
-QEMU process. Important: do **not** use `debug::exit` on hardware; this function
-can corrupt your OpenOCD session and you will not be able to debug more programs
-until you restart it.
+`exit`半主机操作也能被用于终止QEMU进程。重要:**不要**在硬件上使用`debug::exit`;这个函数会关闭你的OpenOCD对话,这样你就不能执行其它的程序调试操作了,除了重启它。
```rust,ignore
#![no_main]
@@ -94,12 +80,9 @@ $ echo $?
1
```
-One last tip: you can set the panicking behavior to `exit(EXIT_FAILURE)`. This
-will let you write `no_std` run-pass tests that you can run on QEMU.
+最后一个提示:你可以将运行时恐慌(panicking)的行为设置成 `exit(EXIT_FAILURE)`。这会允许你编写可以在QEMU上运行通过的 `no_std` 测试。
-For convenience, the `panic-semihosting` crate has an "exit" feature that when
-enabled invokes `exit(EXIT_FAILURE)` after logging the panic message to the host
-stderr.
+为了方便,`panic-semihosting` crate有一个 "exit" 特性。当它使能的时候,在主机stderr上打印恐慌(painc)信息后会调用 `exit(EXIT_FAILURE)` 。
```rust,ignore
#![no_main]
@@ -131,15 +114,13 @@ $ echo $?
1
```
-**NOTE**: To enable this feature on `panic-semihosting`, edit your
-`Cargo.toml` dependencies section where `panic-semihosting` is specified with:
+**注意**: 为了在`panic-semihosting`上使能这个特性,编辑你的`Cargo.toml`依赖,`panic-semihosting`改写成:
``` toml
panic-semihosting = { version = "VERSION", features = ["exit"] }
```
-where `VERSION` is the version desired. For more information on dependencies
-features check the [`specifying dependencies`] section of the Cargo book.
+`VERSION`是想要的版本。关于依赖features的更多信息查看Cargo book的[`specifying dependencies`]部分。
[`specifying dependencies`]:
https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
diff --git a/src/static-guarantees/design-contracts.md b/src/static-guarantees/design-contracts.md
index 56fec45b..74a6cedd 100644
--- a/src/static-guarantees/design-contracts.md
+++ b/src/static-guarantees/design-contracts.md
@@ -1,27 +1,29 @@
-# Design Contracts
+# 设计约定(design contracts)
-In our last chapter, we wrote an interface that *didn't* enforce design contracts. Let's take another look at our imaginary GPIO configuration register:
+在我们的上个章节中,我们写了一个接口,但没有强制遵守设计约定。让我们再看下我们假想的GPIO配置寄存器:
-| Name | Bit Number(s) | Value | Meaning | Notes |
+
+| 名字 | 位数(s) | 值 | 含义 | 注释 |
| ---: | ------------: | ----: | ------: | ----: |
-| enable | 0 | 0 | disabled | Disables the GPIO |
-| | | 1 | enabled | Enables the GPIO |
-| direction | 1 | 0 | input | Sets the direction to Input |
-| | | 1 | output | Sets the direction to Output |
-| input_mode | 2..3 | 00 | hi-z | Sets the input as high resistance |
-| | | 01 | pull-low | Input pin is pulled low |
-| | | 10 | pull-high | Input pin is pulled high |
-| | | 11 | n/a | Invalid state. Do not set |
-| output_mode | 4 | 0 | set-low | Output pin is driven low |
-| | | 1 | set-high | Output pin is driven high |
-| input_status | 5 | x | in-val | 0 if input is < 1.5v, 1 if input >= 1.5v |
-
-If we instead checked the state before making use of the underlying hardware, enforcing our design contracts at runtime, we might write code that looks like this instead:
+| 使能 | 0 | 0 | 关闭 | 关闭GPIO |
+| | | 1 | 使能 | 使能GPIO |
+| 方向 | 1 | 0 | 输入 | 方向设置成输入 |
+| | | 1 | 输出 | 方向设置成输出 |
+| 输入模式 | 2..3 | 00 | 高阻态 | 输入设置为高阻态 |
+| | | 01 | 下拉 | 下拉输入管脚 |
+| | | 10 | 上拉 | 上拉输入管脚 |
+| | | 11 | n/a | 无效状态。不要设置 |
+| 输出模式 | 4 | 0 | 拉低 | 把管脚设置成低电平 |
+| | | 1 | 拉高 | 把管脚设置成高电平 |
+| 输入状态 | 5 | x | 输入电平 | 如果输入 < 1.5v 为0,如果输入 >= 1.5v 为1 |
+
+
+如果在使用底层硬件之前检查硬件的状态,在运行时强制用户遵守设计约定,代码可能像这一样:
```rust,ignore
-/// GPIO interface
+/// GPIO接口
struct GpioConfig {
- /// GPIO Configuration structure generated by svd2rust
+ /// 由svd2rust生成的GPIO配制结构体
periph: GPIO_CONFIG,
}
@@ -34,7 +36,7 @@ impl GpioConfig {
pub fn set_direction(&mut self, is_output: bool) -> Result<(), ()> {
if self.periph.read().enable().bit_is_clear() {
- // Must be enabled to set direction
+ // 必须被使能配置方向
return Err(());
}
@@ -47,12 +49,12 @@ impl GpioConfig {
pub fn set_input_mode(&mut self, variant: InputMode) -> Result<(), ()> {
if self.periph.read().enable().bit_is_clear() {
- // Must be enabled to set input mode
+ // 必须被使能配置输入模式
return Err(());
}
if self.periph.read().direction().bit_is_set() {
- // Direction must be input
+ // 方向必须被设置成输入
return Err(());
}
@@ -65,12 +67,12 @@ impl GpioConfig {
pub fn set_output_status(&mut self, is_high: bool) -> Result<(), ()> {
if self.periph.read().enable().bit_is_clear() {
- // Must be enabled to set output status
+ // 设置输出状态必须被使能
return Err(());
}
if self.periph.read().direction().bit_is_clear() {
- // Direction must be output
+ // 方向必须是输出
return Err(());
}
@@ -83,12 +85,12 @@ impl GpioConfig {
pub fn get_input_status(&self) -> Result {
if self.periph.read().enable().bit_is_clear() {
- // Must be enabled to get status
+ // 获取状态必须被使能
return Err(());
}
if self.periph.read().direction().bit_is_set() {
- // Direction must be input
+ // 方向必须是输入
return Err(());
}
@@ -97,23 +99,23 @@ impl GpioConfig {
}
```
-Because we need to enforce the restrictions on the hardware, we end up doing a lot of runtime checking which wastes time and resources, and this code will be much less pleasant for the developer to use.
+因为需要强制遵守硬件上的限制,所以最后做了很多运行时检查,它浪费了我们很多时间和资源,对于开发者来说,这个代码用起来就没那么愉快了。
-## Type States
+## 类型状态(Type states)
-But what if instead, we used Rust's type system to enforce the state transition rules? Take this example:
+但是,如果我们让Rust的类型系统去强制遵守状态转换的规则会怎样?看下这个例子:
```rust,ignore
-/// GPIO interface
+/// GPIO接口
struct GpioConfig {
- /// GPIO Configuration structure generated by svd2rust
+ /// 由svd2rust产生的GPIO配置结构体
periph: GPIO_CONFIG,
enabled: ENABLED,
direction: DIRECTION,
mode: MODE,
}
-// Type states for MODE in GpioConfig
+// GpioConfig中MODE的类型状态
struct Disabled;
struct Enabled;
struct Output;
@@ -123,7 +125,7 @@ struct PulledHigh;
struct HighZ;
struct DontCare;
-/// These functions may be used on any GPIO Pin
+/// 这些函数可能被用于所有的GPIO管脚
impl GpioConfig {
pub fn into_disabled(self) -> GpioConfig {
self.periph.modify(|_r, w| w.enable.disabled());
@@ -164,14 +166,14 @@ impl GpioConfig {
}
}
-/// This function may be used on an Output Pin
+/// 这个函数可能被用于一个输出管脚
impl GpioConfig {
pub fn set_bit(&mut self, set_high: bool) {
self.periph.modify(|_r, w| w.output_mode.set_bit(set_high));
}
}
-/// These methods may be used on any enabled input GPIO
+/// 这些方法可能被用于任意一个使能的输入GPIO
impl GpioConfig {
pub fn bit_is_set(&self) -> bool {
self.periph.read().input_status.bit_is_set()
@@ -209,46 +211,46 @@ impl GpioConfig {
}
```
-Now let's see what the code using this would look like:
+现在让我们看下代码如何用这个API:
```rust,ignore
/*
- * Example 1: Unconfigured to High-Z input
+ * 案例 1: 从未配置到高阻输入
*/
let pin: GpioConfig = get_gpio();
-// Can't do this, pin isn't enabled!
+// 不能这么做,pin没有被使能
// pin.into_input_pull_down();
-// Now turn the pin from unconfigured to a high-z input
+// 现在把管脚从未配置变为高阻输入
let input_pin = pin.into_enabled_input();
-// Read from the pin
+// 从管脚读取
let pin_state = input_pin.bit_is_set();
-// Can't do this, input pins don't have this interface!
+// 不能这么做,输入管脚没有这个接口
// input_pin.set_bit(true);
/*
- * Example 2: High-Z input to Pulled Low input
+ * 案例 2: 高阻输入到下拉输入
*/
let pulled_low = input_pin.into_input_pull_down();
let pin_state = pulled_low.bit_is_set();
/*
- * Example 3: Pulled Low input to Output, set high
+ * 案例 3: 下拉输入到输出, 拉高
*/
let output_pin = pulled_low.into_enabled_output();
output_pin.set_bit(true);
-// Can't do this, output pins don't have this interface!
+// 不能这么做,输出管脚没有这个接口
// output_pin.into_input_pull_down();
```
-This is definitely a convenient way to store the state of the pin, but why do it this way? Why is this better than storing the state as an `enum` inside of our `GpioConfig` structure?
+这绝对是存储管脚状态的便捷方法,但是为什么这么做?为什么这比把状态当成一个`enum`存在我们的`GpioConfig`结构体中更好?
-## Compile Time Functional Safety
+## 编译时功能安全(Functional Safety)
-Because we are enforcing our design constraints entirely at compile time, this incurs no runtime cost. It is impossible to set an output mode when you have a pin in an input mode. Instead, you must walk through the states by converting it to an output pin, and then setting the output mode. Because of this, there is no runtime penalty due to checking the current state before executing a function.
+因为我们在编译时完全强制遵守设计约定,这造成了没有运行时开销。当管脚处于输入模式时时,是不可能设置输出模式的。必须先把它设置成一个输出管脚,然后再设置输出模式。因为在执行一个函数前会检查现在的状态,因此没有运行时消耗。
-Also, because these states are enforced by the type system, there is no longer room for errors by consumers of this interface. If they try to perform an illegal state transition, the code will not compile!
+也因为这些状态被类型系统强制遵守,因此没有为这个接口的使用者留太多的犯错余地。如果它们尝试执行一个非法的状态转换,代码将不会编译成功!
diff --git a/src/static-guarantees/index.md b/src/static-guarantees/index.md
index a5bb8fa3..5dcfe15b 100644
--- a/src/static-guarantees/index.md
+++ b/src/static-guarantees/index.md
@@ -1,23 +1,13 @@
-# Static Guarantees
+# 静态保障
-Rust's type system prevents data races at compile time (see [`Send`] and
-[`Sync`] traits). The type system can also be used to check other properties at
-compile time; reducing the need for runtime checks in some cases.
+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
-When applied to embedded programs these *static checks* can be used, for
-example, to enforce that configuration of I/O interfaces is done properly. For
-instance, one can design an API where it is only possible to initialize a serial
-interface by first configuring the pins that will be used by the interface.
+当应用到嵌入式程序时,这些*静态检查*能被用来,比如,强制按需配置I/O接口。例如,可以设计一个初始化串行接口的API,这个API只有在配置好接口需要的管脚后才可以被正确地使用。
-One can also statically check that operations, like setting a pin low, can only
-be performed on correctly configured peripherals. For example, trying to change
-the output state of a pin configured in floating input mode would raise a
-compile error.
+也可以静态检查,是否是在正确配置了的外设上执行的操作,像是拉低一个管脚这种操作。比如尝试修改一个被配置成浮空输入模式的管脚的输出状态时,会触发一个编译时错误。
+
+并且,像是在前面章节看到的,所有权的概念能被应用到外设上确保一个程序只有某些部分可以修改一个外设。与将这个外设当做全局可变的状态相比,*访问控制*(assess control)使得软件更容易推理。
-And, as seen in the previous chapter, the concept of ownership can be applied
-to peripherals to ensure that only certain parts of a program can modify a
-peripheral. This *access control* makes software easier to reason about
-compared to the alternative of treating peripherals as global mutable state.
diff --git a/src/static-guarantees/state-machines.md b/src/static-guarantees/state-machines.md
index 84207911..44f3d7bd 100644
--- a/src/static-guarantees/state-machines.md
+++ b/src/static-guarantees/state-machines.md
@@ -1,62 +1,61 @@
-# Peripherals as State Machines
+# 作为状态机的外设
-The peripherals of a microcontroller can be thought of as set of state machines. For example, the configuration of a simplified [GPIO pin] could be represented as the following tree of states:
+一个微控制器的外设可以被想成是一组状态机。比如,一个简化的[GPIO管脚]的配置可以被表达成下列的状态树:
-[GPIO pin]: https://en.wikipedia.org/wiki/General-purpose_input/output
+[GPIO管脚]: https://en.wikipedia.org/wiki/General-purpose_input/output
-* Disabled
-* Enabled
- * Configured as Output
- * Output: High
- * Output: Low
- * Configured as Input
- * Input: High Resistance
- * Input: Pulled Low
- * Input: Pulled High
+* 关闭
+* 使能
+ * 配置成输出
+ * 输出: 高
+ * 输出: 低
+ * 配置成输入
+ * 输入: 高阻态
+ * 输入: 下拉
+ * 输入: 上拉
-If the peripheral starts in the `Disabled` mode, to move to the `Input: High Resistance` mode, we must perform the following steps:
+如果外设开始于`关闭`模式,切换到`输入: 高阻态`模式,我们必须执行下面的步骤:
-1. Disabled
-2. Enabled
-3. Configured as Input
-4. Input: High Resistance
+1. 关闭
+2. 使能
+3. 配置成输入
+4. 输入: 高阻态
-If we wanted to move from `Input: High Resistance` to `Input: Pulled Low`, we must perform the following steps:
+如果我们想要从`输入: 高阻态`切换到`输入: 下拉`,我们必须执行下列的步骤:
-1. Input: High Resistance
-2. Input: Pulled Low
+1. 输入: 高阻抗
+2. 输入: 下拉
-Similarly, if we want to move a GPIO pin from configured as `Input: Pulled Low` to `Output: High`, we must perform the following steps:
+同样地,如果我们想要把一个GPIO管脚从`输入: 下拉`切换到`输出: 高`,我们必须执行下列的步骤:
+1. 输入: 下拉
+2. 配置成输入
+3. 配置成输出
+4. 输出: 高
-1. Input: Pulled Low
-2. Configured as Input
-3. Configured as Output
-4. Output: High
+## 硬件表征(Hardware Representation)
-## Hardware Representation
+通常,通过向映射到GPIO外设上的指定的寄存器中写入值可以配置上面列出的状态。让我们定义一个假想的GPIO配置寄存器来解释下它:
-Typically the states listed above are set by writing values to given registers mapped to a GPIO peripheral. Let's define an imaginary GPIO Configuration Register to illustrate this:
-
-| Name | Bit Number(s) | Value | Meaning | Notes |
+| 名字 | 位数(s) | 值 | 含义 | 注释 |
| ---: | ------------: | ----: | ------: | ----: |
-| enable | 0 | 0 | disabled | Disables the GPIO |
-| | | 1 | enabled | Enables the GPIO |
-| direction | 1 | 0 | input | Sets the direction to Input |
-| | | 1 | output | Sets the direction to Output |
-| input_mode | 2..3 | 00 | hi-z | Sets the input as high resistance |
-| | | 01 | pull-low | Input pin is pulled low |
-| | | 10 | pull-high | Input pin is pulled high |
-| | | 11 | n/a | Invalid state. Do not set |
-| output_mode | 4 | 0 | set-low | Output pin is driven low |
-| | | 1 | set-high | Output pin is driven high |
-| input_status | 5 | x | in-val | 0 if input is < 1.5v, 1 if input >= 1.5v |
-
-We _could_ expose the following structure in Rust to control this GPIO:
+| 使能 | 0 | 0 | 关闭 | 关闭GPIO |
+| | | 1 | 使能 | 使能GPIO |
+| 方向 | 1 | 0 | 输入 | 方向设置成输入 |
+| | | 1 | 输出 | 方向设置成输出 |
+| 输入模式 | 2..3 | 00 | hi-z | 输入设置为高阻态 |
+| | | 01 | 下拉 | 下拉输入管脚 |
+| | | 10 | 上拉 | 上拉输入管脚 |
+| | | 11 | n/a | 无效状态。不要设置 |
+| 输出模式 | 4 | 0 | 拉低 | 输出管脚变成地电平 |
+| | | 1 | 拉高 | 输出管脚变成高电平 |
+| 输入状态 | 5 | x | in-val | 如果输入 < 1.5v为0,如果输入 >= 1.5v为1 |
+
+_可以_ 在Rust中暴露下列的结构体来控制这个GPIO:
```rust,ignore
-/// GPIO interface
+/// GPIO接口
struct GpioConfig {
- /// GPIO Configuration structure generated by svd2rust
+ /// 由svd2rust生成的GPIO配置结构体
periph: GPIO_CONFIG,
}
@@ -91,8 +90,8 @@ impl GpioConfig {
}
```
-However, this would allow us to modify certain registers that do not make sense. For example, what happens if we set the `output_mode` field when our GPIO is configured as an input?
+然而,这会允许我们修改某些没有意义的寄存器。比如,如果当我们的GPIO被配置为输入时我们设置`output_mode`字段,将会发生什么?
-In general, use of this structure would allow us to reach states not defined by our state machine above: e.g. an output that is pulled low, or an input that is set high. For some hardware, this may not matter. On other hardware, it could cause unexpected or undefined behavior!
+通常使用这个结构体会允许我们访问到上面的状态机没有定义的状态:比如,一个被上拉的输出,或者一个被拉高的输入。对于一些硬件,这并没有关系。对另外一些硬件来说,这将会导致不可预期或者没有定义的行为!
-Although this interface is convenient to write, it doesn't enforce the design contracts set out by our hardware implementation.
+虽然这个接口很方便写入,但是它没有强制我们遵守硬件实现所设的设计约定。
diff --git a/src/static-guarantees/typestate-programming.md b/src/static-guarantees/typestate-programming.md
index 9bd70f40..10f5af6d 100644
--- a/src/static-guarantees/typestate-programming.md
+++ b/src/static-guarantees/typestate-programming.md
@@ -1,9 +1,9 @@
-# Typestate Programming
+# 类型状态编程(Typestate Programming)
-The concept of [typestates] describes the encoding of information about the current state of an object into the type of that object. Although this can sound a little arcane, if you have used the [Builder Pattern] in Rust, you have already started using Typestate Programming!
+[typestates]的概念是指将有关对象当前状态的信息编码进该对象的类型中。虽然这听起来有点神秘,如果你在Rust中用过[建造者模式],你就已经开始使用类型状态编程了!
[typestates]: https://en.wikipedia.org/wiki/Typestate_analysis
-[Builder Pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
+[建造者模式]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
```rust
pub mod foo_module {
@@ -49,17 +49,18 @@ fn main() {
}
```
-In this example, there is no direct way to create a `Foo` object. We must create a `FooBuilder`, and properly initialize it before we can obtain the `Foo` object we want.
+在这个例子里,不能直接生成一个`Foo`对象。必须先生成一个`FooBuilder`,并且恰当地初始化`FooBuilder`后,才能获取到需要的`Foo`对象。
-This minimal example encodes two states:
+这个最小的例子编码了两个状态:
-* `FooBuilder`, which represents an "unconfigured", or "configuration in process" state
-* `Foo`, which represents a "configured", or "ready to use" state.
+* `FooBuilder`,其表示一个"没有被配置",或者"正在配置"状态
+* `Foo`,其表示了一个"被配置",或者"可以使用"状态。
-## Strong Types
+## 强类型
-Because Rust has a [Strong Type System], there is no easy way to magically create an instance of `Foo`, or to turn a `FooBuilder` into a `Foo` without calling the `into_foo()` method. Additionally, calling the `into_foo()` method consumes the original `FooBuilder` structure, meaning it can not be reused without the creation of a new instance.
+因为Rust有一个[强类型系统],没有什么简单的方法可以奇迹般地生成一个`Foo`实例,也没有简单的方法可以不用调用`into_foo()`方法而把一个`FooBuilder`变成一个`Foo`。另外,调用`into_foo()`方法消费了最初的`FooBuilder`结构体,意味着不生成一个新的实例就不能被再次使用它。
-[Strong Type System]: https://en.wikipedia.org/wiki/Strong_and_weak_typing
+[强类型系统]: https://en.wikipedia.org/wiki/Strong_and_weak_typing
+
+这允许我们可以将系统的状态表示成类型,把状态转换必须的动作包括进转换两个类型的方法中。通过生成一个 `FooBuilder`,转换成一个 `Foo` 对象,我们已经使用了一个基本的状态机。
-This allows us to represent the states of our system as types, and to include the necessary actions for state transitions into the methods that exchange one type for another. By creating a `FooBuilder`, and exchanging it for a `Foo` object, we have walked through the steps of a basic state machine.
diff --git a/src/static-guarantees/zero-cost-abstractions.md b/src/static-guarantees/zero-cost-abstractions.md
index f7defd18..ef88f021 100644
--- a/src/static-guarantees/zero-cost-abstractions.md
+++ b/src/static-guarantees/zero-cost-abstractions.md
@@ -1,6 +1,6 @@
-# Zero Cost Abstractions
+# 零成本抽象
-Type states are also an excellent example of Zero Cost Abstractions - the ability to move certain behaviors to compile time execution or analysis. These type states contain no actual data, and are instead used as markers. Since they contain no data, they have no actual representation in memory at runtime:
+类型状态是一个零成本抽象的杰出案例 - 把某些行为移到编译时执行或者分析的能力。这些类型状态不包含真实的数据,只用来作为标记。因为它们不包含数据,在运行时它们在内存中不存在实际的表示。
```rust,ignore
use core::mem::size_of;
@@ -11,15 +11,15 @@ let _ = size_of::(); // == 0
let _ = size_of::>(); // == 0
```
-## Zero Sized Types
+## 零大小的类型(Zero Sized Types)
```rust,ignore
struct Enabled;
```
-Structures defined like this are called Zero Sized Types, as they contain no actual data. Although these types act "real" at compile time - you can copy them, move them, take references to them, etc., however the optimizer will completely strip them away.
+像这样定义的结构体被称为零大小的类型,因为它们不包含实际数据。虽然这些类型在编译时像是"真实的"(real) - 你可以拷贝它们,移动它们,引用它们,等等,然而优化器将会完全跳过它们。
-In this snippet of code:
+在这个代码片段里:
```rust,ignore
pub fn into_input_high_z(self) -> GpioConfig {
@@ -33,10 +33,10 @@ pub fn into_input_high_z(self) -> GpioConfig {
}
```
-The GpioConfig we return never exists at runtime. Calling this function will generally boil down to a single assembly instruction - storing a constant register value to a register location. This means that the type state interface we've developed is a zero cost abstraction - it uses no more CPU, RAM, or code space tracking the state of `GpioConfig`, and renders to the same machine code as a direct register access.
+我们返回的GpioConfig在运行时并不存在。对这个函数的调用通常会被归纳为一条汇编指令 - 把一个常量寄存器值存进一个寄存器里。这意味着我们开发的类型状态接口是一个零成本抽象 - 它不会用更多的CPU,RAM,或者代码空间去跟踪`GpioConfig`的状态,会被渲染成和直接访问寄存器一样的机器码。
-## Nesting
+## 嵌套
-In general, these abstractions may be nested as deeply as you would like. As long as all components used are zero sized types, the whole structure will not exist at runtime.
+通常,这些抽象可能会被深深地嵌套起来。一旦结构体使用的所有的组件是零大小类型的,整个结构体将不会在运行时存在。
-For complex or deeply nested structures, it may be tedious to define all possible combinations of state. In these cases, macros may be used to generate all implementations.
+对于复杂或者深度嵌套的结构体,定义所有可能的状态组合可能很乏味。在这些例子中,宏可能可以被用来生成所有的实现。
diff --git a/src/unsorted/index.md b/src/unsorted/index.md
index 4be66549..cb8f7a4e 100644
--- a/src/unsorted/index.md
+++ b/src/unsorted/index.md
@@ -1 +1 @@
-# Unsorted topics
+# 没有排序的主题
diff --git a/src/unsorted/math.md b/src/unsorted/math.md
index 4367b867..9cfb0741 100644
--- a/src/unsorted/math.md
+++ b/src/unsorted/math.md
@@ -1,11 +1,9 @@
-# Performing math functionality with `#[no_std]`
+# 在`#[no_std]`下执行数学运算
-If you want to perform math related functionality like calculating the squareroot or
-the exponential of a number and you have the full standard library available, your code
-might look like this:
+如果你想要执行数学相关的函数,像是计算平方根或者一个数的指数并有完整的标准库支持,代码可能看起来像这样:
```rs
-//! Some mathematical functions with standard support available
+//! 可用一些标准支持的数学函数
fn main() {
let float: f32 = 4.82832;
@@ -26,9 +24,7 @@ fn main() {
}
```
-Without standard library support, these functions are not available.
-An external crate like [`libm`](https://crates.io/crates/libm) can be used instead. The example code
-would then look like this:
+没有标准库支持的时候,这些函数不可用。反而可以使用像是[`libm`](https://crates.io/crates/libm)这样一个外部库。示例的代码将会看起来像这样:
```rs
#![no_main]
@@ -58,16 +54,15 @@ fn main() -> ! {
exponential_of_four
)
.unwrap();
- // exit QEMU
- // NOTE do not run this on hardware; it can corrupt OpenOCD state
+ // 退出QEMU
+ // 注意不要在硬件上使用这个; 它能破坏OpenOCD的状态
// debug::exit(debug::EXIT_SUCCESS);
loop {}
}
```
-If you need to perform more complex operations like DSP signal processing or advanced linear
-algebra on your MCU, the following crates might help you
+如果需要在MCU上执行更复杂的操作,像是DSP信号处理或者更高级的线性代数,下列的crates可能可以帮到你
- [CMSIS DSP library binding](/~https://github.com/jacobrosenthal/cmsis-dsp-sys)
- [`constgebra`](https://crates.io/crates/constgebra)
diff --git a/src/unsorted/speed-vs-size.md b/src/unsorted/speed-vs-size.md
index 4721c068..b939a386 100644
--- a/src/unsorted/speed-vs-size.md
+++ b/src/unsorted/speed-vs-size.md
@@ -1,56 +1,35 @@
-# Optimizations: the speed size tradeoff
+# 优化: 速度与大小之间的博弈
-Everyone wants their program to be super fast and super small but it's usually
-not possible to have both characteristics. This section discusses the
-different optimization levels that `rustc` provides and how they affect the
-execution time and binary size of a program.
+每个人都想要程序变得即快又小,但是同时满足这两个条件是不可能的。这部分讨论`rustc`提供的不同的优化等级,和它们是如何影响执行时间和一个程序的二进制项的大小。
-## No optimizations
+## 无优化
-This is the default. When you call `cargo build` you use the development (AKA
-`dev`) profile. This profile is optimized for debugging so it enables debug
-information and does *not* enable any optimizations, i.e. it uses `-C opt-level
-= 0`.
+这是默认的。当你调用`cargo build`时,你使用的是development(又叫`dev`)配置。这个配置优化的目的是为了调试,因此它使能了调试信息且*关闭*了所有优化,i.e. 它使用 `-C opt-level = 0` 。
-At least for bare metal development, debuginfo is zero cost in the sense that it
-won't occupy space in Flash / ROM so we actually recommend that you enable
-debuginfo in the release profile -- it is disabled by default. That will let you
-use breakpoints when debugging release builds.
+至少对于裸机开发来说,调试信息不会占用Flash/ROM中的空间,意味着在这种情况下,调试信息是零开销的,因此实际上我们推荐你在release配置中使能调试信息 -- 默认它被关闭了。那会让你调试release版本的固件时可以使用断点。
``` toml
[profile.release]
-# symbols are nice and they don't increase the size on Flash
+# 调试符号很好且它们不会增加Flash上的大小
debug = true
```
-No optimizations is great for debugging because stepping through the code feels
-like you are executing the program statement by statement, plus you can `print`
-stack variables and function arguments in GDB. When the code is optimized, trying
-to print variables results in `$0 = ` being printed.
+无优化对于调试来说是最好的选择,因为单步调试代码感觉像是你正在逐条语句地执行程序,且你能在GDB中`print`栈变量和函数参数。当代码被优化了,尝试打印变量会导致`$0 = `被打印出来。
-The biggest downside of the `dev` profile is that the resulting binary will be
-huge and slow. The size is usually more of a problem because unoptimized
-binaries can occupy dozens of KiB of Flash, which your target device may not
-have -- the result: your unoptimized binary doesn't fit in your device!
+`dev`配置最大的缺点就是最终的二进制项将会变得巨大且缓慢。大小通常是一个更大的问题,因为未优化的二进制项会占据大量KiB的Flash,你的目标设备可能没这么多Flash -- 结果: 你未优化的二进制项无法烧录进你的设备中!
-Can we have smaller, debugger friendly binaries? Yes, there's a trick.
+我们可以有更小的,调试友好的二进制项吗?是的,这里有一个技巧。
-### Optimizing dependencies
+### 优化依赖
-There's a Cargo feature named [`profile-overrides`] that lets you
-override the optimization level of dependencies. You can use that feature to
-optimize all dependencies for size while keeping the top crate unoptimized and
-debugger friendly.
+这里有个名为[`profile-overrides`]的Cargo feature,其可以让你覆盖依赖项的优化等级。你能使用这个feature去优化所有依赖的大小,而保持顶层的crate没有被优化以致调试起来友好。
-Beware that generic code can sometimes be optimized alongside the crate where it
-is instantiated, rather than the crate where it is defined. If you create an
-instance of a generic struct in your application and find that it pulls in code
-with a large footprint, it may be that increasing the optimisation level of the
-relevant dependencies has no effect.
+需要知道,泛型代码有时是在它被实例化的库中被优化的,而不是它被定义的地方.如果你在你的应用中生成了一个泛型结构体的实例,
+并且发现它让代码体积变得更大,那可能是因为相关的依赖的优化等级的增加没有造成影响.
[`profile-overrides`]: https://doc.rust-lang.org/cargo/reference/profiles.html#overrides
-Here's an example:
+这是一个示例:
``` toml
# Cargo.toml
@@ -62,7 +41,7 @@ name = "app"
opt-level = "z" # +
```
-Without the override:
+没有覆盖:
``` text
$ cargo size --bin app -- -A
@@ -75,7 +54,7 @@ section size addr
.bss 4 0x20000000
```
-With the override:
+有覆盖:
``` text
$ cargo size --bin app -- -A
@@ -88,54 +67,36 @@ section size addr
.bss 4 0x20000000
```
-That's a 6 KiB reduction in Flash usage without any loss in the debuggability of
-the top crate. If you step into a dependency then you'll start seeing those
-`` messages again but it's usually the case that you want
-to debug the top crate and not the dependencies. And if you *do* need to debug a
-dependency then you can use the `profile-overrides` feature to exclude a
-particular dependency from being optimized. See example below:
+在Flash的使用上减少了6KiB,而不会损害顶层crate的可调试性。如果你步进一个依赖项,然后你将开始再次看到那些``信息,但是通常的情况下你只想调试顶层的crate而不是依赖项。如果你 *需要* 调试一个依赖项,那么你可以使用`profile-overrides` feature去防止一个特定的依赖项被优化。看下面的例子:
``` toml
# ..
-# don't optimize the `cortex-m-rt` crate
+# 不要优化`cortex-m-rt` crate
[profile.dev.package.cortex-m-rt] # +
opt-level = 0 # +
-# but do optimize all the other dependencies
+# 但是优化所有其它依赖项
[profile.dev.package."*"]
codegen-units = 1 # better optimizations
opt-level = "z"
```
-Now the top crate and `cortex-m-rt` are debugger friendly!
+现在顶层的crate和`cortex-m-rt`对调试器很友好!
-## Optimize for speed
+## 优化速度
-As of 2018-09-18 `rustc` supports three "optimize for speed" levels: `opt-level
-= 1`, `2` and `3`. When you run `cargo build --release` you are using the release
-profile which defaults to `opt-level = 3`.
+自2018-09-18开始 `rustc` 支持三个 "优化速度" 的等级: `opt-level = 1`, `2` 和 `3` 。当你运行 `cargo build --release` 时,你正在使用的是release配置,其默认是 `opt-level = 3` 。
-Both `opt-level = 2` and `3` optimize for speed at the expense of binary size,
-but level `3` does more vectorization and inlining than level `2`. In
-particular, you'll see that at `opt-level` equal to or greater than `2` LLVM will
-unroll loops. Loop unrolling has a rather high cost in terms of Flash / ROM
-(e.g. from 26 bytes to 194 for a zero this array loop) but can also halve the
-execution time given the right conditions (e.g. number of iterations is big
-enough).
+`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)但是如果条件合适(迭代次数足够大),也可以将执行时间减半。
-Currently there's no way to disable loop unrolling in `opt-level = 2` and `3` so
-if you can't afford its cost you should optimize your program for size.
+现在还没有办法在`opt-level = 2`和`3`的情况下关闭循环展开,因此如果你不能接受它的开销,你应该选择优化你的程序的大小。
-## Optimize for size
+## 优化大小
-As of 2018-09-18 `rustc` supports two "optimize for size" levels: `opt-level =
-"s"` and `"z"`. These names were inherited from clang / LLVM and are not too
-descriptive but `"z"` is meant to give the idea that it produces smaller
-binaries than `"s"`.
+自2018-09-18开始`rustc`支持两个"优化大小"的等级: `opt-level = "s"` 和 `"z"` 。这些名字传承自 clang / LLVM 且不具有描述性,但是`"z"`意味着它产生的二进制文件比`"s"`更小。
-If you want your release binaries to be optimized for size then change the
-`profile.release.opt-level` setting in `Cargo.toml` as shown below.
+如果你想要发布一个优化了大小的二进制项,那么改变下面展示的`Cargo.toml`中的`profile.release.opt-level`配置。
``` toml
[profile.release]
@@ -143,22 +104,13 @@ If you want your release binaries to be optimized for size then change the
opt-level = "s"
```
-These two optimization levels greatly reduce LLVM's inline threshold, a metric
-used to decide whether to inline a function or not. One of Rust principles are
-zero cost abstractions; these abstractions tend to use a lot of newtypes and
-small functions to hold invariants (e.g. functions that borrow an inner value
-like `deref`, `as_ref`) so a low inline threshold can make LLVM miss
-optimization opportunities (e.g. eliminate dead branches, inline calls to
-closures).
+这两个优化等级极大地减小了LLVM的内联阈值,一个用来决定是否内联或者不内联一个函数的度量。Rust其中一个概念是零成本抽象;这些抽象趋向于去使用许多新类型和小函数去保持不变量(e.g. 像是`deref`,`as_ref`这样借用内部值的函数)因此一个低内联阈值会使LLVM失去优化的机会(e.g. 去掉死分支(dead branches),内联对闭包的调用)。
-When optimizing for size you may want to try increasing the inline threshold to
-see if that has any effect on the binary size. The recommended way to change the
-inline threshold is to append the `-C inline-threshold` flag to the other
-rustflags in `.cargo/config.toml`.
+当优化大小时,你可能想要尝试增加内联阈值去观察是否会对你的二进制项的大小有影响。推荐的改变内联阈值的方法是在`.cargo/config.toml`中往其它rustflags后插入`-C inline-threshold` 。
``` toml
# .cargo/config.toml
-# this assumes that you are using the cortex-m-quickstart template
+# 这里假设你正在使用cortex-m-quickstart模板
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = [
# ..
@@ -166,14 +118,13 @@ rustflags = [
]
```
-What value to use? [As of 1.29.0 these are the inline thresholds that the
-different optimization levels use][inline-threshold]:
+用什么值?[从1.29.0开始,这些是不同优化级别使用的内联阈值][inline-threshold]:
[inline-threshold]: /~https://github.com/rust-lang/rust/blob/1.29.0/src/librustc_codegen_llvm/back/write.rs#L2105-L2122
-- `opt-level = 3` uses 275
-- `opt-level = 2` uses 225
-- `opt-level = "s"` uses 75
-- `opt-level = "z"` uses 25
+- `opt-level = 3` 使用 275
+- `opt-level = 2` 使用 225
+- `opt-level = "s"` 使用 75
+- `opt-level = "z"` 使用 25
-You should try `225` and `275` when optimizing for size.
+当优化大小时,你应该尝试`225`和`275` 。