123 lines
3.2 KiB
Rust
123 lines
3.2 KiB
Rust
#![no_std]
|
|
|
|
extern crate cortex_m;
|
|
extern crate embedded_hal;
|
|
extern crate itsybitsy_m0 as hal;
|
|
extern crate panic_halt;
|
|
|
|
pub use cortex_m::asm::delay;
|
|
pub use hal::entry;
|
|
|
|
use core::fmt::Debug;
|
|
use core::u8;
|
|
pub use embedded_hal::digital::v2::OutputPin;
|
|
use hal::{clock::GenericClockController, pac::Peripherals};
|
|
use lights::{HardwareRgb, Lights};
|
|
|
|
pub fn boot() -> NeopixelLights<
|
|
impl OutputPin<Error = ()>,
|
|
impl OutputPin<Error = ()>,
|
|
impl OutputPin<Error = ()>,
|
|
> {
|
|
let mut peripherals = Peripherals::take().unwrap();
|
|
let _clock = GenericClockController::with_internal_32kosc(
|
|
peripherals.GCLK,
|
|
&mut peripherals.PM,
|
|
&mut peripherals.SYSCTRL,
|
|
&mut peripherals.NVMCTRL,
|
|
);
|
|
|
|
let mut pins = hal::Pins::new(peripherals.PORT);
|
|
|
|
NeopixelLights {
|
|
out: pins.d7.into_push_pull_output(&mut pins.port),
|
|
high_out: pins.d5.into_push_pull_output(&mut pins.port),
|
|
red_led: pins.d13.into_open_drain_output(&mut pins.port),
|
|
debug_light: false,
|
|
}
|
|
}
|
|
|
|
// WS2815 lighting times
|
|
// approx. 20ns per clock cycle;
|
|
// const ZERO_HIGH_CYCLES: u32 = 11; // about 220ns
|
|
// const ONE_HIGH_CYCLES: u32 = 29; // about 580ns
|
|
// const ZERO_LOW_CYCLES: u32 = 29; // about 580ns
|
|
// const ONE_LOW_CYCLES: u32 = 11; // about 220ns
|
|
|
|
// experimentally, there is some unknown overhead
|
|
// but these timings appear to work for me:
|
|
const ZERO_HIGH_CYCLES: u32 = 2;
|
|
const ONE_HIGH_CYCLES: u32 = 9;
|
|
const ZERO_LOW_CYCLES: u32 = 10;
|
|
const ONE_LOW_CYCLES: u32 = 10;
|
|
const LATCH_CYCLES: u32 = 15000; // about 300us
|
|
|
|
pub struct NeopixelLights<T: OutputPin, U: OutputPin, V: OutputPin> {
|
|
pub out: T,
|
|
pub high_out: U,
|
|
pub red_led: V,
|
|
pub debug_light: bool,
|
|
}
|
|
|
|
impl<T: OutputPin, U: OutputPin, V: OutputPin> NeopixelLights<T, U, V>
|
|
where
|
|
T::Error: Debug,
|
|
U::Error: Debug,
|
|
V::Error: Debug,
|
|
{
|
|
#[inline]
|
|
pub fn write(&mut self, bit: bool) {
|
|
self.high_out.set_high().unwrap();
|
|
delay(if bit {
|
|
ONE_HIGH_CYCLES
|
|
} else {
|
|
ZERO_HIGH_CYCLES
|
|
});
|
|
self.high_out.set_low().unwrap();
|
|
delay(if bit { ONE_LOW_CYCLES } else { ZERO_LOW_CYCLES });
|
|
}
|
|
|
|
#[inline]
|
|
pub fn byte(&mut self, byte: u8) {
|
|
self.write(byte & 0x80 != 0);
|
|
self.write(byte & 0x40 != 0);
|
|
self.write(byte & 0x20 != 0);
|
|
self.write(byte & 0x10 != 0);
|
|
self.write(byte & 0x08 != 0);
|
|
self.write(byte & 0x04 != 0);
|
|
self.write(byte & 0x02 != 0);
|
|
self.write(byte & 0x01 != 0);
|
|
delay(5);
|
|
}
|
|
|
|
/// Blink light, probably to indicate a loop is still running
|
|
pub fn heartbeat(&mut self) {
|
|
if self.debug_light {
|
|
self.red_led.set_low().unwrap();
|
|
} else {
|
|
self.red_led.set_high().unwrap();
|
|
}
|
|
self.debug_light = !self.debug_light;
|
|
}
|
|
}
|
|
|
|
impl<T: OutputPin, U: OutputPin, V: OutputPin> Lights for NeopixelLights<T, U, V>
|
|
where
|
|
T::Error: Debug,
|
|
U::Error: Debug,
|
|
V::Error: Debug,
|
|
{
|
|
type Pixel = HardwareRgb;
|
|
|
|
#[inline]
|
|
fn render(&mut self, rgb: &HardwareRgb) {
|
|
// GRB pixel order
|
|
self.byte(rgb.1);
|
|
self.byte(rgb.0);
|
|
self.byte(rgb.2);
|
|
}
|
|
#[inline]
|
|
fn latch(&mut self) {
|
|
delay(LATCH_CYCLES);
|
|
}
|
|
}
|