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