diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..b5ea198 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,41 @@ +# +# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository. +# +# Copyright (c) The RP-RS Developers, 2021 +# +# The copy in this repository is only used for compiling examples. +# +# This file is MIT or Apache-2.0 as per the repository README.md file +# + +[build] +# Set the default target to match the Cortex-M0+ in the RP2040 +target = "thumbv6m-none-eabi" + +# Target specific options +[target.thumbv6m-none-eabi] +# Pass some extra options to rustc, some of which get passed on to the linker. +# +# * linker argument --nmagic turns off page alignment of sections (which saves +# flash space) +# * linker argument -Tlink.x tells the linker to use link.x as the linker +# script. This is usually provided by the cortex-m-rt crate, and by default +# the version in that crate will include a file called `memory.x` which +# describes the particular memory layout for your specific chip. +# * inline-threshold=5 makes the compiler more aggressive and inlining functions +# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't +# have SIMD) +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "inline-threshold=5", + "-C", "no-vectorize-loops", +] + +# This runner will make a UF2 file and then copy it to a mounted RP2040 in USB +# Bootloader mode: +runner = "elf2uf2-rs -d" + +# This runner will find a supported SWD debug probe and flash your RP2040 over +# SWD: +# runner = "probe-run --chip RP2040" diff --git a/Cargo.toml b/Cargo.toml index 92838d2..82939c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,8 @@ embedded-time = "0.12.0" nb = "1.0.0" pio = "0.2.0" rp2040-hal = "0.4.0" + +[dev-dependencies] +panic-halt= "0.2.0" +rp-pico = "0.3.0" +cortex-m-rt = { version = "0.7" } diff --git a/examples/pico_i2c_pio.rs b/examples/pico_i2c_pio.rs new file mode 100644 index 0000000..b4a263c --- /dev/null +++ b/examples/pico_i2c_pio.rs @@ -0,0 +1,156 @@ +//! # Pico I2C PIO Example +//! +//! Reads the temperature from an LM75B +//! +//! This read over I2C the temerature from an LM75B temperature sensor wired on pins 20 and 21 +//! using the PIO peripheral as an I2C bus controller. +//! The pins used for the I2C can be remapped to any other pin available to the PIO0 peripheral. +//! +//! See the `Cargo.toml` file for Copyright and license details. + +#![no_std] +#![no_main] + +// The trait used by formatting macros like write! and writeln! +use core::fmt::Write as FmtWrite; + +// The macro for our start-up function +use cortex_m_rt::entry; + +// I2C HAL traits & Types. +use embedded_hal::blocking::i2c::{Operation, Read, Transactional, Write}; + +// Time handling traits +use embedded_time::rate::*; + +// Ensure we halt the program on panic (if we don't mention this crate it won't +// be linked) +use panic_halt as _; + +// Pull in any important traits +use rp_pico::hal::prelude::*; + +// A shorter alias for the Peripheral Access Crate, which provides low-level +// register access +use rp_pico::hal::pac; + +// A shorter alias for the Hardware Abstraction Layer, which provides +// higher-level drivers. +use rp_pico::hal; + +/// Prints the temperature received from the sensor +fn print_temperature(serial: &mut impl FmtWrite, temp: [u8; 2]) { + let temp_i16 = i16::from_be_bytes(temp) >> 5; + let temp_f32 = f32::from(temp_i16) * 0.125; + + // Write formatted output but ignore any error. + let _ = writeln!(serial, "Temperature: {:0.2}°C", temp_f32); +} + +/// Entry point to our bare-metal application. +/// +/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function +/// as soon as all global variables are initialised. +/// +/// The function configures the RP2040 peripherals, reads the temperature from +/// the attached LM75B using PIO0. +#[entry] +fn main() -> ! { + // Grab our singleton objects + let mut pac = pac::Peripherals::take().unwrap(); + + // Set up the watchdog driver - needed by the clock setup code + let mut watchdog = hal::Watchdog::new(pac.WATCHDOG); + + // Configure the clocks + // + // The default is to generate a 125 MHz system clock + let clocks = hal::clocks::init_clocks_and_plls( + rp_pico::XOSC_CRYSTAL_FREQ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + // The single-cycle I/O block controls our GPIO pins + let sio = hal::Sio::new(pac.SIO); + + // Set the pins up according to their function on this particular board + let pins = rp_pico::Pins::new( + pac.IO_BANK0, + pac.PADS_BANK0, + sio.gpio_bank0, + &mut pac.RESETS, + ); + + let uart_pins = ( + // UART TX (characters sent from RP2040) on pin 1 (GPIO0) + pins.gpio0.into_mode::(), + // UART RX (characters received by RP2040) on pin 2 (GPIO1) + pins.gpio1.into_mode::(), + ); + + let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) + .enable( + hal::uart::common_configs::_115200_8_N_1, + clocks.peripheral_clock.into(), + ) + .unwrap(); + + let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS); + + let mut i2c_pio = i2c_pio::I2C::new( + &mut pio, + pins.gpio20, + pins.gpio21, + sm0, + 100_000.Hz(), + clocks.system_clock.freq(), + ); + + let mut temp = [0; 2]; + i2c_pio + .read(0x48u8, &mut temp) + .expect("Failed to read from the peripheral"); + print_temperature(&mut uart, temp); + + i2c_pio + .write(0x48u8, &[0]) + .expect("Failed to write to the peripheral"); + + let mut temp = [0; 2]; + i2c_pio + .read(0x48u8, &mut temp) + .expect("Failed to read from the peripheral"); + print_temperature(&mut uart, temp); + + let mut config = [0]; + let mut thyst = [0; 2]; + let mut tos = [0; 2]; + let mut temp = [0; 2]; + let mut operations = [ + Operation::Write(&[1]), + Operation::Read(&mut config), + Operation::Write(&[2]), + Operation::Read(&mut thyst), + Operation::Write(&[3]), + Operation::Read(&mut tos), + Operation::Write(&[0]), + Operation::Read(&mut temp), + ]; + i2c_pio + .exec(0x48u8, &mut operations) + .expect("Failed to run all operations"); + print_temperature(&mut uart, temp); + + loop { + cortex_m::asm::nop(); + } +} + +// End of file diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..cc6acfa --- /dev/null +++ b/memory.x @@ -0,0 +1,17 @@ +/* This file is only used for compiling examples */ + +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +EXTERN(BOOT2_FIRMWARE) + +SECTIONS { + /* ### Boot loader */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text;