Controlling an Output: Programming a Raspberry Pi Pico with Rust
It's not a requirement, but to follow along in code with this post, check out my GitHub Repo for my project called PicoPonics.
What is Output Control?
Controlling an output from a microcontroller means producing a signal to a device that can recognize that type of signal.
In our case, the device hosting our microcontroller is the Raspberry Pi Pico, and the actual microcontroller chip on this platform is called the RP2040.
Code that has logical authority over an output is commonly called control logic. Control Logic can be composed in a variety of languages and styles. But before this code can be interpreted by the controller, it must first be compiled to the appropriate format. For more information about compilation, check out this post. {{Link coming in Future}}
Here is a list of things that must be understood to handle controlling an output:
1. What type of output is needed to control a field device
2. Safety Risks
3. How to write code to create the conditions for the device to be activated and deactivated
4. How to compile that code into a format that is recognizable to the target microcontroller
5. How to move that code onto the controller (write / flash)
6. How to wire up the GPIO to the target device
Tell the Microcontroller what its Pins are doing
The GPIO pins on the Raspberry Pi Pico are capable of functioning in a general way. They are multi-talented and can handle many different responsibilities. They can handle Input, Digital Output, PWM, and Comms protocols. It's a requirement to explicitly tell the controller what the role of each used pin is, so that it knows how to implement it.
let mut gp18 = pins.gpio18.into_push_pull_output();
In this code above, GPIO Pin 18 is configured to act as a push-pull output. This 'Push-Pull' term is essential because it can be hooked up to outputs that function in either sourcing or sinking mode. I won't cover that now, but I can at least say if it’s not clear whether the output is sinking or sourcing use push-pull. But best to know for safety, and for the device's longevity.
GPIO18 in the example above is configured to be a Discrete output, which is either On or Off. Information about other types of outputs is coming in a future post.
Setting Up Pins using a Hardware Abstraction Layer (HAL)
The below example shows how the HAL is imported into the file, then used to configure a variable called pins. This variable has all the power once created over how the pins will be configured.
If you’re interested to write your own code straight away, you can download my Super Blank Pico Project Repo, and start monkeying around with the pins variable in there to get some stuff happening on your own pico.
// A shorter alias for the Peripheral Access Crate, which provides low-level
// register access
use hal::pac;
// The single-cycle I/O block controls our GPIO pins
let sio = hal::Sio::new(pac.SIO);
// Set the pins to their default state
let pins = hal::gpio::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS
);
// Configure a pin to be used as an output pin
let mut gp18 = pins.gpio18.into_push_pull_output();
Some quick notes about the above code
use hal::pac;
This is
let sio = hal::Sio::new(pac.SIO);
SIO means Single-Cycle IO Bus/Block Control. This appears to be related to the cortex-m chip style directly. This very well-written post gives an incredibly detailed description of the inner workings of handling IO with the chipset. But that’s relevant only if that’s something you’re interested in learning.
let mut gp18 = …
let is rust’s way of saying “What follows is a variable.”
mut is telling the rust compiler that this variable will change. Rust prefers variables that are immutable by default. It’s an interesting paradigm that will change how you program.
gp18 is my given name for the gpio pin number 18 on the pico board.
And we covered push-pull above in this post, so we’re good!
Writing Control Logic
The idea here is to be able to express in code a set of conditions that describe when and what signals the controller should be sending to its connected devices in the field.
To see some examples of control logic look in the PicoPonics repo, under the pico-ponics-mk1.rs file.
Conclusion
We've covered what an output is, how to configure them, and some basics on controlling them. With this information, it’s possible to start getting project work done with the Raspberry Pi Pico and Rust.
If you’ve got questions or if something in this article wasn’t clear, please let me know by one of the contact methods listed below, and I’ll come back through and update the article for clarity.