In my last post I spoke about why I’m learning Rust and laid out a roadmap to learning. This is the first post in a series of posts showing my progress along that route.
As a quick recap, I’m working through an experimental college course created by Sergio Benitez. This is the first assignment from the course. Naturally, it is assignment zero.
Phase 0 – Gathering Materials
Before getting to program the Pi, obviously I had to gather materials. The list of required hardware is listed below:
- 1 Raspberry Pi 3
- 1 1⁄2-sized breadboard
- 1 4GiB microSD card (I used a 64GB mSD card instead)
- 1 microSD card USB adapter (I have an SD card slot on my PC, so used a mSD to SD converter)
- 1 CP2102 USB TTL adapter w/4 jumper cables (I used this USB TTL adapter)
- 10 multicolored LEDs (I only have red LEDs)
- 4 100 ohm resistors
- 4 1k ohm resistors
- 10 male-male DuPont jumper cables
- 10 female-male DuPont jumper cables
Phase 1
Once I had the hardware gathered, I was ready for phase 1. In phase 1 I simply tried to boot the Pi with the provided system images, then bring up a shell with the USB TTL adapter.
NOTE: I tried to format the 64GB mSD card that I have into exFAT. It failed to boot. This link claims that Raspberry Pi will only boot with FAT32 NOT exFAT.
Resources
This is the data sheet Sergio provides for reference when programming the board
Phase 2
In phase 2 I wired up the Pi to a simple circuit with an LED and a few resistors. Essentially an embedded developer’s version of “Hello World”
Phase 3
Phase 3 in this assignment is where I consider things to get more serious and thinking is finally required.
Looking at the documentation for the Pi, the assignment instructions are to figure out which bit to set in order to select the GPIO functionality and its default value.
Based on the documentation, address 0x 7E20 0004
is the address for General Purpose Function selection GPFSEL1
(GPIO can be set to input, output, or one of several alternative functions).
The documentation is clear enough to figure it out. In a subsequest section of the doucmentation, it shows the use of the 32 bit values within the 32 bit GPFSEL1
field length.
FSEL16
(function selection for pin 16) is bit 18-20 of the 32 bit GPFSEL1
- Setting bits 18-20 to
000
sets GPIO pin 16 to input - Setting bits 18-20 to
001
sets GPIO pin 16 to output
We want FSEL16
to be output, so we would set bit 18 to 1
Bit 18 is offset 0x12
from address 0x 7320 0004
, which means we need to
write 1
to address 0x 7320 0016
I wrote 1
to address 0x 7320 0016
with a bitmask using the bitwise or operator |
In C that looked like:
*GPIO_FSEL1 |= 0x40000;
to write 1 to bit 18 of the 32 bit value
stored in the memory mapped GPIO function selection address*GPIO_SET0 |= 0x10000;
to write 1 to bit 16 of the 32 bit value
stored in the memory mapped GPIO state assignment address- Note there is a separate state assignment address for clearing the GPIO value
*GPIO_CLR0 |= 0x10000;
to write 1 to bit 16 of the 32 bit value
stored in the memory mapped GPIO clear state assignment address
In Rust that looks like:
fn set_fsel_output() {
unsafe {
let write_bits = GPIO_FSEL1.read_volatile() | 0x40000;
GPIO_FSEL1.write_volatile(write_bits);
}
}
fn set_led_on() {
unsafe {
let write_bits = GPIO_SET0.read_volatile() | 0x10000;
GPIO_SET0.write_volatile(write_bits);
}
}
fn set_led_off() {
unsafe {
let write_bits = GPIO_CLR0.read_volatile() | 0x10000;
GPIO_CLR0.write_volatile(write_bits);
}
}
My main function simply sets up the GPIO as output, then loops forever toggling the LED off then on. The spin_sleep_ms(100)
just sends a nop
instruction for 1 second, to give the LED a pulse.
#[no_mangle]
pub unsafe extern "C" fn kmain() {
set_fsel_output();
let mut on = true;
loop {
if on {
set_led_on();
}
else {
set_led_off();
}
on = !on;
spin_sleep_ms(100);
}
}