Programing a PIC24 with Atom

Posted on August 10, 2016

There is a surprising dearth of documentation and examples for programming with Atom. Despite this (or, perhaps because of it) I have decided to try doing it. The project I’m working on is basically a temperature sensor with an EPD display. I have not built the boards, but have been presented with them as part of the project.

The benefits that I anticipate (based on what I’ve read):

I’m writing this as a journal of my experiences, it will not be a good how-to – it will probably be spotty at best. But if you are desperate for basic information, it might just have what you’re looking for.

Das Blinkenlights (in C)

Ah, the howto of the embedded world: Turn on an LED. Most of the LEDs on this board are driven with I2C LED drivers, which are no good for the howto. But thankfully there are LEDs for lighting up the display, and those are controlled through a simple DIO pin RD9. That is where I start.

Since this is not just my first time using Atom, but also my first time programming a PIC, I start in familiar territory. Abandon Atom and Haskell and just write C:

#define FOSC (7470000ULL)
#define FCY  (FOSC/2)

#include <p24FJ256GA106.h>
#include <libpic30.h>

_CONFIG1( JTAGEN_OFF & BKBUG_ON & ICS_PGx1 & FWDTEN_OFF )
_CONFIG2( FNOSC_FRC );

int main()
{
    OSCTUN = 0;
    RCONbits.SWDTEN = 0; // disable watchdog

    AD1PCFG = 0b0000000000001;

    // Initialize RD9 as a DIO output and make
    // sure it's on.
    TRISDbits.TRISD9 = 0;
    LATDbits.LATD9 = 1;

    while( true )
    {
        // this space intentionally blank
    }

    return 0;
}

Build, run, see LEDs come on, celebrate. Next, I want to make sure that I have some kind of timescale working, change the initialization and while loop:

int main()
{
    ...

    TRISDbits.TRISD9 = 0;
    LATDbits.LATD9 = 0;

    while( true )
    {
        __delay_ms( 950 );
        LATDbits.LATD9 = 1;

        __delay_ms( 50 );
        LATDbits.LATD9 = 0;
    }

    return 0;
}

Build, run, see strobe, celebrate.

Das Blinkenlights (in Atom)

Now it’s time to play with Atom. Initially, I just want to replicate this behaviour. I’ll create Atom-based actions for the on and off.

light :: Bool -> Atom ()
light onOff = action (\_ -> "LATDbits.LATD9 = " ++ val) []
  where val = if onOff then "1" else "0"

Then I define an Atom to deal with the blinking:

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

    period 1000 $ phase 500 $ atom "lightOn" $ do
      light True
      lightStat <== true
      startTimer dutyTimer $ Const 50

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

Finally, I create a function to “compile” that to C:

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

The atom examples that I’ve seen so far define main in the prePostCode sections. This is not how I want to work, I would rather define main somewhere that I can get proper syntax highlighting, so main.c has main, which will include temperature.h which is the generated header from the above haskell code.

prePostCode :: [Name] -> [Name] -> [(Name, Type)] -> (String, String)
prePostCode assertions coverages probes =
  ( unlines [ "#include <p24FJ256GA106.h>" ],
    unlines [])

Now, I define my main a bit differently, though I keep the initialization outside Atom at this time, I may look to integrate it later.

#include "temperature.h"

int main()
{
    OSCTUN = 0;
    RCONbits.SWDTEN = 0;

    AD1PCFG = 0b0000000000001;

    TRISDbits.TRISD9 = 0;
    LATDbits.LATD9 = 0;

    while( true )
    {
        atom_tick();
        __delay_us( 1000 );
    }

    return 0;
}

Build, run, see strobe, celebrate.

Using a delay between calls to atom_tick is wildly inaccurate, but for the proof-of-concept, it does the trick.

Next time, I will incorporate a hardware timer to govern the tick execution so that my program’s timing is consistent with my expectations.