Initial commit

Much of the content here is a direct port from https://github.com/japaric/discovery
This commit is contained in:
Michael Droogleever
2018-07-02 23:01:03 +02:00
commit 8e05dfd010
32 changed files with 2513 additions and 0 deletions

65
src/README.md Normal file
View File

@@ -0,0 +1,65 @@
# MicroRust
> Discover the world of microcontrollers through [Rust] on the [BBC micro:bit][microbit]!
[Rust]: https://www.rust-lang.org/
[microbit]: https://microbit.org/
This book is an introductory course on microcontroller-based embedded systems that uses Rust as the
teaching language (rather than the usual C/C++), and the micro:bit as target system.
## Scope
The following topics will be covered:
- How to write, build, flash and debug an embedded program.
- Functionality ("peripherals") commonly found in microcontrollers:
- Digital input and output, using buttons and LEDs
<!-- - Functionality ("peripherals") commonly found in microcontrollers: Digital input and output, Pulse
Width Modulation (PWM), Analog to Digital Converters (ADC), common communication protocols like
Serial, I2C and SPI, etc. -->
<!-- - Multitasking concepts: cooperative vs preemptive multitasking, interrupts, schedulers, etc. -->
<!-- - Control systems concepts: sensors, calibration, digital filters, actuators, open loop control,
closed loop control, etc. -->
## Approach
- Beginner friendly.
No previous experience with microcontrollers or embedded systems is required.
- Hands on.
*You* will be doing most of the work here.
When possible, pages will end on a problem for you to solve, with the solution on the next page.
Plenty of exercises to put the theory into practice.
- Standard. We'll make plenty use of standard tooling and processes to ease development.
Fixing compiler errors, debugging with GDB, and logging will be introduced early on.
Using LEDs as a debugging mechanism has no place here.
## Non-goals
What's out of scope for this book:
- Teaching Rust.
There's plenty of material on that topic already.
We'll focus on microcontrollers and embedded systems.
- Teaching electric circuit theory or electronics.
We'll cover the minimum required to understand how some devices work along the way.
- Covering Rustic, low level details.
We won't be talking about linker scripts, the boot process,
or how to glue those two into a minimally working Rust program.
## Reporting problems
The source of this book is in [this repository].
If you encounter any typo or problem with the code report it on the [issue tracker],
or even submit a pull request.
[this repository]: https://github.com/droogmic/microrust
[issue tracker]: https://github.com/droogmic/microrust/issues

34
src/SUMMARY.md Normal file
View File

@@ -0,0 +1,34 @@
[Introduction](README.md)
[Background](background/README.md)
[Requirements](requirements/README.md)
- [Meet your hardware](hardware/README.md)
- [Development environment setup](setup/README.md)
- [Linux](setup/LINUX.md)
- [Windows](setup/WINDOWS.md)
- [macOS](setup/MACOS.md)
- [Verify the installation](setup/VERIFY.md)
- [Getting started](getting-started/00.00.README.md)
- [Building](getting-started/01.00.PROJECT.md)
- [Flashing](getting-started/02.00.FLASH.md)
- [LED](getting-started/03.00.LED.md)
- [Debugging](getting-started/04.00.DEBUGGING.md)
[Explore](appendix/explore.md)
[General troubleshooting](appendix/troubleshooting.md)
<!-- - [LED roulette](05-led-roulette/README.md)
- [Build it](05-led-roulette/build-it.md)
- [Flash it](05-led-roulette/flash-it.md)
- [Debug it](05-led-roulette/debug-it.md)
- [The `led` and `delay` abstractions](05-led-roulette/the-led-and-delay-abstractions.md)
- [The challenge](05-led-roulette/the-challenge.md)
- [My solution](05-led-roulette/my-solution.md) -->
<!-- - [Hello, world!](06-hello-world/README.md)
- [`panic!`](06-hello-world/panic.md) -->

162
src/appendix/explore.md Normal file
View File

