Yet another Atom Arduino blinker

While experimenting with Atom I produced a minimal example for making my old Arduino Uno blink its LED periodically.

Although there are plenty of examples out there (some of them even work!) I didn’t want to introduce any dependency to the Arduino libraries. Besides making it harder to build (even if arscons is quite nice) it increases the object size and Arduino only supports a limited range of processors. I need the flexibility to use the code on controlles like the ATTiny45

Most examples are uncommented and therefore hard to understand for beginners. Although I think Atom is conceptually significantly easier to understand than Copilot the classical embedded C programmer might require commented source code in order to fully understand Atom.

Additionally, I use the technique described in the QuasiQutation post in order to increase the readability of the inlined C code. Additionally, I disabled features like assertions and rule coverage

{-# LANGUAGE QuasiQuotes #-}
{-|
Minimalistic Atom reset example for ATMega328P.
The LED is assumed to be on PB5 (e.g. Arduino Uno).

No Arduino Libraries are required.

Compilation example:
@
runghc Blink.hs
avr-gcc -o Blink.elf -Os -Wall -mmcu=atmega328p -DF_CPU=16000000L blink.c
avr-objcopy -O ihex -R .eeprom Blink.elf Blink.hex
@
-}
module Blink (main) where
 
import StringEmbed
import Language.Atom

-- | Our main Atom program.
--   Periodically blinks the LED
blink :: Atom ()
blink = do
    -- Declare a local state variable
    --  that controls if the LED is on or off
    on <- bool "on" True
    -- Every 50000'th call of blink() ...
    period 50000 $ atom "blinkOn" $ do
        -- invert LED state
        on <== not_ (value on)
        -- and write the new state to PORTB
        call "showLED"
 
-- | C code that will be inserted BEFORE the atom code
cHeader :: String
cHeader = [embedStr|
#include <avr/io.h>
#include <util/delay.h>

static inline void showLED(void);
|]
 
-- | C code that will be inserted AFTER the atom code
cFooter :: String
cFooter = [embedStr|
//LED: PB5
#define LED_PIN (1<<5) 

//Set/reset the LED
static inline void showLED() {
    if(state.blink.on) {
        PORTB |= LED_PIN;
    } else {
        PORTB &= ~(LED_PIN);
    }
}

int main (void) { 
    //Set LED pin to OUTPUT
    DDRB |= LED_PIN;
    while(1) {
        blink();
    }
    return 0; //Never reache
}
|]

main :: IO ()
main = do
    let code _ _ _ = (cHeader, cFooter)
    let cfg = defaults {cCode = code,
                        cRuleCoverage = False,
                        cAssert = False}
    (schedule, _, _, _, _) <- compile "blink" cfg blink
    putStrLn $ reportSchedule schedule