Alexander Sayapin Teacher's site

NUCLEO-f030R8 Embedded Rust Quick Start Tutorial (Part 1)

Posted on Mon 16 December 2019

In Общие вопросы.

tags: embedded rust STM32 openocd gdb nucleo


Rust programming language is a great tool for embedded development, at least due to its memory ownership mechanism. I believe it's the future of the embedded world (well... not the present though :) ). To make the future a bit closer, I decided to use the Rust language for my next projects. To make it happen, it is necassery to create a template. Let's do it!

Common info

I am using Ubuntu 16.04 at the moment.

The evaluation board I use is NUCLEO-F030R8, and it's a bit harder to work with the hardware not described in the official manuals (but it's not a rocket science too).

My preferrable IDE for such kind of projects is Microsoft Visual Studio Code.

The sources I used are:

Prerequisites

Let's prepare the environment.

First, we need Rust itself:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

After the installation process is done, we can check it:

rustc -V

We should see something like this:

rustc 1.38.0 (625451e37 2019-09-23)

Then (we know the type of the microcontroller we are going to use, but let this manual be as universal as possible) we have to install the toolchains:

rustup target add thumbv6m-none-eabi
rustup target add thumbv7m-none-eabi
rustup target add thumbv7em-none-eabi
rustup target add thumbv7em-none-eabih

For the Cortex-M0, M0+, and M1 (ARMv6-M architecture), Cortex-M3 (ARMv7-M architecture), Cortex-M4 and M7 without hardware floating point (ARMv7E-M architecture) and Cortex-M4F and M7F with hardware floating point (ARMv7E-M architecture) respectively. You can choose only one you are going to use.

Also we need cargo-binutils

cargo install cargo-binutils

and LLVM compiler

rustup component add llvm-tools-preview

and itmdump

cargo install itm

We will need gdb (for flashing firmware into the microcontroller and debugging), openocd (to connect to the microcontroller), screen to interact through an UART port (though official guide recommends minicom), and qemu as an emulator. Let's install it:

sudo apt install gdb-arm-none-eabi openocd screen qemu-system-arm

If you are using Ubuntu 18.04, you would need the following command:

sudo apt install gdb-multiarch openocd screen qemu-system-arm

And to make the life easier, I created the file /etc/udev/rules.d/70-st-link.rules:

nano /etc/udev/rules.d/70-st-link.rules

with the following content:

# STM32F3DISCOVERY rev A/B - ST-LINK/V2
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="3748", TAG+="uaccess"

# STM32F3DISCOVERY rev C+ - ST-LINK/V2-1
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", TAG+="uaccess"

and realoaded the rules

sudo udevadm control --reload-rules

so it would take effect.

Now it's time to check if the software sees the board. Connect the board, start the terminal and run

openocd -f interface/stlink-v2-1.cfg -f target/stm32f0x.cfg

It would work for the NUCLEO-F030R8 board, but there can be some differences: you can work with Blue Pill (STM32F103) board. As it is 100th serie, the command would be

openocd -f interface/stlink-v2-1.cfg -f target/stm32f1x.cfg

If you know the chip serie, you can choose the right configuration file taking a look at /usr/share/openocd/scripts/target.

It also may happen that the flash programmer you use may be different (if your board is not a NUCLEO, or you use an external ST-LINK flash programmer). The version of the programmer can be different, so it could be stlink-v2.cfg (different configuration files lie in the /usr/share/openocd/scripts/interface dir, check it for yours).

For example, if you are going to use Blue Pill with an old ST-LINK v2, the command would be

openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg

Anyway, if you have chosen the right parameters, you'll see something like that:

Open On-Chip Debugger 0.9.0 (2018-01-24-01:05)
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>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
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 v23 API v2 SWIM v9 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 3.273594
Info : stm32f0x.cpu: hardware has 4 breakpoints, 2 watchpoints

It worked!

Now it's time to go to the next part, which is

Creation of the project template with examples

To have an opportunity to create a project from a template, we have to install a crate called cargo-generate:

cargo install cargo-generate

Get to the folder where you are going to store your projects, start the terminal and run the command:

cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart

