diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1e3d552..6d327a43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,11 @@ on: branches: [ staging, trying, master ] pull_request: +permissions: + contents: read + pages: write + id-token: write + jobs: build: runs-on: ubuntu-20.04 @@ -21,25 +26,9 @@ jobs: - name: Put pip binary directory into path run: echo "~/.local/bin" >> $GITHUB_PATH - - name: Cache Cargo installed binaries - uses: actions/cache@v1 - id: cache-cargo - with: - path: ~/cargo-bin - key: cache-cargo - - name: Install mdbook - if: steps.cache-cargo.outputs.cache-hit != 'true' - uses: actions-rs/install@v0.1 + - uses: moonrepo/setup-rust@v0 with: - crate: mdbook - version: latest - - name: Copy mdbook to cache directory - if: steps.cache-cargo.outputs.cache-hit != 'true' - run: | - mkdir ~/cargo-bin - cp ~/.cargo/bin/mdbook ~/cargo-bin - - name: Put new cargo binary directory into path - run: echo "~/cargo-bin" >> $GITHUB_PATH + bins: mdbook, mdbook-i18n-helpers - name: Build book run: mdbook build @@ -48,10 +37,28 @@ jobs: - name: Check links run: linkchecker book - - name: Deploy book - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} - uses: peaceiris/actions-gh-pages@v3 + - name: Build translations + # Expand the list of langauges in the for loop as needed. + # Update the language picker in theme/index.hbs when a + # language is complete enough to be found by users. + run: | + for po_lang in da; do + echo "::group::Building $po_lang translation" + MDBOOK_BOOK__LANGUAGE=$po_lang \ + MDBOOK_OUTPUT__HTML__SITE_URL=/book/$po_lang/ \ + mdbook build -d book/$po_lang + echo "::endgroup::" + done + + - name: Setup Pages + uses: actions/configure-pages@v2 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: book - force_orphan: true + path: book + + - name: Deploy to GitHub Pages + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + id: deployment + uses: actions/deploy-pages@v1 diff --git a/.gitignore b/.gitignore index 7585238e..4eb50620 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ book +po/messages.pot diff --git a/book.toml b/book.toml index fa5b7e3a..64ba4ebb 100644 --- a/book.toml +++ b/book.toml @@ -4,5 +4,11 @@ multilingual = false src = "src" title = "The Embedded Rust Book" +[build] +extra-watch-dirs = ["po"] + +[preprocessor.gettext] +after = ["links"] + [output.html] git-repository-url = "https://github.com/rust-embedded/book" diff --git a/po/da.po b/po/da.po new file mode 100644 index 00000000..35b9c3bc --- /dev/null +++ b/po/da.po @@ -0,0 +1,9435 @@ +msgid "" +msgstr "" +"Project-Id-Version: The Embedded Rust Book\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: 2023-05-16 10:58+0200\n" +"Last-Translator: Martin Geisler \n" +"Language-Team: Danish \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: da\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: src/SUMMARY.md:12 +msgid "Introduction" +msgstr "Introduktion" + +#: src/SUMMARY.md:13 src/SUMMARY.md:23 +msgid "Hardware" +msgstr "Hardware" + +#: src/SUMMARY.md:14 +msgid "no_std" +msgstr "no_std" + +#: src/SUMMARY.md:15 +msgid "Tooling" +msgstr "Værktøjer" + +#: src/SUMMARY.md:16 +msgid "Installation" +msgstr "Installation" + +#: src/SUMMARY.md:17 +msgid "Linux" +msgstr "Linux" + +#: src/SUMMARY.md:18 +msgid "MacOS" +msgstr "MacOS" + +#: src/SUMMARY.md:19 +msgid "Windows" +msgstr "Windows" + +#: src/SUMMARY.md:20 +msgid "Verify Installation" +msgstr "Verificer installationen" + +#: src/SUMMARY.md:21 +msgid "Getting started" +msgstr "Komme godt igang" + +#: src/SUMMARY.md:22 +msgid "QEMU" +msgstr "QEMU" + +#: src/SUMMARY.md:24 +msgid "Memory-mapped Registers" +msgstr "" + +#: src/SUMMARY.md:25 +msgid "Semihosting" +msgstr "" + +#: src/SUMMARY.md:26 +msgid "Panicking" +msgstr "" + +#: src/SUMMARY.md:27 +msgid "Exceptions" +msgstr "" + +#: src/SUMMARY.md:28 +msgid "Interrupts" +msgstr "" + +#: src/SUMMARY.md:29 +msgid "IO" +msgstr "" + +#: src/SUMMARY.md:30 +msgid "Peripherals" +msgstr "" + +#: src/SUMMARY.md:31 +msgid "A first attempt in Rust" +msgstr "Et første forsøg i Rust" + +#: src/SUMMARY.md:32 +msgid "The Borrow Checker" +msgstr "" + +#: src/SUMMARY.md:33 +msgid "Singletons" +msgstr "" + +#: src/SUMMARY.md:34 +msgid "Static Guarantees" +msgstr "Statiske garantier" + +#: src/SUMMARY.md:35 +msgid "Typestate Programming" +msgstr "" + +#: src/SUMMARY.md:36 +msgid "Peripherals as State Machines" +msgstr "" + +#: src/SUMMARY.md:37 +msgid "Design Contracts" +msgstr "Designkontrakter" + +#: src/SUMMARY.md:38 +msgid "Zero Cost Abstractions" +msgstr "" + +#: src/SUMMARY.md:39 +msgid "Portability" +msgstr "" + +#: src/SUMMARY.md:40 +msgid "Concurrency" +msgstr "" + +#: src/SUMMARY.md:41 +msgid "Collections" +msgstr "" + +#: src/SUMMARY.md:42 +msgid "Design Patterns" +msgstr "" + +#: src/SUMMARY.md:43 +msgid "HALs" +msgstr "" + +#: src/SUMMARY.md:44 +msgid "Checklist" +msgstr "" + +#: src/SUMMARY.md:45 +msgid "Naming" +msgstr "" + +#: src/SUMMARY.md:46 src/SUMMARY.md:51 +msgid "Interoperability" +msgstr "" + +#: src/SUMMARY.md:47 +msgid "Predictability" +msgstr "" + +#: src/SUMMARY.md:48 +msgid "GPIO" +msgstr "" + +#: src/SUMMARY.md:49 +msgid "Tips for embedded C developers" +msgstr "" + +#: src/SUMMARY.md:52 +msgid "A little C with your Rust" +msgstr "" + +#: src/SUMMARY.md:53 +msgid "A little Rust with your C" +msgstr "" + +#: src/SUMMARY.md:54 +msgid "Unsorted topics" +msgstr "" + +#: src/SUMMARY.md:55 +msgid "Optimizations: The speed size tradeoff" +msgstr "" + +#: src/SUMMARY.md:56 +msgid "Performing Math Functionality" +msgstr "" + +#: src/SUMMARY.md:60 +msgid "Appendix A: Glossary" +msgstr "" + +#: src/intro/index.md:1 +msgid "# Introduction" +msgstr "# Introduktion" + +#: src/intro/index.md:3 +msgid "" +"Welcome to The Embedded Rust Book: An introductory book about using the " +"Rust\n" +"Programming Language on \"Bare Metal\" embedded systems, such as " +"Microcontrollers." +msgstr "" +"Velkommen til The Embedded Rust Book: en begynderbog om at bruge\n" +"programmeringssproget Rust på \"bare metal\" indlejrede systemer, såsom\n" +"mikrocontrollere." + +#: src/intro/index.md:6 +msgid "## Who Embedded Rust is For" +msgstr "## Hvem er indlejret Rust for" + +#: src/intro/index.md:7 +msgid "" +"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.\n" +"(See also [Who Rust Is For](https://doc.rust-lang.org/book/ch00-00-" +"introduction.html))" +msgstr "" +"Indlejret Rust er for enhver som har lyst til at lave indlejret\n" +"programmering og samtidig udnytte de højniveaus koncepter og\n" +"sikkerhedsgarantier som Rust sproger tilbyder." + +#: src/intro/index.md:10 +msgid "## Scope" +msgstr "" + +#: src/intro/index.md:12 +msgid "The goals of this book are:" +msgstr "Målene for denne bog er:" + +#: src/intro/index.md:14 +msgid "" +"* Get developers up to speed with embedded Rust development. i.e. How to " +"set\n" +" up a development environment.\n" +"\n" +"* Share *current* best practices about using Rust for embedded development. " +"i.e.\n" +" How to best use Rust language features to write more correct embedded\n" +" software.\n" +"\n" +"* Serve as a cookbook in some cases. e.g. How do I mix C and Rust in a " +"single\n" +" project?" +msgstr "" + +#: src/intro/index.md:24 +msgid "" +"This book tries to be as general as possible but to make things easier for " +"both\n" +"the readers and the writers it uses the ARM Cortex-M architecture in all " +"its\n" +"examples. However, the book doesn't assume that the reader is familiar with " +"this\n" +"particular architecture and explains details particular to this " +"architecture\n" +"where required." +msgstr "" + +#: src/intro/index.md:30 +msgid "## Who This Book is For" +msgstr "## Hvem denne bog er for" + +#: src/intro/index.md:31 +msgid "" +"This book caters towards people with either some embedded background or some " +"Rust background, however we believe\n" +"everybody curious about embedded Rust programming can get something out of " +"this book. For those without any prior knowledge\n" +"we suggest you read the \"Assumptions and Prerequisites\" section and catch " +"up on missing knowledge to get more out of the book\n" +"and improve your reading experience. You can check out the \"Other " +"Resources\" section to find resources on topics\n" +"you might want to catch up on." +msgstr "" + +#: src/intro/index.md:37 +msgid "### Assumptions and Prerequisites" +msgstr "" + +#: src/intro/index.md:39 +msgid "" +"* You are comfortable using the Rust Programming Language, and have " +"written,\n" +" run, and debugged Rust applications on a desktop environment. You should " +"also\n" +" be familiar with the idioms of the [2018 edition] as this book targets\n" +" Rust 2018." +msgstr "" + +#: src/intro/index.md:46 +msgid "" +"* You are comfortable developing and debugging embedded systems in another\n" +" language such as C, C++, or Ada, and are familiar with concepts such as:\n" +" * Cross Compilation\n" +" * Memory Mapped Peripherals\n" +" * Interrupts\n" +" * Common interfaces such as I2C, SPI, Serial, etc." +msgstr "" + +#: src/intro/index.md:53 +msgid "### Other Resources" +msgstr "" + +#: src/intro/index.md:54 +msgid "" +"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." +msgstr "" + +#: src/intro/index.md:56 +msgid "" +"| Topic | Resource | Description |\n" +"|--------------|----------|-------------|\n" +"| Rust | [Rust Book](https://doc.rust-lang.org/book/) | If you are " +"not yet comfortable with Rust, we highly suggest reading this book. |\n" +"| 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 |\n" +"| Rust, Embedded | [Embedded Rust Bookshelf](https://docs.rust-embedded.org) " +"| Here you can find several other resources provided by Rust's Embedded " +"Working Group. |\n" +"| Rust, Embedded | [Embedonomicon](https://docs.rust-embedded.org/" +"embedonomicon/) | The nitty gritty details when doing embedded programming " +"in Rust. |\n" +"| Rust, Embedded | [embedded FAQ](https://docs.rust-embedded.org/faq.html) | " +"Frequently asked questions about Rust in an embedded context. |\n" +"| 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 |\n" +"| Interrupts | [Interrupt](https://en.wikipedia.org/wiki/Interrupt) | - |\n" +"| Memory-mapped IO/Peripherals | [Memory-mapped I/O](https://en.wikipedia." +"org/wiki/Memory-mapped_I/O) | - |\n" +"| 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) | " +"- |" +msgstr "" + +#: src/intro/index.md:68 +msgid "### Translations" +msgstr "" + +#: src/intro/index.md:70 +msgid "" +"This book has been translated by generous volunteers. If you would like " +"your\n" +"translation listed here, please open a PR to add it." +msgstr "" + +#: src/intro/index.md:73 +msgid "" +"* [Japanese](https://tomoyuki-nakabayashi.github.io/book/)\n" +" ([repository](https://github.com/tomoyuki-nakabayashi/book))\n" +"\n" +"* [Chinese](https://xxchang.github.io/book/)\n" +" ([repository](https://github.com/XxChang/book))" +msgstr "" + +#: src/intro/index.md:79 +msgid "## How to Use This Book" +msgstr "" + +#: src/intro/index.md:81 +msgid "" +"This book generally assumes that you’re reading it front-to-back. Later\n" +"chapters build on concepts in earlier chapters, and earlier chapters may\n" +"not dig into details on a topic, revisiting the topic in a later chapter." +msgstr "" + +#: src/intro/index.md:85 +msgid "" +"This book will be using the [STM32F3DISCOVERY] development board from\n" +"STMicroelectronics for the majority of the examples contained within. This " +"board\n" +"is based on the ARM Cortex-M architecture, and while basic functionality is\n" +"the same across most CPUs based on this architecture, peripherals and other\n" +"implementation details of Microcontrollers are different between different\n" +"vendors, and often even different between Microcontroller families from the " +"same\n" +"vendor." +msgstr "" + +#: src/intro/index.md:93 +msgid "" +"For this reason, we suggest purchasing the [STM32F3DISCOVERY] development " +"board\n" +"for the purpose of following the examples in this book." +msgstr "" + +#: src/intro/index.md:98 +msgid "## Contributing to This Book" +msgstr "" + +#: src/intro/index.md:100 +msgid "" +"The work on this book is coordinated in [this repository] and is mainly\n" +"developed by the [resources team]." +msgstr "" + +#: src/intro/index.md:106 +msgid "" +"If you have trouble following the instructions in this book or find that " +"some\n" +"section of the book is not clear enough or hard to follow then that's a bug " +"and\n" +"it should be reported in [the issue tracker] of this book." +msgstr "" + +#: src/intro/index.md:112 +msgid "Pull requests fixing typos and adding new content are very welcome!" +msgstr "" + +#: src/intro/index.md:114 +msgid "## Re-using this material" +msgstr "" + +#: src/intro/index.md:116 +msgid "This book is distributed under the following licenses:" +msgstr "" + +#: src/intro/index.md:118 +msgid "" +"* 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].\n" +"* 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." +msgstr "" + +#: src/intro/index.md:125 +msgid "TL;DR: If you want to use our text or images in your work, you need to:" +msgstr "" + +#: src/intro/index.md:127 +msgid "" +"* Give the appropriate credit (i.e. mention this book on your slide, and " +"provide a link to the relevant page)\n" +"* Provide a link to the [CC-BY-SA v4.0] licence\n" +"* Indicate if you have changed the material in any way, and make any changes " +"to our material available under the same licence" +msgstr "" + +#: src/intro/index.md:131 +msgid "Also, please do let us know if you find this book useful!" +msgstr "" + +#: src/intro/hardware.md:1 +msgid "# Meet Your Hardware" +msgstr "" + +#: src/intro/hardware.md:3 +msgid "Let's get familiar with the hardware we'll be working with." +msgstr "" + +#: src/intro/hardware.md:5 +msgid "## STM32F3DISCOVERY (the \"F3\")" +msgstr "" + +#: src/intro/hardware.md:7 +msgid "" +"

\n" +"\n" +"

