mirror of
https://github.com/italicsjenga/gba.git
synced 2025-01-11 03:21:30 +11:00
timers
This commit is contained in:
parent
796e8351f3
commit
8d7df89f8d
|
@ -1,22 +1,98 @@
|
||||||
|
//! Module to interface with the GBA's four timer units.
|
||||||
|
//!
|
||||||
|
//! Similar to the background layers and DMA units, there are four timer units
|
||||||
|
//! and they're numbered 0 through 3.
|
||||||
|
//!
|
||||||
|
//! There's two hardware addresses that control each timer.
|
||||||
|
//! * The timer's high address is the [`TimerControl`] bits.
|
||||||
|
//! * The timer's low address is a `u16` which *reads* the timer's "count"
|
||||||
|
//! value, but *writes* the timer's "reload" value. In this crate we actually
|
||||||
|
//! represent that as two separate MMIO controls for improved code clarity.
|
||||||
|
//! Just be aware that in mGBA's debugger and in other documentation you'll
|
||||||
|
//! see it as a single address.
|
||||||
|
//!
|
||||||
|
//! When a timer is disabled, it will continue to read the count value that it
|
||||||
|
//! stopped at.
|
||||||
|
//!
|
||||||
|
//! ## Reloading
|
||||||
|
//!
|
||||||
|
//! When the timer goes from disabled to enabled, or when the timer overflows,
|
||||||
|
//! the last set reload value is copied to the counter value.
|
||||||
|
//!
|
||||||
|
//! ## Ticking
|
||||||
|
//!
|
||||||
|
//! When a timer is enabled, the timer will tick every so often. Each tick
|
||||||
|
//! increases the counter value by 1. The rate at which the timer ticks depends
|
||||||
|
//! on the timer's configuration:
|
||||||
|
//!
|
||||||
|
//! * If the `cascade` bit is set the timer will tick once per overflow of the
|
||||||
|
//! next lower timer. For example, if timer 3 is set to cascade, it will tick
|
||||||
|
//! once per overflow of timer 2. Note that timer 0 ignores the cascade bit,
|
||||||
|
//! since it doesn't have a "next lower" timer.
|
||||||
|
//! * Otherwise, the timer ticks every one or more CPU cycles, according to the
|
||||||
|
//! [`TimerScale`] set in the `scale` field.
|
||||||
|
//!
|
||||||
|
//! ## Overflows
|
||||||
|
//!
|
||||||
|
//! When a timer would tick *above* `u16::MAX` then an overflow occurs. This can
|
||||||
|
//! trigger an interrupt, and will also cause the timer to copy its reload value
|
||||||
|
//! into its counter.
|
||||||
|
//!
|
||||||
|
//! If you want a timer to overflow every `x` ticks (where `x` is non-zero),
|
||||||
|
//! then use the [`wrapping_neg`](u16::wrapping_neg) method to easily get the
|
||||||
|
//! right reload value to set:
|
||||||
|
//!
|
||||||
|
//! ```
|
||||||
|
//! # use gba::prelude::*;
|
||||||
|
//! let x = 7_u16;
|
||||||
|
//! TIMER0_RELOAD.write(x.wrapping_neg());
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ## Timer Tips
|
||||||
|
//!
|
||||||
|
//! When a timer goes from disabled to enabled it will reset the counter value
|
||||||
|
//! to the reload value. If you want to temporarily pause a timer *without*
|
||||||
|
//! having the counter value get reset when you resume the timer you can instead
|
||||||
|
//! set the `cascade` bit of the timer while the next lower timer is disabled.
|
||||||
|
//! This keeps the timer active but prevents it from ticking. Note that this
|
||||||
|
//! doesn't work for timer 0 because that timer ignores the cascade bit.
|
||||||
|
|
||||||
use crate::macros::{pub_const_fn_new_zeroed, u16_bool_field, u16_enum_field};
|
use crate::macros::{pub_const_fn_new_zeroed, u16_bool_field, u16_enum_field};
|
||||||
|
|
||||||
|
/// A number of CPU cycles per timer tick.
|
||||||
|
///
|
||||||
|
/// * The GBA's CPU runs at 16,777,216 cycles per second (16.78 Mhz).
|
||||||
|
/// * The GBA's PPU outputs one pixel per 4 CPU cycles.
|
||||||
|
/// * It takes 280,896 cycles for one full frame (when you add up all the draw
|
||||||
|
/// and blank periods).
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
pub enum TimerPrescale {
|
pub enum TimerScale {
|
||||||
|
/// Approximately 59.6 nanoseconds
|
||||||
#[default]
|
#[default]
|
||||||
_1 = 0,
|
_1 = 0,
|
||||||
|
/// Approximately 3.815 microseconds
|
||||||
_64 = 1,
|
_64 = 1,
|
||||||
|
/// Approximately 15.26 microseconds
|
||||||
_256 = 2,
|
_256 = 2,
|
||||||
|
/// Approximately 61.04 microseconds
|
||||||
_1024 = 3,
|
_1024 = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Timer configuration bits.
|
||||||
|
///
|
||||||
|
/// * `prescale` is how many CPU cycles per tick
|
||||||
|
/// * `cascade` will override the prescale value and instead tick the timer once
|
||||||
|
/// per overflow of the next lower timer. Timer 0 ignores the cascade bit.
|
||||||
|
/// * `overflow_irq` will cause an IRQ to be sent each overflow.
|
||||||
|
/// * `enabled` makes the timer tick.
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct TimerControl(u16);
|
pub struct TimerControl(u16);
|
||||||
impl TimerControl {
|
impl TimerControl {
|
||||||
pub_const_fn_new_zeroed!();
|
pub_const_fn_new_zeroed!();
|
||||||
u16_enum_field!(0 - 1: TimerPrescale, prescale, with_prescale);
|
u16_enum_field!(0 - 1: TimerScale, scale, with_scale);
|
||||||
u16_bool_field!(2, chained, with_chained);
|
u16_bool_field!(2, cascade, with_cascade);
|
||||||
u16_bool_field!(6, overflow_irq, with_overflow_irq);
|
u16_bool_field!(6, overflow_irq, with_overflow_irq);
|
||||||
u16_bool_field!(7, enabled, with_enabled);
|
u16_bool_field!(7, enabled, with_enabled);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue