|
| 1 | +#![no_std] |
| 2 | +#![no_main] |
| 3 | + |
| 4 | +use core::{fmt::Write, panic::PanicInfo}; |
| 5 | + |
| 6 | +use esp32_hal::{ |
| 7 | + clock_control::sleep, |
| 8 | + dport::Split, |
| 9 | + dprintln, |
| 10 | + gpio::{Event, Floating, InputPin, OutputPin, Pin, Pull, RTCInputPin, RTCOutputPin}, |
| 11 | + interrupt::{Interrupt, InterruptLevel}, |
| 12 | + prelude::*, |
| 13 | + serial::{config::Config, Serial}, |
| 14 | + target, |
| 15 | + timer::Timer, |
| 16 | + Core, |
| 17 | +}; |
| 18 | + |
| 19 | +static SERIAL: CriticalSectionSpinLockMutex< |
| 20 | + Option< |
| 21 | + esp32_hal::serial::Serial< |
| 22 | + esp32::UART0, |
| 23 | + esp32_hal::gpio::Gpio1<esp32_hal::gpio::Unknown>, |
| 24 | + esp32_hal::gpio::Gpio3<esp32_hal::gpio::Unknown>, |
| 25 | + >, |
| 26 | + >, |
| 27 | +> = CriticalSectionSpinLockMutex::new(None); |
| 28 | + |
| 29 | +static GPIO: CriticalSectionSpinLockMutex< |
| 30 | + Option<esp32_hal::gpio::Gpio26<esp32_hal::gpio::RTCInput<Floating>>>, |
| 31 | +> = CriticalSectionSpinLockMutex::new(None); |
| 32 | + |
| 33 | +#[entry] |
| 34 | +fn main() -> ! { |
| 35 | + let dp = target::Peripherals::take().unwrap(); |
| 36 | + |
| 37 | + let (mut dport, dport_clock_control) = dp.DPORT.split(); |
| 38 | + |
| 39 | + let clkcntrl = esp32_hal::clock_control::ClockControl::new( |
| 40 | + dp.RTCCNTL, |
| 41 | + dp.APB_CTRL, |
| 42 | + dport_clock_control, |
| 43 | + esp32_hal::clock_control::XTAL_FREQUENCY_AUTO, |
| 44 | + ) |
| 45 | + .unwrap(); |
| 46 | + |
| 47 | + let (clkcntrl_config, mut watchdog_rtc) = clkcntrl.freeze().unwrap(); |
| 48 | + let (_, _, _, mut watchdog0) = Timer::new(dp.TIMG0, clkcntrl_config); |
| 49 | + let (_, _, _, mut watchdog1) = Timer::new(dp.TIMG1, clkcntrl_config); |
| 50 | + |
| 51 | + watchdog_rtc.disable(); |
| 52 | + watchdog0.disable(); |
| 53 | + watchdog1.disable(); |
| 54 | + |
| 55 | + let gpios = dp.GPIO.split(); |
| 56 | + |
| 57 | + let mut gpio = gpios.gpio26.into_floating_rtc_input(); |
| 58 | + gpio.internal_pull_up(true); |
| 59 | + gpio.enable_hold(false); |
| 60 | + gpio.enable_input(false); |
| 61 | + gpio.rtc_enable_input(true); |
| 62 | + |
| 63 | + gpio.listen_with_options(Event::LowLevel, true, false, true, false, false); |
| 64 | + |
| 65 | + // setup serial controller |
| 66 | + let mut serial: Serial<_, _, _> = Serial::new( |
| 67 | + dp.UART0, |
| 68 | + esp32_hal::serial::Pins { |
| 69 | + tx: gpios.gpio1, |
| 70 | + rx: gpios.gpio3, |
| 71 | + cts: None, |
| 72 | + rts: None, |
| 73 | + }, |
| 74 | + Config::default().baudrate(115_200.Hz()), |
| 75 | + clkcntrl_config, |
| 76 | + &mut dport, |
| 77 | + ) |
| 78 | + .unwrap(); |
| 79 | + |
| 80 | + writeln!(serial, "\n\nESP32 Started\n\n").unwrap(); |
| 81 | + |
| 82 | + (&SERIAL).lock(|val| *val = Some(serial)); |
| 83 | + (&GPIO).lock(|val| *val = Some(gpio)); |
| 84 | + |
| 85 | + interrupt::enable(Interrupt::GPIO_INTR).unwrap(); |
| 86 | + |
| 87 | + // Even though the interrupt is called GPIO_NMI is can be routed to any interrupt level. |
| 88 | + // Using NMI level (7) is in principle a risk for deadlocks because the |
| 89 | + // CriticalSectionSpinLockMutex does not disable the NMI. Therefore using level 5 instead. |
| 90 | + |
| 91 | + // Because the level 5 interrupt clears the interrupt, the regular level 1 handler |
| 92 | + // will not be called. |
| 93 | + // Comment out the next line to test the level 1 handler |
| 94 | + interrupt::enable_with_priority(Core::PRO, Interrupt::GPIO_NMI, InterruptLevel(5)).unwrap(); |
| 95 | + |
| 96 | + let mut x = 0; |
| 97 | + loop { |
| 98 | + x = x + 1; |
| 99 | + (&SERIAL, &GPIO).lock(|serial, gpio| { |
| 100 | + let serial = serial.as_mut().unwrap(); |
| 101 | + let gpio = gpio.as_mut().unwrap(); |
| 102 | + writeln!( |
| 103 | + serial, |
| 104 | + "Loop: {} {} {} {}", |
| 105 | + x, |
| 106 | + gpio.is_high().unwrap(), |
| 107 | + gpio.is_input_high(), |
| 108 | + gpio.rtc_is_input_high() |
| 109 | + ) |
| 110 | + .unwrap(); |
| 111 | + }); |
| 112 | + |
| 113 | + sleep(500.ms()); |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +fn handle_gpio_interrupt() { |
| 118 | + (&GPIO, &SERIAL).lock(|gpio, serial| { |
| 119 | + let gpio = gpio.as_mut().unwrap(); |
| 120 | + let serial = serial.as_mut().unwrap(); |
| 121 | + |
| 122 | + if gpio.is_interrupt_set() || gpio.is_non_maskable_interrupt_set() { |
| 123 | + writeln!( |
| 124 | + serial, |
| 125 | + " Interrupt level: {}, pin state: {}", |
| 126 | + xtensa_lx6::interrupt::get_level(), |
| 127 | + gpio.is_high().unwrap() |
| 128 | + ) |
| 129 | + .unwrap(); |
| 130 | + |
| 131 | + if gpio.is_high().unwrap() { |
| 132 | + gpio.listen_with_options(Event::LowLevel, true, false, true, false, false); |
| 133 | + } else { |
| 134 | + gpio.listen_with_options(Event::HighLevel, true, false, true, false, false); |
| 135 | + }; |
| 136 | + // need to change listen before clearing interrupt, otherwise will fire |
| 137 | + // immediately again. |
| 138 | + gpio.clear_interrupt(); |
| 139 | + } |
| 140 | + }); |
| 141 | +} |
| 142 | + |
| 143 | +#[interrupt] |
| 144 | +fn GPIO_INTR() { |
| 145 | + handle_gpio_interrupt(); |
| 146 | +} |
| 147 | + |
| 148 | +#[interrupt] |
| 149 | +fn GPIO_NMI() { |
| 150 | + handle_gpio_interrupt(); |
| 151 | +} |
| 152 | + |
| 153 | +#[panic_handler] |
| 154 | +fn panic(info: &PanicInfo) -> ! { |
| 155 | + dprintln!("\n\n*** {:?}", info); |
| 156 | + loop {} |
| 157 | +} |
0 commit comments