/* lib.rs
*
* Developed by Tim Walls <tim.walls@snowgoons.com>
* Copyright (c) All Rights Reserved, Tim Walls
*/
//! A simple Rust runtime for AVR microcontrollers.
//!
//! AVRoxide is a simple Hardware Abstraction Layer and runtime for
//! developing software for ATmega AVR microcontrollers.
//!
//! It was born out of frustration of the lack of options available for
//! Rust development using the ATmega4809 microcontroller present in
//! Arduino Nano Every embedded processor boards, but is intended to grow
//! to support multiple target devices.
//!
//! Key features include:
//! * Abstractions of the key hardware devices provided by the controller,
//! like USARTs, timers, and GPIO ports.
//! * Higher-level abstractions - like 'clock' and 'button' - for simple
//! application programming.
//! * An "Arduino Familiar" way of referring to hardware components,
//! while also offering complete access to the underlying chip for regular,
//! non-Arduino, embedded solutions.
//! * A simple runtime for event-based application development, using
//! interrupt-driven IO for power-efficient operation.
//! * Pre-emptive multithreading supervisor with basic synchronisation
//! primitives.
//!
//! # Feature Flags
//! Feature flags are used so you can ensure minimal code size by disabling
//! features that are not required, and also to configure some of those
//! features at compile-time (important in an embedded system.) Some of these
//! features are mandatory, and describe the type of hardware you are building
//! for. Others describe optional functionality.
//!
//! ## Mandatory Features
//! It is necessary to tell AVRoxide what device you are supporting by
//! providing both a processor feature, and a clockspeed feature, from the
//! list below. The CPU feature flag determines which hardware devices are
//! exposed by the HAL, while the clockspeed feature is used to calculate
//! certain constants for things like timer and baud rate calculations.
//!
//! | CPU Feature | Clockspeed features (pick 1) |
//! | ----------- | ---------------------------- |
//! | `atmega4809` | `16MHz`, `20MHz` |
//! | `atmega328p` | `16MHz` |
//!
//! ## Optional Features
//! ### Board Compatibility
//! Feature flags can be used to enable a board-compatibility module that
//! you can then use to access pins via "board-specific" aliases. For example,
//! if you enable on of the Arduino board features, you can access the
//! A and D pins using the Arduino naming conventions using aliases like so:
//!
//! ```rust,no_run
//! # #![no_std]
//! # #![no_main]
//! #
//! # use avr_oxide::alloc::boxed::Box;
//! # use avr_oxide::devices::UsesPin;
//! # use avr_oxide::devices::debouncer::Debouncer;
//! # use avr_oxide::devices::{ OxideLed, OxideButton, OxideSerialPort };
//! # use avr_oxide::hal::generic::serial::{BaudRate, DataBits, Parity, SerialPortMode, StopBits};
//! # use avr_oxide::io::Write;
//! # use avr_oxide::StaticWrap;
//! use avr_oxide::boards::board;
//!
//! #[avr_oxide::main(chip="atmega4809",stacksize=512)]
//! pub fn main() {
//! # let supervisor = avr_oxide::oxide::instance();
//! let green_led = OxideLed::with_pin(board::pin_d(7));
//! # supervisor.run()
//! }
//! ```
//!
//! The relevant feature flags are:
//!
//! | Feature name | |
//! | ------------ | ------- |
//! | `arduino_nanoevery` | Arduino Nano Every with ATmega4809 CPU |
//! | `arduino_uno` | Arduino Uno with ATmega328P CPU |
//! | `atmega4809_xplained_pro` | AVR ATMega4809-XPlained-Pro board |
//!
//! ### Panic handler
//! If the `panic_handler` feature is enabled, a default panic handler will
//! be provided.
//!
//! ### Panic to serial port support
//! You can configure a serial port which will be used by the AVRoxide
//! [panic handler] to output error line information on panic.
//!
//! It is your responsibility to initialise that port with a
//! suitable baud rate/serial protocol as early as possible in your program.
//!
//! | For Processor | Available panic output Features (pick 1) |
//! | -------------- | ---------------------------------------- |
//! | `atmega4809` | `panicout_usart0`, `panicout_usart1`, `panicout_usart2`, `panicout_usart3` |
//! | `atmega328p` | `panicout_usart0` |
//!
//! > *Important*: Because of the way Rust embeds panic information in your
//! > binary, enabling this feature can take up a fair amount of Flash memory
//! > space. For example, a simple test app that uses 37882 bytes of Flash
//! > without this feature uses 44330 bytes with it.
//! >
//! > Note also that because Rust embeds absolute pathnames for source files
//! > into the binary, the size of your binary can change depending on where
//! > (the directory path) you compile it on your development machine!
//!
//! Often, we need to disable Rust's built-in panic handling entirely in
//! `.cargo/config.toml`, like so:
//!
//! ```toml
//! build-std-features = ["compiler-builtins-mangled-names","panic_immediate_abort"]
//! ```
//!
//! In this case, there is still value to nominating a `panicout` port;
//! avr-oxide will still use this serial port to dump thread dumps, or
//! for use with the built-in serial debug methods in the [`avr_oxide::util::debug`] module.
//!
//! ### Dynamic Allocator
//! A dynamic allocator implementation is provided; this will allocate a
//! certain amount of the device's memory as a heap for dynamic data
//! allocation. The size of the heap will be automatically calculated after
//! taking into account space used by statics/global variables.
//!
//! A certain minimum amount of memory allocatable to heap is required by
//! AVRoxide. If this minimum amount of RAM is not available, the system
//! will halt on boot with an [`avr_oxide::oserror::OsError::NotEnoughRam`] error.
//!
//! The minimum heap size requirement is dependent on the processor:
//!
//! | For Processor | Minimum memory for heap |
//! | ------------- | ----------------------- |
//! | `atmega4809` | 2048 bytes |
//! | `atmega328p` | 512 bytes |
//!
//! #### Stack size
//! Note that the thread stack is allocated from the *heap* when a thread
//! is created (including your initial main thread.)
//!
//! #### Rust memory use with panics
//! The way Rust currently handles panics results in inordinate use of
//! not just EEPROM, but also RAM. You can help significantly by adding the
//! `panic_immediate_abort` feature to your `.cargo/config.toml`:
//!
//! ```toml
//! build-std-features = ["compiler-builtins-mangled-names","panic_immediate_abort"]
//! ```
//!
//! If your application will not run, with an `OsError::NotEnoughRam` error,
//! you may need to do this.
//!
//! ### Device Features
//! Some individual device 'drivers' are enabled by feature flags. If you
//! don't need one, don't enable it and maybe you can save a few precious
//! bytes of RAM...
//!
//! | For Processor | Optional Device Feature Flags |
//! | ------------- | ----------------------------- |
//! | `atmega4809` | `usart0`,`usart1`,`usart2`,`usart3`,`tcb0`,`tcb1`,`tcb2`,`tcb3`,`rtc`, `twi0` |
//! | `atmega328p` | `usart0` |
//!
//! ### Low-Power Modes
//! By default, Oxide configures the chip to run without a clock prescaler
//! (i.e. it runs at the full configured clockspeed), and a basic internal
//! clock interrupt frequency of 80Hz (this is the maximum frequency for
//! which you can configure `MasterClock` device events.)
//!
//! It is possible however to enable lower-power-use modes through either
//! the `power_med` or `power_low` feature flags; these have the following
//! effect:
//!
//! | Power mode flag | Processor clock speed | Oxide clock frequency |
//! | --------------- | --------------------- | --------------------- |
//! | None set | 1:1 (equal to hardware clock source) | 80 Hz |
//! | `power_med` | 1/4th (0.25) of hardware clock source | 40 Hz |
//! | `power_low` | 1/8th (0.125) of hardware clock source | 10 Hz |
//!
//! ### Pre-Emptive Multithreading
//! AVRoxide supports multithreading, with threads created/joined using a
//! simplified version of the standard Rust `thread::spawn()` and
//! `thread::yield_now()` methods (provided through the [`avr_oxide::thread`]
//! module.) Familiar synchronisation primitives are provided in [`avr_oxide::sync`].
//!
//! Cooperative threading (using `yield_now()` and/or syncorhonisation
//! primitives which yield when blocked as and when necessary) requires no
//! further configuration.
//!
//! Pre-emptive multithreading is supported as well, but does require one
//! further step - a timer that will drive thread preemption. This means
//! at least one MasterClock device must be created and running:
//!
//! ```rust,no_run
//! #![no_std]
//! #![no_main]
//!
//! use avr_oxide::devices::{ UsesPin, OxideLed, OxideMasterClock };
//! use avr_oxide::boards::board;
//! use avr_oxide::thread;
//! use avr_oxide::hardware;
//! use avr_oxide::StaticWrap;
//!
//! #[avr_oxide::main(chip="atmega4809",stacksize=512)]
//! pub fn main() {
//! let supervisor = avr_oxide::oxide::instance();
//!
//! // Configure a 50Hz master clock device on TCB0
//! let master_clock = StaticWrap::new(OxideMasterClock::with_timer::<50>(hardware::timer::tcb0::instance()));
//! supervisor.listen(master_clock.borrow());
//!
//! // The MasterCLock will by default drive context switches, thus threads
//! // will now be pre-emptively multitasked, and this code will work without
//! // locking up:
//! let _jh = thread::Builder::new().stack_size(32).spawn(||{
//! let white_led = OxideLed::with_pin(board::pin_d(10));
//!
//! loop {
//! white_led.toggle();
//! }
//! });
//!
//! // Note that all the usual functionality of the MasterClock device
//! // (delay timers, regular Oxide event handlers) remains available -
//! // it will be used for thread preemption *in addition* to its usual
//! // function, not instead.
//!
//! supervisor.run();
//! }
//! ```
//!
//! Note that by default all MasterClock devices trigger context switches.
//! You can disable this behaviour with the [`disable_preemption()`] method
//! on a device that you don't wish to trigger context switches.
//!
//! [`disable_preemption()`]: avr_oxide::devices::MasterClock::disable_preemption
//!
//! ### Runtime checks
//! The kernel performs various sanity checks during runtime, if enabled
//! with the `runtime_checks` feature. It is strongly encouraged that you
//! *do* use this feature, however it does imply a certain amount of
//! overhead (in particular during the context-switch interrupt routine, which
//! is when most of these are executed), so if you need to squeeze every last
//! ounce of performance out of the processor you may disable them.
//!
//! The table below summarises the checks performed:
//!
//! | Test | With `runtime_checks` | Without `runtime_checks` |
//! | ---- | --------------------- | ------------------------ |
//! | Thread stack overflow | Halt with [`OsError::StackOverflow`] | Undefined behaviour |
//! | Kernel structure consistency | Halt with [`OsError::KernelGuardCrashed`] | Undefined behaviour |
//! | Event queue overflow | Halt with [`OsError::OxideEventOverflow`] | Events silently discarded |
//! | Attempt to block inside an ISR | Halt with [`OsError::BadThreadState`] | Undefined behaviour |
//! | A StaticWrap dropped | Halt with [`OsError::StaticDropped`] | Memory leaked |
//!
//! [`OsError::StackOverflow`]: avr_oxide::oserror::OsError::StackOverflow
//! [`OsError::KernelGuardCrashed`]: avr_oxide::oserror::OsError::KernelGuardCrashed
//! [`OsError::OxideEventOverflow`]: avr_oxide::oserror::OsError::OxideEventOverflow
//! [`OsError::BadThreadState`]: avr_oxide::oserror::OsError::BadThreadState
//! [`OsError::StaticDropped`]: avr_oxide::oserror::OsError::StaticDropped
//!
//! # A Minimal AVRoxide Program
//! This program shows how to access the devices using the Arduino aliases
//! for an Arduino Nano Every, and how to listen to button events from a
//! button attached to pin A2 to toggle an LED attached to pin D7 every time
//! the button is pressed.
//!
//! We also show the use of a software debouncer on the pin, and configuring
//! a serial port.
//!
//! ```no_run
//! #![no_std]
//! #![no_main]
//!
//! use avr_oxide::alloc::boxed::Box;
//! use avr_oxide::devices::UsesPin;
//! use avr_oxide::devices::debouncer::Debouncer;
//! use avr_oxide::devices::{ OxideLed, OxideButton, OxideSerialPort };
//! use avr_oxide::hal::generic::serial::{BaudRate, DataBits, Parity, SerialPortMode, StopBits};
//! use avr_oxide::io::Write;
//! use avr_oxide::boards::board;
//! use avr_oxide::StaticWrap;
//!
//! #[avr_oxide::main(chip="atmega4809")]
//! pub fn main() {
//! let supervisor = avr_oxide::oxide::instance();
//!
//! // Configure the serial port early so we can get any panic!() messages
//! let mut serial= OxideSerialPort::using_port_and_pins(board::usb_serial(),
//! board::usb_serial_pins().0,
//! board::usb_serial_pins().1).mode(SerialPortMode::Asynch(BaudRate::Baud9600, DataBits::Bits8, Parity::None, StopBits::Bits1));
//! serial.write(b"Welcome to AVRoxide\n");
//!
//! let green_led = OxideLed::with_pin(board::pin_d(7));
//! let mut green_button = StaticWrap::new(OxideButton::using(Debouncer::with_pin(board::pin_a(2))));
//!
//! green_button.borrow().on_click(Box::new(move |_pinid, _state|{
//! green_led.toggle();
//! }));
//!
//! // Tell the supervisor which devices to listen to
//! supervisor.listen(green_button.borrow());
//!
//! // Now enter the event loop
//! supervisor.run();
//! }
//! ```
//!
//! # Examples and Templates
//! A more comprehensive example program can be found [here](https://avroxi.de/post/an-example-program/).
//!
//! 'Empty' templates can be found at the [AVRoxide Templates](https://gitlab.com/snowgoonspub/avr-oxide-templates) gitlab repo,
//! with all the necessary bits and pieces to get up and running quickly.
//!
//! The project homepage is at [avroxi.de](https://avroxi.de), where you'll find
//! more getting-started style guides and documentation.
//!
// no_std if built for AVR, if built for anything else it'll be for testing
// and then we do want std
extern crate self as avr_oxide;
pub
pub use ;
pub use OxideResult;
pub use main as main;
// For convenience let's re-export the AVR device as a generic name
pub use atmega4809 as hardware;
pub use atmega328p as hardware;
// Re-export a few well buried modules at the top level
pub use sync as sync;
pub use thread as thread;