" +msgstr "" + +#: src/intro/hardware.md:11 +msgid "What does this board contain?" +msgstr "" + +#: src/intro/hardware.md:13 +msgid "" +"- A [STM32F303VCT6](https://www.st.com/en/microcontrollers/stm32f303vc.html) " +"microcontroller. This microcontroller has\n" +" - A single-core ARM Cortex-M4F processor with hardware support for single-" +"precision floating point\n" +" operations and a maximum clock frequency of 72 MHz.\n" +"\n" +" - 256 KiB of \"Flash\" memory. (1 KiB = 10**24** bytes)\n" +"\n" +" - 48 KiB of RAM.\n" +"\n" +" - A variety of integrated peripherals such as timers, I2C, SPI and USART.\n" +"\n" +" - General purpose Input Output (GPIO) and other types of pins accessible " +"through the two rows of headers along side the board.\n" +" \n" +" - A USB interface accessible through the USB port labeled \"USB USER\".\n" +"\n" +"- An [accelerometer](https://en.wikipedia.org/wiki/Accelerometer) as part of " +"the [LSM303DLHC](https://www.st.com/en/mems-and-sensors/lsm303dlhc.html) " +"chip.\n" +"\n" +"- A [magnetometer](https://en.wikipedia.org/wiki/Magnetometer) as part of " +"the [LSM303DLHC](https://www.st.com/en/mems-and-sensors/lsm303dlhc.html) " +"chip.\n" +"\n" +"- A [gyroscope](https://en.wikipedia.org/wiki/Gyroscope) as part of the " +"[L3GD20](https://www.pololu.com/file/0J563/L3GD20.pdf) chip.\n" +"\n" +"- 8 user LEDs arranged in the shape of a compass.\n" +"\n" +"- 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\"." +msgstr "" + +#: src/intro/hardware.md:37 +msgid "" +"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." +msgstr "" + +#: src/intro/hardware.md:39 +msgid "" +"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)" +msgstr "" + +#: src/intro/no-std.md:1 +msgid "# A `no_std` Rust Environment" +msgstr "" + +#: src/intro/no-std.md:3 +msgid "" +"The term Embedded Programming is used for a wide range of different classes " +"of programming.\n" +"Ranging from programming 8-Bit MCUs (like the [ST72325xx](https://www.st.com/" +"resource/en/datasheet/st72325j6.pdf))\n" +"with just a few KB of RAM and ROM, up to systems like the Raspberry Pi\n" +"([Model B 3+](https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications)) " +"which has a 32/64-bit\n" +"4-core Cortex-A53 @ 1.4 GHz and 1GB of RAM. Different restrictions/" +"limitations will apply when writing code\n" +"depending on what kind of target and use case you have." +msgstr "" + +#: src/intro/no-std.md:10 +msgid "There are two general Embedded Programming classifications:" +msgstr "" + +#: src/intro/no-std.md:12 +msgid "## Hosted Environments" +msgstr "" + +#: src/intro/no-std.md:13 +msgid "" +"These kinds of environments are close to a normal PC environment.\n" +"What this means is that you are provided with a System Interface [E.G. POSIX]" +"(https://en.wikipedia.org/wiki/POSIX)\n" +"that provides you with primitives to interact with various systems, such as " +"file systems, networking, memory management, threads, etc.\n" +"Standard libraries in turn usually depend on these primitives to implement " +"their functionality.\n" +"You may also have some sort of sysroot and restrictions on RAM/ROM-usage, " +"and perhaps some\n" +"special HW or I/Os. Overall it feels like coding on a special-purpose PC " +"environment." +msgstr "" + +#: src/intro/no-std.md:20 +msgid "## Bare Metal Environments" +msgstr "" + +#: src/intro/no-std.md:21 +msgid "" +"In a bare metal environment no code has been loaded before your program.\n" +"Without the software provided by an OS we can not load the standard " +"library.\n" +"Instead the program, along with the crates it uses, can only use the " +"hardware (bare metal) to run.\n" +"To prevent rust from loading the standard library use `no_std`.\n" +"The platform-agnostic parts of the standard library are available through " +"[libcore](https://doc.rust-lang.org/core/).\n" +"libcore also excludes things which are not always desirable in an embedded " +"environment.\n" +"One of these things is a memory allocator for dynamic memory allocation.\n" +"If you require this or any other functionalities there are often crates " +"which provide these." +msgstr "" + +#: src/intro/no-std.md:30 +msgid "### The libstd Runtime" +msgstr "" + +#: src/intro/no-std.md:31 +msgid "" +"As mentioned before using [libstd](https://doc.rust-lang.org/std/) requires " +"some sort of system integration, but this is not only because\n" +"[libstd](https://doc.rust-lang.org/std/) is just providing a common way of " +"accessing OS abstractions, it also provides a runtime.\n" +"This runtime, among other things, takes care of setting up stack overflow " +"protection, processing command line arguments,\n" +"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." +msgstr "" + +#: src/intro/no-std.md:36 +msgid "## Summary" +msgstr "" + +#: src/intro/no-std.md:37 +msgid "" +"`#![no_std]` is a crate-level attribute that indicates that the crate will " +"link to the core-crate instead of the std-crate.\n" +"The [libcore](https://doc.rust-lang.org/core/) crate in turn is a platform-" +"agnostic subset of the std crate\n" +"which makes no assumptions about the system the program will run on.\n" +"As such, it provides APIs for language primitives like floats, strings and " +"slices, as well as APIs that expose processor features\n" +"like atomic operations and SIMD instructions. However it lacks APIs for " +"anything that involves platform integration.\n" +"Because of these properties no\\_std and [libcore](https://doc.rust-lang.org/" +"core/) code can be used for any kind of\n" +"bootstrapping (stage 0) code like bootloaders, firmware or kernels." +msgstr "" + +#: src/intro/no-std.md:45 +msgid "### Overview" +msgstr "" + +#: src/intro/no-std.md:47 +msgid "" +"| feature | no\\_std | std " +"|\n" +"|-----------------------------------------------------------|--------|-----|\n" +"| heap (dynamic memory) | * | ✓ " +"|\n" +"| collections (Vec, BTreeMap, etc) | ** | ✓ " +"|\n" +"| stack overflow protection | ✘ | ✓ " +"|\n" +"| runs init code before main | ✘ | ✓ " +"|\n" +"| libstd available | ✘ | ✓ " +"|\n" +"| libcore available | ✓ | ✓ " +"|\n" +"| writing firmware, kernel, or bootloader code | ✓ | ✘ |" +msgstr "" + +#: src/intro/no-std.md:57 +msgid "" +"* Only if you use the `alloc` crate and use a suitable allocator like [alloc-" +"cortex-m]." +msgstr "" + +#: src/intro/no-std.md:59 +msgid "" +"** Only if you use the `collections` crate and configure a global default " +"allocator." +msgstr "" + +#: src/intro/no-std.md:61 +msgid "" +"** HashMap and HashSet are not available due to a lack of a secure random " +"number generator." +msgstr "" + +#: src/intro/no-std.md:65 +msgid "## See Also" +msgstr "" + +#: src/intro/no-std.md:66 +msgid "" +"* [RFC-1184](https://github.com/rust-lang/rfcs/blob/master/text/1184-" +"stabilize-no_std.md)" +msgstr "" + +#: src/intro/tooling.md:1 +msgid "# Tooling" +msgstr "" + +#: src/intro/tooling.md:3 +msgid "" +"Dealing with microcontrollers involves using several different tools as " +"we'll be\n" +"dealing with an architecture different than your laptop's and we'll have to " +"run\n" +"and debug programs on a *remote* device." +msgstr "" + +#: src/intro/tooling.md:7 +msgid "" +"We'll use all the tools listed below. Any recent version should work when a\n" +"minimum version is not specified, but we have listed the versions we have\n" +"tested." +msgstr "" + +#: src/intro/tooling.md:11 +msgid "" +"- Rust 1.31, 1.31-beta, or a newer toolchain PLUS ARM Cortex-M compilation\n" +" support.\n" +"- [`cargo-binutils`](https://github.com/rust-embedded/cargo-binutils) " +"~0.1.4\n" +"- [`qemu-system-arm`](https://www.qemu.org/). Tested versions: 3.0.0\n" +"- OpenOCD >=0.8. Tested versions: v0.9.0 and v0.10.0\n" +"- GDB with ARM support. Version 7.12 or newer highly recommended. Tested\n" +" versions: 7.10, 7.11, 7.12 and 8.1\n" +"- [`cargo-generate`](https://github.com/ashleygwilliams/cargo-generate) or " +"`git`.\n" +" These tools are optional but will make it easier to follow along with the " +"book." +msgstr "" + +#: src/intro/tooling.md:21 +msgid "" +"The text below explains why we are using these tools. Installation " +"instructions\n" +"can be found on the next page." +msgstr "" + +#: src/intro/tooling.md:24 +msgid "## `cargo-generate` OR `git`" +msgstr "" + +#: src/intro/tooling.md:26 +msgid "" +"Bare metal programs are non-standard (`no_std`) Rust programs that require " +"some\n" +"adjustments to the linking process in order to get the memory layout of the " +"program\n" +"right. This requires some additional files (like linker scripts) and \n" +"settings (like linker flags). We have packaged those for you in a template\n" +"such that you only need to fill in the missing information (such as the " +"project name and the\n" +"characteristics of your target hardware)." +msgstr "" + +#: src/intro/tooling.md:33 +msgid "" +"Our template is compatible with `cargo-generate`: a Cargo subcommand for\n" +"creating new Cargo projects from templates. You can also download the\n" +"template using `git`, `curl`, `wget`, or your web browser." +msgstr "" + +#: src/intro/tooling.md:37 +msgid "## `cargo-binutils`" +msgstr "" + +#: src/intro/tooling.md:39 +msgid "" +"`cargo-binutils` is a collection of Cargo subcommands that make it easy to " +"use\n" +"the LLVM tools that are shipped with the Rust toolchain. These tools include " +"the\n" +"LLVM versions of `objdump`, `nm` and `size` and are used for inspecting\n" +"binaries." +msgstr "" + +#: src/intro/tooling.md:44 +msgid "" +"The advantage of using these tools over GNU binutils is that (a) installing " +"the\n" +"LLVM tools is the same one-command installation (`rustup component add\n" +"llvm-tools-preview`) regardless of your OS and (b) tools like `objdump` " +"support\n" +"all the architectures that `rustc` supports -- from ARM to x86_64 -- " +"because\n" +"they both share the same LLVM backend." +msgstr "" + +#: src/intro/tooling.md:50 +msgid "## `qemu-system-arm`" +msgstr "" + +#: src/intro/tooling.md:52 +msgid "" +"QEMU is an emulator. In this case we use the variant that can fully emulate " +"ARM\n" +"systems. We use QEMU to run embedded programs on the host. Thanks to this " +"you\n" +"can follow some parts of this book even if you don't have any hardware with " +"you!" +msgstr "" + +#: src/intro/tooling.md:56 +msgid "## GDB" +msgstr "" + +#: src/intro/tooling.md:58 +msgid "" +"A debugger is a very important component of embedded development as you may " +"not\n" +"always have the luxury to log stuff to the host console. In some cases, you " +"may\n" +"not even have LEDs to blink on your hardware!" +msgstr "" + +#: src/intro/tooling.md:62 +msgid "" +"In general, LLDB works as well as GDB when it comes to debugging but we " +"haven't\n" +"found an LLDB counterpart to GDB's `load` command, which uploads the program " +"to\n" +"the target hardware, so currently we recommend that you use GDB." +msgstr "" + +#: src/intro/tooling.md:66 src/intro/install/windows.md:17 +msgid "## OpenOCD" +msgstr "" + +#: src/intro/tooling.md:68 +msgid "" +"GDB isn't able to communicate directly with the ST-Link debugging hardware " +"on\n" +"your STM32F3DISCOVERY development board. It needs a translator and the Open\n" +"On-Chip Debugger, OpenOCD, is that translator. OpenOCD is a program that " +"runs\n" +"on your laptop/PC and translates between GDB's TCP/IP based remote debug\n" +"protocol and ST-Link's USB based protocol." +msgstr "" + +#: src/intro/tooling.md:74 +msgid "" +"OpenOCD also performs other important work as part of its translation for " +"the\n" +"debugging of the ARM Cortex-M based microcontroller on your " +"STM32F3DISCOVERY\n" +"development board:" +msgstr "" + +#: src/intro/tooling.md:77 +msgid "" +"* It knows how to interact with the memory mapped registers used by the ARM\n" +" CoreSight debug peripheral. It is these CoreSight registers that allow " +"for:\n" +" * Breakpoint/Watchpoint manipulation\n" +" * Reading and writing of the CPU registers\n" +" * Detecting when the CPU has been halted for a debug event\n" +" * Continuing CPU execution after a debug event has been encountered\n" +" * etc.\n" +"* It also knows how to erase and write to the microcontroller's FLASH" +msgstr "" + +#: src/intro/install.md:1 +msgid "# Installing the tools" +msgstr "" + +#: src/intro/install.md:3 +msgid "" +"This page contains OS-agnostic installation instructions for a few of the " +"tools:" +msgstr "" + +#: src/intro/install.md:5 +msgid "### Rust Toolchain" +msgstr "" + +#: src/intro/install.md:7 +msgid "" +"Install rustup by following the instructions at [https://rustup.rs](https://" +"rustup.rs)." +msgstr "" + +#: src/intro/install.md:9 +msgid "" +"**NOTE** Make sure you have a compiler version equal to or newer than " +"`1.31`. `rustc\n" +"-V` should return a date newer than the one shown below." +msgstr "" + +#: src/intro/install.md:12 +msgid "" +"``` text\n" +"$ rustc -V\n" +"rustc 1.31.1 (b6c32da9b 2018-12-18)\n" +"```" +msgstr "" + +#: src/intro/install.md:17 +msgid "" +"For bandwidth and disk usage concerns the default installation only " +"supports\n" +"native compilation. To add cross compilation support for the ARM Cortex-M\n" +"architectures choose one of the following compilation targets. For the " +"STM32F3DISCOVERY\n" +"board used for the examples in this book, use the `thumbv7em-none-eabihf` " +"target." +msgstr "" + +#: src/intro/install.md:22 +msgid "Cortex-M0, M0+, and M1 (ARMv6-M architecture):" +msgstr "" + +#: src/intro/install.md:23 +msgid "" +"``` console\n" +"rustup target add thumbv6m-none-eabi\n" +"```" +msgstr "" + +#: src/intro/install.md:27 +msgid "Cortex-M3 (ARMv7-M architecture):" +msgstr "" + +#: src/intro/install.md:28 src/start/qemu.md:160 +msgid "" +"``` console\n" +"rustup target add thumbv7m-none-eabi\n" +"```" +msgstr "" + +#: src/intro/install.md:32 +msgid "" +"Cortex-M4 and M7 without hardware floating point (ARMv7E-M architecture):" +msgstr "" + +#: src/intro/install.md:33 +msgid "" +"``` console\n" +"rustup target add thumbv7em-none-eabi\n" +"```" +msgstr "" + +#: src/intro/install.md:37 +msgid "" +"Cortex-M4F and M7F with hardware floating point (ARMv7E-M architecture):" +msgstr "" + +#: src/intro/install.md:38 +msgid "" +"``` console\n" +"rustup target add thumbv7em-none-eabihf\n" +"```" +msgstr "" + +#: src/intro/install.md:42 +msgid "Cortex-M23 (ARMv8-M architecture):" +msgstr "" + +#: src/intro/install.md:43 +msgid "" +"``` console\n" +"rustup target add thumbv8m.base-none-eabi\n" +"```" +msgstr "" + +#: src/intro/install.md:47 +msgid "Cortex-M33 and M35P (ARMv8-M architecture):" +msgstr "" + +#: src/intro/install.md:48 +msgid "" +"``` console\n" +"rustup target add thumbv8m.main-none-eabi\n" +"```" +msgstr "" + +#: src/intro/install.md:52 +msgid "" +"Cortex-M33F and M35PF with hardware floating point (ARMv8-M architecture):" +msgstr "" + +#: src/intro/install.md:53 +msgid "" +"``` console\n" +"rustup target add thumbv8m.main-none-eabihf\n" +"```" +msgstr "" + +#: src/intro/install.md:58 +msgid "### `cargo-binutils`" +msgstr "" + +#: src/intro/install.md:60 +msgid "" +"``` text\n" +"cargo install cargo-binutils\n" +"\n" +"rustup component add llvm-tools-preview\n" +"```" +msgstr "" + +#: src/intro/install.md:65 +msgid "" +"WINDOWS: prerequisite C++ Build Tools for Visual Studio 2019 is installed. " +"https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?" +"sku=BuildTools&rel=16 " +msgstr "" + +#: src/intro/install.md:66 +msgid "### `cargo-generate`" +msgstr "" + +#: src/intro/install.md:68 +msgid "We'll use this later to generate a project from a template." +msgstr "" + +#: src/intro/install.md:70 +msgid "" +"``` console\n" +"cargo install cargo-generate\n" +"```" +msgstr "" + +#: src/intro/install.md:74 +msgid "" +"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." +msgstr "" + +#: src/intro/install.md:76 +msgid "### OS-Specific Instructions" +msgstr "" + +#: src/intro/install.md:78 +msgid "Now follow the instructions specific to the OS you are using:" +msgstr "" + +#: src/intro/install.md:80 +msgid "" +"- [Linux](install/linux.md)\n" +"- [Windows](install/windows.md)\n" +"- [macOS](install/macos.md)" +msgstr "" + +#: src/intro/install/linux.md:1 +msgid "# Linux" +msgstr "" + +#: src/intro/install/linux.md:3 +msgid "Here are the installation commands for a few Linux distributions." +msgstr "" + +#: src/intro/install/linux.md:5 +msgid "## Packages" +msgstr "" + +#: src/intro/install/linux.md:7 +msgid "- Ubuntu 18.04 or newer / Debian stretch or newer" +msgstr "" + +#: src/intro/install/linux.md:9 +msgid "" +"> **NOTE** `gdb-multiarch` is the GDB command you'll use to debug your ARM\n" +"> Cortex-M programs" +msgstr "" + +#: src/intro/install/linux.md:12 +msgid "" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"" +msgstr "" + +#: src/intro/install/linux.md:22 +msgid "" +"``` console\n" +"sudo apt install gdb-multiarch openocd qemu-system-arm\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:26 +msgid "- Ubuntu 14.04 and 16.04" +msgstr "" + +#: src/intro/install/linux.md:28 +msgid "" +"> **NOTE** `arm-none-eabi-gdb` is the GDB command you'll use to debug your " +"ARM\n" +"> Cortex-M programs" +msgstr "" + +#: src/intro/install/linux.md:31 +msgid "" +"\n" +"\n" +"\n" +"" +msgstr "" + +#: src/intro/install/linux.md:36 +msgid "" +"``` console\n" +"sudo apt install gdb-arm-none-eabi openocd qemu-system-arm\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:40 +msgid "- Fedora 27 or newer" +msgstr "" + +#: src/intro/install/linux.md:42 +msgid "" +"\n" +"\n" +"\n" +"" +msgstr "" + +#: src/intro/install/linux.md:47 +msgid "" +"``` console\n" +"sudo dnf install gdb openocd qemu-system-arm\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:51 +msgid "- Arch Linux" +msgstr "" + +#: src/intro/install/linux.md:53 +msgid "" +"> **NOTE** `arm-none-eabi-gdb` is the GDB command you'll use to debug ARM\n" +"> Cortex-M programs" +msgstr "" + +#: src/intro/install/linux.md:56 +msgid "" +"``` console\n" +"sudo pacman -S arm-none-eabi-gdb qemu-arch-extra openocd\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:60 +msgid "## udev rules" +msgstr "" + +#: src/intro/install/linux.md:62 +msgid "" +"This rule lets you use OpenOCD with the Discovery board without root " +"privilege." +msgstr "" + +#: src/intro/install/linux.md:64 +msgid "" +"Create the file `/etc/udev/rules.d/70-st-link.rules` with the contents shown " +"below." +msgstr "" + +#: src/intro/install/linux.md:66 +msgid "" +"``` text\n" +"# STM32F3DISCOVERY rev A/B - ST-LINK/V2\n" +"ATTRS{idVendor}==\"0483\", ATTRS{idProduct}==\"3748\", TAG+=\"uaccess\"\n" +"\n" +"# STM32F3DISCOVERY rev C+ - ST-LINK/V2-1\n" +"ATTRS{idVendor}==\"0483\", ATTRS{idProduct}==\"374b\", TAG+=\"uaccess\"\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:74 +msgid "Then reload all the udev rules with:" +msgstr "" + +#: src/intro/install/linux.md:76 +msgid "" +"``` console\n" +"sudo udevadm control --reload-rules\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:80 +msgid "" +"If you had the board plugged to your laptop, unplug it and then plug it " +"again." +msgstr "" + +#: src/intro/install/linux.md:82 +msgid "You can check the permissions by running this command:" +msgstr "" + +#: src/intro/install/linux.md:84 +msgid "" +"``` console\n" +"lsusb\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:88 +msgid "Which should show something like" +msgstr "" + +#: src/intro/install/linux.md:90 +msgid "" +"```text\n" +"(..)\n" +"Bus 001 Device 018: ID 0483:374b STMicroelectronics ST-LINK/V2.1\n" +"(..)\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:96 +msgid "" +"Take note of the bus and device numbers. Use those numbers to create a path " +"like\n" +"`/dev/bus/usb//`. Then use this path like so:" +msgstr "" + +#: src/intro/install/linux.md:99 +msgid "" +"``` console\n" +"ls -l /dev/bus/usb/001/018\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:103 +msgid "" +"```text\n" +"crw-------+ 1 root root 189, 17 Sep 13 12:34 /dev/bus/usb/001/018\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:107 +msgid "" +"```console\n" +"getfacl /dev/bus/usb/001/018 | grep user\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:111 +msgid "" +"```text\n" +"user::rw-\n" +"user:you:rw-\n" +"```" +msgstr "" + +#: src/intro/install/linux.md:116 +msgid "" +"The `+` appended to permissions indicates the existence of an extended\n" +"permission. The `getfacl` command tells the user `you` can make use of\n" +"this device." +msgstr "" + +#: src/intro/install/linux.md:120 +msgid "Now, go to the [next section]." +msgstr "" + +#: src/intro/install/macos.md:1 +msgid "# macOS" +msgstr "" + +#: src/intro/install/macos.md:3 +msgid "All the tools can be installed using [Homebrew] or [MacPorts]:" +msgstr "" + +#: src/intro/install/macos.md:8 +msgid "## Install tools with [Homebrew]" +msgstr "" + +#: src/intro/install/macos.md:10 +msgid "" +"``` text\n" +"$ # GDB\n" +"$ brew install armmbed/formulae/arm-none-eabi-gcc\n" +"\n" +"$ # OpenOCD\n" +"$ brew install openocd\n" +"\n" +"$ # QEMU\n" +"$ brew install qemu\n" +"```" +msgstr "" + +#: src/intro/install/macos.md:21 +msgid "" +"> **NOTE** If OpenOCD crashes you may need to install the latest version " +"using: " +msgstr "" + +#: src/intro/install/macos.md:22 +msgid "" +"```text\n" +"$ brew install --HEAD openocd\n" +"```" +msgstr "" + +#: src/intro/install/macos.md:26 +msgid "## Install tools with [MacPorts]" +msgstr "" + +#: src/intro/install/macos.md:28 +msgid "" +"``` text\n" +"$ # GDB\n" +"$ sudo port install arm-none-eabi-gcc\n" +"\n" +"$ # OpenOCD\n" +"$ sudo port install openocd\n" +"\n" +"$ # QEMU\n" +"$ sudo port install qemu\n" +"```" +msgstr "" + +#: src/intro/install/macos.md:41 src/intro/install/windows.md:48 +msgid "That's all! Go to the [next section]." +msgstr "" + +#: src/intro/install/windows.md:1 +msgid "# Windows" +msgstr "" + +#: src/intro/install/windows.md:3 +msgid "## `arm-none-eabi-gdb`" +msgstr "" + +#: src/intro/install/windows.md:5 +msgid "" +"ARM provides `.exe` installers for Windows. Grab one from [here][gcc], and " +"follow the instructions.\n" +"Just before the installation process finishes tick/select the \"Add path to " +"environment variable\"\n" +"option. Then verify that the tools are in your `%PATH%`:" +msgstr "" + +#: src/intro/install/windows.md:9 +msgid "" +"``` text\n" +"$ arm-none-eabi-gdb -v\n" +"GNU gdb (GNU Tools for Arm Embedded Processors 7-2018-q2-update) " +"8.1.0.20180315-git\n" +"(..)\n" +"```" +msgstr "" + +#: src/intro/install/windows.md:19 +msgid "" +"There's no official binary release of OpenOCD for Windows but if you're not " +"in the mood to compile\n" +"it yourself, the xPack project provides a binary distribution, [here]" +"[openocd]. Follow the\n" +"provided installation instructions. Then update your `%PATH%` environment " +"variable to\n" +"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\\`,\n" +"if you've been using the easy install) " +msgstr "" + +#: src/intro/install/windows.md:27 +msgid "Verify that OpenOCD is in your `%PATH%` with:" +msgstr "" + +#: src/intro/install/windows.md:29 +msgid "" +"``` text\n" +"$ openocd -v\n" +"Open On-Chip Debugger 0.10.0\n" +"(..)\n" +"```" +msgstr "" + +#: src/intro/install/windows.md:35 +msgid "## QEMU" +msgstr "" + +#: src/intro/install/windows.md:37 +msgid "Grab QEMU from [the official website][qemu]." +msgstr "" + +#: src/intro/install/windows.md:41 +msgid "## ST-LINK USB driver" +msgstr "" + +#: src/intro/install/windows.md:43 +msgid "" +"You'll also need to install [this USB driver] or OpenOCD won't work. Follow " +"the installer\n" +"instructions and make sure you install the right version (32-bit or 64-bit) " +"of the driver." +msgstr "" + +#: src/intro/install/verify.md:1 +msgid "# Verify Installation" +msgstr "" + +#: src/intro/install/verify.md:3 +msgid "" +"In this section we check that some of the required tools / drivers have " +"been\n" +"correctly installed and configured." +msgstr "" + +#: src/intro/install/verify.md:6 +msgid "" +"Connect your laptop / PC to the discovery board using a micro USB cable. " +"The\n" +"discovery board has two USB connectors; use the one labeled \"USB ST-LINK\" " +"that\n" +"sits on the center of the edge of the board." +msgstr "" + +#: src/intro/install/verify.md:10 +msgid "" +"Also check that the ST-LINK header is populated. See the picture below; the\n" +"ST-LINK header is highlighted." +msgstr "" + +#: src/intro/install/verify.md:13 +msgid "" +"

\n" +"\n" +"

" +msgstr "" + +#: src/intro/install/verify.md:17 +msgid "Now run the following command:" +msgstr "" + +#: src/intro/install/verify.md:19 +msgid "" +"``` console\n" +"openocd -f interface/stlink.cfg -f target/stm32f3x.cfg\n" +"```" +msgstr "" + +#: src/intro/install/verify.md:23 +msgid "" +"> **NOTE**: Old versions of openocd, including the 0.10.0 release from 2017, " +"do\n" +"> not contain the new (and preferable) `interface/stlink.cfg` file; instead " +"you\n" +"> may need to use `interface/stlink-v2.cfg` or `interface/stlink-v2-1.cfg`." +msgstr "" + +#: src/intro/install/verify.md:27 +msgid "" +"You should get the following output and the program should block the console:" +msgstr "" + +#: src/intro/install/verify.md:29 +msgid "" +"``` text\n" +"Open On-Chip Debugger 0.10.0\n" +"Licensed under GNU GPL v2\n" +"For bug reports, read\n" +" http://openocd.org/doc/doxygen/bugs.html\n" +"Info : auto-selecting first available session transport \"hla_swd\". To " +"override use 'transport select '.\n" +"adapter speed: 1000 kHz\n" +"adapter_nsrst_delay: 100\n" +"Info : The selected transport took over low-level target control. The " +"results might differ compared to plain JTAG/SWD\n" +"none separate\n" +"Info : Unable to match requested speed 1000 kHz, using 950 kHz\n" +"Info : Unable to match requested speed 1000 kHz, using 950 kHz\n" +"Info : clock speed 950 kHz\n" +"Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B\n" +"Info : using stlink api v2\n" +"Info : Target voltage: 2.919881\n" +"Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints\n" +"```" +msgstr "" + +#: src/intro/install/verify.md:48 +msgid "" +"The contents may not match exactly but you should get the last line about\n" +"breakpoints and watchpoints. If you got it then terminate the OpenOCD " +"process\n" +"and move to the [next section]." +msgstr "" + +#: src/intro/install/verify.md:54 +msgid "" +"If you didn't get the \"breakpoints\" line then try one of the following " +"commands." +msgstr "" + +#: src/intro/install/verify.md:56 +msgid "" +"``` console\n" +"openocd -f interface/stlink-v2.cfg -f target/stm32f3x.cfg\n" +"```" +msgstr "" + +#: src/intro/install/verify.md:60 +msgid "" +"``` console\n" +"openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg\n" +"```" +msgstr "" + +#: src/intro/install/verify.md:64 +msgid "" +"If one of those commands works it means you got an old hardware revision of " +"the\n" +"discovery board. That won't be a problem but commit that fact to memory as\n" +"you'll need to configure things a bit differently later on. You can move to " +"the\n" +"[next section]." +msgstr "" + +#: src/intro/install/verify.md:69 +msgid "" +"If none of the commands work as a normal user then try to run them with " +"root\n" +"permission (e.g. `sudo openocd ..`). If the commands do work with root\n" +"permission then check that the [udev rules] have been correctly set." +msgstr "" + +#: src/intro/install/verify.md:75 +msgid "" +"If you have reached this point and OpenOCD is not working please open [an " +"issue]\n" +"and we'll help you out!" +msgstr "" + +#: src/start/index.md:1 +msgid "# Getting Started" +msgstr "" + +#: src/start/index.md:3 +msgid "" +"In this section we'll walk you through the process of writing, building,\n" +"flashing and debugging embedded programs. You will be able to try most of " +"the\n" +"examples without any special hardware as we will show you the basics using\n" +"QEMU, a popular open-source hardware emulator. The only section where " +"hardware\n" +"is required is, naturally enough, the [Hardware](./hardware.md) section,\n" +"where we use OpenOCD to program an [STM32F3DISCOVERY]." +msgstr "" + +#: src/start/qemu.md:1 +msgid "# QEMU" +msgstr "" + +#: src/start/qemu.md:3 +msgid "" +"We'll start writing a program for the [LM3S6965], a Cortex-M3 " +"microcontroller.\n" +"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\n" +"so you don't need to fiddle with hardware in this section and we can focus " +"on\n" +"the tooling and the development process." +msgstr "" + +#: src/start/qemu.md:10 +msgid "" +"**IMPORTANT**\n" +"We'll use the name \"app\" for the project name in this tutorial.\n" +"Whenever you see the word \"app\" you should replace it with the name you " +"selected\n" +"for your project. Or, you could also name your project \"app\" and avoid " +"the\n" +"substitutions." +msgstr "" + +#: src/start/qemu.md:16 +msgid "## Creating a non standard Rust program" +msgstr "" + +#: src/start/qemu.md:18 +msgid "" +"We'll use the [`cortex-m-quickstart`] project template to generate a new\n" +"project from it. The created project will contain a barebone application: a " +"good\n" +"starting point for a new embedded rust application. In addition, the project " +"will\n" +"contain an `examples` directory, with several separate applications, " +"highlighting\n" +"some of the key embedded rust functionality. " +msgstr "" + +#: src/start/qemu.md:26 +msgid "### Using `cargo-generate`" +msgstr "" + +#: src/start/qemu.md:27 +msgid "First install cargo-generate" +msgstr "" + +#: src/start/qemu.md:28 +msgid "" +"```console\n" +"cargo install cargo-generate\n" +"```" +msgstr "" + +#: src/start/qemu.md:31 +msgid "Then generate a new project" +msgstr "" + +#: src/start/qemu.md:32 +msgid "" +"```console\n" +"cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart\n" +"```" +msgstr "" + +#: src/start/qemu.md:36 +msgid "" +"```text\n" +" Project Name: app\n" +" Creating project called `app`...\n" +" Done! New project created /tmp/app\n" +"```" +msgstr "" + +#: src/start/qemu.md:42 +msgid "" +"```console\n" +"cd app\n" +"```" +msgstr "" + +#: src/start/qemu.md:46 +msgid "### Using `git`" +msgstr "" + +#: src/start/qemu.md:48 +msgid "Clone the repository" +msgstr "" + +#: src/start/qemu.md:50 +msgid "" +"```console\n" +"git clone https://github.com/rust-embedded/cortex-m-quickstart app\n" +"cd app\n" +"```" +msgstr "" + +#: src/start/qemu.md:55 +msgid "And then fill in the placeholders in the `Cargo.toml` file" +msgstr "" + +#: src/start/qemu.md:57 +msgid "" +"```toml\n" +"[package]\n" +"authors = [\"{{authors}}\"] # \"{{authors}}\" -> \"John Smith\"\n" +"edition = \"2018\"\n" +"name = \"{{project-name}}\" # \"{{project-name}}\" -> \"app\"\n" +"version = \"0.1.0\"\n" +"\n" +"# ..\n" +"\n" +"[[bin]]\n" +"name = \"{{project-name}}\" # \"{{project-name}}\" -> \"app\"\n" +"test = false\n" +"bench = false\n" +"```" +msgstr "" + +#: src/start/qemu.md:72 +msgid "### Using neither" +msgstr "" + +#: src/start/qemu.md:74 +msgid "" +"Grab the latest snapshot of the `cortex-m-quickstart` template and extract " +"it." +msgstr "" + +#: src/start/qemu.md:76 +msgid "" +"```console\n" +"curl -LO https://github.com/rust-embedded/cortex-m-quickstart/archive/master." +"zip\n" +"unzip master.zip\n" +"mv cortex-m-quickstart-master app\n" +"cd app\n" +"```" +msgstr "" + +#: src/start/qemu.md:83 +msgid "" +"Or you can browse to [`cortex-m-quickstart`], click the green \"Clone or\n" +"download\" button and then click \"Download ZIP\"." +msgstr "" + +#: src/start/qemu.md:86 +msgid "" +"Then fill in the placeholders in the `Cargo.toml` file as done in the " +"second\n" +"part of the \"Using `git`\" version." +msgstr "" + +#: src/start/qemu.md:89 +msgid "## Program Overview" +msgstr "" + +#: src/start/qemu.md:91 +msgid "" +"For convenience here are the most important parts of the source code in `src/" +"main.rs`:" +msgstr "" + +#: src/start/qemu.md:93 +msgid "" +"```rust,ignore\n" +"#![no_std]\n" +"#![no_main]\n" +"\n" +"use panic_halt as _;\n" +"\n" +"use cortex_m_rt::entry;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" loop {\n" +" // your code goes here\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/start/qemu.md:109 +msgid "" +"This program is a bit different from a standard Rust program so let's take " +"a\n" +"closer look." +msgstr "" + +#: src/start/qemu.md:112 +msgid "" +"`#![no_std]` indicates that this program will *not* link to the standard " +"crate,\n" +"`std`. Instead it will link to its subset: the `core` crate." +msgstr "" + +#: src/start/qemu.md:115 +msgid "" +"`#![no_main]` indicates that this program won't use the standard `main`\n" +"interface that most Rust programs use. The main (no pun intended) reason to " +"go\n" +"with `no_main` is that using the `main` interface in `no_std` context " +"requires\n" +"nightly." +msgstr "" + +#: src/start/qemu.md:120 +msgid "" +"`use panic_halt as _;`. This crate provides a `panic_handler` that defines\n" +"the panicking behavior of the program. We will cover this in more detail in " +"the\n" +"[Panicking](panicking.md) chapter of the book." +msgstr "" + +#: src/start/qemu.md:124 +msgid "" +"[`#[entry]`][entry] is an attribute provided by the [`cortex-m-rt`] crate " +"that's used\n" +"to mark the entry point of the program. As we are not using the standard " +"`main`\n" +"interface we need another way to indicate the entry point of the program " +"and\n" +"that'd be `#[entry]`." +msgstr "" + +#: src/start/qemu.md:132 +msgid "" +"`fn main() -> !`. Our program will be the *only* process running on the " +"target\n" +"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 `-> !`\n" +"bit in the function signature) to ensure at compile time that'll be the case." +msgstr "" + +#: src/start/qemu.md:136 +msgid "## Cross compiling" +msgstr "" + +#: src/start/qemu.md:138 +msgid "" +"The next step is to *cross* compile the program for the Cortex-M3 " +"architecture.\n" +"That's as simple as running `cargo build --target $TRIPLE` if you know what " +"the\n" +"compilation target (`$TRIPLE`) should be. Luckily, the `.cargo/config.toml` " +"in the\n" +"template has the answer:" +msgstr "" + +#: src/start/qemu.md:143 +msgid "" +"```console\n" +"tail -n6 .cargo/config.toml\n" +"```" +msgstr "" + +#: src/start/qemu.md:147 +msgid "" +"```toml\n" +"[build]\n" +"# Pick ONE of these compilation targets\n" +"# target = \"thumbv6m-none-eabi\" # Cortex-M0 and Cortex-M0+\n" +"target = \"thumbv7m-none-eabi\" # Cortex-M3\n" +"# target = \"thumbv7em-none-eabi\" # Cortex-M4 and Cortex-M7 (no FPU)\n" +"# target = \"thumbv7em-none-eabihf\" # Cortex-M4F and Cortex-M7F (with FPU)\n" +"```" +msgstr "" + +#: src/start/qemu.md:156 +msgid "" +"To cross compile for the Cortex-M3 architecture we have to use\n" +"`thumbv7m-none-eabi`. That target is not automatically installed when " +"installing\n" +"the Rust toolchain, it would now be a good time to add that target to the " +"toolchain,\n" +"if you haven't done it yet:" +msgstr "" + +#: src/start/qemu.md:163 +msgid "" +"Since the `thumbv7m-none-eabi` compilation target has been set as the " +"default in \n" +" your `.cargo/config.toml` file, the two commands below do the same:" +msgstr "" + +#: src/start/qemu.md:166 +msgid "" +"```console\n" +"cargo build --target thumbv7m-none-eabi\n" +"cargo build\n" +"```" +msgstr "" + +#: src/start/qemu.md:171 +msgid "## Inspecting" +msgstr "" + +#: src/start/qemu.md:173 +msgid "" +"Now we have a non-native ELF binary in `target/thumbv7m-none-eabi/debug/" +"app`. We\n" +"can inspect it using `cargo-binutils`." +msgstr "" + +#: src/start/qemu.md:176 +msgid "" +"With `cargo-readobj` we can print the ELF headers to confirm that this is an " +"ARM\n" +"binary." +msgstr "" + +#: src/start/qemu.md:179 +msgid "" +"``` console\n" +"cargo readobj --bin app -- --file-headers\n" +"```" +msgstr "" + +#: src/start/qemu.md:183 +msgid "Note that:" +msgstr "" + +#: src/start/qemu.md:184 +msgid "" +"* `--bin app` is sugar for inspect the binary at `target/$TRIPLE/debug/app`\n" +"* `--bin app` will also (re)compile the binary, if necessary" +msgstr "" + +#: src/start/qemu.md:188 +msgid "" +"``` text\n" +"ELF Header:\n" +" Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00\n" +" Class: ELF32\n" +" Data: 2's complement, little endian\n" +" Version: 1 (current)\n" +" OS/ABI: UNIX - System V\n" +" ABI Version: 0x0\n" +" Type: EXEC (Executable file)\n" +" Machine: ARM\n" +" Version: 0x1\n" +" Entry point address: 0x405\n" +" Start of program headers: 52 (bytes into file)\n" +" Start of section headers: 153204 (bytes into file)\n" +" Flags: 0x5000200\n" +" Size of this header: 52 (bytes)\n" +" Size of program headers: 32 (bytes)\n" +" Number of program headers: 2\n" +" Size of section headers: 40 (bytes)\n" +" Number of section headers: 19\n" +" Section header string table index: 18\n" +"```" +msgstr "" + +#: src/start/qemu.md:211 +msgid "`cargo-size` can print the size of the linker sections of the binary." +msgstr "" + +#: src/start/qemu.md:214 +msgid "" +"```console\n" +"cargo size --bin app --release -- -A\n" +"```" +msgstr "" + +#: src/start/qemu.md:217 +msgid "we use `--release` to inspect the optimized version" +msgstr "" + +#: src/start/qemu.md:219 +msgid "" +"``` text\n" +"app :\n" +"section size addr\n" +".vector_table 1024 0x0\n" +".text 92 0x400\n" +".rodata 0 0x45c\n" +".data 0 0x20000000\n" +".bss 0 0x20000000\n" +".debug_str 2958 0x0\n" +".debug_loc 19 0x0\n" +".debug_abbrev 567 0x0\n" +".debug_info 4929 0x0\n" +".debug_ranges 40 0x0\n" +".debug_macinfo 1 0x0\n" +".debug_pubnames 2035 0x0\n" +".debug_pubtypes 1892 0x0\n" +".ARM.attributes 46 0x0\n" +".debug_frame 100 0x0\n" +".debug_line 867 0x0\n" +"Total 14570\n" +"```" +msgstr "" + +#: src/start/qemu.md:241 +msgid "" +"> A refresher on ELF linker sections\n" +">\n" +"> - `.text` contains the program instructions\n" +"> - `.rodata` contains constant values like strings\n" +"> - `.data` contains statically allocated variables whose initial values " +"are\n" +"> *not* zero\n" +"> - `.bss` also contains statically allocated variables whose initial " +"values\n" +"> *are* zero\n" +"> - `.vector_table` is a *non*-standard section that we use to store the " +"vector\n" +"> (interrupt) table\n" +"> - `.ARM.attributes` and the `.debug_*` sections contain metadata and will\n" +"> *not* be loaded onto the target when flashing the binary." +msgstr "" + +#: src/start/qemu.md:254 +msgid "" +"**IMPORTANT**: ELF files contain metadata like debug information so their " +"*size\n" +"on disk* does *not* accurately reflect the space the program will occupy " +"when\n" +"flashed on a device. *Always* use `cargo-size` to check how big a binary " +"really\n" +"is." +msgstr "" + +#: src/start/qemu.md:259 +msgid "`cargo-objdump` can be used to disassemble the binary." +msgstr "" + +#: src/start/qemu.md:261 +msgid "" +"```console\n" +"cargo objdump --bin app --release -- --disassemble --no-show-raw-insn --" +"print-imm-hex\n" +"```" +msgstr "" + +#: src/start/qemu.md:265 +msgid "" +"> **NOTE** if the above command complains about `Unknown command line " +"argument` see\n" +"> the following bug report: https://github.com/rust-embedded/book/issues/269" +msgstr "" + +#: src/start/qemu.md:268 +msgid "" +"> **NOTE** this output can differ on your system. New versions of rustc, " +"LLVM\n" +"> and libraries can generate different assembly. We truncated some of the " +"instructions\n" +"> to keep the snippet small." +msgstr "" + +#: src/start/qemu.md:272 +msgid "" +"```text\n" +"app: file format ELF32-arm-little\n" +"\n" +"Disassembly of section .text:\n" +"main:\n" +" 400: bl #0x256\n" +" 404: b #-0x4 \n" +"\n" +"Reset:\n" +" 406: bl #0x24e\n" +" 40a: movw r0, #0x0\n" +" < .. truncated any more instructions .. >\n" +"\n" +"DefaultHandler_:\n" +" 656: b #-0x4 \n" +"\n" +"UsageFault:\n" +" 657: strb r7, [r4, #0x3]\n" +"\n" +"DefaultPreInit:\n" +" 658: bx lr\n" +"\n" +"__pre_init:\n" +" 659: strb r7, [r0, #0x1]\n" +"\n" +"__nop:\n" +" 65a: bx lr\n" +"\n" +"HardFaultTrampoline:\n" +" 65c: mrs r0, msp\n" +" 660: b #-0x2 \n" +"\n" +"HardFault_:\n" +" 662: b #-0x4 \n" +"\n" +"HardFault:\n" +" 663: \n" +"```" +msgstr "" + +#: src/start/qemu.md:311 +msgid "## Running" +msgstr "" + +#: src/start/qemu.md:313 +msgid "" +"Next, let's see how to run an embedded program on QEMU! This time we'll use " +"the\n" +"`hello` example which actually does something." +msgstr "" + +#: src/start/qemu.md:316 +msgid "For convenience here's the source code of `examples/hello.rs`:" +msgstr "" + +#: src/start/qemu.md:318 +msgid "" +"```rust,ignore\n" +"//! Prints \"Hello, world!\" on the host console using semihosting\n" +"\n" +"#![no_main]\n" +"#![no_std]\n" +"\n" +"use panic_halt as _;\n" +"\n" +"use cortex_m_rt::entry;\n" +"use cortex_m_semihosting::{debug, hprintln};\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" hprintln!(\"Hello, world!\").unwrap();\n" +"\n" +" // exit QEMU\n" +" // NOTE do not run this on hardware; it can corrupt OpenOCD state\n" +" debug::exit(debug::EXIT_SUCCESS);\n" +"\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/start/qemu.md:341 +msgid "" +"This program uses something called semihosting to print text to the *host*\n" +"console. When using real hardware this requires a debug session but when " +"using\n" +"QEMU this Just Works." +msgstr "" + +#: src/start/qemu.md:345 +msgid "Let's start by compiling the example:" +msgstr "" + +#: src/start/qemu.md:347 +msgid "" +"```console\n" +"cargo build --example hello\n" +"```" +msgstr "" + +#: src/start/qemu.md:351 +msgid "" +"The output binary will be located at\n" +"`target/thumbv7m-none-eabi/debug/examples/hello`." +msgstr "" + +#: src/start/qemu.md:354 +msgid "To run this binary on QEMU run the following command:" +msgstr "" + +#: src/start/qemu.md:356 +msgid "" +"```console\n" +"qemu-system-arm \\\n" +" -cpu cortex-m3 \\\n" +" -machine lm3s6965evb \\\n" +" -nographic \\\n" +" -semihosting-config enable=on,target=native \\\n" +" -kernel target/thumbv7m-none-eabi/debug/examples/hello\n" +"```" +msgstr "" + +#: src/start/qemu.md:365 +msgid "" +"```text\n" +"Hello, world!\n" +"```" +msgstr "" + +#: src/start/qemu.md:369 +msgid "" +"The command should successfully exit (exit code = 0) after printing the " +"text. On\n" +"*nix you can check that with the following command:" +msgstr "" + +#: src/start/qemu.md:372 +msgid "" +"```console\n" +"echo $?\n" +"```" +msgstr "" + +#: src/start/qemu.md:376 +msgid "" +"```text\n" +"0\n" +"```" +msgstr "" + +#: src/start/qemu.md:380 +msgid "Let's break down that QEMU command:" +msgstr "" + +#: src/start/qemu.md:382 +msgid "" +"- `qemu-system-arm`. This is the QEMU emulator. There are a few variants of\n" +" these QEMU binaries; this one does full *system* emulation of *ARM* " +"machines\n" +" hence the name.\n" +"\n" +"- `-cpu cortex-m3`. This tells QEMU to emulate a Cortex-M3 CPU. Specifying " +"the\n" +" CPU model lets us catch some miscompilation errors: for example, running " +"a\n" +" program compiled for the Cortex-M4F, which has a hardware FPU, will make " +"QEMU\n" +" error during its execution.\n" +"\n" +"- `-machine lm3s6965evb`. This tells QEMU to emulate the LM3S6965EVB, a\n" +" evaluation board that contains a LM3S6965 microcontroller.\n" +"\n" +"- `-nographic`. This tells QEMU to not launch its GUI.\n" +"\n" +"- `-semihosting-config (..)`. This tells QEMU to enable semihosting. " +"Semihosting\n" +" lets the emulated device, among other things, use the host stdout, stderr " +"and\n" +" stdin and create files on the host.\n" +"\n" +"- `-kernel $file`. This tells QEMU which binary to load and run on the " +"emulated\n" +" machine." +msgstr "" + +#: src/start/qemu.md:403 +msgid "" +"Typing out that long QEMU command is too much work! We can set a custom " +"runner\n" +"to simplify the process. `.cargo/config.toml` has a commented out runner " +"that invokes\n" +"QEMU; let's uncomment it:" +msgstr "" + +#: src/start/qemu.md:407 +msgid "" +"```console\n" +"head -n3 .cargo/config.toml\n" +"```" +msgstr "" + +#: src/start/qemu.md:411 +msgid "" +"```toml\n" +"[target.thumbv7m-none-eabi]\n" +"# uncomment this to make `cargo run` execute programs on QEMU\n" +"runner = \"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -" +"semihosting-config enable=on,target=native -kernel\"\n" +"```" +msgstr "" + +#: src/start/qemu.md:417 +msgid "" +"This runner only applies to the `thumbv7m-none-eabi` target, which is our\n" +"default compilation target. Now `cargo run` will compile the program and run " +"it\n" +"on QEMU:" +msgstr "" + +#: src/start/qemu.md:421 +msgid "" +"```console\n" +"cargo run --example hello --release\n" +"```" +msgstr "" + +#: src/start/qemu.md:425 +msgid "" +"```text\n" +" Compiling app v0.1.0 (file:///tmp/app)\n" +" Finished release [optimized + debuginfo] target(s) in 0.26s\n" +" Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic " +"-semihosting-config enable=on,target=native -kernel target/thumbv7m-none-" +"eabi/release/examples/hello`\n" +"Hello, world!\n" +"```" +msgstr "" + +#: src/start/qemu.md:432 src/start/hardware.md:113 +msgid "## Debugging" +msgstr "" + +#: src/start/qemu.md:434 +msgid "Debugging is critical to embedded development. Let's see how it's done." +msgstr "" + +#: src/start/qemu.md:436 +msgid "" +"Debugging an embedded device involves *remote* debugging as the program that " +"we\n" +"want to debug won't be running on the machine that's running the debugger\n" +"program (GDB or LLDB)." +msgstr "" + +#: src/start/qemu.md:440 +msgid "" +"Remote debugging involves a client and a server. In a QEMU setup, the " +"client\n" +"will be a GDB (or LLDB) process and the server will be the QEMU process " +"that's\n" +"also running the embedded program." +msgstr "" + +#: src/start/qemu.md:444 +msgid "In this section we'll use the `hello` example we already compiled." +msgstr "" + +#: src/start/qemu.md:446 +msgid "The first debugging step is to launch QEMU in debugging mode:" +msgstr "" + +#: src/start/qemu.md:448 +msgid "" +"```console\n" +"qemu-system-arm \\\n" +" -cpu cortex-m3 \\\n" +" -machine lm3s6965evb \\\n" +" -nographic \\\n" +" -semihosting-config enable=on,target=native \\\n" +" -gdb tcp::3333 \\\n" +" -S \\\n" +" -kernel target/thumbv7m-none-eabi/debug/examples/hello\n" +"```" +msgstr "" + +#: src/start/qemu.md:459 +msgid "" +"This command won't print anything to the console and will block the " +"terminal. We\n" +"have passed two extra flags this time:" +msgstr "" + +#: src/start/qemu.md:462 +msgid "" +"- `-gdb tcp::3333`. This tells QEMU to wait for a GDB connection on TCP\n" +" port 3333.\n" +"\n" +"- `-S`. This tells QEMU to freeze the machine at startup. Without this the\n" +" program would have reached the end of main before we had a chance to " +"launch\n" +" the debugger!" +msgstr "" + +#: src/start/qemu.md:469 +msgid "" +"Next we launch GDB in another terminal and tell it to load the debug symbols " +"of\n" +"the example:" +msgstr "" + +#: src/start/qemu.md:472 +msgid "" +"```console\n" +"gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello\n" +"```" +msgstr "" + +#: src/start/qemu.md:476 +msgid "" +"**NOTE**: you might need another version of gdb instead of `gdb-multiarch` " +"depending\n" +"on which one you installed in the installation chapter. This could also be\n" +"`arm-none-eabi-gdb` or just `gdb`." +msgstr "" + +#: src/start/qemu.md:480 +msgid "" +"Then within the GDB shell we connect to QEMU, which is waiting for a " +"connection\n" +"on TCP port 3333." +msgstr "" + +#: src/start/qemu.md:483 +msgid "" +"```console\n" +"target remote :3333\n" +"```" +msgstr "" + +#: src/start/qemu.md:487 +msgid "" +"```text\n" +"Remote debugging using :3333\n" +"Reset () at $REGISTRY/cortex-m-rt-0.6.1/src/lib.rs:473\n" +"473 pub unsafe extern \"C\" fn Reset() -> ! {\n" +"```" +msgstr "" + +#: src/start/qemu.md:494 +msgid "" +"You'll see that the process is halted and that the program counter is " +"pointing\n" +"to a function named `Reset`. That is the reset handler: what Cortex-M cores\n" +"execute upon booting." +msgstr "" + +#: src/start/qemu.md:498 +msgid "" +"> 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 : \n" +">\n" +">`core::num::bignum::Big32x40::mul_small () at src/libcore/num/bignum." +"rs:254`\n" +"> ` src/libcore/num/bignum.rs: No such file or directory.`\n" +"> \n" +"> That's a known glitch. You can safely ignore those warnings, you're most " +"likely at Reset(). " +msgstr "" + +#: src/start/qemu.md:506 +msgid "" +"This reset handler will eventually call our main function. Let's skip all " +"the\n" +"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." +msgstr "" + +#: src/start/qemu.md:509 +msgid "" +"```console\n" +"list main\n" +"```" +msgstr "" + +#: src/start/qemu.md:512 +msgid "This will show the source code, from the file examples/hello.rs. " +msgstr "" + +#: src/start/qemu.md:514 +msgid "" +"```text\n" +"6 use panic_halt as _;\n" +"7\n" +"8 use cortex_m_rt::entry;\n" +"9 use cortex_m_semihosting::{debug, hprintln};\n" +"10\n" +"11 #[entry]\n" +"12 fn main() -> ! {\n" +"13 hprintln!(\"Hello, world!\").unwrap();\n" +"14\n" +"15 // exit QEMU\n" +"```" +msgstr "" + +#: src/start/qemu.md:526 +msgid "" +"We would like to add a breakpoint just before the \"Hello, world!\", which " +"is on line 13. We do that with the `break` command:" +msgstr "" + +#: src/start/qemu.md:528 +msgid "" +"```console\n" +"break 13\n" +"```" +msgstr "" + +#: src/start/qemu.md:531 +msgid "" +"We can now instruct gdb to run up to our main function, with the `continue` " +"command:" +msgstr "" + +#: src/start/qemu.md:533 +msgid "" +"```console\n" +"continue\n" +"```" +msgstr "" + +#: src/start/qemu.md:537 +msgid "" +"```text\n" +"Continuing.\n" +"\n" +"Breakpoint 1, hello::__cortex_m_rt_main () at examples\\hello.rs:13\n" +"13 hprintln!(\"Hello, world!\").unwrap();\n" +"```" +msgstr "" + +#: src/start/qemu.md:544 +msgid "" +"We are now close to the code that prints \"Hello, world!\". Let's move " +"forward\n" +"using the `next` command." +msgstr "" + +#: src/start/qemu.md:547 +msgid "" +"``` console\n" +"next\n" +"```" +msgstr "" + +#: src/start/qemu.md:551 +msgid "" +"```text\n" +"16 debug::exit(debug::EXIT_SUCCESS);\n" +"```" +msgstr "" + +#: src/start/qemu.md:555 +msgid "" +"At this point you should see \"Hello, world!\" printed on the terminal " +"that's\n" +"running `qemu-system-arm`." +msgstr "" + +#: src/start/qemu.md:558 +msgid "" +"```text\n" +"$ qemu-system-arm (..)\n" +"Hello, world!\n" +"```" +msgstr "" + +#: src/start/qemu.md:563 +msgid "Calling `next` again will terminate the QEMU process." +msgstr "" + +#: src/start/qemu.md:565 +msgid "" +"```console\n" +"next\n" +"```" +msgstr "" + +#: src/start/qemu.md:569 +msgid "" +"```text\n" +"[Inferior 1 (Remote target) exited normally]\n" +"```" +msgstr "" + +#: src/start/qemu.md:573 +msgid "You can now exit the GDB session." +msgstr "" + +#: src/start/qemu.md:575 +msgid "" +"``` console\n" +"quit\n" +"```" +msgstr "" + +#: src/start/hardware.md:1 +msgid "# Hardware" +msgstr "" + +#: src/start/hardware.md:3 +msgid "" +"By now you should be somewhat familiar with the tooling and the development\n" +"process. In this section we'll switch to real hardware; the process will " +"remain\n" +"largely the same. Let's dive in." +msgstr "" + +#: src/start/hardware.md:7 +msgid "## Know your hardware" +msgstr "" + +#: src/start/hardware.md:9 +msgid "" +"Before we begin you need to identify some characteristics of the target " +"device\n" +"as these will be used to configure the project:" +msgstr "" + +#: src/start/hardware.md:12 +msgid "" +"- The ARM core. e.g. Cortex-M3.\n" +"\n" +"- Does the ARM core include an FPU? Cortex-M4**F** and Cortex-M7**F** cores " +"do.\n" +"\n" +"- How much Flash memory and RAM does the target device have? e.g. 256 KiB " +"of\n" +" Flash and 32 KiB of RAM.\n" +"\n" +"- Where are Flash memory and RAM mapped in the address space? e.g. RAM is\n" +" commonly located at address `0x2000_0000`." +msgstr "" + +#: src/start/hardware.md:22 +msgid "" +"You can find this information in the data sheet or the reference manual of " +"your\n" +"device." +msgstr "" + +#: src/start/hardware.md:25 +msgid "" +"In this section we'll be using our reference hardware, the " +"STM32F3DISCOVERY.\n" +"This board contains an STM32F303VCT6 microcontroller. This microcontroller " +"has:" +msgstr "" + +#: src/start/hardware.md:28 +msgid "" +"- A Cortex-M4F core that includes a single precision FPU\n" +"\n" +"- 256 KiB of Flash located at address 0x0800_0000.\n" +"\n" +"- 40 KiB of RAM located at address 0x2000_0000. (There's another RAM region " +"but\n" +" for simplicity we'll ignore it)." +msgstr "" + +#: src/start/hardware.md:35 +msgid "## Configuring" +msgstr "" + +#: src/start/hardware.md:37 +msgid "" +"We'll start from scratch with a fresh template instance. Refer to the\n" +"[previous section on QEMU] for a refresher on how to do this without\n" +"`cargo-generate`." +msgstr "" + +#: src/start/hardware.md:43 +msgid "" +"``` text\n" +"$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart\n" +" Project Name: app\n" +" Creating project called `app`...\n" +" Done! New project created /tmp/app\n" +"\n" +"$ cd app\n" +"```" +msgstr "" + +#: src/start/hardware.md:52 +msgid "" +"Step number one is to set a default compilation target in `.cargo/config." +"toml`." +msgstr "" + +#: src/start/hardware.md:54 +msgid "" +"``` console\n" +"tail -n5 .cargo/config.toml\n" +"```" +msgstr "" + +#: src/start/hardware.md:58 +msgid "" +"``` toml\n" +"# Pick ONE of these compilation targets\n" +"# target = \"thumbv6m-none-eabi\" # Cortex-M0 and Cortex-M0+\n" +"# target = \"thumbv7m-none-eabi\" # Cortex-M3\n" +"# target = \"thumbv7em-none-eabi\" # Cortex-M4 and Cortex-M7 (no FPU)\n" +"target = \"thumbv7em-none-eabihf\" # Cortex-M4F and Cortex-M7F (with FPU)\n" +"```" +msgstr "" + +#: src/start/hardware.md:66 +msgid "We'll use `thumbv7em-none-eabihf` as that covers the Cortex-M4F core." +msgstr "" + +#: src/start/hardware.md:68 +msgid "" +"The second step is to enter the memory region information into the `memory." +"x`\n" +"file." +msgstr "" + +#: src/start/hardware.md:71 +msgid "" +"``` text\n" +"$ cat memory.x\n" +"/* Linker script for the STM32F303VCT6 */\n" +"MEMORY\n" +"{\n" +" /* NOTE 1 K = 1 KiBi = 1024 bytes */\n" +" FLASH : ORIGIN = 0x08000000, LENGTH = 256K\n" +" RAM : ORIGIN = 0x20000000, LENGTH = 40K\n" +"}\n" +"```" +msgstr "" + +#: src/start/hardware.md:81 +msgid "" +"> **NOTE**: If you for some reason changed the `memory.x` file after you had " +"made\n" +"> the first build of a specific build target, then do `cargo clean` before\n" +"> `cargo build`, because `cargo build` may not track updates of `memory.x`." +msgstr "" + +#: src/start/hardware.md:85 +msgid "" +"We'll start with the hello example again, but first we have to make a small\n" +"change." +msgstr "" + +#: src/start/hardware.md:88 +msgid "" +"In `examples/hello.rs`, make sure the `debug::exit()` call is commented out " +"or\n" +"removed. It is used only for running in QEMU." +msgstr "" + +#: src/start/hardware.md:91 +msgid "" +"```rust,ignore\n" +"#[entry]\n" +"fn main() -> ! {\n" +" hprintln!(\"Hello, world!\").unwrap();\n" +"\n" +" // exit QEMU\n" +" // NOTE do not run this on hardware; it can corrupt OpenOCD state\n" +" // debug::exit(debug::EXIT_SUCCESS);\n" +"\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/start/hardware.md:104 +msgid "" +"You can now cross compile programs using `cargo build`\n" +"and inspect the binaries using `cargo-binutils` as you did before. The\n" +"`cortex-m-rt` crate handles all the magic required to get your chip " +"running,\n" +"as helpfully, pretty much all Cortex-M CPUs boot in the same fashion." +msgstr "" + +#: src/start/hardware.md:109 +msgid "" +"``` console\n" +"cargo build --example hello\n" +"```" +msgstr "" + +#: src/start/hardware.md:115 +msgid "" +"Debugging will look a bit different. In fact, the first steps can look " +"different\n" +"depending on the target device. In this section we'll show the steps " +"required to\n" +"debug a program running on the STM32F3DISCOVERY. This is meant to serve as " +"a\n" +"reference; for device specific information about debugging check out [the\n" +"Debugonomicon](https://github.com/rust-embedded/debugonomicon)." +msgstr "" + +#: src/start/hardware.md:121 +msgid "" +"As before we'll do remote debugging and the client will be a GDB process. " +"This\n" +"time, however, the server will be OpenOCD." +msgstr "" + +#: src/start/hardware.md:124 +msgid "" +"As done during the [verify] section connect the discovery board to your " +"laptop /\n" +"PC and check that the ST-LINK header is populated." +msgstr "" + +#: src/start/hardware.md:129 +msgid "" +"On a terminal run `openocd` to connect to the ST-LINK on the discovery " +"board.\n" +"Run this command from the root of the template; `openocd` will pick up the\n" +"`openocd.cfg` file which indicates which interface file and target file to " +"use." +msgstr "" + +#: src/start/hardware.md:133 +msgid "" +"``` console\n" +"cat openocd.cfg\n" +"```" +msgstr "" + +#: src/start/hardware.md:137 +msgid "" +"``` text\n" +"# Sample OpenOCD configuration for the STM32F3DISCOVERY development board\n" +"\n" +"# Depending on the hardware revision you got you'll have to pick ONE of " +"these\n" +"# interfaces. At any time only one interface should be commented out.\n" +"\n" +"# Revision C (newer revision)\n" +"source [find interface/stlink.cfg]\n" +"\n" +"# Revision A and B (older revisions)\n" +"# source [find interface/stlink-v2.cfg]\n" +"\n" +"source [find target/stm32f3x.cfg]\n" +"```" +msgstr "" + +#: src/start/hardware.md:152 +msgid "" +"> **NOTE** If you found out that you have an older revision of the " +"discovery\n" +"> board during the [verify] section then you should modify the `openocd." +"cfg`\n" +"> file at this point to use `interface/stlink-v2.cfg`." +msgstr "" + +#: src/start/hardware.md:156 +msgid "" +"``` text\n" +"$ openocd\n" +"Open On-Chip Debugger 0.10.0\n" +"Licensed under GNU GPL v2\n" +"For bug reports, read\n" +" http://openocd.org/doc/doxygen/bugs.html\n" +"Info : auto-selecting first available session transport \"hla_swd\". To " +"override use 'transport select '.\n" +"adapter speed: 1000 kHz\n" +"adapter_nsrst_delay: 100\n" +"Info : The selected transport took over low-level target control. The " +"results might differ compared to plain JTAG/SWD\n" +"none separate\n" +"Info : Unable to match requested speed 1000 kHz, using 950 kHz\n" +"Info : Unable to match requested speed 1000 kHz, using 950 kHz\n" +"Info : clock speed 950 kHz\n" +"Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B\n" +"Info : using stlink api v2\n" +"Info : Target voltage: 2.913879\n" +"Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints\n" +"```" +msgstr "" + +#: src/start/hardware.md:176 +msgid "On another terminal run GDB, also from the root of the template." +msgstr "" + +#: src/start/hardware.md:178 +msgid "" +"``` text\n" +"gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello\n" +"```" +msgstr "" + +#: src/start/hardware.md:182 +msgid "" +"**NOTE**: like before you might need another version of gdb instead of `gdb-" +"multiarch` depending\n" +"on which one you installed in the installation chapter. This could also be\n" +"`arm-none-eabi-gdb` or just `gdb`." +msgstr "" + +#: src/start/hardware.md:186 +msgid "" +"Next connect GDB to OpenOCD, which is waiting for a TCP connection on port " +"3333." +msgstr "" + +#: src/start/hardware.md:188 +msgid "" +"``` console\n" +"(gdb) target remote :3333\n" +"Remote debugging using :3333\n" +"0x00000000 in ?? ()\n" +"```" +msgstr "" + +#: src/start/hardware.md:194 +msgid "" +"Now proceed to *flash* (load) the program onto the microcontroller using " +"the\n" +"`load` command." +msgstr "" + +#: src/start/hardware.md:197 +msgid "" +"``` console\n" +"(gdb) load\n" +"Loading section .vector_table, size 0x400 lma 0x8000000\n" +"Loading section .text, size 0x1518 lma 0x8000400\n" +"Loading section .rodata, size 0x414 lma 0x8001918\n" +"Start address 0x08000400, load size 7468\n" +"Transfer rate: 13 KB/sec, 2489 bytes/write.\n" +"```" +msgstr "" + +#: src/start/hardware.md:206 +msgid "" +"The program is now loaded. This program uses semihosting so before we do " +"any\n" +"semihosting call we have to tell OpenOCD to enable semihosting. You can " +"send\n" +"commands to OpenOCD using the `monitor` command." +msgstr "" + +#: src/start/hardware.md:210 src/start/semihosting.md:43 +msgid "" +"``` console\n" +"(gdb) monitor arm semihosting enable\n" +"semihosting is enabled\n" +"```" +msgstr "" + +#: src/start/hardware.md:215 +msgid "" +"> You can see all the OpenOCD commands by invoking the `monitor help` " +"command." +msgstr "" + +#: src/start/hardware.md:217 +msgid "" +"Like before we can skip all the way to `main` using a breakpoint and the\n" +"`continue` command." +msgstr "" + +#: src/start/hardware.md:220 +msgid "" +"``` console\n" +"(gdb) break main\n" +"Breakpoint 1 at 0x8000490: file examples/hello.rs, line 11.\n" +"Note: automatically using hardware breakpoints for read-only addresses.\n" +"\n" +"(gdb) continue\n" +"Continuing.\n" +"\n" +"Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello." +"rs:11\n" +"11 #[entry]\n" +"```" +msgstr "" + +#: src/start/hardware.md:232 +msgid "" +"> **NOTE** If GDB blocks the terminal instead of hitting the breakpoint " +"after\n" +"> you issue the `continue` command above, you might want to double check " +"that\n" +"> the memory region information in the `memory.x` file is correctly set up\n" +"> for your device (both the starts *and* lengths). " +msgstr "" + +#: src/start/hardware.md:237 +msgid "Step into the main function with `step`." +msgstr "" + +#: src/start/hardware.md:239 +msgid "" +"``` console\n" +"(gdb) step\n" +"halted: PC: 0x08000496\n" +"hello::__cortex_m_rt_main () at examples/hello.rs:13\n" +"13 hprintln!(\"Hello, world!\").unwrap();\n" +"```" +msgstr "" + +#: src/start/hardware.md:246 +msgid "" +"After advancing the program with `next` you should see \"Hello, world!\" " +"printed on the OpenOCD console,\n" +"among other stuff." +msgstr "" + +#: src/start/hardware.md:249 +msgid "" +"``` console\n" +"$ openocd\n" +"(..)\n" +"Info : halted: PC: 0x08000502\n" +"Hello, world!\n" +"Info : halted: PC: 0x080004ac\n" +"Info : halted: PC: 0x080004ae\n" +"Info : halted: PC: 0x080004b0\n" +"Info : halted: PC: 0x080004b4\n" +"Info : halted: PC: 0x080004b8\n" +"Info : halted: PC: 0x080004bc\n" +"```" +msgstr "" + +#: src/start/hardware.md:261 +msgid "" +"The message is only displayed once as the program is about to enter the " +"infinite loop defined in line 19: `loop {}`" +msgstr "" + +#: src/start/hardware.md:263 +msgid "You can now exit GDB using the `quit` command." +msgstr "" + +#: src/start/hardware.md:265 +msgid "" +"``` console\n" +"(gdb) quit\n" +"A debugging session is active.\n" +"\n" +" Inferior 1 [Remote target] will be detached.\n" +"\n" +"Quit anyway? (y or n)\n" +"```" +msgstr "" + +#: src/start/hardware.md:274 +msgid "" +"Debugging now requires a few more steps so we have packed all those steps " +"into a\n" +"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:" +msgstr "" + +#: src/start/hardware.md:277 +msgid "" +"``` console\n" +"cat openocd.gdb\n" +"```" +msgstr "" + +#: src/start/hardware.md:281 +msgid "" +"``` text\n" +"target extended-remote :3333\n" +"\n" +"# print demangled symbols\n" +"set print asm-demangle on\n" +"\n" +"# detect unhandled exceptions, hard faults and panics\n" +"break DefaultHandler\n" +"break HardFault\n" +"break rust_begin_unwind\n" +"\n" +"monitor arm semihosting enable\n" +"\n" +"load\n" +"\n" +"# start the process but immediately halt the processor\n" +"stepi\n" +"```" +msgstr "" + +#: src/start/hardware.md:300 +msgid "" +"Now running ` -x openocd.gdb target/thumbv7em-none-eabihf/debug/" +"examples/hello` will immediately connect GDB to\n" +"OpenOCD, enable semihosting, load the program and start the process." +msgstr "" + +#: src/start/hardware.md:303 +msgid "" +"Alternatively, you can turn ` -x openocd.gdb` into a custom runner to " +"make\n" +"`cargo run` build a program *and* start a GDB session. This runner is " +"included\n" +"in `.cargo/config.toml` but it's commented out." +msgstr "" + +#: src/start/hardware.md:307 +msgid "" +"``` console\n" +"head -n10 .cargo/config.toml\n" +"```" +msgstr "" + +#: src/start/hardware.md:311 +msgid "" +"``` toml\n" +"[target.thumbv7m-none-eabi]\n" +"# uncomment this to make `cargo run` execute programs on QEMU\n" +"# runner = \"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -" +"semihosting-config enable=on,target=native -kernel\"\n" +"\n" +"[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n" +"# uncomment ONE of these three option to make `cargo run` start a GDB " +"session\n" +"# which option to pick depends on your system\n" +"runner = \"arm-none-eabi-gdb -x openocd.gdb\"\n" +"# runner = \"gdb-multiarch -x openocd.gdb\"\n" +"# runner = \"gdb -x openocd.gdb\"\n" +"```" +msgstr "" + +#: src/start/hardware.md:324 +msgid "" +"``` text\n" +"$ cargo run --example hello\n" +"(..)\n" +"Loading section .vector_table, size 0x400 lma 0x8000000\n" +"Loading section .text, size 0x1e70 lma 0x8000400\n" +"Loading section .rodata, size 0x61c lma 0x8002270\n" +"Start address 0x800144e, load size 10380\n" +"Transfer rate: 17 KB/sec, 3460 bytes/write.\n" +"(gdb)\n" +"```" +msgstr "" + +#: src/start/registers.md:1 +msgid "# Memory Mapped Registers" +msgstr "" + +#: src/start/registers.md:3 +msgid "" +"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'." +msgstr "" + +#: src/start/registers.md:5 +msgid "" +"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:" +msgstr "" + +#: src/start/registers.md:6 +msgid "" +"

\n" +"\n" +"

" +msgstr "" + +#: src/start/registers.md:10 +msgid "" +"* 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.\n" +"* 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.\n" +"* 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].\n" +"* 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." +msgstr "" + +#: src/start/registers.md:23 +msgid "## Board Crate" +msgstr "" + +#: src/start/registers.md:25 +msgid "" +"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." +msgstr "" + +#: src/start/registers.md:27 +msgid "" +"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." +msgstr "" + +#: src/start/registers.md:29 +msgid "" +"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." +msgstr "" + +#: src/start/registers.md:31 +msgid "## Micro-architecture crate" +msgstr "" + +#: src/start/registers.md:33 +msgid "" +"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:" +msgstr "" + +#: src/start/registers.md:35 +msgid "" +"```rust,ignore\n" +"#![no_std]\n" +"#![no_main]\n" +"use cortex_m::peripheral::{syst, Peripherals};\n" +"use cortex_m_rt::entry;\n" +"use panic_halt as _;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" let peripherals = Peripherals::take().unwrap();\n" +" let mut systick = peripherals.SYST;\n" +" systick.set_clock_source(syst::SystClkSource::Core);\n" +" systick.set_reload(1_000);\n" +" systick.clear_current();\n" +" systick.enable_counter();\n" +" while !systick.has_wrapped() {\n" +" // Loop\n" +" }\n" +"\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/start/registers.md:57 +msgid "" +"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." +msgstr "" + +#: src/start/registers.md:61 +msgid "## Using a Peripheral Access Crate (PAC)" +msgstr "" + +#: src/start/registers.md:63 +msgid "" +"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." +msgstr "" + +#: src/start/registers.md:65 +msgid "" +"```rust,ignore\n" +"#![no_std]\n" +"#![no_main]\n" +"\n" +"use panic_halt as _; // panic handler\n" +"\n" +"use cortex_m_rt::entry;\n" +"use tm4c123x;\n" +"\n" +"#[entry]\n" +"pub fn init() -> (Delay, Leds) {\n" +" let cp = cortex_m::Peripherals::take().unwrap();\n" +" let p = tm4c123x::Peripherals::take().unwrap();\n" +"\n" +" let pwm = p.PWM0;\n" +" pwm.ctl.write(|w| w.globalsync0().clear_bit());\n" +" // Mode = 1 => Count up/down mode\n" +" pwm._2_ctl.write(|w| w.enable().set_bit().mode().set_bit());\n" +" pwm._2_gena.write(|w| w.actcmpau().zero().actcmpad().one());\n" +" // 528 cycles (264 up and down) = 4 loops per video line (2112 cycles)\n" +" pwm._2_load.write(|w| unsafe { w.load().bits(263) });\n" +" pwm._2_cmpa.write(|w| unsafe { w.compa().bits(64) });\n" +" pwm.enable.write(|w| w.pwm4en().set_bit());\n" +"}\n" +"\n" +"```" +msgstr "" + +#: src/start/registers.md:92 +msgid "" +"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." +msgstr "" + +#: src/start/registers.md:94 +msgid "### Reading" +msgstr "" + +#: src/start/registers.md:96 +msgid "" +"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]." +msgstr "" + +#: src/start/registers.md:98 +msgid "" +"```rust,ignore\n" +"if pwm.ctl.read().globalsync0().is_set() {\n" +" // Do a thing\n" +"}\n" +"```" +msgstr "" + +#: src/start/registers.md:104 +msgid "### Writing" +msgstr "" + +#: src/start/registers.md:106 +msgid "" +"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." +msgstr "" + +#: src/start/registers.md:108 +msgid "" +"```rust,ignore\n" +"pwm.ctl.write(|w| w.globalsync0().clear_bit());\n" +"```" +msgstr "" + +#: src/start/registers.md:112 +msgid "### Modifying" +msgstr "" + +#: src/start/registers.md:114 +msgid "" +"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." +msgstr "" + +#: src/start/registers.md:116 +msgid "" +"```rust,ignore\n" +"pwm.ctl.modify(|r, w| w.globalsync0().clear_bit());\n" +"```" +msgstr "" + +#: src/start/registers.md:120 +msgid "" +"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:" +msgstr "" + +#: src/start/registers.md:122 +msgid "" +"```C\n" +"uint32_t temp = pwm0.ctl.read();\n" +"temp |= PWM0_CTL_GLOBALSYNC0;\n" +"pwm0.ctl.write(temp);\n" +"uint32_t temp2 = pwm0.enable.read();\n" +"temp2 |= PWM0_ENABLE_PWM4EN;\n" +"pwm0.enable.write(temp); // Uh oh! Wrong variable!\n" +"```" +msgstr "" + +#: src/start/registers.md:135 +msgid "## Using a HAL crate" +msgstr "" + +#: src/start/registers.md:137 +msgid "" +"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!" +msgstr "" + +#: src/start/registers.md:139 +msgid "Let's see an example:" +msgstr "" + +#: src/start/registers.md:141 +msgid "" +"```rust,ignore\n" +"#![no_std]\n" +"#![no_main]\n" +"\n" +"use panic_halt as _; // panic handler\n" +"\n" +"use cortex_m_rt::entry;\n" +"use tm4c123x_hal as hal;\n" +"use tm4c123x_hal::prelude::*;\n" +"use tm4c123x_hal::serial::{NewlineMode, Serial};\n" +"use tm4c123x_hal::sysctl;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" let p = hal::Peripherals::take().unwrap();\n" +" let cp = hal::CorePeripherals::take().unwrap();\n" +"\n" +" // Wrap up the SYSCTL struct into an object with a higher-layer API\n" +" let mut sc = p.SYSCTL.constrain();\n" +" // Pick our oscillation settings\n" +" sc.clock_setup.oscillator = sysctl::Oscillator::Main(\n" +" sysctl::CrystalFrequency::_16mhz,\n" +" sysctl::SystemClock::UsePll(sysctl::PllOutputFrequency::_80_00mhz),\n" +" );\n" +" // Configure the PLL with those settings\n" +" let clocks = sc.clock_setup.freeze();\n" +"\n" +" // Wrap up the GPIO_PORTA struct into an object with a higher-layer " +"API.\n" +" // Note it needs to borrow `sc.power_control` so it can power up the " +"GPIO\n" +" // peripheral automatically.\n" +" let mut porta = p.GPIO_PORTA.split(&sc.power_control);\n" +"\n" +" // Activate the UART.\n" +" let uart = Serial::uart0(\n" +" p.UART0,\n" +" // The transmit pin\n" +" porta\n" +" .pa1\n" +" .into_af_push_pull::(&mut porta.control),\n" +" // The receive pin\n" +" porta\n" +" .pa0\n" +" .into_af_push_pull::(&mut porta.control),\n" +" // No RTS or CTS required\n" +" (),\n" +" (),\n" +" // The baud rate\n" +" 115200_u32.bps(),\n" +" // Output handling\n" +" NewlineMode::SwapLFtoCRLF,\n" +" // We need the clock rates to calculate the baud rate divisors\n" +" &clocks,\n" +" // We need this to power up the UART peripheral\n" +" &sc.power_control,\n" +" );\n" +"\n" +" loop {\n" +" writeln!(uart, \"Hello, World!\\r\\n\").unwrap();\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/start/semihosting.md:1 +msgid "# Semihosting" +msgstr "" + +#: src/start/semihosting.md:3 +msgid "" +"Semihosting is a mechanism that lets embedded devices do I/O on the host and " +"is\n" +"mainly used to log messages to the host console. Semihosting requires a " +"debug\n" +"session and pretty much nothing else (no extra wires!) so it's super " +"convenient\n" +"to use. The downside is that it's super slow: each write operation can take\n" +"several milliseconds depending on the hardware debugger (e.g. ST-Link) you " +"use." +msgstr "" + +#: src/start/semihosting.md:9 +msgid "" +"The [`cortex-m-semihosting`] crate provides an API to do semihosting " +"operations\n" +"on Cortex-M devices. The program below is the semihosting version of " +"\"Hello,\n" +"world!\":" +msgstr "" + +#: src/start/semihosting.md:15 +msgid "" +"```rust,ignore\n" +"#![no_main]\n" +"#![no_std]\n" +"\n" +"use panic_halt as _;\n" +"\n" +"use cortex_m_rt::entry;\n" +"use cortex_m_semihosting::hprintln;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" hprintln!(\"Hello, world!\").unwrap();\n" +"\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/start/semihosting.md:32 +msgid "" +"If you run this program on hardware you'll see the \"Hello, world!\" " +"message\n" +"within the OpenOCD logs." +msgstr "" + +#: src/start/semihosting.md:35 +msgid "" +"``` text\n" +"$ openocd\n" +"(..)\n" +"Hello, world!\n" +"(..)\n" +"```" +msgstr "" + +#: src/start/semihosting.md:42 +msgid "You do need to enable semihosting in OpenOCD from GDB first:" +msgstr "" + +#: src/start/semihosting.md:48 +msgid "" +"QEMU understands semihosting operations so the above program will also work " +"with\n" +"`qemu-system-arm` without having to start a debug session. Note that you'll\n" +"need to pass the `-semihosting-config` flag to QEMU to enable semihosting\n" +"support; these flags are already included in the `.cargo/config.toml` file " +"of the\n" +"template." +msgstr "" + +#: src/start/semihosting.md:54 +msgid "" +"``` text\n" +"$ # this program will block the terminal\n" +"$ cargo run\n" +" Running `qemu-system-arm (..)\n" +"Hello, world!\n" +"```" +msgstr "" + +#: src/start/semihosting.md:61 +msgid "" +"There's also an `exit` semihosting operation that can be used to terminate " +"the\n" +"QEMU process. Important: do **not** use `debug::exit` on hardware; this " +"function\n" +"can corrupt your OpenOCD session and you will not be able to debug more " +"programs\n" +"until you restart it." +msgstr "" + +#: src/start/semihosting.md:66 +msgid "" +"```rust,ignore\n" +"#![no_main]\n" +"#![no_std]\n" +"\n" +"use panic_halt as _;\n" +"\n" +"use cortex_m_rt::entry;\n" +"use cortex_m_semihosting::debug;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" let roses = \"blue\";\n" +"\n" +" if roses == \"red\" {\n" +" debug::exit(debug::EXIT_SUCCESS);\n" +" } else {\n" +" debug::exit(debug::EXIT_FAILURE);\n" +" }\n" +"\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/start/semihosting.md:89 +msgid "" +"``` text\n" +"$ cargo run\n" +" Running `qemu-system-arm (..)\n" +"\n" +"$ echo $?\n" +"1\n" +"```" +msgstr "" + +#: src/start/semihosting.md:97 +msgid "" +"One last tip: you can set the panicking behavior to `exit(EXIT_FAILURE)`. " +"This\n" +"will let you write `no_std` run-pass tests that you can run on QEMU." +msgstr "" + +#: src/start/semihosting.md:100 +msgid "" +"For convenience, the `panic-semihosting` crate has an \"exit\" feature that " +"when\n" +"enabled invokes `exit(EXIT_FAILURE)` after logging the panic message to the " +"host\n" +"stderr." +msgstr "" + +#: src/start/semihosting.md:104 +msgid "" +"```rust,ignore\n" +"#![no_main]\n" +"#![no_std]\n" +"\n" +"use panic_semihosting as _; // features = [\"exit\"]\n" +"\n" +"use cortex_m_rt::entry;\n" +"use cortex_m_semihosting::debug;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" let roses = \"blue\";\n" +"\n" +" assert_eq!(roses, \"red\");\n" +"\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/start/semihosting.md:123 +msgid "" +"``` text\n" +"$ cargo run\n" +" Running `qemu-system-arm (..)\n" +"panicked at 'assertion failed: `(left == right)`\n" +" left: `\"blue\"`,\n" +" right: `\"red\"`', examples/hello.rs:15:5\n" +"\n" +"$ echo $?\n" +"1\n" +"```" +msgstr "" + +#: src/start/semihosting.md:134 +msgid "" +"**NOTE**: To enable this feature on `panic-semihosting`, edit your\n" +"`Cargo.toml` dependencies section where `panic-semihosting` is specified " +"with:" +msgstr "" + +#: src/start/semihosting.md:137 +msgid "" +"``` toml\n" +"panic-semihosting = { version = \"VERSION\", features = [\"exit\"] }\n" +"```" +msgstr "" + +#: src/start/semihosting.md:141 +msgid "" +"where `VERSION` is the version desired. For more information on " +"dependencies\n" +"features check the [`specifying dependencies`] section of the Cargo book." +msgstr "" + +#: src/start/panicking.md:1 +msgid "# Panicking" +msgstr "" + +#: src/start/panicking.md:3 +msgid "" +"Panicking is a core part of the Rust language. Built-in operations like " +"indexing\n" +"are runtime checked for memory safety. When out of bounds indexing is " +"attempted\n" +"this results in a panic." +msgstr "" + +#: src/start/panicking.md:7 +msgid "" +"In the standard library panicking has a defined behavior: it unwinds the " +"stack\n" +"of the panicking thread, unless the user opted for aborting the program on\n" +"panics." +msgstr "" + +#: src/start/panicking.md:11 +msgid "" +"In programs without standard library, however, the panicking behavior is " +"left\n" +"undefined. A behavior can be chosen by declaring a `#[panic_handler]` " +"function.\n" +"This function must appear exactly *once* in the dependency graph of a " +"program,\n" +"and must have the following signature: `fn(&PanicInfo) -> !`, where " +"[`PanicInfo`]\n" +"is a struct containing information about the location of the panic." +msgstr "" + +#: src/start/panicking.md:19 +msgid "" +"Given that embedded systems range from user facing to safety critical " +"(cannot\n" +"crash) there's no one size fits all panicking behavior but there are plenty " +"of\n" +"commonly used behaviors. These common behaviors have been packaged into " +"crates\n" +"that define the `#[panic_handler]` function. Some examples include:" +msgstr "" + +#: src/start/panicking.md:24 +msgid "" +"- [`panic-abort`]. A panic causes the abort instruction to be executed.\n" +"- [`panic-halt`]. A panic causes the program, or the current thread, to halt " +"by\n" +" entering an infinite loop.\n" +"- [`panic-itm`]. The panicking message is logged using the ITM, an ARM " +"Cortex-M\n" +" specific peripheral.\n" +"- [`panic-semihosting`]. The panicking message is logged to the host using " +"the\n" +" semihosting technique." +msgstr "" + +#: src/start/panicking.md:37 +msgid "" +"You may be able to find even more crates searching for the [`panic-" +"handler`]\n" +"keyword on crates.io." +msgstr "" + +#: src/start/panicking.md:42 +msgid "" +"A program can pick one of these behaviors simply by linking to the " +"corresponding\n" +"crate. The fact that the panicking behavior is expressed in the source of\n" +"an application as a single line of code is not only useful as documentation " +"but\n" +"can also be used to change the panicking behavior according to the " +"compilation\n" +"profile. For example:" +msgstr "" + +#: src/start/panicking.md:48 +msgid "" +"``` rust,ignore\n" +"#![no_main]\n" +"#![no_std]\n" +"\n" +"// dev profile: easier to debug panics; can put a breakpoint on " +"`rust_begin_unwind`\n" +"#[cfg(debug_assertions)]\n" +"use panic_halt as _;\n" +"\n" +"// release profile: minimize the binary size of the application\n" +"#[cfg(not(debug_assertions))]\n" +"use panic_abort as _;\n" +"\n" +"// ..\n" +"```" +msgstr "" + +#: src/start/panicking.md:63 +msgid "" +"In this example the crate links to the `panic-halt` crate when built with " +"the\n" +"dev profile (`cargo build`), but links to the `panic-abort` crate when " +"built\n" +"with the release profile (`cargo build --release`)." +msgstr "" + +#: src/start/panicking.md:67 +msgid "" +"> The `use panic_abort as _;` form of the `use` statement is used to ensure " +"the `panic_abort` panic handler is\n" +"> included in our final executable while making it clear to the compiler " +"that we won't explicitly use anything from\n" +"> the crate. Without the `as _` rename, the compiler would warn that we have " +"an unused import.\n" +"> Sometimes you might see `extern crate panic_abort` instead, which is an " +"older style used before the\n" +"> 2018 edition of Rust, and should now only be used for \"sysroot\" crates " +"(those distributed with Rust itself) such\n" +"> as `proc_macro`, `alloc`, `std`, and `test`." +msgstr "" + +#: src/start/panicking.md:74 +msgid "## An example" +msgstr "" + +#: src/start/panicking.md:76 +msgid "" +"Here's an example that tries to index an array beyond its length. The " +"operation\n" +"results in a panic." +msgstr "" + +#: src/start/panicking.md:79 +msgid "" +"```rust,ignore\n" +"#![no_main]\n" +"#![no_std]\n" +"\n" +"use panic_semihosting as _;\n" +"\n" +"use cortex_m_rt::entry;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" let xs = [0, 1, 2];\n" +" let i = xs.len() + 1;\n" +" let _y = xs[i]; // out of bounds access\n" +"\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/start/panicking.md:97 +msgid "" +"This example chose the `panic-semihosting` behavior which prints the panic\n" +"message to the host console using semihosting." +msgstr "" + +#: src/start/panicking.md:100 +msgid "" +"``` text\n" +"$ cargo run\n" +" Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb (..)\n" +"panicked at 'index out of bounds: the len is 3 but the index is 4', src/main." +"rs:12:13\n" +"```" +msgstr "" + +#: src/start/panicking.md:106 +msgid "" +"You can try changing the behavior to `panic-halt` and confirm that no " +"message is\n" +"printed in that case." +msgstr "" + +#: src/start/exceptions.md:1 +msgid "# Exceptions" +msgstr "" + +#: src/start/exceptions.md:3 +msgid "" +"Exceptions, and interrupts, are a hardware mechanism by which the processor\n" +"handles asynchronous events and fatal errors (e.g. executing an invalid\n" +"instruction). Exceptions imply preemption and involve exception handlers,\n" +"subroutines executed in response to the signal that triggered the event." +msgstr "" + +#: src/start/exceptions.md:8 +msgid "" +"The `cortex-m-rt` crate provides an [`exception`] attribute to declare " +"exception\n" +"handlers." +msgstr "" + +#: src/start/exceptions.md:13 +msgid "" +"``` rust,ignore\n" +"// Exception handler for the SysTick (System Timer) exception\n" +"#[exception]\n" +"fn SysTick() {\n" +" // ..\n" +"}\n" +"```" +msgstr "" + +#: src/start/exceptions.md:21 +msgid "" +"Other than the `exception` attribute exception handlers look like plain\n" +"functions but there's one more difference: `exception` handlers can *not* " +"be\n" +"called by software. Following the previous example, the statement `SysTick();" +"`\n" +"would result in a compilation error." +msgstr "" + +#: src/start/exceptions.md:26 +msgid "" +"This behavior is pretty much intended and it's required to provide a " +"feature:\n" +"`static mut` variables declared *inside* `exception` handlers are *safe* to " +"use." +msgstr "" + +#: src/start/exceptions.md:29 +msgid "" +"``` rust,ignore\n" +"#[exception]\n" +"fn SysTick() {\n" +" static mut COUNT: u32 = 0;\n" +"\n" +" // `COUNT` has transformed to type `&mut u32` and it's safe to use\n" +" *COUNT += 1;\n" +"}\n" +"```" +msgstr "" + +#: src/start/exceptions.md:39 +msgid "" +"As you may know, using `static mut` variables in a function makes it\n" +"[*non-reentrant*](https://en.wikipedia.org/wiki/Reentrancy_(computing)). " +"It's undefined behavior to call a non-reentrant function,\n" +"directly or indirectly, from more than one exception / interrupt handler or " +"from\n" +"`main` and one or more exception / interrupt handlers." +msgstr "" + +#: src/start/exceptions.md:44 +msgid "" +"Safe Rust must never result in undefined behavior so non-reentrant " +"functions\n" +"must be marked as `unsafe`. Yet I just told that `exception` handlers can " +"safely\n" +"use `static mut` variables. How is this possible? This is possible because\n" +"`exception` handlers can *not* be called by software thus reentrancy is not\n" +"possible." +msgstr "" + +#: src/start/exceptions.md:50 +msgid "" +"> Note that the `exception` attribute transforms definitions of static " +"variables\n" +"> inside the function by wrapping them into `unsafe` blocks and providing " +"us\n" +"> with new appropriate variables of type `&mut` of the same name.\n" +"> Thus we can dereference the reference via `*` to access the values of the " +"variables without\n" +"> needing to wrap them in an `unsafe` block." +msgstr "" + +#: src/start/exceptions.md:56 +msgid "## A complete example" +msgstr "" + +#: src/start/exceptions.md:58 +msgid "" +"Here's an example that uses the system timer to raise a `SysTick` exception\n" +"roughly every second. The `SysTick` exception handler keeps track of how " +"many\n" +"times it has been called in the `COUNT` variable and then prints the value " +"of\n" +"`COUNT` to the host console using semihosting." +msgstr "" + +#: src/start/exceptions.md:63 +msgid "" +"> **NOTE**: You can run this example on any Cortex-M device; you can also " +"run it\n" +"> on QEMU" +msgstr "" + +#: src/start/exceptions.md:66 +msgid "" +"```rust,ignore\n" +"#![deny(unsafe_code)]\n" +"#![no_main]\n" +"#![no_std]\n" +"\n" +"use panic_halt as _;\n" +"\n" +"use core::fmt::Write;\n" +"\n" +"use cortex_m::peripheral::syst::SystClkSource;\n" +"use cortex_m_rt::{entry, exception};\n" +"use cortex_m_semihosting::{\n" +" debug,\n" +" hio::{self, HStdout},\n" +"};\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" let p = cortex_m::Peripherals::take().unwrap();\n" +" let mut syst = p.SYST;\n" +"\n" +" // configures the system timer to trigger a SysTick exception every " +"second\n" +" syst.set_clock_source(SystClkSource::Core);\n" +" // this is configured for the LM3S6965 which has a default CPU clock of " +"12 MHz\n" +" syst.set_reload(12_000_000);\n" +" syst.clear_current();\n" +" syst.enable_counter();\n" +" syst.enable_interrupt();\n" +"\n" +" loop {}\n" +"}\n" +"\n" +"#[exception]\n" +"fn SysTick() {\n" +" static mut COUNT: u32 = 0;\n" +" static mut STDOUT: Option = None;\n" +"\n" +" *COUNT += 1;\n" +"\n" +" // Lazy initialization\n" +" if STDOUT.is_none() {\n" +" *STDOUT = hio::hstdout().ok();\n" +" }\n" +"\n" +" if let Some(hstdout) = STDOUT.as_mut() {\n" +" write!(hstdout, \"{}\", *COUNT).ok();\n" +" }\n" +"\n" +" // IMPORTANT omit this `if` block if running on real hardware or your\n" +" // debugger will end in an inconsistent state\n" +" if *COUNT == 9 {\n" +" // This will terminate the QEMU process\n" +" debug::exit(debug::EXIT_SUCCESS);\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/start/exceptions.md:123 +msgid "" +"``` console\n" +"tail -n5 Cargo.toml\n" +"```" +msgstr "" + +#: src/start/exceptions.md:127 +msgid "" +"``` toml\n" +"[dependencies]\n" +"cortex-m = \"0.5.7\"\n" +"cortex-m-rt = \"0.6.3\"\n" +"panic-halt = \"0.2.0\"\n" +"cortex-m-semihosting = \"0.3.1\"\n" +"```" +msgstr "" + +#: src/start/exceptions.md:135 +msgid "" +"``` text\n" +"$ cargo run --release\n" +" Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb (..)\n" +"123456789\n" +"```" +msgstr "" + +#: src/start/exceptions.md:141 +msgid "" +"If you run this on the Discovery board you'll see the output on the OpenOCD\n" +"console. Also, the program will *not* stop when the count reaches 9." +msgstr "" + +#: src/start/exceptions.md:144 +msgid "## The default exception handler" +msgstr "" + +#: src/start/exceptions.md:146 +msgid "" +"What the `exception` attribute actually does is *override* the default " +"exception\n" +"handler for a specific exception. If you don't override the handler for a\n" +"particular exception it will be handled by the `DefaultHandler` function, " +"which\n" +"defaults to:" +msgstr "" + +#: src/start/exceptions.md:151 +msgid "" +"``` rust,ignore\n" +"fn DefaultHandler() {\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/start/exceptions.md:157 +msgid "" +"This function is provided by the `cortex-m-rt` crate and marked as\n" +"`#[no_mangle]` so you can put a breakpoint on \"DefaultHandler\" and catch\n" +"*unhandled* exceptions." +msgstr "" + +#: src/start/exceptions.md:161 +msgid "" +"It's possible to override this `DefaultHandler` using the `exception` " +"attribute:" +msgstr "" + +#: src/start/exceptions.md:163 +msgid "" +"``` rust,ignore\n" +"#[exception]\n" +"fn DefaultHandler(irqn: i16) {\n" +" // custom default handler\n" +"}\n" +"```" +msgstr "" + +#: src/start/exceptions.md:170 +msgid "" +"The `irqn` argument indicates which exception is being serviced. A negative\n" +"value indicates that a Cortex-M exception is being serviced; and zero or a\n" +"positive value indicate that a device specific exception, AKA interrupt, is\n" +"being serviced." +msgstr "" + +#: src/start/exceptions.md:175 +msgid "## The hard fault handler" +msgstr "" + +#: src/start/exceptions.md:177 +msgid "" +"The `HardFault` exception is a bit special. This exception is fired when " +"the\n" +"program enters an invalid state so its handler can *not* return as that " +"could\n" +"result in undefined behavior. Also, the runtime crate does a bit of work " +"before\n" +"the user defined `HardFault` handler is invoked to improve debuggability." +msgstr "" + +#: src/start/exceptions.md:182 +msgid "" +"The result is that the `HardFault` handler must have the following " +"signature:\n" +"`fn(&ExceptionFrame) -> !`. The argument of the handler is a pointer to\n" +"registers that were pushed into the stack by the exception. These registers " +"are\n" +"a snapshot of the processor state at the moment the exception was triggered " +"and\n" +"are useful to diagnose a hard fault." +msgstr "" + +#: src/start/exceptions.md:188 +msgid "" +"Here's an example that performs an illegal operation: a read to a " +"nonexistent\n" +"memory location." +msgstr "" + +#: src/start/exceptions.md:191 +msgid "" +"> **NOTE**: This program won't work, i.e. it won't crash, on QEMU because\n" +"> `qemu-system-arm -machine lm3s6965evb` doesn't check memory loads and " +"will\n" +"> happily return `0 `on reads to invalid memory." +msgstr "" + +#: src/start/exceptions.md:195 +msgid "" +"```rust,ignore\n" +"#![no_main]\n" +"#![no_std]\n" +"\n" +"use panic_halt as _;\n" +"\n" +"use core::fmt::Write;\n" +"use core::ptr;\n" +"\n" +"use cortex_m_rt::{entry, exception, ExceptionFrame};\n" +"use cortex_m_semihosting::hio;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" // read a nonexistent memory location\n" +" unsafe {\n" +" ptr::read_volatile(0x3FFF_FFFE as *const u32);\n" +" }\n" +"\n" +" loop {}\n" +"}\n" +"\n" +"#[exception]\n" +"fn HardFault(ef: &ExceptionFrame) -> ! {\n" +" if let Ok(mut hstdout) = hio::hstdout() {\n" +" writeln!(hstdout, \"{:#?}\", ef).ok();\n" +" }\n" +"\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/start/exceptions.md:227 +msgid "" +"The `HardFault` handler prints the `ExceptionFrame` value. If you run this\n" +"you'll see something like this on the OpenOCD console." +msgstr "" + +#: src/start/exceptions.md:230 +msgid "" +"``` text\n" +"$ openocd\n" +"(..)\n" +"ExceptionFrame {\n" +" r0: 0x3ffffffe,\n" +" r1: 0x00f00000,\n" +" r2: 0x20000000,\n" +" r3: 0x00000000,\n" +" r12: 0x00000000,\n" +" lr: 0x080008f7,\n" +" pc: 0x0800094a,\n" +" xpsr: 0x61000000\n" +"}\n" +"```" +msgstr "" + +#: src/start/exceptions.md:245 +msgid "" +"The `pc` value is the value of the Program Counter at the time of the " +"exception\n" +"and it points to the instruction that triggered the exception." +msgstr "" + +#: src/start/exceptions.md:248 +msgid "If you look at the disassembly of the program:" +msgstr "" + +#: src/start/exceptions.md:251 +msgid "" +"``` text\n" +"$ cargo objdump --bin app --release -- -d --no-show-raw-insn --print-imm-" +"hex\n" +"(..)\n" +"ResetTrampoline:\n" +" 8000942: movw r0, #0xfffe\n" +" 8000946: movt r0, #0x3fff\n" +" 800094a: ldr r0, [r0]\n" +" 800094c: b #-0x4 \n" +"```" +msgstr "" + +#: src/start/exceptions.md:261 +msgid "" +"You can lookup the value of the program counter `0x0800094a` in the " +"disassembly.\n" +"You'll see that a load operation (`ldr r0, [r0]` ) caused the exception.\n" +"The `r0` field of `ExceptionFrame` will tell you the value of register `r0`\n" +"was `0x3fff_fffe` at that time." +msgstr "" + +#: src/start/interrupts.md:1 +msgid "# Interrupts" +msgstr "" + +#: src/start/interrupts.md:3 +msgid "" +"Interrupts differ from exceptions in a variety of ways but their operation " +"and\n" +"use is largely similar and they are also handled by the same interrupt\n" +"controller. Whereas exceptions are defined by the Cortex-M architecture,\n" +"interrupts are always vendor (and often even chip) specific " +"implementations,\n" +"both in naming and functionality." +msgstr "" + +#: src/start/interrupts.md:9 +msgid "" +"Interrupts do allow for a lot of flexibility which needs to be accounted " +"for\n" +"when attempting to use them in an advanced way. We will not cover those uses " +"in\n" +"this book, however it is a good idea to keep the following in mind:" +msgstr "" + +#: src/start/interrupts.md:13 +msgid "" +"* Interrupts have programmable priorities which determine their handlers' " +"execution order\n" +"* Interrupts can nest and preempt, i.e. execution of an interrupt handler " +"might be interrupted by another higher-priority interrupt\n" +"* In general the reason causing the interrupt to trigger needs to be cleared " +"to prevent re-entering the interrupt handler endlessly" +msgstr "" + +#: src/start/interrupts.md:17 +msgid "The general initialization steps at runtime are always the same:" +msgstr "" + +#: src/start/interrupts.md:18 +msgid "" +"* Setup the peripheral(s) to generate interrupts requests at the desired " +"occasions\n" +"* Set the desired priority of the interrupt handler in the interrupt " +"controller\n" +"* Enable the interrupt handler in the interrupt controller" +msgstr "" + +#: src/start/interrupts.md:22 +msgid "" +"Similarly to exceptions, the `cortex-m-rt` crate provides an [`interrupt`]\n" +"attribute to declare interrupt handlers. The available interrupts (and\n" +"their position in the interrupt handler table) are usually automatically\n" +"generated via `svd2rust` from a SVD description." +msgstr "" + +#: src/start/interrupts.md:29 +msgid "" +"``` rust,ignore\n" +"// Interrupt handler for the Timer2 interrupt\n" +"#[interrupt]\n" +"fn TIM2() {\n" +" // ..\n" +" // Clear reason for the generated interrupt request\n" +"}\n" +"```" +msgstr "" + +#: src/start/interrupts.md:38 +msgid "" +"Interrupt handlers look like plain functions (except for the lack of " +"arguments)\n" +"similar to exception handlers. However they can not be called directly by " +"other\n" +"parts of the firmware due to the special calling conventions. It is however\n" +"possible to generate interrupt requests in software to trigger a diversion " +"to\n" +"the interrupt handler." +msgstr "" + +#: src/start/interrupts.md:44 +msgid "" +"Similar to exception handlers it is also possible to declare `static mut`\n" +"variables inside the interrupt handlers for *safe* state keeping." +msgstr "" + +#: src/start/interrupts.md:47 +msgid "" +"``` rust,ignore\n" +"#[interrupt]\n" +"fn TIM2() {\n" +" static mut COUNT: u32 = 0;\n" +"\n" +" // `COUNT` has type `&mut u32` and it's safe to use\n" +" *COUNT += 1;\n" +"}\n" +"```" +msgstr "" + +#: src/start/interrupts.md:57 +msgid "" +"For a more detailed description about the mechanisms demonstrated here " +"please\n" +"refer to the [exceptions section]." +msgstr "" + +#: src/start/io.md:1 +msgid "# IO" +msgstr "" + +#: src/start/io.md:3 +msgid "> **TODO** Cover memory mapped I/O using registers." +msgstr "" + +#: src/peripherals/index.md:1 +msgid "# Peripherals" +msgstr "" + +#: src/peripherals/index.md:3 +msgid "## What are Peripherals?" +msgstr "" + +#: src/peripherals/index.md:5 +msgid "" +"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." +msgstr "" + +#: src/peripherals/index.md:7 +msgid "" +"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." +msgstr "" + +#: src/peripherals/index.md:9 +msgid "" +"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:" +msgstr "" + +#: src/peripherals/index.md:11 +msgid "" +"* A processor\n" +"* A RAM chip\n" +"* A ROM chip\n" +"* An I/O controller" +msgstr "" + +#: src/peripherals/index.md:16 +msgid "" +"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." +msgstr "" + +#: src/peripherals/index.md:18 +msgid "" +"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." +msgstr "" + +#: src/peripherals/index.md:20 +msgid "## Linear and Real Memory Space" +msgstr "" + +#: src/peripherals/index.md:22 +msgid "" +"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." +msgstr "" + +#: src/peripherals/index.md:24 +msgid "" +"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." +msgstr "" + +#: src/peripherals/index.md:26 +msgid "" +"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:" +msgstr "" + +#: src/peripherals/index.md:30 src/peripherals/index.md:40 +msgid "[Nordic nRF52832 Datasheet (pdf)]" +msgstr "" + +#: src/peripherals/index.md:32 +msgid "## Memory Mapped Peripherals" +msgstr "" + +#: src/peripherals/index.md:34 +msgid "" +"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." +msgstr "" + +#: src/peripherals/index.md:36 +msgid "" +"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:" +msgstr "" + +#: src/peripherals/index.md:42 +msgid "" +"This interface is how interactions with the hardware are made, no matter " +"what language is used, whether that language is Assembly, C, or Rust." +msgstr "" + +#: src/peripherals/a-first-attempt.md:1 +msgid "# A First Attempt" +msgstr "" + +#: src/peripherals/a-first-attempt.md:3 +msgid "## The Registers" +msgstr "" + +#: src/peripherals/a-first-attempt.md:5 +msgid "" +"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:" +msgstr "" + +#: src/peripherals/a-first-attempt.md:9 +msgid "" +"| Offset | Name | Description | Width |\n" +"|--------|-------------|-----------------------------|--------|\n" +"| 0x00 | SYST_CSR | Control and Status Register | 32 bits|\n" +"| 0x04 | SYST_RVR | Reload Value Register | 32 bits|\n" +"| 0x08 | SYST_CVR | Current Value Register | 32 bits|\n" +"| 0x0C | SYST_CALIB | Calibration Value Register | 32 bits|" +msgstr "" + +#: src/peripherals/a-first-attempt.md:16 +msgid "## The C Approach" +msgstr "" + +#: src/peripherals/a-first-attempt.md:18 +msgid "" +"In Rust, we can represent a collection of registers in exactly the same way " +"as we do in C - with a `struct`." +msgstr "" + +#: src/peripherals/a-first-attempt.md:20 +msgid "" +"```rust,ignore\n" +"#[repr(C)]\n" +"struct SysTick {\n" +" pub csr: u32,\n" +" pub rvr: u32,\n" +" pub cvr: u32,\n" +" pub calib: u32,\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/a-first-attempt.md:30 +msgid "" +"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." +msgstr "" + +#: src/peripherals/a-first-attempt.md:32 +msgid "" +"```rust,ignore\n" +"let systick = 0xE000_E010 as *mut SysTick;\n" +"let time = unsafe { (*systick).cvr };\n" +"```" +msgstr "" + +#: src/peripherals/a-first-attempt.md:37 +msgid "## Volatile Accesses" +msgstr "" + +#: src/peripherals/a-first-attempt.md:39 +msgid "Now, there are a couple of problems with the approach above." +msgstr "" + +#: src/peripherals/a-first-attempt.md:41 +msgid "" +"1. We have to use unsafe every time we want to access our Peripheral.\n" +"2. We've got no way of specifying which registers are read-only or read-" +"write.\n" +"3. Any piece of code anywhere in your program could access the hardware\n" +" through this structure.\n" +"4. Most importantly, it doesn't actually work..." +msgstr "" + +#: src/peripherals/a-first-attempt.md:47 +msgid "" +"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." +msgstr "" + +#: src/peripherals/a-first-attempt.md:49 +msgid "" +"```rust,ignore\n" +"let systick = unsafe { &mut *(0xE000_E010 as *mut SysTick) };\n" +"let time = unsafe { core::ptr::read_volatile(&mut systick.cvr) };\n" +"```" +msgstr "" + +#: src/peripherals/a-first-attempt.md:54 +msgid "" +"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`]." +msgstr "" + +#: src/peripherals/a-first-attempt.md:58 +msgid "" +"```rust,ignore\n" +"use volatile_register::{RW, RO};\n" +"\n" +"#[repr(C)]\n" +"struct SysTick {\n" +" pub csr: RW,\n" +" pub rvr: RW,\n" +" pub cvr: RW,\n" +" pub calib: RO,\n" +"}\n" +"\n" +"fn get_systick() -> &'static mut SysTick {\n" +" unsafe { &mut *(0xE000_E010 as *mut SysTick) }\n" +"}\n" +"\n" +"fn get_time() -> u32 {\n" +" let systick = get_systick();\n" +" systick.cvr.read()\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/a-first-attempt.md:79 +msgid "" +"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." +msgstr "" + +#: src/peripherals/a-first-attempt.md:81 +msgid "## The Rusty Wrapper" +msgstr "" + +#: src/peripherals/a-first-attempt.md:83 +msgid "" +"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!)." +msgstr "" + +#: src/peripherals/a-first-attempt.md:85 +msgid "One example might be:" +msgstr "" + +#: src/peripherals/a-first-attempt.md:87 +msgid "" +"```rust,ignore\n" +"use volatile_register::{RW, RO};\n" +"\n" +"pub struct SystemTimer {\n" +" p: &'static mut RegisterBlock\n" +"}\n" +"\n" +"#[repr(C)]\n" +"struct RegisterBlock {\n" +" pub csr: RW,\n" +" pub rvr: RW,\n" +" pub cvr: RW,\n" +" pub calib: RO,\n" +"}\n" +"\n" +"impl SystemTimer {\n" +" pub fn new() -> SystemTimer {\n" +" SystemTimer {\n" +" p: unsafe { &mut *(0xE000_E010 as *mut RegisterBlock) }\n" +" }\n" +" }\n" +"\n" +" pub fn get_time(&self) -> u32 {\n" +" self.p.cvr.read()\n" +" }\n" +"\n" +" pub fn set_reload(&mut self, reload_value: u32) {\n" +" unsafe { self.p.rvr.write(reload_value) }\n" +" }\n" +"}\n" +"\n" +"pub fn example_usage() -> String {\n" +" let mut st = SystemTimer::new();\n" +" st.set_reload(0x00FF_FFFF);\n" +" format!(\"Time is now 0x{:08x}\", st.get_time())\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/a-first-attempt.md:125 +msgid "" +"Now, the problem with this approach is that the following code is perfectly " +"acceptable to the compiler:" +msgstr "" + +#: src/peripherals/a-first-attempt.md:127 +msgid "" +"```rust,ignore\n" +"fn thread1() {\n" +" let mut st = SystemTimer::new();\n" +" st.set_reload(2000);\n" +"}\n" +"\n" +"fn thread2() {\n" +" let mut st = SystemTimer::new();\n" +" st.set_reload(1000);\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/a-first-attempt.md:139 +msgid "" +"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." +msgstr "" + +#: src/peripherals/borrowck.md:1 +msgid "## Mutable Global State" +msgstr "" + +#: src/peripherals/borrowck.md:3 +msgid "" +"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." +msgstr "" + +#: src/peripherals/borrowck.md:5 +msgid "## What should our rules be?" +msgstr "" + +#: src/peripherals/borrowck.md:7 +msgid "How can we reliably interact with these peripherals?" +msgstr "" + +#: src/peripherals/borrowck.md:9 +msgid "" +"1. Always use `volatile` methods to read or write to peripheral memory, as " +"it can change at any time\n" +"2. In software, we should be able to share any number of read-only accesses " +"to these peripherals\n" +"3. If some software should have read-write access to a peripheral, it should " +"hold the only reference to that peripheral" +msgstr "" + +#: src/peripherals/borrowck.md:13 +msgid "## The Borrow Checker" +msgstr "" + +#: src/peripherals/borrowck.md:15 +msgid "" +"The last two of these rules sound suspiciously similar to what the Borrow " +"Checker does already!" +msgstr "" + +#: src/peripherals/borrowck.md:17 +msgid "" +"Imagine if we could pass around ownership of these peripherals, or offer " +"immutable or mutable references to them?" +msgstr "" + +#: src/peripherals/borrowck.md:19 +msgid "" +"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?" +msgstr "" + +#: src/peripherals/singletons.md:1 +msgid "# Singletons" +msgstr "" + +#: src/peripherals/singletons.md:3 +msgid "" +"> In software engineering, the singleton pattern is a software design " +"pattern that restricts the instantiation of a class to one object.\n" +">\n" +"> *Wikipedia: [Singleton Pattern]*" +msgstr "" + +#: src/peripherals/singletons.md:10 +msgid "## But why can't we just use global variable(s)?" +msgstr "" + +#: src/peripherals/singletons.md:12 +msgid "We could make everything a public static, like this" +msgstr "" + +#: src/peripherals/singletons.md:14 +msgid "" +"```rust,ignore\n" +"static mut THE_SERIAL_PORT: SerialPort = SerialPort;\n" +"\n" +"fn main() {\n" +" let _ = unsafe {\n" +" THE_SERIAL_PORT.read_speed();\n" +" };\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/singletons.md:24 +msgid "" +"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." +msgstr "" + +#: src/peripherals/singletons.md:26 +msgid "## How do we do this in Rust?" +msgstr "" + +#: src/peripherals/singletons.md:28 +msgid "" +"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." +msgstr "" + +#: src/peripherals/singletons.md:30 +msgid "" +"```rust,ignore\n" +"struct Peripherals {\n" +" serial: Option,\n" +"}\n" +"impl Peripherals {\n" +" fn take_serial(&mut self) -> SerialPort {\n" +" let p = replace(&mut self.serial, None);\n" +" p.unwrap()\n" +" }\n" +"}\n" +"static mut PERIPHERALS: Peripherals = Peripherals {\n" +" serial: Some(SerialPort),\n" +"};\n" +"```" +msgstr "" + +#: src/peripherals/singletons.md:45 +msgid "" +"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!" +msgstr "" + +#: src/peripherals/singletons.md:47 +msgid "" +"```rust,ignore\n" +"fn main() {\n" +" let serial_1 = unsafe { PERIPHERALS.take_serial() };\n" +" // This panics!\n" +" // let serial_2 = unsafe { PERIPHERALS.take_serial() };\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/singletons.md:55 +msgid "" +"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." +msgstr "" + +#: src/peripherals/singletons.md:57 +msgid "" +"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." +msgstr "" + +#: src/peripherals/singletons.md:59 +msgid "## Existing library support" +msgstr "" + +#: src/peripherals/singletons.md:61 +msgid "" +"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." +msgstr "" + +#: src/peripherals/singletons.md:63 +msgid "" +"```rust,ignore\n" +"use cortex_m::singleton;\n" +"\n" +"fn main() {\n" +" // OK if `main` is executed only once\n" +" let x: &'static mut bool =\n" +" singleton!(: bool = false).unwrap();\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/singletons.md:73 +msgid "" +"[cortex_m docs](https://docs.rs/cortex-m/latest/cortex_m/macro.singleton." +"html)" +msgstr "" + +#: src/peripherals/singletons.md:75 +msgid "" +"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." +msgstr "" + +#: src/peripherals/singletons.md:77 +msgid "" +"```rust,ignore\n" +"// cortex-m-rtic v0.5.x\n" +"#[rtic::app(device = lm3s6965, peripherals = true)]\n" +"const APP: () = {\n" +" #[init]\n" +" fn init(cx: init::Context) {\n" +" static mut X: u32 = 0;\n" +" \n" +" // Cortex-M peripherals\n" +" let core: cortex_m::Peripherals = cx.core;\n" +" \n" +" // Device specific peripherals\n" +" let device: lm3s6965::Peripherals = cx.device;\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/singletons.md:94 +msgid "## But why?" +msgstr "" + +#: src/peripherals/singletons.md:96 +msgid "" +"But how do these Singletons make a noticeable difference in how our Rust " +"code works?" +msgstr "" + +#: src/peripherals/singletons.md:98 +msgid "" +"```rust,ignore\n" +"impl SerialPort {\n" +" const SER_PORT_SPEED_REG: *mut u32 = 0x4000_1000 as _;\n" +"\n" +" fn read_speed(\n" +" &self // <------ This is really, really important\n" +" ) -> u32 {\n" +" unsafe {\n" +" ptr::read_volatile(Self::SER_PORT_SPEED_REG)\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/singletons.md:112 +msgid "There are two important factors in play here:" +msgstr "" + +#: src/peripherals/singletons.md:114 +msgid "" +"* Because we are using a singleton, there is only one way or place to obtain " +"a `SerialPort` structure\n" +"* To call the `read_speed()` method, we must have ownership or a reference " +"to a `SerialPort` structure" +msgstr "" + +#: src/peripherals/singletons.md:117 +msgid "" +"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!" +msgstr "" + +#: src/peripherals/singletons.md:119 +msgid "" +"```rust,ignore\n" +"fn main() {\n" +" // missing reference to `self`! Won't work.\n" +" // SerialPort::read_speed();\n" +"\n" +" let serial_1 = unsafe { PERIPHERALS.take_serial() };\n" +"\n" +" // you can only read what you have access to\n" +" let _ = serial_1.read_speed();\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/singletons.md:131 +msgid "## Treat your hardware like data" +msgstr "" + +#: src/peripherals/singletons.md:133 +msgid "" +"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," +msgstr "" + +#: src/peripherals/singletons.md:135 +msgid "This is allowed to change hardware settings:" +msgstr "" + +#: src/peripherals/singletons.md:137 +msgid "" +"```rust,ignore\n" +"fn setup_spi_port(\n" +" spi: &mut SpiPort,\n" +" cs_pin: &mut GpioPin\n" +") -> Result<()> {\n" +" // ...\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/singletons.md:146 +msgid "This isn't:" +msgstr "" + +#: src/peripherals/singletons.md:148 +msgid "" +"```rust,ignore\n" +"fn read_button(gpio: &GpioPin) -> bool {\n" +" // ...\n" +"}\n" +"```" +msgstr "" + +#: src/peripherals/singletons.md:154 +msgid "" +"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." +msgstr "" + +#: src/static-guarantees/index.md:1 +msgid "# Static Guarantees" +msgstr "" + +#: src/static-guarantees/index.md:3 +msgid "" +"Rust's type system prevents data races at compile time (see [`Send`] and\n" +"[`Sync`] traits). The type system can also be used to check other properties " +"at\n" +"compile time; reducing the need for runtime checks in some cases." +msgstr "" + +#: src/static-guarantees/index.md:10 +msgid "" +"When applied to embedded programs these *static checks* can be used, for\n" +"example, to enforce that configuration of I/O interfaces is done properly. " +"For\n" +"instance, one can design an API where it is only possible to initialize a " +"serial\n" +"interface by first configuring the pins that will be used by the interface." +msgstr "" + +#: src/static-guarantees/index.md:15 +msgid "" +"One can also statically check that operations, like setting a pin low, can " +"only\n" +"be performed on correctly configured peripherals. For example, trying to " +"change\n" +"the output state of a pin configured in floating input mode would raise a\n" +"compile error." +msgstr "" + +#: src/static-guarantees/index.md:20 +msgid "" +"And, as seen in the previous chapter, the concept of ownership can be " +"applied\n" +"to peripherals to ensure that only certain parts of a program can modify a\n" +"peripheral. This *access control* makes software easier to reason about\n" +"compared to the alternative of treating peripherals as global mutable state." +msgstr "" + +#: src/static-guarantees/typestate-programming.md:1 +msgid "# Typestate Programming" +msgstr "" + +#: src/static-guarantees/typestate-programming.md:3 +msgid "" +"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!" +msgstr "" + +#: src/static-guarantees/typestate-programming.md:8 +msgid "" +"```rust\n" +"pub mod foo_module {\n" +" #[derive(Debug)]\n" +" pub struct Foo {\n" +" inner: u32,\n" +" }\n" +"\n" +" pub struct FooBuilder {\n" +" a: u32,\n" +" b: u32,\n" +" }\n" +"\n" +" impl FooBuilder {\n" +" pub fn new(starter: u32) -> Self {\n" +" Self {\n" +" a: starter,\n" +" b: starter,\n" +" }\n" +" }\n" +"\n" +" pub fn double_a(self) -> Self {\n" +" Self {\n" +" a: self.a * 2,\n" +" b: self.b,\n" +" }\n" +" }\n" +"\n" +" pub fn into_foo(self) -> Foo {\n" +" Foo {\n" +" inner: self.a + self.b,\n" +" }\n" +" }\n" +" }\n" +"}\n" +"\n" +"fn main() {\n" +" let x = foo_module::FooBuilder::new(10)\n" +" .double_a()\n" +" .into_foo();\n" +"\n" +" println!(\"{:#?}\", x);\n" +"}\n" +"```" +msgstr "" + +#: src/static-guarantees/typestate-programming.md:52 +msgid "" +"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." +msgstr "" + +#: src/static-guarantees/typestate-programming.md:54 +msgid "This minimal example encodes two states:" +msgstr "" + +#: src/static-guarantees/typestate-programming.md:56 +msgid "" +"* `FooBuilder`, which represents an \"unconfigured\", or \"configuration in " +"process\" state\n" +"* `Foo`, which represents a \"configured\", or \"ready to use\" state." +msgstr "" + +#: src/static-guarantees/typestate-programming.md:59 +msgid "## Strong Types" +msgstr "" + +#: src/static-guarantees/typestate-programming.md:61 +msgid "" +"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." +msgstr "" + +#: src/static-guarantees/typestate-programming.md:65 +msgid "" +"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." +msgstr "" + +#: src/static-guarantees/state-machines.md:1 +msgid "# Peripherals as State Machines" +msgstr "" + +#: src/static-guarantees/state-machines.md:3 +msgid "" +"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:" +msgstr "" + +#: src/static-guarantees/state-machines.md:7 +msgid "" +"* Disabled\n" +"* Enabled\n" +" * Configured as Output\n" +" * Output: High\n" +" * Output: Low\n" +" * Configured as Input\n" +" * Input: High Resistance\n" +" * Input: Pulled Low\n" +" * Input: Pulled High" +msgstr "" + +#: src/static-guarantees/state-machines.md:17 +msgid "" +"If the peripheral starts in the `Disabled` mode, to move to the `Input: High " +"Resistance` mode, we must perform the following steps:" +msgstr "" + +#: src/static-guarantees/state-machines.md:19 +msgid "" +"1. Disabled\n" +"2. Enabled\n" +"3. Configured as Input\n" +"4. Input: High Resistance" +msgstr "" + +#: src/static-guarantees/state-machines.md:24 +msgid "" +"If we wanted to move from `Input: High Resistance` to `Input: Pulled Low`, " +"we must perform the following steps:" +msgstr "" + +#: src/static-guarantees/state-machines.md:26 +msgid "" +"1. Input: High Resistance\n" +"2. Input: Pulled Low" +msgstr "" + +#: src/static-guarantees/state-machines.md:29 +msgid "" +"Similarly, if we want to move a GPIO pin from configured as `Input: Pulled " +"Low` to `Output: High`, we must perform the following steps:" +msgstr "" + +#: src/static-guarantees/state-machines.md:31 +msgid "" +"1. Input: Pulled Low\n" +"2. Configured as Input\n" +"3. Configured as Output\n" +"4. Output: High" +msgstr "" + +#: src/static-guarantees/state-machines.md:36 +msgid "## Hardware Representation" +msgstr "" + +#: src/static-guarantees/state-machines.md:38 +msgid "" +"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:" +msgstr "" + +#: src/static-guarantees/state-machines.md:40 +#: src/static-guarantees/design-contracts.md:5 +msgid "" +"| Name | Bit Number(s) | Value | Meaning | Notes |\n" +"| ---: | ------------: | ----: | ------: | ----: |\n" +"| enable | 0 | 0 | disabled | Disables the GPIO |\n" +"| | | 1 | enabled | Enables the GPIO |\n" +"| direction | 1 | 0 | input | Sets the direction to " +"Input |\n" +"| | | 1 | output | Sets the direction to " +"Output |\n" +"| input_mode | 2..3 | 00 | hi-z | Sets the input as high " +"resistance |\n" +"| | | 01 | pull-low | Input pin is pulled low " +"|\n" +"| | | 10 | pull-high | Input pin is pulled " +"high |\n" +"| | | 11 | n/a | Invalid state. Do not " +"set |\n" +"| output_mode | 4 | 0 | set-low | Output pin is driven " +"low |\n" +"| | | 1 | set-high | Output pin is driven " +"high |\n" +"| input_status | 5 | x | in-val | 0 if input is < 1.5v, 1 " +"if input >= 1.5v |" +msgstr "" + +#: src/static-guarantees/state-machines.md:54 +msgid "We _could_ expose the following structure in Rust to control this GPIO:" +msgstr "" + +#: src/static-guarantees/state-machines.md:56 +msgid "" +"```rust,ignore\n" +"/// GPIO interface\n" +"struct GpioConfig {\n" +" /// GPIO Configuration structure generated by svd2rust\n" +" periph: GPIO_CONFIG,\n" +"}\n" +"\n" +"impl GpioConfig {\n" +" pub fn set_enable(&mut self, is_enabled: bool) {\n" +" self.periph.modify(|_r, w| {\n" +" w.enable().set_bit(is_enabled)\n" +" });\n" +" }\n" +"\n" +" pub fn set_direction(&mut self, is_output: bool) {\n" +" self.periph.modify(|_r, w| {\n" +" w.direction().set_bit(is_output)\n" +" });\n" +" }\n" +"\n" +" pub fn set_input_mode(&mut self, variant: InputMode) {\n" +" self.periph.modify(|_r, w| {\n" +" w.input_mode().variant(variant)\n" +" });\n" +" }\n" +"\n" +" pub fn set_output_mode(&mut self, is_high: bool) {\n" +" self.periph.modify(|_r, w| {\n" +" w.output_mode.set_bit(is_high)\n" +" });\n" +" }\n" +"\n" +" pub fn get_input_status(&self) -> bool {\n" +" self.periph.read().input_status().bit_is_set()\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/static-guarantees/state-machines.md:94 +msgid "" +"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? " +msgstr "" + +#: src/static-guarantees/state-machines.md:96 +msgid "" +"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!" +msgstr "" + +#: src/static-guarantees/state-machines.md:98 +msgid "" +"Although this interface is convenient to write, it doesn't enforce the " +"design contracts set out by our hardware implementation." +msgstr "" + +#: src/static-guarantees/design-contracts.md:1 +msgid "# Design Contracts" +msgstr "" + +#: src/static-guarantees/design-contracts.md:3 +msgid "" +"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:" +msgstr "" + +#: src/static-guarantees/design-contracts.md:19 +msgid "" +"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:" +msgstr "" + +#: src/static-guarantees/design-contracts.md:21 +msgid "" +"```rust,ignore\n" +"/// GPIO interface\n" +"struct GpioConfig {\n" +" /// GPIO Configuration structure generated by svd2rust\n" +" periph: GPIO_CONFIG,\n" +"}\n" +"\n" +"impl GpioConfig {\n" +" pub fn set_enable(&mut self, is_enabled: bool) {\n" +" self.periph.modify(|_r, w| {\n" +" w.enable().set_bit(is_enabled)\n" +" });\n" +" }\n" +"\n" +" pub fn set_direction(&mut self, is_output: bool) -> Result<(), ()> {\n" +" if self.periph.read().enable().bit_is_clear() {\n" +" // Must be enabled to set direction\n" +" return Err(());\n" +" }\n" +"\n" +" self.periph.modify(|r, w| {\n" +" w.direction().set_bit(is_output)\n" +" });\n" +"\n" +" Ok(())\n" +" }\n" +"\n" +" pub fn set_input_mode(&mut self, variant: InputMode) -> Result<(), ()> " +"{\n" +" if self.periph.read().enable().bit_is_clear() {\n" +" // Must be enabled to set input mode\n" +" return Err(());\n" +" }\n" +"\n" +" if self.periph.read().direction().bit_is_set() {\n" +" // Direction must be input\n" +" return Err(());\n" +" }\n" +"\n" +" self.periph.modify(|_r, w| {\n" +" w.input_mode().variant(variant)\n" +" });\n" +"\n" +" Ok(())\n" +" }\n" +"\n" +" pub fn set_output_status(&mut self, is_high: bool) -> Result<(), ()> {\n" +" if self.periph.read().enable().bit_is_clear() {\n" +" // Must be enabled to set output status\n" +" return Err(());\n" +" }\n" +"\n" +" if self.periph.read().direction().bit_is_clear() {\n" +" // Direction must be output\n" +" return Err(());\n" +" }\n" +"\n" +" self.periph.modify(|_r, w| {\n" +" w.output_mode.set_bit(is_high)\n" +" });\n" +"\n" +" Ok(())\n" +" }\n" +"\n" +" pub fn get_input_status(&self) -> Result {\n" +" if self.periph.read().enable().bit_is_clear() {\n" +" // Must be enabled to get status\n" +" return Err(());\n" +" }\n" +"\n" +" if self.periph.read().direction().bit_is_set() {\n" +" // Direction must be input\n" +" return Err(());\n" +" }\n" +"\n" +" Ok(self.periph.read().input_status().bit_is_set())\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/static-guarantees/design-contracts.md:100 +msgid "" +"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." +msgstr "" + +#: src/static-guarantees/design-contracts.md:102 +msgid "## Type States" +msgstr "" + +#: src/static-guarantees/design-contracts.md:104 +msgid "" +"But what if instead, we used Rust's type system to enforce the state " +"transition rules? Take this example:" +msgstr "" + +#: src/static-guarantees/design-contracts.md:106 +msgid "" +"```rust,ignore\n" +"/// GPIO interface\n" +"struct GpioConfig {\n" +" /// GPIO Configuration structure generated by svd2rust\n" +" periph: GPIO_CONFIG,\n" +" enabled: ENABLED,\n" +" direction: DIRECTION,\n" +" mode: MODE,\n" +"}\n" +"\n" +"// Type states for MODE in GpioConfig\n" +"struct Disabled;\n" +"struct Enabled;\n" +"struct Output;\n" +"struct Input;\n" +"struct PulledLow;\n" +"struct PulledHigh;\n" +"struct HighZ;\n" +"struct DontCare;\n" +"\n" +"/// These functions may be used on any GPIO Pin\n" +"impl GpioConfig {\n" +" pub fn into_disabled(self) -> GpioConfig " +"{\n" +" self.periph.modify(|_r, w| w.enable.disabled());\n" +" GpioConfig {\n" +" periph: self.periph,\n" +" enabled: Disabled,\n" +" direction: DontCare,\n" +" mode: DontCare,\n" +" }\n" +" }\n" +"\n" +" pub fn into_enabled_input(self) -> GpioConfig {\n" +" self.periph.modify(|_r, w| {\n" +" w.enable.enabled()\n" +" .direction.input()\n" +" .input_mode.high_z()\n" +" });\n" +" GpioConfig {\n" +" periph: self.periph,\n" +" enabled: Enabled,\n" +" direction: Input,\n" +" mode: HighZ,\n" +" }\n" +" }\n" +"\n" +" pub fn into_enabled_output(self) -> GpioConfig {\n" +" self.periph.modify(|_r, w| {\n" +" w.enable.enabled()\n" +" .direction.output()\n" +" .input_mode.set_high()\n" +" });\n" +" GpioConfig {\n" +" periph: self.periph,\n" +" enabled: Enabled,\n" +" direction: Output,\n" +" mode: DontCare,\n" +" }\n" +" }\n" +"}\n" +"\n" +"/// This function may be used on an Output Pin\n" +"impl GpioConfig {\n" +" pub fn set_bit(&mut self, set_high: bool) {\n" +" self.periph.modify(|_r, w| w.output_mode.set_bit(set_high));\n" +" }\n" +"}\n" +"\n" +"/// These methods may be used on any enabled input GPIO\n" +"impl GpioConfig {\n" +" pub fn bit_is_set(&self) -> bool {\n" +" self.periph.read().input_status.bit_is_set()\n" +" }\n" +"\n" +" pub fn into_input_high_z(self) -> GpioConfig {\n" +" self.periph.modify(|_r, w| w.input_mode().high_z());\n" +" GpioConfig {\n" +" periph: self.periph,\n" +" enabled: Enabled,\n" +" direction: Input,\n" +" mode: HighZ,\n" +" }\n" +" }\n" +"\n" +" pub fn into_input_pull_down(self) -> GpioConfig {\n" +" self.periph.modify(|_r, w| w.input_mode().pull_low());\n" +" GpioConfig {\n" +" periph: self.periph,\n" +" enabled: Enabled,\n" +" direction: Input,\n" +" mode: PulledLow,\n" +" }\n" +" }\n" +"\n" +" pub fn into_input_pull_up(self) -> GpioConfig {\n" +" self.periph.modify(|_r, w| w.input_mode().pull_high());\n" +" GpioConfig {\n" +" periph: self.periph,\n" +" enabled: Enabled,\n" +" direction: Input,\n" +" mode: PulledHigh,\n" +" }\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/static-guarantees/design-contracts.md:212 +msgid "Now let's see what the code using this would look like:" +msgstr "" + +#: src/static-guarantees/design-contracts.md:214 +msgid "" +"```rust,ignore\n" +"/*\n" +" * Example 1: Unconfigured to High-Z input\n" +" */\n" +"let pin: GpioConfig = get_gpio();\n" +"\n" +"// Can't do this, pin isn't enabled!\n" +"// pin.into_input_pull_down();\n" +"\n" +"// Now turn the pin from unconfigured to a high-z input\n" +"let input_pin = pin.into_enabled_input();\n" +"\n" +"// Read from the pin\n" +"let pin_state = input_pin.bit_is_set();\n" +"\n" +"// Can't do this, input pins don't have this interface!\n" +"// input_pin.set_bit(true);\n" +"\n" +"/*\n" +" * Example 2: High-Z input to Pulled Low input\n" +" */\n" +"let pulled_low = input_pin.into_input_pull_down();\n" +"let pin_state = pulled_low.bit_is_set();\n" +"\n" +"/*\n" +" * Example 3: Pulled Low input to Output, set high\n" +" */\n" +"let output_pin = pulled_low.into_enabled_output();\n" +"output_pin.set_bit(true);\n" +"\n" +"// Can't do this, output pins don't have this interface!\n" +"// output_pin.into_input_pull_down();\n" +"```" +msgstr "" + +#: src/static-guarantees/design-contracts.md:248 +msgid "" +"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?" +msgstr "" + +#: src/static-guarantees/design-contracts.md:250 +msgid "## Compile Time Functional Safety" +msgstr "" + +#: src/static-guarantees/design-contracts.md:252 +msgid "" +"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." +msgstr "" + +#: src/static-guarantees/design-contracts.md:254 +msgid "" +"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!" +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:1 +msgid "# Zero Cost Abstractions" +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:3 +msgid "" +"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:" +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:5 +msgid "" +"```rust,ignore\n" +"use core::mem::size_of;\n" +"\n" +"let _ = size_of::(); // == 0\n" +"let _ = size_of::(); // == 0\n" +"let _ = size_of::(); // == 0\n" +"let _ = size_of::>(); // == 0\n" +"```" +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:14 +msgid "## Zero Sized Types" +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:16 +msgid "" +"```rust,ignore\n" +"struct Enabled;\n" +"```" +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:20 +msgid "" +"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." +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:22 +msgid "In this snippet of code:" +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:24 +msgid "" +"```rust,ignore\n" +"pub fn into_input_high_z(self) -> GpioConfig {\n" +" self.periph.modify(|_r, w| w.input_mode().high_z());\n" +" GpioConfig {\n" +" periph: self.periph,\n" +" enabled: Enabled,\n" +" direction: Input,\n" +" mode: HighZ,\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:36 +msgid "" +"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." +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:38 +msgid "## Nesting" +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:40 +msgid "" +"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." +msgstr "" + +#: src/static-guarantees/zero-cost-abstractions.md:42 +msgid "" +"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." +msgstr "" + +#: src/portability/index.md:1 +msgid "# Portability" +msgstr "" + +#: src/portability/index.md:3 +msgid "" +"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." +msgstr "" + +#: src/portability/index.md:5 +msgid "" +"A common way to equalize such differences is via a layer called Hardware " +"Abstraction layer or **HAL**." +msgstr "" + +#: src/portability/index.md:7 +msgid "" +"> Hardware abstractions are sets of routines in software that emulate some " +"platform-specific details, giving programs direct access to the hardware " +"resources.\n" +">\n" +"> They often allow programmers to write device-independent, high performance " +"applications by providing standard operating system (OS) calls to hardware.\n" +">\n" +"> *Wikipedia: [Hardware Abstraction Layer]*" +msgstr "" + +#: src/portability/index.md:15 +msgid "" +"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." +msgstr "" + +#: src/portability/index.md:17 +msgid "How do we do this in Rust? Enter **embedded-hal**..." +msgstr "" + +#: src/portability/index.md:19 +msgid "## What is embedded-hal?" +msgstr "" + +#: src/portability/index.md:21 +msgid "" +"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)." +msgstr "" + +#: src/portability/index.md:23 +msgid "A typical layering might look like this:" +msgstr "" + +#: src/portability/index.md:27 +msgid "Some of the defined traits in **embedded-hal** are:" +msgstr "" + +#: src/portability/index.md:28 +msgid "" +"* GPIO (input and output pins)\n" +"* Serial communication\n" +"* I2C\n" +"* SPI\n" +"* Timers/Countdowns\n" +"* Analog Digital Conversion" +msgstr "" + +#: src/portability/index.md:35 +msgid "" +"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." +msgstr "" + +#: src/portability/index.md:37 +msgid "## Users of the embedded-hal" +msgstr "" + +#: src/portability/index.md:39 +msgid "As said above there are three main users of the HAL:" +msgstr "" + +#: src/portability/index.md:41 +msgid "### HAL implementation" +msgstr "" + +#: src/portability/index.md:43 +msgid "" +"A HAL implementation provides the interfacing between the hardware and the " +"users of the HAL traits. Typical implementations consist of three parts:" +msgstr "" + +#: src/portability/index.md:44 +msgid "" +"* One or more hardware specific types\n" +"* Functions to create and initialize such a type, often providing various " +"configuration options (speed, operation mode, use pins, etc.)\n" +"* one or more `trait` `impl` of **embedded-hal** traits for that type" +msgstr "" + +#: src/portability/index.md:48 +msgid "Such a **HAL implementation** can come in various flavours:" +msgstr "" + +#: src/portability/index.md:49 +msgid "" +"* Via low-level hardware access, e.g. via registers\n" +"* Via operating system, e.g. by using the `sysfs` under Linux\n" +"* Via adapter, e.g. a mock of types for unit testing\n" +"* Via driver for hardware adapters, e.g. I2C multiplexer or GPIO expander" +msgstr "" + +#: src/portability/index.md:54 +msgid "### Driver" +msgstr "" + +#: src/portability/index.md:56 +msgid "" +"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)." +msgstr "" + +#: src/portability/index.md:58 +msgid "" +"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." +msgstr "" + +#: src/portability/index.md:60 +msgid "### Application" +msgstr "" + +#: src/portability/index.md:62 +msgid "" +"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)." +msgstr "" + +#: src/concurrency/index.md:1 +msgid "# Concurrency" +msgstr "" + +#: src/concurrency/index.md:3 +msgid "" +"Concurrency happens whenever different parts of your program might execute\n" +"at different times or out of order. In an embedded context, this includes:" +msgstr "" + +#: src/concurrency/index.md:6 +msgid "" +"* interrupt handlers, which run whenever the associated interrupt happens,\n" +"* various forms of multithreading, where your microprocessor regularly " +"swaps\n" +" between parts of your program,\n" +"* and in some systems, multiple-core microprocessors, where each core can " +"be\n" +" independently running a different part of your program at the same time." +msgstr "" + +#: src/concurrency/index.md:12 +msgid "" +"Since many embedded programs need to deal with interrupts, concurrency will\n" +"usually come up sooner or later, and it's also where many subtle and " +"difficult\n" +"bugs can occur. Luckily, Rust provides a number of abstractions and safety\n" +"guarantees to help us write correct code." +msgstr "" + +#: src/concurrency/index.md:17 +msgid "## No Concurrency" +msgstr "" + +#: src/concurrency/index.md:19 +msgid "" +"The simplest concurrency for an embedded program is no concurrency: your\n" +"software consists of a single main loop which just keeps running, and there\n" +"are no interrupts at all. Sometimes this is perfectly suited to the problem\n" +"at hand! Typically your loop will read some inputs, perform some " +"processing,\n" +"and write some outputs." +msgstr "" + +#: src/concurrency/index.md:25 +msgid "" +"```rust,ignore\n" +"#[entry]\n" +"fn main() {\n" +" let peripherals = setup_peripherals();\n" +" loop {\n" +" let inputs = read_inputs(&peripherals);\n" +" let outputs = process(inputs);\n" +" write_outputs(&peripherals, outputs);\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/concurrency/index.md:37 +msgid "" +"Since there's no concurrency, there's no need to worry about sharing data\n" +"between parts of your program or synchronising access to peripherals. If\n" +"you can get away with such a simple approach this can be a great solution." +msgstr "" + +#: src/concurrency/index.md:41 +msgid "## Global Mutable Data" +msgstr "" + +#: src/concurrency/index.md:43 +msgid "" +"Unlike non-embedded Rust, we will not usually have the luxury of creating\n" +"heap allocations and passing references to that data into a newly-created\n" +"thread. Instead, our interrupt handlers might be called at any time and " +"must\n" +"know how to access whatever shared memory we are using. At the lowest " +"level,\n" +"this means we must have _statically allocated_ mutable memory, which\n" +"both the interrupt handler and the main code can refer to." +msgstr "" + +#: src/concurrency/index.md:50 +msgid "" +"In Rust, such [`static mut`] variables are always unsafe to read or write,\n" +"because without taking special care, you might trigger a race condition,\n" +"where your access to the variable is interrupted halfway through by an\n" +"interrupt which also accesses that variable." +msgstr "" + +#: src/concurrency/index.md:57 +msgid "" +"For an example of how this behaviour can cause subtle errors in your code,\n" +"consider an embedded program which counts rising edges of some input signal\n" +"in each one-second period (a frequency counter):" +msgstr "" + +#: src/concurrency/index.md:61 +msgid "" +"```rust,ignore\n" +"static mut COUNTER: u32 = 0;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" set_timer_1hz();\n" +" let mut last_state = false;\n" +" loop {\n" +" let state = read_signal_level();\n" +" if state && !last_state {\n" +" // DANGER - Not actually safe! Could cause data races.\n" +" unsafe { COUNTER += 1 };\n" +" }\n" +" last_state = state;\n" +" }\n" +"}\n" +"\n" +"#[interrupt]\n" +"fn timer() {\n" +" unsafe { COUNTER = 0; }\n" +"}\n" +"```" +msgstr "" + +#: src/concurrency/index.md:84 +msgid "" +"Each second, the timer interrupt sets the counter back to 0. Meanwhile, the\n" +"main loop continually measures the signal, and incremements the counter " +"when\n" +"it sees a change from low to high. We've had to use `unsafe` to access\n" +"`COUNTER`, as it's `static mut`, and that means we're promising the " +"compiler\n" +"we won't cause any undefined behaviour. Can you spot the race condition? " +"The\n" +"increment on `COUNTER` is _not_ guaranteed to be atomic — in fact, on most\n" +"embedded platforms, it will be split into a load, then the increment, then\n" +"a store. If the interrupt fired after the load but before the store, the\n" +"reset back to 0 would be ignored after the interrupt returns — and we would\n" +"count twice as many transitions for that period." +msgstr "" + +#: src/concurrency/index.md:95 +msgid "## Critical Sections" +msgstr "" + +#: src/concurrency/index.md:97 +msgid "" +"So, what can we do about data races? A simple approach is to use _critical\n" +"sections_, a context where interrupts are disabled. By wrapping the access " +"to\n" +"`COUNTER` in `main` in a critical section, we can be sure the timer " +"interrupt\n" +"will not fire until we're finished incrementing `COUNTER`:" +msgstr "" + +#: src/concurrency/index.md:102 +msgid "" +"```rust,ignore\n" +"static mut COUNTER: u32 = 0;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" set_timer_1hz();\n" +" let mut last_state = false;\n" +" loop {\n" +" let state = read_signal_level();\n" +" if state && !last_state {\n" +" // New critical section ensures synchronised access to COUNTER\n" +" cortex_m::interrupt::free(|_| {\n" +" unsafe { COUNTER += 1 };\n" +" });\n" +" }\n" +" last_state = state;\n" +" }\n" +"}\n" +"\n" +"#[interrupt]\n" +"fn timer() {\n" +" unsafe { COUNTER = 0; }\n" +"}\n" +"```" +msgstr "" + +#: src/concurrency/index.md:127 +msgid "" +"In this example, we use `cortex_m::interrupt::free`, but other platforms " +"will\n" +"have similar mechanisms for executing code in a critical section. This is " +"also\n" +"the same as disabling interrupts, running some code, and then re-enabling\n" +"interrupts." +msgstr "" + +#: src/concurrency/index.md:132 +msgid "" +"Note we didn't need to put a critical section inside the timer interrupt,\n" +"for two reasons:" +msgstr "" + +#: src/concurrency/index.md:135 +msgid "" +" * Writing 0 to `COUNTER` can't be affected by a race since we don't read " +"it\n" +" * It will never be interrupted by the `main` thread anyway" +msgstr "" + +#: src/concurrency/index.md:138 +msgid "" +"If `COUNTER` was being shared by multiple interrupt handlers that might\n" +"_preempt_ each other, then each one might require a critical section as well." +msgstr "" + +#: src/concurrency/index.md:141 +msgid "" +"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." +msgstr "" + +#: src/concurrency/index.md:143 +msgid "" +"It's worth noting that while a critical section guarantees no interrupts " +"will\n" +"fire, it does not provide an exclusivity guarantee on multi-core systems! " +"The\n" +"other core could be happily accessing the same memory as your core, even\n" +"without interrupts. You will need stronger synchronisation primitives if " +"you\n" +"are using multiple cores." +msgstr "" + +#: src/concurrency/index.md:149 +msgid "## Atomic Access" +msgstr "" + +#: src/concurrency/index.md:151 +msgid "" +"On some platforms, special atomic instructions are available, which provide\n" +"guarantees about read-modify-write operations. Specifically for Cortex-M: " +"`thumbv6`\n" +"(Cortex-M0, Cortex-M0+) only provide atomic load and store instructions,\n" +"while `thumbv7` (Cortex-M3 and above) provide full Compare and Swap (CAS)\n" +"instructions. These CAS instructions give an alternative to the heavy-" +"handed\n" +"disabling of all interrupts: we can attempt the increment, it will succeed " +"most\n" +"of the time, but if it was interrupted it will automatically retry the " +"entire\n" +"increment operation. These atomic operations are safe even across multiple\n" +"cores." +msgstr "" + +#: src/concurrency/index.md:161 +msgid "" +"```rust,ignore\n" +"use core::sync::atomic::{AtomicUsize, Ordering};\n" +"\n" +"static COUNTER: AtomicUsize = AtomicUsize::new(0);\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" set_timer_1hz();\n" +" let mut last_state = false;\n" +" loop {\n" +" let state = read_signal_level();\n" +" if state && !last_state {\n" +" // Use `fetch_add` to atomically add 1 to COUNTER\n" +" COUNTER.fetch_add(1, Ordering::Relaxed);\n" +" }\n" +" last_state = state;\n" +" }\n" +"}\n" +"\n" +"#[interrupt]\n" +"fn timer() {\n" +" // Use `store` to write 0 directly to COUNTER\n" +" COUNTER.store(0, Ordering::Relaxed)\n" +"}\n" +"```" +msgstr "" + +#: src/concurrency/index.md:187 +msgid "" +"This time `COUNTER` is a safe `static` variable. Thanks to the " +"`AtomicUsize`\n" +"type `COUNTER` can be safely modified from both the interrupt handler and " +"the\n" +"main thread without disabling interrupts. When possible, this is a better\n" +"solution — but it may not be supported on your platform." +msgstr "" + +#: src/concurrency/index.md:192 +msgid "" +"A note on [`Ordering`]: this affects how the compiler and hardware may " +"reorder\n" +"instructions, and also has consequences on cache visibility. Assuming that " +"the\n" +"target is a single core platform `Relaxed` is sufficient and the most " +"efficient\n" +"choice in this particular case. Stricter ordering will cause the compiler " +"to\n" +"emit memory barriers around the atomic operations; depending on what you're\n" +"using atomics for you may or may not need this! The precise details of the\n" +"atomic model are complicated and best described elsewhere." +msgstr "" + +#: src/concurrency/index.md:200 +msgid "For more details on atomics and ordering, see the [nomicon]." +msgstr "" + +#: src/concurrency/index.md:206 +msgid "## Abstractions, Send, and Sync" +msgstr "" + +#: src/concurrency/index.md:208 +msgid "" +"None of the above solutions are especially satisfactory. They require " +"`unsafe`\n" +"blocks which must be very carefully checked and are not ergonomic. Surely " +"we\n" +"can do better in Rust!" +msgstr "" + +#: src/concurrency/index.md:212 +msgid "" +"We can abstract our counter into a safe interface which can be safely used\n" +"anywhere else in our code. For this example, we'll use the critical-section\n" +"counter, but you could do something very similar with atomics." +msgstr "" + +#: src/concurrency/index.md:216 +msgid "" +"```rust,ignore\n" +"use core::cell::UnsafeCell;\n" +"use cortex_m::interrupt;\n" +"\n" +"// Our counter is just a wrapper around UnsafeCell, which is the heart\n" +"// of interior mutability in Rust. By using interior mutability, we can " +"have\n" +"// COUNTER be `static` instead of `static mut`, but still able to mutate\n" +"// its counter value.\n" +"struct CSCounter(UnsafeCell);\n" +"\n" +"const CS_COUNTER_INIT: CSCounter = CSCounter(UnsafeCell::new(0));\n" +"\n" +"impl CSCounter {\n" +" pub fn reset(&self, _cs: &interrupt::CriticalSection) {\n" +" // By requiring a CriticalSection be passed in, we know we must\n" +" // be operating inside a CriticalSection, and so can confidently\n" +" // use this unsafe block (required to call UnsafeCell::get).\n" +" unsafe { *self.0.get() = 0 };\n" +" }\n" +"\n" +" pub fn increment(&self, _cs: &interrupt::CriticalSection) {\n" +" unsafe { *self.0.get() += 1 };\n" +" }\n" +"}\n" +"\n" +"// Required to allow static CSCounter. See explanation below.\n" +"unsafe impl Sync for CSCounter {}\n" +"\n" +"// COUNTER is no longer `mut` as it uses interior mutability;\n" +"// therefore it also no longer requires unsafe blocks to access.\n" +"static COUNTER: CSCounter = CS_COUNTER_INIT;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" set_timer_1hz();\n" +" let mut last_state = false;\n" +" loop {\n" +" let state = read_signal_level();\n" +" if state && !last_state {\n" +" // No unsafe here!\n" +" interrupt::free(|cs| COUNTER.increment(cs));\n" +" }\n" +" last_state = state;\n" +" }\n" +"}\n" +"\n" +"#[interrupt]\n" +"fn timer() {\n" +" // We do need to enter a critical section here just to obtain a valid\n" +" // cs token, even though we know no other interrupt could pre-empt\n" +" // this one.\n" +" interrupt::free(|cs| COUNTER.reset(cs));\n" +"\n" +" // We could use unsafe code to generate a fake CriticalSection if we\n" +" // really wanted to, avoiding the overhead:\n" +" // let cs = unsafe { interrupt::CriticalSection::new() };\n" +"}\n" +"```" +msgstr "" + +#: src/concurrency/index.md:275 +msgid "" +"We've moved our `unsafe` code to inside our carefully-planned abstraction,\n" +"and now our application code does not contain any `unsafe` blocks." +msgstr "" + +#: src/concurrency/index.md:278 +msgid "" +"This design requires that the application pass a `CriticalSection` token " +"in:\n" +"these tokens are only safely generated by `interrupt::free`, so by " +"requiring\n" +"one be passed in, we ensure we are operating inside a critical section, " +"without\n" +"having to actually do the lock ourselves. This guarantee is provided " +"statically\n" +"by the compiler: there won't be any runtime overhead associated with `cs`.\n" +"If we had multiple counters, they could all be given the same `cs`, without\n" +"requiring multiple nested critical sections." +msgstr "" + +#: src/concurrency/index.md:286 +msgid "" +"This also brings up an important topic for concurrency in Rust: the\n" +"[`Send` and `Sync`] traits. To summarise the Rust book, a type is Send\n" +"when it can safely be moved to another thread, while it is Sync when\n" +"it can be safely shared between multiple threads. In an embedded context,\n" +"we consider interrupts to be executing in a separate thread to the " +"application\n" +"code, so variables accessed by both an interrupt and the main code must be\n" +"Sync." +msgstr "" + +#: src/concurrency/index.md:296 +msgid "" +"For most types in Rust, both of these traits are automatically derived for " +"you\n" +"by the compiler. However, because `CSCounter` contains an [`UnsafeCell`], it " +"is\n" +"not Sync, and therefore we could not make a `static CSCounter`: `static`\n" +"variables _must_ be Sync, since they can be accessed by multiple threads." +msgstr "" + +#: src/concurrency/index.md:303 +msgid "" +"To tell the compiler we have taken care that the `CSCounter` is in fact " +"safe\n" +"to share between threads, we implement the Sync trait explicitly. As with " +"the\n" +"previous use of critical sections, this is only safe on single-core " +"platforms:\n" +"with multiple cores, you would need to go to greater lengths to ensure " +"safety." +msgstr "" + +#: src/concurrency/index.md:308 +msgid "## Mutexes" +msgstr "" + +#: src/concurrency/index.md:310 +msgid "" +"We've created a useful abstraction specific to our counter problem, but\n" +"there are many common abstractions used for concurrency." +msgstr "" + +#: src/concurrency/index.md:313 +msgid "" +"One such _synchronisation primitive_ is a mutex, short for mutual " +"exclusion.\n" +"These constructs ensure exclusive access to a variable, such as our counter. " +"A\n" +"thread can attempt to _lock_ (or _acquire_) the mutex, and either succeeds\n" +"immediately, or blocks waiting for the lock to be acquired, or returns an " +"error\n" +"that the mutex could not be locked. While that thread holds the lock, it is\n" +"granted access to the protected data. When the thread is done, it _unlocks_ " +"(or\n" +"_releases_) the mutex, allowing another thread to lock it. In Rust, we " +"would\n" +"usually implement the unlock using the [`Drop`] trait to ensure it is " +"always\n" +"released when the mutex goes out of scope." +msgstr "" + +#: src/concurrency/index.md:325 +msgid "" +"Using a mutex with interrupt handlers can be tricky: it is not normally\n" +"acceptable for the interrupt handler to block, and it would be especially\n" +"disastrous for it to block waiting for the main thread to release a lock,\n" +"since we would then _deadlock_ (the main thread will never release the lock\n" +"because execution stays in the interrupt handler). Deadlocking is not\n" +"considered unsafe: it is possible even in safe Rust." +msgstr "" + +#: src/concurrency/index.md:332 +msgid "" +"To avoid this behaviour entirely, we could implement a mutex which requires\n" +"a critical section to lock, just like our counter example. So long as the\n" +"critical section must last as long as the lock, we can be sure we have\n" +"exclusive access to the wrapped variable without even needing to track\n" +"the lock/unlock state of the mutex." +msgstr "" + +#: src/concurrency/index.md:338 +msgid "" +"This is in fact done for us in the `cortex_m` crate! We could have written\n" +"our counter using it:" +msgstr "" + +#: src/concurrency/index.md:341 +msgid "" +"```rust,ignore\n" +"use core::cell::Cell;\n" +"use cortex_m::interrupt::Mutex;\n" +"\n" +"static COUNTER: Mutex> = Mutex::new(Cell::new(0));\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" set_timer_1hz();\n" +" let mut last_state = false;\n" +" loop {\n" +" let state = read_signal_level();\n" +" if state && !last_state {\n" +" interrupt::free(|cs|\n" +" COUNTER.borrow(cs).set(COUNTER.borrow(cs).get() + 1));\n" +" }\n" +" last_state = state;\n" +" }\n" +"}\n" +"\n" +"#[interrupt]\n" +"fn timer() {\n" +" // We still need to enter a critical section here to satisfy the Mutex.\n" +" interrupt::free(|cs| COUNTER.borrow(cs).set(0));\n" +"}\n" +"```" +msgstr "" + +#: src/concurrency/index.md:368 +msgid "" +"We're now using [`Cell`], which along with its sibling `RefCell` is used to\n" +"provide safe interior mutability. We've already seen `UnsafeCell` which is\n" +"the bottom layer of interior mutability in Rust: it allows you to obtain\n" +"multiple mutable references to its value, but only with unsafe code. A " +"`Cell`\n" +"is like an `UnsafeCell` but it provides a safe interface: it only permits\n" +"taking a copy of the current value or replacing it, not taking a reference,\n" +"and since it is not Sync, it cannot be shared between threads. These\n" +"constraints mean it's safe to use, but we couldn't use it directly in a\n" +"`static` variable as a `static` must be Sync." +msgstr "" + +#: src/concurrency/index.md:380 +msgid "" +"So why does the example above work? The `Mutex` implements Sync for any\n" +"`T` which is Send — such as a `Cell`. It can do this safely because it only\n" +"gives access to its contents during a critical section. We're therefore " +"able\n" +"to get a safe counter with no unsafe code at all!" +msgstr "" + +#: src/concurrency/index.md:385 +msgid "" +"This is great for simple types like the `u32` of our counter, but what " +"about\n" +"more complex types which are not Copy? An extremely common example in an\n" +"embedded context is a peripheral struct, which generally is not Copy.\n" +"For that, we can turn to `RefCell`." +msgstr "" + +#: src/concurrency/index.md:390 +msgid "## Sharing Peripherals" +msgstr "" + +#: src/concurrency/index.md:392 +msgid "" +"Device crates generated using `svd2rust` and similar abstractions provide\n" +"safe access to peripherals by enforcing that only one instance of the\n" +"peripheral struct can exist at a time. This ensures safety, but makes it\n" +"difficult to access a peripheral from both the main thread and an interrupt\n" +"handler." +msgstr "" + +#: src/concurrency/index.md:398 +msgid "" +"To safely share peripheral access, we can use the `Mutex` we saw before. " +"We'll\n" +"also need to use [`RefCell`], which uses a runtime check to ensure only one\n" +"reference to a peripheral is given out at a time. This has more overhead " +"than\n" +"the plain `Cell`, but since we are giving out references rather than " +"copies,\n" +"we must be sure only one exists at a time." +msgstr "" + +#: src/concurrency/index.md:406 +msgid "" +"Finally, we'll also have to account for somehow moving the peripheral into\n" +"the shared variable after it has been initialised in the main code. To do\n" +"this we can use the `Option` type, initialised to `None` and later set to\n" +"the instance of the peripheral." +msgstr "" + +#: src/concurrency/index.md:411 +msgid "" +"```rust,ignore\n" +"use core::cell::RefCell;\n" +"use cortex_m::interrupt::{self, Mutex};\n" +"use stm32f4::stm32f405;\n" +"\n" +"static MY_GPIO: Mutex>> =\n" +" Mutex::new(RefCell::new(None));\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" // Obtain the peripheral singletons and configure it.\n" +" // This example is from an svd2rust-generated crate, but\n" +" // most embedded device crates will be similar.\n" +" let dp = stm32f405::Peripherals::take().unwrap();\n" +" let gpioa = &dp.GPIOA;\n" +"\n" +" // Some sort of configuration function.\n" +" // Assume it sets PA0 to an input and PA1 to an output.\n" +" configure_gpio(gpioa);\n" +"\n" +" // Store the GPIOA in the mutex, moving it.\n" +" interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA)));\n" +" // We can no longer use `gpioa` or `dp.GPIOA`, and instead have to\n" +" // access it via the mutex.\n" +"\n" +" // Be careful to enable the interrupt only after setting MY_GPIO:\n" +" // otherwise the interrupt might fire while it still contains None,\n" +" // and as-written (with `unwrap()`), it would panic.\n" +" set_timer_1hz();\n" +" let mut last_state = false;\n" +" loop {\n" +" // We'll now read state as a digital input, via the mutex\n" +" let state = interrupt::free(|cs| {\n" +" let gpioa = MY_GPIO.borrow(cs).borrow();\n" +" gpioa.as_ref().unwrap().idr.read().idr0().bit_is_set()\n" +" });\n" +"\n" +" if state && !last_state {\n" +" // Set PA1 high if we've seen a rising edge on PA0.\n" +" interrupt::free(|cs| {\n" +" let gpioa = MY_GPIO.borrow(cs).borrow();\n" +" gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1()." +"set_bit());\n" +" });\n" +" }\n" +" last_state = state;\n" +" }\n" +"}\n" +"\n" +"#[interrupt]\n" +"fn timer() {\n" +" // This time in the interrupt we'll just clear PA0.\n" +" interrupt::free(|cs| {\n" +" // We can use `unwrap()` because we know the interrupt wasn't " +"enabled\n" +" // until after MY_GPIO was set; otherwise we should handle the " +"potential\n" +" // for a None value.\n" +" let gpioa = MY_GPIO.borrow(cs).borrow();\n" +" gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().clear_bit());\n" +" });\n" +"}\n" +"```" +msgstr "" + +#: src/concurrency/index.md:472 +msgid "That's quite a lot to take in, so let's break down the important lines." +msgstr "" + +#: src/concurrency/index.md:474 +msgid "" +"```rust,ignore\n" +"static MY_GPIO: Mutex>> =\n" +" Mutex::new(RefCell::new(None));\n" +"```" +msgstr "" + +#: src/concurrency/index.md:479 +msgid "" +"Our shared variable is now a `Mutex` around a `RefCell` which contains an\n" +"`Option`. The `Mutex` ensures we only have access during a critical " +"section,\n" +"and therefore makes the variable Sync, even though a plain `RefCell` would " +"not\n" +"be Sync. The `RefCell` gives us interior mutability with references, which\n" +"we'll need to use our `GPIOA`. The `Option` lets us initialise this " +"variable\n" +"to something empty, and only later actually move the variable in. We cannot\n" +"access the peripheral singleton statically, only at runtime, so this is\n" +"required." +msgstr "" + +#: src/concurrency/index.md:488 +msgid "" +"```rust,ignore\n" +"interrupt::free(|cs| MY_GPIO.borrow(cs).replace(Some(dp.GPIOA)));\n" +"```" +msgstr "" + +#: src/concurrency/index.md:492 +msgid "" +"Inside a critical section we can call `borrow()` on the mutex, which gives " +"us\n" +"a reference to the `RefCell`. We then call `replace()` to move our new " +"value\n" +"into the `RefCell`." +msgstr "" + +#: src/concurrency/index.md:496 +msgid "" +"```rust,ignore\n" +"interrupt::free(|cs| {\n" +" let gpioa = MY_GPIO.borrow(cs).borrow();\n" +" gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().set_bit());\n" +"});\n" +"```" +msgstr "" + +#: src/concurrency/index.md:503 +msgid "" +"Finally, we use `MY_GPIO` in a safe and concurrent fashion. The critical " +"section\n" +"prevents the interrupt firing as usual, and lets us borrow the mutex. The\n" +"`RefCell` then gives us an `&Option`, and tracks how long it remains\n" +"borrowed - once that reference goes out of scope, the `RefCell` will be " +"updated\n" +"to indicate it is no longer borrowed." +msgstr "" + +#: src/concurrency/index.md:509 +msgid "" +"Since we can't move the `GPIOA` out of the `&Option`, we need to convert it " +"to\n" +"an `&Option<&GPIOA>` with `as_ref()`, which we can finally `unwrap()` to " +"obtain\n" +"the `&GPIOA` which lets us modify the peripheral." +msgstr "" + +#: src/concurrency/index.md:513 +msgid "" +"If we need a mutable reference to a shared resource, then `borrow_mut` and " +"`deref_mut`\n" +"should be used instead. The following code shows an example using the TIM2 " +"timer." +msgstr "" + +#: src/concurrency/index.md:516 +msgid "" +"```rust,ignore\n" +"use core::cell::RefCell;\n" +"use core::ops::DerefMut;\n" +"use cortex_m::interrupt::{self, Mutex};\n" +"use cortex_m::asm::wfi;\n" +"use stm32f4::stm32f405;\n" +"\n" +"static G_TIM: Mutex>>> =\n" +"\tMutex::new(RefCell::new(None));\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" let mut cp = cm::Peripherals::take().unwrap();\n" +" let dp = stm32f405::Peripherals::take().unwrap();\n" +"\n" +" // Some sort of timer configuration function.\n" +" // Assume it configures the TIM2 timer, its NVIC interrupt,\n" +" // and finally starts the timer.\n" +" let tim = configure_timer_interrupt(&mut cp, dp);\n" +"\n" +" interrupt::free(|cs| {\n" +" G_TIM.borrow(cs).replace(Some(tim));\n" +" });\n" +"\n" +" loop {\n" +" wfi();\n" +" }\n" +"}\n" +"\n" +"#[interrupt]\n" +"fn timer() {\n" +" interrupt::free(|cs| {\n" +" if let Some(ref mut tim)) = G_TIM.borrow(cs).borrow_mut()." +"deref_mut() {\n" +" tim.start(1.hz());\n" +" }\n" +" });\n" +"}\n" +"\n" +"```" +msgstr "" + +#: src/concurrency/index.md:556 +msgid "" +"Whew! This is safe, but it is also a little unwieldy. Is there anything " +"else\n" +"we can do?" +msgstr "" + +#: src/concurrency/index.md:559 +msgid "## RTIC" +msgstr "" + +#: src/concurrency/index.md:561 +msgid "" +"One alternative is the [RTIC framework], short for Real Time Interrupt-" +"driven Concurrency. It\n" +"enforces static priorities and tracks accesses to `static mut` variables\n" +"(\"resources\") to statically ensure that shared resources are always " +"accessed\n" +"safely, without requiring the overhead of always entering critical sections " +"and\n" +"using reference counting (as in `RefCell`). This has a number of advantages " +"such\n" +"as guaranteeing no deadlocks and giving extremely low time and memory " +"overhead." +msgstr "" + +#: src/concurrency/index.md:570 +msgid "" +"The framework also includes other features like message passing, which " +"reduces\n" +"the need for explicit shared state, and the ability to schedule tasks to run " +"at\n" +"a given time, which can be used to implement periodic tasks. Check out [the\n" +"documentation] for more information!" +msgstr "" + +#: src/concurrency/index.md:577 +msgid "## Real Time Operating Systems" +msgstr "" + +#: src/concurrency/index.md:579 +msgid "" +"Another common model for embedded concurrency is the real-time operating " +"system\n" +"(RTOS). While currently less well explored in Rust, they are widely used in\n" +"traditional embedded development. Open source examples include [FreeRTOS] " +"and\n" +"[ChibiOS]. These RTOSs provide support for running multiple application " +"threads\n" +"which the CPU swaps between, either when the threads yield control (called\n" +"cooperative multitasking) or based on a regular timer or interrupts " +"(preemptive\n" +"multitasking). The RTOS typically provide mutexes and other synchronisation\n" +"primitives, and often interoperate with hardware features such as DMA " +"engines." +msgstr "" + +#: src/concurrency/index.md:591 +msgid "" +"At the time of writing, there are not many Rust RTOS examples to point to,\n" +"but it's an interesting area so watch this space!" +msgstr "" + +#: src/concurrency/index.md:594 +msgid "## Multiple Cores" +msgstr "" + +#: src/concurrency/index.md:596 +msgid "" +"It is becoming more common to have two or more cores in embedded " +"processors,\n" +"which adds an extra layer of complexity to concurrency. All the examples " +"using\n" +"a critical section (including the `cortex_m::interrupt::Mutex`) assume the " +"only\n" +"other execution thread is the interrupt thread, but on a multi-core system\n" +"that's no longer true. Instead, we'll need synchronisation primitives " +"designed\n" +"for multiple cores (also called SMP, for symmetric multi-processing)." +msgstr "" + +#: src/concurrency/index.md:603 +msgid "" +"These typically use the atomic instructions we saw earlier, since the\n" +"processing system will ensure that atomicity is maintained over all cores." +msgstr "" + +#: src/concurrency/index.md:606 +msgid "" +"Covering these topics in detail is currently beyond the scope of this book,\n" +"but the general patterns are the same as for the single-core case." +msgstr "" + +#: src/collections/index.md:1 +msgid "# Collections" +msgstr "" + +#: src/collections/index.md:3 +msgid "" +"Eventually you'll want to use dynamic data structures (AKA collections) in " +"your\n" +"program. `std` provides a set of common collections: [`Vec`], [`String`],\n" +"[`HashMap`], etc. All the collections implemented in `std` use a global " +"dynamic\n" +"memory allocator (AKA the heap)." +msgstr "" + +#: src/collections/index.md:12 +msgid "" +"As `core` is, by definition, free of memory allocations these " +"implementations\n" +"are not available there, but they can be found in the `alloc` crate\n" +"that's shipped with the compiler." +msgstr "" + +#: src/collections/index.md:16 +msgid "" +"If you need collections, a heap allocated implementation is not your only\n" +"option. You can also use *fixed capacity* collections; one such " +"implementation\n" +"can be found in the [`heapless`] crate." +msgstr "" + +#: src/collections/index.md:22 +msgid "In this section, we'll explore and compare these two implementations." +msgstr "" + +#: src/collections/index.md:24 +msgid "## Using `alloc`" +msgstr "" + +#: src/collections/index.md:26 +msgid "" +"The `alloc` crate is shipped with the standard Rust distribution. To import " +"the\n" +"crate you can directly `use` it *without* declaring it as a dependency in " +"your\n" +"`Cargo.toml` file." +msgstr "" + +#: src/collections/index.md:30 +msgid "" +"``` rust,ignore\n" +"#![feature(alloc)]\n" +"\n" +"extern crate alloc;\n" +"\n" +"use alloc::vec::Vec;\n" +"```" +msgstr "" + +#: src/collections/index.md:38 +msgid "" +"To be able to use any collection you'll first need use the " +"`global_allocator`\n" +"attribute to declare the global allocator your program will use. It's " +"required\n" +"that the allocator you select implements the [`GlobalAlloc`] trait." +msgstr "" + +#: src/collections/index.md:44 +msgid "" +"For completeness and to keep this section as self-contained as possible " +"we'll\n" +"implement a simple bump pointer allocator and use that as the global " +"allocator.\n" +"However, we *strongly* suggest you use a battle tested allocator from crates." +"io\n" +"in your program instead of this allocator." +msgstr "" + +#: src/collections/index.md:49 +msgid "" +"``` rust,ignore\n" +"// Bump pointer allocator implementation\n" +"\n" +"use core::alloc::{GlobalAlloc, Layout};\n" +"use core::cell::UnsafeCell;\n" +"use core::ptr;\n" +"\n" +"use cortex_m::interrupt;\n" +"\n" +"// Bump pointer allocator for *single* core systems\n" +"struct BumpPointerAlloc {\n" +" head: UnsafeCell,\n" +" end: usize,\n" +"}\n" +"\n" +"unsafe impl Sync for BumpPointerAlloc {}\n" +"\n" +"unsafe impl GlobalAlloc for BumpPointerAlloc {\n" +" unsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n" +" // `interrupt::free` is a critical section that makes our allocator " +"safe\n" +" // to use from within interrupts\n" +" interrupt::free(|_| {\n" +" let head = self.head.get();\n" +" let size = layout.size();\n" +" let align = layout.align();\n" +" let align_mask = !(align - 1);\n" +"\n" +" // move start up to the next alignment boundary\n" +" let start = (*head + align - 1) & align_mask;\n" +"\n" +" if start + size > self.end {\n" +" // a null pointer signal an Out Of Memory condition\n" +" ptr::null_mut()\n" +" } else {\n" +" *head = start + size;\n" +" start as *mut u8\n" +" }\n" +" })\n" +" }\n" +"\n" +" unsafe fn dealloc(&self, _: *mut u8, _: Layout) {\n" +" // this allocator never deallocates memory\n" +" }\n" +"}\n" +"\n" +"// Declaration of the global memory allocator\n" +"// NOTE the user must ensure that the memory region `[0x2000_0100, " +"0x2000_0200]`\n" +"// is not used by other parts of the program\n" +"#[global_allocator]\n" +"static HEAP: BumpPointerAlloc = BumpPointerAlloc {\n" +" head: UnsafeCell::new(0x2000_0100),\n" +" end: 0x2000_0200,\n" +"};\n" +"```" +msgstr "" + +#: src/collections/index.md:104 +msgid "" +"Apart from selecting a global allocator the user will also have to define " +"how\n" +"Out Of Memory (OOM) errors are handled using the *unstable*\n" +"`alloc_error_handler` attribute." +msgstr "" + +#: src/collections/index.md:108 +msgid "" +"``` rust,ignore\n" +"#![feature(alloc_error_handler)]\n" +"\n" +"use cortex_m::asm;\n" +"\n" +"#[alloc_error_handler]\n" +"fn on_oom(_layout: Layout) -> ! {\n" +" asm::bkpt();\n" +"\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/collections/index.md:121 +msgid "" +"Once all that is in place, the user can finally use the collections in " +"`alloc`." +msgstr "" + +#: src/collections/index.md:123 +msgid "" +"```rust,ignore\n" +"#[entry]\n" +"fn main() -> ! {\n" +" let mut xs = Vec::new();\n" +"\n" +" xs.push(42);\n" +" assert!(xs.pop(), Some(42));\n" +"\n" +" loop {\n" +" // ..\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/collections/index.md:137 +msgid "" +"If you have used the collections in the `std` crate then these will be " +"familiar\n" +"as they are exact same implementation." +msgstr "" + +#: src/collections/index.md:140 +msgid "## Using `heapless`" +msgstr "" + +#: src/collections/index.md:142 +msgid "" +"`heapless` requires no setup as its collections don't depend on a global " +"memory\n" +"allocator. Just `use` its collections and proceed to instantiate them:" +msgstr "" + +#: src/collections/index.md:145 +msgid "" +"```rust,ignore\n" +"// heapless version: v0.4.x\n" +"use heapless::Vec;\n" +"use heapless::consts::*;\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" let mut xs: Vec<_, U8> = Vec::new();\n" +"\n" +" xs.push(42).unwrap();\n" +" assert_eq!(xs.pop(), Some(42));\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/collections/index.md:160 +msgid "" +"You'll note two differences between these collections and the ones in " +"`alloc`." +msgstr "" + +#: src/collections/index.md:162 +msgid "" +"First, you have to declare upfront the capacity of the collection. " +"`heapless`\n" +"collections never reallocate and have fixed capacities; this capacity is " +"part of\n" +"the type signature of the collection. In this case we have declared that " +"`xs`\n" +"has a capacity of 8 elements that is the vector can, at most, hold 8 " +"elements.\n" +"This is indicated by the `U8` (see [`typenum`]) in the type signature." +msgstr "" + +#: src/collections/index.md:170 +msgid "" +"Second, the `push` method, and many other methods, return a `Result`. Since " +"the\n" +"`heapless` collections have fixed capacity all operations that insert " +"elements\n" +"into the collection can potentially fail. The API reflects this problem by\n" +"returning a `Result` indicating whether the operation succeeded or not. In\n" +"contrast, `alloc` collections will reallocate themselves on the heap to " +"increase\n" +"their capacity." +msgstr "" + +#: src/collections/index.md:177 +msgid "" +"As of version v0.4.x all `heapless` collections store all their elements " +"inline.\n" +"This means that an operation like `let x = heapless::Vec::new();` will " +"allocate\n" +"the collection on the stack, but it's also possible to allocate the " +"collection\n" +"on a `static` variable, or even on the heap (`Box>`)." +msgstr "" + +#: src/collections/index.md:182 +msgid "## Trade-offs" +msgstr "" + +#: src/collections/index.md:184 +msgid "" +"Keep these in mind when choosing between heap allocated, relocatable " +"collections\n" +"and fixed capacity collections." +msgstr "" + +#: src/collections/index.md:187 +msgid "### Out Of Memory and error handling" +msgstr "" + +#: src/collections/index.md:189 +msgid "" +"With heap allocations Out Of Memory is always a possibility and can occur " +"in\n" +"any place where a collection may need to grow: for example, all\n" +"`alloc::Vec.push` invocations can potentially generate an OOM condition. " +"Thus\n" +"some operations can *implicitly* fail. Some `alloc` collections expose\n" +"`try_reserve` methods that let you check for potential OOM conditions when\n" +"growing the collection but you need be proactive about using them." +msgstr "" + +#: src/collections/index.md:196 +msgid "" +"If you exclusively use `heapless` collections and you don't use a memory\n" +"allocator for anything else then an OOM condition is impossible. Instead, " +"you'll\n" +"have to deal with collections running out of capacity on a case by case " +"basis.\n" +"That is you'll have deal with *all* the `Result`s returned by methods like\n" +"`Vec.push`." +msgstr "" + +#: src/collections/index.md:202 +msgid "" +"OOM failures can be harder to debug than say `unwrap`-ing on all `Result`s\n" +"returned by `heapless::Vec.push` because the observed location of failure " +"may\n" +"*not* match with the location of the cause of the problem. For example, " +"even\n" +"`vec.reserve(1)` can trigger an OOM if the allocator is nearly exhausted " +"because\n" +"some other collection was leaking memory (memory leaks are possible in safe\n" +"Rust)." +msgstr "" + +#: src/collections/index.md:209 +msgid "### Memory usage" +msgstr "" + +#: src/collections/index.md:211 +msgid "" +"Reasoning about memory usage of heap allocated collections is hard because " +"the\n" +"capacity of long lived collections can change at runtime. Some operations " +"may\n" +"implicitly reallocate the collection increasing its memory usage, and some\n" +"collections expose methods like `shrink_to_fit` that can potentially reduce " +"the\n" +"memory used by the collection -- ultimately, it's up to the allocator to " +"decide\n" +"whether to actually shrink the memory allocation or not. Additionally, the\n" +"allocator may have to deal with memory fragmentation which can increase the\n" +"*apparent* memory usage." +msgstr "" + +#: src/collections/index.md:220 +msgid "" +"On the other hand if you exclusively use fixed capacity collections, store\n" +"most of them in `static` variables and set a maximum size for the call " +"stack\n" +"then the linker will detect if you try to use more memory than what's " +"physically\n" +"available." +msgstr "" + +#: src/collections/index.md:225 +msgid "" +"Furthermore, fixed capacity collections allocated on the stack will be " +"reported\n" +"by [`-Z emit-stack-sizes`] flag which means that tools that analyze stack " +"usage\n" +"(like [`stack-sizes`]) will include them in their analysis." +msgstr "" + +#: src/collections/index.md:232 +msgid "" +"However, fixed capacity collections can *not* be shrunk which can result in\n" +"lower load factors (the ratio between the size of the collection and its\n" +"capacity) than what relocatable collections can achieve." +msgstr "" + +#: src/collections/index.md:236 +msgid "### Worst Case Execution Time (WCET)" +msgstr "" + +#: src/collections/index.md:238 +msgid "" +"If you are building time sensitive applications or hard real time " +"applications\n" +"then you care, maybe a lot, about the worst case execution time of the " +"different\n" +"parts of your program." +msgstr "" + +#: src/collections/index.md:242 +msgid "" +"The `alloc` collections can reallocate so the WCET of operations that may " +"grow\n" +"the collection will also include the time it takes to reallocate the " +"collection,\n" +"which itself depends on the *runtime* capacity of the collection. This makes " +"it\n" +"hard to determine the WCET of, for example, the `alloc::Vec.push` operation " +"as\n" +"it depends on both the allocator being used and its runtime capacity." +msgstr "" + +#: src/collections/index.md:248 +msgid "" +"On the other hand fixed capacity collections never reallocate so all " +"operations\n" +"have a predictable execution time. For example, `heapless::Vec.push` " +"executes in\n" +"constant time." +msgstr "" + +#: src/collections/index.md:252 +msgid "### Ease of use" +msgstr "" + +#: src/collections/index.md:254 +msgid "" +"`alloc` requires setting up a global allocator whereas `heapless` does not.\n" +"However, `heapless` requires you to pick the capacity of each collection " +"that\n" +"you instantiate." +msgstr "" + +#: src/collections/index.md:258 +msgid "" +"The `alloc` API will be familiar to virtually every Rust developer. The\n" +"`heapless` API tries to closely mimic the `alloc` API but it will never be\n" +"exactly the same due to its explicit error handling -- some developers may " +"feel\n" +"the explicit error handling is excessive or too cumbersome." +msgstr "" + +#: src/design-patterns/index.md:1 +msgid "# Design Patterns" +msgstr "" + +#: src/design-patterns/index.md:3 +msgid "" +"This chapter aims to collect various useful design patterns for embedded " +"Rust." +msgstr "" + +#: src/design-patterns/hal/index.md:1 +msgid "# HAL Design Patterns" +msgstr "" + +#: src/design-patterns/hal/index.md:3 +msgid "" +"This is a set of common and recommended patterns for writing hardware\n" +"abstraction layers (HALs) for microcontrollers in Rust. These patterns are\n" +"intended to be used in addition to the existing [Rust API Guidelines] when\n" +"writing HALs for microcontrollers." +msgstr "" + +#: src/design-patterns/hal/index.md:10 +msgid "[Checklist](checklist.md)" +msgstr "" + +#: src/design-patterns/hal/index.md:12 +msgid "" +"- [Naming](naming.md)\n" +"- [Interoperability](interoperability.md)\n" +"- [Predictability](predictability.md)\n" +"- [GPIO](gpio.md)" +msgstr "" + +#: src/design-patterns/hal/checklist.md:1 +msgid "# HAL Design Patterns Checklist" +msgstr "" + +#: src/design-patterns/hal/checklist.md:3 +msgid "" +"- **Naming** *(crate aligns with Rust naming conventions)*\n" +" - [ ] The crate is named appropriately ([C-CRATE-NAME])\n" +"- **Interoperability** *(crate interacts nicely with other library " +"functionality)*\n" +" - [ ] Wrapper types provide a destructor method ([C-FREE])\n" +" - [ ] HALs reexport their register access crate ([C-REEXPORT-PAC])\n" +" - [ ] Types implement the `embedded-hal` traits ([C-HAL-TRAITS])\n" +"- **Predictability** *(crate enables legible code that acts how it looks)*\n" +" - [ ] Constructors are used instead of extension traits ([C-CTOR])\n" +"- **GPIO Interfaces** *(GPIO Interfaces follow a common pattern)*\n" +" - [ ] Pin types are zero-sized by default ([C-ZST-PIN])\n" +" - [ ] Pin types provide methods to erase pin and port ([C-ERASED-PIN])\n" +" - [ ] Pin state should be encoded as type parameters ([C-PIN-STATE])" +msgstr "" + +#: src/design-patterns/hal/naming.md:1 +msgid "# Naming" +msgstr "" + +#: src/design-patterns/hal/naming.md:4 +msgid "" +msgstr "" + +#: src/design-patterns/hal/naming.md:5 +msgid "## The crate is named appropriately (C-CRATE-NAME)" +msgstr "" + +#: src/design-patterns/hal/naming.md:7 +msgid "" +"HAL crates should be named after the chip or family of chips they aim to\n" +"support. Their name should end with `-hal` to distinguish them from " +"register\n" +"access crates. The name should not contain underscores (use dashes instead)." +msgstr "" + +#: src/design-patterns/hal/interoperability.md:1 +#: src/interoperability/index.md:1 +msgid "# Interoperability" +msgstr "" + +#: src/design-patterns/hal/interoperability.md:4 +msgid "" +msgstr "" + +#: src/design-patterns/hal/interoperability.md:5 +msgid "## Wrapper types provide a destructor method (C-FREE)" +msgstr "" + +#: src/design-patterns/hal/interoperability.md:7 +msgid "" +"Any non-`Copy` wrapper type provided by the HAL should provide a `free` " +"method\n" +"that consumes the wrapper and returns back the raw peripheral (and possibly\n" +"other objects) it was created from." +msgstr "" + +#: src/design-patterns/hal/interoperability.md:11 +msgid "" +"The method should shut down and reset the peripheral if necessary. Calling " +"`new`\n" +"with the raw peripheral returned by `free` should not fail due to an " +"unexpected\n" +"state of the peripheral." +msgstr "" + +#: src/design-patterns/hal/interoperability.md:15 +msgid "" +"If the HAL type requires other non-`Copy` objects to be constructed (for " +"example\n" +"I/O pins), any such object should be released and returned by `free` as " +"well.\n" +"`free` should return a tuple in that case." +msgstr "" + +#: src/design-patterns/hal/interoperability.md:19 +msgid "For example:" +msgstr "" + +#: src/design-patterns/hal/interoperability.md:21 +msgid "" +"```rust\n" +"# pub struct TIMER0;\n" +"pub struct Timer(TIMER0);\n" +"\n" +"impl Timer {\n" +" pub fn new(periph: TIMER0) -> Self {\n" +" Self(periph)\n" +" }\n" +"\n" +" pub fn free(self) -> TIMER0 {\n" +" self.0\n" +" }\n" +"}\n" +"```" +msgstr "" + +#: src/design-patterns/hal/interoperability.md:36 +msgid "" +msgstr "" + +#: src/design-patterns/hal/interoperability.md:37 +msgid "## HALs reexport their register access crate (C-REEXPORT-PAC)" +msgstr "" + +#: src/design-patterns/hal/interoperability.md:39 +msgid "" +"HALs can be written on top of [svd2rust]-generated PACs, or on top of other\n" +"crates that provide raw register access. HALs should always reexport the\n" +"register access crate they are based on in their crate root." +msgstr "" + +#: src/design-patterns/hal/interoperability.md:43 +msgid "" +"A PAC should be reexported under the name `pac`, regardless of the actual " +"name\n" +"of the crate, as the name of the HAL should already make it clear what PAC " +"is\n" +"being accessed." +msgstr "" + +#: src/design-patterns/hal/interoperability.md:49 +msgid "" +msgstr "" + +#: src/design-patterns/hal/interoperability.md:50 +msgid "## Types implement the `embedded-hal` traits (C-HAL-TRAITS)" +msgstr "" + +#: src/design-patterns/hal/interoperability.md:52 +msgid "" +"Types provided by the HAL should implement all applicable traits provided by " +"the\n" +"[`embedded-hal`] crate." +msgstr "" + +#: src/design-patterns/hal/interoperability.md:55 +msgid "Multiple traits may be implemented for the same type." +msgstr "" + +#: src/design-patterns/hal/predictability.md:1 +msgid "# Predictability" +msgstr "" + +#: src/design-patterns/hal/predictability.md:4 +msgid "" +msgstr "" + +#: src/design-patterns/hal/predictability.md:5 +msgid "## Constructors are used instead of extension traits (C-CTOR)" +msgstr "" + +#: src/design-patterns/hal/predictability.md:7 +msgid "" +"All peripherals to which the HAL adds functionality should be wrapped in a " +"new\n" +"type, even if no additional fields are required for that functionality." +msgstr "" + +#: src/design-patterns/hal/predictability.md:10 +msgid "Extension traits implemented for the raw peripheral should be avoided." +msgstr "" + +#: src/design-patterns/hal/predictability.md:12 +msgid "" +msgstr "" + +#: src/design-patterns/hal/predictability.md:13 +msgid "## Methods are decorated with `#[inline]` where appropriate (C-INLINE)" +msgstr "" + +#: src/design-patterns/hal/predictability.md:15 +msgid "" +"The Rust compiler does not by default perform full inlining across crate\n" +"boundaries. As embedded applications are sensitive to unexpected code size\n" +"increases, `#[inline]` should be used to guide the compiler as follows:" +msgstr "" + +#: src/design-patterns/hal/predictability.md:19 +msgid "" +"* All \"small\" functions should be marked `#[inline]`. What qualifies as " +"\"small\"\n" +" is subjective, but generally all functions that are expected to compile " +"down\n" +" to single-digit instruction sequences qualify as small.\n" +"* Functions that are very likely to take constant values as parameters " +"should be\n" +" marked as `#[inline]`. This enables the compiler to compute even " +"complicated\n" +" initialization logic at compile time, provided the function inputs are " +"known." +msgstr "" + +#: src/design-patterns/hal/gpio.md:1 +msgid "# Recommendations for GPIO Interfaces" +msgstr "" + +#: src/design-patterns/hal/gpio.md:3 +msgid "" +msgstr "" + +#: src/design-patterns/hal/gpio.md:4 +msgid "## Pin types are zero-sized by default (C-ZST-PIN)" +msgstr "" + +#: src/design-patterns/hal/gpio.md:6 +msgid "" +"GPIO Interfaces exposed by the HAL should provide dedicated zero-sized types " +"for\n" +"each pin on every interface or port, resulting in a zero-cost GPIO " +"abstraction\n" +"when all pin assignments are statically known." +msgstr "" + +#: src/design-patterns/hal/gpio.md:10 +msgid "" +"Each GPIO Interface or Port should implement a `split` method returning a\n" +"struct with every pin." +msgstr "" + +#: src/design-patterns/hal/gpio.md:13 src/design-patterns/hal/gpio.md:45 +#: src/design-patterns/hal/gpio.md:127 +msgid "Example:" +msgstr "" + +#: src/design-patterns/hal/gpio.md:15 +msgid "" +"```rust\n" +"pub struct PA0;\n" +"pub struct PA1;\n" +"// ...\n" +"\n" +"pub struct PortA;\n" +"\n" +"impl PortA {\n" +" pub fn split(self) -> PortAPins {\n" +" PortAPins {\n" +" pa0: PA0,\n" +" pa1: PA1,\n" +" // ...\n" +" }\n" +" }\n" +"}\n" +"\n" +"pub struct PortAPins {\n" +" pub pa0: PA0,\n" +" pub pa1: PA1,\n" +" // ...\n" +"}\n" +"```" +msgstr "" + +#: src/design-patterns/hal/gpio.md:39 +msgid "" +msgstr "" + +#: src/design-patterns/hal/gpio.md:40 +msgid "## Pin types provide methods to erase pin and port (C-ERASED-PIN)" +msgstr "" + +#: src/design-patterns/hal/gpio.md:42 +msgid "" +"Pins should provide type erasure methods that move their properties from\n" +"compile time to runtime, and allow more flexibility in applications." +msgstr "" + +#: src/design-patterns/hal/gpio.md:47 +msgid "" +"```rust\n" +"/// Port A, pin 0.\n" +"pub struct PA0;\n" +"\n" +"impl PA0 {\n" +" pub fn erase_pin(self) -> PA {\n" +" PA { pin: 0 }\n" +" }\n" +"}\n" +"\n" +"/// A pin on port A.\n" +"pub struct PA {\n" +" /// The pin number.\n" +" pin: u8,\n" +"}\n" +"\n" +"impl PA {\n" +" pub fn erase_port(self) -> Pin {\n" +" Pin {\n" +" port: Port::A,\n" +" pin: self.pin,\n" +" }\n" +" }\n" +"}\n" +"\n" +"pub struct Pin {\n" +" port: Port,\n" +" pin: u8,\n" +" // (these fields can be packed to reduce the memory footprint)\n" +"}\n" +"\n" +"enum Port {\n" +" A,\n" +" B,\n" +" C,\n" +" D,\n" +"}\n" +"```" +msgstr "" + +#: src/design-patterns/hal/gpio.md:86 +msgid "" +msgstr "" + +#: src/design-patterns/hal/gpio.md:87 +msgid "## Pin state should be encoded as type parameters (C-PIN-STATE)" +msgstr "" + +#: src/design-patterns/hal/gpio.md:89 +msgid "" +"Pins may be configured as input or output with different characteristics\n" +"depending on the chip or family. This state should be encoded in the type " +"system\n" +"to prevent use of pins in incorrect states." +msgstr "" + +#: src/design-patterns/hal/gpio.md:93 +msgid "" +"Additional, chip-specific state (eg. drive strength) may also be encoded in " +"this\n" +"way, using additional type parameters." +msgstr "" + +#: src/design-patterns/hal/gpio.md:96 +msgid "" +"Methods for changing the pin state should be provided as `into_input` and\n" +"`into_output` methods." +msgstr "" + +#: src/design-patterns/hal/gpio.md:99 +msgid "" +"Additionally, `with_{input,output}_state` methods should be provided that\n" +"temporarily reconfigure a pin in a different state without moving it." +msgstr "" + +#: src/design-patterns/hal/gpio.md:102 +msgid "" +"The following methods should be provided for every pin type (that is, both\n" +"erased and non-erased pin types should provide the same API):" +msgstr "" + +#: src/design-patterns/hal/gpio.md:105 +msgid "" +"* `pub fn into_input(self, input: N) -> Pin`\n" +"* `pub fn into_output(self, output: N) -> Pin`\n" +"* ```ignore\n" +" pub fn with_input_state(\n" +" &mut self,\n" +" input: N,\n" +" f: impl FnOnce(&mut PA1) -> R,\n" +" ) -> R\n" +" ```\n" +"* ```ignore\n" +" pub fn with_output_state(\n" +" &mut self,\n" +" output: N,\n" +" f: impl FnOnce(&mut PA1) -> R,\n" +" ) -> R\n" +" ```" +msgstr "" + +#: src/design-patterns/hal/gpio.md:123 +msgid "" +"Pin state should be bounded by sealed traits. Users of the HAL should have " +"no\n" +"need to add their own state. The traits can provide HAL-specific methods\n" +"required to implement the pin state API." +msgstr "" + +#: src/design-patterns/hal/gpio.md:129 +msgid "" +"```rust\n" +"# use std::marker::PhantomData;\n" +"mod sealed {\n" +" pub trait Sealed {}\n" +"}\n" +"\n" +"pub trait PinState: sealed::Sealed {}\n" +"pub trait OutputState: sealed::Sealed {}\n" +"pub trait InputState: sealed::Sealed {\n" +" // ...\n" +"}\n" +"\n" +"pub struct Output {\n" +" _p: PhantomData,\n" +"}\n" +"\n" +"impl PinState for Output {}\n" +"impl sealed::Sealed for Output {}\n" +"\n" +"pub struct PushPull;\n" +"pub struct OpenDrain;\n" +"\n" +"impl OutputState for PushPull {}\n" +"impl OutputState for OpenDrain {}\n" +"impl sealed::Sealed for PushPull {}\n" +"impl sealed::Sealed for OpenDrain {}\n" +"\n" +"pub struct Input {\n" +" _p: PhantomData,\n" +"}\n" +"\n" +"impl PinState for Input {}\n" +"impl sealed::Sealed for Input {}\n" +"\n" +"pub struct Floating;\n" +"pub struct PullUp;\n" +"pub struct PullDown;\n" +"\n" +"impl InputState for Floating {}\n" +"impl InputState for PullUp {}\n" +"impl InputState for PullDown {}\n" +"impl sealed::Sealed for Floating {}\n" +"impl sealed::Sealed for PullUp {}\n" +"impl sealed::Sealed for PullDown {}\n" +"\n" +"pub struct PA1 {\n" +" _p: PhantomData,\n" +"}\n" +"\n" +"impl PA1 {\n" +" pub fn into_input(self, input: N) -> PA1> {\n" +" todo!()\n" +" }\n" +"\n" +" pub fn into_output(self, output: N) -> PA1> {\n" +" todo!()\n" +" }\n" +"\n" +" pub fn with_input_state(\n" +" &mut self,\n" +" input: N,\n" +" f: impl FnOnce(&mut PA1) -> R,\n" +" ) -> R {\n" +" todo!()\n" +" }\n" +"\n" +" pub fn with_output_state(\n" +" &mut self,\n" +" output: N,\n" +" f: impl FnOnce(&mut PA1) -> R,\n" +" ) -> R {\n" +" todo!()\n" +" }\n" +"}\n" +"\n" +"// Same for `PA` and `Pin`, and other pin types.\n" +"```" +msgstr "" + +#: src/c-tips/index.md:1 +msgid "# Tips for embedded C developers" +msgstr "" + +#: src/c-tips/index.md:3 +msgid "" +"This chapter collects a variety of tips that might be useful to experienced\n" +"embedded C developers looking to start writing Rust. It will especially\n" +"highlight how things you might already be used to in C are different in Rust." +msgstr "" + +#: src/c-tips/index.md:7 +msgid "## Preprocessor" +msgstr "" + +#: src/c-tips/index.md:9 +msgid "" +"In embedded C it is very common to use the preprocessor for a variety of\n" +"purposes, such as:" +msgstr "" + +#: src/c-tips/index.md:12 +msgid "" +"* Compile-time selection of code blocks with `#ifdef`\n" +"* Compile-time array sizes and computations\n" +"* Macros to simplify common patterns (to avoid function call overhead)" +msgstr "" + +#: src/c-tips/index.md:16 +msgid "" +"In Rust there is no preprocessor, and so many of these use cases are " +"addressed\n" +"differently. In the rest of this section we cover various alternatives to\n" +"using the preprocessor." +msgstr "" + +#: src/c-tips/index.md:20 +msgid "### Compile-Time Code Selection" +msgstr "" + +#: src/c-tips/index.md:22 +msgid "" +"The closest match to `#ifdef ... #endif` in Rust are [Cargo features]. " +"These\n" +"are a little more formal than the C preprocessor: all possible features are\n" +"explicitly listed per crate, and can only be either on or off. Features are\n" +"turned on when you list a crate as a dependency, and are additive: if any " +"crate\n" +"in your dependency tree enables a feature for another crate, that feature " +"will\n" +"be enabled for all users of that crate." +msgstr "" + +#: src/c-tips/index.md:31 +msgid "" +"For example, you might have a crate which provides a library of signal\n" +"processing primitives. Each one might take some extra time to compile or\n" +"declare some large table of constants which you'd like to avoid. You could\n" +"declare a Cargo feature for each component in your `Cargo.toml`:" +msgstr "" + +#: src/c-tips/index.md:36 +msgid "" +"```toml\n" +"[features]\n" +"FIR = []\n" +"IIR = []\n" +"```" +msgstr "" + +#: src/c-tips/index.md:42 +msgid "" +"Then, in your code, use `#[cfg(feature=\"FIR\")]` to control what is " +"included." +msgstr "" + +#: src/c-tips/index.md:44 +msgid "" +"```rust\n" +"/// In your top-level lib.rs\n" +"\n" +"#[cfg(feature=\"FIR\")]\n" +"pub mod fir;\n" +"\n" +"#[cfg(feature=\"IIR\")]\n" +"pub mod iir;\n" +"```" +msgstr "" + +#: src/c-tips/index.md:54 +msgid "" +"You can similarly include code blocks only if a feature is _not_ enabled, or " +"if\n" +"any combination of features are or are not enabled." +msgstr "" + +#: src/c-tips/index.md:57 +msgid "" +"Additionally, Rust provides a number of automatically-set conditions you " +"can\n" +"use, such as `target_arch` to select different code based on architecture. " +"For\n" +"full details of the conditional compilation support, refer to the\n" +"[conditional compilation] chapter of the Rust reference." +msgstr "" + +#: src/c-tips/index.md:64 +msgid "" +"The conditional compilation will only apply to the next statement or block. " +"If\n" +"a block can not be used in the current scope then the `cfg` attribute will\n" +"need to be used multiple times. It's worth noting that most of the time it " +"is\n" +"better to simply include all the code and allow the compiler to remove dead\n" +"code when optimising: it's simpler for you and your users, and in general " +"the\n" +"compiler will do a good job of removing unused code." +msgstr "" + +#: src/c-tips/index.md:71 +msgid "### Compile-Time Sizes and Computation" +msgstr "" + +#: src/c-tips/index.md:73 +msgid "" +"Rust supports `const fn`, functions which are guaranteed to be evaluable at\n" +"compile-time and can therefore be used where constants are required, such " +"as\n" +"in the size of arrays. This can be used alongside features mentioned above,\n" +"for example:" +msgstr "" + +#: src/c-tips/index.md:78 +msgid "" +"```rust\n" +"const fn array_size() -> usize {\n" +" #[cfg(feature=\"use_more_ram\")]\n" +" { 1024 }\n" +" #[cfg(not(feature=\"use_more_ram\"))]\n" +" { 128 }\n" +"}\n" +"\n" +"static BUF: [u32; array_size()] = [0u32; array_size()];\n" +"```" +msgstr "" + +#: src/c-tips/index.md:89 +msgid "" +"These are new to stable Rust as of 1.31, so documentation is still sparse. " +"The\n" +"functionality available to `const fn` is also very limited at the time of\n" +"writing; in future Rust releases it is expected to expand on what is " +"permitted\n" +"in a `const fn`." +msgstr "" + +#: src/c-tips/index.md:94 +msgid "### Macros" +msgstr "" + +#: src/c-tips/index.md:96 +msgid "" +"Rust provides an extremely powerful [macro system]. While the C " +"preprocessor\n" +"operates almost directly on the text of your source code, the Rust macro " +"system\n" +"operates at a higher level. There are two varieties of Rust macro: _macros " +"by\n" +"example_ and _procedural macros_. The former are simpler and most common; " +"they\n" +"look like function calls and can expand to a complete expression, " +"statement,\n" +"item, or pattern. Procedural macros are more complex but permit extremely\n" +"powerful additions to the Rust language: they can transform arbitrary Rust\n" +"syntax into new Rust syntax." +msgstr "" + +#: src/c-tips/index.md:107 +msgid "" +"In general, where you might have used a C preprocessor macro, you probably " +"want\n" +"to see if a macro-by-example can do the job instead. They can be defined in\n" +"your crate and easily used by your own crate or exported for other users. " +"Be\n" +"aware that since they must expand to complete expressions, statements, " +"items,\n" +"or patterns, some use cases of C preprocessor macros will not work, for " +"example\n" +"a macro that expands to part of a variable name or an incomplete set of " +"items\n" +"in a list." +msgstr "" + +#: src/c-tips/index.md:115 +msgid "" +"As with Cargo features, it is worth considering if you even need the macro. " +"In\n" +"many cases a regular function is easier to understand and will be inlined " +"to\n" +"the same code as a macro. The `#[inline]` and `#[inline(always)]` " +"[attributes]\n" +"give you further control over this process, although care should be taken " +"here\n" +"as well — the compiler will automatically inline functions from the same " +"crate\n" +"where appropriate, so forcing it to do so inappropriately might actually " +"lead\n" +"to decreased performance." +msgstr "" + +#: src/c-tips/index.md:125 +msgid "" +"Explaining the entire Rust macro system is out of scope for this tips page, " +"so\n" +"you are encouraged to consult the Rust documentation for full details." +msgstr "" + +#: src/c-tips/index.md:128 +msgid "## Build System" +msgstr "" + +#: src/c-tips/index.md:130 +msgid "" +"Most Rust crates are built using Cargo (although it is not required). This\n" +"takes care of many difficult problems with traditional build systems. " +"However,\n" +"you may wish to customise the build process. Cargo provides [`build.rs`\n" +"scripts] for this purpose. They are Rust scripts which can interact with " +"the\n" +"Cargo build system as required." +msgstr "" + +#: src/c-tips/index.md:138 +msgid "Common use cases for build scripts include:" +msgstr "" + +#: src/c-tips/index.md:140 +msgid "" +"* provide build-time information, for example statically embedding the " +"build\n" +" date or Git commit hash into your executable\n" +"* generate linker scripts at build time depending on selected features or " +"other\n" +" logic\n" +"* change the Cargo build configuration\n" +"* add extra static libraries to link against" +msgstr "" + +#: src/c-tips/index.md:147 +msgid "" +"At present there is no support for post-build scripts, which you might\n" +"traditionally have used for tasks like automatic generation of binaries " +"from\n" +"the build objects or printing build information." +msgstr "" + +#: src/c-tips/index.md:151 +msgid "### Cross-Compiling" +msgstr "" + +#: src/c-tips/index.md:153 +msgid "" +"Using Cargo for your build system also simplifies cross-compiling. In most\n" +"cases it suffices to tell Cargo `--target thumbv6m-none-eabi` and find a\n" +"suitable executable in `target/thumbv6m-none-eabi/debug/myapp`." +msgstr "" + +#: src/c-tips/index.md:157 +msgid "" +"For platforms not natively supported by Rust, you will need to build " +"`libcore`\n" +"for that target yourself. On such platforms, [Xargo] can be used as a stand-" +"in\n" +"for Cargo which automatically builds `libcore` for you." +msgstr "" + +#: src/c-tips/index.md:163 +msgid "## Iterators vs Array Access" +msgstr "" + +#: src/c-tips/index.md:165 +msgid "In C you are probably used to accessing arrays directly by their index:" +msgstr "" + +#: src/c-tips/index.md:167 +msgid "" +"```c\n" +"int16_t arr[16];\n" +"int i;\n" +"for(i=0; i` being printed." +msgstr "" + +#: src/unsorted/speed-vs-size.md:31 +msgid "" +"The biggest downside of the `dev` profile is that the resulting binary will " +"be\n" +"huge and slow. The size is usually more of a problem because unoptimized\n" +"binaries can occupy dozens of KiB of Flash, which your target device may " +"not\n" +"have -- the result: your unoptimized binary doesn't fit in your device!" +msgstr "" + +#: src/unsorted/speed-vs-size.md:36 +msgid "Can we have smaller, debugger friendly binaries? Yes, there's a trick." +msgstr "" + +#: src/unsorted/speed-vs-size.md:38 +msgid "### Optimizing dependencies" +msgstr "" + +#: src/unsorted/speed-vs-size.md:40 +msgid "" +"There's a Cargo feature named [`profile-overrides`] that lets you\n" +"override the optimization level of dependencies. You can use that feature " +"to\n" +"optimize all dependencies for size while keeping the top crate unoptimized " +"and\n" +"debugger friendly." +msgstr "" + +#: src/unsorted/speed-vs-size.md:47 +msgid "Here's an example:" +msgstr "" + +#: src/unsorted/speed-vs-size.md:49 +msgid "" +"``` toml\n" +"# Cargo.toml\n" +"[package]\n" +"name = \"app\"\n" +"# ..\n" +"\n" +"[profile.dev.package.\"*\"] # +\n" +"opt-level = \"z\" # +\n" +"```" +msgstr "" + +#: src/unsorted/speed-vs-size.md:59 +msgid "Without the override:" +msgstr "" + +#: src/unsorted/speed-vs-size.md:61 +msgid "" +"``` text\n" +"$ cargo size --bin app -- -A\n" +"app :\n" +"section size addr\n" +".vector_table 1024 0x8000000\n" +".text 9060 0x8000400\n" +".rodata 1708 0x8002780\n" +".data 0 0x20000000\n" +".bss 4 0x20000000\n" +"```" +msgstr "" + +#: src/unsorted/speed-vs-size.md:72 +msgid "With the override:" +msgstr "" + +#: src/unsorted/speed-vs-size.md:74 +msgid "" +"``` text\n" +"$ cargo size --bin app -- -A\n" +"app :\n" +"section size addr\n" +".vector_table 1024 0x8000000\n" +".text 3490 0x8000400\n" +".rodata 1100 0x80011c0\n" +".data 0 0x20000000\n" +".bss 4 0x20000000\n" +"```" +msgstr "" + +#: src/unsorted/speed-vs-size.md:85 +msgid "" +"That's a 6 KiB reduction in Flash usage without any loss in the " +"debuggability of\n" +"the top crate. If you step into a dependency then you'll start seeing those\n" +"`` messages again but it's usually the case that you " +"want\n" +"to debug the top crate and not the dependencies. And if you *do* need to " +"debug a\n" +"dependency then you can use the `profile-overrides` feature to exclude a\n" +"particular dependency from being optimized. See example below:" +msgstr "" + +#: src/unsorted/speed-vs-size.md:92 +msgid "" +"``` toml\n" +"# ..\n" +"\n" +"# don't optimize the `cortex-m-rt` crate\n" +"[profile.dev.package.cortex-m-rt] # +\n" +"opt-level = 0 # +\n" +"\n" +"# but do optimize all the other dependencies\n" +"[profile.dev.package.\"*\"]\n" +"codegen-units = 1 # better optimizations\n" +"opt-level = \"z\"\n" +"```" +msgstr "" + +#: src/unsorted/speed-vs-size.md:105 +msgid "Now the top crate and `cortex-m-rt` are debugger friendly!" +msgstr "" + +#: src/unsorted/speed-vs-size.md:107 +msgid "## Optimize for speed" +msgstr "" + +#: src/unsorted/speed-vs-size.md:109 +msgid "" +"As of 2018-09-18 `rustc` supports three \"optimize for speed\" levels: `opt-" +"level\n" +"= 1`, `2` and `3`. When you run `cargo build --release` you are using the " +"release\n" +"profile which defaults to `opt-level = 3`." +msgstr "" + +#: src/unsorted/speed-vs-size.md:113 +msgid "" +"Both `opt-level = 2` and `3` optimize for speed at the expense of binary " +"size,\n" +"but level `3` does more vectorization and inlining than level `2`. In\n" +"particular, you'll see that at `opt-level` equal to or greater than `2` LLVM " +"will\n" +"unroll loops. Loop unrolling has a rather high cost in terms of Flash / ROM\n" +"(e.g. from 26 bytes to 194 for a zero this array loop) but can also halve " +"the\n" +"execution time given the right conditions (e.g. number of iterations is big\n" +"enough)." +msgstr "" + +#: src/unsorted/speed-vs-size.md:121 +msgid "" +"Currently there's no way to disable loop unrolling in `opt-level = 2` and " +"`3` so\n" +"if you can't afford its cost you should optimize your program for size." +msgstr "" + +#: src/unsorted/speed-vs-size.md:124 +msgid "## Optimize for size" +msgstr "" + +#: src/unsorted/speed-vs-size.md:126 +msgid "" +"As of 2018-09-18 `rustc` supports two \"optimize for size\" levels: `opt-" +"level =\n" +"\"s\"` and `\"z\"`. These names were inherited from clang / LLVM and are not " +"too\n" +"descriptive but `\"z\"` is meant to give the idea that it produces smaller\n" +"binaries than `\"s\"`." +msgstr "" + +#: src/unsorted/speed-vs-size.md:131 +msgid "" +"If you want your release binaries to be optimized for size then change the\n" +"`profile.release.opt-level` setting in `Cargo.toml` as shown below." +msgstr "" + +#: src/unsorted/speed-vs-size.md:134 +msgid "" +"``` toml\n" +"[profile.release]\n" +"# or \"z\"\n" +"opt-level = \"s\"\n" +"```" +msgstr "" + +#: src/unsorted/speed-vs-size.md:140 +msgid "" +"These two optimization levels greatly reduce LLVM's inline threshold, a " +"metric\n" +"used to decide whether to inline a function or not. One of Rust principles " +"are\n" +"zero cost abstractions; these abstractions tend to use a lot of newtypes " +"and\n" +"small functions to hold invariants (e.g. functions that borrow an inner " +"value\n" +"like `deref`, `as_ref`) so a low inline threshold can make LLVM miss\n" +"optimization opportunities (e.g. eliminate dead branches, inline calls to\n" +"closures)." +msgstr "" + +#: src/unsorted/speed-vs-size.md:148 +msgid "" +"When optimizing for size you may want to try increasing the inline threshold " +"to\n" +"see if that has any effect on the binary size. The recommended way to change " +"the\n" +"inline threshold is to append the `-C inline-threshold` flag to the other\n" +"rustflags in `.cargo/config.toml`." +msgstr "" + +#: src/unsorted/speed-vs-size.md:153 +msgid "" +"``` toml\n" +"# .cargo/config.toml\n" +"# this assumes that you are using the cortex-m-quickstart template\n" +"[target.'cfg(all(target_arch = \"arm\", target_os = \"none\"))']\n" +"rustflags = [\n" +" # ..\n" +" \"-C\", \"inline-threshold=123\", # +\n" +"]\n" +"```" +msgstr "" + +#: src/unsorted/speed-vs-size.md:163 +msgid "" +"What value to use? [As of 1.29.0 these are the inline thresholds that the\n" +"different optimization levels use][inline-threshold]:" +msgstr "" + +#: src/unsorted/speed-vs-size.md:168 +msgid "" +"- `opt-level = 3` uses 275\n" +"- `opt-level = 2` uses 225\n" +"- `opt-level = \"s\"` uses 75\n" +"- `opt-level = \"z\"` uses 25" +msgstr "" + +#: src/unsorted/speed-vs-size.md:173 +msgid "You should try `225` and `275` when optimizing for size." +msgstr "" + +#: src/unsorted/math.md:1 +msgid "# Performing math functionality with `#[no_std]`" +msgstr "" + +#: src/unsorted/math.md:3 +msgid "" +"If you want to perform math related functionality like calculating the " +"squareroot or\n" +"the exponential of a number and you have the full standard library " +"available, your code\n" +"might look like this:" +msgstr "" + +#: src/unsorted/math.md:7 +msgid "" +"```rs\n" +"//! Some mathematical functions with standard support available\n" +"\n" +"fn main() {\n" +" let float: f32 = 4.82832;\n" +" let floored_float = float.floor();\n" +"\n" +" let sqrt_of_four = floored_float.sqrt();\n" +"\n" +" let sinus_of_four = floored_float.sin();\n" +"\n" +" let exponential_of_four = floored_float.exp();\n" +" println!(\"Floored test float {} to {}\", float, floored_float);\n" +" println!(\"The square root of {} is {}\", floored_float, sqrt_of_four);\n" +" println!(\"The sinus of four is {}\", sinus_of_four);\n" +" println!(\n" +" \"The exponential of four to the base e is {}\",\n" +" exponential_of_four\n" +" )\n" +"}\n" +"```" +msgstr "" + +#: src/unsorted/math.md:29 +msgid "" +"Without standard library support, these functions are not available.\n" +"An external crate like [`libm`](https://crates.io/crates/libm) can be used " +"instead. The example code\n" +"would then look like this:" +msgstr "" + +#: src/unsorted/math.md:33 +msgid "" +"```rs\n" +"#![no_main]\n" +"#![no_std]\n" +"\n" +"use panic_halt as _;\n" +"\n" +"use cortex_m_rt::entry;\n" +"use cortex_m_semihosting::{debug, hprintln};\n" +"use libm::{exp, floorf, sin, sqrtf};\n" +"\n" +"#[entry]\n" +"fn main() -> ! {\n" +" let float = 4.82832;\n" +" let floored_float = floorf(float);\n" +"\n" +" let sqrt_of_four = sqrtf(floored_float);\n" +"\n" +" let sinus_of_four = sin(floored_float.into());\n" +"\n" +" let exponential_of_four = exp(floored_float.into());\n" +" hprintln!(\"Floored test float {} to {}\", float, floored_float)." +"unwrap();\n" +" hprintln!(\"The square root of {} is {}\", floored_float, sqrt_of_four)." +"unwrap();\n" +" hprintln!(\"The sinus of four is {}\", sinus_of_four).unwrap();\n" +" hprintln!(\n" +" \"The exponential of four to the base e is {}\",\n" +" exponential_of_four\n" +" )\n" +" .unwrap();\n" +" // exit QEMU\n" +" // NOTE do not run this on hardware; it can corrupt OpenOCD state\n" +" // debug::exit(debug::EXIT_SUCCESS);\n" +"\n" +" loop {}\n" +"}\n" +"```" +msgstr "" + +#: src/unsorted/math.md:69 +msgid "" +"If you need to perform more complex operations like DSP signal processing or " +"advanced linear\n" +"algebra on your MCU, the following crates might help you" +msgstr "" + +#: src/unsorted/math.md:72 +msgid "" +"- [CMSIS DSP library binding](https://github.com/jacobrosenthal/cmsis-dsp-" +"sys)\n" +"- [`micromath`](https://github.com/tarcieri/micromath)\n" +"- [`microfft`](https://crates.io/crates/microfft)\n" +"- [`nalgebra`](https://github.com/dimforge/nalgebra)" +msgstr "" + +#: src/appendix/glossary.md:1 +msgid "# Appendix A: Glossary" +msgstr "" + +#: src/appendix/glossary.md:3 +msgid "" +"The embedded ecosystem is full of different protocols, hardware components " +"and\n" +"vendor-specific things that use their own terms and abbreviations. This " +"Glossary\n" +"attempts to list them with pointers for understanding them better." +msgstr "" + +#: src/appendix/glossary.md:7 +msgid "### BSP" +msgstr "" + +#: src/appendix/glossary.md:9 +msgid "" +"A Board Support Crate provides a high level interface configured for a " +"specific\n" +"board. It usually depends on a [HAL](#hal) crate.\n" +"There is a more detailed description on the [memory-mapped registers page]" +"(../start/registers.md)\n" +"or for a broader overview see [this video](https://youtu.be/vLYit_HHPaY)." +msgstr "" + +#: src/appendix/glossary.md:14 +msgid "### FPU" +msgstr "" + +#: src/appendix/glossary.md:16 +msgid "" +"Floating-point Unit. A 'math processor' running only operations on floating-" +"point numbers." +msgstr "" + +#: src/appendix/glossary.md:18 +msgid "### HAL" +msgstr "" + +#: src/appendix/glossary.md:20 +msgid "" +"A Hardware Abstraction Layer crate provides a developer friendly interface " +"to a microcontroller's\n" +"features and peripherals. It is usually implemented on top of a [Peripheral " +"Access Crate (PAC)](#pac).\n" +"It may also implement traits from the [`embedded-hal`](https://crates.io/" +"crates/embedded-hal) crate.\n" +"There is a more detailed description on the [memory-mapped registers page]" +"(../start/registers.md)\n" +"or for a broader overview see [this video](https://youtu.be/vLYit_HHPaY)." +msgstr "" + +#: src/appendix/glossary.md:26 +msgid "### I2C" +msgstr "" + +#: src/appendix/glossary.md:28 +msgid "" +"Sometimes referred to as `I²C` or Inter-IC. It is a protocol meant for " +"hardware communication\n" +"within a single integrated circuit. See [here][i2c] for more details" +msgstr "" + +#: src/appendix/glossary.md:33 +msgid "### PAC" +msgstr "" + +#: src/appendix/glossary.md:35 +msgid "" +"A Peripheral Access Crate provides access to a microcontroller's " +"peripherals. It is one of\n" +"the lower level crates and is usually generated directly from the provided " +"[SVD](#svd), often\n" +"using [svd2rust](https://github.com/rust-embedded/svd2rust/). The [Hardware " +"Abstraction Layer](#hal)\n" +"would usually depend on this crate.\n" +"There is a more detailed description on the [memory-mapped registers page]" +"(../start/registers.md)\n" +"or for a broader overview see [this video](https://youtu.be/vLYit_HHPaY)." +msgstr "" + +#: src/appendix/glossary.md:42 +msgid "### SPI" +msgstr "" + +#: src/appendix/glossary.md:44 +msgid "Serial Peripheral Interface. See [here][spi] for more information." +msgstr "" + +#: src/appendix/glossary.md:48 +msgid "### SVD" +msgstr "" + +#: src/appendix/glossary.md:50 +msgid "" +"System View Description is an XML file format used to describe the " +"programmers view of a\n" +"microcontroller device. You can read more about it on\n" +"[the ARM CMSIS documentation site](https://www.keil.com/pack/doc/CMSIS/SVD/" +"html/index.html)." +msgstr "" + +#: src/appendix/glossary.md:54 +msgid "### UART" +msgstr "" + +#: src/appendix/glossary.md:56 +msgid "" +"Universal asynchronous receiver-transmitter. See [here][uart] for more " +"information." +msgstr "" + +#: src/appendix/glossary.md:60 +msgid "### USART" +msgstr "" + +#: src/appendix/glossary.md:62 +msgid "" +"Universal synchronous and asynchronous receiver-transmitter. See [here]" +"[usart] for more information." +msgstr "" diff --git a/theme/index.hbs b/theme/index.hbs new file mode 100644 index 00000000..5def1666 --- /dev/null +++ b/theme/index.hbs @@ -0,0 +1,378 @@ + + + + + + {{ title }} + {{#if is_print }} + + {{/if}} + {{#if base_url}} + + {{/if}} + + + + {{> head}} + + + + + + {{#if favicon_svg}} + + {{/if}} + {{#if favicon_png}} + + {{/if}} + + + + {{#if print_enable}} + + {{/if}} + + + + + + {{#if copy_fonts}} + + {{/if}} + + + + + + + + {{#each additional_css}} + + {{/each}} + + {{#if mathjax_support}} + + + {{/if}} + + +
+ + + + + + + + + + + + + + +
+ +
+ {{> header}} + + + + {{#if search_enabled}} + + {{/if}} + + + + +
+
+ {{{ content }}} +
+ + +
+
+ + + +
+ + {{#if live_reload_endpoint}} + + + {{/if}} + + {{#if google_analytics}} + + + {{/if}} + + {{#if playground_line_numbers}} + + {{/if}} + + {{#if playground_copyable}} + + {{/if}} + + {{#if playground_js}} + + + + + + {{/if}} + + {{#if search_js}} + + + + {{/if}} + + + + + + + {{#each additional_js}} + + {{/each}} + + {{#if is_print}} + {{#if mathjax_support}} + + {{else}} + + {{/if}} + {{/if}} + +
+ +