Cleaning Up The Timer Code

In yesterday's article, I explained the Parallel I/O (PIO) code and presented a cleaned up version. Today I'm going to to the same for the code that uses the Real-time Timer (RTT).

First, let's take a look at the constants for the RTT that are defined in our program:

const TIMER_MODE_REGISTER : *mut   u32 = 0x400E1A30 as *mut   u32;
const TIMER_VALUE_REGISTER: *const u32 = 0x400E1A38 as *const u32;

Just like PIO, the RTT can be controlled using memory-mapped I/O[1]. Two registers are defined here:

Here's the code that configures the timer using TIMER_MODE_REGISTER:

*TIMER_MODE_REGISTER = 0x00000020;

The timer mode register consists of two halves (16 bits each): Only the lower half is of concern to us. It is used to set the Real-time Timer Prescaler Value (RTPRES).

Here's how that works: The Real-time Timer is powered by a clock source called Slow Clock (SCLK)[4], which is the only permanently running clock in the system[5]. This means that while other clocks might be disabled when the system enters a low-power mode, the slow clock will keep running and can be used to wake the system up again.

None of this is very relevant for us right now. What we need to know is that the Slow Clock runs at a frequency of 32,768 Hz. However, the Real-time timer is not updated at that frequency. If it were, the timer value register would overflow every ~36 hours[6], which might be too often, depending on the application.

To solve this problem, the RTT allows us to set the prescaler value. The update rate is divided by the prescaler value. For example, a value of 2 gives us overflows roughly every 73 hours while halving our timer accuracy.

Since our use case is just to blink an LED, we don't need a very high accuracy. And unless we plan to blink that LED continuously for many days, neither do we care about an overflow of the timer register[7]. For convenience, I opted to set the prescaler value to 32 (0x20 in hexadecimal[8]). That gives us a resolution of around one millisecond.

But enough exposition, let's get to the last missing piece of code:

fn sleep_ms(milliseconds: u32) {
	unsafe {
		let sleep_until = *TIMER_VALUE_REGISTER + milliseconds;
		while *TIMER_VALUE_REGISTER < sleep_until {}

This function computes the value the timer has to reach before it is allowed to return and then simply keeps checking the timer until it reaches that value. There are several problems with this approach:

Since all we're doing right now is blink an LED, none of those shortcomings are really relevant for us. At some point in the future, we are going to need a more robust implementation of this function. But for now, it is good enough.

While I'm (currently) quite happy with the generally low quality of our wait function, I still want to do a tiny bit of cleaning up. First, let's create a struct for accessing all of the available RTT registers:

pub struct Rtt {
	pub mode  : u32,
	pub alarm : u32,
	pub value : u32,
	pub status: u32,

As you can see by the number of registers, RTT is much simpler than PIO. There's also just one Real-time Timer, so we only need to define one constant for its address:

pub const RTT: *mut Rtt = 0x400E1A30 as *mut Rtt;

The setup code has changed only a little:

(*RTT).mode = 0x00000020;

As has the sleep function:

fn sleep_ms(milliseconds: u32) {
	unsafe {
		let sleep_until = (*RTT).value + milliseconds;
		while (*RTT).value < sleep_until {}

That's it for today. As always, the full code is available on GitHub. See you next time!