During the process you will be asked to provide the project name. Choose the one you like, and we will use the name app here, just as in the official manual.

The project based on the https://github.com/rust-embedded/cortex-m-quickstart repository would be generated, thanks to the cortex team.

Now you can run the vs code IDE, and open the newly generated folder app.

In the IDE open the file config in the .cargo directory and uncomment the target line that corresponds your microcontroller (I use target = "thumbv6m-none-eabi" here as my NUCLEO board holds STM32F030R8T6 MCU, which belongs to the Cortex-M0 family).

Now we have to tune the project so it describes our MCU.

Go to the memory.x file and change the lines

FLASH : ORIGIN = 0x08000000, LENGTH = 64K
RAM : ORIGIN = 0x20000000, LENGTH = 8K

the way they describe your microcontroller. The lines above describe STM32F030R8T6, and necessary information can be found at the corresponding datasheet, the RAM origin in the 2.2.2 Memory map and register boundary addresses topic, and FLASH origin in the 3.2.1 Flash memory organization topic.

And finally we can edit the file hello.rs in the examples folder, commenting out the

debug::exit(debug::EXIT_SUCCESS);

line.

Now we have to

Build it!

Open the terminal in the VS Code and run

cargo build --example hello

Yep, that simple!

You'll see:

Compiling semver-parser v0.7.0
   Compiling typenum v1.11.2
   Compiling proc-macro2 v1.0.6
   Compiling unicode-xid v0.2.0
   Compiling syn v1.0.11
   Compiling stable_deref_trait v1.1.1
   Compiling vcell v0.1.2
   Compiling cortex-m v0.6.1
   Compiling cortex-m-rt v0.6.11
   Compiling cortex-m-semihosting v0.3.5
   Compiling r0 v0.2.2
   Compiling app v0.1.0 (/home/aleksandr/Projects/Rust/app)
   Compiling panic-halt v0.2.0
   Compiling semver v0.9.0
   Compiling volatile-register v0.2.0
   Compiling rustc_version v0.2.3
   Compiling quote v1.0.2
   Compiling bare-metal v0.2.5
   Compiling generic-array v0.13.2
   Compiling generic-array v0.12.3
   Compiling as-slice v0.1.2
   Compiling aligned v0.3.2
   Compiling cortex-m-rt-macros v0.1.7
warning: unused import: `debug`
 --> examples/hello.rs:9:28
  |
9 | use cortex_m_semihosting::{debug, hprintln};
  |                            ^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

    Finished dev [unoptimized + debuginfo] target(s) in 35.32s

Don't mention the warning, it's quite ok. If you want it to disappear, you can remove the debug element from the

use cortex_m_semihosting::{debug, hprintln};

line, so it would look like that

use cortex_m_semihosting::hprintln;

Let's flash it into the microcontroller and check if it works!

Flashing and running the program

Let's adjsut some configuration files here.

Switch to the openocd.cfg file in VS Code IDE and adjust the lines

source [find interface/stlink-v2-1.cfg]
source [find target/stm32f0x.cfg]

Do you remeber you started openocd? Change the strings in the openocd.cfg accordingly (here are mines, for my NUCLEO board).

Now return to the .cargo/config file, and uncomment the line

runner = "arm-none-eabi-gdb -q -x openocd.gdb"

Note that it's for my system, Ubuntu 16.04. If you use Ubuntu 18.04, you have to uncomment another one,

runner = "gdb-multiarch -q -x openocd.gdb"

That would allow you to flash and run your microcontroller with a single cargo run command.

In the build section the line that corresponds your MCU has to be uncommented too, for me (for my NUCLEO-F030) it's the

target = "thumbv6m-none-eabi"    # Cortex-M0 and Cortex-M0+

line.

Now follow ~the white rabbit~ to the project dir (which is app in our case), run there two terminals, in the first one start the command

openocd

(it would use openocd.cfg file automatically), and in the other terminal window the following one

arm-none-eabi-gdb -q target/thumbv6m-none-eabi/debug/examples/hello

Remember, the arm-none-eabi-gdb part is for Ubuntu 16.04 (and it would be gdb-multiarch for the 18.04 system), and the path target/thumbv6m-none-eabi/debug/examples/hello depends on the microcontroller you use (for example, the path would be target/thumbv7em-none-eabihf/debug/examples/hello for the Cortex-M4 with FPU).

The line like

Reading symbols from target/thumbv6m-none-eabi/debug/examples/hello...done.

appears if the path is correct and now you can connect to the openocd by entering the command

target remote :3333

in the gdb terminal window. It tells the gdb to connect to the remote (not quite in this case, but anyway, to speak with it through the network interface) openocd instance (on the same computer, without IP address, on 3333 port, :3333).

It will show

Remote debugging using :3333
0x00000000 in ?? ()

in the gdb window and

Info : accepting 'gdb' connection on tcp/3333
Info : device id = 0x20006440
Info : flash size = 64kbytes

in the openocd window.

By the way, you may see the

undefined debug reason 7 - target needs reset

message. In that case, enter the

monitor reset halt

command in the gdb window. You'll see the message

target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0x08001060 msp: 0x20002000

It's quite ok.

Now you can flash your newly created program into the microcontroller by printing

load

in the gdb window.

Now you have to see something like that:

Loading section .vector_table, size 0xc0 lma 0x8000000
Loading section .text, size 0x1414 lma 0x80000c0
Loading section .rodata, size 0x5c0 lma 0x80014e0
Start address 0x8001060, load size 6804
Transfer rate: 14 KB/sec, 2268 bytes/write.

in the gdb window.

Now enter the next command to enable the semihosting

monitor arm semihosting enable

and now (finally!) you can run the program by printing the command

continue

in the gdb window.

Immediately you'll see the message

Hello, world!

in the openocd window.

If you return to the gdb window and press Ctrl-C, you'll see

^C
Program received signal SIGINT, Interrupt.
0x08000156 in hello::__cortex_m_rt_main::h4f811533e300e6b9 ()
    at examples/hello.rs:19
19      loop {}

message, which tells us that the program was interrupted during the performing the loop procedure at line 19.

Let's make the life a bit easier: we can use openocd.gdb file prepared by the cortex team for us (thanks again).

Do you remember the monitor reset halt command we used earlier? Let's add it to the openocd.gdb file so we do not need to enter it manually. Add

monitor halt reset

line to the openocd.gdb file before load command.

Open two different terminal windows in the project directory, and perform commands

openocd

and

arm-none-eabi-gdb -x openocd.gdb target/thumbv6m-none-eabi/debug/examples/hello

You'll see a lot of information in gdb window:

GNU gdb (7.10-1ubuntu3+9) 7.10
Copyright (C) 2015 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from target/thumbv6m-none-eabi/debug/examples/hello...done.
DefaultPreInit ()
    at /home/aleksandr/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.11/src/lib.rs:571
571 pub unsafe extern "C" fn DefaultPreInit() {}
Breakpoint 1 at 0x800109a: file /home/aleksandr/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.11/src/lib.rs, line 562.
Breakpoint 2 at 0x80014c0: file /home/aleksandr/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.11/src/lib.rs, line 552.
Breakpoint 3 at 0x8000ebc: file /home/aleksandr/.cargo/registry/src/github.com-1---Type <return> to continue, or q <return> to quit---

Press enter, and program will be loaded to the microcontroller.

You'll see:

ecc6299db9ec823/panic-halt-0.2.0/src/lib.rs, line 32.
Breakpoint 4 at 0x8000130: file examples/hello.rs, line 11.
semihosting is enabled
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0x08001060 msp: 0x20002000, semihosting
Loading section .vector_table, size 0xc0 lma 0x8000000
Loading section .text, size 0x1414 lma 0x80000c0
Loading section .rodata, size 0x5c0 lma 0x80014e0
Start address 0x8001060, load size 6804
Transfer rate: 14 KB/sec, 2268 bytes/write.
Note: automatically using hardware breakpoints for read-only addresses.
DefaultPreInit ()
    at /home/aleksandr/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.11/src/lib.rs:571
571 pub unsafe extern "C" fn DefaultPreInit() {}

