diff --git a/pico/src/input.rs b/pico/src/input.rs new file mode 100644 index 0000000..45f187e --- /dev/null +++ b/pico/src/input.rs @@ -0,0 +1,75 @@ +use as5600::As5600; +use embassy_executor::Spawner; +use embassy_rp::{ + Peri, + gpio::{AnyPin, Input}, + i2c::{Config, I2c}, + peripherals::{I2C0, PIN_4, PIN_5}, +}; +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, channel::Channel, mutex::Mutex}; +use embassy_time::{Duration, Timer}; + +use crate::{Irqs, unwrap}; + +/// Button input notification channel +pub static INPUT: Channel = Channel::new(); + +#[embassy_executor::task] +/// Polls a single pin for falling edge (assumes buttons are pulled up and shorted when pressed) +/// Sends [INPUT] with given id +pub async fn button_poll_task(mut button: Input<'static>, id: u8) { + loop { + button.wait_for_falling_edge().await; + INPUT.send(id).await; + } +} + +pub static ANGLE: Mutex = Mutex::new(0); + +#[embassy_executor::task] +pub async fn rotation_read_task( + i2c: Peri<'static, I2C0>, + scl: Peri<'static, PIN_5>, + sda: Peri<'static, PIN_4>, +) { + let i2c = I2c::new_async(i2c, scl, sda, Irqs, Config::default()); + let mut as5600 = As5600::new(i2c); + + loop { + let angle = as5600.angle().unwrap_or(0); + let mut locked = ANGLE.lock().await; + let old = *locked as i32; + *locked = angle; + let left_diff = old - angle as i32; + let right_diff = angle as i32 - old; + let diff = if left_diff.abs() < right_diff.abs() { + left_diff + } else { + right_diff + }; + Timer::after(Duration::from_millis(50)).await; + } +} + +pub struct InputConfig { + pub i2c0: Peri<'static, I2C0>, + pub scl: Peri<'static, PIN_5>, + pub sda: Peri<'static, PIN_4>, + + pub button_pins: [(Peri<'static, AnyPin>, u8); 4], +} + +pub fn setup(spawner: Spawner, config: InputConfig) { + spawner.spawn(unwrap!(rotation_read_task( + config.i2c0, + config.scl, + config.sda, + ))); + + for (pin, id) in config.button_pins { + spawner.spawn(unwrap!(button_poll_task( + Input::new(pin, embassy_rp::gpio::Pull::Up), + id, + ))); + } +} diff --git a/pico/src/main.rs b/pico/src/main.rs index 0cbb723..bef0558 100644 --- a/pico/src/main.rs +++ b/pico/src/main.rs @@ -1,40 +1,36 @@ #![no_std] #![no_main] -use core::net::SocketAddrV4; use core::str::FromStr; -use embassy_futures::join::join; -use embassy_net::tcp::{TcpReader, TcpSocket, TcpWriter}; +use embassy_net::tcp::{TcpReader, TcpWriter}; use embassy_rp::multicore::{Stack, spawn_core1}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; +use embassy_sync::signal::Signal; use embedded_io::Write; use owned_str::OwnedStr; -use crate::buffer::{WriteBuffer, wait_for_config}; -use crate::screen::{SCREEN_BUFFER, lcd_display_task}; +use crate::buffer::WriteBuffer; +use crate::input::{ANGLE, INPUT, InputConfig}; +use crate::net::network_setup_task; +use crate::screen::lcd_display_task; use ag_lcd::{Blink, Cursor, Display, LcdDisplay}; -use as5600::As5600; -use cyw43::{JoinOptions, aligned_bytes}; -use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; use defmt::*; -use embassy_executor::{Executor, Spawner}; -use embassy_net::StackResources; -use embassy_rp::clocks::RoscRng; -use embassy_rp::gpio::{Input, Level, Output}; -use embassy_rp::i2c::{self, Config, I2c}; -use embassy_rp::peripherals::{DMA_CH0, I2C0, PIN_4, PIN_5, PIN_23, PIN_24, PIN_25, PIN_29, PIO0}; -use embassy_rp::pio::{InterruptHandler, Pio}; -use embassy_rp::{Peri, bind_interrupts, dma}; +use embassy_executor::Executor; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::i2c::{self}; +use embassy_rp::peripherals::{DMA_CH0, I2C0, PIO0}; +use embassy_rp::pio::InterruptHandler; +use embassy_rp::{bind_interrupts, dma}; use embassy_rp::{peripherals::USB, usb}; -use embassy_time::{Delay, Duration, Timer}; +use embassy_time::Delay; use static_cell::StaticCell; -use ufmt::uwrite; use {defmt_rtt as _, panic_probe as _}; mod buffer; +mod input; +mod net; mod screen; const WIFI_NETWORK: &str = "flamme"; @@ -49,161 +45,12 @@ bind_interrupts!(struct Irqs { I2C0_IRQ => i2c::InterruptHandler; }); -#[embassy_executor::task] -async fn cyw43_task( - runner: cyw43::Runner<'static, cyw43::SpiBus, PioSpi<'static, PIO0, 0>>>, -) -> ! { - runner.run().await -} #[embassy_executor::task] async fn logger_task(usb: embassy_rp::Peri<'static, embassy_rp::peripherals::USB>) { let driver = embassy_rp::usb::Driver::new(usb, Irqs); embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver); } -#[embassy_executor::task] -async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { - runner.run().await -} - -/// Button input notification channel -static INPUT: Channel = Channel::new(); - -#[embassy_executor::task] -/// Polls a single pin for falling edge (assumes buttons are pulled up and shorted when pressed) -/// Sends [INPUT] with given id -async fn button_poll_task(mut button: Input<'static>, id: u8) { - loop { - button.wait_for_falling_edge().await; - INPUT.send(id).await; - } -} - -static ANGLE: Mutex = Mutex::new(0); - -#[embassy_executor::task] -async fn rotation_read_task( - i2c: Peri<'static, I2C0>, - scl: Peri<'static, PIN_5>, - sda: Peri<'static, PIN_4>, -) { - let i2c = I2c::new_async(i2c, scl, sda, Irqs, Config::default()); - let mut as5600 = As5600::new(i2c); - - loop { - let angle = as5600.angle().unwrap_or(0); - *ANGLE.lock().await = angle; - Timer::after(Duration::from_millis(50)).await; - } -} - -#[embassy_executor::task] -async fn network_setup_task( - pin23: Peri<'static, PIN_23>, - pin25: Peri<'static, PIN_25>, - pio: Peri<'static, PIO0>, - pin24: Peri<'static, PIN_24>, - pin29: Peri<'static, PIN_29>, - dma: Peri<'static, DMA_CH0>, - spawner: Spawner, -) { - let fw = aligned_bytes!("../firmware/43439A0.bin"); - let clm = aligned_bytes!("../firmware/43439A0_clm.bin"); - let nvram = aligned_bytes!("../firmware/nvram_rp2040.bin"); - let mut rng = RoscRng; - - // To make flashing faster for development, you may want to flash the firmwares independently - // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: - // probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000 - // probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000 - //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; - //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; - - let pwr = Output::new(pin23, Level::Low); - let cs = Output::new(pin25, Level::High); - let mut pio = Pio::new(pio, Irqs); - let spi = PioSpi::new( - &mut pio.common, - pio.sm0, - DEFAULT_CLOCK_DIVIDER, - pio.irq0, - cs, - pin24, - pin29, - dma::Channel::new(dma, Irqs), - ); - - static STATE: StaticCell = StaticCell::new(); - let state = STATE.init(cyw43::State::new()); - let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw, nvram).await; - spawner.spawn(unwrap!(cyw43_task(runner))); - - control.init(clm).await; - control - .set_power_management(cyw43::PowerManagementMode::PowerSave) - .await; - - static RESOURCES: StaticCell> = StaticCell::new(); - let (stack, runner) = embassy_net::new( - net_device, - embassy_net::Config::dhcpv4(Default::default()), - RESOURCES.init(StackResources::new()), - rng.next_u64(), - ); - spawner.spawn(unwrap!(net_task(runner))); - uwrite!(SCREEN_BUFFER.lock().await.line1(), "con"); - - while let Err(err) = control - .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) - .await - { - let num = match err { - cyw43::JoinError::AuthenticationFailure => 200, - cyw43::JoinError::JoinFailure(e) => e, - cyw43::JoinError::NetworkNotFound => 201, - }; - uwrite!(SCREEN_BUFFER.lock().await.line1(), "join {}", num); - } - - // uwrite!(lcd, "link."); - uwrite!(SCREEN_BUFFER.lock().await.line1(), "link."); - stack.wait_link_up().await; - - // lcd.clear(); - // // lcd.home(); - // uwrite!(lcd, "dhcp."); - let mut buf = SCREEN_BUFFER.lock().await; - buf.clear(); - uwrite!(buf.line1(), "dhcp."); - drop(buf); - stack.wait_config_up().await; - let cfg = wait_for_config(stack).await; - let local_addr = cfg.address.address(); - info!("IP address: {:?}", local_addr); - - let host_addr = embassy_net::Ipv4Address::from_str(TARGET_IP).unwrap(); - // let target = UdpMetadata::from(SocketAddrV4::new(host_addr, 7070)); - let mut rx_buffer = [0; 4096]; - // let mut rx_meta = [PacketMetadata::EMPTY; 16]; - let mut tx_buffer = [0; 4096]; - let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); - socket.set_timeout(Some(Duration::from_secs(10))); - if let Err(e) = socket - .connect(SocketAddrV4::new(host_addr, TARGET_PORT)) - .await - { - // uwrite!(lcd, "tcperr"); - uwrite!(SCREEN_BUFFER.lock().await.line1(), "tcperr"); - error!("tcp connect error: {}", e); - } else { - // uwrite!(lcd, "tcpok"); - uwrite!(SCREEN_BUFFER.lock().await.line1(), "tcpok"); - } - - let (read, write) = socket.split(); - - join(tcp_read_loop(read), tcp_write_loop(write)).await; -} enum QuestionType { Choice, @@ -216,7 +63,11 @@ struct QuestionData { points: i32, } -async fn tcp_read_loop(mut read: TcpReader<'_>) { +static QUESTION: Mutex> = Mutex::new(None); +static QUESTION_UPDATE: Signal = Signal::new(); +static WHEEL_VALUE: Mutex = Mutex::new(0); + +pub async fn tcp_read_loop(mut read: TcpReader<'_>) { let mut buf = [0u8; 1024]; while let Ok(len) = read.read(&mut buf).await { @@ -228,6 +79,7 @@ async fn tcp_read_loop(mut read: TcpReader<'_>) { }; let mut counter = 0; let mut question_data = None; + let mut future_wheel = 0; for line in str.lines() { if line == "$$" { counter = 1; @@ -266,6 +118,14 @@ async fn tcp_read_loop(mut read: TcpReader<'_>) { } } + match q_type { + QuestionType::Numeric { min, max } => { + let diff = max - min; + future_wheel = min + diff / 2; + } + _ => {} + } + question_data = Some(QuestionData { text: OwnedStr::new(), q_type, @@ -279,16 +139,22 @@ async fn tcp_read_loop(mut read: TcpReader<'_>) { counter = 0; } } + + if let Some(question_data) = question_data { + *QUESTION.lock().await = Some(question_data); + *WHEEL_VALUE.lock().await = future_wheel; + QUESTION_UPDATE.signal(()); + } } } -async fn tcp_write_loop(mut write: TcpWriter<'_>) { +pub async fn tcp_write_loop(mut write: TcpWriter<'_>) { let mut buffer = WriteBuffer::<256>::new(); loop { let data = INPUT.receive().await; let angle = *ANGLE.lock().await; core::writeln!(buffer, "button={} angle={}", data, angle).ok(); - write.write(&*buffer).await.ok(); + write.write(&buffer).await.ok(); buffer.clear(); } } @@ -301,13 +167,6 @@ static EXECUTOR1: StaticCell = StaticCell::new(); fn main() -> ! { let p = embassy_rp::init(Default::default()); - let pin23 = p.PIN_23; - let pin24 = p.PIN_24; - let pin25 = p.PIN_25; - let pin29 = p.PIN_29; - let pio = p.PIO0; - let dma = p.DMA_CH0; - let lcd: LcdDisplay<_, _> = LcdDisplay::new( Output::new(p.PIN_15, Level::Low), Output::new(p.PIN_14, Level::Low), @@ -343,65 +202,21 @@ fn main() -> ! { executor0.run(|spawner| { spawner.spawn(unwrap!(logger_task(p.USB))); spawner.spawn(unwrap!(network_setup_task( - pin23, pin25, pio, pin24, pin29, dma, spawner - ))); - spawner.spawn(unwrap!(rotation_read_task(p.I2C0, p.PIN_5, p.PIN_4,))); - - spawner.spawn(unwrap!(button_poll_task( - Input::new(p.PIN_18, embassy_rp::gpio::Pull::Up), - 1, - ))); - spawner.spawn(unwrap!(button_poll_task( - Input::new(p.PIN_19, embassy_rp::gpio::Pull::Up), - 2, - ))); - spawner.spawn(unwrap!(button_poll_task( - Input::new(p.PIN_20, embassy_rp::gpio::Pull::Up), - 3, - ))); - spawner.spawn(unwrap!(button_poll_task( - Input::new(p.PIN_21, embassy_rp::gpio::Pull::Up), - 4, + p.PIN_23, p.PIN_25, p.PIO0, p.PIN_24, p.PIN_29, p.DMA_CH0, spawner ))); + crate::input::setup( + spawner, + InputConfig { + i2c0: p.I2C0, + scl: p.PIN_5, + sda: p.PIN_4, + button_pins: [ + (p.PIN_18.into(), 1), + (p.PIN_19.into(), 2), + (p.PIN_20.into(), 3), + (p.PIN_21.into(), 4), + ], + }, + ) }); } - -// async fn mainz(spawner: Spawner) { -// let delay = Duration::from_millis(100); -// loop { -// // let angle = as5600.angle().unwrap_or(0); -// // { -// // use embedded_io::Write; -// // let _ = core::writeln!( -// // &mut buffer, -// // "in1={} in2={} in3={} in4={} angle={}", -// // in1, -// // in2, -// // in3, -// // in4, -// // angle -// // ); -// // } -// // { -// // let _ = write.write(&*buffer).await; -// // buffer.clear(); -// // } -// if read.read_ready().unwrap() { -// let mut rx_buffer = [0; 4096]; -// let n = read.read(&mut rx_buffer).await.unwrap(); -// if n > 0 { -// lcd.clear(); -// lcd.home(); -// let s = core::str::from_utf8(&rx_buffer[..n]).unwrap_or(""); -// let npos = s.find('\n').unwrap_or(n); -// let display_text = &s[..npos]; -// lcd.write_str(display_text).ok(); - -// Timer::after(Duration::from_micros(100)).await; -// lcd.set_position(0, 1); -// lcd.write_str(&s[npos..]); -// } -// } -// Timer::after(delay).await; -// } -// } diff --git a/pico/src/net.rs b/pico/src/net.rs new file mode 100644 index 0000000..1aaec09 --- /dev/null +++ b/pico/src/net.rs @@ -0,0 +1,135 @@ +use core::net::SocketAddrV4; +use core::str::FromStr; +use embassy_futures::join::join; +use embassy_net::tcp::TcpSocket; + +use crate::buffer::wait_for_config; +use crate::screen::SCREEN_BUFFER; +use crate::{ + Irqs, TARGET_IP, TARGET_PORT, WIFI_NETWORK, WIFI_PASSWORD, tcp_read_loop, tcp_write_loop, +}; +use cyw43::{JoinOptions, aligned_bytes}; +use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; +use defmt::*; +use embassy_executor::Spawner; +use embassy_net::StackResources; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::{DMA_CH0, PIN_23, PIN_24, PIN_25, PIN_29, PIO0}; +use embassy_rp::pio::Pio; +use embassy_rp::{Peri, dma}; +use embassy_time::Duration; +use static_cell::StaticCell; +use ufmt::uwrite; + +#[embassy_executor::task] +async fn cyw43_task( + runner: cyw43::Runner<'static, cyw43::SpiBus, PioSpi<'static, PIO0, 0>>>, +) -> ! { + runner.run().await +} + +#[embassy_executor::task] +async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { + runner.run().await +} + +#[embassy_executor::task] +pub async fn network_setup_task( + pin23: Peri<'static, PIN_23>, + pin25: Peri<'static, PIN_25>, + pio: Peri<'static, PIO0>, + pin24: Peri<'static, PIN_24>, + pin29: Peri<'static, PIN_29>, + dma: Peri<'static, DMA_CH0>, + spawner: Spawner, +) { + let fw = aligned_bytes!("../firmware/43439A0.bin"); + let clm = aligned_bytes!("../firmware/43439A0_clm.bin"); + let nvram = aligned_bytes!("../firmware/nvram_rp2040.bin"); + let mut rng = RoscRng; + + // To make flashing faster for development, you may want to flash the firmwares independently + // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: + // probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000 + // probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000 + //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; + //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; + + let pwr = Output::new(pin23, Level::Low); + let cs = Output::new(pin25, Level::High); + let mut pio = Pio::new(pio, Irqs); + let spi = PioSpi::new( + &mut pio.common, + pio.sm0, + DEFAULT_CLOCK_DIVIDER, + pio.irq0, + cs, + pin24, + pin29, + dma::Channel::new(dma, Irqs), + ); + + static STATE: StaticCell = StaticCell::new(); + let state = STATE.init(cyw43::State::new()); + let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw, nvram).await; + spawner.spawn(unwrap!(cyw43_task(runner))); + + control.init(clm).await; + control + .set_power_management(cyw43::PowerManagementMode::PowerSave) + .await; + + static RESOURCES: StaticCell> = StaticCell::new(); + let (stack, runner) = embassy_net::new( + net_device, + embassy_net::Config::dhcpv4(Default::default()), + RESOURCES.init(StackResources::new()), + rng.next_u64(), + ); + spawner.spawn(unwrap!(net_task(runner))); + uwrite!(SCREEN_BUFFER.lock().await.line1(), "con"); + + while let Err(err) = control + .join(WIFI_NETWORK, JoinOptions::new(WIFI_PASSWORD.as_bytes())) + .await + { + let num = match err { + cyw43::JoinError::AuthenticationFailure => 200, + cyw43::JoinError::JoinFailure(e) => e, + cyw43::JoinError::NetworkNotFound => 201, + }; + uwrite!(SCREEN_BUFFER.lock().await.line1(), "join {}", num); + } + + uwrite!(SCREEN_BUFFER.lock().await.line1(), "link."); + stack.wait_link_up().await; + + let mut buf = SCREEN_BUFFER.lock().await; + buf.clear(); + uwrite!(buf.line1(), "dhcp."); + drop(buf); + stack.wait_config_up().await; + let cfg = wait_for_config(stack).await; + let local_addr = cfg.address.address(); + info!("IP address: {:?}", local_addr); + + let host_addr = embassy_net::Ipv4Address::from_str(TARGET_IP).unwrap(); + let mut rx_buffer = [0; 4096]; + let mut tx_buffer = [0; 4096]; + let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); + socket.set_timeout(Some(Duration::from_secs(10))); + if let Err(e) = socket + .connect(SocketAddrV4::new(host_addr, TARGET_PORT)) + .await + { + uwrite!(SCREEN_BUFFER.lock().await.line1(), "tcperr"); + error!("tcp connect error: {}", e); + } else { + uwrite!(SCREEN_BUFFER.lock().await.line1(), "tcpok"); + } + + let (read, write) = socket.split(); + + join(tcp_read_loop(read), tcp_write_loop(write)).await; +} diff --git a/pico/src/screen.rs b/pico/src/screen.rs index fb97e98..201e1cf 100644 --- a/pico/src/screen.rs +++ b/pico/src/screen.rs @@ -73,49 +73,6 @@ pub static SCREEN_BUFFER: Mutex = Mutex:: line2_ptr: 0, }); -struct LcdWriter; -pub type LcdLine1 = LcdWriter<0>; -pub type LcdLine2 = LcdWriter<1>; - -impl uWrite for LcdWriter { - type Error = Infallible; - - fn write_char(&mut self, c: char) -> Result<(), Self::Error> { - block_on(async { - let mut buffer = SCREEN_BUFFER.lock().await; - if LINE == 0 { - let ptr = buffer.line1_ptr as usize; - buffer.line1[ptr] = c as u8; - buffer.line1_ptr = (buffer.line1_ptr + 1) % 16; - } else { - let ptr = buffer.line2_ptr as usize; - buffer.line2[ptr] = c as u8; - buffer.line2_ptr = (buffer.line2_ptr + 1) % 16; - } - LCD_UPDATE.signal(()); - }); - Ok(()) - } - fn write_str(&mut self, s: &str) -> Result<(), Self::Error> { - block_on(async { - let mut buffer = SCREEN_BUFFER.lock().await; - for c in s.chars() { - if LINE == 0 { - let ptr = buffer.line1_ptr as usize; - buffer.line1[ptr] = c as u8; - buffer.line1_ptr = (buffer.line1_ptr + 1) % 16; - } else { - let ptr = buffer.line2_ptr as usize; - buffer.line2[ptr] = c as u8; - buffer.line2_ptr = (buffer.line2_ptr + 1) % 16; - } - } - LCD_UPDATE.signal(()); - }); - Ok(()) - } -} - static LCD_UPDATE: Signal = Signal::new(); #[embassy_executor::task]