1#![no_std]
2#![allow(clippy::tabs_in_doc_comments)]
3#![warn(unreachable_pub)]
4#![cfg_attr(all(doc, nightly), feature(doc_auto_cfg))]
5#![allow(deprecated)]
6#![cfg_attr(
7 all(feature = "bluescreen-message-nightly", nightly),
8 feature(panic_info_message)
9)]
10
11#![doc = document_features::document_features!()]
109
110#[cfg(feature = "bluescreen")]
111use core::fmt::Write;
112pub use cortex_m;
113#[cfg(feature = "neopixel")]
114use edgebadge::gpio::v2::PA15;
115use edgebadge::{
116 gpio,
117 gpio::{v2::PA23, *},
118 hal, pac,
119 prelude::*,
120 Pins
121};
122#[cfg(feature = "bluescreen")]
123use embedded_graphics::{
124 mono_font::{ascii::FONT_6X10, MonoTextStyle},
125 prelude::*,
126 text::Text
127};
128#[cfg(feature = "neopixel")]
129use embedded_hal::digital::v1_compat::OldOutputPin;
130#[cfg(feature = "neopixel")]
131use hal::timer::SpinTimer;
132use hal::{clock::GenericClockController, pwm::Pwm2, sercom::SPIMaster4};
133#[cfg(any(feature = "usb", feature = "time"))]
134use pac::interrupt;
135use pac::{CorePeripherals, Peripherals};
136#[cfg(feature = "neopixel")]
137use smart_leds_trait::SmartLedsWrite;
138use st7735_lcd::ST7735;
139#[cfg(feature = "neopixel")]
140use ws2812::Ws2812;
141#[cfg(feature = "neopixel")]
142use ws2812_timer_delay as ws2812;
143
144pub mod time;
145
146pub mod buttons;
148use buttons::Buttons;
149
150pub mod prelude {
151 pub use cortex_m_rt::entry;
152 pub use edgebadge::prelude::{
153 _embedded_hal_blocking_delay_DelayMs, _embedded_hal_blocking_delay_DelayUs
154 };
155 #[cfg(feature = "neopixel")]
156 pub use smart_leds_trait::SmartLedsWrite;
157}
158
159#[cfg(feature = "usb")]
160pub mod usb;
161#[cfg(feature = "usb")]
162use usb::UsbBuilder;
163
164#[cfg(feature = "flash")]
165mod flash;
166#[doc(hidden)] #[cfg(feature = "flash")]
168pub use flash::Flash;
169
170#[cfg(feature = "pwm_sound")]
171mod sound;
172#[cfg(feature = "pwm_sound")]
173pub use sound::PwmSound;
174
175pub type Color = embedded_graphics::pixelcolor::Rgb565;
177pub type Backlight = Pwm2<gpio::v2::PA01>;
179pub type Display = ST7735<
183 SPIMaster4<
184 hal::sercom::Sercom4Pad2<Pb14<PfC>>,
185 hal::sercom::Sercom4Pad3<Pb15<PfC>>,
186 hal::sercom::Sercom4Pad1<Pb13<PfC>>
187 >,
188 Pb5<Output<PushPull>>,
189 Pa0<Output<PushPull>>
190>;
191pub type Delay = edgebadge::delay::Delay;
192#[cfg(feature = "neopixel")]
193pub type NeoPixel = Ws2812<
199 SpinTimer,
200 OldOutputPin<edgebadge::gpio::Pin<PA15, gpio::v2::Output<gpio::v2::PushPull>>>
201>;
202#[cfg(feature = "neopixel")]
203pub type NeoPixelColor = <NeoPixel as SmartLedsWrite>::Color;
205
206#[cfg(all(feature = "bluescreen-message-nightly", not(feature = "bluescreen")))]
207core::compile_error!(
208 "bluescreen-message-nightly feature is enbaled but bluescreen feature is disable"
209);
210#[cfg(all(feature = "bluescreen-message-nightly", not(nightly)))]
211build_alert::yellow! {"
212WARNING:
213 bluescreen-message-nightly feature is enabled, but nigthly toolchain is not used!
214 This feature has no effect if stable toolchain is used
215"}
216
217pub struct Led {
219 pin: Pin<PA23, Output<PushPull>>
220}
221
222impl Led {
223 pub fn off(&mut self) -> Result<(), ()> {
224 self.pin.set_low()
225 }
226
227 pub fn on(&mut self) -> Result<(), ()> {
228 self.pin.set_high()
229 }
230}
231
232#[non_exhaustive]
236pub struct PyBadge {
237 pub backlight: Backlight,
238 pub display: Display,
239 pub buttons: Buttons,
240 pub red_led: Led,
241 pub delay: Delay,
242 #[cfg(feature = "neopixel")]
243 pub neopixel: NeoPixel,
244 #[doc(hidden)] #[cfg(feature = "flash")]
246 pub flash: Flash,
247 #[cfg(feature = "pwm_sound")]
248 pub speaker: PwmSound,
249 #[cfg(feature = "usb")]
250 pub usb_builder: UsbBuilder
251}
252
253impl PyBadge {
254 pub fn take() -> Result<PyBadge, ()> {
258 let mut peripherals = Peripherals::take().ok_or(())?;
259 #[allow(unused_mut)] let mut core = CorePeripherals::take().ok_or(())?;
261 let mut clocks = GenericClockController::with_internal_32kosc(
262 peripherals.GCLK,
263 &mut peripherals.MCLK,
264 &mut peripherals.OSC32KCTRL,
265 &mut peripherals.OSCCTRL,
266 &mut peripherals.NVMCTRL
267 );
268 let mut pins = Pins::new(peripherals.PORT).split();
269 let mut delay = hal::delay::Delay::new(core.SYST, &mut clocks);
270
271 let (display, backlight) = pins.display.init(
274 &mut clocks,
275 peripherals.SERCOM4,
276 &mut peripherals.MCLK,
277 peripherals.TC2,
278 &mut delay,
279 &mut pins.port
280 )?;
281
282 let buttons = {
284 let latch = pins.buttons.latch.into_push_pull_output(&mut pins.port);
285 let data_in = pins.buttons.data_in.into_floating_input(&mut pins.port);
286 let clock = pins.buttons.clock.into_push_pull_output(&mut pins.port);
287 Buttons {
288 current_state: 0,
289 last_state: 0,
290 latch,
291 data_in,
292 clock
293 }
294 };
295
296 let red_led = {
298 let mut led = Led {
299 pin: pins.led_pin.into_push_pull_output(&mut pins.port)
300 };
301 led.off()?;
302 led
303 };
304
305 #[cfg(feature = "neopixel")]
307 let neopixel = {
308 let timer = SpinTimer::new(4);
309 pins.neopixel.init(timer, &mut pins.port)
310 };
311
312 #[cfg(feature = "flash")]
314 let flash = flash::Flash::init(
315 pins.flash,
316 &mut peripherals.MCLK,
317 peripherals.QSPI,
318 &mut delay
319 );
320
321 #[cfg(any(feature = "pwm_sound", feature = "time"))]
324 let tc4_tc5 = {
325 let gclk = clocks.gclk1();
326 clocks.tc4_tc5(&gclk).unwrap()
327 };
328
329 #[cfg(feature = "pwm_sound")]
332 let speaker = {
333 let enable_pin = pins.speaker.enable.into_push_pull_output(&mut pins.port);
334 let speaker_pin = pins.speaker.speaker.into_push_pull_output(&mut pins.port);
335 let counter = edgebadge::thumbv7em::timer::TimerCounter::tc4_(
336 &tc4_tc5,
337 peripherals.TC4,
338 &mut peripherals.MCLK
339 );
340 sound::PwmSound::init(enable_pin, speaker_pin, counter)
341 };
342
343 #[cfg(feature = "time")]
346 {
347 let counter = edgebadge::thumbv7em::timer::TimerCounter::tc5_(
348 &tc4_tc5,
349 peripherals.TC5,
350 &mut peripherals.MCLK
351 );
352 time::init_counter(counter);
353 unsafe {
354 core.NVIC.set_priority(interrupt::TC5, 0);
356 }
357 };
358
359 #[cfg(feature = "usb")]
361 let usb_builder = {
362 unsafe {
363 core.NVIC.set_priority(interrupt::USB_OTHER, 1);
364 core.NVIC.set_priority(interrupt::USB_TRCPT0, 1);
365 core.NVIC.set_priority(interrupt::USB_TRCPT1, 1);
366 }
367 UsbBuilder {
368 usb_vid: 0x16c0,
369 usb_pid: 0x27dd,
370 manufacturer: "Fake company",
371 product: "Serial port",
372 serial_number: "Test",
373 pins: pins.usb,
374 peripherals: peripherals.USB,
375 clocks,
376 mclk: peripherals.MCLK
377 }
378 };
379
380 Ok(PyBadge {
381 backlight,
382 display,
383 buttons,
384 red_led,
385 #[cfg(feature = "neopixel")]
386 neopixel,
387 #[cfg(feature = "flash")]
388 flash,
389 #[cfg(feature = "pwm_sound")]
390 speaker,
391 #[cfg(feature = "usb")]
392 usb_builder,
393 delay
394 })
395 }
396}
397
398#[inline(never)]
399#[panic_handler]
400#[allow(unused_variables)] fn panic(panic_info: &core::panic::PanicInfo) -> ! {
402 let mut peripherals = unsafe { crate::pac::Peripherals::steal() };
404 let mut pins = Pins::new(peripherals.PORT).split();
405 let mut led = pins.led_pin.into_push_pull_output(&mut pins.port);
406 led.set_high().ok();
407
408 let core = unsafe { CorePeripherals::steal() };
410 let mut clocks = GenericClockController::with_internal_32kosc(
411 peripherals.GCLK,
412 &mut peripherals.MCLK,
413 &mut peripherals.OSC32KCTRL,
414 &mut peripherals.OSCCTRL,
415 &mut peripherals.NVMCTRL
416 );
417
418 let mut delay = hal::delay::Delay::new(core.SYST, &mut clocks);
419 let mut speaker_enable = pins.speaker.enable.into_push_pull_output(&mut pins.port);
420 let mut speaker = pins.speaker.speaker.into_push_pull_output(&mut pins.port);
421
422 #[cfg(feature = "bluescreen")]
423 {
424 let dislpay = pins
425 .display
426 .init(
427 &mut clocks,
428 peripherals.SERCOM4,
429 &mut peripherals.MCLK,
430 peripherals.TC2,
431 &mut delay,
432 &mut pins.port
433 )
434 .ok()
435 .map(|(display, _backlight)| display);
436 if let Some(mut display) = dislpay {
437 display.clear(Color::BLUE).unwrap();
438 let mut output = heapless::String::<1024>::new();
439 writeln!(output, "program panicked at\n").ok();
440 if let Some(location) = panic_info.location() {
441 writeln!(
442 output,
443 "{}:{}:{}",
444 location.file(),
445 location.line(),
446 location.column()
447 )
448 .ok();
449 }
450 #[cfg(all(feature = "bluescreen-message-nightly", nightly))]
451 if let Some(message) = panic_info.message() {
452 writeln!(output, "{message}").ok();
453 }
454 let old_output = output;
456 let mut output = heapless::String::<1024>::new();
457 let mut i: usize = 0;
458 for char in old_output.chars() {
459 i += 1;
460 if char == '\n' {
461 i = 0;
462 }
463 if i >= 26 {
464 i = 0;
465 writeln!(output).ok();
466 }
467 write!(output, "{char}").ok();
468 }
469 let style = MonoTextStyle::new(&FONT_6X10, Color::WHITE);
470 Text::new(&output, Point::new(5, 20), style)
471 .draw(&mut display)
472 .ok();
473 }
474 }
475
476 let mut i = 0_u8;
477 loop {
478 led.toggle();
479 if i <= 8 && cfg!(feature = "beep_panic") {
481 speaker_enable.toggle();
482 i += 1
483 } else {
484 speaker_enable.set_low().ok();
485 }
486 for _ in 0..100 {
487 delay.delay_ms(2_u8);
488 speaker.toggle();
489 }
490 }
491}