Timing -- Programming a PIC24 with Atom

Posted on August 11, 2016

So far I have managed to shoehorn Atom into the basic Hello World of embedded programming. Today I ventured off into making the timing which drives Atom more predictable.

Housekeeping

First, some things that need to change from the previous implementation.

Variables, Not Actions

Using an action to actuate the LED DIO pin was silly. Rather I can define a variable which refers to the register/pin in Atom speak.

light = bool' "LATDbits.LATD9"

Now I can turn the lights on and off simply by doing:

light <== true -- turn LED on
light <== false -- turn LED off

This feels much cleaner.

Main Really Should Be “generated”

The more I read about the configuration options to Atom’s compile function, the more I believe that having the main generated with the tick function is somewhat necessary. Specifically, once I get into more complex timing structures (hopefully next post) it appears that the tick rate can take multiple clock cycles, and that the actions can be scheduled to the clock cycles from within the ticks.

To that end, the __global_clock variable appears to be generated as a static variable by Atom, which means that any main() referencing it will need to be in that file (or appear to be at least)

Setting Up the Timer

I must reiterate, embedded programming is not my specialty; it’s just something I am called on to do sometimes when there’s no one better available. To this end, I’m sorry if my code appears amaturish.

To my main() function, I need to add the initialization of a timer. I would like to initially set up the timer to overflow every 1ms, since that’s the timeframe expected by my period and startTimer invocations from last time.

It is important to note that I am using the internal RC occilator on the PIC24F, which runs at about 8MHz. I need to mentally divide that by two (though I’m not sure why) before setting Timer1’s prescalar to 1:8 in order to get .5MHz. Once that’s done, I set the overflow (period register) to 500 to get a 1kHz overflow interrupt. I wish I had known all of that before this day began :P

...

T1CON           = 0x00; // stop timer and reset.
T1CONbits.TCS   = 0;    // use internal clock source
T1CONbits.TSYNC = 0;    // asynchronous to clock input
T1CONbits.TCKPS = 0b01; // set prescalar to 1:8
T1CONbits.TSIDL = 1;    // Halt timer when idle
PR1             = 500;  // Use a 500 cycle overflow
IPC0bits.T1IP   = 0x01; // set interrupt priority 1
IFS0bits.T1IF   = 0;    // clear interrupt
IEC0bits.T1IE   = 1;    // enable timer interrupts
T1CONbits.TON   = 1;    // start the timer.

...

Obviously, since I’m using the interrupt on the timer, I need a variable to indicate whether I’ve gotten to the interrupt period or not, and the interrupt handler to make the change. I define this above main()

static volatile bool wait = true;

void __attribute__((__interrupt__, __no_auto_psv, __shadow__))
    _T1Interrupt( void )
{
   wait = false;      // clear the waiting flag.
   IFS0bits.T1IF = 0; // reset interrupt status
}

So my runloop changes substantially, to wait on the flag to flip before invoking each tick.

while( true )
{
    atom_tick();

    while( wait ); // semicolon intentional
    wait = true;
}

Build, run, see strobe as before, celebrate.

Higher Fidelity

Just as an experiment, I want to increase the rate of ticks; If I change the timer overflow from 500 to 50 I can get a 10kHz tick, which is quite appreciable for most simple user-driven applications. I have yet to meet the human capable of clicking a button anywhere near 1kHz, but if I want to interleave something like I2C, I’m going to need a higher rate of execution.

The problem with this is that if I change the timer overflow to 50, I’m going to have to change every time-based period and timer in the entire system.

I also like to ensure I have an adequate level of abstraction to express myself without having to do math at every step. Isn’t that what programming languages are for? Isn’t that why I’m using haskell as the equivalent of a mass-preprocessor on C?

So, to begin, I’m going to make a few definitions to correlate the tick and clock rate to real time. At this time, I don’t have a clock rate which exceeds my cycle (tick) rate, so I’ll use the one to define the other.

cycleRate :: Integer
cycleRate = 1000

clockRate :: Integer
clockRate = cycleRate

Using these, I can create abstractions on the period and startTimer (and maybe others later) functions:

periodMillis :: Integer -> Atom a -> Atom a
periodMillis milliCount =
  period (fromIntegral cycleCount)
  where
    cycleCount = cycleRate * milliCount `div` 1000

startTimerMillis :: Timer -> E Word64 -> Atom ()
startTimerMillis t (Const milliCount) =
  startTimer t (Const $ fromIntegral clockCount)
  where
    clockCount = clockRate * (fromIntegral milliCount) `div` 1000
