first version, first embedded lib, please help.
This commit is contained in:
commit
ce75a8f890
1013
Cargo.lock
generated
Normal file
1013
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
30
Cargo.toml
Normal file
30
Cargo.toml
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
[package]
|
||||||
|
name = "led-button"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 's' # default is opt-level = '0', but that makes very verbose machine code
|
||||||
|
codegen-units = 1 # trade compile speed for slightly better optimisations
|
||||||
|
|
||||||
|
# cargo build/run --release
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 's' # default is opt-level = '3', but that makes quite verbose machine code
|
||||||
|
codegen-units = 1 # trade compile speed for slightly better optimisations
|
||||||
|
lto = 'fat' # Use Link Time Optimisations to further inline things across crates
|
||||||
|
debug = 2 # Leave the debug symbols in (default is no debug info)
|
||||||
|
strip = true # strip stupid stuff.
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7.7" }
|
||||||
|
cortex-m-rt = "0.7.5"
|
||||||
|
critical-section = { version = "1.2.0", features = ["restore-state-u8"] }
|
||||||
|
embedded-hal = "1.0.0"
|
||||||
|
rp235x-hal = { version = "0.4.0", features = ["rt", "critical-section-impl"] }
|
||||||
|
semihosting = "0.1.25"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "led-button-example"
|
||||||
|
path = "src/example/led_button.rs"
|
||||||
|
test = false
|
||||||
|
bench = false
|
||||||
27
build.rs
Normal file
27
build.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
//! Set up linker scripts for the rp235x-hal examples
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Put the linker script somewhere the linker can find it
|
||||||
|
let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
|
// The file `memory.x` is loaded by cortex-m-rt's `link.x` script, which
|
||||||
|
// is what we specify in `.cargo/config.toml` for Arm builds
|
||||||
|
let memory_x = include_bytes!("memory.x");
|
||||||
|
let mut f = File::create(out.join("memory.x")).unwrap();
|
||||||
|
f.write_all(memory_x).unwrap();
|
||||||
|
println!("cargo:rerun-if-changed=memory.x");
|
||||||
|
|
||||||
|
// The file `rp235x_riscv.x` is what we specify in `.cargo/config.toml` for
|
||||||
|
// RISC-V builds
|
||||||
|
let rp235x_riscv_x = include_bytes!("rp235x_riscv.x");
|
||||||
|
let mut f = File::create(out.join("rp235x_riscv.x")).unwrap();
|
||||||
|
f.write_all(rp235x_riscv_x).unwrap();
|
||||||
|
println!("cargo:rerun-if-changed=rp235x_riscv.x");
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
}
|
||||||
25
src/button_config.rs
Normal file
25
src/button_config.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//! Button config
|
||||||
|
|
||||||
|
use super::PullConfig;
|
||||||
|
|
||||||
|
/// The configuration of a simple button.
|
||||||
|
pub struct ButtonConfig {
|
||||||
|
/// fn_refresh * pressing_time = how many loops to trigger ButtonState::Pressed
|
||||||
|
pub pressing_time : f32,
|
||||||
|
/// fn_refresh * longpressing_time = how many loops to trigger ButtonState::LongPress
|
||||||
|
pub longpressing_time : f32,
|
||||||
|
/// if the button is in pull-up/down configuration.
|
||||||
|
pub pull_configuration : PullConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ButtonConfig {
|
||||||
|
|
||||||
|
/// some random default values.
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
pressing_time : 0.25,
|
||||||
|
longpressing_time : 1.77,
|
||||||
|
pull_configuration : PullConfig::PullUp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/button_state.rs
Normal file
13
src/button_state.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//! contains the enum ButtonState.
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ButtonState {
|
||||||
|
NotPressed,
|
||||||
|
Released,
|
||||||
|
LongReleased,
|
||||||
|
Flank,
|
||||||
|
LongFlank,
|
||||||
|
Pressing,
|
||||||
|
Pressed,
|
||||||
|
LongPress,
|
||||||
|
}
|
||||||
141
src/button_stateful.rs
Normal file
141
src/button_stateful.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
//! # A stateful button
|
||||||
|
//! -------------------
|
||||||
|
//! has counter and poll that can be used to avoid contact bounce.
|
||||||
|
//! very very nice, much wow.
|
||||||
|
|
||||||
|
use embedded_hal::digital::InputPin;
|
||||||
|
use super::button_state::ButtonState;
|
||||||
|
use super::button_config::ButtonConfig;
|
||||||
|
use super::PullConfig;
|
||||||
|
|
||||||
|
/// A stateful input pin represented as a button.
|
||||||
|
pub struct StatefulButton<IP> {
|
||||||
|
/// The pin from hal denoted as IP for Input Pin.
|
||||||
|
pub pin : IP,
|
||||||
|
|
||||||
|
/// ButtonConfig struct with a few options like pull-configuration.
|
||||||
|
pub config : ButtonConfig,
|
||||||
|
|
||||||
|
/// Current state of the button
|
||||||
|
state : ButtonState,
|
||||||
|
|
||||||
|
/// The previous state of the button
|
||||||
|
last_state : ButtonState,
|
||||||
|
|
||||||
|
/// counter for how many loops the button have been pressed.
|
||||||
|
pub counter : u32,
|
||||||
|
|
||||||
|
/// Threshold for triggering long pressed.
|
||||||
|
long_press_th : u32,
|
||||||
|
|
||||||
|
/// Threshold for triggering pressed.
|
||||||
|
pressing_th : u32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IP: InputPin> StatefulButton<IP> {
|
||||||
|
|
||||||
|
/// Creates a stateful button.
|
||||||
|
///
|
||||||
|
/// ## Parameters:
|
||||||
|
/// - button_pin : IP
|
||||||
|
/// - An input pin from embedded-hal crate.
|
||||||
|
/// - config : ButtonConfig
|
||||||
|
/// - a config from this crate (see led_button::button_config::ButtonConfig)
|
||||||
|
/// - fn_refresh : f32
|
||||||
|
/// - function refresh, lets say your program runs the function 10.000.000 times a seconds then thats a good value to use here.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use led_button::button_stateful::StatefulButton;
|
||||||
|
/// use led_button::button_config::ButtonConfig;
|
||||||
|
///
|
||||||
|
/// // remember to always do a weird dance..
|
||||||
|
///
|
||||||
|
/// let button = StatefulButton::create(
|
||||||
|
/// pins.gpio21.into_pull_up_input(),
|
||||||
|
/// ButtonConfig::default(),
|
||||||
|
/// 274000.1
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
pub fn create(button_pin: IP, config: ButtonConfig, fn_refresh: f32) -> Self {
|
||||||
|
let lpt = config.longpressing_time;
|
||||||
|
let pt = config.pressing_time;
|
||||||
|
Self {
|
||||||
|
pin : button_pin,
|
||||||
|
config : config,
|
||||||
|
long_press_th : (fn_refresh * lpt) as u32,
|
||||||
|
pressing_th : (fn_refresh * pt) as u32,
|
||||||
|
state : ButtonState::NotPressed,
|
||||||
|
last_state : ButtonState::NotPressed,
|
||||||
|
counter : 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// checks if button is in active state.
|
||||||
|
pub fn is_pressed(&mut self) -> u8 {
|
||||||
|
match (&self.config.pull_configuration, self.pin.is_low()) {
|
||||||
|
(PullConfig::PullUp, Ok(true)) => 1,
|
||||||
|
(PullConfig::PullDown, Ok(false)) => 1,
|
||||||
|
_ => 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// trigger a state, in case you want to.
|
||||||
|
pub fn trigger_state(&mut self, state: ButtonState) {
|
||||||
|
self.last_state = self.state;
|
||||||
|
self.state = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// polling logic will increase self.counter by one each time you call this function and the button is pressed.
|
||||||
|
/// if the button is released and poll is called the counter is reset.
|
||||||
|
/// will also set internal state of the button for a press, long press, not pressed and pressing.
|
||||||
|
pub fn poll(&mut self) {
|
||||||
|
self.last_state = self.state;
|
||||||
|
if self.is_pressed() > 0 {
|
||||||
|
self.counter = self.counter.wrapping_add(1);
|
||||||
|
if self.counter >= self.long_press_th {
|
||||||
|
self.state = ButtonState::LongPress;
|
||||||
|
}
|
||||||
|
else if self.counter >= self.pressing_th {
|
||||||
|
self.state = ButtonState::Pressed;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.state = ButtonState::Pressing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read the current state and previous state and return one of the following states.
|
||||||
|
/// - NotPressed
|
||||||
|
/// - Released
|
||||||
|
/// - LongReleased
|
||||||
|
/// - Pressing
|
||||||
|
/// - Flank
|
||||||
|
/// - Pressed
|
||||||
|
/// - LongFlank
|
||||||
|
/// - LongPress
|
||||||
|
pub fn read(&mut self) -> ButtonState {
|
||||||
|
match (self.state, self.last_state) {
|
||||||
|
(ButtonState::NotPressed, ButtonState::NotPressed) => ButtonState::NotPressed,
|
||||||
|
(ButtonState::NotPressed, ButtonState::Pressed) => ButtonState::Released,
|
||||||
|
(ButtonState::NotPressed, ButtonState::Pressing) => ButtonState::Released,
|
||||||
|
(ButtonState::NotPressed, ButtonState::LongPress) => ButtonState::LongReleased,
|
||||||
|
(ButtonState::Pressing, _) => ButtonState::Pressing,
|
||||||
|
(ButtonState::Pressed, ButtonState::Pressing) => ButtonState::Flank,
|
||||||
|
(ButtonState::Pressed, ButtonState::Pressed) => ButtonState::Pressed,
|
||||||
|
(ButtonState::LongPress, ButtonState::Pressed) => ButtonState::LongFlank,
|
||||||
|
(ButtonState::LongPress, ButtonState::LongPress) => ButtonState::LongPress,
|
||||||
|
_ => ButtonState::NotPressed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// resets the counter and set state and previous state to NotPressed.
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.state = ButtonState::NotPressed;
|
||||||
|
self.last_state = ButtonState::NotPressed;
|
||||||
|
self.counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/errors.rs
Normal file
73
src/errors.rs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
use core::{
|
||||||
|
fmt,
|
||||||
|
//convert::{Infallible},
|
||||||
|
};
|
||||||
|
|
||||||
|
//#[cfg(feature = "defmt-03")]
|
||||||
|
//use crate::defmt;
|
||||||
|
/*
|
||||||
|
pub trait Error: fmt::Debug {
|
||||||
|
/// Convert error to a generic error kind
|
||||||
|
///
|
||||||
|
/// By using this method, errors freely defined by HAL implementations
|
||||||
|
/// can be converted to a set of generic errors upon which generic
|
||||||
|
/// code can act.
|
||||||
|
fn kind(&self) -> ErrorKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for Infallible {
|
||||||
|
fn kind(&self) -> ErrorKind {
|
||||||
|
match *self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/// Error kind.
|
||||||
|
///
|
||||||
|
/// This represents a common set of operation errors. HAL implementations are
|
||||||
|
/// free to define more specific or additional error types. However, by providing
|
||||||
|
/// a mapping to these common errors, generic code can still react to them.
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
//#[cfg_attr(feature = "defmt-03", derive(defmt::Format))]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum ErrorKind {
|
||||||
|
LedCouldNotTurnPinLow,
|
||||||
|
LedCouldNotTurnPinHigh,
|
||||||
|
ButtonSomething
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ErrorKind {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::LedCouldNotTurnPinLow => write!(
|
||||||
|
f,
|
||||||
|
"[puckoprutt error] <- could not turn on led."
|
||||||
|
),
|
||||||
|
Self::LedCouldNotTurnPinHigh => write!(
|
||||||
|
f,
|
||||||
|
"[puckoprutt error] <- could not turn off led."
|
||||||
|
),
|
||||||
|
Self::ButtonSomething => write!(
|
||||||
|
f,
|
||||||
|
"[placeholder]"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
/// Error type trait.
|
||||||
|
///
|
||||||
|
/// This just defines the error type, to be used by the other traits.
|
||||||
|
pub trait ErrorType {
|
||||||
|
type Error: Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ErrorType + ?Sized> ErrorType for &T {
|
||||||
|
type Error = T::Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ErrorType + ?Sized> ErrorType for &mut T {
|
||||||
|
type Error = T::Error;
|
||||||
|
}
|
||||||
|
*/
|
||||||
126
src/example/led_button.rs
Normal file
126
src/example/led_button.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
//!
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use cortex_m::asm;
|
||||||
|
use cortex_m::peripheral::NVIC;
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use led_button::{LedButton, PullConfig};
|
||||||
|
use led_button::led_state::LedState;
|
||||||
|
use led_button::button_config::ButtonConfig;
|
||||||
|
use rp235x_hal::{
|
||||||
|
self as hal,
|
||||||
|
block::ImageDef,
|
||||||
|
pac::{Peripherals, interrupt},
|
||||||
|
gpio::{
|
||||||
|
Interrupt,
|
||||||
|
Pin, FunctionSio,
|
||||||
|
PullDown, PullUp,
|
||||||
|
SioInput, SioOutput
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type LedPin1 = Pin<hal::gpio::bank0::Gpio15, FunctionSio<SioOutput>, PullDown>;
|
||||||
|
type ButtonPin1 = Pin<hal::gpio::bank0::Gpio16, FunctionSio<SioInput>, PullUp>;
|
||||||
|
type LedPin2 = Pin<hal::gpio::bank0::Gpio13, FunctionSio<SioOutput>, PullDown>;
|
||||||
|
type ButtonPin2 = Pin<hal::gpio::bank0::Gpio21, FunctionSio<SioInput>, PullUp>;
|
||||||
|
|
||||||
|
static GLOBAL_LED_BTN_1 : Mutex<RefCell<Option<LedButton<LedPin1, ButtonPin1>>>> = Mutex::new(RefCell::new(None));
|
||||||
|
static GLOBAL_LED_BTN_2 : Mutex<RefCell<Option<LedButton<LedPin2, ButtonPin2>>>> = Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
pub const XOSC_CRYSTAL_FREQ : u32 = 12000000;
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn IO_IRQ_BANK0() {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut button1_ref = GLOBAL_LED_BTN_1.borrow(cs).borrow_mut();
|
||||||
|
let mut button2_ref = GLOBAL_LED_BTN_2.borrow(cs).borrow_mut();
|
||||||
|
|
||||||
|
if let (Some(button1), Some(button2)) = (button1_ref.as_mut(), button2_ref.as_mut()) {
|
||||||
|
if button1.button.pin.interrupt_status(Interrupt::EdgeLow) {
|
||||||
|
let _ = button2.toggle();
|
||||||
|
button1.button.pin.clear_interrupt(Interrupt::EdgeLow);
|
||||||
|
}
|
||||||
|
if button2.button.pin.interrupt_status(Interrupt::EdgeLow) {
|
||||||
|
let _ = button1.toggle();
|
||||||
|
button2.button.pin.clear_interrupt(Interrupt::EdgeLow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let mut pac = Peripherals::take().unwrap();
|
||||||
|
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
|
||||||
|
|
||||||
|
// clock and timers
|
||||||
|
let _clocks = hal::clocks::init_clocks_and_plls(
|
||||||
|
XOSC_CRYSTAL_FREQ,
|
||||||
|
pac.XOSC,
|
||||||
|
pac.CLOCKS,
|
||||||
|
pac.PLL_SYS,
|
||||||
|
pac.PLL_USB,
|
||||||
|
&mut pac.RESETS,
|
||||||
|
&mut watchdog
|
||||||
|
).ok().unwrap();
|
||||||
|
|
||||||
|
// init gpio
|
||||||
|
let sio = hal::Sio::new(pac.SIO);
|
||||||
|
let pins = hal::gpio::Pins::new(
|
||||||
|
pac.IO_BANK0,
|
||||||
|
pac.PADS_BANK0,
|
||||||
|
sio.gpio_bank0,
|
||||||
|
&mut pac.RESETS
|
||||||
|
);
|
||||||
|
|
||||||
|
let led1_pin = pins.gpio15.into_push_pull_output();
|
||||||
|
let button1_pin = pins.gpio16.into_pull_up_input();
|
||||||
|
let led2_pin = pins.gpio13.into_push_pull_output();
|
||||||
|
let button2_pin = pins.gpio21.into_pull_up_input();
|
||||||
|
button1_pin.set_interrupt_enabled(Interrupt::EdgeLow, true);
|
||||||
|
button2_pin.set_interrupt_enabled(Interrupt::EdgeLow, true);
|
||||||
|
|
||||||
|
let button1 = LedButton::create(
|
||||||
|
led1_pin, LedState::Off, PullConfig::PullDown, button1_pin, ButtonConfig::default(), 274000.0
|
||||||
|
);
|
||||||
|
let button2 = LedButton::create(
|
||||||
|
led2_pin, LedState::Off, PullConfig::PullDown, button2_pin, ButtonConfig::default(), 274000.0
|
||||||
|
);
|
||||||
|
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
GLOBAL_LED_BTN_1.borrow(cs).replace(Some(button1));
|
||||||
|
GLOBAL_LED_BTN_2.borrow(cs).replace(Some(button2));
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
NVIC::unmask(hal::pac::Interrupt::IO_IRQ_BANK0);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
asm::wfi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(link_section = ".start_block")]
|
||||||
|
#[used]
|
||||||
|
pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe();
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &PanicInfo) -> ! {
|
||||||
|
loop { asm::udf() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit() -> ! {
|
||||||
|
semihosting::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[cortex_m_rt::exception]
|
||||||
|
unsafe fn HardFault(_frame: &cortex_m_rt::ExceptionFrame) -> ! {
|
||||||
|
semihosting::process::exit(1);
|
||||||
|
}
|
||||||
55
src/led_state.rs
Normal file
55
src/led_state.rs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
//! LedState is pretty much a bool.
|
||||||
|
|
||||||
|
use core::ops::Not;
|
||||||
|
|
||||||
|
/// Digital output led state.
|
||||||
|
///
|
||||||
|
/// Conversion from `bool` and logical negation are also implemented
|
||||||
|
/// for this type.
|
||||||
|
/// ```rust
|
||||||
|
/// # use led_button::let_state::LedState;
|
||||||
|
/// let state = LedState::from(false);
|
||||||
|
/// assert_eq!(state, LedState::Low);
|
||||||
|
/// assert_eq!(!state, LedState::High);
|
||||||
|
///
|
||||||
|
/// let state = LedState::from(LedState::On);
|
||||||
|
/// assert_eq!(state, false);
|
||||||
|
/// assert_eq!(!state, true);
|
||||||
|
/// ```
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum LedState {
|
||||||
|
On,
|
||||||
|
Off
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for LedState {
|
||||||
|
#[inline]
|
||||||
|
fn from(on: bool) -> Self {
|
||||||
|
match on {
|
||||||
|
true => LedState::On,
|
||||||
|
false => LedState::Off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LedState> for bool {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: LedState) -> bool {
|
||||||
|
match value {
|
||||||
|
LedState::Off => false,
|
||||||
|
LedState::On => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Not for LedState {
|
||||||
|
type Output = LedState;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn not(self) -> Self::Output {
|
||||||
|
match self {
|
||||||
|
LedState::Off => LedState::On,
|
||||||
|
LedState::On => LedState::Off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/led_stateful.rs
Normal file
111
src/led_stateful.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
//! # Stateful LED
|
||||||
|
//! --------------
|
||||||
|
//! cool stuff.
|
||||||
|
//! very much wow factor.
|
||||||
|
|
||||||
|
use embedded_hal::digital::OutputPin;
|
||||||
|
use super::PullConfig;
|
||||||
|
use super::led_state::LedState;
|
||||||
|
use super::errors::ErrorKind;
|
||||||
|
|
||||||
|
/// A stateful output pin representing a LED.
|
||||||
|
pub struct StatefulLed<OP> {
|
||||||
|
/// The pin from hal denoted as OP for Output Pin.
|
||||||
|
pub pin : OP,
|
||||||
|
|
||||||
|
/// The current state of the led, can be On or Off.
|
||||||
|
pub current_state : LedState,
|
||||||
|
|
||||||
|
/// Resistor pull-up/down configuration. is the led "on" by a low or high signal.
|
||||||
|
pull_configuration : PullConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<OP: OutputPin> StatefulLed<OP> {
|
||||||
|
/// Creates a StatefulLed
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use led_button::led_stateful::StatefulLed;
|
||||||
|
/// use led_button::led_state::LedState;
|
||||||
|
/// use led_button::button_config::Buttonconfig;
|
||||||
|
/// use led_button::PullConfig;
|
||||||
|
///
|
||||||
|
/// // ... init your hal and do a weird a dance..
|
||||||
|
///
|
||||||
|
/// let led1_pin = pins.gpio15.into_push_pull_output();
|
||||||
|
/// let button1_pin = pins.gpio16.into_pull_up_input();
|
||||||
|
///
|
||||||
|
/// let button1 = LedButton::create(
|
||||||
|
/// led1_pin,
|
||||||
|
/// LedState::Off,
|
||||||
|
/// PullConfig::PullDown,
|
||||||
|
/// button1_pin,
|
||||||
|
/// ButtonConfig::default(),
|
||||||
|
/// 274000.0
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// // ... continue doing a weird a dance.
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub fn create(led_pin: OP, pull_config: PullConfig, start_state: LedState) -> Self {
|
||||||
|
Self {
|
||||||
|
pin : led_pin,
|
||||||
|
pull_configuration : pull_config,
|
||||||
|
current_state : start_state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_low(&mut self) -> Result<(), ErrorKind>{
|
||||||
|
match self.pin.set_low() {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(_e) => Err(ErrorKind::LedCouldNotTurnPinLow)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_high(&mut self) -> Result<(), ErrorKind>{
|
||||||
|
match self.pin.set_high() {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(_e) => Err(ErrorKind::LedCouldNotTurnPinHigh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// will trigger 'on' state depending on your pull configuration.
|
||||||
|
pub fn on(&mut self) -> Result<(), ErrorKind> {
|
||||||
|
match self.set_state(LedState::On) {
|
||||||
|
Ok(()) => {
|
||||||
|
self.current_state = LedState::On;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// will trigger 'off' state depending on your pull configuration.
|
||||||
|
pub fn off(&mut self) -> Result<(), ErrorKind>{
|
||||||
|
match self.set_state(LedState::Off) {
|
||||||
|
Ok(()) => {
|
||||||
|
self.current_state = LedState::Off;
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// will trigger the 'on/off' state thats not in use.
|
||||||
|
pub fn toggle(&mut self) -> Result<(), ErrorKind>{
|
||||||
|
match self.current_state {
|
||||||
|
LedState::On => self.off(),
|
||||||
|
LedState::Off => self.on(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set the state that was given in the 'state' argument.
|
||||||
|
pub fn set_state(&mut self, state: LedState) -> Result<(), ErrorKind> {
|
||||||
|
match (state, self.pull_configuration) {
|
||||||
|
(LedState::Off, PullConfig::PullDown) => { self.set_low() }
|
||||||
|
(LedState::On, PullConfig::PullDown) => { self.set_high() }
|
||||||
|
(LedState::Off, PullConfig::PullUp) => { self.set_high() }
|
||||||
|
(LedState::On, PullConfig::PullUp) => { self.set_low() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
126
src/lib.rs
Normal file
126
src/lib.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
//! Hej och välkommen till denna skit.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
mod errors;
|
||||||
|
pub mod button_state;
|
||||||
|
pub mod button_config;
|
||||||
|
pub mod button_stateful;
|
||||||
|
pub mod led_state;
|
||||||
|
pub mod led_stateful;
|
||||||
|
|
||||||
|
use embedded_hal::digital::{InputPin, OutputPin};
|
||||||
|
use led_state::LedState;
|
||||||
|
use led_stateful::StatefulLed;
|
||||||
|
use button_stateful::StatefulButton;
|
||||||
|
use button_config::ButtonConfig;
|
||||||
|
use crate::button_state::ButtonState;
|
||||||
|
|
||||||
|
/// Pull configuration, used by both StatefulButton and StatefulLed.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum PullConfig {
|
||||||
|
PullUp,
|
||||||
|
PullDown
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a button with built in led.
|
||||||
|
pub struct LedButton<OP, IP> {
|
||||||
|
/// the pin from mcu to LED
|
||||||
|
pub led : StatefulLed<OP>,
|
||||||
|
|
||||||
|
/// the pin from mcu to button
|
||||||
|
pub button : StatefulButton<IP>,
|
||||||
|
|
||||||
|
/// the state of the button, always initialised to NotPressed.
|
||||||
|
pub state : ButtonState
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<OP: OutputPin, IP: InputPin> LedButton<OP, IP> {
|
||||||
|
/// Creates a StatefulLed
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use led_button::led_stateful::StatefulLed;
|
||||||
|
/// use led_button::led_state::LedState;
|
||||||
|
/// use led_button::button_config::Buttonconfig;
|
||||||
|
/// use led_button::PullConfig;
|
||||||
|
///
|
||||||
|
/// // ... init your hal and do a weird a dance..
|
||||||
|
///
|
||||||
|
/// let led1_pin = pins.gpio15.into_push_pull_output();
|
||||||
|
/// let button1_pin = pins.gpio16.into_pull_up_input();
|
||||||
|
///
|
||||||
|
/// let button1 = LedButton::create(
|
||||||
|
/// led1_pin,
|
||||||
|
/// LedState::Off,
|
||||||
|
/// PullConfig::PullDown,
|
||||||
|
/// button1_pin,
|
||||||
|
/// ButtonConfig::default(),
|
||||||
|
/// 274000.0
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// // ... continue doing a weird a dance.
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub fn create(led_pin: OP, led_state: LedState, led_pullconf: PullConfig, button_pin: IP, button_config: ButtonConfig, fn_refresh: f32 ) -> Self {
|
||||||
|
Self {
|
||||||
|
led : StatefulLed::create(led_pin, led_pullconf, led_state),
|
||||||
|
button : StatefulButton::create(button_pin, button_config, fn_refresh),
|
||||||
|
state : ButtonState::NotPressed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get the current state of the LED.
|
||||||
|
pub fn led_state(self) -> LedState {
|
||||||
|
self.led.current_state
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set the LED to "On"-state.
|
||||||
|
pub fn on(&mut self) {
|
||||||
|
let _ = self.led.on();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// set the LED to "Off"-state.
|
||||||
|
pub fn off(&mut self) {
|
||||||
|
let _ = self.led.off();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// toggle the LED.
|
||||||
|
pub fn toggle(&mut self) {
|
||||||
|
let _ = self.led.toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// the current counter value of the button.
|
||||||
|
pub fn counter(self) -> u32 {
|
||||||
|
self.button.counter
|
||||||
|
}
|
||||||
|
|
||||||
|
/// polls the button and read the current state
|
||||||
|
pub fn poll_and_read(&mut self, led_while_pressed: bool) -> ButtonState {
|
||||||
|
self.button.poll();
|
||||||
|
if led_while_pressed { let _ = self.led.on(); }
|
||||||
|
match self.button.read() {
|
||||||
|
ButtonState::Released => {
|
||||||
|
if led_while_pressed { let _ = self.led.off(); }
|
||||||
|
self.state = ButtonState::NotPressed;
|
||||||
|
ButtonState::Released
|
||||||
|
}
|
||||||
|
ButtonState::LongReleased => {
|
||||||
|
if led_while_pressed { let _ = self.led.off(); }
|
||||||
|
self.state = ButtonState::NotPressed;
|
||||||
|
ButtonState::LongReleased
|
||||||
|
}
|
||||||
|
ButtonState::Flank => {
|
||||||
|
self.state = ButtonState::Pressed;
|
||||||
|
ButtonState::Flank
|
||||||
|
}
|
||||||
|
ButtonState::LongFlank => {
|
||||||
|
self.state = ButtonState::LongPress;
|
||||||
|
ButtonState::LongFlank
|
||||||
|
}
|
||||||
|
n => {
|
||||||
|
n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user