Enter the command continue in the gdb window twice and see the Hello, world! message in the openocd window.

Pressing Ctrl-C in the gdb window will interrupt the program in the microcontroler, and you would be able to quit the gdb, if you want.

Hooray!

It all finally worked.

Close the gdb window by entering the quit command, and interrupt the openocd session by pressing Ctrl-C in its window.

Now you can close both terminals.

And finally, there' another option, the most easy one :)

Return back to .cargo/config file, make sure the line

runner = "arm-none-eabi-gdb -x openocd.gdb"

is uncommented (runner = "gdb-multiarch -x openocd.gdb") for Ubuntu 18.04. Make sure the line

monitor reset halt

is added to the openocd.gdb file.

If everything is ok, you can load the program to the microcontroller and start debugging by entering command

openocd

in a terminal window in the project directory and command

cargo run --example hello

in the VS Code terminal.

You'll see something like that:

    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `arm-none-eabi-gdb -q -x openocd.gdb target/thumbv6m-none-eabi/debug/examples/hello`
Reading symbols from target/thumbv6m-none-eabi/debug/examples/hello...done.
0x08000156 in hello::__cortex_m_rt_main::h4f811533e300e6b9 () at examples/hello.rs:19
19          loop {}
Breakpoint 1 at 0x800109a: file /home/aleksandr/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.11/src/lib.rs, line 562.
Breakpoint 2 at 0x80014c0: file /home/aleksandr/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.11/src/lib.rs, line 552.
Breakpoint 3 at 0x8000ebc: file /home/aleksandr/.cargo/registry/src/github.com-1ecc6299db9ec823/panic-halt-0.2.0/src/lib.rs, line 32.
Breakpoint 4 at 0x8000130: file examples/hello.rs, line 11.
semihosting is enabled
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0xc1000000 pc: 0x08001060 msp: 0x20002000, semihosting
Loading section .vector_table, size 0xc0 lma 0x8000000
Loading section .text, size 0x1414 lma 0x80000c0
Loading section .rodata, size 0x5c0 lma 0x80014e0
Start address 0x8001060, load size 6804
Transfer rate: 14 KB/sec, 2268 bytes/write.
Note: automatically using hardware breakpoints for read-only addresses.
DefaultPreInit () at /home/aleksandr/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.11/src/lib.rs:571
571     pub unsafe extern "C" fn DefaultPreInit() {}
(gdb)

Enter the command continue twice, and you'll see message Hello, world! in the openocd window.

That's all for today :)

Stay tuned for the next part!

tags

алфавит (1) архитектура ЭВМ (4) asp.net (1) бгд (22) бисв (23) бкб (22) бме (22) бпэ (23) бпэз (4) бпэзу (1) бпм (20) бпм объявления (7) certbot (1) cheatsheet (1) checkinstall (1) csv (1) дискретная математика (25) экзамен (1) embedded rust (2) english (1) формальные грамматики (1) gdb (2) язык (1) исследование операций (1) jupyter (1) критерии (2) курсовая работа (2) lighttpd (2) low-latency (1) machine learning (3) make (1) make install (1) markdown (1) машинное обучение (1) математическая лингвистика (1) математическая логика (1) математическая статистика (2) Математические основы кмпьютерной графики (1) Математические основы компьютерного моделирования (1) Математические основы компьютерной графики (1) методы оптимизации (20) методы оптмимизации (1) методы принятия решений (1) миа (7) мии (8) мик (7) мим (8) миo (5) мип (9) мит (44) миу (14) миз (13) ml (1) mono (1) мпм (9) natural language processing (1) nlp (1) nucleo (2) объявления (31) оформление (2) openocd (2) openpgp (1) pandas (1) pgp (1) подтверждение вывода (1) programming (3) python (3) robot (1) robotics (2) setup (6) шпаргалка (1) системы компьютерной математики (1) smartcard (1) ssh (1) ssl (1) STM32 (2) streaming (1) строка (1) тб (21) teaching (1) teaching statement (1) Теоретические основы цифровой обработки изображений (2) тест (1) учебник (1) up board (1) video (1) вкр (2) xls (1)