Compare commits
8 commits
Author | SHA1 | Date | |
---|---|---|---|
Gabe Venberg | c726ab4aa7 | ||
Gabe Venberg | 6f6ecc833e | ||
Gabe Venberg | 8bbc5a6c43 | ||
Gabe Venberg | c1e16006f3 | ||
Gabe Venberg | 188fb05b25 | ||
Gabe Venberg | 24be0a8f2a | ||
Gabe Venberg | 910010ba22 | ||
Gabe Venberg | eb0bda3300 |
|
@ -7,7 +7,10 @@ license = "MIT OR Apache-2.0"
|
|||
[dependencies]
|
||||
cortex-m = {version = "0.7"}
|
||||
cortex-m-rt = "0.7"
|
||||
cortex-m-rtic = "1.1.4"
|
||||
embedded-hal = { version = "0.2.5", features = ["unproven"] }
|
||||
embedded-alloc = "0.5.1"
|
||||
rp2040-monotonic = "1.3.0"
|
||||
|
||||
defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
|
@ -17,7 +20,8 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
|
|||
rp-pico = "0.8"
|
||||
max31855 = "0.1.0"
|
||||
hd44780-driver = "0.4.0"
|
||||
embedded-alloc = "0.5.1"
|
||||
pio-proc = "0.2"
|
||||
pio = "0.2"
|
||||
|
||||
# cargo build/run
|
||||
[profile.dev]
|
||||
|
|
51
src/encoder.pio
Normal file
51
src/encoder.pio
Normal file
|
@ -0,0 +1,51 @@
|
|||
; from https://github.com/GitJer/Rotary_encoder/blob/master/pio_rotary_encoder.pio
|
||||
.program pio_rotary_encoder
|
||||
.wrap_target
|
||||
.origin 0 ; The jump table has to start at 0
|
||||
; it contains the correct jumps for each of the 16
|
||||
; combination of 4 bits formed by A'B'AB
|
||||
; A = current reading of pin_A of the rotary encoder
|
||||
; A' = previous reading of pin_A of the rotary encoder
|
||||
; B = current reading of pin_B of the rotary encoder
|
||||
; B' = previous reading of pin_B of the rotary encoder
|
||||
jmp read ; 0000 = from 00 to 00 = no change in reading
|
||||
jmp CW ; 0001 = from 00 to 01 = clockwise rotation
|
||||
jmp CCW ; 0010 = from 00 to 10 = counter clockwise rotation
|
||||
jmp read ; 0011 = from 00 to 11 = error
|
||||
|
||||
jmp CCW ; 0100 = from 01 to 00 = counter clockwise rotation
|
||||
jmp read ; 0101 = from 01 to 01 = no change in reading
|
||||
jmp read ; 0110 = from 01 to 10 = error
|
||||
jmp CW ; 0111 = from 01 to 11 = clockwise rotation
|
||||
|
||||
jmp CW ; 1000 = from 10 to 00 = clockwise rotation
|
||||
jmp read ; 1001 = from 10 to 01 = error
|
||||
jmp read ; 1010 = from 10 to 10 = no change in reading
|
||||
jmp CCW ; 1011 = from 10 to 11 = counter clockwise rotation
|
||||
|
||||
jmp read ; 1100 = from 11 to 00 = error
|
||||
jmp CCW ; 1101 = from 11 to 01 = counter clockwise rotation
|
||||
jmp CW ; 1110 = from 11 to 10 = clockwise rotation
|
||||
jmp read ; 1111 = from 11 to 11 = no change in reading
|
||||
|
||||
pc_start: ; this is the entry point for the program
|
||||
in pins 2 ; read the current values of A and B and use
|
||||
; them to initialize the previous values (A'B')
|
||||
read:
|
||||
mov osr isr ; the OSR is (after the next instruction) used to shift
|
||||
; the two bits with the previous values into the ISR
|
||||
out isr 2 ; shift the previous value into the ISR. This also sets
|
||||
; all other bits in the ISR to 0
|
||||
in pins 2 ; shift the current value into the ISR
|
||||
; the 16 LSB of the ISR now contain 000000000000A'B'AB
|
||||
; this represents a jmp instruction to the address A'B'AB
|
||||
mov exec isr ; do the jmp encoded in the ISR
|
||||
CW: ; a clockwise rotation was detected
|
||||
irq 0 ; signal a clockwise rotation via an IRQ
|
||||
jmp read ; jump to reading the current values of A and B
|
||||
CCW: ; a counter clockwise rotation was detected
|
||||
irq 1 ; signal a counter clockwise rotation via an IRQ
|
||||
; jmp read ; jump to reading the current values of A and B.
|
||||
; the jmp isn't needed because of the .wrap, and the first
|
||||
; statement of the program happens to be a jmp read
|
||||
.wrap
|
349
src/main.rs
349
src/main.rs
|
@ -1,68 +1,137 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate alloc;
|
||||
use alloc::format;
|
||||
use bsp::{
|
||||
entry,
|
||||
#[rtic::app(
|
||||
device = rp_pico::hal::pac,
|
||||
dispatchers = [TIMER_IRQ_1, TIMER_IRQ_2, TIMER_IRQ_3]
|
||||
)]
|
||||
mod app {
|
||||
extern crate alloc;
|
||||
use alloc::format;
|
||||
use core::fmt::Write;
|
||||
use cortex_m::delay::Delay;
|
||||
use defmt_rtt as _;
|
||||
use embedded_alloc::Heap;
|
||||
use embedded_hal::{digital::v2::ToggleableOutputPin, spi::MODE_0};
|
||||
use hd44780::{bus::FourBitBus, HD44780};
|
||||
use panic_probe as _;
|
||||
|
||||
use rp2040_monotonic::{fugit::Duration, Rp2040Monotonic};
|
||||
|
||||
use rp_pico::{
|
||||
hal::{
|
||||
gpio,
|
||||
uart::{UartConfig, UartPeripheral},
|
||||
},
|
||||
};
|
||||
use core::fmt::Write;
|
||||
use cortex_m::delay::Delay;
|
||||
use defmt_rtt as _;
|
||||
use embedded_alloc::Heap;
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use embedded_hal::spi::MODE_0;
|
||||
use panic_probe as _;
|
||||
|
||||
// Provide an alias for our BSP so we can switch targets quickly.
|
||||
// Uncomment the BSP you included in Cargo.toml, the rest of the code does not need to change.
|
||||
use rp_pico as bsp;
|
||||
|
||||
use bsp::hal::{
|
||||
clocks::{init_clocks_and_plls, Clock},
|
||||
fugit::RateExtU32,
|
||||
pac,
|
||||
gpio::{
|
||||
self,
|
||||
bank0::{
|
||||
Gpio0, Gpio1, Gpio14, Gpio16, Gpio17, Gpio18, Gpio19, Gpio2, Gpio20, Gpio21,
|
||||
Gpio25, Gpio3, Gpio4, Gpio5,
|
||||
},
|
||||
FunctionPio0, FunctionSio, FunctionSpi, FunctionUart, Pin, PullDown, SioOutput,
|
||||
},
|
||||
pio::PIOExt,
|
||||
sio::Sio,
|
||||
spi::Spi,
|
||||
spi::{self, Spi},
|
||||
uart::{self, UartConfig, UartPeripheral},
|
||||
watchdog::Watchdog,
|
||||
};
|
||||
},
|
||||
pac::{self, SPI0, UART0},
|
||||
Pins,
|
||||
};
|
||||
|
||||
use hd44780_driver as hd44780;
|
||||
use max31855::{Max31855, Unit};
|
||||
use hd44780_driver as hd44780;
|
||||
use max31855::{Max31855, Unit};
|
||||
|
||||
#[global_allocator]
|
||||
static HEAP: Heap = Heap::empty();
|
||||
#[global_allocator]
|
||||
static HEAP: Heap = Heap::empty();
|
||||
|
||||
fn write_lcd<T: hd44780_driver::bus::DataBus>(
|
||||
lcd: &mut hd44780::HD44780<T>,
|
||||
delay: &mut Delay,
|
||||
string: &str,
|
||||
) {
|
||||
lcd.reset(delay).unwrap();
|
||||
lcd.clear(delay).unwrap();
|
||||
lcd.write_str(string, delay).unwrap();
|
||||
}
|
||||
const MONO_NUM: u32 = 1;
|
||||
const MONO_DENOM: u32 = 1_000_000;
|
||||
const ONE_SEC_TICKS: u64 = 1_000_000;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
type Uart = UartPeripheral<
|
||||
uart::Enabled,
|
||||
UART0,
|
||||
(
|
||||
Pin<Gpio0, FunctionUart, PullDown>,
|
||||
Pin<Gpio1, FunctionUart, PullDown>,
|
||||
),
|
||||
>;
|
||||
|
||||
type Led = Pin<Gpio25, FunctionSio<SioOutput>, PullDown>;
|
||||
|
||||
type ExternalLed = Pin<Gpio14, FunctionSio<SioOutput>, PullDown>;
|
||||
|
||||
type ThermocoupleSpi = Spi<
|
||||
spi::Enabled,
|
||||
SPI0,
|
||||
(
|
||||
Pin<Gpio3, FunctionSpi, PullDown>,
|
||||
Pin<Gpio4, FunctionSpi, PullDown>,
|
||||
Pin<Gpio2, FunctionSpi, PullDown>,
|
||||
),
|
||||
>;
|
||||
|
||||
type Lcd = HD44780<
|
||||
FourBitBus<
|
||||
Pin<Gpio16, FunctionSio<SioOutput>, PullDown>,
|
||||
Pin<Gpio17, FunctionSio<SioOutput>, PullDown>,
|
||||
Pin<Gpio18, FunctionSio<SioOutput>, PullDown>,
|
||||
Pin<Gpio19, FunctionSio<SioOutput>, PullDown>,
|
||||
Pin<Gpio20, FunctionSio<SioOutput>, PullDown>,
|
||||
Pin<Gpio21, FunctionSio<SioOutput>, PullDown>,
|
||||
>,
|
||||
>;
|
||||
|
||||
#[monotonic(binds = TIMER_IRQ_0, default = true)]
|
||||
type Rp2040Mono = Rp2040Monotonic;
|
||||
|
||||
pub struct Thermocouple {
|
||||
cs: Pin<Gpio5, FunctionSio<SioOutput>, PullDown>,
|
||||
spi: ThermocoupleSpi,
|
||||
}
|
||||
|
||||
impl Thermocouple {
|
||||
fn read_temp(
|
||||
&mut self,
|
||||
) -> Result<f32, max31855::Error<core::convert::Infallible, core::convert::Infallible>>
|
||||
{
|
||||
self.spi.read_thermocouple(&mut self.cs, Unit::Celsius)
|
||||
}
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct Shared {
|
||||
delay: Delay,
|
||||
recent_temp: f32,
|
||||
target_temp: f32,
|
||||
uart: Uart,
|
||||
}
|
||||
|
||||
#[local]
|
||||
struct Local {
|
||||
led: Led,
|
||||
thermocouple: Thermocouple,
|
||||
lcd: Lcd,
|
||||
external_led: ExternalLed,
|
||||
}
|
||||
|
||||
#[init]
|
||||
fn init(mut ctx: init::Context) -> (Shared, Local, init::Monotonics) {
|
||||
let mut watchdog = Watchdog::new(ctx.device.WATCHDOG);
|
||||
let mut pac = pac::Peripherals::take().unwrap();
|
||||
let core = pac::CorePeripherals::take().unwrap();
|
||||
let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
||||
let sio = Sio::new(pac.SIO);
|
||||
let sio = Sio::new(ctx.device.SIO);
|
||||
|
||||
// External high-speed crystal on the pico board is 12Mhz
|
||||
let external_xtal_freq_hz = 12_000_000u32;
|
||||
let clocks = init_clocks_and_plls(
|
||||
external_xtal_freq_hz,
|
||||
pac.XOSC,
|
||||
pac.CLOCKS,
|
||||
pac.PLL_SYS,
|
||||
pac.PLL_USB,
|
||||
&mut pac.RESETS,
|
||||
ctx.device.XOSC,
|
||||
ctx.device.CLOCKS,
|
||||
ctx.device.PLL_SYS,
|
||||
ctx.device.PLL_USB,
|
||||
&mut ctx.device.RESETS,
|
||||
&mut watchdog,
|
||||
)
|
||||
.ok()
|
||||
|
@ -76,23 +145,27 @@ fn main() -> ! {
|
|||
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
|
||||
}
|
||||
|
||||
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
|
||||
let mut delay =
|
||||
cortex_m::delay::Delay::new(ctx.core.SYST, clocks.system_clock.freq().to_Hz());
|
||||
|
||||
let pins = bsp::Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
let pins = Pins::new(
|
||||
ctx.device.IO_BANK0,
|
||||
ctx.device.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
&mut ctx.device.RESETS,
|
||||
);
|
||||
|
||||
let uart_pins = (pins.gpio0.into_function(), pins.gpio1.into_function());
|
||||
let mut uart = UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
|
||||
let mut uart: Uart = UartPeripheral::new(
|
||||
ctx.device.UART0,
|
||||
(pins.gpio0.into_function(), pins.gpio1.into_function()),
|
||||
&mut ctx.device.RESETS,
|
||||
)
|
||||
.enable(
|
||||
UartConfig::new(
|
||||
9600u32.Hz(),
|
||||
bsp::hal::uart::DataBits::Eight,
|
||||
rp_pico::hal::uart::DataBits::Eight,
|
||||
None,
|
||||
bsp::hal::uart::StopBits::One,
|
||||
rp_pico::hal::uart::StopBits::One,
|
||||
),
|
||||
clocks.peripheral_clock.freq(),
|
||||
)
|
||||
|
@ -101,29 +174,36 @@ fn main() -> ! {
|
|||
defmt::info!("Program start");
|
||||
writeln!(uart, "Program start\r",).unwrap();
|
||||
|
||||
let mut led_pin = pins.led.into_push_pull_output();
|
||||
let mut external_led_pin = pins.gpio14.into_push_pull_output();
|
||||
//clk
|
||||
let thermometer_spi_sck = pins.gpio2.into_function::<gpio::FunctionSpi>();
|
||||
//MOSI, I think unused?
|
||||
let thermometer_spi_tx = pins.gpio3.into_function::<gpio::FunctionSpi>();
|
||||
//do, or MISO.
|
||||
let thermometer_spi_rx = pins.gpio4.into_function::<gpio::FunctionSpi>();
|
||||
//cs
|
||||
let mut thermometer_spi_csn = pins
|
||||
.gpio5
|
||||
.into_push_pull_output_in_state(gpio::PinState::Low);
|
||||
let led_pin: Led = pins.led.into_push_pull_output();
|
||||
let external_led_pin: ExternalLed = pins.gpio14.into_push_pull_output();
|
||||
|
||||
let thermometer_spi_device = pac.SPI0;
|
||||
let spi_pin_layout = (thermometer_spi_tx, thermometer_spi_rx, thermometer_spi_sck);
|
||||
let mut spi = Spi::<_, _, _, 8>::new(thermometer_spi_device, spi_pin_layout).init(
|
||||
&mut pac.RESETS,
|
||||
//cs
|
||||
let thermometer_spi_device = ctx.device.SPI0;
|
||||
let thermocouple = Thermocouple {
|
||||
cs: pins
|
||||
.gpio5
|
||||
.into_push_pull_output_in_state(gpio::PinState::Low),
|
||||
|
||||
spi: Spi::<_, _, _, 8>::new(
|
||||
thermometer_spi_device,
|
||||
(
|
||||
//mosi
|
||||
pins.gpio3.into_function::<gpio::FunctionSpi>(),
|
||||
//miso/do
|
||||
pins.gpio4.into_function::<gpio::FunctionSpi>(),
|
||||
//clk
|
||||
pins.gpio2.into_function::<gpio::FunctionSpi>(),
|
||||
),
|
||||
)
|
||||
.init(
|
||||
&mut ctx.device.RESETS,
|
||||
125_000_000u32.Hz(),
|
||||
4u32.MHz(),
|
||||
MODE_0,
|
||||
);
|
||||
),
|
||||
};
|
||||
|
||||
let mut lcd = hd44780::HD44780::new_4bit(
|
||||
let lcd: Lcd = hd44780::HD44780::new_4bit(
|
||||
pins.gpio16.into_push_pull_output(), //rs
|
||||
pins.gpio17.into_push_pull_output(), //enable
|
||||
pins.gpio18.into_push_pull_output(), //d4
|
||||
|
@ -134,20 +214,117 @@ fn main() -> ! {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
write_lcd(&mut lcd, &mut delay, "Starting");
|
||||
loop {
|
||||
led_pin.set_high().unwrap();
|
||||
external_led_pin.set_low().unwrap();
|
||||
delay.delay_ms(500);
|
||||
led_pin.set_low().unwrap();
|
||||
external_led_pin.set_high().unwrap();
|
||||
delay.delay_ms(500);
|
||||
match spi.read_thermocouple(&mut thermometer_spi_csn, Unit::Celsius) {
|
||||
let rotary_clk: Pin<_, FunctionPio0, _> = pins.gpio11.into_function();
|
||||
let rotary_clk_id = rotary_clk.id().num;
|
||||
let rotary_dt: Pin<_, FunctionPio0, _> = pins.gpio12.into_function();
|
||||
let rotary_dt_id = rotary_dt.id().num;
|
||||
let program = pio_proc::pio_file!("src/encoder.pio");
|
||||
|
||||
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
|
||||
pio.irq0().enable_sm_interrupt(0);
|
||||
pio.irq1().enable_sm_interrupt(1);
|
||||
let installed = pio.install(&program.program).unwrap();
|
||||
let (mut sm, _, _) = rp_pico::hal::pio::PIOBuilder::from_program(installed)
|
||||
.set_pins(rotary_clk_id, 2)
|
||||
.build(sm0);
|
||||
sm.set_pindirs([
|
||||
(rotary_clk_id, rp_pico::hal::pio::PinDir::Output),
|
||||
(rotary_dt_id, rp_pico::hal::pio::PinDir::Output),
|
||||
]);
|
||||
sm.start();
|
||||
|
||||
let mono = Rp2040Mono::new(ctx.device.TIMER);
|
||||
|
||||
heartbeat::spawn().unwrap();
|
||||
second_heartbeat::spawn().unwrap();
|
||||
|
||||
(
|
||||
Shared {
|
||||
delay,
|
||||
recent_temp: 0.0,
|
||||
target_temp: 25.0,
|
||||
uart,
|
||||
},
|
||||
Local {
|
||||
led: led_pin,
|
||||
thermocouple,
|
||||
lcd,
|
||||
external_led: external_led_pin,
|
||||
},
|
||||
init::Monotonics(mono),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(binds = PIO0_IRQ_0, shared = [target_temp])]
|
||||
fn inc_target(mut ctx: inc_target::Context) {
|
||||
ctx.shared.target_temp.lock(|t| {
|
||||
*t += 1.0;
|
||||
});
|
||||
update_display::spawn().unwrap();
|
||||
}
|
||||
|
||||
#[task(binds = PIO0_IRQ_1, shared = [target_temp])]
|
||||
fn dec_target(mut ctx: dec_target::Context) {
|
||||
ctx.shared.target_temp.lock(|t| {
|
||||
*t -= 1.0;
|
||||
});
|
||||
update_display::spawn().unwrap();
|
||||
}
|
||||
|
||||
#[task(local = [thermocouple], shared = [recent_temp, uart])]
|
||||
fn read_temp(mut ctx: read_temp::Context) {
|
||||
match ctx.local.thermocouple.read_temp() {
|
||||
Ok(v) => {
|
||||
writeln!(uart, "Current: {} \r", v).unwrap();
|
||||
write_lcd(&mut lcd, &mut delay, &format!("{:02.2} C", v))
|
||||
ctx.shared
|
||||
.uart
|
||||
.lock(|u| writeln!(u, "{:02.2} C\r", v).unwrap());
|
||||
ctx.shared.recent_temp.lock(|t| *t = v);
|
||||
update_display::spawn().unwrap();
|
||||
}
|
||||
Err(e) => defmt::error!("error reading temp {}", defmt::Debug2Format(&e)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[task(local = [lcd], shared = [delay, recent_temp, target_temp])]
|
||||
fn update_display(mut ctx: update_display::Context) {
|
||||
ctx.shared.delay.lock(|d| {
|
||||
ctx.local.lcd.reset(d).unwrap();
|
||||
ctx.local.lcd.clear(d).unwrap();
|
||||
ctx.shared.recent_temp.lock(|t| {
|
||||
ctx.local
|
||||
.lcd
|
||||
.write_str(&format!("{:02.2} C", t), d)
|
||||
.unwrap();
|
||||
});
|
||||
ctx.local.lcd.set_cursor_pos(40, d).unwrap();
|
||||
ctx.shared.target_temp.lock(|t| {
|
||||
ctx.local
|
||||
.lcd
|
||||
.write_str(&format!("Target: {:02.2} C", t), d)
|
||||
.unwrap()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[task(local = [led])]
|
||||
fn heartbeat(ctx: heartbeat::Context) {
|
||||
// Flicker the built-in LED
|
||||
ctx.local.led.toggle().unwrap();
|
||||
|
||||
// Re-spawn this task after 1 second
|
||||
let one_second = Duration::<u64, MONO_NUM, MONO_DENOM>::from_ticks(ONE_SEC_TICKS);
|
||||
read_temp::spawn().unwrap();
|
||||
heartbeat::spawn_after(one_second).unwrap();
|
||||
}
|
||||
|
||||
#[task(local = [external_led])]
|
||||
fn second_heartbeat(ctx: second_heartbeat::Context) {
|
||||
// Flicker the built-in LED
|
||||
ctx.local.external_led.toggle().unwrap();
|
||||
|
||||
// Re-spawn this task after 1 second
|
||||
let one_and_one_half_second =
|
||||
Duration::<u64, MONO_NUM, MONO_DENOM>::from_ticks((ONE_SEC_TICKS as f64 * 1.14) as u64);
|
||||
second_heartbeat::spawn_after(one_and_one_half_second).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
BIN
wiring.fzz
BIN
wiring.fzz
Binary file not shown.
Loading…
Reference in a new issue