In my final Oxide-linked post I purchased Oxide’s Propolis application running and stated I could maybe strive to accumulate their sled agent up and running next. Anyhow that didn’t happen. Instead I stopped up reading datasheets, writing rust codegen, spending 16 gigs of ram for an hour to construct scientific doctors for a crate that’s appropriate a glorified bundle of pointers, dreaming about serial records switch, and uploading code to my beautiful look over the slowest debug hyperlink I’ve ever had the displeasure of utilizing. *List ScratchYou’re potentially wondering how I purchased on this danger. Successfully, all of it started when I realized the nRF52832 microcontroller has a memory safety unit.
So yeah, I ported Oxide’s embedded kernel, Hubris, to my PineTime beautiful look, and now I’m going to expose you about it. If you happen to’re no longer into embedded dev noteworthy, stick round for fairly! It’s no longer all scary, nevertheless don’t in actuality feel bad in expose so that you just can bail because the tail discontinue of this post descends into technical madness. If you happen to are into embedded dev although, smartly, non-public I purchased a contend with for you. Earlier than I accumulate into the how, I’m going to focus on fairly about what Hubris is, why a beautiful look is in overall a correct put of dwelling to note it, and some ideas on things I worship and things I don’t. Then I’ll present you the memoir of how I purchased it running on my hardware particularly. Nonetheless first, a demo!
(twister math in accordance with this pico8 demo by visy)
Also, as soon as you’re appropriate in the code, here’s my fork. The GPIO and SPI code are in a pretty correct put of dwelling, although I’m lacking a pair hardware configuration alternate choices in both. Possess enjoyable!
Hubris: what?
I’m no longer the authority on the topic here, and in expose for you an rationalization from any individual who wrote the dang factor you are going to non-public to soundless look Cliff Biffle’s focus on it or be taught the transcript.
Let me come up with the basics although, so that you just non-public some grounding. Hubris is a kernel for embedded devices that uses a hardware perform a form of of us non-public forgotten about: the Memory Security Unit. This portion of hardware in many ARM and RISC-V chips lets in the kernel to lock down whether or no longer varied segments of memory are readable, writable, and executable. Then the kernel can enact a activity in that minute context. And incidentally, since all IO is memory mapped on these techniques, memory safety and IO safety are the same factor. If you happen to would possibly maybe’t entry a peripheral’s handle space, that that you just can maybe’t entry that peripheral!
Nonetheless howdy, what’s the tremendous deal appropriate? We’re all passe to this in working techniques worship Linux, Windows, macOS, and plenty others. Successfully, in the embedded world, it would possibly maybe shock you to be taught that most of us are appropriate available shoving a bunch of duties onto a chip with a kernel that doesn’t bother with this. Those duties can fully stomp on every other’s memory, effect no topic IO they wish, put of dwelling your cat on fire, it’s a free for all in there. Another kernels provide MPU functionality, nevertheless pickings are slim.
Hubris says “that’s bad, in actuality”. The discontinue result is an architecture where duties can simplest work collectively through message-passing. Hardware interplay is encapsulated in duties too, which helps debugging a ton. As an instance, that that you just can maybe know with sure bet that if one thing is toggling a GPIO pin, it’s the GPIO activity. That you would be in a position to add debugging hooks into that activity to hint what’s sending messages to it, and now you non-public a full excessive stage watch of the entirety doing GPIO. That you would be in a position to put in pressure mutexes in relate that two duties can’t both simultaneously inquire of the SPI activity to effect an records transmission on the same time. It’s tremendous.
Since Hubris is written in rust, it would possibly maybe also accumulate the borrow checker in on the enjoyable. Hubris extends the thought of borrow checking with a one thing known as “Leases”. When a activity sends a message to 1 other activity, it will comprise a Lease to a pair vary of memory. Because the recipient of a rent, that that you just can maybe’t entry that memory straight, nevertheless you can inquire of the kernel to be taught or write that memory for your behalf. The kernel tests to be sure you’ve purchased a authentic rent, and copies memory between your handle space and the rent sender’s handle space. Since rust’s borrow checker made definite the sender had the rights at hand out the rent in the major put of dwelling, the total factor is memory staunch.
Oh yeah also they’ve a debugger known as Humility, which knocked my programmer socks off. If you happen to’ve purchased a debug hyperlink to your instrument that that you just can maybe use Humility to effect things worship accumulate a list of running duties, accumulate a backtrace of a failing activity, compare out ringbuffer logs, mess round with GPIO/SPI/i2c. That you would be in a position to plod even extra extreme by asking it in your duties’ memory spaces, and then launch mucking round reading or writing bytes straight in memory.
Possess a look at this backtrace I purchased debugging my demo code:
vi@navi ~/p/hubris (pinetime) [1]> cargo xtask humility app/demo-pinetime/app.toml -- duties -sl lcd
Accomplished dev [optimized + debuginfo] goal(s) in 3.34s
Operating `goal/debug/xtask humility app/demo-pinetime/app.toml -- duties -sl lcd`
humility: linked through OpenOCD
machine time=129006
ID TASK GEN PRI STATE
3 lcd 61 3 FAULT: PANIC (used to be: attractive)
|
+---> 0x20002208 0x00008cb2 userlib::sys_panic_stub
@ /hubris//sys/userlib/src/lib.rs: 989
0x20002210 0x00008cb8 userlib::sys_panic
@ /hubris//sys/userlib/src/lib.rs: 981
0x20002210 0x00008cc0 rust_begin_unwind
@ /hubris//sys/userlib/src/lib.rs: 1444
0x20002218 0x000086ce core::panicking::panic_fmt
@ /rustc/ac2d9fc509e36d1b32513744adf58c34bcc4f43c//library/core/src/panicking.rs: 88
0x20002220 0x0000898a core::panicking::alarm
@ /rustc/ac2d9fc509e36d1b32513744adf58c34bcc4f43c//library/core/src/panicking.rs: 39
0x20002380 0x000084f6 major
@ /hubris//activity/pinetime-lcd/src/major.rs: 113
If this looks worship an uneventful ol’ stack hint, yeah, that’s what’s so thrilling! Expressionless ol’ stack traces are on the total no longer this straightforward to accumulate ahold of in the embedded world, and I’ll admit I’ve caught to printf debugging in the past in desire to manage with the opposite debugging instruments on hand. This is so straightforward that even I don’t non-public an excuse anymore.
Oops, Hubris on a beautiful look is in overall purposeful
I fully anticipated the entirety I did with Oxide application to be enjoyable, nevertheless in any other case impractical for hobbyist initiatives at residence. Hubris is various.
Search, on a beautiful look, you’d like so that you just can load a bunch of apps onto your look without anxious if the timer app you appropriate put in is in overall counting its plot the total plot down to nuking your EEPROM, inserting you staunch into a bootloop, and texting your ex. On the acute discontinue, an extraordinarily unlucky portion of code would possibly maybe refined-brick your look until you unglue the support (breaking the watertight seal), slide a programmer into the debug port, and reprogram it. Nonetheless despite the truth that it by no plot will get that bad, it’s appropriate tremendous to no longer must contend with any extra portion of application as a land mine.
Enter, Hubris. Tasks isolated from every other? Performed. Tasks isolated from hardware? yup! That’s the total basis you have to launch building a tough look working machine. Get your self some devoted duties for stuff worship input, graphics compositing, bluetooth, and toddler you’ve purchased a stew going!
Nonetheless will non-public to soundless you utilize it yet?
It is dependent how adventurous you are, and how noteworthy you’re attractive to effect without enhance. To cite Hubris’ CONTRIBUTING.md
:
On the opposite hand, Hubris is no longer completed, or even attractive. It’s potentially no longer a correct match in your use case, because it’s no longer yet a correct match for our use case!
… snip …
and so, we thought it used to be valuable to prove where we’re at the moment at, and
organize your expectations.
- We are a small firm.
- Our present goal is to accumulate our first generation products completed and in
clients’ hands.- We’re writing Hubris in enhance of that goal, no longer as its have factor. Hubris has
a total of zero corpulent time engineers – we’re all working on the products, and
tool trend is a side build.- For expediency, we’re constructing our server firmware and Hubris in the same
repo. We can potentially destroy up this up later to receive it extra obtrusive use
Hubris from other functions. Nonetheless, for now, we’re essentially centered on
getting our firmware attractive, because, as soon as more, we must carry out our computer techniques.- These functions collectively imply that we would possibly maybe no longer non-public enough bandwidth to appear at and
integrate start air PRs appropriate now. This would possibly maybe commerce in due route.
So, you shouldn’t inquire of enhance, and likewise you shouldn’t inquire of any individual to be on hand to stroll you through things in my blueprint.
On the opposite hand, the entirety I did on this post, and the entirety I realized along the plot, came nearly solely from reading the present scientific doctors (they’re correct!) and the source code (it’s correct too! and commented!). I purchased some suited hints along the plot from Oxide of us on twitter, nevertheless I went out of my plot to identify as noteworthy as doable on my have to undercover agent if it used to be doable. If you happen to’re elated with that, and likewise you’re graceful with utilizing an early stage mission that’s soundless being molded into its remaining receive, I’m elated to yarn there’s nothing stopping you from utilizing Hubris appropriate now!
The scheme back to hardware isolation.
Leases are tremendous, nevertheless they’ve purchased overhead:
- You’ve purchased to round outing through the kernel for memory accesses
- The kernel has to file its taxes to be sure you’re allowed to entry the rent in question
- The kernel has to reproduction memory between the 2 duties’ memory spaces
The first two functions listed below are a little bit mitigated by the LeaseBufReader
/LeaseBufWriter
wrappers that buffer be taught/writes and batch the kernel calls, nevertheless this appropriate trades CPU time for RAM, one thing microcontrollers famously don’t non-public very noteworthy of.
And naturally, the message passing itself journeys through the kernel and has a charge, and the SPI activity has its have taxes it wants to file to work generically.
I without notice met this head first when working on my graphics demo. My prove is linked over a 8MHz hyperlink and uses 16-bit shade, so in thought I will non-public to soundless be ready to exchange half of the show veil at 16fps, if my code did nothing else. In actuality, I used to be getting someplace from 1-4fps with my LCD activity speaking to the SPI activity, sending six write messages per row of pixels. I could maybe lower this overhead by buffering extra pixels sooner than handing them over to the SPI activity, nevertheless now I’m spending extra ram, and the memcopy isn’t free both. None of this even accounts for the total GPIO messages which would possibly maybe be despatched to the GPIO activity staunch through this from both the SPI activity and my LCD code.
The straightforward technique to here’s to present the LCD activity say entry to the low stage hardware peripheral in desire to isolating it, nevertheless there’s extra than appropriate the LCD on that SPI bus; there’s some flash memory on there too. I’m left with a different:
- Insist extra ram, gash down overhead fairly, soundless non-public slower LCD entry, nevertheless withhold hardware isolation
- Lower the generic SPI middletask out of the equation, and roll LCD and Flash entry staunch into a single monolithic activity that Does Each and every Straight For Some Reason.
- Deal with the low bandwidth and withhold things as they’re in actuality.
The 2d option here would possibly maybe very smartly be what I’ll effect if I withhold working on this mission, because LCD bustle is extra valuable than a natty separation of issues as soon as you’re going through genuine time particular person interactions. Study these two videos of writing a staunch block of shade to the show veil, first through the SPI activity, and 2d with say SPI hardware entry:
The 2d video in all equity flickery, so shiny warning.
It sucks that here’s a compromise I must receive. I non-public some unfamiliar ideas to in part mitigate the topic by creating a DMA-compatibly memory buffer in my LCD activity and shoving a pointer to that through the SPI activity and into the DMA SPI hardware, nevertheless I’m somewhat definite this violates memory safety, and the top factor it would mitigate is the Lease overhead. Although this labored, I’d soundless be caught with astronomical pixel buffers I don’t need or need.
I’m definite here’s a notify the Hubris of us are attentive to (howdy, as soon as you’re reading and I’m lacking one thing obtrusive, let me know and I’ll exchange the post). I’m to undercover agent what their alternate choices to this watch worship, or if they’re appropriate utilizing faster chips than me.
Intermission
Lovely gods I definite am writing a form of words. I’ve been working on this for the past two weeks and it turns out I’ve purchased lots to deliver! From this point on I’ll be speaking about how I purchased to where I am now, the random bullshit I without notice met, and how I solved it. If you happen to’re queer about what porting Hubris to a brand original chip family looks worship, here’s for you. I also suggest making an strive out the commit history to undercover agent how I purchased here framed in code. I’ve left the commit history messy so that that that you just can maybe undercover agent the total trials and missteps along the plot.
So that you just have to port Hubris to a brand original chip
I came into this brilliant fully nothing about Hubris, and I’m going to present this to you from that standpoint, so that that that you just can maybe undercover agent this mission with fresh eyes the plot I did. The first factor I did used to be bustle the major describe in the README that looked vaguely precious.
cargo xtask dist app/demo-stm32f4-discovery/app.toml
stm32 is a family of arm microcontrollers that I acknowledge, so I started there. This describe constructed a bunch of stuff in the drv/
and activity/
folder, and generated a binary attractive to flash onto a chip. drv/
and activity/
non-public a bunch of drivers and application-stage duties respectively, nevertheless what’s in the app.toml
? Successfully, here’s a hyperlink to undercover agent in your self. Amongst other things we’ve purchased
- The chip and board the app is intended to bustle on.
- The memory structure of the app.
- What duties the app needs to comprise.
- Some runtime configuration switches.
Many of the duties I will be able to present I don’t need. Ping and pong watch worship check heartbeat apps and usart serial isn’t going to effect me noteworthy correct appropriate now so I bet that’s out. At final we accumulate the total plot down to a pair of duties that we effect in actuality make a selection running: hiffy
, jefe
, and idle
.
Hiffy is the “HIF Interpreter”. I’ll let activity/hiffy/src/major.rs
effect the speaking:
//! HIF is the Hubris/Humility Interchange Structure, a straightforward stack-essentially based
//! machine that lets in for some dynamic programmability of Hubris. In
//! particular, this activity provides a HIF interpreter to enable for Humility
//! instructions worship `humility i2c`, `humility pmbus` and `humility jefe`. The
//! debugger locations HIF in [`HIFFY_TEXT`], and then indicates that textual bid is
//! describe by incrementing [`HIFFY_KICK`]. This activity executes the specified
//! HIF, with the return stack located in [`HIFFY_RSTACK`].
Then, essentially based on activity/jefe/README.mkdn
, jefe
is “the supervisory activity for the demo application, which handles final-ditch error reporting, activity restarting, and the worship.”.
In a roundabout plot, idle
is scheduled when nothing else wants to bustle. Its sole perform is to effect nothing. Gods I wish that had been me.
Citing the kernel
The PineTime uses an nRF52832 microprocessor, a lil toddler 64MHz ARM chip with bluetooth. Hubris doesn’t non-public any enhance for it in the upstream repo so I added my have enhance. How did I effect that? Successfully I wakened one morning, put on some lofi beats to write embedded application to, and over the next few hours I
- copied the
app/demo-stm32f4-discovery/
folder toapp/demo-pinetime/
. - renamed the entirety interior to pinetime.
- added
app/demo-pinetime
to theworkspace.contributors
of the pinnacle stageCargo.toml
- copied the
chips/stm32f4.toml
file tochips/nRF52832.toml
, leaving the values there by myself for now. - adjusted the flash/ram addresses in
app.toml
essentially based on the nRF52832 datasheet. - commented out duties so I simplest had
jefe
,hiffy
, andidle
. - adjusted the imports in the
app/demo-pinetime/
to import the nRF52832 hardware crates in desire to the stm stuff. - messed round with the openocd config file until it labored with my BusPirate.
- flashed
goal/demo-pinetime/dist/remaining.bin
to the look. - it labored???
It’s amazing what that that you just can maybe effect as soon as you’re working with code that’s designed to be transportable. The supreme little bit of this used to be the memory handle adjustments. Chip datasheets will present you the memory structure of your chip and your compiler and linker would in actuality worship to grab this records. Right here’s a screenshot from the nRF scientific doctors:
And then, here’s the corresponding bits in the app.toml
:
vi@navi ~/p/hubris (pinetime)> cat app/demo-pinetime/app.toml
# bla bla bla
[outputs.flash]
handle=0x0000_0000
measurement=0x0008_0000
be taught=moral
enact=moral
[outputs.ram]
handle=0x2000_0000
measurement=0x0001_0000
be taught=moral
write=moral
enact=moral
# bla bla bla
Desirable appropriate?
By the plot, don’t use a BusPirate for flashing chips as soon as you non-public one thing greater. I worship this factor and it’s an amazing little multi-tool nevertheless it absolutely took, no exaggeration, 15 minutes to carry out uploading the firmware. I in actuality did it manually in desire to utilizing GDB because I used to be satisfied GDB used to be appropriate bugging out on me nevertheless in retrospect I appropriate by no plot gave it enough time to carry out the add. I non-public since purchased some genuine flashing hardware and I’ll be very elated when it will get here.
Anyway, now I had a kernel doing fuck-all on a beautiful look and I used to be extremely corpulent of myself. I went to twitter to claim victory worship I had appropriate gash off the hydra’s head, fully clueless to the destiny I’d appropriate consigned myself to. Search, I wasn’t bid to appropriate bustle a kernel. I main to pressure the prove, which plot I main to focus on to the prove controller. For that I main to put in pressure an SPI activity, and in flip that lead to a GPIO activity. At this point my yak stack used to be having a watch somewhat mountainous, nevertheless the top factor to effect used to be launch shearing.
GPIO
Continuing the pattern of reproduction-pasting code and hammering it into submission, I copied the drv/stm32xx-sys
, drv/stm32xx-sys-api
, and drv/stm32xx-gpio-approved
folders, renaming the prefix to nrf52832
. I also added these to the root-stage Cargo.toml
’s workspace appropriate worship with the app folder. For the comfort of this post I’m going to go that bit out, nevertheless in overall, any time you’ve purchased a brand original Cargo.toml
in a subdirectory you potentially must add its folder to the workspace.
The stm32xx-sys
activity handles GPIO and RCC configuration. These passe to be separate nevertheless had been merged staunch into a single activity to lower memory utilization, since every extra activity charges some extra memory overhead. I didn’t know that on the time, nevertheless I did know my chip’s spec sheet doesn’t point out an instantaneous corollary to the RCC, so I renamed my -sys
folders support to -gpio
and deleted the total references to RCC in the code.
The stm32 chips also non-public extra GPIO configuration alternate choices than my nRF52832, and multiple GPIO banks. We don’t must contend with that on the nRF chip so I cleared all that out too and transformed the API fairly to compare.
The order quo of bare steel rust. Care for, in actuality bare steel
If you happen to’ve labored with one thing worship Arduino sooner than you’re accustomed to having some fairly efficient abstraction over the hardware that’s staunch all the plot through various CPUs. These abstractions assign you from having a watch up chip-particular tutorials or spec sheets to effect one thing traditional. That’s moral in rust too as soon as you utilize the Hardware Abstraction Layer (hal) crates, nevertheless with hubris we don’t non-public that luxury, because these crates explain they’re working with none form of memory safety or CPU privilege machine in put of dwelling. Instead, we plod a layer deeper and use Peripheral Get entry to (pac) crates. These are auto-generated from particular person chip descriptions and provides a form-staunch plot to entry chip registers with niceties worship enums for multi-different alternate choices. Right here’s an instance from the GPIO:
use nrf52832_pac as instrument;
// GPIO port 0 register put of dwelling
let p0=unsafe { &*instrument::P0::ptr() };
// Configure pin 2 as an output with pullup resistor
self.p0.pin_cnf[2].write(|w| {
w
.dir().variant(instrument::p0::pin_cnf::DIR_A::OUTPUT)
.pull().variant(instrument::p0::pin_cnf::PULL_A::PULLUP)
});
These writers will let you change multiple fields in the same 32-bit hardware register without having to juggle a bunch of integer constants and bitwise operations. It’s somewhat tremendous in actuality! The scheme back is these crates accumulate somewhat astronomical, and some of them don’t even non-public genuine scientific doctors on scientific doctors.rs. Search this extremely broken put of dwelling of stm32h7 scientific doctors as an illustration. It’s no longer that there’s one thing sophisticated relating to the construct itself, it’s appropriate that it consumes so noteworthy resources the scientific doctors.rs backend is killing off the scientific doctors mid-construct. I constructed the scientific doctors for this crate particularly on the tremendous chonker I passe for my final post on Propolis, and it took 16 gigs of ram and an hour genuine-time. The nRF52832 pac crate is okay on scientific doctors.rs, nevertheless that that you just can maybe must construct scientific doctors in the neighborhood reckoning on what chip you’re working with.
Anyhow, on with the prove.
Nonetheless wait there’s codegen
The remaining portion to accumulate this all compiling used to be the .idol
file, one thing I hadn’t noticed up until this point. These files deliver the message passing API surface of a activity, so any time you receive adjustments to that API you’ve purchased to exchange the .idol
file too. As soon as extra I duplicated the stm32’s sys idol file to a gpio idol file for my chip, and here’s a pattern of what that looks worship:
Interface(
identify: "GPIO",
ops: {
"gpio_configure_raw": (
args: {
"pin": "u8",
"config": "u32",
},
answer: Result(
okay: "()",
err: CLike("GpioError"),
),
idempotent: moral,
),
"gpio_configure_gourmet": (
args: {
"pin": "u8",
"mode": (
form: "Mode",
recv: FromPrimitive("u8"),
),
"output_type": (
form: "OutputType",
recv: FromPrimitive("u8"),
),
"pull": (
form: "Pull",
recv: FromPrimitive("u8"),
),
},
answer: Result(
okay: "()",
err: CLike("GpioError"),
),
idempotent: moral,
),
}
)
The nrf52832-gpio
crate uses this at bring collectively time to generate the server trait so that you just can put in pressure, and the nrf52832-gpio-api
crate generates a corresponding client stub to plumb the internal workings of speaking to that server. All a server has to effect is put in pressure the appropriate trait and provide a major
perform that pumps the message queue. Customers appropriate import the api crate and name the api worship a perform, with the inter-activity communication hidden away as soon as you don’t decide to take into myth it.
When I updated my idol file and pointed my construct.rs
files at it, I had a working GPIO activity! All I had to effect used to be add it to my app.toml
and I used to be correct to head. Or, so I thought. I had in actuality missed one thing main, nevertheless to figure that out I had to eradicate a watch at and use my GPIO for one thing.
Starting up the LCD activity
With a GPIO activity up and running, I had enough to in actuality receive my look effect one thing viewed. The LCD backlight is suitable managed by some GPIO pins, so I whipped up a temporary LCD activity to receive it blink.
To effect that, I copied activity/pong
over to activity/pinetime-lcd
and stripped out the entirety from the major loop aside from for what looked worship some sleep code (it used to be!). I also changed the USER_LEDS
activity slot with GPIO
, imported the GPIO api, and sprinkled in some GPIO adjust of the backlight pin.
#![no_std]
#![no_main]
use userlib::*;
use drv_nrf52832_gpio_api as gpio_api;
task_slot!(GPIO, gpio);
#[export_name="main"]
pub fn major() -> ! {
const TIMER_NOTIFICATION: u32=1;
const INTERVAL: u64=3000;
const BACKLIGHT_HIGH=23;
// Get handle to focus on to the gpio activity
let gpio=gpio_api::GPIO::from(GPIO.get_task_id());
// Configure pin for output
gpio.gpio_configure_output(BACKLIGHT_HIGH, gpio_api::OutputType::PushPull, gpio_api::Pull::None).unwrap();
let mut msg=[0; 16];
let mut closing date=INTERVAL;
sys_set_timer(Some(closing date), TIMER_NOTIFICATION);
loop {
let msginfo=sys_recv_open(&mut msg, TIMER_NOTIFICATION);
// Toggle backlight
gpio.gpio_toggle(1
Then it was the song and dance of updating my Cargo.toml
and my app.toml
. Here we get to see task slots for the first time! I’ll give you the abridged version from the app.toml
:
[tasks.gpio]
# all the gpio config
[tasks.lcd]
# all the lcd config, but then
task-slots=["gpio"]
So to recap,
- The rust code declares a task slot with
task_slot!(GPIO, gpio);
- In our
app.toml
, we declare agpio
task - we fill the LCD task’s
gpio
slot with that GPIO task. - At run time, the task slot provides the GPIO task’s ID, and the rust code uses that to build a client struct to talk to GPIO.
Excellent, surely this works right? Well, uh, no. And … this GEN
eration number in humility tasks
seems to keep going up. I think my GPIO task is crashing, and my .unwrap()
s are taking the LCD down with it.
vi@navi ~/p/hubris (pinetime)> cargo xtask humility app/demo-pinetime/app.toml duties
Accomplished dev [optimized + debuginfo] goal(s) in 3.41s
Operating `goal/debug/xtask humility app/demo-pinetime/app.toml duties`
humility: linked through OpenOCD
machine time=183050
ID TASK GEN PRI STATE
0 jefe 0 0 recv, notif: bit0
1 gpio 315251 1 recv
2 lcd 318758 3 no longer started
3 hiffy 0 3 attractive
4 idle 0 5 attractive
Belief no one, no longer even your self
This, my friends, is the memory safety unit in action. There’s one little detail I didn’t point out in the GPIO part earlier, because I had forgotten it myself: we must give our GPIO activity entry to the memory space of the GPIO peripheral. If we don’t, the MPU presentations up and unalives our little GPIO activity with out a emotions of remorse.
In a roundabout plot, we be taught that here’s what that chips/
folder is for. Every entry in our chips/nRF52832.toml
defines the handle and measurement of some memory block, and provides that block a identify we are in a position to use to let duties use it. So for GPIO, I added this to my chips file:
[gpio]
handle=0x5000_0000
measurement=0x1000
And in my app.toml
, I added
[tasks.gpio]
# The identify of the memory vary would now not will non-public to soundless be linked to the activity identify, nevertheless on this case it's.
uses=["gpio"]
With that, we non-public a sexy blinky show veil!
SPI’s sappin’ my sanity
The following factor to effect is to in actuality flip the show veil on and accumulate some pixel records on there, and for that we would like SPI. SPI is a serial protocol whereby one host instrument (our microcontroller) is linked to a number of client devices (our LCD, also some SPI flash memory) over three shared traces carrying bidirectional records and a clock signal. Every client instrument also has a right chip-eradicate signal which is pulled low to expose that instrument it’s being addressed and pulled excessive to expose it to ignore no topic’s going on on the road. Our prove is linked over a SPI hyperlink, and our microcontroller has devoted SPI hardware to utilize that hyperlink successfully. We appropriate must write some code to utilize the SPI hardware.
As soon as extra, I copied the stm32 SPI driver and began cutting away on the parts I didn’t need, for the reason that nRF has noteworthy extra superb SPI hardware with much less configuration eager. It’s purchased two methods to utilize the SPI, Vow Memory Get entry to (DMA) and the extra superb register-pushed variant. DMA is extra efficient because we are in a position to point the SPI hardware at a astronomical chunk of memory, present it to head to city on that memory, and then yield to other duties for fairly. The scheme back is, it’s extra sophisticated to utilize. Within the hobby of Getting Something Working I passe the extra superb SPI interface that need us to feed in bytes one at a time as they’re transmitted.
Right here’s where things purchased sophisticated although, no longer thanks to the SPI hardware, nevertheless the configuration round it. Our app.toml
provides activity configuration sections that our duties can be taught at construct time. The SPI driver I copied converts this configuration to a struct with the total instrument and mux configuration. This involves walking the toml records, validating that it’s certainly a satisfiable configuration, and generating rust code to portray that configuration. I’ve by no plot in actuality completed rust codegen until now, nevertheless it absolutely’s no longer too dissimilar from one thing worship Haskell codegen, in relate that part didn’t alarm me off too bad.
What did put of dwelling off me a headache although used to be this cursed error yarn:
error: did now not bustle personalized construct describe for `drv-nrf52832-spi-server v0.1.0 (/sd/vi/residence/p/hubris/drv/nrf52832-spi-server)`
Resulted in by:
process did now not exit successfully: `/sd/vi/residence/p/hubris/goal/start/construct/drv-nrf52832-spi-server-b7d1371bb53586d5/construct-script-construct` (exit order: 1)
--- stdout
--- toml for $HUBRIS_TASK_CONFIG ---
[spi]
global_config="spi1"
cargo:rerun-if-env-changed=HUBRIS_TASK_CONFIG
--- stderr
Error: environment variable no longer found
Stack backtrace:
0: anyhow::error:: for anyhow::Error>::from
at /residence/vi/.cargo/registry/src/github.com-1285ae84e5963aae/anyhow-1.0.44/src/error.rs: 530: 25
1: <:result::result> as core::ops::try_trait::FromResidual<:result::result>>>::from_residual
at /rustc/ac2d9fc509e36d1b32513744adf58c34bcc4f43c/library/core/src/result.rs: 1915: 27
2: build_util::toml_from_env
at /sd/vi/residence/p/hubris/construct/util/src/lib.rs: 60: 18
3: build_util::config
at /sd/vi/residence/p/hubris/construct/util/src/lib.rs: 51:5
4: build_script_build::major
at ./construct.rs: 17: 25
Huh? The failing line is simply let global_config=build_util::config::
.
A pair hours later and I eventually found the culprit. The new app I copied didn’t non-public any SPI, and when I used to be having a watch on the opposite ones that did I missed a config part down on the backside with keys worship [config.spi.spi1]
. That global_config
surroundings tells the construct machine what key in actuality holds the SPI configuration valuable functions, and if that key isn’t in actuality describe you accumulate the cryptic error message above about lacking environment variables.
At final although I did accumulate SPI up and running, and likewise that that you just can maybe undercover agent a pattern of the config for that below. I’m somewhat elated with where the implementation is now after a pair of extra days of refactoring and refining it down, nevertheless it absolutely would possibly maybe stand for doing a DMA model at some point soon.
[config]
[config.spi.spi0]
controller=0
[config.spi.spi0.mux_options.lcd]
miso_pin=4
mosi_pin=3
sck_pin=2
[config.spi.spi0.devices.lcd]
mux="lcd"
cs=25
frequency="M8"
spi_mode=3
Pixels pixels pixels pixels pixels
With SPI working I could maybe launch getting pixels on the show veil. This is a easy case of “be taught the datasheet and effect what it says”. The prove controller in here is also very just just like the ones they’ve on the TI-84+CSE, one thing I non-public a history of working with, so I used to be appropriate at residence with it. No interlacing on this one although sadly, so I will be able to’t effect the half of-resolution hack to squeeze extra performance out of it. Commands are despatched by retaining the describe pin low and sending the 8-bit describe code over the serial bus, and then describe records comes after with the describe pin held excessive. I’m utilizing 16-bit shade, nevertheless it absolutely can settle for 12-bit shade to assign bandwidth. The scheme back is you’ve purchased to effort about byte alignment, and that’s a wretchedness.
At final I purchased a wintry lil man on my show veil surrounded by undefined RAM records:
A little bit extra effort and a detour into demoscene be taught and I purchased that natty twister you saw on the pinnacle of the show veil!
SPI is unimaginative, prolonged dwell SPI.
Remember how I talked about earlier that SPI entry from the LCD activity is much faster? Successfully, I main to animate my twister and that’s when I without notice met troubles, because show veil updates had been taking agonizingly prolonged. It wasn’t so noteworthy an animation because it used to be a slideshow. Which capacity I used to be forced to gash the SPI activity I labored so onerous on out of the equation and provides the SPI hardware handle space over to my LCD activity as a change. This gave me the tender animation I used to be shopping for, nevertheless it absolutely used to be roughly disappointing to must effect. oh smartly!
There’s a agreeable distance to head
Getting this mission from where it’s now to an fully purposeful smartwatch OS would possibly maybe be rather the endeavor. We’d must bring up i2