Hello, world!

HEADS UP Several readers have reported that the “solder bridge” SB10 (see back of the board)on the STM32F3DISCOVERY, which is required to use the ITM and the iprint! macros shown below, isnot soldered even though the User Manual (page 21) says that it should be.

TL;DR You have two options to fix this: Either solder the solder bridge SB10 or connect awire between SWO and PB3 as shown in the picture below.

Hello, world! - 图1


Just a little more of helpful magic before we start doing low level stuff.

Blinking an LED is like the “Hello, world” of the embedded world.

But in this section, we’ll run a proper “Hello, world” program that prints stuff to your laptopconsole.

Go to the 06-hello-world directory. There’s some starter code in it:

  1. {{#include src/main.rs}}

The iprintln macro will format messages and output them to the microcontroller’s ITM. ITM standsfor Instrumentation Trace Macrocell and it’s a communication protocol on top of SWD (Serial WireDebug) which can be used to send messages from the microcontroller to the debugging host. Thiscommunication is only one way: the debugging host can’t send data to the microcontroller.

OpenOCD, which is managing the debug session, can receive data sent through this ITM channel andredirect it to a file.

The ITM protocol works with frames (you can think of them as Ethernet frames). Each frame has aheader and a variable length payload. OpenOCD will receive these frames and write them directly to afile without parsing them. So, if the microntroller sends the string “Hello, world!” using theiprintln macro, OpenOCD’s output file won’t exactly contain that string.

To retrieve the original string, OpenOCD’s output file will have to be parsed. We’ll use theitmdump program to perform the parsing as new data arrives.

You should have already installed the itmdump program during the installation chapter.

In a new terminal, run this command inside the /tmp directory, if you are using a *nix OS, or fromwithin the %TEMP% directory, if you are running Windows. This should be the same directory fromwhere you are running OpenOCD.

NOTE It’s very important that both itmdump and openocd are runningfrom the same directory!

  1. $ # itmdump terminal
  2. $ # *nix
  3. $ cd /tmp && touch itm.txt
  4. $ # Windows
  5. $ cd %TEMP% && type nul >> itm.txt
  6. $ # both
  7. $ itmdump -F -f itm.txt

This command will block as itmdump is now watching the itm.txt file. Leave this terminal open.

Alright. Now, let’s build the starter code and flash it into the microcontroller.

To avoid passing the --target thumbv7em-none-eabihf flag to every Cargo invocation we can set adefault target in .cargo/config:

  1. [target.thumbv7em-none-eabihf]
  2. runner = "arm-none-eabi-gdb -q -x openocd.gdb"
  3. rustflags = [
  4. "-C", "link-arg=-Tlink.x",
  5. ]
  6. +[build]
  7. +target = "thumbv7em-none-eabihf"

Now if --target is not specified Cargo will assume that the target is thumbv7em-none-eabihf.

  1. $ cargo run
  2. Reading symbols from target/thumbv7em-none-eabihf/debug/hello-world...done.
  3. (..)
  4. Loading section .vector_table, size 0x400 lma 0x8000000
  5. Loading section .text, size 0x27c4 lma 0x8000400
  6. Loading section .rodata, size 0x744 lma 0x8002be0
  7. Start address 0x8002980, load size 13064
  8. Transfer rate: 18 KB/sec, 4354 bytes/write.
  9. Breakpoint 1 at 0x8000402: file src/06-hello-world/src/main.rs, line 10.
  10. Note: automatically using hardware breakpoints for read-only addresses.
  11. Breakpoint 1, main () at src/06-hello-world/src/main.rs:10
  12. 10 let mut itm = aux6::init();

Note that there’s a .gdbinit at the root of the Cargo project. It’s pretty similar to the one weused in the previous section.

Before we execute the iprintln! statement. We have to instruct OpenOCD to redirect the ITM outputinto the same file that itmdump is watching.

  1. (gdb) # globally enable the ITM and redirect all output to itm.txt
  2. (gdb) monitor tpiu config internal itm.txt uart off 8000000
  3. (gdb) # enable the ITM port 0
  4. (gdb) monitor itm port 0 on

All should be ready! Now execute the iprintln! statement.

  1. (gdb) next
  2. 12 iprintln!(&mut itm.stim[0], "Hello, world!");
  3. (gdb) next
  4. 14 loop {}

You should see some output in the itmdump terminal:

  1. $ itmdump -F -f itm.txt
  2. (..)
  3. Hello, world!

Awesome, right? Feel free to use iprintln as a logging tool in the coming sections.

Next: That’s not all! The iprint! macros are not the only thing that uses the ITM. :-)