@@ -0,0 +1,162 @@
# What's left for you to explore
We have barely scratched the surface! There's lots of stuff left for you to explore:
## Multitasking
All our programs executed a single task. How could we achieve multitasking in a system with no OS,
and thus no threads. There are two main approaches to multitasking: preemptive multitasking and
cooperative multitasking.
In preemptive multitasking a task that's currently being executed can, at any point in time, be
*preempted* (interrupted) by another task. On preemption, the first task will be suspended and the
processor will instead execute the second task. At some point the first task will be resumed.
Microcontrollers provide hardware support for preemption in the form of *interrupts*.
In cooperative multitasking a task that's being executed will run until it reaches a *suspension
point*. When the processor reaches that suspension point it will stop executing the current task and
instead go and execute a different task. At some point the first task will be resumed. The main
difference between these two approaches to multitasking is that in cooperative multitasking *yields*
execution control at *known* suspension points instead of being forcefully preempted at any point of
its execution.
## Direct Memory Access (DMA).
This peripheral is a kind of *asynchronous* `memcpy`. So far our programs have
been pumping data, byte by byte, into peripherals like UART and I2C. This DMA
peripheral can be used to perform bulk transfers of data. Either from RAM to
RAM, from a peripheral, like a UART, to RAM or from RAM to a peripheral. You can
schedule a DMA transfer, like read 256 bytes from USART1 into this buffer, leave
it running in the background and then poll some register to see if it has
completed so you can do other stuff while the transfer is ongoing.
## Sleeping
All our programs have been continuously polling peripherals to see if there's
anything that needs to be done. However, some times there's nothing to be done!
At those times, the microcontroller should "sleep".
When the processor sleeps, it stops executing instructions and this saves power.
It's almost always a good idea to save power so your microcontroller should be
sleeping as much as possible. But, how does it know when it has to wake up to
perform some action? "Interrupts" are one of the events that wake up the
microcontroller but there are others and the `wfi` and `wfe` are the
instructions that make the processor "sleep".
## Pulse Width Modulation (PWM)
In a nutshell, PWM is turning on something and then turning it off periodically
while keeping some proportion ("duty cycle") between the "on time" and the "off
time". When used on a LED with a sufficiently high frequency, this can be used
to dim the LED. A low duty cycle, say 10% on time and 90% off time, will make
the LED very dim wheres a high duty cycle, say 90% on time and 10% off time,
will make the LED much brighter (almost as if it were fully powered).
In general, PWM can be used to control how much *power* is given to some
electric device. With proper (power) electronics between a microcontroller and
an electrical motor, PWM can be used to control how much power is given to the
motor thus it can be used to control its torque and speed. Then you can add an
angular position sensor and you got yourself a closed loop controller that can
control the position of the motor at different loads.
## Digital input
We have used the microcontroller pins as digital outputs, to drive LEDs. But
these pins can also be configured as digital inputs. As digital inputs, these
pins can read the binary state of switches (on/off) or buttons (pressed/not
pressed).
(*spoilers* reading the binary state of switches / buttons is not as
straightforward as it sounds ;-)
## Sensor fusion
The STM32F3DISCOVERY contains three motion sensors: an accelerometer, a
gyroscope and a magnetometer. On their own these measure: (proper) acceleration,
angular speed and (the Earth's) magnetic field. But these magnitudes can be
"fused" into something more useful: a "robust" measurement of the orientation of
the board. Where robust means with less measurement error than a single sensor
would be capable of.
This idea of deriving more reliable data from different sources is known as
sensor fusion.
## Analog-to-Digital Converters (ADC)
There are a lots of digital sensors out there. You can use a protocol like I2C
and SPI to read them. But analog sensors also exist! These sensors just output a
voltage level that's proportional to the magnitude they are sensing.
The ADC peripheral can be use to convert that "analog" voltage level, say `1.25`
Volts,into a "digital" number, say in the `[0, 65535]` range, that the processor
can use in its calculations.
## Digital-to-Analog Converters (DAC)
As you might expect a DAC is exactly the opposite of ADC. You can write some
digital value into a register to produce a voltage in the `[0, 3.3V]` range
(assuming a `3.3V` power supply) on some "analog" pin. When this analog pin is
connected to some appropriate electronics and the register is written to at some
constant, fast rate (frequency) with the right values you can produce sounds or
even music!
## Real Time Clock (RTC)
This peripheral can be used to track time in "human format". Seconds, minutes,
hours, days, months and years. This peripheral handles the translation from
"ticks" to these human friendly units of time. It even handles leap years and
Daylight Save Time for you!
## Other communication protocols
SPI, I2S, SMBUS, CAN, IrDA, Ethernet, USB, Bluetooth, etc.
Different applications use different communication protocols. User facing
applications usually have an USB connector because USB is an ubiquitous
protocol in PCs and smartphones. Whereas inside cars you'll find plenty of CAN
"buses". Some digital sensors use SPI, others use I2C and others, SMBUS.
---
So where to next? There are several options:
- You could check out the examples in the [`microbit`] board support crate repository. All those examples work for
the micro:bit board you have.
[`f3`]: https://docs.rs/f3
- You could try out [this motion sensors demo][madgwick]. Details about the implementation and
source code are available in [this blog post][wd-1-2].
[madgwick]: https://mobile.twitter.com/japaricious/status/962770003325005824
[wd-1-2]: http://blog.japaric.io/wd-1-2-l3gd20-lsm303dlhc-madgwick/
- You could check out [Real Time for The Masses]. A very efficient preemptive multitasking framework
that supports task prioritization and dead lock free execution.
[Real Time for The Masses]: https://docs.rs/cortex-m-rtfm
- You could try running Rust on a different development board. The easiest way to get started is to
use the [`cortex-m-quickstart`] Cargo project template.
[`cortex-m-quickstart`]: https://docs.rs/cortex-m-quickstart/0.2.4/cortex_m_quickstart
- You could check out [this blog post][brave-new-io] which describes how Rust type system can
prevent bugs in I/O configuration.
[brave-new-io]: http://blog.japaric.io/brave-new-io/
- You could check out my [blog] for miscellaneous topics about embedded development with Rust.
[blog]: http://blog.japaric.io
- You could check out the [`embedded-hal`] project which aims to build abstractions (traits) for all
the embedded I/O functionality commonly found on microcontrollers.
[`embedded-hal`]: https://github.com/japaric/embedded-hal
- You could join the [Weekly driver initiative] and help us write generic drivers on top of the
`embedded-hal` traits and that work for all sorts of platforms (ARM Cortex-M, AVR, MSP430, RISCV,
etc.)
[Weekly driver initiative]: https://github.com/rust-lang-nursery/embedded-wg/issues/39

View File

@@ -0,0 +1,170 @@
# General troubleshooting
## OpenOCD problems
### can't connect to OpenOCD - "Error: open failed"
#### Symptoms
Upon trying to establish a *new connection* with the device you get an error
that looks like this:
```
$ openocd -f (..)
(..)
Error: open failed
in procedure 'init'
in procedure 'ocd_bouncer'
```
#### Cause + Fix
- All: The device is not (properly) connected. Check the USB connection using
`lsusb` or the Device Manager.
- Linux: You may not have enough permission to open the device. Try again with
`sudo`. If that works, you can use [these instructions] to make OpenOCD work
without root privilege.
- Windows: You are probably missing the ST-LINK USB driver. Installation
instructions [here].
[these instructions]: 03-setup/linux.html#udev%20rules
[here]: 03-setup/windows.html#ST-LINK%20USB%20driver
### can't connect to OpenOCD - "Polling again in X00ms"
#### Symptoms
Upon trying to establish a *new connection* with the device you get an error
that looks like this:
```
$ openocd -f (..)
(..)
Error: jtag status contains invalid mode value - communication failure
Polling target stm32f3x.cpu failed, trying to reexamine
Examination failed, GDB will be halted. Polling again in 100ms
Info : Previous state query failed, trying to reconnect
Error: jtag status contains invalid mode value - communication failure
Polling target stm32f3x.cpu failed, trying to reexamine
Examination failed, GDB will be halted. Polling again in 300ms
Info : Previous state query failed, trying to reconnect
```
#### Cause
The microcontroller may have get stuck in some tight infinite loop or it may be
continuously raising an exception, e.g. the exception handler is raising an
exception.
#### Fix
- Close OpenOCD, if running
- Press and hold the reset (black) button
- Launch the OpenOCD command
- Now, release the reset button
### OpenOCD connection lost - "Polling again in X00ms"
#### Symptoms
A *running* OpenOCD session suddenly errors with:
```
# openocd -f (..)
Error: jtag status contains invalid mode value - communication failure
Polling target stm32f3x.cpu failed, trying to reexamine
Examination failed, GDB will be halted. Polling again in 100ms
Info : Previous state query failed, trying to reconnect
Error: jtag status contains invalid mode value - communication failure
Polling target stm32f3x.cpu failed, trying to reexamine
Examination failed, GDB will be halted. Polling again in 300ms
Info : Previous state query failed, trying to reconnect
```
#### Cause
The USB connection was lost.
#### Fix
- Close OpenOCD
- Disconnect and re-connect the USB cable.
- Re-launch OpenOCD
### Can't flash the device - "Ignoring packet error, continuing..."
#### Symptoms
While flashing the device, you get:
```
$ arm-none-eabi-gdb $file
Start address 0x8000194, load size 31588
Transfer rate: 22 KB/sec, 5264 bytes/write.
Ignoring packet error, continuing...
Ignoring packet error, continuing...
```
#### Cause
Closed `itmdump` while a program that "printed" to the ITM was running. The
current GDB session will appear to work normally, just without ITM output but
the next GDB session will error with the message that was shown in the previous
section.
Or, `itmdump` was called **after** the `monitor tpiu` was issued thus making
`itmdump` delete the file / named-pipe that OpenOCD was writing to.
#### Fix
- Close/kill GDB, OpenOCD and `itmdump`
- Remove the file / named-pipe that `itmdump` was using (for example,
`itm.txt`).
- Launch OpenOCD
- Then, launch `itmdump`
- Then, launch the GDB session that executes the `monitor tpiu` command.
## Cargo problems
### "can't find crate for `core`"
#### Symptoms
```
Compiling volatile-register v0.1.2
Compiling rlibc v1.0.0
Compiling r0 v0.1.0
error[E0463]: can't find crate for `core`
error: aborting due to previous error
error[E0463]: can't find crate for `core`
error: aborting due to previous error
error[E0463]: can't find crate for `core`
error: aborting due to previous error
Build failed, waiting for other jobs to finish...
Build failed, waiting for other jobs to finish...
error: Could not compile `r0`.
To learn more, run the command again with --verbose.
```
#### Cause
You are using a toolchain older than `nightly-2018-04-08` and forgot to call `rustup target add
thumbv7m-none-eabi`.
#### Fix
Update your nightly and install the `thumbv7em-none-eabihf` target.
``` console
$ rustup update nightly
$ rustup target add thumbv7em-none-eabihf
```

78
src/background/README.md Normal file
View File

@@ -0,0 +1,78 @@
# Background
## What is a microcontroller?
A microcontroller is a *system* on a chip. Whereas your laptop is made up of several discrete
components: a processor, RAM sticks, a hard drive, an ethernet port, etc.; a microcontroller has all
those components built into a single "chip" or package. This makes it possible to build systems with
minimal part count.
## What can you do with a microcontroller?
Lots of things! Microcontrollers are the central part of systems known as *embedded* systems. These
systems are everywhere but you don't usually notice them. These systems control the brakes of your
car, wash your clothes, print your documents, keep you warm, keep you cool, optimize the fuel
consumption of your car, etc.
The main trait of these systems is that they operate without user intervention even if they expose a
user interface like a washing machine does; most of their operation is done on their own.
The other common trait of these systems is that they *control* a process. And for that these systems
usually have one or more sensors and one or more actuators. For example, an HVAC system has several
sensors, thermometers and humidy sensors spread across some area, and several actuators as well,
heating elements and fans connected to ducts.
## When should I use a microcontroller?
All these application I've mentioned, you can probably implement with a Raspberry Pi, a computer
that runs Linux. Why should I bother with a microcontroller that operates without an OS? Sounds like
it would be harder to develop a program.
One main reason is cost. A microcontroller is much cheaper than a general purpose computer. Not only
the microcontroller is cheaper; it also requires much less external electrical components to
operate. This makes Printed Circuit Boards (PCB) smaller and cheaper to design and manufacture.
Another big reason is power consumption. A microcontroller consumes orders of magnitude less power
than a full blown processor. If your application will run on batteries that makes a huge difference.
And last but not least: (hard) *real time* constraints. Some processes require their controllers to
respond to some events within some time interval (e.g. a quadcopter/drone hit by a wind gust). If
this *deadline* is not met, the process could end in catastrophic failure (e.g. the drone crashes to
the ground). A general purpose computer running a general purpose OS has many services running in
the background. This makes it hard to guarantee execution of a program within tight time constraints.
## When should I *not* use a microcontroller?
Where heavy computations are involved. To keep their power consumption low, microcontrollers have
very limited computational resources available to them. For example, some microcontrollers don't
even have hardware support for floating point operations. On those devices, performing a simple
addition of single precision numbers can take hundreds of CPU cycles.
## Development on the micro:bit
The [micro:bit website](https://microbit.org/code/) offers several very simple ways of programming a
microbit, aimed at teaching school children how to program. This is a very good introduction to the
world of microcontollers **and** programming, but falls short of teaching true embedded development.
From there you would usually move to C to develop skills useful in industry, developing performant
embedded software.
## Why use Rust and not C?
Hopefully I don't need to convince you here as you are probably familiar with the language
differences between Rust and C. One point I do want to bring up is package management. C lacks an
official, widely accepted package management solution whereas Rust has Cargo. This makes development
*much* easier. And, IMO, easy package management encourages code reuse because libraries can be
easily integrated into an application which is also a good thing as libraries get more "battle
testing".
## Why should I not use Rust?
Or why should I prefer C over Rust?
The C ecosystem is way more mature. Off the shelf solution for several problems already exist. If
you need to control a time sensitive process, you can grab one of the existing commercial Real Time
Operating Systems (RTOS) out there and solve your problem. There are no commercial, production-grade
RTOSes in Rust yet so you would have to either create one yourself or try one of the ones that are
in development. If you are looking to develop your skills to find a job, it is currently unlikely
that a company doing embedded software development will be using Rust, and so your time would be
better spent learning normal embedded development using C.

View File

@@ -0,0 +1,9 @@
[target.thumbv6m-none-eabi]
runner = "arm-none-eabi-gdb"
rustflags = [
"-C", "link-arg=-Wl,-Tlink.x",
"-C", "link-arg=-nostartfiles",
]
[build]
target = "thumbv6m-none-eabi"

View File

@@ -0,0 +1,4 @@
target remote :3333
load
break main
continue

View File

@@ -0,0 +1,13 @@
# Getting started
Alright, let's start as you usually would with Rust.
``` console
$ rustup update
```
It is always good to keep your toolchain up to date.
Now let's make a new binary project.
You might not do this often, so it is understandeable to forget.
If you run `$ cargo`, you will be given a hint.

View File

@@ -0,0 +1,388 @@
# New Project
``` console
$ cargo new rustled
Created binary (application) `rustled` project
$ cd rustled
Cargo.toml src
```
This has created a binary crate.
Now we could `$ cargo build` this, and even `$ cargo run` it,
but everything is being compiled for, and run on, your computer.
## Targets
The micro:bit has a different architecture than your computer,
so the first step will be to cross compile for the micro:bit's architecture.
If you were to do an internet search, you would find a [platform support list for Rust][platforms].
Looking into this page, you will find the micro:bit's nRF51822 Cortex-M0 microprocessor:
> `thumbv6m-none-eabi [*] [ ] [ ] Bare Cortex-M0, M0+, M1`
"thumbv6m-none-eabi" is known a a target triple. Note what the star represents:
> These are bare-metal microcontroller targets that only have access to the core library, not std.
To install this target:
``` console
$ rustup target add thumbv6m-none-eabi
```
## Build 1
Now how should we use this? Well, if you were to take a look at `$ cargo build -h`, you would try:
``` console
$ cargo build --target thumbv6m-none-eabi
```
```
error[E0463]: can't find crate for `std`
|
= note: the `thumbv6m-none-eabi` target may not be installed
error: aborting due to previous error
For more information about this error, try `rustc --explain E0463`.
error: Could not compile `rustled`.
To learn more, run the command again with --verbose.
```
The help note is rather unhelpful because we just installed that target.
We also just noted that the thumbv6m-none-eabi target does not include std,
only the core crate, which is has a platform independent subset of the std features.
Why is it still looking for the std crate when we build?
### `no_std`
It turns out, rust will always look for the std crate unless explicitly disabled,
so we will add the no_std attribute
`src/main.rs`
``` rust
#![no_std]
fn main() {
println!("Hello, world!");
}
```
## Build 2
``` console
$ cargo build --target thumbv6m-none-eabi
```
```
error: cannot find macro `println!` in this scope
--> src/main.rs:4:5
|
4 | println!("Hello, world!");
| ^^^^^^^
```
`println` is a macro found in the std crate.
We don't need it at the moment, so we can remove it and try to compile again:
```
error: language item required, but not found: `panic_impl`
```
This error, is because the panic macro is unimplemented,
when rustc needs it to have an implementation.
### `panic_impl`
We could try and implement the panic macro ourselves,
but it's easier and more portable to use a crate that does it for us.
If we look on [crates.io for the panic-impl keyword][panic] we will find some examples.
Let us pic the simplest one, and add it to our Cargo.toml.
If you have forgotten how to do this, try looking at [the cargo book][cargo].
[platforms]: https://forge.rust-lang.org/platform-support.html
[panic]: https://crates.io/keywords/panic-impl
[cargo]: https://doc.rust-lang.org/stable/cargo/
`Cargo.toml`
```
[dependencies]
panic-abort = "~0.2"
```
`src/main.rs`
```
#![no_std]
extern crate panic_abort;
fn main() {
}
```
## Build 3
``` console
$ cargo build --target thumbv6m-none-eabi
```
```
error: requires `start` lang_item
```
### `no_main`
In the normal command line rust binaries you would be used to making,
executing the binary usually has the operating system start by executing the C runtime library (crt0).
This in turn invokes the Rust runtime, as marked by the `start` language item,
which in turn invokes the main function.
Having enabled `no_std`, as we are targeting on a microcontroller,
neither the crt0 nor the rust runtime are available,
so even implementing `start` would not help us.
We need to replace the operating system entry point.
You could for example name a function after the default entry point, which for linux is `_main`,
and start that way. Note, you would also need to disable [name mangling][nm]:
```
#![no_std]
#![no_main]
#[no_mangle]
pub extern "C" fn _start() -> ! {
loop {}
}
```
[nm]: https://en.wikipedia.org/wiki/Name_mangling
This is the end of the road for trying to get this to work on our own.
At this point we need the help of a board specific crate and a few cargo tweaks to get this working.
## microbit crate
Let us add a dependency on the board crate for the micro:bit.
```
[dependencies]
panic-abort = "~0.2"
microbit="~0.5"
```
The microbit crate has 2 notable dependencies:
### `embedded-hal`
This crate is a HAL implementation crate, where HAL stands for *hardware abstraction layer*.
As rust becomes more and more popular in embedded development,
it is desireable to have as little hardware specific implementation as possible.
For this reason, the `embedded-hal` crate contains a range of hardware abstraction traits which can
be implemented by board specific crates.
### `cortex-m-rt`
This crate implements the minimal startup / runtime for Cortex-M microcontrollers.
Among other things this crate provides:
- the `entry!` macro, to define the entry point of the program.
- the `exception!` macro, to set or override a processor core exception handler.
This crate requires:
- a definition of the specific microcontroller's memory layout as a memory.x file.
- a definition of the hard fault handler
- a definition of the default exception handler
For more detailed information,
you can use the helpful [cortex-m-quickstart crate][qs] and [its documentation][doc].
[qs]: https://docs.rs/crate/cortex-m-quickstart
[doc]: https://docs.rs/cortex-m-quickstart
## cargo config
Before we go any further,
we are going to tweak the cargo's configuration by editing `rustled/.cargo/config`.
For more information, you can read [the documentation here][cargoconfig].
[cargoconfig]: https://doc.rust-lang.org/cargo/reference/config.html
``` toml
# Configure builds for our target
[target.thumbv6m-none-eabi]
# Execute binary using gdb
runner = "arm-none-eabi-gdb"
# Tweak to the linking process required by the cortex-m-rt crate
rustflags = [
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-nostartfiles",
]
# Automatically select this target when running cargo for this project
[build]
target = "thumbv6m-none-eabi"
```
### arm-none-eabi-gdb
This is a version of gdb (the GNU debugger) for the ARM EABI (embedded application binary interface).
It will allow us to debug the code running on our micro:bit, from your computer.
### Build target
Now, all you need to do is run `$ cargo build`,
and cargo will automatically add `--target thumbv6m-none-eabi`.
## Build 4
`Cargo.toml`
```
[dependencies]
panic-abort = "~0.2"
microbit="~0.5"
```
`src/main.rs`
```
#![no_std]
#![no_main]
extern crate panic_abort;
entry!(main);
fn main() {
}
```
``` console
cargo build
```
```
error[E0308]: mismatched types
--> src/main.rs:9:1
|
8 | entry!(main);
| ^^^^^^^^^^^^^ expected !, found ()
|
= note: expected type `fn() -> !`
found type `fn() {main}`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
```
## `!` return type
A little known rust feature, so I will forgive you if you do not know what this means.
A return type of `!` means that the function cannot return
An easy way to implement this, is by using an infinite loop.
`src/main.rs`
```
#![no_std]
#![no_main]
extern crate panic_abort;
#[macro_use(entry)]
extern crate microbit;
entry!(main);
fn main() -> ! {
loop {}
}
```
## Build 5
```
error: linking with `arm-none-eabi-gcc` failed: exit code: 1
|
= note: "arm-none-eabi-gcc" "-L" "/home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/thumbv6m-none-eabi/lib" "/home/xxx/rust/rustled/target/thumbv6m-none-eabi/debug/deps/rustled-e6053d34b0422141.2yhvr0tmp69gb94x.rcgu.o" "-o"
# SNIP
"/home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/thumbv6m-none-eabi/lib/libcore-fb37a4ea1db1e473.rlib" "-Wl,--end-group" "/home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/thumbv6m-none-eabi/lib/libcompiler_builtins-f2357c0397dd7e0d.rlib" "-Wl,-Tlink.x" "-nostartfiles" "-Wl,-Bdynamic"
= note: /usr/lib/gcc/arm-none-eabi/8.1.0/../../../../arm-none-eabi/bin/ld: cannot open linker script file memory.x: No such file or directory
collect2: error: ld returned 1 exit status
```
A scary error, but if you look closely you will see `cannot open linker script file memory.x: No such file or directory`.
We mentioned something a little earlier about memory.x file.
To save you the hassle of scouring the internet for one or creating your own, you can copy it over into your project:
``` console
cp ../getting-started/memory.x
```
> Often a board support crate will already include this, so this step will not be necessary.
## Build 6
```
error: linking with `arm-none-eabi-gcc` failed: exit code: 1
|
= note: "arm-none-eabi-gcc" "-L" "/home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/thumbv6m-none-eabi/lib" "/home/xxx/rust/rustled/target/thumbv6m-none-eabi/debug/deps/rustled-e6053d34b0422141.2yhvr0tmp69gb94x.rcgu.o" "-o"
# SNIP
"/home/xxx/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/thumbv6m-none-eabi/lib/libcompiler_builtins-f2357c0397dd7e0d.rlib" "-Wl,-Tlink.x" "-nostartfiles" "-Wl,-Bdynamic"
= note: device.x:1: undefined symbol `DefaultHandler' referenced in expression
collect2: error: ld returned 1 exit status
```
Notice `undefined symbol 'DefaultHandler' referenced in expression`.
We said earlier, as with the memory,
that the hard fault handler and default exception handler both need defining.
`Cargo.toml`
```
[dependencies]
panic-abort = "~0.2"
cortex-m-rt="~0.5"
microbit="~0.5"
```
`src/main.rs`
```
#![no_std]
#![no_main]
extern crate panic_abort;
extern crate cortex_m_rt as rt;
#[macro_use(entry, exception)]
extern crate microbit;
use rt::ExceptionFrame;
entry!(main);
fn main() -> ! {
loop {}
}
exception!(HardFault, hard_fault);
fn hard_fault(ef: &ExceptionFrame) -> ! {
panic!("{:#?}", ef);
}
exception!(*, default_handler);
fn default_handler(irqn: i16) {
panic!("Unhandled exception (IRQn = {})", irqn);
}
```
It is all a bit ugly, but fortunately it only needs to be done once.
If you try building now, you should finally be greeted with `Finished`!
## Build Complete
As a sanity check, let's verify that the produced executable is actually an ARM binary:
``` console
$ file target/thumbv6m-none-eabi/debug/rustled
target/thumbv6m-none-eabi/debug/rustled: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped
^^^ ^^^^
```

View File

@@ -0,0 +1,192 @@
# Flashing
Flashing is the process of moving our program into the microcontroller's (persistent) memory. Once flashed, the microcontroller will execute the flashed program every time it is powered on.
In this case, our `rustled` program will be the only program in the microcontroller memory. By this I mean that there's nothing else running on the microcontroller: no OS, no "daemon", nothing. `rustled` has full control over the device. This is what is meant by *bare-metal* programming.
Onto the actual flashing. First thing we need is to do is launch OpenOCD. We did that in the previous section but this time we'll run the command inside a temporary directory (/tmp on *nix; %TEMP% on Windows).
Connect the mirco:bit to your computer and run the following commands on a new terminal.
``` console
$ # *nix
$ cd /tmp
$ # Windows
$ cd %TEMP%
```
We need to give OCD the name of the interfaces we are using:
``` console
$ # All
$ # Windows: remember that you need an extra `-s %PATH_TO_OPENOCD%\share\scripts`
$ openocd -f interface/cmsis-dap.cfg -f target/nrf51.cfg
```
The program will block; leave that terminal open.
Now it's a good time to explain what this command is actually doing.
I mentioned that the micro:bit actually has two microcontrollers.
One of them is used as a USB interface and programmer/debugger.
This microcontroller is connected to the target microcontroller using a Serial Wire Debug (SWD) interface
(this interface is an ARM standard so you'll run into it when dealing with other Cortex-M based microcontrollers).
This SWD interface can be used to flash and debug a microcontroller.
It uses the CMSIS-DAP protocol for host debugging of application programs.
It will appear as a USB device when you connect the micro:bit to your laptop.
As for OpenOCD, it's software that provides some services like a *GDB server* on top of USB
devices that expose a debugging protocol like SWD or JTAG.
Onto the actual command: those `.cfg` files we are using instruct OpenOCD to look for
- a CMSIS-DAP USB interface device (`interface/cmsis-dap.cfg`)
- a nRF51XXX microcontroller target (`target/nrf51.cfg`) to be connected to the USB interface.
The OpenOCD output looks like this:
``` console
Open On-Chip Debugger 0.9.0 (2016-04-27-23:18)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 2.919073
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
```
The "6 breakpoints, 4 watchpoints" part indicates the debugging features the processor has
available.
I mentioned that OpenOCD provides a GDB server so let's connect to that right now:
``` console
$ arm-none-eabi-gdb -q target/thumbv7em-none-eabihf/debug/led-roulette
Reading symbols from target/thumbv7em-none-eabihf/debug/led-roulette...done.
(gdb)
```
This only opens a GDB shell. To actually connect to the OpenOCD GDB server, use the following
command within the GDB shell:
```
(gdb) target remote :3333
Remote debugging using :3333
0x00000000 in ?? ()
```
By default OpenOCD's GDB server listens on TCP port 3333 (localhost). This command is connecting to
that port.
After entering this command, you'll see new output in the OpenOCD terminal:
``` diff
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
+Info : accepting 'gdb' connection on tcp/3333
+Info : device id = 0x10036422
+Info : flash size = 256kbytes
```
Almost there. To flash the device, we'll use the `load` command inside the GDB shell:
```
(gdb) load
Loading section .vector_table, size 0x188 lma 0x8000000
Loading section .text, size 0x38a lma 0x8000188
Loading section .rodata, size 0x8 lma 0x8000514
Start address 0x8000188, load size 1306
Transfer rate: 6 KB/sec, 435 bytes/write.
```
And that's it. You'll also see new output in the OpenOCD terminal.
``` diff
Info : flash size = 256kbytes
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
+adapter speed: 950 kHz
+target state: halted
+target halted due to debug-request, current mode: Thread
+xPSR: 0x01000000 pc: 0x08000194 msp: 0x2000a000
+Info : Unable to match requested speed 8000 kHz, using 4000 kHz
+Info : Unable to match requested speed 8000 kHz, using 4000 kHz
+adapter speed: 4000 kHz
+target state: halted
+target halted due to breakpoint, current mode: Thread
+xPSR: 0x61000000 pc: 0x2000003a msp: 0x2000a000
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
+adapter speed: 950 kHz
+target state: halted
+target halted due to debug-request, current mode: Thread
+xPSR: 0x01000000 pc: 0x08000194 msp: 0x2000a000
```
Our program is loaded, we can now run it!
```
(gdb) continue
Continuing.
```
Continue runs the program until the next breakpoint.
This time it blocks, nothing happens.
This is because all we have in our code is a loop!
## `.gdbinit`
Before we move on though, we are going to add one more file to our project.
This will automate the last few steps so we don't need to repeatedly do the same actions in gdb:
`.gdbinit`
```
target remote :3333
load
```
## LED
Let us now turn on an LED! But how?
Well, first we should look at the documentation of our crate,
and you should be able to figure out how to get access to the gpio,
and set individual pins high and low:
``` rust
if let Some(p) = microbit::Peripherals::take() {
let mut gpio = p.GPIO.split();
let mut pin1 = gpio.pin1.into_push_pull_output();
pin1.set_high();
}
```
Next we need to see how these pins are hooked up,
for that we need [the micro:bit schematics][schematics] linked to at the bottom of [the hardware overview][hw].
On the first sheet you should find a diagram with a grid of numbered LEDs.
> If you do not know much about electronics:
> Each row and column (labelled ROW and COL) represent a GPIO output pin.
> The components labelled LED are light emitting diodes;
> LEDs only let current flow one way, and only emit light when current is flowing.
> If a row is set high, high voltage, and a column is set low, low voltage,
> the LED at the point that they cross will have a potential difference across it,
> so current will flow and it will light up.
It is worth noting that the 5x5 array of LEDs is wired up as a 9x3 array, with 2 missing.
This is usually done to make the circuit design easier.
The fifth sheet shows how each row and column correspond to each GPIO pin.
[hw]: http://tech.microbit.org/hardware/
[schematics]: https://github.com/bbcmicrobit/hardware/blob/master/SCH_BBC-Microbit_V1.3B.pdf
You should now have enough information to try and turn on an LED.

View File

@@ -0,0 +1,41 @@
# LED
This is my solution:
``` rust
#![no_std]
#![no_main]
extern crate panic_abort;
extern crate cortex_m_rt as rt;
#[macro_use(entry, exception)]
extern crate microbit;
use rt::ExceptionFrame;
use microbit::hal::prelude::*;
entry!(main);
fn main() -> ! {
if let Some(p) = microbit::Peripherals::take() {
let mut gpio = p.GPIO.split();
let mut led = gpio.pin13.into_push_pull_output();
let _ = gpio.pin4.into_push_pull_output();
led.set_high();
}
loop {}
}
exception!(HardFault, hard_fault);
fn hard_fault(ef: &ExceptionFrame) -> ! {
panic!("{:#?}", ef);
}
exception!(*, default_handler);
fn default_handler(irqn: i16) {
panic!("Unhandled exception (IRQn = {})", irqn);
}
```
It is worth noting that pin4 starts low, so does not need to be explicitly set low.

View File

@@ -0,0 +1 @@
# Debugging

View File

@@ -0,0 +1,8 @@
[package]
name = "rustled"
version = "0.1.0"
[dependencies]
panic-abort = "~0.2"
cortex-m-rt="~0.5"
microbit="~0.5"

View File

@@ -0,0 +1,11 @@
MEMORY
{
/* NOTE K = KiBi = 1024 bytes */
FLASH : ORIGIN = 0x00000000, LENGTH = 256K
RAM : ORIGIN = 0x20000000, LENGTH = 16K
}
/* This is where the call stack will be allocated. */
/* The stack is of the full descending type. */
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
_stack_start = ORIGIN(RAM) + LENGTH(RAM);

View File

@@ -0,0 +1,29 @@
#![deny(unsafe_code)]
#![no_std]
extern crate aux5;
use aux5::prelude::*;
use aux5::{Delay, Leds};
fn main() {
let (mut delay, mut leds): (Delay, Leds) = aux5::init();
let period = 50_u16;
let mut step: usize = 0;
loop {
match step % 2 {
0 => leds[step/2].on(),
1 => {
let wrap_step = ((step + 16 - 3) / 2) % 8;
leds[wrap_step].off();
},
_ => unreachable!(),
}
delay.delay_ms(period);
match step {
15 => step = 0,
_ => step += 1,
}
}
}

58
src/hardware/README.md Normal file
View File

@@ -0,0 +1,58 @@
# Meet your hardware
Let's get familiar with the hardware we'll be working with.
## BBC micro:bit (the "microbit")
<p align="center">
<img title="microbit" src="http://tech.microbit.org/docs/hardware/assets/microbit-overview.png">
</p>
What does this board contain? For full details see the [microbit hardware page][microbit].
[microbit]: http://tech.microbit.org/hardware
- A Nordic nRF51822 microcontroller. This microcontroller has
- A single core ARM Cortex-M0 processor with a maximum clock frequency of 16 MHz.
- 256 KB of flash memory. (1 KB = 10**24** bytes)
- 16 KB of static RAM.
- many "peripherals": timers, GPIO, I2C, SPI, UART, etc.
- This microcontroller operates at (around) 3.3V.
- 2 user buttons on the front and 1 reset button on the back.
- A 5x5 array of user LEDs.
- A configureable 23-pin edge connector
- A 2.4GHz radio transciever with support for [bluetooth low energy][ble] (BLE).
[ble]: https://en.wikipedia.org/wiki/Bluetooth_Low_Energy
- An on-core nRF51 temperature sensor.
- An NXP/Freescale MMA8652 3-axis [accelerometer].
[accelerometer]: https://en.wikipedia.org/wiki/Accelerometer
- An NXP/Freescale MAG3110 3-axis [magnetometer].
[magnetometer]: https://en.wikipedia.org/wiki/Magnetometer
- A second microcontroller: NXP/Freescale KL26Z. This microcontroller handles the USB interface,
communication between your computer and the main microcontroller,
and changing the USB's input voltage from 5V to 3.3V.
## Micro-USB Cable
This can be any generic cable, and is used to connect the microbit to your computer.
## External battery pack
The external battery pack that comes with the microbit will not be used explicitly as part of this guide,
but feel free to use it to test your software without being tethered to a computer.

View File

@@ -0,0 +1,52 @@
# Requirements
## Knowledge
The only knowledge requirement to read this book is to know *some* Rust. It's hard to
quantify *some* but a good benchmark is having read and understood the first 14 chapters of [the Rust book][thebook].
[thebook]: https://doc.rust-lang.org/nightly/book/
## Hardware
To follow this material you'll only need a [micro:bit].
[micro:bit]: http://tech.microbit.org/hardware/
You can purchase the BBC micro:bit from [a large list of international resellers][resellers].
[resellers]: https://microbit.org/resellers/
<p align="center">
<img title="microbit" src="https://microbit.org/images/microbit-front.png" width="45%">
<img title="microbit" src="https://microbit.org/images/microbit-back.png" width="45%">
</p>
> **FAQ**: Wait, why do I need this specific device?
It makes my life and yours much easier.
The material is much, much more approachable if we don't have to worry about hardware differences.
Trust me on this one.
> **FAQ**: Can I follow this material with a different development board?
Maybe? It depends mainly on two things: your previous experience with microcontrollers and/or
whether there already exists a high level crate. A list of boards with high level crates available can be found [here][bsc].
[bsc]: https://github.com/rust-embedded/awesome-embedded-rust#board-support-crates
With other development boards, this text would lose most if not all its beginner friendliness
and "easy to follow"-ness, IMO.
There are other similar guides for different hardware. For a full list see [this list][books].
[books]: https://github.com/rust-embedded/awesome-embedded-rust/#books-blogs-and-training-materials
The following are worth a special mention:
- [Discovery](https://japaric.github.io/discovery/) by @japaric: The genesis guide which this is based on. Uses the STM32F3DISCOVERY.
If you have a different cortex-m development board and you don't consider yourself a total beginner, you are
better off starting with the [cortex-m-quickstart] project template.
[cortex-m-quickstart]: https://docs.rs/crate/cortex-m-quickstart

158
src/setup/LINUX.md Normal file
View File

@@ -0,0 +1,158 @@
# Linux
Here are the installation commands for a few Linux distributions.
## REQUIRED packages
- Ubuntu 16.04 or newer / Debian Jessie or newer
``` console
$ sudo apt-get install \
gcc-arm-none-eabi \
gdb-arm-none-eabi \
minicom \
openocd
```
- Fedora 23 or newer
``` console
$ sudo dnf install \
arm-none-eabi-gcc-cs \
arm-none-eabi-gdb \
minicom \
openocd
```
- Arch Linux
``` console
$ sudo pacman -S \
arm-none-eabi-gcc \
arm-none-eabi-gdb \
minicom \
openocd
```
- Other distros
For distros that don't have packages for [ARM's pre-built toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads), download the "Linux 64-bit" file and put its `bin` directory on your path. Here's one way to do it:
``` console
$ mkdir -p ~/local && cd ~/local
$ tar xjf /path/to/downloaded/file/gcc-arm-none-eabi-7-2017-q4-major-linux.tar.bz2.tbz
```
Then, use your editor of choice to append to your `PATH` in the appropriate shell init file (e.g. `~/.zshrc` or `~/.bashrc`):
```
PATH=$PATH:$HOME/local/gcc-arm-none-eabi-7-2017-q4-major/bin
```
## Optional packages
- Ubuntu / Debian
``` console
$ sudo apt-get install \
bluez \
rfkill
```
- Fedora
``` console
$ sudo dnf install \
bluez \
rfkill
```
- Arch Linux
``` console
$ sudo pacman -S \
bluez \
bluez-utils \
rfkill
```
## udev rules
These rules let you use USB devices like the F3 and the Serial module without root privilege, i.e.
`sudo`.
Create these two files in `/etc/udev/rules.d` with the contents shown below.
``` console
$ cat /etc/udev/rules.d/99-ftdi.rules
```
``` text
# FT232 - USB <-> Serial Converter
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", GROUP="uucp"
```
``` console
$ cat /etc/udev/rules.d/99-openocd.rules
```
``` text
# STM32F3DISCOVERY rev A/B - ST-LINK/V2
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", GROUP="uucp"
# STM32F3DISCOVERY rev C+ - ST-LINK/V2-1
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", GROUP="uucp"
```
Then reload the udev rules with:
``` console
$ sudo udevadm control --reload-rules
```
If you had any board plugged to your laptop, unplug them and then plug them in again.
Finally, check if you are in the `uucp` group.
``` console
$ groups $(id -nu)
(..) uucp (..)
$ # ^^^^
```
(`$(id -nu)` returns your user name. In my case it's `japaric`.)
If `uucp` appears in the output. You are all set! Go to the [next section]. Otherwise, keep reading:
[next section]: 03-setup/verify.html
- Add yourself to the `uucp` group.
``` console
$ sudo usermod -a -G uucp $(id -u -n)
```
- Check again the output of `groups`. `uucp` should be there this time!
``` console
$ groups $(id -nu)
(..) uucp (..)
$ # ^^^^
```
You'll have to re-log for these changes to take effect. You have two options:
You can reboot or log out from your current session and then log in; this will close all the
programs you have open right now.
The other option is to use the command below:
``` console
$ su - $(id -nu)
```
to re-log *only in the current shell* and get access to `uucp` devices *only on that shell*. Other
shells *won't* have access to `uucp` devices unless you manually re-log on them with the same `su`
command.
Now, go to the [next section].

17
src/setup/MACOS.md Normal file
View File

@@ -0,0 +1,17 @@
# macOS
All the tools can be install using [Homebrew]:
[Homebrew]: http://brew.sh/
``` console
$ brew cask install gcc-arm-embedded
$ brew install minicom openocd
```
If the `brew cask` command doesn't work (`Error: Unknown command: cask`), then run `brew tap
Caskroom/tap` first and try again.
That's all! Go to the [next section].
[next section]: 03-setup/verify.html

66
src/setup/README.md Normal file
View File

@@ -0,0 +1,66 @@
# Development environment setup
Dealing with microcontrollers involves several tools as we'll be dealing with an architecture
different than your laptop's and we'll have to run and debug programs on a "remote" device.
## Documentation
Without documentation it is pretty much impossible to work with microcontrollers.
We'll be referring to the [micro:bit hardware page][microbit] and the links found within.
[microbit]: http://tech.microbit.org/hardware
*HEADS UP* Some of the links point to large PDF files several MBs in size.
## Tools
We'll use all the tools listed below. Where a minimum version is not specified, any recent version
should work but we have listed the version we have tested.
- Cargo & `rustc`.
- OpenOCD. version >=0.8
- `arm-none-eabi` toolchain. Tested version: gcc 8.1.0, binutils 2.30.
- `arm-none-eabi-gdb`.
- `minicom` on Linux and macOS. Tested version: 2.7.
Readers report that `picocom` also works but we'll use `minicom` in this book.
- `PuTTY` on Windows.
Next, follow OS-agnostic installation instructions for a few of the tools:
### `rustc` & Cargo
Install rustup by following the instructions at [https://rustup.rs](https://rustup.rs).
Then, install or switch to the nightly channel.
``` console
$ rustup default nightly
```
**NOTE** Make sure you have a nightly newer than `nightly-2018-06-22`.
`rustc -V` should return a date newer than the one shown below:
``` console
$ rustc -V
rustc 1.28.0-nightly (056f589fb 2018-06-22)
```
### `itmdump`
``` console
$ cargo install itm
```
### OS specific instructions
Now follow the instructions specific to the OS you are using:
- [Linux](setup/linux.html)
- [Windows](setup/windows.html)
- [macOS](setup/macos.html)

114
src/setup/VERIFY.md Normal file
View File

@@ -0,0 +1,114 @@
# Verify the installation
Let's verify that all the tools were installed correctly.
## Linux only
### Verify permissions
Connect the F3 to your laptop using an USB cable. Be sure to connect the cable to the "USB ST-LINK"
port, the USB port in the center of the edge of the board.
The F3 should now appear as a USB device (file) in `/dev/bus/usb`. Let's find out how it got
enumerated:
``` console
$ lsusb | grep -i stm
Bus 003 Device 004: ID 0483:374b STMicroelectronics ST-LINK/V2.1
$ # ^^^ ^^^
```
In my case, the F3 got connected to the bus #3 and got enumerated as the device #4. This means the
file `/dev/bus/usb/003/004` *is* the F3. Let's check its permissions:
``` console
$ ls -l /dev/bus/usb/003/004
crw-rw-r-- 1 root uucp 189, 262 Oct 27 00:00 /dev/bus/usb/003/004
```
The group should be `uucp`. If it's not ... then check your [udev rules] and try re-loading them
with:
[udev rules]: 03-setup/linux.html#udev%20rules
``` console
$ sudo udevadm control --reload-rules
```
Now let's repeat the procedure for the Serial module.
Unplug the F3 and plug the Serial module. Now, figure out what's its associated file:
``` console
$ lsusb | grep -i ft232
Bus 003 Device 005: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
```
In my case, it's the `/dev/bus/usb/003/005`. Now, check its permissions:
``` console
$ ls -l /dev/bus/usb/003/005
crw-rw-r--+ 1 root uucp 189, 261 Oct 27 00:00 /dev/bus/usb/003/005
```
As before, the group should be `uucp`.
## All
### First OpenOCD connection
First, connect the F3 to your laptop using an USB cable. Connect the cable to the USB port in the
center of edge of the board, the one that's labeled "USB ST-LINK".
Two *red* LEDs should turn on right after connecting the USB cable to the board.
Next, run this command:
``` console
$ # *nix
$ openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
$ # Windows
$ # NOTE cygwin users have reported problems with the -s flag. If you run into
$ # that you can call openocd from the `C:\OpenOCD\share\scripts` directory
$ openocd -s C:\OpenOCD\share\scripts -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
```
> **NOTE** Windows users: `C:\OpenOCD` is the directory where you installed OpenOCD to.
> **IMPORTANT** There is more than one hardware revision of the STM32F3DISCOVERY board. For older
> revisions, you'll need to change the "interface" argument to `-f interface/stlink-v2.cfg` (note:
> no `-1` at the end). Alternatively, older revisions can use `-f board/stm32f3discovery.cfg`
> instead of `-f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg`.
You should see output like this:
``` console
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : Unable to match requested speed 1000 kHz, using 950 kHz
Info : clock speed 950 kHz
Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 2.915608
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
```
(If you don't ... then check the [general troubleshooting] instructions.)
[general troubleshooting]: appendix/1-general-troubleshooting/README.html
`openocd` will block the terminal. That's fine.
Also, one of the red LEDs, the one closest to the USB port, should start oscillating between red
light and green light.
That's it! It works. You can now close/kill `openocd`.

50
src/setup/WINDOWS.md Normal file
View File

@@ -0,0 +1,50 @@
# Windows
## `arm-none-eabi-*`
ARM provides `.exe` installers for Windows. Grab one from [here][gcc], and follow the instructions.
Just before the installation process finishes tick/select the "Add path to environment variable"
option. Then verify that the tools are in your `%PATH%`:
``` console
$ arm-none-eabi-gcc -v
(..)
gcc version 5.4.1 20160919 (release) (..)
```
[gcc]: https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
## OpenOCD
There's no official binary release of OpenOCD for Windows but there are unofficial releases
available [here][openocd]. Grab the 0.10.x zipfile and extract it somewhere in your drive (I
recommend `C:\OpenOCD` but with the drive letter that makes sense to you) then update your `%PATH%`
environment variable to include the following path: `C:\OpenOCD\bin` (or the path that you used
before).
[openocd]: https://github.com/gnu-mcu-eclipse/openocd/releases
Verify that OpenOCD is in yout `%PATH%` with:
``` console
$ openocd -v
Open On-Chip Debugger 0.10.0
(..)
```
## PuTTY
Download the latest `putty.exe` from [this site] and place it somewhere in your `%PATH%`.
[this site]: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
## ST-LINK USB driver
You'll also need to install [this USB driver] or OpenOCD won't work. Follow the installer
instructions and make sure you install the right (32-bit or 64-bit) version of the driver.
[this USB driver]: http://www.st.com/en/embedded-software/stsw-link009.html
That's all! Go to the [next section].
[next section]: 03-setup/verify.html