startTimerMillis t millis =
  startTimer t clockCount
  where
    clockCount = (Const $ fromIntegral clocksPerSecond) * millis `div_` 1000

A few things of note in this code: Periods are defined in terms of cycles, whereas the timers are defined in terms of clocks. Additionally, the timer’s value can come from runtime, but can also be a constant. If I intend to use a constant, I would rather do the time conversion at compile time rather than during runtime, and since I do not entirely trust the compiler to perform the constant-time arithmetic during its optimizations, I can do that here, leaving only a constant value in the resultant C.

Now I just need to change my flashLight function to use these new timings:

flashLight = do
  lightStat <- bool "lightStatus" False
  dutyTimer <- timer "duty"

  periodMillis 1000 $ atom "lightOn" $ do
    light <== true
    lightStat <= true
    startTimerMillis dutyTimer $ Const 50

  atom "lightOff" $ do
    cond $ value lightStat &&. timerDone dutyTimer
    light <== false
    lightStat <== false

Build, run, see strobe, celebrate.

Now, as long as cycleRate and clockRate correspond with the configured rate of the hardware timer driving the loop, I never need to think about whether I’m using the right number of cycles to obtain a particular timing. This could probably be made more robust by ensuring that the timer and period are at least 1 unit, but for my purposes, I don’t expect to underrun the timer any time soon.

Once More, With Input

Since that only took most of the day for me to figure out, I thought I’d move onto a more complex problem before the day was done. On my board, I have a button whose purpose is going to be to turn on this backlight so that I can see the temperature displayed on the EPD. While I don’t feel confident enough to tackle the EPD today, I can make use of that button now. It’s tied to a simple DIO (RF3 specifically) which is already configured for input.

First I define the button:

pushbutton :: V Bool
pushbutton = bool' "PORTFbits.RF3"

This is good, except that pushing the button brings the voltage low, rather than high; so I want the value inverted. Since I can’t write to this register pin anyway, why not just apply the inversion at the source?

pushbutton :: E Bool
pushbutton = not_ (value $ bool1 "PORTFbits.RF3")

Now I have a simple boolean expression which tells me that my button is either pushed, or not. I can incorporate that into a very clever 7 lines of code:

lightActivity :: Atom ()
lightActivity = do
  isInteracting <- bool "interacting" False
  darkTimer <- timer "dark"

  -- Activate the light when the button is pushed
  atom "active" $ do
    cond pushbutton
    isInteracting <== true
    light <== true
    startTimerMillis darkTimer $ Const 2000

  -- Deactivate the light a short time after
  -- the button stops being pushed
  atom "inactive" $ do
    cond $ value isInteracting &&. timerDone darkTimer
    isInteracting <== false
    light <== false

Now, if I change my compile line from the last post to compile lightActivity instead of flashLight I have an LED which lights up when the pushbutton is pushed, and stays lit until the user has stopped mashing the button for 2 seconds.

main = do
  (schedule, _, _, _, _) <- compile "temperature" cfg lightActivity
  putStrLn $ reportSchedule schedule
  where
    cfg = defaults { cFuncName = "atom_tick"
                   , cStateName = "atom_state"
                   , cCode = prePostCode
                   , cRuleCoverage = False
                   }

Getting silly

Just to prove how crazy I am, I decided to strobe the LED while it’s inactive, instead of just having it go dark. Since I have both branches of functionality, I just need to link them. First add a line to the end of lightActivity:

  return isInteracting

Then create a wrapper for the two:

silliness :: Atom ()
silliness = do
  interacting <- lightActivity

  atom "strobe_otherwise" $ do
    cond $ not_ interacting
    flashLight

And change to compile that:

main = do
  (schedule, _, _, _, _) <- compile "temperature" cfg silliness
  putStrLn $ reportSchedule schedule
  where
    cfg = defaults { cFuncName = "atom_tick"
                   , cStateName = "atom_state"
                   , cCode = prePostCode
                   , cRuleCoverage = False
                   }

Now, the LED strobes annoyingly unless you press the button, at which point it simply blinds you for two seconds and then goes back to trying to give you an epilleptic fit. Neat huh?

But more importantly, this proved to me that Atom really is worth the time, even if you’re not looking for ensuring hard realtime constraints. If I had figured both these bits of logic out in C, and then wanted to combine them in such a clever manner, I would probably have kept the first two functions as reference, and used them as the basis for the beginnings of a state machine. The resulting code would not have been nearly as easy to understand as “this block does something if we’re interacting, and when we’re not, we’d like to do this other block”.

That’s nearly the definition of a state machine, eh?

To be continued…